mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge branch 'main' into i5318
Signed-off-by: Ayaz <20735482+ayazhafiz@users.noreply.github.com>
This commit is contained in:
commit
19c04c69c0
34 changed files with 574 additions and 64 deletions
|
@ -698,7 +698,7 @@ fn solve(
|
||||||
// after a LetCon, we must check if any of the variables that we introduced
|
// after a LetCon, we must check if any of the variables that we introduced
|
||||||
// loop back to themselves after solving the ret_constraint
|
// loop back to themselves after solving the ret_constraint
|
||||||
for (symbol, loc_var) in def_vars.iter() {
|
for (symbol, loc_var) in def_vars.iter() {
|
||||||
check_for_infinite_type(subs, problems, *symbol, *loc_var);
|
check_for_infinite_type(subs, pools, problems, *symbol, *loc_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@ -1331,7 +1331,7 @@ fn solve(
|
||||||
*type_index,
|
*type_index,
|
||||||
);
|
);
|
||||||
|
|
||||||
open_tag_union(subs, actual);
|
open_tag_union(subs, pools, actual);
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
@ -1571,7 +1571,7 @@ fn solve(
|
||||||
let almost_eq_snapshot = subs.snapshot();
|
let almost_eq_snapshot = subs.snapshot();
|
||||||
// TODO: turn this on for bidirectional exhaustiveness checking
|
// TODO: turn this on for bidirectional exhaustiveness checking
|
||||||
// open_tag_union(subs, real_var);
|
// open_tag_union(subs, real_var);
|
||||||
open_tag_union(subs, branches_var);
|
open_tag_union(subs, pools, branches_var);
|
||||||
let almost_eq = matches!(
|
let almost_eq = matches!(
|
||||||
unify(
|
unify(
|
||||||
&mut UEnv::new(subs),
|
&mut UEnv::new(subs),
|
||||||
|
@ -1901,7 +1901,7 @@ fn compact_lambdas_and_check_obligations(
|
||||||
awaiting_specialization.union(new_awaiting);
|
awaiting_specialization.union(new_awaiting);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_tag_union(subs: &mut Subs, var: Variable) {
|
fn open_tag_union(subs: &mut Subs, pools: &mut Pools, var: Variable) {
|
||||||
let mut stack = vec![var];
|
let mut stack = vec![var];
|
||||||
while let Some(var) = stack.pop() {
|
while let Some(var) = stack.pop() {
|
||||||
use {Content::*, FlatType::*};
|
use {Content::*, FlatType::*};
|
||||||
|
@ -1910,9 +1910,9 @@ fn open_tag_union(subs: &mut Subs, var: Variable) {
|
||||||
match desc.content {
|
match desc.content {
|
||||||
Structure(TagUnion(tags, ext)) => {
|
Structure(TagUnion(tags, ext)) => {
|
||||||
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext.var()) {
|
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext.var()) {
|
||||||
let new_ext = TagExt::Any(subs.fresh_unnamed_flex_var());
|
let new_ext_var = register(subs, desc.rank, pools, Content::FlexVar(None));
|
||||||
subs.set_rank(new_ext.var(), desc.rank);
|
|
||||||
let new_union = Structure(TagUnion(tags, new_ext));
|
let new_union = Structure(TagUnion(tags, TagExt::Any(new_ext_var)));
|
||||||
subs.set_content(var, new_union);
|
subs.set_content(var, new_union);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3592,6 +3592,7 @@ fn create_union_lambda(
|
||||||
|
|
||||||
fn check_for_infinite_type(
|
fn check_for_infinite_type(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
pools: &mut Pools,
|
||||||
problems: &mut Vec<TypeError>,
|
problems: &mut Vec<TypeError>,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
loc_var: Loc<Variable>,
|
loc_var: Loc<Variable>,
|
||||||
|
@ -3604,7 +3605,9 @@ fn check_for_infinite_type(
|
||||||
for &var in chain.iter().rev() {
|
for &var in chain.iter().rev() {
|
||||||
match *subs.get_content_without_compacting(var) {
|
match *subs.get_content_without_compacting(var) {
|
||||||
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
||||||
subs.mark_tag_union_recursive(var, tags, ext_var);
|
let rec_var = subs.mark_tag_union_recursive(var, tags, ext_var);
|
||||||
|
register_to_pools(subs, rec_var, pools);
|
||||||
|
|
||||||
continue 'next_occurs_check;
|
continue 'next_occurs_check;
|
||||||
}
|
}
|
||||||
Content::LambdaSet(subs::LambdaSet {
|
Content::LambdaSet(subs::LambdaSet {
|
||||||
|
@ -3613,12 +3616,14 @@ fn check_for_infinite_type(
|
||||||
unspecialized,
|
unspecialized,
|
||||||
ambient_function: ambient_function_var,
|
ambient_function: ambient_function_var,
|
||||||
}) => {
|
}) => {
|
||||||
subs.mark_lambda_set_recursive(
|
let rec_var = subs.mark_lambda_set_recursive(
|
||||||
var,
|
var,
|
||||||
solved,
|
solved,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
ambient_function_var,
|
ambient_function_var,
|
||||||
);
|
);
|
||||||
|
register_to_pools(subs, rec_var, pools);
|
||||||
|
|
||||||
continue 'next_occurs_check;
|
continue 'next_occurs_check;
|
||||||
}
|
}
|
||||||
_ => { /* fall through */ }
|
_ => { /* fall through */ }
|
||||||
|
@ -4495,3 +4500,8 @@ fn register_with_known_var(
|
||||||
|
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn register_to_pools(subs: &Subs, var: Variable, pools: &mut Pools) {
|
||||||
|
pools.get_mut(subs.get_rank(var)).push(var);
|
||||||
|
}
|
||||||
|
|
|
@ -1512,6 +1512,7 @@ fn opaque_assign_to_symbol() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
|
#[ignore = "Blocked on https://github.com/roc-lang/roc/issues/5354"]
|
||||||
fn issue_2777_default_branch_codegen() {
|
fn issue_2777_default_branch_codegen() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
202
crates/compiler/test_mono/generated/issue_4770.txt
Normal file
202
crates/compiler/test_mono/generated/issue_4770.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
procedure Bool.1 ():
|
||||||
|
let Bool.25 : Int1 = false;
|
||||||
|
ret Bool.25;
|
||||||
|
|
||||||
|
procedure Bool.2 ():
|
||||||
|
let Bool.24 : Int1 = true;
|
||||||
|
ret Bool.24;
|
||||||
|
|
||||||
|
procedure List.188 (List.496, List.189, List.187):
|
||||||
|
let List.526 : Int1 = CallByName Test.1 List.189;
|
||||||
|
if List.526 then
|
||||||
|
let List.528 : {} = Struct {};
|
||||||
|
let List.527 : [C {}, C {}] = TagId(1) List.528;
|
||||||
|
ret List.527;
|
||||||
|
else
|
||||||
|
let List.525 : {} = Struct {};
|
||||||
|
let List.524 : [C {}, C {}] = TagId(0) List.525;
|
||||||
|
ret List.524;
|
||||||
|
|
||||||
|
procedure List.23 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
|
let List.529 : List {[<r>C I64, C List *self], [<r>C I64, C List *self]} = lowlevel ListMap2 { xs: `#Attr.#arg1`, ys: `#Attr.#arg2` } #Attr.2 #Attr.3 Test.15 #Attr.4;
|
||||||
|
decref #Attr.3;
|
||||||
|
decref #Attr.2;
|
||||||
|
ret List.529;
|
||||||
|
|
||||||
|
procedure List.56 (List.186, List.187):
|
||||||
|
let List.505 : {} = Struct {};
|
||||||
|
let List.497 : [C {}, C {}] = CallByName List.92 List.186 List.505 List.187;
|
||||||
|
let List.502 : U8 = 1i64;
|
||||||
|
let List.503 : U8 = GetTagId List.497;
|
||||||
|
let List.504 : Int1 = lowlevel Eq List.502 List.503;
|
||||||
|
if List.504 then
|
||||||
|
let List.498 : Int1 = CallByName Bool.2;
|
||||||
|
ret List.498;
|
||||||
|
else
|
||||||
|
let List.499 : Int1 = CallByName Bool.1;
|
||||||
|
ret List.499;
|
||||||
|
|
||||||
|
procedure List.6 (#Attr.2):
|
||||||
|
let List.495 : U64 = lowlevel ListLen #Attr.2;
|
||||||
|
ret List.495;
|
||||||
|
|
||||||
|
procedure List.6 (#Attr.2):
|
||||||
|
let List.523 : U64 = lowlevel ListLen #Attr.2;
|
||||||
|
ret List.523;
|
||||||
|
|
||||||
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
|
let List.522 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
|
ret List.522;
|
||||||
|
|
||||||
|
procedure List.80 (List.534, List.535, List.536, List.537, List.538):
|
||||||
|
joinpoint List.510 List.433 List.434 List.435 List.436 List.437:
|
||||||
|
let List.512 : Int1 = CallByName Num.22 List.436 List.437;
|
||||||
|
if List.512 then
|
||||||
|
let List.521 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = CallByName List.66 List.433 List.436;
|
||||||
|
inc List.521;
|
||||||
|
let List.513 : [C {}, C {}] = CallByName List.188 List.434 List.521 List.435;
|
||||||
|
let List.518 : U8 = 1i64;
|
||||||
|
let List.519 : U8 = GetTagId List.513;
|
||||||
|
let List.520 : Int1 = lowlevel Eq List.518 List.519;
|
||||||
|
if List.520 then
|
||||||
|
let List.438 : {} = UnionAtIndex (Id 1) (Index 0) List.513;
|
||||||
|
let List.516 : U64 = 1i64;
|
||||||
|
let List.515 : U64 = CallByName Num.19 List.436 List.516;
|
||||||
|
jump List.510 List.433 List.438 List.435 List.515 List.437;
|
||||||
|
else
|
||||||
|
dec List.433;
|
||||||
|
let List.439 : {} = UnionAtIndex (Id 0) (Index 0) List.513;
|
||||||
|
let List.517 : [C {}, C {}] = TagId(0) List.439;
|
||||||
|
ret List.517;
|
||||||
|
else
|
||||||
|
dec List.433;
|
||||||
|
let List.511 : [C {}, C {}] = TagId(1) List.434;
|
||||||
|
ret List.511;
|
||||||
|
in
|
||||||
|
jump List.510 List.534 List.535 List.536 List.537 List.538;
|
||||||
|
|
||||||
|
procedure List.92 (List.430, List.431, List.432):
|
||||||
|
let List.508 : U64 = 0i64;
|
||||||
|
let List.509 : U64 = CallByName List.6 List.430;
|
||||||
|
let List.507 : [C {}, C {}] = CallByName List.80 List.430 List.431 List.432 List.508 List.509;
|
||||||
|
ret List.507;
|
||||||
|
|
||||||
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
|
let Num.280 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
|
ret Num.280;
|
||||||
|
|
||||||
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
|
let Num.278 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
|
ret Num.278;
|
||||||
|
|
||||||
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
|
let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
|
ret Num.281;
|
||||||
|
|
||||||
|
procedure Test.1 (Test.77):
|
||||||
|
joinpoint Test.26 Test.6:
|
||||||
|
let Test.65 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
|
let Test.66 : U8 = 0i64;
|
||||||
|
let Test.67 : U8 = GetTagId Test.65;
|
||||||
|
let Test.68 : Int1 = lowlevel Eq Test.66 Test.67;
|
||||||
|
if Test.68 then
|
||||||
|
let Test.57 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
|
inc Test.57;
|
||||||
|
let Test.58 : U8 = 0i64;
|
||||||
|
let Test.59 : U8 = GetTagId Test.57;
|
||||||
|
let #Derived_gen.1 : [<r>C I64, C List *self] = Reset { symbol: Test.57, id: UpdateModeId { id: 1 } };
|
||||||
|
let Test.60 : Int1 = lowlevel Eq Test.58 Test.59;
|
||||||
|
if Test.60 then
|
||||||
|
decref #Derived_gen.1;
|
||||||
|
let Test.50 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
|
let Test.8 : I64 = UnionAtIndex (Id 0) (Index 0) Test.50;
|
||||||
|
let Test.49 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
|
dec Test.50;
|
||||||
|
let Test.10 : I64 = UnionAtIndex (Id 0) (Index 0) Test.49;
|
||||||
|
joinpoint #Derived_gen.7:
|
||||||
|
let Test.27 : Int1 = CallByName Num.22 Test.8 Test.10;
|
||||||
|
ret Test.27;
|
||||||
|
in
|
||||||
|
let #Derived_gen.8 : Int1 = lowlevel RefCountIsUnique Test.49;
|
||||||
|
if #Derived_gen.8 then
|
||||||
|
decref Test.49;
|
||||||
|
jump #Derived_gen.7;
|
||||||
|
else
|
||||||
|
decref Test.49;
|
||||||
|
jump #Derived_gen.7;
|
||||||
|
else
|
||||||
|
let Test.39 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
|
let Test.42 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
|
let Test.41 : List [<r>C I64, C List *self] = Array [Test.42];
|
||||||
|
let Test.40 : [<r>C I64, C List *self] = Reuse #Derived_gen.1 UpdateModeId { id: 1 } TagId(1) Test.41;
|
||||||
|
let Test.38 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.39, Test.40};
|
||||||
|
jump Test.26 Test.38;
|
||||||
|
else
|
||||||
|
let Test.61 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
|
inc Test.61;
|
||||||
|
let Test.62 : U8 = 1i64;
|
||||||
|
let Test.63 : U8 = GetTagId Test.61;
|
||||||
|
let #Derived_gen.4 : [<r>C I64, C List *self] = Reset { symbol: Test.61, id: UpdateModeId { id: 4 } };
|
||||||
|
let Test.64 : Int1 = lowlevel Eq Test.62 Test.63;
|
||||||
|
if Test.64 then
|
||||||
|
decref #Derived_gen.4;
|
||||||
|
let Test.52 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
|
let Test.12 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.52;
|
||||||
|
inc Test.12;
|
||||||
|
let Test.51 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
|
dec Test.52;
|
||||||
|
let Test.14 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.51;
|
||||||
|
joinpoint #Derived_gen.9:
|
||||||
|
let Test.35 : {} = Struct {};
|
||||||
|
inc Test.12;
|
||||||
|
inc Test.14;
|
||||||
|
let Test.33 : List {[<r>C I64, C List *self], [<r>C I64, C List *self]} = CallByName List.23 Test.12 Test.14 Test.35;
|
||||||
|
let Test.34 : {} = Struct {};
|
||||||
|
let Test.29 : Int1 = CallByName List.56 Test.33 Test.34;
|
||||||
|
if Test.29 then
|
||||||
|
let Test.31 : U64 = CallByName List.6 Test.12;
|
||||||
|
dec Test.12;
|
||||||
|
let Test.32 : U64 = CallByName List.6 Test.14;
|
||||||
|
dec Test.14;
|
||||||
|
let Test.30 : Int1 = CallByName Num.22 Test.31 Test.32;
|
||||||
|
ret Test.30;
|
||||||
|
else
|
||||||
|
dec Test.12;
|
||||||
|
dec Test.14;
|
||||||
|
let Test.28 : Int1 = CallByName Bool.1;
|
||||||
|
ret Test.28;
|
||||||
|
in
|
||||||
|
let #Derived_gen.10 : Int1 = lowlevel RefCountIsUnique Test.51;
|
||||||
|
if #Derived_gen.10 then
|
||||||
|
decref Test.51;
|
||||||
|
jump #Derived_gen.9;
|
||||||
|
else
|
||||||
|
inc Test.14;
|
||||||
|
decref Test.51;
|
||||||
|
jump #Derived_gen.9;
|
||||||
|
else
|
||||||
|
let Test.48 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
|
let Test.47 : List [<r>C I64, C List *self] = Array [Test.48];
|
||||||
|
let Test.45 : [<r>C I64, C List *self] = Reuse #Derived_gen.4 UpdateModeId { id: 4 } TagId(1) Test.47;
|
||||||
|
let Test.46 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
|
let Test.44 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.45, Test.46};
|
||||||
|
jump Test.26 Test.44;
|
||||||
|
in
|
||||||
|
jump Test.26 Test.77;
|
||||||
|
|
||||||
|
procedure Test.15 (Test.16, Test.17):
|
||||||
|
let Test.36 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.16, Test.17};
|
||||||
|
ret Test.36;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.76 : I64 = 10i64;
|
||||||
|
let Test.75 : [<r>C I64, C List *self] = TagId(0) Test.76;
|
||||||
|
let Test.74 : List [<r>C I64, C List *self] = Array [Test.75];
|
||||||
|
let Test.69 : [<r>C I64, C List *self] = TagId(1) Test.74;
|
||||||
|
let Test.73 : I64 = 20i64;
|
||||||
|
let Test.72 : [<r>C I64, C List *self] = TagId(0) Test.73;
|
||||||
|
let Test.71 : List [<r>C I64, C List *self] = Array [Test.72];
|
||||||
|
let Test.70 : [<r>C I64, C List *self] = TagId(1) Test.71;
|
||||||
|
let Test.25 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.69, Test.70};
|
||||||
|
let Test.24 : Int1 = CallByName Test.1 Test.25;
|
||||||
|
ret Test.24;
|
|
@ -2889,6 +2889,30 @@ fn layout_cache_structure_with_multiple_recursive_structures() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn issue_4770() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
isCorrectOrder { left: IsList [IsInteger 10], right: IsList [IsInteger 20] }
|
||||||
|
|
||||||
|
isCorrectOrder = \pair ->
|
||||||
|
when pair is
|
||||||
|
{ left: IsInteger left, right: IsInteger right } -> left < right
|
||||||
|
{ left: IsList l, right: IsList r } ->
|
||||||
|
if List.map2 l r (\left, right -> { left, right }) |> List.all isCorrectOrder then
|
||||||
|
List.len l < List.len r
|
||||||
|
else
|
||||||
|
Bool.false
|
||||||
|
|
||||||
|
{ left: IsList _, right: IsInteger _ } -> isCorrectOrder { left: pair.left, right: IsList [pair.right] }
|
||||||
|
{ left: IsInteger _, right: IsList _ } -> isCorrectOrder { left: IsList [pair.left], right: pair.right }
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[mono_test(allow_type_errors = "true")]
|
#[mono_test(allow_type_errors = "true")]
|
||||||
fn error_on_erroneous_condition() {
|
fn error_on_erroneous_condition() {
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -2898,4 +2922,4 @@ fn error_on_erroneous_condition() {
|
||||||
main = if True then 1 else 2
|
main = if True then 1 else 2
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -257,6 +257,7 @@ pub struct InferOptions {
|
||||||
pub allow_errors: bool,
|
pub allow_errors: bool,
|
||||||
pub print_can_decls: bool,
|
pub print_can_decls: bool,
|
||||||
pub print_only_under_alias: bool,
|
pub print_only_under_alias: bool,
|
||||||
|
pub print_ranks: bool,
|
||||||
pub no_promote: bool,
|
pub no_promote: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,6 +466,7 @@ impl<'a> QueryCtx<'a> {
|
||||||
DebugPrint {
|
DebugPrint {
|
||||||
print_lambda_sets: true,
|
print_lambda_sets: true,
|
||||||
print_only_under_alias: self.options.print_only_under_alias,
|
print_only_under_alias: self.options.print_only_under_alias,
|
||||||
|
print_ranks: self.options.print_ranks,
|
||||||
ignore_polarity: true,
|
ignore_polarity: true,
|
||||||
print_weakened_vars: true,
|
print_weakened_vars: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,6 +59,7 @@ macro_rules! write_parens {
|
||||||
pub struct DebugPrint {
|
pub struct DebugPrint {
|
||||||
pub print_lambda_sets: bool,
|
pub print_lambda_sets: bool,
|
||||||
pub print_only_under_alias: bool,
|
pub print_only_under_alias: bool,
|
||||||
|
pub print_ranks: bool,
|
||||||
pub ignore_polarity: bool,
|
pub ignore_polarity: bool,
|
||||||
pub print_weakened_vars: bool,
|
pub print_weakened_vars: bool,
|
||||||
}
|
}
|
||||||
|
@ -67,6 +68,7 @@ impl DebugPrint {
|
||||||
pub const NOTHING: DebugPrint = DebugPrint {
|
pub const NOTHING: DebugPrint = DebugPrint {
|
||||||
print_lambda_sets: false,
|
print_lambda_sets: false,
|
||||||
print_only_under_alias: false,
|
print_only_under_alias: false,
|
||||||
|
print_ranks: false,
|
||||||
ignore_polarity: false,
|
ignore_polarity: false,
|
||||||
print_weakened_vars: false,
|
print_weakened_vars: false,
|
||||||
};
|
};
|
||||||
|
@ -653,6 +655,10 @@ fn write_content<'a>(
|
||||||
) {
|
) {
|
||||||
use crate::subs::Content::*;
|
use crate::subs::Content::*;
|
||||||
|
|
||||||
|
if env.debug.print_ranks {
|
||||||
|
buf.push_str(&format!("⟨@{:?}⟩", subs.get_rank(var).into_usize()));
|
||||||
|
}
|
||||||
|
|
||||||
match subs.get_content_without_compacting(var) {
|
match subs.get_content_without_compacting(var) {
|
||||||
FlexVar(Some(name_index)) => {
|
FlexVar(Some(name_index)) => {
|
||||||
let name = &subs.field_names[name_index.index as usize];
|
let name = &subs.field_names[name_index.index as usize];
|
||||||
|
|
|
@ -2010,22 +2010,35 @@ impl Subs {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_tag_union_recursive(&mut self, recursive: Variable, tags: UnionTags, ext: TagExt) {
|
/// Returns the new recursion variable, which should be introduced to the environment as
|
||||||
|
/// appropriate.
|
||||||
|
#[must_use]
|
||||||
|
pub fn mark_tag_union_recursive(
|
||||||
|
&mut self,
|
||||||
|
recursive: Variable,
|
||||||
|
tags: UnionTags,
|
||||||
|
ext: TagExt,
|
||||||
|
) -> Variable {
|
||||||
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, tags);
|
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, tags);
|
||||||
|
|
||||||
let new_ext = ext.map(|v| self.explicit_substitute(recursive, rec_var, v));
|
let new_ext = ext.map(|v| self.explicit_substitute(recursive, rec_var, v));
|
||||||
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext);
|
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext);
|
||||||
|
|
||||||
self.set_content(recursive, Content::Structure(flat_type));
|
self.set_content(recursive, Content::Structure(flat_type));
|
||||||
|
|
||||||
|
rec_var
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the new recursion variable, which should be introduced to the environment as
|
||||||
|
/// appropriate.
|
||||||
|
#[must_use]
|
||||||
pub fn mark_lambda_set_recursive(
|
pub fn mark_lambda_set_recursive(
|
||||||
&mut self,
|
&mut self,
|
||||||
recursive: Variable,
|
recursive: Variable,
|
||||||
solved_lambdas: UnionLambdas,
|
solved_lambdas: UnionLambdas,
|
||||||
unspecialized_lambdas: SubsSlice<Uls>,
|
unspecialized_lambdas: SubsSlice<Uls>,
|
||||||
ambient_function_var: Variable,
|
ambient_function_var: Variable,
|
||||||
) {
|
) -> Variable {
|
||||||
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, solved_lambdas);
|
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, solved_lambdas);
|
||||||
|
|
||||||
let new_lambda_set = Content::LambdaSet(LambdaSet {
|
let new_lambda_set = Content::LambdaSet(LambdaSet {
|
||||||
|
@ -2036,6 +2049,8 @@ impl Subs {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.set_content(recursive, new_lambda_set);
|
self.set_content(recursive, new_lambda_set);
|
||||||
|
|
||||||
|
rec_var
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_union_recursive_help<L: Label>(
|
fn mark_union_recursive_help<L: Label>(
|
||||||
|
|
|
@ -256,6 +256,7 @@ impl<'a> TestCase<'a> {
|
||||||
match opt.trim() {
|
match opt.trim() {
|
||||||
"allow_errors" => infer_opts.allow_errors = true,
|
"allow_errors" => infer_opts.allow_errors = true,
|
||||||
"print_only_under_alias" => infer_opts.print_only_under_alias = true,
|
"print_only_under_alias" => infer_opts.print_only_under_alias = true,
|
||||||
|
"print_ranks" => infer_opts.print_ranks = true,
|
||||||
other => return Err(format!("unknown infer option: {other:?}").into()),
|
other => return Err(format!("unknown infer option: {other:?}").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,6 @@ app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
main =
|
main =
|
||||||
\x -> when x is
|
\x -> when x is
|
||||||
#^ [A [B]w_a [C]w_b]
|
#^ [A [B]* [C]*]
|
||||||
A B _ -> ""
|
A B _ -> ""
|
||||||
A _ C -> ""
|
A _ C -> ""
|
||||||
|
|
|
@ -2,6 +2,6 @@ app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
main =
|
main =
|
||||||
\x -> when x is
|
\x -> when x is
|
||||||
#^ { a : [A { b : [B]w_a }*]w_b }*
|
#^ { a : [A { b : [B]* }*]* }*
|
||||||
{ a: A { b: B } } -> ""
|
{ a: A { b: B } } -> ""
|
||||||
_ -> ""
|
_ -> ""
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
isCorrectOrder (IsList [IsStr ""])
|
||||||
|
# ^^^^^^^^^^^^^^ [IsList (List a), IsStr Str]w_b as a -[[isCorrectOrder(1)]]-> Bool
|
||||||
|
|
||||||
|
isCorrectOrder = \pair ->
|
||||||
|
#^^^^^^^^^^^^^^{-1} [IsList (List a)]* as a -[[isCorrectOrder(1)]]-> Bool
|
||||||
|
when pair is
|
||||||
|
IsList l -> List.all l isCorrectOrder
|
||||||
|
|
||||||
|
_ -> Bool.false
|
|
@ -0,0 +1,10 @@
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main = isCorrectOrder (IsList [IsStr ""])
|
||||||
|
# ^^^^^^^^^^^^^^ [IsList (List a), IsStr Str] as a -[[isCorrectOrder(1)]]-> Bool
|
||||||
|
|
||||||
|
isCorrectOrder = \pair ->
|
||||||
|
#^^^^^^^^^^^^^^{-1} [IsList (List a), IsStr *] as a -[[isCorrectOrder(1)]]-> Bool
|
||||||
|
when pair is
|
||||||
|
IsList l -> List.all l isCorrectOrder
|
||||||
|
IsStr _ -> isCorrectOrder (IsList [pair])
|
|
@ -1,6 +1,6 @@
|
||||||
app "test" provides [getInfallible] to "./platform"
|
app "test" provides [getInfallible] to "./platform"
|
||||||
|
|
||||||
getInfallible = \result -> when result is
|
getInfallible = \result -> when result is
|
||||||
#^^^^^^^^^^^^^{-1} [Ok a]w_b -[[getInfallible(0)]]-> a
|
#^^^^^^^^^^^^^{-1} [Ok a]* -[[getInfallible(0)]]-> a
|
||||||
Ok x -> x
|
Ok x -> x
|
||||||
_ -> crash "turns out this was fallible"
|
_ -> crash "turns out this was fallible"
|
||||||
|
|
|
@ -1449,8 +1449,8 @@ fn separate_union_lambdas<M: MetaCollector>(
|
||||||
// code generator, we have not yet observed a case where they must
|
// code generator, we have not yet observed a case where they must
|
||||||
// collapsed to the type checker of the surface syntax.
|
// collapsed to the type checker of the surface syntax.
|
||||||
// It is possible this assumption will be invalidated!
|
// It is possible this assumption will be invalidated!
|
||||||
maybe_mark_union_recursive(env, var1);
|
maybe_mark_union_recursive(env, pool, var1);
|
||||||
maybe_mark_union_recursive(env, var2);
|
maybe_mark_union_recursive(env, pool, var2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the two type variables in the closure set are
|
// Check whether the two type variables in the closure set are
|
||||||
|
@ -2721,7 +2721,6 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
initial_ext1: TagExt,
|
initial_ext1: TagExt,
|
||||||
tags2: UnionTags,
|
tags2: UnionTags,
|
||||||
initial_ext2: TagExt,
|
initial_ext2: TagExt,
|
||||||
recursion_var: Rec,
|
|
||||||
) -> Outcome<M> {
|
) -> Outcome<M> {
|
||||||
let (separate, mut ext1, mut ext2) =
|
let (separate, mut ext1, mut ext2) =
|
||||||
separate_union_tags(env.subs, tags1, initial_ext1, tags2, initial_ext2);
|
separate_union_tags(env.subs, tags1, initial_ext1, tags2, initial_ext2);
|
||||||
|
@ -2781,7 +2780,6 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
shared_tags,
|
shared_tags,
|
||||||
OtherTags2::Empty,
|
OtherTags2::Empty,
|
||||||
merge_tag_exts(ext1, ext2),
|
merge_tag_exts(ext1, ext2),
|
||||||
recursion_var,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
shared_tags_outcome.union(ext_outcome);
|
shared_tags_outcome.union(ext_outcome);
|
||||||
|
@ -2818,15 +2816,8 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
|
|
||||||
let combined_ext = ext1.map(|_| extra_tags_in_2);
|
let combined_ext = ext1.map(|_| extra_tags_in_2);
|
||||||
|
|
||||||
let mut shared_tags_outcome = unify_shared_tags(
|
let mut shared_tags_outcome =
|
||||||
env,
|
unify_shared_tags(env, pool, ctx, shared_tags, OtherTags2::Empty, combined_ext);
|
||||||
pool,
|
|
||||||
ctx,
|
|
||||||
shared_tags,
|
|
||||||
OtherTags2::Empty,
|
|
||||||
combined_ext,
|
|
||||||
recursion_var,
|
|
||||||
);
|
|
||||||
|
|
||||||
shared_tags_outcome.union(ext_outcome);
|
shared_tags_outcome.union(ext_outcome);
|
||||||
|
|
||||||
|
@ -2881,15 +2872,8 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
|
|
||||||
let combined_ext = ext2.map(|_| extra_tags_in_1);
|
let combined_ext = ext2.map(|_| extra_tags_in_1);
|
||||||
|
|
||||||
let shared_tags_outcome = unify_shared_tags(
|
let shared_tags_outcome =
|
||||||
env,
|
unify_shared_tags(env, pool, ctx, shared_tags, OtherTags2::Empty, combined_ext);
|
||||||
pool,
|
|
||||||
ctx,
|
|
||||||
shared_tags,
|
|
||||||
OtherTags2::Empty,
|
|
||||||
combined_ext,
|
|
||||||
recursion_var,
|
|
||||||
);
|
|
||||||
total_outcome.union(shared_tags_outcome);
|
total_outcome.union(shared_tags_outcome);
|
||||||
|
|
||||||
if extend_ext_with_uninhabited {
|
if extend_ext_with_uninhabited {
|
||||||
|
@ -2954,8 +2938,7 @@ fn unify_tag_unions<M: MetaCollector>(
|
||||||
|
|
||||||
env.subs.commit_snapshot(snapshot);
|
env.subs.commit_snapshot(snapshot);
|
||||||
|
|
||||||
let shared_tags_outcome =
|
let shared_tags_outcome = unify_shared_tags(env, pool, ctx, shared_tags, other_tags, ext);
|
||||||
unify_shared_tags(env, pool, ctx, shared_tags, other_tags, ext, recursion_var);
|
|
||||||
total_outcome.union(shared_tags_outcome);
|
total_outcome.union(shared_tags_outcome);
|
||||||
total_outcome
|
total_outcome
|
||||||
}
|
}
|
||||||
|
@ -2972,7 +2955,7 @@ enum OtherTags2 {
|
||||||
|
|
||||||
/// Promotes a non-recursive tag union or lambda set to its recursive variant, if it is found to be
|
/// Promotes a non-recursive tag union or lambda set to its recursive variant, if it is found to be
|
||||||
/// recursive.
|
/// recursive.
|
||||||
fn maybe_mark_union_recursive(env: &mut Env, union_var: Variable) {
|
fn maybe_mark_union_recursive(env: &mut Env, pool: &mut Pool, union_var: Variable) {
|
||||||
let subs = &mut env.subs;
|
let subs = &mut env.subs;
|
||||||
'outer: while let Err((_, chain)) = subs.occurs(union_var) {
|
'outer: while let Err((_, chain)) = subs.occurs(union_var) {
|
||||||
// walk the chain till we find a tag union or lambda set, starting from the variable that
|
// walk the chain till we find a tag union or lambda set, starting from the variable that
|
||||||
|
@ -2981,7 +2964,9 @@ fn maybe_mark_union_recursive(env: &mut Env, union_var: Variable) {
|
||||||
let description = subs.get(v);
|
let description = subs.get(v);
|
||||||
match description.content {
|
match description.content {
|
||||||
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
||||||
subs.mark_tag_union_recursive(v, tags, ext_var);
|
let rec_var = subs.mark_tag_union_recursive(v, tags, ext_var);
|
||||||
|
pool.push(rec_var);
|
||||||
|
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
LambdaSet(self::LambdaSet {
|
LambdaSet(self::LambdaSet {
|
||||||
|
@ -2990,7 +2975,14 @@ fn maybe_mark_union_recursive(env: &mut Env, union_var: Variable) {
|
||||||
unspecialized,
|
unspecialized,
|
||||||
ambient_function: ambient_function_var,
|
ambient_function: ambient_function_var,
|
||||||
}) => {
|
}) => {
|
||||||
subs.mark_lambda_set_recursive(v, solved, unspecialized, ambient_function_var);
|
let rec_var = subs.mark_lambda_set_recursive(
|
||||||
|
v,
|
||||||
|
solved,
|
||||||
|
unspecialized,
|
||||||
|
ambient_function_var,
|
||||||
|
);
|
||||||
|
pool.push(rec_var);
|
||||||
|
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
_ => { /* fall through */ }
|
_ => { /* fall through */ }
|
||||||
|
@ -3066,6 +3058,24 @@ fn choose_merged_var(subs: &Subs, var1: Variable, var2: Variable) -> Variable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn find_union_rec(subs: &Subs, ctx: &Context) -> Rec {
|
||||||
|
match (
|
||||||
|
subs.get_content_without_compacting(ctx.first),
|
||||||
|
subs.get_content_without_compacting(ctx.second),
|
||||||
|
) {
|
||||||
|
(Structure(s1), Structure(s2)) => match (s1, s2) {
|
||||||
|
(FlatType::RecursiveTagUnion(l, _, _), FlatType::RecursiveTagUnion(r, _, _)) => {
|
||||||
|
Rec::Both(*l, *r)
|
||||||
|
}
|
||||||
|
(FlatType::RecursiveTagUnion(l, _, _), _) => Rec::Left(*l),
|
||||||
|
(_, FlatType::RecursiveTagUnion(r, _, _)) => Rec::Right(*r),
|
||||||
|
_ => Rec::None,
|
||||||
|
},
|
||||||
|
_ => Rec::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn unify_shared_tags<M: MetaCollector>(
|
fn unify_shared_tags<M: MetaCollector>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
|
@ -3074,7 +3084,6 @@ fn unify_shared_tags<M: MetaCollector>(
|
||||||
shared_tags: Vec<(TagName, (VariableSubsSlice, VariableSubsSlice))>,
|
shared_tags: Vec<(TagName, (VariableSubsSlice, VariableSubsSlice))>,
|
||||||
other_tags: OtherTags2,
|
other_tags: OtherTags2,
|
||||||
ext: TagExt,
|
ext: TagExt,
|
||||||
recursion_var: Rec,
|
|
||||||
) -> Outcome<M> {
|
) -> Outcome<M> {
|
||||||
let mut matching_tags = Vec::default();
|
let mut matching_tags = Vec::default();
|
||||||
let num_shared_tags = shared_tags.len();
|
let num_shared_tags = shared_tags.len();
|
||||||
|
@ -3119,8 +3128,8 @@ fn unify_shared_tags<M: MetaCollector>(
|
||||||
// since we're expanding tag unions to equal depths as described above,
|
// since we're expanding tag unions to equal depths as described above,
|
||||||
// we'll always pass through this branch. So, we promote tag unions to recursive
|
// we'll always pass through this branch. So, we promote tag unions to recursive
|
||||||
// ones here if it turns out they are that.
|
// ones here if it turns out they are that.
|
||||||
maybe_mark_union_recursive(env, actual);
|
maybe_mark_union_recursive(env, pool, actual);
|
||||||
maybe_mark_union_recursive(env, expected);
|
maybe_mark_union_recursive(env, pool, expected);
|
||||||
|
|
||||||
let mut outcome = Outcome::<M>::default();
|
let mut outcome = Outcome::<M>::default();
|
||||||
|
|
||||||
|
@ -3183,6 +3192,13 @@ fn unify_shared_tags<M: MetaCollector>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Look up if either unions are recursive, and if so, what the recursive variable is.
|
||||||
|
//
|
||||||
|
// We wait until we're about to merge the unions to do this, since above, while unifying
|
||||||
|
// payloads, we may have promoted a non-recursive union involved in this unification to
|
||||||
|
// a recursive one.
|
||||||
|
let recursion_var = find_union_rec(env.subs, ctx);
|
||||||
|
|
||||||
let merge_outcome = unify_shared_tags_merge(env, ctx, new_tags, new_ext_var, recursion_var);
|
let merge_outcome = unify_shared_tags_merge(env, ctx, new_tags, new_ext_var, recursion_var);
|
||||||
|
|
||||||
total_outcome.union(merge_outcome);
|
total_outcome.union(merge_outcome);
|
||||||
|
@ -3266,24 +3282,20 @@ fn unify_flat_type<M: MetaCollector>(
|
||||||
}
|
}
|
||||||
|
|
||||||
(TagUnion(tags1, ext1), TagUnion(tags2, ext2)) => {
|
(TagUnion(tags1, ext1), TagUnion(tags2, ext2)) => {
|
||||||
unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, Rec::None)
|
unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2)
|
||||||
}
|
}
|
||||||
|
|
||||||
(RecursiveTagUnion(recursion_var, tags1, ext1), TagUnion(tags2, ext2)) => {
|
(RecursiveTagUnion(recursion_var, tags1, ext1), TagUnion(tags2, ext2)) => {
|
||||||
debug_assert!(is_recursion_var(env.subs, *recursion_var));
|
debug_assert!(is_recursion_var(env.subs, *recursion_var));
|
||||||
// this never happens in type-correct programs, but may happen if there is a type error
|
// this never happens in type-correct programs, but may happen if there is a type error
|
||||||
|
|
||||||
let rec = Rec::Left(*recursion_var);
|
unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2)
|
||||||
|
|
||||||
unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(TagUnion(tags1, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => {
|
(TagUnion(tags1, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => {
|
||||||
debug_assert!(is_recursion_var(env.subs, *recursion_var));
|
debug_assert!(is_recursion_var(env.subs, *recursion_var));
|
||||||
|
|
||||||
let rec = Rec::Right(*recursion_var);
|
unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2)
|
||||||
|
|
||||||
unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(RecursiveTagUnion(rec1, tags1, ext1), RecursiveTagUnion(rec2, tags2, ext2)) => {
|
(RecursiveTagUnion(rec1, tags1, ext1), RecursiveTagUnion(rec2, tags2, ext2)) => {
|
||||||
|
@ -3298,8 +3310,7 @@ fn unify_flat_type<M: MetaCollector>(
|
||||||
env.subs.dbg(*rec2)
|
env.subs.dbg(*rec2)
|
||||||
);
|
);
|
||||||
|
|
||||||
let rec = Rec::Both(*rec1, *rec2);
|
let mut outcome = unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2);
|
||||||
let mut outcome = unify_tag_unions(env, pool, ctx, *tags1, *ext1, *tags2, *ext2, rec);
|
|
||||||
outcome.union(unify_pool(env, pool, *rec1, *rec2, ctx.mode));
|
outcome.union(unify_pool(env, pool, *rec1, *rec2, ctx.mode));
|
||||||
|
|
||||||
outcome
|
outcome
|
||||||
|
@ -3398,7 +3409,7 @@ fn unify_flat_type<M: MetaCollector>(
|
||||||
);
|
);
|
||||||
let tags2 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
let tags2 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
||||||
|
|
||||||
unify_tag_unions(env, pool, ctx, *tags1, *ext1, tags2, *ext2, Rec::None)
|
unify_tag_unions(env, pool, ctx, *tags1, *ext1, tags2, *ext2)
|
||||||
}
|
}
|
||||||
(FunctionOrTagUnion(tag_names, _, ext1), TagUnion(tags2, ext2)) => {
|
(FunctionOrTagUnion(tag_names, _, ext1), TagUnion(tags2, ext2)) => {
|
||||||
let empty_tag_var_slices = SubsSlice::extend_new(
|
let empty_tag_var_slices = SubsSlice::extend_new(
|
||||||
|
@ -3407,7 +3418,7 @@ fn unify_flat_type<M: MetaCollector>(
|
||||||
);
|
);
|
||||||
let tags1 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
let tags1 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
||||||
|
|
||||||
unify_tag_unions(env, pool, ctx, tags1, *ext1, *tags2, *ext2, Rec::None)
|
unify_tag_unions(env, pool, ctx, tags1, *ext1, *tags2, *ext2)
|
||||||
}
|
}
|
||||||
|
|
||||||
(RecursiveTagUnion(recursion_var, tags1, ext1), FunctionOrTagUnion(tag_names, _, ext2)) => {
|
(RecursiveTagUnion(recursion_var, tags1, ext1), FunctionOrTagUnion(tag_names, _, ext2)) => {
|
||||||
|
@ -3419,9 +3430,8 @@ fn unify_flat_type<M: MetaCollector>(
|
||||||
std::iter::repeat(Default::default()).take(tag_names.len()),
|
std::iter::repeat(Default::default()).take(tag_names.len()),
|
||||||
);
|
);
|
||||||
let tags2 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
let tags2 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
||||||
let rec = Rec::Left(*recursion_var);
|
|
||||||
|
|
||||||
unify_tag_unions(env, pool, ctx, *tags1, *ext1, tags2, *ext2, rec)
|
unify_tag_unions(env, pool, ctx, *tags1, *ext1, tags2, *ext2)
|
||||||
}
|
}
|
||||||
|
|
||||||
(FunctionOrTagUnion(tag_names, _, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => {
|
(FunctionOrTagUnion(tag_names, _, ext1), RecursiveTagUnion(recursion_var, tags2, ext2)) => {
|
||||||
|
@ -3432,9 +3442,8 @@ fn unify_flat_type<M: MetaCollector>(
|
||||||
std::iter::repeat(Default::default()).take(tag_names.len()),
|
std::iter::repeat(Default::default()).take(tag_names.len()),
|
||||||
);
|
);
|
||||||
let tags1 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
let tags1 = UnionTags::from_slices(*tag_names, empty_tag_var_slices);
|
||||||
let rec = Rec::Right(*recursion_var);
|
|
||||||
|
|
||||||
unify_tag_unions(env, pool, ctx, tags1, *ext1, *tags2, *ext2, rec)
|
unify_tag_unions(env, pool, ctx, tags1, *ext1, *tags2, *ext2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// these have underscores because they're unused in --release builds
|
// these have underscores because they're unused in --release builds
|
||||||
|
|
|
@ -621,6 +621,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
|
||||||
Content::Structure(FlatType::Record(fields, _)) => {
|
Content::Structure(FlatType::Record(fields, _)) => {
|
||||||
struct_to_ast(env, mem, addr, *fields)
|
struct_to_ast(env, mem, addr, *fields)
|
||||||
}
|
}
|
||||||
|
Content::Structure(FlatType::Tuple(elems,_)) => {
|
||||||
|
struct_to_ast_tuple(env, mem, addr, *elems)
|
||||||
|
}
|
||||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||||
debug_assert_eq!(tags.len(), 1);
|
debug_assert_eq!(tags.len(), 1);
|
||||||
|
|
||||||
|
|
|
@ -1292,3 +1292,15 @@ fn tuple() {
|
||||||
r#"("a", 2) : ( Str, U32 )*"#,
|
r#"("a", 2) : ( Str, U32 )*"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_tuple() {
|
||||||
|
expect_success(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
("a", (2u32, 3u32))
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
r#"("a", (2, 3)) : ( Str, ( U32, U32 )a )a"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
build
|
build
|
||||||
node_modules
|
node_modules
|
||||||
hello.js
|
|
||||||
libhello
|
|
|
@ -1,4 +1,4 @@
|
||||||
platform "typescript-interop"
|
platform "nodejs-interop"
|
||||||
requires {} { main : Str -> Str }
|
requires {} { main : Str -> Str }
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
1
examples/nodejs-interop/wasm/.gitignore
vendored
Normal file
1
examples/nodejs-interop/wasm/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
roc-app.wasm
|
12
examples/nodejs-interop/wasm/README.md
Normal file
12
examples/nodejs-interop/wasm/README.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# NodeJS Interop
|
||||||
|
|
||||||
|
This is an example of calling Roc code from [Node.js](https://nodejs.org/en/).
|
||||||
|
|
||||||
|
You'll need to have [Zig](https://zig-lang.org) installed. Run this from the current directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
roc build --target=wasm32
|
||||||
|
node hello.js
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it!
|
59
examples/nodejs-interop/wasm/hello.js
Normal file
59
examples/nodejs-interop/wasm/hello.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
const fs = require('fs/promises');
|
||||||
|
const { TextDecoder } = require('util');
|
||||||
|
|
||||||
|
async function roc_nodejs_platform_run(wasm_filename, callback) {
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
let wasmMemoryBuffer;
|
||||||
|
let exit_code;
|
||||||
|
|
||||||
|
function js_display_roc_string(str_bytes, str_len) {
|
||||||
|
const utf8_bytes = wasmMemoryBuffer.subarray(str_bytes, str_bytes + str_len);
|
||||||
|
const js_string = decoder.decode(utf8_bytes);
|
||||||
|
callback(js_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
const importObj = {
|
||||||
|
wasi_snapshot_preview1: {
|
||||||
|
proc_exit: (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
console.error(`Exited with code ${code}`);
|
||||||
|
}
|
||||||
|
exit_code = code;
|
||||||
|
},
|
||||||
|
fd_write: (x) => {
|
||||||
|
console.error(`fd_write not supported: ${x}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
js_display_roc_string,
|
||||||
|
roc_panic: (_pointer, _tag_id) => {
|
||||||
|
throw "Roc panicked!";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const module_bytes = await fs.readFile(wasm_filename);
|
||||||
|
const wasm = await WebAssembly.instantiate(module_bytes, importObj);
|
||||||
|
|
||||||
|
wasmMemoryBuffer = new Uint8Array(wasm.instance.exports.memory.buffer);
|
||||||
|
|
||||||
|
try {
|
||||||
|
wasm.instance.exports._start();
|
||||||
|
} catch (e) {
|
||||||
|
const is_ok = e.message === "unreachable" && exit_code === 0;
|
||||||
|
if (!is_ok) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== "undefined") {
|
||||||
|
module.exports = {
|
||||||
|
roc_nodejs_platform_run,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the code with a sample WebAssembly file
|
||||||
|
roc_nodejs_platform_run('./roc-app.wasm', (output) => {
|
||||||
|
console.log(output);
|
||||||
|
});
|
7
examples/nodejs-interop/wasm/main.roc
Normal file
7
examples/nodejs-interop/wasm/main.roc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
app "roc-app"
|
||||||
|
packages { pf: "platform/main.roc" }
|
||||||
|
imports []
|
||||||
|
provides [main] to pf
|
||||||
|
|
||||||
|
main : Str
|
||||||
|
main = "Hello from Roc!"
|
61
examples/nodejs-interop/wasm/platform/host.js
Normal file
61
examples/nodejs-interop/wasm/platform/host.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
async function roc_web_platform_run(wasm_filename, callback) {
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
let memory_bytes;
|
||||||
|
let exit_code;
|
||||||
|
|
||||||
|
function js_display_roc_string(str_bytes, str_len) {
|
||||||
|
const utf8_bytes = memory_bytes.subarray(str_bytes, str_bytes + str_len);
|
||||||
|
const js_string = decoder.decode(utf8_bytes);
|
||||||
|
callback(js_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
const importObj = {
|
||||||
|
wasi_snapshot_preview1: {
|
||||||
|
proc_exit: (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
console.error(`Exited with code ${code}`);
|
||||||
|
}
|
||||||
|
exit_code = code;
|
||||||
|
},
|
||||||
|
fd_write: (x) => {
|
||||||
|
console.error(`fd_write not supported: ${x}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
js_display_roc_string,
|
||||||
|
roc_panic: (_pointer, _tag_id) => {
|
||||||
|
throw "Roc panicked!";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchPromise = fetch(wasm_filename);
|
||||||
|
|
||||||
|
let wasm;
|
||||||
|
if (WebAssembly.instantiateStreaming) {
|
||||||
|
// streaming API has better performance if available
|
||||||
|
// It can start compiling Wasm before it has fetched all of the bytes, so we don't `await` the request!
|
||||||
|
wasm = await WebAssembly.instantiateStreaming(fetchPromise, importObj);
|
||||||
|
} else {
|
||||||
|
const response = await fetchPromise;
|
||||||
|
const module_bytes = await response.arrayBuffer();
|
||||||
|
wasm = await WebAssembly.instantiate(module_bytes, importObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_bytes = new Uint8Array(wasm.instance.exports.memory.buffer);
|
||||||
|
|
||||||
|
try {
|
||||||
|
wasm.instance.exports._start();
|
||||||
|
} catch (e) {
|
||||||
|
const is_ok = e.message === "unreachable" && exit_code === 0;
|
||||||
|
if (!is_ok) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== "undefined") {
|
||||||
|
module.exports = {
|
||||||
|
roc_web_platform_run,
|
||||||
|
};
|
||||||
|
}
|
57
examples/nodejs-interop/wasm/platform/host.zig
Normal file
57
examples/nodejs-interop/wasm/platform/host.zig
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
const str = @import("glue").str;
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const RocStr = str.RocStr;
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
if (builtin.target.cpu.arch != .wasm32) {
|
||||||
|
@compileError("This platform is for WebAssembly only. You need to pass `--target wasm32` to the Roc compiler.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Align = extern struct { a: usize, b: usize };
|
||||||
|
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) anyopaque;
|
||||||
|
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||||
|
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
|
||||||
|
extern fn memcpy(dest: *anyopaque, src: *anyopaque, count: usize) *anyopaque;
|
||||||
|
|
||||||
|
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||||
|
_ = alignment;
|
||||||
|
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||||
|
_ = old_size;
|
||||||
|
_ = alignment;
|
||||||
|
|
||||||
|
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
|
||||||
|
_ = alignment;
|
||||||
|
|
||||||
|
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_memcpy(dest: *anyopaque, src: *anyopaque, count: usize) callconv(.C) void {
|
||||||
|
_ = memcpy(dest, src, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE roc_panic is provided in the JS file, so it can throw an exception
|
||||||
|
|
||||||
|
extern fn roc__mainForHost_1_exposed(*RocStr) void;
|
||||||
|
|
||||||
|
extern fn js_display_roc_string(str_bytes: ?[*]u8, str_len: usize) void;
|
||||||
|
|
||||||
|
pub fn main() u8 {
|
||||||
|
// actually call roc to populate the callresult
|
||||||
|
var callresult = RocStr.empty();
|
||||||
|
roc__mainForHost_1_exposed(&callresult);
|
||||||
|
|
||||||
|
// display the result using JavaScript
|
||||||
|
js_display_roc_string(callresult.asU8ptrMut(), callresult.len());
|
||||||
|
|
||||||
|
callresult.decref();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
9
examples/nodejs-interop/wasm/platform/main.roc
Normal file
9
examples/nodejs-interop/wasm/platform/main.roc
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
platform "wasm-nodejs-example-platform"
|
||||||
|
requires {} { main : Str }
|
||||||
|
exposes []
|
||||||
|
packages {}
|
||||||
|
imports []
|
||||||
|
provides [mainForHost]
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
|
@ -1 +0,0 @@
|
||||||
# TODO
|
|
Loading…
Add table
Add a link
Reference in a new issue