Merge remote-tracking branch 'remote/main' into builtin-task

This commit is contained in:
Luke Boswell 2024-07-19 19:51:50 +10:00
commit b489c44b19
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
262 changed files with 11354 additions and 5821 deletions

View file

@ -3912,7 +3912,6 @@ fn list_range_length_overflow() {
);
}
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
mod pattern_match {
#[allow(unused_imports)]
use crate::helpers::with_larger_debug_stack;
@ -3926,9 +3925,10 @@ mod pattern_match {
#[cfg(feature = "gen-dev")]
use crate::helpers::dev::assert_evals_to;
use super::RocList;
use super::{RocList, RocStr};
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn unary_exact_size_match() {
assert_evals_to!(
r"
@ -3944,6 +3944,7 @@ mod pattern_match {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn many_exact_size_match() {
assert_evals_to!(
r"
@ -3962,6 +3963,7 @@ mod pattern_match {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn ranged_matches_head() {
with_larger_debug_stack(|| {
assert_evals_to!(
@ -3994,6 +3996,7 @@ mod pattern_match {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn ranged_matches_tail() {
with_larger_debug_stack(|| {
assert_evals_to!(
@ -4026,6 +4029,7 @@ mod pattern_match {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn bind_variables() {
assert_evals_to!(
r"
@ -4054,6 +4058,7 @@ mod pattern_match {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn order_list_size_tests_issue_4732() {
assert_evals_to!(
r"
@ -4101,6 +4106,7 @@ mod pattern_match {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn rest_as() {
assert_evals_to!(
r"
@ -4122,6 +4128,20 @@ mod pattern_match {
RocList<u8>
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn list_str() {
assert_evals_to!(
r#"
when ["d"] is
[alone] -> [alone]
other -> other
"#,
RocList::from_slice(&[RocStr::from("d")]),
RocList<RocStr>
)
}
}
#[test]

View file

@ -1,5 +1,5 @@
#[cfg(feature = "gen-wasm")]
use crate::helpers::{wasm::assert_refcounts, RefCount::*};
use crate::helpers::{wasm::assert_refcounts, RefCount::*, RefCountLoc::*};
#[allow(unused_imports)]
use indoc::indoc;
@ -25,8 +25,8 @@ fn str_inc() {
),
RocList<RocStr>,
&[
Live(3), // s
Live(1) // result
(StandardRC, Live(3)), // s
(AfterSize, Live(1)) // result
]
);
}
@ -43,7 +43,63 @@ fn str_dealloc() {
"#
),
bool,
&[Deallocated]
&[(StandardRC, Deallocated)]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn str_to_utf8() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
Str.toUtf8 s
"#
),
RocStr,
&[
(StandardRC, Live(1)), // s
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn str_from_utf8() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
Str.toUtf8 s
|> Str.fromUtf8
"#
),
RocStr,
&[
(StandardRC, Live(1)), // s
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn str_to_utf8_dealloc() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
Str.toUtf8 s
|> List.len
"#
),
i64,
&[
(StandardRC, Deallocated), // s
]
);
}
@ -59,8 +115,8 @@ fn list_int_inc() {
),
RocList<RocList<i64>>,
&[
Live(3), // list
Live(1) // result
(StandardRC, Live(3)), // list
(AfterSize, Live(1)) // result
]
);
}
@ -77,8 +133,26 @@ fn list_int_dealloc() {
),
u64,
&[
Deallocated, // list
Deallocated // result
(StandardRC, Deallocated), // list
(StandardRC, Deallocated) // result
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_str() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
[s, s, s]
"#
),
RocList<RocStr>,
&[
(StandardRC, Live(3)), // s
(AfterSize, Live(1)), // Result
]
);
}
@ -96,9 +170,192 @@ fn list_str_inc() {
),
RocList<RocList<RocStr>>,
&[
Live(6), // s
Live(2), // list
Live(1) // result
(StandardRC, Live(3)), // s
(AfterSize, Live(2)), // list
(AfterSize, Live(1)) // result
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_str_drop_first() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
list = [s, s, s]
List.dropFirst list 1
"#
),
RocList<RocList<RocStr>>,
&[
// Still has 3 refcounts cause the slice holds onto the list.
// So nothing in the list is freed yet.
(StandardRC, Live(3)), // s
(AfterSize, Live(1)) // result
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_str_take_first() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
list = [s, s, s]
List.takeFirst list 1
"#
),
RocList<RocList<RocStr>>,
&[
// Take will free tail of a unique list.
(StandardRC, Live(1)), // s
(AfterSize, Live(1)) // result
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_str_split() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
list = [s, s, s]
List.split list 1
"#
),
(RocList<RocStr>, RocList<RocStr>),
&[
(StandardRC, Live(3)), // s
(AfterSize, Live(2)), // list
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_str_split_zero() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
list = [s, s, s]
List.split list 0
"#
),
(RocList<RocStr>, RocList<RocStr>),
&[
(StandardRC, Live(3)), // s
(AfterSize, Live(1)), // list
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_get() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
i1 = [s, s, s]
List.get i1 1
|> Result.withDefault ""
"#
),
RocStr,
&[
(StandardRC, Live(1)), // s
(AfterSize, Deallocated), // i1
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_map() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
i1 = [s, s, s]
List.map i1 Str.toUtf8
"#
),
RocList<RocStr>,
&[
(StandardRC, Live(3)), // s
(AfterSize, Deallocated), // i1
(AfterSize, Live(1)), // Result
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_map_dealloc() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
i1 = [s, s, s]
List.map i1 Str.toUtf8
|> List.len
"#
),
i64,
&[
(StandardRC, Deallocated), // s
(AfterSize, Deallocated), // i1
(AfterSize, Deallocated), // Result
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_map2_dealloc_tail() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
i1 = [s, s, s]
i2 = [1i32, 2]
List.map2 i1 i2 \a, b -> (a, b)
"#
),
RocList<(RocStr, i64)>,
&[
(StandardRC, Live(2)), // s
(AfterSize, Deallocated), // i1
(StandardRC, Deallocated), // i2
(AfterSize, Live(1)), // Result
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn list_map2_dealloc() {
assert_refcounts!(
indoc!(
r#"
s = Str.concat "A long enough string " "to be heap-allocated"
i1 = [s, s, s]
List.map2 i1 i1 \a, b -> (a, b)
|> List.len
"#
),
i64,
&[
(StandardRC, Deallocated), // s
(AfterSize, Deallocated), // i1
(AfterSize, Deallocated), // Result
]
);
}
@ -116,9 +373,9 @@ fn list_str_dealloc() {
),
u64,
&[
Deallocated, // s
Deallocated, // list
Deallocated // result
(StandardRC, Deallocated), // s
(AfterSize, Deallocated), // list
(AfterSize, Deallocated) // result
]
);
}
@ -136,7 +393,7 @@ fn struct_inc() {
"#
),
[(i64, RocStr, RocStr); 2],
&[Live(4)] // s
&[(StandardRC, Live(4))] // s
);
}
@ -154,7 +411,7 @@ fn struct_dealloc() {
"#
),
i64,
&[Deallocated] // s
&[(StandardRC, Deallocated)] // s
);
}
@ -180,7 +437,7 @@ fn union_nonrecursive_inc() {
"#
),
(TwoStr, TwoStr, i64),
&[Live(4)]
&[(StandardRC, Live(4))]
);
}
@ -203,7 +460,7 @@ fn union_nonrecursive_dec() {
"#
),
RocStr,
&[Live(1)] // s
&[(StandardRC, Live(1))] // s
);
}
@ -228,9 +485,9 @@ fn union_recursive_inc() {
),
(Pointer, Pointer),
&[
Live(1), // s
Live(2), // x
Live(2), // e
(StandardRC, Live(1)), // s
(StandardRC, Live(2)), // x
(StandardRC, Live(2)), // e
]
);
}
@ -258,9 +515,9 @@ fn union_recursive_dec() {
),
Pointer,
&[
Live(1), // s
Live(1), // sym
Deallocated // e
(StandardRC, Live(1)), // s
(StandardRC, Live(1)), // sym
(StandardRC, Deallocated) // e
]
);
}
@ -294,13 +551,13 @@ fn refcount_different_rosetrees_inc() {
),
(Pointer, Pointer),
&[
Live(1), // s
Live(3), // i1
Live(2), // s1
Live(1), // [i1, i1]
Live(1), // i2
Live(1), // [s1, s1]
Live(1) // s2
(StandardRC, Live(1)), // s
(StandardRC, Live(3)), // i1
(StandardRC, Live(2)), // s1
(AfterSize, Live(1)), // [i1, i1]
(StandardRC, Live(1)), // i2
(AfterSize, Live(1)), // [s1, s1]
(StandardRC, Live(1)) // s2
]
);
}
@ -335,13 +592,13 @@ fn refcount_different_rosetrees_dec() {
),
i64,
&[
Deallocated, // s
Deallocated, // i1
Deallocated, // s1
Deallocated, // [i1, i1]
Deallocated, // i2
Deallocated, // [s1, s1]
Deallocated, // s2
(StandardRC, Deallocated), // s
(StandardRC, Deallocated), // i1
(StandardRC, Deallocated), // s1
(StandardRC, Deallocated), // [i1, i1]
(StandardRC, Deallocated), // i2
(StandardRC, Deallocated), // [s1, s1]
(StandardRC, Deallocated), // s2
]
);
}
@ -364,10 +621,10 @@ fn union_linked_list_inc() {
),
(Pointer, Pointer),
&[
Live(3), // s
Live(1), // inner-most Cons
Live(1), // middle Cons
Live(2), // linked
(StandardRC, Live(3)), // s
(StandardRC, Live(1)), // inner-most Cons
(StandardRC, Live(1)), // middle Cons
(StandardRC, Live(2)), // linked
]
);
}
@ -392,10 +649,10 @@ fn union_linked_list_dec() {
),
RocStr,
&[
Live(1), // s
Deallocated, // Cons
Deallocated, // Cons
Deallocated, // Cons
(StandardRC, Live(1)), // s
(StandardRC, Deallocated), // Cons
(StandardRC, Deallocated), // Cons
(StandardRC, Deallocated), // Cons
]
);
}
@ -403,7 +660,7 @@ fn union_linked_list_dec() {
#[test]
#[cfg(feature = "gen-wasm")]
fn union_linked_list_nil_dec() {
let no_refcounts: &[crate::helpers::RefCount] = &[];
let no_refcounts: &[(crate::helpers::RefCountLoc, crate::helpers::RefCount)] = &[];
assert_refcounts!(
indoc!(
r#"
@ -450,7 +707,7 @@ fn union_linked_list_long_dec() {
"#
),
i64,
&[Deallocated; 1_000]
&[(StandardRC, Deallocated); 1_000]
);
}
@ -468,8 +725,8 @@ fn boxed_str_inc() {
),
(Pointer, Pointer),
&[
Live(1), // s
Live(2), // b
(StandardRC, Live(1)), // s
(StandardRC, Live(2)), // b
]
);
}
@ -491,8 +748,8 @@ fn boxed_str_dec() {
),
(i32, i32),
&[
Deallocated, // s
Deallocated, // b
(StandardRC, Deallocated), // s
(StandardRC, Deallocated), // b
]
);
}
@ -520,9 +777,9 @@ fn non_nullable_unwrapped_alignment_8() {
),
i64,
&[
Deallocated, // Val 4
Deallocated, // Val 5
Deallocated, // ZAdd _ _
(StandardRC, Deallocated), // Val 4
(StandardRC, Deallocated), // Val 5
(StandardRC, Deallocated), // ZAdd _ _
]
);
}
@ -565,9 +822,38 @@ fn reset_reuse_alignment_8() {
),
i64,
&[
Deallocated, // Val 4
Deallocated, // Val 5
Deallocated, // ZAdd _ _
(StandardRC, Deallocated), // Val 4
(StandardRC, Deallocated), // Val 5
(StandardRC, Deallocated), // ZAdd _ _
]
);
}
#[test]
#[cfg(feature = "gen-wasm")]
fn basic_cli_parser() {
assert_refcounts!(
indoc!(
r#"
in =
"d"
|> Str.toUtf8
|> List.keepOks \c -> Str.fromUtf8 [c]
out =
when in is
[alone] -> [alone]
other -> other
List.len out
"#
),
i64,
&[
(StandardRC, Deallocated), // str
(StandardRC, Deallocated), // [c]
(AfterSize, Deallocated), // in
(AfterSize, Deallocated), // out
]
);
}

View file

@ -89,13 +89,10 @@ pub fn helper(
// while you're working on the dev backend!
{
// println!("=========== Procedures ==========");
// if pretty_print_ir_symbols() {
// println!("");
// for proc in procedures.values() {
// println!("{}", proc.to_pretty(200));
// }
// } else {
// println!("{:?}", procedures.values());
// let pretty = pretty_print_ir_symbols();
// println!("");
// for proc in procedures.values() {
// println!("{}", proc.to_pretty(&layout_interner, 200, pretty));
// }
// println!("=================================\n");

View file

@ -1,7 +1,7 @@
use roc_error_macros::internal_error;
use roc_gen_wasm::wasm32_sized::Wasm32Sized;
use roc_mono::layout::Builtin;
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocRefcounted, RocResult, RocStr, I128, U128};
use roc_wasm_module::round_up_to_alignment;
use std::convert::TryInto;
@ -84,7 +84,10 @@ impl FromWasm32Memory for RocStr {
}
}
impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocList<T> {
impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocList<T>
where
T: RocRefcounted,
{
fn decode(memory: &[u8], offset: u32) -> Self {
let elements = <u32 as FromWasm32Memory>::decode(memory, offset + 4 * Builtin::WRAPPER_PTR);
let length = <u32 as FromWasm32Memory>::decode(memory, offset + 4 * Builtin::WRAPPER_LEN);
@ -103,7 +106,10 @@ impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocList<T> {
}
}
impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocBox<T> {
impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocBox<T>
where
T: RocRefcounted,
{
fn decode(memory: &[u8], offset: u32) -> Self {
let ptr = <u32 as FromWasm32Memory>::decode(memory, offset + 4 * Builtin::WRAPPER_PTR);
debug_assert_ne!(ptr, 0);

View file

@ -96,3 +96,10 @@ pub enum RefCount {
Deallocated,
Constant,
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RefCountLoc {
StandardRC,
AfterSize,
}

View file

@ -1,4 +1,4 @@
use super::RefCount;
use super::{RefCount, RefCountLoc};
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
use bumpalo::Bump;
use roc_collections::all::MutSet;
@ -258,7 +258,7 @@ where
pub fn assert_wasm_refcounts_help<T>(
src: &str,
phantom: PhantomData<T>,
num_refcounts: usize,
refcount_locs: &[RefCountLoc],
) -> Result<Vec<RefCount>, String>
where
T: FromWasm32Memory + Wasm32Result,
@ -278,6 +278,7 @@ where
let mut inst = Instance::for_module(&arena, &module, dispatcher, is_debug_mode)?;
// Allocate a vector in the test host that refcounts will be copied into
let num_refcounts = refcount_locs.len();
let mut refcount_vector_addr: i32 = inst
.call_export(INIT_REFCOUNT_NAME, [Value::I32(num_refcounts as i32)])?
.ok_or_else(|| format!("No return address from {}", INIT_REFCOUNT_NAME))?
@ -302,15 +303,21 @@ where
// Read the refcounts
let mut refcounts = Vec::with_capacity(num_refcounts);
for _ in 0..num_refcounts {
for refcount_loc in refcount_locs {
// Get the next RC pointer from the host's vector
refcount_vector_addr += 4;
let rc_ptr = read_i32(&inst.memory, refcount_vector_addr);
let mut rc_ptr = read_i32(&inst.memory, refcount_vector_addr);
let rc = if rc_ptr == 0 {
RefCount::Deallocated
} else {
// If size is store on the heap for this type, the rc pointer is directly after.
if matches!(refcount_loc, RefCountLoc::AfterSize) {
rc_ptr += 4
}
// Dereference the RC pointer and decode its value from the negative number format
let rc_encoded = read_i32(&inst.memory, rc_ptr);
if rc_encoded == 0 {
RefCount::Constant
} else {
@ -397,15 +404,18 @@ macro_rules! assert_refcounts {
// We need the result type to generate the test_wrapper, even though we ignore the value!
// We can't just call `main` with no args, because some tests return structs, via pointer arg!
// Also we need to know how much stack space to reserve for the struct.
($src: expr, $ty: ty, $expected_refcounts: expr) => {{
($src: expr, $ty: ty, $expected: expr) => {{
let phantom = std::marker::PhantomData;
let num_refcounts = $expected_refcounts.len();
let (refcount_locs, expected_refcounts): (
Vec<$crate::helpers::RefCountLoc>,
Vec<$crate::helpers::RefCount>,
) = $expected.into_iter().map(|x| *x).unzip();
let result =
$crate::helpers::wasm::assert_wasm_refcounts_help::<$ty>($src, phantom, num_refcounts);
$crate::helpers::wasm::assert_wasm_refcounts_help::<$ty>($src, phantom, &refcount_locs);
match result {
Err(msg) => panic!("{:?}", msg),
Ok(actual_refcounts) => {
assert_eq!(&actual_refcounts, $expected_refcounts)
assert_eq!(actual_refcounts, expected_refcounts)
}
}
}};