mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-29 09:23:46 +00:00
commit
58fad36f9d
62 changed files with 1247 additions and 455 deletions
|
@ -640,10 +640,13 @@ fn stmt_spec<'a>(
|
||||||
let jpid = env.join_points[id];
|
let jpid = env.join_points[id];
|
||||||
builder.add_jump(block, jpid, argument, ret_type_id)
|
builder.add_jump(block, jpid, argument, ret_type_id)
|
||||||
}
|
}
|
||||||
RuntimeError(_) => {
|
Crash(msg, _) => {
|
||||||
let type_id = layout_spec(env, builder, interner, layout, &WhenRecursive::Unreachable)?;
|
// Model this as a foreign call rather than TERMINATE because
|
||||||
|
// we want ownership of the message.
|
||||||
|
let result_type =
|
||||||
|
layout_spec(env, builder, interner, layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
builder.add_terminate(block, type_id)
|
builder.add_unknown_with(block, &[env.symbols[msg]], result_type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ const math = std.math;
|
||||||
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||||
const RocStr = str.RocStr;
|
const RocStr = str.RocStr;
|
||||||
const WithOverflow = utils.WithOverflow;
|
const WithOverflow = utils.WithOverflow;
|
||||||
const roc_panic = utils.panic;
|
const roc_panic = @import("panic.zig").panic_help;
|
||||||
const U256 = num_.U256;
|
const U256 = num_.U256;
|
||||||
const mul_u128 = num_.mul_u128;
|
const mul_u128 = num_.mul_u128;
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ pub const RocDec = extern struct {
|
||||||
const answer = RocDec.addWithOverflow(self, other);
|
const answer = RocDec.addWithOverflow(self, other);
|
||||||
|
|
||||||
if (answer.has_overflowed) {
|
if (answer.has_overflowed) {
|
||||||
roc_panic("Decimal addition overflowed!", 1);
|
roc_panic("Decimal addition overflowed!", 0);
|
||||||
unreachable;
|
unreachable;
|
||||||
} else {
|
} else {
|
||||||
return answer.value;
|
return answer.value;
|
||||||
|
@ -265,7 +265,7 @@ pub const RocDec = extern struct {
|
||||||
const answer = RocDec.subWithOverflow(self, other);
|
const answer = RocDec.subWithOverflow(self, other);
|
||||||
|
|
||||||
if (answer.has_overflowed) {
|
if (answer.has_overflowed) {
|
||||||
roc_panic("Decimal subtraction overflowed!", 1);
|
roc_panic("Decimal subtraction overflowed!", 0);
|
||||||
unreachable;
|
unreachable;
|
||||||
} else {
|
} else {
|
||||||
return answer.value;
|
return answer.value;
|
||||||
|
@ -329,7 +329,7 @@ pub const RocDec = extern struct {
|
||||||
const answer = RocDec.mulWithOverflow(self, other);
|
const answer = RocDec.mulWithOverflow(self, other);
|
||||||
|
|
||||||
if (answer.has_overflowed) {
|
if (answer.has_overflowed) {
|
||||||
roc_panic("Decimal multiplication overflowed!", 1);
|
roc_panic("Decimal multiplication overflowed!", 0);
|
||||||
unreachable;
|
unreachable;
|
||||||
} else {
|
} else {
|
||||||
return answer.value;
|
return answer.value;
|
||||||
|
|
|
@ -3,6 +3,7 @@ const builtin = @import("builtin");
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
const utils = @import("utils.zig");
|
const utils = @import("utils.zig");
|
||||||
const expect = @import("expect.zig");
|
const expect = @import("expect.zig");
|
||||||
|
const panic_utils = @import("panic.zig");
|
||||||
|
|
||||||
const ROC_BUILTINS = "roc_builtins";
|
const ROC_BUILTINS = "roc_builtins";
|
||||||
const NUM = "num";
|
const NUM = "num";
|
||||||
|
@ -166,7 +167,7 @@ comptime {
|
||||||
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
|
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
|
||||||
exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount");
|
exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount");
|
||||||
|
|
||||||
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
@export(panic_utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
||||||
|
|
||||||
if (builtin.target.cpu.arch != .wasm32) {
|
if (builtin.target.cpu.arch != .wasm32) {
|
||||||
exportUtilsFn(expect.expectFailedStartSharedBuffer, "expect_failed_start_shared_buffer");
|
exportUtilsFn(expect.expectFailedStartSharedBuffer, "expect_failed_start_shared_buffer");
|
||||||
|
|
|
@ -4,7 +4,7 @@ const math = std.math;
|
||||||
const RocList = @import("list.zig").RocList;
|
const RocList = @import("list.zig").RocList;
|
||||||
const RocStr = @import("str.zig").RocStr;
|
const RocStr = @import("str.zig").RocStr;
|
||||||
const WithOverflow = @import("utils.zig").WithOverflow;
|
const WithOverflow = @import("utils.zig").WithOverflow;
|
||||||
const roc_panic = @import("utils.zig").panic;
|
const roc_panic = @import("panic.zig").panic_help;
|
||||||
|
|
||||||
pub fn NumParseResult(comptime T: type) type {
|
pub fn NumParseResult(comptime T: type) type {
|
||||||
// on the roc side we sort by alignment; putting the errorcode last
|
// on the roc side we sort by alignment; putting the errorcode last
|
||||||
|
@ -284,7 +284,7 @@ pub fn exportAddOrPanic(comptime T: type, comptime name: []const u8) void {
|
||||||
fn func(self: T, other: T) callconv(.C) T {
|
fn func(self: T, other: T) callconv(.C) T {
|
||||||
const result = addWithOverflow(T, self, other);
|
const result = addWithOverflow(T, self, other);
|
||||||
if (result.has_overflowed) {
|
if (result.has_overflowed) {
|
||||||
roc_panic("integer addition overflowed!", 1);
|
roc_panic("integer addition overflowed!", 0);
|
||||||
unreachable;
|
unreachable;
|
||||||
} else {
|
} else {
|
||||||
return result.value;
|
return result.value;
|
||||||
|
@ -343,7 +343,7 @@ pub fn exportSubOrPanic(comptime T: type, comptime name: []const u8) void {
|
||||||
fn func(self: T, other: T) callconv(.C) T {
|
fn func(self: T, other: T) callconv(.C) T {
|
||||||
const result = subWithOverflow(T, self, other);
|
const result = subWithOverflow(T, self, other);
|
||||||
if (result.has_overflowed) {
|
if (result.has_overflowed) {
|
||||||
roc_panic("integer subtraction overflowed!", 1);
|
roc_panic("integer subtraction overflowed!", 0);
|
||||||
unreachable;
|
unreachable;
|
||||||
} else {
|
} else {
|
||||||
return result.value;
|
return result.value;
|
||||||
|
@ -451,7 +451,7 @@ pub fn exportMulOrPanic(comptime T: type, comptime W: type, comptime name: []con
|
||||||
fn func(self: T, other: T) callconv(.C) T {
|
fn func(self: T, other: T) callconv(.C) T {
|
||||||
const result = @call(.{ .modifier = always_inline }, mulWithOverflow, .{ T, W, self, other });
|
const result = @call(.{ .modifier = always_inline }, mulWithOverflow, .{ T, W, self, other });
|
||||||
if (result.has_overflowed) {
|
if (result.has_overflowed) {
|
||||||
roc_panic("integer multiplication overflowed!", 1);
|
roc_panic("integer multiplication overflowed!", 0);
|
||||||
unreachable;
|
unreachable;
|
||||||
} else {
|
} else {
|
||||||
return result.value;
|
return result.value;
|
||||||
|
|
16
crates/compiler/builtins/bitcode/src/panic.zig
Normal file
16
crates/compiler/builtins/bitcode/src/panic.zig
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const RocStr = @import("str.zig").RocStr;
|
||||||
|
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||||
|
|
||||||
|
// Signals to the host that the program has panicked
|
||||||
|
extern fn roc_panic(msg: *const RocStr, tag_id: u32) callconv(.C) void;
|
||||||
|
|
||||||
|
pub fn panic_help(msg: []const u8, tag_id: u32) void {
|
||||||
|
var str = RocStr.init(msg.ptr, msg.len);
|
||||||
|
roc_panic(&str, tag_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// must export this explicitly because right now it is not used from zig code
|
||||||
|
pub fn panic(msg: *const RocStr, alignment: u32) callconv(.C) void {
|
||||||
|
return @call(.{ .modifier = always_inline }, roc_panic, .{ msg, alignment });
|
||||||
|
}
|
|
@ -16,9 +16,6 @@ extern fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, align
|
||||||
// This should never be passed a null pointer.
|
// This should never be passed a null pointer.
|
||||||
extern fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void;
|
extern fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void;
|
||||||
|
|
||||||
// Signals to the host that the program has panicked
|
|
||||||
extern fn roc_panic(c_ptr: *const anyopaque, tag_id: u32) callconv(.C) void;
|
|
||||||
|
|
||||||
// should work just like libc memcpy (we can't assume libc is present)
|
// should work just like libc memcpy (we can't assume libc is present)
|
||||||
extern fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
extern fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
||||||
|
|
||||||
|
@ -108,11 +105,6 @@ pub fn dealloc(c_ptr: [*]u8, alignment: u32) void {
|
||||||
return @call(.{ .modifier = always_inline }, roc_dealloc, .{ c_ptr, alignment });
|
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: *const anyopaque, alignment: u32) callconv(.C) void {
|
|
||||||
return @call(.{ .modifier = always_inline }, roc_panic, .{ c_ptr, alignment });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn memcpy(dst: [*]u8, src: [*]u8, size: usize) void {
|
pub fn memcpy(dst: [*]u8, src: [*]u8, size: usize) void {
|
||||||
@call(.{ .modifier = always_inline }, roc_memcpy, .{ dst, src, size });
|
@call(.{ .modifier = always_inline }, roc_memcpy, .{ dst, src, size });
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,6 +376,10 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
||||||
*called_via,
|
*called_via,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Crash { msg, ret_var } => Crash {
|
||||||
|
msg: Box::new(msg.map(|m| go_help!(m))),
|
||||||
|
ret_var: sub!(*ret_var),
|
||||||
|
},
|
||||||
RunLowLevel { op, args, ret_var } => RunLowLevel {
|
RunLowLevel { op, args, ret_var } => RunLowLevel {
|
||||||
op: *op,
|
op: *op,
|
||||||
args: args
|
args: args
|
||||||
|
|
|
@ -166,6 +166,12 @@ pub enum Expr {
|
||||||
/// Empty record constant
|
/// Empty record constant
|
||||||
EmptyRecord,
|
EmptyRecord,
|
||||||
|
|
||||||
|
/// The "crash" keyword
|
||||||
|
Crash {
|
||||||
|
msg: Box<Loc<Expr>>,
|
||||||
|
ret_var: Variable,
|
||||||
|
},
|
||||||
|
|
||||||
/// Look up exactly one field on a record, e.g. (expr).foo.
|
/// Look up exactly one field on a record, e.g. (expr).foo.
|
||||||
Access {
|
Access {
|
||||||
record_var: Variable,
|
record_var: Variable,
|
||||||
|
@ -309,6 +315,7 @@ impl Expr {
|
||||||
}
|
}
|
||||||
Self::Expect { .. } => Category::Expect,
|
Self::Expect { .. } => Category::Expect,
|
||||||
Self::ExpectFx { .. } => Category::Expect,
|
Self::ExpectFx { .. } => Category::Expect,
|
||||||
|
Self::Crash { .. } => Category::Crash,
|
||||||
|
|
||||||
Self::Dbg { .. } => Category::Expect,
|
Self::Dbg { .. } => Category::Expect,
|
||||||
|
|
||||||
|
@ -784,6 +791,47 @@ pub fn canonicalize_expr<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let ast::Expr::Crash = loc_fn.value {
|
||||||
|
// We treat crash specially, since crashing must be applied with one argument.
|
||||||
|
|
||||||
|
debug_assert!(!args.is_empty());
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
let mut output = Output::default();
|
||||||
|
|
||||||
|
for loc_arg in loc_args.iter() {
|
||||||
|
let (arg_expr, arg_out) =
|
||||||
|
canonicalize_expr(env, var_store, scope, loc_arg.region, &loc_arg.value);
|
||||||
|
|
||||||
|
args.push(arg_expr);
|
||||||
|
output.references.union_mut(&arg_out.references);
|
||||||
|
}
|
||||||
|
|
||||||
|
let crash = if args.len() > 1 {
|
||||||
|
let args_region = Region::span_across(
|
||||||
|
&loc_args.first().unwrap().region,
|
||||||
|
&loc_args.last().unwrap().region,
|
||||||
|
);
|
||||||
|
env.problem(Problem::OverAppliedCrash {
|
||||||
|
region: args_region,
|
||||||
|
});
|
||||||
|
// Still crash, just with our own message, and drop the references.
|
||||||
|
Crash {
|
||||||
|
msg: Box::new(Loc::at(
|
||||||
|
region,
|
||||||
|
Expr::Str(String::from("hit a crash!").into_boxed_str()),
|
||||||
|
)),
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let msg = args.pop().unwrap();
|
||||||
|
Crash {
|
||||||
|
msg: Box::new(msg),
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(crash, output)
|
||||||
} else {
|
} else {
|
||||||
// Canonicalize the function expression and its arguments
|
// Canonicalize the function expression and its arguments
|
||||||
let (fn_expr, fn_expr_output) =
|
let (fn_expr, fn_expr_output) =
|
||||||
|
@ -874,6 +922,22 @@ pub fn canonicalize_expr<'a>(
|
||||||
|
|
||||||
(RuntimeError(problem), Output::default())
|
(RuntimeError(problem), Output::default())
|
||||||
}
|
}
|
||||||
|
ast::Expr::Crash => {
|
||||||
|
// Naked crashes aren't allowed; we'll admit this with our own message, but yield an
|
||||||
|
// error.
|
||||||
|
env.problem(Problem::UnappliedCrash { region });
|
||||||
|
|
||||||
|
(
|
||||||
|
Crash {
|
||||||
|
msg: Box::new(Loc::at(
|
||||||
|
region,
|
||||||
|
Expr::Str(String::from("hit a crash!").into_boxed_str()),
|
||||||
|
)),
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
},
|
||||||
|
Output::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
ast::Expr::Defs(loc_defs, loc_ret) => {
|
ast::Expr::Defs(loc_defs, loc_ret) => {
|
||||||
// The body expression gets a new scope for canonicalization,
|
// The body expression gets a new scope for canonicalization,
|
||||||
scope.inner_scope(|inner_scope| {
|
scope.inner_scope(|inner_scope| {
|
||||||
|
@ -1728,7 +1792,8 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
||||||
| other @ RunLowLevel { .. }
|
| other @ RunLowLevel { .. }
|
||||||
| other @ TypedHole { .. }
|
| other @ TypedHole { .. }
|
||||||
| other @ ForeignCall { .. }
|
| other @ ForeignCall { .. }
|
||||||
| other @ OpaqueWrapFunction(_) => other,
|
| other @ OpaqueWrapFunction(_)
|
||||||
|
| other @ Crash { .. } => other,
|
||||||
|
|
||||||
List {
|
List {
|
||||||
elem_var,
|
elem_var,
|
||||||
|
@ -2826,6 +2891,7 @@ fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
||||||
// Intentionally ignore the lookups in the nested `expect` condition itself,
|
// Intentionally ignore the lookups in the nested `expect` condition itself,
|
||||||
// because they couldn't possibly influence the outcome of this `expect`!
|
// because they couldn't possibly influence the outcome of this `expect`!
|
||||||
}
|
}
|
||||||
|
Expr::Crash { msg, .. } => stack.push(&msg.value),
|
||||||
Expr::Num(_, _, _, _)
|
Expr::Num(_, _, _, _)
|
||||||
| Expr::Float(_, _, _, _, _)
|
| Expr::Float(_, _, _, _, _)
|
||||||
| Expr::Int(_, _, _, _, _)
|
| Expr::Int(_, _, _, _, _)
|
||||||
|
|
|
@ -981,6 +981,14 @@ fn fix_values_captured_in_closure_expr(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Crash { msg, ret_var: _ } => {
|
||||||
|
fix_values_captured_in_closure_expr(
|
||||||
|
&mut msg.value,
|
||||||
|
no_capture_symbols,
|
||||||
|
closure_captures,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Closure(ClosureData {
|
Closure(ClosureData {
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -138,7 +138,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
| PrecedenceConflict { .. }
|
| PrecedenceConflict { .. }
|
||||||
| Tag(_)
|
| Tag(_)
|
||||||
| OpaqueRef(_) => loc_expr,
|
| OpaqueRef(_)
|
||||||
|
| Crash => loc_expr,
|
||||||
|
|
||||||
TupleAccess(_sub_expr, _paths) => todo!("Handle TupleAccess"),
|
TupleAccess(_sub_expr, _paths) => todo!("Handle TupleAccess"),
|
||||||
RecordAccess(sub_expr, paths) => {
|
RecordAccess(sub_expr, paths) => {
|
||||||
|
|
|
@ -203,6 +203,9 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
|
||||||
let (fn_var, loc_fn, _closure_var, _ret_var) = &**f;
|
let (fn_var, loc_fn, _closure_var, _ret_var) = &**f;
|
||||||
walk_call(visitor, *fn_var, loc_fn, args);
|
walk_call(visitor, *fn_var, loc_fn, args);
|
||||||
}
|
}
|
||||||
|
Expr::Crash { msg, .. } => {
|
||||||
|
visitor.visit_expr(&msg.value, msg.region, Variable::STR);
|
||||||
|
}
|
||||||
Expr::RunLowLevel {
|
Expr::RunLowLevel {
|
||||||
op: _,
|
op: _,
|
||||||
args,
|
args,
|
||||||
|
|
|
@ -482,6 +482,28 @@ pub fn constrain_expr(
|
||||||
let and_constraint = constraints.and_constraint(and_cons);
|
let and_constraint = constraints.and_constraint(and_cons);
|
||||||
constraints.exists(vars, and_constraint)
|
constraints.exists(vars, and_constraint)
|
||||||
}
|
}
|
||||||
|
Expr::Crash { msg, ret_var } => {
|
||||||
|
let str_index = constraints.push_type(types, Types::STR);
|
||||||
|
let expected_msg = constraints.push_expected_type(Expected::ForReason(
|
||||||
|
Reason::CrashArg,
|
||||||
|
str_index,
|
||||||
|
msg.region,
|
||||||
|
));
|
||||||
|
|
||||||
|
let msg_is_str = constrain_expr(
|
||||||
|
types,
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
msg.region,
|
||||||
|
&msg.value,
|
||||||
|
expected_msg,
|
||||||
|
);
|
||||||
|
let magic = constraints.equal_types_var(*ret_var, expected, Category::Crash, region);
|
||||||
|
|
||||||
|
let and = constraints.and_constraint([msg_is_str, magic]);
|
||||||
|
|
||||||
|
constraints.exists([*ret_var], and)
|
||||||
|
}
|
||||||
Var(symbol, variable) => {
|
Var(symbol, variable) => {
|
||||||
// Save the expectation in the variable, then lookup the symbol's type in the environment
|
// Save the expectation in the variable, then lookup the symbol's type in the environment
|
||||||
let expected_type = *constraints[expected].get_type_ref();
|
let expected_type = *constraints[expected].get_type_ref();
|
||||||
|
|
|
@ -43,7 +43,8 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
| MalformedIdent(_, _)
|
| MalformedIdent(_, _)
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
| Tag(_)
|
| Tag(_)
|
||||||
| OpaqueRef(_) => false,
|
| OpaqueRef(_)
|
||||||
|
| Crash => false,
|
||||||
|
|
||||||
// These expressions always have newlines
|
// These expressions always have newlines
|
||||||
Defs(_, _) | When(_, _) => true,
|
Defs(_, _) | When(_, _) => true,
|
||||||
|
@ -191,6 +192,10 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
buf.push('_');
|
buf.push('_');
|
||||||
buf.push_str(name);
|
buf.push_str(name);
|
||||||
}
|
}
|
||||||
|
Crash => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str("crash");
|
||||||
|
}
|
||||||
Apply(loc_expr, loc_args, _) => {
|
Apply(loc_expr, loc_args, _) => {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
if apply_needs_parens && !loc_args.is_empty() {
|
if apply_needs_parens && !loc_args.is_empty() {
|
||||||
|
|
|
@ -666,6 +666,7 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
arena.alloc(b.remove_spaces(arena)),
|
arena.alloc(b.remove_spaces(arena)),
|
||||||
),
|
),
|
||||||
|
Expr::Crash => Expr::Crash,
|
||||||
Expr::Defs(a, b) => {
|
Expr::Defs(a, b) => {
|
||||||
let mut defs = a.clone();
|
let mut defs = a.clone();
|
||||||
defs.space_before = vec![Default::default(); defs.len()];
|
defs.space_before = vec![Default::default(); defs.len()];
|
||||||
|
|
|
@ -5829,6 +5829,42 @@ mod test_fmt {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_crash() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
_ = crash
|
||||||
|
_ = crash ""
|
||||||
|
|
||||||
|
crash "" ""
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
_ = crash
|
||||||
|
_ = crash ""
|
||||||
|
_ = crash "" ""
|
||||||
|
try
|
||||||
|
foo
|
||||||
|
(\_ -> crash "")
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
_ = crash
|
||||||
|
_ = crash ""
|
||||||
|
_ = crash "" ""
|
||||||
|
|
||||||
|
try
|
||||||
|
foo
|
||||||
|
(\_ -> crash "")
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// this is a parse error atm
|
// this is a parse error atm
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn multiline_apply() {
|
// fn multiline_apply() {
|
||||||
|
|
|
@ -1110,7 +1110,7 @@ trait Backend<'a> {
|
||||||
Stmt::Expect { .. } => todo!("expect is not implemented in the dev backend"),
|
Stmt::Expect { .. } => todo!("expect is not implemented in the dev backend"),
|
||||||
Stmt::ExpectFx { .. } => todo!("expect-fx is not implemented in the dev backend"),
|
Stmt::ExpectFx { .. } => todo!("expect-fx is not implemented in the dev backend"),
|
||||||
|
|
||||||
Stmt::RuntimeError(_) => {}
|
Stmt::Crash(..) => todo!("crash is not implemented in the dev backend"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ use roc_debug_flags::dbg_do;
|
||||||
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
|
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
BranchInfo, CallType, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc, OptLevel,
|
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
|
||||||
ProcLayout,
|
OptLevel, ProcLayout,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, RawFunctionLayout,
|
Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, RawFunctionLayout,
|
||||||
|
@ -183,22 +183,6 @@ pub struct Env<'a, 'ctx, 'env> {
|
||||||
pub exposed_to_host: MutSet<Symbol>,
|
pub exposed_to_host: MutSet<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum PanicTagId {
|
|
||||||
NullTerminatedString = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::TryFrom<u32> for PanicTagId {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
0 => Ok(PanicTagId::NullTerminatedString),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
||||||
/// The integer type representing a pointer
|
/// The integer type representing a pointer
|
||||||
///
|
///
|
||||||
|
@ -344,16 +328,33 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_panic(&self, message: PointerValue<'ctx>, tag_id: PanicTagId) {
|
pub fn call_panic(
|
||||||
|
&self,
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
message: BasicValueEnum<'ctx>,
|
||||||
|
tag: CrashTag,
|
||||||
|
) {
|
||||||
let function = self.module.get_function("roc_panic").unwrap();
|
let function = self.module.get_function("roc_panic").unwrap();
|
||||||
let tag_id = self
|
let tag_id = self.context.i32_type().const_int(tag as u32 as u64, false);
|
||||||
.context
|
|
||||||
.i32_type()
|
let msg = match env.target_info.ptr_width() {
|
||||||
.const_int(tag_id as u32 as u64, false);
|
PtrWidth::Bytes4 => {
|
||||||
|
// we need to pass the message by reference, but we currently hold the value.
|
||||||
|
let alloca = env
|
||||||
|
.builder
|
||||||
|
.build_alloca(message.get_type(), "alloca_panic_msg");
|
||||||
|
env.builder.build_store(alloca, message);
|
||||||
|
alloca.into()
|
||||||
|
}
|
||||||
|
PtrWidth::Bytes8 => {
|
||||||
|
// string is already held by reference
|
||||||
|
message
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let call = self
|
let call = self
|
||||||
.builder
|
.builder
|
||||||
.build_call(function, &[message.into(), tag_id.into()], "roc_panic");
|
.build_call(function, &[msg.into(), tag_id.into()], "roc_panic");
|
||||||
|
|
||||||
call.set_call_convention(C_CALL_CONV);
|
call.set_call_convention(C_CALL_CONV);
|
||||||
}
|
}
|
||||||
|
@ -750,7 +751,15 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
||||||
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
||||||
Str(str_literal) => {
|
Str(str_literal) => build_string_literal(env, parent, str_literal),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_string_literal<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
str_literal: &str,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
if str_literal.len() < env.small_str_bytes() as usize {
|
if str_literal.len() < env.small_str_bytes() as usize {
|
||||||
match env.small_str_bytes() {
|
match env.small_str_bytes() {
|
||||||
24 => small_str_ptr_width_8(env, parent, str_literal).into(),
|
24 => small_str_ptr_width_8(env, parent, str_literal).into(),
|
||||||
|
@ -761,16 +770,13 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||||
let ptr = define_global_str_literal_ptr(env, str_literal);
|
let ptr = define_global_str_literal_ptr(env, str_literal);
|
||||||
let number_of_elements = env.ptr_int().const_int(str_literal.len() as u64, false);
|
let number_of_elements = env.ptr_int().const_int(str_literal.len() as u64, false);
|
||||||
|
|
||||||
let alloca =
|
let alloca = const_str_alloca_ptr(env, parent, ptr, number_of_elements, number_of_elements);
|
||||||
const_str_alloca_ptr(env, parent, ptr, number_of_elements, number_of_elements);
|
|
||||||
|
|
||||||
match env.target_info.ptr_width() {
|
match env.target_info.ptr_width() {
|
||||||
PtrWidth::Bytes4 => env.builder.build_load(alloca, "load_const_str"),
|
PtrWidth::Bytes4 => env.builder.build_load(alloca, "load_const_str"),
|
||||||
PtrWidth::Bytes8 => alloca.into(),
|
PtrWidth::Bytes8 => alloca.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_str_alloca_ptr<'a, 'ctx, 'env>(
|
fn const_str_alloca_ptr<'a, 'ctx, 'env>(
|
||||||
|
@ -2621,7 +2627,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
roc_target::PtrWidth::Bytes4 => {
|
roc_target::PtrWidth::Bytes4 => {
|
||||||
// temporary WASM implementation
|
// temporary WASM implementation
|
||||||
throw_exception(env, "An expectation failed!");
|
throw_internal_exception(env, parent, "An expectation failed!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -2683,7 +2689,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
roc_target::PtrWidth::Bytes4 => {
|
roc_target::PtrWidth::Bytes4 => {
|
||||||
// temporary WASM implementation
|
// temporary WASM implementation
|
||||||
throw_exception(env, "An expectation failed!");
|
throw_internal_exception(env, parent, "An expectation failed!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -2703,8 +2709,8 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeError(error_msg) => {
|
Crash(sym, tag) => {
|
||||||
throw_exception(env, error_msg);
|
throw_exception(env, scope, sym, *tag);
|
||||||
|
|
||||||
// unused value (must return a BasicValue)
|
// unused value (must return a BasicValue)
|
||||||
let zero = env.context.i64_type().const_zero();
|
let zero = env.context.i64_type().const_zero();
|
||||||
|
@ -3336,7 +3342,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
builder.position_at_end(entry);
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
let wrapped_layout = roc_result_layout(env.arena, return_layout, env.target_info);
|
let wrapped_layout = roc_call_result_layout(env.arena, return_layout, env.target_info);
|
||||||
call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call)
|
call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call)
|
||||||
} else {
|
} else {
|
||||||
call_roc_function(env, roc_function, &return_layout, arguments_for_call)
|
call_roc_function(env, roc_function, &return_layout, arguments_for_call)
|
||||||
|
@ -3366,7 +3372,8 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
|
||||||
// a tagged union to indicate to the test loader that a panic occurred.
|
// a tagged union to indicate to the test loader that a panic occurred.
|
||||||
// especially when running 32-bit binaries on a 64-bit machine, there
|
// especially when running 32-bit binaries on a 64-bit machine, there
|
||||||
// does not seem to be a smarter solution
|
// does not seem to be a smarter solution
|
||||||
let wrapper_return_type = roc_result_type(env, basic_type_from_layout(env, &return_layout));
|
let wrapper_return_type =
|
||||||
|
roc_call_result_type(env, basic_type_from_layout(env, &return_layout));
|
||||||
|
|
||||||
let mut cc_argument_types = Vec::with_capacity_in(arguments.len(), env.arena);
|
let mut cc_argument_types = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
for layout in arguments {
|
for layout in arguments {
|
||||||
|
@ -3755,7 +3762,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let return_type = match env.mode {
|
let return_type = match env.mode {
|
||||||
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest | LlvmBackendMode::CliTest => {
|
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest | LlvmBackendMode::CliTest => {
|
||||||
roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
|
roc_call_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev => {
|
LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev => {
|
||||||
|
@ -3862,14 +3869,29 @@ pub fn build_setjmp_call<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pointer to pointer of the panic message.
|
/// Pointer to RocStr which is the panic message.
|
||||||
pub fn get_panic_msg_ptr<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
|
pub fn get_panic_msg_ptr<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
|
||||||
let ptr_to_u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
let str_typ = zig_str_type(env);
|
||||||
|
|
||||||
let global_name = "roc_panic_msg_ptr";
|
let global_name = "roc_panic_msg_str";
|
||||||
let global = env.module.get_global(global_name).unwrap_or_else(|| {
|
let global = env.module.get_global(global_name).unwrap_or_else(|| {
|
||||||
let global = env.module.add_global(ptr_to_u8_ptr, None, global_name);
|
let global = env.module.add_global(str_typ, None, global_name);
|
||||||
global.set_initializer(&ptr_to_u8_ptr.const_zero());
|
global.set_initializer(&str_typ.const_zero());
|
||||||
|
global
|
||||||
|
});
|
||||||
|
|
||||||
|
global.as_pointer_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pointer to the panic tag.
|
||||||
|
/// Only non-zero values must be written into here.
|
||||||
|
pub fn get_panic_tag_ptr<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
|
||||||
|
let i64_typ = env.context.i64_type();
|
||||||
|
|
||||||
|
let global_name = "roc_panic_msg_tag";
|
||||||
|
let global = env.module.get_global(global_name).unwrap_or_else(|| {
|
||||||
|
let global = env.module.add_global(i64_typ, None, global_name);
|
||||||
|
global.set_initializer(&i64_typ.const_zero());
|
||||||
global
|
global
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3887,7 +3909,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
let return_type = basic_type_from_layout(env, &return_layout);
|
let return_type = basic_type_from_layout(env, &return_layout);
|
||||||
let call_result_type = roc_result_type(env, return_type.as_basic_type_enum());
|
let call_result_type = roc_call_result_type(env, return_type.as_basic_type_enum());
|
||||||
let result_alloca = builder.build_alloca(call_result_type, "result");
|
let result_alloca = builder.build_alloca(call_result_type, "result");
|
||||||
|
|
||||||
let then_block = context.append_basic_block(parent, "then_block");
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
|
@ -3922,26 +3944,21 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
|
||||||
{
|
{
|
||||||
builder.position_at_end(catch_block);
|
builder.position_at_end(catch_block);
|
||||||
|
|
||||||
let error_msg = {
|
// RocStr* global
|
||||||
// u8**
|
let error_msg_ptr = get_panic_msg_ptr(env);
|
||||||
let ptr_int_ptr = get_panic_msg_ptr(env);
|
// i64* global
|
||||||
|
let error_tag_ptr = get_panic_tag_ptr(env);
|
||||||
// u8* again
|
|
||||||
builder.build_load(ptr_int_ptr, "ptr_int")
|
|
||||||
};
|
|
||||||
|
|
||||||
let return_value = {
|
let return_value = {
|
||||||
let v1 = call_result_type.const_zero();
|
let v1 = call_result_type.const_zero();
|
||||||
|
|
||||||
// flag is non-zero, indicating failure
|
// tag must be non-zero, indicating failure
|
||||||
let flag = context.i64_type().const_int(1, false);
|
let tag = builder.build_load(error_tag_ptr, "load_panic_tag");
|
||||||
|
|
||||||
let v2 = builder
|
let v2 = builder.build_insert_value(v1, tag, 0, "set_error").unwrap();
|
||||||
.build_insert_value(v1, flag, 0, "set_error")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let v3 = builder
|
let v3 = builder
|
||||||
.build_insert_value(v2, error_msg, 1, "set_exception")
|
.build_insert_value(v2, error_msg_ptr, 1, "set_exception")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
v3
|
v3
|
||||||
};
|
};
|
||||||
|
@ -3971,7 +3988,7 @@ fn make_exception_catcher<'a, 'ctx, 'env>(
|
||||||
function_value
|
function_value
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_result_layout<'a>(
|
fn roc_call_result_layout<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
return_layout: Layout<'a>,
|
return_layout: Layout<'a>,
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
|
@ -3981,14 +3998,14 @@ fn roc_result_layout<'a>(
|
||||||
Layout::struct_no_name_order(arena.alloc(elements))
|
Layout::struct_no_name_order(arena.alloc(elements))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_result_type<'a, 'ctx, 'env>(
|
fn roc_call_result_type<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
return_type: BasicTypeEnum<'ctx>,
|
return_type: BasicTypeEnum<'ctx>,
|
||||||
) -> StructType<'ctx> {
|
) -> StructType<'ctx> {
|
||||||
env.context.struct_type(
|
env.context.struct_type(
|
||||||
&[
|
&[
|
||||||
env.context.i64_type().into(),
|
env.context.i64_type().into(),
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic).into(),
|
zig_str_type(env).ptr_type(AddressSpace::Generic).into(),
|
||||||
return_type,
|
return_type,
|
||||||
],
|
],
|
||||||
false,
|
false,
|
||||||
|
@ -4003,7 +4020,7 @@ fn make_good_roc_result<'a, 'ctx, 'env>(
|
||||||
let context = env.context;
|
let context = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
let v1 = roc_result_type(env, basic_type_from_layout(env, &return_layout)).const_zero();
|
let v1 = roc_call_result_type(env, basic_type_from_layout(env, &return_layout)).const_zero();
|
||||||
|
|
||||||
let v2 = builder
|
let v2 = builder
|
||||||
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
|
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
|
||||||
|
@ -4050,7 +4067,8 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let wrapper_return_type = roc_result_type(env, basic_type_from_layout(env, &return_layout));
|
let wrapper_return_type =
|
||||||
|
roc_call_result_type(env, basic_type_from_layout(env, &return_layout));
|
||||||
|
|
||||||
// argument_types.push(wrapper_return_type.ptr_type(AddressSpace::Generic).into());
|
// argument_types.push(wrapper_return_type.ptr_type(AddressSpace::Generic).into());
|
||||||
|
|
||||||
|
@ -5520,51 +5538,33 @@ fn define_global_str_literal<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_global_error_str<'a, 'ctx, 'env>(
|
pub(crate) fn throw_internal_exception<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
message: &str,
|
message: &str,
|
||||||
) -> inkwell::values::GlobalValue<'ctx> {
|
) {
|
||||||
let module = env.module;
|
|
||||||
|
|
||||||
// hash the name so we don't re-define existing messages
|
|
||||||
let name = {
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
message.hash(&mut hasher);
|
|
||||||
let hash = hasher.finish();
|
|
||||||
|
|
||||||
format!("_Error_message_{}", hash)
|
|
||||||
};
|
|
||||||
|
|
||||||
match module.get_global(&name) {
|
|
||||||
Some(current) => current,
|
|
||||||
None => unsafe { env.builder.build_global_string(message, name.as_str()) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, message: &str) {
|
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
// define the error message as a global
|
let str = build_string_literal(env, parent, message);
|
||||||
// (a hash is used such that the same value is not defined repeatedly)
|
|
||||||
let error_msg_global = define_global_error_str(env, message);
|
|
||||||
|
|
||||||
let cast = env
|
env.call_panic(env, str, CrashTag::Roc);
|
||||||
.builder
|
|
||||||
.build_bitcast(
|
|
||||||
error_msg_global.as_pointer_value(),
|
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
|
||||||
"cast_void",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
env.call_panic(cast, PanicTagId::NullTerminatedString);
|
|
||||||
|
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn throw_exception<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
|
message: &Symbol,
|
||||||
|
tag: CrashTag,
|
||||||
|
) {
|
||||||
|
let msg_val = load_symbol(scope, message);
|
||||||
|
|
||||||
|
env.call_panic(env, msg_val, tag);
|
||||||
|
|
||||||
|
env.builder.build_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
fn get_foreign_symbol<'a, 'ctx, 'env>(
|
fn get_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
foreign_symbol: roc_module::ident::ForeignSymbol,
|
foreign_symbol: roc_module::ident::ForeignSymbol,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||||
use crate::llvm::build::{add_func, get_panic_msg_ptr, C_CALL_CONV};
|
use crate::llvm::build::{add_func, get_panic_msg_ptr, get_panic_tag_ptr, C_CALL_CONV};
|
||||||
use crate::llvm::build::{CCReturn, Env, FunctionSpec};
|
use crate::llvm::build::{CCReturn, Env, FunctionSpec};
|
||||||
use inkwell::module::Linkage;
|
use inkwell::module::Linkage;
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
|
@ -193,10 +193,9 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
||||||
// already been defined by the builtins, which rely on it.
|
// already been defined by the builtins, which rely on it.
|
||||||
let fn_val = module.get_function("roc_panic").unwrap();
|
let fn_val = module.get_function("roc_panic").unwrap();
|
||||||
let mut params = fn_val.get_param_iter();
|
let mut params = fn_val.get_param_iter();
|
||||||
let ptr_arg = params.next().unwrap();
|
let roc_str_arg = params.next().unwrap();
|
||||||
|
|
||||||
// in debug mode, this is assumed to be NullTerminatedString
|
let tag_id_arg = params.next().unwrap();
|
||||||
let _tag_id_arg = params.next().unwrap();
|
|
||||||
|
|
||||||
debug_assert!(params.next().is_none());
|
debug_assert!(params.next().is_none());
|
||||||
|
|
||||||
|
@ -210,8 +209,38 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
||||||
|
|
||||||
builder.position_at_end(entry);
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
// write our error message pointer
|
// write our error message to the RocStr pointer
|
||||||
env.builder.build_store(get_panic_msg_ptr(env), ptr_arg);
|
{
|
||||||
|
let loaded_roc_str = match env.target_info.ptr_width() {
|
||||||
|
roc_target::PtrWidth::Bytes4 => roc_str_arg,
|
||||||
|
// On 64-bit we pass RocStrs by reference internally
|
||||||
|
roc_target::PtrWidth::Bytes8 => {
|
||||||
|
builder.build_load(roc_str_arg.into_pointer_value(), "load_roc_str")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_store(get_panic_msg_ptr(env), loaded_roc_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the panic tag.
|
||||||
|
// increment by 1, since the tag we'll get from the Roc program is 0-based,
|
||||||
|
// but we use 0 for marking a successful call.
|
||||||
|
{
|
||||||
|
let cast_tag_id = builder.build_int_z_extend(
|
||||||
|
tag_id_arg.into_int_value(),
|
||||||
|
env.context.i64_type(),
|
||||||
|
"zext_panic_tag",
|
||||||
|
);
|
||||||
|
|
||||||
|
let inc_tag_id = builder.build_int_add(
|
||||||
|
cast_tag_id,
|
||||||
|
env.context.i64_type().const_int(1, false),
|
||||||
|
"inc_panic_tag",
|
||||||
|
);
|
||||||
|
|
||||||
|
env.builder.build_store(get_panic_tag_ptr(env), inc_tag_id);
|
||||||
|
}
|
||||||
|
|
||||||
build_longjmp_call(env);
|
build_longjmp_call(env);
|
||||||
|
|
||||||
|
|
|
@ -41,9 +41,9 @@ use crate::llvm::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::convert::zig_with_overflow_roc_dec;
|
use super::{build::throw_internal_exception, convert::zig_with_overflow_roc_dec};
|
||||||
use super::{
|
use super::{
|
||||||
build::{load_symbol, load_symbol_and_layout, throw_exception, Env, Scope},
|
build::{load_symbol, load_symbol_and_layout, Env, Scope},
|
||||||
convert::zig_dec_type,
|
convert::zig_dec_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1557,7 +1557,7 @@ fn throw_on_overflow<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
bd.position_at_end(throw_block);
|
bd.position_at_end(throw_block);
|
||||||
|
|
||||||
throw_exception(env, message);
|
throw_internal_exception(env, parent, message);
|
||||||
|
|
||||||
bd.position_at_end(then_block);
|
bd.position_at_end(then_block);
|
||||||
|
|
||||||
|
@ -2003,8 +2003,9 @@ fn int_neg_raise_on_overflow<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
builder.position_at_end(then_block);
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
throw_exception(
|
throw_internal_exception(
|
||||||
env,
|
env,
|
||||||
|
parent,
|
||||||
"integer negation overflowed because its argument is the minimum value",
|
"integer negation overflowed because its argument is the minimum value",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2033,8 +2034,9 @@ fn int_abs_raise_on_overflow<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
builder.position_at_end(then_block);
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
throw_exception(
|
throw_internal_exception(
|
||||||
env,
|
env,
|
||||||
|
parent,
|
||||||
"integer absolute overflowed because its argument is the minimum value",
|
"integer absolute overflowed because its argument is the minimum value",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::os::raw::c_char;
|
|
||||||
|
use roc_mono::ir::CrashTag;
|
||||||
|
use roc_std::RocStr;
|
||||||
|
|
||||||
/// This must have the same size as the repr() of RocCallResult!
|
/// This must have the same size as the repr() of RocCallResult!
|
||||||
pub const ROC_CALL_RESULT_DISCRIMINANT_SIZE: usize = std::mem::size_of::<u64>();
|
pub const ROC_CALL_RESULT_DISCRIMINANT_SIZE: usize = std::mem::size_of::<u64>();
|
||||||
|
@ -8,7 +9,7 @@ pub const ROC_CALL_RESULT_DISCRIMINANT_SIZE: usize = std::mem::size_of::<u64>();
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct RocCallResult<T> {
|
pub struct RocCallResult<T> {
|
||||||
tag: u64,
|
tag: u64,
|
||||||
error_msg: *mut c_char,
|
error_msg: *mut RocStr,
|
||||||
value: MaybeUninit<T>,
|
value: MaybeUninit<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,14 +33,18 @@ impl<T: Default> Default for RocCallResult<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
|
impl<T: Sized> From<RocCallResult<T>> for Result<T, (String, CrashTag)> {
|
||||||
fn from(call_result: RocCallResult<T>) -> Self {
|
fn from(call_result: RocCallResult<T>) -> Self {
|
||||||
match call_result.tag {
|
match call_result.tag {
|
||||||
0 => Ok(unsafe { call_result.value.assume_init() }),
|
0 => Ok(unsafe { call_result.value.assume_init() }),
|
||||||
_ => Err({
|
n => Err({
|
||||||
let raw = unsafe { CStr::from_ptr(call_result.error_msg) };
|
let msg: &RocStr = unsafe { &*call_result.error_msg };
|
||||||
|
let tag = (n - 1) as u32;
|
||||||
|
let tag = tag
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or_else(|_| panic!("received illegal tag: {tag}"));
|
||||||
|
|
||||||
raw.to_str().unwrap().to_owned()
|
(msg.as_str().to_owned(), tag)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +125,7 @@ macro_rules! run_jit_function {
|
||||||
|
|
||||||
$transform(success)
|
$transform(success)
|
||||||
}
|
}
|
||||||
Err(error_msg) => {
|
Err((error_msg, _)) => {
|
||||||
eprintln!("This Roc code crashed with: \"{error_msg}\"");
|
eprintln!("This Roc code crashed with: \"{error_msg}\"");
|
||||||
|
|
||||||
Expr::MalformedClosure
|
Expr::MalformedClosure
|
||||||
|
|
|
@ -8,8 +8,8 @@ use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::code_gen_help::{CodeGenHelp, HelperOp, REFCOUNT_MAX};
|
use roc_mono::code_gen_help::{CodeGenHelp, HelperOp, REFCOUNT_MAX};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc, Param, Proc,
|
BranchInfo, CallType, CrashTag, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc,
|
||||||
ProcLayout, Stmt,
|
Param, Proc, ProcLayout, Stmt,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
|
@ -717,7 +717,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"),
|
Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"),
|
||||||
Stmt::ExpectFx { .. } => todo!("expect-fx is not implemented in the wasm backend"),
|
Stmt::ExpectFx { .. } => todo!("expect-fx is not implemented in the wasm backend"),
|
||||||
|
|
||||||
Stmt::RuntimeError(msg) => self.stmt_runtime_error(msg),
|
Stmt::Crash(sym, tag) => self.stmt_crash(*sym, *tag),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,19 +987,31 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.stmt(rc_stmt);
|
self.stmt(rc_stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stmt_runtime_error(&mut self, msg: &'a str) {
|
pub fn stmt_internal_error(&mut self, msg: &'a str) {
|
||||||
// Create a zero-terminated version of the message string
|
let msg_sym = self.create_symbol("panic_str");
|
||||||
let mut bytes = Vec::with_capacity_in(msg.len() + 1, self.env.arena);
|
let msg_storage = self.storage.allocate_var(
|
||||||
bytes.extend_from_slice(msg.as_bytes());
|
self.env.layout_interner,
|
||||||
bytes.push(0);
|
Layout::Builtin(Builtin::Str),
|
||||||
|
msg_sym,
|
||||||
|
StoredVarKind::Variable,
|
||||||
|
);
|
||||||
|
|
||||||
// Store it in the app's data section
|
// Store the message as a RocStr on the stack
|
||||||
let elements_addr = self.store_bytes_in_data_section(&bytes);
|
let (local_id, offset) = match msg_storage {
|
||||||
|
StoredValue::StackMemory { location, .. } => {
|
||||||
|
location.local_and_offset(self.storage.stack_frame_pointer)
|
||||||
|
}
|
||||||
|
_ => internal_error!("String must always have stack memory"),
|
||||||
|
};
|
||||||
|
self.expr_string_literal(msg, local_id, offset);
|
||||||
|
|
||||||
// Pass its address to roc_panic
|
self.stmt_crash(msg_sym, CrashTag::Roc);
|
||||||
let tag_id = 0;
|
}
|
||||||
self.code_builder.i32_const(elements_addr as i32);
|
|
||||||
self.code_builder.i32_const(tag_id);
|
pub fn stmt_crash(&mut self, msg: Symbol, tag: CrashTag) {
|
||||||
|
// load the pointer
|
||||||
|
self.storage.load_symbols(&mut self.code_builder, &[msg]);
|
||||||
|
self.code_builder.i32_const(tag as _);
|
||||||
self.call_host_fn_after_loading_args("roc_panic", 2, false);
|
self.call_host_fn_after_loading_args("roc_panic", 2, false);
|
||||||
|
|
||||||
self.code_builder.unreachable_();
|
self.code_builder.unreachable_();
|
||||||
|
@ -1128,6 +1140,18 @@ impl<'a> WasmBackend<'a> {
|
||||||
let (local_id, offset) =
|
let (local_id, offset) =
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||||
|
|
||||||
|
self.expr_string_literal(string, local_id, offset);
|
||||||
|
}
|
||||||
|
// Bools and bytes should not be stored in the stack frame
|
||||||
|
Literal::Bool(_) | Literal::Byte(_) => invalid_error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => invalid_error(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_string_literal(&mut self, string: &str, local_id: LocalId, offset: u32) {
|
||||||
let len = string.len();
|
let len = string.len();
|
||||||
if len < 12 {
|
if len < 12 {
|
||||||
// Construct the bytes of the small string
|
// Construct the bytes of the small string
|
||||||
|
@ -1136,8 +1160,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
bytes[11] = 0x80 | (len as u8);
|
bytes[11] = 0x80 | (len as u8);
|
||||||
|
|
||||||
// Transform into two integers, to minimise number of instructions
|
// Transform into two integers, to minimise number of instructions
|
||||||
let bytes_split: &([u8; 8], [u8; 4]) =
|
let bytes_split: &([u8; 8], [u8; 4]) = unsafe { std::mem::transmute(&bytes) };
|
||||||
unsafe { std::mem::transmute(&bytes) };
|
|
||||||
let int64 = i64::from_le_bytes(bytes_split.0);
|
let int64 = i64::from_le_bytes(bytes_split.0);
|
||||||
let int32 = i32::from_le_bytes(bytes_split.1);
|
let int32 = i32::from_le_bytes(bytes_split.1);
|
||||||
|
|
||||||
|
@ -1168,14 +1191,6 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.code_builder.i32_store(Align::Bytes4, offset + 8);
|
self.code_builder.i32_store(Align::Bytes4, offset + 8);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Bools and bytes should not be stored in the stack frame
|
|
||||||
Literal::Bool(_) | Literal::Byte(_) => invalid_error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => invalid_error(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a string constant in the module data section
|
/// Create a string constant in the module data section
|
||||||
/// Return the data we need for code gen: linker symbol index and memory address
|
/// Return the data we need for code gen: linker symbol index and memory address
|
||||||
|
|
|
@ -1362,7 +1362,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend.code_builder.i32_const(i32::MIN);
|
backend.code_builder.i32_const(i32::MIN);
|
||||||
backend.code_builder.i32_eq();
|
backend.code_builder.i32_eq();
|
||||||
backend.code_builder.if_();
|
backend.code_builder.if_();
|
||||||
backend.stmt_runtime_error(PANIC_MSG);
|
backend.stmt_internal_error(PANIC_MSG);
|
||||||
backend.code_builder.end();
|
backend.code_builder.end();
|
||||||
|
|
||||||
// x
|
// x
|
||||||
|
@ -1388,7 +1388,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend.code_builder.i64_const(i64::MIN);
|
backend.code_builder.i64_const(i64::MIN);
|
||||||
backend.code_builder.i64_eq();
|
backend.code_builder.i64_eq();
|
||||||
backend.code_builder.if_();
|
backend.code_builder.if_();
|
||||||
backend.stmt_runtime_error(PANIC_MSG);
|
backend.stmt_internal_error(PANIC_MSG);
|
||||||
backend.code_builder.end();
|
backend.code_builder.end();
|
||||||
|
|
||||||
// x
|
// x
|
||||||
|
@ -1422,7 +1422,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend.code_builder.i32_const(i32::MIN);
|
backend.code_builder.i32_const(i32::MIN);
|
||||||
backend.code_builder.i32_eq();
|
backend.code_builder.i32_eq();
|
||||||
backend.code_builder.if_();
|
backend.code_builder.if_();
|
||||||
backend.stmt_runtime_error(PANIC_MSG);
|
backend.stmt_internal_error(PANIC_MSG);
|
||||||
backend.code_builder.end();
|
backend.code_builder.end();
|
||||||
|
|
||||||
backend.code_builder.i32_const(0);
|
backend.code_builder.i32_const(0);
|
||||||
|
@ -1433,7 +1433,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend.code_builder.i64_const(i64::MIN);
|
backend.code_builder.i64_const(i64::MIN);
|
||||||
backend.code_builder.i64_eq();
|
backend.code_builder.i64_eq();
|
||||||
backend.code_builder.if_();
|
backend.code_builder.if_();
|
||||||
backend.stmt_runtime_error(PANIC_MSG);
|
backend.stmt_internal_error(PANIC_MSG);
|
||||||
backend.code_builder.end();
|
backend.code_builder.end();
|
||||||
|
|
||||||
backend.code_builder.i64_const(0);
|
backend.code_builder.i64_const(0);
|
||||||
|
|
|
@ -321,7 +321,7 @@ impl<'a> ParamMap<'a> {
|
||||||
}
|
}
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | Crash(..) => {
|
||||||
// these are terminal, do nothing
|
// these are terminal, do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -827,7 +827,12 @@ impl<'a> BorrowInfState<'a> {
|
||||||
|
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | RuntimeError(_) => {
|
Crash(msg, _) => {
|
||||||
|
// Crash is a foreign call, so we must own the argument.
|
||||||
|
self.own_var(*msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ret(_) => {
|
||||||
// these are terminal, do nothing
|
// these are terminal, do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1001,7 +1006,7 @@ fn call_info_stmt<'a>(arena: &'a Bump, stmt: &Stmt<'a>, info: &mut CallInfo<'a>)
|
||||||
|
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | Crash(..) => {
|
||||||
// these are terminal, do nothing
|
// these are terminal, do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,7 +158,9 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
||||||
stack.push(default_branch.1);
|
stack.push(default_branch.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeError(_) => {}
|
Crash(sym, _) => {
|
||||||
|
result.insert(*sym);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1240,7 +1242,20 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||||
(expect, b_live_vars)
|
(expect, b_live_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeError(_) | Refcounting(_, _) => (stmt, MutSet::default()),
|
Crash(x, _) => {
|
||||||
|
let info = self.get_var_info(*x);
|
||||||
|
|
||||||
|
let mut live_vars = MutSet::default();
|
||||||
|
live_vars.insert(*x);
|
||||||
|
|
||||||
|
if info.reference && !info.consume {
|
||||||
|
(self.add_inc(*x, 1, stmt), live_vars)
|
||||||
|
} else {
|
||||||
|
(stmt, live_vars)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Refcounting(_, _) => (stmt, MutSet::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1411,7 +1426,10 @@ pub fn collect_stmt(
|
||||||
vars
|
vars
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeError(_) => vars,
|
Crash(m, _) => {
|
||||||
|
vars.insert(*m);
|
||||||
|
vars
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,16 @@ roc_error_macros::assert_sizeof_non_wasm!(ProcLayout, 8 * 8);
|
||||||
roc_error_macros::assert_sizeof_non_wasm!(Call, 9 * 8);
|
roc_error_macros::assert_sizeof_non_wasm!(Call, 9 * 8);
|
||||||
roc_error_macros::assert_sizeof_non_wasm!(CallType, 7 * 8);
|
roc_error_macros::assert_sizeof_non_wasm!(CallType, 7 * 8);
|
||||||
|
|
||||||
|
fn runtime_error<'a>(env: &mut Env<'a, '_>, msg: &'a str) -> Stmt<'a> {
|
||||||
|
let sym = env.unique_symbol();
|
||||||
|
Stmt::Let(
|
||||||
|
sym,
|
||||||
|
Expr::Literal(Literal::Str(msg)),
|
||||||
|
Layout::Builtin(Builtin::Str),
|
||||||
|
env.arena.alloc(Stmt::Crash(sym, CrashTag::Roc)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! return_on_layout_error {
|
macro_rules! return_on_layout_error {
|
||||||
($env:expr, $layout_result:expr, $context_msg:expr) => {
|
($env:expr, $layout_result:expr, $context_msg:expr) => {
|
||||||
match $layout_result {
|
match $layout_result {
|
||||||
|
@ -84,15 +94,17 @@ macro_rules! return_on_layout_error_help {
|
||||||
($env:expr, $error:expr, $context_msg:expr) => {{
|
($env:expr, $error:expr, $context_msg:expr) => {{
|
||||||
match $error {
|
match $error {
|
||||||
LayoutProblem::UnresolvedTypeVar(_) => {
|
LayoutProblem::UnresolvedTypeVar(_) => {
|
||||||
return Stmt::RuntimeError(
|
return runtime_error(
|
||||||
|
$env,
|
||||||
$env.arena
|
$env.arena
|
||||||
.alloc(format!("UnresolvedTypeVar: {}", $context_msg,)),
|
.alloc(format!("UnresolvedTypeVar: {}", $context_msg,)),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
LayoutProblem::Erroneous => {
|
LayoutProblem::Erroneous => {
|
||||||
return Stmt::RuntimeError(
|
return runtime_error(
|
||||||
|
$env,
|
||||||
$env.arena.alloc(format!("Erroneous: {}", $context_msg,)),
|
$env.arena.alloc(format!("Erroneous: {}", $context_msg,)),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
@ -1611,6 +1623,7 @@ pub fn cond<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Stmt<'a> {
|
pub enum Stmt<'a> {
|
||||||
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
||||||
|
@ -1655,7 +1668,29 @@ pub enum Stmt<'a> {
|
||||||
remainder: &'a Stmt<'a>,
|
remainder: &'a Stmt<'a>,
|
||||||
},
|
},
|
||||||
Jump(JoinPointId, &'a [Symbol]),
|
Jump(JoinPointId, &'a [Symbol]),
|
||||||
RuntimeError(&'a str),
|
Crash(Symbol, CrashTag),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Source of crash, and its runtime representation to roc_panic.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum CrashTag {
|
||||||
|
/// The crash is due to Roc, either via a builtin or type error.
|
||||||
|
Roc = 0,
|
||||||
|
/// The crash is user-defined.
|
||||||
|
User = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for CrashTag {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(Self::Roc),
|
||||||
|
1 => Ok(Self::User),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// in the block below, symbol `scrutinee` is assumed be be of shape `tag_id`
|
/// in the block below, symbol `scrutinee` is assumed be be of shape `tag_id`
|
||||||
|
@ -2303,7 +2338,7 @@ impl<'a> Stmt<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeError(s) => alloc.text(format!("Error {}", s)),
|
Crash(s, _src) => alloc.text("Crash ").append(symbol_to_doc(alloc, *s)),
|
||||||
|
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
|
@ -3152,7 +3187,7 @@ fn generate_runtime_error_function<'a>(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let runtime_error = Stmt::RuntimeError(msg.into_bump_str());
|
let runtime_error = runtime_error(env, msg.into_bump_str());
|
||||||
|
|
||||||
let (args, ret_layout) = match layout {
|
let (args, ret_layout) = match layout {
|
||||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
||||||
|
@ -4300,7 +4335,7 @@ pub fn with_hole<'a>(
|
||||||
};
|
};
|
||||||
let sorted_fields = match sorted_fields_result {
|
let sorted_fields = match sorted_fields_result {
|
||||||
Ok(fields) => fields,
|
Ok(fields) => fields,
|
||||||
Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"),
|
Err(_) => return runtime_error(env, "Can't create record with improper layout"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
|
@ -4352,7 +4387,7 @@ pub fn with_hole<'a>(
|
||||||
// creating a record from the var will unpack it if it's just a single field.
|
// creating a record from the var will unpack it if it's just a single field.
|
||||||
let layout = match layout_cache.from_var(env.arena, record_var, env.subs) {
|
let layout = match layout_cache.from_var(env.arena, record_var, env.subs) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"),
|
Err(_) => return runtime_error(env, "Can't create record with improper layout"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_symbols = field_symbols.into_bump_slice();
|
let field_symbols = field_symbols.into_bump_slice();
|
||||||
|
@ -4531,8 +4566,8 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Err(_), _) => Stmt::RuntimeError("invalid ret_layout"),
|
(Err(_), _) => runtime_error(env, "invalid ret_layout"),
|
||||||
(_, Err(_)) => Stmt::RuntimeError("invalid cond_layout"),
|
(_, Err(_)) => runtime_error(env, "invalid cond_layout"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4698,7 +4733,7 @@ pub fn with_hole<'a>(
|
||||||
};
|
};
|
||||||
let sorted_fields = match sorted_fields_result {
|
let sorted_fields = match sorted_fields_result {
|
||||||
Ok(fields) => fields,
|
Ok(fields) => fields,
|
||||||
Err(_) => return Stmt::RuntimeError("Can't access record with improper layout"),
|
Err(_) => return runtime_error(env, "Can't access record with improper layout"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut index = None;
|
let mut index = None;
|
||||||
|
@ -4816,7 +4851,8 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(_error) => Stmt::RuntimeError(
|
Err(_error) => runtime_error(
|
||||||
|
env,
|
||||||
"TODO convert anonymous function error to a RuntimeError string",
|
"TODO convert anonymous function error to a RuntimeError string",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -4872,7 +4908,8 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(_error) => Stmt::RuntimeError(
|
Err(_error) => runtime_error(
|
||||||
|
env,
|
||||||
"TODO convert anonymous function error to a RuntimeError string",
|
"TODO convert anonymous function error to a RuntimeError string",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -4907,7 +4944,7 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
let sorted_fields = match sorted_fields_result {
|
let sorted_fields = match sorted_fields_result {
|
||||||
Ok(fields) => fields,
|
Ok(fields) => fields,
|
||||||
Err(_) => return Stmt::RuntimeError("Can't update record with improper layout"),
|
Err(_) => return runtime_error(env, "Can't update record with improper layout"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
@ -5093,10 +5130,10 @@ pub fn with_hole<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(runtime_error) = inserted {
|
if let Err(e) = inserted {
|
||||||
return Stmt::RuntimeError(
|
return runtime_error(
|
||||||
env.arena
|
env,
|
||||||
.alloc(format!("RuntimeError: {:?}", runtime_error,)),
|
env.arena.alloc(format!("RuntimeError: {:?}", e,)),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
drop(inserted);
|
drop(inserted);
|
||||||
|
@ -5560,8 +5597,20 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypedHole(_) => Stmt::RuntimeError("Hit a blank"),
|
TypedHole(_) => runtime_error(env, "Hit a blank"),
|
||||||
RuntimeError(e) => Stmt::RuntimeError(env.arena.alloc(e.runtime_message())),
|
RuntimeError(e) => runtime_error(env, env.arena.alloc(e.runtime_message())),
|
||||||
|
Crash { msg, ret_var: _ } => {
|
||||||
|
let msg_sym = possible_reuse_symbol_or_specialize(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
&msg.value,
|
||||||
|
Variable::STR,
|
||||||
|
);
|
||||||
|
let stmt = Stmt::Crash(msg_sym, CrashTag::User);
|
||||||
|
|
||||||
|
assign_to_symbol(env, procs, layout_cache, Variable::STR, *msg, msg_sym, stmt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5820,16 +5869,22 @@ fn convert_tag_union<'a>(
|
||||||
let variant = match res_variant {
|
let variant = match res_variant {
|
||||||
Ok(cached) => cached,
|
Ok(cached) => cached,
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||||
return Stmt::RuntimeError(env.arena.alloc(format!(
|
return runtime_error(
|
||||||
|
env,
|
||||||
|
env.arena.alloc(format!(
|
||||||
"Unresolved type variable for tag {}",
|
"Unresolved type variable for tag {}",
|
||||||
tag_name.0.as_str()
|
tag_name.0.as_str()
|
||||||
)))
|
)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Err(LayoutProblem::Erroneous) => {
|
Err(LayoutProblem::Erroneous) => {
|
||||||
return Stmt::RuntimeError(env.arena.alloc(format!(
|
return runtime_error(
|
||||||
|
env,
|
||||||
|
env.arena.alloc(format!(
|
||||||
"Tag {} was part of a type error!",
|
"Tag {} was part of a type error!",
|
||||||
tag_name.0.as_str()
|
tag_name.0.as_str()
|
||||||
)));
|
)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5857,7 +5912,7 @@ fn convert_tag_union<'a>(
|
||||||
Layout::Builtin(Builtin::Int(IntWidth::U8)),
|
Layout::Builtin(Builtin::Int(IntWidth::U8)),
|
||||||
hole,
|
hole,
|
||||||
),
|
),
|
||||||
None => Stmt::RuntimeError("tag must be in its own type"),
|
None => runtime_error(env, "tag must be in its own type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5897,7 +5952,7 @@ fn convert_tag_union<'a>(
|
||||||
|
|
||||||
if dataful_tag != tag_name {
|
if dataful_tag != tag_name {
|
||||||
// this tag is not represented, and hence will never be reached, at runtime.
|
// this tag is not represented, and hence will never be reached, at runtime.
|
||||||
Stmt::RuntimeError("voided tag constructor is unreachable")
|
runtime_error(env, "voided tag constructor is unreachable")
|
||||||
} else {
|
} else {
|
||||||
let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args);
|
let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args);
|
||||||
|
|
||||||
|
@ -6161,10 +6216,13 @@ fn tag_union_to_function<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(runtime_error) => Stmt::RuntimeError(env.arena.alloc(format!(
|
Err(e) => runtime_error(
|
||||||
|
env,
|
||||||
|
env.arena.alloc(format!(
|
||||||
"Could not produce tag function due to a runtime error: {:?}",
|
"Could not produce tag function due to a runtime error: {:?}",
|
||||||
runtime_error,
|
e,
|
||||||
))),
|
)),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6722,7 +6780,7 @@ fn from_can_when<'a>(
|
||||||
if branches.is_empty() {
|
if branches.is_empty() {
|
||||||
// A when-expression with no branches is a runtime error.
|
// A when-expression with no branches is a runtime error.
|
||||||
// We can't know what to return!
|
// We can't know what to return!
|
||||||
return Stmt::RuntimeError("Hit a 0-branch when expression");
|
return runtime_error(env, "Hit a 0-branch when expression");
|
||||||
}
|
}
|
||||||
let opt_branches = to_opt_branches(env, procs, branches, exhaustive_mark, layout_cache);
|
let opt_branches = to_opt_branches(env, procs, branches, exhaustive_mark, layout_cache);
|
||||||
|
|
||||||
|
@ -7025,8 +7083,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Crash(msg, tag) => substitute(subs, *msg).map(|new| &*arena.alloc(Crash(new, *tag))),
|
||||||
RuntimeError(_) => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8409,7 +8466,7 @@ fn evaluate_arguments_then_runtime_error<'a>(
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
// eventually we will throw this runtime error
|
// eventually we will throw this runtime error
|
||||||
let result = Stmt::RuntimeError(env.arena.alloc(msg));
|
let result = runtime_error(env, env.arena.alloc(msg));
|
||||||
|
|
||||||
// but, we also still evaluate and specialize the arguments to give better error messages
|
// but, we also still evaluate and specialize the arguments to give better error messages
|
||||||
let arg_symbols = Vec::from_iter_in(
|
let arg_symbols = Vec::from_iter_in(
|
||||||
|
@ -8634,7 +8691,7 @@ fn call_by_name_help<'a>(
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// One of this function's arguments code gens to a runtime error,
|
// One of this function's arguments code gens to a runtime error,
|
||||||
// so attempting to call it will immediately crash.
|
// so attempting to call it will immediately crash.
|
||||||
return Stmt::RuntimeError("TODO runtime error for invalid layout");
|
return runtime_error(env, "TODO runtime error for invalid layout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10133,7 +10190,7 @@ where
|
||||||
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
match lambda_set.call_by_name_options(&layout_cache.interner) {
|
match lambda_set.call_by_name_options(&layout_cache.interner) {
|
||||||
ClosureCallOptions::Void => empty_lambda_set_error(),
|
ClosureCallOptions::Void => empty_lambda_set_error(env),
|
||||||
ClosureCallOptions::Union(union_layout) => {
|
ClosureCallOptions::Union(union_layout) => {
|
||||||
let closure_tag_id_symbol = env.unique_symbol();
|
let closure_tag_id_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
@ -10312,9 +10369,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_lambda_set_error() -> Stmt<'static> {
|
fn empty_lambda_set_error<'a>(env: &mut Env<'a, '_>) -> Stmt<'a> {
|
||||||
let msg = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
let msg = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||||
Stmt::RuntimeError(msg)
|
runtime_error(env, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use the lambda set to figure out how to make a call-by-name
|
/// Use the lambda set to figure out how to make a call-by-name
|
||||||
|
@ -10332,7 +10389,7 @@ fn match_on_lambda_set<'a>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
match lambda_set.call_by_name_options(&layout_cache.interner) {
|
match lambda_set.call_by_name_options(&layout_cache.interner) {
|
||||||
ClosureCallOptions::Void => empty_lambda_set_error(),
|
ClosureCallOptions::Void => empty_lambda_set_error(env),
|
||||||
ClosureCallOptions::Union(union_layout) => {
|
ClosureCallOptions::Union(union_layout) => {
|
||||||
let closure_tag_id_symbol = env.unique_symbol();
|
let closure_tag_id_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
@ -10486,7 +10543,7 @@ fn union_lambda_set_to_switch<'a>(
|
||||||
// there is really nothing we can do here. We generate a runtime error here which allows
|
// 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
|
// code gen to proceed. We then assume that we hit another (more descriptive) error before
|
||||||
// hitting this one
|
// hitting this one
|
||||||
return empty_lambda_set_error();
|
return empty_lambda_set_error(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
let join_point_id = JoinPointId(env.unique_symbol());
|
let join_point_id = JoinPointId(env.unique_symbol());
|
||||||
|
|
|
@ -241,7 +241,7 @@ fn function_s<'a, 'i>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
Ret(_) | Jump(_, _) | Crash(..) => stmt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +535,7 @@ fn function_d_main<'a, 'i>(
|
||||||
|
|
||||||
(arena.alloc(new_join), found)
|
(arena.alloc(new_join), found)
|
||||||
}
|
}
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => (stmt, has_live_var(&env.jp_live_vars, stmt, x)),
|
Ret(_) | Jump(_, _) | Crash(..) => (stmt, has_live_var(&env.jp_live_vars, stmt, x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +696,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||||
arena.alloc(expect)
|
arena.alloc(expect)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | Crash(..) => {
|
||||||
// terminals
|
// terminals
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
@ -761,7 +761,7 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
||||||
Jump(id, arguments) => {
|
Jump(id, arguments) => {
|
||||||
arguments.iter().any(|s| *s == needle) || jp_live_vars[id].contains(&needle)
|
arguments.iter().any(|s| *s == needle) || jp_live_vars[id].contains(&needle)
|
||||||
}
|
}
|
||||||
RuntimeError(_) => false,
|
Crash(m, _) => *m == needle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,6 +299,6 @@ fn insert_jumps<'a>(
|
||||||
|
|
||||||
Ret(_) => None,
|
Ret(_) => None,
|
||||||
Jump(_, _) => None,
|
Jump(_, _) => None,
|
||||||
RuntimeError(_) => None,
|
Crash(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,6 +196,9 @@ pub enum Expr<'a> {
|
||||||
|
|
||||||
Underscore(&'a str),
|
Underscore(&'a str),
|
||||||
|
|
||||||
|
// The "crash" keyword
|
||||||
|
Crash,
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
Tag(&'a str),
|
Tag(&'a str),
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,7 @@ fn loc_term_or_underscore_or_conditional<'a>(
|
||||||
loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())),
|
loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())),
|
||||||
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||||
loc!(specialize(EExpr::Closure, closure_help(options))),
|
loc!(specialize(EExpr::Closure, closure_help(options))),
|
||||||
|
loc!(crash_kw()),
|
||||||
loc!(underscore_expression()),
|
loc!(underscore_expression()),
|
||||||
loc!(record_literal_help()),
|
loc!(record_literal_help()),
|
||||||
loc!(specialize(EExpr::List, list_literal_help())),
|
loc!(specialize(EExpr::List, list_literal_help())),
|
||||||
|
@ -238,6 +239,15 @@ fn underscore_expression<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn crash_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||||
|
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
|
||||||
|
let (_, _, next_state) = crate::parser::keyword_e(crate::keyword::CRASH, EExpr::Crash)
|
||||||
|
.parse(arena, state, min_indent)?;
|
||||||
|
|
||||||
|
Ok((MadeProgress, Expr::Crash, next_state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn loc_possibly_negative_or_negated_term<'a>(
|
fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
options: ExprParseOptions,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||||
|
@ -1886,7 +1896,8 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||||
| Expr::MalformedClosure
|
| Expr::MalformedClosure
|
||||||
| Expr::PrecedenceConflict { .. }
|
| Expr::PrecedenceConflict { .. }
|
||||||
| Expr::RecordUpdate { .. }
|
| Expr::RecordUpdate { .. }
|
||||||
| Expr::UnaryOp(_, _) => Err(()),
|
| Expr::UnaryOp(_, _)
|
||||||
|
| Expr::Crash => Err(()),
|
||||||
|
|
||||||
Expr::Str(string) => Ok(Pattern::StrLiteral(*string)),
|
Expr::Str(string) => Ok(Pattern::StrLiteral(*string)),
|
||||||
Expr::SingleQuote(string) => Ok(Pattern::SingleQuote(string)),
|
Expr::SingleQuote(string) => Ok(Pattern::SingleQuote(string)),
|
||||||
|
|
|
@ -7,5 +7,6 @@ pub const IS: &str = "is";
|
||||||
pub const DBG: &str = "dbg";
|
pub const DBG: &str = "dbg";
|
||||||
pub const EXPECT: &str = "expect";
|
pub const EXPECT: &str = "expect";
|
||||||
pub const EXPECT_FX: &str = "expect-fx";
|
pub const EXPECT_FX: &str = "expect-fx";
|
||||||
|
pub const CRASH: &str = "crash";
|
||||||
|
|
||||||
pub const KEYWORDS: [&str; 8] = [IF, THEN, ELSE, WHEN, AS, IS, EXPECT, EXPECT_FX];
|
pub const KEYWORDS: [&str; 10] = [IF, THEN, ELSE, WHEN, AS, IS, DBG, EXPECT, EXPECT_FX, CRASH];
|
||||||
|
|
|
@ -358,6 +358,7 @@ pub enum EExpr<'a> {
|
||||||
|
|
||||||
Closure(EClosure<'a>, Position),
|
Closure(EClosure<'a>, Position),
|
||||||
Underscore(Position),
|
Underscore(Position),
|
||||||
|
Crash(Position),
|
||||||
|
|
||||||
InParens(EInParens<'a>, Position),
|
InParens(EInParens<'a>, Position),
|
||||||
Record(ERecord<'a>, Position),
|
Record(ERecord<'a>, Position),
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
_ = crash ""
|
||||||
|
_ = crash "" ""
|
||||||
|
_ = crash 15 123
|
||||||
|
_ = try foo (\_ -> crash "")
|
||||||
|
_ =
|
||||||
|
_ = crash ""
|
||||||
|
|
||||||
|
crash
|
||||||
|
|
||||||
|
{ f: crash "" }
|
210
crates/compiler/parse/tests/snapshots/pass/crash.expr.result-ast
Normal file
210
crates/compiler/parse/tests/snapshots/pass/crash.expr.result-ast
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(2147483648),
|
||||||
|
Index(2147483649),
|
||||||
|
Index(2147483650),
|
||||||
|
Index(2147483651),
|
||||||
|
Index(2147483652),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-12,
|
||||||
|
@13-28,
|
||||||
|
@29-45,
|
||||||
|
@46-74,
|
||||||
|
@75-101,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
Slice(start = 0, length = 1),
|
||||||
|
Slice(start = 1, length = 1),
|
||||||
|
Slice(start = 2, length = 1),
|
||||||
|
Slice(start = 3, length = 1),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
Slice(start = 1, length = 0),
|
||||||
|
Slice(start = 2, length = 0),
|
||||||
|
Slice(start = 3, length = 0),
|
||||||
|
Slice(start = 4, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [
|
||||||
|
Newline,
|
||||||
|
Newline,
|
||||||
|
Newline,
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Body(
|
||||||
|
@0-1 Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
@4-12 Apply(
|
||||||
|
@4-9 Crash,
|
||||||
|
[
|
||||||
|
@10-12 Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Body(
|
||||||
|
@13-14 Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
@17-28 Apply(
|
||||||
|
@17-22 Crash,
|
||||||
|
[
|
||||||
|
@23-25 Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
@26-28 Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Body(
|
||||||
|
@29-30 Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
@33-45 Apply(
|
||||||
|
@33-38 Crash,
|
||||||
|
[
|
||||||
|
@39-41 Num(
|
||||||
|
"15",
|
||||||
|
),
|
||||||
|
@42-45 Num(
|
||||||
|
"123",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Body(
|
||||||
|
@46-47 Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
@50-74 Apply(
|
||||||
|
@50-53 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "try",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
@54-57 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "foo",
|
||||||
|
},
|
||||||
|
@59-73 ParensAround(
|
||||||
|
Closure(
|
||||||
|
[
|
||||||
|
@60-61 Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@65-73 Apply(
|
||||||
|
@65-70 Crash,
|
||||||
|
[
|
||||||
|
@71-73 Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Body(
|
||||||
|
@75-76 Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
@81-101 SpaceBefore(
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@81-93,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Body(
|
||||||
|
@81-82 Underscore(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
@85-93 Apply(
|
||||||
|
@85-90 Crash,
|
||||||
|
[
|
||||||
|
@91-93 Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@96-101 SpaceBefore(
|
||||||
|
Crash,
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@103-118 SpaceBefore(
|
||||||
|
Record(
|
||||||
|
[
|
||||||
|
@105-116 RequiredValue(
|
||||||
|
@105-106 "f",
|
||||||
|
[],
|
||||||
|
@108-116 Apply(
|
||||||
|
@108-113 Crash,
|
||||||
|
[
|
||||||
|
@114-116 Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
_ = crash ""
|
||||||
|
_ = crash "" ""
|
||||||
|
_ = crash 15 123
|
||||||
|
_ = try foo (\_ -> crash "")
|
||||||
|
_ =
|
||||||
|
_ = crash ""
|
||||||
|
crash
|
||||||
|
|
||||||
|
{ f: crash "" }
|
|
@ -156,6 +156,7 @@ mod test_parse {
|
||||||
pass/comment_before_op.expr,
|
pass/comment_before_op.expr,
|
||||||
pass/comment_inside_empty_list.expr,
|
pass/comment_inside_empty_list.expr,
|
||||||
pass/comment_with_non_ascii.expr,
|
pass/comment_with_non_ascii.expr,
|
||||||
|
pass/crash.expr,
|
||||||
pass/destructure_tag_assignment.expr,
|
pass/destructure_tag_assignment.expr,
|
||||||
pass/empty_app_header.header,
|
pass/empty_app_header.header,
|
||||||
pass/empty_hosted_header.header,
|
pass/empty_hosted_header.header,
|
||||||
|
|
|
@ -195,6 +195,12 @@ pub enum Problem {
|
||||||
type_got: u8,
|
type_got: u8,
|
||||||
alias_kind: AliasKind,
|
alias_kind: AliasKind,
|
||||||
},
|
},
|
||||||
|
UnappliedCrash {
|
||||||
|
region: Region,
|
||||||
|
},
|
||||||
|
OverAppliedCrash {
|
||||||
|
region: Region,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Problem {
|
impl Problem {
|
||||||
|
@ -325,7 +331,9 @@ impl Problem {
|
||||||
}
|
}
|
||||||
| Problem::MultipleListRestPattern { region }
|
| Problem::MultipleListRestPattern { region }
|
||||||
| Problem::BadTypeArguments { region, .. }
|
| Problem::BadTypeArguments { region, .. }
|
||||||
| Problem::UnnecessaryOutputWildcard { region } => Some(*region),
|
| Problem::UnnecessaryOutputWildcard { region }
|
||||||
|
| Problem::OverAppliedCrash { region }
|
||||||
|
| Problem::UnappliedCrash { region } => Some(*region),
|
||||||
Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries))
|
Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries))
|
||||||
| Problem::BadRecursion(cycle_entries) => {
|
| Problem::BadRecursion(cycle_entries) => {
|
||||||
cycle_entries.first().map(|entry| entry.expr_region)
|
cycle_entries.first().map(|entry| entry.expr_region)
|
||||||
|
|
|
@ -8352,4 +8352,20 @@ mod solve_expr {
|
||||||
@"translateStatic : [Element (List a)] as a -[[translateStatic(0)]]-> [Element (List b)]* as b"
|
@"translateStatic : [Element (List a)] as a -[[translateStatic(0)]]-> [Element (List b)]* as b"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_contextual_crash() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [getInfallible] to "./platform"
|
||||||
|
|
||||||
|
getInfallible = \result -> when result is
|
||||||
|
Ok x -> x
|
||||||
|
_ -> crash "turns out this was fallible"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"[Ok a]* -> a",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,6 +279,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||||
)
|
)
|
||||||
.group()
|
.group()
|
||||||
),
|
),
|
||||||
|
Crash { .. } => todo!(),
|
||||||
ZeroArgumentTag { .. } => todo!(),
|
ZeroArgumentTag { .. } => todo!(),
|
||||||
OpaqueRef { .. } => todo!(),
|
OpaqueRef { .. } => todo!(),
|
||||||
Dbg { .. } => todo!(),
|
Dbg { .. } => todo!(),
|
||||||
|
|
85
crates/compiler/test_gen/src/gen_panic.rs
Normal file
85
crates/compiler/test_gen/src/gen_panic.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
use indoc::indoc;
|
||||||
|
use roc_std::RocList;
|
||||||
|
|
||||||
|
#[cfg(feature = "gen-llvm")]
|
||||||
|
use crate::helpers::llvm::assert_evals_to;
|
||||||
|
|
||||||
|
#[cfg(feature = "gen-wasm")]
|
||||||
|
use crate::helpers::wasm::assert_evals_to;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
#[should_panic = r#"User crash with message: "hello crash""#]
|
||||||
|
fn crash_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main = if Bool.true then crash "hello crash" else 1u8
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1u8,
|
||||||
|
u8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
#[should_panic = r#"User crash with message: "hello crash""#]
|
||||||
|
fn crash_variable() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
msg = "hello crash"
|
||||||
|
if Bool.true then crash msg else 1u8
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1u8,
|
||||||
|
u8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
#[should_panic = r#"User crash with message: "turns out this was fallible""#]
|
||||||
|
fn crash_in_call() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
getInfallible = \result -> when result is
|
||||||
|
Ok x -> x
|
||||||
|
_ -> crash "turns out this was fallible"
|
||||||
|
|
||||||
|
main =
|
||||||
|
x : [Ok U64, Err Str]
|
||||||
|
x = Err ""
|
||||||
|
getInfallible x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1u64,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
#[should_panic = r#"User crash with message: "no new even primes""#]
|
||||||
|
fn crash_in_passed_closure() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main = List.map [1, 2, 3] \n -> if n == 2 then crash "no new even primes" else ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[1u8]),
|
||||||
|
RocList<u8>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
#[cfg(feature = "gen-llvm")]
|
#[cfg(feature = "gen-llvm")]
|
||||||
use crate::helpers::llvm::{assert_evals_to, expect_runtime_error_panic};
|
use crate::helpers::llvm::assert_evals_to;
|
||||||
|
|
||||||
#[cfg(feature = "gen-dev")]
|
#[cfg(feature = "gen-dev")]
|
||||||
use crate::helpers::dev::assert_evals_to;
|
use crate::helpers::dev::assert_evals_to;
|
||||||
|
|
||||||
#[cfg(feature = "gen-wasm")]
|
#[cfg(feature = "gen-wasm")]
|
||||||
use crate::helpers::wasm::{assert_evals_to, expect_runtime_error_panic};
|
use crate::helpers::wasm::assert_evals_to;
|
||||||
|
|
||||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
// use crate::assert_wasm_evals_to as assert_evals_to;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
@ -1019,7 +1019,8 @@ fn different_proc_types_specialized_to_same_layout() {
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
#[should_panic(expected = r#"Roc failed with message: "Can't create record with improper layout""#)]
|
#[should_panic(expected = r#"Roc failed with message: "Can't create record with improper layout""#)]
|
||||||
fn call_with_bad_record_runtime_error() {
|
fn call_with_bad_record_runtime_error() {
|
||||||
expect_runtime_error_panic!(indoc!(
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [main] to "./platform"
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
@ -1028,7 +1029,12 @@ fn call_with_bad_record_runtime_error() {
|
||||||
get = \{a} -> a
|
get = \{a} -> a
|
||||||
get {b: ""}
|
get {b: ""}
|
||||||
"#
|
"#
|
||||||
))
|
),
|
||||||
|
true,
|
||||||
|
bool,
|
||||||
|
|x| x,
|
||||||
|
true // ignore type errors
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_collections::all::MutSet;
|
||||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult};
|
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult};
|
||||||
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
|
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::{CrashTag, OptLevel};
|
||||||
use roc_region::all::LineInfo;
|
use roc_region::all::LineInfo;
|
||||||
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
|
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
|
||||||
use roc_utils::zig;
|
use roc_utils::zig;
|
||||||
|
@ -544,7 +544,10 @@ macro_rules! assert_wasm_evals_to {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn try_run_lib_function<T>(main_fn_name: &str, lib: &libloading::Library) -> Result<T, String> {
|
pub fn try_run_lib_function<T>(
|
||||||
|
main_fn_name: &str,
|
||||||
|
lib: &libloading::Library,
|
||||||
|
) -> Result<T, (String, CrashTag)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<T>)> = lib
|
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<T>)> = lib
|
||||||
.get(main_fn_name.as_bytes())
|
.get(main_fn_name.as_bytes())
|
||||||
|
@ -565,6 +568,7 @@ macro_rules! assert_llvm_evals_to {
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||||
|
use roc_mono::ir::CrashTag;
|
||||||
|
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
|
@ -594,7 +598,10 @@ macro_rules! assert_llvm_evals_to {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
std::mem::forget(given);
|
std::mem::forget(given);
|
||||||
}
|
}
|
||||||
Err(msg) => panic!("Roc failed with message: \"{}\"", msg),
|
Err((msg, tag)) => match tag {
|
||||||
|
CrashTag::Roc => panic!(r#"Roc failed with message: "{}""#, msg),
|
||||||
|
CrashTag::User => panic!(r#"User crash with message: "{}""#, msg),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// artificially extend the lifetime of `lib`
|
// artificially extend the lifetime of `lib`
|
||||||
|
@ -655,29 +662,6 @@ macro_rules! assert_evals_to {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! expect_runtime_error_panic {
|
|
||||||
($src:expr) => {{
|
|
||||||
#[cfg(feature = "gen-llvm-wasm")]
|
|
||||||
$crate::helpers::llvm::assert_wasm_evals_to!(
|
|
||||||
$src,
|
|
||||||
false, // fake value/type for eval
|
|
||||||
bool,
|
|
||||||
$crate::helpers::llvm::identity,
|
|
||||||
true // ignore problems
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "gen-llvm-wasm"))]
|
|
||||||
$crate::helpers::llvm::assert_llvm_evals_to!(
|
|
||||||
$src,
|
|
||||||
false, // fake value/type for eval
|
|
||||||
bool,
|
|
||||||
$crate::helpers::llvm::identity,
|
|
||||||
true // ignore problems
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn identity<T>(value: T) -> T {
|
pub fn identity<T>(value: T) -> T {
|
||||||
value
|
value
|
||||||
|
@ -689,5 +673,3 @@ pub(crate) use assert_evals_to;
|
||||||
pub(crate) use assert_llvm_evals_to;
|
pub(crate) use assert_llvm_evals_to;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) use assert_wasm_evals_to;
|
pub(crate) use assert_wasm_evals_to;
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub(crate) use expect_runtime_error_panic;
|
|
||||||
|
|
|
@ -32,23 +32,3 @@ pub unsafe fn roc_realloc(
|
||||||
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||||
libc::free(c_ptr)
|
libc::free(c_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
/// The Roc application needs this.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
|
||||||
use roc_gen_llvm::llvm::build::PanicTagId;
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
|
|
||||||
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!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ use roc_gen_wasm::wasm32_result::Wasm32Result;
|
||||||
use roc_gen_wasm::DEBUG_SETTINGS;
|
use roc_gen_wasm::DEBUG_SETTINGS;
|
||||||
use roc_load::{ExecutionMode, LoadConfig, Threading};
|
use roc_load::{ExecutionMode, LoadConfig, Threading};
|
||||||
use roc_reporting::report::DEFAULT_PALETTE_HTML;
|
use roc_reporting::report::DEFAULT_PALETTE_HTML;
|
||||||
|
use roc_std::RocStr;
|
||||||
use roc_wasm_module::{Export, ExportType};
|
use roc_wasm_module::{Export, ExportType};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -193,7 +194,7 @@ where
|
||||||
|
|
||||||
let parsed = Module::parse(&env, &wasm_bytes[..]).expect("Unable to parse module");
|
let parsed = Module::parse(&env, &wasm_bytes[..]).expect("Unable to parse module");
|
||||||
let mut module = rt.load_module(parsed).expect("Unable to load module");
|
let mut module = rt.load_module(parsed).expect("Unable to load module");
|
||||||
let panic_msg: Rc<Mutex<Option<(i32, i32)>>> = Default::default();
|
let panic_msg: Rc<Mutex<Option<(i32, u32)>>> = Default::default();
|
||||||
link_module(&mut module, panic_msg.clone());
|
link_module(&mut module, panic_msg.clone());
|
||||||
|
|
||||||
let test_wrapper = module
|
let test_wrapper = module
|
||||||
|
@ -202,12 +203,18 @@ where
|
||||||
|
|
||||||
match test_wrapper.call() {
|
match test_wrapper.call() {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some((msg_ptr, msg_len)) = *panic_msg.lock().unwrap() {
|
if let Some((msg_ptr, tag)) = *panic_msg.lock().unwrap() {
|
||||||
let memory: &[u8] = get_memory(&rt);
|
let memory: &[u8] = get_memory(&rt);
|
||||||
let msg_bytes = &memory[msg_ptr as usize..][..msg_len as usize];
|
let msg = RocStr::decode(memory, msg_ptr as _);
|
||||||
let msg = std::str::from_utf8(msg_bytes).unwrap();
|
|
||||||
|
|
||||||
Err(format!("Roc failed with message: \"{}\"", msg))
|
dbg!(tag);
|
||||||
|
let msg = match tag {
|
||||||
|
0 => format!(r#"Roc failed with message: "{}""#, msg),
|
||||||
|
1 => format!(r#"User crash with message: "{}""#, msg),
|
||||||
|
tag => format!(r#"Got an invald panic tag: "{}""#, tag),
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(msg)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("{}", e))
|
Err(format!("{}", e))
|
||||||
}
|
}
|
||||||
|
@ -253,7 +260,7 @@ where
|
||||||
let parsed = Module::parse(&env, wasm_bytes).expect("Unable to parse module");
|
let parsed = Module::parse(&env, wasm_bytes).expect("Unable to parse module");
|
||||||
let mut module = rt.load_module(parsed).expect("Unable to load module");
|
let mut module = rt.load_module(parsed).expect("Unable to load module");
|
||||||
|
|
||||||
let panic_msg: Rc<Mutex<Option<(i32, i32)>>> = Default::default();
|
let panic_msg: Rc<Mutex<Option<(i32, u32)>>> = Default::default();
|
||||||
link_module(&mut module, panic_msg.clone());
|
link_module(&mut module, panic_msg.clone());
|
||||||
|
|
||||||
let expected_len = num_refcounts as i32;
|
let expected_len = num_refcounts as i32;
|
||||||
|
@ -316,13 +323,13 @@ fn read_i32(memory: &[u8], ptr: usize) -> i32 {
|
||||||
i32::from_le_bytes(bytes)
|
i32::from_le_bytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_module(module: &mut Module, panic_msg: Rc<Mutex<Option<(i32, i32)>>>) {
|
fn link_module(module: &mut Module, panic_msg: Rc<Mutex<Option<(i32, u32)>>>) {
|
||||||
let try_link_panic = module.link_closure(
|
let try_link_panic = module.link_closure(
|
||||||
"env",
|
"env",
|
||||||
"send_panic_msg_to_rust",
|
"send_panic_msg_to_rust",
|
||||||
move |_call_context, args: (i32, i32)| {
|
move |_call_context, (msg_ptr, tag): (i32, u32)| {
|
||||||
let mut w = panic_msg.lock().unwrap();
|
let mut w = panic_msg.lock().unwrap();
|
||||||
*w = Some(args);
|
*w = Some((msg_ptr, tag));
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -390,19 +397,6 @@ macro_rules! assert_evals_to {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! expect_runtime_error_panic {
|
|
||||||
($src:expr) => {{
|
|
||||||
$crate::helpers::wasm::assert_evals_to!(
|
|
||||||
$src,
|
|
||||||
false, // fake value/type for eval
|
|
||||||
bool,
|
|
||||||
$crate::helpers::wasm::identity,
|
|
||||||
true // ignore problems
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn identity<T>(value: T) -> T {
|
pub fn identity<T>(value: T) -> T {
|
||||||
value
|
value
|
||||||
|
@ -430,8 +424,5 @@ macro_rules! assert_refcounts {
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) use assert_evals_to;
|
pub(crate) use assert_evals_to;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub(crate) use expect_runtime_error_panic;
|
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) use assert_refcounts;
|
pub(crate) use assert_refcounts;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -123,12 +124,11 @@ void roc_dealloc(void *ptr, unsigned int alignment)
|
||||||
|
|
||||||
//--------------------------
|
//--------------------------
|
||||||
|
|
||||||
extern void send_panic_msg_to_rust(char* msg, int len);
|
extern void send_panic_msg_to_rust(void* msg, uint32_t tag_id);
|
||||||
|
|
||||||
void roc_panic(char *msg, unsigned int tag_id)
|
void roc_panic(void* msg, unsigned int tag_id)
|
||||||
{
|
{
|
||||||
int len = strlen(msg);
|
send_panic_msg_to_rust(msg, tag_id);
|
||||||
send_panic_msg_to_rust(msg, len);
|
|
||||||
exit(101);
|
exit(101);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub mod gen_compare;
|
||||||
pub mod gen_dict;
|
pub mod gen_dict;
|
||||||
pub mod gen_list;
|
pub mod gen_list;
|
||||||
pub mod gen_num;
|
pub mod gen_num;
|
||||||
|
pub mod gen_panic;
|
||||||
pub mod gen_primitives;
|
pub mod gen_primitives;
|
||||||
pub mod gen_records;
|
pub mod gen_records;
|
||||||
pub mod gen_refcount;
|
pub mod gen_refcount;
|
||||||
|
|
|
@ -5,7 +5,8 @@ procedure List.5 (#Attr.2, #Attr.3):
|
||||||
|
|
||||||
procedure Test.2 (Test.3):
|
procedure Test.2 (Test.3):
|
||||||
let Test.7 : {} = Struct {};
|
let Test.7 : {} = Struct {};
|
||||||
Error a Lambda Set is empty. Most likely there is a type error in your program.
|
let Test.8 : Str = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||||
|
Crash Test.8
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.1 : List [] = Array [];
|
let Test.1 : List [] = Array [];
|
||||||
|
|
|
@ -5,7 +5,8 @@ procedure List.5 (#Attr.2, #Attr.3):
|
||||||
|
|
||||||
procedure Test.2 (Test.3):
|
procedure Test.2 (Test.3):
|
||||||
let Test.7 : {} = Struct {};
|
let Test.7 : {} = Struct {};
|
||||||
Error a Lambda Set is empty. Most likely there is a type error in your program.
|
let Test.8 : Str = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||||
|
Crash Test.8
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.1 : List [] = Array [];
|
let Test.1 : List [] = Array [];
|
||||||
|
|
18
crates/compiler/test_mono/generated/crash.txt
Normal file
18
crates/compiler/test_mono/generated/crash.txt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
procedure Test.1 (Test.2):
|
||||||
|
let Test.10 : U8 = 1i64;
|
||||||
|
let Test.11 : U8 = GetTagId Test.2;
|
||||||
|
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
|
||||||
|
if Test.12 then
|
||||||
|
let Test.3 : U64 = UnionAtIndex (Id 1) (Index 0) Test.2;
|
||||||
|
dec Test.2;
|
||||||
|
ret Test.3;
|
||||||
|
else
|
||||||
|
dec Test.2;
|
||||||
|
let Test.9 : Str = "turns out this was fallible";
|
||||||
|
Crash Test.9
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.13 : U64 = 78i64;
|
||||||
|
let Test.4 : [C Str, C U64] = TagId(1) Test.13;
|
||||||
|
let Test.6 : U64 = CallByName Test.1 Test.4;
|
||||||
|
ret Test.6;
|
|
@ -205,58 +205,58 @@ procedure Json.96 (Json.97, Json.473, Json.95):
|
||||||
ret Json.475;
|
ret Json.475;
|
||||||
|
|
||||||
procedure List.137 (List.138, List.139, List.136):
|
procedure List.137 (List.138, List.139, List.136):
|
||||||
let List.449 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
let List.450 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
||||||
ret List.449;
|
ret List.450;
|
||||||
|
|
||||||
procedure List.137 (List.138, List.139, List.136):
|
procedure List.137 (List.138, List.139, List.136):
|
||||||
let List.521 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
let List.523 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
||||||
ret List.521;
|
ret List.523;
|
||||||
|
|
||||||
procedure List.18 (List.134, List.135, List.136):
|
procedure List.18 (List.134, List.135, List.136):
|
||||||
let List.431 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
let List.431 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
||||||
ret List.431;
|
ret List.431;
|
||||||
|
|
||||||
procedure List.18 (List.134, List.135, List.136):
|
procedure List.18 (List.134, List.135, List.136):
|
||||||
let List.503 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
let List.504 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
||||||
ret List.503;
|
ret List.504;
|
||||||
|
|
||||||
procedure List.4 (List.105, List.106):
|
procedure List.4 (List.105, List.106):
|
||||||
let List.502 : U64 = 1i64;
|
let List.503 : U64 = 1i64;
|
||||||
let List.501 : List U8 = CallByName List.70 List.105 List.502;
|
let List.502 : List U8 = CallByName List.70 List.105 List.503;
|
||||||
let List.500 : List U8 = CallByName List.71 List.501 List.106;
|
let List.501 : List U8 = CallByName List.71 List.502 List.106;
|
||||||
ret List.500;
|
ret List.501;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.409 : U64 = lowlevel ListLen #Attr.2;
|
let List.409 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.409;
|
ret List.409;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.451 : U64 = lowlevel ListLen #Attr.2;
|
let List.452 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.451;
|
ret List.452;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.524 : U64 = lowlevel ListLen #Attr.2;
|
let List.526 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.524;
|
ret List.526;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.446 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.447 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.446;
|
ret List.447;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.518 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.520 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.518;
|
ret List.520;
|
||||||
|
|
||||||
procedure List.70 (#Attr.2, #Attr.3):
|
procedure List.70 (#Attr.2, #Attr.3):
|
||||||
let List.481 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.482 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
ret List.481;
|
ret List.482;
|
||||||
|
|
||||||
procedure List.71 (#Attr.2, #Attr.3):
|
procedure List.71 (#Attr.2, #Attr.3):
|
||||||
let List.479 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
let List.480 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.479;
|
ret List.480;
|
||||||
|
|
||||||
procedure List.8 (#Attr.2, #Attr.3):
|
procedure List.8 (#Attr.2, #Attr.3):
|
||||||
let List.523 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
let List.525 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||||
ret List.523;
|
ret List.525;
|
||||||
|
|
||||||
procedure List.89 (List.385, List.386, List.387):
|
procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.435 : U64 = 0i64;
|
let List.435 : U64 = 0i64;
|
||||||
|
@ -265,38 +265,38 @@ procedure List.89 (List.385, List.386, List.387):
|
||||||
ret List.434;
|
ret List.434;
|
||||||
|
|
||||||
procedure List.89 (List.385, List.386, List.387):
|
procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.507 : U64 = 0i64;
|
let List.508 : U64 = 0i64;
|
||||||
let List.508 : U64 = CallByName List.6 List.385;
|
let List.509 : U64 = CallByName List.6 List.385;
|
||||||
let List.506 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.507 List.508;
|
let List.507 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.508 List.509;
|
||||||
ret List.506;
|
ret List.507;
|
||||||
|
|
||||||
procedure List.90 (List.461, List.462, List.463, List.464, List.465):
|
procedure List.90 (List.462, List.463, List.464, List.465, List.466):
|
||||||
joinpoint List.437 List.388 List.389 List.390 List.391 List.392:
|
joinpoint List.437 List.388 List.389 List.390 List.391 List.392:
|
||||||
let List.439 : Int1 = CallByName Num.22 List.391 List.392;
|
let List.439 : Int1 = CallByName Num.22 List.391 List.392;
|
||||||
if List.439 then
|
if List.439 then
|
||||||
let List.445 : {Str, Str} = CallByName List.66 List.388 List.391;
|
let List.446 : {Str, Str} = CallByName List.66 List.388 List.391;
|
||||||
let List.440 : {List U8, U64} = CallByName List.137 List.389 List.445 List.390;
|
let List.440 : {List U8, U64} = CallByName List.137 List.389 List.446 List.390;
|
||||||
let List.443 : U64 = 1i64;
|
let List.443 : U64 = 1i64;
|
||||||
let List.442 : U64 = CallByName Num.19 List.391 List.443;
|
let List.442 : U64 = CallByName Num.19 List.391 List.443;
|
||||||
jump List.437 List.388 List.440 List.390 List.442 List.392;
|
jump List.437 List.388 List.440 List.390 List.442 List.392;
|
||||||
else
|
else
|
||||||
ret List.389;
|
ret List.389;
|
||||||
in
|
in
|
||||||
jump List.437 List.461 List.462 List.463 List.464 List.465;
|
jump List.437 List.462 List.463 List.464 List.465 List.466;
|
||||||
|
|
||||||
procedure List.90 (List.534, List.535, List.536, List.537, List.538):
|
procedure List.90 (List.536, List.537, List.538, List.539, List.540):
|
||||||
joinpoint List.509 List.388 List.389 List.390 List.391 List.392:
|
joinpoint List.510 List.388 List.389 List.390 List.391 List.392:
|
||||||
let List.511 : Int1 = CallByName Num.22 List.391 List.392;
|
let List.512 : Int1 = CallByName Num.22 List.391 List.392;
|
||||||
if List.511 then
|
if List.512 then
|
||||||
let List.517 : {Str, Str} = CallByName List.66 List.388 List.391;
|
let List.519 : {Str, Str} = CallByName List.66 List.388 List.391;
|
||||||
let List.512 : {List U8, U64} = CallByName List.137 List.389 List.517 List.390;
|
let List.513 : {List U8, U64} = CallByName List.137 List.389 List.519 List.390;
|
||||||
let List.515 : U64 = 1i64;
|
let List.516 : U64 = 1i64;
|
||||||
let List.514 : U64 = CallByName Num.19 List.391 List.515;
|
let List.515 : U64 = CallByName Num.19 List.391 List.516;
|
||||||
jump List.509 List.388 List.512 List.390 List.514 List.392;
|
jump List.510 List.388 List.513 List.390 List.515 List.392;
|
||||||
else
|
else
|
||||||
ret List.389;
|
ret List.389;
|
||||||
in
|
in
|
||||||
jump List.509 List.534 List.535 List.536 List.537 List.538;
|
jump List.510 List.536 List.537 List.538 List.539 List.540;
|
||||||
|
|
||||||
procedure Num.125 (#Attr.2):
|
procedure Num.125 (#Attr.2):
|
||||||
let Num.282 : U8 = lowlevel NumIntCast #Attr.2;
|
let Num.282 : U8 = lowlevel NumIntCast #Attr.2;
|
||||||
|
|
|
@ -118,8 +118,8 @@ procedure Json.96 (Json.97, Json.433, Json.95):
|
||||||
ret Json.435;
|
ret Json.435;
|
||||||
|
|
||||||
procedure List.137 (List.138, List.139, List.136):
|
procedure List.137 (List.138, List.139, List.136):
|
||||||
let List.455 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
let List.456 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
||||||
ret List.455;
|
ret List.456;
|
||||||
|
|
||||||
procedure List.18 (List.134, List.135, List.136):
|
procedure List.18 (List.134, List.135, List.136):
|
||||||
let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
||||||
|
@ -136,12 +136,12 @@ procedure List.6 (#Attr.2):
|
||||||
ret List.409;
|
ret List.409;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.458 : U64 = lowlevel ListLen #Attr.2;
|
let List.459 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.458;
|
ret List.459;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.452 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.453 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.452;
|
ret List.453;
|
||||||
|
|
||||||
procedure List.70 (#Attr.2, #Attr.3):
|
procedure List.70 (#Attr.2, #Attr.3):
|
||||||
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
|
@ -152,8 +152,8 @@ procedure List.71 (#Attr.2, #Attr.3):
|
||||||
ret List.413;
|
ret List.413;
|
||||||
|
|
||||||
procedure List.8 (#Attr.2, #Attr.3):
|
procedure List.8 (#Attr.2, #Attr.3):
|
||||||
let List.457 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
let List.458 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||||
ret List.457;
|
ret List.458;
|
||||||
|
|
||||||
procedure List.89 (List.385, List.386, List.387):
|
procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.441 : U64 = 0i64;
|
let List.441 : U64 = 0i64;
|
||||||
|
@ -161,19 +161,19 @@ procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442;
|
let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442;
|
||||||
ret List.440;
|
ret List.440;
|
||||||
|
|
||||||
procedure List.90 (List.468, List.469, List.470, List.471, List.472):
|
procedure List.90 (List.469, List.470, List.471, List.472, List.473):
|
||||||
joinpoint List.443 List.388 List.389 List.390 List.391 List.392:
|
joinpoint List.443 List.388 List.389 List.390 List.391 List.392:
|
||||||
let List.445 : Int1 = CallByName Num.22 List.391 List.392;
|
let List.445 : Int1 = CallByName Num.22 List.391 List.392;
|
||||||
if List.445 then
|
if List.445 then
|
||||||
let List.451 : {Str, Str} = CallByName List.66 List.388 List.391;
|
let List.452 : {Str, Str} = CallByName List.66 List.388 List.391;
|
||||||
let List.446 : {List U8, U64} = CallByName List.137 List.389 List.451 List.390;
|
let List.446 : {List U8, U64} = CallByName List.137 List.389 List.452 List.390;
|
||||||
let List.449 : U64 = 1i64;
|
let List.449 : U64 = 1i64;
|
||||||
let List.448 : U64 = CallByName Num.19 List.391 List.449;
|
let List.448 : U64 = CallByName Num.19 List.391 List.449;
|
||||||
jump List.443 List.388 List.446 List.390 List.448 List.392;
|
jump List.443 List.388 List.446 List.390 List.448 List.392;
|
||||||
else
|
else
|
||||||
ret List.389;
|
ret List.389;
|
||||||
in
|
in
|
||||||
jump List.443 List.468 List.469 List.470 List.471 List.472;
|
jump List.443 List.469 List.470 List.471 List.472 List.473;
|
||||||
|
|
||||||
procedure Num.125 (#Attr.2):
|
procedure Num.125 (#Attr.2):
|
||||||
let Num.263 : U8 = lowlevel NumIntCast #Attr.2;
|
let Num.263 : U8 = lowlevel NumIntCast #Attr.2;
|
||||||
|
|
|
@ -126,8 +126,8 @@ procedure Json.96 (Json.97, Json.433, Json.95):
|
||||||
ret Json.435;
|
ret Json.435;
|
||||||
|
|
||||||
procedure List.137 (List.138, List.139, List.136):
|
procedure List.137 (List.138, List.139, List.136):
|
||||||
let List.455 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
let List.456 : {List U8, U64} = CallByName Json.114 List.138 List.139;
|
||||||
ret List.455;
|
ret List.456;
|
||||||
|
|
||||||
procedure List.18 (List.134, List.135, List.136):
|
procedure List.18 (List.134, List.135, List.136):
|
||||||
let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
||||||
|
@ -144,12 +144,12 @@ procedure List.6 (#Attr.2):
|
||||||
ret List.409;
|
ret List.409;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.458 : U64 = lowlevel ListLen #Attr.2;
|
let List.459 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.458;
|
ret List.459;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.452 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.453 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.452;
|
ret List.453;
|
||||||
|
|
||||||
procedure List.70 (#Attr.2, #Attr.3):
|
procedure List.70 (#Attr.2, #Attr.3):
|
||||||
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
|
@ -160,8 +160,8 @@ procedure List.71 (#Attr.2, #Attr.3):
|
||||||
ret List.413;
|
ret List.413;
|
||||||
|
|
||||||
procedure List.8 (#Attr.2, #Attr.3):
|
procedure List.8 (#Attr.2, #Attr.3):
|
||||||
let List.457 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
let List.458 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||||
ret List.457;
|
ret List.458;
|
||||||
|
|
||||||
procedure List.89 (List.385, List.386, List.387):
|
procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.441 : U64 = 0i64;
|
let List.441 : U64 = 0i64;
|
||||||
|
@ -169,19 +169,19 @@ procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442;
|
let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442;
|
||||||
ret List.440;
|
ret List.440;
|
||||||
|
|
||||||
procedure List.90 (List.468, List.469, List.470, List.471, List.472):
|
procedure List.90 (List.469, List.470, List.471, List.472, List.473):
|
||||||
joinpoint List.443 List.388 List.389 List.390 List.391 List.392:
|
joinpoint List.443 List.388 List.389 List.390 List.391 List.392:
|
||||||
let List.445 : Int1 = CallByName Num.22 List.391 List.392;
|
let List.445 : Int1 = CallByName Num.22 List.391 List.392;
|
||||||
if List.445 then
|
if List.445 then
|
||||||
let List.451 : {Str, Str} = CallByName List.66 List.388 List.391;
|
let List.452 : {Str, Str} = CallByName List.66 List.388 List.391;
|
||||||
let List.446 : {List U8, U64} = CallByName List.137 List.389 List.451 List.390;
|
let List.446 : {List U8, U64} = CallByName List.137 List.389 List.452 List.390;
|
||||||
let List.449 : U64 = 1i64;
|
let List.449 : U64 = 1i64;
|
||||||
let List.448 : U64 = CallByName Num.19 List.391 List.449;
|
let List.448 : U64 = CallByName Num.19 List.391 List.449;
|
||||||
jump List.443 List.388 List.446 List.390 List.448 List.392;
|
jump List.443 List.388 List.446 List.390 List.448 List.392;
|
||||||
else
|
else
|
||||||
ret List.389;
|
ret List.389;
|
||||||
in
|
in
|
||||||
jump List.443 List.468 List.469 List.470 List.471 List.472;
|
jump List.443 List.469 List.470 List.471 List.472 List.473;
|
||||||
|
|
||||||
procedure Num.125 (#Attr.2):
|
procedure Num.125 (#Attr.2):
|
||||||
let Num.263 : U8 = lowlevel NumIntCast #Attr.2;
|
let Num.263 : U8 = lowlevel NumIntCast #Attr.2;
|
||||||
|
|
|
@ -127,8 +127,8 @@ procedure Json.96 (Json.97, Json.438, Json.95):
|
||||||
ret Json.440;
|
ret Json.440;
|
||||||
|
|
||||||
procedure List.137 (List.138, List.139, List.136):
|
procedure List.137 (List.138, List.139, List.136):
|
||||||
let List.461 : {List U8, U64} = CallByName Json.128 List.138 List.139;
|
let List.462 : {List U8, U64} = CallByName Json.128 List.138 List.139;
|
||||||
ret List.461;
|
ret List.462;
|
||||||
|
|
||||||
procedure List.18 (List.134, List.135, List.136):
|
procedure List.18 (List.134, List.135, List.136):
|
||||||
let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
||||||
|
@ -145,12 +145,12 @@ procedure List.6 (#Attr.2):
|
||||||
ret List.409;
|
ret List.409;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.462 : U64 = lowlevel ListLen #Attr.2;
|
let List.463 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.462;
|
ret List.463;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.458 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.459 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.458;
|
ret List.459;
|
||||||
|
|
||||||
procedure List.70 (#Attr.2, #Attr.3):
|
procedure List.70 (#Attr.2, #Attr.3):
|
||||||
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
|
@ -161,8 +161,8 @@ procedure List.71 (#Attr.2, #Attr.3):
|
||||||
ret List.413;
|
ret List.413;
|
||||||
|
|
||||||
procedure List.8 (#Attr.2, #Attr.3):
|
procedure List.8 (#Attr.2, #Attr.3):
|
||||||
let List.464 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
let List.465 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||||
ret List.464;
|
ret List.465;
|
||||||
|
|
||||||
procedure List.89 (List.385, List.386, List.387):
|
procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.447 : U64 = 0i64;
|
let List.447 : U64 = 0i64;
|
||||||
|
@ -170,19 +170,19 @@ procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448;
|
let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448;
|
||||||
ret List.446;
|
ret List.446;
|
||||||
|
|
||||||
procedure List.90 (List.474, List.475, List.476, List.477, List.478):
|
procedure List.90 (List.475, List.476, List.477, List.478, List.479):
|
||||||
joinpoint List.449 List.388 List.389 List.390 List.391 List.392:
|
joinpoint List.449 List.388 List.389 List.390 List.391 List.392:
|
||||||
let List.451 : Int1 = CallByName Num.22 List.391 List.392;
|
let List.451 : Int1 = CallByName Num.22 List.391 List.392;
|
||||||
if List.451 then
|
if List.451 then
|
||||||
let List.457 : Str = CallByName List.66 List.388 List.391;
|
let List.458 : Str = CallByName List.66 List.388 List.391;
|
||||||
let List.452 : {List U8, U64} = CallByName List.137 List.389 List.457 List.390;
|
let List.452 : {List U8, U64} = CallByName List.137 List.389 List.458 List.390;
|
||||||
let List.455 : U64 = 1i64;
|
let List.455 : U64 = 1i64;
|
||||||
let List.454 : U64 = CallByName Num.19 List.391 List.455;
|
let List.454 : U64 = CallByName Num.19 List.391 List.455;
|
||||||
jump List.449 List.388 List.452 List.390 List.454 List.392;
|
jump List.449 List.388 List.452 List.390 List.454 List.392;
|
||||||
else
|
else
|
||||||
ret List.389;
|
ret List.389;
|
||||||
in
|
in
|
||||||
jump List.449 List.474 List.475 List.476 List.477 List.478;
|
jump List.449 List.475 List.476 List.477 List.478 List.479;
|
||||||
|
|
||||||
procedure Num.125 (#Attr.2):
|
procedure Num.125 (#Attr.2):
|
||||||
let Num.265 : U8 = lowlevel NumIntCast #Attr.2;
|
let Num.265 : U8 = lowlevel NumIntCast #Attr.2;
|
||||||
|
|
|
@ -133,8 +133,8 @@ procedure Json.96 (Json.97, Json.438, Json.95):
|
||||||
ret Json.440;
|
ret Json.440;
|
||||||
|
|
||||||
procedure List.137 (List.138, List.139, List.136):
|
procedure List.137 (List.138, List.139, List.136):
|
||||||
let List.461 : {List U8, U64} = CallByName Json.128 List.138 List.139;
|
let List.462 : {List U8, U64} = CallByName Json.128 List.138 List.139;
|
||||||
ret List.461;
|
ret List.462;
|
||||||
|
|
||||||
procedure List.18 (List.134, List.135, List.136):
|
procedure List.18 (List.134, List.135, List.136):
|
||||||
let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136;
|
||||||
|
@ -151,12 +151,12 @@ procedure List.6 (#Attr.2):
|
||||||
ret List.409;
|
ret List.409;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.462 : U64 = lowlevel ListLen #Attr.2;
|
let List.463 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.462;
|
ret List.463;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.458 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.459 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.458;
|
ret List.459;
|
||||||
|
|
||||||
procedure List.70 (#Attr.2, #Attr.3):
|
procedure List.70 (#Attr.2, #Attr.3):
|
||||||
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
|
@ -167,8 +167,8 @@ procedure List.71 (#Attr.2, #Attr.3):
|
||||||
ret List.413;
|
ret List.413;
|
||||||
|
|
||||||
procedure List.8 (#Attr.2, #Attr.3):
|
procedure List.8 (#Attr.2, #Attr.3):
|
||||||
let List.464 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
let List.465 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||||
ret List.464;
|
ret List.465;
|
||||||
|
|
||||||
procedure List.89 (List.385, List.386, List.387):
|
procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.447 : U64 = 0i64;
|
let List.447 : U64 = 0i64;
|
||||||
|
@ -176,19 +176,19 @@ procedure List.89 (List.385, List.386, List.387):
|
||||||
let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448;
|
let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448;
|
||||||
ret List.446;
|
ret List.446;
|
||||||
|
|
||||||
procedure List.90 (List.474, List.475, List.476, List.477, List.478):
|
procedure List.90 (List.475, List.476, List.477, List.478, List.479):
|
||||||
joinpoint List.449 List.388 List.389 List.390 List.391 List.392:
|
joinpoint List.449 List.388 List.389 List.390 List.391 List.392:
|
||||||
let List.451 : Int1 = CallByName Num.22 List.391 List.392;
|
let List.451 : Int1 = CallByName Num.22 List.391 List.392;
|
||||||
if List.451 then
|
if List.451 then
|
||||||
let List.457 : Str = CallByName List.66 List.388 List.391;
|
let List.458 : Str = CallByName List.66 List.388 List.391;
|
||||||
let List.452 : {List U8, U64} = CallByName List.137 List.389 List.457 List.390;
|
let List.452 : {List U8, U64} = CallByName List.137 List.389 List.458 List.390;
|
||||||
let List.455 : U64 = 1i64;
|
let List.455 : U64 = 1i64;
|
||||||
let List.454 : U64 = CallByName Num.19 List.391 List.455;
|
let List.454 : U64 = CallByName Num.19 List.391 List.455;
|
||||||
jump List.449 List.388 List.452 List.390 List.454 List.392;
|
jump List.449 List.388 List.452 List.390 List.454 List.392;
|
||||||
else
|
else
|
||||||
ret List.389;
|
ret List.389;
|
||||||
in
|
in
|
||||||
jump List.449 List.474 List.475 List.476 List.477 List.478;
|
jump List.449 List.475 List.476 List.477 List.478 List.479;
|
||||||
|
|
||||||
procedure Num.125 (#Attr.2):
|
procedure Num.125 (#Attr.2):
|
||||||
let Num.265 : U8 = lowlevel NumIntCast #Attr.2;
|
let Num.265 : U8 = lowlevel NumIntCast #Attr.2;
|
||||||
|
|
|
@ -5,7 +5,8 @@ procedure Bool.2 ():
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.6 : Int1 = CallByName Bool.2;
|
let Test.6 : Int1 = CallByName Bool.2;
|
||||||
if Test.6 then
|
if Test.6 then
|
||||||
Error voided tag constructor is unreachable
|
let Test.8 : Str = "voided tag constructor is unreachable";
|
||||||
|
Crash Test.8
|
||||||
else
|
else
|
||||||
let Test.5 : Str = "abc";
|
let Test.5 : Str = "abc";
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
|
@ -2030,3 +2030,21 @@ fn recursive_function_and_union_with_inference_hole() {
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn crash() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
getInfallible = \result -> when result is
|
||||||
|
Ok x -> x
|
||||||
|
_ -> crash "turns out this was fallible"
|
||||||
|
|
||||||
|
main =
|
||||||
|
x : [Ok U64, Err Str]
|
||||||
|
x = Ok 78
|
||||||
|
getInfallible x
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1667,6 +1667,8 @@ impl Subs {
|
||||||
pub const TAG_NAME_BAD_UTF_8: SubsIndex<TagName> = SubsIndex::new(3);
|
pub const TAG_NAME_BAD_UTF_8: SubsIndex<TagName> = SubsIndex::new(3);
|
||||||
pub const TAG_NAME_OUT_OF_BOUNDS: SubsIndex<TagName> = SubsIndex::new(4);
|
pub const TAG_NAME_OUT_OF_BOUNDS: SubsIndex<TagName> = SubsIndex::new(4);
|
||||||
|
|
||||||
|
pub const STR_SLICE: VariableSubsSlice = SubsSlice::new(0, 1);
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1);
|
pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
@ -1704,14 +1706,18 @@ impl Subs {
|
||||||
|
|
||||||
let mut subs = Subs {
|
let mut subs = Subs {
|
||||||
utable: UnificationTable::default(),
|
utable: UnificationTable::default(),
|
||||||
variables: Vec::new(),
|
variables: vec![
|
||||||
|
// Used for STR_SLICE
|
||||||
|
Variable::STR,
|
||||||
|
],
|
||||||
tag_names,
|
tag_names,
|
||||||
symbol_names,
|
symbol_names,
|
||||||
field_names: Vec::new(),
|
field_names: Vec::new(),
|
||||||
record_fields: Vec::new(),
|
record_fields: Vec::new(),
|
||||||
// store an empty slice at the first position
|
variable_slices: vec![
|
||||||
// used for "TagOrFunction"
|
// used for "TagOrFunction"
|
||||||
variable_slices: vec![VariableSubsSlice::default()],
|
VariableSubsSlice::default(),
|
||||||
|
],
|
||||||
unspecialized_lambda_sets: Vec::new(),
|
unspecialized_lambda_sets: Vec::new(),
|
||||||
tag_name_cache: Default::default(),
|
tag_name_cache: Default::default(),
|
||||||
uls_of_var: Default::default(),
|
uls_of_var: Default::default(),
|
||||||
|
|
|
@ -3486,6 +3486,7 @@ pub enum Reason {
|
||||||
member_name: Symbol,
|
member_name: Symbol,
|
||||||
def_region: Region,
|
def_region: Region,
|
||||||
},
|
},
|
||||||
|
CrashArg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
@ -3528,6 +3529,8 @@ pub enum Category {
|
||||||
|
|
||||||
AbilityMemberSpecialization(Symbol),
|
AbilityMemberSpecialization(Symbol),
|
||||||
|
|
||||||
|
Crash,
|
||||||
|
|
||||||
Expect,
|
Expect,
|
||||||
Dbg,
|
Dbg,
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|
|
@ -235,7 +235,7 @@ fn run_expect_pure<'a, W: std::io::Write>(
|
||||||
|
|
||||||
let sequence = ExpectSequence::new(shared_memory.ptr.cast());
|
let sequence = ExpectSequence::new(shared_memory.ptr.cast());
|
||||||
|
|
||||||
let result: Result<(), String> = try_run_jit_function!(lib, expect.name, (), |v: ()| v);
|
let result: Result<(), (String, _)> = try_run_jit_function!(lib, expect.name, (), |v: ()| v);
|
||||||
|
|
||||||
let shared_memory_ptr: *const u8 = shared_memory.ptr.cast();
|
let shared_memory_ptr: *const u8 = shared_memory.ptr.cast();
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@ fn run_expect_pure<'a, W: std::io::Write>(
|
||||||
|
|
||||||
let renderer = Renderer::new(arena, interns, render_target, module_id, filename, &source);
|
let renderer = Renderer::new(arena, interns, render_target, module_id, filename, &source);
|
||||||
|
|
||||||
if let Err(roc_panic_message) = result {
|
if let Err((roc_panic_message, _roc_panic_tag)) = result {
|
||||||
renderer.render_panic(writer, &roc_panic_message, expect.region)?;
|
renderer.render_panic(writer, &roc_panic_message, expect.region)?;
|
||||||
} else {
|
} else {
|
||||||
let mut offset = ExpectSequence::START_OFFSET;
|
let mut offset = ExpectSequence::START_OFFSET;
|
||||||
|
@ -305,9 +305,10 @@ fn run_expect_fx<'a, W: std::io::Write>(
|
||||||
|
|
||||||
child_memory.set_shared_buffer(lib);
|
child_memory.set_shared_buffer(lib);
|
||||||
|
|
||||||
let result: Result<(), String> = try_run_jit_function!(lib, expect.name, (), |v: ()| v);
|
let result: Result<(), (String, _)> =
|
||||||
|
try_run_jit_function!(lib, expect.name, (), |v: ()| v);
|
||||||
|
|
||||||
if let Err(msg) = result {
|
if let Err((msg, _)) = result {
|
||||||
panic!("roc panic {}", msg);
|
panic!("roc panic {}", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1086,7 +1086,36 @@ pub fn can_problem<'b>(
|
||||||
} else {
|
} else {
|
||||||
"TOO FEW TYPE ARGUMENTS".to_string()
|
"TOO FEW TYPE ARGUMENTS".to_string()
|
||||||
};
|
};
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
|
Problem::UnappliedCrash { region } => {
|
||||||
|
doc = alloc.stack([
|
||||||
|
alloc.concat([
|
||||||
|
alloc.reflow("This "), alloc.keyword("crash"), alloc.reflow(" doesn't have a message given to it:")
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.concat([
|
||||||
|
alloc.keyword("crash"), alloc.reflow(" must be passed a message to crash with at the exact place it's used. "),
|
||||||
|
alloc.keyword("crash"), alloc.reflow(" can't be used as a value that's passed around, like functions can be - it must be applied immediately!"),
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
title = "UNAPPLIED CRASH".to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
|
Problem::OverAppliedCrash { region } => {
|
||||||
|
doc = alloc.stack([
|
||||||
|
alloc.concat([
|
||||||
|
alloc.reflow("This "),
|
||||||
|
alloc.keyword("crash"),
|
||||||
|
alloc.reflow(" has too many values given to it:"),
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.concat([
|
||||||
|
alloc.keyword("crash"),
|
||||||
|
alloc.reflow(" must be given exacly one message to crash with."),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
title = "OVERAPPLIED CRASH".to_string();
|
||||||
severity = Severity::RuntimeError;
|
severity = Severity::RuntimeError;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1375,6 +1375,42 @@ fn to_expr_report<'b>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reason::CrashArg => {
|
||||||
|
let this_is = alloc.reflow("The value is");
|
||||||
|
|
||||||
|
let wanted = alloc.concat([
|
||||||
|
alloc.reflow("But I can only "),
|
||||||
|
alloc.keyword("crash"),
|
||||||
|
alloc.reflow(" with messages of type"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let details = None;
|
||||||
|
|
||||||
|
let lines = [
|
||||||
|
alloc
|
||||||
|
.reflow("This value passed to ")
|
||||||
|
.append(alloc.keyword("crash"))
|
||||||
|
.append(alloc.reflow(" is not a string:")),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
type_comparison(
|
||||||
|
alloc,
|
||||||
|
found,
|
||||||
|
expected_type,
|
||||||
|
ExpectationContext::WhenCondition,
|
||||||
|
add_category(alloc, this_is, &category),
|
||||||
|
wanted,
|
||||||
|
details,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
title: "TYPE MISMATCH".to_string(),
|
||||||
|
doc: alloc.stack(lines),
|
||||||
|
severity: Severity::RuntimeError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reason::LowLevelOpArg { op, arg_index } => {
|
Reason::LowLevelOpArg { op, arg_index } => {
|
||||||
panic!(
|
panic!(
|
||||||
"Compiler bug: argument #{} to low-level operation {:?} was the wrong type!",
|
"Compiler bug: argument #{} to low-level operation {:?} was the wrong type!",
|
||||||
|
@ -1680,6 +1716,10 @@ fn format_category<'b>(
|
||||||
alloc.concat([this_is, alloc.text(" an uniqueness attribute")]),
|
alloc.concat([this_is, alloc.text(" an uniqueness attribute")]),
|
||||||
alloc.text(" of type:"),
|
alloc.text(" of type:"),
|
||||||
),
|
),
|
||||||
|
Crash => {
|
||||||
|
internal_error!("calls to crash should be unconditionally admitted in any context, unexpected reachability!");
|
||||||
|
}
|
||||||
|
|
||||||
Storage(..) | Unknown => (
|
Storage(..) | Unknown => (
|
||||||
alloc.concat([this_is, alloc.text(" a value")]),
|
alloc.concat([this_is, alloc.text(" a value")]),
|
||||||
alloc.text(" of type:"),
|
alloc.text(" of type:"),
|
||||||
|
|
|
@ -12420,4 +12420,69 @@ All branches in an `if` must have the same type!
|
||||||
to be more general?
|
to be more general?
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
crash_given_non_string,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
crash {}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This value passed to `crash` is not a string:
|
||||||
|
|
||||||
|
4│ crash {}
|
||||||
|
^^
|
||||||
|
|
||||||
|
The value is a record of type:
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
But I can only `crash` with messages of type
|
||||||
|
|
||||||
|
Str
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
crash_unapplied,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
crash
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── UNAPPLIED CRASH ─────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This `crash` doesn't have a message given to it:
|
||||||
|
|
||||||
|
4│ crash
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
`crash` must be passed a message to crash with at the exact place it's
|
||||||
|
used. `crash` can't be used as a value that's passed around, like
|
||||||
|
functions can be - it must be applied immediately!
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
crash_overapplied,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
crash "" ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── OVERAPPLIED CRASH ───────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This `crash` has too many values given to it:
|
||||||
|
|
||||||
|
4│ crash "" ""
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
`crash` must be given exacly one message to crash with.
|
||||||
|
"###
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,12 +58,16 @@ pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
pub unsafe extern "C" fn roc_panic(msg: &RocStr, tag_id: u32) {
|
||||||
match tag_id {
|
match tag_id {
|
||||||
0 => {
|
0 => {
|
||||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
eprintln!("Roc crashed with:\n\n\t{}\n", msg.as_str());
|
||||||
let string = slice.to_str().unwrap();
|
|
||||||
eprintln!("Roc crashed with:\n\n\t{}\n", string);
|
print_backtrace();
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
eprintln!("The program crashed with:\n\n\t{}\n", msg.as_str());
|
||||||
|
|
||||||
print_backtrace();
|
print_backtrace();
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue