mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge branch 'trunk' into gen-dev/records-base
This commit is contained in:
commit
03bc61cbf8
70 changed files with 2617 additions and 2352 deletions
|
@ -1,12 +1,10 @@
|
|||
# Building the Roc compiler from source
|
||||
|
||||
|
||||
## Installing LLVM, Python, Zig, valgrind, libunwind, and libc++-dev
|
||||
## Installing LLVM, Zig, valgrind, and Python 2.7
|
||||
|
||||
To build the compiler, you need these installed:
|
||||
|
||||
* `libunwind` (macOS should already have this one installed)
|
||||
* `libc++-dev` and `libc++abi-dev`
|
||||
* Python 2.7 (Windows only), `python-is-python3` (Ubuntu)
|
||||
* [Zig](https://ziglang.org/), see below for version
|
||||
* LLVM, see below for version
|
||||
|
@ -18,11 +16,6 @@ Alternatively, you can use `cargo test --no-fail-fast` or `cargo test -p specifi
|
|||
|
||||
For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). This dependency is only required to build with the `--debug` flag, and for normal developtment you should be fine without it.
|
||||
|
||||
### libunwind & libc++-dev
|
||||
|
||||
MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be done with `sudo apt-get install libunwind-dev`).
|
||||
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev libc++abi-dev`.)
|
||||
|
||||
### libcxb libraries
|
||||
|
||||
You may see an error like this during builds:
|
||||
|
|
|
@ -352,8 +352,6 @@ fn jit_to_ast_help<'a>(
|
|||
| Layout::RecursivePointer => {
|
||||
todo!("add support for rendering recursive tag unions in the REPL")
|
||||
}
|
||||
|
||||
Layout::Closure(_, _, _) => Err(ToAstProblem::FunctionLayout),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -135,10 +135,6 @@ pub fn gen_and_eval<'a>(
|
|||
&context, "",
|
||||
));
|
||||
|
||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||
// platform to provide them.
|
||||
add_default_roc_externs(&context, module, &builder, ptr_bytes);
|
||||
|
||||
// mark our zig-defined builtins as internal
|
||||
for function in FunctionIterator::from_module(module) {
|
||||
let name = function.get_name().to_str().unwrap();
|
||||
|
@ -183,11 +179,15 @@ pub fn gen_and_eval<'a>(
|
|||
interns,
|
||||
module,
|
||||
ptr_bytes,
|
||||
leak: false,
|
||||
is_gen_test: false,
|
||||
// important! we don't want any procedures to get the C calling convention
|
||||
exposed_to_host: MutSet::default(),
|
||||
};
|
||||
|
||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||
// platform to provide them.
|
||||
add_default_roc_externs(&env);
|
||||
|
||||
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
||||
&env,
|
||||
opt_level,
|
||||
|
|
|
@ -40,6 +40,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
|
|
@ -40,6 +40,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
|
|
@ -421,9 +421,6 @@ fn link_linux(
|
|||
"-lrt",
|
||||
"-lutil",
|
||||
"-lc_nonshared",
|
||||
"-lc++",
|
||||
"-lc++abi",
|
||||
"-lunwind",
|
||||
libgcc_path.to_str().unwrap(),
|
||||
// Output
|
||||
"-o",
|
||||
|
@ -488,9 +485,6 @@ fn link_macos(
|
|||
// "-lrt", // TODO shouldn't we need this?
|
||||
// "-lc_nonshared", // TODO shouldn't we need this?
|
||||
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||
"-lc++",
|
||||
// "-lc++abi",
|
||||
// "-lunwind", // TODO will eventually need this, see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||
// "-framework", // Uncomment this line & the following ro run the `rand` crate in examples/cli
|
||||
// "Security",
|
||||
// Output
|
||||
|
|
|
@ -144,7 +144,9 @@ pub fn gen_from_mono_module(
|
|||
interns: loaded.interns,
|
||||
module,
|
||||
ptr_bytes,
|
||||
leak: false,
|
||||
// in gen_tests, the compiler provides roc_panic
|
||||
// and sets up the setjump/longjump exception handling
|
||||
is_gen_test: false,
|
||||
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
|
||||
};
|
||||
|
||||
|
@ -158,6 +160,9 @@ pub fn gen_from_mono_module(
|
|||
|
||||
env.dibuilder.finalize();
|
||||
|
||||
// we don't use the debug info, and it causes weird errors.
|
||||
module.strip_debug_info();
|
||||
|
||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
|
|
|
@ -79,6 +79,9 @@ comptime {
|
|||
exportNumFn(num.powInt, "pow_int");
|
||||
exportNumFn(num.acos, "acos");
|
||||
exportNumFn(num.asin, "asin");
|
||||
exportNumFn(num.bytesToU16C, "bytes_to_u16");
|
||||
exportNumFn(num.bytesToU32C, "bytes_to_u32");
|
||||
exportNumFn(num.round, "round");
|
||||
}
|
||||
|
||||
// Str Module
|
||||
|
@ -102,6 +105,14 @@ comptime {
|
|||
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
|
||||
}
|
||||
|
||||
// Utils
|
||||
const utils = @import("utils.zig");
|
||||
comptime {
|
||||
exportUtilsFn(utils.test_panic, "test_panic");
|
||||
|
||||
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
||||
}
|
||||
|
||||
// Export helpers - Must be run inside a comptime
|
||||
fn exportBuiltinFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||
@export(func, .{ .name = "roc_builtins." ++ func_name, .linkage = .Strong });
|
||||
|
@ -122,6 +133,10 @@ fn exportDecFn(comptime func: anytype, comptime func_name: []const u8) void {
|
|||
exportBuiltinFn(func, "dec." ++ func_name);
|
||||
}
|
||||
|
||||
fn exportUtilsFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||
exportBuiltinFn(func, "utils." ++ func_name);
|
||||
}
|
||||
|
||||
// Custom panic function, as builtin Zig version errors during LLVM verification
|
||||
pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace) noreturn {
|
||||
std.debug.print("{s}: {?}", .{ message, stacktrace });
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const std = @import("std");
|
||||
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||
const math = std.math;
|
||||
const RocList = @import("list.zig").RocList;
|
||||
|
||||
pub fn atan(num: f64) callconv(.C) f64 {
|
||||
return @call(.{ .modifier = always_inline }, math.atan, .{num});
|
||||
|
@ -21,3 +22,25 @@ pub fn acos(num: f64) callconv(.C) f64 {
|
|||
pub fn asin(num: f64) callconv(.C) f64 {
|
||||
return @call(.{ .modifier = always_inline }, math.asin, .{num});
|
||||
}
|
||||
|
||||
pub fn bytesToU16C(arg: RocList, position: usize) callconv(.C) u16 {
|
||||
return @call(.{ .modifier = always_inline }, bytesToU16, .{ arg, position });
|
||||
}
|
||||
|
||||
fn bytesToU16(arg: RocList, position: usize) u16 {
|
||||
const bytes = @ptrCast([*]const u8, arg.bytes);
|
||||
return @bitCast(u16, [_]u8{ bytes[position], bytes[position + 1] });
|
||||
}
|
||||
|
||||
pub fn bytesToU32C(arg: RocList, position: usize) callconv(.C) u32 {
|
||||
return @call(.{ .modifier = always_inline }, bytesToU32, .{ arg, position });
|
||||
}
|
||||
|
||||
fn bytesToU32(arg: RocList, position: usize) u32 {
|
||||
const bytes = @ptrCast([*]const u8, arg.bytes);
|
||||
return @bitCast(u32, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3] });
|
||||
}
|
||||
|
||||
pub fn round(num: f64) callconv(.C) i64 {
|
||||
return @floatToInt(i32, (@round(num)));
|
||||
}
|
||||
|
|
|
@ -15,12 +15,16 @@ extern fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignmen
|
|||
// This should never be passed a null pointer.
|
||||
extern fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void;
|
||||
|
||||
// Signals to the host that the program has paniced
|
||||
extern fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void;
|
||||
|
||||
comptime {
|
||||
// During tetsts, use the testing allocators to satisfy these functions.
|
||||
if (std.builtin.is_test) {
|
||||
@export(testing_roc_alloc, .{ .name = "roc_alloc", .linkage = .Strong });
|
||||
@export(testing_roc_realloc, .{ .name = "roc_realloc", .linkage = .Strong });
|
||||
@export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong });
|
||||
@export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +45,10 @@ fn testing_roc_dealloc(c_ptr: *c_void, _: u32) callconv(.C) void {
|
|||
std.testing.allocator.destroy(ptr);
|
||||
}
|
||||
|
||||
fn testing_roc_panic(c_ptr: *c_void, _: u32) callconv(.C) void {
|
||||
@panic("Roc paniced");
|
||||
}
|
||||
|
||||
pub fn alloc(size: usize, alignment: u32) [*]u8 {
|
||||
return @ptrCast([*]u8, @call(.{ .modifier = always_inline }, roc_alloc, .{ size, alignment }));
|
||||
}
|
||||
|
@ -53,6 +61,22 @@ pub fn dealloc(c_ptr: [*]u8, alignment: u32) void {
|
|||
return @call(.{ .modifier = always_inline }, roc_dealloc, .{ c_ptr, alignment });
|
||||
}
|
||||
|
||||
// must export this explicitly because right now it is not used from zig code
|
||||
pub fn panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
return @call(.{ .modifier = always_inline }, roc_panic, .{ c_ptr, alignment });
|
||||
}
|
||||
|
||||
// indirection because otherwise zig creats an alias to the panic function which our LLVM code
|
||||
// does not know how to deal with
|
||||
pub fn test_panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
const cstr = @ptrCast([*:0]u8, c_ptr);
|
||||
|
||||
// const stderr = std.io.getStdErr().writer();
|
||||
// stderr.print("Roc panicked: {s}!\n", .{cstr}) catch unreachable;
|
||||
|
||||
std.c.exit(1);
|
||||
}
|
||||
|
||||
pub const Inc = fn (?[*]u8) callconv(.C) void;
|
||||
pub const IncN = fn (?[*]u8, u64) callconv(.C) void;
|
||||
pub const Dec = fn (?[*]u8) callconv(.C) void;
|
||||
|
|
|
@ -8,6 +8,9 @@ pub const NUM_ACOS: &str = "roc_builtins.num.acos";
|
|||
pub const NUM_ATAN: &str = "roc_builtins.num.atan";
|
||||
pub const NUM_IS_FINITE: &str = "roc_builtins.num.is_finite";
|
||||
pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int";
|
||||
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
|
||||
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
|
||||
pub const NUM_ROUND: &str = "roc_builtins.num.round";
|
||||
|
||||
pub const STR_INIT: &str = "roc_builtins.str.init";
|
||||
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
||||
|
@ -77,3 +80,5 @@ pub const DEC_ADD_WITH_OVERFLOW: &str = "roc_builtins.dec.add_with_overflow";
|
|||
pub const DEC_SUB_WITH_OVERFLOW: &str = "roc_builtins.dec.sub_with_overflow";
|
||||
pub const DEC_MUL_WITH_OVERFLOW: &str = "roc_builtins.dec.mul_with_overflow";
|
||||
pub const DEC_DIV: &str = "roc_builtins.dec.div";
|
||||
|
||||
pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
|
||||
|
|
|
@ -4,8 +4,8 @@ use roc_module::symbol::Symbol;
|
|||
use roc_region::all::Region;
|
||||
use roc_types::builtin_aliases::{
|
||||
bool_type, dict_type, float_type, i128_type, int_type, list_type, nat_type, num_type,
|
||||
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u32_type, u64_type,
|
||||
u8_type,
|
||||
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u16_type, u32_type,
|
||||
u64_type, u8_type,
|
||||
};
|
||||
use roc_types::solved_types::SolvedType;
|
||||
use roc_types::subs::VarId;
|
||||
|
@ -501,6 +501,32 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
Box::new(float_type(flex(TVAR1))),
|
||||
);
|
||||
|
||||
// bytesToU16 : List U8, Nat -> Result U16 [ OutOfBounds ]
|
||||
{
|
||||
let position_out_of_bounds = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("OutOfBounds".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_BYTES_TO_U16,
|
||||
vec![list_type(u8_type()), nat_type()],
|
||||
Box::new(result_type(u16_type(), position_out_of_bounds)),
|
||||
);
|
||||
}
|
||||
|
||||
// bytesToU32 : List U8, Nat -> Result U32 [ OutOfBounds ]
|
||||
{
|
||||
let position_out_of_bounds = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("OutOfBounds".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_BYTES_TO_U32,
|
||||
vec![list_type(u8_type()), nat_type()],
|
||||
Box::new(result_type(u32_type(), position_out_of_bounds)),
|
||||
);
|
||||
}
|
||||
|
||||
// Bool module
|
||||
|
||||
// and : Bool, Bool -> Bool
|
||||
|
|
|
@ -161,6 +161,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
NUM_ATAN => num_atan,
|
||||
NUM_ACOS => num_acos,
|
||||
NUM_ASIN => num_asin,
|
||||
NUM_BYTES_TO_U16 => num_bytes_to_u16,
|
||||
NUM_BYTES_TO_U32 => num_bytes_to_u32,
|
||||
NUM_MAX_INT => num_max_int,
|
||||
NUM_MIN_INT => num_min_int,
|
||||
NUM_BITWISE_AND => num_bitwise_and,
|
||||
|
@ -1088,6 +1090,16 @@ fn num_asin(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)
|
||||
}
|
||||
|
||||
/// Num.bytesToU16 : List U8, Nat -> Result U16 [ OutOfBounds ]
|
||||
fn num_bytes_to_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_bytes_to(symbol, var_store, 1, LowLevel::NumBytesToU16)
|
||||
}
|
||||
|
||||
/// Num.bytesToU32 : List U8, Nat -> Result U32 [ OutOfBounds ]
|
||||
fn num_bytes_to_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_bytes_to(symbol, var_store, 3, LowLevel::NumBytesToU32)
|
||||
}
|
||||
|
||||
/// Num.bitwiseAnd : Int a, Int a -> Int a
|
||||
fn num_bitwise_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_binop(symbol, var_store, LowLevel::NumBitwiseAnd)
|
||||
|
@ -3359,6 +3371,97 @@ fn defn(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level: LowLevel) -> Def {
|
||||
let len_var = var_store.fresh();
|
||||
let list_var = var_store.fresh();
|
||||
let elem_var = var_store.fresh();
|
||||
|
||||
let ret_var = var_store.fresh();
|
||||
let bool_var = var_store.fresh();
|
||||
let add_var = var_store.fresh();
|
||||
let cast_var = var_store.fresh();
|
||||
|
||||
// Perform a bounds check. If it passes, run LowLevel::low_level
|
||||
let body = If {
|
||||
cond_var: bool_var,
|
||||
branch_var: var_store.fresh(),
|
||||
branches: vec![(
|
||||
// if-condition
|
||||
no_region(
|
||||
// index + offset < List.len list
|
||||
RunLowLevel {
|
||||
op: LowLevel::NumLt,
|
||||
args: vec![
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
op: LowLevel::NumAdd,
|
||||
args: vec![
|
||||
(add_var, Var(Symbol::ARG_2)),
|
||||
(
|
||||
add_var,
|
||||
RunLowLevel {
|
||||
ret_var: cast_var,
|
||||
args: vec![(cast_var, Num(var_store.fresh(), offset))],
|
||||
op: LowLevel::NumIntCast,
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_var: add_var,
|
||||
},
|
||||
),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
op: LowLevel::ListLen,
|
||||
args: vec![(list_var, Var(Symbol::ARG_1))],
|
||||
ret_var: len_var,
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
),
|
||||
// then-branch
|
||||
no_region(
|
||||
// Ok
|
||||
tag(
|
||||
"Ok",
|
||||
vec![RunLowLevel {
|
||||
op: low_level,
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(len_var, Var(Symbol::ARG_2)),
|
||||
],
|
||||
ret_var: elem_var,
|
||||
}],
|
||||
var_store,
|
||||
),
|
||||
),
|
||||
)],
|
||||
final_else: Box::new(
|
||||
// else-branch
|
||||
no_region(
|
||||
// Err
|
||||
tag(
|
||||
"Err",
|
||||
vec![tag("OutOfBounds", Vec::new(), var_store)],
|
||||
var_store,
|
||||
),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
defn(
|
||||
symbol,
|
||||
vec![(list_var, Symbol::ARG_1), (len_var, Symbol::ARG_2)],
|
||||
var_store,
|
||||
body,
|
||||
ret_var,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn defn_help(
|
||||
fn_name: Symbol,
|
||||
|
|
|
@ -204,6 +204,12 @@ where
|
|||
Symbol::NUM_SUB => {
|
||||
self.build_run_low_level(sym, &LowLevel::NumSub, arguments, layout)
|
||||
}
|
||||
Symbol::NUM_ROUND => self.build_run_low_level(
|
||||
sym,
|
||||
&LowLevel::NumRound,
|
||||
arguments,
|
||||
layout,
|
||||
),
|
||||
Symbol::BOOL_EQ => {
|
||||
self.build_run_low_level(sym, &LowLevel::Eq, arguments, layout)
|
||||
}
|
||||
|
@ -312,7 +318,13 @@ where
|
|||
// Should we panic?
|
||||
x => Err(format!("wrong layout, {:?}, for LowLevel::Eq", x)),
|
||||
},
|
||||
|
||||
LowLevel::NumRound => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::NUM_ROUND.to_string(),
|
||||
args,
|
||||
&[Layout::Builtin(Builtin::Float64)],
|
||||
layout,
|
||||
),
|
||||
x => Err(format!("low level, {:?}. is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
|
@ -516,20 +528,6 @@ where
|
|||
self.scan_ast(following);
|
||||
}
|
||||
|
||||
Stmt::Invoke {
|
||||
symbol,
|
||||
layout: _,
|
||||
call,
|
||||
pass,
|
||||
fail: _,
|
||||
exception_id: _,
|
||||
} => {
|
||||
// for now, treat invoke as a normal call
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
self.scan_ast_call(call, stmt);
|
||||
self.scan_ast(pass);
|
||||
}
|
||||
|
||||
Stmt::Switch {
|
||||
cond_symbol,
|
||||
branches,
|
||||
|
@ -545,7 +543,6 @@ where
|
|||
Stmt::Ret(sym) => {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
Stmt::Resume(_exception_id) => {}
|
||||
Stmt::Refcounting(modify, following) => {
|
||||
let sym = modify.get_symbol();
|
||||
|
||||
|
|
|
@ -179,6 +179,12 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
"roc_dealloc".into(),
|
||||
"free".into(),
|
||||
)?;
|
||||
generate_wrapper(
|
||||
&mut backend,
|
||||
&mut output,
|
||||
"roc_panic".into(),
|
||||
"roc_builtins.utils.test_panic".into(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Setup layout_ids for procedure calls.
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -73,17 +73,22 @@ pub fn helper<'a>(
|
|||
procedures.insert(key, proc);
|
||||
}
|
||||
|
||||
// println!("=========== Procedures ==========");
|
||||
// println!("{:?}", procedures);
|
||||
// println!("=================================\n");
|
||||
// You can comment and uncomment this block out to get more useful information
|
||||
// while you're working on the dev backend!
|
||||
{
|
||||
// println!("=========== Procedures ==========");
|
||||
// println!("{:?}", procedures);
|
||||
// println!("=================================\n");
|
||||
|
||||
// println!("=========== Interns ==========");
|
||||
// println!("{:?}", interns);
|
||||
// println!("=================================\n");
|
||||
// println!("=========== Interns ==========");
|
||||
// println!("{:?}", interns);
|
||||
// println!("=================================\n");
|
||||
|
||||
// println!("=========== Exposed ==========");
|
||||
// println!("{:?}", exposed_to_host);
|
||||
// println!("=================================\n");
|
||||
}
|
||||
|
||||
// println!("=========== Exposed ==========");
|
||||
// println!("{:?}", exposed_to_host);
|
||||
// println!("=================================\n");
|
||||
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||
let main_fn_symbol = loaded.entry_point.symbol;
|
||||
let main_fn_layout = loaded.entry_point.layout;
|
||||
|
|
|
@ -271,33 +271,11 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
match closure_data_layout {
|
||||
Layout::Closure(_, lambda_set, _) => {
|
||||
if let Layout::Struct(&[]) = lambda_set.runtime_representation() {
|
||||
// do nothing
|
||||
} else {
|
||||
let closure_type =
|
||||
basic_type_from_layout(env, &lambda_set.runtime_representation())
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let closure_cast = env
|
||||
.builder
|
||||
.build_bitcast(closure_ptr, closure_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
||||
|
||||
arguments_cast.push(closure_data);
|
||||
}
|
||||
Layout::Struct(&[]) => {
|
||||
// nothing to add
|
||||
}
|
||||
Layout::Struct([Layout::Closure(_, lambda_set, _)]) => {
|
||||
// a case required for Set.walk; may be able to remove when we can define builtins in
|
||||
// terms of other builtins in the right way (using their function symbols instead of
|
||||
// hacking with lowlevel ops).
|
||||
let closure_type = basic_type_from_layout(
|
||||
env,
|
||||
&Layout::Struct(&[lambda_set.runtime_representation()]),
|
||||
)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
other => {
|
||||
let closure_type = basic_type_from_layout(env, &other).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let closure_cast = env
|
||||
.builder
|
||||
|
@ -308,13 +286,6 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
|||
|
||||
arguments_cast.push(closure_data);
|
||||
}
|
||||
Layout::Struct([]) => {
|
||||
// do nothing, should try to remove this case later
|
||||
}
|
||||
Layout::Struct(_) => {
|
||||
// do nothing, should try to remove this case later
|
||||
}
|
||||
other => unreachable!("layout is not valid for a closure: {:?}", other),
|
||||
}
|
||||
|
||||
let call = {
|
||||
|
@ -625,26 +596,23 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
|||
let default = [value1, value2];
|
||||
|
||||
let arguments_cast = match closure_data_layout {
|
||||
Layout::Closure(_, lambda_set, _) => {
|
||||
if let Layout::Struct(&[]) = lambda_set.runtime_representation() {
|
||||
&default
|
||||
} else {
|
||||
let closure_type =
|
||||
basic_type_from_layout(env, &lambda_set.runtime_representation())
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let closure_cast = env
|
||||
.builder
|
||||
.build_bitcast(closure_ptr, closure_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
||||
|
||||
env.arena.alloc([value1, value2, closure_data]) as &[_]
|
||||
}
|
||||
Layout::Struct(&[]) => {
|
||||
// nothing to add
|
||||
&default
|
||||
}
|
||||
other => {
|
||||
let closure_type =
|
||||
basic_type_from_layout(env, &other).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let closure_cast = env
|
||||
.builder
|
||||
.build_bitcast(closure_ptr, closure_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
||||
|
||||
env.arena.alloc([value1, value2, closure_data]) as &[_]
|
||||
}
|
||||
Layout::Struct([]) => &default,
|
||||
other => unreachable!("layout is not valid for a closure: {:?}", other),
|
||||
};
|
||||
|
||||
let call = env.builder.build_call(
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -88,10 +88,6 @@ fn build_hash_layout<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
},
|
||||
|
||||
Layout::Closure(_, _, _) => {
|
||||
unreachable!("the type system will guarantee these are never hashed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -198,10 +198,6 @@ fn build_eq<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
},
|
||||
|
||||
Layout::Closure(_, _, _) => {
|
||||
unreachable!("the type system will guarantee these are never compared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,10 +336,6 @@ fn build_neq<'a, 'ctx, 'env>(
|
|||
Layout::RecursivePointer => {
|
||||
unreachable!("recursion pointers should never be compared directly")
|
||||
}
|
||||
|
||||
Layout::Closure(_, _, _) => {
|
||||
unreachable!("the type system will guarantee these are never compared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,6 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
|||
use Layout::*;
|
||||
|
||||
match layout {
|
||||
Closure(_args, closure_layout, _ret_layout) => {
|
||||
let closure_data_layout = closure_layout.runtime_representation();
|
||||
basic_type_from_layout(env, &closure_data_layout)
|
||||
}
|
||||
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
|
||||
Union(union_layout) => {
|
||||
use UnionLayout::*;
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
use crate::llvm::build::Env;
|
||||
use crate::llvm::build::{add_func, C_CALL_CONV};
|
||||
use crate::llvm::convert::ptr_int;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::module::{Linkage, Module};
|
||||
use inkwell::module::Linkage;
|
||||
use inkwell::values::BasicValue;
|
||||
use inkwell::AddressSpace;
|
||||
|
||||
/// Define functions for roc_alloc, roc_realloc, and roc_dealloc
|
||||
/// which use libc implementations (malloc, realloc, and free)
|
||||
pub fn add_default_roc_externs<'ctx>(
|
||||
ctx: &'ctx Context,
|
||||
module: &Module<'ctx>,
|
||||
builder: &Builder<'ctx>,
|
||||
ptr_bytes: u32,
|
||||
) {
|
||||
pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
||||
let ctx = env.context;
|
||||
let module = env.module;
|
||||
let builder = env.builder;
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
let usize_type = ptr_int(ctx, ptr_bytes);
|
||||
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
|
@ -139,4 +138,69 @@ pub fn add_default_roc_externs<'ctx>(
|
|||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
add_sjlj_roc_panic(env)
|
||||
}
|
||||
|
||||
pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
||||
let ctx = env.context;
|
||||
let module = env.module;
|
||||
let builder = env.builder;
|
||||
|
||||
// roc_panic
|
||||
{
|
||||
use crate::llvm::build::LLVM_LONGJMP;
|
||||
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_panic").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
|
||||
// in debug mode, this is assumed to be NullTerminatedString
|
||||
let _tag_id_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
let subprogram = env.new_subprogram("roc_panic");
|
||||
fn_val.set_subprogram(subprogram);
|
||||
|
||||
env.dibuilder.finalize();
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let buffer = crate::llvm::build::get_sjlj_buffer(env);
|
||||
|
||||
// write our error message pointer
|
||||
let index = env.ptr_int().const_int(3 * env.ptr_bytes as u64, false);
|
||||
let message_buffer_raw =
|
||||
unsafe { builder.build_gep(buffer, &[index], "raw_msg_buffer_ptr") };
|
||||
let message_buffer = builder.build_bitcast(
|
||||
message_buffer_raw,
|
||||
env.context
|
||||
.i8_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.ptr_type(AddressSpace::Generic),
|
||||
"to **u8",
|
||||
);
|
||||
|
||||
env.builder
|
||||
.build_store(message_buffer.into_pointer_value(), ptr_arg);
|
||||
|
||||
let tag = env.context.i32_type().const_int(1, false);
|
||||
if true {
|
||||
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[buffer.into()]);
|
||||
} else {
|
||||
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[buffer.into(), tag.into()]);
|
||||
}
|
||||
|
||||
builder.build_unreachable();
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,7 +278,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
// build then block
|
||||
{
|
||||
builder.position_at_end(then_block);
|
||||
if !env.leak {
|
||||
if !env.is_gen_test {
|
||||
let ptr = builder.build_pointer_cast(
|
||||
refcount_ptr.value,
|
||||
ctx.i8_type().ptr_type(AddressSpace::Generic),
|
||||
|
@ -659,23 +659,6 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
|
|||
Some(function)
|
||||
}
|
||||
|
||||
Closure(_, lambda_set, _) => {
|
||||
if lambda_set.contains_refcounted() {
|
||||
let function = modify_refcount_layout_build_function(
|
||||
env,
|
||||
parent,
|
||||
layout_ids,
|
||||
mode,
|
||||
when_recursive,
|
||||
&lambda_set.runtime_representation(),
|
||||
)?;
|
||||
|
||||
Some(function)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Struct(layouts) => {
|
||||
let function = modify_refcount_struct(env, layout_ids, layouts, mode, when_recursive);
|
||||
|
||||
|
|
|
@ -4060,7 +4060,7 @@ fn add_def_to_module<'a>(
|
|||
pattern_vars.push(*var);
|
||||
}
|
||||
|
||||
let layout = match layout_cache.from_var(
|
||||
let layout = match layout_cache.raw_from_var(
|
||||
mono_env.arena,
|
||||
annotation,
|
||||
mono_env.subs,
|
||||
|
@ -4085,7 +4085,7 @@ fn add_def_to_module<'a>(
|
|||
|
||||
procs.insert_exposed(
|
||||
symbol,
|
||||
ProcLayout::from_layout(mono_env.arena, layout),
|
||||
ProcLayout::from_raw(mono_env.arena, layout),
|
||||
mono_env.arena,
|
||||
mono_env.subs,
|
||||
def.annotation,
|
||||
|
|
|
@ -87,6 +87,8 @@ pub enum LowLevel {
|
|||
NumAtan,
|
||||
NumAcos,
|
||||
NumAsin,
|
||||
NumBytesToU16,
|
||||
NumBytesToU32,
|
||||
NumBitwiseAnd,
|
||||
NumBitwiseXor,
|
||||
NumBitwiseOr,
|
||||
|
@ -122,10 +124,9 @@ impl LowLevel {
|
|||
| NumRemUnchecked | NumIsMultipleOf | NumAbs | NumNeg | NumSin | NumCos
|
||||
| NumSqrtUnchecked | NumLogUnchecked | NumRound | NumToFloat | NumPow | NumCeiling
|
||||
| NumPowInt | NumFloor | NumIsFinite | NumAtan | NumAcos | NumAsin | NumBitwiseAnd
|
||||
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy
|
||||
| NumShiftRightZfBy | NumIntCast | Eq | NotEq | And | Or | Not | Hash | ExpectTrue => {
|
||||
false
|
||||
}
|
||||
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy | NumBytesToU16
|
||||
| NumBytesToU32 | NumShiftRightZfBy | NumIntCast | Eq | NotEq | And | Or | Not
|
||||
| Hash | ExpectTrue => false,
|
||||
|
||||
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||
|
|
|
@ -894,7 +894,9 @@ define_builtins! {
|
|||
100 NUM_AT_DECIMAL: "@Decimal"
|
||||
101 NUM_DECIMAL: "Decimal" imported
|
||||
102 NUM_DEC: "Dec" imported // the Num.Dectype alias
|
||||
|
||||
103 NUM_BYTES_TO_U16: "bytesToU16"
|
||||
104 NUM_BYTES_TO_U32: "bytesToU32"
|
||||
105 NUM_CAST_TO_NAT: "#castToNat"
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
|
|
|
@ -64,24 +64,10 @@ where
|
|||
let mut hasher = DefaultHasher::new();
|
||||
|
||||
for layout in argument_layouts {
|
||||
match layout {
|
||||
Layout::Closure(_, lambda_set, _) => {
|
||||
lambda_set.runtime_representation().hash(&mut hasher);
|
||||
}
|
||||
_ => {
|
||||
layout.hash(&mut hasher);
|
||||
}
|
||||
}
|
||||
layout.hash(&mut hasher);
|
||||
}
|
||||
|
||||
match return_layout {
|
||||
Layout::Closure(_, lambda_set, _) => {
|
||||
lambda_set.runtime_representation().hash(&mut hasher);
|
||||
}
|
||||
_ => {
|
||||
return_layout.hash(&mut hasher);
|
||||
}
|
||||
}
|
||||
return_layout.hash(&mut hasher);
|
||||
|
||||
hasher.finish()
|
||||
};
|
||||
|
@ -303,30 +289,6 @@ fn stmt_spec<'a>(
|
|||
|
||||
Ok(result)
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout: call_layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id: _,
|
||||
} => {
|
||||
// a call that might throw an exception
|
||||
|
||||
let value_id = call_spec(builder, env, block, call_layout, call)?;
|
||||
|
||||
let pass_block = builder.add_block();
|
||||
env.symbols.insert(*symbol, value_id);
|
||||
let pass_value_id = stmt_spec(builder, env, pass_block, layout, pass)?;
|
||||
env.symbols.remove(symbol);
|
||||
let pass_block_expr = BlockExpr(pass_block, pass_value_id);
|
||||
|
||||
let fail_block = builder.add_block();
|
||||
let fail_value_id = stmt_spec(builder, env, fail_block, layout, fail)?;
|
||||
let fail_block_expr = BlockExpr(fail_block, fail_value_id);
|
||||
|
||||
builder.add_choice(block, &[pass_block_expr, fail_block_expr])
|
||||
}
|
||||
Switch {
|
||||
cond_symbol: _,
|
||||
cond_layout: _,
|
||||
|
@ -434,7 +396,7 @@ fn stmt_spec<'a>(
|
|||
let jpid = env.join_points[id];
|
||||
builder.add_jump(block, jpid, argument, ret_type_id)
|
||||
}
|
||||
Resume(_) | RuntimeError(_) => {
|
||||
RuntimeError(_) => {
|
||||
let type_id = layout_spec(builder, layout)?;
|
||||
|
||||
builder.add_terminate(block, type_id)
|
||||
|
@ -1258,11 +1220,6 @@ fn layout_spec_help(
|
|||
}
|
||||
},
|
||||
},
|
||||
Closure(_, lambda_set, _) => layout_spec_help(
|
||||
builder,
|
||||
&lambda_set.runtime_representation(),
|
||||
when_recursive,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,7 @@ pub const OWNED: bool = false;
|
|||
pub const BORROWED: bool = true;
|
||||
|
||||
fn should_borrow_layout(layout: &Layout) -> bool {
|
||||
match layout {
|
||||
Layout::Closure(_, _, _) => false,
|
||||
_ => layout.is_refcounted(),
|
||||
}
|
||||
layout.is_refcounted()
|
||||
}
|
||||
|
||||
pub fn infer_borrow<'a>(
|
||||
|
@ -49,7 +46,14 @@ pub fn infer_borrow<'a>(
|
|||
// component (in top-sorted order, from primitives (std-lib) to main)
|
||||
|
||||
let successor_map = &make_successor_mapping(arena, procs);
|
||||
let successors = move |key: &Symbol| successor_map[key].iter().copied();
|
||||
let successors = move |key: &Symbol| {
|
||||
let slice = match successor_map.get(key) {
|
||||
None => &[] as &[_],
|
||||
Some(s) => s.as_slice(),
|
||||
};
|
||||
|
||||
slice.iter().copied()
|
||||
};
|
||||
|
||||
let mut symbols = Vec::with_capacity_in(procs.len(), arena);
|
||||
symbols.extend(procs.keys().map(|x| x.0));
|
||||
|
@ -220,7 +224,10 @@ impl<'a> DeclarationToIndex<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
unreachable!("symbol/layout combo must be in DeclarationToIndex")
|
||||
unreachable!(
|
||||
"symbol/layout {:?} {:?} combo must be in DeclarationToIndex",
|
||||
needle_symbol, needle_layout
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,10 +369,6 @@ impl<'a> ParamMap<'a> {
|
|||
Let(_, _, _, cont) => {
|
||||
stack.push(cont);
|
||||
}
|
||||
Invoke { pass, fail, .. } => {
|
||||
stack.push(pass);
|
||||
stack.push(fail);
|
||||
}
|
||||
Switch {
|
||||
branches,
|
||||
default_branch,
|
||||
|
@ -376,7 +379,7 @@ impl<'a> ParamMap<'a> {
|
|||
}
|
||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||
|
||||
Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
// these are terminal, do nothing
|
||||
}
|
||||
}
|
||||
|
@ -881,23 +884,6 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout: _,
|
||||
pass,
|
||||
fail,
|
||||
exception_id: _,
|
||||
} => {
|
||||
self.collect_stmt(param_map, pass);
|
||||
self.collect_stmt(param_map, fail);
|
||||
|
||||
self.collect_call(param_map, *symbol, call);
|
||||
|
||||
// TODO how to preserve the tail call of an invoke?
|
||||
// self.preserve_tail_call(*x, v, b);
|
||||
}
|
||||
|
||||
Jump(j, ys) => {
|
||||
let ps = param_map.get_join_point(*j);
|
||||
|
||||
|
@ -919,7 +905,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||
|
||||
Ret(_) | RuntimeError(_) | Resume(_) => {
|
||||
Ret(_) | RuntimeError(_) => {
|
||||
// these are terminal, do nothing
|
||||
}
|
||||
}
|
||||
|
@ -1012,6 +998,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound
|
||||
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin
|
||||
| NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
|
||||
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||
StrStartsWithCodePt => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),
|
||||
|
@ -1097,13 +1085,6 @@ fn call_info_stmt<'a>(arena: &'a Bump, stmt: &Stmt<'a>, info: &mut CallInfo<'a>)
|
|||
}
|
||||
stack.push(cont);
|
||||
}
|
||||
Invoke {
|
||||
call, pass, fail, ..
|
||||
} => {
|
||||
call_info_call(call, info);
|
||||
stack.push(pass);
|
||||
stack.push(fail);
|
||||
}
|
||||
Switch {
|
||||
branches,
|
||||
default_branch,
|
||||
|
@ -1114,7 +1095,7 @@ fn call_info_stmt<'a>(arena: &'a Bump, stmt: &Stmt<'a>, info: &mut CallInfo<'a>)
|
|||
}
|
||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||
|
||||
Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
// these are terminal, do nothing
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,17 +160,9 @@ impl<'a, 'i> Env<'a, 'i> {
|
|||
fn try_insert_struct_info(&mut self, symbol: Symbol, layout: &Layout<'a>) {
|
||||
use Layout::*;
|
||||
|
||||
match layout {
|
||||
Struct(fields) => {
|
||||
self.constructor_map.insert(symbol, 0);
|
||||
self.layout_map.insert(symbol, Layout::Struct(fields));
|
||||
}
|
||||
Closure(_, lambda_set, _) => {
|
||||
self.constructor_map.insert(symbol, 0);
|
||||
self.layout_map
|
||||
.insert(symbol, lambda_set.runtime_representation());
|
||||
}
|
||||
_ => {}
|
||||
if let Struct(fields) = layout {
|
||||
self.constructor_map.insert(symbol, 0);
|
||||
self.layout_map.insert(symbol, Layout::Struct(fields));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,10 +236,6 @@ fn layout_for_constructor<'a>(
|
|||
debug_assert_eq!(constructor, 0);
|
||||
HasFields(fields)
|
||||
}
|
||||
Closure(_arguments, _lambda_set, _result) => {
|
||||
// HasFields(fields)
|
||||
ConstructorLayout::Unknown
|
||||
}
|
||||
other => unreachable!("weird layout {:?}", other),
|
||||
}
|
||||
}
|
||||
|
@ -368,20 +356,10 @@ pub fn expand_and_cancel_proc<'a>(
|
|||
let mut introduced = Vec::new_in(env.arena);
|
||||
|
||||
for (layout, symbol) in arguments {
|
||||
match layout {
|
||||
Layout::Struct(fields) => {
|
||||
env.insert_struct_info(*symbol, fields);
|
||||
if let Layout::Struct(fields) = layout {
|
||||
env.insert_struct_info(*symbol, fields);
|
||||
|
||||
introduced.push(*symbol);
|
||||
}
|
||||
Layout::Closure(_arguments, _lambda_set, _result) => {
|
||||
// TODO can this be improved again?
|
||||
// let fpointer = Layout::FunctionPointer(arguments, result);
|
||||
// let fields = env.arena.alloc([fpointer, *closure_layout.layout]);
|
||||
// env.insert_struct_info(*symbol, fields);
|
||||
// introduced.push(*symbol);
|
||||
}
|
||||
_ => {}
|
||||
introduced.push(*symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -585,29 +563,6 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
|||
expand_and_cancel(env, cont)
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id,
|
||||
} => {
|
||||
let pass = expand_and_cancel(env, pass);
|
||||
let fail = expand_and_cancel(env, fail);
|
||||
|
||||
let stmt = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
layout: *layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id: *exception_id,
|
||||
};
|
||||
|
||||
env.arena.alloc(stmt)
|
||||
}
|
||||
|
||||
Join {
|
||||
id,
|
||||
parameters,
|
||||
|
@ -627,7 +582,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
|||
env.arena.alloc(stmt)
|
||||
}
|
||||
|
||||
Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -32,26 +32,10 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
|||
stack.push(cont);
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => {
|
||||
occurring_variables_call(call, &mut result);
|
||||
result.insert(*symbol);
|
||||
bound_variables.insert(*symbol);
|
||||
stack.push(pass);
|
||||
stack.push(fail);
|
||||
}
|
||||
|
||||
Ret(symbol) => {
|
||||
result.insert(*symbol);
|
||||
}
|
||||
|
||||
Resume(_) => {}
|
||||
|
||||
Refcounting(modify, cont) => {
|
||||
let symbol = modify.get_symbol();
|
||||
result.insert(symbol);
|
||||
|
@ -238,11 +222,6 @@ fn consume_expr(m: &VarMap, e: &Expr<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn consume_call(_: &VarMap, _: &crate::ir::Call<'_>) -> bool {
|
||||
// variables bound by a call (or invoke) must always be consumed
|
||||
true
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
||||
let mut vars = MutMap::default();
|
||||
|
@ -271,10 +250,20 @@ impl<'a> Context<'a> {
|
|||
fn get_var_info(&self, symbol: Symbol) -> VarInfo {
|
||||
match self.vars.get(&symbol) {
|
||||
Some(info) => *info,
|
||||
None => panic!(
|
||||
"Symbol {:?} {} has no info in {:?}",
|
||||
symbol, symbol, self.vars
|
||||
),
|
||||
None => {
|
||||
eprintln!(
|
||||
"Symbol {:?} {} has no info in self.vars",
|
||||
symbol,
|
||||
symbol, // self.vars
|
||||
);
|
||||
|
||||
VarInfo {
|
||||
persistent: true,
|
||||
reference: false,
|
||||
consume: false,
|
||||
reset: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -804,22 +793,6 @@ impl<'a> Context<'a> {
|
|||
(new_b, live_vars)
|
||||
}
|
||||
|
||||
fn update_var_info_invoke(
|
||||
&self,
|
||||
symbol: Symbol,
|
||||
layout: &Layout<'a>,
|
||||
call: &crate::ir::Call<'a>,
|
||||
) -> Self {
|
||||
// is this value a constant?
|
||||
// TODO do function pointers also fall into this category?
|
||||
let persistent = call.arguments.is_empty();
|
||||
|
||||
// must this value be consumed?
|
||||
let consume = consume_call(&self.vars, call);
|
||||
|
||||
self.update_var_info_help(symbol, layout, persistent, consume, false)
|
||||
}
|
||||
|
||||
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
|
||||
// is this value a constant?
|
||||
// TODO do function pointers also fall into this category?
|
||||
|
@ -955,82 +928,6 @@ impl<'a> Context<'a> {
|
|||
ctx.visit_variable_declaration(*symbol, expr.clone(), *layout, b, &b_live_vars)
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
layout,
|
||||
exception_id,
|
||||
} => {
|
||||
// live vars of the whole expression
|
||||
let invoke_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
||||
|
||||
let fail = {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
let (b, alt_live_vars) = ctx.visit_stmt(fail);
|
||||
ctx.add_dec_for_alt(&invoke_live_vars, &alt_live_vars, b)
|
||||
};
|
||||
|
||||
let pass = {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
let ctx = ctx.update_var_info_invoke(*symbol, layout, call);
|
||||
let (b, alt_live_vars) = ctx.visit_stmt(pass);
|
||||
ctx.add_dec_for_alt(&invoke_live_vars, &alt_live_vars, b)
|
||||
};
|
||||
|
||||
let invoke = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
pass,
|
||||
fail,
|
||||
layout: *layout,
|
||||
exception_id: *exception_id,
|
||||
};
|
||||
|
||||
let cont = self.arena.alloc(invoke);
|
||||
|
||||
use crate::ir::CallType;
|
||||
let stmt = match &call.call_type {
|
||||
CallType::LowLevel { op, .. } => {
|
||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||
self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars)
|
||||
}
|
||||
|
||||
CallType::HigherOrderLowLevel { .. } => {
|
||||
todo!("copy the code for normal calls over to here");
|
||||
}
|
||||
|
||||
CallType::Foreign { .. } => {
|
||||
let ps = crate::borrow::foreign_borrow_signature(
|
||||
self.arena,
|
||||
call.arguments.len(),
|
||||
);
|
||||
|
||||
self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars)
|
||||
}
|
||||
|
||||
CallType::ByName {
|
||||
name,
|
||||
ret_layout,
|
||||
arg_layouts,
|
||||
..
|
||||
} => {
|
||||
let top_level = ProcLayout::new(self.arena, arg_layouts, *ret_layout);
|
||||
|
||||
// get the borrow signature
|
||||
let ps = self
|
||||
.param_map
|
||||
.get_symbol(*name, top_level)
|
||||
.expect("function is defined");
|
||||
self.add_dec_after_application(call.arguments, ps, cont, &invoke_live_vars)
|
||||
}
|
||||
};
|
||||
|
||||
(stmt, invoke_live_vars)
|
||||
}
|
||||
Join {
|
||||
id: j,
|
||||
parameters: _,
|
||||
|
@ -1076,8 +973,6 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Resume(_) => (stmt, MutSet::default()),
|
||||
|
||||
Jump(j, xs) => {
|
||||
let empty = MutSet::default();
|
||||
let j_live_vars = match self.jp_live_vars.get(j) {
|
||||
|
@ -1166,25 +1061,7 @@ pub fn collect_stmt(
|
|||
|
||||
vars
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => {
|
||||
vars = collect_stmt(pass, jp_live_vars, vars);
|
||||
vars = collect_stmt(fail, jp_live_vars, vars);
|
||||
|
||||
vars.remove(symbol);
|
||||
|
||||
let mut result = MutSet::default();
|
||||
occurring_variables_call(call, &mut result);
|
||||
|
||||
vars.extend(result);
|
||||
|
||||
vars
|
||||
}
|
||||
Ret(symbol) => {
|
||||
vars.insert(*symbol);
|
||||
vars
|
||||
|
@ -1242,8 +1119,6 @@ pub fn collect_stmt(
|
|||
vars
|
||||
}
|
||||
|
||||
Resume(_) => vars,
|
||||
|
||||
RuntimeError(_) => vars,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -546,11 +546,11 @@ impl<'a> Procs<'a> {
|
|||
// anonymous functions cannot reference themselves, therefore cannot be tail-recursive
|
||||
let is_self_recursive = false;
|
||||
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, annotation, env.subs)
|
||||
let raw_layout = layout_cache
|
||||
.raw_from_var(env.arena, annotation, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||
|
||||
let top_level = ProcLayout::from_layout(env.arena, layout);
|
||||
let top_level = ProcLayout::from_raw(env.arena, raw_layout);
|
||||
|
||||
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
||||
Ok((_, pattern_symbols, body)) => {
|
||||
|
@ -617,7 +617,11 @@ impl<'a> Procs<'a> {
|
|||
Ok((proc, layout)) => {
|
||||
let top_level = ProcLayout::from_raw(env.arena, layout);
|
||||
|
||||
debug_assert_eq!(outside_layout, top_level);
|
||||
debug_assert_eq!(
|
||||
outside_layout, top_level,
|
||||
"different raw layouts for {:?}",
|
||||
proc.name
|
||||
);
|
||||
|
||||
if self.module_thunks.contains(&proc.name) {
|
||||
debug_assert!(top_level.arguments.is_empty());
|
||||
|
@ -690,10 +694,8 @@ impl<'a> Procs<'a> {
|
|||
layout: ProcLayout<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
) {
|
||||
let tuple = (name, layout);
|
||||
|
||||
// If we've already specialized this one, no further work is needed.
|
||||
if self.specialized.contains_key(&tuple) {
|
||||
if self.specialized.contains_key(&(name, layout)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -703,15 +705,12 @@ impl<'a> Procs<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
// We're done with that tuple, so move layout back out to avoid cloning it.
|
||||
let (name, layout) = tuple;
|
||||
|
||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||
|
||||
// This should only be called when pending_specializations is Some.
|
||||
// Otherwise, it's being called in the wrong pass!
|
||||
match &mut self.pending_specializations {
|
||||
Some(pending_specializations) => {
|
||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||
|
||||
// register the pending specialization, so this gets code genned later
|
||||
if self.module_thunks.contains(&name) {
|
||||
debug_assert!(layout.arguments.is_empty());
|
||||
|
@ -732,7 +731,26 @@ impl<'a> Procs<'a> {
|
|||
// (We had a bug around this before this system existed!)
|
||||
self.specialized.insert((symbol, layout), InProgress);
|
||||
|
||||
match specialize(env, self, symbol, layout_cache, pending, partial_proc) {
|
||||
// See https://github.com/rtfeldman/roc/issues/1600
|
||||
//
|
||||
// The annotation variable is the generic/lifted/top-level annotation.
|
||||
// It is connected to the variables of the function's body
|
||||
//
|
||||
// fn_var is the variable representing the type that we actually need for the
|
||||
// function right here.
|
||||
//
|
||||
// For some reason, it matters that we unify with the original variable. Extracting
|
||||
// that variable into a SolvedType and then introducing it again severs some
|
||||
// connection that turns out to be important
|
||||
match specialize_variable(
|
||||
env,
|
||||
self,
|
||||
symbol,
|
||||
layout_cache,
|
||||
fn_var,
|
||||
Default::default(),
|
||||
partial_proc,
|
||||
) {
|
||||
Ok((proc, _ignore_layout)) => {
|
||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||
// closure. We only specialize functions, storing this value with a closure
|
||||
|
@ -881,17 +899,6 @@ pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Stmt<'a> {
|
||||
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
||||
Invoke {
|
||||
symbol: Symbol,
|
||||
call: Call<'a>,
|
||||
layout: Layout<'a>,
|
||||
pass: &'a Stmt<'a>,
|
||||
fail: &'a Stmt<'a>,
|
||||
exception_id: ExceptionId,
|
||||
},
|
||||
/// after cleanup, rethrow the exception object (stored in the exception id)
|
||||
/// so it bubbles up
|
||||
Resume(ExceptionId),
|
||||
Switch {
|
||||
/// This *must* stand for an integer, because Switch potentially compiles to a jump table.
|
||||
cond_symbol: Symbol,
|
||||
|
@ -1375,45 +1382,11 @@ impl<'a> Stmt<'a> {
|
|||
.append(alloc.hardline())
|
||||
.append(cont.to_doc(alloc)),
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail: Stmt::Resume(_),
|
||||
..
|
||||
} => alloc
|
||||
.text("let ")
|
||||
.append(symbol_to_doc(alloc, *symbol))
|
||||
.append(" = ")
|
||||
.append(call.to_doc(alloc))
|
||||
.append(";")
|
||||
.append(alloc.hardline())
|
||||
.append(pass.to_doc(alloc)),
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => alloc
|
||||
.text("invoke ")
|
||||
.append(symbol_to_doc(alloc, *symbol))
|
||||
.append(" = ")
|
||||
.append(call.to_doc(alloc))
|
||||
.append(" catch")
|
||||
.append(alloc.hardline())
|
||||
.append(fail.to_doc(alloc).indent(4))
|
||||
.append(alloc.hardline())
|
||||
.append(pass.to_doc(alloc)),
|
||||
|
||||
Ret(symbol) => alloc
|
||||
.text("ret ")
|
||||
.append(symbol_to_doc(alloc, *symbol))
|
||||
.append(";"),
|
||||
|
||||
Resume(_) => alloc.text("unreachable;"),
|
||||
|
||||
Switch {
|
||||
cond_symbol,
|
||||
branches,
|
||||
|
@ -2028,7 +2001,9 @@ fn specialize_external<'a>(
|
|||
|
||||
aliases.insert(*symbol, (name, top_level, layout));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!("so far"),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!("so far");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2096,7 +2071,7 @@ fn specialize_external<'a>(
|
|||
|
||||
match closure_layout.layout_for_member(proc_name) {
|
||||
ClosureRepresentation::Union {
|
||||
tag_layout: field_layouts,
|
||||
alphabetic_order_fields: field_layouts,
|
||||
union_layout,
|
||||
tag_id,
|
||||
..
|
||||
|
@ -2104,7 +2079,23 @@ fn specialize_external<'a>(
|
|||
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
|
||||
debug_assert_eq!(field_layouts.len(), captured.len());
|
||||
|
||||
for (index, (symbol, _variable)) in captured.iter().enumerate() {
|
||||
// captured variables are in symbol-alphabetic order, but now we want
|
||||
// them ordered by their alignment requirements
|
||||
let mut combined = Vec::from_iter_in(
|
||||
captured.iter().map(|(x, _)| x).zip(field_layouts.iter()),
|
||||
env.arena,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
size2.cmp(&size1)
|
||||
});
|
||||
|
||||
for (index, (symbol, layout)) in combined.iter().enumerate() {
|
||||
let expr = Expr::UnionAtIndex {
|
||||
tag_id,
|
||||
structure: Symbol::ARG_CLOSURE,
|
||||
|
@ -2112,52 +2103,65 @@ fn specialize_external<'a>(
|
|||
union_layout,
|
||||
};
|
||||
|
||||
let layout = field_layouts[index];
|
||||
|
||||
specialized_body = Stmt::Let(
|
||||
*symbol,
|
||||
**symbol,
|
||||
expr,
|
||||
layout,
|
||||
**layout,
|
||||
env.arena.alloc(specialized_body),
|
||||
);
|
||||
}
|
||||
}
|
||||
ClosureRepresentation::Other(layout) => match layout {
|
||||
Layout::Struct(field_layouts) => {
|
||||
debug_assert_eq!(
|
||||
captured.len(),
|
||||
field_layouts.len(),
|
||||
"{:?} captures {:?} but has layout {:?}",
|
||||
proc_name,
|
||||
&captured,
|
||||
&field_layouts
|
||||
ClosureRepresentation::AlphabeticOrderStruct(field_layouts) => {
|
||||
// captured variables are in symbol-alphabetic order, but now we want
|
||||
// them ordered by their alignment requirements
|
||||
let mut combined = Vec::from_iter_in(
|
||||
captured.iter().map(|(x, _)| x).zip(field_layouts.iter()),
|
||||
env.arena,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
size2.cmp(&size1)
|
||||
});
|
||||
|
||||
debug_assert_eq!(
|
||||
captured.len(),
|
||||
field_layouts.len(),
|
||||
"{:?} captures {:?} but has layout {:?}",
|
||||
proc_name,
|
||||
&captured,
|
||||
&field_layouts
|
||||
);
|
||||
|
||||
for (index, (symbol, layout)) in combined.iter().enumerate() {
|
||||
let expr = Expr::StructAtIndex {
|
||||
index: index as _,
|
||||
field_layouts,
|
||||
structure: Symbol::ARG_CLOSURE,
|
||||
};
|
||||
|
||||
specialized_body = Stmt::Let(
|
||||
**symbol,
|
||||
expr,
|
||||
**layout,
|
||||
env.arena.alloc(specialized_body),
|
||||
);
|
||||
|
||||
for (index, (symbol, _variable)) in captured.iter().enumerate() {
|
||||
let expr = Expr::StructAtIndex {
|
||||
index: index as _,
|
||||
field_layouts,
|
||||
structure: Symbol::ARG_CLOSURE,
|
||||
};
|
||||
|
||||
let layout = field_layouts[index];
|
||||
|
||||
specialized_body = Stmt::Let(
|
||||
*symbol,
|
||||
expr,
|
||||
layout,
|
||||
env.arena.alloc(specialized_body),
|
||||
);
|
||||
}
|
||||
// let symbol = captured[0].0;
|
||||
//
|
||||
// substitute_in_exprs(
|
||||
// env.arena,
|
||||
// &mut specialized_body,
|
||||
// symbol,
|
||||
// Symbol::ARG_CLOSURE,
|
||||
// );
|
||||
}
|
||||
// let symbol = captured[0].0;
|
||||
//
|
||||
// substitute_in_exprs(
|
||||
// env.arena,
|
||||
// &mut specialized_body,
|
||||
// symbol,
|
||||
// Symbol::ARG_CLOSURE,
|
||||
// );
|
||||
}
|
||||
|
||||
ClosureRepresentation::Other(layout) => match layout {
|
||||
Layout::Builtin(Builtin::Int1) => {
|
||||
// just ignore this value
|
||||
// IDEA don't pass this value in the future
|
||||
|
@ -2458,28 +2462,72 @@ fn specialize_solved_type<'a>(
|
|||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||
partial_proc: PartialProc<'a>,
|
||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||
specialize_variable_help(
|
||||
env,
|
||||
procs,
|
||||
proc_name,
|
||||
layout_cache,
|
||||
|env| introduce_solved_type_to_subs(env, &solved_type),
|
||||
host_exposed_aliases,
|
||||
partial_proc,
|
||||
)
|
||||
}
|
||||
|
||||
fn specialize_variable<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
proc_name: Symbol,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
fn_var: Variable,
|
||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||
partial_proc: PartialProc<'a>,
|
||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||
specialize_variable_help(
|
||||
env,
|
||||
procs,
|
||||
proc_name,
|
||||
layout_cache,
|
||||
|_| fn_var,
|
||||
host_exposed_aliases,
|
||||
partial_proc,
|
||||
)
|
||||
}
|
||||
|
||||
fn specialize_variable_help<'a, F>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
proc_name: Symbol,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
fn_var_thunk: F,
|
||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||
partial_proc: PartialProc<'a>,
|
||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>>
|
||||
where
|
||||
F: FnOnce(&mut Env<'a, '_>) -> Variable,
|
||||
{
|
||||
// add the specializations that other modules require of us
|
||||
use roc_solve::solve::instantiate_rigids;
|
||||
|
||||
let snapshot = env.subs.snapshot();
|
||||
let cache_snapshot = layout_cache.snapshot();
|
||||
|
||||
let fn_var = introduce_solved_type_to_subs(env, &solved_type);
|
||||
// important: evaluate after the snapshot has been created!
|
||||
let fn_var = fn_var_thunk(env);
|
||||
|
||||
// for debugging only
|
||||
let attempted_layout = layout_cache
|
||||
.from_var(env.arena, fn_var, env.subs)
|
||||
let raw = layout_cache
|
||||
.raw_from_var(env.arena, fn_var, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
||||
|
||||
let raw = match attempted_layout {
|
||||
Layout::Closure(a, lambda_set, c) => {
|
||||
if procs.module_thunks.contains(&proc_name) {
|
||||
let raw = if procs.module_thunks.contains(&proc_name) {
|
||||
match raw {
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
RawFunctionLayout::ZeroArgumentThunk(lambda_set.runtime_representation())
|
||||
} else {
|
||||
RawFunctionLayout::Function(a, lambda_set, c)
|
||||
}
|
||||
_ => raw,
|
||||
}
|
||||
_ => RawFunctionLayout::ZeroArgumentThunk(attempted_layout),
|
||||
} else {
|
||||
raw
|
||||
};
|
||||
|
||||
// make sure rigid variables in the annotation are converted to flex variables
|
||||
|
@ -2506,12 +2554,12 @@ fn specialize_solved_type<'a>(
|
|||
match specialized {
|
||||
Ok(proc) => {
|
||||
// when successful, the layout after unification should be the layout before unification
|
||||
debug_assert_eq!(
|
||||
attempted_layout,
|
||||
layout_cache
|
||||
.from_var(env.arena, fn_var, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err))
|
||||
);
|
||||
// debug_assert_eq!(
|
||||
// attempted_layout,
|
||||
// layout_cache
|
||||
// .from_var(env.arena, fn_var, env.subs)
|
||||
// .unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err))
|
||||
// );
|
||||
|
||||
env.subs.rollback_to(snapshot);
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
|
@ -2541,39 +2589,20 @@ impl<'a> ProcLayout<'a> {
|
|||
let mut arguments = Vec::with_capacity_in(old_arguments.len(), arena);
|
||||
|
||||
for old in old_arguments {
|
||||
match old {
|
||||
Layout::Closure(_, lambda_set, _) => {
|
||||
let repr = lambda_set.runtime_representation();
|
||||
arguments.push(repr)
|
||||
}
|
||||
other => arguments.push(*other),
|
||||
}
|
||||
let other = old;
|
||||
arguments.push(*other);
|
||||
}
|
||||
|
||||
let new_result = match result {
|
||||
Layout::Closure(_, lambda_set, _) => lambda_set.runtime_representation(),
|
||||
other => other,
|
||||
};
|
||||
let other = result;
|
||||
let new_result = other;
|
||||
|
||||
ProcLayout {
|
||||
arguments: arguments.into_bump_slice(),
|
||||
result: new_result,
|
||||
}
|
||||
}
|
||||
pub fn from_layout(arena: &'a Bump, layout: Layout<'a>) -> Self {
|
||||
match layout {
|
||||
Layout::Closure(arguments, lambda_set, result) => {
|
||||
let arguments = lambda_set.extend_argument_list(arena, arguments);
|
||||
ProcLayout::new(arena, arguments, *result)
|
||||
}
|
||||
_ => ProcLayout {
|
||||
arguments: &[],
|
||||
result: layout,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_raw(arena: &'a Bump, raw: RawFunctionLayout<'a>) -> Self {
|
||||
pub fn from_raw(arena: &'a Bump, raw: RawFunctionLayout<'a>) -> Self {
|
||||
match raw {
|
||||
RawFunctionLayout::Function(arguments, lambda_set, result) => {
|
||||
let arguments = lambda_set.extend_argument_list(arena, arguments);
|
||||
|
@ -2666,11 +2695,13 @@ macro_rules! match_on_closure_argument {
|
|||
let arg_layouts = top_level.arguments;
|
||||
let ret_layout = top_level.result;
|
||||
|
||||
|
||||
match closure_data_layout {
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
lowlevel_match_on_lambda_set(
|
||||
$env,
|
||||
lambda_set,
|
||||
$op,
|
||||
$closure_data_symbol,
|
||||
|top_level_function, closure_data, closure_env_layout, specialization_id| self::Call {
|
||||
call_type: CallType::HigherOrderLowLevel {
|
||||
|
@ -2750,32 +2781,36 @@ pub fn with_hole<'a>(
|
|||
hole,
|
||||
),
|
||||
|
||||
Num(var, num) => match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num.into())),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num.into())),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
),
|
||||
},
|
||||
Num(var, num) => {
|
||||
// first figure out what kind of number this is
|
||||
|
||||
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num.into())),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num.into())),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
),
|
||||
}
|
||||
}
|
||||
LetNonRec(def, cont, _) => {
|
||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||
if let Closure {
|
||||
|
@ -4054,10 +4089,27 @@ fn construct_closure_data<'a>(
|
|||
match lambda_set.layout_for_member(name) {
|
||||
ClosureRepresentation::Union {
|
||||
tag_id,
|
||||
tag_layout: _,
|
||||
alphabetic_order_fields: field_layouts,
|
||||
tag_name,
|
||||
union_layout,
|
||||
} => {
|
||||
// captured variables are in symbol-alphabetic order, but now we want
|
||||
// them ordered by their alignment requirements
|
||||
let mut combined =
|
||||
Vec::from_iter_in(symbols.iter().zip(field_layouts.iter()), env.arena);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
size2.cmp(&size1)
|
||||
});
|
||||
|
||||
let symbols =
|
||||
Vec::from_iter_in(combined.iter().map(|(a, _)| **a), env.arena).into_bump_slice();
|
||||
|
||||
let expr = Expr::Tag {
|
||||
tag_id,
|
||||
tag_layout: union_layout,
|
||||
|
@ -4072,9 +4124,33 @@ fn construct_closure_data<'a>(
|
|||
env.arena.alloc(hole),
|
||||
)
|
||||
}
|
||||
ClosureRepresentation::Other(Layout::Struct(field_layouts)) => {
|
||||
ClosureRepresentation::AlphabeticOrderStruct(field_layouts) => {
|
||||
debug_assert_eq!(field_layouts.len(), symbols.len());
|
||||
|
||||
// captured variables are in symbol-alphabetic order, but now we want
|
||||
// them ordered by their alignment requirements
|
||||
let mut combined =
|
||||
Vec::from_iter_in(symbols.iter().zip(field_layouts.iter()), env.arena);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
size2.cmp(&size1)
|
||||
});
|
||||
|
||||
let symbols =
|
||||
Vec::from_iter_in(combined.iter().map(|(a, _)| **a), env.arena).into_bump_slice();
|
||||
let field_layouts =
|
||||
Vec::from_iter_in(combined.iter().map(|(_, b)| **b), env.arena).into_bump_slice();
|
||||
|
||||
debug_assert_eq!(
|
||||
Layout::Struct(field_layouts),
|
||||
lambda_set.runtime_representation()
|
||||
);
|
||||
|
||||
let expr = Expr::Struct(symbols);
|
||||
|
||||
Stmt::Let(assigned, expr, lambda_set.runtime_representation(), hole)
|
||||
|
@ -4554,15 +4630,12 @@ pub fn from_can<'a>(
|
|||
arguments,
|
||||
};
|
||||
|
||||
let exception_id = ExceptionId(env.unique_symbol());
|
||||
let rest = Stmt::Invoke {
|
||||
symbol: env.unique_symbol(),
|
||||
call,
|
||||
layout: bool_layout,
|
||||
pass: env.arena.alloc(rest),
|
||||
fail: env.arena.alloc(Stmt::Resume(exception_id)),
|
||||
exception_id,
|
||||
};
|
||||
let rest = Stmt::Let(
|
||||
env.unique_symbol(),
|
||||
Expr::Call(call),
|
||||
bool_layout,
|
||||
env.arena.alloc(rest),
|
||||
);
|
||||
|
||||
with_hole(
|
||||
env,
|
||||
|
@ -4673,14 +4746,16 @@ pub fn from_can<'a>(
|
|||
);
|
||||
CapturedSymbols::None
|
||||
}
|
||||
Err(e) => {
|
||||
debug_assert!(
|
||||
captured_symbols.is_empty(),
|
||||
"{:?}, {:?}",
|
||||
&captured_symbols,
|
||||
e
|
||||
);
|
||||
CapturedSymbols::None
|
||||
Err(_) => {
|
||||
// just allow this. see https://github.com/rtfeldman/roc/issues/1585
|
||||
if captured_symbols.is_empty() {
|
||||
CapturedSymbols::None
|
||||
} else {
|
||||
let mut temp =
|
||||
Vec::from_iter_in(captured_symbols, env.arena);
|
||||
temp.sort();
|
||||
CapturedSymbols::Captured(temp.into_bump_slice())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5146,35 +5221,6 @@ fn substitute_in_stmt_help<'a>(
|
|||
None
|
||||
}
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id,
|
||||
} => {
|
||||
let opt_call = substitute_in_call(arena, call, subs);
|
||||
let opt_pass = substitute_in_stmt_help(arena, pass, subs);
|
||||
let opt_fail = substitute_in_stmt_help(arena, fail, subs);
|
||||
|
||||
if opt_pass.is_some() || opt_fail.is_some() | opt_call.is_some() {
|
||||
let pass = opt_pass.unwrap_or(pass);
|
||||
let fail = opt_fail.unwrap_or_else(|| *fail);
|
||||
let call = opt_call.unwrap_or_else(|| call.clone());
|
||||
|
||||
Some(arena.alloc(Invoke {
|
||||
symbol: *symbol,
|
||||
call,
|
||||
layout: *layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id: *exception_id,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Join {
|
||||
id,
|
||||
parameters,
|
||||
|
@ -5289,8 +5335,6 @@ fn substitute_in_stmt_help<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Resume(_) => None,
|
||||
|
||||
RuntimeError(_) => None,
|
||||
}
|
||||
}
|
||||
|
@ -5952,12 +5996,21 @@ fn reuse_function_symbol<'a>(
|
|||
None => {
|
||||
match arg_var {
|
||||
Some(arg_var) if env.is_imported_symbol(original) => {
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, arg_var, env.subs)
|
||||
let raw = layout_cache
|
||||
.raw_from_var(env.arena, arg_var, env.subs)
|
||||
.expect("creating layout does not fail");
|
||||
|
||||
if procs.imported_module_thunks.contains(&original) {
|
||||
let top_level = ProcLayout::new(env.arena, &[], layout);
|
||||
let layout = match raw {
|
||||
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
lambda_set.runtime_representation()
|
||||
}
|
||||
};
|
||||
|
||||
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
||||
let top_level = ProcLayout::from_raw(env.arena, raw);
|
||||
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
|
@ -5968,7 +6021,7 @@ fn reuse_function_symbol<'a>(
|
|||
|
||||
force_thunk(env, original, layout, symbol, env.arena.alloc(result))
|
||||
} else {
|
||||
let top_level = ProcLayout::from_layout(env.arena, layout);
|
||||
let top_level = ProcLayout::from_raw(env.arena, raw);
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
|
@ -6011,7 +6064,7 @@ fn reuse_function_symbol<'a>(
|
|||
let captured = partial_proc.captured_symbols.clone();
|
||||
|
||||
match res_layout {
|
||||
RawFunctionLayout::Function(argument_layouts, lambda_set, ret_layout) => {
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
// define the function pointer
|
||||
let function_ptr_layout = ProcLayout::from_raw(env.arena, res_layout);
|
||||
|
||||
|
@ -6045,7 +6098,11 @@ fn reuse_function_symbol<'a>(
|
|||
)
|
||||
} else if procs.module_thunks.contains(&original) {
|
||||
// this is a 0-argument thunk
|
||||
let layout = Layout::Closure(argument_layouts, lambda_set, ret_layout);
|
||||
|
||||
// TODO suspicious
|
||||
// let layout = Layout::Closure(argument_layouts, lambda_set, ret_layout);
|
||||
// panic!("suspicious");
|
||||
let layout = lambda_set.runtime_representation();
|
||||
let top_level = ProcLayout::new(env.arena, &[], layout);
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
|
@ -6156,69 +6213,46 @@ fn add_needed_external<'a>(
|
|||
existing.insert(name, solved_type);
|
||||
}
|
||||
|
||||
fn can_throw_exception(call: &Call) -> bool {
|
||||
match call.call_type {
|
||||
CallType::ByName { name, .. } => matches!(
|
||||
name,
|
||||
Symbol::NUM_ADD
|
||||
| Symbol::NUM_SUB
|
||||
| Symbol::NUM_MUL
|
||||
| Symbol::NUM_DIV_FLOAT
|
||||
| Symbol::NUM_ABS
|
||||
| Symbol::NUM_NEG
|
||||
),
|
||||
|
||||
CallType::Foreign { .. } => {
|
||||
// calling foreign functions is very unsafe
|
||||
true
|
||||
}
|
||||
|
||||
CallType::LowLevel { .. } => {
|
||||
// lowlevel operations themselves don't throw
|
||||
// TODO except for on allocation?
|
||||
false
|
||||
}
|
||||
CallType::HigherOrderLowLevel { .. } => {
|
||||
// TODO throwing is based on whether the HOF can throw
|
||||
// or if there is (potentially) allocation in the lowlevel
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Symbol that links an Invoke with a Rethrow
|
||||
/// we'll assign the exception object to this symbol
|
||||
/// so we can later rethrow the exception
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct ExceptionId(Symbol);
|
||||
|
||||
impl ExceptionId {
|
||||
pub fn into_inner(self) -> Symbol {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn build_call<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
_env: &mut Env<'a, '_>,
|
||||
call: Call<'a>,
|
||||
assigned: Symbol,
|
||||
return_layout: Layout<'a>,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
if can_throw_exception(&call) {
|
||||
let id = ExceptionId(env.unique_symbol());
|
||||
let fail = env.arena.alloc(Stmt::Resume(id));
|
||||
Stmt::Invoke {
|
||||
symbol: assigned,
|
||||
call,
|
||||
layout: return_layout,
|
||||
fail,
|
||||
pass: hole,
|
||||
exception_id: id,
|
||||
}
|
||||
} else {
|
||||
Stmt::Let(assigned, Expr::Call(call), return_layout, hole)
|
||||
}
|
||||
Stmt::Let(assigned, Expr::Call(call), return_layout, hole)
|
||||
}
|
||||
|
||||
/// See https://github.com/rtfeldman/roc/issues/1549
|
||||
///
|
||||
/// What happened is that a function has a type error, but the arguments are not processed.
|
||||
/// That means specializations were missing. Normally that is not a problem, but because
|
||||
/// of our closure strategy, internal functions can "leak". That's what happened here.
|
||||
///
|
||||
/// The solution is to evaluate the arguments as normal, and only when calling the function give an error
|
||||
fn evaluate_arguments_then_runtime_error<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
msg: String,
|
||||
loc_args: std::vec::Vec<(Variable, Located<roc_can::expr::Expr>)>,
|
||||
) -> Stmt<'a> {
|
||||
let arena = env.arena;
|
||||
|
||||
// eventually we will throw this runtime error
|
||||
let result = Stmt::RuntimeError(env.arena.alloc(msg));
|
||||
|
||||
// but, we also still evaluate and specialize the arguments to give better error messages
|
||||
let arg_symbols = Vec::from_iter_in(
|
||||
loc_args
|
||||
.iter()
|
||||
.map(|(_, arg_expr)| possible_reuse_symbol(env, procs, &arg_expr.value)),
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice();
|
||||
|
||||
let iter = loc_args.into_iter().rev().zip(arg_symbols.iter().rev());
|
||||
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -6239,14 +6273,16 @@ fn call_by_name<'a>(
|
|||
"Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})",
|
||||
var, proc_name, fn_var
|
||||
);
|
||||
Stmt::RuntimeError(env.arena.alloc(msg))
|
||||
|
||||
evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args)
|
||||
}
|
||||
Err(LayoutProblem::Erroneous) => {
|
||||
let msg = format!(
|
||||
"Hit an erroneous type when creating a layout for {:?}",
|
||||
proc_name
|
||||
);
|
||||
Stmt::RuntimeError(env.arena.alloc(msg))
|
||||
|
||||
evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args)
|
||||
}
|
||||
Ok(RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout)) => {
|
||||
if procs.module_thunks.contains(&proc_name) {
|
||||
|
@ -6651,8 +6687,12 @@ fn call_by_name_module_thunk<'a>(
|
|||
|
||||
match specialize(env, procs, proc_name, layout_cache, pending, partial_proc)
|
||||
{
|
||||
Ok((proc, layout)) => {
|
||||
debug_assert!(layout.is_zero_argument_thunk());
|
||||
Ok((proc, raw_layout)) => {
|
||||
debug_assert!(
|
||||
raw_layout.is_zero_argument_thunk(),
|
||||
"but actually {:?}",
|
||||
raw_layout
|
||||
);
|
||||
|
||||
let was_present =
|
||||
procs.specialized.remove(&(proc_name, top_level_layout));
|
||||
|
@ -7613,6 +7653,7 @@ pub fn num_argument_to_int_or_float(
|
|||
fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
||||
env: &mut Env<'a, '_>,
|
||||
lambda_set: LambdaSet<'a>,
|
||||
op: LowLevel,
|
||||
closure_data_symbol: Symbol,
|
||||
to_lowlevel_call: ToLowLevelCall,
|
||||
return_layout: Layout<'a>,
|
||||
|
@ -7652,19 +7693,29 @@ where
|
|||
env.arena.alloc(result),
|
||||
)
|
||||
}
|
||||
Layout::Struct(_) => {
|
||||
let function_symbol = lambda_set.set[0].0;
|
||||
Layout::Struct(_) => match lambda_set.set.get(0) {
|
||||
Some((function_symbol, _)) => {
|
||||
let call_spec_id = env.next_call_specialization_id();
|
||||
let call = to_lowlevel_call(
|
||||
*function_symbol,
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
call_spec_id,
|
||||
);
|
||||
|
||||
let call_spec_id = env.next_call_specialization_id();
|
||||
let call = to_lowlevel_call(
|
||||
function_symbol,
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
call_spec_id,
|
||||
);
|
||||
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
||||
}
|
||||
None => {
|
||||
eprintln!(
|
||||
"a function passed to `{:?}` LowLevel call has an empty lambda set!
|
||||
The most likely reason is that some symbol you use is not in scope.
|
||||
",
|
||||
op
|
||||
);
|
||||
|
||||
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
||||
}
|
||||
hole.clone()
|
||||
}
|
||||
},
|
||||
Layout::Builtin(Builtin::Int1) => {
|
||||
let closure_tag_id_symbol = closure_data_symbol;
|
||||
|
||||
|
@ -7876,7 +7927,15 @@ fn union_lambda_set_to_switch<'a>(
|
|||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
debug_assert!(!lambda_set.is_empty());
|
||||
if lambda_set.is_empty() {
|
||||
// NOTE this can happen if there is a type error somewhere. Since the lambda set is empty,
|
||||
// there is really nothing we can do here. We generate a runtime error here which allows
|
||||
// code gen to proceed. We then assume that we hit another (more descriptive) error before
|
||||
// hitting this one
|
||||
|
||||
let msg = "a Lambda Set isempty. Most likely there is a type error in your program.";
|
||||
return Stmt::RuntimeError(msg);
|
||||
}
|
||||
|
||||
let join_point_id = JoinPointId(env.unique_symbol());
|
||||
|
||||
|
|
|
@ -29,10 +29,154 @@ pub enum RawFunctionLayout<'a> {
|
|||
ZeroArgumentThunk(Layout<'a>),
|
||||
}
|
||||
|
||||
impl RawFunctionLayout<'_> {
|
||||
impl<'a> RawFunctionLayout<'a> {
|
||||
pub fn is_zero_argument_thunk(&self) -> bool {
|
||||
matches!(self, RawFunctionLayout::ZeroArgumentThunk(_))
|
||||
}
|
||||
|
||||
fn new_help<'b>(
|
||||
env: &mut Env<'a, 'b>,
|
||||
var: Variable,
|
||||
content: Content,
|
||||
) -> Result<Self, LayoutProblem> {
|
||||
use roc_types::subs::Content::*;
|
||||
match content {
|
||||
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
||||
RecursionVar { structure, .. } => {
|
||||
let structure_content = env.subs.get_content_without_compacting(structure);
|
||||
Self::new_help(env, structure, structure_content.clone())
|
||||
}
|
||||
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
|
||||
|
||||
// Ints
|
||||
Alias(Symbol::NUM_I128, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int128)))
|
||||
}
|
||||
Alias(Symbol::NUM_I64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int64)))
|
||||
}
|
||||
Alias(Symbol::NUM_I32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int32)))
|
||||
}
|
||||
Alias(Symbol::NUM_I16, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int16)))
|
||||
}
|
||||
Alias(Symbol::NUM_I8, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int8)))
|
||||
}
|
||||
|
||||
// I think unsigned and signed use the same layout
|
||||
Alias(Symbol::NUM_U128, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int128)))
|
||||
}
|
||||
Alias(Symbol::NUM_U64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int64)))
|
||||
}
|
||||
Alias(Symbol::NUM_U32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int32)))
|
||||
}
|
||||
Alias(Symbol::NUM_U16, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int16)))
|
||||
}
|
||||
Alias(Symbol::NUM_U8, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int8)))
|
||||
}
|
||||
|
||||
Alias(Symbol::NUM_NAT, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Usize)))
|
||||
}
|
||||
|
||||
// Floats
|
||||
Alias(Symbol::NUM_F64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Float64)))
|
||||
}
|
||||
Alias(Symbol::NUM_F32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Float32)))
|
||||
}
|
||||
|
||||
Alias(symbol, _, _) if symbol.is_builtin() => Ok(Self::ZeroArgumentThunk(
|
||||
Layout::new_help(env, var, content)?,
|
||||
)),
|
||||
|
||||
Alias(_, _, var) => Self::from_var(env, var),
|
||||
Error => Err(LayoutProblem::Erroneous),
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_from_flat_type(
|
||||
env: &mut Env<'a, '_>,
|
||||
flat_type: FlatType,
|
||||
) -> Result<Self, LayoutProblem> {
|
||||
use roc_types::subs::FlatType::*;
|
||||
|
||||
let arena = env.arena;
|
||||
|
||||
match flat_type {
|
||||
Func(args, closure_var, ret_var) => {
|
||||
let mut fn_args = Vec::with_capacity_in(args.len(), arena);
|
||||
|
||||
for index in args.into_iter() {
|
||||
let arg_var = env.subs[index];
|
||||
fn_args.push(Layout::from_var(env, arg_var)?);
|
||||
}
|
||||
|
||||
let ret = Layout::from_var(env, ret_var)?;
|
||||
|
||||
let fn_args = fn_args.into_bump_slice();
|
||||
let ret = arena.alloc(ret);
|
||||
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
|
||||
|
||||
Ok(Self::Function(fn_args, lambda_set, ret))
|
||||
}
|
||||
TagUnion(tags, ext) if tags.is_newtype_wrapper(env.subs) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(env.subs, ext));
|
||||
let slice_index = tags.variables().into_iter().next().unwrap();
|
||||
let slice = env.subs[slice_index];
|
||||
let var_index = slice.into_iter().next().unwrap();
|
||||
let var = env.subs[var_index];
|
||||
|
||||
Self::from_var(env, var)
|
||||
}
|
||||
Record(fields, ext) if fields.len() == 1 => {
|
||||
debug_assert!(ext_var_is_empty_record(env.subs, ext));
|
||||
|
||||
let var_index = fields.iter_variables().next().unwrap();
|
||||
let var = env.subs[var_index];
|
||||
|
||||
Self::from_var(env, var)
|
||||
}
|
||||
_ => {
|
||||
let layout = layout_from_flat_type(env, flat_type)?;
|
||||
Ok(Self::ZeroArgumentThunk(layout))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
|
||||
/// Panics if given a FlexVar or RigidVar, since those should have been
|
||||
/// monomorphized away already!
|
||||
fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result<Self, LayoutProblem> {
|
||||
if env.is_seen(var) {
|
||||
unreachable!("The initial variable of a signature cannot be seen already")
|
||||
} else {
|
||||
let content = env.subs.get_content_without_compacting(var);
|
||||
Self::new_help(env, var, content.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Types for code gen must be monomorphic. No type variables allowed!
|
||||
|
@ -45,9 +189,6 @@ pub enum Layout<'a> {
|
|||
Struct(&'a [Layout<'a>]),
|
||||
Union(UnionLayout<'a>),
|
||||
RecursivePointer,
|
||||
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
Closure(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
|
@ -259,11 +400,16 @@ pub struct LambdaSet<'a> {
|
|||
pub enum ClosureRepresentation<'a> {
|
||||
/// the closure is represented as a union. Includes the tag ID!
|
||||
Union {
|
||||
tag_layout: &'a [Layout<'a>],
|
||||
alphabetic_order_fields: &'a [Layout<'a>],
|
||||
tag_name: TagName,
|
||||
tag_id: u8,
|
||||
union_layout: UnionLayout<'a>,
|
||||
},
|
||||
/// The closure is represented as a struct. The layouts are sorted
|
||||
/// alphabetically by the identifier that is captured.
|
||||
///
|
||||
/// We MUST sort these according to their stack size before code gen!
|
||||
AlphabeticOrderStruct(&'a [Layout<'a>]),
|
||||
/// the representation is anything but a union
|
||||
Other(Layout<'a>),
|
||||
}
|
||||
|
@ -292,16 +438,19 @@ impl<'a> LambdaSet<'a> {
|
|||
// here we rely on the fact that a union in a closure would be stored in a one-element record.
|
||||
// a closure representation that is itself union must be a of the shape `Closure1 ... | Closure2 ...`
|
||||
match union {
|
||||
UnionLayout::NonRecursive(tags) => {
|
||||
let index = self
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
// get the fields from the set, where they are sorted in alphabetic order
|
||||
// (and not yet sorted by their alignment)
|
||||
let (index, (_, fields)) = self
|
||||
.set
|
||||
.iter()
|
||||
.position(|(s, _)| *s == function_symbol)
|
||||
.enumerate()
|
||||
.find(|(_, (s, _))| *s == function_symbol)
|
||||
.unwrap();
|
||||
|
||||
ClosureRepresentation::Union {
|
||||
tag_id: index as u8,
|
||||
tag_layout: tags[index],
|
||||
alphabetic_order_fields: fields,
|
||||
tag_name: TagName::Closure(function_symbol),
|
||||
union_layout: *union,
|
||||
}
|
||||
|
@ -318,6 +467,17 @@ impl<'a> LambdaSet<'a> {
|
|||
} => todo!("recursive closures"),
|
||||
}
|
||||
}
|
||||
Layout::Struct(_) => {
|
||||
// get the fields from the set, where they are sorted in alphabetic order
|
||||
// (and not yet sorted by their alignment)
|
||||
let (_, fields) = self
|
||||
.set
|
||||
.iter()
|
||||
.find(|(s, _)| *s == function_symbol)
|
||||
.unwrap();
|
||||
|
||||
ClosureRepresentation::AlphabeticOrderStruct(fields)
|
||||
}
|
||||
_ => ClosureRepresentation::Other(*self.representation),
|
||||
}
|
||||
}
|
||||
|
@ -394,10 +554,10 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) => {
|
||||
// TODO hack for builting functions.
|
||||
// this can happen when there is a type error somewhere
|
||||
Ok(LambdaSet {
|
||||
set: &[],
|
||||
representation: arena.alloc(Layout::Struct(&[])),
|
||||
representation: arena.alloc(Layout::Union(UnionLayout::NonRecursive(&[]))),
|
||||
})
|
||||
}
|
||||
_ => panic!("called LambdaSet.from_var on invalid input"),
|
||||
|
@ -587,6 +747,12 @@ impl<'a> Layout<'a> {
|
|||
Ok(Layout::Builtin(Builtin::Float32))
|
||||
}
|
||||
|
||||
// Nat
|
||||
Alias(Symbol::NUM_NAT, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Usize))
|
||||
}
|
||||
|
||||
Alias(_, _, var) => Self::from_var(env, var),
|
||||
Error => Err(LayoutProblem::Erroneous),
|
||||
}
|
||||
|
@ -628,7 +794,6 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Closure(_, closure_layout, _) => closure_layout.safe_to_memcpy(),
|
||||
RecursivePointer => {
|
||||
// We cannot memcpy pointers, because then we would have the same pointer in multiple places!
|
||||
false
|
||||
|
@ -693,7 +858,6 @@ impl<'a> Layout<'a> {
|
|||
| NonNullableUnwrapped(_) => pointer_size,
|
||||
}
|
||||
}
|
||||
Closure(_, lambda_set, _) => lambda_set.stack_size(pointer_size),
|
||||
RecursivePointer => pointer_size,
|
||||
}
|
||||
}
|
||||
|
@ -725,9 +889,6 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
Layout::Builtin(builtin) => builtin.alignment_bytes(pointer_size),
|
||||
Layout::RecursivePointer => pointer_size,
|
||||
Layout::Closure(_, captured, _) => {
|
||||
pointer_size.max(captured.alignment_bytes(pointer_size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -778,8 +939,6 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
RecursivePointer => true,
|
||||
|
||||
Closure(_, closure_layout, _) => closure_layout.contains_refcounted(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -803,20 +962,6 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
Union(union_layout) => union_layout.to_doc(alloc, parens),
|
||||
RecursivePointer => alloc.text("*self"),
|
||||
Closure(args, closure_layout, result) => {
|
||||
let args_doc = args.iter().map(|x| x.to_doc(alloc, Parens::InFunction));
|
||||
|
||||
let bom = closure_layout
|
||||
.representation
|
||||
.to_doc(alloc, Parens::NotNeeded);
|
||||
|
||||
alloc
|
||||
.intersperse(args_doc, ", ")
|
||||
.append(alloc.text(" {| "))
|
||||
.append(bom)
|
||||
.append(" |} -> ")
|
||||
.append(result.to_doc(alloc, Parens::InFunction))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -873,10 +1018,7 @@ impl<'a> LayoutCache<'a> {
|
|||
seen: Vec::new_in(arena),
|
||||
};
|
||||
|
||||
Layout::from_var(&mut env, var).map(|l| match l {
|
||||
Layout::Closure(a, b, c) => RawFunctionLayout::Function(a, b, c),
|
||||
other => RawFunctionLayout::ZeroArgumentThunk(other),
|
||||
})
|
||||
RawFunctionLayout::from_var(&mut env, var)
|
||||
}
|
||||
|
||||
pub fn snapshot(&mut self) -> SnapshotKeyPlaceholder {
|
||||
|
@ -1130,22 +1272,10 @@ fn layout_from_flat_type<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Func(args, closure_var, ret_var) => {
|
||||
let mut fn_args = Vec::with_capacity_in(args.len(), arena);
|
||||
|
||||
for index in args.into_iter() {
|
||||
let arg_var = env.subs[index];
|
||||
fn_args.push(Layout::from_var(env, arg_var)?);
|
||||
}
|
||||
|
||||
let ret = Layout::from_var(env, ret_var)?;
|
||||
|
||||
let fn_args = fn_args.into_bump_slice();
|
||||
let ret = arena.alloc(ret);
|
||||
|
||||
Func(_, closure_var, _) => {
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
|
||||
|
||||
Ok(Layout::Closure(fn_args, lambda_set, ret))
|
||||
Ok(lambda_set.runtime_representation())
|
||||
}
|
||||
Record(fields, ext_var) => {
|
||||
// extract any values from the ext_var
|
||||
|
@ -2083,6 +2213,20 @@ fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> L
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn ext_var_is_empty_record(subs: &Subs, ext_var: Variable) -> bool {
|
||||
// the ext_var is empty
|
||||
let fields = roc_types::types::gather_fields(subs, RecordFields::empty(), ext_var);
|
||||
|
||||
fields.fields.is_empty()
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn ext_var_is_empty_record(_subs: &Subs, _ext_var: Variable) -> bool {
|
||||
// This should only ever be used in debug_assert! macros
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn ext_var_is_empty_tag_union(subs: &Subs, ext_var: Variable) -> bool {
|
||||
// the ext_var is empty
|
||||
|
|
|
@ -135,28 +135,6 @@ fn function_s<'a, 'i>(
|
|||
|
||||
arena.alloc(new_join)
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id,
|
||||
} => {
|
||||
let new_pass = function_s(env, w, c, pass);
|
||||
let new_fail = function_s(env, w, c, fail);
|
||||
|
||||
let new_invoke = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
layout: *layout,
|
||||
pass: new_pass,
|
||||
fail: new_fail,
|
||||
exception_id: *exception_id,
|
||||
};
|
||||
|
||||
arena.alloc(new_invoke)
|
||||
}
|
||||
Switch {
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
|
@ -195,7 +173,7 @@ fn function_s<'a, 'i>(
|
|||
arena.alloc(new_refcounting)
|
||||
}
|
||||
}
|
||||
Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,37 +296,6 @@ fn function_d_main<'a, 'i>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id,
|
||||
} => {
|
||||
if has_live_var(&env.jp_live_vars, stmt, x) {
|
||||
let new_pass = {
|
||||
let temp = function_d_main(env, x, c, pass);
|
||||
function_d_finalize(env, x, c, temp)
|
||||
};
|
||||
let new_fail = {
|
||||
let temp = function_d_main(env, x, c, fail);
|
||||
function_d_finalize(env, x, c, temp)
|
||||
};
|
||||
let new_switch = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
layout: *layout,
|
||||
pass: new_pass,
|
||||
fail: new_fail,
|
||||
exception_id: *exception_id,
|
||||
};
|
||||
|
||||
(arena.alloc(new_switch), true)
|
||||
} else {
|
||||
(stmt, false)
|
||||
}
|
||||
}
|
||||
Switch {
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
|
@ -433,9 +380,7 @@ fn function_d_main<'a, 'i>(
|
|||
|
||||
(arena.alloc(new_join), found)
|
||||
}
|
||||
Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
(stmt, has_live_var(&env.jp_live_vars, stmt, x))
|
||||
}
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => (stmt, has_live_var(&env.jp_live_vars, stmt, x)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -550,36 +495,13 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
|||
|
||||
arena.alloc(Let(*symbol, expr.clone(), *layout, b))
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id,
|
||||
} => {
|
||||
let branch_info = BranchInfo::None;
|
||||
let new_pass = function_r_branch_body(env, &branch_info, pass);
|
||||
let new_fail = function_r_branch_body(env, &branch_info, fail);
|
||||
|
||||
let invoke = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
layout: *layout,
|
||||
pass: new_pass,
|
||||
fail: new_fail,
|
||||
exception_id: *exception_id,
|
||||
};
|
||||
|
||||
arena.alloc(invoke)
|
||||
}
|
||||
Refcounting(modify_rc, continuation) => {
|
||||
let b = function_r(env, continuation);
|
||||
|
||||
arena.alloc(Refcounting(*modify_rc, b))
|
||||
}
|
||||
|
||||
Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
// terminals
|
||||
stmt
|
||||
}
|
||||
|
@ -594,19 +516,6 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
|||
debug_assert_ne!(*s, needle);
|
||||
has_live_var_expr(e, needle) || has_live_var(jp_live_vars, c, needle)
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => {
|
||||
debug_assert_ne!(*symbol, needle);
|
||||
|
||||
has_live_var_call(call, needle)
|
||||
|| has_live_var(jp_live_vars, pass, needle)
|
||||
|| has_live_var(jp_live_vars, fail, needle)
|
||||
}
|
||||
Switch { cond_symbol, .. } if *cond_symbol == needle => true,
|
||||
Switch {
|
||||
branches,
|
||||
|
@ -647,7 +556,7 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
|||
Jump(id, arguments) => {
|
||||
arguments.iter().any(|s| *s == needle) || jp_live_vars[id].contains(&needle)
|
||||
}
|
||||
Resume(_) | RuntimeError(_) => false,
|
||||
RuntimeError(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,28 +92,6 @@ fn insert_jumps<'a>(
|
|||
Some(arena.alloc(jump))
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call:
|
||||
crate::ir::Call {
|
||||
call_type: CallType::ByName { name: fsym, .. },
|
||||
arguments,
|
||||
..
|
||||
},
|
||||
fail,
|
||||
pass: Stmt::Ret(rsym),
|
||||
exception_id,
|
||||
..
|
||||
} if needle == *fsym && symbol == rsym => {
|
||||
debug_assert_eq!(fail, &&Stmt::Resume(*exception_id));
|
||||
|
||||
// replace the call and return with a jump
|
||||
|
||||
let jump = Stmt::Jump(goal_id, arguments);
|
||||
|
||||
Some(arena.alloc(jump))
|
||||
}
|
||||
|
||||
Let(symbol, expr, layout, cont) => {
|
||||
let opt_cont = insert_jumps(arena, cont, goal_id, needle);
|
||||
|
||||
|
@ -126,36 +104,6 @@ fn insert_jumps<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
fail,
|
||||
pass,
|
||||
layout,
|
||||
exception_id,
|
||||
} => {
|
||||
let opt_pass = insert_jumps(arena, pass, goal_id, needle);
|
||||
let opt_fail = insert_jumps(arena, fail, goal_id, needle);
|
||||
|
||||
if opt_pass.is_some() || opt_fail.is_some() {
|
||||
let pass = opt_pass.unwrap_or(pass);
|
||||
let fail = opt_fail.unwrap_or(fail);
|
||||
|
||||
let stmt = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
layout: *layout,
|
||||
pass,
|
||||
fail,
|
||||
exception_id: *exception_id,
|
||||
};
|
||||
|
||||
Some(arena.alloc(stmt))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Join {
|
||||
id,
|
||||
parameters,
|
||||
|
@ -241,7 +189,6 @@ fn insert_jumps<'a>(
|
|||
None => None,
|
||||
},
|
||||
|
||||
Resume(_) => None,
|
||||
Ret(_) => None,
|
||||
Jump(_, _) => None,
|
||||
RuntimeError(_) => None,
|
||||
|
|
|
@ -27,6 +27,25 @@ pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
|||
libc::free(c_ptr)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
use roc_gen_llvm::llvm::build::PanicTagId;
|
||||
|
||||
use libc::c_char;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::CStr;
|
||||
|
||||
match PanicTagId::try_from(tag_id) {
|
||||
Ok(PanicTagId::NullTerminatedString) => {
|
||||
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);
|
||||
}
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roc_list_construction() {
|
||||
let list = RocList::from_slice(&[1i64; 23]);
|
||||
|
|
|
@ -481,8 +481,12 @@ mod gen_num {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn f64_round_old() {
|
||||
fn f64_round() {
|
||||
assert_evals_to!("Num.round 3.6", 4, i64);
|
||||
assert_evals_to!("Num.round 3.4", 3, i64);
|
||||
assert_evals_to!("Num.round 2.5", 3, i64);
|
||||
assert_evals_to!("Num.round -2.3", -2, i64);
|
||||
assert_evals_to!("Num.round -2.5", -3, i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1620,4 +1624,158 @@ mod gen_num {
|
|||
// overflow
|
||||
assert_evals_to!("Num.isMultipleOf -9223372036854775808 -1", true, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u16_clearly_out_of_bounds() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
bytes = Str.toUtf8 "hello"
|
||||
when Num.bytesToU16 bytes 234 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
1,
|
||||
u16
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u16_subtly_out_of_bounds() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
bytes = Str.toUtf8 "hello"
|
||||
when Num.bytesToU16 bytes 4 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
1,
|
||||
u16
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u32_clearly_out_of_bounds() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
bytes = Str.toUtf8 "hello"
|
||||
when Num.bytesToU32 bytes 234 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
1,
|
||||
u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u32_subtly_out_of_bounds() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
bytes = Str.toUtf8 "hello"
|
||||
when Num.bytesToU32 bytes 2 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
1,
|
||||
u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u16_max_u8s() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.bytesToU16 [255, 255] 0 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
65535,
|
||||
u16
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u16_min_u8s() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.bytesToU16 [0, 0] 0 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
0,
|
||||
u16
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u16_random_u8s() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.bytesToU16 [164, 215] 0 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
55_204,
|
||||
u16
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u32_min_u8s() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.bytesToU32 [0, 0, 0, 0] 0 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
0,
|
||||
u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u32_max_u8s() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.bytesToU32 [255, 255, 255, 255] 0 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
4_294_967_295,
|
||||
u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_to_u32_random_u8s() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.bytesToU32 [252, 124, 128, 121] 0 is
|
||||
Ok v -> v
|
||||
Err OutOfBounds -> 1
|
||||
"#
|
||||
),
|
||||
2_038_463_740,
|
||||
u32
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2393,7 +2393,6 @@ fn call_invalid_layout() {
|
|||
3,
|
||||
i64,
|
||||
|x| x,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
@ -2657,3 +2656,125 @@ fn lambda_set_enum_byte_byte() {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_walk_until() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1576
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
|
||||
satisfyA : {} -> List {}
|
||||
satisfyA = \_ -> []
|
||||
|
||||
oneOfResult =
|
||||
List.walkUntil [ satisfyA ] (\_, _ -> Stop []) []
|
||||
|
||||
main =
|
||||
when oneOfResult is
|
||||
_ -> 32
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_literal_not_specialized_with_annotation() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1600
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main =
|
||||
satisfy : (U8 -> Str) -> Str
|
||||
satisfy = \_ -> "foo"
|
||||
|
||||
myEq : a, a -> Str
|
||||
myEq = \_, _ -> "bar"
|
||||
|
||||
p1 : Num * -> Str
|
||||
p1 = (\u -> myEq u 64)
|
||||
|
||||
when satisfy p1 is
|
||||
_ -> 32
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_literal_not_specialized_no_annotation() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1600
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main =
|
||||
satisfy : (U8 -> Str) -> Str
|
||||
satisfy = \_ -> "foo"
|
||||
|
||||
myEq : a, a -> Str
|
||||
myEq = \_, _ -> "bar"
|
||||
|
||||
p1 = (\u -> myEq u 64)
|
||||
|
||||
when satisfy p1 is
|
||||
_ -> 32
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_tvar_when_capture_is_unused() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1585
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main : I64
|
||||
main =
|
||||
r : Bool
|
||||
r = False
|
||||
|
||||
p1 = (\_ -> r == (1 == 1))
|
||||
oneOfResult = List.map [p1] (\p -> p Green)
|
||||
|
||||
when oneOfResult is
|
||||
_ -> 32
|
||||
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Roc failed with message: ")]
|
||||
fn value_not_exposed_hits_panic() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main : I64
|
||||
main =
|
||||
Str.toInt 32
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ pub fn helper<'a>(
|
|||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
leak: bool,
|
||||
is_gen_test: bool,
|
||||
ignore_problems: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, String, Library) {
|
||||
|
@ -171,13 +171,6 @@ pub fn helper<'a>(
|
|||
let builder = context.create_builder();
|
||||
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app");
|
||||
|
||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||
// platform to provide them.
|
||||
add_default_roc_externs(context, &module, &builder, ptr_bytes);
|
||||
|
||||
// strip Zig debug stuff
|
||||
module.strip_debug_info();
|
||||
|
||||
let opt_level = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
} else {
|
||||
|
@ -219,11 +212,18 @@ pub fn helper<'a>(
|
|||
interns,
|
||||
module,
|
||||
ptr_bytes,
|
||||
leak,
|
||||
is_gen_test,
|
||||
// important! we don't want any procedures to get the C calling convention
|
||||
exposed_to_host: MutSet::default(),
|
||||
};
|
||||
|
||||
// strip Zig debug stuff
|
||||
module.strip_debug_info();
|
||||
|
||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||
// platform to provide them.
|
||||
add_default_roc_externs(&env);
|
||||
|
||||
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
||||
&env,
|
||||
opt_level,
|
||||
|
@ -233,6 +233,9 @@ pub fn helper<'a>(
|
|||
|
||||
env.dibuilder.finalize();
|
||||
|
||||
// strip all debug info: we don't use it at the moment and causes weird validation issues
|
||||
module.strip_debug_info();
|
||||
|
||||
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
|
@ -260,7 +263,7 @@ pub fn helper<'a>(
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! assert_llvm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $ignore_problems:expr) => {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use roc_gen_llvm::run_jit_function;
|
||||
|
@ -271,8 +274,15 @@ macro_rules! assert_llvm_evals_to {
|
|||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, $ignore_problems, &context);
|
||||
let is_gen_test = true;
|
||||
let (main_fn_name, errors, lib) = $crate::helpers::eval::helper(
|
||||
&arena,
|
||||
$src,
|
||||
stdlib,
|
||||
is_gen_test,
|
||||
$ignore_problems,
|
||||
&context,
|
||||
);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
|
@ -284,7 +294,7 @@ macro_rules! assert_llvm_evals_to {
|
|||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -296,20 +306,7 @@ macro_rules! assert_evals_to {
|
|||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
assert_evals_to!($src, $expected, $ty, $transform, true);
|
||||
}
|
||||
};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
||||
// Run un-optimized tests, and then optimized tests, in separate scopes.
|
||||
// These each rebuild everything from scratch, starting with
|
||||
// parsing the source, so that there's no chance their passing
|
||||
// or failing depends on leftover state from the previous one.
|
||||
{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak, false);
|
||||
}
|
||||
{
|
||||
// NOTE at the moment, the optimized tests do the same thing
|
||||
// assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -322,10 +319,10 @@ macro_rules! assert_non_opt_evals_to {
|
|||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
}
|
||||
};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.25 (#Attr.2, #Attr.3):
|
||||
let Test.15 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Test.15;
|
||||
let Test.14 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Test.14;
|
||||
|
||||
procedure Num.26 (#Attr.2, #Attr.3):
|
||||
let Test.12 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
|
@ -8,13 +8,13 @@ procedure Num.26 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.1 (Test.2, Test.3):
|
||||
joinpoint Test.7 Test.2 Test.3:
|
||||
let Test.17 = 0i64;
|
||||
let Test.18 = lowlevel Eq Test.17 Test.2;
|
||||
if Test.18 then
|
||||
let Test.15 = 0i64;
|
||||
let Test.16 = lowlevel Eq Test.15 Test.2;
|
||||
if Test.16 then
|
||||
ret Test.3;
|
||||
else
|
||||
let Test.14 = 1i64;
|
||||
let Test.10 = CallByName Num.25 Test.2 Test.14;
|
||||
let Test.13 = 1i64;
|
||||
let Test.10 = CallByName Num.25 Test.2 Test.13;
|
||||
let Test.11 = CallByName Num.26 Test.2 Test.3;
|
||||
jump Test.7 Test.10 Test.11;
|
||||
in
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
procedure List.7 (#Attr.2):
|
||||
let Test.7 = lowlevel ListLen #Attr.2;
|
||||
ret Test.7;
|
||||
let Test.6 = lowlevel ListLen #Attr.2;
|
||||
ret Test.6;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.5 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.5;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.14 = 1i64;
|
||||
let Test.15 = 2i64;
|
||||
let Test.1 = Array [Test.14, Test.15];
|
||||
let Test.11 = 5i64;
|
||||
let Test.12 = 4i64;
|
||||
invoke Test.8 = CallByName Num.24 Test.11 Test.12 catch
|
||||
dec Test.1;
|
||||
unreachable;
|
||||
let Test.9 = 3i64;
|
||||
invoke Test.3 = CallByName Num.24 Test.8 Test.9 catch
|
||||
dec Test.1;
|
||||
unreachable;
|
||||
let Test.11 = 1i64;
|
||||
let Test.12 = 2i64;
|
||||
let Test.1 = Array [Test.11, Test.12];
|
||||
let Test.9 = 5i64;
|
||||
let Test.10 = 4i64;
|
||||
let Test.7 = CallByName Num.24 Test.9 Test.10;
|
||||
let Test.8 = 3i64;
|
||||
let Test.3 = CallByName Num.24 Test.7 Test.8;
|
||||
let Test.4 = CallByName List.7 Test.1;
|
||||
dec Test.1;
|
||||
let Test.2 = CallByName Num.24 Test.3 Test.4;
|
||||
|
|
|
@ -3,16 +3,16 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.6;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.12 = 41i64;
|
||||
let Test.1 = Just Test.12;
|
||||
let Test.9 = 0i64;
|
||||
let Test.10 = GetTagId Test.1;
|
||||
let Test.11 = lowlevel Eq Test.9 Test.10;
|
||||
if Test.11 then
|
||||
let Test.11 = 41i64;
|
||||
let Test.1 = Just Test.11;
|
||||
let Test.8 = 0i64;
|
||||
let Test.9 = GetTagId Test.1;
|
||||
let Test.10 = lowlevel Eq Test.8 Test.9;
|
||||
if Test.10 then
|
||||
let Test.3 = UnionAtIndex (Id 0) (Index 0) Test.1;
|
||||
let Test.5 = 1i64;
|
||||
let Test.4 = CallByName Num.24 Test.3 Test.5;
|
||||
ret Test.4;
|
||||
else
|
||||
let Test.8 = 1i64;
|
||||
ret Test.8;
|
||||
let Test.7 = 1i64;
|
||||
ret Test.7;
|
||||
|
|
|
@ -3,18 +3,18 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.10;
|
||||
|
||||
procedure Test.3 (Test.5):
|
||||
let Test.18 = 1i64;
|
||||
let Test.19 = GetTagId Test.5;
|
||||
let Test.20 = lowlevel Eq Test.18 Test.19;
|
||||
if Test.20 then
|
||||
let Test.13 = 0i64;
|
||||
ret Test.13;
|
||||
let Test.16 = 1i64;
|
||||
let Test.17 = GetTagId Test.5;
|
||||
let Test.18 = lowlevel Eq Test.16 Test.17;
|
||||
if Test.18 then
|
||||
let Test.12 = 0i64;
|
||||
ret Test.12;
|
||||
else
|
||||
let Test.6 = UnionAtIndex (Id 0) (Index 1) Test.5;
|
||||
let Test.15 = 1i64;
|
||||
let Test.16 = CallByName Test.3 Test.6;
|
||||
let Test.14 = CallByName Num.24 Test.15 Test.16;
|
||||
ret Test.14;
|
||||
let Test.14 = 1i64;
|
||||
let Test.15 = CallByName Test.3 Test.6;
|
||||
let Test.13 = CallByName Num.24 Test.14 Test.15;
|
||||
ret Test.13;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 = Nil ;
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Test.23 = lowlevel ListLen #Attr.2;
|
||||
let Test.21 = lowlevel NumLt #Attr.3 Test.23;
|
||||
if Test.21 then
|
||||
let Test.22 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Test.22;
|
||||
let Test.22 = lowlevel ListLen #Attr.2;
|
||||
let Test.20 = lowlevel NumLt #Attr.3 Test.22;
|
||||
if Test.20 then
|
||||
let Test.21 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Test.21;
|
||||
else
|
||||
ret #Attr.2;
|
||||
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.10 = lowlevel ListLen #Attr.2;
|
||||
ret Test.10;
|
||||
let Test.9 = lowlevel ListLen #Attr.2;
|
||||
ret Test.9;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.7 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.7;
|
||||
|
||||
procedure Test.1 ():
|
||||
let Test.12 = 1i64;
|
||||
let Test.13 = 2i64;
|
||||
let Test.14 = 3i64;
|
||||
let Test.11 = Array [Test.12, Test.13, Test.14];
|
||||
ret Test.11;
|
||||
let Test.11 = 1i64;
|
||||
let Test.12 = 2i64;
|
||||
let Test.13 = 3i64;
|
||||
let Test.10 = Array [Test.11, Test.12, Test.13];
|
||||
ret Test.10;
|
||||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.17 = 0i64;
|
||||
let Test.18 = 0i64;
|
||||
let Test.19 = 0i64;
|
||||
let Test.17 = CallByName List.4 Test.3 Test.18 Test.19;
|
||||
ret Test.17;
|
||||
let Test.16 = CallByName List.4 Test.3 Test.17 Test.18;
|
||||
ret Test.16;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.16 = CallByName Test.1;
|
||||
let Test.15 = CallByName Test.2 Test.16;
|
||||
let Test.5 = CallByName List.7 Test.15;
|
||||
dec Test.15;
|
||||
let Test.9 = CallByName Test.1;
|
||||
let Test.6 = CallByName List.7 Test.9;
|
||||
dec Test.9;
|
||||
let Test.15 = CallByName Test.1;
|
||||
let Test.14 = CallByName Test.2 Test.15;
|
||||
let Test.5 = CallByName List.7 Test.14;
|
||||
dec Test.14;
|
||||
let Test.8 = CallByName Test.1;
|
||||
let Test.6 = CallByName List.7 Test.8;
|
||||
dec Test.8;
|
||||
let Test.4 = CallByName Num.24 Test.5 Test.6;
|
||||
ret Test.4;
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
procedure List.7 (#Attr.2):
|
||||
let Test.7 = lowlevel ListLen #Attr.2;
|
||||
ret Test.7;
|
||||
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.8 = lowlevel ListLen #Attr.2;
|
||||
ret Test.8;
|
||||
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.9 = lowlevel ListLen #Attr.2;
|
||||
ret Test.9;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.6 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.6;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 = 1i64;
|
||||
let Test.12 = 2i64;
|
||||
let Test.13 = 3i64;
|
||||
let Test.1 = Array [Test.11, Test.12, Test.13];
|
||||
let Test.10 = 1f64;
|
||||
let Test.2 = Array [Test.10];
|
||||
let Test.10 = 1i64;
|
||||
let Test.11 = 2i64;
|
||||
let Test.12 = 3i64;
|
||||
let Test.1 = Array [Test.10, Test.11, Test.12];
|
||||
let Test.9 = 1f64;
|
||||
let Test.2 = Array [Test.9];
|
||||
let Test.4 = CallByName List.7 Test.1;
|
||||
dec Test.1;
|
||||
let Test.5 = CallByName List.7 Test.2;
|
||||
|
|
|
@ -3,28 +3,28 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.21 = 41i64;
|
||||
let Test.20 = Just Test.21;
|
||||
let Test.2 = Just Test.20;
|
||||
joinpoint Test.17:
|
||||
let Test.10 = 1i64;
|
||||
ret Test.10;
|
||||
let Test.20 = 41i64;
|
||||
let Test.19 = Just Test.20;
|
||||
let Test.2 = Just Test.19;
|
||||
joinpoint Test.16:
|
||||
let Test.9 = 1i64;
|
||||
ret Test.9;
|
||||
in
|
||||
let Test.15 = 0i64;
|
||||
let Test.16 = GetTagId Test.2;
|
||||
let Test.19 = lowlevel Eq Test.15 Test.16;
|
||||
if Test.19 then
|
||||
let Test.12 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.13 = 0i64;
|
||||
let Test.14 = GetTagId Test.12;
|
||||
let Test.18 = lowlevel Eq Test.13 Test.14;
|
||||
if Test.18 then
|
||||
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.11;
|
||||
let Test.14 = 0i64;
|
||||
let Test.15 = GetTagId Test.2;
|
||||
let Test.18 = lowlevel Eq Test.14 Test.15;
|
||||
if Test.18 then
|
||||
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.12 = 0i64;
|
||||
let Test.13 = GetTagId Test.11;
|
||||
let Test.17 = lowlevel Eq Test.12 Test.13;
|
||||
if Test.17 then
|
||||
let Test.10 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.10;
|
||||
let Test.7 = 1i64;
|
||||
let Test.6 = CallByName Num.24 Test.5 Test.7;
|
||||
ret Test.6;
|
||||
else
|
||||
jump Test.17;
|
||||
jump Test.16;
|
||||
else
|
||||
jump Test.17;
|
||||
jump Test.16;
|
||||
|
|
|
@ -3,10 +3,10 @@ procedure Num.26 (#Attr.2, #Attr.3):
|
|||
ret Test.17;
|
||||
|
||||
procedure Test.1 (Test.6):
|
||||
let Test.25 = StructAtIndex 1 Test.6;
|
||||
let Test.26 = false;
|
||||
let Test.27 = lowlevel Eq Test.26 Test.25;
|
||||
if Test.27 then
|
||||
let Test.22 = StructAtIndex 1 Test.6;
|
||||
let Test.23 = false;
|
||||
let Test.24 = lowlevel Eq Test.23 Test.22;
|
||||
if Test.24 then
|
||||
let Test.8 = StructAtIndex 0 Test.6;
|
||||
ret Test.8;
|
||||
else
|
||||
|
@ -14,9 +14,9 @@ procedure Test.1 (Test.6):
|
|||
ret Test.10;
|
||||
|
||||
procedure Test.1 (Test.6):
|
||||
let Test.36 = false;
|
||||
let Test.37 = lowlevel Eq Test.36 Test.6;
|
||||
if Test.37 then
|
||||
let Test.33 = false;
|
||||
let Test.34 = lowlevel Eq Test.33 Test.6;
|
||||
if Test.34 then
|
||||
let Test.8 = 3i64;
|
||||
ret Test.8;
|
||||
else
|
||||
|
@ -24,19 +24,19 @@ procedure Test.1 (Test.6):
|
|||
ret Test.10;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.40 = true;
|
||||
let Test.5 = CallByName Test.1 Test.40;
|
||||
let Test.38 = false;
|
||||
let Test.3 = CallByName Test.1 Test.38;
|
||||
let Test.31 = 11i64;
|
||||
let Test.32 = true;
|
||||
let Test.30 = Struct {Test.31, Test.32};
|
||||
let Test.4 = CallByName Test.1 Test.30;
|
||||
let Test.28 = 7i64;
|
||||
let Test.29 = false;
|
||||
let Test.22 = Struct {Test.28, Test.29};
|
||||
let Test.2 = CallByName Test.1 Test.22;
|
||||
let Test.19 = CallByName Num.26 Test.2 Test.3;
|
||||
let Test.16 = CallByName Num.26 Test.19 Test.4;
|
||||
let Test.37 = true;
|
||||
let Test.5 = CallByName Test.1 Test.37;
|
||||
let Test.35 = false;
|
||||
let Test.3 = CallByName Test.1 Test.35;
|
||||
let Test.28 = 11i64;
|
||||
let Test.29 = true;
|
||||
let Test.27 = Struct {Test.28, Test.29};
|
||||
let Test.4 = CallByName Test.1 Test.27;
|
||||
let Test.25 = 7i64;
|
||||
let Test.26 = false;
|
||||
let Test.19 = Struct {Test.25, Test.26};
|
||||
let Test.2 = CallByName Test.1 Test.19;
|
||||
let Test.18 = CallByName Num.26 Test.2 Test.3;
|
||||
let Test.16 = CallByName Num.26 Test.18 Test.4;
|
||||
let Test.15 = CallByName Num.26 Test.16 Test.5;
|
||||
ret Test.15;
|
||||
|
|
|
@ -3,30 +3,28 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.19;
|
||||
|
||||
procedure Num.25 (#Attr.2, #Attr.3):
|
||||
let Test.23 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Test.23;
|
||||
let Test.22 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Test.22;
|
||||
|
||||
procedure Num.27 (#Attr.2, #Attr.3):
|
||||
let Test.28 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Test.28;
|
||||
let Test.26 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Test.26;
|
||||
|
||||
procedure Test.1 (Test.2, Test.3, Test.4):
|
||||
joinpoint Test.12 Test.2 Test.3 Test.4:
|
||||
let Test.14 = CallByName Num.27 Test.3 Test.4;
|
||||
if Test.14 then
|
||||
dec Test.2;
|
||||
let Test.27 = Array [];
|
||||
let Test.26 = 0i64;
|
||||
let Test.25 = Struct {Test.26, Test.27};
|
||||
let Test.5 = StructAtIndex 0 Test.25;
|
||||
let Test.6 = StructAtIndex 1 Test.25;
|
||||
let Test.22 = 1i64;
|
||||
let Test.21 = CallByName Num.25 Test.5 Test.22;
|
||||
let Test.16 = CallByName Test.1 Test.6 Test.3 Test.21;
|
||||
let Test.25 = Array [];
|
||||
let Test.24 = 0i64;
|
||||
let Test.23 = Struct {Test.24, Test.25};
|
||||
let Test.5 = StructAtIndex 0 Test.23;
|
||||
let Test.6 = StructAtIndex 1 Test.23;
|
||||
let Test.21 = 1i64;
|
||||
let Test.20 = CallByName Num.25 Test.5 Test.21;
|
||||
let Test.16 = CallByName Test.1 Test.6 Test.3 Test.20;
|
||||
let Test.18 = 1i64;
|
||||
invoke Test.17 = CallByName Num.24 Test.5 Test.18 catch
|
||||
dec Test.16;
|
||||
unreachable;
|
||||
let Test.17 = CallByName Num.24 Test.5 Test.18;
|
||||
jump Test.12 Test.16 Test.17 Test.4;
|
||||
else
|
||||
ret Test.2;
|
||||
|
|
|
@ -10,8 +10,8 @@ procedure Test.1 (Test.4):
|
|||
ret Test.7;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.10 = 4i64;
|
||||
let Test.11 = 9i64;
|
||||
let Test.6 = Struct {Test.10, Test.11};
|
||||
let Test.9 = 4i64;
|
||||
let Test.10 = 9i64;
|
||||
let Test.6 = Struct {Test.9, Test.10};
|
||||
let Test.5 = CallByName Test.1 Test.6;
|
||||
ret Test.5;
|
||||
|
|
|
@ -9,6 +9,6 @@ procedure Test.1 (Test.4):
|
|||
ret Test.7;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.10 = 9i64;
|
||||
let Test.5 = CallByName Test.1 Test.10;
|
||||
let Test.9 = 9i64;
|
||||
let Test.5 = CallByName Test.1 Test.9;
|
||||
ret Test.5;
|
||||
|
|
|
@ -9,8 +9,8 @@ procedure Test.1 (Test.2):
|
|||
ret Test.7;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.10 = 4i64;
|
||||
let Test.11 = 9i64;
|
||||
let Test.6 = Struct {Test.10, Test.11};
|
||||
let Test.9 = 4i64;
|
||||
let Test.10 = 9i64;
|
||||
let Test.6 = Struct {Test.9, Test.10};
|
||||
let Test.5 = CallByName Test.1 Test.6;
|
||||
ret Test.5;
|
||||
|
|
|
@ -8,6 +8,6 @@ procedure Test.1 (Test.2):
|
|||
ret Test.7;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.10 = 9i64;
|
||||
let Test.5 = CallByName Test.1 Test.10;
|
||||
let Test.9 = 9i64;
|
||||
let Test.5 = CallByName Test.1 Test.9;
|
||||
ret Test.5;
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.25 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.25;
|
||||
let Test.24 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.24;
|
||||
|
||||
procedure Num.26 (#Attr.2, #Attr.3):
|
||||
let Test.19 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Test.19;
|
||||
|
||||
procedure Test.1 ():
|
||||
let Test.27 = 1i64;
|
||||
ret Test.27;
|
||||
let Test.25 = 1i64;
|
||||
ret Test.25;
|
||||
|
||||
procedure Test.2 ():
|
||||
let Test.21 = 2i64;
|
||||
ret Test.21;
|
||||
let Test.20 = 2i64;
|
||||
ret Test.20;
|
||||
|
||||
procedure Test.3 (Test.6):
|
||||
let Test.24 = CallByName Test.1;
|
||||
let Test.23 = CallByName Num.24 Test.6 Test.24;
|
||||
ret Test.23;
|
||||
let Test.23 = CallByName Test.1;
|
||||
let Test.22 = CallByName Num.24 Test.6 Test.23;
|
||||
ret Test.22;
|
||||
|
||||
procedure Test.4 (Test.7):
|
||||
let Test.18 = CallByName Test.2;
|
||||
|
@ -34,8 +34,8 @@ procedure Test.0 ():
|
|||
let Test.11 = CallByName Test.5 Test.12 Test.13;
|
||||
ret Test.11;
|
||||
in
|
||||
let Test.22 = true;
|
||||
if Test.22 then
|
||||
let Test.21 = true;
|
||||
if Test.21 then
|
||||
let Test.3 = Struct {};
|
||||
jump Test.16 Test.3;
|
||||
else
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.29 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.29;
|
||||
let Test.28 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.28;
|
||||
|
||||
procedure Num.26 (#Attr.2, #Attr.3):
|
||||
let Test.25 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
|
@ -23,8 +23,8 @@ procedure Test.1 (Test.2, Test.3):
|
|||
|
||||
procedure Test.7 (Test.10, #Attr.12):
|
||||
let Test.4 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
let Test.28 = CallByName Num.24 Test.10 Test.4;
|
||||
ret Test.28;
|
||||
let Test.27 = CallByName Num.24 Test.10 Test.4;
|
||||
ret Test.27;
|
||||
|
||||
procedure Test.8 (Test.11, #Attr.12):
|
||||
let Test.6 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||
|
@ -44,8 +44,8 @@ procedure Test.0 ():
|
|||
let Test.13 = CallByName Test.1 Test.14 Test.15;
|
||||
ret Test.13;
|
||||
in
|
||||
let Test.27 = true;
|
||||
if Test.27 then
|
||||
let Test.26 = true;
|
||||
if Test.26 then
|
||||
let Test.7 = ClosureTag(Test.7) Test.4;
|
||||
jump Test.22 Test.7;
|
||||
else
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.25 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.25;
|
||||
let Test.24 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.24;
|
||||
|
||||
procedure Num.26 (#Attr.2, #Attr.3):
|
||||
let Test.21 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
|
@ -8,8 +8,8 @@ procedure Num.26 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.6 (Test.8, #Attr.12):
|
||||
let Test.4 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
let Test.24 = CallByName Num.24 Test.8 Test.4;
|
||||
ret Test.24;
|
||||
let Test.23 = CallByName Num.24 Test.8 Test.4;
|
||||
ret Test.23;
|
||||
|
||||
procedure Test.7 (Test.9, #Attr.12):
|
||||
let Test.5 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
|
@ -35,8 +35,8 @@ procedure Test.0 ():
|
|||
jump Test.15 Test.17;
|
||||
|
||||
in
|
||||
let Test.23 = true;
|
||||
if Test.23 then
|
||||
let Test.22 = true;
|
||||
if Test.22 then
|
||||
let Test.6 = ClosureTag(Test.6) Test.4;
|
||||
jump Test.19 Test.6;
|
||||
else
|
||||
|
|
|
@ -3,28 +3,28 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.21 = 41i64;
|
||||
let Test.20 = Just Test.21;
|
||||
let Test.2 = Just Test.20;
|
||||
joinpoint Test.17:
|
||||
let Test.10 = 1i64;
|
||||
ret Test.10;
|
||||
let Test.20 = 41i64;
|
||||
let Test.19 = Just Test.20;
|
||||
let Test.2 = Just Test.19;
|
||||
joinpoint Test.16:
|
||||
let Test.9 = 1i64;
|
||||
ret Test.9;
|
||||
in
|
||||
let Test.15 = 0i64;
|
||||
let Test.16 = GetTagId Test.2;
|
||||
let Test.19 = lowlevel Eq Test.15 Test.16;
|
||||
if Test.19 then
|
||||
let Test.12 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.13 = 0i64;
|
||||
let Test.14 = GetTagId Test.12;
|
||||
let Test.18 = lowlevel Eq Test.13 Test.14;
|
||||
if Test.18 then
|
||||
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.11;
|
||||
let Test.14 = 0i64;
|
||||
let Test.15 = GetTagId Test.2;
|
||||
let Test.18 = lowlevel Eq Test.14 Test.15;
|
||||
if Test.18 then
|
||||
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.12 = 0i64;
|
||||
let Test.13 = GetTagId Test.11;
|
||||
let Test.17 = lowlevel Eq Test.12 Test.13;
|
||||
if Test.17 then
|
||||
let Test.10 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.10;
|
||||
let Test.7 = 1i64;
|
||||
let Test.6 = CallByName Num.24 Test.5 Test.7;
|
||||
ret Test.6;
|
||||
else
|
||||
jump Test.17;
|
||||
jump Test.16;
|
||||
else
|
||||
jump Test.17;
|
||||
jump Test.16;
|
||||
|
|
|
@ -3,7 +3,7 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.5;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.7 = 2i64;
|
||||
let Test.6 = 2i64;
|
||||
let Test.4 = 3i64;
|
||||
let Test.3 = CallByName Num.24 Test.7 Test.4;
|
||||
let Test.3 = CallByName Num.24 Test.6 Test.4;
|
||||
ret Test.3;
|
||||
|
|
|
@ -3,26 +3,26 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.7;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.17 = 3i64;
|
||||
let Test.16 = 2i64;
|
||||
let Test.4 = Struct {Test.16, Test.17};
|
||||
joinpoint Test.13:
|
||||
let Test.16 = 3i64;
|
||||
let Test.15 = 2i64;
|
||||
let Test.4 = Struct {Test.15, Test.16};
|
||||
joinpoint Test.12:
|
||||
let Test.2 = StructAtIndex 0 Test.4;
|
||||
let Test.3 = StructAtIndex 1 Test.4;
|
||||
let Test.6 = CallByName Num.24 Test.2 Test.3;
|
||||
ret Test.6;
|
||||
in
|
||||
let Test.11 = StructAtIndex 1 Test.4;
|
||||
let Test.12 = 3i64;
|
||||
let Test.15 = lowlevel Eq Test.12 Test.11;
|
||||
if Test.15 then
|
||||
let Test.9 = StructAtIndex 0 Test.4;
|
||||
let Test.10 = 4i64;
|
||||
let Test.14 = lowlevel Eq Test.10 Test.9;
|
||||
if Test.14 then
|
||||
let Test.10 = StructAtIndex 1 Test.4;
|
||||
let Test.11 = 3i64;
|
||||
let Test.14 = lowlevel Eq Test.11 Test.10;
|
||||
if Test.14 then
|
||||
let Test.8 = StructAtIndex 0 Test.4;
|
||||
let Test.9 = 4i64;
|
||||
let Test.13 = lowlevel Eq Test.9 Test.8;
|
||||
if Test.13 then
|
||||
let Test.5 = 9i64;
|
||||
ret Test.5;
|
||||
else
|
||||
jump Test.13;
|
||||
jump Test.12;
|
||||
else
|
||||
jump Test.13;
|
||||
jump Test.12;
|
||||
|
|
|
@ -521,6 +521,16 @@ pub fn u8_type() -> SolvedType {
|
|||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn u16_type() -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_U16,
|
||||
vec![],
|
||||
vec![],
|
||||
Box::new(int_alias_content(unsigned16_type())),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn binary64_type() -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
|
|
|
@ -327,6 +327,14 @@ fn subs_fmt_desc(this: &Descriptor, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
|||
write!(f, " m: {:?}", &this.mark)
|
||||
}
|
||||
|
||||
pub struct SubsFmtContent<'a>(pub &'a Content, pub &'a Subs);
|
||||
|
||||
impl<'a> fmt::Debug for SubsFmtContent<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
subs_fmt_content(self.0, self.1, f)
|
||||
}
|
||||
}
|
||||
|
||||
fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match this {
|
||||
Content::FlexVar(name) => write!(f, "Flex({:?})", name),
|
||||
|
@ -337,12 +345,22 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
|||
} => write!(f, "Recursion({:?}, {:?})", structure, opt_name),
|
||||
Content::Structure(flat_type) => subs_fmt_flat_type(flat_type, subs, f),
|
||||
Content::Alias(name, arguments, actual) => {
|
||||
write!(f, "Alias({:?}, {:?}, {:?})", name, arguments, actual)
|
||||
let slice = subs.get_subs_slice(*arguments.variables().as_subs_slice());
|
||||
|
||||
write!(f, "Alias({:?}, {:?}, {:?})", name, slice, actual)
|
||||
}
|
||||
Content::Error => write!(f, "Error"),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SubsFmtFlatType<'a>(pub &'a FlatType, pub &'a Subs);
|
||||
|
||||
impl<'a> fmt::Debug for SubsFmtFlatType<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
subs_fmt_flat_type(self.0, self.1, f)
|
||||
}
|
||||
}
|
||||
|
||||
fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match this {
|
||||
FlatType::Apply(name, arguments) => {
|
||||
|
@ -354,7 +372,21 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f
|
|||
let slice = subs.get_subs_slice(*arguments.as_subs_slice());
|
||||
write!(f, "Func({:?}, {:?}, {:?})", slice, lambda_set, result)
|
||||
}
|
||||
FlatType::Record(_, _) => todo!(),
|
||||
FlatType::Record(fields, ext) => {
|
||||
write!(f, "{{ ")?;
|
||||
|
||||
let (it, new_ext) = fields.sorted_iterator_and_ext(subs, *ext);
|
||||
for (name, content) in it {
|
||||
let separator = match content {
|
||||
RecordField::Optional(_) => '?',
|
||||
RecordField::Required(_) => ':',
|
||||
RecordField::Demanded(_) => ':',
|
||||
};
|
||||
write!(f, "{:?} {} {:?}, ", name, separator, content)?;
|
||||
}
|
||||
|
||||
write!(f, "}}<{:?}>", new_ext)
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) => {
|
||||
write!(f, "[ ")?;
|
||||
|
||||
|
|
|
@ -1076,8 +1076,8 @@ fn unify_flat_type(
|
|||
// any other combination is a mismatch
|
||||
mismatch!(
|
||||
"Trying to unify two flat types that are incompatible: {:?} ~ {:?}",
|
||||
other1,
|
||||
other2
|
||||
roc_types::subs::SubsFmtFlatType(other1, subs),
|
||||
roc_types::subs::SubsFmtFlatType(other2, subs)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,6 +155,10 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
|||
* ...
|
||||
* smart insert: press a shortcut and enter a plain english description of a code snippet you need. Examples: "convert string to list of chars", "sort list of records by field foo descending", "plot this list with date on x-axis"...
|
||||
* After the user has refactored code to be simpler, try finding other places in the code base where the same simplification can be made.
|
||||
* Show most commonly changed settings on first run so new users can quickly customize their experience. Keeping record of changed settings should be opt-in.
|
||||
* Detection of multiple people within same company/team working on same code at the same time (opt-in).
|
||||
* Autocorrect likely typos for stuff like `-<` when not in string.
|
||||
* If multiple functions are available for import, use function were types would match in insetion position.
|
||||
|
||||
#### Autocomplete
|
||||
|
||||
|
@ -236,6 +240,7 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
|||
### Inspiration
|
||||
|
||||
- [Boop](https://github.com/IvanMathy/Boop) scriptable scratchpad for developers. Contains collection of useful conversions: json formatting, url encoding, encode to base64...
|
||||
- [processing](processing.org) Interactive editor, dragging left or right with mouse to change values. Instant results.
|
||||
|
||||
## High performance
|
||||
|
||||
|
@ -243,6 +248,23 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
|||
|
||||
- [10x editor](http://www.10xeditor.com/) IDE/Editor targeted at the professional developer with an emphasis on performance and scalability.
|
||||
|
||||
|
||||
## Positive feedback
|
||||
|
||||
- It's nice to enhance the feeling of reward after completing a task, this increases motivation.
|
||||
- Great for tutorials and the first run of the editor.
|
||||
- Suggestions of occasions for positive feedback:
|
||||
- Being able to compile successfully after starting out with more than X errors.
|
||||
- Making a test succeed after repeated failures.
|
||||
- Positive feedback could be delivered with messages and/or animations. Animations could be with fireworks, flying roc logo birds, sounds...
|
||||
- The intensity of the message/animation could be increased based on the duration/difficulty of the task.
|
||||
- Suggest to search for help or take a break after being stuck on a test/compile errors... for some time. A search could be done for group chats for relevant libraries.
|
||||
|
||||
### Inspiration
|
||||
|
||||
- [Duolingo](https://www.duolingo.com) app to learn languages
|
||||
- [Khan academy](https://www.khanacademy.org/) free quality education for everyone
|
||||
|
||||
## General Thoughts/Ideas
|
||||
|
||||
Thoughts and ideas possibly taken from above inspirations or separate.
|
||||
|
|
|
@ -45,6 +45,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() u8 {
|
||||
|
|
|
@ -45,6 +45,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() u8 {
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
use core::ffi::c_void;
|
||||
use core::mem::MaybeUninit;
|
||||
use libc::c_char;
|
||||
use roc_std::{RocCallResult, RocStr};
|
||||
use std::ffi::CStr;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
|
@ -29,6 +31,19 @@ pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
|||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe 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 fn rust_main() -> isize {
|
||||
let mut call_result: MaybeUninit<RocCallResult<RocStr>> = MaybeUninit::uninit();
|
||||
|
|
|
@ -17,6 +17,12 @@ void roc_dealloc(void* ptr, unsigned int alignment) {
|
|||
free(ptr);
|
||||
}
|
||||
|
||||
void roc_panic(void* ptr, unsigned int alignment) {
|
||||
char* msg = (char *)ptr;
|
||||
fprintf(stderr, "Application crashed with message\n\n %s\n\nShutting down\n", msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
struct RocStr {
|
||||
char* bytes;
|
||||
size_t len;
|
||||
|
|
|
@ -35,6 +35,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
|
|
@ -38,6 +38,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
// warning! the array is currently stack-allocated so don't make this too big
|
||||
const NUM_NUMS = 100;
|
||||
|
||||
|
|
|
@ -91,12 +91,16 @@ impl<T> RocList<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_storage_ptr(&self) -> *const isize {
|
||||
let ptr = self.elements as *const isize;
|
||||
fn get_storage_ptr_help(elements: *mut T) -> *mut isize {
|
||||
let ptr = elements as *mut isize;
|
||||
|
||||
unsafe { ptr.offset(-1) }
|
||||
}
|
||||
|
||||
fn get_storage_ptr(&self) -> *const isize {
|
||||
Self::get_storage_ptr_help(self.elements)
|
||||
}
|
||||
|
||||
fn get_storage_ptr_mut(&mut self) -> *mut isize {
|
||||
self.get_storage_ptr() as *mut isize
|
||||
}
|
||||
|
@ -278,6 +282,103 @@ impl<T> RocList<T> {
|
|||
fn align_of_storage_ptr() -> u32 {
|
||||
mem::align_of::<T>().max(mem::align_of::<usize>()) as u32
|
||||
}
|
||||
|
||||
unsafe fn drop_pointer_to_first_argument(ptr: *mut T) {
|
||||
let storage_ptr = Self::get_storage_ptr_help(ptr);
|
||||
let storage_val = *storage_ptr;
|
||||
|
||||
if storage_val == REFCOUNT_1 || storage_val > 0 {
|
||||
// If we have no more references, or if this was unique,
|
||||
// deallocate it.
|
||||
roc_dealloc(storage_ptr as *mut c_void, Self::align_of_storage_ptr());
|
||||
} else if storage_val < 0 {
|
||||
// If this still has more references, decrement one.
|
||||
*storage_ptr = storage_val - 1;
|
||||
}
|
||||
|
||||
// The only remaining option is that this is in readonly memory,
|
||||
// in which case we shouldn't attempt to do anything to it.
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a RocList<T> {
|
||||
type Item = &'a T;
|
||||
|
||||
type IntoIter = <&'a [T] as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.as_slice().iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for RocList<T> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
let remaining = self.len();
|
||||
|
||||
let buf = unsafe { NonNull::new_unchecked(self.elements as _) };
|
||||
let ptr = self.elements;
|
||||
|
||||
IntoIter {
|
||||
buf,
|
||||
ptr,
|
||||
remaining,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use core::ptr::NonNull;
|
||||
|
||||
pub struct IntoIter<T> {
|
||||
buf: NonNull<T>,
|
||||
// pub cap: usize,
|
||||
ptr: *const T,
|
||||
remaining: usize,
|
||||
}
|
||||
|
||||
impl<T> Iterator for IntoIter<T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
next_help(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn next_help<T>(this: &mut IntoIter<T>) -> Option<T> {
|
||||
if this.remaining == 0 {
|
||||
None
|
||||
} else if mem::size_of::<T>() == 0 {
|
||||
// purposefully don't use 'ptr.offset' because for
|
||||
// vectors with 0-size elements this would return the
|
||||
// same pointer.
|
||||
this.remaining -= 1;
|
||||
|
||||
// Make up a value of this ZST.
|
||||
Some(unsafe { mem::zeroed() })
|
||||
} else {
|
||||
let old = this.ptr;
|
||||
this.ptr = unsafe { this.ptr.offset(1) };
|
||||
this.remaining -= 1;
|
||||
|
||||
Some(unsafe { ptr::read(old) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for IntoIter<T> {
|
||||
fn drop(&mut self) {
|
||||
// drop the elements that we have not yet returned.
|
||||
while let Some(item) = next_help(self) {
|
||||
drop(item);
|
||||
}
|
||||
|
||||
// deallocate the whole buffer
|
||||
unsafe {
|
||||
RocList::drop_pointer_to_first_argument(self.buf.as_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for RocList<T> {
|
||||
|
|
|
@ -50,10 +50,7 @@ let
|
|||
|
||||
# lib deps
|
||||
glibc_multi
|
||||
llvmPkgs.libcxx
|
||||
llvmPkgs.libcxxabi
|
||||
libffi
|
||||
libunwind
|
||||
libxml2
|
||||
ncurses
|
||||
zlib
|
||||
|
@ -78,9 +75,6 @@ in pkgs.mkShell {
|
|||
lib.makeLibraryPath ([
|
||||
pkg-config
|
||||
stdenv.cc.cc.lib
|
||||
llvmPkgs.libcxx
|
||||
llvmPkgs.libcxxabi
|
||||
libunwind
|
||||
libffi
|
||||
ncurses
|
||||
zlib
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue