mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Merge remote-tracking branch 'origin/main' into expect-fx-codegen
This commit is contained in:
commit
a22e04361c
222 changed files with 10039 additions and 1945 deletions
|
@ -172,7 +172,7 @@ ask the compiler to emit debug information during various stages of compilation.
|
|||
|
||||
There are some goals for more sophisticated debugging tools:
|
||||
|
||||
- A nicer unification debugger, see https://github.com/rtfeldman/roc/issues/2486.
|
||||
- A nicer unification debugger, see https://github.com/roc-lang/roc/issues/2486.
|
||||
Any interest in helping out here is greatly appreciated.
|
||||
|
||||
### General Tips
|
||||
|
|
|
@ -3,6 +3,6 @@ name = "arena-pool"
|
|||
version = "0.0.1"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
repository = "https://github.com/rtfeldman/roc"
|
||||
repository = "https://github.com/roc-lang/roc"
|
||||
edition = "2021"
|
||||
description = "A CLI for Roc"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod link;
|
||||
pub mod program;
|
||||
|
|
|
@ -113,7 +113,6 @@ pub fn build_zig_host_native(
|
|||
target: &str,
|
||||
opt_level: OptLevel,
|
||||
shared_lib_path: Option<&Path>,
|
||||
_target_valgrind: bool,
|
||||
) -> Output {
|
||||
let mut command = Command::new(&zig_executable());
|
||||
command
|
||||
|
@ -149,13 +148,10 @@ pub fn build_zig_host_native(
|
|||
target,
|
||||
]);
|
||||
|
||||
// use single threaded testing for cli_run and enable this code if valgrind fails with unhandled instruction bytes, see #1963.
|
||||
/*if target_valgrind {
|
||||
command.args(&[
|
||||
"-mcpu",
|
||||
"x86_64"
|
||||
]);
|
||||
}*/
|
||||
// valgrind does not yet support avx512 instructions, see #1963.
|
||||
if env::var("NO_AVX512").is_ok() {
|
||||
command.args(&["-mcpu", "x86_64"]);
|
||||
}
|
||||
|
||||
if matches!(opt_level, OptLevel::Optimize) {
|
||||
command.args(&["-O", "ReleaseSafe"]);
|
||||
|
@ -177,7 +173,6 @@ pub fn build_zig_host_native(
|
|||
target: &str,
|
||||
opt_level: OptLevel,
|
||||
shared_lib_path: Option<&Path>,
|
||||
_target_valgrind: bool,
|
||||
) -> Output {
|
||||
let mut command = Command::new(&zig_executable());
|
||||
command
|
||||
|
@ -234,7 +229,6 @@ pub fn build_zig_host_native(
|
|||
opt_level: OptLevel,
|
||||
shared_lib_path: Option<&Path>,
|
||||
// For compatibility with the non-macOS def above. Keep these in sync.
|
||||
_target_valgrind: bool,
|
||||
) -> Output {
|
||||
use serde_json::Value;
|
||||
|
||||
|
@ -463,7 +457,6 @@ pub fn rebuild_host(
|
|||
target: &Triple,
|
||||
host_input_path: &Path,
|
||||
shared_lib_path: Option<&Path>,
|
||||
target_valgrind: bool,
|
||||
) -> PathBuf {
|
||||
let c_host_src = host_input_path.with_file_name("host.c");
|
||||
let c_host_dest = host_input_path.with_file_name("c_host.o");
|
||||
|
@ -535,7 +528,6 @@ pub fn rebuild_host(
|
|||
"native",
|
||||
opt_level,
|
||||
shared_lib_path,
|
||||
target_valgrind,
|
||||
)
|
||||
}
|
||||
Architecture::X86_32(_) => {
|
||||
|
@ -549,7 +541,6 @@ pub fn rebuild_host(
|
|||
"i386-linux-musl",
|
||||
opt_level,
|
||||
shared_lib_path,
|
||||
target_valgrind,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -564,7 +555,6 @@ pub fn rebuild_host(
|
|||
target_zig_str(target),
|
||||
opt_level,
|
||||
shared_lib_path,
|
||||
target_valgrind,
|
||||
)
|
||||
}
|
||||
_ => panic!("Unsupported architecture {:?}", target.architecture),
|
||||
|
@ -971,7 +961,7 @@ fn link_linux(
|
|||
// ld.lld requires this argument, and does not accept --arch
|
||||
// .args(&["-L/usr/lib/x86_64-linux-gnu"])
|
||||
.args(&[
|
||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496365925
|
||||
// Libraries - see https://github.com/roc-lang/roc/pull/554#discussion_r496365925
|
||||
// for discussion and further references
|
||||
"-lc",
|
||||
"-lm",
|
||||
|
@ -1054,7 +1044,7 @@ fn link_macos(
|
|||
}
|
||||
|
||||
ld_command.args(&[
|
||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
||||
// Libraries - see https://github.com/roc-lang/roc/pull/554#discussion_r496392274
|
||||
// for discussion and further references
|
||||
"-lSystem",
|
||||
"-lresolv",
|
||||
|
@ -1079,7 +1069,7 @@ fn link_macos(
|
|||
"QuartzCore",
|
||||
// "-lrt", // TODO shouldn't we need this?
|
||||
// "-lc_nonshared", // TODO shouldn't we need this?
|
||||
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/roc-lang/roc/pull/554#discussion_r496370840
|
||||
"-framework",
|
||||
"Security",
|
||||
// Output
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
||||
set -euxo pipefail
|
||||
|
||||
# Test every zig
|
||||
# Execute zig tests (see build.zig)
|
||||
zig build test
|
||||
|
||||
# fmt every zig
|
||||
# check formatting of zig files
|
||||
find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check || (echo "zig fmt --check FAILED! Check the previous lines to see which files were improperly formatted." && exit 1)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
||||
set -euxo pipefail
|
||||
|
||||
# Test failures will always point at the _start function
|
||||
|
|
|
@ -181,7 +181,7 @@ comptime {
|
|||
|
||||
// Utils continued - SJLJ
|
||||
// For tests (in particular test_gen), roc_panic is implemented in terms of
|
||||
// setjmp/longjmp. LLVM is unable to generate code for longjmp on AArch64 (https://github.com/rtfeldman/roc/issues/2965),
|
||||
// setjmp/longjmp. LLVM is unable to generate code for longjmp on AArch64 (https://github.com/roc-lang/roc/issues/2965),
|
||||
// so instead we ask Zig to please provide implementations for us, which is does
|
||||
// (seemingly via musl).
|
||||
pub extern fn setjmp([*c]c_int) c_int;
|
||||
|
|
|
@ -22,6 +22,7 @@ interface Decode
|
|||
bool,
|
||||
string,
|
||||
list,
|
||||
record,
|
||||
custom,
|
||||
decodeWith,
|
||||
fromBytesPartial,
|
||||
|
@ -57,6 +58,7 @@ DecoderFormatting has
|
|||
bool : Decoder Bool fmt | fmt has DecoderFormatting
|
||||
string : Decoder Str fmt | fmt has DecoderFormatting
|
||||
list : Decoder elem fmt -> Decoder (List elem) fmt | fmt has DecoderFormatting
|
||||
record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
|
||||
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
custom = \decode -> @Decoder decode
|
||||
|
|
|
@ -16,6 +16,7 @@ interface Json
|
|||
Decode,
|
||||
Decode.{
|
||||
DecoderFormatting,
|
||||
DecodeResult,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -57,6 +58,7 @@ Json := {} has [
|
|||
bool: decodeBool,
|
||||
string: decodeString,
|
||||
list: decodeList,
|
||||
record: decodeRecord,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -316,7 +318,8 @@ decodeBool = Decode.custom \bytes, @Json {} ->
|
|||
else
|
||||
{ result: Err TooShort, rest: bytes }
|
||||
|
||||
decodeString = Decode.custom \bytes, @Json {} ->
|
||||
jsonString : List U8 -> DecodeResult Str
|
||||
jsonString = \bytes ->
|
||||
{ before, others: afterStartingQuote } = List.split bytes 1
|
||||
|
||||
if
|
||||
|
@ -335,6 +338,9 @@ decodeString = Decode.custom \bytes, @Json {} ->
|
|||
else
|
||||
{ result: Err TooShort, rest: bytes }
|
||||
|
||||
decodeString = Decode.custom \bytes, @Json {} ->
|
||||
jsonString bytes
|
||||
|
||||
decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
|
||||
decodeElems = \chunk, accum ->
|
||||
when Decode.decodeWith chunk decodeElem (@Json {}) is
|
||||
|
@ -372,3 +378,72 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
|
|||
{ result: Err TooShort, rest }
|
||||
else
|
||||
{ result: Err TooShort, rest: bytes }
|
||||
|
||||
parseExactChar : List U8, U8 -> DecodeResult {}
|
||||
parseExactChar = \bytes, char ->
|
||||
when List.get bytes 0 is
|
||||
Ok c ->
|
||||
if
|
||||
c == char
|
||||
then
|
||||
{ result: Ok {}, rest: (List.split bytes 1).others }
|
||||
else
|
||||
{ result: Err TooShort, rest: bytes }
|
||||
|
||||
Err _ -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
openBrace : List U8 -> DecodeResult {}
|
||||
openBrace = \bytes -> parseExactChar bytes (asciiByte '{')
|
||||
|
||||
closingBrace : List U8 -> DecodeResult {}
|
||||
closingBrace = \bytes -> parseExactChar bytes (asciiByte '}')
|
||||
|
||||
recordKey : List U8 -> DecodeResult Str
|
||||
recordKey = \bytes -> jsonString bytes
|
||||
|
||||
anything : List U8 -> DecodeResult {}
|
||||
anything = \bytes -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
colon : List U8 -> DecodeResult {}
|
||||
colon = \bytes -> parseExactChar bytes (asciiByte ':')
|
||||
|
||||
comma : List U8 -> DecodeResult {}
|
||||
comma = \bytes -> parseExactChar bytes (asciiByte ',')
|
||||
|
||||
tryDecode : DecodeResult a, ({ val : a, rest : List U8 } -> DecodeResult b) -> DecodeResult b
|
||||
tryDecode = \{ result, rest }, mapper ->
|
||||
when result is
|
||||
Ok val -> mapper { val, rest }
|
||||
Err e -> { result: Err e, rest }
|
||||
|
||||
decodeRecord = \initialState, stepField, finalizer -> Decode.custom \bytes, @Json {} ->
|
||||
# NB: the stepper function must be passed explicitly until #2894 is resolved.
|
||||
decodeFields = \stepper, state, kvBytes ->
|
||||
{ val: key, rest } <- recordKey kvBytes |> tryDecode
|
||||
{ rest: afterColonBytes } <- colon rest |> tryDecode
|
||||
{ val: newState, rest: beforeCommaOrBreak } <- tryDecode
|
||||
(
|
||||
when stepper state key is
|
||||
Skip ->
|
||||
{ rest: beforeCommaOrBreak } <- afterColonBytes |> anything |> tryDecode
|
||||
{ result: Ok state, rest: beforeCommaOrBreak }
|
||||
|
||||
Keep decoder ->
|
||||
Decode.decodeWith afterColonBytes decoder (@Json {})
|
||||
)
|
||||
|
||||
{ result: commaResult, rest: nextBytes } = comma beforeCommaOrBreak
|
||||
|
||||
when commaResult is
|
||||
Ok {} -> decodeFields stepField newState nextBytes
|
||||
Err _ -> { result: Ok newState, rest: nextBytes }
|
||||
|
||||
{ rest: afterBraceBytes } <- bytes |> openBrace |> tryDecode
|
||||
|
||||
{ val: endStateResult, rest: beforeClosingBraceBytes } <- decodeFields stepField initialState afterBraceBytes |> tryDecode
|
||||
|
||||
{ rest: afterRecordBytes } <- beforeClosingBraceBytes |> closingBrace |> tryDecode
|
||||
|
||||
when finalizer endStateResult is
|
||||
Ok val -> { result: Ok val, rest: afterRecordBytes }
|
||||
Err e -> { result: Err e, rest: afterRecordBytes }
|
||||
|
|
|
@ -857,8 +857,44 @@ isMultipleOf : Int a, Int a -> Bool
|
|||
bitwiseAnd : Int a, Int a -> Int a
|
||||
bitwiseXor : Int a, Int a -> Int a
|
||||
bitwiseOr : Int a, Int a -> Int a
|
||||
|
||||
## Bitwise left shift of a number by another
|
||||
##
|
||||
## The least significant bits always become 0. This means that shifting left is
|
||||
## like multiplying by factors of two for unsigned integers.
|
||||
##
|
||||
## >>> shiftLeftBy 0b0000_0011 2 == 0b0000_1100
|
||||
##
|
||||
## >>> 0b0000_0101 |> shiftLeftBy 2 == 0b0000_1100
|
||||
##
|
||||
## In some languages `shiftLeftBy` is implemented as a binary operator `<<`.
|
||||
shiftLeftBy : Int a, Int a -> Int a
|
||||
|
||||
## Bitwise arithmetic shift of a number by another
|
||||
##
|
||||
## The most significant bits are copied from the current.
|
||||
##
|
||||
## >>> shiftRightBy 0b0000_0011 2 == 0b0000_1100
|
||||
##
|
||||
## >>> 0b0001_0100 |> shiftRightBy 2 == 0b0000_0101
|
||||
##
|
||||
## >>> 0b1001_0000 |> shiftRightBy 2 == 0b1110_0100
|
||||
##
|
||||
## In some languages `shiftRightBy` is implemented as a binary operator `>>>`.
|
||||
shiftRightBy : Int a, Int a -> Int a
|
||||
|
||||
## Bitwise logical right shift of a number by another
|
||||
##
|
||||
## The most significant bits always become 0. This means that shifting left is
|
||||
## like dividing by factors of two for unsigned integers.
|
||||
##
|
||||
## >>> shiftRightBy 0b0010_1000 2 == 0b0000_1010
|
||||
##
|
||||
## >>> 0b0010_1000 |> shiftRightBy 2 == 0b0000_1010
|
||||
##
|
||||
## >>> 0b1001_0000 |> shiftRightBy 2 == 0b0010_0100
|
||||
##
|
||||
## In some languages `shiftRightBy` is implemented as a binary operator `>>`.
|
||||
shiftRightZfBy : Int a, Int a -> Int a
|
||||
|
||||
## Round off the given fraction to the nearest integer.
|
||||
|
|
|
@ -41,10 +41,39 @@ insert = \@Set dict, key ->
|
|||
|> Dict.insert key {}
|
||||
|> @Set
|
||||
|
||||
# Inserting a duplicate key has no effect.
|
||||
expect
|
||||
actual =
|
||||
Set.empty
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "bar"
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "baz"
|
||||
|
||||
expected =
|
||||
Set.empty
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "bar"
|
||||
|> Set.insert "baz"
|
||||
|
||||
expected == actual
|
||||
|
||||
len : Set k -> Nat
|
||||
len = \@Set dict ->
|
||||
Dict.len dict
|
||||
|
||||
# Inserting a duplicate key has no effect on length.
|
||||
expect
|
||||
actual =
|
||||
Set.empty
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "bar"
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "baz"
|
||||
|> Set.len
|
||||
|
||||
actual == 3
|
||||
|
||||
## Drops the given element from the set.
|
||||
remove : Set k, k -> Set k
|
||||
remove = \@Set dict, key ->
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod bitcode;
|
||||
pub mod roc;
|
||||
|
|
|
@ -20,4 +20,4 @@ bitvec = "1"
|
|||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.0.0"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
|
|
|
@ -444,7 +444,7 @@ fn no_region<T>(value: T) -> Loc<T> {
|
|||
#[inline(always)]
|
||||
fn tag(name: &'static str, args: Vec<Expr>, var_store: &mut VarStore) -> Expr {
|
||||
Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
tag_union_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: TagName(name.into()),
|
||||
arguments: args
|
||||
|
|
|
@ -535,12 +535,12 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
},
|
||||
|
||||
Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments,
|
||||
} => Tag {
|
||||
variant_var: sub!(*variant_var),
|
||||
tag_union_var: sub!(*variant_var),
|
||||
ext_var: sub!(*ext_var),
|
||||
name: name.clone(),
|
||||
arguments: arguments
|
||||
|
@ -1164,13 +1164,13 @@ mod test {
|
|||
let var2 = new_var(&mut subs, FlexVar(Some(b)));
|
||||
|
||||
let expr = Expr::Tag {
|
||||
variant_var: var1,
|
||||
tag_union_var: var1,
|
||||
ext_var: Variable::EMPTY_TAG_UNION,
|
||||
name: TagName("F".into()),
|
||||
arguments: vec![(
|
||||
var2,
|
||||
Loc::at_zero(Expr::Tag {
|
||||
variant_var: var2,
|
||||
tag_union_var: var2,
|
||||
ext_var: Variable::EMPTY_TAG_UNION,
|
||||
name: TagName("G".into()),
|
||||
arguments: vec![],
|
||||
|
@ -1185,7 +1185,7 @@ mod test {
|
|||
|
||||
match expr {
|
||||
Expr::Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
mut arguments,
|
||||
|
@ -1219,7 +1219,7 @@ mod test {
|
|||
|
||||
match arg.value {
|
||||
Expr::Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments,
|
||||
|
@ -1250,13 +1250,13 @@ mod test {
|
|||
let var2 = new_var(&mut source, FlexVar(Some(b)));
|
||||
|
||||
let expr = Expr::Tag {
|
||||
variant_var: var1,
|
||||
tag_union_var: var1,
|
||||
ext_var: Variable::EMPTY_TAG_UNION,
|
||||
name: TagName("F".into()),
|
||||
arguments: vec![(
|
||||
var2,
|
||||
Loc::at_zero(Expr::Tag {
|
||||
variant_var: var2,
|
||||
tag_union_var: var2,
|
||||
ext_var: Variable::EMPTY_TAG_UNION,
|
||||
name: TagName("G".into()),
|
||||
arguments: vec![],
|
||||
|
@ -1271,7 +1271,7 @@ mod test {
|
|||
|
||||
match expr {
|
||||
Expr::Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
mut arguments,
|
||||
|
@ -1300,7 +1300,7 @@ mod test {
|
|||
|
||||
match arg.value {
|
||||
Expr::Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments,
|
||||
|
|
|
@ -232,7 +232,7 @@ impl PendingTypeDef<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Declaration {
|
||||
|
|
|
@ -184,7 +184,7 @@ pub enum Expr {
|
|||
|
||||
// Sum Types
|
||||
Tag {
|
||||
variant_var: Variable,
|
||||
tag_union_var: Variable,
|
||||
ext_var: Variable,
|
||||
name: TagName,
|
||||
arguments: Vec<(Variable, Loc<Expr>)>,
|
||||
|
@ -780,12 +780,12 @@ pub fn canonicalize_expr<'a>(
|
|||
return (fn_expr, output);
|
||||
}
|
||||
Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
..
|
||||
} => Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments: args,
|
||||
|
@ -796,7 +796,7 @@ pub fn canonicalize_expr<'a>(
|
|||
name,
|
||||
..
|
||||
} => Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments: args,
|
||||
|
@ -1422,7 +1422,7 @@ fn canonicalize_when_branch<'a>(
|
|||
if output.references.has_value_lookup(symbol) {
|
||||
pattern_bound_symbols_body_needs.insert(symbol);
|
||||
} else {
|
||||
env.problem(Problem::UnusedDef(symbol, region));
|
||||
env.problem(Problem::UnusedBranchDef(symbol, region));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1893,7 +1893,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
}
|
||||
|
||||
Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod abilities;
|
||||
pub mod annotation;
|
||||
|
|
|
@ -696,15 +696,26 @@ pub fn canonicalize_module_defs<'a>(
|
|||
referenced_values.extend(env.qualified_value_lookups.iter().copied());
|
||||
referenced_types.extend(env.qualified_type_lookups.iter().copied());
|
||||
|
||||
let mut fix_closures_no_capture_symbols = VecSet::default();
|
||||
let mut fix_closures_closure_captures = VecMap::default();
|
||||
for index in 0..declarations.len() {
|
||||
use crate::expr::DeclarationTag::*;
|
||||
|
||||
// For each declaration, we need to fixup the closures inside its def.
|
||||
// Reuse the fixup buffer allocations from the previous iteration.
|
||||
fix_closures_no_capture_symbols.clear();
|
||||
fix_closures_closure_captures.clear();
|
||||
|
||||
match declarations.declarations[index] {
|
||||
Value => {
|
||||
// def pattern has no default expressions, so skip
|
||||
let loc_expr = &mut declarations.expressions[index];
|
||||
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default());
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
Function(f_index) | Recursive(f_index) | TailRecursive(f_index) => {
|
||||
let name = declarations.symbols[index].value;
|
||||
|
@ -722,30 +733,51 @@ pub fn canonicalize_module_defs<'a>(
|
|||
for (_, _, loc_pat) in function_def.arguments.iter_mut() {
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_pat.value,
|
||||
&mut no_capture_symbols,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
Destructure(d_index) => {
|
||||
let destruct_def = &mut declarations.destructs[d_index.index()];
|
||||
let loc_pat = &mut destruct_def.loc_pattern;
|
||||
let loc_expr = &mut declarations.expressions[index];
|
||||
|
||||
fix_values_captured_in_closure_pattern(&mut loc_pat.value, &mut VecSet::default());
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default());
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_pat.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
MutualRecursion { .. } => {
|
||||
// the declarations of this group will be treaded individually by later iterations
|
||||
}
|
||||
Expectation => {
|
||||
let loc_expr = &mut declarations.expressions[index];
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default());
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_expr = &mut declarations.expressions[index];
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default());
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -771,16 +803,26 @@ pub fn canonicalize_module_defs<'a>(
|
|||
fn fix_values_captured_in_closure_def(
|
||||
def: &mut crate::def::Def,
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
closure_captures: &mut VecMap<Symbol, Vec<(Symbol, Variable)>>,
|
||||
) {
|
||||
// patterns can contain default expressions, so much go over them too!
|
||||
fix_values_captured_in_closure_pattern(&mut def.loc_pattern.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut def.loc_pattern.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
|
||||
fix_values_captured_in_closure_expr(&mut def.loc_expr.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut def.loc_expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
fn fix_values_captured_in_closure_defs(
|
||||
defs: &mut [crate::def::Def],
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
closure_captures: &mut VecMap<Symbol, Vec<(Symbol, Variable)>>,
|
||||
) {
|
||||
// recursive defs cannot capture each other
|
||||
for def in defs.iter() {
|
||||
|
@ -789,16 +831,38 @@ fn fix_values_captured_in_closure_defs(
|
|||
);
|
||||
}
|
||||
|
||||
// TODO mutually recursive functions should both capture the union of both their capture sets
|
||||
|
||||
for def in defs.iter_mut() {
|
||||
fix_values_captured_in_closure_def(def, no_capture_symbols);
|
||||
fix_values_captured_in_closure_def(def, no_capture_symbols, closure_captures);
|
||||
}
|
||||
|
||||
// Mutually recursive functions should both capture the union of all their capture sets
|
||||
//
|
||||
// Really unfortunate we make a lot of clones here, can this be done more efficiently?
|
||||
let mut total_capture_set = Vec::default();
|
||||
for def in defs.iter_mut() {
|
||||
if let Expr::Closure(ClosureData {
|
||||
captured_symbols, ..
|
||||
}) = &def.loc_expr.value
|
||||
{
|
||||
total_capture_set.extend(captured_symbols.iter().copied());
|
||||
}
|
||||
}
|
||||
total_capture_set.sort_by_key(|(sym, _)| *sym);
|
||||
total_capture_set.dedup_by_key(|(sym, _)| *sym);
|
||||
for def in defs.iter_mut() {
|
||||
if let Expr::Closure(ClosureData {
|
||||
captured_symbols, ..
|
||||
}) = &mut def.loc_expr.value
|
||||
{
|
||||
*captured_symbols = total_capture_set.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_values_captured_in_closure_pattern(
|
||||
pattern: &mut crate::pattern::Pattern,
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
closure_captures: &mut VecMap<Symbol, Vec<(Symbol, Variable)>>,
|
||||
) {
|
||||
use crate::pattern::Pattern::*;
|
||||
|
||||
|
@ -808,24 +872,35 @@ fn fix_values_captured_in_closure_pattern(
|
|||
..
|
||||
} => {
|
||||
for (_, loc_arg) in loc_args.iter_mut() {
|
||||
fix_values_captured_in_closure_pattern(&mut loc_arg.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_arg.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
UnwrappedOpaque { argument, .. } => {
|
||||
let (_, loc_arg) = &mut **argument;
|
||||
fix_values_captured_in_closure_pattern(&mut loc_arg.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_arg.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
RecordDestructure { destructs, .. } => {
|
||||
for loc_destruct in destructs.iter_mut() {
|
||||
use crate::pattern::DestructType::*;
|
||||
match &mut loc_destruct.value.typ {
|
||||
Required => {}
|
||||
Optional(_, loc_expr) => {
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols)
|
||||
}
|
||||
Optional(_, loc_expr) => fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
),
|
||||
Guard(_, loc_pattern) => fix_values_captured_in_closure_pattern(
|
||||
&mut loc_pattern.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -848,19 +923,28 @@ fn fix_values_captured_in_closure_pattern(
|
|||
fn fix_values_captured_in_closure_expr(
|
||||
expr: &mut crate::expr::Expr,
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
closure_captures: &mut VecMap<Symbol, Vec<(Symbol, Variable)>>,
|
||||
) {
|
||||
use crate::expr::Expr::*;
|
||||
|
||||
match expr {
|
||||
LetNonRec(def, loc_expr) => {
|
||||
// LetNonRec(Box<Def>, Box<Located<Expr>>, Variable, Aliases),
|
||||
fix_values_captured_in_closure_def(def, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_def(def, no_capture_symbols, closure_captures);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
LetRec(defs, loc_expr, _) => {
|
||||
// LetRec(Vec<Def>, Box<Located<Expr>>, Variable, Aliases),
|
||||
fix_values_captured_in_closure_defs(defs, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_defs(defs, no_capture_symbols, closure_captures);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
Expect {
|
||||
|
@ -868,8 +952,16 @@ fn fix_values_captured_in_closure_expr(
|
|||
loc_continuation,
|
||||
lookups_in_cond: _,
|
||||
} => {
|
||||
fix_values_captured_in_closure_expr(&mut loc_condition.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(&mut loc_continuation.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_condition.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_continuation.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
|
@ -891,16 +983,58 @@ fn fix_values_captured_in_closure_expr(
|
|||
captured_symbols.retain(|(s, _)| !no_capture_symbols.contains(s));
|
||||
captured_symbols.retain(|(s, _)| s != name);
|
||||
|
||||
let original_captures_len = captured_symbols.len();
|
||||
let mut num_visited = 0;
|
||||
let mut i = 0;
|
||||
while num_visited < original_captures_len {
|
||||
// If we've captured a capturing closure, replace the captured closure symbol with
|
||||
// the symbols of its captures. That way, we can construct the closure with the
|
||||
// captures it needs inside our body.
|
||||
//
|
||||
// E.g.
|
||||
// x = ""
|
||||
// inner = \{} -> x
|
||||
// outer = \{} -> inner {}
|
||||
//
|
||||
// initially `outer` captures [inner], but this is then replaced with just [x].
|
||||
let (captured_symbol, _) = captured_symbols[i];
|
||||
if let Some(captures) = closure_captures.get(&captured_symbol) {
|
||||
debug_assert!(!captures.is_empty());
|
||||
captured_symbols.swap_remove(i);
|
||||
captured_symbols.extend(captures);
|
||||
// Jump two, because the next element is now one of the newly-added captures,
|
||||
// which we don't need to check.
|
||||
i += 2;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
num_visited += 1;
|
||||
}
|
||||
if captured_symbols.len() > original_captures_len {
|
||||
// Re-sort, since we've added new captures.
|
||||
captured_symbols.sort_by_key(|(sym, _)| *sym);
|
||||
}
|
||||
|
||||
if captured_symbols.is_empty() {
|
||||
no_capture_symbols.insert(*name);
|
||||
} else {
|
||||
closure_captures.insert(*name, captured_symbols.to_vec());
|
||||
}
|
||||
|
||||
// patterns can contain default expressions, so much go over them too!
|
||||
for (_, _, loc_pat) in arguments.iter_mut() {
|
||||
fix_values_captured_in_closure_pattern(&mut loc_pat.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_pat.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
fix_values_captured_in_closure_expr(&mut loc_body.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_body.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
Num(..)
|
||||
|
@ -918,28 +1052,45 @@ fn fix_values_captured_in_closure_expr(
|
|||
|
||||
List { loc_elems, .. } => {
|
||||
for elem in loc_elems.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(&mut elem.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut elem.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
When {
|
||||
loc_cond, branches, ..
|
||||
} => {
|
||||
fix_values_captured_in_closure_expr(&mut loc_cond.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_cond.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
|
||||
for branch in branches.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(&mut branch.value.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut branch.value.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
|
||||
// patterns can contain default expressions, so much go over them too!
|
||||
for loc_pat in branch.patterns.iter_mut() {
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_pat.pattern.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(guard) = &mut branch.guard {
|
||||
fix_values_captured_in_closure_expr(&mut guard.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut guard.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -950,23 +1101,43 @@ fn fix_values_captured_in_closure_expr(
|
|||
..
|
||||
} => {
|
||||
for (loc_cond, loc_then) in branches.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(&mut loc_cond.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(&mut loc_then.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_cond.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_then.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
fix_values_captured_in_closure_expr(&mut final_else.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut final_else.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
Call(function, arguments, _) => {
|
||||
fix_values_captured_in_closure_expr(&mut function.1.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut function.1.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
|
||||
for (_, loc_arg) in arguments.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_arg.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
RunLowLevel { args, .. } | ForeignCall { args, .. } => {
|
||||
for (_, arg) in args.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(arg, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(arg, no_capture_symbols, closure_captures);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,22 +1146,38 @@ fn fix_values_captured_in_closure_expr(
|
|||
updates: fields, ..
|
||||
} => {
|
||||
for (_, field) in fields.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(&mut field.loc_expr.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut field.loc_expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Access { loc_expr, .. } => {
|
||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
Tag { arguments, .. } => {
|
||||
for (_, loc_arg) in arguments.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_arg.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
OpaqueRef { argument, .. } => {
|
||||
let (_, loc_arg) = &mut **argument;
|
||||
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_arg.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
OpaqueWrapFunction(_) => {}
|
||||
}
|
||||
|
|
|
@ -244,7 +244,7 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
|
|||
walk_record_fields(visitor, updates.iter());
|
||||
}
|
||||
Expr::Tag {
|
||||
variant_var: _,
|
||||
tag_union_var: _,
|
||||
ext_var: _,
|
||||
name: _,
|
||||
arguments,
|
||||
|
|
|
@ -1023,7 +1023,7 @@ mod test_can {
|
|||
# There was a bug where annotating a def meant that its
|
||||
# references no longer got reported.
|
||||
#
|
||||
# https://github.com/rtfeldman/roc/issues/298
|
||||
# https://github.com/roc-lang/roc/issues/298
|
||||
x : List Booly
|
||||
x = [y]
|
||||
|
||||
|
|
|
@ -10,5 +10,5 @@ im = "15.0.0"
|
|||
im-rc = "15.0.0"
|
||||
wyhash = "0.5.0"
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
hashbrown = { version = "0.12.1", features = [ "bumpalo" ] }
|
||||
hashbrown = { version = "0.12.3", features = [ "bumpalo" ] }
|
||||
bitvec = "1"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
pub mod all;
|
||||
|
|
|
@ -140,6 +140,12 @@ impl<K: PartialEq, V> VecMap<K, V> {
|
|||
cur_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all key/value pairs from the map, without affecting its allocated capacity.
|
||||
pub fn clear(&mut self) {
|
||||
self.keys.clear();
|
||||
self.values.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: PartialEq, V> Extend<(K, V)> for VecMap<K, V> {
|
||||
|
|
|
@ -83,6 +83,11 @@ impl<T: PartialEq> VecSet<T> {
|
|||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||
self.elements.iter_mut()
|
||||
}
|
||||
|
||||
/// Removes all elements from the set, without affecting its allocated capacity.
|
||||
pub fn clear(&mut self) {
|
||||
self.elements.clear()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord> Extend<A> for VecSet<A> {
|
||||
|
|
|
@ -1040,7 +1040,7 @@ pub fn constrain_expr(
|
|||
body_con
|
||||
}
|
||||
Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod builtins;
|
||||
pub mod expr;
|
||||
|
|
|
@ -19,3 +19,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
|||
[features]
|
||||
default = []
|
||||
debug-derived-symbols = ["roc_module/debug-symbols"]
|
||||
# Enables open extension variables for constructed records and tag unions.
|
||||
# This is not necessary for code generation, but may be necessary if you are
|
||||
# constraining and solving generated derived bodies.
|
||||
open-extension-vars = []
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -133,6 +133,12 @@ impl DerivedModule {
|
|||
self.map.entry(key).or_insert(triple)
|
||||
}
|
||||
|
||||
pub fn is_derived_def(&self, def_symbol: Symbol) -> bool {
|
||||
self.map
|
||||
.iter()
|
||||
.any(|(_, (symbol, _, _))| *symbol == def_symbol)
|
||||
}
|
||||
|
||||
pub fn iter_all(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&DeriveKey, &(Symbol, Def, SpecializationLambdaSets))> {
|
||||
|
|
|
@ -130,7 +130,7 @@ impl Env<'_> {
|
|||
// we only expect `bar` to polymorphic at this stage!
|
||||
//
|
||||
// TODO: it would be better if `unify` could prune these for us. See also
|
||||
// https://github.com/rtfeldman/roc/issues/3207; that is a blocker for this TODO.
|
||||
// https://github.com/roc-lang/roc/issues/3207; that is a blocker for this TODO.
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
for (spec_var, lambda_sets) in _lambda_sets_to_specialize.drain() {
|
||||
|
@ -152,4 +152,27 @@ impl Env<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an extension variable for a tag union or record.
|
||||
///
|
||||
/// Derivers should always construct tag union and record types such that they are closed.
|
||||
/// If the `open-extension-vars` feature is turned on, flex extension vars will be
|
||||
/// returned; otherwise, the appropriate closed extension variable for the type will be
|
||||
/// returned.
|
||||
#[inline(always)]
|
||||
pub fn new_ext_var(&mut self, kind: ExtensionKind) -> Variable {
|
||||
if cfg!(feature = "open-extension-vars") {
|
||||
self.subs.fresh_unnamed_flex_var()
|
||||
} else {
|
||||
match kind {
|
||||
ExtensionKind::Record => Variable::EMPTY_RECORD,
|
||||
ExtensionKind::TagUnion => Variable::EMPTY_TAG_UNION,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ExtensionKind {
|
||||
Record,
|
||||
TagUnion,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_module::{ident::Lowercase, symbol::Symbol};
|
||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||
|
||||
use crate::DeriveError;
|
||||
use crate::{util::debug_name_record, DeriveError};
|
||||
|
||||
#[derive(Hash)]
|
||||
pub enum FlatDecodable {
|
||||
|
@ -12,12 +12,16 @@ pub enum FlatDecodable {
|
|||
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
||||
pub enum FlatDecodableKey {
|
||||
List(/* takes one variable */),
|
||||
|
||||
// Unfortunate that we must allocate here, c'est la vie
|
||||
Record(Vec<Lowercase>),
|
||||
}
|
||||
|
||||
impl FlatDecodableKey {
|
||||
pub(crate) fn debug_name(&self) -> String {
|
||||
match self {
|
||||
FlatDecodableKey::List() => "list".to_string(),
|
||||
FlatDecodableKey::Record(fields) => debug_name_record(fields),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +37,25 @@ impl FlatDecodable {
|
|||
Symbol::STR_STR => Ok(Immediate(Symbol::DECODE_STRING)),
|
||||
_ => Err(Underivable),
|
||||
},
|
||||
FlatType::Record(_fields, _ext) => {
|
||||
Err(Underivable) // yet
|
||||
FlatType::Record(fields, ext) => {
|
||||
let fields_iter = match fields.unsorted_iterator(subs, ext) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Err(Underivable),
|
||||
};
|
||||
|
||||
let mut field_names = Vec::with_capacity(fields.len());
|
||||
for (field_name, record_field) in fields_iter {
|
||||
if record_field.is_optional() {
|
||||
// Can't derive a concrete decoder for optional fields, since those are
|
||||
// compile-time-polymorphic
|
||||
return Err(Underivable);
|
||||
}
|
||||
field_names.push(field_name.clone());
|
||||
}
|
||||
|
||||
field_names.sort();
|
||||
|
||||
Ok(Key(FlatDecodableKey::Record(field_names)))
|
||||
}
|
||||
FlatType::TagUnion(_tags, _ext) | FlatType::RecursiveTagUnion(_, _tags, _ext) => {
|
||||
Err(Underivable) // yet
|
||||
|
@ -42,9 +63,7 @@ impl FlatDecodable {
|
|||
FlatType::FunctionOrTagUnion(_name_index, _, _) => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
FlatType::EmptyRecord => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
FlatType::EmptyRecord => Ok(Key(FlatDecodableKey::Record(vec![]))),
|
||||
FlatType::EmptyTagUnion => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ use roc_module::{
|
|||
};
|
||||
use roc_types::subs::{Content, FlatType, GetSubsSlice, Subs, Variable};
|
||||
|
||||
use crate::DeriveError;
|
||||
use crate::{
|
||||
util::{check_empty_ext_var, debug_name_record},
|
||||
DeriveError,
|
||||
};
|
||||
|
||||
#[derive(Hash)]
|
||||
pub enum FlatEncodable {
|
||||
|
@ -28,17 +31,7 @@ impl FlatEncodableKey {
|
|||
FlatEncodableKey::List() => "list".to_string(),
|
||||
FlatEncodableKey::Set() => "set".to_string(),
|
||||
FlatEncodableKey::Dict() => "dict".to_string(),
|
||||
FlatEncodableKey::Record(fields) => {
|
||||
let mut str = String::from('{');
|
||||
fields.iter().enumerate().for_each(|(i, f)| {
|
||||
if i > 0 {
|
||||
str.push(',');
|
||||
}
|
||||
str.push_str(f.as_str());
|
||||
});
|
||||
str.push('}');
|
||||
str
|
||||
}
|
||||
FlatEncodableKey::Record(fields) => debug_name_record(fields),
|
||||
FlatEncodableKey::TagUnion(tags) => {
|
||||
let mut str = String::from('[');
|
||||
tags.iter().enumerate().for_each(|(i, (tag, arity))| {
|
||||
|
@ -56,22 +49,6 @@ impl FlatEncodableKey {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_ext_var(
|
||||
subs: &Subs,
|
||||
ext_var: Variable,
|
||||
is_empty_ext: impl Fn(&Content) -> bool,
|
||||
) -> Result<(), DeriveError> {
|
||||
let ext_content = subs.get_content_without_compacting(ext_var);
|
||||
if is_empty_ext(ext_content) {
|
||||
Ok(())
|
||||
} else {
|
||||
match ext_content {
|
||||
Content::FlexVar(_) => Err(DeriveError::UnboundVar),
|
||||
_ => Err(DeriveError::Underivable),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlatEncodable {
|
||||
pub(crate) fn from_var(subs: &Subs, var: Variable) -> Result<FlatEncodable, DeriveError> {
|
||||
use DeriveError::*;
|
||||
|
@ -86,7 +63,7 @@ impl FlatEncodable {
|
|||
_ => Err(Underivable),
|
||||
},
|
||||
FlatType::Record(fields, ext) => {
|
||||
check_ext_var(subs, ext, |ext| {
|
||||
check_empty_ext_var(subs, ext, |ext| {
|
||||
matches!(ext, Content::Structure(FlatType::EmptyRecord))
|
||||
})?;
|
||||
|
||||
|
@ -106,7 +83,7 @@ impl FlatEncodable {
|
|||
// [ A t1, B t1 t2 ] as R
|
||||
// look the same on the surface, because `R` is only somewhere inside of the
|
||||
// `t`-prefixed payload types.
|
||||
check_ext_var(subs, ext, |ext| {
|
||||
check_empty_ext_var(subs, ext, |ext| {
|
||||
matches!(ext, Content::Structure(FlatType::EmptyTagUnion))
|
||||
})?;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
pub mod decoding;
|
||||
pub mod encoding;
|
||||
mod util;
|
||||
|
||||
use decoding::{FlatDecodable, FlatDecodableKey};
|
||||
use encoding::{FlatEncodable, FlatEncodableKey};
|
||||
|
|
32
crates/compiler/derive_key/src/util.rs
Normal file
32
crates/compiler/derive_key/src/util.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use roc_module::ident::Lowercase;
|
||||
use roc_types::subs::{Content, Subs, Variable};
|
||||
|
||||
use crate::DeriveError;
|
||||
|
||||
pub(crate) fn check_empty_ext_var(
|
||||
subs: &Subs,
|
||||
ext_var: Variable,
|
||||
is_empty_ext: impl Fn(&Content) -> bool,
|
||||
) -> Result<(), DeriveError> {
|
||||
let ext_content = subs.get_content_without_compacting(ext_var);
|
||||
if is_empty_ext(ext_content) {
|
||||
Ok(())
|
||||
} else {
|
||||
match ext_content {
|
||||
Content::FlexVar(_) => Err(DeriveError::UnboundVar),
|
||||
_ => Err(DeriveError::Underivable),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn debug_name_record(fields: &[Lowercase]) -> String {
|
||||
let mut str = String::from('{');
|
||||
fields.iter().enumerate().for_each(|(i, f)| {
|
||||
if i > 0 {
|
||||
str.push(',');
|
||||
}
|
||||
str.push_str(f.as_str());
|
||||
});
|
||||
str.push('}');
|
||||
str
|
||||
}
|
|
@ -14,6 +14,6 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
|||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.0.0"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
roc_test_utils = { path = "../../test_utils" }
|
||||
walkdir = "2.3.2"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod annotation;
|
||||
pub mod collection;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Dev Backend
|
||||
|
||||
The dev backend is focused on generating decent binaries extremely fast.
|
||||
It goes from Roc's [mono ir](https://github.com/rtfeldman/roc/blob/trunk/compiler/mono/src/ir.rs) to an object file ready to be linked.
|
||||
It goes from Roc's [mono ir](https://github.com/roc-lang/roc/blob/main/crates/compiler/mono/src/ir.rs) to an object file ready to be linked.
|
||||
|
||||
## General Process
|
||||
|
||||
|
@ -22,14 +22,14 @@ rust should be abled compile each specific target (`linux-arm`, `darwin-x86_64`,
|
|||
|
||||
### Backend
|
||||
|
||||
[Backend](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/lib.rs) is the core abstraction.
|
||||
[Backend](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/lib.rs) is the core abstraction.
|
||||
It understands Roc's mono ir and some high level ideas about the generation process.
|
||||
The main job of Backend is to do high level optimizatons (like lazy literal loading) and parse the mono ir.
|
||||
Every target specific backend must implement this trait.
|
||||
|
||||
### Backend64Bit
|
||||
|
||||
[Backend64Bit](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs) is more or less what it sounds like.
|
||||
[Backend64Bit](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs) is more or less what it sounds like.
|
||||
It is the backend that understands 64 bit architectures.
|
||||
Currently it is the only backend implementation, but a 32 bit implementation will probably come in the future.
|
||||
This backend understands that the unit of data movement is 64 bit.
|
||||
|
@ -44,39 +44,39 @@ Backend64Bit is generic over these types instead of containing these types withi
|
|||
|
||||
### Assembler
|
||||
|
||||
[Assembler](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs) is the trait for generating assembly bytes.
|
||||
[Assembler](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs) is the trait for generating assembly bytes.
|
||||
It defines a set of RISC-like assembly calls that must be implemented for each architecture.
|
||||
A lot of these calls may not map one to one with actual assembly instructions for each architecture.
|
||||
Instead, they are a general abstraction over functionality shared between all architectures.
|
||||
This will grow regularly as more Roc builtins are added.
|
||||
Here are example implementations for [arm](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/x86_64.rs).
|
||||
Here are example implementations for [arm](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/x86_64.rs).
|
||||
|
||||
### CallConv
|
||||
|
||||
[CallConv](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs) is the abstraction over calling conventions.
|
||||
[CallConv](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs) is the abstraction over calling conventions.
|
||||
It deals with register and stack specific information related to passing and returning arguments.
|
||||
Here are example implementations for [arm](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/x86_64.rs).
|
||||
Here are example implementations for [arm](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/x86_64.rs).
|
||||
|
||||
## Adding New Features
|
||||
|
||||
Adding a new builtin to the dev backend can be pretty simple.
|
||||
Here is [an example](https://github.com/rtfeldman/roc/pull/893/files) of adding `Num.Sub`.
|
||||
Here is [an example](https://github.com/roc-lang/roc/pull/893/files) of adding `Num.Sub`.
|
||||
|
||||
This is the general procedure I follow with some helpful links:
|
||||
|
||||
1. Find a feature that is just n+1.
|
||||
For example, since we already have integers, adding a builtin that functions on them should be n+1.
|
||||
On the other hand, since we don't yet have booleans/conditionals, adding if statements may not yet be n+1.
|
||||
A good place to look for missing features is in the test files for generation in [test_gen](https://github.com/rtfeldman/roc/tree/trunk/compiler/test_gen). Any test that is not enabled for the `gen-dev` feature still needs to be added to the dev backend. Eventually all features should be enabled for the dev backend.
|
||||
A good place to look for missing features is in the test files for generation in [test_gen](https://github.com/roc-lang/roc/tree/main/crates/compiler/test_gen). Any test that is not enabled for the `gen-dev` feature still needs to be added to the dev backend. Eventually all features should be enabled for the dev backend.
|
||||
1. Pick/write the simplest test case you can find for the new feature.
|
||||
Just add `feature = "gen-dev"` to the `cfg` line for the test case.
|
||||
1. Uncomment the code to print out procedures [from here](https://github.com/rtfeldman/roc/blob/b03ed18553569314a420d5bf1fb0ead4b6b5ecda/compiler/test_gen/src/helpers/dev.rs#L76) and run the test.
|
||||
1. Uncomment the code to print out procedures [from here](https://github.com/roc-lang/roc/blob/b03ed18553569314a420d5bf1fb0ead4b6b5ecda/compiler/test_gen/src/helpers/dev.rs#L76) and run the test.
|
||||
It should fail and print out the mono ir for this test case.
|
||||
Seeing the actual mono ir tends to be very helpful for complex additions.
|
||||
1. Generally it will fail in one of the match statements in the [Backend](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/lib.rs) trait.
|
||||
1. Generally it will fail in one of the match statements in the [Backend](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/lib.rs) trait.
|
||||
Add the correct pattern matching and likely new function for your new builtin.
|
||||
This will break the compile until you add the same function to places that implement the trait,
|
||||
like [Backend64Bit](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs).
|
||||
like [Backend64Bit](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs).
|
||||
1. Keep following the chain down.
|
||||
To implement the function in Backend64Bit, you may need to add new assembly calls.
|
||||
Feel free to ignore backends that aren't x86_64 for now and just add `unimplemented!`.
|
||||
|
|
|
@ -440,7 +440,39 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) {
|
||||
todo!("register multiplication for AArch64");
|
||||
todo!("register signed multiplication for AArch64");
|
||||
}
|
||||
|
||||
fn umul_reg64_reg64_reg64<'a, ASM, CC>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
_storage_manager: &mut StorageManager<'a, AArch64GeneralReg, AArch64FloatReg, ASM, CC>,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) where
|
||||
ASM: Assembler<AArch64GeneralReg, AArch64FloatReg>,
|
||||
CC: CallConv<AArch64GeneralReg, AArch64FloatReg, ASM>,
|
||||
{
|
||||
todo!("register unsigned multiplication for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn mul_freg32_freg32_freg32(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_dst: AArch64FloatReg,
|
||||
_src1: AArch64FloatReg,
|
||||
_src2: AArch64FloatReg,
|
||||
) {
|
||||
todo!("multiplication for floats for AArch64");
|
||||
}
|
||||
#[inline(always)]
|
||||
fn mul_freg64_freg64_freg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_dst: AArch64FloatReg,
|
||||
_src1: AArch64FloatReg,
|
||||
_src2: AArch64FloatReg,
|
||||
) {
|
||||
todo!("multiplication for floats for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -21,7 +21,7 @@ mod disassembler_test_macro;
|
|||
pub(crate) mod storage;
|
||||
pub(crate) mod x86_64;
|
||||
|
||||
use storage::StorageManager;
|
||||
use storage::{RegStorage, StorageManager};
|
||||
|
||||
const REFCOUNT_ONE: u64 = i64::MIN as u64;
|
||||
// TODO: on all number functions double check and deal with over/underflow.
|
||||
|
@ -210,12 +210,33 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
|||
fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
|
||||
|
||||
fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||
fn mul_freg32_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: FloatReg,
|
||||
src1: FloatReg,
|
||||
src2: FloatReg,
|
||||
);
|
||||
fn mul_freg64_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: FloatReg,
|
||||
src1: FloatReg,
|
||||
src2: FloatReg,
|
||||
);
|
||||
fn imul_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
);
|
||||
fn umul_reg64_reg64_reg64<'a, ASM, CC>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, CC>,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
) where
|
||||
ASM: Assembler<GeneralReg, FloatReg>,
|
||||
CC: CallConv<GeneralReg, FloatReg, ASM>;
|
||||
|
||||
fn sub_reg64_reg64_imm32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src1: GeneralReg, imm32: i32);
|
||||
fn sub_reg64_reg64_reg64(
|
||||
|
@ -339,6 +360,19 @@ pub fn new_backend_64bit<
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! quadword_and_smaller {
|
||||
() => {
|
||||
IntWidth::I64
|
||||
| IntWidth::U64
|
||||
| IntWidth::I32
|
||||
| IntWidth::U32
|
||||
| IntWidth::I16
|
||||
| IntWidth::U16
|
||||
| IntWidth::I8
|
||||
| IntWidth::U8
|
||||
};
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
GeneralReg: RegTrait,
|
||||
|
@ -699,16 +733,7 @@ impl<
|
|||
|
||||
fn build_num_add(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>) {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int(
|
||||
IntWidth::I64
|
||||
| IntWidth::U64
|
||||
| IntWidth::I32
|
||||
| IntWidth::U32
|
||||
| IntWidth::I16
|
||||
| IntWidth::U16
|
||||
| IntWidth::I8
|
||||
| IntWidth::U8,
|
||||
)) => {
|
||||
Layout::Builtin(Builtin::Int(quadword_and_smaller!())) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
let src1_reg = self
|
||||
.storage_manager
|
||||
|
@ -736,7 +761,9 @@ impl<
|
|||
|
||||
fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>) {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
|
||||
Layout::Builtin(Builtin::Int(
|
||||
IntWidth::I64 | IntWidth::I32 | IntWidth::I16 | IntWidth::I8,
|
||||
)) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
let src1_reg = self
|
||||
.storage_manager
|
||||
|
@ -746,6 +773,37 @@ impl<
|
|||
.load_to_general_reg(&mut self.buf, src2);
|
||||
ASM::imul_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(
|
||||
IntWidth::U64 | IntWidth::U32 | IntWidth::U16 | IntWidth::U8,
|
||||
)) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
let src1_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src1);
|
||||
let src2_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src2);
|
||||
|
||||
ASM::umul_reg64_reg64_reg64(
|
||||
&mut self.buf,
|
||||
&mut self.storage_manager,
|
||||
dst_reg,
|
||||
src1_reg,
|
||||
src2_reg,
|
||||
);
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
|
||||
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
|
||||
ASM::mul_freg64_freg64_freg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F32)) => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
|
||||
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
|
||||
ASM::mul_freg32_freg32_freg32(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
}
|
||||
x => todo!("NumMul: layout, {:?}", x),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ use StackStorage::*;
|
|||
use Storage::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum RegStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
pub enum RegStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
General(GeneralReg),
|
||||
Float(FloatReg),
|
||||
}
|
||||
|
@ -756,7 +756,7 @@ impl<
|
|||
|
||||
#[allow(dead_code)]
|
||||
/// Ensures that a register is free. If it is not free, data will be moved to make it free.
|
||||
fn ensure_reg_free(
|
||||
pub fn ensure_reg_free(
|
||||
&mut self,
|
||||
buf: &mut Vec<'a, u8>,
|
||||
wanted_reg: RegStorage<GeneralReg, FloatReg>,
|
||||
|
|
|
@ -1009,6 +1009,58 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
imul_reg64_reg64(buf, dst, src2);
|
||||
}
|
||||
|
||||
fn umul_reg64_reg64_reg64<'a, ASM, CC>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
storage_manager: &mut StorageManager<'a, X86_64GeneralReg, X86_64FloatReg, ASM, CC>,
|
||||
dst: X86_64GeneralReg,
|
||||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) where
|
||||
ASM: Assembler<X86_64GeneralReg, X86_64FloatReg>,
|
||||
CC: CallConv<X86_64GeneralReg, X86_64FloatReg, ASM>,
|
||||
{
|
||||
use crate::generic64::RegStorage;
|
||||
|
||||
storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RAX));
|
||||
storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RDX));
|
||||
|
||||
mov_reg64_reg64(buf, X86_64GeneralReg::RAX, src1);
|
||||
mul_reg64_reg64(buf, src2);
|
||||
mov_reg64_reg64(buf, dst, X86_64GeneralReg::RAX);
|
||||
}
|
||||
|
||||
fn mul_freg32_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: X86_64FloatReg,
|
||||
src1: X86_64FloatReg,
|
||||
src2: X86_64FloatReg,
|
||||
) {
|
||||
if dst == src1 {
|
||||
mulss_freg32_freg32(buf, dst, src2);
|
||||
} else if dst == src2 {
|
||||
mulss_freg32_freg32(buf, dst, src1);
|
||||
} else {
|
||||
movss_freg32_freg32(buf, dst, src1);
|
||||
mulss_freg32_freg32(buf, dst, src2);
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn mul_freg64_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: X86_64FloatReg,
|
||||
src1: X86_64FloatReg,
|
||||
src2: X86_64FloatReg,
|
||||
) {
|
||||
if dst == src1 {
|
||||
mulsd_freg64_freg64(buf, dst, src2);
|
||||
} else if dst == src2 {
|
||||
mulsd_freg64_freg64(buf, dst, src1);
|
||||
} else {
|
||||
movsd_freg64_freg64(buf, dst, src1);
|
||||
mulsd_freg64_freg64(buf, dst, src2);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn jmp_imm32(buf: &mut Vec<'_, u8>, offset: i32) -> usize {
|
||||
jmp_imm32(buf, offset);
|
||||
|
@ -1280,12 +1332,25 @@ impl X86_64Assembler {
|
|||
}
|
||||
}
|
||||
const REX: u8 = 0x40;
|
||||
const REX_W: u8 = REX | 0x8;
|
||||
|
||||
// see https://wiki.osdev.org/X86-64_Instruction_Encoding#Encoding
|
||||
/// If set, 64-bit operand size is used
|
||||
const REX_PREFIX_W: u8 = 0b1000;
|
||||
/// Extension to the MODRM.reg
|
||||
const REX_PREFIX_R: u8 = 0b0100;
|
||||
#[allow(unused)]
|
||||
/// Extension to the SIB.index field
|
||||
const REX_PREFIX_X: u8 = 0b0010;
|
||||
/// Extension to the MODRM.rm
|
||||
const REX_PREFIX_B: u8 = 0b0001;
|
||||
|
||||
/// Wide REX
|
||||
const REX_W: u8 = REX | REX_PREFIX_W;
|
||||
|
||||
#[inline(always)]
|
||||
fn add_rm_extension<T: RegTrait>(reg: T, byte: u8) -> u8 {
|
||||
if reg.value() > 7 {
|
||||
byte | 1
|
||||
byte | REX_PREFIX_B
|
||||
} else {
|
||||
byte
|
||||
}
|
||||
|
@ -1299,7 +1364,7 @@ fn add_opcode_extension(reg: X86_64GeneralReg, byte: u8) -> u8 {
|
|||
#[inline(always)]
|
||||
fn add_reg_extension<T: RegTrait>(reg: T, byte: u8) -> u8 {
|
||||
if reg.value() > 7 {
|
||||
byte | 4
|
||||
byte | REX_PREFIX_R
|
||||
} else {
|
||||
byte
|
||||
}
|
||||
|
@ -1396,6 +1461,46 @@ fn addss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl
|
|||
}
|
||||
}
|
||||
|
||||
/// `MULSD xmm1,xmm2/m64` -> Multiply the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
|
||||
#[inline(always)]
|
||||
fn mulsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = src as u8 % 8;
|
||||
if dst_high || src_high {
|
||||
buf.extend(&[
|
||||
0xF2,
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x59,
|
||||
0xC0 | (dst_mod << 3) | (src_mod),
|
||||
])
|
||||
} else {
|
||||
buf.extend(&[0xF2, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)])
|
||||
}
|
||||
}
|
||||
|
||||
/// `ADDSS xmm1,xmm2/m64` -> Add the low single-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
|
||||
#[inline(always)]
|
||||
fn mulss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = src as u8 % 8;
|
||||
if dst_high || src_high {
|
||||
buf.extend(&[
|
||||
0xF3,
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x59,
|
||||
0xC0 | (dst_mod << 3) | (src_mod),
|
||||
])
|
||||
} else {
|
||||
buf.extend(&[0xF3, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)])
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn andpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
|
@ -1465,6 +1570,19 @@ fn imul_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64Gen
|
|||
extended_binop_reg64_reg64(0x0F, 0xAF, buf, src, dst);
|
||||
}
|
||||
|
||||
/// `MUL r/m64` -> Unsigned Multiply r/m64 to r64.
|
||||
#[inline(always)]
|
||||
fn mul_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) {
|
||||
let mut rex = REX_W;
|
||||
rex = add_reg_extension(src, rex);
|
||||
|
||||
if src.value() > 7 {
|
||||
rex |= REX_PREFIX_B;
|
||||
}
|
||||
|
||||
buf.extend(&[rex, 0xF7, 0b1110_0000 | (src as u8 % 8)]);
|
||||
}
|
||||
|
||||
/// Jump near, relative, RIP = RIP + 32-bit displacement sign extended to 64-bits.
|
||||
#[inline(always)]
|
||||
fn jmp_imm32(buf: &mut Vec<'_, u8>, imm: i32) {
|
||||
|
@ -2086,6 +2204,35 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_reg64_reg64() {
|
||||
disassembler_test!(
|
||||
mul_reg64_reg64,
|
||||
|reg| format!("mul {}", reg),
|
||||
ALL_GENERAL_REGS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mulsd_freg64_freg64() {
|
||||
disassembler_test!(
|
||||
mulsd_freg64_freg64,
|
||||
|reg1, reg2| format!("mulsd {}, {}", reg1, reg2),
|
||||
ALL_FLOAT_REGS,
|
||||
ALL_FLOAT_REGS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mulss_freg32_freg32() {
|
||||
disassembler_test!(
|
||||
mulss_freg32_freg32,
|
||||
|reg1, reg2| format!("mulss {}, {}", reg1, reg2),
|
||||
ALL_FLOAT_REGS,
|
||||
ALL_FLOAT_REGS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jmp_imm32() {
|
||||
const INST_SIZE: i32 = 5;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
|
|
|
@ -172,7 +172,7 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
let arena = backend.env().arena;
|
||||
|
||||
/*
|
||||
// Commented out because we couldn't figure out how to get it to work on mac - see https://github.com/rtfeldman/roc/pull/1323
|
||||
// Commented out because we couldn't figure out how to get it to work on mac - see https://github.com/roc-lang/roc/pull/1323
|
||||
let comment = output.add_section(vec![], b".comment".to_vec(), SectionKind::OtherString);
|
||||
output.append_section_data(
|
||||
comment,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
// we actually want to compare against the literal float bits
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
|
|
@ -3929,7 +3929,7 @@ pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValu
|
|||
pub fn build_setjmp_call<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
||||
let jmp_buf = get_sjlj_buffer(env);
|
||||
if cfg!(target_arch = "aarch64") {
|
||||
// Due to https://github.com/rtfeldman/roc/issues/2965, we use a setjmp we linked in from Zig
|
||||
// Due to https://github.com/roc-lang/roc/issues/2965, we use a setjmp we linked in from Zig
|
||||
call_bitcode_fn(env, &[jmp_buf.into()], bitcode::UTILS_SETJMP)
|
||||
} else {
|
||||
// Anywhere else, use the LLVM intrinsic.
|
||||
|
@ -6459,11 +6459,11 @@ fn to_cc_type<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'a>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
match layout {
|
||||
Layout::Builtin(builtin) => to_cc_type_builtin(env, builtin),
|
||||
_ => {
|
||||
match layout.runtime_representation() {
|
||||
Layout::Builtin(builtin) => to_cc_type_builtin(env, &builtin),
|
||||
layout => {
|
||||
// TODO this is almost certainly incorrect for bigger structs
|
||||
basic_type_from_layout(env, layout)
|
||||
basic_type_from_layout(env, &layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7087,22 +7087,13 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
|||
NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(),
|
||||
NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(),
|
||||
NumBitwiseOr => bd.build_or(lhs, rhs, "int_bitwise_or").into(),
|
||||
NumShiftLeftBy => {
|
||||
// NOTE arguments are flipped;
|
||||
// we write `assert_eq!(0b0000_0001 << 0, 0b0000_0001);`
|
||||
// as `Num.shiftLeftBy 0 0b0000_0001
|
||||
bd.build_left_shift(rhs, lhs, "int_shift_left").into()
|
||||
}
|
||||
NumShiftRightBy => {
|
||||
// NOTE arguments are flipped;
|
||||
bd.build_right_shift(rhs, lhs, true, "int_shift_right")
|
||||
.into()
|
||||
}
|
||||
NumShiftRightZfBy => {
|
||||
// NOTE arguments are flipped;
|
||||
bd.build_right_shift(rhs, lhs, false, "int_shift_right_zf")
|
||||
.into()
|
||||
}
|
||||
NumShiftLeftBy => bd.build_left_shift(lhs, rhs, "int_shift_left").into(),
|
||||
NumShiftRightBy => bd
|
||||
.build_right_shift(lhs, rhs, true, "int_shift_right")
|
||||
.into(),
|
||||
NumShiftRightZfBy => bd
|
||||
.build_right_shift(lhs, rhs, false, "int_shift_right_zf")
|
||||
.into(),
|
||||
|
||||
_ => {
|
||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||
|
|
|
@ -361,7 +361,7 @@ impl<'ctx> RocUnion<'ctx> {
|
|||
// set the tag id
|
||||
//
|
||||
// NOTE: setting the tag id initially happened before writing the data into it.
|
||||
// That turned out to expose UB. More info at https://github.com/rtfeldman/roc/issues/3554
|
||||
// That turned out to expose UB. More info at https://github.com/roc-lang/roc/issues/3554
|
||||
if let Some(tag_id) = tag_id {
|
||||
let tag_id_type = match self.tag_type.unwrap() {
|
||||
TagType::I8 => env.context.i8_type(),
|
||||
|
|
|
@ -517,7 +517,7 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
// Load all the arguments for the inner function
|
||||
for (i, wrapper_arg) in wrapper_arg_layouts.iter().enumerate() {
|
||||
let is_closure_data = i == 0; // Skip closure data (first for wrapper, last for inner)
|
||||
let is_closure_data = i == 0; // Skip closure data (first for wrapper, last for inner). We'll handle it below.
|
||||
let is_return_pointer = i == wrapper_arg_layouts.len() - 1; // Skip return pointer (may not be an arg for inner. And if it is, swaps from end to start)
|
||||
if is_closure_data || is_return_pointer {
|
||||
continue;
|
||||
|
@ -540,7 +540,23 @@ impl<'a> WasmBackend<'a> {
|
|||
// If the inner function has closure data, it's the last arg of the inner fn
|
||||
let closure_data_layout = wrapper_arg_layouts[0];
|
||||
if closure_data_layout.stack_size(TARGET_INFO) > 0 {
|
||||
// The closure data exists, and will have been passed in to the wrapper as a
|
||||
// one-element struct.
|
||||
let inner_closure_data_layout = match closure_data_layout {
|
||||
Layout::Struct {
|
||||
field_layouts: [inner],
|
||||
..
|
||||
} => inner,
|
||||
other => internal_error!(
|
||||
"Expected a boxed layout for wrapped closure data, got {:?}",
|
||||
other
|
||||
),
|
||||
};
|
||||
self.code_builder.get_local(LocalId(0));
|
||||
// Since the closure data is wrapped in a one-element struct, we've been passed in the
|
||||
// pointer to that struct in the stack memory. To get the closure data we just need to
|
||||
// dereference the pointer.
|
||||
self.dereference_boxed_value(inner_closure_data_layout);
|
||||
}
|
||||
|
||||
// Call the wrapped inner function
|
||||
|
|
|
@ -183,7 +183,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
/// Wrap an integer that should have less than 32 bits, but is represented in Wasm as i32.
|
||||
/// This may seem like deliberately introducing an error!
|
||||
/// But we want all targets to behave the same, and hash algos rely on wrapping.
|
||||
/// Discussion: https://github.com/rtfeldman/roc/pull/2117#discussion_r760723063
|
||||
/// Discussion: https://github.com/roc-lang/roc/pull/2117#discussion_r760723063
|
||||
fn wrap_small_int(&self, backend: &mut WasmBackend<'a>, int_width: IntWidth) {
|
||||
let bits = 8 * int_width.stack_size() as i32;
|
||||
let shift = 32 - bits;
|
||||
|
@ -1599,11 +1599,11 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
}
|
||||
NumShiftLeftBy => {
|
||||
// Swap order of arguments
|
||||
backend.storage.load_symbols(
|
||||
&mut backend.code_builder,
|
||||
&[self.arguments[1], self.arguments[0]],
|
||||
);
|
||||
let num = self.arguments[0];
|
||||
let bits = self.arguments[1];
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||
match CodeGenNumType::from(self.ret_layout) {
|
||||
I32 => backend.code_builder.i32_shl(),
|
||||
I64 => backend.code_builder.i64_shl(),
|
||||
|
@ -1612,8 +1612,8 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
}
|
||||
NumShiftRightBy => {
|
||||
let bits = self.arguments[0];
|
||||
let num = self.arguments[1];
|
||||
let num = self.arguments[0];
|
||||
let bits = self.arguments[1];
|
||||
match CodeGenNumType::from(self.ret_layout) {
|
||||
I32 => {
|
||||
// In most languages this operation is for signed numbers, but Roc defines it on all integers.
|
||||
|
@ -1657,41 +1657,39 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
}
|
||||
NumShiftRightZfBy => {
|
||||
let num = self.arguments[0];
|
||||
let bits = self.arguments[1];
|
||||
match CodeGenNumType::from(self.ret_layout) {
|
||||
I32 => {
|
||||
// In most languages this operation is for unsigned numbers, but Roc defines it on all integers.
|
||||
// So the argument is implicitly converted to unsigned before the shift operator.
|
||||
// We need to make that conversion explicit for i8 and i16, which use Wasm's i32 type.
|
||||
let bit_width = 8 * self.ret_layout.stack_size(TARGET_INFO);
|
||||
if bit_width < 32 && symbol_is_signed_int(backend, self.arguments[0]) {
|
||||
if bit_width < 32 && symbol_is_signed_int(backend, bits) {
|
||||
let mask = (1 << bit_width) - 1;
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[self.arguments[1]]);
|
||||
.load_symbols(&mut backend.code_builder, &[num]);
|
||||
|
||||
backend.code_builder.i32_const(mask);
|
||||
backend.code_builder.i32_and();
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[self.arguments[0]]);
|
||||
.load_symbols(&mut backend.code_builder, &[bits]);
|
||||
} else {
|
||||
// swap the arguments
|
||||
backend.storage.load_symbols(
|
||||
&mut backend.code_builder,
|
||||
&[self.arguments[1], self.arguments[0]],
|
||||
);
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||
}
|
||||
|
||||
backend.code_builder.i32_shr_u();
|
||||
}
|
||||
I64 => {
|
||||
// swap the arguments
|
||||
backend.storage.load_symbols(
|
||||
&mut backend.code_builder,
|
||||
&[self.arguments[1], self.arguments[0]],
|
||||
);
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||
backend.code_builder.i64_shr_u();
|
||||
}
|
||||
I128 => todo!("{:?} for I128", self.lowlevel),
|
||||
|
@ -1857,11 +1855,15 @@ impl<'a> LowLevelCall<'a> {
|
|||
/// Equality and inequality
|
||||
/// These can operate on any data type (except functions) so they're more complex than other operators.
|
||||
fn eq_or_neq(&self, backend: &mut WasmBackend<'a>) {
|
||||
let arg_layout = backend.storage.symbol_layouts[&self.arguments[0]];
|
||||
let other_arg_layout = backend.storage.symbol_layouts[&self.arguments[1]];
|
||||
let arg_layout =
|
||||
backend.storage.symbol_layouts[&self.arguments[0]].runtime_representation();
|
||||
let other_arg_layout =
|
||||
backend.storage.symbol_layouts[&self.arguments[1]].runtime_representation();
|
||||
debug_assert!(
|
||||
arg_layout == other_arg_layout,
|
||||
"Cannot do `==` comparison on different types"
|
||||
"Cannot do `==` comparison on different types: {:?} vs {:?}",
|
||||
arg_layout,
|
||||
other_arg_layout
|
||||
);
|
||||
|
||||
let invert_result = matches!(self.lowlevel, LowLevel::NotEq);
|
||||
|
@ -2113,6 +2115,18 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
..
|
||||
} = passed_function;
|
||||
|
||||
// The zig lowlevel builtins expect the passed functions' closure data to always
|
||||
// be sent as an opaque pointer. On the Roc side, however, we need to call the passed function
|
||||
// with the Roc representation of the closure data. There are three possible cases for that
|
||||
// representation:
|
||||
//
|
||||
// 1. The closure data is a struct
|
||||
// 2. The closure data is an unwrapped value
|
||||
// 3. There is no closure data
|
||||
//
|
||||
// To uniformly deal with the first two cases, always put the closure data, when it exists,
|
||||
// into a one-element struct. That way, we get a pointer (i32) that can be passed to the zig lowlevels.
|
||||
// The wrapper around the passed function will access the actual closure data in the struct.
|
||||
let (closure_data_layout, closure_data_exists) =
|
||||
match backend.storage.symbol_layouts[captured_environment] {
|
||||
Layout::LambdaSet(lambda_set) => {
|
||||
|
@ -2131,6 +2145,46 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
x => internal_error!("Closure data has an invalid layout\n{:?}", x),
|
||||
};
|
||||
|
||||
let (wrapped_captured_environment, wrapped_captures_layout) = if closure_data_exists {
|
||||
// If there is closure data, make sure we put in a struct it before passing it to the
|
||||
// external builtin impl. That way it's always an `i32` pointer.
|
||||
let wrapped_closure_data_sym = backend.create_symbol("wrapped_captures");
|
||||
let wrapped_captures_layout =
|
||||
Layout::struct_no_name_order(backend.env.arena.alloc([closure_data_layout]));
|
||||
|
||||
// make sure that the wrapping struct is available in stack memory, so we can hand out a
|
||||
// pointer to it.
|
||||
let wrapped_storage = backend.storage.allocate_var(
|
||||
wrapped_captures_layout,
|
||||
wrapped_closure_data_sym,
|
||||
crate::storage::StoredVarKind::Variable,
|
||||
);
|
||||
|
||||
let (wrapped_storage_local_ptr, wrapped_storage_offset) = match wrapped_storage {
|
||||
StoredValue::StackMemory { location, .. } => {
|
||||
location.local_and_offset(backend.storage.stack_frame_pointer)
|
||||
}
|
||||
other => internal_error!(
|
||||
"Struct should be allocated in stack memory, but it's in {:?}",
|
||||
other
|
||||
),
|
||||
};
|
||||
|
||||
// copy the actual closure data into the first and only element of the wrapping struct.
|
||||
backend.storage.copy_value_to_memory(
|
||||
&mut backend.code_builder,
|
||||
wrapped_storage_local_ptr,
|
||||
wrapped_storage_offset,
|
||||
*captured_environment,
|
||||
);
|
||||
|
||||
(wrapped_closure_data_sym, wrapped_captures_layout)
|
||||
} else {
|
||||
// If we don't capture anything, pass along the captured environment as-is - the wrapper
|
||||
// function will take care not to unwrap this.
|
||||
(*captured_environment, closure_data_layout)
|
||||
};
|
||||
|
||||
// We create a wrapper around the passed function, which just unboxes the arguments.
|
||||
// This allows Zig builtins to have a generic pointer-based interface.
|
||||
let helper_proc_source = {
|
||||
|
@ -2164,7 +2218,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
argument_layouts.len()
|
||||
};
|
||||
|
||||
wrapper_arg_layouts.push(closure_data_layout);
|
||||
wrapper_arg_layouts.push(wrapped_captures_layout);
|
||||
wrapper_arg_layouts.extend(
|
||||
argument_layouts
|
||||
.iter()
|
||||
|
@ -2204,7 +2258,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
.get_refcount_fn_index(Layout::Builtin(Builtin::Int(IntWidth::I32)), HelperOp::Inc);
|
||||
backend.get_fn_ptr(inc_fn)
|
||||
} else {
|
||||
let inc_fn = backend.get_refcount_fn_index(closure_data_layout, HelperOp::Inc);
|
||||
let inc_fn = backend.get_refcount_fn_index(wrapped_captures_layout, HelperOp::Inc);
|
||||
backend.get_fn_ptr(inc_fn)
|
||||
};
|
||||
|
||||
|
@ -2218,7 +2272,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
|
@ -2231,7 +2285,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
|
@ -2244,7 +2298,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
|
@ -2257,7 +2311,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
|
@ -2280,7 +2334,9 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
backend.storage.load_symbol_zig(cb, *xs);
|
||||
cb.i32_const(wrapper_fn_ptr);
|
||||
if closure_data_exists {
|
||||
backend.storage.load_symbols(cb, &[*captured_environment]);
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(cb, &[wrapped_captured_environment]);
|
||||
} else {
|
||||
// load_symbols assumes that a zero-size arg should be eliminated in code gen,
|
||||
// but that's a specialization that our Zig code doesn't have! Pass a null pointer.
|
||||
|
|
|
@ -719,9 +719,10 @@ impl<'a> Storage<'a> {
|
|||
) => {
|
||||
debug_assert!(to_value_type == from_value_type);
|
||||
debug_assert!(to_size == from_size);
|
||||
// Note: load_symbols will not destroy the value, so we can use it again later.
|
||||
// It will leave a Popped marker in the VM stack model in CodeBuilder
|
||||
self.load_symbols(code_builder, &[from_symbol]);
|
||||
code_builder.set_local(*to_local_id);
|
||||
self.symbol_storage_map.insert(from_symbol, to.clone());
|
||||
}
|
||||
|
||||
(
|
||||
|
|
|
@ -234,6 +234,14 @@ impl Wasm32Result for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl Wasm32Result for std::convert::Infallible {
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
code_builder.call(main_function_index, 0, false);
|
||||
code_builder.get_global(0);
|
||||
code_builder.build_fn_header_and_footer(&[], 0, None);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Wasm32Result for (T, U)
|
||||
where
|
||||
T: Wasm32Result + Wasm32Sized,
|
||||
|
|
|
@ -203,6 +203,30 @@ impl From<&str> for IdentStr {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<IdentStr> for String {
|
||||
fn from(ident_str: IdentStr) -> Self {
|
||||
if ident_str.is_small_str() {
|
||||
// Copy it to a heap allocation
|
||||
ident_str.as_str().to_string()
|
||||
} else {
|
||||
// Reuse the existing heap allocation
|
||||
let string = unsafe {
|
||||
String::from_raw_parts(
|
||||
ident_str.as_ptr() as *mut u8,
|
||||
ident_str.len(),
|
||||
ident_str.len(),
|
||||
)
|
||||
};
|
||||
|
||||
// Make sure not to drop the IdentStr, since now there's
|
||||
// a String referencing its heap-allocated contents.
|
||||
std::mem::forget(ident_str);
|
||||
|
||||
string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for IdentStr {
|
||||
fn from(string: String) -> Self {
|
||||
if string.len() <= Self::SMALL_STR_BYTES {
|
||||
|
|
|
@ -358,7 +358,7 @@ pub fn unify(
|
|||
);
|
||||
// At this point we can't do anything with must-implement constraints, since we're no
|
||||
// longer solving. We must assume that they were totally caught during solving.
|
||||
// After we land https://github.com/rtfeldman/roc/issues/3207 this concern should totally
|
||||
// After we land https://github.com/roc-lang/roc/issues/3207 this concern should totally
|
||||
// go away.
|
||||
let _ = must_implement_constraints;
|
||||
// Pools are only used to keep track of variable ranks for generalization purposes.
|
||||
|
|
|
@ -54,6 +54,29 @@ pub fn load_single_threaded<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum LoadMonomorphizedError<'a> {
|
||||
LoadingProblem(LoadingProblem<'a>),
|
||||
/// Errors in the module that should be reported, without compiling the executable.
|
||||
/// Relevant in check-and-then-build mode.
|
||||
ErrorModule(LoadedModule),
|
||||
}
|
||||
|
||||
impl<'a> From<LoadingProblem<'a>> for LoadMonomorphizedError<'a> {
|
||||
fn from(problem: LoadingProblem<'a>) -> Self {
|
||||
Self::LoadingProblem(problem)
|
||||
}
|
||||
}
|
||||
|
||||
// HACK only relevant because of some uses of `map_err` that decay into this error, but call `todo` -
|
||||
// rustc seems to be unhappy with that.
|
||||
impl<'a> From<()> for LoadMonomorphizedError<'a> {
|
||||
fn from(_: ()) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn load_and_monomorphize_from_str<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -78,14 +101,14 @@ pub fn load_and_monomorphize(
|
|||
filename: PathBuf,
|
||||
exposed_types: ExposedByModule,
|
||||
load_config: LoadConfig,
|
||||
) -> Result<MonomorphizedModule<'_>, LoadingProblem<'_>> {
|
||||
) -> Result<MonomorphizedModule<'_>, LoadMonomorphizedError<'_>> {
|
||||
use LoadResult::*;
|
||||
|
||||
let load_start = LoadStart::from_path(arena, filename, load_config.render)?;
|
||||
|
||||
match load(arena, load_start, exposed_types, load_config)? {
|
||||
Monomorphized(module) => Ok(module),
|
||||
TypeChecked(_) => unreachable!(""),
|
||||
TypeChecked(module) => Err(LoadMonomorphizedError::ErrorModule(module)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ roc_debug_flags = { path = "../debug_flags" }
|
|||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
parking_lot = "0.12"
|
||||
crossbeam = "0.8.1"
|
||||
crossbeam = "0.8.2"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.0.0"
|
||||
maplit = "1.0.2"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
roc_test_utils = { path = "../../test_utils" }
|
||||
|
|
|
@ -130,13 +130,15 @@ pub enum ExecutionMode {
|
|||
Test,
|
||||
Check,
|
||||
Executable,
|
||||
/// Like [`ExecutionMode::Executable`], but stops in the presence of type errors.
|
||||
ExecutableIfCheck,
|
||||
}
|
||||
|
||||
impl ExecutionMode {
|
||||
fn goal_phase(&self) -> Phase {
|
||||
match self {
|
||||
ExecutionMode::Test | ExecutionMode::Executable => Phase::MakeSpecializations,
|
||||
ExecutionMode::Check => Phase::SolveTypes,
|
||||
ExecutionMode::Check | ExecutionMode::ExecutableIfCheck => Phase::SolveTypes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,6 +170,22 @@ struct ModuleCache<'a> {
|
|||
sources: MutMap<ModuleId, (PathBuf, &'a str)>,
|
||||
}
|
||||
|
||||
impl<'a> ModuleCache<'a> {
|
||||
pub fn total_problems(&self) -> usize {
|
||||
let mut total = 0;
|
||||
|
||||
for problems in self.can_problems.values() {
|
||||
total += problems.len();
|
||||
}
|
||||
|
||||
for problems in self.type_problems.values() {
|
||||
total += problems.len();
|
||||
}
|
||||
|
||||
total
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ModuleCache<'_> {
|
||||
fn default() -> Self {
|
||||
let mut module_names = MutMap::default();
|
||||
|
@ -2385,12 +2403,35 @@ fn update<'a>(
|
|||
.extend(solved_module.aliases.keys().copied());
|
||||
}
|
||||
|
||||
if is_host_exposed && state.goal_phase() == Phase::SolveTypes {
|
||||
let finish_type_checking = is_host_exposed &&
|
||||
(state.goal_phase() == Phase::SolveTypes)
|
||||
// If we're running in check-and-then-build mode, only exit now there are errors.
|
||||
&& (!matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) || state.module_cache.total_problems() > 0);
|
||||
|
||||
if finish_type_checking {
|
||||
debug_assert!(work.is_empty());
|
||||
debug_assert!(state.dependencies.solved_all());
|
||||
|
||||
state.timings.insert(module_id, module_timing);
|
||||
|
||||
if matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) {
|
||||
// We there may outstanding modules in the typecheked cache whose ident IDs
|
||||
// aren't registered; transfer all of their idents over to the state, since
|
||||
// we're now done and ready to report errors.
|
||||
for (
|
||||
module_id,
|
||||
TypeCheckedModule {
|
||||
ident_ids,
|
||||
module_timing,
|
||||
..
|
||||
},
|
||||
) in state.module_cache.typechecked.drain()
|
||||
{
|
||||
state.constrained_ident_ids.insert(module_id, ident_ids);
|
||||
state.timings.insert(module_id, module_timing);
|
||||
}
|
||||
}
|
||||
|
||||
let documentation = {
|
||||
let mut empty = MutMap::default();
|
||||
std::mem::swap(&mut empty, &mut state.module_cache.documentation);
|
||||
|
@ -2427,7 +2468,9 @@ fn update<'a>(
|
|||
},
|
||||
);
|
||||
|
||||
if state.goal_phase() > Phase::SolveTypes {
|
||||
if state.goal_phase() > Phase::SolveTypes
|
||||
|| matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
|
||||
{
|
||||
let layout_cache = state
|
||||
.layout_caches
|
||||
.pop()
|
||||
|
@ -2452,6 +2495,25 @@ fn update<'a>(
|
|||
state.timings.insert(module_id, module_timing);
|
||||
}
|
||||
|
||||
let work = if is_host_exposed
|
||||
&& matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
|
||||
{
|
||||
debug_assert!(
|
||||
work.is_empty(),
|
||||
"work left over after host exposed is checked"
|
||||
);
|
||||
|
||||
// Update the goal phase to target full codegen.
|
||||
state.exec_mode = ExecutionMode::Executable;
|
||||
|
||||
// Load the find + make specializations portion of the dependency graph.
|
||||
state
|
||||
.dependencies
|
||||
.load_find_and_make_specializations_after_check()
|
||||
} else {
|
||||
work
|
||||
};
|
||||
|
||||
start_tasks(arena, &mut state, work, injector, worker_listeners)?;
|
||||
}
|
||||
|
||||
|
@ -2810,7 +2872,7 @@ fn finish_specialization(
|
|||
let entry_point = {
|
||||
match exec_mode {
|
||||
ExecutionMode::Test => EntryPoint::Test,
|
||||
ExecutionMode::Executable => {
|
||||
ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck => {
|
||||
let path_to_platform = {
|
||||
use PlatformPath::*;
|
||||
let package_name = match platform_path {
|
||||
|
@ -5007,7 +5069,9 @@ fn build_pending_specializations<'a>(
|
|||
// skip expectations if we're not going to run them
|
||||
match execution_mode {
|
||||
ExecutionMode::Test => { /* fall through */ }
|
||||
ExecutionMode::Check | ExecutionMode::Executable => continue,
|
||||
ExecutionMode::Check
|
||||
| ExecutionMode::Executable
|
||||
| ExecutionMode::ExecutableIfCheck => continue,
|
||||
}
|
||||
|
||||
// mark this symbol as a top-level thunk before any other work on the procs
|
||||
|
@ -5081,7 +5145,9 @@ fn build_pending_specializations<'a>(
|
|||
// skip expectations if we're not going to run them
|
||||
match execution_mode {
|
||||
ExecutionMode::Test => { /* fall through */ }
|
||||
ExecutionMode::Check | ExecutionMode::Executable => continue,
|
||||
ExecutionMode::Check
|
||||
| ExecutionMode::Executable
|
||||
| ExecutionMode::ExecutableIfCheck => continue,
|
||||
}
|
||||
|
||||
// mark this symbol as a top-level thunk before any other work on the procs
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod docs;
|
||||
pub mod file;
|
||||
|
|
|
@ -166,11 +166,10 @@ impl<'a> Dependencies<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if goal_phase >= MakeSpecializations {
|
||||
// Add make specialization dependents
|
||||
self.make_specializations_dependents
|
||||
.add_succ(module_id, dependencies.iter().map(|dep| *dep.as_inner()));
|
||||
}
|
||||
// Add "make specialization" dependents. Even if we're not targeting making
|
||||
// specializations right now, we may re-enter to do so later.
|
||||
self.make_specializations_dependents
|
||||
.add_succ(module_id, dependencies.iter().map(|dep| *dep.as_inner()));
|
||||
|
||||
// add dependencies for self
|
||||
// phase i + 1 of a file always depends on phase i being completed
|
||||
|
@ -374,6 +373,80 @@ impl<'a> Dependencies<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Loads the dependency graph to find and make specializations, and returns the next jobs to
|
||||
/// be run.
|
||||
///
|
||||
/// This should be used when the compiler wants to build or run a Roc executable if and only if
|
||||
/// previous stages succeed; in such cases we load the dependency graph dynamically.
|
||||
pub fn load_find_and_make_specializations_after_check(&mut self) -> MutSet<(ModuleId, Phase)> {
|
||||
let mut output = MutSet::default();
|
||||
|
||||
let mut make_specializations_dependents = MakeSpecializationsDependents::default();
|
||||
let default_make_specializations_dependents_len = make_specializations_dependents.0.len();
|
||||
std::mem::swap(
|
||||
&mut self.make_specializations_dependents,
|
||||
&mut make_specializations_dependents,
|
||||
);
|
||||
|
||||
for (&module, info) in make_specializations_dependents.0.iter_mut() {
|
||||
debug_assert!(self.status.get_mut(&Job::Step(module, Phase::FindSpecializations)).is_none(), "should only have targeted solving types, but there is already a goal to find specializations");
|
||||
debug_assert!(self.status.get_mut(&Job::Step(module, Phase::MakeSpecializations)).is_none(), "should only have targeted solving types, but there is already a goal to make specializations");
|
||||
debug_assert!(
|
||||
module == ModuleId::DERIVED_GEN || info.succ.contains(&ModuleId::DERIVED_GEN),
|
||||
"derived module not accounted for in {:?}",
|
||||
(module, info)
|
||||
);
|
||||
|
||||
let mut has_find_specialization_dep = false;
|
||||
for &module_dep in info.succ.iter() {
|
||||
// The modules in `succ` are the modules for which specializations should be made
|
||||
// after the current one. But, their specializations should be found before the
|
||||
// current one.
|
||||
if module_dep != ModuleId::DERIVED_GEN {
|
||||
// We never find specializations for DERIVED_GEN
|
||||
self.add_dependency(module, module_dep, Phase::FindSpecializations);
|
||||
has_find_specialization_dep = true;
|
||||
}
|
||||
|
||||
self.add_dependency(module_dep, module, Phase::MakeSpecializations);
|
||||
self.add_dependency(ModuleId::DERIVED_GEN, module, Phase::MakeSpecializations);
|
||||
|
||||
// `module_dep` can't make its specializations until the current module does.
|
||||
info.has_pred = true;
|
||||
}
|
||||
|
||||
if module != ModuleId::DERIVED_GEN {
|
||||
self.add_to_status_for_phase(module, Phase::FindSpecializations);
|
||||
self.add_dependency_help(
|
||||
module,
|
||||
module,
|
||||
Phase::MakeSpecializations,
|
||||
Phase::FindSpecializations,
|
||||
);
|
||||
}
|
||||
self.add_to_status_for_phase(module, Phase::MakeSpecializations);
|
||||
|
||||
if !has_find_specialization_dep && module != ModuleId::DERIVED_GEN {
|
||||
// We don't depend on any other modules having their specializations found first,
|
||||
// so start finding specializations from this module.
|
||||
output.insert((module, Phase::FindSpecializations));
|
||||
}
|
||||
}
|
||||
|
||||
std::mem::swap(
|
||||
&mut self.make_specializations_dependents,
|
||||
&mut make_specializations_dependents,
|
||||
);
|
||||
debug_assert_eq!(
|
||||
make_specializations_dependents.0.len(),
|
||||
default_make_specializations_dependents_len,
|
||||
"more modules were added to the graph: {:?}",
|
||||
make_specializations_dependents
|
||||
);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
/// Load the entire "make specializations" dependency graph and start from the top.
|
||||
pub fn reload_make_specialization_pass(&mut self) -> MutSet<(ModuleId, Phase)> {
|
||||
let mut output = MutSet::default();
|
||||
|
|
|
@ -699,7 +699,7 @@ fn platform_parse_error() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
// See https://github.com/rtfeldman/roc/issues/2413
|
||||
// See https://github.com/roc-lang/roc/issues/2413
|
||||
fn platform_exposes_main_return_by_pointer_issue() {
|
||||
let modules = vec![
|
||||
(
|
||||
|
|
|
@ -65,6 +65,12 @@ impl TagName {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&str> for TagName {
|
||||
fn from(string: &str) -> Self {
|
||||
Self(string.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleName {
|
||||
// NOTE: After adding one of these, go to `impl ModuleId` and
|
||||
// add a corresponding ModuleId to there!
|
||||
|
@ -187,6 +193,20 @@ impl Lowercase {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Lowercase> for String {
|
||||
fn from(lowercase: Lowercase) -> Self {
|
||||
lowercase.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Lowercase> for Box<str> {
|
||||
fn from(lowercase: Lowercase) -> Self {
|
||||
let string: String = lowercase.0.into();
|
||||
|
||||
string.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Lowercase {
|
||||
fn from(string: &'a str) -> Self {
|
||||
Self(string.into())
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
pub mod called_via;
|
||||
|
|
|
@ -152,6 +152,7 @@ impl Symbol {
|
|||
}
|
||||
|
||||
pub const fn to_ne_bytes(self) -> [u8; 8] {
|
||||
// repr(packed(4)) is repr(c), and with the fields as defined will not having padding.
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
|
||||
|
@ -1416,10 +1417,11 @@ define_builtins! {
|
|||
19 DECODE_BOOL: "bool"
|
||||
20 DECODE_STRING: "string"
|
||||
21 DECODE_LIST: "list"
|
||||
22 DECODE_CUSTOM: "custom"
|
||||
23 DECODE_DECODE_WITH: "decodeWith"
|
||||
24 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial"
|
||||
25 DECODE_FROM_BYTES: "fromBytes"
|
||||
22 DECODE_RECORD: "record"
|
||||
23 DECODE_CUSTOM: "custom"
|
||||
24 DECODE_DECODE_WITH: "decodeWith"
|
||||
25 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial"
|
||||
26 DECODE_FROM_BYTES: "fromBytes"
|
||||
}
|
||||
13 JSON: "Json" => {
|
||||
0 JSON_JSON: "Json"
|
||||
|
|
|
@ -23,5 +23,5 @@ roc_error_macros = {path="../../error_macros"}
|
|||
roc_debug_flags = {path="../debug_flags"}
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
hashbrown = { version = "0.12.1", features = [ "bumpalo" ] }
|
||||
hashbrown = { version = "0.12.3", features = [ "bumpalo" ] }
|
||||
static_assertions = "1.1.0"
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#![allow(clippy::manual_map)]
|
||||
|
||||
use crate::layout::{
|
||||
Builtin, CapturesNiche, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache,
|
||||
LayoutProblem, RawFunctionLayout, TagIdIntType, UnionLayout, WrappedVariant,
|
||||
Builtin, CapturesNiche, ClosureCallOptions, ClosureRepresentation, EnumDispatch, LambdaName,
|
||||
LambdaSet, Layout, LayoutCache, LayoutProblem, RawFunctionLayout, TagIdIntType, UnionLayout,
|
||||
WrappedVariant,
|
||||
};
|
||||
use bumpalo::collections::{CollectIn, Vec};
|
||||
use bumpalo::Bump;
|
||||
|
@ -343,10 +344,16 @@ impl<'a> Proc<'a> {
|
|||
D::Doc: Clone,
|
||||
A: Clone,
|
||||
{
|
||||
let args_doc = self
|
||||
.args
|
||||
.iter()
|
||||
.map(|(_, symbol)| symbol_to_doc(alloc, *symbol));
|
||||
let args_doc = self.args.iter().map(|(layout, symbol)| {
|
||||
let arg_doc = symbol_to_doc(alloc, *symbol);
|
||||
if pretty_print_ir_symbols() {
|
||||
arg_doc
|
||||
.append(alloc.reflow(": "))
|
||||
.append(layout.to_doc(alloc, Parens::NotNeeded))
|
||||
} else {
|
||||
arg_doc
|
||||
}
|
||||
});
|
||||
|
||||
if pretty_print_ir_symbols() {
|
||||
alloc
|
||||
|
@ -621,6 +628,10 @@ impl<'a> Suspended<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.symbol_or_lambdas.is_empty()
|
||||
}
|
||||
|
||||
fn specialization(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
|
@ -654,8 +665,22 @@ enum PendingSpecializations<'a> {
|
|||
/// that we can give specializations we need to modules higher up in the dependency chain, so
|
||||
/// that they can start making specializations too
|
||||
Finding(Suspended<'a>),
|
||||
/// We are making specializations. If any new one comes up, we can just make it immediately
|
||||
Making,
|
||||
/// We are making specializations.
|
||||
/// If any new one comes up while specializing a body, we can do one of two things:
|
||||
/// - if the new specialization is for a symbol that is not in the current stack of symbols
|
||||
/// being specialized, make it immediately
|
||||
/// - if it is, we must suspend the specialization, and we'll do it once the stack is clear
|
||||
/// again.
|
||||
Making(Suspended<'a>),
|
||||
}
|
||||
|
||||
impl<'a> PendingSpecializations<'a> {
|
||||
fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
PendingSpecializations::Finding(suspended)
|
||||
| PendingSpecializations::Making(suspended) => suspended.is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -940,6 +965,8 @@ pub struct Procs<'a> {
|
|||
pub runtime_errors: BumpMap<Symbol, &'a str>,
|
||||
pub externals_we_need: BumpMap<ModuleId, ExternalSpecializations<'a>>,
|
||||
symbol_specializations: SymbolSpecializations<'a>,
|
||||
/// The current set of functions under specialization.
|
||||
pub specialization_stack: Vec<'a, Symbol>,
|
||||
}
|
||||
|
||||
impl<'a> Procs<'a> {
|
||||
|
@ -954,8 +981,43 @@ impl<'a> Procs<'a> {
|
|||
runtime_errors: BumpMap::new_in(arena),
|
||||
externals_we_need: BumpMap::new_in(arena),
|
||||
symbol_specializations: Default::default(),
|
||||
specialization_stack: Vec::with_capacity_in(16, arena),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_active_specialization(&mut self, specialization: Symbol) {
|
||||
self.specialization_stack.push(specialization);
|
||||
}
|
||||
|
||||
fn pop_active_specialization(&mut self, specialization: Symbol) {
|
||||
let popped = self
|
||||
.specialization_stack
|
||||
.pop()
|
||||
.expect("specialization stack is empty");
|
||||
debug_assert_eq!(
|
||||
popped, specialization,
|
||||
"incorrect popped specialization: passed {:?}, but was {:?}",
|
||||
specialization, popped
|
||||
);
|
||||
}
|
||||
|
||||
/// If we need to specialize a function that is already in the stack, we must wait to do so
|
||||
/// until that function is popped off. That's because the type environment will be configured
|
||||
/// for the existing specialization on the stack.
|
||||
///
|
||||
/// For example, in
|
||||
///
|
||||
/// foo = \val, b -> if b then "done" else bar val
|
||||
/// bar = \_ -> foo {} True
|
||||
/// foo "" False
|
||||
///
|
||||
/// During the specialization of `foo : Str False -> Str`, we specialize `bar : Str -> Str`,
|
||||
/// which in turn needs a specialization of `foo : {} False -> Str`. However, we can't
|
||||
/// specialize both `foo : Str False -> Str` and `foo : {} False -> Str` at the same time, so
|
||||
/// the latter specialization must be deferred.
|
||||
fn symbol_needs_suspended_specialization(&self, specialization: Symbol) -> bool {
|
||||
self.specialization_stack.contains(&specialization)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -1045,8 +1107,14 @@ impl<'a> Procs<'a> {
|
|||
debug_assert!(layout.arguments.is_empty());
|
||||
}
|
||||
|
||||
match &mut self.pending_specializations {
|
||||
PendingSpecializations::Finding(suspended) => {
|
||||
let needs_suspended_specialization =
|
||||
self.symbol_needs_suspended_specialization(name.name());
|
||||
match (
|
||||
&mut self.pending_specializations,
|
||||
needs_suspended_specialization,
|
||||
) {
|
||||
(PendingSpecializations::Finding(suspended), _)
|
||||
| (PendingSpecializations::Making(suspended), true) => {
|
||||
// register the pending specialization, so this gets code genned later
|
||||
suspended.specialization(env.subs, name, layout, annotation);
|
||||
|
||||
|
@ -1080,7 +1148,7 @@ impl<'a> Procs<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
PendingSpecializations::Making => {
|
||||
(PendingSpecializations::Making(_), false) => {
|
||||
// Mark this proc as in-progress, so if we're dealing with
|
||||
// mutually recursive functions, we don't loop forever.
|
||||
// (We had a bug around this before this system existed!)
|
||||
|
@ -1178,7 +1246,8 @@ impl<'a> Procs<'a> {
|
|||
}
|
||||
|
||||
// If this is an imported symbol, let its home module make this specialization
|
||||
if env.is_imported_symbol(name.name()) {
|
||||
if env.is_imported_symbol(name.name()) || env.is_unloaded_derived_symbol(name.name(), self)
|
||||
{
|
||||
add_needed_external(self, env, fn_var, name);
|
||||
return;
|
||||
}
|
||||
|
@ -1190,11 +1259,17 @@ impl<'a> Procs<'a> {
|
|||
|
||||
// This should only be called when pending_specializations is Some.
|
||||
// Otherwise, it's being called in the wrong pass!
|
||||
match &mut self.pending_specializations {
|
||||
PendingSpecializations::Finding(suspended) => {
|
||||
let needs_suspended_specialization =
|
||||
self.symbol_needs_suspended_specialization(name.name());
|
||||
match (
|
||||
&mut self.pending_specializations,
|
||||
needs_suspended_specialization,
|
||||
) {
|
||||
(PendingSpecializations::Finding(suspended), _)
|
||||
| (PendingSpecializations::Making(suspended), true) => {
|
||||
suspended.specialization(env.subs, name, layout, fn_var);
|
||||
}
|
||||
PendingSpecializations::Making => {
|
||||
(PendingSpecializations::Making(_), false) => {
|
||||
let symbol = name;
|
||||
|
||||
let partial_proc_id = match self.partial_procs.symbol_to_id(symbol.name()) {
|
||||
|
@ -1207,7 +1282,7 @@ impl<'a> Procs<'a> {
|
|||
// (We had a bug around this before this system existed!)
|
||||
self.specialized.mark_in_progress(symbol.name(), layout);
|
||||
|
||||
// See https://github.com/rtfeldman/roc/issues/1600
|
||||
// See https://github.com/roc-lang/roc/issues/1600
|
||||
//
|
||||
// The annotation variable is the generic/lifted/top-level annotation.
|
||||
// It is connected to the variables of the function's body
|
||||
|
@ -1342,6 +1417,13 @@ impl<'a, 'i> Env<'a, 'i> {
|
|||
self.home == ModuleId::DERIVED_GEN
|
||||
&& symbol.module_id() == ModuleId::DERIVED_SYNTH
|
||||
&& !procs.partial_procs.contains_key(symbol)
|
||||
// TODO: locking to find the answer in the `Derived_gen` module is not great, since
|
||||
// Derived_gen also blocks other modules specializing. Improve this later.
|
||||
&& self
|
||||
.derived_module
|
||||
.lock()
|
||||
.expect("derived module is poisoned")
|
||||
.is_derived_def(symbol)
|
||||
}
|
||||
|
||||
/// Unifies two variables and performs lambda set compaction.
|
||||
|
@ -2501,7 +2583,7 @@ fn patterns_to_when<'a>(
|
|||
// are only stores anyway, no branches.
|
||||
//
|
||||
// NOTE this fails if the pattern contains rigid variables,
|
||||
// see https://github.com/rtfeldman/roc/issues/786
|
||||
// see https://github.com/roc-lang/roc/issues/786
|
||||
// this must be fixed when moving exhaustiveness checking to the new canonical AST
|
||||
for (pattern_var, annotated_mark, pattern) in patterns.into_iter() {
|
||||
if annotated_mark.exhaustive.is_non_exhaustive(env.subs) {
|
||||
|
@ -2739,26 +2821,51 @@ pub fn specialize_all<'a>(
|
|||
specializations_for_host: HostSpecializations<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
) -> Procs<'a> {
|
||||
for externals in externals_others_need {
|
||||
specialize_external_specializations(env, &mut procs, layout_cache, externals);
|
||||
}
|
||||
|
||||
// When calling from_can, pending_specializations should be unavailable.
|
||||
// This must be a single pass, and we must not add any more entries to it!
|
||||
let pending_specializations = std::mem::replace(
|
||||
&mut procs.pending_specializations,
|
||||
PendingSpecializations::Making,
|
||||
PendingSpecializations::Making(Suspended::new_in(env.arena)),
|
||||
);
|
||||
|
||||
// Add all of our existing pending specializations.
|
||||
match pending_specializations {
|
||||
PendingSpecializations::Making => {}
|
||||
PendingSpecializations::Finding(suspended) => {
|
||||
specialize_suspended(env, &mut procs, layout_cache, suspended)
|
||||
}
|
||||
PendingSpecializations::Making(suspended) => {
|
||||
debug_assert!(
|
||||
suspended.is_empty(),
|
||||
"suspended specializations cannot ever start off non-empty when making"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Specialize all the symbols everyone else needs.
|
||||
for externals in externals_others_need {
|
||||
specialize_external_specializations(env, &mut procs, layout_cache, externals);
|
||||
}
|
||||
|
||||
// Specialize any symbols the host needs.
|
||||
specialize_host_specializations(env, &mut procs, layout_cache, specializations_for_host);
|
||||
|
||||
// Now, we must go through and continuously complete any new suspended specializations that were
|
||||
// discovered in specializing the other demanded symbols.
|
||||
while !procs.pending_specializations.is_empty() {
|
||||
let pending_specializations = std::mem::replace(
|
||||
&mut procs.pending_specializations,
|
||||
PendingSpecializations::Making(Suspended::new_in(env.arena)),
|
||||
);
|
||||
match pending_specializations {
|
||||
PendingSpecializations::Making(suspended) => {
|
||||
specialize_suspended(env, &mut procs, layout_cache, suspended);
|
||||
}
|
||||
PendingSpecializations::Finding(_) => {
|
||||
internal_error!("should not have this variant after making specializations")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(
|
||||
procs.symbol_specializations.is_empty(),
|
||||
"{:?}",
|
||||
|
@ -3127,6 +3234,8 @@ fn specialize_external<'a>(
|
|||
closure: opt_closure_layout,
|
||||
ret_layout,
|
||||
} => {
|
||||
let mut proc_args = Vec::from_iter_in(proc_args.iter().copied(), env.arena);
|
||||
|
||||
// unpack the closure symbols, if any
|
||||
match (opt_closure_layout, captured_symbols) {
|
||||
(Some(closure_layout), CapturedSymbols::Captured(captured)) => {
|
||||
|
@ -3196,6 +3305,9 @@ fn specialize_external<'a>(
|
|||
ClosureRepresentation::AlphabeticOrderStruct(field_layouts) => {
|
||||
// captured variables are in symbol-alphabetic order, but now we want
|
||||
// them ordered by their alignment requirements
|
||||
//
|
||||
// TODO: sort only the fields and apply the found permutation to the symbols
|
||||
// TODO: can we move this ordering to `layout_for_member`?
|
||||
let mut combined = Vec::from_iter_in(
|
||||
captured.iter().map(|(x, _)| x).zip(field_layouts.iter()),
|
||||
env.arena,
|
||||
|
@ -3210,19 +3322,25 @@ fn specialize_external<'a>(
|
|||
size2.cmp(&size1)
|
||||
});
|
||||
|
||||
let ordered_field_layouts = Vec::from_iter_in(
|
||||
combined.iter().map(|(_, layout)| **layout),
|
||||
env.arena,
|
||||
);
|
||||
let ordered_field_layouts = ordered_field_layouts.into_bump_slice();
|
||||
|
||||
debug_assert_eq!(
|
||||
captured.len(),
|
||||
field_layouts.len(),
|
||||
ordered_field_layouts.len(),
|
||||
"{:?} captures {:?} but has layout {:?}",
|
||||
lambda_name,
|
||||
&captured,
|
||||
&field_layouts
|
||||
&ordered_field_layouts
|
||||
);
|
||||
|
||||
for (index, (symbol, layout)) in combined.iter().enumerate() {
|
||||
let expr = Expr::StructAtIndex {
|
||||
index: index as _,
|
||||
field_layouts,
|
||||
field_layouts: ordered_field_layouts,
|
||||
structure: Symbol::ARG_CLOSURE,
|
||||
};
|
||||
|
||||
|
@ -3235,51 +3353,37 @@ fn specialize_external<'a>(
|
|||
env.arena.alloc(specialized_body),
|
||||
);
|
||||
}
|
||||
// let symbol = captured[0].0;
|
||||
//
|
||||
// substitute_in_exprs(
|
||||
// env.arena,
|
||||
// &mut specialized_body,
|
||||
// symbol,
|
||||
// Symbol::ARG_CLOSURE,
|
||||
// );
|
||||
}
|
||||
|
||||
ClosureRepresentation::Other(layout) => match layout {
|
||||
Layout::Builtin(Builtin::Bool) => {
|
||||
// just ignore this value
|
||||
// IDEA don't pass this value in the future
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
|
||||
// just ignore this value
|
||||
// IDEA don't pass this value in the future
|
||||
}
|
||||
other => {
|
||||
// NOTE other values always should be wrapped in a 1-element record
|
||||
unreachable!(
|
||||
"{:?} is not a valid closure data representation",
|
||||
other
|
||||
)
|
||||
}
|
||||
},
|
||||
ClosureRepresentation::UnwrappedCapture(_layout) => {
|
||||
debug_assert_eq!(captured.len(), 1);
|
||||
let (captured_symbol, _captured_layout) = captured[0];
|
||||
|
||||
// The capture set is unwrapped, so simply replace the closure argument
|
||||
// to the function with the unwrapped capture name.
|
||||
let captured_symbol = get_specialized_name(captured_symbol);
|
||||
let closure_arg = proc_args.last_mut().unwrap();
|
||||
debug_assert_eq!(closure_arg.1, Symbol::ARG_CLOSURE);
|
||||
closure_arg.1 = captured_symbol;
|
||||
}
|
||||
|
||||
ClosureRepresentation::EnumDispatch(_) => {
|
||||
// just ignore this value, since it's not a capture
|
||||
// IDEA don't pass this value in the future
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, CapturedSymbols::None) | (None, CapturedSymbols::Captured([])) => {}
|
||||
_ => unreachable!("to closure or not to closure?"),
|
||||
}
|
||||
|
||||
let proc_args: Vec<_> = proc_args
|
||||
.iter()
|
||||
.map(|&(layout, symbol)| {
|
||||
// Grab the specialization symbol, if it exists.
|
||||
let symbol = procs
|
||||
.symbol_specializations
|
||||
.remove_single(symbol)
|
||||
.unwrap_or(symbol);
|
||||
|
||||
(layout, symbol)
|
||||
})
|
||||
.collect_in(env.arena);
|
||||
proc_args.iter_mut().for_each(|(_layout, symbol)| {
|
||||
// Grab the specialization symbol, if it exists.
|
||||
*symbol = procs
|
||||
.symbol_specializations
|
||||
.remove_single(*symbol)
|
||||
.unwrap_or(*symbol);
|
||||
});
|
||||
|
||||
// reset subs, so we don't get type errors when specializing for a different signature
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
|
@ -3566,6 +3670,8 @@ where
|
|||
let annotation_var = procs.partial_procs.get_id(partial_proc_id).annotation;
|
||||
instantiate_rigids(env.subs, annotation_var);
|
||||
|
||||
procs.push_active_specialization(proc_name.name());
|
||||
|
||||
let specialized = specialize_external(
|
||||
env,
|
||||
procs,
|
||||
|
@ -3576,6 +3682,8 @@ where
|
|||
partial_proc_id,
|
||||
);
|
||||
|
||||
procs.pop_active_specialization(proc_name.name());
|
||||
|
||||
match specialized {
|
||||
Ok(proc) => {
|
||||
// when successful, the layout after unification should be the layout before unification
|
||||
|
@ -3869,7 +3977,7 @@ pub fn with_hole<'a>(
|
|||
)
|
||||
}
|
||||
Tag {
|
||||
variant_var,
|
||||
tag_union_var: variant_var,
|
||||
name: tag_name,
|
||||
arguments: args,
|
||||
..
|
||||
|
@ -5396,29 +5504,42 @@ where
|
|||
|
||||
Stmt::Let(assigned, expr, lambda_set_layout, hole)
|
||||
}
|
||||
ClosureRepresentation::Other(Layout::Builtin(Builtin::Bool)) => {
|
||||
debug_assert_eq!(symbols.len(), 0);
|
||||
ClosureRepresentation::UnwrappedCapture(_layout) => {
|
||||
debug_assert_eq!(symbols.len(), 1);
|
||||
|
||||
debug_assert_eq!(lambda_set.set.len(), 2);
|
||||
let tag_id = name.name() != lambda_set.iter_set().next().unwrap().name();
|
||||
let expr = Expr::Literal(Literal::Bool(tag_id));
|
||||
let mut symbols = symbols;
|
||||
let (captured_symbol, _) = symbols.next().unwrap();
|
||||
|
||||
Stmt::Let(assigned, expr, lambda_set_layout, hole)
|
||||
// The capture set is unwrapped, so just replaced the assigned capture symbol with the
|
||||
// only capture.
|
||||
let mut hole = hole.clone();
|
||||
substitute_in_exprs(env.arena, &mut hole, assigned, *captured_symbol);
|
||||
hole
|
||||
}
|
||||
ClosureRepresentation::Other(Layout::Builtin(Builtin::Int(IntWidth::U8))) => {
|
||||
debug_assert_eq!(symbols.len(), 0);
|
||||
ClosureRepresentation::EnumDispatch(repr) => match repr {
|
||||
EnumDispatch::Bool => {
|
||||
debug_assert_eq!(symbols.len(), 0);
|
||||
|
||||
debug_assert!(lambda_set.set.len() > 2);
|
||||
let tag_id = lambda_set
|
||||
.iter_set()
|
||||
.position(|s| s.name() == name.name())
|
||||
.unwrap() as u8;
|
||||
debug_assert_eq!(lambda_set.len(), 2);
|
||||
let tag_id = name.name() != lambda_set.iter_set().next().unwrap().name();
|
||||
let expr = Expr::Literal(Literal::Bool(tag_id));
|
||||
|
||||
let expr = Expr::Literal(Literal::Byte(tag_id));
|
||||
Stmt::Let(assigned, expr, lambda_set_layout, hole)
|
||||
}
|
||||
EnumDispatch::U8 => {
|
||||
debug_assert_eq!(symbols.len(), 0);
|
||||
|
||||
Stmt::Let(assigned, expr, lambda_set_layout, hole)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
debug_assert!(lambda_set.len() > 2);
|
||||
let tag_id = lambda_set
|
||||
.iter_set()
|
||||
.position(|s| s.name() == name.name())
|
||||
.unwrap() as u8;
|
||||
|
||||
let expr = Expr::Literal(Literal::Byte(tag_id));
|
||||
|
||||
Stmt::Let(assigned, expr, lambda_set_layout, hole)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
result
|
||||
|
@ -5692,7 +5813,7 @@ fn tag_union_to_function<'a>(
|
|||
}
|
||||
|
||||
let loc_body = Loc::at_zero(roc_can::expr::Expr::Tag {
|
||||
variant_var: return_variable,
|
||||
tag_union_var: return_variable,
|
||||
name: tag_name,
|
||||
arguments: loc_expr_args,
|
||||
ext_var,
|
||||
|
@ -5857,10 +5978,7 @@ fn register_capturing_closure<'a>(
|
|||
Content::Structure(FlatType::Func(_, closure_var, _)) => {
|
||||
match LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info) {
|
||||
Ok(lambda_set) => {
|
||||
if let Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
} = lambda_set.runtime_representation()
|
||||
{
|
||||
if lambda_set.is_represented().is_none() {
|
||||
CapturedSymbols::None
|
||||
} else {
|
||||
let mut temp = Vec::from_iter_in(captured_symbols, env.arena);
|
||||
|
@ -5869,7 +5987,7 @@ fn register_capturing_closure<'a>(
|
|||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// just allow this. see https://github.com/rtfeldman/roc/issues/1585
|
||||
// just allow this. see https://github.com/roc-lang/roc/issues/1585
|
||||
if captured_symbols.is_empty() {
|
||||
CapturedSymbols::None
|
||||
} else {
|
||||
|
@ -7162,7 +7280,7 @@ fn can_reuse_symbol<'a>(
|
|||
|
||||
if arguments.contains(&symbol) {
|
||||
Value(symbol)
|
||||
} else if env.is_imported_symbol(symbol) {
|
||||
} else if env.is_imported_symbol(symbol) || env.is_unloaded_derived_symbol(symbol, procs) {
|
||||
Imported(symbol)
|
||||
} else if procs.partial_procs.contains_key(symbol) {
|
||||
LocalFunction(symbol)
|
||||
|
@ -7334,7 +7452,10 @@ fn specialize_symbol<'a>(
|
|||
match procs.get_partial_proc(original) {
|
||||
None => {
|
||||
match arg_var {
|
||||
Some(arg_var) if env.is_imported_symbol(original) => {
|
||||
Some(arg_var)
|
||||
if env.is_imported_symbol(original)
|
||||
|| env.is_unloaded_derived_symbol(original, procs) =>
|
||||
{
|
||||
let raw = match layout_cache.raw_from_var(env.arena, arg_var, env.subs) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return_on_layout_error_help!(env, e, "specialize_symbol"),
|
||||
|
@ -7603,7 +7724,7 @@ fn build_call<'a>(
|
|||
Stmt::Let(assigned, Expr::Call(call), return_layout, hole)
|
||||
}
|
||||
|
||||
/// See https://github.com/rtfeldman/roc/issues/1549
|
||||
/// See https://github.com/roc-lang/roc/issues/1549
|
||||
///
|
||||
/// What happened is that a function has a type error, but the arguments are not processed.
|
||||
/// That means specializations were missing. Normally that is not a problem, but because
|
||||
|
@ -7936,8 +8057,14 @@ fn call_by_name_help<'a>(
|
|||
debug_assert!(top_level_layout.arguments.is_empty());
|
||||
}
|
||||
|
||||
match &mut procs.pending_specializations {
|
||||
PendingSpecializations::Finding(suspended) => {
|
||||
let needs_suspended_specialization =
|
||||
procs.symbol_needs_suspended_specialization(proc_name.name());
|
||||
match (
|
||||
&mut procs.pending_specializations,
|
||||
needs_suspended_specialization,
|
||||
) {
|
||||
(PendingSpecializations::Finding(suspended), _)
|
||||
| (PendingSpecializations::Making(suspended), true) => {
|
||||
debug_assert!(!env.is_imported_symbol(proc_name.name()));
|
||||
|
||||
// register the pending specialization, so this gets code genned later
|
||||
|
@ -7966,7 +8093,7 @@ fn call_by_name_help<'a>(
|
|||
hole,
|
||||
)
|
||||
}
|
||||
PendingSpecializations::Making => {
|
||||
(PendingSpecializations::Making(_), false) => {
|
||||
let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name.name());
|
||||
|
||||
let field_symbols = field_symbols.into_bump_slice();
|
||||
|
@ -8100,8 +8227,13 @@ fn call_by_name_module_thunk<'a>(
|
|||
debug_assert!(top_level_layout.arguments.is_empty());
|
||||
}
|
||||
|
||||
match &mut procs.pending_specializations {
|
||||
PendingSpecializations::Finding(suspended) => {
|
||||
let needs_suspended_specialization = procs.symbol_needs_suspended_specialization(proc_name);
|
||||
match (
|
||||
&mut procs.pending_specializations,
|
||||
needs_suspended_specialization,
|
||||
) {
|
||||
(PendingSpecializations::Finding(suspended), _)
|
||||
| (PendingSpecializations::Making(suspended), true) => {
|
||||
debug_assert!(!env.is_imported_symbol(proc_name));
|
||||
|
||||
// register the pending specialization, so this gets code genned later
|
||||
|
@ -8114,7 +8246,7 @@ fn call_by_name_module_thunk<'a>(
|
|||
|
||||
force_thunk(env, proc_name, inner_layout, assigned, hole)
|
||||
}
|
||||
PendingSpecializations::Making => {
|
||||
(PendingSpecializations::Making(_), false) => {
|
||||
let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name);
|
||||
|
||||
match opt_partial_proc {
|
||||
|
@ -9166,9 +9298,9 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
|||
where
|
||||
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
||||
{
|
||||
match lambda_set.runtime_representation() {
|
||||
Layout::VOID => empty_lambda_set_error(),
|
||||
Layout::Union(union_layout) => {
|
||||
match lambda_set.call_by_name_options() {
|
||||
ClosureCallOptions::Void => empty_lambda_set_error(),
|
||||
ClosureCallOptions::Union(union_layout) => {
|
||||
let closure_tag_id_symbol = env.unique_symbol();
|
||||
|
||||
let result = lowlevel_union_lambda_set_to_switch(
|
||||
|
@ -9197,7 +9329,7 @@ where
|
|||
env.arena.alloc(result),
|
||||
)
|
||||
}
|
||||
Layout::Struct { .. } => match lambda_set.iter_set().next() {
|
||||
ClosureCallOptions::Struct { .. } => match lambda_set.iter_set().next() {
|
||||
Some(lambda_name) => {
|
||||
let call_spec_id = env.next_call_specialization_id();
|
||||
let update_mode = env.next_update_mode_id();
|
||||
|
@ -9222,39 +9354,58 @@ where
|
|||
hole.clone()
|
||||
}
|
||||
},
|
||||
Layout::Builtin(Builtin::Bool) => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
ClosureCallOptions::UnwrappedCapture(_) => {
|
||||
let lambda_name = lambda_set
|
||||
.iter_set()
|
||||
.next()
|
||||
.expect("no function in lambda set");
|
||||
|
||||
lowlevel_enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
let call_spec_id = env.next_call_specialization_id();
|
||||
let update_mode = env.next_update_mode_id();
|
||||
let call = to_lowlevel_call((
|
||||
lambda_name,
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
to_lowlevel_call,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
call_spec_id,
|
||||
update_mode,
|
||||
));
|
||||
|
||||
lowlevel_enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)),
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
to_lowlevel_call,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
||||
}
|
||||
other => todo!("{:?}", other),
|
||||
ClosureCallOptions::EnumDispatch(repr) => match repr {
|
||||
EnumDispatch::Bool => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
|
||||
lowlevel_enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
to_lowlevel_call,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
EnumDispatch::U8 => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
|
||||
lowlevel_enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)),
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
to_lowlevel_call,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9345,15 +9496,14 @@ fn match_on_lambda_set<'a>(
|
|||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
match lambda_set.runtime_representation() {
|
||||
Layout::VOID => empty_lambda_set_error(),
|
||||
Layout::Union(union_layout) => {
|
||||
match lambda_set.call_by_name_options() {
|
||||
ClosureCallOptions::Void => empty_lambda_set_error(),
|
||||
ClosureCallOptions::Union(union_layout) => {
|
||||
let closure_tag_id_symbol = env.unique_symbol();
|
||||
|
||||
let result = union_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set,
|
||||
Layout::Union(union_layout),
|
||||
closure_tag_id_symbol,
|
||||
union_layout.tag_id_layout(),
|
||||
closure_data_symbol,
|
||||
|
@ -9377,9 +9527,9 @@ fn match_on_lambda_set<'a>(
|
|||
env.arena.alloc(result),
|
||||
)
|
||||
}
|
||||
Layout::Struct {
|
||||
ClosureCallOptions::Struct {
|
||||
field_layouts,
|
||||
field_order_hash,
|
||||
field_order_hash: _,
|
||||
} => {
|
||||
let function_symbol = match lambda_set.iter_set().next() {
|
||||
Some(function_symbol) => function_symbol,
|
||||
|
@ -9403,10 +9553,6 @@ fn match_on_lambda_set<'a>(
|
|||
_ => ClosureInfo::Captures {
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
closure_data_layout: Layout::Struct {
|
||||
field_layouts,
|
||||
field_order_hash,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -9421,15 +9567,21 @@ fn match_on_lambda_set<'a>(
|
|||
hole,
|
||||
)
|
||||
}
|
||||
Layout::Builtin(Builtin::Bool) => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
ClosureCallOptions::UnwrappedCapture(_) => {
|
||||
let function_symbol = lambda_set
|
||||
.iter_set()
|
||||
.next()
|
||||
.expect("no function in lambda set");
|
||||
|
||||
enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
let closure_info = ClosureInfo::Captures {
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
};
|
||||
|
||||
union_lambda_set_branch_help(
|
||||
env,
|
||||
function_symbol,
|
||||
closure_info,
|
||||
argument_symbols,
|
||||
argument_layouts,
|
||||
return_layout,
|
||||
|
@ -9437,23 +9589,38 @@ fn match_on_lambda_set<'a>(
|
|||
hole,
|
||||
)
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
ClosureCallOptions::EnumDispatch(repr) => match repr {
|
||||
EnumDispatch::Bool => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
|
||||
enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)),
|
||||
closure_data_symbol,
|
||||
argument_symbols,
|
||||
argument_layouts,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
other => todo!("{:?}", other),
|
||||
enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
argument_symbols,
|
||||
argument_layouts,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
EnumDispatch::U8 => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
|
||||
enum_lambda_set_to_switch(
|
||||
env,
|
||||
lambda_set.iter_set(),
|
||||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)),
|
||||
argument_symbols,
|
||||
argument_layouts,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9461,7 +9628,6 @@ fn match_on_lambda_set<'a>(
|
|||
fn union_lambda_set_to_switch<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
lambda_set: LambdaSet<'a>,
|
||||
closure_layout: Layout<'a>,
|
||||
closure_tag_id_symbol: Symbol,
|
||||
closure_tag_id_layout: Layout<'a>,
|
||||
closure_data_symbol: Symbol,
|
||||
|
@ -9471,7 +9637,7 @@ fn union_lambda_set_to_switch<'a>(
|
|||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
if lambda_set.set.is_empty() {
|
||||
if lambda_set.is_empty() {
|
||||
// NOTE this can happen if there is a type error somewhere. Since the lambda set is empty,
|
||||
// there is really nothing we can do here. We generate a runtime error here which allows
|
||||
// code gen to proceed. We then assume that we hit another (more descriptive) error before
|
||||
|
@ -9481,7 +9647,7 @@ fn union_lambda_set_to_switch<'a>(
|
|||
|
||||
let join_point_id = JoinPointId(env.unique_symbol());
|
||||
|
||||
let mut branches = Vec::with_capacity_in(lambda_set.set.len(), env.arena);
|
||||
let mut branches = Vec::with_capacity_in(lambda_set.len(), env.arena);
|
||||
|
||||
for (i, lambda_name) in lambda_set.iter_set().enumerate() {
|
||||
let closure_info = if lambda_name.no_captures() {
|
||||
|
@ -9490,7 +9656,6 @@ fn union_lambda_set_to_switch<'a>(
|
|||
ClosureInfo::Captures {
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
closure_data_layout: closure_layout,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9560,11 +9725,10 @@ fn union_lambda_set_branch<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum ClosureInfo<'a> {
|
||||
Captures {
|
||||
closure_data_symbol: Symbol,
|
||||
/// The layout of this closure variant
|
||||
closure_data_layout: Layout<'a>,
|
||||
/// The whole lambda set representation this closure is a variant of
|
||||
lambda_set: LambdaSet<'a>,
|
||||
},
|
||||
|
@ -9586,34 +9750,21 @@ fn union_lambda_set_branch_help<'a>(
|
|||
ClosureInfo::Captures {
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
closure_data_layout,
|
||||
} => match closure_data_layout {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
}
|
||||
| Layout::Builtin(Builtin::Bool)
|
||||
| Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
|
||||
(argument_layouts_slice, argument_symbols_slice)
|
||||
}
|
||||
_ => {
|
||||
// extend layouts with the layout of the closure environment
|
||||
let mut argument_layouts =
|
||||
Vec::with_capacity_in(argument_layouts_slice.len() + 1, env.arena);
|
||||
argument_layouts.extend(argument_layouts_slice);
|
||||
argument_layouts.push(Layout::LambdaSet(lambda_set));
|
||||
|
||||
} => {
|
||||
let argument_layouts =
|
||||
lambda_set.extend_argument_list(env.arena, argument_layouts_slice);
|
||||
let argument_symbols = if argument_layouts.len() > argument_layouts_slice.len() {
|
||||
// extend symbols with the symbol of the closure environment
|
||||
let mut argument_symbols =
|
||||
Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena);
|
||||
argument_symbols.extend(argument_symbols_slice);
|
||||
argument_symbols.push(closure_data_symbol);
|
||||
|
||||
(
|
||||
argument_layouts.into_bump_slice(),
|
||||
argument_symbols.into_bump_slice(),
|
||||
)
|
||||
}
|
||||
},
|
||||
argument_symbols.into_bump_slice()
|
||||
} else {
|
||||
argument_symbols_slice
|
||||
};
|
||||
(argument_layouts, argument_symbols)
|
||||
}
|
||||
ClosureInfo::DoesNotCapture => {
|
||||
// sometimes unification causes a function that does not itself capture anything
|
||||
// to still get a lambda set that does store information. We must not pass a closure
|
||||
|
@ -9637,13 +9788,14 @@ fn union_lambda_set_branch_help<'a>(
|
|||
build_call(env, call, assigned, *return_layout, hole)
|
||||
}
|
||||
|
||||
/// Switches over a enum lambda set, which may dispatch to different functions, none of which
|
||||
/// capture.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn enum_lambda_set_to_switch<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
lambda_set: impl ExactSizeIterator<Item = LambdaName<'a>>,
|
||||
closure_tag_id_symbol: Symbol,
|
||||
closure_tag_id_layout: Layout<'a>,
|
||||
closure_data_symbol: Symbol,
|
||||
argument_symbols: &'a [Symbol],
|
||||
argument_layouts: &'a [Layout<'a>],
|
||||
return_layout: &'a Layout<'a>,
|
||||
|
@ -9656,15 +9808,11 @@ fn enum_lambda_set_to_switch<'a>(
|
|||
|
||||
let mut branches = Vec::with_capacity_in(lambda_set.len(), env.arena);
|
||||
|
||||
let closure_layout = closure_tag_id_layout;
|
||||
|
||||
for (i, lambda_name) in lambda_set.into_iter().enumerate() {
|
||||
let stmt = enum_lambda_set_branch(
|
||||
env,
|
||||
join_point_id,
|
||||
lambda_name,
|
||||
closure_data_symbol,
|
||||
closure_layout,
|
||||
argument_symbols,
|
||||
argument_layouts,
|
||||
return_layout,
|
||||
|
@ -9700,15 +9848,14 @@ fn enum_lambda_set_to_switch<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// A branch for an enum lambda set branch dispatch, which never capture!
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn enum_lambda_set_branch<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
join_point_id: JoinPointId,
|
||||
lambda_name: LambdaName<'a>,
|
||||
closure_data_symbol: Symbol,
|
||||
closure_data_layout: Layout<'a>,
|
||||
argument_symbols_slice: &'a [Symbol],
|
||||
argument_layouts_slice: &'a [Layout<'a>],
|
||||
argument_symbols: &'a [Symbol],
|
||||
argument_layouts: &'a [Layout<'a>],
|
||||
return_layout: &'a Layout<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let result_symbol = env.unique_symbol();
|
||||
|
@ -9717,34 +9864,6 @@ fn enum_lambda_set_branch<'a>(
|
|||
|
||||
let assigned = result_symbol;
|
||||
|
||||
let (argument_layouts, argument_symbols) = match closure_data_layout {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
}
|
||||
| Layout::Builtin(Builtin::Bool)
|
||||
| Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
|
||||
(argument_layouts_slice, argument_symbols_slice)
|
||||
}
|
||||
_ => {
|
||||
// extend layouts with the layout of the closure environment
|
||||
let mut argument_layouts =
|
||||
Vec::with_capacity_in(argument_layouts_slice.len() + 1, env.arena);
|
||||
argument_layouts.extend(argument_layouts_slice);
|
||||
argument_layouts.push(closure_data_layout);
|
||||
|
||||
// extend symbols with the symbol of the closure environment
|
||||
let mut argument_symbols =
|
||||
Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena);
|
||||
argument_symbols.extend(argument_symbols_slice);
|
||||
argument_symbols.push(closure_data_symbol);
|
||||
|
||||
(
|
||||
argument_layouts.into_bump_slice(),
|
||||
argument_symbols.into_bump_slice(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let call = self::Call {
|
||||
call_type: CallType::ByName {
|
||||
name: lambda_name,
|
||||
|
|
|
@ -263,7 +263,7 @@ pub enum Layout<'a> {
|
|||
/// so keep a hash of the record order for disambiguation. This still of course may result
|
||||
/// in collisions, but it's unlikely.
|
||||
///
|
||||
/// See also https://github.com/rtfeldman/roc/issues/2535.
|
||||
/// See also https://github.com/roc-lang/roc/issues/2535.
|
||||
field_order_hash: FieldOrderHash,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
},
|
||||
|
@ -731,7 +731,7 @@ impl std::fmt::Debug for LambdaSet<'_> {
|
|||
/// By recording the captures layouts this lambda expects in its identifier, we can distinguish
|
||||
/// between such differences when constructing closure capture data.
|
||||
///
|
||||
/// See also https://github.com/rtfeldman/roc/issues/3336.
|
||||
/// See also https://github.com/roc-lang/roc/issues/3336.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct CapturesNiche<'a>(&'a [Layout<'a>]);
|
||||
|
||||
|
@ -783,28 +783,56 @@ impl<'a> LambdaName<'a> {
|
|||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct LambdaSet<'a> {
|
||||
/// collection of function names and their closure arguments
|
||||
pub set: &'a [(Symbol, &'a [Layout<'a>])],
|
||||
set: &'a [(Symbol, &'a [Layout<'a>])],
|
||||
/// how the closure will be represented at runtime
|
||||
representation: &'a Layout<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EnumDispatch {
|
||||
Bool,
|
||||
U8,
|
||||
}
|
||||
|
||||
/// representation of the closure *for a particular function*
|
||||
#[derive(Debug)]
|
||||
pub enum ClosureRepresentation<'a> {
|
||||
/// the closure is represented as a union. Includes the tag ID!
|
||||
/// The closure is represented as a union. Includes the tag ID!
|
||||
/// Each variant is a different function, and its payloads are the captures.
|
||||
Union {
|
||||
alphabetic_order_fields: &'a [Layout<'a>],
|
||||
closure_name: Symbol,
|
||||
tag_id: TagIdIntType,
|
||||
union_layout: UnionLayout<'a>,
|
||||
},
|
||||
/// The closure is represented as a struct. The layouts are sorted
|
||||
/// alphabetically by the identifier that is captured.
|
||||
/// The closure is one function, whose captures are represented as a struct.
|
||||
/// The layouts are sorted alphabetically by the identifier that is captured.
|
||||
///
|
||||
/// We MUST sort these according to their stack size before code gen!
|
||||
AlphabeticOrderStruct(&'a [Layout<'a>]),
|
||||
/// the representation is anything but a union
|
||||
Other(Layout<'a>),
|
||||
/// The closure is one function that captures a single identifier, whose value is unwrapped.
|
||||
UnwrappedCapture(Layout<'a>),
|
||||
/// The closure dispatches to multiple functions, but none of them capture anything, so this is
|
||||
/// a boolean or integer flag.
|
||||
EnumDispatch(EnumDispatch),
|
||||
}
|
||||
|
||||
/// How the closure should be seen when determining a call-by-name.
|
||||
#[derive(Debug)]
|
||||
pub enum ClosureCallOptions<'a> {
|
||||
/// This is an empty lambda set, dispatching is an error
|
||||
Void,
|
||||
/// One of a few capturing functions can be called to
|
||||
Union(UnionLayout<'a>),
|
||||
/// The closure is one function, whose captures are represented as a struct.
|
||||
Struct {
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
field_order_hash: FieldOrderHash,
|
||||
},
|
||||
/// The closure is one function that captures a single identifier, whose value is unwrapped.
|
||||
UnwrappedCapture(Layout<'a>),
|
||||
/// The closure dispatches to multiple possible functions, none of which capture.
|
||||
EnumDispatch(EnumDispatch),
|
||||
}
|
||||
|
||||
impl<'a> LambdaSet<'a> {
|
||||
|
@ -818,13 +846,17 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
|
||||
pub fn is_represented(&self) -> Option<Layout<'a>> {
|
||||
match self.representation {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
if self.has_unwrapped_capture_repr() {
|
||||
Some(*self.representation)
|
||||
} else if self.has_enum_dispatch_repr() {
|
||||
None
|
||||
} else {
|
||||
match self.representation {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => None,
|
||||
repr => Some(*repr),
|
||||
}
|
||||
| Layout::Builtin(Builtin::Bool)
|
||||
| Layout::Builtin(Builtin::Int(..)) => None,
|
||||
repr => Some(*repr),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -835,6 +867,16 @@ impl<'a> LambdaSet<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.set.is_empty()
|
||||
}
|
||||
|
||||
pub fn layout_for_member_with_lambda_name(
|
||||
&self,
|
||||
lambda_name: LambdaName,
|
||||
|
@ -923,6 +965,11 @@ impl<'a> LambdaSet<'a> {
|
|||
where
|
||||
F: Fn(Symbol, &[Layout]) -> bool,
|
||||
{
|
||||
if self.has_unwrapped_capture_repr() {
|
||||
// Only one function, that captures one identifier.
|
||||
return ClosureRepresentation::UnwrappedCapture(*self.representation);
|
||||
}
|
||||
|
||||
match self.representation {
|
||||
Layout::Union(union) => {
|
||||
// here we rely on the fact that a union in a closure would be stored in a one-element record.
|
||||
|
@ -1004,7 +1051,58 @@ impl<'a> LambdaSet<'a> {
|
|||
|
||||
ClosureRepresentation::AlphabeticOrderStruct(fields)
|
||||
}
|
||||
_ => ClosureRepresentation::Other(*self.representation),
|
||||
layout => {
|
||||
debug_assert!(self.has_enum_dispatch_repr(),);
|
||||
let enum_repr = match layout {
|
||||
Layout::Builtin(Builtin::Bool) => EnumDispatch::Bool,
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)) => EnumDispatch::U8,
|
||||
other => internal_error!("Invalid layout for enum dispatch: {:?}", other),
|
||||
};
|
||||
ClosureRepresentation::EnumDispatch(enum_repr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_unwrapped_capture_repr(&self) -> bool {
|
||||
self.set.len() == 1 && self.set[0].1.len() == 1
|
||||
}
|
||||
|
||||
fn has_enum_dispatch_repr(&self) -> bool {
|
||||
self.set.len() > 1 && self.set.iter().all(|(_, captures)| captures.is_empty())
|
||||
}
|
||||
|
||||
pub fn call_by_name_options(&self) -> ClosureCallOptions<'a> {
|
||||
if self.has_unwrapped_capture_repr() {
|
||||
return ClosureCallOptions::UnwrappedCapture(*self.representation);
|
||||
}
|
||||
|
||||
match self.representation {
|
||||
Layout::Union(union_layout) => {
|
||||
if self.representation == &Layout::VOID {
|
||||
debug_assert!(self.set.is_empty());
|
||||
return ClosureCallOptions::Void;
|
||||
}
|
||||
ClosureCallOptions::Union(*union_layout)
|
||||
}
|
||||
Layout::Struct {
|
||||
field_layouts,
|
||||
field_order_hash,
|
||||
} => {
|
||||
debug_assert_eq!(self.set.len(), 1);
|
||||
ClosureCallOptions::Struct {
|
||||
field_layouts,
|
||||
field_order_hash: *field_order_hash,
|
||||
}
|
||||
}
|
||||
layout => {
|
||||
debug_assert!(self.has_enum_dispatch_repr());
|
||||
let enum_repr = match layout {
|
||||
Layout::Builtin(Builtin::Bool) => EnumDispatch::Bool,
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8)) => EnumDispatch::U8,
|
||||
other => internal_error!("Invalid layout for enum dispatch: {:?}", other),
|
||||
};
|
||||
ClosureCallOptions::EnumDispatch(enum_repr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1013,30 +1111,27 @@ impl<'a> LambdaSet<'a> {
|
|||
arena: &'a Bump,
|
||||
argument_layouts: &'a [Layout<'a>],
|
||||
) -> &'a [Layout<'a>] {
|
||||
if let [] = self.set {
|
||||
// TERRIBLE HACK for builting functions
|
||||
argument_layouts
|
||||
} else {
|
||||
match self.representation {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => {
|
||||
// this function does not have anything in its closure, and the lambda set is a
|
||||
// singleton, so we pass no extra argument
|
||||
argument_layouts
|
||||
}
|
||||
Layout::Builtin(Builtin::Bool)
|
||||
| Layout::Builtin(Builtin::Int(IntWidth::I8 | IntWidth::U8)) => {
|
||||
// we don't pass this along either
|
||||
argument_layouts
|
||||
}
|
||||
_ => {
|
||||
let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena);
|
||||
arguments.extend(argument_layouts);
|
||||
arguments.push(Layout::LambdaSet(*self));
|
||||
match self.call_by_name_options() {
|
||||
ClosureCallOptions::Void => argument_layouts,
|
||||
ClosureCallOptions::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => {
|
||||
// this function does not have anything in its closure, and the lambda set is a
|
||||
// singleton, so we pass no extra argument
|
||||
argument_layouts
|
||||
}
|
||||
ClosureCallOptions::Struct { .. }
|
||||
| ClosureCallOptions::Union(_)
|
||||
| ClosureCallOptions::UnwrappedCapture(_) => {
|
||||
let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena);
|
||||
arguments.extend(argument_layouts);
|
||||
arguments.push(Layout::LambdaSet(*self));
|
||||
|
||||
arguments.into_bump_slice()
|
||||
}
|
||||
arguments.into_bump_slice()
|
||||
}
|
||||
ClosureCallOptions::EnumDispatch(_) => {
|
||||
// No captures, don't pass this along
|
||||
argument_layouts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1053,7 +1148,7 @@ impl<'a> LambdaSet<'a> {
|
|||
lambdas.sort_by_key(|(sym, _)| *sym);
|
||||
|
||||
let mut set: Vec<(Symbol, &[Layout])> = Vec::with_capacity_in(lambdas.len(), arena);
|
||||
let mut set_with_variables: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)> =
|
||||
let mut set_with_variables: std::vec::Vec<(&Symbol, &[Variable])> =
|
||||
std::vec::Vec::with_capacity(lambdas.len());
|
||||
|
||||
let mut last_function_symbol = None;
|
||||
|
@ -1090,7 +1185,7 @@ impl<'a> LambdaSet<'a> {
|
|||
has_duplicate_lambda_names = has_duplicate_lambda_names || is_multimorphic;
|
||||
|
||||
set.push((*function_symbol, arguments));
|
||||
set_with_variables.push((*function_symbol, variables.to_vec()));
|
||||
set_with_variables.push((function_symbol, variables.as_slice()));
|
||||
|
||||
last_function_symbol = Some(function_symbol);
|
||||
}
|
||||
|
@ -1139,7 +1234,7 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
ResolvedLambdaSet::Unbound => {
|
||||
// The lambda set is unbound which means it must be unused. Just give it the empty lambda set.
|
||||
// See also https://github.com/rtfeldman/roc/issues/3163.
|
||||
// See also https://github.com/roc-lang/roc/issues/3163.
|
||||
Ok(LambdaSet {
|
||||
set: &[],
|
||||
representation: arena.alloc(Layout::UNIT),
|
||||
|
@ -1151,70 +1246,23 @@ impl<'a> LambdaSet<'a> {
|
|||
fn make_representation(
|
||||
arena: &'a Bump,
|
||||
subs: &Subs,
|
||||
tags: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>,
|
||||
tags: std::vec::Vec<(&Symbol, &[Variable])>,
|
||||
opt_rec_var: Option<Variable>,
|
||||
target_info: TargetInfo,
|
||||
) -> Layout<'a> {
|
||||
if let Some(rec_var) = opt_rec_var {
|
||||
let tags: std::vec::Vec<_> = tags
|
||||
.iter()
|
||||
.map(|(sym, vars)| (sym, vars.as_slice()))
|
||||
.collect();
|
||||
let tags = UnsortedUnionLabels { tags };
|
||||
let mut env = Env {
|
||||
seen: Vec::new_in(arena),
|
||||
target_info,
|
||||
arena,
|
||||
subs,
|
||||
};
|
||||
let union_labels = UnsortedUnionLabels { tags };
|
||||
let mut env = Env {
|
||||
seen: Vec::new_in(arena),
|
||||
target_info,
|
||||
arena,
|
||||
subs,
|
||||
};
|
||||
|
||||
return layout_from_recursive_union(&mut env, rec_var, &tags)
|
||||
.expect("unable to create lambda set representation");
|
||||
}
|
||||
match opt_rec_var {
|
||||
Some(rec_var) => layout_from_recursive_union(&mut env, rec_var, &union_labels)
|
||||
.expect("unable to create lambda set representation"),
|
||||
|
||||
// otherwise, this is a closure with a payload
|
||||
let variant = union_sorted_tags_help(arena, tags, opt_rec_var, subs, target_info);
|
||||
|
||||
use UnionVariant::*;
|
||||
match variant {
|
||||
Never => Layout::VOID,
|
||||
BoolUnion { .. } => Layout::bool(),
|
||||
ByteUnion { .. } => Layout::u8(),
|
||||
Unit | UnitWithArguments => {
|
||||
// no useful information to store
|
||||
Layout::UNIT
|
||||
}
|
||||
Newtype {
|
||||
arguments: layouts, ..
|
||||
} => Layout::struct_no_name_order(layouts.into_bump_slice()),
|
||||
Wrapped(variant) => {
|
||||
use WrappedVariant::*;
|
||||
|
||||
match variant {
|
||||
NonRecursive {
|
||||
sorted_tag_layouts: tags,
|
||||
} => {
|
||||
debug_assert!(tags.len() > 1);
|
||||
|
||||
// if the closed-over value is actually a layout, it should be wrapped in a 1-element record
|
||||
debug_assert!(matches!(tags[0].0, TagOrClosure::Closure(_)));
|
||||
|
||||
let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena);
|
||||
|
||||
for (_, tag_args) in tags.iter() {
|
||||
tag_arguments.push(&tag_args[0..]);
|
||||
}
|
||||
Layout::Union(UnionLayout::NonRecursive(tag_arguments.into_bump_slice()))
|
||||
}
|
||||
|
||||
Recursive { .. }
|
||||
| NullableUnwrapped { .. }
|
||||
| NullableWrapped { .. }
|
||||
| NonNullableUnwrapped { .. } => {
|
||||
internal_error!("Recursive layouts should be produced in an earlier branch")
|
||||
}
|
||||
}
|
||||
}
|
||||
None => layout_from_union(&mut env, &union_labels),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1239,7 +1287,7 @@ enum ResolvedLambdaSet {
|
|||
OptVariable,
|
||||
),
|
||||
/// TODO: figure out if this can happen in a correct program, or is the result of a bug in our
|
||||
/// compiler. See https://github.com/rtfeldman/roc/issues/3163.
|
||||
/// compiler. See https://github.com/roc-lang/roc/issues/3163.
|
||||
Unbound,
|
||||
}
|
||||
|
||||
|
@ -1777,6 +1825,13 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runtime_representation(&self) -> Self {
|
||||
match self {
|
||||
Layout::LambdaSet(lambda_set) => lambda_set.runtime_representation(),
|
||||
other => *other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Avoid recomputing Layout from Variable multiple times.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
pub mod borrow;
|
||||
|
|
|
@ -13,12 +13,12 @@ roc_collections = { path = "../collections" }
|
|||
roc_region = { path = "../region" }
|
||||
roc_module = { path = "../module" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
encode_unicode = "0.3.6"
|
||||
encode_unicode = "1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs", features = ["html_reports"]}
|
||||
pretty_assertions = "1.0.0"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
quickcheck = "1.0.3"
|
||||
quickcheck_macros = "1.0.0"
|
||||
roc_test_utils = { path = "../../test_utils" }
|
||||
|
|
318
crates/compiler/parse/fuzz/Cargo.lock
generated
318
crates/compiler/parse/fuzz/Cargo.lock
generated
|
@ -1,11 +1,54 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitmaps"
|
||||
version = "2.1.0"
|
||||
|
@ -16,16 +59,40 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.4.0"
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.61"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
|
@ -34,13 +101,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "im"
|
||||
version = "14.3.0"
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "696059c87b83c5a258817ecd67c3af915e3ed141891fc35a1e79908801cf0ce7"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bumpalo",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "im"
|
||||
version = "15.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"rand_core 0.5.1",
|
||||
"rand_core",
|
||||
"rand_xoshiro",
|
||||
"sized-chunks",
|
||||
"typenum",
|
||||
|
@ -49,30 +155,30 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "im-rc"
|
||||
version = "14.3.0"
|
||||
version = "15.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "303f7e6256d546e01979071417432425f15c1891fb309a5f2d724ee908fabd6e"
|
||||
checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"rand_core 0.5.1",
|
||||
"rand_core",
|
||||
"rand_xoshiro",
|
||||
"sized-chunks",
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6ee2a7da03bfc3b66ca47c92c2e392fcc053ea040a85561749b026f7aad09a"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.131"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.3.4"
|
||||
|
@ -84,54 +190,114 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
|
||||
[[package]]
|
||||
name = "rand_xoshiro"
|
||||
version = "0.4.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
|
||||
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_collections"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bumpalo",
|
||||
"hashbrown",
|
||||
"im",
|
||||
"im-rc",
|
||||
"wyhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_error_macros"
|
||||
version = "0.0.1"
|
||||
|
||||
[[package]]
|
||||
name = "roc_ident"
|
||||
version = "0.0.1"
|
||||
|
||||
[[package]]
|
||||
name = "roc_module"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"inlinable_string",
|
||||
"lazy_static",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_ident",
|
||||
"roc_region",
|
||||
"snafu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_parse"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"encode_unicode",
|
||||
"inlinable_string",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_region",
|
||||
|
@ -148,24 +314,85 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "roc_region"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "sized-chunks"
|
||||
version = "0.5.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59044ea371ad781ff976f7b06480b9f0180e834eda94114f2afb4afc12b7718"
|
||||
checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"doc-comment",
|
||||
"snafu-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu-derive"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
|
@ -173,10 +400,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "wyhash"
|
||||
version = "0.3.0"
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "782a50f48ac4336916227cd199c61c7b42f38d0ad705421b49eb12c74c53ae00"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wyhash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf6e163c25e3fac820b4b453185ea2dea3b6a3e0a721d4d23d75bd33734c295"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -173,8 +173,8 @@ mod test_parse {
|
|||
pass/lowest_float.expr,
|
||||
pass/lowest_int.expr,
|
||||
pass/malformed_ident_due_to_underscore.expr,
|
||||
pass/malformed_pattern_field_access.expr, // See https://github.com/rtfeldman/roc/issues/399
|
||||
pass/malformed_pattern_module_name.expr, // See https://github.com/rtfeldman/roc/issues/399
|
||||
pass/malformed_pattern_field_access.expr, // See https://github.com/roc-lang/roc/issues/399
|
||||
pass/malformed_pattern_module_name.expr, // See https://github.com/roc-lang/roc/issues/399
|
||||
pass/minimal_app_header.header,
|
||||
pass/minus_twelve_minus_five.expr,
|
||||
pass/mixed_docs.expr,
|
||||
|
@ -191,7 +191,7 @@ mod test_parse {
|
|||
pass/nested_def_annotation.module,
|
||||
pass/nested_if.expr,
|
||||
pass/nested_module.header,
|
||||
pass/newline_after_equals.expr, // Regression test for https://github.com/rtfeldman/roc/issues/51
|
||||
pass/newline_after_equals.expr, // Regression test for https://github.com/roc-lang/roc/issues/51
|
||||
pass/newline_after_mul.expr,
|
||||
pass/newline_after_sub.expr,
|
||||
pass/newline_and_spaces_before_less_than.expr,
|
||||
|
@ -229,7 +229,7 @@ mod test_parse {
|
|||
pass/parenthetical_var.expr,
|
||||
pass/parse_alias.expr,
|
||||
pass/parse_as_ann.expr,
|
||||
pass/pattern_with_space_in_parens.expr, // https://github.com/rtfeldman/roc/issues/929
|
||||
pass/pattern_with_space_in_parens.expr, // https://github.com/roc-lang/roc/issues/929
|
||||
pass/plus_if.expr,
|
||||
pass/plus_when.expr,
|
||||
pass/pos_inf_float.expr,
|
||||
|
@ -263,7 +263,7 @@ mod test_parse {
|
|||
pass/two_branch_when.expr,
|
||||
pass/two_spaced_def.expr,
|
||||
pass/type_decl_with_underscore.expr,
|
||||
pass/unary_negation_access.expr, // Regression test for https://github.com/rtfeldman/roc/issues/509
|
||||
pass/unary_negation_access.expr, // Regression test for https://github.com/roc-lang/roc/issues/509
|
||||
pass/unary_negation_arg.expr,
|
||||
pass/unary_negation_with_parens.expr,
|
||||
pass/unary_negation.expr,
|
||||
|
|
|
@ -38,6 +38,7 @@ pub enum Problem {
|
|||
/// Bool is whether the closure is anonymous
|
||||
/// Second symbol is the name of the argument that is unused
|
||||
UnusedArgument(Symbol, bool, Symbol, Region),
|
||||
UnusedBranchDef(Symbol, Region),
|
||||
PrecedenceProblem(PrecedenceProblem),
|
||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||
UnsupportedPattern(BadPattern, Region),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod can;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
pub mod all;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
use strum_macros::{EnumCount, EnumIter};
|
||||
|
|
|
@ -32,9 +32,9 @@ roc_target = { path = "../roc_target" }
|
|||
roc_reporting = { path = "../../reporting" }
|
||||
roc_derive = { path = "../derive", features = ["debug-derived-symbols"] }
|
||||
pretty_assertions = "1.0.0"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
tempfile = "3.2.0"
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
regex = "1.5.5"
|
||||
lazy_static = "1.4.0"
|
||||
insta = "1.15.0"
|
||||
insta = "1.18.2"
|
||||
|
|
|
@ -32,7 +32,7 @@ f = if True then id1 else id2
|
|||
^ f : a -[[id1, id2]] -> a
|
||||
```
|
||||
|
||||
The syntax `-[[id1]]->` can be read as “a function that dispatches to `id1`". Then the arrow `-[[id1, id2]]->` then is “a function that dispatches to `id1`, or `id2`". The tag union `[id1, id2]` can contain payloads to represent the captures of `id1` and `id2`; however, the implications of that are out of scope for this discussion, see [Folkert’s great explanation](https://github.com/rtfeldman/roc/pull/2307#discussion_r777042512) for more information. During compile-time, Roc would attach a run-time examinable tag to the value in each branch of the `f` expression body, representing whether to dispatch to `id1` or `id2`. Whenever `f` is dispatched, that tag is examined to determine exactly which function should be dispatched to. This is “**defunctionalization**”.
|
||||
The syntax `-[[id1]]->` can be read as “a function that dispatches to `id1`". Then the arrow `-[[id1, id2]]->` then is “a function that dispatches to `id1`, or `id2`". The tag union `[id1, id2]` can contain payloads to represent the captures of `id1` and `id2`; however, the implications of that are out of scope for this discussion, see [Folkert’s great explanation](https://github.com/roc-lang/roc/pull/2307#discussion_r777042512) for more information. During compile-time, Roc would attach a run-time examinable tag to the value in each branch of the `f` expression body, representing whether to dispatch to `id1` or `id2`. Whenever `f` is dispatched, that tag is examined to determine exactly which function should be dispatched to. This is “**defunctionalization**”.
|
||||
|
||||
In the presence of [abilities](https://docs.google.com/document/d/1kUh53p1Du3fWP_jZp-sdqwb5C9DuS43YJwXHg1NzETY/edit), lambda sets get more complicated. Now, I can write something like
|
||||
|
||||
|
@ -100,7 +100,7 @@ The unification trace for the call `f (@Foo {})` proceeds as follows. I use `'tN
|
|||
Foo -[[zeroHash] + Foo:hashThunk:1]-> ({} -[[lam1] + Foo:hashThunk:2]-> U64)
|
||||
```
|
||||
|
||||
Now that the specialization lambdas’ type variables point to concrete types, we can resolve the concrete lambdas of `Foo:hashThunk:1` and `Foo:hashThunk:2`. Cool! Let’s do that. We know that
|
||||
Now that the specialization lambdas’ type variables point to concrete types, we can resolve the concrete lambdas of `Foo:hashThunk:1` and `Foo:hashThunk:2`. Cool! Let’s do that. We know that
|
||||
|
||||
```
|
||||
hashThunk = \@Foo {} -> \{} -> 1
|
||||
|
@ -205,46 +205,46 @@ Okay, so first we’ll enumerate some terminology, and the exact algorithm. Then
|
|||
### Some definitions
|
||||
|
||||
- **The region invariant.** Previously we discussed the “region” of a lambda set in a specialization function definition. The way regions are assigned in the compiler follows a very specific ordering and holds a invariant we’ll call the “region invariant”. First, let’s define a procedure for creating function types and assigning regions:
|
||||
|
||||
|
||||
```
|
||||
Type = \region ->
|
||||
Type = \region ->
|
||||
(Type_atom, region)
|
||||
| Type_function region
|
||||
|
||||
|
||||
Type_function = \region ->
|
||||
let left_type, new_region = Type (region + 1)
|
||||
let right_type, new_region = Type (new_region)
|
||||
let func_type = left_type -[Lambda region]-> right_type
|
||||
(func_type, new_region)
|
||||
```
|
||||
|
||||
|
||||
This procedure would create functions that look like the trees(abbreviating `L=Lambda`, `a=atom` below)
|
||||
|
||||
|
||||
```
|
||||
-[L 1]->
|
||||
a a
|
||||
|
||||
|
||||
===
|
||||
|
||||
|
||||
-[L 1]->
|
||||
-[L 2]-> -[L 3]->
|
||||
a a a a
|
||||
|
||||
|
||||
===
|
||||
-[L 1]->
|
||||
-[L 2]-> -[L 5]->
|
||||
-[L 3]-> -[L 4]-> -[L 6]-> -[L 7]->
|
||||
a a a a a a a a
|
||||
```
|
||||
|
||||
|
||||
The invariant is this: for a region `r`, the only functions enclosing `r` have a region number that is less than `r`. Moreover, every region `r' < r`, either the function at `r'` encloses `r`, or is disjoint from `r`.
|
||||
|
||||
|
||||
- **Ambient functions.** For a given lambda set at region `r`, any function that encloses `r` is called an **ambient function** of `r`. The function directly at region `r` is called the **directly ambient function**.
|
||||
|
||||
|
||||
For example, the functions identified by `L 4`, `L 2`, and `L 1` in the last example tree above are all ambient functions of the function identified by `L 4`.
|
||||
|
||||
The region invariant means that the only functions that are ambient of a region `r` are those identified by regions `< r`.
|
||||
|
||||
|
||||
The region invariant means that the only functions that are ambient of a region `r` are those identified by regions `< r`.
|
||||
|
||||
- `uls_of_var`. A look aside table of the unspecialized lambda sets (uls) depending on a variable. For example, in `a -[[] + a:f:1]-> (b -[[] + a:f:2]-> {})`, there would be a mapping of `a => { [[] + a:f:1]; [[] + a:f:2] }`. When `a` gets instantiated with a concrete type, we know that these lambda sets are ready to be resolved.
|
||||
|
||||
### Explicit Description
|
||||
|
@ -440,13 +440,13 @@ F has f : a, b -> ({} -> ({} -> {})) | a has F, b has G
|
|||
# ^ a, b -[[] + a:f:1]-> ({} -[[] + a:f:2]-> ({} -[[] + a:f:3]-> {})) | a has F, b has G
|
||||
G has g : b -> ({} -> {}) | b has G
|
||||
# ^ b -[[] + b:g:1]-> ({} -[[] + b:g:2]-> {}) | b has G
|
||||
|
||||
|
||||
Fo := {}
|
||||
f = \@Fo {}, b -> \{} -> g b
|
||||
#^ Fo, b -[[Fo#f]]-> ({} -[[lamF b]]-> ({} -[[] + b:g:2]]-> {})) | b has G
|
||||
# instantiation with a=Fo of
|
||||
# a, b -[[] + a:f:1]-> ({} -[[] + a:f:2]-> ({} -[[] + a:f:3]-> {})) | a has F, b has G
|
||||
|
||||
|
||||
Go := {}
|
||||
g = \@Go {} -> \{} -> {}
|
||||
#^ {} -[[Go#g]]-> ({} -[[lamG]]-> {})
|
||||
|
@ -670,7 +670,7 @@ You may have observed that step 1 and step 2 of the algorithm are somewhat overk
|
|||
This optimization is correct with a change to the region numbering scheme:
|
||||
|
||||
```python
|
||||
Type = \region ->
|
||||
Type = \region ->
|
||||
(Type_atom, region)
|
||||
| Type_function region
|
||||
|
||||
|
|
|
@ -442,16 +442,6 @@ trait DerivableVisitor {
|
|||
false
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_flex(var: Variable) -> Result<(), DerivableError> {
|
||||
Err(DerivableError::NotDerivable(var))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_rigid(var: Variable) -> Result<(), DerivableError> {
|
||||
Err(DerivableError::NotDerivable(var))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_flex_able(var: Variable, ability: Symbol) -> Result<(), DerivableError> {
|
||||
if ability != Self::ABILITY {
|
||||
|
@ -529,7 +519,7 @@ trait DerivableVisitor {
|
|||
fn is_derivable(
|
||||
obligation_cache: &mut ObligationCache,
|
||||
abilities_store: &AbilitiesStore,
|
||||
subs: &Subs,
|
||||
subs: &mut Subs,
|
||||
var: Variable,
|
||||
) -> Result<(), DerivableError> {
|
||||
let mut stack = vec![var];
|
||||
|
@ -552,8 +542,11 @@ trait DerivableVisitor {
|
|||
use DerivableError::*;
|
||||
use FlatType::*;
|
||||
match *content {
|
||||
FlexVar(_) => Self::visit_flex(var)?,
|
||||
RigidVar(_) => Self::visit_rigid(var)?,
|
||||
FlexVar(opt_name) => {
|
||||
// Promote the flex var to be bound to the ability.
|
||||
subs.set_content(var, Content::FlexAbleVar(opt_name, Self::ABILITY));
|
||||
}
|
||||
RigidVar(_) => return Err(NotDerivable(var)),
|
||||
FlexAbleVar(_, ability) => Self::visit_flex_able(var, ability)?,
|
||||
RigidAbleVar(_, ability) => Self::visit_rigid_able(var, ability)?,
|
||||
RecursionVar {
|
||||
|
@ -584,7 +577,15 @@ trait DerivableVisitor {
|
|||
let descend = Self::visit_record(var)?;
|
||||
if descend.0 {
|
||||
push_var_slice!(fields.variables());
|
||||
stack.push(ext);
|
||||
if !matches!(
|
||||
subs.get_content_without_compacting(ext),
|
||||
Content::FlexVar(_) | Content::RigidVar(_)
|
||||
) {
|
||||
// TODO: currently, just we suppose the presence of a flex var may
|
||||
// include more or less things which we can derive. But, we should
|
||||
// instead recurse here, and add a `t ~ u | u has Decode` constraint as needed.
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
pub mod ability;
|
||||
|
|
|
@ -3113,7 +3113,7 @@ fn adjust_rank_content(
|
|||
// inside a lambda set but not on the left or right of an arrow, and records should not
|
||||
// force de-generalization in such cases.
|
||||
//
|
||||
// See https://github.com/rtfeldman/roc/issues/3641 for a longer discussion and
|
||||
// See https://github.com/roc-lang/roc/issues/3641 for a longer discussion and
|
||||
// example.
|
||||
group_rank
|
||||
}
|
||||
|
|
|
@ -182,7 +182,13 @@ mod solve_expr {
|
|||
|
||||
// Disregard UnusedDef problems, because those are unavoidable when
|
||||
// returning a function from the test expression.
|
||||
can_problems.retain(|prob| !matches!(prob, roc_problem::can::Problem::UnusedDef(_, _)));
|
||||
can_problems.retain(|prob| {
|
||||
!matches!(
|
||||
prob,
|
||||
roc_problem::can::Problem::UnusedDef(_, _)
|
||||
| roc_problem::can::Problem::UnusedBranchDef(..)
|
||||
)
|
||||
});
|
||||
|
||||
let (can_problems, type_problems) =
|
||||
format_problems(&src, home, &interns, can_problems, type_problems);
|
||||
|
@ -4916,7 +4922,7 @@ mod solve_expr {
|
|||
|
||||
#[test]
|
||||
fn rigid_type_variable_problem() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1162
|
||||
// see https://github.com/roc-lang/roc/issues/1162
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -5024,7 +5030,7 @@ mod solve_expr {
|
|||
#[test]
|
||||
fn inference_var_tag_union_ext() {
|
||||
// TODO: we should really be inferring [Blue, Orange]a -> [Lavender, Peach]a here.
|
||||
// See https://github.com/rtfeldman/roc/issues/2053
|
||||
// See https://github.com/roc-lang/roc/issues/2053
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -5491,7 +5497,7 @@ mod solve_expr {
|
|||
)
|
||||
}
|
||||
|
||||
// https://github.com/rtfeldman/roc/issues/2379
|
||||
// https://github.com/roc-lang/roc/issues/2379
|
||||
#[test]
|
||||
fn copy_vars_referencing_copied_vars() {
|
||||
infer_eq_without_problem(
|
||||
|
@ -5866,7 +5872,7 @@ mod solve_expr {
|
|||
}
|
||||
|
||||
#[test]
|
||||
// https://github.com/rtfeldman/roc/issues/2702
|
||||
// https://github.com/roc-lang/roc/issues/2702
|
||||
fn tag_inclusion_behind_opaque() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
|
@ -7691,4 +7697,68 @@ mod solve_expr {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transient_captures() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = "abc"
|
||||
|
||||
getX = \{} -> x
|
||||
|
||||
h = \{} -> (getX {})
|
||||
#^{-1}
|
||||
|
||||
h {}
|
||||
"#
|
||||
),
|
||||
@"h : {}* -[[h(3) Str]]-> Str"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transient_captures_after_def_ordering() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
h = \{} -> (getX {})
|
||||
#^{-1}
|
||||
|
||||
getX = \{} -> x
|
||||
|
||||
x = "abc"
|
||||
|
||||
h {}
|
||||
"#
|
||||
),
|
||||
@"h : {}* -[[h(1) Str]]-> Str"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mutually_recursive_captures() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = True
|
||||
y = False
|
||||
|
||||
a = "foo"
|
||||
b = "bar"
|
||||
|
||||
foo = \{} -> if x then a else bar {}
|
||||
#^^^{-1}
|
||||
bar = \{} -> if y then b else foo {}
|
||||
#^^^{-1}
|
||||
|
||||
bar {}
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
foo : {} -[[foo(5) [True]* [False]* Str Str]]-> Str
|
||||
bar : {} -[[bar(6) [True]* [False]* Str Str]]-> Str
|
||||
"###
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ roc_builtins = { path = "../builtins" }
|
|||
roc_load_internal = { path = "../load_internal" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_derive_key = { path = "../derive_key" }
|
||||
roc_derive = { path = "../derive", features = ["debug-derived-symbols"] }
|
||||
roc_derive = { path = "../derive", features = ["debug-derived-symbols", "open-extension-vars"] }
|
||||
roc_target = { path = "../roc_target" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
|
@ -26,7 +26,7 @@ roc_solve = { path = "../solve" }
|
|||
roc_debug_flags = { path = "../debug_flags" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
lazy_static = "1.4.0"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
pretty_assertions = "1.0.0"
|
||||
insta = "1.15.0"
|
||||
insta = "1.18.2"
|
||||
|
|
|
@ -5,14 +5,48 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::{
|
||||
util::{check_immediate, derive_test},
|
||||
test_key_eq, test_key_neq,
|
||||
util::{check_immediate, check_underivable, derive_test},
|
||||
v,
|
||||
};
|
||||
use insta::assert_snapshot;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_types::subs::Variable;
|
||||
|
||||
use roc_derive_key::DeriveBuiltin::Decoder;
|
||||
use roc_derive_key::{DeriveBuiltin::Decoder, DeriveError};
|
||||
|
||||
test_key_eq! {
|
||||
Decoder,
|
||||
|
||||
same_record:
|
||||
v!({ a: v!(U8), }), v!({ a: v!(U8), })
|
||||
same_record_fields_diff_types:
|
||||
v!({ a: v!(U8), }), v!({ a: v!(STR), })
|
||||
same_record_fields_any_order:
|
||||
v!({ a: v!(U8), b: v!(U8), c: v!(U8), }),
|
||||
v!({ c: v!(U8), a: v!(U8), b: v!(U8), })
|
||||
explicit_empty_record_and_implicit_empty_record:
|
||||
v!(EMPTY_RECORD), v!({})
|
||||
|
||||
list_list_diff_types:
|
||||
v!(Symbol::LIST_LIST v!(STR)), v!(Symbol::LIST_LIST v!(U8))
|
||||
str_str:
|
||||
v!(Symbol::STR_STR), v!(Symbol::STR_STR)
|
||||
}
|
||||
|
||||
test_key_neq! {
|
||||
Decoder,
|
||||
|
||||
different_record_fields:
|
||||
v!({ a: v!(U8), }), v!({ b: v!(U8), })
|
||||
record_empty_vs_nonempty:
|
||||
v!(EMPTY_RECORD), v!({ a: v!(U8), })
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_record_field_derive_error() {
|
||||
check_underivable(Decoder, v!({ ?a: v!(U8), }), DeriveError::Underivable);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn immediates() {
|
||||
|
@ -49,3 +83,66 @@ fn list() {
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_2_fields() {
|
||||
derive_test(Decoder, v!({first: v!(STR), second: v!(STR),}), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for { first : Str, second : Str }
|
||||
# Decoder { first : val, second : val1 } fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
|
||||
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok { first : val, second : val1 }] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[custom(22)]]
|
||||
#Derived.decoder_{first,second} =
|
||||
Decode.custom
|
||||
\#Derived.bytes3, #Derived.fmt3 ->
|
||||
Decode.decodeWith
|
||||
#Derived.bytes3
|
||||
(Decode.record
|
||||
{ second: Err NoField, first: Err NoField }
|
||||
\#Derived.stateRecord2, #Derived.field ->
|
||||
when #Derived.field is
|
||||
"first" ->
|
||||
Keep (Decode.custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
when Decode.decodeWith
|
||||
#Derived.bytes
|
||||
Decode.decoder
|
||||
#Derived.fmt is
|
||||
#Derived.rec ->
|
||||
{
|
||||
result: when #Derived.rec.result is
|
||||
Ok #Derived.val ->
|
||||
Ok { stateRecord2 & first: Ok #Derived.val }
|
||||
Err #Derived.err -> Err #Derived.err,
|
||||
rest: #Derived.rec.rest
|
||||
})
|
||||
"second" ->
|
||||
Keep (Decode.custom
|
||||
\#Derived.bytes2, #Derived.fmt2 ->
|
||||
when Decode.decodeWith
|
||||
#Derived.bytes2
|
||||
Decode.decoder
|
||||
#Derived.fmt2 is
|
||||
#Derived.rec2 ->
|
||||
{
|
||||
result: when #Derived.rec2.result is
|
||||
Ok #Derived.val2 ->
|
||||
Ok { stateRecord2 & second: Ok #Derived.val2 }
|
||||
Err #Derived.err2 -> Err #Derived.err2,
|
||||
rest: #Derived.rec2.rest
|
||||
})
|
||||
_ -> Skip
|
||||
\#Derived.stateRecord ->
|
||||
when #Derived.stateRecord.first is
|
||||
Ok #Derived.first ->
|
||||
when #Derived.stateRecord.second is
|
||||
Ok #Derived.second ->
|
||||
Ok { second: #Derived.second, first: #Derived.first }
|
||||
_ -> Err TooShort
|
||||
_ -> Err TooShort)
|
||||
#Derived.fmt3
|
||||
"###
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
use insta::assert_snapshot;
|
||||
|
||||
use crate::{
|
||||
test_hash_eq, test_hash_neq,
|
||||
test_key_eq, test_key_neq,
|
||||
util::{check_immediate, derive_test},
|
||||
v,
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ use roc_types::subs::Variable;
|
|||
|
||||
// {{{ hash tests
|
||||
|
||||
test_hash_eq! {
|
||||
test_key_eq! {
|
||||
ToEncoder,
|
||||
|
||||
same_record:
|
||||
|
@ -70,7 +70,7 @@ test_hash_eq! {
|
|||
v!(@Symbol::BOOL_BOOL => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([False, True]))
|
||||
}
|
||||
|
||||
test_hash_neq! {
|
||||
test_key_neq! {
|
||||
ToEncoder,
|
||||
|
||||
different_record_fields:
|
||||
|
@ -174,10 +174,7 @@ fn one_field_record() {
|
|||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
#Derived.bytes
|
||||
(Encode.record
|
||||
[
|
||||
{ value: Encode.toEncoder #Derived.rcd.a, key: "a", },
|
||||
])
|
||||
(Encode.record [{ value: Encode.toEncoder #Derived.rcd.a, key: "a" }])
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
|
@ -202,8 +199,8 @@ fn two_field_record() {
|
|||
#Derived.bytes
|
||||
(Encode.record
|
||||
[
|
||||
{ value: Encode.toEncoder #Derived.rcd.a, key: "a", },
|
||||
{ value: Encode.toEncoder #Derived.rcd.b, key: "b", },
|
||||
{ value: Encode.toEncoder #Derived.rcd.a, key: "a" },
|
||||
{ value: Encode.toEncoder #Derived.rcd.b, key: "b" },
|
||||
])
|
||||
#Derived.fmt
|
||||
"###
|
||||
|
|
|
@ -19,7 +19,10 @@ pub fn pretty_print_def(c: &Ctx, d: &Def) -> String {
|
|||
|
||||
macro_rules! maybe_paren {
|
||||
($paren_if_above:expr, $my_prec:expr, $doc:expr) => {
|
||||
if $my_prec > $paren_if_above {
|
||||
maybe_paren!($paren_if_above, $my_prec, || true, $doc)
|
||||
};
|
||||
($paren_if_above:expr, $my_prec:expr, $extra_cond:expr, $doc:expr) => {
|
||||
if $my_prec > $paren_if_above && $extra_cond() {
|
||||
$doc.parens().group()
|
||||
} else {
|
||||
$doc
|
||||
|
@ -47,7 +50,7 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> {
|
|||
#[derive(PartialEq, PartialOrd)]
|
||||
enum EPrec {
|
||||
Free,
|
||||
CallArg,
|
||||
AppArg,
|
||||
}
|
||||
|
||||
fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, Arena<'a>> {
|
||||
|
@ -137,11 +140,11 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
maybe_paren!(
|
||||
Free,
|
||||
p,
|
||||
expr(c, CallArg, f, &fun.value)
|
||||
expr(c, AppArg, f, &fun.value)
|
||||
.append(
|
||||
f.concat(args.iter().map(|le| f.line().append(expr(
|
||||
c,
|
||||
CallArg,
|
||||
AppArg,
|
||||
f,
|
||||
&le.1.value
|
||||
))))
|
||||
|
@ -175,15 +178,18 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
Record { fields, .. } => f
|
||||
.reflow("{")
|
||||
.append(
|
||||
f.concat(fields.iter().map(|(name, field)| {
|
||||
let field = f
|
||||
.text(name.as_str())
|
||||
.append(f.reflow(": "))
|
||||
.append(expr(c, Free, f, &field.loc_expr.value))
|
||||
.nest(2)
|
||||
.group();
|
||||
f.line().append(field).append(",")
|
||||
}))
|
||||
f.intersperse(
|
||||
fields.iter().map(|(name, field)| {
|
||||
let field = f
|
||||
.text(name.as_str())
|
||||
.append(f.reflow(": "))
|
||||
.append(expr(c, Free, f, &field.loc_expr.value))
|
||||
.nest(2)
|
||||
.group();
|
||||
f.line().append(field)
|
||||
}),
|
||||
f.reflow(","),
|
||||
)
|
||||
.nest(2)
|
||||
.group(),
|
||||
)
|
||||
|
@ -193,15 +199,61 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
EmptyRecord => f.text("{}"),
|
||||
Access {
|
||||
loc_expr, field, ..
|
||||
} => expr(c, CallArg, f, &loc_expr.value)
|
||||
} => expr(c, AppArg, f, &loc_expr.value)
|
||||
.append(f.text(format!(".{}", field.as_str())))
|
||||
.group(),
|
||||
OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => {
|
||||
f.text(format!("@{}", opaque_name.as_str(c.interns)))
|
||||
}
|
||||
Accessor(_) => todo!(),
|
||||
Update { .. } => todo!(),
|
||||
Tag { .. } => todo!(),
|
||||
Update {
|
||||
symbol, updates, ..
|
||||
} => f
|
||||
.reflow("{")
|
||||
.append(f.line())
|
||||
.append(f.text(symbol.as_str(c.interns).to_string()))
|
||||
.append(f.reflow(" &"))
|
||||
.append(
|
||||
f.intersperse(
|
||||
updates.iter().map(|(name, field)| {
|
||||
let field = f
|
||||
.text(name.as_str())
|
||||
.append(f.reflow(": "))
|
||||
.append(expr(c, Free, f, &field.loc_expr.value))
|
||||
.nest(2)
|
||||
.group();
|
||||
f.line().append(field)
|
||||
}),
|
||||
f.reflow(","),
|
||||
)
|
||||
.nest(2)
|
||||
.group(),
|
||||
)
|
||||
.append(f.line())
|
||||
.append(f.text("}"))
|
||||
.group(),
|
||||
Tag {
|
||||
name, arguments, ..
|
||||
} => maybe_paren!(
|
||||
Free,
|
||||
p,
|
||||
|| !arguments.is_empty(),
|
||||
f.text(name.0.as_str())
|
||||
.append(if arguments.is_empty() {
|
||||
f.nil()
|
||||
} else {
|
||||
f.space()
|
||||
})
|
||||
.append(
|
||||
f.intersperse(
|
||||
arguments
|
||||
.iter()
|
||||
.map(|(_, le)| expr(c, AppArg, f, &le.value)),
|
||||
f.space(),
|
||||
)
|
||||
)
|
||||
.group()
|
||||
),
|
||||
ZeroArgumentTag { .. } => todo!(),
|
||||
OpaqueRef { .. } => todo!(),
|
||||
Expect { .. } => todo!(),
|
||||
|
|
|
@ -18,7 +18,7 @@ use roc_collections::VecSet;
|
|||
use roc_constrain::expr::constrain_decls;
|
||||
use roc_debug_flags::dbg_do;
|
||||
use roc_derive::DerivedModule;
|
||||
use roc_derive_key::{DeriveBuiltin, DeriveKey, Derived};
|
||||
use roc_derive_key::{DeriveBuiltin, DeriveError, DeriveKey, Derived};
|
||||
use roc_load_internal::file::{add_imports, default_aliases, LoadedModule, Threading};
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, Symbol};
|
||||
use roc_region::all::LineInfo;
|
||||
|
@ -53,7 +53,7 @@ fn module_source_and_path(builtin: DeriveBuiltin) -> (ModuleId, &'static str, Pa
|
|||
}
|
||||
}
|
||||
|
||||
/// DSL for creating [`Content`][crate::subs::Content].
|
||||
/// DSL for creating [`Content`][roc_types::subs::Content].
|
||||
#[macro_export]
|
||||
macro_rules! v {
|
||||
({ $($field:ident: $make_v:expr,)* $(?$opt_field:ident : $make_opt_v:expr,)* }) => {{
|
||||
|
@ -65,7 +65,7 @@ macro_rules! v {
|
|||
$(let $opt_field = $make_opt_v(subs);)*
|
||||
let fields = vec![
|
||||
$( (stringify!($field).into(), RecordField::Required($field)) ,)*
|
||||
$( (stringify!($opt_field).into(), RecordField::Required($opt_field)) ,)*
|
||||
$( (stringify!($opt_field).into(), RecordField::Optional($opt_field)) ,)*
|
||||
];
|
||||
let fields = RecordFields::insert_into_subs(subs, fields);
|
||||
roc_derive::synth_var(subs, Content::Structure(FlatType::Record(fields, Variable::EMPTY_RECORD)))
|
||||
|
@ -178,7 +178,7 @@ where
|
|||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_hash_eq {
|
||||
macro_rules! test_key_eq {
|
||||
($builtin:expr, $($name:ident: $synth1:expr, $synth2:expr)*) => {$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
|
@ -188,7 +188,7 @@ macro_rules! test_hash_eq {
|
|||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_hash_neq {
|
||||
macro_rules! test_key_neq {
|
||||
($builtin:expr, $($name:ident: $synth1:expr, $synth2:expr)*) => {$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
|
@ -197,6 +197,18 @@ macro_rules! test_hash_neq {
|
|||
)*};
|
||||
}
|
||||
|
||||
pub(crate) fn check_underivable<Sy>(builtin: DeriveBuiltin, synth: Sy, err: DeriveError)
|
||||
where
|
||||
Sy: FnOnce(&mut Subs) -> Variable,
|
||||
{
|
||||
let mut subs = Subs::new();
|
||||
let var = synth(&mut subs);
|
||||
|
||||
let key = Derived::builtin(builtin, &subs, var);
|
||||
|
||||
assert_eq!(key, Err(err));
|
||||
}
|
||||
|
||||
pub(crate) fn check_immediate<S>(builtin: DeriveBuiltin, synth: S, immediate: Symbol)
|
||||
where
|
||||
S: FnOnce(&mut Subs) -> Variable,
|
||||
|
@ -324,7 +336,7 @@ fn check_derived_typechecks_and_golden(
|
|||
// run the solver, print and fail if we have errors
|
||||
dbg_do!(
|
||||
roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED,
|
||||
std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED, "1")
|
||||
std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS, "1")
|
||||
);
|
||||
let (mut solved_subs, _, problems, _) = roc_solve::module::run_solve(
|
||||
test_module,
|
||||
|
@ -338,6 +350,10 @@ fn check_derived_typechecks_and_golden(
|
|||
&exposed_for_module.exposed_by_module,
|
||||
Default::default(),
|
||||
);
|
||||
dbg_do!(
|
||||
roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED,
|
||||
std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS, "0")
|
||||
);
|
||||
let subs = solved_subs.inner_mut();
|
||||
|
||||
if !problems.is_empty() {
|
||||
|
|
|
@ -43,9 +43,9 @@ inkwell = { path = "../../vendor/inkwell" }
|
|||
target-lexicon = "0.12.3"
|
||||
libloading = "0.7.1"
|
||||
tempfile = "3.2.0"
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs" }
|
||||
wasm3 = "0.3.1"
|
||||
wasm3 = { git = "https://github.com/roc-lang/wasm3-rs", rev = "f0f807d1fc0a50d1d68e5799e54ee62c05af00f5" }
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[features]
|
||||
|
|
|
@ -468,9 +468,9 @@ mod encode_immediate {
|
|||
17, u32
|
||||
17, u64
|
||||
17, u128
|
||||
// 17.23, f32 TODO https://github.com/rtfeldman/roc/issues/3522
|
||||
17.25, f32
|
||||
17.23, f64
|
||||
// 17.23, dec TODO https://github.com/rtfeldman/roc/issues/3522
|
||||
17.23, dec
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,7 +691,6 @@ fn encode_derived_list_of_records() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[ignore = "#3696: Currently hits some weird panic in borrow checking, not sure if it's directly related to abilities."]
|
||||
fn encode_derived_list_of_lists_of_strings() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -715,10 +714,7 @@ fn encode_derived_list_of_lists_of_strings() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(
|
||||
any(feature = "gen-llvm", feature = "gen-wasm"),
|
||||
not(feature = "gen-llvm-wasm") // hits a stack limit in wasm3
|
||||
))]
|
||||
#[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))]
|
||||
fn encode_derived_record_with_many_types() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -877,7 +873,7 @@ mod decode_immediate {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn dec() {
|
||||
use roc_std::RocDec;
|
||||
|
||||
|
@ -899,7 +895,7 @@ mod decode_immediate {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn decode_list_of_strings() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -918,10 +914,7 @@ fn decode_list_of_strings() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(
|
||||
any(feature = "gen-llvm"), // currently fails on gen-wasm
|
||||
not(feature = "gen-llvm-wasm") // hits a stack limit in wasm3
|
||||
))]
|
||||
#[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))]
|
||||
fn encode_then_decode_list_of_strings() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -958,3 +951,142 @@ fn encode_then_decode_list_of_lists_of_strings() {
|
|||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn decode_record_two_fields() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok {first: "ab", second: "cd"} -> "abcd"
|
||||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
RocStr::from("abcd"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn decode_record_two_fields_string_and_int() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.toUtf8 "{\"first\":\"ab\",\"second\":10}" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok {first: "ab", second: 10u8} -> "ab10"
|
||||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
RocStr::from("ab10"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn decode_record_two_fields_string_and_string_infer() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok {first, second} -> Str.concat first second
|
||||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
RocStr::from("abcd"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn decode_record_two_fields_string_and_string_infer_local_var() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8
|
||||
when decoded is
|
||||
Ok rcd -> Str.concat rcd.first rcd.second
|
||||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
RocStr::from("abcd"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn decode_record_two_fields_string_and_string_infer_local_var_destructured() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8
|
||||
when decoded is
|
||||
Ok {first, second} -> Str.concat first second
|
||||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
RocStr::from("abcd"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[ignore = "json parsing impl must be fixed first"]
|
||||
fn decode_empty_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.toUtf8 "{}" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok {} -> "empty"
|
||||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
RocStr::from("empty"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(
|
||||
any(feature = "gen-llvm", feature = "gen-wasm"),
|
||||
not(feature = "gen-llvm-wasm") // hits a wasm3 stack overflow
|
||||
))]
|
||||
fn decode_record_of_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.toUtf8 "{\"outer\":{\"inner\":\"a\"},\"other\":{\"one\":\"b\",\"two\":10}}" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok {outer: {inner: "a"}, other: {one: "b", two: 10u8}} -> "ab10"
|
||||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
RocStr::from("ab10"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2831,7 +2831,7 @@ fn lists_with_incompatible_type_param_in_if() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn map_with_index_multi_record() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1700
|
||||
// see https://github.com/roc-lang/roc/issues/1700
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2846,7 +2846,7 @@ fn map_with_index_multi_record() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn empty_list_of_function_type() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1732
|
||||
// see https://github.com/roc-lang/roc/issues/1732
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -3276,7 +3276,7 @@ fn monomorphized_lists() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn with_capacity() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1732
|
||||
// see https://github.com/roc-lang/roc/issues/1732
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -3369,3 +3369,23 @@ fn issue_3530_uninitialized_capacity_in_list_literal() {
|
|||
|(_, _, cap)| cap
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_let_generalization() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List a
|
||||
empty = []
|
||||
|
||||
xs : List Str
|
||||
xs = List.append empty "foo"
|
||||
|
||||
List.len xs
|
||||
"#
|
||||
),
|
||||
1,
|
||||
usize
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1114,18 +1114,35 @@ fn gen_mul_dec() {
|
|||
i128
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn gen_mul_i64() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
2 * 4 * 6
|
||||
"#
|
||||
),
|
||||
48,
|
||||
i64
|
||||
);
|
||||
fn gen_signed_mul_quadword_and_lower() {
|
||||
assert_evals_to!("2i64 * 4 * 6", 48, i64);
|
||||
assert_evals_to!("2i32 * 4 * 6", 48, i32);
|
||||
assert_evals_to!("2i16 * 4 * 6", 48, i16);
|
||||
assert_evals_to!("2i8 * 4 * 6", 48, i8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn gen_unsigned_mul_quadword_and_lower() {
|
||||
assert_evals_to!("2u64 * 4 * 6", 48, u64);
|
||||
assert_evals_to!("2u32 * 4 * 6", 48, u32);
|
||||
assert_evals_to!("2u16 * 4 * 6", 48, u16);
|
||||
assert_evals_to!("2u8 * 4 * 6", 48, u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn gen_mul_f64() {
|
||||
assert_evals_to!("2f64 * 4 * 6", 48.0, f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn gen_mul_f32() {
|
||||
assert_evals_to!("2f32 * 4 * 6", 48.0, f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2091,9 +2108,9 @@ fn float_mul_checked() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn shift_left_by() {
|
||||
assert_evals_to!("Num.shiftLeftBy 0 0b0000_0001", 0b0000_0001, i64);
|
||||
assert_evals_to!("Num.shiftLeftBy 1 0b0000_0001", 0b0000_0010, i64);
|
||||
assert_evals_to!("Num.shiftLeftBy 2 0b0000_0011", 0b0000_1100, i64);
|
||||
assert_evals_to!("Num.shiftLeftBy 0b0000_0001 0", 0b0000_0001, i64);
|
||||
assert_evals_to!("Num.shiftLeftBy 0b0000_0001 1", 0b0000_0010, i64);
|
||||
assert_evals_to!("Num.shiftLeftBy 0b0000_0011 2", 0b0000_1100, i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2105,43 +2122,43 @@ fn shift_right_by() {
|
|||
|
||||
// FIXME (Brian) Something funny happening with 8-bit binary literals in tests
|
||||
assert_evals_to!(
|
||||
"Num.shiftRightBy 2 (Num.toI8 0b1100_0000u8)",
|
||||
"Num.shiftRightBy (Num.toI8 0b1100_0000u8) 2",
|
||||
0b1111_0000u8 as i8,
|
||||
i8
|
||||
);
|
||||
assert_evals_to!("Num.shiftRightBy 2 0b0100_0000i8", 0b0001_0000i8, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 1 0b1110_0000u8", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 2 0b1100_0000u8", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 12 0b0100_0000u8", 0b0000_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 0b0100_0000i8 2", 0b0001_0000i8, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 0b1110_0000u8 1", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 0b1100_0000u8 2", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 0b0100_0000u8 12", 0b0000_0000u8, u8);
|
||||
|
||||
// LLVM in release mode returns 0 instead of -1 for some reason
|
||||
if !is_llvm_release_mode {
|
||||
assert_evals_to!("Num.shiftRightBy 12 0b1000_0000u8", 0b1111_1111u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 0b1000_0000u8 12", 0b1111_1111u8, u8);
|
||||
}
|
||||
assert_evals_to!("Num.shiftRightBy 0 12", 12, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 1 12", 6, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 1 -12", -6, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 8 12", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 8 -12", -1, i64);
|
||||
assert_evals_to!("Num.shiftRightBy -1 12", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 12 0", 12, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 12 1", 6, i64);
|
||||
assert_evals_to!("Num.shiftRightBy -12 1", -6, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 12 8", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy -12 8", -1, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 12 -1", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 0 0", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 1 0", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 0 1", 0, i64);
|
||||
|
||||
assert_evals_to!("Num.shiftRightBy 0 12i32", 12, i32);
|
||||
assert_evals_to!("Num.shiftRightBy 1 12i32", 6, i32);
|
||||
assert_evals_to!("Num.shiftRightBy 1 -12i32", -6, i32);
|
||||
assert_evals_to!("Num.shiftRightBy 8 12i32", 0, i32);
|
||||
assert_evals_to!("Num.shiftRightBy 8 -12i32", -1, i32);
|
||||
assert_evals_to!("Num.shiftRightBy 12i32 0", 12, i32);
|
||||
assert_evals_to!("Num.shiftRightBy 12i32 1", 6, i32);
|
||||
assert_evals_to!("Num.shiftRightBy -12i32 1", -6, i32);
|
||||
assert_evals_to!("Num.shiftRightBy 12i32 8", 0, i32);
|
||||
assert_evals_to!("Num.shiftRightBy -12i32 8", -1, i32);
|
||||
|
||||
assert_evals_to!("Num.shiftRightBy 0 12i8", 12, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 1 12i8", 6, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 1 -12i8", -6, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 8 12i8", 0, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 12i8 0", 12, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 12i8 1", 6, i8);
|
||||
assert_evals_to!("Num.shiftRightBy -12i8 1", -6, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 12i8 8", 0, i8);
|
||||
|
||||
if !is_llvm_release_mode {
|
||||
assert_evals_to!("Num.shiftRightBy -1 0", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy -1 -12", -1, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 8 -12i8", -1, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 0 -1", 0, i64);
|
||||
assert_evals_to!("Num.shiftRightBy -12 -1", -1, i64);
|
||||
assert_evals_to!("Num.shiftRightBy -12i8 8", -1, i8);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2150,14 +2167,14 @@ fn shift_right_by() {
|
|||
fn shift_right_zf_by() {
|
||||
// Logical Right Shift
|
||||
assert_evals_to!(
|
||||
"Num.shiftRightZfBy 2 (Num.toI8 0b1100_0000u8)",
|
||||
"Num.shiftRightZfBy (Num.toI8 0b1100_0000u8) 2",
|
||||
0b0011_0000i8,
|
||||
i8
|
||||
);
|
||||
assert_evals_to!("Num.shiftRightZfBy 2 0b1100_0000u8", 0b0011_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightZfBy 1 0b0000_0010u8", 0b0000_0001u8, u8);
|
||||
assert_evals_to!("Num.shiftRightZfBy 2 0b0000_1100u8", 0b0000_0011u8, u8);
|
||||
assert_evals_to!("Num.shiftRightZfBy 12 0b1000_0000u8", 0b0000_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightZfBy 0b1100_0000u8 2", 0b0011_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightZfBy 0b0000_0010u8 1", 0b0000_0001u8, u8);
|
||||
assert_evals_to!("Num.shiftRightZfBy 0b0000_1100u8 2", 0b0000_0011u8, u8);
|
||||
assert_evals_to!("Num.shiftRightZfBy 0b1000_0000u8 12", 0b0000_0000u8, u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3567,7 +3584,7 @@ fn to_float_f64() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// https://github.com/rtfeldman/roc/issues/2696
|
||||
// https://github.com/roc-lang/roc/issues/2696
|
||||
fn upcast_of_int_is_zext() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -3582,7 +3599,7 @@ fn upcast_of_int_is_zext() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// https://github.com/rtfeldman/roc/issues/2696
|
||||
// https://github.com/roc-lang/roc/issues/2696
|
||||
fn upcast_of_int_checked_is_zext() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
|
|
@ -9,6 +9,8 @@ use crate::helpers::wasm::assert_evals_to;
|
|||
|
||||
use indoc::indoc;
|
||||
#[allow(unused_imports)]
|
||||
use roc_std::RocList;
|
||||
#[allow(unused_imports)]
|
||||
use roc_std::RocStr;
|
||||
|
||||
#[test]
|
||||
|
@ -1232,8 +1234,8 @@ fn return_wrapped_closure() {
|
|||
main = foo
|
||||
"#
|
||||
),
|
||||
[5],
|
||||
[i64; 1]
|
||||
5,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2643,12 +2645,12 @@ fn pattern_match_unit_tag() {
|
|||
);
|
||||
}
|
||||
|
||||
// see for why this is disabled on wasm32 https://github.com/rtfeldman/roc/issues/1687
|
||||
// see for why this is disabled on wasm32 https://github.com/roc-lang/roc/issues/1687
|
||||
#[cfg(not(feature = "gen-llvm-wasm"))]
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn mirror_llvm_alignment_padding() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1569
|
||||
// see https://github.com/roc-lang/roc/issues/1569
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2781,7 +2783,7 @@ fn lambda_set_enum_byte_byte() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_walk_until() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1576
|
||||
// see https://github.com/roc-lang/roc/issues/1576
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2807,7 +2809,7 @@ fn list_walk_until() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn int_literal_not_specialized_with_annotation() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1600
|
||||
// see https://github.com/roc-lang/roc/issues/1600
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2835,7 +2837,7 @@ fn int_literal_not_specialized_with_annotation() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn int_literal_not_specialized_no_annotation() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1600
|
||||
// see https://github.com/roc-lang/roc/issues/1600
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2862,7 +2864,7 @@ fn int_literal_not_specialized_no_annotation() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn unresolved_tvar_when_capture_is_unused() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1585
|
||||
// see https://github.com/roc-lang/roc/issues/1585
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2908,7 +2910,7 @@ fn value_not_exposed_hits_panic() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn mix_function_and_closure() {
|
||||
// see https://github.com/rtfeldman/roc/pull/1706
|
||||
// see https://github.com/roc-lang/roc/pull/1706
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2934,7 +2936,7 @@ fn mix_function_and_closure() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn mix_function_and_closure_level_of_indirection() {
|
||||
// see https://github.com/rtfeldman/roc/pull/1706
|
||||
// see https://github.com/roc-lang/roc/pull/1706
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2960,7 +2962,7 @@ fn mix_function_and_closure_level_of_indirection() {
|
|||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg_attr(debug_assertions, ignore)] // this test stack-overflows the compiler in debug mode
|
||||
fn do_pass_bool_byte_closure_layout() {
|
||||
// see https://github.com/rtfeldman/roc/pull/1706
|
||||
// see https://github.com/roc-lang/roc/pull/1706
|
||||
// the distinction is actually important, dropping that info means some functions just get
|
||||
// skipped
|
||||
assert_evals_to!(
|
||||
|
@ -3422,7 +3424,7 @@ fn polymorphic_lambda_set_multiple_specializations() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map2_conslist() {
|
||||
// this had an RC problem, https://github.com/rtfeldman/roc/issues/2968
|
||||
// this had an RC problem, https://github.com/roc-lang/roc/issues/2968
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -3724,7 +3726,7 @@ fn recursive_lambda_set_issue_3444() {
|
|||
),
|
||||
RocStr::from("c"),
|
||||
RocStr
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3838,3 +3840,222 @@ fn compose_recursive_lambda_set_productive_inferred() {
|
|||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn local_binding_aliases_function() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
f : {} -> List a
|
||||
f = \_ -> []
|
||||
|
||||
main : List U8
|
||||
main =
|
||||
g = f
|
||||
|
||||
g {}
|
||||
"#
|
||||
),
|
||||
RocList::<u8>::from_slice(&[]),
|
||||
RocList<u8>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn local_binding_aliases_function_inferred() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
f = \_ -> []
|
||||
|
||||
main =
|
||||
g = f
|
||||
|
||||
g {}
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[]),
|
||||
RocList<std::convert::Infallible>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn transient_captures() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = "abc"
|
||||
|
||||
getX = \{} -> x
|
||||
|
||||
h = \{} -> getX {}
|
||||
|
||||
h {}
|
||||
"#
|
||||
),
|
||||
RocStr::from("abc"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn transient_captures_after_def_ordering() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
h = \{} -> getX {}
|
||||
|
||||
getX = \{} -> x
|
||||
|
||||
x = "abc"
|
||||
|
||||
h {}
|
||||
"#
|
||||
),
|
||||
RocStr::from("abc"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn deep_transient_capture_chain() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
z = "abc"
|
||||
|
||||
getX = \{} -> getY {}
|
||||
getY = \{} -> getZ {}
|
||||
getZ = \{} -> z
|
||||
|
||||
h = \{} -> getX {}
|
||||
|
||||
h {}
|
||||
"#
|
||||
),
|
||||
RocStr::from("abc"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn deep_transient_capture_chain_with_multiple_captures() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
h = "h"
|
||||
x = "x"
|
||||
y = "y"
|
||||
z = "z"
|
||||
|
||||
getX = \{} -> Str.concat x (getY {})
|
||||
getY = \{} -> Str.concat y (getZ {})
|
||||
getZ = \{} -> z
|
||||
|
||||
getH = \{} -> Str.concat h (getX {})
|
||||
|
||||
getH {}
|
||||
"#
|
||||
),
|
||||
RocStr::from("hxyz"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn transient_captures_from_outer_scope() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = "abc"
|
||||
|
||||
getX = \{} -> x
|
||||
|
||||
innerScope =
|
||||
h = \{} -> getX {}
|
||||
h {}
|
||||
|
||||
innerScope
|
||||
"#
|
||||
),
|
||||
RocStr::from("abc"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn mutually_recursive_captures() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Bool
|
||||
x = True
|
||||
|
||||
y : Bool
|
||||
y = False
|
||||
|
||||
a = "foo"
|
||||
b = "bar"
|
||||
|
||||
foo = \{} -> if x then a else bar {}
|
||||
bar = \{} -> if y then b else foo {}
|
||||
|
||||
bar {}
|
||||
"#
|
||||
),
|
||||
RocStr::from("foo"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn monomorphization_sees_polymorphic_recursion() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
foo : a, Bool -> Str
|
||||
foo = \in, b -> if b then "done" else bar in
|
||||
|
||||
bar = \_ -> foo {} True
|
||||
|
||||
foo "" False
|
||||
"#
|
||||
),
|
||||
RocStr::from("done"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn int_let_generalization() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
manyAux : {} -> I32
|
||||
manyAux = \_ ->
|
||||
output = \_ -> 42
|
||||
|
||||
output {}
|
||||
|
||||
when manyAux {} is
|
||||
_ -> "done"
|
||||
"#
|
||||
),
|
||||
RocStr::from("done"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
|
|
@ -964,7 +964,7 @@ fn update_the_only_field() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// https://github.com/rtfeldman/roc/issues/1513
|
||||
// https://github.com/roc-lang/roc/issues/1513
|
||||
fn both_have_unique_fields() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -985,7 +985,7 @@ fn both_have_unique_fields() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// https://github.com/rtfeldman/roc/issues/2535
|
||||
// https://github.com/roc-lang/roc/issues/2535
|
||||
fn different_proc_types_specialized_to_same_layout() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
|
|
@ -1154,7 +1154,7 @@ fn applied_tag_function_result() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[ignore = "This test has incorrect refcounts: https://github.com/rtfeldman/roc/issues/2968"]
|
||||
#[ignore = "This test has incorrect refcounts: https://github.com/roc-lang/roc/issues/2968"]
|
||||
fn applied_tag_function_linked_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1469,7 +1469,7 @@ fn issue_2458() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "See https://github.com/rtfeldman/roc/issues/2466"]
|
||||
#[ignore = "See https://github.com/roc-lang/roc/issues/2466"]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn issue_2458_deep_recursion_var() {
|
||||
assert_evals_to!(
|
||||
|
@ -1921,10 +1921,10 @@ fn issue_2165_recursive_tag_destructure() {
|
|||
indoc!(
|
||||
r#"
|
||||
SomeTag : [ Ctor { rec : List SomeTag } ]
|
||||
|
||||
|
||||
x : SomeTag
|
||||
x = Ctor { rec: [] }
|
||||
|
||||
|
||||
when x is
|
||||
Ctor { rec } -> Num.toStr (List.len rec)
|
||||
"#
|
||||
|
@ -1933,3 +1933,25 @@ fn issue_2165_recursive_tag_destructure() {
|
|||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn tag_union_let_generalization() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
manyAux : {} -> [ Loop, Done ]
|
||||
manyAux = \_ ->
|
||||
output = Done
|
||||
|
||||
output
|
||||
|
||||
when manyAux {} is
|
||||
Loop -> "loop"
|
||||
Done -> "done"
|
||||
"#
|
||||
),
|
||||
RocStr::from("done"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
// we actually want to compare against the literal float bits
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
|
|
@ -20,4 +20,4 @@ roc_target = { path = "../roc_target" }
|
|||
roc_reporting = { path = "../../reporting" }
|
||||
test_mono_macros = { path = "../test_mono_macros" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
indoc = "1.0.3"
|
||||
indoc = "1.0.7"
|
||||
|
|
|
@ -4,17 +4,15 @@ procedure List.6 (#Attr.2):
|
|||
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.2 : I64 = 41i64;
|
||||
let Test.10 : {I64} = Struct {Test.2};
|
||||
let Test.9 : List {I64} = Array [Test.10];
|
||||
let Test.9 : List I64 = Array [Test.2];
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.3 (Test.8, #Attr.12):
|
||||
let Test.2 : I64 = StructAtIndex 0 #Attr.12;
|
||||
procedure Test.3 (Test.8, Test.2):
|
||||
ret Test.2;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.7 : {} = Struct {};
|
||||
let Test.4 : List {I64} = CallByName Test.1 Test.7;
|
||||
let Test.4 : List I64 = CallByName Test.1 Test.7;
|
||||
let Test.6 : U64 = CallByName List.6 Test.4;
|
||||
dec Test.4;
|
||||
ret Test.6;
|
||||
|
|
|
@ -12,22 +12,20 @@ procedure List.71 (#Attr.2, #Attr.3):
|
|||
let List.388 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.388;
|
||||
|
||||
procedure Test.23 (Test.24, Test.35, #Attr.12):
|
||||
let Test.22 : U8 = StructAtIndex 0 #Attr.12;
|
||||
procedure Test.23 (Test.24, Test.35, Test.22):
|
||||
let Test.37 : List U8 = CallByName List.4 Test.24 Test.22;
|
||||
ret Test.37;
|
||||
|
||||
procedure Test.8 (Test.22):
|
||||
let Test.34 : {U8} = Struct {Test.22};
|
||||
ret Test.34;
|
||||
ret Test.22;
|
||||
|
||||
procedure Test.9 (Test.27):
|
||||
let Test.33 : {U8} = CallByName Test.8 Test.27;
|
||||
let Test.33 : U8 = CallByName Test.8 Test.27;
|
||||
ret Test.33;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.32 : U8 = 15i64;
|
||||
let Test.28 : {U8} = CallByName Test.9 Test.32;
|
||||
let Test.28 : U8 = CallByName Test.9 Test.32;
|
||||
let Test.30 : List U8 = Array [];
|
||||
let Test.31 : {} = Struct {};
|
||||
let Test.29 : List U8 = CallByName Test.23 Test.30 Test.31 Test.28;
|
||||
|
|
|
@ -1,34 +1,26 @@
|
|||
procedure #Derived.0 (#Derived.1):
|
||||
let #Derived_gen.1 : {Str} = Struct {#Derived.1};
|
||||
let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1;
|
||||
let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1;
|
||||
ret #Derived_gen.0;
|
||||
|
||||
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
|
||||
let #Derived.1 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc #Derived.1;
|
||||
dec #Attr.12;
|
||||
procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1):
|
||||
let #Derived_gen.7 : Str = "a";
|
||||
let #Derived_gen.8 : {Str} = CallByName #Derived.5 #Derived.1;
|
||||
let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.7, #Derived_gen.8};
|
||||
let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6];
|
||||
let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.5;
|
||||
let #Derived_gen.8 : Str = CallByName #Derived.5 #Derived.1;
|
||||
let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8};
|
||||
let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6];
|
||||
let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5;
|
||||
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4;
|
||||
ret #Derived_gen.3;
|
||||
|
||||
procedure #Derived.5 (#Derived.6):
|
||||
let #Derived_gen.15 : {Str} = Struct {#Derived.6};
|
||||
let #Derived_gen.14 : {Str} = CallByName Encode.22 #Derived_gen.15;
|
||||
let #Derived_gen.14 : Str = CallByName Encode.22 #Derived.6;
|
||||
ret #Derived_gen.14;
|
||||
|
||||
procedure #Derived.7 (#Derived.8, #Derived.9, #Attr.12):
|
||||
let #Derived.6 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc #Derived.6;
|
||||
dec #Attr.12;
|
||||
procedure #Derived.7 (#Derived.8, #Derived.9, #Derived.6):
|
||||
let #Derived_gen.21 : Str = "b";
|
||||
let #Derived_gen.22 : {Str} = CallByName Json.18 #Derived.6;
|
||||
let #Derived_gen.20 : {Str, {Str}} = Struct {#Derived_gen.21, #Derived_gen.22};
|
||||
let #Derived_gen.19 : List {Str, {Str}} = Array [#Derived_gen.20];
|
||||
let #Derived_gen.18 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.19;
|
||||
let #Derived_gen.22 : Str = CallByName Json.18 #Derived.6;
|
||||
let #Derived_gen.20 : {Str, Str} = Struct {#Derived_gen.21, #Derived_gen.22};
|
||||
let #Derived_gen.19 : List {Str, Str} = Array [#Derived_gen.20];
|
||||
let #Derived_gen.18 : List {Str, Str} = CallByName Json.20 #Derived_gen.19;
|
||||
let #Derived_gen.17 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.18 #Derived.9;
|
||||
ret #Derived_gen.17;
|
||||
|
||||
|
@ -69,7 +61,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96):
|
|||
|
||||
procedure Encode.25 (Encode.100, Encode.101):
|
||||
let Encode.104 : List U8 = Array [];
|
||||
let Encode.105 : {Str} = CallByName #Derived.0 Encode.100;
|
||||
let Encode.105 : Str = CallByName #Derived.0 Encode.100;
|
||||
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
|
||||
ret Encode.103;
|
||||
|
||||
|
@ -77,10 +69,7 @@ procedure Json.1 ():
|
|||
let Json.318 : {} = Struct {};
|
||||
ret Json.318;
|
||||
|
||||
procedure Json.103 (Json.104, Json.321, #Attr.12):
|
||||
let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
|
||||
inc Json.102;
|
||||
dec #Attr.12;
|
||||
procedure Json.103 (Json.104, Json.321, Json.102):
|
||||
let Json.354 : I32 = 123i64;
|
||||
let Json.353 : U8 = CallByName Num.123 Json.354;
|
||||
let Json.106 : List U8 = CallByName List.4 Json.104 Json.353;
|
||||
|
@ -97,10 +86,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12):
|
|||
let Json.325 : List U8 = CallByName List.4 Json.108 Json.326;
|
||||
ret Json.325;
|
||||
|
||||
procedure Json.103 (Json.104, Json.321, #Attr.12):
|
||||
let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
|
||||
inc Json.102;
|
||||
dec #Attr.12;
|
||||
procedure Json.103 (Json.104, Json.321, Json.102):
|
||||
let Json.397 : I32 = 123i64;
|
||||
let Json.396 : U8 = CallByName Num.123 Json.397;
|
||||
let Json.106 : List U8 = CallByName List.4 Json.104 Json.396;
|
||||
|
@ -120,7 +106,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12):
|
|||
procedure Json.105 (Json.323, Json.324):
|
||||
let Json.111 : Str = StructAtIndex 0 Json.324;
|
||||
inc Json.111;
|
||||
let Json.112 : {Str} = StructAtIndex 1 Json.324;
|
||||
let Json.112 : Str = StructAtIndex 1 Json.324;
|
||||
inc Json.112;
|
||||
dec Json.324;
|
||||
let Json.109 : List U8 = StructAtIndex 0 Json.323;
|
||||
|
@ -159,7 +145,7 @@ procedure Json.105 (Json.323, Json.324):
|
|||
procedure Json.105 (Json.323, Json.324):
|
||||
let Json.111 : Str = StructAtIndex 0 Json.324;
|
||||
inc Json.111;
|
||||
let Json.112 : {Str} = StructAtIndex 1 Json.324;
|
||||
let Json.112 : Str = StructAtIndex 1 Json.324;
|
||||
inc Json.112;
|
||||
dec Json.324;
|
||||
let Json.109 : List U8 = StructAtIndex 0 Json.323;
|
||||
|
@ -196,24 +182,18 @@ procedure Json.105 (Json.323, Json.324):
|
|||
jump Json.378 Json.113;
|
||||
|
||||
procedure Json.18 (Json.86):
|
||||
let Json.365 : {Str} = Struct {Json.86};
|
||||
let Json.364 : {Str} = CallByName Encode.22 Json.365;
|
||||
let Json.364 : Str = CallByName Encode.22 Json.86;
|
||||
ret Json.364;
|
||||
|
||||
procedure Json.20 (Json.102):
|
||||
let Json.320 : {List {Str, {Str}}} = Struct {Json.102};
|
||||
let Json.319 : {List {Str, {Str}}} = CallByName Encode.22 Json.320;
|
||||
let Json.319 : List {Str, Str} = CallByName Encode.22 Json.102;
|
||||
ret Json.319;
|
||||
|
||||
procedure Json.20 (Json.102):
|
||||
let Json.362 : {List {Str, {Str}}} = Struct {Json.102};
|
||||
let Json.361 : {List {Str, {Str}}} = CallByName Encode.22 Json.362;
|
||||
let Json.361 : List {Str, Str} = CallByName Encode.22 Json.102;
|
||||
ret Json.361;
|
||||
|
||||
procedure Json.87 (Json.88, Json.366, #Attr.12):
|
||||
let Json.86 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc Json.86;
|
||||
dec #Attr.12;
|
||||
procedure Json.87 (Json.88, Json.366, Json.86):
|
||||
let Json.406 : I32 = 34i64;
|
||||
let Json.405 : U8 = CallByName Num.123 Json.406;
|
||||
let Json.403 : List U8 = CallByName List.4 Json.88 Json.405;
|
||||
|
@ -224,21 +204,18 @@ procedure Json.87 (Json.88, Json.366, #Attr.12):
|
|||
let Json.399 : List U8 = CallByName List.4 Json.400 Json.401;
|
||||
ret Json.399;
|
||||
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
procedure List.133 (List.134, List.135, List.132):
|
||||
let List.434 : {List U8, U64} = CallByName Json.105 List.134 List.135;
|
||||
let List.433 : [C [], C {List U8, U64}] = TagId(1) List.434;
|
||||
ret List.433;
|
||||
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
procedure List.133 (List.134, List.135, List.132):
|
||||
let List.515 : {List U8, U64} = CallByName Json.105 List.134 List.135;
|
||||
let List.514 : [C [], C {List U8, U64}] = TagId(1) List.515;
|
||||
ret List.514;
|
||||
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.411 : {{}} = Struct {List.132};
|
||||
let List.405 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.411;
|
||||
let List.405 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132;
|
||||
let List.408 : U8 = 1i64;
|
||||
let List.409 : U8 = GetTagId List.405;
|
||||
let List.410 : Int1 = lowlevel Eq List.408 List.409;
|
||||
|
@ -254,8 +231,7 @@ procedure List.18 (List.130, List.131, List.132):
|
|||
ret List.407;
|
||||
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.491 : {{}} = Struct {List.132};
|
||||
let List.485 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.491;
|
||||
let List.485 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132;
|
||||
let List.488 : U8 = 1i64;
|
||||
let List.489 : U8 = GetTagId List.485;
|
||||
let List.490 : Int1 = lowlevel Eq List.488 List.489;
|
||||
|
@ -289,11 +265,11 @@ procedure List.6 (#Attr.2):
|
|||
ret List.494;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.432 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let List.432 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.432;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.513 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let List.513 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.513;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
|
@ -328,7 +304,7 @@ procedure List.86 (List.448, List.449, List.450, List.451, List.452):
|
|||
joinpoint List.420 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.422 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.422 then
|
||||
let List.431 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.431 : {Str, Str} = CallByName List.66 List.364 List.367;
|
||||
let List.423 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.431 List.366;
|
||||
let List.428 : U8 = 1i64;
|
||||
let List.429 : U8 = GetTagId List.423;
|
||||
|
@ -355,7 +331,7 @@ procedure List.86 (List.529, List.530, List.531, List.532, List.533):
|
|||
joinpoint List.501 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.503 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.503 then
|
||||
let List.512 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.512 : {Str, Str} = CallByName List.66 List.364 List.367;
|
||||
let List.504 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.512 List.366;
|
||||
let List.509 : U8 = 1i64;
|
||||
let List.510 : U8 = GetTagId List.504;
|
||||
|
@ -399,31 +375,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.285;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.212 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.212;
|
||||
let Str.219 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.219;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.204;
|
||||
let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.211;
|
||||
|
||||
procedure Str.9 (Str.69):
|
||||
let Str.202 : U64 = 0i64;
|
||||
let Str.203 : U64 = CallByName List.6 Str.69;
|
||||
let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203;
|
||||
let Str.199 : Int1 = StructAtIndex 2 Str.70;
|
||||
if Str.199 then
|
||||
let Str.201 : Str = StructAtIndex 1 Str.70;
|
||||
inc Str.201;
|
||||
let Str.209 : U64 = 0i64;
|
||||
let Str.210 : U64 = CallByName List.6 Str.69;
|
||||
let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210;
|
||||
let Str.206 : Int1 = StructAtIndex 2 Str.70;
|
||||
if Str.206 then
|
||||
let Str.208 : Str = StructAtIndex 1 Str.70;
|
||||
inc Str.208;
|
||||
dec Str.70;
|
||||
let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201;
|
||||
ret Str.200;
|
||||
let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208;
|
||||
ret Str.207;
|
||||
else
|
||||
let Str.197 : U8 = StructAtIndex 3 Str.70;
|
||||
let Str.198 : U64 = StructAtIndex 0 Str.70;
|
||||
let Str.204 : U8 = StructAtIndex 3 Str.70;
|
||||
let Str.205 : U64 = StructAtIndex 0 Str.70;
|
||||
dec Str.70;
|
||||
let Str.196 : {U64, U8} = Struct {Str.198, Str.197};
|
||||
let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196;
|
||||
ret Str.195;
|
||||
let Str.203 : {U64, U8} = Struct {Str.205, Str.204};
|
||||
let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203;
|
||||
ret Str.202;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.12 : Str = "bar";
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
procedure #Derived.0 (#Derived.1):
|
||||
let #Derived_gen.1 : {Str} = Struct {#Derived.1};
|
||||
let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1;
|
||||
let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1;
|
||||
ret #Derived_gen.0;
|
||||
|
||||
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
|
||||
let #Derived.1 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc #Derived.1;
|
||||
dec #Attr.12;
|
||||
procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1):
|
||||
let #Derived_gen.7 : Str = "a";
|
||||
let #Derived_gen.8 : {Str} = CallByName Json.18 #Derived.1;
|
||||
let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.7, #Derived_gen.8};
|
||||
let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6];
|
||||
let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.5;
|
||||
let #Derived_gen.8 : Str = CallByName Json.18 #Derived.1;
|
||||
let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8};
|
||||
let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6];
|
||||
let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5;
|
||||
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4;
|
||||
ret #Derived_gen.3;
|
||||
|
||||
|
@ -38,7 +34,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96):
|
|||
|
||||
procedure Encode.25 (Encode.100, Encode.101):
|
||||
let Encode.104 : List U8 = Array [];
|
||||
let Encode.105 : {Str} = CallByName #Derived.0 Encode.100;
|
||||
let Encode.105 : Str = CallByName #Derived.0 Encode.100;
|
||||
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
|
||||
ret Encode.103;
|
||||
|
||||
|
@ -46,10 +42,7 @@ procedure Json.1 ():
|
|||
let Json.318 : {} = Struct {};
|
||||
ret Json.318;
|
||||
|
||||
procedure Json.103 (Json.104, Json.321, #Attr.12):
|
||||
let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
|
||||
inc Json.102;
|
||||
dec #Attr.12;
|
||||
procedure Json.103 (Json.104, Json.321, Json.102):
|
||||
let Json.357 : I32 = 123i64;
|
||||
let Json.356 : U8 = CallByName Num.123 Json.357;
|
||||
let Json.106 : List U8 = CallByName List.4 Json.104 Json.356;
|
||||
|
@ -69,7 +62,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12):
|
|||
procedure Json.105 (Json.326, Json.327):
|
||||
let Json.111 : Str = StructAtIndex 0 Json.327;
|
||||
inc Json.111;
|
||||
let Json.112 : {Str} = StructAtIndex 1 Json.327;
|
||||
let Json.112 : Str = StructAtIndex 1 Json.327;
|
||||
inc Json.112;
|
||||
dec Json.327;
|
||||
let Json.109 : List U8 = StructAtIndex 0 Json.326;
|
||||
|
@ -106,19 +99,14 @@ procedure Json.105 (Json.326, Json.327):
|
|||
jump Json.338 Json.113;
|
||||
|
||||
procedure Json.18 (Json.86):
|
||||
let Json.323 : {Str} = Struct {Json.86};
|
||||
let Json.322 : {Str} = CallByName Encode.22 Json.323;
|
||||
let Json.322 : Str = CallByName Encode.22 Json.86;
|
||||
ret Json.322;
|
||||
|
||||
procedure Json.20 (Json.102):
|
||||
let Json.320 : {List {Str, {Str}}} = Struct {Json.102};
|
||||
let Json.319 : {List {Str, {Str}}} = CallByName Encode.22 Json.320;
|
||||
let Json.319 : List {Str, Str} = CallByName Encode.22 Json.102;
|
||||
ret Json.319;
|
||||
|
||||
procedure Json.87 (Json.88, Json.324, #Attr.12):
|
||||
let Json.86 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc Json.86;
|
||||
dec #Attr.12;
|
||||
procedure Json.87 (Json.88, Json.324, Json.86):
|
||||
let Json.366 : I32 = 34i64;
|
||||
let Json.365 : U8 = CallByName Num.123 Json.366;
|
||||
let Json.363 : List U8 = CallByName List.4 Json.88 Json.365;
|
||||
|
@ -129,15 +117,13 @@ procedure Json.87 (Json.88, Json.324, #Attr.12):
|
|||
let Json.359 : List U8 = CallByName List.4 Json.360 Json.361;
|
||||
ret Json.359;
|
||||
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
procedure List.133 (List.134, List.135, List.132):
|
||||
let List.441 : {List U8, U64} = CallByName Json.105 List.134 List.135;
|
||||
let List.440 : [C [], C {List U8, U64}] = TagId(1) List.441;
|
||||
ret List.440;
|
||||
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.417 : {{}} = Struct {List.132};
|
||||
let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.417;
|
||||
let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132;
|
||||
let List.414 : U8 = 1i64;
|
||||
let List.415 : U8 = GetTagId List.411;
|
||||
let List.416 : Int1 = lowlevel Eq List.414 List.415;
|
||||
|
@ -167,7 +153,7 @@ procedure List.6 (#Attr.2):
|
|||
ret List.420;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.439 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let List.439 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.439;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
|
@ -196,7 +182,7 @@ procedure List.86 (List.455, List.456, List.457, List.458, List.459):
|
|||
joinpoint List.427 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.429 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.429 then
|
||||
let List.438 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.438 : {Str, Str} = CallByName List.66 List.364 List.367;
|
||||
let List.430 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.438 List.366;
|
||||
let List.435 : U8 = 1i64;
|
||||
let List.436 : U8 = GetTagId List.430;
|
||||
|
@ -240,31 +226,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.266;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.210;
|
||||
let Str.217 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.217;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.204;
|
||||
let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.211;
|
||||
|
||||
procedure Str.9 (Str.69):
|
||||
let Str.202 : U64 = 0i64;
|
||||
let Str.203 : U64 = CallByName List.6 Str.69;
|
||||
let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203;
|
||||
let Str.199 : Int1 = StructAtIndex 2 Str.70;
|
||||
if Str.199 then
|
||||
let Str.201 : Str = StructAtIndex 1 Str.70;
|
||||
inc Str.201;
|
||||
let Str.209 : U64 = 0i64;
|
||||
let Str.210 : U64 = CallByName List.6 Str.69;
|
||||
let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210;
|
||||
let Str.206 : Int1 = StructAtIndex 2 Str.70;
|
||||
if Str.206 then
|
||||
let Str.208 : Str = StructAtIndex 1 Str.70;
|
||||
inc Str.208;
|
||||
dec Str.70;
|
||||
let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201;
|
||||
ret Str.200;
|
||||
let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208;
|
||||
ret Str.207;
|
||||
else
|
||||
let Str.197 : U8 = StructAtIndex 3 Str.70;
|
||||
let Str.198 : U64 = StructAtIndex 0 Str.70;
|
||||
let Str.204 : U8 = StructAtIndex 3 Str.70;
|
||||
let Str.205 : U64 = StructAtIndex 0 Str.70;
|
||||
dec Str.70;
|
||||
let Str.196 : {U64, U8} = Struct {Str.198, Str.197};
|
||||
let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196;
|
||||
ret Str.195;
|
||||
let Str.203 : {U64, U8} = Struct {Str.205, Str.204};
|
||||
let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203;
|
||||
ret Str.202;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 : Str = "foo";
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
procedure #Derived.0 (#Derived.1):
|
||||
let #Derived_gen.1 : {{Str, Str}} = Struct {#Derived.1};
|
||||
let #Derived_gen.0 : {{Str, Str}} = CallByName Encode.22 #Derived_gen.1;
|
||||
let #Derived_gen.0 : {Str, Str} = CallByName Encode.22 #Derived.1;
|
||||
ret #Derived_gen.0;
|
||||
|
||||
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
|
||||
let #Derived.1 : {Str, Str} = StructAtIndex 0 #Attr.12;
|
||||
inc #Derived.1;
|
||||
dec #Attr.12;
|
||||
procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1):
|
||||
let #Derived_gen.11 : Str = "a";
|
||||
let #Derived_gen.13 : Str = StructAtIndex 0 #Derived.1;
|
||||
inc #Derived_gen.13;
|
||||
let #Derived_gen.12 : {Str} = CallByName Json.18 #Derived_gen.13;
|
||||
let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.11, #Derived_gen.12};
|
||||
let #Derived_gen.12 : Str = CallByName Json.18 #Derived_gen.13;
|
||||
let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.11, #Derived_gen.12};
|
||||
let #Derived_gen.8 : Str = "b";
|
||||
let #Derived_gen.10 : Str = StructAtIndex 1 #Derived.1;
|
||||
inc #Derived_gen.10;
|
||||
dec #Derived.1;
|
||||
let #Derived_gen.9 : {Str} = CallByName Json.18 #Derived_gen.10;
|
||||
let #Derived_gen.7 : {Str, {Str}} = Struct {#Derived_gen.8, #Derived_gen.9};
|
||||
let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6, #Derived_gen.7];
|
||||
let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.5;
|
||||
let #Derived_gen.9 : Str = CallByName Json.18 #Derived_gen.10;
|
||||
let #Derived_gen.7 : {Str, Str} = Struct {#Derived_gen.8, #Derived_gen.9};
|
||||
let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6, #Derived_gen.7];
|
||||
let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5;
|
||||
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4;
|
||||
ret #Derived_gen.3;
|
||||
|
||||
|
@ -46,7 +42,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96):
|
|||
|
||||
procedure Encode.25 (Encode.100, Encode.101):
|
||||
let Encode.104 : List U8 = Array [];
|
||||
let Encode.105 : {{Str, Str}} = CallByName #Derived.0 Encode.100;
|
||||
let Encode.105 : {Str, Str} = CallByName #Derived.0 Encode.100;
|
||||
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
|
||||
ret Encode.103;
|
||||
|
||||
|
@ -54,10 +50,7 @@ procedure Json.1 ():
|
|||
let Json.318 : {} = Struct {};
|
||||
ret Json.318;
|
||||
|
||||
procedure Json.103 (Json.104, Json.321, #Attr.12):
|
||||
let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
|
||||
inc Json.102;
|
||||
dec #Attr.12;
|
||||
procedure Json.103 (Json.104, Json.321, Json.102):
|
||||
let Json.360 : I32 = 123i64;
|
||||
let Json.359 : U8 = CallByName Num.123 Json.360;
|
||||
let Json.106 : List U8 = CallByName List.4 Json.104 Json.359;
|
||||
|
@ -77,7 +70,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12):
|
|||
procedure Json.105 (Json.329, Json.330):
|
||||
let Json.111 : Str = StructAtIndex 0 Json.330;
|
||||
inc Json.111;
|
||||
let Json.112 : {Str} = StructAtIndex 1 Json.330;
|
||||
let Json.112 : Str = StructAtIndex 1 Json.330;
|
||||
inc Json.112;
|
||||
dec Json.330;
|
||||
let Json.109 : List U8 = StructAtIndex 0 Json.329;
|
||||
|
@ -114,19 +107,14 @@ procedure Json.105 (Json.329, Json.330):
|
|||
jump Json.341 Json.113;
|
||||
|
||||
procedure Json.18 (Json.86):
|
||||
let Json.326 : {Str} = Struct {Json.86};
|
||||
let Json.325 : {Str} = CallByName Encode.22 Json.326;
|
||||
let Json.325 : Str = CallByName Encode.22 Json.86;
|
||||
ret Json.325;
|
||||
|
||||
procedure Json.20 (Json.102):
|
||||
let Json.320 : {List {Str, {Str}}} = Struct {Json.102};
|
||||
let Json.319 : {List {Str, {Str}}} = CallByName Encode.22 Json.320;
|
||||
let Json.319 : List {Str, Str} = CallByName Encode.22 Json.102;
|
||||
ret Json.319;
|
||||
|
||||
procedure Json.87 (Json.88, Json.324, #Attr.12):
|
||||
let Json.86 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc Json.86;
|
||||
dec #Attr.12;
|
||||
procedure Json.87 (Json.88, Json.324, Json.86):
|
||||
let Json.369 : I32 = 34i64;
|
||||
let Json.368 : U8 = CallByName Num.123 Json.369;
|
||||
let Json.366 : List U8 = CallByName List.4 Json.88 Json.368;
|
||||
|
@ -137,15 +125,13 @@ procedure Json.87 (Json.88, Json.324, #Attr.12):
|
|||
let Json.362 : List U8 = CallByName List.4 Json.363 Json.364;
|
||||
ret Json.362;
|
||||
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
procedure List.133 (List.134, List.135, List.132):
|
||||
let List.441 : {List U8, U64} = CallByName Json.105 List.134 List.135;
|
||||
let List.440 : [C [], C {List U8, U64}] = TagId(1) List.441;
|
||||
ret List.440;
|
||||
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.417 : {{}} = Struct {List.132};
|
||||
let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.417;
|
||||
let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132;
|
||||
let List.414 : U8 = 1i64;
|
||||
let List.415 : U8 = GetTagId List.411;
|
||||
let List.416 : Int1 = lowlevel Eq List.414 List.415;
|
||||
|
@ -175,7 +161,7 @@ procedure List.6 (#Attr.2):
|
|||
ret List.420;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.439 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let List.439 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.439;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
|
@ -204,7 +190,7 @@ procedure List.86 (List.455, List.456, List.457, List.458, List.459):
|
|||
joinpoint List.427 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.429 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.429 then
|
||||
let List.438 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.438 : {Str, Str} = CallByName List.66 List.364 List.367;
|
||||
let List.430 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.438 List.366;
|
||||
let List.435 : U8 = 1i64;
|
||||
let List.436 : U8 = GetTagId List.430;
|
||||
|
@ -248,31 +234,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.266;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.210;
|
||||
let Str.217 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.217;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.204;
|
||||
let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.211;
|
||||
|
||||
procedure Str.9 (Str.69):
|
||||
let Str.202 : U64 = 0i64;
|
||||
let Str.203 : U64 = CallByName List.6 Str.69;
|
||||
let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203;
|
||||
let Str.199 : Int1 = StructAtIndex 2 Str.70;
|
||||
if Str.199 then
|
||||
let Str.201 : Str = StructAtIndex 1 Str.70;
|
||||
inc Str.201;
|
||||
let Str.209 : U64 = 0i64;
|
||||
let Str.210 : U64 = CallByName List.6 Str.69;
|
||||
let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210;
|
||||
let Str.206 : Int1 = StructAtIndex 2 Str.70;
|
||||
if Str.206 then
|
||||
let Str.208 : Str = StructAtIndex 1 Str.70;
|
||||
inc Str.208;
|
||||
dec Str.70;
|
||||
let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201;
|
||||
ret Str.200;
|
||||
let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208;
|
||||
ret Str.207;
|
||||
else
|
||||
let Str.197 : U8 = StructAtIndex 3 Str.70;
|
||||
let Str.198 : U64 = StructAtIndex 0 Str.70;
|
||||
let Str.204 : U8 = StructAtIndex 3 Str.70;
|
||||
let Str.205 : U64 = StructAtIndex 0 Str.70;
|
||||
dec Str.70;
|
||||
let Str.196 : {U64, U8} = Struct {Str.198, Str.197};
|
||||
let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196;
|
||||
ret Str.195;
|
||||
let Str.203 : {U64, U8} = Struct {Str.205, Str.204};
|
||||
let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203;
|
||||
ret Str.202;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 : Str = "foo";
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue