mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Merge branch 'main' into builtin-json
This commit is contained in:
commit
105c0e7581
39 changed files with 1941 additions and 477 deletions
|
@ -553,7 +553,7 @@ fn mul_and_decimalize(a: u128, b: u128) i128 {
|
|||
|
||||
// Multiply two 128-bit ints and divide the result by 10^DECIMAL_PLACES
|
||||
//
|
||||
// Adapted from https://github.com/nlordell/ethnum-rs
|
||||
// Adapted from https://github.com/nlordell/ethnum-rs/blob/c9ed57e131bffde7bcc8274f376e5becf62ef9ac/src/intrinsics/native/divmod.rs
|
||||
// Copyright (c) 2020 Nicholas Rodrigues Lordello
|
||||
// Licensed under the Apache License version 2.0
|
||||
//
|
||||
|
@ -682,16 +682,25 @@ fn div_u256_by_u128(numer: U256, denom: u128) U256 {
|
|||
lo_overflowed = @subWithOverflow(u128, lo, 1, &lo);
|
||||
hi = hi -% @intCast(u128, @bitCast(u1, lo_overflowed));
|
||||
|
||||
// TODO this U256 was originally created by:
|
||||
// NOTE: this U256 was originally created by:
|
||||
//
|
||||
// ((hi as i128) >> 127).as_u256()
|
||||
//
|
||||
// ...however, I can't figure out where that function is defined.
|
||||
// Maybe it's defined using a macro or something. Anyway, hopefully
|
||||
// this is what it would do in this scenario.
|
||||
// As an implementation of `as_u256`, we wrap a negative value around to the maximum value of U256.
|
||||
|
||||
var s_u128 = math.shr(u128, hi, 127);
|
||||
var s_hi: u128 = undefined;
|
||||
var s_lo: u128 = undefined;
|
||||
if (s_u128 == 1) {
|
||||
s_hi = math.maxInt(u128);
|
||||
s_lo = math.maxInt(u128);
|
||||
} else {
|
||||
s_hi = 0;
|
||||
s_lo = 0;
|
||||
}
|
||||
var s = .{
|
||||
.hi = 0,
|
||||
.lo = math.shr(u128, hi, 127),
|
||||
.hi = s_hi,
|
||||
.lo = s_lo,
|
||||
};
|
||||
|
||||
carry = s.lo & 1;
|
||||
|
@ -1055,6 +1064,32 @@ test "div: 10 / 3" {
|
|||
try expectEqual(res, numer.div(denom));
|
||||
}
|
||||
|
||||
test "div: 341 / 341" {
|
||||
var number1: RocDec = RocDec.fromU64(341);
|
||||
var number2: RocDec = RocDec.fromU64(341);
|
||||
try expectEqual(RocDec.fromU64(1), number1.div(number2));
|
||||
}
|
||||
|
||||
test "div: 342 / 343" {
|
||||
var number1: RocDec = RocDec.fromU64(342);
|
||||
var number2: RocDec = RocDec.fromU64(343);
|
||||
var roc_str = RocStr.init("0.997084548104956268", 20);
|
||||
try expectEqual(RocDec.fromStr(roc_str), number1.div(number2));
|
||||
}
|
||||
|
||||
test "div: 680 / 340" {
|
||||
var number1: RocDec = RocDec.fromU64(680);
|
||||
var number2: RocDec = RocDec.fromU64(340);
|
||||
try expectEqual(RocDec.fromU64(2), number1.div(number2));
|
||||
}
|
||||
|
||||
test "div: 500 / 1000" {
|
||||
var number1: RocDec = RocDec.fromU64(500);
|
||||
var number2: RocDec = RocDec.fromU64(1000);
|
||||
var roc_str = RocStr.init("0.5", 3);
|
||||
try expectEqual(RocDec.fromStr(roc_str), number1.div(number2));
|
||||
}
|
||||
|
||||
// exports
|
||||
|
||||
pub fn fromStr(arg: RocStr) callconv(.C) num_.NumParseResult(i128) {
|
||||
|
|
|
@ -7,7 +7,7 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{InLayout, STLayoutInterner};
|
||||
|
||||
use super::CompareOperation;
|
||||
use super::{CompareOperation, RegisterWidth};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
#[allow(dead_code)]
|
||||
|
@ -854,6 +854,7 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
#[inline(always)]
|
||||
fn eq_reg64_reg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_register_width: RegisterWidth,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
|
@ -864,6 +865,7 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
#[inline(always)]
|
||||
fn neq_reg64_reg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_register_width: RegisterWidth,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
|
@ -871,26 +873,6 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
todo!("registers non-equality for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ilt_reg64_reg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) {
|
||||
todo!("registers signed less than for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ult_reg64_reg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) {
|
||||
todo!("registers unsigned less than for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn cmp_freg_freg_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
|
@ -903,26 +885,6 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
todo!("registers float comparison for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn igt_reg64_reg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) {
|
||||
todo!("registers signed greater than for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ugt_reg64_reg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) {
|
||||
todo!("registers unsigned greater than for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_float_freg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
|
@ -1061,6 +1023,28 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
fn sqrt_freg32_freg32(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _src: AArch64FloatReg) {
|
||||
todo!("sqrt")
|
||||
}
|
||||
|
||||
fn signed_compare_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_register_width: RegisterWidth,
|
||||
_operation: CompareOperation,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) {
|
||||
todo!("signed compare")
|
||||
}
|
||||
|
||||
fn unsigned_compare_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_register_width: RegisterWidth,
|
||||
_operation: CompareOperation,
|
||||
_dst: AArch64GeneralReg,
|
||||
_src1: AArch64GeneralReg,
|
||||
_src2: AArch64GeneralReg,
|
||||
) {
|
||||
todo!("unsigned compare")
|
||||
}
|
||||
}
|
||||
|
||||
impl AArch64Assembler {}
|
||||
|
|
|
@ -27,6 +27,14 @@ use storage::{RegStorage, StorageManager};
|
|||
|
||||
// TODO: on all number functions double check and deal with over/underflow.
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum RegisterWidth {
|
||||
W8,
|
||||
W16,
|
||||
W32,
|
||||
W64,
|
||||
}
|
||||
|
||||
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<GeneralReg, FloatReg>>:
|
||||
Sized + Copy
|
||||
{
|
||||
|
@ -390,6 +398,7 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
|||
|
||||
fn eq_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
|
@ -397,20 +406,25 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
|||
|
||||
fn neq_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
);
|
||||
|
||||
fn ilt_reg64_reg64_reg64(
|
||||
fn signed_compare_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
operation: CompareOperation,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
);
|
||||
|
||||
fn ult_reg64_reg64_reg64(
|
||||
fn unsigned_compare_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
operation: CompareOperation,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
|
@ -425,20 +439,6 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
|||
operation: CompareOperation,
|
||||
);
|
||||
|
||||
fn igt_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
);
|
||||
|
||||
fn ugt_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: GeneralReg,
|
||||
src1: GeneralReg,
|
||||
src2: GeneralReg,
|
||||
);
|
||||
|
||||
fn to_float_freg32_reg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: GeneralReg);
|
||||
|
||||
fn to_float_freg64_reg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: GeneralReg);
|
||||
|
@ -769,6 +769,10 @@ impl<
|
|||
// Call function and generate reloc.
|
||||
ASM::call(&mut self.buf, &mut self.relocs, fn_name);
|
||||
|
||||
self.move_return_value(dst, ret_layout)
|
||||
}
|
||||
|
||||
fn move_return_value(&mut self, dst: &Symbol, ret_layout: &InLayout<'a>) {
|
||||
// move return value to dst.
|
||||
match *ret_layout {
|
||||
single_register_integers!() => {
|
||||
|
@ -786,6 +790,9 @@ impl<
|
|||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, CC::GENERAL_RETURN_REGS[0]);
|
||||
}
|
||||
Layout::LambdaSet(lambda_set) => {
|
||||
self.move_return_value(dst, &lambda_set.runtime_representation())
|
||||
}
|
||||
_ => {
|
||||
CC::load_returned_complex_symbol(
|
||||
&mut self.buf,
|
||||
|
@ -1191,6 +1198,14 @@ impl<
|
|||
fn build_eq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>) {
|
||||
match *arg_layout {
|
||||
single_register_int_builtins!() | Layout::BOOL => {
|
||||
let width = match *arg_layout {
|
||||
Layout::BOOL | Layout::I8 | Layout::U8 => RegisterWidth::W8,
|
||||
Layout::I16 | Layout::U16 => RegisterWidth::W16,
|
||||
Layout::U32 | Layout::I32 => RegisterWidth::W32,
|
||||
Layout::I64 | Layout::U64 => RegisterWidth::W64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
let src1_reg = self
|
||||
.storage_manager
|
||||
|
@ -1198,7 +1213,7 @@ impl<
|
|||
let src2_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src2);
|
||||
ASM::eq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
ASM::eq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, src1_reg, src2_reg);
|
||||
}
|
||||
Layout::STR => {
|
||||
// use a zig call
|
||||
|
@ -1208,7 +1223,17 @@ impl<
|
|||
&[*src1, *src2],
|
||||
&[Layout::STR, Layout::STR],
|
||||
&Layout::BOOL,
|
||||
)
|
||||
);
|
||||
|
||||
// mask the result; we pass booleans around as 64-bit values, but branch on 0x0 and 0x1.
|
||||
// Zig gives back values where not all of the upper bits are zero, so we must clear them ourselves
|
||||
let tmp = &Symbol::DEV_TMP;
|
||||
let tmp_reg = self.storage_manager.claim_general_reg(&mut self.buf, tmp);
|
||||
ASM::mov_reg64_imm64(&mut self.buf, tmp_reg, true as i64);
|
||||
|
||||
let width = RegisterWidth::W8; // we're comparing booleans
|
||||
let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst);
|
||||
ASM::eq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, dst_reg, tmp_reg);
|
||||
}
|
||||
x => todo!("NumEq: layout, {:?}", x),
|
||||
}
|
||||
|
@ -1217,6 +1242,14 @@ impl<
|
|||
fn build_neq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>) {
|
||||
match *arg_layout {
|
||||
single_register_int_builtins!() | Layout::BOOL => {
|
||||
let width = match *arg_layout {
|
||||
Layout::BOOL | Layout::I8 | Layout::U8 => RegisterWidth::W8,
|
||||
Layout::I16 | Layout::U16 => RegisterWidth::W16,
|
||||
Layout::U32 | Layout::I32 => RegisterWidth::W32,
|
||||
Layout::I64 | Layout::U64 => RegisterWidth::W64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
let src1_reg = self
|
||||
.storage_manager
|
||||
|
@ -1224,7 +1257,7 @@ impl<
|
|||
let src2_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src2);
|
||||
ASM::neq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
ASM::neq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, src1_reg, src2_reg);
|
||||
}
|
||||
Layout::STR => {
|
||||
self.build_fn_call(
|
||||
|
@ -1238,10 +1271,11 @@ impl<
|
|||
// negate the result
|
||||
let tmp = &Symbol::DEV_TMP;
|
||||
let tmp_reg = self.storage_manager.claim_general_reg(&mut self.buf, tmp);
|
||||
ASM::mov_reg64_imm64(&mut self.buf, tmp_reg, 164);
|
||||
ASM::mov_reg64_imm64(&mut self.buf, tmp_reg, true as i64);
|
||||
|
||||
let width = RegisterWidth::W8; // we're comparing booleans
|
||||
let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst);
|
||||
ASM::neq_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, tmp_reg);
|
||||
ASM::neq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, dst_reg, tmp_reg);
|
||||
}
|
||||
x => todo!("NumNeq: layout, {:?}", x),
|
||||
}
|
||||
|
@ -1280,7 +1314,14 @@ impl<
|
|||
let src2_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src2);
|
||||
ASM::ilt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
ASM::signed_compare_reg64(
|
||||
&mut self.buf,
|
||||
RegisterWidth::W64,
|
||||
CompareOperation::LessThan,
|
||||
dst_reg,
|
||||
src1_reg,
|
||||
src2_reg,
|
||||
);
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U64)) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
|
@ -1290,7 +1331,14 @@ impl<
|
|||
let src2_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src2);
|
||||
ASM::ult_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
ASM::unsigned_compare_reg64(
|
||||
&mut self.buf,
|
||||
RegisterWidth::W64,
|
||||
CompareOperation::LessThan,
|
||||
dst_reg,
|
||||
src1_reg,
|
||||
src2_reg,
|
||||
);
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(width)) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
|
@ -1326,7 +1374,14 @@ impl<
|
|||
let src2_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src2);
|
||||
ASM::igt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
ASM::signed_compare_reg64(
|
||||
&mut self.buf,
|
||||
RegisterWidth::W64,
|
||||
CompareOperation::GreaterThan,
|
||||
dst_reg,
|
||||
src1_reg,
|
||||
src2_reg,
|
||||
);
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U64)) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
|
@ -1336,7 +1391,14 @@ impl<
|
|||
let src2_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, src2);
|
||||
ASM::ugt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
ASM::unsigned_compare_reg64(
|
||||
&mut self.buf,
|
||||
RegisterWidth::W64,
|
||||
CompareOperation::GreaterThan,
|
||||
dst_reg,
|
||||
src1_reg,
|
||||
src2_reg,
|
||||
);
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(width)) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
|
@ -2216,7 +2278,13 @@ impl<
|
|||
}
|
||||
|
||||
fn load_literal(&mut self, sym: &Symbol, layout: &InLayout<'a>, lit: &Literal<'a>) {
|
||||
match (lit, self.layout_interner.get(*layout)) {
|
||||
let layout = self.layout_interner.get(*layout);
|
||||
|
||||
if let Layout::LambdaSet(lambda_set) = layout {
|
||||
return self.load_literal(sym, &lambda_set.runtime_representation(), lit);
|
||||
}
|
||||
|
||||
match (lit, layout) {
|
||||
(
|
||||
Literal::Int(x),
|
||||
Layout::Builtin(Builtin::Int(
|
||||
|
@ -2263,8 +2331,7 @@ impl<
|
|||
}
|
||||
(Literal::Bool(x), Layout::Builtin(Builtin::Bool)) => {
|
||||
let reg = self.storage_manager.claim_general_reg(&mut self.buf, sym);
|
||||
let val = [*x as u8; 16];
|
||||
ASM::mov_reg64_imm64(&mut self.buf, reg, i128::from_ne_bytes(val) as i64);
|
||||
ASM::mov_reg64_imm64(&mut self.buf, reg, *x as i64);
|
||||
}
|
||||
(Literal::Float(x), Layout::Builtin(Builtin::Float(FloatWidth::F64))) => {
|
||||
let reg = self.storage_manager.claim_float_reg(&mut self.buf, sym);
|
||||
|
@ -2371,6 +2438,9 @@ impl<
|
|||
CC::GENERAL_RETURN_REGS[0],
|
||||
);
|
||||
}
|
||||
Layout::LambdaSet(lambda_set) => {
|
||||
self.return_symbol(sym, &lambda_set.runtime_representation())
|
||||
}
|
||||
_ => {
|
||||
internal_error!("All primitive values should fit in a single register");
|
||||
}
|
||||
|
|
|
@ -785,9 +785,22 @@ impl<
|
|||
FloatWidth::F32 => todo!(),
|
||||
},
|
||||
Builtin::Bool => {
|
||||
// same as 8-bit integer
|
||||
let reg = self.load_to_general_reg(buf, sym);
|
||||
ASM::mov_base32_reg8(buf, to_offset, reg);
|
||||
// same as 8-bit integer, but we special-case true/false because these symbols
|
||||
// are thunks and literal values
|
||||
match *sym {
|
||||
Symbol::BOOL_FALSE => {
|
||||
let reg = self.claim_general_reg(buf, sym);
|
||||
ASM::mov_reg64_imm64(buf, reg, false as i64)
|
||||
}
|
||||
Symbol::BOOL_TRUE => {
|
||||
let reg = self.claim_general_reg(buf, sym);
|
||||
ASM::mov_reg64_imm64(buf, reg, true as i64)
|
||||
}
|
||||
_ => {
|
||||
let reg = self.load_to_general_reg(buf, sym);
|
||||
ASM::mov_base32_reg8(buf, to_offset, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
Builtin::Decimal => todo!(),
|
||||
Builtin::Str | Builtin::List(_) => {
|
||||
|
@ -1166,9 +1179,9 @@ impl<
|
|||
Some(storages) => storages,
|
||||
None => internal_error!("Jump: unknown point specified to jump to: {:?}", id),
|
||||
};
|
||||
for ((sym, layout), wanted_storage) in
|
||||
args.iter().zip(arg_layouts).zip(param_storage.iter())
|
||||
{
|
||||
|
||||
let it = args.iter().zip(arg_layouts).zip(param_storage.iter());
|
||||
for ((sym, layout), wanted_storage) in it {
|
||||
// Note: it is possible that the storage we want to move to is in use by one of the args we want to pass.
|
||||
if self.get_storage_for_sym(sym) == wanted_storage {
|
||||
continue;
|
||||
|
|
|
@ -9,7 +9,7 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner};
|
||||
|
||||
use super::CompareOperation;
|
||||
use super::{CompareOperation, RegisterWidth};
|
||||
|
||||
// Not sure exactly how I want to represent registers.
|
||||
// If we want max speed, we would likely make them structs that impl the same trait to avoid ifs.
|
||||
|
@ -1554,45 +1554,71 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
#[inline(always)]
|
||||
fn eq_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
dst: X86_64GeneralReg,
|
||||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
cmp_reg64_reg64(buf, register_width, src1, src2);
|
||||
sete_reg64(buf, dst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neq_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
dst: X86_64GeneralReg,
|
||||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
cmp_reg64_reg64(buf, register_width, src1, src2);
|
||||
setne_reg64(buf, dst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ilt_reg64_reg64_reg64(
|
||||
fn signed_compare_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
operation: CompareOperation,
|
||||
dst: X86_64GeneralReg,
|
||||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
setl_reg64(buf, dst);
|
||||
match operation {
|
||||
CompareOperation::LessThan => {
|
||||
cmp_reg64_reg64(buf, register_width, src1, src2);
|
||||
setl_reg64(buf, dst);
|
||||
}
|
||||
CompareOperation::LessThanOrEqual => todo!(),
|
||||
CompareOperation::GreaterThan => {
|
||||
cmp_reg64_reg64(buf, register_width, src1, src2);
|
||||
setg_reg64(buf, dst);
|
||||
}
|
||||
CompareOperation::GreaterThanOrEqual => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ult_reg64_reg64_reg64(
|
||||
fn unsigned_compare_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
operation: CompareOperation,
|
||||
dst: X86_64GeneralReg,
|
||||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
setb_reg64(buf, dst);
|
||||
match operation {
|
||||
CompareOperation::LessThan => {
|
||||
cmp_reg64_reg64(buf, register_width, src1, src2);
|
||||
setb_reg64(buf, dst);
|
||||
}
|
||||
CompareOperation::LessThanOrEqual => todo!(),
|
||||
CompareOperation::GreaterThan => {
|
||||
cmp_reg64_reg64(buf, register_width, src1, src2);
|
||||
seta_reg64(buf, dst);
|
||||
}
|
||||
|
||||
CompareOperation::GreaterThanOrEqual => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1622,28 +1648,6 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
};
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn igt_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: X86_64GeneralReg,
|
||||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
setg_reg64(buf, dst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ugt_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: X86_64GeneralReg,
|
||||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
seta_reg64(buf, dst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_float_freg32_reg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64GeneralReg) {
|
||||
cvtsi2ss_freg64_reg64(buf, dst, src);
|
||||
|
@ -1671,7 +1675,7 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
cmp_reg64_reg64(buf, RegisterWidth::W64, src1, src2);
|
||||
setle_reg64(buf, dst);
|
||||
}
|
||||
|
||||
|
@ -1682,7 +1686,7 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
src1: X86_64GeneralReg,
|
||||
src2: X86_64GeneralReg,
|
||||
) {
|
||||
cmp_reg64_reg64(buf, src1, src2);
|
||||
cmp_reg64_reg64(buf, RegisterWidth::W64, src1, src2);
|
||||
setge_reg64(buf, dst);
|
||||
}
|
||||
|
||||
|
@ -1847,6 +1851,50 @@ fn add_reg_extension<T: RegTrait>(reg: T, byte: u8) -> u8 {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn binop_reg16_reg16(
|
||||
op_code: u8,
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: X86_64GeneralReg,
|
||||
src: X86_64GeneralReg,
|
||||
) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = (src as u8 % 8) << 3;
|
||||
|
||||
if dst_high || src_high {
|
||||
let rex = add_rm_extension(dst, REX);
|
||||
let rex = add_reg_extension(src, rex);
|
||||
|
||||
buf.extend([0x66, rex, op_code, 0xC0 | dst_mod | src_mod])
|
||||
} else {
|
||||
buf.extend([0x66, op_code, 0xC0 | dst_mod | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn binop_reg32_reg32(
|
||||
op_code: u8,
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: X86_64GeneralReg,
|
||||
src: X86_64GeneralReg,
|
||||
) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = (src as u8 % 8) << 3;
|
||||
|
||||
if dst_high || src_high {
|
||||
let rex = add_rm_extension(dst, REX);
|
||||
let rex = add_reg_extension(src, rex);
|
||||
|
||||
buf.extend([rex, op_code, 0xC0 | dst_mod | src_mod])
|
||||
} else {
|
||||
buf.extend([op_code, 0xC0 | dst_mod | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn binop_reg64_reg64(
|
||||
op_code: u8,
|
||||
|
@ -2119,8 +2167,18 @@ fn cmp_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) {
|
|||
|
||||
/// `CMP r/m64,r64` -> Compare r64 to r/m64.
|
||||
#[inline(always)]
|
||||
fn cmp_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
|
||||
binop_reg64_reg64(0x39, buf, dst, src);
|
||||
fn cmp_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
register_width: RegisterWidth,
|
||||
dst: X86_64GeneralReg,
|
||||
src: X86_64GeneralReg,
|
||||
) {
|
||||
match register_width {
|
||||
RegisterWidth::W8 => binop_reg64_reg64(0x38, buf, dst, src),
|
||||
RegisterWidth::W16 => binop_reg16_reg16(0x39, buf, dst, src),
|
||||
RegisterWidth::W32 => binop_reg32_reg32(0x39, buf, dst, src),
|
||||
RegisterWidth::W64 => binop_reg64_reg64(0x39, buf, dst, src),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -2419,13 +2477,6 @@ fn mov_base8_offset32_reg8(
|
|||
buf.extend(offset.to_le_bytes());
|
||||
}
|
||||
|
||||
enum RegisterWidth {
|
||||
W8,
|
||||
W16,
|
||||
W32,
|
||||
W64,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn mov_reg_base_offset32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
@ -3671,4 +3722,51 @@ mod tests {
|
|||
ALL_FLOAT_REGS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_cmp() {
|
||||
disassembler_test!(
|
||||
cmp_reg64_reg64,
|
||||
|_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!(
|
||||
"cmp {}, {}",
|
||||
dst.low_8bits_string(),
|
||||
src.low_8bits_string()
|
||||
),
|
||||
[RegisterWidth::W8],
|
||||
ALL_GENERAL_REGS,
|
||||
ALL_GENERAL_REGS
|
||||
);
|
||||
|
||||
disassembler_test!(
|
||||
cmp_reg64_reg64,
|
||||
|_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!(
|
||||
"cmp {}, {}",
|
||||
dbg!(dst.low_16bits_string()),
|
||||
dbg!(src.low_16bits_string())
|
||||
),
|
||||
[RegisterWidth::W16],
|
||||
ALL_GENERAL_REGS,
|
||||
ALL_GENERAL_REGS
|
||||
);
|
||||
|
||||
disassembler_test!(
|
||||
cmp_reg64_reg64,
|
||||
|_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!(
|
||||
"cmp {}, {}",
|
||||
dbg!(dst.low_32bits_string()),
|
||||
dbg!(src.low_32bits_string())
|
||||
),
|
||||
[RegisterWidth::W32],
|
||||
ALL_GENERAL_REGS,
|
||||
ALL_GENERAL_REGS
|
||||
);
|
||||
|
||||
disassembler_test!(
|
||||
cmp_reg64_reg64,
|
||||
|_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!("cmp {dst}, {src}",),
|
||||
[RegisterWidth::W64],
|
||||
ALL_GENERAL_REGS,
|
||||
ALL_GENERAL_REGS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,6 +213,7 @@ trait Backend<'a> {
|
|||
self.free_symbols(stmt);
|
||||
}
|
||||
Stmt::Jump(id, args) => {
|
||||
self.load_literal_symbols(args);
|
||||
let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> =
|
||||
bumpalo::vec![in self.env().arena];
|
||||
arg_layouts.reserve(args.len());
|
||||
|
@ -1130,6 +1131,9 @@ trait Backend<'a> {
|
|||
ret_layout: &InLayout<'a>,
|
||||
);
|
||||
|
||||
/// Move a returned value into `dst`
|
||||
fn move_return_value(&mut self, dst: &Symbol, ret_layout: &InLayout<'a>);
|
||||
|
||||
/// build_num_abs stores the absolute value of src into dst.
|
||||
fn build_num_abs(&mut self, dst: &Symbol, src: &Symbol, layout: &InLayout<'a>);
|
||||
|
||||
|
|
|
@ -9647,6 +9647,9 @@ where
|
|||
lambda_set_id = lambda_set_id.next();
|
||||
|
||||
stack.push(layout_interner.get(lambda_set.runtime_representation()));
|
||||
|
||||
// TODO: figure out if we need to look at the other layouts
|
||||
// stack.push(layout_interner.get(lambda_set.ret));
|
||||
}
|
||||
Layout::RecursivePointer(_) => {
|
||||
/* do nothing, we've already generated for this type through the Union(_) */
|
||||
|
|
|
@ -4586,4 +4586,10 @@ mod test {
|
|||
let target_info = TargetInfo::default_x86_64();
|
||||
assert_eq!(Layout::VOID_NAKED.stack_size(&interner, target_info), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn align_u128_in_tag_union() {
|
||||
let interner = STLayoutInterner::with_capacity(4, TargetInfo::default_x86_64());
|
||||
assert_eq!(interner.alignment_bytes(Layout::U128), 16);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -387,7 +387,28 @@ impl<'a> Copy for InLayout<'a> {}
|
|||
|
||||
impl std::fmt::Debug for InLayout<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("InLayout").field(&self.0).finish()
|
||||
match *self {
|
||||
Layout::VOID => f.write_str("InLayout(VOID)"),
|
||||
Layout::UNIT => f.write_str("InLayout(UNIT)"),
|
||||
Layout::BOOL => f.write_str("InLayout(BOOL)"),
|
||||
Layout::U8 => f.write_str("InLayout(U8)"),
|
||||
Layout::U16 => f.write_str("InLayout(U16)"),
|
||||
Layout::U32 => f.write_str("InLayout(U32)"),
|
||||
Layout::U64 => f.write_str("InLayout(U64)"),
|
||||
Layout::U128 => f.write_str("InLayout(U128)"),
|
||||
Layout::I8 => f.write_str("InLayout(I8)"),
|
||||
Layout::I16 => f.write_str("InLayout(I16)"),
|
||||
Layout::I32 => f.write_str("InLayout(I32)"),
|
||||
Layout::I64 => f.write_str("InLayout(I64)"),
|
||||
Layout::I128 => f.write_str("InLayout(I128)"),
|
||||
Layout::F32 => f.write_str("InLayout(F32)"),
|
||||
Layout::F64 => f.write_str("InLayout(F64)"),
|
||||
Layout::DEC => f.write_str("InLayout(DEC)"),
|
||||
Layout::STR => f.write_str("InLayout(STR)"),
|
||||
Layout::OPAQUE_PTR => f.write_str("InLayout(OPAQUE_PTR)"),
|
||||
Layout::NAKED_RECURSIVE_PTR => f.write_str("InLayout(NAKED_RECURSIVE_PTR)"),
|
||||
_ => f.debug_tuple("InLayout").field(&self.0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4124,7 +4124,7 @@ fn int_let_generalization() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
manyAux : {} -> I32
|
||||
manyAux : {} -> I32
|
||||
manyAux = \_ ->
|
||||
output = \_ -> 42
|
||||
|
||||
|
@ -4403,17 +4403,17 @@ fn layout_cache_structure_with_multiple_recursive_structures() {
|
|||
LinkedList : [Nil, Cons { first : Chain, rest : LinkedList }]
|
||||
|
||||
main =
|
||||
base : LinkedList
|
||||
base : LinkedList
|
||||
base = Nil
|
||||
|
||||
walker : LinkedList, Chain -> LinkedList
|
||||
walker = \rest, first -> Cons { first, rest }
|
||||
walker = \rest, first -> Cons { first, rest }
|
||||
|
||||
list : List Chain
|
||||
list = []
|
||||
|
||||
r = List.walk list base walker
|
||||
|
||||
|
||||
if r == base then 11u8 else 22u8
|
||||
"#
|
||||
),
|
||||
|
@ -4460,3 +4460,23 @@ fn reset_recursive_type_wraps_in_named_type() {
|
|||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn pass_lambda_set_to_function() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
instr = if Bool.true then Num.mul else Num.add
|
||||
|
||||
fn = \a -> instr a a
|
||||
|
||||
main = fn 3
|
||||
"#
|
||||
),
|
||||
3 * 3,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,40 @@ use indoc::indoc;
|
|||
#[allow(unused_imports)]
|
||||
use roc_std::{RocList, RocResult, RocStr};
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn string_eq() {
|
||||
// context: the dev backend did not correctly mask the boolean that zig returns here
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
main : I64
|
||||
main = if "*" == "*" then 123 else 456
|
||||
"#
|
||||
),
|
||||
123,
|
||||
u64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn string_neq() {
|
||||
// context: the dev backend did not correctly mask the boolean that zig returns here
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
main : I64
|
||||
main = if "*" != "*" then 123 else 456
|
||||
"#
|
||||
),
|
||||
456,
|
||||
u64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
fn str_split_empty_delimiter() {
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::helpers::wasm::assert_evals_to;
|
|||
use indoc::indoc;
|
||||
|
||||
use roc_mono::layout::STLayoutInterner;
|
||||
#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))]
|
||||
#[cfg(test)]
|
||||
use roc_std::{RocList, RocStr, U128};
|
||||
|
||||
#[test]
|
||||
|
@ -52,7 +52,7 @@ fn applied_tag_nothing() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn applied_tag_just() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -93,24 +93,8 @@ fn applied_tag_just_enum() {
|
|||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// fn raw_result() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// x : Result I64 I64
|
||||
// x = Err 41
|
||||
|
||||
// x
|
||||
// "#
|
||||
// ),
|
||||
// 0,
|
||||
// i8
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn true_is_true() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -127,7 +111,7 @@ fn true_is_true() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn false_is_false() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -144,7 +128,7 @@ fn false_is_false() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn basic_enum() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -165,67 +149,8 @@ fn basic_enum() {
|
|||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// fn linked_list_empty() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// LinkedList a : [Cons a (LinkedList a), Nil]
|
||||
//
|
||||
// empty : LinkedList I64
|
||||
// empty = Nil
|
||||
//
|
||||
// 1
|
||||
// "#
|
||||
// ),
|
||||
// 1,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// fn linked_list_singleton() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// LinkedList a : [Cons a (LinkedList a), Nil]
|
||||
//
|
||||
// singleton : LinkedList I64
|
||||
// singleton = Cons 0x1 Nil
|
||||
//
|
||||
// 1
|
||||
// "#
|
||||
// ),
|
||||
// 1,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// fn linked_list_is_empty() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// LinkedList a : [Cons a (LinkedList a), Nil]
|
||||
//
|
||||
// isEmpty : LinkedList a -> Bool
|
||||
// isEmpty = \list ->
|
||||
// when list is
|
||||
// Nil -> Bool.true
|
||||
// Cons _ _ -> Bool.false
|
||||
//
|
||||
// isEmpty (Cons 4 Nil)
|
||||
// "#
|
||||
// ),
|
||||
// false,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn even_odd() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -251,7 +176,7 @@ fn even_odd() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn gen_literal_true() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -265,7 +190,7 @@ fn gen_literal_true() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn gen_if_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -480,7 +405,7 @@ fn nested_pattern_match() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_vanilla() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -496,7 +421,7 @@ fn if_guard_vanilla() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn when_on_single_value_tag() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -512,7 +437,7 @@ fn when_on_single_value_tag() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_multiple() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -533,7 +458,7 @@ fn if_guard_multiple() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_constructor_switch() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -577,7 +502,7 @@ fn if_guard_constructor_switch() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_constructor_chain() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -594,7 +519,7 @@ fn if_guard_constructor_chain() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_pattern_false() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -613,7 +538,7 @@ fn if_guard_pattern_false() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_switch() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -632,7 +557,7 @@ fn if_guard_switch() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_pattern_true() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -651,7 +576,7 @@ fn if_guard_pattern_true() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn if_guard_exhaustiveness() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -670,7 +595,7 @@ fn if_guard_exhaustiveness() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn when_on_enum() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -692,7 +617,7 @@ fn when_on_enum() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn pattern_matching_unit() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -751,7 +676,7 @@ fn pattern_matching_unit() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn one_element_tag() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -768,7 +693,7 @@ fn one_element_tag() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn nested_tag_union() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -789,7 +714,7 @@ fn nested_tag_union() {
|
|||
);
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn unit_type() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -808,7 +733,7 @@ fn unit_type() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn join_point_if() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -825,7 +750,7 @@ fn join_point_if() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn join_point_when() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -851,7 +776,7 @@ fn join_point_when() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn join_point_with_cond_expr() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -890,7 +815,7 @@ fn join_point_with_cond_expr() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn alignment_in_single_tag_construction() {
|
||||
assert_evals_to!(indoc!("Three (1 == 1) 32"), (32i64, true), (i64, bool));
|
||||
|
||||
|
@ -902,7 +827,7 @@ fn alignment_in_single_tag_construction() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn alignment_in_single_tag_pattern_match() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -934,7 +859,7 @@ fn alignment_in_single_tag_pattern_match() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn alignment_in_multi_tag_construction_two() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -952,7 +877,7 @@ fn alignment_in_multi_tag_construction_two() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn alignment_in_multi_tag_construction_three() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -969,7 +894,7 @@ fn alignment_in_multi_tag_construction_three() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn alignment_in_multi_tag_pattern_match() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1056,7 +981,7 @@ fn phantom_polymorphic_record() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn result_never() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1232,7 +1157,7 @@ fn recursive_tag_union_into_flat_tag_union() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn monomorphized_tag() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1249,7 +1174,7 @@ fn monomorphized_tag() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn monomorphized_applied_tag() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1337,7 +1262,7 @@ fn monomorphized_tag_with_polymorphic_arg_and_monomorphic_arg() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn issue_2365_monomorphize_tag_with_non_empty_ext_var() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1362,7 +1287,7 @@ fn issue_2365_monomorphize_tag_with_non_empty_ext_var() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1391,7 +1316,7 @@ fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped_nested() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1526,7 +1451,7 @@ fn issue_1162() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn polymorphic_tag() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1542,7 +1467,7 @@ fn polymorphic_tag() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn issue_2725_alias_polymorphic_lambda() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1583,7 +1508,7 @@ fn opaque_assign_to_symbol() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn issue_2777_default_branch_codegen() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1812,7 +1737,7 @@ fn issue_3560_nested_tag_constructor_is_record_newtype() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn issue_3560_newtype_tag_constructor_has_nested_constructor_with_no_payload() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1828,7 +1753,7 @@ fn issue_3560_newtype_tag_constructor_has_nested_constructor_with_no_payload() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn alignment_i128() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1939,7 +1864,7 @@ fn issue_2165_recursive_tag_destructure() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn tag_union_let_generalization() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1988,7 +1913,7 @@ fn fit_recursive_union_in_struct_into_recursive_pointer() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn match_on_result_with_uninhabited_error_branch() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -2085,7 +2010,7 @@ fn unify_types_with_fixed_fixpoints_outside_fixing_region() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn lambda_set_with_imported_toplevels_issue_4733() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
|
|
@ -25,7 +25,8 @@ roc_error_macros::assert_sizeof_all!(RecordFields, 2 * 8);
|
|||
pub struct Mark(i32);
|
||||
|
||||
impl Mark {
|
||||
pub const NONE: Mark = Mark(2);
|
||||
pub const NONE: Mark = Mark(3);
|
||||
pub const VISITED_IN_OCCURS_CHECK: Mark = Mark(2);
|
||||
pub const OCCURS: Mark = Mark(1);
|
||||
pub const GET_VAR_NAMES: Mark = Mark(0);
|
||||
|
||||
|
@ -1996,9 +1997,15 @@ impl Subs {
|
|||
///
|
||||
/// This ignores [Content::RecursionVar]s that occur recursively, because those are
|
||||
/// already priced in and expected to occur.
|
||||
pub fn occurs(&self, var: Variable) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
///
|
||||
/// Although `subs` is taken as mutable reference, this function will return it in the same
|
||||
/// state it was given.
|
||||
pub fn occurs(&mut self, var: Variable) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
let mut scratchpad = take_occurs_scratchpad();
|
||||
let result = occurs(self, &mut scratchpad, var);
|
||||
for v in &scratchpad.all_visited {
|
||||
self.set_mark_unchecked(*v, Mark::NONE);
|
||||
}
|
||||
put_occurs_scratchpad(scratchpad);
|
||||
result
|
||||
}
|
||||
|
@ -3434,15 +3441,34 @@ impl TupleElems {
|
|||
}
|
||||
}
|
||||
|
||||
std::thread_local! {
|
||||
static SCRATCHPAD_FOR_OCCURS: RefCell<Option<Vec<Variable>>> = RefCell::new(Some(Vec::with_capacity(1024)));
|
||||
struct OccursScratchpad {
|
||||
seen: Vec<Variable>,
|
||||
all_visited: Vec<Variable>,
|
||||
}
|
||||
|
||||
fn take_occurs_scratchpad() -> Vec<Variable> {
|
||||
impl OccursScratchpad {
|
||||
fn new_static() -> Self {
|
||||
Self {
|
||||
seen: Vec::with_capacity(1024),
|
||||
all_visited: Vec::with_capacity(1024),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.seen.clear();
|
||||
self.all_visited.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::thread_local! {
|
||||
static SCRATCHPAD_FOR_OCCURS: RefCell<Option<OccursScratchpad>> = RefCell::new(Some(OccursScratchpad::new_static()));
|
||||
}
|
||||
|
||||
fn take_occurs_scratchpad() -> OccursScratchpad {
|
||||
SCRATCHPAD_FOR_OCCURS.with(|f| f.take().unwrap())
|
||||
}
|
||||
|
||||
fn put_occurs_scratchpad(mut scratchpad: Vec<Variable>) {
|
||||
fn put_occurs_scratchpad(mut scratchpad: OccursScratchpad) {
|
||||
SCRATCHPAD_FOR_OCCURS.with(|f| {
|
||||
scratchpad.clear();
|
||||
f.replace(Some(scratchpad));
|
||||
|
@ -3450,20 +3476,36 @@ fn put_occurs_scratchpad(mut scratchpad: Vec<Variable>) {
|
|||
}
|
||||
|
||||
fn occurs(
|
||||
subs: &Subs,
|
||||
seen: &mut Vec<Variable>,
|
||||
subs: &mut Subs,
|
||||
ctx: &mut OccursScratchpad,
|
||||
input_var: Variable,
|
||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
// NB(subs-invariant): it is pivotal that subs is not modified in any material way.
|
||||
// As variables are visited, they are marked as observed so they are not revisited,
|
||||
// but no other modification should take place.
|
||||
|
||||
use self::Content::*;
|
||||
use self::FlatType::*;
|
||||
|
||||
let root_var = subs.get_root_key_without_compacting(input_var);
|
||||
|
||||
if seen.contains(&root_var) {
|
||||
// SAFETY: due to XREF(subs-invariant), only the mark in a variable is modified, and all
|
||||
// variable (and other content) identities are guaranteed to be preserved during an occurs
|
||||
// check. As a result, we can freely take references of variables and UnionTags.
|
||||
macro_rules! safe {
|
||||
($t:ty, $expr:expr) => {
|
||||
unsafe { std::mem::transmute::<_, &'static $t>($expr) }
|
||||
};
|
||||
}
|
||||
|
||||
if ctx.seen.contains(&root_var) {
|
||||
Err((root_var, Vec::with_capacity(0)))
|
||||
} else if subs.get_mark_unchecked(root_var) == Mark::VISITED_IN_OCCURS_CHECK {
|
||||
Ok(())
|
||||
} else {
|
||||
seen.push(root_var);
|
||||
let result = (|| match subs.get_content_without_compacting(root_var) {
|
||||
ctx.seen.push(root_var);
|
||||
ctx.all_visited.push(root_var);
|
||||
let result = (|| match subs.get_content_unchecked(root_var) {
|
||||
FlexVar(_)
|
||||
| RigidVar(_)
|
||||
| FlexAbleVar(_, _)
|
||||
|
@ -3472,47 +3514,57 @@ fn occurs(
|
|||
| Error => Ok(()),
|
||||
|
||||
Structure(flat_type) => match flat_type {
|
||||
Apply(_, args) => {
|
||||
short_circuit(subs, root_var, seen, subs.get_subs_slice(*args).iter())
|
||||
}
|
||||
Apply(_, args) => short_circuit(
|
||||
subs,
|
||||
root_var,
|
||||
ctx,
|
||||
safe!([Variable], subs.get_subs_slice(*args)).iter(),
|
||||
),
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
let it = once(ret_var)
|
||||
.chain(once(closure_var))
|
||||
.chain(subs.get_subs_slice(*arg_vars).iter());
|
||||
short_circuit(subs, root_var, seen, it)
|
||||
let it = once(safe!(Variable, ret_var))
|
||||
.chain(once(safe!(Variable, closure_var)))
|
||||
.chain(safe!([Variable], subs.get_subs_slice(*arg_vars)).iter());
|
||||
short_circuit(subs, root_var, ctx, it)
|
||||
}
|
||||
Record(vars_by_field, ext) => {
|
||||
let slice = SubsSlice::new(vars_by_field.variables_start, vars_by_field.length);
|
||||
let it = once(ext).chain(subs.get_subs_slice(slice).iter());
|
||||
short_circuit(subs, root_var, seen, it)
|
||||
let slice =
|
||||
VariableSubsSlice::new(vars_by_field.variables_start, vars_by_field.length);
|
||||
let it = once(safe!(Variable, ext))
|
||||
.chain(safe!([Variable], subs.get_subs_slice(slice)).iter());
|
||||
short_circuit(subs, root_var, ctx, it)
|
||||
}
|
||||
Tuple(vars_by_elem, ext) => {
|
||||
let slice = SubsSlice::new(vars_by_elem.variables_start, vars_by_elem.length);
|
||||
let it = once(ext).chain(subs.get_subs_slice(slice).iter());
|
||||
short_circuit(subs, root_var, seen, it)
|
||||
let slice =
|
||||
VariableSubsSlice::new(vars_by_elem.variables_start, vars_by_elem.length);
|
||||
let it = once(safe!(Variable, ext))
|
||||
.chain(safe!([Variable], subs.get_subs_slice(slice)).iter());
|
||||
short_circuit(subs, root_var, ctx, it)
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
occurs_union(subs, root_var, seen, tags)?;
|
||||
let ext_var = ext.var();
|
||||
occurs_union(subs, root_var, ctx, safe!(UnionLabels<TagName>, tags))?;
|
||||
|
||||
short_circuit_help(subs, root_var, seen, ext.var())
|
||||
short_circuit_help(subs, root_var, ctx, ext_var)
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
short_circuit(subs, root_var, seen, once(&ext.var()))
|
||||
short_circuit(subs, root_var, ctx, once(&ext.var()))
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) => {
|
||||
occurs_union(subs, root_var, seen, tags)?;
|
||||
let ext_var = ext.var();
|
||||
occurs_union(subs, root_var, ctx, safe!(UnionLabels<TagName>, tags))?;
|
||||
|
||||
short_circuit_help(subs, root_var, seen, ext.var())
|
||||
short_circuit_help(subs, root_var, ctx, ext_var)
|
||||
}
|
||||
EmptyRecord | EmptyTuple | EmptyTagUnion => Ok(()),
|
||||
},
|
||||
Alias(_, args, real_var, _) => {
|
||||
let real_var = *real_var;
|
||||
for var_index in args.into_iter() {
|
||||
let var = subs[var_index];
|
||||
if short_circuit_help(subs, root_var, seen, var).is_err() {
|
||||
if short_circuit_help(subs, root_var, ctx, var).is_err() {
|
||||
// Pay the cost and figure out what the actual recursion point is
|
||||
|
||||
return short_circuit_help(subs, root_var, seen, *real_var);
|
||||
return short_circuit_help(subs, root_var, ctx, real_var);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3527,27 +3579,33 @@ fn occurs(
|
|||
// unspecialized lambda vars excluded because they are not explicitly part of the
|
||||
// type (they only matter after being resolved).
|
||||
|
||||
occurs_union(subs, root_var, seen, solved)
|
||||
occurs_union(subs, root_var, ctx, safe!(UnionLabels<Symbol>, solved))
|
||||
}
|
||||
RangedNumber(_range_vars) => Ok(()),
|
||||
})();
|
||||
seen.pop();
|
||||
|
||||
// Cache the variable's property of having no cycle, but only if it indeed has no cycle.
|
||||
if result.is_ok() {
|
||||
subs.set_mark_unchecked(root_var, Mark::VISITED_IN_OCCURS_CHECK);
|
||||
}
|
||||
|
||||
ctx.seen.pop();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn occurs_union<L: Label>(
|
||||
subs: &Subs,
|
||||
subs: &mut Subs,
|
||||
root_var: Variable,
|
||||
seen: &mut Vec<Variable>,
|
||||
ctx: &mut OccursScratchpad,
|
||||
tags: &UnionLabels<L>,
|
||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
for slice_index in tags.variables() {
|
||||
let slice = subs[slice_index];
|
||||
for var_index in slice {
|
||||
let var = subs[var_index];
|
||||
short_circuit_help(subs, root_var, seen, var)?;
|
||||
short_circuit_help(subs, root_var, ctx, var)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -3555,16 +3613,16 @@ fn occurs_union<L: Label>(
|
|||
|
||||
#[inline(always)]
|
||||
fn short_circuit<'a, T>(
|
||||
subs: &Subs,
|
||||
subs: &mut Subs,
|
||||
root_key: Variable,
|
||||
seen: &mut Vec<Variable>,
|
||||
ctx: &mut OccursScratchpad,
|
||||
iter: T,
|
||||
) -> Result<(), (Variable, Vec<Variable>)>
|
||||
where
|
||||
T: Iterator<Item = &'a Variable>,
|
||||
{
|
||||
for var in iter {
|
||||
short_circuit_help(subs, root_key, seen, *var)?;
|
||||
short_circuit_help(subs, root_key, ctx, *var)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -3572,12 +3630,12 @@ where
|
|||
|
||||
#[inline(always)]
|
||||
fn short_circuit_help(
|
||||
subs: &Subs,
|
||||
subs: &mut Subs,
|
||||
root_key: Variable,
|
||||
seen: &mut Vec<Variable>,
|
||||
ctx: &mut OccursScratchpad,
|
||||
var: Variable,
|
||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
if let Err((v, mut vec)) = occurs(subs, seen, var) {
|
||||
if let Err((v, mut vec)) = occurs(subs, ctx, var) {
|
||||
vec.push(root_key);
|
||||
return Err((v, vec));
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -72,16 +72,20 @@ impl Types {
|
|||
|
||||
pub fn with_capacity(cap: usize, target_info: TargetInfo) -> Self {
|
||||
let mut types = Vec::with_capacity(cap);
|
||||
let mut sizes = Vec::with_capacity(cap);
|
||||
let mut aligns = Vec::with_capacity(cap);
|
||||
|
||||
types.push(RocType::Unit);
|
||||
sizes.push(1);
|
||||
aligns.push(1);
|
||||
|
||||
Self {
|
||||
target: target_info,
|
||||
types,
|
||||
sizes,
|
||||
aligns,
|
||||
types_by_name: FnvHashMap::with_capacity_and_hasher(10, Default::default()),
|
||||
entry_points: Vec::new(),
|
||||
sizes: Vec::new(),
|
||||
aligns: Vec::new(),
|
||||
deps: VecMap::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
|
@ -542,14 +546,19 @@ impl Types {
|
|||
}
|
||||
}
|
||||
|
||||
debug_assert_eq!(self.types.len(), self.sizes.len());
|
||||
debug_assert_eq!(self.types.len(), self.aligns.len());
|
||||
|
||||
let id = TypeId(self.types.len());
|
||||
|
||||
assert!(id.0 <= TypeId::MAX.0);
|
||||
|
||||
let size = interner.stack_size(layout);
|
||||
let align = interner.alignment_bytes(layout);
|
||||
|
||||
self.types.push(typ);
|
||||
self.sizes
|
||||
.push(interner.stack_size_without_alignment(layout));
|
||||
self.aligns.push(interner.alignment_bytes(layout));
|
||||
self.sizes.push(size);
|
||||
self.aligns.push(align);
|
||||
|
||||
id
|
||||
}
|
||||
|
@ -660,7 +669,7 @@ impl From<&Types> for roc_type::Types {
|
|||
deps,
|
||||
entrypoints,
|
||||
sizes: types.sizes.as_slice().into(),
|
||||
types: types.types.iter().map(|t| t.into()).collect(),
|
||||
types: types.types.iter().map(roc_type::RocType::from).collect(),
|
||||
typesByName: types_by_name,
|
||||
target: types.target.into(),
|
||||
}
|
||||
|
@ -1355,6 +1364,8 @@ fn add_function_type<'a>(
|
|||
.from_var(env.arena, closure_var, env.subs)
|
||||
.expect("Something weird ended up in the content");
|
||||
|
||||
// TODO this treats any lambda set as unsized. We should be able to figure out whether a
|
||||
// lambda set is unsized in practice, and use the runtime representation otherwise.
|
||||
add_type_help(env, lambda_set_layout, closure_var, None, types)
|
||||
};
|
||||
|
||||
|
@ -1920,7 +1931,7 @@ where
|
|||
RocStructFields::HasClosure { fields }
|
||||
}
|
||||
None => {
|
||||
debug_assert!(!layout.has_varying_stack_size(&env.layout_cache.interner, arena));
|
||||
// debug_assert!(!layout.has_varying_stack_size(&env.layout_cache.interner, arena));
|
||||
|
||||
let fields: Vec<(String, TypeId)> = sortables
|
||||
.into_iter()
|
||||
|
|
|
@ -22,5 +22,5 @@ Job : [
|
|||
|
||||
Rbt : { default : Job }
|
||||
|
||||
mainForHost : Rbt
|
||||
mainForHost = main
|
||||
mainForHost : {} -> Rbt
|
||||
mainForHost = \{} -> main
|
||||
|
|
|
@ -1,29 +1,19 @@
|
|||
mod test_glue;
|
||||
|
||||
use indoc::indoc;
|
||||
use test_glue::Rbt;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut Rbt);
|
||||
}
|
||||
// use test_glue::Rbt;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<Rbt> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
let tag_union = test_glue::mainForHost(());
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
|
||||
assert!(tag_union == tag_union); // PartialEq
|
||||
|
||||
assert!(tag_union.clone() == tag_union.clone()); // Clone
|
||||
|
||||
assert!(tag_union.partial_cmp(&tag_union) == Some(Ordering::Equal)); // PartialOrd
|
||||
|
@ -57,7 +47,7 @@ use std::os::raw::c_char;
|
|||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
libc::malloc(size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
|
@ -7,5 +7,5 @@ platform "test-platform"
|
|||
|
||||
Expr : [String Str, Concat Expr Expr]
|
||||
|
||||
mainForHost : Expr
|
||||
mainForHost = main
|
||||
mainForHost : {} -> Expr
|
||||
mainForHost = \{} -> main
|
||||
|
|
|
@ -13,13 +13,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<Expr> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
let tag_union = test_glue::mainForHost(());
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
|
||||
|
|
|
@ -7,5 +7,5 @@ platform "test-platform"
|
|||
|
||||
StrConsList : [Nil, Cons Str StrConsList]
|
||||
|
||||
mainForHost : StrConsList
|
||||
mainForHost = main
|
||||
mainForHost : {} -> StrConsList
|
||||
mainForHost = \{} -> main
|
||||
|
|
|
@ -13,17 +13,12 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<StrConsList> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
let tag_union = test_glue::mainForHost(());
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
|
||||
assert!(tag_union == tag_union); // PartialEq
|
||||
|
||||
assert!(tag_union.clone() == tag_union.clone()); // Clone
|
||||
|
||||
assert!(tag_union.partial_cmp(&tag_union) == Some(Ordering::Equal)); // PartialOrd
|
||||
|
@ -38,8 +33,8 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
"#
|
||||
),
|
||||
tag_union,
|
||||
StrConsList::Cons("small str".into(), StrConsList::Nil),
|
||||
StrConsList::Nil,
|
||||
StrConsList::Cons("small str".into(), StrConsList::Nil()),
|
||||
StrConsList::Nil(),
|
||||
); // Debug
|
||||
|
||||
let mut set = HashSet::new();
|
||||
|
|
|
@ -7,5 +7,5 @@ platform "test-platform"
|
|||
|
||||
StrFingerTree : [Empty, Single Str, More Str StrFingerTree]
|
||||
|
||||
mainForHost : StrFingerTree
|
||||
mainForHost = main
|
||||
mainForHost : {} -> StrFingerTree
|
||||
mainForHost = \{} -> main
|
||||
|
|
|
@ -14,26 +14,20 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<StrFingerTree> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
let tag_union = test_glue::mainForHost(());
|
||||
|
||||
// Eq
|
||||
assert!(StrFingerTree::Empty == StrFingerTree::Empty);
|
||||
assert!(StrFingerTree::Empty != tag_union);
|
||||
assert!(StrFingerTree::Empty() == StrFingerTree::Empty());
|
||||
assert!(StrFingerTree::Empty() != tag_union);
|
||||
assert!(
|
||||
StrFingerTree::Single(RocStr::from("foo")) == StrFingerTree::Single(RocStr::from("foo"))
|
||||
);
|
||||
assert!(StrFingerTree::Single(RocStr::from("foo")) != StrFingerTree::Empty);
|
||||
assert!(StrFingerTree::Single(RocStr::from("foo")) != StrFingerTree::Empty());
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
assert!(tag_union == tag_union); // PartialEq
|
||||
assert!(tag_union.clone() == tag_union.clone()); // Clone
|
||||
assert!(StrFingerTree::Empty.clone() == StrFingerTree::Empty); // Clone
|
||||
assert!(StrFingerTree::Empty().clone() == StrFingerTree::Empty()); // Clone
|
||||
|
||||
assert!(tag_union.partial_cmp(&tag_union) == Some(Ordering::Equal)); // PartialOrd
|
||||
assert!(tag_union.cmp(&tag_union) == Ordering::Equal); // Ord
|
||||
|
@ -53,9 +47,9 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
"small str".into(),
|
||||
StrFingerTree::Single("other str".into()),
|
||||
),
|
||||
StrFingerTree::More("small str".into(), StrFingerTree::Empty),
|
||||
StrFingerTree::More("small str".into(), StrFingerTree::Empty()),
|
||||
StrFingerTree::Single("small str".into()),
|
||||
StrFingerTree::Empty,
|
||||
StrFingerTree::Empty(),
|
||||
); // Debug
|
||||
|
||||
let mut set = HashSet::new();
|
||||
|
|
11
crates/glue/tests/fixtures/option/app.roc
vendored
Normal file
11
crates/glue/tests/fixtures/option/app.roc
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
app "app"
|
||||
packages { pf: "platform.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main : Bool -> [ Some Str, None ]
|
||||
main = \returnStr ->
|
||||
if returnStr then
|
||||
Some "Hello World!"
|
||||
else
|
||||
None
|
9
crates/glue/tests/fixtures/option/platform.roc
vendored
Normal file
9
crates/glue/tests/fixtures/option/platform.roc
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
platform "test-platform"
|
||||
requires {} { main : Bool -> [ Some Str, None ] }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Bool -> [ Some Str, None ]
|
||||
mainForHost = \u -> main u
|
62
crates/glue/tests/fixtures/option/src/lib.rs
vendored
Normal file
62
crates/glue/tests/fixtures/option/src/lib.rs
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
mod test_glue;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
let string = test_glue::mainForHost(true);
|
||||
println!("Answer was: {:?}", string.unwrap_Some()); // Debug
|
||||
//
|
||||
let integer = test_glue::mainForHost(false);
|
||||
println!("Answer was: {:?}", integer.discriminant()); // Debug
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
// Externs required by roc_std and by the Roc app
|
||||
|
||||
use core::ffi::c_void;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
13
crates/glue/tests/fixtures/return-function/app.roc
vendored
Normal file
13
crates/glue/tests/fixtures/return-function/app.roc
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
app "app"
|
||||
packages { pf: "platform.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main : { f: I64, I64 -> I64, g: I64, I64 -> I64 }
|
||||
main = { f: add, g: sub }
|
||||
|
||||
add : I64, I64 -> I64
|
||||
add = \x, y -> x + y
|
||||
|
||||
sub : I64, I64 -> I64
|
||||
sub = \x, y -> x - y
|
9
crates/glue/tests/fixtures/return-function/platform.roc
vendored
Normal file
9
crates/glue/tests/fixtures/return-function/platform.roc
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
platform "test-platform"
|
||||
requires {} { main : { f: I64, I64 -> I64, g: I64, I64 -> I64 } }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : { f: I64, I64 -> I64, g: I64, I64 -> I64 }
|
||||
mainForHost = main
|
62
crates/glue/tests/fixtures/return-function/src/lib.rs
vendored
Normal file
62
crates/glue/tests/fixtures/return-function/src/lib.rs
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
mod test_glue;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
let record = test_glue::mainForHost();
|
||||
let answer1 = record.f.force_thunk(42i64, 1);
|
||||
let answer2 = record.g.force_thunk(42i64, 1);
|
||||
|
||||
println!("Answer was: {:?} {:?}", answer1, answer2);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
// Externs required by roc_std and by the Roc app
|
||||
|
||||
use core::ffi::c_void;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
11
crates/glue/tests/fixtures/rocresult/app.roc
vendored
Normal file
11
crates/glue/tests/fixtures/rocresult/app.roc
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
app "app"
|
||||
packages { pf: "platform.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main : Bool -> Result Str I32
|
||||
main = \returnStr ->
|
||||
if returnStr then
|
||||
Ok "Hello World!"
|
||||
else
|
||||
Err 42
|
9
crates/glue/tests/fixtures/rocresult/platform.roc
vendored
Normal file
9
crates/glue/tests/fixtures/rocresult/platform.roc
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
platform "test-platform"
|
||||
requires {} { main : Bool -> Result Str I32 }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Bool -> Result Str I32
|
||||
mainForHost = \u -> main u
|
62
crates/glue/tests/fixtures/rocresult/src/lib.rs
vendored
Normal file
62
crates/glue/tests/fixtures/rocresult/src/lib.rs
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
mod test_glue;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
let string = test_glue::mainForHost(true);
|
||||
println!("Answer was: {:?}", string); // Debug
|
||||
//
|
||||
let integer = test_glue::mainForHost(false);
|
||||
println!("Answer was: {:?}", integer); // Debug
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
// Externs required by roc_std and by the Roc app
|
||||
|
||||
use core::ffi::c_void;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
|
@ -13,5 +13,5 @@ platform "test-platform"
|
|||
# that all variants have.
|
||||
NonRecursive : [Foo Str, Bar U128, Blah I32, Baz]
|
||||
|
||||
mainForHost : NonRecursive
|
||||
mainForHost = main
|
||||
mainForHost : {} -> NonRecursive
|
||||
mainForHost = \{} -> main
|
||||
|
|
|
@ -12,13 +12,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<NonRecursive> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
let tag_union = test_glue::mainForHost(());
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
|
||||
|
@ -33,8 +27,8 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
tag_union,
|
||||
NonRecursive::Foo("small str".into()),
|
||||
NonRecursive::Foo("A long enough string to not be small".into()),
|
||||
NonRecursive::Bar(123.into()),
|
||||
NonRecursive::Baz,
|
||||
NonRecursive::Bar(123),
|
||||
NonRecursive::Baz(),
|
||||
NonRecursive::Blah(456),
|
||||
); // Debug
|
||||
|
||||
|
|
|
@ -11,5 +11,5 @@ platform "test-platform"
|
|||
# to store the discriminant. We have to generate glue code accordingly!
|
||||
NonRecursive : [Foo Str, Bar I64, Blah I32, Baz]
|
||||
|
||||
mainForHost : NonRecursive
|
||||
mainForHost = main
|
||||
mainForHost : {} -> NonRecursive
|
||||
mainForHost = \{} -> main
|
||||
|
|
|
@ -10,14 +10,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<test_glue::NonRecursive> =
|
||||
core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
let tag_union = test_glue::mainForHost(());
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
|
||||
|
@ -32,7 +25,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
tag_union,
|
||||
test_glue::NonRecursive::Foo("small str".into()),
|
||||
test_glue::NonRecursive::Bar(123),
|
||||
test_glue::NonRecursive::Baz,
|
||||
test_glue::NonRecursive::Baz(),
|
||||
test_glue::NonRecursive::Blah(456),
|
||||
); // Debug
|
||||
|
||||
|
|
|
@ -74,57 +74,68 @@ mod glue_cli_run {
|
|||
basic_record:"basic-record" => "Record was: MyRcd { b: 42, a: 1995 }\n",
|
||||
nested_record:"nested-record" => "Record was: Outer { y: \"foo\", z: [1, 2], x: Inner { b: 24.0, a: 5 } }\n",
|
||||
enumeration:"enumeration" => "tag_union was: MyEnum::Foo, Bar is: MyEnum::Bar, Baz is: MyEnum::Baz\n",
|
||||
// union_with_padding:"union-with-padding" => indoc!(r#"
|
||||
// tag_union was: NonRecursive::Foo("This is a test")
|
||||
// `Foo "small str"` is: NonRecursive::Foo("small str")
|
||||
// `Foo "A long enough string to not be small"` is: NonRecursive::Foo("A long enough string to not be small")
|
||||
// `Bar 123` is: NonRecursive::Bar(123)
|
||||
// `Baz` is: NonRecursive::Baz
|
||||
// `Blah 456` is: NonRecursive::Blah(456)
|
||||
// "#),
|
||||
single_tag_union:"single-tag-union" => indoc!(r#"
|
||||
tag_union was: SingleTagUnion::OneTag
|
||||
"#),
|
||||
// union_without_padding:"union-without-padding" => indoc!(r#"
|
||||
// tag_union was: NonRecursive::Foo("This is a test")
|
||||
// `Foo "small str"` is: NonRecursive::Foo("small str")
|
||||
// `Bar 123` is: NonRecursive::Bar(123)
|
||||
// `Baz` is: NonRecursive::Baz
|
||||
// `Blah 456` is: NonRecursive::Blah(456)
|
||||
// "#),
|
||||
// nullable_wrapped:"nullable-wrapped" => indoc!(r#"
|
||||
// tag_union was: StrFingerTree::More("foo", StrFingerTree::More("bar", StrFingerTree::Empty))
|
||||
// `More "small str" (Single "other str")` is: StrFingerTree::More("small str", StrFingerTree::Single("other str"))
|
||||
// `More "small str" Empty` is: StrFingerTree::More("small str", StrFingerTree::Empty)
|
||||
// `Single "small str"` is: StrFingerTree::Single("small str")
|
||||
// `Empty` is: StrFingerTree::Empty
|
||||
// "#),
|
||||
// nullable_unwrapped:"nullable-unwrapped" => indoc!(r#"
|
||||
// tag_union was: StrConsList::Cons("World!", StrConsList::Cons("Hello ", StrConsList::Nil))
|
||||
// `Cons "small str" Nil` is: StrConsList::Cons("small str", StrConsList::Nil)
|
||||
// `Nil` is: StrConsList::Nil
|
||||
// "#),
|
||||
// nonnullable_unwrapped:"nonnullable-unwrapped" => indoc!(r#"
|
||||
// tag_union was: StrRoseTree::Tree(ManuallyDrop { value: StrRoseTree_Tree { f0: "root", f1: [StrRoseTree::Tree(ManuallyDrop { value: StrRoseTree_Tree { f0: "leaf1", f1: [] } }), StrRoseTree::Tree(ManuallyDrop { value: StrRoseTree_Tree { f0: "leaf2", f1: [] } })] } })
|
||||
// Tree "foo" [] is: StrRoseTree::Tree(ManuallyDrop { value: StrRoseTree_Tree { f0: "foo", f1: [] } })
|
||||
// "#),
|
||||
// basic_recursive_union:"basic-recursive-union" => indoc!(r#"
|
||||
// tag_union was: Expr::Concat(Expr::String("Hello, "), Expr::String("World!"))
|
||||
// `Concat (String "Hello, ") (String "World!")` is: Expr::Concat(Expr::String("Hello, "), Expr::String("World!"))
|
||||
// `String "this is a test"` is: Expr::String("this is a test")
|
||||
// "#),
|
||||
// advanced_recursive_union:"advanced-recursive-union" => indoc!(r#"
|
||||
// rbt was: Rbt { default: Job::Job(R1 { command: Command::Command(R2 { tool: Tool::SystemTool(R4 { name: "test", num: 42 }) }), inputFiles: ["foo"] }) }
|
||||
// "#),
|
||||
// list_recursive_union:"list-recursive-union" => indoc!(r#"
|
||||
// rbt was: Rbt { default: Job::Job(R1 { command: Command::Command(R2 { args: [], tool: Tool::SystemTool(R3 { name: "test" }) }), inputFiles: ["foo"], job: [] }) }
|
||||
// "#),
|
||||
union_with_padding:"union-with-padding" => indoc!(r#"
|
||||
tag_union was: NonRecursive::Foo("This is a test")
|
||||
`Foo "small str"` is: NonRecursive::Foo("small str")
|
||||
`Foo "A long enough string to not be small"` is: NonRecursive::Foo("A long enough string to not be small")
|
||||
`Bar 123` is: NonRecursive::Bar(123)
|
||||
`Baz` is: NonRecursive::Baz(())
|
||||
`Blah 456` is: NonRecursive::Blah(456)
|
||||
"#),
|
||||
union_without_padding:"union-without-padding" => indoc!(r#"
|
||||
tag_union was: NonRecursive::Foo("This is a test")
|
||||
`Foo "small str"` is: NonRecursive::Foo("small str")
|
||||
`Bar 123` is: NonRecursive::Bar(123)
|
||||
`Baz` is: NonRecursive::Baz(())
|
||||
`Blah 456` is: NonRecursive::Blah(456)
|
||||
"#),
|
||||
nullable_wrapped:"nullable-wrapped" => indoc!(r#"
|
||||
tag_union was: StrFingerTree::More("foo", StrFingerTree::More("bar", StrFingerTree::Empty))
|
||||
`More "small str" (Single "other str")` is: StrFingerTree::More("small str", StrFingerTree::Single("other str"))
|
||||
`More "small str" Empty` is: StrFingerTree::More("small str", StrFingerTree::Empty)
|
||||
`Single "small str"` is: StrFingerTree::Single("small str")
|
||||
`Empty` is: StrFingerTree::Empty
|
||||
"#),
|
||||
nullable_unwrapped:"nullable-unwrapped" => indoc!(r#"
|
||||
tag_union was: StrConsList::Cons("World!", StrConsList::Cons("Hello ", StrConsList::Nil))
|
||||
`Cons "small str" Nil` is: StrConsList::Cons("small str", StrConsList::Nil)
|
||||
`Nil` is: StrConsList::Nil
|
||||
"#),
|
||||
nonnullable_unwrapped:"nonnullable-unwrapped" => indoc!(r#"
|
||||
tag_union was: StrRoseTree::Tree("root", [StrRoseTree::Tree("leaf1", []), StrRoseTree::Tree("leaf2", [])])
|
||||
Tree "foo" [] is: StrRoseTree::Tree("foo", [])
|
||||
"#),
|
||||
basic_recursive_union:"basic-recursive-union" => indoc!(r#"
|
||||
tag_union was: Expr::Concat(Expr::String("Hello, "), Expr::String("World!"))
|
||||
`Concat (String "Hello, ") (String "World!")` is: Expr::Concat(Expr::String("Hello, "), Expr::String("World!"))
|
||||
`String "this is a test"` is: Expr::String("this is a test")
|
||||
"#),
|
||||
advanced_recursive_union:"advanced-recursive-union" => indoc!(r#"
|
||||
rbt was: Rbt { default: Job::Job(R1 { command: Command::Command(R2 { tool: Tool::SystemTool(R4 { name: "test", num: 42 }) }), inputFiles: ["foo"] }) }
|
||||
"#),
|
||||
list_recursive_union:"list-recursive-union" => indoc!(r#"
|
||||
rbt was: Rbt { default: Job::Job(R1 { command: Command::Command(R2 { args: [], tool: Tool::SystemTool(R3 { name: "test" }) }), inputFiles: ["foo"], job: [] }) }
|
||||
"#),
|
||||
multiple_modules:"multiple-modules" => indoc!(r#"
|
||||
combined was: Combined { s1: DepStr1::S("hello"), s2: DepStr2::R("world") }
|
||||
"#),
|
||||
arguments:"arguments" => indoc!(r#"
|
||||
Answer was: 84
|
||||
"#),
|
||||
rocresult:"rocresult" => indoc!(r#"
|
||||
Answer was: RocOk(ManuallyDrop { value: "Hello World!" })
|
||||
Answer was: RocErr(ManuallyDrop { value: 42 })
|
||||
"#),
|
||||
option:"option" => indoc!(r#"
|
||||
Answer was: "Hello World!"
|
||||
Answer was: discriminant_U1::None
|
||||
"#),
|
||||
return_function:"return-function" => indoc!(r#"
|
||||
Answer was: 43 41
|
||||
"#),
|
||||
}
|
||||
|
||||
fn check_for_tests(all_fixtures: &mut roc_collections::VecSet<String>) {
|
||||
|
|
|
@ -33,7 +33,7 @@ impl<T> RocBox<T> {
|
|||
let contents = unsafe {
|
||||
let contents_ptr = ptr.cast::<u8>().add(alignment).cast::<T>();
|
||||
|
||||
*contents_ptr = contents;
|
||||
core::ptr::write(contents_ptr, contents);
|
||||
|
||||
// We already verified that the original alloc pointer was non-null,
|
||||
// and this one is the alloc pointer with `alignment` bytes added to it,
|
||||
|
@ -44,6 +44,15 @@ impl<T> RocBox<T> {
|
|||
Self { contents }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The box must be unique in order to leak it safely
|
||||
pub unsafe fn leak(self) -> *mut T {
|
||||
let ptr = self.contents.as_ptr() as *mut T;
|
||||
core::mem::forget(self);
|
||||
ptr
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn alloc_alignment() -> usize {
|
||||
mem::align_of::<T>().max(mem::align_of::<Storage>())
|
||||
|
@ -110,6 +119,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: core::hash::Hash> core::hash::Hash for RocBox<T> {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.contents.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for RocBox<T>
|
||||
where
|
||||
T: Debug,
|
||||
|
|
|
@ -21,6 +21,12 @@ fn build_host() {
|
|||
let preprocessed_host_path =
|
||||
platform_main_roc.with_file_name(preprocessed_host_filename(&target).unwrap());
|
||||
|
||||
// valgrind does not support avx512 yet: https://bugs.kde.org/show_bug.cgi?id=383010
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
if is_x86_feature_detected!("avx512f") {
|
||||
std::env::set_var("NO_AVX512", "1");
|
||||
}
|
||||
|
||||
build_and_preprocess_host(
|
||||
roc_mono::ir::OptLevel::Normal,
|
||||
&target,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue