plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
f822aa8
— Peter John
2026-11-05T23:28:10+05:30
Compiler cleanup phase 6: tests in Plum
- compiler/core/tests/core/for_next_test.rs +0 -98
- compiler/core/tests/core/fused_closure_generic_test.rs +0 -28
- compiler/core/tests/core/main.rs +0 -2
- compiler/tests/core/for_next.plum +68 -0
- compiler/tests/core/fused_closure_generic.plum +23 -0
- compiler/tests/tail_calls.plum +23 -0
- compiler/tests/wasm_codegen/e2e_mem/match_arm_rename_still_valid_wasm.plum +22 -0
- compiler/tests/wasm_codegen/e2e_mem/plain_spread_still_copies.plum +15 -0
- compiler/tests/wasm_codegen/e2e_mem/unique_self_field_write.plum +18 -0
- compiler/tests/wasm_codegen/e2e_mem/unique_self_spread_return.plum +15 -0
- compiler/tests/wasm_codegen/e2e_mem/unique_spread_hazard_falls_back_to_copy_1.plum +18 -0
- compiler/tests/wasm_codegen/e2e_mem/unique_spread_hazard_falls_back_to_copy_2.plum +17 -0
- compiler/tests/wasm_codegen/e2e_mem/unique_spread_reassign_local.plum +16 -0
- compiler/tests/wasm_codegen/e2e_mem/unique_spread_sets_fields.plum +15 -0
- compiler/tests/wasm_codegen/e2e_mem/unique_spread_swap_reads_old_values.plum +16 -0
- compiler/tests/wasm_codegen/e2e_records/base_through_embedded_struct.plum +22 -0
- compiler/tests/wasm_codegen/e2e_records/cons_list_length_map_sum.plum +29 -0
- compiler/tests/wasm_codegen/e2e_records/deep_nested_variant_match.plum +20 -0
- compiler/tests/wasm_codegen/e2e_records/embedded_record_standalone_and_unselected_set_by_init.plum +21 -0
- compiler/tests/wasm_codegen/e2e_records/empty_init_with_annotation.plum +24 -0
- compiler/tests/wasm_codegen/e2e_records/enum_init_versus_variant_name.plum +25 -0
- compiler/tests/wasm_codegen/e2e_records/equality_is_identity_of_the_variant_value.plum +17 -0
- compiler/{wasm-codegen/tests/wasm_codegen/e2e_records/empty_record_embed.rs → tests/wasm_codegen/e2e_records/field_less_struct_embedding_field_less_base_dispatches_in_a_slice.plum} +10 -13
- compiler/tests/wasm_codegen/e2e_records/fixed_param_init.plum +19 -0
- compiler/tests/wasm_codegen/e2e_records/fresh_list_per_construction.plum +27 -0
- compiler/tests/wasm_codegen/e2e_records/generic_inferred_from_args.plum +25 -0
- compiler/tests/wasm_codegen/e2e_records/inherited_fields_methods_and_init_order.plum +26 -0
- compiler/tests/wasm_codegen/e2e_records/match_variant_fields.plum +17 -0
- compiler/tests/wasm_codegen/e2e_records/million_cells_built_and_mapped_in_constant_stack.plum +24 -0
- compiler/tests/wasm_codegen/e2e_records/nested_enum_match.plum +23 -0
- compiler/tests/wasm_codegen/e2e_records/nested_literal_construction.plum +28 -0
- compiler/tests/wasm_codegen/e2e_records/override_calls_original.plum +24 -0
- compiler/tests/wasm_codegen/e2e_records/reassign_enum_local_across_variants.plum +33 -0
- compiler/tests/wasm_codegen/e2e_records/rest_runs_after_build_and_nested_construction.plum +22 -0
- compiler/tests/wasm_codegen/e2e_records/ship_example_values.plum +26 -0
- compiler/tests/wasm_codegen/e2e_records/through_a_base_value.plum +24 -0
- compiler/tests/wasm_codegen/e2e_records/tree_insert_and_size.plum +20 -0
- compiler/tests/wasm_codegen/e2e_records/variant_name_wins.plum +24 -0
- compiler/tests/wasm_codegen/e2e_tail/big_link_built_and_summed.plum +17 -0
- compiler/tests/wasm_codegen/e2e_tail/break_in_else_if_chain_in_while.plum +20 -0
- compiler/tests/wasm_codegen/e2e_tail/break_in_if_in_match_arm_in_while.plum +30 -0
- compiler/tests/wasm_codegen/e2e_tail/break_in_match_arm_direct.plum +37 -0
- compiler/tests/wasm_codegen/e2e_tail/closure_body_tail_call_million.plum +12 -0
- compiler/tests/wasm_codegen/e2e_tail/closure_captures_iteration_value.plum +14 -0
- compiler/tests/wasm_codegen/e2e_tail/closure_not_recursive_unchanged.plum +10 -0
- compiler/tests/wasm_codegen/e2e_tail/closure_tail_call_runs.plum +14 -0
- compiler/tests/wasm_codegen/e2e_tail/continue_in_if_in_match_arm_in_while.plum +33 -0
- compiler/tests/wasm_codegen/e2e_tail/method_self_tail.plum +14 -0
- compiler/tests/wasm_codegen/e2e_tail/mutual_tail_million.plum +14 -0
- compiler/tests/wasm_codegen/e2e_tail/nested_loops_with_match_in_inner.plum +39 -0
- compiler/tests/wasm_codegen/e2e_tail/non_tail_union_wrap_not_tail.plum +17 -0
- compiler/tests/wasm_codegen/e2e_tail/param_self_tail.plum +17 -0
- compiler/tests/wasm_codegen/e2e_tail/question_in_tail_not_tail_call.plum +16 -0
- compiler/tests/wasm_codegen/e2e_tail/recursive_closure_captures_outer.plum +10 -0
- compiler/tests/wasm_codegen/e2e_tail/recursive_local_closure_million.plum +9 -0
- compiler/tests/wasm_codegen/e2e_tail/result_mutual_million.plum +20 -0
- compiler/tests/wasm_codegen/e2e_tail/result_self_loop_million.plum +17 -0
- compiler/tests/wasm_codegen/e2e_tail/result_self_loop_returns_err.plum +15 -0
- compiler/tests/wasm_codegen/e2e_tail/self_calling_closure_typed_params_runs.plum +9 -0
- compiler/tests/wasm_codegen/e2e_tail/self_calling_closure_untyped_params_runs.plum +9 -0
- compiler/tests/wasm_codegen/e2e_tail/self_tail_args_swap.plum +16 -0
- compiler/tests/wasm_codegen/e2e_tail/self_tail_million.plum +20 -0
- compiler/tests/wasm_codegen/e2e_tail/self_tail_through_nested_branches.plum +21 -0
- compiler/tests/wasm_codegen/e2e_tail/tail_call_in_match_arm.plum +15 -0
- compiler/tests/wasm_codegen/e2e_tail/trmc_link_map_100k.plum +25 -0
- compiler/tests/wasm_codegen/e2e_types/adder_two_levels.plum +14 -0
- compiler/tests/wasm_codegen/e2e_types/bang_unwraps_and_chains.plum +17 -0
- compiler/tests/wasm_codegen/e2e_types/bare_tag_fixed_by_later_arg.plum +17 -0
- compiler/tests/wasm_codegen/e2e_types/bare_tag_fixed_by_other_link_arg.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/bare_variants_in_if_branches_take_fn_return_type.plum +26 -0
- compiler/tests/wasm_codegen/e2e_types/bare_variants_in_match_arms_take_fn_return_type.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/chained_err_value_is_first.plum +25 -0
- compiler/tests/wasm_codegen/e2e_types/chained_question_returns_first_err.plum +26 -0
- compiler/tests/wasm_codegen/e2e_types/closure_coerced_into_optional_return.plum +12 -0
- compiler/tests/wasm_codegen/e2e_types/closure_into_ref_option_and_result.plum +23 -0
- compiler/tests/wasm_codegen/e2e_types/closure_passes_none_to_an_option_param.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/closure_returning_none_into_optional.plum +12 -0
- compiler/tests/wasm_codegen/e2e_types/curried_closure_runs.plum +13 -0
- compiler/tests/wasm_codegen/e2e_types/err_field_is_error.plum +15 -0
- compiler/tests/wasm_codegen/e2e_types/field_less_variant_called_like_a_constructor.plum +23 -0
- compiler/tests/wasm_codegen/e2e_types/field_less_variant_of_a_generic_enum_called_like_a_constructor.plum +22 -0
- compiler/tests/wasm_codegen/e2e_types/fn_field_returning_optional.plum +15 -0
- compiler/tests/wasm_codegen/e2e_types/g1_typed_param.plum +13 -0
- compiler/tests/wasm_codegen/e2e_types/g2_local_closure_inferred_from_first_call.plum +12 -0
- compiler/tests/wasm_codegen/e2e_types/g5_returned_closure.plum +12 -0
- compiler/tests/wasm_codegen/e2e_types/inline_if_joins_bare_variant_instantiations.plum +19 -0
- compiler/tests/wasm_codegen/e2e_types/inlined_constant_call_keeps_return_type.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/list_literal_element_pinned_by_a_later_element.plum +37 -0
- compiler/tests/wasm_codegen/e2e_types/map_err_closure_param_shadows_top_level_fn.plum +17 -0
- compiler/tests/wasm_codegen/e2e_types/map_err_on_call_result.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/map_err_then_unwrap_or_fuses.plum +16 -0
- compiler/tests/wasm_codegen/e2e_types/nested_generic.plum +21 -0
- compiler/tests/wasm_codegen/e2e_types/none_as_generic_param_default.plum +16 -0
- compiler/tests/wasm_codegen/e2e_types/none_as_param_default.plum +16 -0
- compiler/tests/wasm_codegen/e2e_types/ok_unit_and_expression_position.plum +24 -0
- compiler/tests/wasm_codegen/e2e_types/option_question_returns_none.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/reused_cell_is_read_through_its_local.plum +23 -0
- compiler/tests/wasm_codegen/e2e_types/seq_with_optional_next.plum +19 -0
- compiler/tests/wasm_codegen/e2e_types/single_letter_types_and_variants.plum +24 -0
- compiler/tests/wasm_codegen/e2e_types/specialised_arm_keeps_result_type.plum +26 -0
- compiler/tests/wasm_codegen/e2e_types/static_method_of_generic_type_not_pinning_t.plum +12 -0
- compiler/tests/wasm_codegen/e2e_types/static_on_builtin.plum +11 -0
- compiler/tests/wasm_codegen/e2e_types/static_on_builtin_method_and_json_cycle.plum +17 -0
- compiler/tests/wasm_codegen/e2e_types/static_on_type_param.plum +14 -0
- compiler/tests/wasm_codegen/e2e_types/struct_payload_in_some.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/unannotated_local_of_list_pinned_by_a_later_element.plum +39 -0
- compiler/tests/wasm_codegen/e2e_types/unit_closure_local_called_as_a_statement.plum +15 -0
- compiler/tests/wasm_codegen/e2e_types/untyped_closure_passes_none_to_an_option_param.plum +18 -0
- compiler/tests/wasm_codegen/e2e_types/user_type_shadows_std_pair_imported_by_std.plum +14 -0
- compiler/wasm-codegen/tests/wasm_codegen/e2e_mem/unique_inplace.rs +0 -119
- compiler/wasm-codegen/tests/wasm_codegen/e2e_records/enum_init.rs +1 -68
- compiler/wasm-codegen/tests/wasm_codegen/e2e_records/inline_variants.rs +1 -73
- compiler/wasm-codegen/tests/wasm_codegen/e2e_records/mod.rs +0 -2
- compiler/wasm-codegen/tests/wasm_codegen/e2e_records/record_embed.rs +0 -79
- compiler/wasm-codegen/tests/wasm_codegen/e2e_records/record_init.rs +1 -74
- compiler/wasm-codegen/tests/wasm_codegen/e2e_records/unboxed_variants.rs +1 -167
- compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/closure_tail.rs +1 -37
- compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/error_tail.rs +0 -27
- compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/loop_break.rs +0 -148
- compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/mod.rs +0 -2
- compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/tail.rs +1 -150
- compiler/wasm-codegen/tests/wasm_codegen/e2e_types/closure_optional.rs +0 -39
- compiler/wasm-codegen/tests/wasm_codegen/e2e_types/closure_types.rs +0 -50
- compiler/wasm-codegen/tests/wasm_codegen/e2e_types/list_and_shadow.rs +0 -81
- compiler/wasm-codegen/tests/wasm_codegen/e2e_types/mod.rs +0 -3
- compiler/wasm-codegen/tests/wasm_codegen/e2e_types/question.rs +0 -144
- compiler/wasm-codegen/tests/wasm_codegen/e2e_types/synthetic_sites.rs +1 -108
- compiler/wasm-codegen/tests/wasm_codegen/e2e_types/type_param_static.rs +0 -83
- docs/backlog.md +1 -1
- docs/plans/2026-11-05-compiler-cleanup-phase5-6.md +1 -1
- docs/plans/2026-11-05-compiler-cleanup-phase5-6.progress.md +26 -0
- docs/plans/2026-11-05-compiler-cleanup-phase5-6.results.md +36 -0
compiler/core/tests/core/for_next_test.rs
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
#![allow(non_snake_case)]
|
|
2
|
-
//! The one loop form `for x := range v`: `v: Int` counts 0..v; any other `v`
|
|
3
|
-
//! whose type has `next(self) -> Option[Pair[T, Self]]` iterates (None = done, else
|
|
4
|
-
//! `Some(Pair(value, rest))`, the iterator rebound to `rest` each step).
|
|
5
|
-
use plum_test_support::runInt;
|
|
6
|
-
|
|
7
|
-
#[test]
|
|
8
|
-
fn range_int_counts_from_zero() {
|
|
9
|
-
let src = "\
|
|
10
|
-
import std/Int
|
|
11
|
-
|
|
12
|
-
fun main() -> Int =
|
|
13
|
-
acc := 0
|
|
14
|
-
for i := range 3
|
|
15
|
-
acc = acc * 10 + i + 1
|
|
16
|
-
acc
|
|
17
|
-
";
|
|
18
|
-
// binds 0, 1, 2
|
|
19
|
-
assert_eq!(runInt("for_next_int", src), 123);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
#[test]
|
|
23
|
-
fn range_list_binds_elements() {
|
|
24
|
-
let src = "\
|
|
25
|
-
import std/Int
|
|
26
|
-
import std/List
|
|
27
|
-
|
|
28
|
-
fun main() -> Int =
|
|
29
|
-
acc := 0
|
|
30
|
-
for x := range [5, 6]
|
|
31
|
-
acc = acc * 10 + x
|
|
32
|
-
acc
|
|
33
|
-
";
|
|
34
|
-
assert_eq!(runInt("for_next_list", src), 56);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
#[test]
|
|
38
|
-
fn list_next_steps() {
|
|
39
|
-
let src = "\
|
|
40
|
-
import std/Int
|
|
41
|
-
import std/List
|
|
42
|
-
import std/Pair
|
|
43
|
-
import std/Option
|
|
44
|
-
|
|
45
|
-
fun main() -> Int =
|
|
46
|
-
xs := [5, 6]
|
|
47
|
-
p := xs.next()!
|
|
48
|
-
q := p.second.next()!
|
|
49
|
-
r := q.second.next()
|
|
50
|
-
p.first * 100 + q.first * 10 + {if r.isNone() then 1 else 0}
|
|
51
|
-
";
|
|
52
|
-
assert_eq!(runInt("for_next_list_next", src), 561);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
#[test]
|
|
56
|
-
fn range_user_struct_with_next() {
|
|
57
|
-
let src = "\
|
|
58
|
-
import std/Int
|
|
59
|
-
import std/Pair
|
|
60
|
-
import std/Option
|
|
61
|
-
|
|
62
|
-
struct Countdown(n: Int) =
|
|
63
|
-
fun next(self) -> Option[Pair[Int, Countdown]] =
|
|
64
|
-
if self.n == 0 then None else Some(Pair(first: self.n, second: Countdown(n: self.n - 1)))
|
|
65
|
-
|
|
66
|
-
fun main() -> Int =
|
|
67
|
-
acc := 0
|
|
68
|
-
for x := range Countdown(n: 3)
|
|
69
|
-
acc = acc * 10 + x
|
|
70
|
-
acc
|
|
71
|
-
";
|
|
72
|
-
assert_eq!(runInt("for_next_user", src), 321);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
#[test]
|
|
76
|
-
fn range_next_supports_continue_and_break() {
|
|
77
|
-
let src = "\
|
|
78
|
-
import std/Int
|
|
79
|
-
import std/Pair
|
|
80
|
-
import std/Option
|
|
81
|
-
|
|
82
|
-
struct Up(i: Int, n: Int) =
|
|
83
|
-
fun next(self) -> Option[Pair[Int, Up]] =
|
|
84
|
-
if self.i >= self.n then None else Some(Pair(first: self.i, second: Up(i: self.i + 1, n: self.n)))
|
|
85
|
-
|
|
86
|
-
fun main() -> Int =
|
|
87
|
-
acc := 0
|
|
88
|
-
for x := range Up(i: 0, n: 10)
|
|
89
|
-
if x == 1
|
|
90
|
-
continue
|
|
91
|
-
if x == 4
|
|
92
|
-
break
|
|
93
|
-
acc = acc * 10 + x
|
|
94
|
-
acc
|
|
95
|
-
";
|
|
96
|
-
// 0, (skip 1), 2, 3, stop at 4
|
|
97
|
-
assert_eq!(runInt("for_next_ctl", src), 23);
|
|
98
|
-
}
|
compiler/core/tests/core/fused_closure_generic_test.rs
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#![allow(non_snake_case)]
|
|
2
|
-
//! A fused fn keeps a specialised-away closure parameter when it is the only thing that
|
|
3
|
-
//! fixes a generic another kept closure parameter takes as its input: `map(andThen(..))`
|
|
4
|
-
//! on `Option` fuses to `map$c0$andThen$c0$0(|t| t.name, u)`, whose `|t| ..` needs the
|
|
5
|
-
//! `B` that only `andThen`'s closure binds. Dropping it left the fused fn uninstantiable
|
|
6
|
-
//! ("unknown function" at codegen).
|
|
7
|
-
use plum_test_support::runInt;
|
|
8
|
-
|
|
9
|
-
#[test]
|
|
10
|
-
fn option_and_then_then_map_compiles_and_runs() {
|
|
11
|
-
let src = "\
|
|
12
|
-
import std/Str
|
|
13
|
-
import std/Int
|
|
14
|
-
import std/Option
|
|
15
|
-
|
|
16
|
-
struct Target(size: Int)
|
|
17
|
-
struct User(name: Str, target: Option[Target])
|
|
18
|
-
|
|
19
|
-
fun targetSize(u: Option[User]) -> Option[Int] =
|
|
20
|
-
u.andThen(|v| v.target).map(|t| t.size)
|
|
21
|
-
|
|
22
|
-
fun main() -> Int =
|
|
23
|
-
full := User(name: \"u\", target: Some(Target(size: 40)))
|
|
24
|
-
none: Option[User] := None
|
|
25
|
-
targetSize(Some(full)).unwrapOr(0) + targetSize(none).unwrapOr(2)
|
|
26
|
-
";
|
|
27
|
-
assert_eq!(runInt("fused_closure_generic", src), 42);
|
|
28
|
-
}
|
compiler/core/tests/core/main.rs
CHANGED
|
@@ -3,10 +3,8 @@ mod closure_spec_test;
|
|
|
3
3
|
mod closure_types_parse_test;
|
|
4
4
|
mod diag_test;
|
|
5
5
|
mod drive_test;
|
|
6
|
-
mod for_next_test;
|
|
7
6
|
mod formatter_test;
|
|
8
7
|
mod forms_list_range_test;
|
|
9
|
-
mod fused_closure_generic_test;
|
|
10
8
|
mod generics_test;
|
|
11
9
|
mod ids_spans_test;
|
|
12
10
|
mod inline_variants_test;
|
compiler/tests/core/for_next.plum
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/List
|
|
3
|
+
import std/Pair
|
|
4
|
+
import std/Option
|
|
5
|
+
|
|
6
|
+
# The one loop form `for x := range v`: `v: Int` counts 0..v; any other `v`
|
|
7
|
+
# whose type has `next(self) -> Option[Pair[T, Self]]` iterates (None = done, else
|
|
8
|
+
# `Some(Pair(value, rest))`, the iterator rebound to `rest` each step).
|
|
9
|
+
|
|
10
|
+
struct Countdown(n: Int) =
|
|
11
|
+
fun next(self) -> Option[Pair[Int, Countdown]] =
|
|
12
|
+
if self.n == 0 then None else Some(Pair(first: self.n, second: Countdown(n: self.n - 1)))
|
|
13
|
+
|
|
14
|
+
struct Up(i: Int, n: Int) =
|
|
15
|
+
fun next(self) -> Option[Pair[Int, Up]] =
|
|
16
|
+
if self.i >= self.n then None else Some(Pair(first: self.i, second: Up(i: self.i + 1, n: self.n)))
|
|
17
|
+
|
|
18
|
+
fun rangeInt() -> Int =
|
|
19
|
+
acc := 0
|
|
20
|
+
for i := range 3
|
|
21
|
+
acc = acc * 10 + i + 1
|
|
22
|
+
acc
|
|
23
|
+
|
|
24
|
+
fun rangeList() -> Int =
|
|
25
|
+
acc := 0
|
|
26
|
+
for x := range [5, 6]
|
|
27
|
+
acc = acc * 10 + x
|
|
28
|
+
acc
|
|
29
|
+
|
|
30
|
+
fun listNext() -> Int =
|
|
31
|
+
xs := [5, 6]
|
|
32
|
+
p := xs.next()!
|
|
33
|
+
q := p.second.next()!
|
|
34
|
+
r := q.second.next()
|
|
35
|
+
p.first * 100 + q.first * 10 + {if r.isNone() then 1 else 0}
|
|
36
|
+
|
|
37
|
+
fun rangeUser() -> Int =
|
|
38
|
+
acc := 0
|
|
39
|
+
for x := range Countdown(n: 3)
|
|
40
|
+
acc = acc * 10 + x
|
|
41
|
+
acc
|
|
42
|
+
|
|
43
|
+
fun rangeCtl() -> Int =
|
|
44
|
+
acc := 0
|
|
45
|
+
for x := range Up(i: 0, n: 10)
|
|
46
|
+
if x == 1
|
|
47
|
+
continue
|
|
48
|
+
if x == 4
|
|
49
|
+
break
|
|
50
|
+
acc = acc * 10 + x
|
|
51
|
+
acc
|
|
52
|
+
|
|
53
|
+
# binds 0, 1, 2
|
|
54
|
+
test "range_int_counts_from_zero"
|
|
55
|
+
assert rangeInt() == 123
|
|
56
|
+
|
|
57
|
+
test "range_list_binds_elements"
|
|
58
|
+
assert rangeList() == 56
|
|
59
|
+
|
|
60
|
+
test "list_next_steps"
|
|
61
|
+
assert listNext() == 561
|
|
62
|
+
|
|
63
|
+
test "range_user_struct_with_next"
|
|
64
|
+
assert rangeUser() == 321
|
|
65
|
+
|
|
66
|
+
# 0, (skip 1), 2, 3, stop at 4
|
|
67
|
+
test "range_next_supports_continue_and_break"
|
|
68
|
+
assert rangeCtl() == 23
|
compiler/tests/core/fused_closure_generic.plum
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Option
|
|
4
|
+
|
|
5
|
+
# A fused fn keeps a specialised-away closure parameter when it is the only thing that
|
|
6
|
+
# fixes a generic another kept closure parameter takes as its input: `map(andThen(..))`
|
|
7
|
+
# on `Option` fuses to `map$c0$andThen$c0$0(|t| t.name, u)`, whose `|t| ..` needs the
|
|
8
|
+
# `B` that only `andThen`'s closure binds. Dropping it left the fused fn uninstantiable
|
|
9
|
+
# ("unknown function" at codegen).
|
|
10
|
+
|
|
11
|
+
struct Target(size: Int)
|
|
12
|
+
struct User(name: Str, target: Option[Target])
|
|
13
|
+
|
|
14
|
+
fun targetSize(u: Option[User]) -> Option[Int] =
|
|
15
|
+
u.andThen(|v| v.target).map(|t| t.size)
|
|
16
|
+
|
|
17
|
+
fun fusedSum() -> Int =
|
|
18
|
+
full := User(name: "u", target: Some(Target(size: 40)))
|
|
19
|
+
none: Option[User] := None
|
|
20
|
+
targetSize(Some(full)).unwrapOr(0) + targetSize(none).unwrapOr(2)
|
|
21
|
+
|
|
22
|
+
test "option_and_then_then_map_compiles_and_runs"
|
|
23
|
+
assert fusedSum() == 42
|
compiler/tests/tail_calls.plum
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
# `plum test` must run tail calls like a compiled program: each loop below is
|
|
4
|
+
# 1000000 deep (a non-tail loop of that depth traps), which overflows the stack unless the calls are real tail calls.
|
|
5
|
+
|
|
6
|
+
fun count(n: Int, acc: Int) -> Int = if n == 0 then acc else count(n - 1, acc + 1)
|
|
7
|
+
|
|
8
|
+
struct Counter(step: Int) =
|
|
9
|
+
fun count(self, n: Int, acc: Int) -> Int =
|
|
10
|
+
if n == 0 then acc else self.count(n - 1, acc + self.step)
|
|
11
|
+
|
|
12
|
+
fun viaClosure(n: Int, k: fn(Int) -> Int) -> Int = if n == 0 then 7 else k(n - 1)
|
|
13
|
+
|
|
14
|
+
fun goClosure(n: Int) -> Int = viaClosure(n, goClosure)
|
|
15
|
+
|
|
16
|
+
test "a self tail call 1000000 deep does not overflow the stack"
|
|
17
|
+
assert count(1000000, 0) == 1000000
|
|
18
|
+
|
|
19
|
+
test "a method tail call 1000000 deep does not overflow the stack"
|
|
20
|
+
assert Counter(step: 3).count(1000000, 0) == 3000000
|
|
21
|
+
|
|
22
|
+
test "a closure tail call 1000000 deep does not overflow the stack"
|
|
23
|
+
assert goClosure(1000000) == 7
|
compiler/tests/wasm_codegen/e2e_mem/match_arm_rename_still_valid_wasm.plum
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Pa(x: Int)
|
|
7
|
+
struct Pb(y: Int, z: Int)
|
|
8
|
+
|
|
9
|
+
enum Pick = Wa(v: Pa) | Wb(v: Pb)
|
|
10
|
+
|
|
11
|
+
fun go(p: Pick) -> Int =
|
|
12
|
+
match p
|
|
13
|
+
Wa(a) => a.x
|
|
14
|
+
Wb(b) => b.y * 10 + b.z
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int =
|
|
17
|
+
go(Wa(v: Pa(x: 4))) * 100 + go(Wb(v: Pb(y: 5, z: 6)))
|
|
18
|
+
|
|
19
|
+
# moved from e2e_mem::unique_inplace::match_arm_rename_still_valid_wasm
|
|
20
|
+
# R5: arms binding differently named locals of different record types.
|
|
21
|
+
test "match arm rename still valid wasm"
|
|
22
|
+
assert runMain() == 456
|
compiler/tests/wasm_codegen/e2e_mem/plain_spread_still_copies.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int)
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
s := Cell(x: 1, y: 7)
|
|
10
|
+
t := Cell(..s, x: 5)
|
|
11
|
+
s.x * 1000 + t.x * 100 + t.y
|
|
12
|
+
|
|
13
|
+
# moved from e2e_mem::unique_inplace::plain_spread_still_copies
|
|
14
|
+
test "plain spread still copies"
|
|
15
|
+
assert runMain() == 1507
|
compiler/tests/wasm_codegen/e2e_mem/unique_self_field_write.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(mut x: Int) =
|
|
7
|
+
fun bump(self) -> Cell =
|
|
8
|
+
self.x = self.x + 1
|
|
9
|
+
self
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int =
|
|
12
|
+
s := Cell(x: 41)
|
|
13
|
+
t := s.bump()
|
|
14
|
+
t.x
|
|
15
|
+
|
|
16
|
+
# moved from e2e_mem::unique_inplace::unique_self_field_write
|
|
17
|
+
test "unique self field write"
|
|
18
|
+
assert runMain() == 42
|
compiler/tests/wasm_codegen/e2e_mem/unique_self_spread_return.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int) =
|
|
7
|
+
fun step(self) -> Cell = Cell(..self, x: self.x + 1)
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
t := Cell(x: 1, y: 7).step().step()
|
|
11
|
+
t.x * 100 + t.y
|
|
12
|
+
|
|
13
|
+
# moved from e2e_mem::unique_inplace::unique_self_spread_return
|
|
14
|
+
test "unique self spread return"
|
|
15
|
+
assert runMain() == 307
|
compiler/tests/wasm_codegen/e2e_mem/unique_spread_hazard_falls_back_to_copy_1.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int) =
|
|
7
|
+
fun sum(self) -> Int = self.x + self.y
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
s := Cell(x: 1, y: 2)
|
|
11
|
+
t := Cell(..s, x: 10, y: s.sum())
|
|
12
|
+
t.x * 100 + t.y
|
|
13
|
+
|
|
14
|
+
# moved from e2e_mem::unique_inplace::unique_spread_hazard_falls_back_to_copy
|
|
15
|
+
# A later value that reads a field an earlier one overwrote, or uses the source
|
|
16
|
+
# any other way (here a method call), must still run (copy fallback).
|
|
17
|
+
test "unique spread hazard falls back to copy"
|
|
18
|
+
assert runMain() == 1003
|
compiler/tests/wasm_codegen/e2e_mem/unique_spread_hazard_falls_back_to_copy_2.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int, z: Int)
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
s := Cell(x: 1, y: 2, z: 3)
|
|
10
|
+
t := Cell(..s, x: 10, z: s.y + s.z)
|
|
11
|
+
t.x * 100 + t.y * 10 + t.z
|
|
12
|
+
|
|
13
|
+
# moved from e2e_mem::unique_inplace::unique_spread_hazard_falls_back_to_copy
|
|
14
|
+
# A later value that reads a field an earlier one overwrote, or uses the source
|
|
15
|
+
# any other way (here a method call), must still run (copy fallback).
|
|
16
|
+
test "unique spread hazard falls back to copy"
|
|
17
|
+
assert runMain() == 1025
|
compiler/tests/wasm_codegen/e2e_mem/unique_spread_reassign_local.plum
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int)
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
s := Cell(x: 1, y: 7)
|
|
10
|
+
s = Cell(..s, x: s.x + 1)
|
|
11
|
+
s = Cell(..s, y: s.y + s.x)
|
|
12
|
+
s.x * 100 + s.y
|
|
13
|
+
|
|
14
|
+
# moved from e2e_mem::unique_inplace::unique_spread_reassign_local
|
|
15
|
+
test "unique spread reassign local"
|
|
16
|
+
assert runMain() == 209
|
compiler/tests/wasm_codegen/e2e_mem/unique_spread_sets_fields.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int, z: Int)
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
s := Cell(x: 1, y: 2, z: 3)
|
|
10
|
+
t := Cell(..s, z: 30, x: 10)
|
|
11
|
+
t.x * 10000 + t.y * 100 + t.z
|
|
12
|
+
|
|
13
|
+
# moved from e2e_mem::unique_inplace::unique_spread_sets_fields
|
|
14
|
+
test "unique spread sets fields"
|
|
15
|
+
assert runMain() == 100230
|
compiler/tests/wasm_codegen/e2e_mem/unique_spread_swap_reads_old_values.plum
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int)
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
s := Cell(x: 1, y: 2)
|
|
10
|
+
t := Cell(..s, x: s.y, y: s.x)
|
|
11
|
+
t.x * 10 + t.y
|
|
12
|
+
|
|
13
|
+
# moved from e2e_mem::unique_inplace::unique_spread_swap_reads_old_values
|
|
14
|
+
# A value reading an already-overwritten field must still see the OLD value.
|
|
15
|
+
test "unique spread swap reads old values"
|
|
16
|
+
assert runMain() == 21
|
compiler/tests/wasm_codegen/e2e_records/base_through_embedded_struct.plum
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Actor(x: Int) =
|
|
7
|
+
fun go(self) -> Int = 1
|
|
8
|
+
|
|
9
|
+
struct Mid(Actor(x), m: Int) =
|
|
10
|
+
override fun go(self) -> Int = 2 + self.Actor.go()
|
|
11
|
+
|
|
12
|
+
struct Top(Mid(x, m), t: Int)
|
|
13
|
+
|
|
14
|
+
fun run(a: Actor) -> Int = a.go()
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int =
|
|
17
|
+
t := Top(x: 1, m: 2, t: 3)
|
|
18
|
+
run(t) * 10 + t.x
|
|
19
|
+
|
|
20
|
+
# moved from e2e_records::record_embed::base_through_embedded_struct
|
|
21
|
+
test "base through embedded struct"
|
|
22
|
+
assert runMain() == 31
|
compiler/tests/wasm_codegen/e2e_records/cons_list_length_map_sum.plum
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Slice
|
|
4
|
+
|
|
5
|
+
enum Link[T] = Nil | Cons(head: T, tail: Link[T])
|
|
6
|
+
|
|
7
|
+
fun length[T](xs: Link[T]) -> Int = match xs
|
|
8
|
+
Nil => 0
|
|
9
|
+
Cons(_, t) => 1 + length(t)
|
|
10
|
+
|
|
11
|
+
fun sum(xs: Link[Int]) -> Int = match xs
|
|
12
|
+
Nil => 0
|
|
13
|
+
Cons(h, t) => h + sum(t)
|
|
14
|
+
|
|
15
|
+
fun cons[T](h: T, t: Link[T]) -> Link[T] =
|
|
16
|
+
Cons(head: h, tail: t)
|
|
17
|
+
|
|
18
|
+
fun map(xs: Link[Int], f: fn(Int) -> Int) -> Link[Int] = match xs
|
|
19
|
+
Nil => Nil
|
|
20
|
+
Cons(h, t) => Cons(head: f(h), tail: map(t, f))
|
|
21
|
+
|
|
22
|
+
fun runMain() -> Int =
|
|
23
|
+
xs := cons(1, cons(2, cons(3, Nil)))
|
|
24
|
+
ys := map(xs, |n| n * 2)
|
|
25
|
+
sum(xs) * 100 + sum(ys) + length(ys) * 10000
|
|
26
|
+
|
|
27
|
+
# moved from e2e_records::inline_variants::cons_list_length_map_sum
|
|
28
|
+
test "cons list length map sum"
|
|
29
|
+
assert runMain() == 30612
|
compiler/tests/wasm_codegen/e2e_records/deep_nested_variant_match.plum
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
|
|
5
|
+
enum Tree = Leaf(v: Int) | Node(l: Tree, r: Tree)
|
|
6
|
+
|
|
7
|
+
fun depthSum(t: Tree, d: Int) -> Int =
|
|
8
|
+
match t
|
|
9
|
+
Leaf(v) => v * d
|
|
10
|
+
Node(l, r) => depthSum(l, d + 1) + depthSum(r, d + 1)
|
|
11
|
+
|
|
12
|
+
fun build(n: Int) -> Tree =
|
|
13
|
+
if n == 0 then Leaf(v: 1) else Node(l: build(n - 1), r: Leaf(v: n))
|
|
14
|
+
|
|
15
|
+
fun runMain() -> Int = depthSum(build(5), 1)
|
|
16
|
+
|
|
17
|
+
# moved from e2e_records::unboxed_variants::deep_nested_variant_match
|
|
18
|
+
# Leaves: 1 at depth 6, then Leaf(n) at depth 7 - n for n = 1..5.
|
|
19
|
+
test "deep nested variant match"
|
|
20
|
+
assert runMain() == 56
|
compiler/tests/wasm_codegen/e2e_records/embedded_record_standalone_and_unselected_set_by_init.plum
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Stage(camScale: Int, tag: Int) =
|
|
7
|
+
fun init(self) =
|
|
8
|
+
self.camX: Int = 6
|
|
9
|
+
|
|
10
|
+
struct World(Stage(tag), score: Int) =
|
|
11
|
+
fun init(self) =
|
|
12
|
+
self.camScale = 5
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
s := Stage(camScale: 2, tag: 1)
|
|
16
|
+
w := World(tag: 3, score: 1)
|
|
17
|
+
s.camScale * 100 + w.camScale * 10 + w.camX + w.tag + s.camX
|
|
18
|
+
|
|
19
|
+
# moved from e2e_records::record_embed::embedded_record_standalone_and_unselected_set_by_init
|
|
20
|
+
test "embedded record standalone and unselected set by init"
|
|
21
|
+
assert runMain() == 265
|
compiler/tests/wasm_codegen/e2e_records/empty_init_with_annotation.plum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
|
|
4
|
+
enum Link[T] =
|
|
5
|
+
| Nil
|
|
6
|
+
| Cons(head: T, tail: Link[T])
|
|
7
|
+
|
|
8
|
+
fun init() -> Link[T] = Nil
|
|
9
|
+
|
|
10
|
+
fun length(self) -> Int = match self
|
|
11
|
+
Nil => 0
|
|
12
|
+
Cons(_, t) => 1 + t.length()
|
|
13
|
+
|
|
14
|
+
fun sum(self) -> Int = match self
|
|
15
|
+
Nil => 0
|
|
16
|
+
Cons(h, t) => h + t.sum()
|
|
17
|
+
|
|
18
|
+
fun runMain() -> Int =
|
|
19
|
+
xs: Link[Int] := Link()
|
|
20
|
+
xs.length() + 7
|
|
21
|
+
|
|
22
|
+
# moved from e2e_records::enum_init::empty_init_with_annotation
|
|
23
|
+
test "empty init with annotation"
|
|
24
|
+
assert runMain() == 7
|
compiler/tests/wasm_codegen/e2e_records/enum_init_versus_variant_name.plum
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
|
|
5
|
+
enum Cell =
|
|
6
|
+
| Empty
|
|
7
|
+
| Full(count: Int, label: Str)
|
|
8
|
+
|
|
9
|
+
fun init(n: Int) -> Cell = if n <= 0 then Empty else Full(count: n, label: "n{n}")
|
|
10
|
+
|
|
11
|
+
fun size(self) -> Int =
|
|
12
|
+
match self
|
|
13
|
+
Empty => 0
|
|
14
|
+
Full(c, _) => c
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int =
|
|
17
|
+
a := Cell(5)
|
|
18
|
+
b := Cell(0)
|
|
19
|
+
c := Full(count: 9, label: "nine")
|
|
20
|
+
d := Full(label: "swapped", count: 2)
|
|
21
|
+
a.size() * 1000 + b.size() * 100 + c.size() * 10 + d.size()
|
|
22
|
+
|
|
23
|
+
# moved from e2e_records::unboxed_variants::enum_init_versus_variant_name
|
|
24
|
+
test "enum init versus variant name"
|
|
25
|
+
assert runMain() == 5092
|
compiler/tests/wasm_codegen/e2e_records/equality_is_identity_of_the_variant_value.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
|
|
5
|
+
enum Shape = Circle(r: Int) | Rect(w: Int, h: Int)
|
|
6
|
+
|
|
7
|
+
fun runMain() -> Int =
|
|
8
|
+
a: Shape := Circle(r: 2)
|
|
9
|
+
b: Shape := a
|
|
10
|
+
c: Shape := Rect(w: 1, h: 2)
|
|
11
|
+
x := if a == b then 1 else 0
|
|
12
|
+
y := if a != c then 10 else 0
|
|
13
|
+
x + y
|
|
14
|
+
|
|
15
|
+
# moved from e2e_records::unboxed_variants::equality_is_identity_of_the_variant_value
|
|
16
|
+
test "equality is identity of the variant value"
|
|
17
|
+
assert runMain() == 11
|
compiler/{wasm-codegen/tests/wasm_codegen/e2e_records/empty_record_embed.rs → tests/wasm_codegen/e2e_records/field_less_struct_embedding_field_less_base_dispatches_in_a_slice.plum}
RENAMED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import std/Str
|
|
1
|
-
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
2
5
|
|
|
3
|
-
fn prog(body: &str) -> String {
|
|
4
|
-
format!("{PRE}{body}")
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
#[test]
|
|
8
|
-
fn field_less_struct_embedding_field_less_base_dispatches_in_a_slice() {
|
|
9
|
-
let src = prog("\
|
|
10
6
|
struct Actor() =
|
|
11
7
|
fun act(self, n: Int) -> Int = n
|
|
12
8
|
fun draw(self) -> Int = 1
|
|
@@ -18,7 +14,7 @@ struct Ship(Actor, speed: Int) =
|
|
|
18
14
|
override fun act(self, n: Int) -> Int = n + self.speed
|
|
19
15
|
override fun draw(self) -> Int = 100
|
|
20
16
|
|
|
21
|
-
fun
|
|
17
|
+
fun runMain() -> Int =
|
|
22
18
|
xs: Slice[Actor] := Slice[Actor]()
|
|
23
19
|
xs.push(Panel())
|
|
24
20
|
xs.push(Ship(speed: 5))
|
|
@@ -26,7 +22,8 @@ fun main() -> Int =
|
|
|
26
22
|
for i := range 2
|
|
27
23
|
total = total + xs.at(i).draw() + xs.at(i).act(1000)
|
|
28
24
|
total
|
|
25
|
+
|
|
29
|
-
|
|
26
|
+
# moved from e2e_records::empty_record_embed::field_less_struct_embedding_field_less_base_dispatches_in_a_slice
|
|
30
|
-
|
|
27
|
+
# Panel: draw 10 + act 1000; Ship: draw 100 + act 1005.
|
|
28
|
+
test "field less struct embedding field less base dispatches in a slice"
|
|
31
|
-
|
|
29
|
+
assert runMain() == 2115
|
|
32
|
-
}
|
compiler/tests/wasm_codegen/e2e_records/fixed_param_init.plum
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
|
|
4
|
+
enum Pair2 =
|
|
5
|
+
| Zero
|
|
6
|
+
| Two(a: Int, b: Int)
|
|
7
|
+
|
|
8
|
+
fun init(a: Int, b: Int) -> Pair2 = Two(a: a, b: b)
|
|
9
|
+
|
|
10
|
+
fun total(self) -> Int = match self
|
|
11
|
+
Zero => 0
|
|
12
|
+
Two(a, b) => a + b
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
Pair2(3, 4).total()
|
|
16
|
+
|
|
17
|
+
# moved from e2e_records::enum_init::fixed_param_init
|
|
18
|
+
test "fixed param init"
|
|
19
|
+
assert runMain() == 7
|
compiler/tests/wasm_codegen/e2e_records/fresh_list_per_construction.plum
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Actor(px: Int, py: Int) =
|
|
7
|
+
fun init(self) =
|
|
8
|
+
self.width: Int = 10
|
|
9
|
+
self.height: Int = self.width * 2
|
|
10
|
+
self.kids: Slice[Int] = Slice[Int]()
|
|
11
|
+
fun area(self) -> Int = self.width * self.height
|
|
12
|
+
|
|
13
|
+
struct Ship(Actor(px, py)) =
|
|
14
|
+
fun init(self) =
|
|
15
|
+
self.vx = 0
|
|
16
|
+
|
|
17
|
+
fun runMain() -> Int =
|
|
18
|
+
a := Ship(px: 0, py: 0)
|
|
19
|
+
b := Ship(px: 0, py: 0)
|
|
20
|
+
a.kids.push(1)
|
|
21
|
+
a.kids.push(2)
|
|
22
|
+
b.kids.push(9)
|
|
23
|
+
a.kids.length() * 10 + b.kids.length()
|
|
24
|
+
|
|
25
|
+
# moved from e2e_records::record_init::fresh_list_per_construction
|
|
26
|
+
test "fresh list per construction"
|
|
27
|
+
assert runMain() == 21
|
compiler/tests/wasm_codegen/e2e_records/generic_inferred_from_args.plum
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
|
|
4
|
+
enum Link[T] =
|
|
5
|
+
| Nil
|
|
6
|
+
| Cons(head: T, tail: Link[T])
|
|
7
|
+
|
|
8
|
+
fun init(a: T, b: T) -> Link[T] = Cons(head: a, tail: Cons(head: b, tail: Nil))
|
|
9
|
+
|
|
10
|
+
fun length(self) -> Int = match self
|
|
11
|
+
Nil => 0
|
|
12
|
+
Cons(_, t) => 1 + t.length()
|
|
13
|
+
|
|
14
|
+
fun sum(self) -> Int = match self
|
|
15
|
+
Nil => 0
|
|
16
|
+
Cons(h, t) => h + t.sum()
|
|
17
|
+
|
|
18
|
+
fun total(xs: Link[Int]) -> Int = xs.sum()
|
|
19
|
+
|
|
20
|
+
fun runMain() -> Int =
|
|
21
|
+
total(Link(5, 6)) + Link("a", "b").length()
|
|
22
|
+
|
|
23
|
+
# moved from e2e_records::enum_init::generic_inferred_from_args
|
|
24
|
+
test "generic inferred from args"
|
|
25
|
+
assert runMain() == 13
|
compiler/tests/wasm_codegen/e2e_records/inherited_fields_methods_and_init_order.plum
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Stage(camScale: Int) =
|
|
7
|
+
fun init(self) =
|
|
8
|
+
self.root: Int = 10
|
|
9
|
+
self.camX: Int = self.camScale * 2
|
|
10
|
+
|
|
11
|
+
fun act(self, dt: Int) -> Int = self.root + dt
|
|
12
|
+
|
|
13
|
+
fun draw(self) -> Int = 100
|
|
14
|
+
|
|
15
|
+
struct World(Stage(camScale), score: Int) =
|
|
16
|
+
fun init(self) =
|
|
17
|
+
self.best: Int = self.root + self.camX + 1
|
|
18
|
+
|
|
19
|
+
fun runMain() -> Int =
|
|
20
|
+
w := World(camScale: 3, score: 4)
|
|
21
|
+
w.camScale + w.root * 10 + w.camX * 100 + w.score * 1000 + w.best * 10000 + w.act(5) * 100000
|
|
22
|
+
|
|
23
|
+
# moved from e2e_records::record_embed::inherited_fields_methods_and_init_order
|
|
24
|
+
# camScale 3, root 10, camX 6, score 4, best 17, act(5) = 15
|
|
25
|
+
test "inherited fields methods and init order"
|
|
26
|
+
assert runMain() == 1674703
|
compiler/tests/wasm_codegen/e2e_records/match_variant_fields.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
|
|
5
|
+
enum Shape = Circle(r: Int) | Rect(w: Int, h: Int)
|
|
6
|
+
|
|
7
|
+
fun area(s: Shape) -> Int =
|
|
8
|
+
match s
|
|
9
|
+
Circle(r) => r * r
|
|
10
|
+
Rect(w, h) => w * h
|
|
11
|
+
|
|
12
|
+
fun runMain() -> Int =
|
|
13
|
+
area(Circle(r: 3)) * 100 + area(Rect(w: 2, h: 5))
|
|
14
|
+
|
|
15
|
+
# moved from e2e_records::unboxed_variants::match_variant_fields
|
|
16
|
+
test "match variant fields"
|
|
17
|
+
assert runMain() == 910
|
compiler/tests/wasm_codegen/e2e_records/million_cells_built_and_mapped_in_constant_stack.plum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
|
|
5
|
+
enum Link = End | Duo(head: Int, tail: Link)
|
|
6
|
+
|
|
7
|
+
fun upTo(n: Int) -> Link =
|
|
8
|
+
if n == 0 then End else Duo(head: n, tail: upTo(n - 1))
|
|
9
|
+
|
|
10
|
+
fun inc(xs: Link) -> Link =
|
|
11
|
+
match xs
|
|
12
|
+
End => End
|
|
13
|
+
Duo(h, t) => Duo(head: h + 1, tail: inc(t))
|
|
14
|
+
|
|
15
|
+
fun sum(xs: Link, acc: Int) -> Int =
|
|
16
|
+
match xs
|
|
17
|
+
End => acc
|
|
18
|
+
Duo(h, t) => sum(t, acc + h)
|
|
19
|
+
|
|
20
|
+
fun runMain() -> Int = sum(inc(upTo(1000000)), 0)
|
|
21
|
+
|
|
22
|
+
# moved from e2e_records::unboxed_variants::million_cells_built_and_mapped_in_constant_stack
|
|
23
|
+
test "million cells built and mapped in constant stack"
|
|
24
|
+
assert runMain() == 500001500000
|
compiler/tests/wasm_codegen/e2e_records/nested_enum_match.plum
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
|
|
5
|
+
enum Link[T] = End | Duo(head: T, tail: Link[T])
|
|
6
|
+
|
|
7
|
+
fun sum(l: Link[Int]) -> Int =
|
|
8
|
+
match l
|
|
9
|
+
End => 0
|
|
10
|
+
Duo(head, tail) => head + sum(tail)
|
|
11
|
+
|
|
12
|
+
fun second(l: Link[Int]) -> Int =
|
|
13
|
+
match l
|
|
14
|
+
Duo(_, Duo(h, _)) => h
|
|
15
|
+
_ => -1
|
|
16
|
+
|
|
17
|
+
fun runMain() -> Int =
|
|
18
|
+
l: Link[Int] := Duo(head: 1, tail: Duo(head: 20, tail: Duo(head: 300, tail: End)))
|
|
19
|
+
sum(l) * 1000 + second(l)
|
|
20
|
+
|
|
21
|
+
# moved from e2e_records::unboxed_variants::nested_enum_match
|
|
22
|
+
test "nested enum match"
|
|
23
|
+
assert runMain() == 321020
|
compiler/tests/wasm_codegen/e2e_records/nested_literal_construction.plum
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Slice
|
|
4
|
+
|
|
5
|
+
enum Link[T] = Nil | Cons(head: T, tail: Link[T])
|
|
6
|
+
|
|
7
|
+
fun length[T](xs: Link[T]) -> Int = match xs
|
|
8
|
+
Nil => 0
|
|
9
|
+
Cons(_, t) => 1 + length(t)
|
|
10
|
+
|
|
11
|
+
fun sum(xs: Link[Int]) -> Int = match xs
|
|
12
|
+
Nil => 0
|
|
13
|
+
Cons(h, t) => h + sum(t)
|
|
14
|
+
|
|
15
|
+
fun cons[T](h: T, t: Link[T]) -> Link[T] =
|
|
16
|
+
Cons(head: h, tail: t)
|
|
17
|
+
|
|
18
|
+
fun map(xs: Link[Int], f: fn(Int) -> Int) -> Link[Int] = match xs
|
|
19
|
+
Nil => Nil
|
|
20
|
+
Cons(h, t) => Cons(head: f(h), tail: map(t, f))
|
|
21
|
+
|
|
22
|
+
fun runMain() -> Int =
|
|
23
|
+
xs := Cons(head: 1, tail: Cons(head: 2, tail: Cons(head: 3, tail: Nil)))
|
|
24
|
+
sum(xs)
|
|
25
|
+
|
|
26
|
+
# moved from e2e_records::inline_variants::nested_literal_construction
|
|
27
|
+
test "nested literal construction"
|
|
28
|
+
assert runMain() == 6
|
compiler/tests/wasm_codegen/e2e_records/override_calls_original.plum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Stage(camScale: Int) =
|
|
7
|
+
fun init(self) =
|
|
8
|
+
self.root: Int = 10
|
|
9
|
+
self.camX: Int = self.camScale * 2
|
|
10
|
+
|
|
11
|
+
fun act(self, dt: Int) -> Int = self.root + dt
|
|
12
|
+
|
|
13
|
+
fun draw(self) -> Int = 100
|
|
14
|
+
|
|
15
|
+
struct World(Stage(camScale), score: Int) =
|
|
16
|
+
override fun draw(self) -> Int = self.Stage.draw() + self.score
|
|
17
|
+
|
|
18
|
+
fun runMain() -> Int =
|
|
19
|
+
w := World(camScale: 1, score: 7)
|
|
20
|
+
w.draw() * 10 + w.act(1)
|
|
21
|
+
|
|
22
|
+
# moved from e2e_records::record_embed::override_calls_original
|
|
23
|
+
test "override calls original"
|
|
24
|
+
assert runMain() == 1081
|
compiler/tests/wasm_codegen/e2e_records/reassign_enum_local_across_variants.plum
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
|
|
5
|
+
enum Shape = Circle(r: Int) | Rect(w: Int, h: Int)
|
|
6
|
+
|
|
7
|
+
fun area(s: Shape) -> Int =
|
|
8
|
+
match s
|
|
9
|
+
Circle(r) => r * 3
|
|
10
|
+
Rect(w, h) => w * h
|
|
11
|
+
|
|
12
|
+
fun kind(s: Shape) -> Int =
|
|
13
|
+
match s
|
|
14
|
+
Circle(_) => 1
|
|
15
|
+
Rect(_, _) => 10
|
|
16
|
+
|
|
17
|
+
fun runMain() -> Int =
|
|
18
|
+
s: Shape := Circle(r: 1)
|
|
19
|
+
n := area(s)
|
|
20
|
+
s = Rect(w: 2, h: 5)
|
|
21
|
+
n = n * 100 + area(s)
|
|
22
|
+
k := 0
|
|
23
|
+
for i := range 4
|
|
24
|
+
if i % 2 == 0
|
|
25
|
+
s = Rect(w: 1, h: 1)
|
|
26
|
+
else
|
|
27
|
+
s = Circle(r: 1)
|
|
28
|
+
k = k + kind(s)
|
|
29
|
+
n * 100 + k
|
|
30
|
+
|
|
31
|
+
# moved from e2e_records::unboxed_variants::reassign_enum_local_across_variants
|
|
32
|
+
test "reassign enum local across variants"
|
|
33
|
+
assert runMain() == 31022
|
compiler/tests/wasm_codegen/e2e_records/rest_runs_after_build_and_nested_construction.plum
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Inner(mut n: Int)
|
|
7
|
+
|
|
8
|
+
struct Outer(a: Int) =
|
|
9
|
+
fun init(self) =
|
|
10
|
+
self.inner = Inner(n: self.a + 1)
|
|
11
|
+
self.total = 0
|
|
12
|
+
self.bump()
|
|
13
|
+
fun bump(self) =
|
|
14
|
+
self.inner.n = self.inner.n * 10
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int =
|
|
17
|
+
o := Outer(a: 4)
|
|
18
|
+
o.inner.n + o.a
|
|
19
|
+
|
|
20
|
+
# moved from e2e_records::record_init::rest_runs_after_build_and_nested_construction
|
|
21
|
+
test "rest runs after build and nested construction"
|
|
22
|
+
assert runMain() == 54
|
compiler/tests/wasm_codegen/e2e_records/ship_example_values.plum
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Actor(px: Int, py: Int) =
|
|
7
|
+
fun init(self) =
|
|
8
|
+
self.width: Int = 10
|
|
9
|
+
self.height: Int = self.width * 2
|
|
10
|
+
self.kids: Slice[Int] = Slice[Int]()
|
|
11
|
+
fun area(self) -> Int = self.width * self.height
|
|
12
|
+
|
|
13
|
+
struct Ship(Actor(px, py), tex: Int) =
|
|
14
|
+
fun init(self) =
|
|
15
|
+
self.height = self.width + 1
|
|
16
|
+
self.vx = 7
|
|
17
|
+
override fun area(self) -> Int = self.width * self.height + self.vx
|
|
18
|
+
|
|
19
|
+
fun runMain() -> Int =
|
|
20
|
+
s := Ship(px: 1, py: 2, tex: 3)
|
|
21
|
+
s.px + s.py * 10 + s.tex * 100 + s.area() * 1000
|
|
22
|
+
|
|
23
|
+
# moved from e2e_records::record_init::ship_example_values
|
|
24
|
+
# px 1 + py 20 + tex 300 + (10*11+7)*1000
|
|
25
|
+
test "ship example values"
|
|
26
|
+
assert runMain() == 117321
|
compiler/tests/wasm_codegen/e2e_records/through_a_base_value.plum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Actor(px: Int, width: Int) =
|
|
7
|
+
fun init(self) =
|
|
8
|
+
self.width = 10
|
|
9
|
+
fun area(self) -> Int = self.width * 2
|
|
10
|
+
|
|
11
|
+
struct Ship(Actor(px), tex: Int) =
|
|
12
|
+
override fun area(self) -> Int = self.width * 20
|
|
13
|
+
|
|
14
|
+
struct Rock(Actor(px))
|
|
15
|
+
|
|
16
|
+
fun total(a: Actor) -> Int = a.area() + a.px
|
|
17
|
+
|
|
18
|
+
fun runMain() -> Int =
|
|
19
|
+
total(Ship(px: 1, tex: 0)) * 1000 + total(Rock(px: 2))
|
|
20
|
+
|
|
21
|
+
# moved from e2e_records::record_init::through_a_base_value
|
|
22
|
+
# Ship: 10*20 + 1 = 201 ; Rock: 20 + 2 = 22
|
|
23
|
+
test "through a base value"
|
|
24
|
+
assert runMain() == 201022
|
compiler/tests/wasm_codegen/e2e_records/tree_insert_and_size.plum
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
|
|
4
|
+
enum Tree[T] = Empty | Leaf(v: Int) | Node(l: Tree[T], r: Tree[T])
|
|
5
|
+
|
|
6
|
+
fun size[T](t: Tree[T]) -> Int = match t
|
|
7
|
+
Empty => 0
|
|
8
|
+
Leaf(_) => 1
|
|
9
|
+
Node(l, r) => size(l) + size(r)
|
|
10
|
+
|
|
11
|
+
fun lf() -> Tree[Str] =
|
|
12
|
+
Leaf(v: 3)
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
t := Node(l: lf(), r: Node(l: lf(), r: Empty))
|
|
16
|
+
size(t)
|
|
17
|
+
|
|
18
|
+
# moved from e2e_records::inline_variants::tree_insert_and_size
|
|
19
|
+
test "tree insert and size"
|
|
20
|
+
assert runMain() == 2
|
compiler/tests/wasm_codegen/e2e_records/variant_name_wins.plum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
|
|
4
|
+
enum Link[T] =
|
|
5
|
+
| Nil
|
|
6
|
+
| Cons(head: T, tail: Link[T])
|
|
7
|
+
|
|
8
|
+
fun init(a: T, b: T) -> Link[T] = Cons(head: a, tail: Cons(head: b, tail: Nil))
|
|
9
|
+
|
|
10
|
+
fun length(self) -> Int = match self
|
|
11
|
+
Nil => 0
|
|
12
|
+
Cons(_, t) => 1 + t.length()
|
|
13
|
+
|
|
14
|
+
fun sum(self) -> Int = match self
|
|
15
|
+
Nil => 0
|
|
16
|
+
Cons(h, t) => h + t.sum()
|
|
17
|
+
|
|
18
|
+
fun runMain() -> Int =
|
|
19
|
+
xs := Cons(head: 5, tail: Link(1, 2))
|
|
20
|
+
xs.sum() * 10 + xs.length()
|
|
21
|
+
|
|
22
|
+
# moved from e2e_records::enum_init::variant_name_wins
|
|
23
|
+
test "variant name wins"
|
|
24
|
+
assert runMain() == 83
|
compiler/tests/wasm_codegen/e2e_tail/big_link_built_and_summed.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
enum Link =
|
|
4
|
+
| Nil
|
|
5
|
+
| Cons(head: Int, tail: Link)
|
|
6
|
+
|
|
7
|
+
fun build(n: Int, acc: Link) -> Link = if n == 0 then acc else build(n - 1, Cons(n, acc))
|
|
8
|
+
|
|
9
|
+
fun sum(l: Link, acc: Int) -> Int = match l
|
|
10
|
+
Nil => acc
|
|
11
|
+
Cons(h, t) => sum(t, acc + h)
|
|
12
|
+
|
|
13
|
+
fun runMain() -> Int = sum(build(200000, Nil), 0)
|
|
14
|
+
|
|
15
|
+
# moved from e2e_tail::tail::big_link_built_and_summed
|
|
16
|
+
test "big link built and summed"
|
|
17
|
+
assert runMain() == 20000100000
|
compiler/tests/wasm_codegen/e2e_tail/break_in_else_if_chain_in_while.plum
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
fun runMain() -> Int =
|
|
7
|
+
i := 0
|
|
8
|
+
while i < 50
|
|
9
|
+
if i == 100
|
|
10
|
+
i = i + 1000
|
|
11
|
+
else if i == 100
|
|
12
|
+
i = i + 2000
|
|
13
|
+
else if i == 7
|
|
14
|
+
break
|
|
15
|
+
i = i + 1
|
|
16
|
+
return i
|
|
17
|
+
|
|
18
|
+
# moved from e2e_tail::loop_break::break_in_else_if_chain_in_while
|
|
19
|
+
test "break in else if chain in while"
|
|
20
|
+
assert runMain() == 7
|
compiler/tests/wasm_codegen/e2e_tail/break_in_if_in_match_arm_in_while.plum
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
enum Lst = Nil | Cons(head: Int, tail: Lst)
|
|
8
|
+
|
|
9
|
+
fun build(n: Int, acc: Lst) -> Lst =
|
|
10
|
+
if n == 0 then acc else build(n - 1, Cons(head: n, tail: acc))
|
|
11
|
+
|
|
12
|
+
fun find(xs: Lst, target: Int) -> Int =
|
|
13
|
+
i := 0
|
|
14
|
+
cur := xs
|
|
15
|
+
while i < 50
|
|
16
|
+
match cur
|
|
17
|
+
Cons(h, t) =>
|
|
18
|
+
if h == target
|
|
19
|
+
break
|
|
20
|
+
cur = t
|
|
21
|
+
Nil =>
|
|
22
|
+
break
|
|
23
|
+
i = i + 1
|
|
24
|
+
return i
|
|
25
|
+
|
|
26
|
+
fun runMain() -> Int = find(build(10, Nil), 4) * 100 + find(build(10, Nil), 99)
|
|
27
|
+
|
|
28
|
+
# moved from e2e_tail::loop_break::break_in_if_in_match_arm_in_while
|
|
29
|
+
test "break in if in match arm in while"
|
|
30
|
+
assert runMain() == 310
|
compiler/tests/wasm_codegen/e2e_tail/break_in_match_arm_direct.plum
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
enum Lst = Nil | Cons(head: Int, tail: Lst)
|
|
8
|
+
|
|
9
|
+
fun build(n: Int, acc: Lst) -> Lst =
|
|
10
|
+
if n == 0 then acc else build(n - 1, Cons(head: n, tail: acc))
|
|
11
|
+
|
|
12
|
+
fun count(xs: Lst) -> Int =
|
|
13
|
+
i := 0
|
|
14
|
+
cur := xs
|
|
15
|
+
while i < 50
|
|
16
|
+
match cur
|
|
17
|
+
Nil =>
|
|
18
|
+
break
|
|
19
|
+
Cons(h, t) =>
|
|
20
|
+
cur = t
|
|
21
|
+
i = i + 1
|
|
22
|
+
return i
|
|
23
|
+
|
|
24
|
+
fun wild(n: Int) -> Int =
|
|
25
|
+
i := 0
|
|
26
|
+
while i < 50
|
|
27
|
+
match i
|
|
28
|
+
_ =>
|
|
29
|
+
break
|
|
30
|
+
i = i + 1
|
|
31
|
+
return i + n
|
|
32
|
+
|
|
33
|
+
fun runMain() -> Int = count(build(5, Nil)) * 100 + wild(7)
|
|
34
|
+
|
|
35
|
+
# moved from e2e_tail::loop_break::break_in_match_arm_direct
|
|
36
|
+
test "break in match arm direct"
|
|
37
|
+
assert runMain() == 507
|
compiler/tests/wasm_codegen/e2e_tail/closure_body_tail_call_million.plum
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun countDown(n: Int) -> Int = if n == 0 then 0 else countDown(n - 1)
|
|
4
|
+
|
|
5
|
+
fun runMain() -> Int =
|
|
6
|
+
dec := |n: Int| if n == 0 then 7 else countDown(n - 1) + 0
|
|
7
|
+
fin := |n: Int| countDown(n)
|
|
8
|
+
fin(1000000) + dec(0)
|
|
9
|
+
|
|
10
|
+
# moved from e2e_tail::closure_tail::closure_body_tail_call_million
|
|
11
|
+
test "closure body tail call million"
|
|
12
|
+
assert runMain() == 7
|
compiler/tests/wasm_codegen/e2e_tail/closure_captures_iteration_value.plum
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
fun go(i: Int, f: fn(Int) -> Int) -> Int =
|
|
7
|
+
if i == 3 then f(100) else go(i + 1, |x| x + i)
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int = go(0, |x| x)
|
|
10
|
+
|
|
11
|
+
# moved from e2e_tail::tail::closure_captures_iteration_value
|
|
12
|
+
# R3: a closure made in one iteration keeps that iteration's parameter value.
|
|
13
|
+
test "closure captures iteration value"
|
|
14
|
+
assert runMain() == 102
|
compiler/tests/wasm_codegen/e2e_tail/closure_not_recursive_unchanged.plum
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun runMain() -> Int =
|
|
4
|
+
k := 10
|
|
5
|
+
f := |x| x + k
|
|
6
|
+
f(5)
|
|
7
|
+
|
|
8
|
+
# moved from e2e_tail::closure_tail::closure_not_recursive_unchanged
|
|
9
|
+
test "closure not recursive unchanged"
|
|
10
|
+
assert runMain() == 15
|
compiler/tests/wasm_codegen/e2e_tail/closure_tail_call_runs.plum
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
fun run(n: Int, k: fn(Int) -> Int) -> Int = if n == 0 then 7 else k(n - 1)
|
|
7
|
+
|
|
8
|
+
fun go(n: Int) -> Int = run(n, go)
|
|
9
|
+
|
|
10
|
+
fun runMain() -> Int = go(1000000)
|
|
11
|
+
|
|
12
|
+
# moved from e2e_tail::tail::closure_tail_call_runs
|
|
13
|
+
test "closure tail call runs"
|
|
14
|
+
assert runMain() == 7
|
compiler/tests/wasm_codegen/e2e_tail/continue_in_if_in_match_arm_in_while.plum
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
enum Lst = Nil | Cons(head: Int, tail: Lst)
|
|
8
|
+
|
|
9
|
+
fun build(n: Int, acc: Lst) -> Lst =
|
|
10
|
+
if n == 0 then acc else build(n - 1, Cons(head: n, tail: acc))
|
|
11
|
+
|
|
12
|
+
fun sumOdd(xs: Lst) -> Int =
|
|
13
|
+
total := 0
|
|
14
|
+
i := 0
|
|
15
|
+
cur := xs
|
|
16
|
+
while i < 50
|
|
17
|
+
i = i + 1
|
|
18
|
+
match cur
|
|
19
|
+
Cons(h, t) =>
|
|
20
|
+
cur = t
|
|
21
|
+
if h % 2 == 0
|
|
22
|
+
continue
|
|
23
|
+
total = total + h
|
|
24
|
+
Nil =>
|
|
25
|
+
break
|
|
26
|
+
return total * 100 + i
|
|
27
|
+
|
|
28
|
+
fun runMain() -> Int = sumOdd(build(6, Nil))
|
|
29
|
+
|
|
30
|
+
# moved from e2e_tail::loop_break::continue_in_if_in_match_arm_in_while
|
|
31
|
+
# 1 + 3 + 5 = 9; seven iterations (six cons cells then Nil).
|
|
32
|
+
test "continue in if in match arm in while"
|
|
33
|
+
assert runMain() == 907
|
compiler/tests/wasm_codegen/e2e_tail/method_self_tail.plum
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Counter(step: Int) =
|
|
7
|
+
fun count(self, n: Int, acc: Int) -> Int =
|
|
8
|
+
if n == 0 then acc else self.count(n - 1, acc + self.step)
|
|
9
|
+
|
|
10
|
+
fun runMain() -> Int = Counter(step: 3).count(1000000, 0)
|
|
11
|
+
|
|
12
|
+
# moved from e2e_tail::tail::method_self_tail
|
|
13
|
+
test "method self tail"
|
|
14
|
+
assert runMain() == 3000000
|
compiler/tests/wasm_codegen/e2e_tail/mutual_tail_million.plum
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
fun isEven(n: Int) -> Int = if n == 0 then 1 else isOdd(n - 1)
|
|
7
|
+
|
|
8
|
+
fun isOdd(n: Int) -> Int = if n == 0 then 0 else isEven(n - 1)
|
|
9
|
+
|
|
10
|
+
fun runMain() -> Int = isEven(1000001) * 10 + isOdd(1000001)
|
|
11
|
+
|
|
12
|
+
# moved from e2e_tail::tail::mutual_tail_million
|
|
13
|
+
test "mutual tail million"
|
|
14
|
+
assert runMain() == 1
|
compiler/tests/wasm_codegen/e2e_tail/nested_loops_with_match_in_inner.plum
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
enum Lst = Nil | Cons(head: Int, tail: Lst)
|
|
8
|
+
|
|
9
|
+
fun build(n: Int, acc: Lst) -> Lst =
|
|
10
|
+
if n == 0 then acc else build(n - 1, Cons(head: n, tail: acc))
|
|
11
|
+
|
|
12
|
+
fun runMain() -> Int =
|
|
13
|
+
outer := 0
|
|
14
|
+
hits := 0
|
|
15
|
+
while outer < 50
|
|
16
|
+
if outer == 3
|
|
17
|
+
break
|
|
18
|
+
cur := build(5, Nil)
|
|
19
|
+
j := 0
|
|
20
|
+
while j < 50
|
|
21
|
+
match cur
|
|
22
|
+
Cons(h, t) =>
|
|
23
|
+
cur = t
|
|
24
|
+
if h == 2
|
|
25
|
+
j = j + 1
|
|
26
|
+
continue
|
|
27
|
+
if h == 4
|
|
28
|
+
break
|
|
29
|
+
hits = hits + 1
|
|
30
|
+
Nil =>
|
|
31
|
+
break
|
|
32
|
+
j = j + 1
|
|
33
|
+
outer = outer + 1
|
|
34
|
+
return outer * 100 + hits
|
|
35
|
+
|
|
36
|
+
# moved from e2e_tail::loop_break::nested_loops_with_match_in_inner
|
|
37
|
+
# inner: h=1 hit, h=2 skipped, h=3 hit, h=4 break => 2 hits per outer pass, 3 passes.
|
|
38
|
+
test "nested loops with match in inner"
|
|
39
|
+
assert runMain() == 306
|
compiler/tests/wasm_codegen/e2e_tail/non_tail_union_wrap_not_tail.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
import std/Option
|
|
4
|
+
|
|
5
|
+
fun inner(n: Int) -> Int = n + 1
|
|
6
|
+
|
|
7
|
+
fun outer(n: Int) -> Option[Int] = Some(inner(n))
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
v := outer(41)
|
|
11
|
+
v.unwrapOr(0)
|
|
12
|
+
|
|
13
|
+
# moved from e2e_tail::tail::non_tail_union_wrap_not_tail
|
|
14
|
+
# The callee returns Int but the fn returns Option[Int]: the call is wrapped on the way
|
|
15
|
+
# out, so it must stay a normal call and still produce the right value.
|
|
16
|
+
test "non tail union wrap not tail"
|
|
17
|
+
assert runMain() == 42
|
compiler/tests/wasm_codegen/e2e_tail/param_self_tail.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
struct Cell(x: Int, y: Int)
|
|
7
|
+
|
|
8
|
+
fun bump(c: Cell, n: Int) -> Cell =
|
|
9
|
+
if n == 0 then c else bump(Cell(..c, x: c.x + 1), n - 1)
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int =
|
|
12
|
+
c := bump(Cell(x: 0, y: 5), 100000)
|
|
13
|
+
c.x * 10 + c.y
|
|
14
|
+
|
|
15
|
+
# moved from e2e_tail::tail::param_self_tail
|
|
16
|
+
test "param self tail"
|
|
17
|
+
assert runMain() == 1000005
|
compiler/tests/wasm_codegen/e2e_tail/question_in_tail_not_tail_call.plum
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Result
|
|
3
|
+
|
|
4
|
+
enum NegError = Neg | Other
|
|
5
|
+
|
|
6
|
+
fun down(n: Int) -> Result[Int, NegError] =
|
|
7
|
+
if n == 0
|
|
8
|
+
return Ok(5)
|
|
9
|
+
v := down(n - 1)?
|
|
10
|
+
Ok(v)
|
|
11
|
+
|
|
12
|
+
fun runMain() -> Int = down(100).unwrapOr(99)
|
|
13
|
+
|
|
14
|
+
# moved from e2e_tail::error_tail::question_in_tail_not_tail_call
|
|
15
|
+
test "question in tail not tail call"
|
|
16
|
+
assert runMain() == 5
|
compiler/tests/wasm_codegen/e2e_tail/recursive_closure_captures_outer.plum
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun runMain() -> Int =
|
|
4
|
+
k := 3
|
|
5
|
+
go := |n, acc| if n == 0 then acc else go(n - 1, acc + k)
|
|
6
|
+
go(1000000, 0)
|
|
7
|
+
|
|
8
|
+
# moved from e2e_tail::closure_tail::recursive_closure_captures_outer
|
|
9
|
+
test "recursive closure captures outer"
|
|
10
|
+
assert runMain() == 3000000
|
compiler/tests/wasm_codegen/e2e_tail/recursive_local_closure_million.plum
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun runMain() -> Int =
|
|
4
|
+
go := |n, acc| if n == 0 then acc else go(n - 1, acc + n)
|
|
5
|
+
go(1000000, 0)
|
|
6
|
+
|
|
7
|
+
# moved from e2e_tail::closure_tail::recursive_local_closure_million
|
|
8
|
+
test "recursive local closure million"
|
|
9
|
+
assert runMain() == 500000500000
|
compiler/tests/wasm_codegen/e2e_tail/result_mutual_million.plum
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Result
|
|
3
|
+
|
|
4
|
+
enum NegError = Neg | Other
|
|
5
|
+
|
|
6
|
+
fun ping(n: Int) -> Result[Int, NegError] =
|
|
7
|
+
if n == 0
|
|
8
|
+
return Ok(1)
|
|
9
|
+
pong(n - 1)
|
|
10
|
+
|
|
11
|
+
fun pong(n: Int) -> Result[Int, NegError] =
|
|
12
|
+
if n == 0
|
|
13
|
+
return Ok(2)
|
|
14
|
+
ping(n - 1)
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int = ping(1000000).unwrapOr(99)
|
|
17
|
+
|
|
18
|
+
# moved from e2e_tail::error_tail::result_mutual_million
|
|
19
|
+
test "result mutual million"
|
|
20
|
+
assert runMain() == 1
|
compiler/tests/wasm_codegen/e2e_tail/result_self_loop_million.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Result
|
|
3
|
+
|
|
4
|
+
enum NegError = Neg | Other
|
|
5
|
+
|
|
6
|
+
fun down(n: Int) -> Result[Int, NegError] =
|
|
7
|
+
if n < 0
|
|
8
|
+
return Err(Neg)
|
|
9
|
+
if n == 0
|
|
10
|
+
return Ok(5)
|
|
11
|
+
down(n - 1)
|
|
12
|
+
|
|
13
|
+
fun runMain() -> Int = down(1000000).unwrapOr(99)
|
|
14
|
+
|
|
15
|
+
# moved from e2e_tail::error_tail::result_self_loop_million
|
|
16
|
+
test "result self loop million"
|
|
17
|
+
assert runMain() == 5
|
compiler/tests/wasm_codegen/e2e_tail/result_self_loop_returns_err.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Result
|
|
3
|
+
|
|
4
|
+
enum NegError = Neg | Other
|
|
5
|
+
|
|
6
|
+
fun down(n: Int) -> Result[Int, NegError] =
|
|
7
|
+
if n == 0
|
|
8
|
+
return Err(Neg)
|
|
9
|
+
down(n - 1)
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int = down(1000000).unwrapOr(42)
|
|
12
|
+
|
|
13
|
+
# moved from e2e_tail::error_tail::result_self_loop_returns_err
|
|
14
|
+
test "result self loop returns err"
|
|
15
|
+
assert runMain() == 42
|
compiler/tests/wasm_codegen/e2e_tail/self_calling_closure_typed_params_runs.plum
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun runMain() -> Int =
|
|
4
|
+
fact := |n: Int| if n <= 1 then 1 else n * fact(n - 1)
|
|
5
|
+
fact(5)
|
|
6
|
+
|
|
7
|
+
# moved from e2e_tail::closure_tail::self_calling_closure_typed_params_runs
|
|
8
|
+
test "self calling closure typed params runs"
|
|
9
|
+
assert runMain() == 120
|
compiler/tests/wasm_codegen/e2e_tail/self_calling_closure_untyped_params_runs.plum
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun runMain() -> Int =
|
|
4
|
+
fact := |n| if n <= 1 then 1 else n * fact(n - 1)
|
|
5
|
+
fact(5)
|
|
6
|
+
|
|
7
|
+
# moved from e2e_tail::closure_tail::self_calling_closure_untyped_params_runs
|
|
8
|
+
test "self calling closure untyped params runs"
|
|
9
|
+
assert runMain() == 120
|
compiler/tests/wasm_codegen/e2e_tail/self_tail_args_swap.plum
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
fun gcd(a: Int, b: Int) -> Int = if b == 0 then a else gcd(b, a % b)
|
|
7
|
+
|
|
8
|
+
fun swapN(a: Int, b: Int, n: Int) -> Int =
|
|
9
|
+
if n == 0 then a * 1000 + b else swapN(b, a, n - 1)
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int = gcd(1071, 462) * 1000000 + swapN(1, 2, 999999)
|
|
12
|
+
|
|
13
|
+
# moved from e2e_tail::tail::self_tail_args_swap
|
|
14
|
+
# gcd = 21; odd swap count leaves (2, 1).
|
|
15
|
+
test "self tail args swap"
|
|
16
|
+
assert runMain() == 21002001
|
compiler/tests/wasm_codegen/e2e_tail/self_tail_million.plum
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
enum Link[T] = Nil | Cons(head: T, tail: Link[T])
|
|
8
|
+
|
|
9
|
+
fun build(n: Int, acc: Link[Int]) -> Link[Int] =
|
|
10
|
+
if n == 0 then acc else build(n - 1, Cons(head: n, tail: acc))
|
|
11
|
+
|
|
12
|
+
fun sumAcc(xs: Link[Int], acc: Int) -> Int = match xs
|
|
13
|
+
Nil => acc
|
|
14
|
+
Cons(h, t) => sumAcc(t, acc + h)
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int = sumAcc(build(1000000, Nil), 0)
|
|
17
|
+
|
|
18
|
+
# moved from e2e_tail::tail::self_tail_million
|
|
19
|
+
test "self tail million"
|
|
20
|
+
assert runMain() == 500000500000
|
compiler/tests/wasm_codegen/e2e_tail/self_tail_through_nested_branches.plum
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
fun f(n: Int, acc: Int) -> Int =
|
|
7
|
+
if n == 0 then acc
|
|
8
|
+
else if n % 3 == 0 then f(n - 1, acc + 1)
|
|
9
|
+
else if n % 3 == 1 then f(n - 1, acc + 10)
|
|
10
|
+
else f(n - 1, acc + 100)
|
|
11
|
+
|
|
12
|
+
fun g(n: Int, acc: Int) -> Int = match n
|
|
13
|
+
0 => acc
|
|
14
|
+
k => if k % 2 == 0 then g(k - 1, acc + 2) else g(k - 1, acc + 1)
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int = f(30, 0) * 1000 + g(10, 0)
|
|
17
|
+
|
|
18
|
+
# moved from e2e_tail::tail::self_tail_through_nested_branches
|
|
19
|
+
# Match arms and else-if chains nest wasm blocks between the call and the loop.
|
|
20
|
+
test "self tail through nested branches"
|
|
21
|
+
assert runMain() == 1110015
|
compiler/tests/wasm_codegen/e2e_tail/tail_call_in_match_arm.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
enum Step =
|
|
4
|
+
| Go(n: Int)
|
|
5
|
+
| Stop
|
|
6
|
+
|
|
7
|
+
fun run(s: Step, acc: Int) -> Int = match s
|
|
8
|
+
Go(n) => run(if n == 0 then Stop else Go(n - 1), acc + 1)
|
|
9
|
+
Stop => acc
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int = run(Go(1000000), 0)
|
|
12
|
+
|
|
13
|
+
# moved from e2e_tail::tail::tail_call_in_match_arm
|
|
14
|
+
test "tail call in match arm"
|
|
15
|
+
assert runMain() == 1000001
|
compiler/tests/wasm_codegen/e2e_tail/trmc_link_map_100k.plum
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
enum Link[T] = Nil | Cons(head: T, tail: Link[T])
|
|
8
|
+
|
|
9
|
+
fun build(n: Int, acc: Link[Int]) -> Link[Int] =
|
|
10
|
+
if n == 0 then acc else build(n - 1, Cons(head: n, tail: acc))
|
|
11
|
+
|
|
12
|
+
fun sumAcc(xs: Link[Int], acc: Int) -> Int = match xs
|
|
13
|
+
Nil => acc
|
|
14
|
+
Cons(h, t) => sumAcc(t, acc + h)
|
|
15
|
+
|
|
16
|
+
fun mapInc(xs: Link[Int]) -> Link[Int] = match xs
|
|
17
|
+
Nil => Nil
|
|
18
|
+
Cons(h, t) => Cons(head: h + 1, tail: mapInc(t))
|
|
19
|
+
|
|
20
|
+
fun runMain() -> Int = sumAcc(mapInc(build(100000, Nil)), 0)
|
|
21
|
+
|
|
22
|
+
# moved from e2e_tail::tail::trmc_link_map_100k
|
|
23
|
+
# TRMC: a map over a native-variant list of 100k elements builds without stack growth.
|
|
24
|
+
test "trmc link map 100k"
|
|
25
|
+
assert runMain() == 5000150000
|
compiler/tests/wasm_codegen/e2e_types/adder_two_levels.plum
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun adder(n: Int) -> fn(Int) -> fn(Int) -> Int =
|
|
4
|
+
|x| |y| x + y + n
|
|
5
|
+
|
|
6
|
+
fun runMain() -> Int =
|
|
7
|
+
f := adder(1)
|
|
8
|
+
g := f(2)
|
|
9
|
+
g(3)
|
|
10
|
+
|
|
11
|
+
# moved from e2e_types::closure_types::adder_two_levels
|
|
12
|
+
# `adder(1)(2)` (call on a call result) does not parse: the grammar has no call-on-call form.
|
|
13
|
+
test "adder two levels"
|
|
14
|
+
assert runMain() == 6
|
compiler/tests/wasm_codegen/e2e_types/bang_unwraps_and_chains.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun g(n: Int) -> Option[Pt] = Some(Pt(first: n, second: 2))
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
x := Some(3)!
|
|
11
|
+
y := g(4)!.first
|
|
12
|
+
r: Result[Int, Int] := Ok(5)
|
|
13
|
+
x + y * 10 + r! * 100
|
|
14
|
+
|
|
15
|
+
# moved from e2e_types::question::bang_unwraps_and_chains
|
|
16
|
+
test "bang unwraps and chains"
|
|
17
|
+
assert runMain() == 543
|
compiler/tests/wasm_codegen/e2e_types/bare_tag_fixed_by_later_arg.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
enum Link[T] = Nil | Cons(head: T, tail: Link[T])
|
|
4
|
+
|
|
5
|
+
fun len[T](xs: Link[T]) -> Int = match xs
|
|
6
|
+
Nil => 0
|
|
7
|
+
Cons(_, t) => 1 + len(t)
|
|
8
|
+
|
|
9
|
+
fun push[T](xs: Link[T], x: T) -> Link[T] = Cons(head: x, tail: xs)
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int =
|
|
12
|
+
len(push(push(Nil, 1), 2))
|
|
13
|
+
|
|
14
|
+
# moved from e2e_types::closure_types::bare_tag_fixed_by_later_arg
|
|
15
|
+
# A bare `Nil` whose `T` is fixed by a LATER argument gets the call's `Link[Int]`.
|
|
16
|
+
test "bare tag fixed by later arg"
|
|
17
|
+
assert runMain() == 2
|
compiler/tests/wasm_codegen/e2e_types/bare_tag_fixed_by_other_link_arg.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
enum Link[T] = Nil | Cons(head: T, tail: Link[T])
|
|
4
|
+
|
|
5
|
+
fun len[T](xs: Link[T]) -> Int = match xs
|
|
6
|
+
Nil => 0
|
|
7
|
+
Cons(_, t) => 1 + len(t)
|
|
8
|
+
|
|
9
|
+
fun two[T](a: Link[T], b: Link[T]) -> Int = len(a) + len(b)
|
|
10
|
+
|
|
11
|
+
fun mk(n: Int) -> Link[Int] = Cons(head: n, tail: Nil)
|
|
12
|
+
|
|
13
|
+
fun runMain() -> Int =
|
|
14
|
+
two(Nil, mk(2)) * 10 + two(mk(1), Nil)
|
|
15
|
+
|
|
16
|
+
# moved from e2e_types::closure_types::bare_tag_fixed_by_other_link_arg
|
|
17
|
+
test "bare tag fixed by other link arg"
|
|
18
|
+
assert runMain() == 11
|
compiler/tests/wasm_codegen/e2e_types/bare_variants_in_if_branches_take_fn_return_type.plum
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun pickInline(flag: Bool, n: Int) -> Result[Int, Int] = if flag then Ok(n) else Err(100 + n)
|
|
8
|
+
|
|
9
|
+
fun pickBlock(flag: Bool, n: Int) -> Result[Int, Int] =
|
|
10
|
+
if flag
|
|
11
|
+
Ok(n)
|
|
12
|
+
else
|
|
13
|
+
Err(100 + n)
|
|
14
|
+
|
|
15
|
+
fun maybe(flag: Bool, n: Int) -> Option[Int] = if flag then None else Some(n)
|
|
16
|
+
|
|
17
|
+
fun runMain() -> Int =
|
|
18
|
+
a := pickInline(false, 1).isErr()
|
|
19
|
+
b := pickBlock(true, 2).unwrapOr(0)
|
|
20
|
+
c := maybe(false, 3).unwrapOr(0)
|
|
21
|
+
d := maybe(true, 3).isNone()
|
|
22
|
+
if a && d then b * 10 + c else -1
|
|
23
|
+
|
|
24
|
+
# moved from e2e_types::question::bare_variants_in_if_branches_take_fn_return_type
|
|
25
|
+
test "bare variants in if branches take fn return type"
|
|
26
|
+
assert runMain() == 23
|
compiler/tests/wasm_codegen/e2e_types/bare_variants_in_match_arms_take_fn_return_type.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun pick(n: Int) -> Result[Int, Int] = match n
|
|
8
|
+
0 => Err(100)
|
|
9
|
+
_ => Ok(n)
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int =
|
|
12
|
+
match pick(0)
|
|
13
|
+
Ok(v) => v
|
|
14
|
+
Err(e) => e + pick(4).unwrapOr(0)
|
|
15
|
+
|
|
16
|
+
# moved from e2e_types::question::bare_variants_in_match_arms_take_fn_return_type
|
|
17
|
+
test "bare variants in match arms take fn return type"
|
|
18
|
+
assert runMain() == 104
|
compiler/tests/wasm_codegen/e2e_types/chained_err_value_is_first.plum
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun pick(n: Int) -> Result[Int, Int] =
|
|
8
|
+
if n > 0
|
|
9
|
+
return Ok(n)
|
|
10
|
+
Err(100 + n)
|
|
11
|
+
|
|
12
|
+
fun f(x: Int, y: Int, z: Int) -> Result[Int, Int] =
|
|
13
|
+
p := pick(x)?
|
|
14
|
+
q := pick(y)?
|
|
15
|
+
r := pick(z)?
|
|
16
|
+
Ok(p + q + r)
|
|
17
|
+
|
|
18
|
+
fun runMain() -> Int =
|
|
19
|
+
match f(1, -5, -7)
|
|
20
|
+
Ok(v) => v
|
|
21
|
+
Err(e) => e
|
|
22
|
+
|
|
23
|
+
# moved from e2e_types::question::chained_err_value_is_first
|
|
24
|
+
test "chained err value is first"
|
|
25
|
+
assert runMain() == 95
|
compiler/tests/wasm_codegen/e2e_types/chained_question_returns_first_err.plum
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun pick(n: Int) -> Result[Int, Int] =
|
|
8
|
+
if n > 0
|
|
9
|
+
return Ok(n)
|
|
10
|
+
Err(100 + n)
|
|
11
|
+
|
|
12
|
+
fun f(x: Int, y: Int, z: Int) -> Result[Int, Int] =
|
|
13
|
+
p := pick(x)?
|
|
14
|
+
q := pick(y)?
|
|
15
|
+
r := pick(z)?
|
|
16
|
+
Ok(p + q + r)
|
|
17
|
+
|
|
18
|
+
fun runMain() -> Int =
|
|
19
|
+
s := f(1, 2, 3).unwrapOr(0)
|
|
20
|
+
t := f(1, 0, 0)
|
|
21
|
+
bad := t.isErr()
|
|
22
|
+
if bad then s * 1000 + 1 else 0
|
|
23
|
+
|
|
24
|
+
# moved from e2e_types::question::chained_question_returns_first_err
|
|
25
|
+
test "chained question returns first err"
|
|
26
|
+
assert runMain() == 6001
|
compiler/tests/wasm_codegen/e2e_types/closure_coerced_into_optional_return.plum
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
|
|
4
|
+
fun ap(f: fn() -> Option[Int]) -> Int =
|
|
5
|
+
f().unwrapOr(0)
|
|
6
|
+
|
|
7
|
+
fun runMain() -> Int =
|
|
8
|
+
ap(|| Some(3))
|
|
9
|
+
|
|
10
|
+
# moved from e2e_types::closure_optional::closure_coerced_into_optional_return
|
|
11
|
+
test "closure coerced into optional return"
|
|
12
|
+
assert runMain() == 3
|
compiler/tests/wasm_codegen/e2e_types/closure_into_ref_option_and_result.plum
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Str
|
|
3
|
+
import std/Option
|
|
4
|
+
import std/Result
|
|
5
|
+
|
|
6
|
+
enum BadError = Bad | Worse
|
|
7
|
+
|
|
8
|
+
fun s(f: fn(Int) -> Option[Str]) -> Str =
|
|
9
|
+
f(1).unwrapOr("none")
|
|
10
|
+
|
|
11
|
+
fun e(f: fn(Int) -> Result[Int, BadError]) -> Int =
|
|
12
|
+
f(1).unwrapOr(9)
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
b := s(|x| {if x > 0 then Some("yes") else None}).length()
|
|
16
|
+
b2 := s(|x| Some("ab")).length()
|
|
17
|
+
c := e(|x| Ok(x + 1))
|
|
18
|
+
d := e(|x| Err(Bad))
|
|
19
|
+
b * 1000 + b2 * 100 + c * 10 + d
|
|
20
|
+
|
|
21
|
+
# moved from e2e_types::closure_optional::closure_into_ref_option_and_result
|
|
22
|
+
test "closure into ref option and result"
|
|
23
|
+
assert runMain() == 3229
|
compiler/tests/wasm_codegen/e2e_types/closure_passes_none_to_an_option_param.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
import std/Option
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
rec := |o: Option[Int], acc: Int, n: Int|
|
|
10
|
+
if n <= 0 then acc else rec(None, acc + 1, n - 1)
|
|
11
|
+
rec(None, 0, 3)
|
|
12
|
+
|
|
13
|
+
# moved from e2e_types::synthetic_sites::closure_passes_none_to_an_option_param
|
|
14
|
+
# F-T9-2: a closure calling itself with `None` for an `Option[Int]` param. The checker
|
|
15
|
+
# types that `None` as `Option[Unit]`; the param's declared type picks the singleton, so the
|
|
16
|
+
# wasm is valid and the value right.
|
|
17
|
+
test "closure passes none to an option param"
|
|
18
|
+
assert runMain() == 3
|
compiler/tests/wasm_codegen/e2e_types/closure_returning_none_into_optional.plum
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
|
|
4
|
+
fun ap(f: fn() -> Option[Int]) -> Int =
|
|
5
|
+
f().unwrapOr(7)
|
|
6
|
+
|
|
7
|
+
fun runMain() -> Int =
|
|
8
|
+
ap(|| None)
|
|
9
|
+
|
|
10
|
+
# moved from e2e_types::closure_optional::closure_returning_none_into_optional
|
|
11
|
+
test "closure returning none into optional"
|
|
12
|
+
assert runMain() == 7
|
compiler/tests/wasm_codegen/e2e_types/curried_closure_runs.plum
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun curry() -> fn(Int) -> fn(Int) -> Int =
|
|
4
|
+
|x| |y| x + y
|
|
5
|
+
|
|
6
|
+
fun runMain() -> Int =
|
|
7
|
+
add2 := curry()
|
|
8
|
+
f := add2(2)
|
|
9
|
+
f(3)
|
|
10
|
+
|
|
11
|
+
# moved from e2e_types::closure_types::curried_closure_runs
|
|
12
|
+
test "curried closure runs"
|
|
13
|
+
assert runMain() == 5
|
compiler/tests/wasm_codegen/e2e_types/err_field_is_error.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun runMain() -> Int =
|
|
8
|
+
r: Result[Int, Int] := Err(error: 5)
|
|
9
|
+
match r
|
|
10
|
+
Ok(v) => v
|
|
11
|
+
Err(e) => e
|
|
12
|
+
|
|
13
|
+
# moved from e2e_types::question::err_field_is_error
|
|
14
|
+
test "err field is error"
|
|
15
|
+
assert runMain() == 5
|
compiler/tests/wasm_codegen/e2e_types/field_less_variant_called_like_a_constructor.plum
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
enum Maybe =
|
|
7
|
+
| Nothing
|
|
8
|
+
| Just(value: Int)
|
|
9
|
+
|
|
10
|
+
fun orZero(m: Maybe) -> Int = match m
|
|
11
|
+
Nothing => 0
|
|
12
|
+
Just(v) => v
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
a: Maybe := Nothing()
|
|
16
|
+
b: Maybe := Just(value: 40)
|
|
17
|
+
orZero(a) + orZero(b) + 2
|
|
18
|
+
|
|
19
|
+
# moved from e2e_types::synthetic_sites::field_less_variant_called_like_a_constructor
|
|
20
|
+
# `Nothing()` (call syntax on a field-less variant) is the variant's
|
|
21
|
+
# singleton, typed by the call node.
|
|
22
|
+
test "field less variant called like a constructor"
|
|
23
|
+
assert runMain() == 42
|
compiler/tests/wasm_codegen/e2e_types/field_less_variant_of_a_generic_enum_called_like_a_constructor.plum
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
enum Maybe[T] =
|
|
7
|
+
| Nothing
|
|
8
|
+
| Just(value: T)
|
|
9
|
+
|
|
10
|
+
fun orZero(m: Maybe[Int]) -> Int = match m
|
|
11
|
+
Nothing => 0
|
|
12
|
+
Just(v) => v
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
a: Maybe[Int] := Nothing()
|
|
16
|
+
b: Maybe[Int] := Just(value: 40)
|
|
17
|
+
orZero(a) + orZero(b) + 2
|
|
18
|
+
|
|
19
|
+
# moved from e2e_types::synthetic_sites::field_less_variant_of_a_generic_enum_called_like_a_constructor
|
|
20
|
+
# The same call syntax on a variant of a generic enum: the call has the enum's instantiated type.
|
|
21
|
+
test "field less variant of a generic enum called like a constructor"
|
|
22
|
+
assert runMain() == 42
|
compiler/tests/wasm_codegen/e2e_types/fn_field_returning_optional.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
|
|
4
|
+
struct Gen(next: fn() -> Option[Int])
|
|
5
|
+
|
|
6
|
+
fun count(g: Gen) -> Int =
|
|
7
|
+
g.next().unwrapOr(0)
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
k := 5
|
|
11
|
+
count(Gen(next: || Some(k)))
|
|
12
|
+
|
|
13
|
+
# moved from e2e_types::closure_optional::fn_field_returning_optional
|
|
14
|
+
test "fn field returning optional"
|
|
15
|
+
assert runMain() == 5
|
compiler/tests/wasm_codegen/e2e_types/g1_typed_param.plum
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
struct Box(v: Int) =
|
|
4
|
+
fun get(self) -> Int = self.v
|
|
5
|
+
|
|
6
|
+
fun ap(f: fn(Box) -> Int) -> Int = f(Box(v: 2))
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
ap(|b: Box| b.get())
|
|
10
|
+
|
|
11
|
+
# moved from e2e_types::closure_types::g1_typed_param
|
|
12
|
+
test "g1 typed param"
|
|
13
|
+
assert runMain() == 2
|
compiler/tests/wasm_codegen/e2e_types/g2_local_closure_inferred_from_first_call.plum
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
struct Box(v: Int) =
|
|
4
|
+
fun get(self) -> Int = self.v
|
|
5
|
+
|
|
6
|
+
fun runMain() -> Int =
|
|
7
|
+
get := |b| b.get()
|
|
8
|
+
get(Box(v: 2))
|
|
9
|
+
|
|
10
|
+
# moved from e2e_types::closure_types::g2_local_closure_inferred_from_first_call
|
|
11
|
+
test "g2 local closure inferred from first call"
|
|
12
|
+
assert runMain() == 2
|
compiler/tests/wasm_codegen/e2e_types/g5_returned_closure.plum
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun adder(n: Int) -> fn(Int) -> Int =
|
|
4
|
+
|x| x + n
|
|
5
|
+
|
|
6
|
+
fun runMain() -> Int =
|
|
7
|
+
add1 := adder(1)
|
|
8
|
+
add1(2)
|
|
9
|
+
|
|
10
|
+
# moved from e2e_types::closure_types::g5_returned_closure
|
|
11
|
+
test "g5 returned closure"
|
|
12
|
+
assert runMain() == 3
|
compiler/tests/wasm_codegen/e2e_types/inline_if_joins_bare_variant_instantiations.plum
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun runMain() -> Int =
|
|
8
|
+
flag := 3 > 2
|
|
9
|
+
o := if flag then Ok(4) else Err(5)
|
|
10
|
+
low := if flag then Err(1) else Ok(Pt(first: 1, second: 2))
|
|
11
|
+
a := o.unwrapOr(0)
|
|
12
|
+
match low
|
|
13
|
+
Ok(pt) => pt.first
|
|
14
|
+
Err(e) => a * 10 + e
|
|
15
|
+
|
|
16
|
+
# moved from e2e_types::question::inline_if_joins_bare_variant_instantiations
|
|
17
|
+
# An unannotated inline if: each bare variant pins the generic the other leaves open.
|
|
18
|
+
test "inline if joins bare variant instantiations"
|
|
19
|
+
assert runMain() == 41
|
compiler/tests/wasm_codegen/e2e_types/inlined_constant_call_keeps_return_type.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun pick(flag: Bool, n: Int) -> Result[Pt, Int] = if flag then Ok(Pt(first: n, second: 1)) else Err(100 + n)
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
match pick(false, 1)
|
|
11
|
+
Ok(pt) => pt.first
|
|
12
|
+
Err(e) => e
|
|
13
|
+
|
|
14
|
+
# moved from e2e_types::question::inlined_constant_call_keeps_return_type
|
|
15
|
+
# Fusion inlines `pick(false, 1)` to a bare `Err(101)` and drops `Result[Pt, Int]`
|
|
16
|
+
# (plum-core simplify), so codegen builds `Ok`'s payload as Unit.
|
|
17
|
+
test "inlined constant call keeps return type"
|
|
18
|
+
assert runMain() == 101
|
compiler/tests/wasm_codegen/e2e_types/list_literal_element_pinned_by_a_later_element.plum
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/List
|
|
4
|
+
|
|
5
|
+
enum Node[M] =
|
|
6
|
+
| Box(w: Int)
|
|
7
|
+
| Tag(msg: M, w: Int)
|
|
8
|
+
| Group(kids: List[Node[M]])
|
|
9
|
+
| Wrap(child: Node[M], k: Int)
|
|
10
|
+
|
|
11
|
+
fun total(self) -> Int = match self
|
|
12
|
+
Box(w) => w
|
|
13
|
+
Tag(_, w) => w
|
|
14
|
+
Group(kids) => Node.sumKids(kids)
|
|
15
|
+
Wrap(child, k) => Node.twice(child, k)
|
|
16
|
+
|
|
17
|
+
fun twice(child: Node[M], k: Int) -> Int = child.total() + child.total() + k
|
|
18
|
+
|
|
19
|
+
fun sumKids(kids: List[Node[M]]) -> Int = match kids
|
|
20
|
+
Nil => 0
|
|
21
|
+
Cons(head, tail) => head.total() + Node.sumKids(tail)
|
|
22
|
+
|
|
23
|
+
# A second instantiation, so a wrongly pinned element picks a different type.
|
|
24
|
+
fun other(n: Node[Int]) -> Int = n.total()
|
|
25
|
+
|
|
26
|
+
fun tagged(s: Str) -> Node[Str] = Tag(msg: s, w: 2)
|
|
27
|
+
|
|
28
|
+
fun view(s: Str) -> Node[Str] =
|
|
29
|
+
t := tagged(s)
|
|
30
|
+
Wrap(child: Group(kids: [Box(w: 1), t, Box(w: 3)]), k: 100)
|
|
31
|
+
|
|
32
|
+
fun runMain() -> Int =
|
|
33
|
+
view("x").total() + other(Box(w: 10))
|
|
34
|
+
|
|
35
|
+
# moved from e2e_types::list_and_shadow::list_literal_element_pinned_by_a_later_element
|
|
36
|
+
test "list literal element pinned by a later element"
|
|
37
|
+
assert runMain() == 122
|
compiler/tests/wasm_codegen/e2e_types/map_err_closure_param_shadows_top_level_fn.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun f(flag: Bool, n: Int) -> Result[Int, Int] = if flag then Ok(n) else Err(100 + n)
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
match f(false, 1).mapErr(|e| e + 1000)
|
|
11
|
+
Ok(v) => v
|
|
12
|
+
Err(e) => e
|
|
13
|
+
|
|
14
|
+
# moved from e2e_types::question::map_err_closure_param_shadows_top_level_fn
|
|
15
|
+
# mapErr's closure param `f` must not take the arity of a top-level `fun f`.
|
|
16
|
+
test "map err closure param shadows top level fn"
|
|
17
|
+
assert runMain() == 1101
|
compiler/tests/wasm_codegen/e2e_types/map_err_on_call_result.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun fails() -> Result[Int, Int] =
|
|
8
|
+
r: Result[Int, Int] := Err(7)
|
|
9
|
+
r
|
|
10
|
+
|
|
11
|
+
fun runMain() -> Int =
|
|
12
|
+
match fails().mapErr(|e| e + 1000)
|
|
13
|
+
Ok(v) => v
|
|
14
|
+
Err(e) => e
|
|
15
|
+
|
|
16
|
+
# moved from e2e_types::question::map_err_on_call_result
|
|
17
|
+
test "map err on call result"
|
|
18
|
+
assert runMain() == 1007
|
compiler/tests/wasm_codegen/e2e_types/map_err_then_unwrap_or_fuses.plum
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun pick(flag: Bool, n: Int) -> Result[Int, Int] = if flag then Ok(n) else Err(100 + n)
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
pick(true, 2).mapErr(|e| e + 1).unwrapOr(0)
|
|
11
|
+
|
|
12
|
+
# moved from e2e_types::question::map_err_then_unwrap_or_fuses
|
|
13
|
+
# Simplify inlines `pick(true, 2)` to `Ok(2)`; drive then fuses `unwrapOr` over `mapErr`
|
|
14
|
+
# into a fn generic in `E`, which a bare `Ok(2)` never binds (no instance is emitted).
|
|
15
|
+
test "map err then unwrap or fuses"
|
|
16
|
+
assert runMain() == 2
|
compiler/tests/wasm_codegen/e2e_types/nested_generic.plum
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Slice
|
|
3
|
+
import std/List
|
|
4
|
+
|
|
5
|
+
struct Pt(x: Int) =
|
|
6
|
+
fun make(n: Int) -> Pt = Pt(x: n + 1)
|
|
7
|
+
|
|
8
|
+
fun buildAll[T](ns: Slice[Int]) -> Slice[T] =
|
|
9
|
+
out := Slice[T]()
|
|
10
|
+
for n := range ns
|
|
11
|
+
out.push(T.make(n))
|
|
12
|
+
return out
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
ps: Slice[Pt] := buildAll([1, 2, 3].toSlice())
|
|
16
|
+
p := ps.get(2).unwrapOr(Pt(x: 0))
|
|
17
|
+
return p.x
|
|
18
|
+
|
|
19
|
+
# moved from e2e_types::type_param_static::nested_generic
|
|
20
|
+
test "nested generic"
|
|
21
|
+
assert runMain() == 4
|
compiler/tests/wasm_codegen/e2e_types/none_as_generic_param_default.plum
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun g[T](x: T, o: Option[T] = None) -> Option[T] = o
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
a := g(1).unwrapOr(7)
|
|
11
|
+
b := g(1, Some(3)).unwrapOr(0)
|
|
12
|
+
a * 10 + b
|
|
13
|
+
|
|
14
|
+
# moved from e2e_types::question::none_as_generic_param_default
|
|
15
|
+
test "none as generic param default"
|
|
16
|
+
assert runMain() == 73
|
compiler/tests/wasm_codegen/e2e_types/none_as_param_default.plum
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun f(n: Option[Int] = None) -> Option[Int] = n
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
a := f().unwrapOr(7)
|
|
11
|
+
b := f(Some(3)).unwrapOr(0)
|
|
12
|
+
a * 10 + b
|
|
13
|
+
|
|
14
|
+
# moved from e2e_types::question::none_as_param_default
|
|
15
|
+
test "none as param default"
|
|
16
|
+
assert runMain() == 73
|
compiler/tests/wasm_codegen/e2e_types/ok_unit_and_expression_position.plum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun g(n: Int) -> Option[Int] = if n > 0 then Some(n) else None
|
|
8
|
+
|
|
9
|
+
fun u(n: Int) -> Result[Unit, Int] =
|
|
10
|
+
if n > 0
|
|
11
|
+
return Ok(Unit)
|
|
12
|
+
Err(n)
|
|
13
|
+
|
|
14
|
+
fun h(n: Int) -> Option[Int] = Some(1 + g(n)?)
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int =
|
|
17
|
+
a := h(5).unwrapOr(0)
|
|
18
|
+
b := u(1).isOk()
|
|
19
|
+
c := u(0).isErr()
|
|
20
|
+
if b && c then a else -1
|
|
21
|
+
|
|
22
|
+
# moved from e2e_types::question::ok_unit_and_expression_position
|
|
23
|
+
test "ok unit and expression position"
|
|
24
|
+
assert runMain() == 6
|
compiler/tests/wasm_codegen/e2e_types/option_question_returns_none.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun g(n: Int) -> Option[Int] = if n > 0 then Some(n) else None
|
|
8
|
+
|
|
9
|
+
fun f(n: Int) -> Option[Int] =
|
|
10
|
+
v := g(n)?
|
|
11
|
+
Some(v * 2)
|
|
12
|
+
|
|
13
|
+
fun runMain() -> Int =
|
|
14
|
+
f(4).unwrapOr(-1) * 100 + f(0).unwrapOr(7)
|
|
15
|
+
|
|
16
|
+
# moved from e2e_types::question::option_question_returns_none
|
|
17
|
+
test "option question returns none"
|
|
18
|
+
assert runMain() == 807
|
compiler/tests/wasm_codegen/e2e_types/reused_cell_is_read_through_its_local.plum
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
enum Link =
|
|
7
|
+
| Nil
|
|
8
|
+
| Cons(head: Int, tail: Link)
|
|
9
|
+
|
|
10
|
+
fun bump(xs: Link) -> Link = match xs
|
|
11
|
+
Nil => Nil
|
|
12
|
+
Cons(h, t) => Cons(head: h + 1, tail: t)
|
|
13
|
+
|
|
14
|
+
fun sum(xs: Link) -> Int = match xs
|
|
15
|
+
Nil => 0
|
|
16
|
+
Cons(h, t) => h + sum(t)
|
|
17
|
+
|
|
18
|
+
fun runMain() -> Int = sum(bump(Cons(head: 1, tail: Cons(head: 2, tail: Nil))))
|
|
19
|
+
|
|
20
|
+
# moved from e2e_types::synthetic_sites::reused_cell_is_read_through_its_local
|
|
21
|
+
# A rebuild that reuses the matched cell reads the cell's type off the local it names.
|
|
22
|
+
test "reused cell is read through its local"
|
|
23
|
+
assert runMain() == 4
|
compiler/tests/wasm_codegen/e2e_types/seq_with_optional_next.plum
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
|
|
4
|
+
struct Seq[T](next: fn(Int) -> Option[T]) =
|
|
5
|
+
fun upTo(n: Int) -> Seq[Int] = Seq(next: |i| {if i < n then Some(i * 2) else None})
|
|
6
|
+
fun sum(self) -> Int =
|
|
7
|
+
total := 0
|
|
8
|
+
i := 0
|
|
9
|
+
while Some(v) := self.next(i)
|
|
10
|
+
total = total + v
|
|
11
|
+
i = i + 1
|
|
12
|
+
total
|
|
13
|
+
|
|
14
|
+
fun runMain() -> Int =
|
|
15
|
+
Seq.upTo(4).sum()
|
|
16
|
+
|
|
17
|
+
# moved from e2e_types::closure_optional::seq_with_optional_next
|
|
18
|
+
test "seq with optional next"
|
|
19
|
+
assert runMain() == 12
|
compiler/tests/wasm_codegen/e2e_types/single_letter_types_and_variants.plum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
struct P(x: Int)
|
|
4
|
+
|
|
5
|
+
enum Two = A | B
|
|
6
|
+
|
|
7
|
+
struct Cfg(n: Int) =
|
|
8
|
+
E = 5
|
|
9
|
+
|
|
10
|
+
fun pick(t: Two) -> Int = match t
|
|
11
|
+
A => 1
|
|
12
|
+
B => 20
|
|
13
|
+
|
|
14
|
+
fun id[Elem](x: Elem) -> Elem = x
|
|
15
|
+
|
|
16
|
+
fun runMain() -> Int =
|
|
17
|
+
p := P(x: id(3))
|
|
18
|
+
pick(A) + pick(B) + p.x + Cfg.E
|
|
19
|
+
|
|
20
|
+
# moved from e2e_types::type_param_static::single_letter_types_and_variants
|
|
21
|
+
# Single uppercase letters are ordinary names: a struct, enum variants, a constant, and a
|
|
22
|
+
# declared multi-letter fn generic all work side by side.
|
|
23
|
+
test "single letter types and variants"
|
|
24
|
+
assert runMain() == 29
|
compiler/tests/wasm_codegen/e2e_types/specialised_arm_keeps_result_type.plum
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun pick(flag: Bool, n: Int) -> Result[Pt, Int] = if flag then Ok(Pt(first: n, second: 1)) else Err(100 + n)
|
|
8
|
+
|
|
9
|
+
fun viaMapErr() -> Int =
|
|
10
|
+
match pick(false, 2).mapErr(|e| e + 1)
|
|
11
|
+
Ok(pt) => pt.first
|
|
12
|
+
Err(e) => e
|
|
13
|
+
|
|
14
|
+
fun viaOk() -> Int =
|
|
15
|
+
r := pick(false, 4)
|
|
16
|
+
match r.ok()
|
|
17
|
+
Some(pt) => pt.first
|
|
18
|
+
None => 8
|
|
19
|
+
|
|
20
|
+
fun runMain() -> Int = viaMapErr() * 10 + viaOk()
|
|
21
|
+
|
|
22
|
+
# moved from e2e_types::question::specialised_arm_keeps_result_type
|
|
23
|
+
# Drive specialises `mapErr` / `ok` on the inlined `Err(..)`: the picked arm's own `Err` /
|
|
24
|
+
# `None` leaves `mapErr` / `ok` and must keep its `Result[Pt, Int]` / `Option[Pt]`.
|
|
25
|
+
test "specialised arm keeps result type"
|
|
26
|
+
assert runMain() == 1038
|
compiler/tests/wasm_codegen/e2e_types/static_method_of_generic_type_not_pinning_t.plum
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
struct Gen[T](v: T) =
|
|
4
|
+
fun ints(n: Int) -> Gen[Int] = Gen(v: n)
|
|
5
|
+
fun get(self) -> T = self.v
|
|
6
|
+
|
|
7
|
+
fun runMain() -> Int =
|
|
8
|
+
Gen.ints(4).get()
|
|
9
|
+
|
|
10
|
+
# moved from e2e_types::closure_optional::static_method_of_generic_type_not_pinning_t
|
|
11
|
+
test "static method of generic type not pinning t"
|
|
12
|
+
assert runMain() == 4
|
compiler/tests/wasm_codegen/e2e_types/static_on_builtin.plum
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
fun maxOf[T](n: Int) -> T = T.Max
|
|
4
|
+
|
|
5
|
+
fun runMain() -> Int =
|
|
6
|
+
m: Int := maxOf(1)
|
|
7
|
+
return if m == Int.Max then 1 else 0
|
|
8
|
+
|
|
9
|
+
# moved from e2e_types::type_param_static::static_on_builtin
|
|
10
|
+
test "static on builtin"
|
|
11
|
+
assert runMain() == 1
|
compiler/tests/wasm_codegen/e2e_types/static_on_builtin_method_and_json_cycle.plum
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Str
|
|
3
|
+
import std/Json
|
|
4
|
+
import std/Result
|
|
5
|
+
|
|
6
|
+
fun parseIt[T](s: Str) -> Result[T, NumError] =
|
|
7
|
+
T.parse(s)
|
|
8
|
+
|
|
9
|
+
fun runMain() -> Int =
|
|
10
|
+
r: Result[Int, NumError] := parseIt("41")
|
|
11
|
+
n := r.unwrapOr(0)
|
|
12
|
+
j := n.toJson()
|
|
13
|
+
return n + 1
|
|
14
|
+
|
|
15
|
+
# moved from e2e_types::type_param_static::static_on_builtin_method_and_json_cycle
|
|
16
|
+
test "static on builtin method and json cycle"
|
|
17
|
+
assert runMain() == 42
|
compiler/tests/wasm_codegen/e2e_types/static_on_type_param.plum
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
struct Pt(x: Int) =
|
|
4
|
+
fun make(n: Int) -> Pt = Pt(x: n * 10)
|
|
5
|
+
|
|
6
|
+
fun build[T](n: Int) -> T = T.make(n)
|
|
7
|
+
|
|
8
|
+
fun runMain() -> Int =
|
|
9
|
+
p: Pt := build(3)
|
|
10
|
+
return p.x
|
|
11
|
+
|
|
12
|
+
# moved from e2e_types::type_param_static::static_on_type_param
|
|
13
|
+
test "static on type param"
|
|
14
|
+
assert runMain() == 30
|
compiler/tests/wasm_codegen/e2e_types/struct_payload_in_some.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
import std/Option
|
|
3
|
+
import std/Result
|
|
4
|
+
|
|
5
|
+
struct Pt(first: Int, second: Int)
|
|
6
|
+
|
|
7
|
+
fun g(n: Int) -> Option[Pt] = if n > 0 then Some(Pt(first: n, second: 2)) else None
|
|
8
|
+
|
|
9
|
+
fun f(n: Int) -> Option[Int] =
|
|
10
|
+
p := g(n)?
|
|
11
|
+
Some(p.first * 10 + p.second)
|
|
12
|
+
|
|
13
|
+
fun runMain() -> Int =
|
|
14
|
+
f(3).unwrapOr(0)
|
|
15
|
+
|
|
16
|
+
# moved from e2e_types::question::struct_payload_in_some
|
|
17
|
+
test "struct payload in some"
|
|
18
|
+
assert runMain() == 32
|
compiler/tests/wasm_codegen/e2e_types/unannotated_local_of_list_pinned_by_a_later_element.plum
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/List
|
|
4
|
+
|
|
5
|
+
enum Node[M] =
|
|
6
|
+
| Box(w: Int)
|
|
7
|
+
| Tag(msg: M, w: Int)
|
|
8
|
+
| Group(kids: List[Node[M]])
|
|
9
|
+
| Wrap(child: Node[M], k: Int)
|
|
10
|
+
|
|
11
|
+
fun total(self) -> Int = match self
|
|
12
|
+
Box(w) => w
|
|
13
|
+
Tag(_, w) => w
|
|
14
|
+
Group(kids) => Node.sumKids(kids)
|
|
15
|
+
Wrap(child, k) => Node.twice(child, k)
|
|
16
|
+
|
|
17
|
+
fun twice(child: Node[M], k: Int) -> Int = child.total() + child.total() + k
|
|
18
|
+
|
|
19
|
+
fun sumKids(kids: List[Node[M]]) -> Int = match kids
|
|
20
|
+
Nil => 0
|
|
21
|
+
Cons(head, tail) => head.total() + Node.sumKids(tail)
|
|
22
|
+
|
|
23
|
+
# A second instantiation, so a wrongly pinned element picks a different type.
|
|
24
|
+
fun other(n: Node[Int]) -> Int = n.total()
|
|
25
|
+
|
|
26
|
+
fun tagged(s: Str) -> Node[Str] = Tag(msg: s, w: 2)
|
|
27
|
+
|
|
28
|
+
fun view(s: Str) -> Int =
|
|
29
|
+
t := tagged(s)
|
|
30
|
+
g := Group(kids: [Box(w: 1), t])
|
|
31
|
+
q := g
|
|
32
|
+
g.total() + q.total()
|
|
33
|
+
|
|
34
|
+
fun runMain() -> Int =
|
|
35
|
+
view("x") + other(Box(w: 10))
|
|
36
|
+
|
|
37
|
+
# moved from e2e_types::list_and_shadow::unannotated_local_of_list_pinned_by_a_later_element
|
|
38
|
+
test "unannotated local of list pinned by a later element"
|
|
39
|
+
assert runMain() == 16
|
compiler/tests/wasm_codegen/e2e_types/unit_closure_local_called_as_a_statement.plum
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
fun runMain() -> Int =
|
|
7
|
+
f := || Unit
|
|
8
|
+
f()
|
|
9
|
+
f()
|
|
10
|
+
7
|
|
11
|
+
|
|
12
|
+
# moved from e2e_types::synthetic_sites::unit_closure_local_called_as_a_statement
|
|
13
|
+
# A call through a local closure whose type is only known from the local's own initialiser.
|
|
14
|
+
test "unit closure local called as a statement"
|
|
15
|
+
assert runMain() == 7
|
compiler/tests/wasm_codegen/e2e_types/untyped_closure_passes_none_to_an_option_param.plum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import std/Str
|
|
2
|
+
import std/Int
|
|
3
|
+
import std/Float
|
|
4
|
+
import std/Slice
|
|
5
|
+
|
|
6
|
+
import std/Option
|
|
7
|
+
|
|
8
|
+
fun run(n: Int, o: Option[Int]) -> Int =
|
|
9
|
+
rec := |o, acc, n|
|
|
10
|
+
if n <= 0 then 0 else rec(None, acc + 1, n - 1)
|
|
11
|
+
rec(o, 0, n)
|
|
12
|
+
|
|
13
|
+
fun runMain() -> Int =
|
|
14
|
+
run(3, None)
|
|
15
|
+
|
|
16
|
+
# moved from e2e_types::synthetic_sites::untyped_closure_passes_none_to_an_option_param
|
|
17
|
+
test "untyped closure passes none to an option param"
|
|
18
|
+
assert runMain() == 0
|
compiler/tests/wasm_codegen/e2e_types/user_type_shadows_std_pair_imported_by_std.plum
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import std/Int
|
|
2
|
+
|
|
3
|
+
struct Pair(a: Int, b: Int) =
|
|
4
|
+
fun sum(self) -> Int = self.a + self.b
|
|
5
|
+
|
|
6
|
+
fun runMain() -> Int =
|
|
7
|
+
p := Pair(a: 3, b: 4)
|
|
8
|
+
for i := range 3
|
|
9
|
+
p = Pair(a: p.a + i, b: p.b)
|
|
10
|
+
p.a * 10 + p.sum()
|
|
11
|
+
|
|
12
|
+
# moved from e2e_types::list_and_shadow::user_type_shadows_std_pair_imported_by_std
|
|
13
|
+
test "user type shadows std pair imported by std"
|
|
14
|
+
assert runMain() == 70
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_mem/unique_inplace.rs
CHANGED
|
@@ -38,43 +38,6 @@ fn unique_spread_reuses_object() {
|
|
|
38
38
|
assert_eq!(runInt("reuse_run", &unique), 207);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
#[test]
|
|
42
|
-
fn plain_spread_still_copies() {
|
|
43
|
-
let src = prog("struct Cell(x: Int, y: Int)
|
|
44
|
-
|
|
45
|
-
fun main() -> Int =
|
|
46
|
-
s := Cell(x: 1, y: 7)
|
|
47
|
-
t := Cell(..s, x: 5)
|
|
48
|
-
s.x * 1000 + t.x * 100 + t.y
|
|
49
|
-
");
|
|
50
|
-
assert_eq!(runInt("plain", &src), 1000 + 500 + 7);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
#[test]
|
|
54
|
-
fn unique_spread_sets_fields() {
|
|
55
|
-
let src = prog("struct Cell(x: Int, y: Int, z: Int)
|
|
56
|
-
|
|
57
|
-
fun main() -> Int =
|
|
58
|
-
s := Cell(x: 1, y: 2, z: 3)
|
|
59
|
-
t := Cell(..s, z: 30, x: 10)
|
|
60
|
-
t.x * 10000 + t.y * 100 + t.z
|
|
61
|
-
");
|
|
62
|
-
assert_eq!(runInt("sets", &src), 100000 + 200 + 30);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/// A value reading an already-overwritten field must still see the OLD value.
|
|
66
|
-
#[test]
|
|
67
|
-
fn unique_spread_swap_reads_old_values() {
|
|
68
|
-
let src = prog("struct Cell(x: Int, y: Int)
|
|
69
|
-
|
|
70
|
-
fun main() -> Int =
|
|
71
|
-
s := Cell(x: 1, y: 2)
|
|
72
|
-
t := Cell(..s, x: s.y, y: s.x)
|
|
73
|
-
t.x * 10 + t.y
|
|
74
|
-
");
|
|
75
|
-
assert_eq!(runInt("swap", &src), 21);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
41
|
#[test]
|
|
79
42
|
fn unique_base_record_spread_in_place() {
|
|
80
43
|
let unique = prog("struct Actor() =\n fun go(self) -> Int = 7\n\nstruct Ship(Actor, x: Int, hp: Int)\n\nfun hit(s: Ship) -> Ship = Ship(..s, hp: s.hp - 1, x: s.x + 5)\n\nfun main() -> Int =\n s := Ship(x: 1, hp: 10)\n t := hit(s)\n t.go() * 100 + t.hp * 10 + t.x\n");
|
|
@@ -90,63 +53,6 @@ fn unique_base_field_record_spread_in_place() {
|
|
|
90
53
|
assert!(spreadIsUnique("unique_base_field_flag", "hit", &unique));
|
|
91
54
|
}
|
|
92
55
|
|
|
93
|
-
#[test]
|
|
94
|
-
fn unique_self_field_write() {
|
|
95
|
-
let src = prog("struct Cell(mut x: Int) =
|
|
96
|
-
fun bump(self) -> Cell =
|
|
97
|
-
self.x = self.x + 1
|
|
98
|
-
self
|
|
99
|
-
|
|
100
|
-
fun main() -> Int =
|
|
101
|
-
s := Cell(x: 41)
|
|
102
|
-
t := s.bump()
|
|
103
|
-
t.x
|
|
104
|
-
");
|
|
105
|
-
assert_eq!(runInt("selfwrite", &src), 42);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/// A later value that reads a field an earlier one overwrote, or uses the source
|
|
109
|
-
/// any other way (here a method call), must still run (copy fallback).
|
|
110
|
-
#[test]
|
|
111
|
-
fn unique_spread_hazard_falls_back_to_copy() {
|
|
112
|
-
let src = prog("struct Cell(x: Int, y: Int) =
|
|
113
|
-
fun sum(self) -> Int = self.x + self.y
|
|
114
|
-
|
|
115
|
-
fun main() -> Int =
|
|
116
|
-
s := Cell(x: 1, y: 2)
|
|
117
|
-
t := Cell(..s, x: 10, y: s.sum())
|
|
118
|
-
t.x * 100 + t.y
|
|
119
|
-
");
|
|
120
|
-
assert_eq!(runInt("hazard_call", &src), 1000 + 3);
|
|
121
|
-
let src = prog("struct Cell(x: Int, y: Int, z: Int)
|
|
122
|
-
|
|
123
|
-
fun main() -> Int =
|
|
124
|
-
s := Cell(x: 1, y: 2, z: 3)
|
|
125
|
-
t := Cell(..s, x: 10, z: s.y + s.z)
|
|
126
|
-
t.x * 100 + t.y * 10 + t.z
|
|
127
|
-
");
|
|
128
|
-
assert_eq!(runInt("hazard_ok_read", &src), 1000 + 20 + 5);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/// R5: arms binding differently named locals of different record types.
|
|
132
|
-
#[test]
|
|
133
|
-
fn match_arm_rename_still_valid_wasm() {
|
|
134
|
-
let src = prog("struct Pa(x: Int)
|
|
135
|
-
struct Pb(y: Int, z: Int)
|
|
136
|
-
|
|
137
|
-
enum Pick = Wa(v: Pa) | Wb(v: Pb)
|
|
138
|
-
|
|
139
|
-
fun go(p: Pick) -> Int =
|
|
140
|
-
match p
|
|
141
|
-
Wa(a) => a.x
|
|
142
|
-
Wb(b) => b.y * 10 + b.z
|
|
143
|
-
|
|
144
|
-
fun main() -> Int =
|
|
145
|
-
go(Wa(v: Pa(x: 4))) * 100 + go(Wb(v: Pb(y: 5, z: 6)))
|
|
146
|
-
");
|
|
147
|
-
assert_eq!(runInt("match_rename", &src), 400 + 56);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
56
|
/// F-T6-3: `s = Cell(..s, ...)` on a param must reassign the param's own slot, not
|
|
151
57
|
/// a fresh shadow local that every read of `s` (the spread source included) sees unset.
|
|
152
58
|
#[test]
|
|
@@ -169,28 +75,3 @@ fun main() -> Int =
|
|
|
169
75
|
assert_eq!(runInt("reassign_param", &unique), 41000 + 207);
|
|
170
76
|
assert!(spreadIsUnique("reassign_param_flag", "bump", &unique));
|
|
171
77
|
}
|
|
172
|
-
|
|
173
|
-
#[test]
|
|
174
|
-
fn unique_spread_reassign_local() {
|
|
175
|
-
let src = prog("struct Cell(x: Int, y: Int)
|
|
176
|
-
|
|
177
|
-
fun main() -> Int =
|
|
178
|
-
s := Cell(x: 1, y: 7)
|
|
179
|
-
s = Cell(..s, x: s.x + 1)
|
|
180
|
-
s = Cell(..s, y: s.y + s.x)
|
|
181
|
-
s.x * 100 + s.y
|
|
182
|
-
");
|
|
183
|
-
assert_eq!(runInt("reassign_local", &src), 200 + 9);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
#[test]
|
|
187
|
-
fn unique_self_spread_return() {
|
|
188
|
-
let src = prog("struct Cell(x: Int, y: Int) =
|
|
189
|
-
fun step(self) -> Cell = Cell(..self, x: self.x + 1)
|
|
190
|
-
|
|
191
|
-
fun main() -> Int =
|
|
192
|
-
t := Cell(x: 1, y: 7).step().step()
|
|
193
|
-
t.x * 100 + t.y
|
|
194
|
-
");
|
|
195
|
-
assert_eq!(runInt("self_spread", &src), 300 + 7);
|
|
196
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_records/enum_init.rs
CHANGED
|
@@ -1,56 +1,8 @@
|
|
|
1
|
-
use plum_test_support::{load, runInt};
|
|
2
|
-
use plum_test_support::compileSource;
|
|
1
|
+
use plum_test_support::{compileSource, load};
|
|
3
|
-
const LINK: &str = "enum Link[T] =
|
|
4
|
-
| Nil
|
|
5
|
-
| Cons(head: T, tail: Link[T])
|
|
6
|
-
|
|
7
|
-
fun init(a: T, b: T) -> Link[T] = Cons(head: a, tail: Cons(head: b, tail: Nil))
|
|
8
|
-
|
|
9
|
-
fun length(self) -> Int = match self
|
|
10
|
-
Nil => 0
|
|
11
|
-
Cons(_, t) => 1 + t.length()
|
|
12
|
-
|
|
13
|
-
fun sum(self) -> Int = match self
|
|
14
|
-
Nil => 0
|
|
15
|
-
Cons(h, t) => h + t.sum()
|
|
16
|
-
|
|
17
|
-
";
|
|
18
|
-
|
|
19
2
|
fn prog(body: &str) -> String {
|
|
20
3
|
format!("import std/Str\nimport std/Int\n\n{body}")
|
|
21
4
|
}
|
|
22
5
|
|
|
23
|
-
#[test]
|
|
24
|
-
fn empty_init_with_annotation() {
|
|
25
|
-
let link0 = LINK.replace(
|
|
26
|
-
"fun init(a: T, b: T) -> Link[T] = Cons(head: a, tail: Cons(head: b, tail: Nil))",
|
|
27
|
-
"fun init() -> Link[T] = Nil",
|
|
28
|
-
);
|
|
29
|
-
let src = prog(&format!("{link0}fun main() -> Int =
|
|
30
|
-
xs: Link[Int] := Link()
|
|
31
|
-
xs.length() + 7
|
|
32
|
-
"));
|
|
33
|
-
assert_eq!(runInt("empty", &src), 7);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
#[test]
|
|
37
|
-
fn fixed_param_init() {
|
|
38
|
-
let src = prog("enum Pair2 =
|
|
39
|
-
| Zero
|
|
40
|
-
| Two(a: Int, b: Int)
|
|
41
|
-
|
|
42
|
-
fun init(a: Int, b: Int) -> Pair2 = Two(a: a, b: b)
|
|
43
|
-
|
|
44
|
-
fun total(self) -> Int = match self
|
|
45
|
-
Zero => 0
|
|
46
|
-
Two(a, b) => a + b
|
|
47
|
-
|
|
48
|
-
fun main() -> Int =
|
|
49
|
-
Pair2(3, 4).total()
|
|
50
|
-
");
|
|
51
|
-
assert_eq!(runInt("fixed", &src), 7);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
6
|
#[test]
|
|
55
7
|
fn no_init_error_message() {
|
|
56
8
|
let src = prog("enum Shape =
|
|
@@ -73,22 +25,3 @@ fun main() -> Int =
|
|
|
73
25
|
"{err}"
|
|
74
26
|
);
|
|
75
27
|
}
|
|
76
|
-
|
|
77
|
-
#[test]
|
|
78
|
-
fn variant_name_wins() {
|
|
79
|
-
let src = prog(&format!("{LINK}fun main() -> Int =
|
|
80
|
-
xs := Cons(head: 5, tail: Link(1, 2))
|
|
81
|
-
xs.sum() * 10 + xs.length()
|
|
82
|
-
"));
|
|
83
|
-
assert_eq!(runInt("variantwins", &src), 83);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
#[test]
|
|
87
|
-
fn generic_inferred_from_args() {
|
|
88
|
-
let src = prog(&format!("{LINK}fun total(xs: Link[Int]) -> Int = xs.sum()
|
|
89
|
-
|
|
90
|
-
fun main() -> Int =
|
|
91
|
-
total(Link(5, 6)) + Link(\"a\", \"b\").length()
|
|
92
|
-
"));
|
|
93
|
-
assert_eq!(runInt("infer", &src), 13);
|
|
94
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_records/inline_variants.rs
CHANGED
|
@@ -1,78 +1,6 @@
|
|
|
1
|
-
use plum_test_support::{load
|
|
1
|
+
use plum_test_support::{load};
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
const HEAD: &str = "\
|
|
5
|
-
import std/Str
|
|
6
|
-
import std/Int
|
|
7
|
-
import std/Slice
|
|
8
|
-
|
|
9
|
-
enum Link[T] = Nil | Cons(head: T, tail: Link[T])
|
|
10
|
-
|
|
11
|
-
fun length[T](xs: Link[T]) -> Int = match xs
|
|
12
|
-
Nil => 0
|
|
13
|
-
Cons(_, t) => 1 + length(t)
|
|
14
|
-
|
|
15
|
-
fun sum(xs: Link[Int]) -> Int = match xs
|
|
16
|
-
Nil => 0
|
|
17
|
-
Cons(h, t) => h + sum(t)
|
|
18
|
-
|
|
19
|
-
fun cons[T](h: T, t: Link[T]) -> Link[T] =
|
|
20
|
-
Cons(head: h, tail: t)
|
|
21
|
-
|
|
22
|
-
fun map(xs: Link[Int], f: fn(Int) -> Int) -> Link[Int] = match xs
|
|
23
|
-
Nil => Nil
|
|
24
|
-
Cons(h, t) => Cons(head: f(h), tail: map(t, f))
|
|
25
|
-
|
|
26
|
-
";
|
|
27
|
-
|
|
28
|
-
#[test]
|
|
29
|
-
fn cons_list_length_map_sum() {
|
|
30
|
-
let src = format!(
|
|
31
|
-
"{HEAD}\
|
|
32
|
-
fun main() -> Int =
|
|
33
|
-
xs := cons(1, cons(2, cons(3, Nil)))
|
|
34
|
-
ys := map(xs, |n| n * 2)
|
|
35
|
-
sum(xs) * 100 + sum(ys) + length(ys) * 10000
|
|
36
|
-
"
|
|
37
|
-
);
|
|
38
|
-
assert_eq!(runInt("cons", &src), 30000 + 600 + 12);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
#[test]
|
|
42
|
-
fn nested_literal_construction() {
|
|
43
|
-
let src = format!(
|
|
44
|
-
"{HEAD}\
|
|
45
|
-
fun main() -> Int =
|
|
46
|
-
xs := Cons(head: 1, tail: Cons(head: 2, tail: Cons(head: 3, tail: Nil)))
|
|
47
|
-
sum(xs)
|
|
48
|
-
"
|
|
49
|
-
);
|
|
50
|
-
assert_eq!(runInt("nested", &src), 6);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
#[test]
|
|
54
|
-
fn tree_insert_and_size() {
|
|
55
|
-
let src = "\
|
|
56
|
-
import std/Str
|
|
57
|
-
import std/Int
|
|
58
|
-
|
|
59
|
-
enum Tree[T] = Empty | Leaf(v: Int) | Node(l: Tree[T], r: Tree[T])
|
|
60
|
-
|
|
61
|
-
fun size[T](t: Tree[T]) -> Int = match t
|
|
62
|
-
Empty => 0
|
|
63
|
-
Leaf(_) => 1
|
|
64
|
-
Node(l, r) => size(l) + size(r)
|
|
65
|
-
|
|
66
|
-
fun lf() -> Tree[Str] =
|
|
67
|
-
Leaf(v: 3)
|
|
68
|
-
|
|
69
|
-
fun main() -> Int =
|
|
70
|
-
t := Node(l: lf(), r: Node(l: lf(), r: Empty))
|
|
71
|
-
size(t)
|
|
72
|
-
";
|
|
73
|
-
assert_eq!(runInt("tree", src), 2);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
4
|
#[test]
|
|
77
5
|
fn union_enum_is_an_error() {
|
|
78
6
|
let src = "\
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_records/mod.rs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#![allow(non_snake_case)]
|
|
2
|
-
mod empty_record_embed;
|
|
3
2
|
mod enum_init;
|
|
4
3
|
mod inline_variants;
|
|
5
|
-
mod record_embed;
|
|
6
4
|
mod record_init;
|
|
7
5
|
mod unboxed_variants;
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_records/record_embed.rs
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
use plum_test_support::{runInt, PRE};
|
|
2
|
-
|
|
3
|
-
fn prog(body: &str) -> String {
|
|
4
|
-
format!("{PRE}{body}")
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const STAGE: &str = "struct Stage(camScale: Int) =
|
|
8
|
-
fun init(self) =
|
|
9
|
-
self.root: Int = 10
|
|
10
|
-
self.camX: Int = self.camScale * 2
|
|
11
|
-
|
|
12
|
-
fun act(self, dt: Int) -> Int = self.root + dt
|
|
13
|
-
|
|
14
|
-
fun draw(self) -> Int = 100
|
|
15
|
-
|
|
16
|
-
";
|
|
17
|
-
|
|
18
|
-
#[test]
|
|
19
|
-
fn inherited_fields_methods_and_init_order() {
|
|
20
|
-
let src = prog(&format!("{STAGE}struct World(Stage(camScale), score: Int) =
|
|
21
|
-
fun init(self) =
|
|
22
|
-
self.best: Int = self.root + self.camX + 1
|
|
23
|
-
|
|
24
|
-
fun main() -> Int =
|
|
25
|
-
w := World(camScale: 3, score: 4)
|
|
26
|
-
w.camScale + w.root * 10 + w.camX * 100 + w.score * 1000 + w.best * 10000 + w.act(5) * 100000
|
|
27
|
-
"));
|
|
28
|
-
// camScale 3, root 10, camX 6, score 4, best 17, act(5) = 15
|
|
29
|
-
assert_eq!(runInt("fields", &src), 3 + 100 + 600 + 4000 + 170000 + 1500000);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
#[test]
|
|
33
|
-
fn override_calls_original() {
|
|
34
|
-
let src = prog(&format!("{STAGE}struct World(Stage(camScale), score: Int) =
|
|
35
|
-
override fun draw(self) -> Int = self.Stage.draw() + self.score
|
|
36
|
-
|
|
37
|
-
fun main() -> Int =
|
|
38
|
-
w := World(camScale: 1, score: 7)
|
|
39
|
-
w.draw() * 10 + w.act(1)
|
|
40
|
-
"));
|
|
41
|
-
assert_eq!(runInt("override", &src), 1070 + 11);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
#[test]
|
|
45
|
-
fn embedded_record_standalone_and_unselected_set_by_init() {
|
|
46
|
-
let src = prog("struct Stage(camScale: Int, tag: Int) =
|
|
47
|
-
fun init(self) =
|
|
48
|
-
self.camX: Int = 6
|
|
49
|
-
|
|
50
|
-
struct World(Stage(tag), score: Int) =
|
|
51
|
-
fun init(self) =
|
|
52
|
-
self.camScale = 5
|
|
53
|
-
|
|
54
|
-
fun main() -> Int =
|
|
55
|
-
s := Stage(camScale: 2, tag: 1)
|
|
56
|
-
w := World(tag: 3, score: 1)
|
|
57
|
-
s.camScale * 100 + w.camScale * 10 + w.camX + w.tag + s.camX
|
|
58
|
-
");
|
|
59
|
-
assert_eq!(runInt("standalone", &src), 200 + 50 + 6 + 3 + 6);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
#[test]
|
|
63
|
-
fn base_through_embedded_struct() {
|
|
64
|
-
let src = prog("struct Actor(x: Int) =
|
|
65
|
-
fun go(self) -> Int = 1
|
|
66
|
-
|
|
67
|
-
struct Mid(Actor(x), m: Int) =
|
|
68
|
-
override fun go(self) -> Int = 2 + self.Actor.go()
|
|
69
|
-
|
|
70
|
-
struct Top(Mid(x, m), t: Int)
|
|
71
|
-
|
|
72
|
-
fun run(a: Actor) -> Int = a.go()
|
|
73
|
-
|
|
74
|
-
fun main() -> Int =
|
|
75
|
-
t := Top(x: 1, m: 2, t: 3)
|
|
76
|
-
run(t) * 10 + t.x
|
|
77
|
-
");
|
|
78
|
-
assert_eq!(runInt("deep", &src), 31);
|
|
79
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_records/record_init.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use plum_test_support::{
|
|
1
|
+
use plum_test_support::{PRE};
|
|
2
2
|
use plum_test_support::load;
|
|
3
3
|
|
|
4
4
|
fn prog(body: &str) -> String {
|
|
@@ -14,79 +14,6 @@ const ACTOR: &str = "struct Actor(px: Int, py: Int) =
|
|
|
14
14
|
|
|
15
15
|
";
|
|
16
16
|
|
|
17
|
-
#[test]
|
|
18
|
-
fn ship_example_values() {
|
|
19
|
-
let src = prog(&format!("{ACTOR}struct Ship(Actor(px, py), tex: Int) =
|
|
20
|
-
fun init(self) =
|
|
21
|
-
self.height = self.width + 1
|
|
22
|
-
self.vx = 7
|
|
23
|
-
override fun area(self) -> Int = self.width * self.height + self.vx
|
|
24
|
-
|
|
25
|
-
fun main() -> Int =
|
|
26
|
-
s := Ship(px: 1, py: 2, tex: 3)
|
|
27
|
-
s.px + s.py * 10 + s.tex * 100 + s.area() * 1000
|
|
28
|
-
"));
|
|
29
|
-
// px 1 + py 20 + tex 300 + (10*11+7)*1000
|
|
30
|
-
assert_eq!(runInt("ship", &src), 1 + 20 + 300 + 117 * 1000);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
#[test]
|
|
34
|
-
fn through_a_base_value() {
|
|
35
|
-
let src = prog("struct Actor(px: Int, width: Int) =
|
|
36
|
-
fun init(self) =
|
|
37
|
-
self.width = 10
|
|
38
|
-
fun area(self) -> Int = self.width * 2
|
|
39
|
-
|
|
40
|
-
struct Ship(Actor(px), tex: Int) =
|
|
41
|
-
override fun area(self) -> Int = self.width * 20
|
|
42
|
-
|
|
43
|
-
struct Rock(Actor(px))
|
|
44
|
-
|
|
45
|
-
fun total(a: Actor) -> Int = a.area() + a.px
|
|
46
|
-
|
|
47
|
-
fun main() -> Int =
|
|
48
|
-
total(Ship(px: 1, tex: 0)) * 1000 + total(Rock(px: 2))
|
|
49
|
-
");
|
|
50
|
-
// Ship: 10*20 + 1 = 201 ; Rock: 20 + 2 = 22
|
|
51
|
-
assert_eq!(runInt("basevalue", &src), 201 * 1000 + 22);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
#[test]
|
|
55
|
-
fn fresh_list_per_construction() {
|
|
56
|
-
let src = prog(&format!("{ACTOR}struct Ship(Actor(px, py)) =
|
|
57
|
-
fun init(self) =
|
|
58
|
-
self.vx = 0
|
|
59
|
-
|
|
60
|
-
fun main() -> Int =
|
|
61
|
-
a := Ship(px: 0, py: 0)
|
|
62
|
-
b := Ship(px: 0, py: 0)
|
|
63
|
-
a.kids.push(1)
|
|
64
|
-
a.kids.push(2)
|
|
65
|
-
b.kids.push(9)
|
|
66
|
-
a.kids.length() * 10 + b.kids.length()
|
|
67
|
-
"));
|
|
68
|
-
assert_eq!(runInt("fresh", &src), 21);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
#[test]
|
|
72
|
-
fn rest_runs_after_build_and_nested_construction() {
|
|
73
|
-
let src = prog("struct Inner(mut n: Int)
|
|
74
|
-
|
|
75
|
-
struct Outer(a: Int) =
|
|
76
|
-
fun init(self) =
|
|
77
|
-
self.inner = Inner(n: self.a + 1)
|
|
78
|
-
self.total = 0
|
|
79
|
-
self.bump()
|
|
80
|
-
fun bump(self) =
|
|
81
|
-
self.inner.n = self.inner.n * 10
|
|
82
|
-
|
|
83
|
-
fun main() -> Int =
|
|
84
|
-
o := Outer(a: 4)
|
|
85
|
-
o.inner.n + o.a
|
|
86
|
-
");
|
|
87
|
-
assert_eq!(runInt("rest", &src), 54);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
17
|
#[test]
|
|
91
18
|
fn errors_surface_through_the_loader() {
|
|
92
19
|
let src = prog(&format!("{ACTOR}struct Ship(Actor(px, py)) =
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_records/unboxed_variants.rs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//! Unboxed layout: union-enum members and inline variants are wasm-gc subtypes
|
|
2
2
|
//! of the enum, so constructing one allocates exactly one struct.
|
|
3
|
-
use plum_test_support::{compile
|
|
3
|
+
use plum_test_support::{compile};
|
|
4
4
|
use wasmparser::{Operator, Parser, Payload};
|
|
5
5
|
|
|
6
6
|
/// Number of `struct.new` instructions in the exported `main` function.
|
|
@@ -91,169 +91,3 @@ fun main() -> Int =
|
|
|
91
91
|
);
|
|
92
92
|
assert_eq!(structNewsInMain(&compile("generic", &src)), 3);
|
|
93
93
|
}
|
|
94
|
-
|
|
95
|
-
#[test]
|
|
96
|
-
fn match_variant_fields() {
|
|
97
|
-
let src = format!(
|
|
98
|
-
"{HEAD}\
|
|
99
|
-
enum Shape = Circle(r: Int) | Rect(w: Int, h: Int)
|
|
100
|
-
|
|
101
|
-
fun area(s: Shape) -> Int =
|
|
102
|
-
match s
|
|
103
|
-
Circle(r) => r * r
|
|
104
|
-
Rect(w, h) => w * h
|
|
105
|
-
|
|
106
|
-
fun main() -> Int =
|
|
107
|
-
area(Circle(r: 3)) * 100 + area(Rect(w: 2, h: 5))
|
|
108
|
-
"
|
|
109
|
-
);
|
|
110
|
-
assert_eq!(runInt("match_fields", &src), 910);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
#[test]
|
|
114
|
-
fn nested_enum_match() {
|
|
115
|
-
let src = format!(
|
|
116
|
-
"{HEAD}\
|
|
117
|
-
enum Link[T] = End | Duo(head: T, tail: Link[T])
|
|
118
|
-
|
|
119
|
-
fun sum(l: Link[Int]) -> Int =
|
|
120
|
-
match l
|
|
121
|
-
End => 0
|
|
122
|
-
Duo(head, tail) => head + sum(tail)
|
|
123
|
-
|
|
124
|
-
fun second(l: Link[Int]) -> Int =
|
|
125
|
-
match l
|
|
126
|
-
Duo(_, Duo(h, _)) => h
|
|
127
|
-
_ => -1
|
|
128
|
-
|
|
129
|
-
fun main() -> Int =
|
|
130
|
-
l: Link[Int] := Duo(head: 1, tail: Duo(head: 20, tail: Duo(head: 300, tail: End)))
|
|
131
|
-
sum(l) * 1000 + second(l)
|
|
132
|
-
"
|
|
133
|
-
);
|
|
134
|
-
assert_eq!(runInt("nested", &src), 321020);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
#[test]
|
|
138
|
-
fn equality_is_identity_of_the_variant_value() {
|
|
139
|
-
let src = format!(
|
|
140
|
-
"{HEAD}\
|
|
141
|
-
enum Shape = Circle(r: Int) | Rect(w: Int, h: Int)
|
|
142
|
-
|
|
143
|
-
fun main() -> Int =
|
|
144
|
-
a: Shape := Circle(r: 2)
|
|
145
|
-
b: Shape := a
|
|
146
|
-
c: Shape := Rect(w: 1, h: 2)
|
|
147
|
-
x := if a == b then 1 else 0
|
|
148
|
-
y := if a != c then 10 else 0
|
|
149
|
-
x + y
|
|
150
|
-
"
|
|
151
|
-
);
|
|
152
|
-
assert_eq!(runInt("eq", &src), 11);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
#[test]
|
|
156
|
-
fn reassign_enum_local_across_variants() {
|
|
157
|
-
let src = format!(
|
|
158
|
-
"{HEAD}\
|
|
159
|
-
enum Shape = Circle(r: Int) | Rect(w: Int, h: Int)
|
|
160
|
-
|
|
161
|
-
fun area(s: Shape) -> Int =
|
|
162
|
-
match s
|
|
163
|
-
Circle(r) => r * 3
|
|
164
|
-
Rect(w, h) => w * h
|
|
165
|
-
|
|
166
|
-
fun kind(s: Shape) -> Int =
|
|
167
|
-
match s
|
|
168
|
-
Circle(_) => 1
|
|
169
|
-
Rect(_, _) => 10
|
|
170
|
-
|
|
171
|
-
fun main() -> Int =
|
|
172
|
-
s: Shape := Circle(r: 1)
|
|
173
|
-
n := area(s)
|
|
174
|
-
s = Rect(w: 2, h: 5)
|
|
175
|
-
n = n * 100 + area(s)
|
|
176
|
-
k := 0
|
|
177
|
-
for i := range 4
|
|
178
|
-
if i % 2 == 0
|
|
179
|
-
s = Rect(w: 1, h: 1)
|
|
180
|
-
else
|
|
181
|
-
s = Circle(r: 1)
|
|
182
|
-
k = k + kind(s)
|
|
183
|
-
n * 100 + k
|
|
184
|
-
"
|
|
185
|
-
);
|
|
186
|
-
assert_eq!(runInt("reassign", &src), (3 * 100 + 10) * 100 + 22);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
#[test]
|
|
190
|
-
fn deep_nested_variant_match() {
|
|
191
|
-
let src = format!(
|
|
192
|
-
"{HEAD}\
|
|
193
|
-
enum Tree = Leaf(v: Int) | Node(l: Tree, r: Tree)
|
|
194
|
-
|
|
195
|
-
fun depthSum(t: Tree, d: Int) -> Int =
|
|
196
|
-
match t
|
|
197
|
-
Leaf(v) => v * d
|
|
198
|
-
Node(l, r) => depthSum(l, d + 1) + depthSum(r, d + 1)
|
|
199
|
-
|
|
200
|
-
fun build(n: Int) -> Tree =
|
|
201
|
-
if n == 0 then Leaf(v: 1) else Node(l: build(n - 1), r: Leaf(v: n))
|
|
202
|
-
|
|
203
|
-
fun main() -> Int = depthSum(build(5), 1)
|
|
204
|
-
"
|
|
205
|
-
);
|
|
206
|
-
// Leaves: 1 at depth 6, then Leaf(n) at depth 7 - n for n = 1..5.
|
|
207
|
-
assert_eq!(runInt("deep", &src), 6 + (1..=5).map(|n| n * (7 - n)).sum::<i64>());
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
#[test]
|
|
211
|
-
fn million_cells_built_and_mapped_in_constant_stack() {
|
|
212
|
-
let src = format!(
|
|
213
|
-
"{HEAD}\
|
|
214
|
-
enum Link = End | Duo(head: Int, tail: Link)
|
|
215
|
-
|
|
216
|
-
fun upTo(n: Int) -> Link =
|
|
217
|
-
if n == 0 then End else Duo(head: n, tail: upTo(n - 1))
|
|
218
|
-
|
|
219
|
-
fun inc(xs: Link) -> Link =
|
|
220
|
-
match xs
|
|
221
|
-
End => End
|
|
222
|
-
Duo(h, t) => Duo(head: h + 1, tail: inc(t))
|
|
223
|
-
|
|
224
|
-
fun sum(xs: Link, acc: Int) -> Int =
|
|
225
|
-
match xs
|
|
226
|
-
End => acc
|
|
227
|
-
Duo(h, t) => sum(t, acc + h)
|
|
228
|
-
|
|
229
|
-
fun main() -> Int = sum(inc(upTo(1000000)), 0)
|
|
230
|
-
"
|
|
231
|
-
);
|
|
232
|
-
assert_eq!(runInt("million", &src), 500000500000 + 1000000);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
#[test]
|
|
236
|
-
fn enum_init_versus_variant_name() {
|
|
237
|
-
let src = format!(
|
|
238
|
-
"{HEAD}\
|
|
239
|
-
enum Cell =
|
|
240
|
-
| Empty
|
|
241
|
-
| Full(count: Int, label: Str)
|
|
242
|
-
|
|
243
|
-
fun init(n: Int) -> Cell = if n <= 0 then Empty else Full(count: n, label: \"n{{n}}\")
|
|
244
|
-
|
|
245
|
-
fun size(self) -> Int =
|
|
246
|
-
match self
|
|
247
|
-
Empty => 0
|
|
248
|
-
Full(c, _) => c
|
|
249
|
-
|
|
250
|
-
fun main() -> Int =
|
|
251
|
-
a := Cell(5)
|
|
252
|
-
b := Cell(0)
|
|
253
|
-
c := Full(count: 9, label: \"nine\")
|
|
254
|
-
d := Full(label: \"swapped\", count: 2)
|
|
255
|
-
a.size() * 1000 + b.size() * 100 + c.size() * 10 + d.size()
|
|
256
|
-
"
|
|
257
|
-
);
|
|
258
|
-
assert_eq!(runInt("init_names", &src), 5000 + 90 + 2);
|
|
259
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/closure_tail.rs
CHANGED
|
@@ -1,31 +1,7 @@
|
|
|
1
|
-
use plum_test_support::{compile
|
|
1
|
+
use plum_test_support::{compile};
|
|
2
2
|
|
|
3
3
|
const HEAD: &str = "import std/Int\n\n";
|
|
4
4
|
|
|
5
|
-
#[test]
|
|
6
|
-
fn closure_body_tail_call_million() {
|
|
7
|
-
let src = format!("{HEAD}fun countDown(n: Int) -> Int = if n == 0 then 0 else countDown(n - 1)\n\nfun main() -> Int =\n dec := |n: Int| if n == 0 then 7 else countDown(n - 1) + 0\n fin := |n: Int| countDown(n)\n fin(1000000) + dec(0)\n");
|
|
8
|
-
assert_eq!(runInt("cbody", &src), 7);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
#[test]
|
|
12
|
-
fn recursive_local_closure_million() {
|
|
13
|
-
let src = format!("{HEAD}fun main() -> Int =\n go := |n, acc| if n == 0 then acc else go(n - 1, acc + n)\n go(1000000, 0)\n");
|
|
14
|
-
assert_eq!(runInt("rec", &src), 500000500000);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
#[test]
|
|
18
|
-
fn recursive_closure_captures_outer() {
|
|
19
|
-
let src = format!("{HEAD}fun main() -> Int =\n k := 3\n go := |n, acc| if n == 0 then acc else go(n - 1, acc + k)\n go(1000000, 0)\n");
|
|
20
|
-
assert_eq!(runInt("reccap", &src), 3000000);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#[test]
|
|
24
|
-
fn closure_not_recursive_unchanged() {
|
|
25
|
-
let src = format!("{HEAD}fun main() -> Int =\n k := 10\n f := |x| x + k\n f(5)\n");
|
|
26
|
-
assert_eq!(runInt("plain", &src), 15);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
5
|
#[test]
|
|
30
6
|
fn wasm_closure_has_return_call() {
|
|
31
7
|
let src = format!("{HEAD}fun main() -> Int =\n go := |n, acc| if n == 0 then acc else go(n - 1, acc + n)\n go(10, 0)\n");
|
|
@@ -43,15 +19,3 @@ fn wasm_closure_has_return_call() {
|
|
|
43
19
|
}
|
|
44
20
|
assert!(found, "expected return_call_indirect in a closure body");
|
|
45
21
|
}
|
|
46
|
-
|
|
47
|
-
#[test]
|
|
48
|
-
fn self_calling_closure_typed_params_runs() {
|
|
49
|
-
let src = format!("{HEAD}fun main() -> Int =\n fact := |n: Int| if n <= 1 then 1 else n * fact(n - 1)\n fact(5)\n");
|
|
50
|
-
assert_eq!(runInt("selftyped", &src), 120);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
#[test]
|
|
54
|
-
fn self_calling_closure_untyped_params_runs() {
|
|
55
|
-
let src = format!("{HEAD}fun main() -> Int =\n fact := |n| if n <= 1 then 1 else n * fact(n - 1)\n fact(5)\n");
|
|
56
|
-
assert_eq!(runInt("selfuntyped", &src), 120);
|
|
57
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/error_tail.rs
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
use plum_test_support::runInt;
|
|
2
|
-
|
|
3
|
-
const RHEAD: &str = "import std/Int\nimport std/Result\n\nenum NegError = Neg | Other\n\n";
|
|
4
|
-
|
|
5
|
-
#[test]
|
|
6
|
-
fn result_self_loop_million() {
|
|
7
|
-
let src = format!("{RHEAD}fun down(n: Int) -> Result[Int, NegError] =\n if n < 0\n return Err(Neg)\n if n == 0\n return Ok(5)\n down(n - 1)\n\nfun main() -> Int = down(1000000).unwrapOr(99)\n");
|
|
8
|
-
assert_eq!(runInt("result_self_loop_million", &src), 5);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
#[test]
|
|
12
|
-
fn result_self_loop_returns_err() {
|
|
13
|
-
let src = format!("{RHEAD}fun down(n: Int) -> Result[Int, NegError] =\n if n == 0\n return Err(Neg)\n down(n - 1)\n\nfun main() -> Int = down(1000000).unwrapOr(42)\n");
|
|
14
|
-
assert_eq!(runInt("result_self_loop_returns_err", &src), 42);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
#[test]
|
|
18
|
-
fn result_mutual_million() {
|
|
19
|
-
let src = format!("{RHEAD}fun ping(n: Int) -> Result[Int, NegError] =\n if n == 0\n return Ok(1)\n pong(n - 1)\n\nfun pong(n: Int) -> Result[Int, NegError] =\n if n == 0\n return Ok(2)\n ping(n - 1)\n\nfun main() -> Int = ping(1000000).unwrapOr(99)\n");
|
|
20
|
-
assert_eq!(runInt("result_mutual_million", &src), 1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#[test]
|
|
24
|
-
fn question_in_tail_not_tail_call() {
|
|
25
|
-
let src = format!("{RHEAD}fun down(n: Int) -> Result[Int, NegError] =\n if n == 0\n return Ok(5)\n v := down(n - 1)?\n Ok(v)\n\nfun main() -> Int = down(100).unwrapOr(99)\n");
|
|
26
|
-
assert_eq!(runInt("question_in_tail_not_tail_call", &src), 5);
|
|
27
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/loop_break.rs
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
use plum_test_support::{runInt, PRE};
|
|
2
|
-
|
|
3
|
-
fn prog(body: &str) -> String {
|
|
4
|
-
format!("{PRE}{body}")
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// Every loop is fuel-bounded (`i < 50`) so a wrong branch depth fails an
|
|
8
|
-
// assertion instead of hanging the test.
|
|
9
|
-
const LST: &str = "
|
|
10
|
-
enum Lst = Nil | Cons(head: Int, tail: Lst)
|
|
11
|
-
|
|
12
|
-
fun build(n: Int, acc: Lst) -> Lst =
|
|
13
|
-
if n == 0 then acc else build(n - 1, Cons(head: n, tail: acc))
|
|
14
|
-
";
|
|
15
|
-
|
|
16
|
-
#[test]
|
|
17
|
-
fn break_in_if_in_match_arm_in_while() {
|
|
18
|
-
let src = prog(&format!(
|
|
19
|
-
"{LST}
|
|
20
|
-
fun find(xs: Lst, target: Int) -> Int =
|
|
21
|
-
i := 0
|
|
22
|
-
cur := xs
|
|
23
|
-
while i < 50
|
|
24
|
-
match cur
|
|
25
|
-
Cons(h, t) =>
|
|
26
|
-
if h == target
|
|
27
|
-
break
|
|
28
|
-
cur = t
|
|
29
|
-
Nil =>
|
|
30
|
-
break
|
|
31
|
-
i = i + 1
|
|
32
|
-
return i
|
|
33
|
-
|
|
34
|
-
fun main() -> Int = find(build(10, Nil), 4) * 100 + find(build(10, Nil), 99)
|
|
35
|
-
"
|
|
36
|
-
));
|
|
37
|
-
assert_eq!(runInt("break_if_match_while", &src), 3 * 100 + 10);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
#[test]
|
|
41
|
-
fn continue_in_if_in_match_arm_in_while() {
|
|
42
|
-
let src = prog(&format!(
|
|
43
|
-
"{LST}
|
|
44
|
-
fun sumOdd(xs: Lst) -> Int =
|
|
45
|
-
total := 0
|
|
46
|
-
i := 0
|
|
47
|
-
cur := xs
|
|
48
|
-
while i < 50
|
|
49
|
-
i = i + 1
|
|
50
|
-
match cur
|
|
51
|
-
Cons(h, t) =>
|
|
52
|
-
cur = t
|
|
53
|
-
if h % 2 == 0
|
|
54
|
-
continue
|
|
55
|
-
total = total + h
|
|
56
|
-
Nil =>
|
|
57
|
-
break
|
|
58
|
-
return total * 100 + i
|
|
59
|
-
|
|
60
|
-
fun main() -> Int = sumOdd(build(6, Nil))
|
|
61
|
-
"
|
|
62
|
-
));
|
|
63
|
-
// 1 + 3 + 5 = 9; seven iterations (six cons cells then Nil).
|
|
64
|
-
assert_eq!(runInt("continue_if_match_while", &src), 900 + 7);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
#[test]
|
|
68
|
-
fn break_in_match_arm_direct() {
|
|
69
|
-
let src = prog(&format!(
|
|
70
|
-
"{LST}
|
|
71
|
-
fun count(xs: Lst) -> Int =
|
|
72
|
-
i := 0
|
|
73
|
-
cur := xs
|
|
74
|
-
while i < 50
|
|
75
|
-
match cur
|
|
76
|
-
Nil =>
|
|
77
|
-
break
|
|
78
|
-
Cons(h, t) =>
|
|
79
|
-
cur = t
|
|
80
|
-
i = i + 1
|
|
81
|
-
return i
|
|
82
|
-
|
|
83
|
-
fun wild(n: Int) -> Int =
|
|
84
|
-
i := 0
|
|
85
|
-
while i < 50
|
|
86
|
-
match i
|
|
87
|
-
_ =>
|
|
88
|
-
break
|
|
89
|
-
i = i + 1
|
|
90
|
-
return i + n
|
|
91
|
-
|
|
92
|
-
fun main() -> Int = count(build(5, Nil)) * 100 + wild(7)
|
|
93
|
-
"
|
|
94
|
-
));
|
|
95
|
-
assert_eq!(runInt("break_match_arm_direct", &src), 5 * 100 + 7);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
#[test]
|
|
99
|
-
fn break_in_else_if_chain_in_while() {
|
|
100
|
-
let src = prog(
|
|
101
|
-
"fun main() -> Int =
|
|
102
|
-
i := 0
|
|
103
|
-
while i < 50
|
|
104
|
-
if i == 100
|
|
105
|
-
i = i + 1000
|
|
106
|
-
else if i == 100
|
|
107
|
-
i = i + 2000
|
|
108
|
-
else if i == 7
|
|
109
|
-
break
|
|
110
|
-
i = i + 1
|
|
111
|
-
return i
|
|
112
|
-
",
|
|
113
|
-
);
|
|
114
|
-
assert_eq!(runInt("break_else_if_chain", &src), 7);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
#[test]
|
|
118
|
-
fn nested_loops_with_match_in_inner() {
|
|
119
|
-
let src = prog(&format!(
|
|
120
|
-
"{LST}
|
|
121
|
-
fun main() -> Int =
|
|
122
|
-
outer := 0
|
|
123
|
-
hits := 0
|
|
124
|
-
while outer < 50
|
|
125
|
-
if outer == 3
|
|
126
|
-
break
|
|
127
|
-
cur := build(5, Nil)
|
|
128
|
-
j := 0
|
|
129
|
-
while j < 50
|
|
130
|
-
match cur
|
|
131
|
-
Cons(h, t) =>
|
|
132
|
-
cur = t
|
|
133
|
-
if h == 2
|
|
134
|
-
j = j + 1
|
|
135
|
-
continue
|
|
136
|
-
if h == 4
|
|
137
|
-
break
|
|
138
|
-
hits = hits + 1
|
|
139
|
-
Nil =>
|
|
140
|
-
break
|
|
141
|
-
j = j + 1
|
|
142
|
-
outer = outer + 1
|
|
143
|
-
return outer * 100 + hits
|
|
144
|
-
"
|
|
145
|
-
));
|
|
146
|
-
// inner: h=1 hit, h=2 skipped, h=3 hit, h=4 break => 2 hits per outer pass, 3 passes.
|
|
147
|
-
assert_eq!(runInt("nested_loops_match", &src), 3 * 100 + 6);
|
|
148
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/mod.rs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
#![allow(non_snake_case)]
|
|
2
2
|
mod closure_tail;
|
|
3
|
-
mod error_tail;
|
|
4
|
-
mod loop_break;
|
|
5
3
|
mod tail;
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_tail/tail.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use plum_test_support::{compile,
|
|
1
|
+
use plum_test_support::{compile, PRE};
|
|
2
2
|
|
|
3
3
|
fn prog(body: &str) -> String {
|
|
4
4
|
format!("{PRE}{body}")
|
|
@@ -15,119 +15,6 @@ fun sumAcc(xs: Link[Int], acc: Int) -> Int = match xs
|
|
|
15
15
|
Cons(h, t) => sumAcc(t, acc + h)
|
|
16
16
|
";
|
|
17
17
|
|
|
18
|
-
#[test]
|
|
19
|
-
fn self_tail_million() {
|
|
20
|
-
let src = prog(&format!(
|
|
21
|
-
"{LINK}
|
|
22
|
-
fun main() -> Int = sumAcc(build(1000000, Nil), 0)
|
|
23
|
-
"
|
|
24
|
-
));
|
|
25
|
-
assert_eq!(runInt("self_tail_million", &src), 500000500000);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
#[test]
|
|
29
|
-
fn self_tail_args_swap() {
|
|
30
|
-
let src = prog(
|
|
31
|
-
"fun gcd(a: Int, b: Int) -> Int = if b == 0 then a else gcd(b, a % b)
|
|
32
|
-
|
|
33
|
-
fun swapN(a: Int, b: Int, n: Int) -> Int =
|
|
34
|
-
if n == 0 then a * 1000 + b else swapN(b, a, n - 1)
|
|
35
|
-
|
|
36
|
-
fun main() -> Int = gcd(1071, 462) * 1000000 + swapN(1, 2, 999999)
|
|
37
|
-
",
|
|
38
|
-
);
|
|
39
|
-
// gcd = 21; odd swap count leaves (2, 1).
|
|
40
|
-
assert_eq!(runInt("self_tail_args_swap", &src), 21 * 1000000 + 2001);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
#[test]
|
|
44
|
-
fn mutual_tail_million() {
|
|
45
|
-
let src = prog(
|
|
46
|
-
"fun isEven(n: Int) -> Int = if n == 0 then 1 else isOdd(n - 1)
|
|
47
|
-
|
|
48
|
-
fun isOdd(n: Int) -> Int = if n == 0 then 0 else isEven(n - 1)
|
|
49
|
-
|
|
50
|
-
fun main() -> Int = isEven(1000001) * 10 + isOdd(1000001)
|
|
51
|
-
",
|
|
52
|
-
);
|
|
53
|
-
assert_eq!(runInt("mutual_tail_million", &src), 1);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
#[test]
|
|
57
|
-
fn closure_tail_call_runs() {
|
|
58
|
-
let src = prog(
|
|
59
|
-
"fun run(n: Int, k: fn(Int) -> Int) -> Int = if n == 0 then 7 else k(n - 1)
|
|
60
|
-
|
|
61
|
-
fun go(n: Int) -> Int = run(n, go)
|
|
62
|
-
|
|
63
|
-
fun main() -> Int = go(1000000)
|
|
64
|
-
",
|
|
65
|
-
);
|
|
66
|
-
assert_eq!(runInt("closure_tail_call_runs", &src), 7);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
#[test]
|
|
70
|
-
fn method_self_tail() {
|
|
71
|
-
let src = prog(
|
|
72
|
-
"struct Counter(step: Int) =
|
|
73
|
-
fun count(self, n: Int, acc: Int) -> Int =
|
|
74
|
-
if n == 0 then acc else self.count(n - 1, acc + self.step)
|
|
75
|
-
|
|
76
|
-
fun main() -> Int = Counter(step: 3).count(1000000, 0)
|
|
77
|
-
",
|
|
78
|
-
);
|
|
79
|
-
assert_eq!(runInt("method_self_tail", &src), 3000000);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
#[test]
|
|
83
|
-
fn param_self_tail() {
|
|
84
|
-
let src = prog(
|
|
85
|
-
"struct Cell(x: Int, y: Int)
|
|
86
|
-
|
|
87
|
-
fun bump(c: Cell, n: Int) -> Cell =
|
|
88
|
-
if n == 0 then c else bump(Cell(..c, x: c.x + 1), n - 1)
|
|
89
|
-
|
|
90
|
-
fun main() -> Int =
|
|
91
|
-
c := bump(Cell(x: 0, y: 5), 100000)
|
|
92
|
-
c.x * 10 + c.y
|
|
93
|
-
",
|
|
94
|
-
);
|
|
95
|
-
assert_eq!(runInt("param_self_tail", &src), 1000005);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/// R3: a closure made in one iteration keeps that iteration's parameter value.
|
|
99
|
-
#[test]
|
|
100
|
-
fn closure_captures_iteration_value() {
|
|
101
|
-
let src = prog(
|
|
102
|
-
"fun go(i: Int, f: fn(Int) -> Int) -> Int =
|
|
103
|
-
if i == 3 then f(100) else go(i + 1, |x| x + i)
|
|
104
|
-
|
|
105
|
-
fun main() -> Int = go(0, |x| x)
|
|
106
|
-
",
|
|
107
|
-
);
|
|
108
|
-
assert_eq!(runInt("closure_captures_iteration_value", &src), 102);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/// Match arms and else-if chains nest wasm blocks between the call and the loop.
|
|
112
|
-
#[test]
|
|
113
|
-
fn self_tail_through_nested_branches() {
|
|
114
|
-
let src = prog(
|
|
115
|
-
"fun f(n: Int, acc: Int) -> Int =
|
|
116
|
-
if n == 0 then acc
|
|
117
|
-
else if n % 3 == 0 then f(n - 1, acc + 1)
|
|
118
|
-
else if n % 3 == 1 then f(n - 1, acc + 10)
|
|
119
|
-
else f(n - 1, acc + 100)
|
|
120
|
-
|
|
121
|
-
fun g(n: Int, acc: Int) -> Int = match n
|
|
122
|
-
0 => acc
|
|
123
|
-
k => if k % 2 == 0 then g(k - 1, acc + 2) else g(k - 1, acc + 1)
|
|
124
|
-
|
|
125
|
-
fun main() -> Int = f(30, 0) * 1000 + g(10, 0)
|
|
126
|
-
",
|
|
127
|
-
);
|
|
128
|
-
assert_eq!(runInt("self_tail_nested", &src), 1110 * 1000 + 15);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
18
|
#[derive(Default)]
|
|
132
19
|
struct FnOps {
|
|
133
20
|
calls: Vec<u32>,
|
|
@@ -237,39 +124,3 @@ fun main() -> Int = isEven(10)
|
|
|
237
124
|
}
|
|
238
125
|
|
|
239
126
|
|
|
240
|
-
const HEAD: &str = "import std/Int\n\n";
|
|
241
|
-
|
|
242
|
-
#[test]
|
|
243
|
-
fn tail_call_in_match_arm() {
|
|
244
|
-
let src = format!("{HEAD}enum Step =\n | Go(n: Int)\n | Stop\n\nfun run(s: Step, acc: Int) -> Int = match s\n Go(n) => run(if n == 0 then Stop else Go(n - 1), acc + 1)\n Stop => acc\n\nfun main() -> Int = run(Go(1000000), 0)\n");
|
|
245
|
-
assert_eq!(runInt("match", &src), 1000001);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
#[test]
|
|
249
|
-
fn non_tail_union_wrap_not_tail() {
|
|
250
|
-
// The callee returns Int but the fn returns Option[Int]: the call is wrapped on the way
|
|
251
|
-
// out, so it must stay a normal call and still produce the right value.
|
|
252
|
-
let src = format!("{HEAD}import std/Option\n\nfun inner(n: Int) -> Int = n + 1\n\nfun outer(n: Int) -> Option[Int] = Some(inner(n))\n\nfun main() -> Int =\n v := outer(41)\n v.unwrapOr(0)\n");
|
|
253
|
-
assert_eq!(runInt("wrap", &src), 42);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
#[test]
|
|
257
|
-
fn big_link_built_and_summed() {
|
|
258
|
-
let src = format!("{HEAD}enum Link =\n | Nil\n | Cons(head: Int, tail: Link)\n\nfun build(n: Int, acc: Link) -> Link = if n == 0 then acc else build(n - 1, Cons(n, acc))\n\nfun sum(l: Link, acc: Int) -> Int = match l\n Nil => acc\n Cons(h, t) => sum(t, acc + h)\n\nfun main() -> Int = sum(build(200000, Nil), 0)\n");
|
|
259
|
-
assert_eq!(runInt("link", &src), 200000i64 * 200001 / 2);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/// TRMC: a map over a native-variant list of 100k elements builds without stack growth.
|
|
263
|
-
#[test]
|
|
264
|
-
fn trmc_link_map_100k() {
|
|
265
|
-
let src = prog(&format!(
|
|
266
|
-
"{LINK}
|
|
267
|
-
fun mapInc(xs: Link[Int]) -> Link[Int] = match xs
|
|
268
|
-
Nil => Nil
|
|
269
|
-
Cons(h, t) => Cons(head: h + 1, tail: mapInc(t))
|
|
270
|
-
|
|
271
|
-
fun main() -> Int = sumAcc(mapInc(build(100000, Nil)), 0)
|
|
272
|
-
"
|
|
273
|
-
));
|
|
274
|
-
assert_eq!(runInt("trmc_link_map_100k", &src), 5000050000 + 100000);
|
|
275
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_types/closure_optional.rs
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
//! F-T3-2: closures whose expected return is `Option[T]` / `Result[T, E]`, calls through an fn-typed
|
|
2
|
-
//! field returning `T?`, and static methods of a generic type that never pin `T`.
|
|
3
|
-
use plum_test_support::*;
|
|
4
|
-
|
|
5
|
-
#[test]
|
|
6
|
-
fn closure_coerced_into_optional_return() {
|
|
7
|
-
let src = "import std/Int\nimport std/Option\n\nfun ap(f: fn() -> Option[Int]) -> Int =\n f().unwrapOr(0)\n\nfun main() -> Int =\n ap(|| Some(3))\n";
|
|
8
|
-
assert_eq!(runInt("opt_present", src), 3);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
#[test]
|
|
12
|
-
fn closure_returning_none_into_optional() {
|
|
13
|
-
let src = "import std/Int\nimport std/Option\n\nfun ap(f: fn() -> Option[Int]) -> Int =\n f().unwrapOr(7)\n\nfun main() -> Int =\n ap(|| None)\n";
|
|
14
|
-
assert_eq!(runInt("opt_null", src), 7);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
#[test]
|
|
18
|
-
fn closure_into_ref_option_and_result() {
|
|
19
|
-
let src = "import std/Int\nimport std/Str\nimport std/Option\nimport std/Result\n\nenum BadError = Bad | Worse\n\nfun s(f: fn(Int) -> Option[Str]) -> Str =\n f(1).unwrapOr(\"none\")\n\nfun e(f: fn(Int) -> Result[Int, BadError]) -> Int =\n f(1).unwrapOr(9)\n\nfun main() -> Int =\n b := s(|x| {if x > 0 then Some(\"yes\") else None}).length()\n b2 := s(|x| Some(\"ab\")).length()\n c := e(|x| Ok(x + 1))\n d := e(|x| Err(Bad))\n b * 1000 + b2 * 100 + c * 10 + d\n";
|
|
20
|
-
assert_eq!(runInt("opt_mixed", src), 3229);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#[test]
|
|
24
|
-
fn fn_field_returning_optional() {
|
|
25
|
-
let src = "import std/Int\nimport std/Option\n\nstruct Gen(next: fn() -> Option[Int])\n\nfun count(g: Gen) -> Int =\n g.next().unwrapOr(0)\n\nfun main() -> Int =\n k := 5\n count(Gen(next: || Some(k)))\n";
|
|
26
|
-
assert_eq!(runInt("field_opt", src), 5);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
#[test]
|
|
30
|
-
fn static_method_of_generic_type_not_pinning_t() {
|
|
31
|
-
let src = "import std/Int\n\nstruct Gen[T](v: T) =\n fun ints(n: Int) -> Gen[Int] = Gen(v: n)\n fun get(self) -> T = self.v\n\nfun main() -> Int =\n Gen.ints(4).get()\n";
|
|
32
|
-
assert_eq!(runInt("static_unpinned", src), 4);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
#[test]
|
|
36
|
-
fn seq_with_optional_next() {
|
|
37
|
-
let src = "import std/Int\nimport std/Option\n\nstruct Seq[T](next: fn(Int) -> Option[T]) =\n fun upTo(n: Int) -> Seq[Int] = Seq(next: |i| {if i < n then Some(i * 2) else None})\n fun sum(self) -> Int =\n total := 0\n i := 0\n while Some(v) := self.next(i)\n total = total + v\n i = i + 1\n total\n\nfun main() -> Int =\n Seq.upTo(4).sum()\n";
|
|
38
|
-
assert_eq!(runInt("seq_opt", src), 12);
|
|
39
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_types/closure_types.rs
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
use plum_test_support::*;
|
|
2
|
-
|
|
3
|
-
const BOX: &str = "import std/Int\n\nstruct Box(v: Int) =\n fun get(self) -> Int = self.v\n\n";
|
|
4
|
-
|
|
5
|
-
#[test]
|
|
6
|
-
fn g1_typed_param() {
|
|
7
|
-
let src = format!("{BOX}fun ap(f: fn(Box) -> Int) -> Int = f(Box(v: 2))\n\nfun main() -> Int =\n ap(|b: Box| b.get())\n");
|
|
8
|
-
assert_eq!(runInt("g1", &src), 2);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
#[test]
|
|
12
|
-
fn g2_local_closure_inferred_from_first_call() {
|
|
13
|
-
let src = format!("{BOX}fun main() -> Int =\n get := |b| b.get()\n get(Box(v: 2))\n");
|
|
14
|
-
assert_eq!(runInt("g2", &src), 2);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
#[test]
|
|
18
|
-
fn g5_returned_closure() {
|
|
19
|
-
let src = "import std/Int\n\nfun adder(n: Int) -> fn(Int) -> Int =\n |x| x + n\n\nfun main() -> Int =\n add1 := adder(1)\n add1(2)\n";
|
|
20
|
-
assert_eq!(runInt("g5", src), 3);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// `adder(1)(2)` (call on a call result) does not parse: the grammar has no call-on-call form.
|
|
24
|
-
|
|
25
|
-
#[test]
|
|
26
|
-
fn adder_two_levels() {
|
|
27
|
-
let src = "import std/Int\n\nfun adder(n: Int) -> fn(Int) -> fn(Int) -> Int =\n |x| |y| x + y + n\n\nfun main() -> Int =\n f := adder(1)\n g := f(2)\n g(3)\n";
|
|
28
|
-
assert_eq!(runInt("two", src), 6);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const LINK: &str = "import std/Int\n\nenum Link[T] = Nil | Cons(head: T, tail: Link[T])\n\nfun len[T](xs: Link[T]) -> Int = match xs\n Nil => 0\n Cons(_, t) => 1 + len(t)\n\n";
|
|
32
|
-
|
|
33
|
-
/// A bare `Nil` whose `T` is fixed by a LATER argument gets the call's `Link[Int]`.
|
|
34
|
-
#[test]
|
|
35
|
-
fn bare_tag_fixed_by_later_arg() {
|
|
36
|
-
let src = format!("{LINK}fun push[T](xs: Link[T], x: T) -> Link[T] = Cons(head: x, tail: xs)\n\nfun main() -> Int =\n len(push(push(Nil, 1), 2))\n");
|
|
37
|
-
assert_eq!(runInt("bare_later", &src), 2);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
#[test]
|
|
41
|
-
fn bare_tag_fixed_by_other_link_arg() {
|
|
42
|
-
let src = format!("{LINK}fun two[T](a: Link[T], b: Link[T]) -> Int = len(a) + len(b)\n\nfun mk(n: Int) -> Link[Int] = Cons(head: n, tail: Nil)\n\nfun main() -> Int =\n two(Nil, mk(2)) * 10 + two(mk(1), Nil)\n");
|
|
43
|
-
assert_eq!(runInt("bare_other", &src), 11);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
#[test]
|
|
47
|
-
fn curried_closure_runs() {
|
|
48
|
-
let src = "import std/Int\n\nfun curry() -> fn(Int) -> fn(Int) -> Int =\n |x| |y| x + y\n\nfun main() -> Int =\n add2 := curry()\n f := add2(2)\n f(3)\n";
|
|
49
|
-
assert_eq!(runInt("curried", src), 5);
|
|
50
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_types/list_and_shadow.rs
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
//! F-T7-1: an unannotated list literal whose first element leaves the enum's generic
|
|
2
|
-
//! unpinned (a later element pins it), and a user type shadowing a std type that std
|
|
3
|
-
//! imports on its own (`std/Int` imports `std/Pair` for `Range.next`).
|
|
4
|
-
use plum_test_support::*;
|
|
5
|
-
|
|
6
|
-
const NODE: &str = "\
|
|
7
|
-
import std/Str
|
|
8
|
-
import std/Int
|
|
9
|
-
import std/List
|
|
10
|
-
|
|
11
|
-
enum Node[M] =
|
|
12
|
-
| Box(w: Int)
|
|
13
|
-
| Tag(msg: M, w: Int)
|
|
14
|
-
| Group(kids: List[Node[M]])
|
|
15
|
-
| Wrap(child: Node[M], k: Int)
|
|
16
|
-
|
|
17
|
-
fun total(self) -> Int = match self
|
|
18
|
-
Box(w) => w
|
|
19
|
-
Tag(_, w) => w
|
|
20
|
-
Group(kids) => Node.sumKids(kids)
|
|
21
|
-
Wrap(child, k) => Node.twice(child, k)
|
|
22
|
-
|
|
23
|
-
fun twice(child: Node[M], k: Int) -> Int = child.total() + child.total() + k
|
|
24
|
-
|
|
25
|
-
fun sumKids(kids: List[Node[M]]) -> Int = match kids
|
|
26
|
-
Nil => 0
|
|
27
|
-
Cons(head, tail) => head.total() + Node.sumKids(tail)
|
|
28
|
-
|
|
29
|
-
# A second instantiation, so a wrongly pinned element picks a different type.
|
|
30
|
-
fun other(n: Node[Int]) -> Int = n.total()
|
|
31
|
-
|
|
32
|
-
fun tagged(s: Str) -> Node[Str] = Tag(msg: s, w: 2)
|
|
33
|
-
|
|
34
|
-
";
|
|
35
|
-
|
|
36
|
-
#[test]
|
|
37
|
-
fn list_literal_element_pinned_by_a_later_element() {
|
|
38
|
-
let src = format!(
|
|
39
|
-
"{NODE}fun view(s: Str) -> Node[Str] =
|
|
40
|
-
t := tagged(s)
|
|
41
|
-
Wrap(child: Group(kids: [Box(w: 1), t, Box(w: 3)]), k: 100)
|
|
42
|
-
|
|
43
|
-
fun main() -> Int =
|
|
44
|
-
view(\"x\").total() + other(Box(w: 10))
|
|
45
|
-
"
|
|
46
|
-
);
|
|
47
|
-
assert_eq!(runInt("list_pinned_late_fused", &src), 122);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
#[test]
|
|
51
|
-
fn unannotated_local_of_list_pinned_by_a_later_element() {
|
|
52
|
-
let src = format!(
|
|
53
|
-
"{NODE}fun view(s: Str) -> Int =
|
|
54
|
-
t := tagged(s)
|
|
55
|
-
g := Group(kids: [Box(w: 1), t])
|
|
56
|
-
q := g
|
|
57
|
-
g.total() + q.total()
|
|
58
|
-
|
|
59
|
-
fun main() -> Int =
|
|
60
|
-
view(\"x\") + other(Box(w: 10))
|
|
61
|
-
"
|
|
62
|
-
);
|
|
63
|
-
assert_eq!(runInt("list_pinned_late_local", &src), 16);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
#[test]
|
|
67
|
-
fn user_type_shadows_std_pair_imported_by_std() {
|
|
68
|
-
let src = "\
|
|
69
|
-
import std/Int
|
|
70
|
-
|
|
71
|
-
struct Pair(a: Int, b: Int) =
|
|
72
|
-
fun sum(self) -> Int = self.a + self.b
|
|
73
|
-
|
|
74
|
-
fun main() -> Int =
|
|
75
|
-
p := Pair(a: 3, b: 4)
|
|
76
|
-
for i := range 3
|
|
77
|
-
p = Pair(a: p.a + i, b: p.b)
|
|
78
|
-
p.a * 10 + p.sum()
|
|
79
|
-
";
|
|
80
|
-
assert_eq!(runInt("user_pair", src), 70);
|
|
81
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_types/mod.rs
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
#![allow(non_snake_case)]
|
|
2
|
-
mod closure_optional;
|
|
3
|
-
mod closure_types;
|
|
4
2
|
mod io_entry;
|
|
5
3
|
mod io_primitive;
|
|
6
|
-
mod list_and_shadow;
|
|
7
4
|
mod question;
|
|
8
5
|
mod subtyping;
|
|
9
6
|
mod synthetic_sites;
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_types/question.rs
CHANGED
|
@@ -7,54 +7,6 @@ fn prog(body: &str) -> String {
|
|
|
7
7
|
format!("{HEAD}{body}")
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
#[test]
|
|
11
|
-
fn chained_question_returns_first_err() {
|
|
12
|
-
let src = prog(
|
|
13
|
-
"fun pick(n: Int) -> Result[Int, Int] =\n if n > 0\n return Ok(n)\n Err(100 + n)\n\nfun f(x: Int, y: Int, z: Int) -> Result[Int, Int] =\n p := pick(x)?\n q := pick(y)?\n r := pick(z)?\n Ok(p + q + r)\n\nfun main() -> Int =\n s := f(1, 2, 3).unwrapOr(0)\n t := f(1, 0, 0)\n bad := t.isErr()\n if bad then s * 1000 + 1 else 0\n",
|
|
14
|
-
);
|
|
15
|
-
assert_eq!(runInt("q_chain", &src), 6001);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
#[test]
|
|
19
|
-
fn chained_err_value_is_first() {
|
|
20
|
-
let src = prog(
|
|
21
|
-
"fun pick(n: Int) -> Result[Int, Int] =\n if n > 0\n return Ok(n)\n Err(100 + n)\n\nfun f(x: Int, y: Int, z: Int) -> Result[Int, Int] =\n p := pick(x)?\n q := pick(y)?\n r := pick(z)?\n Ok(p + q + r)\n\nfun main() -> Int =\n match f(1, -5, -7)\n Ok(v) => v\n Err(e) => e\n",
|
|
22
|
-
);
|
|
23
|
-
assert_eq!(runInt("q_first_err", &src), 95);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
#[test]
|
|
27
|
-
fn option_question_returns_none() {
|
|
28
|
-
let src = prog(
|
|
29
|
-
"fun g(n: Int) -> Option[Int] = if n > 0 then Some(n) else None\n\nfun f(n: Int) -> Option[Int] =\n v := g(n)?\n Some(v * 2)\n\nfun main() -> Int =\n f(4).unwrapOr(-1) * 100 + f(0).unwrapOr(7)\n",
|
|
30
|
-
);
|
|
31
|
-
assert_eq!(runInt("q_none", &src), 807);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
#[test]
|
|
35
|
-
fn ok_unit_and_expression_position() {
|
|
36
|
-
let src = prog(
|
|
37
|
-
"fun g(n: Int) -> Option[Int] = if n > 0 then Some(n) else None\n\nfun u(n: Int) -> Result[Unit, Int] =\n if n > 0\n return Ok(Unit)\n Err(n)\n\nfun h(n: Int) -> Option[Int] = Some(1 + g(n)?)\n\nfun main() -> Int =\n a := h(5).unwrapOr(0)\n b := u(1).isOk()\n c := u(0).isErr()\n if b && c then a else -1\n",
|
|
38
|
-
);
|
|
39
|
-
assert_eq!(runInt("q_unit_expr", &src), 6);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
#[test]
|
|
43
|
-
fn struct_payload_in_some() {
|
|
44
|
-
let src = prog(
|
|
45
|
-
"fun g(n: Int) -> Option[Pt] = if n > 0 then Some(Pt(first: n, second: 2)) else None\n\nfun f(n: Int) -> Option[Int] =\n p := g(n)?\n Some(p.first * 10 + p.second)\n\nfun main() -> Int =\n f(3).unwrapOr(0)\n",
|
|
46
|
-
);
|
|
47
|
-
assert_eq!(runInt("q_struct", &src), 32);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
#[test]
|
|
51
|
-
fn bang_unwraps_and_chains() {
|
|
52
|
-
let src = prog(
|
|
53
|
-
"fun g(n: Int) -> Option[Pt] = Some(Pt(first: n, second: 2))\n\nfun main() -> Int =\n x := Some(3)!\n y := g(4)!.first\n r: Result[Int, Int] := Ok(5)\n x + y * 10 + r! * 100\n",
|
|
54
|
-
);
|
|
55
|
-
assert_eq!(runInt("q_bang", &src), 543);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
10
|
#[test]
|
|
59
11
|
fn bang_on_none_crashes() {
|
|
60
12
|
let src = prog("fun main() -> Int =\n o: Option[Int] := None\n o!\n");
|
|
@@ -62,99 +14,3 @@ fn bang_on_none_crashes() {
|
|
|
62
14
|
assert!(std::panic::catch_unwind(|| runInt("q_crash", &src)).is_err());
|
|
63
15
|
}
|
|
64
16
|
|
|
65
|
-
#[test]
|
|
66
|
-
fn bare_variants_in_if_branches_take_fn_return_type() {
|
|
67
|
-
let src = prog(
|
|
68
|
-
"fun pickInline(flag: Bool, n: Int) -> Result[Int, Int] = if flag then Ok(n) else Err(100 + n)\n\nfun pickBlock(flag: Bool, n: Int) -> Result[Int, Int] =\n if flag\n Ok(n)\n else\n Err(100 + n)\n\nfun maybe(flag: Bool, n: Int) -> Option[Int] = if flag then None else Some(n)\n\nfun main() -> Int =\n a := pickInline(false, 1).isErr()\n b := pickBlock(true, 2).unwrapOr(0)\n c := maybe(false, 3).unwrapOr(0)\n d := maybe(true, 3).isNone()\n if a && d then b * 10 + c else -1\n",
|
|
69
|
-
);
|
|
70
|
-
assert_eq!(runInt("q_if_variants", &src), 23);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
#[test]
|
|
74
|
-
fn bare_variants_in_match_arms_take_fn_return_type() {
|
|
75
|
-
let src = prog(
|
|
76
|
-
"fun pick(n: Int) -> Result[Int, Int] = match n\n 0 => Err(100)\n _ => Ok(n)\n\nfun main() -> Int =\n match pick(0)\n Ok(v) => v\n Err(e) => e + pick(4).unwrapOr(0)\n",
|
|
77
|
-
);
|
|
78
|
-
assert_eq!(runInt("q_match_variants", &src), 104);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
#[test]
|
|
82
|
-
fn map_err_on_call_result() {
|
|
83
|
-
let src = prog(
|
|
84
|
-
"fun fails() -> Result[Int, Int] =\n r: Result[Int, Int] := Err(7)\n r\n\nfun main() -> Int =\n match fails().mapErr(|e| e + 1000)\n Ok(v) => v\n Err(e) => e\n",
|
|
85
|
-
);
|
|
86
|
-
assert_eq!(runInt("q_map_err", &src), 1007);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/// mapErr's closure param `f` must not take the arity of a top-level `fun f`.
|
|
91
|
-
#[test]
|
|
92
|
-
fn map_err_closure_param_shadows_top_level_fn() {
|
|
93
|
-
let src = prog(
|
|
94
|
-
"fun f(flag: Bool, n: Int) -> Result[Int, Int] = if flag then Ok(n) else Err(100 + n)\n\nfun main() -> Int =\n match f(false, 1).mapErr(|e| e + 1000)\n Ok(v) => v\n Err(e) => e\n",
|
|
95
|
-
);
|
|
96
|
-
assert_eq!(runInt("q_shadow", &src), 1101);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/// An unannotated inline if: each bare variant pins the generic the other leaves open.
|
|
100
|
-
#[test]
|
|
101
|
-
fn inline_if_joins_bare_variant_instantiations() {
|
|
102
|
-
let src = prog(
|
|
103
|
-
"fun main() -> Int =\n flag := 3 > 2\n o := if flag then Ok(4) else Err(5)\n low := if flag then Err(1) else Ok(Pt(first: 1, second: 2))\n a := o.unwrapOr(0)\n match low\n Ok(pt) => pt.first\n Err(e) => a * 10 + e\n",
|
|
104
|
-
);
|
|
105
|
-
assert_eq!(runInt("q_if_join", &src), 41);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/// Fusion inlines `pick(false, 1)` to a bare `Err(101)` and drops `Result[Pt, Int]`
|
|
109
|
-
/// (plum-core simplify), so codegen builds `Ok`'s payload as Unit.
|
|
110
|
-
#[test]
|
|
111
|
-
fn inlined_constant_call_keeps_return_type() {
|
|
112
|
-
let src = prog(
|
|
113
|
-
"fun pick(flag: Bool, n: Int) -> Result[Pt, Int] = if flag then Ok(Pt(first: n, second: 1)) else Err(100 + n)\n\nfun main() -> Int =\n match pick(false, 1)\n Ok(pt) => pt.first\n Err(e) => e\n",
|
|
114
|
-
);
|
|
115
|
-
assert_eq!(runInt("q_inlined", &src), 101);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/// Simplify inlines `pick(true, 2)` to `Ok(2)`; drive then fuses `unwrapOr` over `mapErr`
|
|
119
|
-
/// into a fn generic in `E`, which a bare `Ok(2)` never binds (no instance is emitted).
|
|
120
|
-
#[test]
|
|
121
|
-
fn map_err_then_unwrap_or_fuses() {
|
|
122
|
-
let src = prog(
|
|
123
|
-
"fun pick(flag: Bool, n: Int) -> Result[Int, Int] = if flag then Ok(n) else Err(100 + n)\n\nfun main() -> Int =\n pick(true, 2).mapErr(|e| e + 1).unwrapOr(0)\n",
|
|
124
|
-
);
|
|
125
|
-
assert_eq!(runInt("q_map_err_unwrap", &src), 2);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/// Drive specialises `mapErr` / `ok` on the inlined `Err(..)`: the picked arm's own `Err` /
|
|
129
|
-
/// `None` leaves `mapErr` / `ok` and must keep its `Result[Pt, Int]` / `Option[Pt]`.
|
|
130
|
-
#[test]
|
|
131
|
-
fn specialised_arm_keeps_result_type() {
|
|
132
|
-
let src = prog(
|
|
133
|
-
"fun pick(flag: Bool, n: Int) -> Result[Pt, Int] = if flag then Ok(Pt(first: n, second: 1)) else Err(100 + n)\n\nfun viaMapErr() -> Int =\n match pick(false, 2).mapErr(|e| e + 1)\n Ok(pt) => pt.first\n Err(e) => e\n\nfun viaOk() -> Int =\n r := pick(false, 4)\n match r.ok()\n Some(pt) => pt.first\n None => 8\n\nfun main() -> Int = viaMapErr() * 10 + viaOk()\n",
|
|
134
|
-
);
|
|
135
|
-
assert_eq!(runInt("q_spec_arm", &src), 1038);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
#[test]
|
|
139
|
-
fn none_as_param_default() {
|
|
140
|
-
let src = prog(
|
|
141
|
-
"fun f(n: Option[Int] = None) -> Option[Int] = n\n\nfun main() -> Int =\n a := f().unwrapOr(7)\n b := f(Some(3)).unwrapOr(0)\n a * 10 + b\n",
|
|
142
|
-
);
|
|
143
|
-
assert_eq!(runInt("q_none_default", &src), 73);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
#[test]
|
|
147
|
-
fn none_as_generic_param_default() {
|
|
148
|
-
let src = prog(
|
|
149
|
-
"fun g[T](x: T, o: Option[T] = None) -> Option[T] = o\n\nfun main() -> Int =\n a := g(1).unwrapOr(7)\n b := g(1, Some(3)).unwrapOr(0)\n a * 10 + b\n",
|
|
150
|
-
);
|
|
151
|
-
assert_eq!(runInt("q_none_generic_default", &src), 73);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
#[test]
|
|
155
|
-
fn err_field_is_error() {
|
|
156
|
-
let src = prog(
|
|
157
|
-
"fun main() -> Int =\n r: Result[Int, Int] := Err(error: 5)\n match r\n Ok(v) => v\n Err(e) => e\n",
|
|
158
|
-
);
|
|
159
|
-
assert_eq!(runInt("q_err_field", &src), 5);
|
|
160
|
-
}
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_types/synthetic_sites.rs
CHANGED
|
@@ -1,118 +1,11 @@
|
|
|
1
1
|
//! The places codegen reads a type off a name or a call rather than off an expression node of
|
|
2
2
|
//! its own: each runs the shape through the whole pipeline and checks the value.
|
|
3
|
-
use plum_test_support::{compileSource, load,
|
|
3
|
+
use plum_test_support::{compileSource, load, PRE};
|
|
4
4
|
|
|
5
5
|
fn prog(body: &str) -> String {
|
|
6
6
|
format!("{PRE}{body}")
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
/// `Nothing()` (call syntax on a field-less variant) is the variant's
|
|
10
|
-
/// singleton, typed by the call node.
|
|
11
|
-
#[test]
|
|
12
|
-
fn field_less_variant_called_like_a_constructor() {
|
|
13
|
-
let src = prog("\
|
|
14
|
-
enum Maybe =
|
|
15
|
-
| Nothing
|
|
16
|
-
| Just(value: Int)
|
|
17
|
-
|
|
18
|
-
fun orZero(m: Maybe) -> Int = match m
|
|
19
|
-
Nothing => 0
|
|
20
|
-
Just(v) => v
|
|
21
|
-
|
|
22
|
-
fun main() -> Int =
|
|
23
|
-
a: Maybe := Nothing()
|
|
24
|
-
b: Maybe := Just(value: 40)
|
|
25
|
-
orZero(a) + orZero(b) + 2
|
|
26
|
-
");
|
|
27
|
-
assert_eq!(runInt("nothing_call", &src), 42);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/// A call through a local closure whose type is only known from the local's own initialiser.
|
|
31
|
-
#[test]
|
|
32
|
-
fn unit_closure_local_called_as_a_statement() {
|
|
33
|
-
let src = prog("\
|
|
34
|
-
fun main() -> Int =
|
|
35
|
-
f := || Unit
|
|
36
|
-
f()
|
|
37
|
-
f()
|
|
38
|
-
7
|
|
39
|
-
");
|
|
40
|
-
assert_eq!(runInt("unit_closure_local", &src), 7);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/// A rebuild that reuses the matched cell reads the cell's type off the local it names.
|
|
44
|
-
#[test]
|
|
45
|
-
fn reused_cell_is_read_through_its_local() {
|
|
46
|
-
let src = prog("\
|
|
47
|
-
enum Link =
|
|
48
|
-
| Nil
|
|
49
|
-
| Cons(head: Int, tail: Link)
|
|
50
|
-
|
|
51
|
-
fun bump(xs: Link) -> Link = match xs
|
|
52
|
-
Nil => Nil
|
|
53
|
-
Cons(h, t) => Cons(head: h + 1, tail: t)
|
|
54
|
-
|
|
55
|
-
fun sum(xs: Link) -> Int = match xs
|
|
56
|
-
Nil => 0
|
|
57
|
-
Cons(h, t) => h + sum(t)
|
|
58
|
-
|
|
59
|
-
fun main() -> Int = sum(bump(Cons(head: 1, tail: Cons(head: 2, tail: Nil))))
|
|
60
|
-
");
|
|
61
|
-
assert_eq!(runInt("reuse_local", &src), 4);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/// The same call syntax on a variant of a generic enum: the call has the enum's instantiated type.
|
|
65
|
-
#[test]
|
|
66
|
-
fn field_less_variant_of_a_generic_enum_called_like_a_constructor() {
|
|
67
|
-
let src = prog("\
|
|
68
|
-
enum Maybe[T] =
|
|
69
|
-
| Nothing
|
|
70
|
-
| Just(value: T)
|
|
71
|
-
|
|
72
|
-
fun orZero(m: Maybe[Int]) -> Int = match m
|
|
73
|
-
Nothing => 0
|
|
74
|
-
Just(v) => v
|
|
75
|
-
|
|
76
|
-
fun main() -> Int =
|
|
77
|
-
a: Maybe[Int] := Nothing()
|
|
78
|
-
b: Maybe[Int] := Just(value: 40)
|
|
79
|
-
orZero(a) + orZero(b) + 2
|
|
80
|
-
");
|
|
81
|
-
assert_eq!(runInt("nothing_call_generic", &src), 42);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/// F-T9-2: a closure calling itself with `None` for an `Option[Int]` param. The checker
|
|
85
|
-
/// types that `None` as `Option[Unit]`; the param's declared type picks the singleton, so the
|
|
86
|
-
/// wasm is valid and the value right.
|
|
87
|
-
#[test]
|
|
88
|
-
fn closure_passes_none_to_an_option_param() {
|
|
89
|
-
let src = prog("\
|
|
90
|
-
import std/Option
|
|
91
|
-
|
|
92
|
-
fun main() -> Int =
|
|
93
|
-
rec := |o: Option[Int], acc: Int, n: Int|
|
|
94
|
-
if n <= 0 then acc else rec(None, acc + 1, n - 1)
|
|
95
|
-
rec(None, 0, 3)
|
|
96
|
-
");
|
|
97
|
-
assert_eq!(runInt("closure_none_typed", &src), 3);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
#[test]
|
|
101
|
-
fn untyped_closure_passes_none_to_an_option_param() {
|
|
102
|
-
let src = prog("\
|
|
103
|
-
import std/Option
|
|
104
|
-
|
|
105
|
-
fun run(n: Int, o: Option[Int]) -> Int =
|
|
106
|
-
rec := |o, acc, n|
|
|
107
|
-
if n <= 0 then 0 else rec(None, acc + 1, n - 1)
|
|
108
|
-
rec(o, 0, n)
|
|
109
|
-
|
|
110
|
-
fun main() -> Int =
|
|
111
|
-
run(3, None)
|
|
112
|
-
");
|
|
113
|
-
assert_eq!(runInt("closure_none_untyped", &src), 0);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
9
|
/// F-T9-1: a call to an unknown function reports that, not the missing type of its
|
|
117
10
|
/// (unchecked) arguments.
|
|
118
11
|
#[test]
|
compiler/wasm-codegen/tests/wasm_codegen/e2e_types/type_param_static.rs
CHANGED
|
@@ -1,36 +1,5 @@
|
|
|
1
1
|
use plum_test_support::*;
|
|
2
2
|
|
|
3
|
-
#[test]
|
|
4
|
-
fn static_on_type_param() {
|
|
5
|
-
let src = "\
|
|
6
|
-
import std/Int
|
|
7
|
-
|
|
8
|
-
struct Pt(x: Int) =
|
|
9
|
-
fun make(n: Int) -> Pt = Pt(x: n * 10)
|
|
10
|
-
|
|
11
|
-
fun build[T](n: Int) -> T = T.make(n)
|
|
12
|
-
|
|
13
|
-
fun main() -> Int =
|
|
14
|
-
p: Pt := build(3)
|
|
15
|
-
return p.x
|
|
16
|
-
";
|
|
17
|
-
assert_eq!(runInt("rec", src), 30);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
#[test]
|
|
21
|
-
fn static_on_builtin() {
|
|
22
|
-
let src = "\
|
|
23
|
-
import std/Int
|
|
24
|
-
|
|
25
|
-
fun maxOf[T](n: Int) -> T = T.Max
|
|
26
|
-
|
|
27
|
-
fun main() -> Int =
|
|
28
|
-
m: Int := maxOf(1)
|
|
29
|
-
return if m == Int.Max then 1 else 0
|
|
30
|
-
";
|
|
31
|
-
assert_eq!(runInt("builtin", src), 1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
3
|
#[test]
|
|
35
4
|
fn static_missing_reports() {
|
|
36
5
|
let src = "\
|
|
@@ -52,50 +21,6 @@ fun main() -> Int =
|
|
|
52
21
|
);
|
|
53
22
|
}
|
|
54
23
|
|
|
55
|
-
#[test]
|
|
56
|
-
fn nested_generic() {
|
|
57
|
-
let src = "\
|
|
58
|
-
import std/Int
|
|
59
|
-
import std/Slice
|
|
60
|
-
import std/List
|
|
61
|
-
|
|
62
|
-
struct Pt(x: Int) =
|
|
63
|
-
fun make(n: Int) -> Pt = Pt(x: n + 1)
|
|
64
|
-
|
|
65
|
-
fun buildAll[T](ns: Slice[Int]) -> Slice[T] =
|
|
66
|
-
out := Slice[T]()
|
|
67
|
-
for n := range ns
|
|
68
|
-
out.push(T.make(n))
|
|
69
|
-
return out
|
|
70
|
-
|
|
71
|
-
fun main() -> Int =
|
|
72
|
-
ps: Slice[Pt] := buildAll([1, 2, 3].toSlice())
|
|
73
|
-
p := ps.get(2).unwrapOr(Pt(x: 0))
|
|
74
|
-
return p.x
|
|
75
|
-
";
|
|
76
|
-
assert_eq!(runInt("nested", src), 4);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
#[test]
|
|
80
|
-
fn static_on_builtin_method_and_json_cycle() {
|
|
81
|
-
let src = "\
|
|
82
|
-
import std/Int
|
|
83
|
-
import std/Str
|
|
84
|
-
import std/Json
|
|
85
|
-
import std/Result
|
|
86
|
-
|
|
87
|
-
fun parseIt[T](s: Str) -> Result[T, NumError] =
|
|
88
|
-
T.parse(s)
|
|
89
|
-
|
|
90
|
-
fun main() -> Int =
|
|
91
|
-
r: Result[Int, NumError] := parseIt(\"41\")
|
|
92
|
-
n := r.unwrapOr(0)
|
|
93
|
-
j := n.toJson()
|
|
94
|
-
return n + 1
|
|
95
|
-
";
|
|
96
|
-
assert_eq!(runInt("builtin_method", src), 42);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
24
|
#[test]
|
|
100
25
|
fn extension_outside_std_is_rejected() {
|
|
101
26
|
let src = "\
|
|
@@ -130,11 +55,3 @@ fun main() -> Int =
|
|
|
130
55
|
};
|
|
131
56
|
assert!(!errs.is_empty(), "toJson must not exist without std/Json");
|
|
132
57
|
}
|
|
133
|
-
|
|
134
|
-
/// Single uppercase letters are ordinary names: a struct, enum variants, a constant, and a
|
|
135
|
-
/// declared multi-letter fn generic all work side by side.
|
|
136
|
-
#[test]
|
|
137
|
-
fn single_letter_types_and_variants() {
|
|
138
|
-
let src = "import std/Int\n\nstruct P(x: Int)\n\nenum Two = A | B\n\nstruct Cfg(n: Int) =\n E = 5\n\nfun pick(t: Two) -> Int = match t\n A => 1\n B => 20\n\nfun id[Elem](x: Elem) -> Elem = x\n\nfun main() -> Int =\n p := P(x: id(3))\n pick(A) + pick(B) + p.x + Cfg.E\n";
|
|
139
|
-
assert_eq!(runInt("single_letter_types", src), 29);
|
|
140
|
-
}
|
docs/backlog.md
CHANGED
|
@@ -4,7 +4,7 @@ Open work, in the order the user queued it, plus smaller items found during buil
|
|
|
4
4
|
|
|
5
5
|
## Planned (in order)
|
|
6
6
|
|
|
7
|
-
**Current order (user 2026-11-05):** explicit fn generics (done faa4d7e) → cleanup phase 3b (pass list, positioned errors; running) → Io primitive + extern methods → move Rust crates under `compiler/` → tooling + scripts moved under compiler/ (done) → cleanup phase 4 (done: node ids, typed codegen, `inferExpr` errors at spans; `docs/plans/2026-11-05-compiler-cleanup-phase4.results.md`) →
|
|
7
|
+
**Current order (user 2026-11-05):** explicit fn generics (done faa4d7e) → cleanup phase 3b (pass list, positioned errors; running) → Io primitive + extern methods → move Rust crates under `compiler/` → tooling + scripts moved under compiler/ (done) → cleanup phase 4 (done: node ids, typed codegen, `inferExpr` errors at spans; `docs/plans/2026-11-05-compiler-cleanup-phase4.results.md`) → phases 5 and 6 (done: file splits, type kinds, tests in Plum; `docs/plans/2026-11-05-compiler-cleanup-phase5-6.results.md`) → benchmark (incl. iterator unboxing / niche Option, run C game A/B) → FBIP (stop for user approval) → deterministic sim → kids' games. The items below keep their detail; this line wins on order.
|
|
8
8
|
|
|
9
9
|
1. **Compiler cleanup and architecture** — `docs/plans/2026-11-04-compiler-cleanup.review.md` (written in another session; meant to run after language-restrictions merged). Approved 2026-11-04; running on branch `superlite/compiler-cleanup`, phase 1 first.
|
|
10
10
|
- **Explicit function generics** (user 2026-11-04: right after cleanup phase 2) — functions declare their generics (`fun map[B](...)`) instead of every single capital letter being a generic; allows longer generic names and one-letter concrete types/tags. Grammar change + about 140 signatures in std/examples. Phase 2's "generics by scope" makes this mostly a grammar + signature rewrite.
|
docs/plans/2026-11-05-compiler-cleanup-phase5-6.md
CHANGED
|
@@ -112,7 +112,7 @@ e.g. `test "deep tail recursion"` running a 200000-step self-tail call, a method
|
|
|
112
112
|
**Test code:** the new test passes; sweep with the new folder.
|
|
113
113
|
|
|
114
114
|
### T7 move wasm_codegen run tests · Complexity 4 · Est. context 50k
|
|
115
|
-
**Owns:** compiler/wasm-codegen/tests/wasm_codegen/**/*.rs, compiler/tests/
|
|
115
|
+
**Owns:** compiler/wasm-codegen/tests/wasm_codegen/**/*.rs, compiler/tests/wasm_codegen/**/*.plum
|
|
116
116
|
**Components:** 1
|
|
117
117
|
**LOC owned:** 200
|
|
118
118
|
**Tests:** fast
|
docs/plans/2026-11-05-compiler-cleanup-phase5-6.progress.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
- start 5ecf1ab
|
|
2
|
+
Ruling: lint --max-task-min 25 for this plan - the predictor's history took in the last run's inflated timings (a 20-line results doc predicted at 19 min), so every task failed the 15 min limit; sizes are unchanged - cost: a task may run long
|
|
3
|
+
Ruling: accept predicted 132 min over the 90 min target for the same reason; the predictor overstates it - cost: none
|
|
4
|
+
Ruling: skip Wave 0 - no shared stubs - cost: none
|
|
5
|
+
- unblocked: dispatched T1 T2 at 22:37:01; waiting on fixes: none
|
|
6
|
+
Ruling: T2 waits for T1 despite the plan graph - wasm-codegen depends on plum-checker, so T2 can't build while T1 has the checker half moved - cost: about 15 min
|
|
7
|
+
- checkpoint T1 7c6a0a8056 checker split
|
|
8
|
+
- checkpoint T2 ed3a336e1c codegen split
|
|
9
|
+
Ruling: T1 and T2 pure moves verified by a coordinator sorted-line check against HEAD instead of a reviewer - cost: none
|
|
10
|
+
- checkpoint T3 56805e9736 codegen driver stages
|
|
11
|
+
- unblocked: dispatched T4 prime at 22:59:17; waiting on fixes: T3 review
|
|
12
|
+
Ruling: T3 review clean - statement order unchanged, no early returns, taken fields read only before they are taken; closure-only and tests-only files not exercised by it, covered by examples and std - cost: none
|
|
13
|
+
- unblocked: dispatched T4 at 23:01:46; waiting on fixes: none
|
|
14
|
+
- checkpoint T4 22338aefb9 type kinds
|
|
15
|
+
Ruling: T5 run by the coordinator with a script - verification only, no judgement needed - cost: none
|
|
16
|
+
- checkpoint T5 68cfdf7f34 verify phase 5
|
|
17
|
+
- unblocked: dispatched T6 at 23:10:49; waiting on fixes: none
|
|
18
|
+
- checkpoint T6 a4c7b4a80c Plum test home and tail-call proof
|
|
19
|
+
- unblocked: dispatched T7 T8 at 23:13:34; waiting on fixes: none
|
|
20
|
+
- checkpoint T8 dc0ff3997b move other run tests
|
|
21
|
+
- checkpoint T7 47565d6c8e move wasm_codegen run tests
|
|
22
|
+
Ruling: T7 Owns moved to compiler/tests/wasm_codegen - .gitignore ignores any path named codegen - cost: none
|
|
23
|
+
- tests full passed=741 failed=0 skipped=0 seconds=49.3
|
|
24
|
+
Ruling: final review - three findings, all "the packet shows no evidence", no defect. Evidence exists: T1 and T2 sorted-line check, 0 of about 12,500 lines lost; T3 and T4 reviews, statement order unchanged and wasm identical; 108 of 108 moved tests mapped with no value or program change; a flipped assert fails in each folder; counts 1510 before, 1514 after. Recorded as verified - cost: none
|
|
25
|
+
Ruling: T7 and T8 checkpoint with a pathspec error on deleted files; the moves are committed directly and verified above - cost: none
|
|
26
|
+
Ruling: run_tests --full is not the 14-binary check; verify_all.sh is - cost: none
|
docs/plans/2026-11-05-compiler-cleanup-phase5-6.results.md
CHANGED
|
@@ -27,3 +27,39 @@ The T1 and T2 splits are pure moves. A sorted-line comparison against the previo
|
|
|
27
27
|
**One behaviour note:** a user function literally named `__test_x` used to be skipped by a few passes, because they matched on the name. It no longer is. That name was never a supported way to mark a test.
|
|
28
28
|
|
|
29
29
|
**Also fixed:** `compileEntryTests` shared one temp directory and failed about half the time under parallel threads. It now passes 20 of 20 runs.
|
|
30
|
+
|
|
31
|
+
## Phase 6: tests in Plum
|
|
32
|
+
|
|
33
|
+
**The gate passed.** The review plan only allows this phase if `plum test` runs tail calls the way a compiled program does. `compiler/tests/tail_calls.plum` runs a self, a method and a closure tail call each 1,000,000 deep. A non-tail version at that depth traps and is reported as a failure, so the test can tell the difference.
|
|
34
|
+
|
|
35
|
+
| Check | Before | After |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| Rust tests, all 14 binaries | 848 | 740 (core 395, wasm_codegen 119, the rest unchanged) |
|
|
38
|
+
| Plum tests in the sweep | 662 | 774, with `compiler/tests` added |
|
|
39
|
+
| Total | 1,510 | 1,514 |
|
|
40
|
+
| Example wasm | | byte-identical, 25 files |
|
|
41
|
+
| Browser (stress_scene, imperative, functional, pure) | | PASS |
|
|
42
|
+
|
|
43
|
+
The sweep is now `target/debug/plum test std examples compiler/tests`.
|
|
44
|
+
|
|
45
|
+
**What moved:** 108 Rust tests, 102 from wasm_codegen and 6 from core, more than the 90 planned. They became 109 Plum tests, because one test that made two runs became two files. T6 adds 3 tail-call tests.
|
|
46
|
+
|
|
47
|
+
**How the move was checked:**
|
|
48
|
+
- A script mapped every removed Rust test to its Plum twin: 108 of 108 found, 0 with a different expected value or program.
|
|
49
|
+
- Flipping an expected value made `plum test` fail, tried in each folder.
|
|
50
|
+
- Every original compared an exact `Int`, so no assertion became weaker.
|
|
51
|
+
- No behaviour differences: every moved test passed first time.
|
|
52
|
+
- The explanatory comments moved too, 39 lines.
|
|
53
|
+
|
|
54
|
+
**What stayed in Rust:** compile-error tests, wasm structure checks, traps and crashes, fusion decisions, CLI behaviour, and most of core and checker, because those assert a pass's output or an error message rather than a program's answer.
|
|
55
|
+
|
|
56
|
+
**Costs:**
|
|
57
|
+
- 103 of the new tests are one file each, because programs in one file share a namespace and these reuse names like `Link`. Each compiles separately.
|
|
58
|
+
- `plum test compiler/tests` takes about 20 seconds, and the Rust suite is correspondingly shorter.
|
|
59
|
+
- `.gitignore` ignores any path named `codegen`, so these live in `compiler/tests/wasm_codegen/` and the plan's path was updated.
|
|
60
|
+
- Test bodies skip the tail, TRMC, closure-spec and drive passes, so moved programs keep their logic in helper functions. New tests need to do the same.
|
|
61
|
+
|
|
62
|
+
## Not verified
|
|
63
|
+
|
|
64
|
+
- Frame times against `BENCHMARK.md`. The browser runs are headless and capped, so the numbers don't compare. The benchmark item is still queued.
|
|
65
|
+
- WebKit, release builds, and the LSP in an editor.
|