mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 19:58:18 +00:00
Merge remote-tracking branch 'remote/main' into builtin-task
This commit is contained in:
commit
eca453d07f
367 changed files with 14084 additions and 12080 deletions
|
@ -6,9 +6,9 @@ use bumpalo::Bump;
|
|||
use roc_error_macros::{internal_error, user_error};
|
||||
use roc_fmt::def::fmt_defs;
|
||||
use roc_fmt::module::fmt_module;
|
||||
use roc_fmt::spaces::RemoveSpaces;
|
||||
use roc_fmt::{Ast, Buf};
|
||||
use roc_parse::module::parse_module_defs;
|
||||
use roc_parse::remove_spaces::RemoveSpaces;
|
||||
use roc_parse::{module, parser::SyntaxError, state::State};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
@ -542,16 +542,6 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_switching_swift() {
|
||||
test_roc_app_slim(
|
||||
"examples/platform-switching",
|
||||
"rocLovesSwift.roc",
|
||||
"Roc <3 Swift!\n",
|
||||
UseValgrind::Yes,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expects_dev_and_test() {
|
||||
// these are in the same test function so we don't have to worry about race conditions
|
||||
|
@ -785,11 +775,6 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_gui() {
|
||||
test_roc_app_slim("examples/gui", "hello-guiBROKEN.roc", "", UseValgrind::No)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn quicksort() {
|
||||
|
@ -910,11 +895,6 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swift_ui() {
|
||||
test_roc_app_slim("examples/swiftui", "main.roc", "", UseValgrind::No)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(cli_platform)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
|
@ -1056,11 +1036,6 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inspect_gui() {
|
||||
test_roc_app_slim("examples", "inspect-gui.roc", "", UseValgrind::No)
|
||||
}
|
||||
|
||||
// TODO not sure if this cfg should still be here: #[cfg(not(debug_assertions))]
|
||||
// this is for testing the benchmarks, to perform proper benchmarks see crates/cli/benches/README.md
|
||||
mod test_benchmarks {
|
||||
|
|
|
@ -6,10 +6,12 @@ const mem = std.mem;
|
|||
const math = std.math;
|
||||
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
|
||||
const EqFn = *const fn (?[*]u8, ?[*]u8) callconv(.C) bool;
|
||||
const CompareFn = *const fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) u8;
|
||||
const Opaque = ?[*]u8;
|
||||
const EqFn = *const fn (Opaque, Opaque) callconv(.C) bool;
|
||||
const CompareFn = *const fn (Opaque, Opaque, Opaque) callconv(.C) u8;
|
||||
const CopyFn = *const fn (Opaque, Opaque) callconv(.C) void;
|
||||
|
||||
const Inc = *const fn (?[*]u8) callconv(.C) void;
|
||||
const IncN = *const fn (?[*]u8, usize) callconv(.C) void;
|
||||
|
@ -425,6 +427,7 @@ pub fn listAppendUnsafe(
|
|||
list: RocList,
|
||||
element: Opaque,
|
||||
element_width: usize,
|
||||
copy: CopyFn,
|
||||
) callconv(.C) RocList {
|
||||
const old_length = list.len();
|
||||
var output = list;
|
||||
|
@ -433,7 +436,7 @@ pub fn listAppendUnsafe(
|
|||
if (output.bytes) |bytes| {
|
||||
if (element) |source| {
|
||||
const target = bytes + old_length * element_width;
|
||||
@memcpy(target[0..element_width], source[0..element_width]);
|
||||
copy(target, source);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,9 +451,10 @@ fn listAppend(
|
|||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
update_mode: UpdateMode,
|
||||
copy: CopyFn,
|
||||
) callconv(.C) RocList {
|
||||
const with_capacity = listReserve(list, alignment, 1, element_width, elements_refcounted, inc, update_mode);
|
||||
return listAppendUnsafe(with_capacity, element, element_width);
|
||||
return listAppendUnsafe(with_capacity, element, element_width, copy);
|
||||
}
|
||||
|
||||
pub fn listPrepend(
|
||||
|
@ -460,6 +464,7 @@ pub fn listPrepend(
|
|||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
copy: CopyFn,
|
||||
) callconv(.C) RocList {
|
||||
const old_length = list.len();
|
||||
// TODO: properly wire in update mode.
|
||||
|
@ -468,20 +473,14 @@ pub fn listPrepend(
|
|||
|
||||
// can't use one memcpy here because source and target overlap
|
||||
if (with_capacity.bytes) |target| {
|
||||
var i: usize = old_length;
|
||||
|
||||
while (i > 0) {
|
||||
i -= 1;
|
||||
|
||||
// move the ith element to the (i + 1)th position
|
||||
const to = target + (i + 1) * element_width;
|
||||
const from = target + i * element_width;
|
||||
@memcpy(to[0..element_width], from[0..element_width]);
|
||||
}
|
||||
const from = target;
|
||||
const to = target + element_width;
|
||||
const size = element_width * old_length;
|
||||
std.mem.copyBackwards(u8, to[0..size], from[0..size]);
|
||||
|
||||
// finally copy in the new first element
|
||||
if (element) |source| {
|
||||
@memcpy(target[0..element_width], source[0..element_width]);
|
||||
copy(target, source);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -498,6 +497,7 @@ pub fn listSwap(
|
|||
inc: Inc,
|
||||
dec: Dec,
|
||||
update_mode: UpdateMode,
|
||||
copy: CopyFn,
|
||||
) callconv(.C) RocList {
|
||||
// Early exit to avoid swapping the same element.
|
||||
if (index_1 == index_2)
|
||||
|
@ -522,7 +522,7 @@ pub fn listSwap(
|
|||
swapElements(source_ptr, element_width, @as(usize,
|
||||
// We already verified that both indices are less than the stored list length,
|
||||
// which is usize, so casting them to usize will definitely be lossless.
|
||||
@intCast(index_1)), @as(usize, @intCast(index_2)));
|
||||
@intCast(index_1)), @as(usize, @intCast(index_2)), copy);
|
||||
|
||||
return newList;
|
||||
}
|
||||
|
@ -653,12 +653,9 @@ pub fn listDropAt(
|
|||
|
||||
if (list.isUnique()) {
|
||||
var i = drop_index;
|
||||
while (i < size - 1) : (i += 1) {
|
||||
const copy_target = source_ptr + i * element_width;
|
||||
const copy_source = copy_target + element_width;
|
||||
|
||||
@memcpy(copy_target[0..element_width], copy_source[0..element_width]);
|
||||
}
|
||||
const copy_target = source_ptr;
|
||||
const copy_source = copy_target + element_width;
|
||||
std.mem.copyForwards(u8, copy_target[i..size], copy_source[i..size]);
|
||||
|
||||
var new_list = list;
|
||||
|
||||
|
@ -693,7 +690,15 @@ pub fn listDropAt(
|
|||
}
|
||||
}
|
||||
|
||||
fn partition(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) isize {
|
||||
fn partition(
|
||||
source_ptr: [*]u8,
|
||||
transform: Opaque,
|
||||
wrapper: CompareFn,
|
||||
element_width: usize,
|
||||
low: isize,
|
||||
high: isize,
|
||||
copy: CopyFn,
|
||||
) isize {
|
||||
const pivot = source_ptr + (@as(usize, @intCast(high)) * element_width);
|
||||
var i = (low - 1); // Index of smaller element and indicates the right position of pivot found so far
|
||||
var j = low;
|
||||
|
@ -708,22 +713,30 @@ fn partition(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_w
|
|||
utils.Ordering.LT => {
|
||||
// the current element is smaller than the pivot; swap it
|
||||
i += 1;
|
||||
swapElements(source_ptr, element_width, @as(usize, @intCast(i)), @as(usize, @intCast(j)));
|
||||
swapElements(source_ptr, element_width, @as(usize, @intCast(i)), @as(usize, @intCast(j)), copy);
|
||||
},
|
||||
utils.Ordering.EQ, utils.Ordering.GT => {},
|
||||
}
|
||||
}
|
||||
swapElements(source_ptr, element_width, @as(usize, @intCast(i + 1)), @as(usize, @intCast(high)));
|
||||
swapElements(source_ptr, element_width, @as(usize, @intCast(i + 1)), @as(usize, @intCast(high)), copy);
|
||||
return (i + 1);
|
||||
}
|
||||
|
||||
fn quicksort(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) void {
|
||||
fn quicksort(
|
||||
source_ptr: [*]u8,
|
||||
transform: Opaque,
|
||||
wrapper: CompareFn,
|
||||
element_width: usize,
|
||||
low: isize,
|
||||
high: isize,
|
||||
copy: CopyFn,
|
||||
) void {
|
||||
if (low < high) {
|
||||
// partition index
|
||||
const pi = partition(source_ptr, transform, wrapper, element_width, low, high);
|
||||
const pi = partition(source_ptr, transform, wrapper, element_width, low, high, copy);
|
||||
|
||||
_ = quicksort(source_ptr, transform, wrapper, element_width, low, pi - 1); // before pi
|
||||
_ = quicksort(source_ptr, transform, wrapper, element_width, pi + 1, high); // after pi
|
||||
_ = quicksort(source_ptr, transform, wrapper, element_width, low, pi - 1, copy); // before pi
|
||||
_ = quicksort(source_ptr, transform, wrapper, element_width, pi + 1, high, copy); // after pi
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -738,6 +751,7 @@ pub fn listSortWith(
|
|||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
copy: CopyFn,
|
||||
) callconv(.C) RocList {
|
||||
var list = input.makeUnique(alignment, element_width, elements_refcounted, inc, dec);
|
||||
|
||||
|
@ -748,7 +762,7 @@ pub fn listSortWith(
|
|||
if (list.bytes) |source_ptr| {
|
||||
const low = 0;
|
||||
const high: isize = @as(isize, @intCast(list.len())) - 1;
|
||||
quicksort(source_ptr, data, caller, element_width, low, high);
|
||||
quicksort(source_ptr, data, caller, element_width, low, high, copy);
|
||||
}
|
||||
|
||||
return list;
|
||||
|
@ -756,29 +770,38 @@ pub fn listSortWith(
|
|||
|
||||
// SWAP ELEMENTS
|
||||
|
||||
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {
|
||||
@memcpy(temporary[0..width], ptr1[0..width]);
|
||||
@memcpy(ptr1[0..width], ptr2[0..width]);
|
||||
@memcpy(ptr2[0..width], temporary[0..width]);
|
||||
}
|
||||
|
||||
fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void {
|
||||
fn swap(
|
||||
element_width: usize,
|
||||
p1: [*]u8,
|
||||
p2: [*]u8,
|
||||
copy: CopyFn,
|
||||
) void {
|
||||
const threshold: usize = 64;
|
||||
|
||||
var width = width_initial;
|
||||
|
||||
var ptr1 = p1;
|
||||
var ptr2 = p2;
|
||||
|
||||
var buffer_actual: [threshold]u8 = undefined;
|
||||
const buffer: [*]u8 = buffer_actual[0..];
|
||||
|
||||
if (element_width <= threshold) {
|
||||
copy(buffer, p1);
|
||||
copy(p1, p2);
|
||||
copy(p2, buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
var width = element_width;
|
||||
|
||||
var ptr1 = p1;
|
||||
var ptr2 = p2;
|
||||
while (true) {
|
||||
if (width < threshold) {
|
||||
swapHelp(width, buffer, ptr1, ptr2);
|
||||
@memcpy(buffer[0..width], ptr1[0..width]);
|
||||
@memcpy(ptr1[0..width], ptr2[0..width]);
|
||||
@memcpy(ptr2[0..width], buffer[0..width]);
|
||||
return;
|
||||
} else {
|
||||
swapHelp(threshold, buffer, ptr1, ptr2);
|
||||
@memcpy(buffer[0..threshold], ptr1[0..threshold]);
|
||||
@memcpy(ptr1[0..threshold], ptr2[0..threshold]);
|
||||
@memcpy(ptr2[0..threshold], buffer[0..threshold]);
|
||||
|
||||
ptr1 += threshold;
|
||||
ptr2 += threshold;
|
||||
|
@ -788,11 +811,17 @@ fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void {
|
|||
}
|
||||
}
|
||||
|
||||
fn swapElements(source_ptr: [*]u8, element_width: usize, index_1: usize, index_2: usize) void {
|
||||
fn swapElements(
|
||||
source_ptr: [*]u8,
|
||||
element_width: usize,
|
||||
index_1: usize,
|
||||
index_2: usize,
|
||||
copy: CopyFn,
|
||||
) void {
|
||||
const element_at_i = source_ptr + (index_1 * element_width);
|
||||
const element_at_j = source_ptr + (index_2 * element_width);
|
||||
|
||||
return swap(element_width, element_at_i, element_at_j);
|
||||
return swap(element_width, element_at_i, element_at_j, copy);
|
||||
}
|
||||
|
||||
pub fn listConcat(
|
||||
|
@ -909,6 +938,7 @@ pub fn listReplaceInPlace(
|
|||
element: Opaque,
|
||||
element_width: usize,
|
||||
out_element: ?[*]u8,
|
||||
copy: CopyFn,
|
||||
) callconv(.C) RocList {
|
||||
// INVARIANT: bounds checking happens on the roc side
|
||||
//
|
||||
|
@ -917,7 +947,7 @@ pub fn listReplaceInPlace(
|
|||
// so we don't do a bounds check here. Hence, the list is also non-empty,
|
||||
// because inserting into an empty list is always out of bounds,
|
||||
// and it's always safe to cast index to usize.
|
||||
return listReplaceInPlaceHelp(list, @as(usize, @intCast(index)), element, element_width, out_element);
|
||||
return listReplaceInPlaceHelp(list, @as(usize, @intCast(index)), element, element_width, out_element, copy);
|
||||
}
|
||||
|
||||
pub fn listReplace(
|
||||
|
@ -930,6 +960,7 @@ pub fn listReplace(
|
|||
inc: Inc,
|
||||
dec: Dec,
|
||||
out_element: ?[*]u8,
|
||||
copy: CopyFn,
|
||||
) callconv(.C) RocList {
|
||||
// INVARIANT: bounds checking happens on the roc side
|
||||
//
|
||||
|
@ -939,7 +970,7 @@ pub fn listReplace(
|
|||
// because inserting into an empty list is always out of bounds,
|
||||
// and it's always safe to cast index to usize.
|
||||
// because inserting into an empty list is always out of bounds
|
||||
return listReplaceInPlaceHelp(list.makeUnique(alignment, element_width, elements_refcounted, inc, dec), @as(usize, @intCast(index)), element, element_width, out_element);
|
||||
return listReplaceInPlaceHelp(list.makeUnique(alignment, element_width, elements_refcounted, inc, dec), @as(usize, @intCast(index)), element, element_width, out_element, copy);
|
||||
}
|
||||
|
||||
inline fn listReplaceInPlaceHelp(
|
||||
|
@ -948,15 +979,16 @@ inline fn listReplaceInPlaceHelp(
|
|||
element: Opaque,
|
||||
element_width: usize,
|
||||
out_element: ?[*]u8,
|
||||
copy: CopyFn,
|
||||
) RocList {
|
||||
// the element we will replace
|
||||
var element_at_index = (list.bytes orelse unreachable) + (index * element_width);
|
||||
|
||||
// copy out the old element
|
||||
@memcpy((out_element orelse unreachable)[0..element_width], element_at_index[0..element_width]);
|
||||
copy((out_element orelse unreachable), element_at_index);
|
||||
|
||||
// copy in the new element
|
||||
@memcpy(element_at_index[0..element_width], (element orelse unreachable)[0..element_width]);
|
||||
copy(element_at_index, (element orelse unreachable));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use roc_module::ident::ModuleName;
|
|||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Collection, ModuleImportParams, OldRecordBuilderField, Pattern, StrLiteral,
|
||||
StrSegment, ValueDef, WhenBranch,
|
||||
StrSegment, TypeAnnotation, ValueDef, WhenBranch,
|
||||
};
|
||||
use roc_region::all::{LineInfo, Loc, Region};
|
||||
|
||||
|
@ -154,15 +154,26 @@ fn desugar_value_def<'a>(
|
|||
IngestedFileImport(_) => *def,
|
||||
|
||||
Stmt(stmt_expr) => {
|
||||
// desugar into a Body({}, stmt_expr)
|
||||
let loc_pattern = arena.alloc(Loc::at(
|
||||
stmt_expr.region,
|
||||
Pattern::RecordDestructure(Collection::empty()),
|
||||
));
|
||||
Body(
|
||||
loc_pattern,
|
||||
desugar_expr(arena, stmt_expr, src, line_info, module_path),
|
||||
)
|
||||
// desugar `stmt_expr!` to
|
||||
// _ : {}
|
||||
// _ = stmt_expr!
|
||||
|
||||
let region = stmt_expr.region;
|
||||
let new_pat = arena.alloc(Loc::at(region, Pattern::Underscore("#!stmt")));
|
||||
|
||||
ValueDef::AnnotatedBody {
|
||||
ann_pattern: new_pat,
|
||||
ann_type: arena.alloc(Loc::at(
|
||||
region,
|
||||
TypeAnnotation::Record {
|
||||
fields: Collection::empty(),
|
||||
ext: None,
|
||||
},
|
||||
)),
|
||||
comment: None,
|
||||
body_pattern: new_pat,
|
||||
body_expr: desugar_expr(arena, stmt_expr, src, line_info, module_path),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +222,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
|||
arena,
|
||||
Body(
|
||||
loc_pattern,
|
||||
apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new),
|
||||
apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None),
|
||||
),
|
||||
),
|
||||
Err(..) => Body(
|
||||
|
@ -254,6 +265,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
|||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
Some((ann_pattern, ann_type)),
|
||||
),
|
||||
},
|
||||
),
|
||||
|
@ -1349,10 +1361,6 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
|
|||
And => (ModuleName::BOOL, "and"),
|
||||
Or => (ModuleName::BOOL, "or"),
|
||||
Pizza => unreachable!("Cannot desugar the |> operator"),
|
||||
Assignment => unreachable!("Cannot desugar the = operator"),
|
||||
IsAliasType => unreachable!("Cannot desugar the : operator"),
|
||||
IsOpaqueType => unreachable!("Cannot desugar the := operator"),
|
||||
Backpassing => unreachable!("Cannot desugar the <- operator"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{is_expr_suffixed, Pattern, ValueDef, WhenBranch};
|
||||
use roc_parse::ast::{is_expr_suffixed, Pattern, TypeAnnotation, ValueDef, WhenBranch};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use std::cell::Cell;
|
||||
|
||||
|
@ -23,14 +23,23 @@ fn next_unique_suffixed_ident() -> String {
|
|||
|
||||
// # is used as prefix because it's impossible for code authors to define names like this.
|
||||
// This makes it easy to see this identifier was created by the compiler.
|
||||
format!("#!a{}", count)
|
||||
format!("#!{}", count)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
///
|
||||
pub enum EUnwrapped<'a> {
|
||||
/// First suffixed expression in a definition. Emitted if an expression has def pattern
|
||||
/// e.g. x = first! (second! 42)
|
||||
/// The first unwrap will produce
|
||||
/// `UnwrappedDefExpr<first (second! 42)>`
|
||||
UnwrappedDefExpr(&'a Loc<Expr<'a>>),
|
||||
|
||||
/// Suffixed sub expression
|
||||
/// e.g. x = first! (second! 42)
|
||||
/// In this example, the second unwrap (after unwrapping the top level `first!`) will produce
|
||||
/// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!0_arg, sub_new: #!0_arg }>`
|
||||
UnwrappedSubExpr {
|
||||
/// the unwrapped expression argument for Task.await
|
||||
sub_arg: &'a Loc<Expr<'a>>,
|
||||
|
@ -42,6 +51,7 @@ pub enum EUnwrapped<'a> {
|
|||
sub_new: &'a Loc<Expr<'a>>,
|
||||
},
|
||||
|
||||
/// Malformed use of the suffix
|
||||
Malformed,
|
||||
}
|
||||
|
||||
|
@ -59,20 +69,18 @@ fn init_unwrapped_err<'a>(
|
|||
None => {
|
||||
// Provide an intermediate answer expression and pattern when unwrapping a
|
||||
// (sub) expression.
|
||||
// e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!a0 -> foo #!a0`
|
||||
let answer_ident = arena.alloc(next_unique_suffixed_ident());
|
||||
// e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!0_arg -> foo #!0_arg`
|
||||
let ident = arena.alloc(format!("{}_arg", next_unique_suffixed_ident()));
|
||||
let sub_new = arena.alloc(Loc::at(
|
||||
unwrapped_expr.region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: answer_ident,
|
||||
ident,
|
||||
},
|
||||
));
|
||||
let sub_pat = arena.alloc(Loc::at(
|
||||
unwrapped_expr.region,
|
||||
Pattern::Identifier {
|
||||
ident: answer_ident,
|
||||
},
|
||||
Pattern::Identifier { ident },
|
||||
));
|
||||
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
|
@ -240,7 +248,7 @@ pub fn unwrap_suffixed_expression_closure_help<'a>(
|
|||
Ok(new_closure)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new);
|
||||
let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None);
|
||||
let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret)));
|
||||
Ok(new_closure)
|
||||
}
|
||||
|
@ -361,8 +369,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
|
|||
sub_pat,
|
||||
sub_new,
|
||||
}) => {
|
||||
let unwrapped_expression =
|
||||
apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new);
|
||||
let unwrapped_expression = apply_task_await(
|
||||
arena,
|
||||
sub_arg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
None,
|
||||
);
|
||||
|
||||
let mut new_if_thens = Vec::new_in(arena);
|
||||
|
||||
|
@ -437,6 +451,7 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
|
|||
sub_arg,
|
||||
sub_pat,
|
||||
new_if,
|
||||
None,
|
||||
);
|
||||
|
||||
return unwrap_suffixed_expression(
|
||||
|
@ -464,6 +479,7 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
|
|||
sub_arg,
|
||||
sub_pat,
|
||||
after_if,
|
||||
None,
|
||||
);
|
||||
|
||||
let before_if_then = arena.alloc(Loc::at(
|
||||
|
@ -500,7 +516,7 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
|
|||
sub_new,
|
||||
}) => {
|
||||
let unwrapped_final_else =
|
||||
apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new);
|
||||
apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None);
|
||||
|
||||
let new_if = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
|
@ -535,7 +551,7 @@ pub fn unwrap_suffixed_expression_when_help<'a>(
|
|||
if is_expr_suffixed(&branch_loc_expr.value) {
|
||||
let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) {
|
||||
Ok(unwrapped_branch_value) => unwrapped_branch_value,
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new),
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None),
|
||||
Err(..) => return Err(EUnwrapped::Malformed),
|
||||
};
|
||||
|
||||
|
@ -564,7 +580,7 @@ pub fn unwrap_suffixed_expression_when_help<'a>(
|
|||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches)));
|
||||
let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when);
|
||||
let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when, None);
|
||||
Ok(applied_task_await)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..))
|
||||
|
@ -601,15 +617,15 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
|
||||
let maybe_suffixed_value_def = match current_value_def {
|
||||
Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None,
|
||||
AnnotatedBody { body_pattern, body_expr, .. } => Some((body_pattern, body_expr)),
|
||||
Body (def_pattern, def_expr, .. ) => Some((def_pattern, def_expr)),
|
||||
AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } => Some((body_pattern, body_expr, Some((ann_pattern, ann_type)))),
|
||||
Body (def_pattern, def_expr) => Some((def_pattern, def_expr, None)),
|
||||
};
|
||||
|
||||
match maybe_suffixed_value_def {
|
||||
None => {
|
||||
// We can't unwrap this def type, continue
|
||||
},
|
||||
Some((def_pattern, def_expr)) => {
|
||||
Some((def_pattern, def_expr, ann_type)) => {
|
||||
match unwrap_suffixed_expression(arena, def_expr, Some(def_pattern)) {
|
||||
Ok(unwrapped_def) => {
|
||||
current_value_def.replace_expr(unwrapped_def);
|
||||
|
@ -626,14 +642,14 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
Ok(next_expr) => next_expr,
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
// We need to apply Task.ok here as the defs final expression was unwrapped
|
||||
apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new)
|
||||
apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
|
||||
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
|
||||
return Err(EUnwrapped::Malformed);
|
||||
},
|
||||
};
|
||||
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr), maybe_def_pat);
|
||||
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type), maybe_def_pat);
|
||||
} else if before_empty {
|
||||
// NIL before, SOME after -> FIRST DEF
|
||||
let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret)));
|
||||
|
@ -641,7 +657,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){
|
||||
Ok(next_expr) => next_expr,
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new)
|
||||
apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
|
||||
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
|
||||
|
@ -649,19 +665,19 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
},
|
||||
};
|
||||
|
||||
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr), maybe_def_pat);
|
||||
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr,ann_type), maybe_def_pat);
|
||||
} else if after_empty {
|
||||
// SOME before, NIL after -> LAST DEF
|
||||
// We pass None as a def pattern here because it's desugaring of the ret expression
|
||||
match unwrap_suffixed_expression(arena,loc_ret,None){
|
||||
Ok(new_loc_ret) => {
|
||||
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
},
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new);
|
||||
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None);
|
||||
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
}
|
||||
|
@ -679,13 +695,13 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
|
||||
match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){
|
||||
Ok(new_loc_ret) => {
|
||||
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
},
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new);
|
||||
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None);
|
||||
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
}
|
||||
|
@ -700,7 +716,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
let new_body_def = ValueDef::Body(def_pattern, sub_new);
|
||||
local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region);
|
||||
let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret)));
|
||||
let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr);
|
||||
let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type);
|
||||
return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat);
|
||||
}
|
||||
Err(err) => return Err(err)
|
||||
|
@ -710,14 +726,14 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
}
|
||||
|
||||
// try to unwrap the loc_ret
|
||||
match unwrap_suffixed_expression(arena,loc_ret,maybe_def_pat){
|
||||
match unwrap_suffixed_expression(arena,loc_ret,None){
|
||||
Ok(new_loc_ret) => {
|
||||
Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))))
|
||||
Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))))
|
||||
},
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new);
|
||||
let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)));
|
||||
unwrap_suffixed_expression(arena, new_defs, maybe_def_pat)
|
||||
unwrap_suffixed_expression(arena, new_defs, None)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
|
||||
// TODO confirm this is correct with test case
|
||||
|
@ -761,7 +777,14 @@ fn unwrap_low_level_dbg<'a>(
|
|||
|
||||
unwrap_suffixed_expression(
|
||||
arena,
|
||||
apply_task_await(arena, new_dbg.region, sub_arg, sub_pat, new_dbg),
|
||||
apply_task_await(
|
||||
arena,
|
||||
new_dbg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
new_dbg,
|
||||
None,
|
||||
),
|
||||
maybe_def_pat,
|
||||
)
|
||||
}
|
||||
|
@ -813,33 +836,122 @@ fn unwrap_low_level_dbg<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Helper for `Task.await (loc_arg) \loc_pat -> loc_new`
|
||||
/// Helper for `Task.await loc_expr \loc_pat -> loc_cont`
|
||||
pub fn apply_task_await<'a>(
|
||||
arena: &'a Bump,
|
||||
region: Region,
|
||||
loc_arg: &'a Loc<Expr<'a>>,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_new: &'a Loc<Expr<'a>>,
|
||||
loc_cont: &'a Loc<Expr<'a>>,
|
||||
maybe_loc_ann: Option<(&'a Loc<Pattern>, &'a Loc<TypeAnnotation<'a>>)>,
|
||||
) -> &'a Loc<Expr<'a>> {
|
||||
// If the pattern and the new are matching answers then we don't need to unwrap anything
|
||||
// e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo`
|
||||
if is_matching_intermediate_answer(loc_pat, loc_new) {
|
||||
return loc_arg;
|
||||
let task_await_first_arg = match maybe_loc_ann {
|
||||
Some((loc_ann_pat, loc_type)) => {
|
||||
// loc_ann_pat : loc_type
|
||||
// loc_pat = loc_expr!
|
||||
// loc_cont
|
||||
|
||||
// desugar to
|
||||
// Task.await
|
||||
// (
|
||||
// #!0_expr : Task loc_type _
|
||||
// #!0_expr = loc_expr
|
||||
// #!0_expr
|
||||
// )
|
||||
// \loc_pat -> loc_cont
|
||||
use roc_parse::ast::*;
|
||||
|
||||
// #!0_expr or #!0_stmt
|
||||
let new_ident = next_unique_suffixed_ident();
|
||||
let new_ident = match loc_pat.value {
|
||||
Pattern::Underscore("#!stmt") => format!("{}_stmt", new_ident),
|
||||
Pattern::Identifier { ident }
|
||||
if ident.starts_with('#') && ident.ends_with("_stmt") =>
|
||||
{
|
||||
format!("{}_stmt", new_ident)
|
||||
}
|
||||
_ => format!("{}_expr", new_ident),
|
||||
};
|
||||
let new_ident = arena.alloc(new_ident);
|
||||
|
||||
// #!0_expr (pattern)
|
||||
// #!0_expr : Task loc_type _
|
||||
// #!0_expr = loc_expr
|
||||
let value_def = ValueDef::AnnotatedBody {
|
||||
ann_pattern: arena.alloc(Loc::at(
|
||||
loc_ann_pat.region,
|
||||
Pattern::Identifier {
|
||||
ident: if loc_ann_pat.value.equivalent(&loc_pat.value) {
|
||||
new_ident
|
||||
} else {
|
||||
// create another pattern to preserve inconsistency
|
||||
arena.alloc(next_unique_suffixed_ident())
|
||||
},
|
||||
},
|
||||
)),
|
||||
ann_type: arena.alloc(Loc::at(
|
||||
loc_type.region,
|
||||
TypeAnnotation::Apply(
|
||||
arena.alloc(""),
|
||||
arena.alloc("Task"),
|
||||
arena.alloc([
|
||||
*loc_type,
|
||||
Loc::at(loc_type.region, TypeAnnotation::Inferred),
|
||||
]),
|
||||
),
|
||||
)),
|
||||
comment: None,
|
||||
body_pattern: arena.alloc(Loc::at(
|
||||
loc_pat.region,
|
||||
Pattern::Identifier { ident: new_ident },
|
||||
)),
|
||||
body_expr: loc_expr,
|
||||
};
|
||||
|
||||
// #!0_expr (variable)
|
||||
let new_var = arena.alloc(Loc::at(
|
||||
region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: new_ident,
|
||||
},
|
||||
));
|
||||
|
||||
// (
|
||||
// #!0_expr : Task loc_type _
|
||||
// #!0_expr = loc_expr
|
||||
// #!0_expr
|
||||
// )
|
||||
let mut defs = roc_parse::ast::Defs::default();
|
||||
defs.push_value_def(value_def, region, &[], &[]);
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
Region::span_across(&loc_ann_pat.region, &loc_expr.region),
|
||||
Defs(arena.alloc(defs), new_var),
|
||||
))
|
||||
}
|
||||
None => {
|
||||
// loc_pat = loc_expr!
|
||||
// loc_cont
|
||||
|
||||
// desugar to
|
||||
// Task.await loc_expr \loc_pat -> loc_cont
|
||||
loc_expr
|
||||
}
|
||||
};
|
||||
|
||||
// If the last expression is suffixed - don't await
|
||||
// e.g.
|
||||
// \x -> x!
|
||||
// \x -> x
|
||||
if is_matching_intermediate_answer(loc_pat, loc_cont) {
|
||||
return task_await_first_arg;
|
||||
}
|
||||
|
||||
let mut task_await_apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
|
||||
|
||||
// apply the unwrapped suffixed expression
|
||||
task_await_apply_args.push(loc_arg);
|
||||
|
||||
// apply the closure
|
||||
let mut closure_pattern = Vec::new_in(arena);
|
||||
closure_pattern.push(*loc_pat);
|
||||
task_await_apply_args.push(arena.alloc(Loc::at(
|
||||
region,
|
||||
Closure(arena.alloc_slice_copy(closure_pattern.as_slice()), loc_new),
|
||||
)));
|
||||
// \loc_pat -> loc_cont
|
||||
let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont)));
|
||||
|
||||
// Task.await task_first_arg closure
|
||||
arena.alloc(Loc::at(
|
||||
region,
|
||||
Apply(
|
||||
|
@ -850,24 +962,12 @@ pub fn apply_task_await<'a>(
|
|||
ident: "await",
|
||||
},
|
||||
}),
|
||||
arena.alloc(task_await_apply_args),
|
||||
arena.alloc([task_await_first_arg, closure]),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn extract_wrapped_task_ok_value<'a>(loc_expr: &'a Loc<Expr<'a>>) -> Option<&'a Loc<Expr<'a>>> {
|
||||
match loc_expr.value {
|
||||
Expr::Apply(function, arguments, _) => match function.value {
|
||||
Var {
|
||||
module_name, ident, ..
|
||||
} if module_name == ModuleName::TASK && ident == "ok" => arguments.first().copied(),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_matching_intermediate_answer<'a>(
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_new: &'a Loc<Expr<'a>>,
|
||||
|
@ -876,24 +976,18 @@ pub fn is_matching_intermediate_answer<'a>(
|
|||
Pattern::Identifier { ident, .. } => Some(ident),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let exp_ident = match loc_new.value {
|
||||
Expr::Var {
|
||||
module_name, ident, ..
|
||||
} if module_name.is_empty() && ident.starts_with('#') => Some(ident),
|
||||
module_name: "",
|
||||
ident,
|
||||
..
|
||||
} => Some(ident),
|
||||
_ => None,
|
||||
};
|
||||
let exp_ident_in_task = match extract_wrapped_task_ok_value(loc_new) {
|
||||
Some(task_expr) => match task_expr.value {
|
||||
Expr::Var {
|
||||
module_name, ident, ..
|
||||
} if module_name.is_empty() && ident.starts_with('#') => Some(ident),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match (pat_ident, exp_ident, exp_ident_in_task) {
|
||||
(Some(a), Some(b), None) => a == b,
|
||||
(Some(a), None, Some(b)) => a == b,
|
||||
|
||||
match (pat_ident, exp_ident) {
|
||||
(Some(a), Some(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -35,7 +37,7 @@ Defs {
|
|||
@15-22 Closure(
|
||||
[
|
||||
@17-18 Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@15-22 Apply(
|
||||
|
@ -51,7 +53,7 @@ Defs {
|
|||
@15-22 Closure(
|
||||
[
|
||||
@20-21 Identifier {
|
||||
ident: "#!a1",
|
||||
ident: "#!1_arg",
|
||||
},
|
||||
],
|
||||
@15-22 Defs(
|
||||
|
@ -83,11 +85,11 @@ Defs {
|
|||
[
|
||||
@17-18 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
@20-21 Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
ident: "#!1_arg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -35,7 +37,7 @@ Defs {
|
|||
@15-19 Closure(
|
||||
[
|
||||
@17-18 Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@15-19 Defs(
|
||||
|
@ -67,7 +69,7 @@ Defs {
|
|||
[
|
||||
@17-18 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -45,7 +47,7 @@ Defs {
|
|||
@15-33 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@15-33 Defs(
|
||||
|
@ -78,7 +80,7 @@ Defs {
|
|||
@20-32 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -45,7 +47,7 @@ Defs {
|
|||
@15-35 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@15-35 Defs(
|
||||
|
@ -73,7 +75,7 @@ Defs {
|
|||
@16-26 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
),
|
||||
[
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -35,7 +37,7 @@ Defs {
|
|||
@15-22 Closure(
|
||||
[
|
||||
@15-16 Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@15-22 Defs(
|
||||
|
@ -67,7 +69,7 @@ Defs {
|
|||
[
|
||||
@15-16 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
BinOp(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -28,14 +30,58 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-15 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
@11-15 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@11-15,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-15 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @11-15 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@11-15 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@11-15 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @11-15 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @11-15 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@11-15 Var {
|
||||
module_name: "",
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
),
|
||||
@11-15 Closure(
|
||||
[
|
||||
@11-15 RecordDestructure(
|
||||
[],
|
||||
@11-15 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@21-26 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -35,7 +37,7 @@ Defs {
|
|||
@16-35 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@16-35 Defs(
|
||||
|
@ -63,7 +65,7 @@ Defs {
|
|||
@17-29 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
),
|
||||
[
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -55,25 +57,69 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@31-42 Apply(
|
||||
@31-42 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@31-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @31-43 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @31-43 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@31-43 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@31-43 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @31-43 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @31-42 Apply(
|
||||
@31-42 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@31-34 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@31-42 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
[
|
||||
@31-34 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
@31-42 Closure(
|
||||
[
|
||||
@31-42 RecordDestructure(
|
||||
[],
|
||||
@31-43 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@52-57 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -70,38 +72,69 @@ Defs {
|
|||
ident: "msg",
|
||||
},
|
||||
],
|
||||
@78-91 Apply(
|
||||
@78-91 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
@56-91 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@82-91,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @56-57 Identifier {
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
ann_type: @60-69 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@60-69 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@65-67 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@68-69 Inferred,
|
||||
],
|
||||
),
|
||||
@60-69 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @78-79 Identifier {
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
body_expr: @82-91 Apply(
|
||||
@82-91 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@88-91 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@82-91 Var {
|
||||
module_name: "",
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
[
|
||||
@78-91 Apply(
|
||||
@78-91 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@88-91 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@78-91 Closure(
|
||||
[
|
||||
@78-79 Identifier {
|
||||
ident: "y",
|
||||
},
|
||||
],
|
||||
@100-101 Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
},
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
},
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -98,23 +100,67 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@76-83 Apply(
|
||||
@76-83 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@76-83,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @76-83 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
ann_type: @76-83 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@76-83 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@76-83 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @76-83 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
body_expr: @76-83 Apply(
|
||||
@76-83 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@82-83 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@76-83 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
[
|
||||
@82-83 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@76-83 Closure(
|
||||
[
|
||||
@76-83 RecordDestructure(
|
||||
[],
|
||||
@76-83 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@92-99 Apply(
|
||||
|
@ -123,23 +169,67 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@92-99 Apply(
|
||||
@92-99 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@92-99,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @92-99 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @92-99 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@92-99 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@92-99 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @92-99 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @92-99 Apply(
|
||||
@92-99 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@98-99 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@92-99 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
[
|
||||
@98-99 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@92-99 Closure(
|
||||
[
|
||||
@92-99 RecordDestructure(
|
||||
[],
|
||||
@92-99 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@109-119 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -38,7 +40,7 @@ Defs {
|
|||
ident: "foo",
|
||||
},
|
||||
],
|
||||
@29-49 LowLevelDbg(
|
||||
@29-36 LowLevelDbg(
|
||||
(
|
||||
"test.roc:3",
|
||||
" ",
|
||||
|
|
|
@ -13,17 +13,19 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-24 Apply(
|
||||
@11-24 Var {
|
||||
@11-17 Apply(
|
||||
@11-17 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
|
@ -32,13 +34,13 @@ Defs {
|
|||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@11-24 Closure(
|
||||
@11-17 Closure(
|
||||
[
|
||||
@15-17 Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@11-24 LowLevelDbg(
|
||||
@11-17 LowLevelDbg(
|
||||
(
|
||||
"test.roc:2",
|
||||
"in",
|
||||
|
@ -51,7 +53,7 @@ Defs {
|
|||
[
|
||||
@15-17 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
|
|
@ -17,11 +17,12 @@ Defs {
|
|||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 2, length = 0),
|
||||
Slice(start = 2, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
|
@ -64,23 +65,67 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@25-39 Apply(
|
||||
@25-39 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
@25-39 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@25-39,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @25-39 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
ann_type: @25-39 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@25-39 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@25-39 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @25-39 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
body_expr: @25-39 Apply(
|
||||
@25-39 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@38-39 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@11-54 Var {
|
||||
module_name: "",
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
[
|
||||
@38-39 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@11-54 Closure(
|
||||
[
|
||||
@25-39 RecordDestructure(
|
||||
[],
|
||||
@25-39 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@45-54 Var {
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
|
|
@ -13,33 +13,56 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-31 Expect(
|
||||
@18-24 Apply(
|
||||
@20-22 Var {
|
||||
module_name: "Bool",
|
||||
ident: "isEq",
|
||||
},
|
||||
[
|
||||
@18-19 Num(
|
||||
"1",
|
||||
),
|
||||
@23-24 Num(
|
||||
"2",
|
||||
),
|
||||
@11-31 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
BinOp(
|
||||
Equals,
|
||||
),
|
||||
),
|
||||
regions: [
|
||||
@11-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Expect {
|
||||
condition: @18-24 Apply(
|
||||
@20-22 Var {
|
||||
module_name: "Bool",
|
||||
ident: "isEq",
|
||||
},
|
||||
[
|
||||
@18-19 Num(
|
||||
"1",
|
||||
),
|
||||
@23-24 Num(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Equals,
|
||||
),
|
||||
),
|
||||
preceding_comment: @11-11,
|
||||
},
|
||||
],
|
||||
},
|
||||
@29-31 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -129,7 +131,7 @@ Defs {
|
|||
Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!1_arg",
|
||||
},
|
||||
],
|
||||
@109-298 If(
|
||||
|
@ -144,7 +146,7 @@ Defs {
|
|||
@114-121 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!1_arg",
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -158,24 +160,68 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@140-152 Apply(
|
||||
@140-152 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@140-152,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @140-152 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @140-152 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@140-152 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@140-152 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @140-152 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @140-152 Apply(
|
||||
@140-152 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@146-152 Str(
|
||||
PlainLine(
|
||||
"fail",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@140-152 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
[
|
||||
@146-152 Str(
|
||||
PlainLine(
|
||||
"fail",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@140-152 Closure(
|
||||
[
|
||||
@140-152 RecordDestructure(
|
||||
[],
|
||||
@140-152 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@165-170 Apply(
|
||||
|
@ -218,7 +264,7 @@ Defs {
|
|||
Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a1",
|
||||
ident: "#!3_arg",
|
||||
},
|
||||
],
|
||||
@109-298 If(
|
||||
|
@ -227,7 +273,7 @@ Defs {
|
|||
@187-209 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
ident: "#!3_arg",
|
||||
},
|
||||
),
|
||||
@227-239 Apply(
|
||||
|
@ -236,24 +282,68 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@227-239 Apply(
|
||||
@227-239 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@227-239,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @227-239 Identifier {
|
||||
ident: "#!2_stmt",
|
||||
},
|
||||
ann_type: @227-239 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@227-239 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@227-239 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @227-239 Identifier {
|
||||
ident: "#!2_stmt",
|
||||
},
|
||||
body_expr: @227-239 Apply(
|
||||
@227-239 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@233-239 Str(
|
||||
PlainLine(
|
||||
"nope",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@227-239 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!2_stmt",
|
||||
},
|
||||
[
|
||||
@233-239 Str(
|
||||
PlainLine(
|
||||
"nope",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@227-239 Closure(
|
||||
[
|
||||
@227-239 RecordDestructure(
|
||||
[],
|
||||
@227-239 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@252-257 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -96,7 +98,7 @@ Defs {
|
|||
@79-87 Closure(
|
||||
[
|
||||
@79-87 Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@76-189 If(
|
||||
|
@ -104,7 +106,7 @@ Defs {
|
|||
(
|
||||
@79-87 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
@101-112 Apply(
|
||||
@101-105 Var {
|
||||
|
@ -135,7 +137,7 @@ Defs {
|
|||
@125-132 Closure(
|
||||
[
|
||||
@125-132 Identifier {
|
||||
ident: "#!a1",
|
||||
ident: "#!1_arg",
|
||||
},
|
||||
],
|
||||
@76-189 If(
|
||||
|
@ -143,7 +145,7 @@ Defs {
|
|||
(
|
||||
@125-132 Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
ident: "#!1_arg",
|
||||
},
|
||||
@146-160 Apply(
|
||||
@146-150 Var {
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -62,7 +64,7 @@ Defs {
|
|||
@11-26 Closure(
|
||||
[
|
||||
@24-25 Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@22-26 Apply(
|
||||
|
@ -73,7 +75,7 @@ Defs {
|
|||
[
|
||||
@24-25 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -28,14 +30,58 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-15 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
@11-15 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@11-15,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-15 Identifier {
|
||||
ident: "#!2_stmt",
|
||||
},
|
||||
ann_type: @11-15 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@11-15 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@11-15 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @11-15 Identifier {
|
||||
ident: "#!2_stmt",
|
||||
},
|
||||
body_expr: @11-15 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@11-15 Var {
|
||||
module_name: "",
|
||||
ident: "#!2_stmt",
|
||||
},
|
||||
),
|
||||
@11-15 Closure(
|
||||
[
|
||||
@11-15 RecordDestructure(
|
||||
[],
|
||||
@11-15 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@20-24 Apply(
|
||||
|
@ -44,14 +90,58 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@20-24 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
@20-24 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@20-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @20-24 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
ann_type: @20-24 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@20-24 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@20-24 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @20-24 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
body_expr: @20-24 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@20-24 Var {
|
||||
module_name: "",
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
),
|
||||
@20-24 Closure(
|
||||
[
|
||||
@20-24 RecordDestructure(
|
||||
[],
|
||||
@20-24 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@29-33 Var {
|
||||
|
|
|
@ -13,17 +13,19 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@0-26 Apply(
|
||||
@0-26 Var {
|
||||
@7-26 Apply(
|
||||
@7-26 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -28,24 +30,68 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-23 Apply(
|
||||
@11-23 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@11-23,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-23 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @11-23 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@11-23 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@11-23 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @11-23 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @11-23 Apply(
|
||||
@11-23 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@17-23 Str(
|
||||
PlainLine(
|
||||
"Ahoy",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@11-23 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
[
|
||||
@17-23 Str(
|
||||
PlainLine(
|
||||
"Ahoy",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@11-23 Closure(
|
||||
[
|
||||
@11-23 RecordDestructure(
|
||||
[],
|
||||
@11-23 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@33-55 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -57,8 +59,8 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@29-41 Apply(
|
||||
@29-41 Var {
|
||||
@33-41 Apply(
|
||||
@33-41 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
|
|
|
@ -13,55 +13,88 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-24 Apply(
|
||||
@11-24 Var {
|
||||
@11-16 Apply(
|
||||
@11-16 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-16 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
@11-24 Closure(
|
||||
[
|
||||
@11-16 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@11-16 Apply(
|
||||
@11-16 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-16 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
@11-16 Closure(
|
||||
[
|
||||
@11-16 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@21-24 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
),
|
||||
@11-16 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
regions: [
|
||||
@11-16,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-16 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
ann_type: @11-16 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@11-16 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@11-16 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@11-16 Inferred,
|
||||
],
|
||||
),
|
||||
@11-16 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @11-16 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
body_expr: @11-16 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@11-16 Var {
|
||||
module_name: "",
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
),
|
||||
@11-16 Closure(
|
||||
[
|
||||
@11-16 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@21-24 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -44,7 +46,7 @@ Defs {
|
|||
@15-43 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@15-43 Apply(
|
||||
|
@ -62,7 +64,7 @@ Defs {
|
|||
@21-29 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
),
|
||||
@32-42 ParensAround(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
|
|
@ -13,17 +13,19 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-3 Identifier {
|
||||
ident: "run",
|
||||
},
|
||||
@0-22 Apply(
|
||||
@0-22 Var {
|
||||
@6-22 Apply(
|
||||
@6-22 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
|
@ -32,14 +34,14 @@ Defs {
|
|||
module_name: "",
|
||||
ident: "nextMsg",
|
||||
},
|
||||
@0-22 Closure(
|
||||
@6-22 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@0-22 Apply(
|
||||
@0-22 Var {
|
||||
@6-22 Apply(
|
||||
@6-22 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
|
@ -47,7 +49,7 @@ Defs {
|
|||
@13-21 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -28,42 +30,86 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-56 Apply(
|
||||
@11-56 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@11-56,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-57 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @11-57 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@11-57 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@11-57 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @11-57 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @11-56 Apply(
|
||||
@11-56 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@11-44 Apply(
|
||||
@26-36 Var {
|
||||
module_name: "Str",
|
||||
ident: "concat",
|
||||
},
|
||||
[
|
||||
@11-18 Str(
|
||||
PlainLine(
|
||||
"hello",
|
||||
),
|
||||
),
|
||||
@37-44 Str(
|
||||
PlainLine(
|
||||
"world",
|
||||
),
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@11-56 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
[
|
||||
@11-44 Apply(
|
||||
@26-36 Var {
|
||||
module_name: "Str",
|
||||
ident: "concat",
|
||||
},
|
||||
[
|
||||
@11-18 Str(
|
||||
PlainLine(
|
||||
"hello",
|
||||
),
|
||||
),
|
||||
@37-44 Str(
|
||||
PlainLine(
|
||||
"world",
|
||||
),
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
@11-56 Closure(
|
||||
[
|
||||
@26-56 RecordDestructure(
|
||||
[],
|
||||
@11-57 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@63-73 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -37,24 +39,68 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@19-30 Apply(
|
||||
@19-30 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@19-30,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @19-30 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
ann_type: @19-30 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@19-30 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@19-30 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @19-30 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
body_expr: @19-30 Apply(
|
||||
@19-30 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@25-30 Str(
|
||||
PlainLine(
|
||||
"FOO",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@19-30 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
[
|
||||
@25-30 Str(
|
||||
PlainLine(
|
||||
"FOO",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@19-30 Closure(
|
||||
[
|
||||
@19-30 RecordDestructure(
|
||||
[],
|
||||
@19-30 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@36-67 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-44,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 Identifier {
|
||||
ident: "f",
|
||||
},
|
||||
@4-44 Closure(
|
||||
[
|
||||
@5-6 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
@28-30 Apply(
|
||||
@28-30 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@14-30 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@28-30,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @14-15 Identifier {
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
ann_type: @18-19 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@18-19 Apply(
|
||||
"",
|
||||
"A",
|
||||
[],
|
||||
),
|
||||
@18-19 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @24-25 Identifier {
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
body_expr: @28-30 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@28-30 Var {
|
||||
module_name: "",
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
),
|
||||
@28-30 Closure(
|
||||
[
|
||||
@24-25 Identifier {
|
||||
ident: "r",
|
||||
},
|
||||
],
|
||||
@35-44 Apply(
|
||||
@35-42 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@43-44 Var {
|
||||
module_name: "",
|
||||
ident: "r",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -44,27 +46,27 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@24-33 Var {
|
||||
@28-33 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
@15-19 Closure(
|
||||
[
|
||||
@24-33 Identifier {
|
||||
ident: "#!a0",
|
||||
@28-33 Identifier {
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@24-33 Apply(
|
||||
@24-33 Var {
|
||||
@28-33 Apply(
|
||||
@28-33 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@24-33 Var {
|
||||
@28-33 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
@24-33 Closure(
|
||||
@28-33 Closure(
|
||||
[
|
||||
@24-25 Identifier {
|
||||
ident: "b",
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -35,13 +37,13 @@ Defs {
|
|||
@11-120 Closure(
|
||||
[
|
||||
@16-24 Identifier {
|
||||
ident: "#!a1",
|
||||
ident: "#!2_arg",
|
||||
},
|
||||
],
|
||||
@11-120 When(
|
||||
@16-24 Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
ident: "#!2_arg",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
|
@ -56,24 +58,68 @@ Defs {
|
|||
ident: "await",
|
||||
},
|
||||
[
|
||||
@54-65 Apply(
|
||||
@54-65 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@54-65,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @54-65 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
ann_type: @54-65 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@54-65 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@54-65 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @54-65 Identifier {
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
body_expr: @54-65 Apply(
|
||||
@54-65 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@60-65 Str(
|
||||
PlainLine(
|
||||
"foo",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@54-65 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
ident: "#!1_stmt",
|
||||
},
|
||||
[
|
||||
@60-65 Str(
|
||||
PlainLine(
|
||||
"foo",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@54-65 Closure(
|
||||
[
|
||||
@54-65 RecordDestructure(
|
||||
[],
|
||||
@54-65 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
@78-89 Apply(
|
||||
|
|
|
@ -13,9 +13,11 @@ Defs {
|
|||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -35,13 +37,13 @@ Defs {
|
|||
@11-74 Closure(
|
||||
[
|
||||
@16-24 Identifier {
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@11-74 When(
|
||||
@16-24 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
|
|
|
@ -382,7 +382,7 @@ mod test_can {
|
|||
let arena = Bump::new();
|
||||
let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src);
|
||||
|
||||
assert_eq!(problems.len(), 0);
|
||||
assert_eq!(problems, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -399,7 +399,7 @@ mod test_can {
|
|||
let arena = Bump::new();
|
||||
let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src);
|
||||
|
||||
assert_eq!(problems.len(), 0);
|
||||
assert_eq!(problems, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -128,7 +128,7 @@ mod suffixed_tests {
|
|||
* Example with a parens suffixed sub-expression
|
||||
* in the function part of an Apply.
|
||||
*
|
||||
* Note how the parens unwraps into an intermediate answer #!a0 instead of
|
||||
* Note how the parens unwraps into an intermediate answer #!0_arg instead of
|
||||
* unwrapping the def `do`.
|
||||
*
|
||||
*/
|
||||
|
@ -162,7 +162,7 @@ mod suffixed_tests {
|
|||
/**
|
||||
* Example with a multiple suffixed Var
|
||||
*
|
||||
* Note it unwraps into an intermediate answer `#!a0`
|
||||
* Note it unwraps into an intermediate answer `#!0_arg`
|
||||
*
|
||||
*/
|
||||
#[test]
|
||||
|
@ -546,43 +546,36 @@ mod suffixed_tests {
|
|||
"##
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_annotation() {
|
||||
run_test!(
|
||||
r##"
|
||||
f = \x ->
|
||||
r : A
|
||||
r = x!
|
||||
Task.ok r
|
||||
"##
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_suffixed_helpers {
|
||||
|
||||
use roc_can::suffixed::is_matching_intermediate_answer;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr;
|
||||
use roc_parse::ast::Pattern;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
#[test]
|
||||
fn test_matching_answer() {
|
||||
let loc_pat = Loc::at_zero(Pattern::Identifier { ident: "#!a0" });
|
||||
let loc_pat = Loc::at_zero(Pattern::Identifier { ident: "#!0_arg" });
|
||||
let loc_new = Loc::at_zero(Expr::Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
ident: "#!0_arg",
|
||||
});
|
||||
|
||||
std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matching_answer_task_ok() {
|
||||
let loc_pat = Loc::at_zero(Pattern::Identifier { ident: "#!a0" });
|
||||
let intermetiate = &[&Loc::at_zero(Expr::Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
})];
|
||||
let task_ok = Loc::at_zero(Expr::Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "ok",
|
||||
});
|
||||
|
||||
let loc_new = Loc::at_zero(Expr::Apply(&task_ok, intermetiate, CalledVia::BangSuffix));
|
||||
|
||||
std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -543,7 +543,7 @@ impl<'a> Formattable for Expr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_str_multiline(literal: &StrLiteral) -> bool {
|
||||
pub fn is_str_multiline(literal: &StrLiteral) -> bool {
|
||||
use roc_parse::ast::StrLiteral::*;
|
||||
|
||||
match literal {
|
||||
|
@ -671,10 +671,6 @@ fn push_op(buf: &mut Buf, op: BinOp) {
|
|||
called_via::BinOp::And => buf.push_str("&&"),
|
||||
called_via::BinOp::Or => buf.push_str("||"),
|
||||
called_via::BinOp::Pizza => buf.push_str("|>"),
|
||||
called_via::BinOp::Assignment => unreachable!(),
|
||||
called_via::BinOp::IsAliasType => unreachable!(),
|
||||
called_via::BinOp::IsOpaqueType => unreachable!(),
|
||||
called_via::BinOp::Backpassing => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1708,10 +1704,6 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
|
|||
| BinOp::And
|
||||
| BinOp::Or
|
||||
| BinOp::Pizza => true,
|
||||
BinOp::Assignment
|
||||
| BinOp::IsAliasType
|
||||
| BinOp::IsOpaqueType
|
||||
| BinOp::Backpassing => false,
|
||||
})
|
||||
}
|
||||
Expr::If(_, _) => true,
|
||||
|
|
|
@ -3,10 +3,8 @@ use std::cmp::max;
|
|||
use crate::annotation::{is_collection_multiline, Formattable, Newlines, Parens};
|
||||
use crate::collection::{fmt_collection, Braces};
|
||||
use crate::expr::fmt_str_literal;
|
||||
use crate::spaces::RemoveSpaces;
|
||||
use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT};
|
||||
use crate::Buf;
|
||||
use bumpalo::Bump;
|
||||
use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces};
|
||||
use roc_parse::header::{
|
||||
AppHeader, ExposedName, ExposesKeyword, HostedHeader, ImportsEntry, ImportsKeyword, Keyword,
|
||||
|
@ -57,12 +55,6 @@ macro_rules! keywords {
|
|||
buf.push_str($name::KEYWORD);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for $name {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::annotation::{Formattable, Newlines, Parens};
|
||||
use crate::expr::{fmt_str_literal, format_sq_literal};
|
||||
use crate::expr::{fmt_str_literal, format_sq_literal, is_str_multiline};
|
||||
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{Base, CommentOrNewline, Pattern, PatternAs};
|
||||
|
@ -48,7 +48,7 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
pattern
|
||||
);
|
||||
|
||||
spaces.iter().any(|s| s.is_comment())
|
||||
spaces.iter().any(|s| s.is_comment()) || pattern.is_multiline()
|
||||
}
|
||||
|
||||
Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()),
|
||||
|
@ -63,15 +63,17 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
list_rest_spaces.iter().any(|s| s.is_comment()) || pattern_as.is_multiline()
|
||||
}
|
||||
},
|
||||
Pattern::StrLiteral(literal) => is_str_multiline(literal),
|
||||
Pattern::Apply(pat, args) => {
|
||||
pat.is_multiline() || args.iter().any(|a| a.is_multiline())
|
||||
}
|
||||
|
||||
Pattern::Identifier { .. }
|
||||
| Pattern::Tag(_)
|
||||
| Pattern::OpaqueRef(_)
|
||||
| Pattern::Apply(_, _)
|
||||
| Pattern::NumLiteral(..)
|
||||
| Pattern::NonBase10Literal { .. }
|
||||
| Pattern::FloatLiteral(..)
|
||||
| Pattern::StrLiteral(_)
|
||||
| Pattern::SingleQuote(_)
|
||||
| Pattern::Underscore(_)
|
||||
| Pattern::Malformed(_)
|
||||
|
@ -100,7 +102,13 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
buf.indent(indent);
|
||||
// Sometimes, an Apply pattern needs parens around it.
|
||||
// In particular when an Apply's argument is itself an Apply (> 0) arguments
|
||||
let parens = !loc_arg_patterns.is_empty() && parens == Parens::InApply;
|
||||
let parens = !loc_arg_patterns.is_empty() && (parens == Parens::InApply);
|
||||
|
||||
let indent_more = if self.is_multiline() {
|
||||
indent + INDENT
|
||||
} else {
|
||||
indent
|
||||
};
|
||||
|
||||
if parens {
|
||||
buf.push('(');
|
||||
|
@ -110,7 +118,7 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
|
||||
for loc_arg in loc_arg_patterns.iter() {
|
||||
buf.spaces(1);
|
||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent_more);
|
||||
}
|
||||
|
||||
if parens {
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_module::called_via::{BinOp, UnaryOp};
|
||||
use roc_parse::{
|
||||
ast::{
|
||||
AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr,
|
||||
Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias,
|
||||
ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation,
|
||||
IngestedFileImport, Module, ModuleImport, ModuleImportParams, OldRecordBuilderField,
|
||||
Pattern, PatternAs, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef, WhenBranch,
|
||||
},
|
||||
header::{
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, KeywordItem, ModuleHeader, ModuleName,
|
||||
ModuleParams, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires,
|
||||
ProvidesTo, To, TypedIdent,
|
||||
},
|
||||
ident::{BadIdent, UppercaseIdent},
|
||||
};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
use roc_parse::{ast::CommentOrNewline, remove_spaces::RemoveSpaces};
|
||||
|
||||
use crate::{Ast, Buf};
|
||||
|
||||
|
@ -211,20 +193,6 @@ fn fmt_docs(buf: &mut Buf, docs: &str) {
|
|||
buf.push_str(docs.trim_end());
|
||||
}
|
||||
|
||||
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.
|
||||
///
|
||||
/// Currently this consists of:
|
||||
/// * Removing newlines
|
||||
/// * Removing comments
|
||||
/// * Removing parens in Exprs
|
||||
///
|
||||
/// Long term, we actually want this transform to preserve comments (so we can assert they're maintained by formatting)
|
||||
/// - but there are currently several bugs where they're _not_ preserved.
|
||||
/// TODO: ensure formatting retains comments
|
||||
pub trait RemoveSpaces<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Ast<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
Ast {
|
||||
|
@ -233,832 +201,3 @@ impl<'a> RemoveSpaces<'a> for Ast<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Defs<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let mut defs = self.clone();
|
||||
|
||||
defs.spaces.clear();
|
||||
defs.space_before.clear();
|
||||
defs.space_after.clear();
|
||||
|
||||
for type_def in defs.type_defs.iter_mut() {
|
||||
*type_def = type_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = value_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
for region_def in defs.regions.iter_mut() {
|
||||
*region_def = region_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
defs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
Spaces {
|
||||
before: &[],
|
||||
item: self.item.remove_spaces(arena),
|
||||
after: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
KeywordItem {
|
||||
keyword: self.keyword.remove_spaces(arena),
|
||||
item: self.item.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ProvidesTo {
|
||||
provides_keyword: self.provides_keyword.remove_spaces(arena),
|
||||
entries: self.entries.remove_spaces(arena),
|
||||
types: self.types.remove_spaces(arena),
|
||||
to_keyword: self.to_keyword.remove_spaces(arena),
|
||||
to: self.to.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Module<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let header = match &self.header {
|
||||
Header::Module(header) => Header::Module(ModuleHeader {
|
||||
after_keyword: &[],
|
||||
params: header.params.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
interface_imports: header.interface_imports.remove_spaces(arena),
|
||||
}),
|
||||
Header::App(header) => Header::App(AppHeader {
|
||||
before_provides: &[],
|
||||
provides: header.provides.remove_spaces(arena),
|
||||
before_packages: &[],
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
old_imports: header.old_imports.remove_spaces(arena),
|
||||
old_provides_to_new_package: header
|
||||
.old_provides_to_new_package
|
||||
.remove_spaces(arena),
|
||||
}),
|
||||
Header::Package(header) => Header::Package(PackageHeader {
|
||||
before_exposes: &[],
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
before_packages: &[],
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
}),
|
||||
Header::Platform(header) => Header::Platform(PlatformHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
requires: header.requires.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
provides: header.provides.remove_spaces(arena),
|
||||
}),
|
||||
Header::Hosted(header) => Header::Hosted(HostedHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
}),
|
||||
};
|
||||
Module {
|
||||
comments: &[],
|
||||
header,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleParams<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ModuleParams {
|
||||
params: self.params.remove_spaces(arena),
|
||||
before_arrow: &[],
|
||||
after_arrow: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Region {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
Region::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for &'a str {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
|
||||
Spaced::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Spaced::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ExposedName<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PackageName<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for To<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
To::ExistingPackage(a) => To::ExistingPackage(a),
|
||||
To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
TypedIdent {
|
||||
ident: self.ident.remove_spaces(arena),
|
||||
spaces_before_colon: &[],
|
||||
ann: self.ann.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
PlatformRequires {
|
||||
rigids: self.rigids.remove_spaces(arena),
|
||||
signature: self.signature.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
PackageEntry {
|
||||
shorthand: self.shorthand,
|
||||
spaces_after_shorthand: &[],
|
||||
platform_marker: match self.platform_marker {
|
||||
Some(_) => Some(&[]),
|
||||
None => None,
|
||||
},
|
||||
package_name: self.package_name.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
|
||||
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
|
||||
ImportsEntry::IngestedFile(a, b) => {
|
||||
ImportsEntry::IngestedFile(a, b.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option<T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
self.as_ref().map(|a| a.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc<T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let res = self.value.remove_spaces(arena);
|
||||
Loc::at(Region::zero(), res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
(self.0.remove_spaces(arena), self.1.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let mut items = Vec::with_capacity_in(self.items.len(), arena);
|
||||
for item in self.items {
|
||||
items.push(item.remove_spaces(arena));
|
||||
}
|
||||
Collection::with_items(items.into_bump_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let mut items = Vec::with_capacity_in(self.len(), arena);
|
||||
for item in *self {
|
||||
let res = item.remove_spaces(arena);
|
||||
items.push(res);
|
||||
}
|
||||
items.into_bump_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for UnaryOp {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for BinOp {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
arena.alloc((*self).remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
use TypeDef::*;
|
||||
|
||||
match *self {
|
||||
Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
} => Alias {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
ann: ann.remove_spaces(arena),
|
||||
},
|
||||
Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
typ,
|
||||
derived,
|
||||
} => Opaque {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
typ: typ.remove_spaces(arena),
|
||||
derived: derived.remove_spaces(arena),
|
||||
},
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_implements: loc_has,
|
||||
members,
|
||||
} => Ability {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
loc_implements: loc_has.remove_spaces(arena),
|
||||
members: members.remove_spaces(arena),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
use ValueDef::*;
|
||||
|
||||
match *self {
|
||||
Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)),
|
||||
Body(a, b) => Body(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: _,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => AnnotatedBody {
|
||||
ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)),
|
||||
ann_type: arena.alloc(ann_type.remove_spaces(arena)),
|
||||
comment: None,
|
||||
body_pattern: arena.alloc(body_pattern.remove_spaces(arena)),
|
||||
body_expr: arena.alloc(body_expr.remove_spaces(arena)),
|
||||
},
|
||||
Dbg {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => Dbg {
|
||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
Expect {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => Expect {
|
||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
ExpectFx {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => ExpectFx {
|
||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)),
|
||||
IngestedFileImport(ingested_file_import) => {
|
||||
IngestedFileImport(ingested_file_import.remove_spaces(arena))
|
||||
}
|
||||
Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleImport<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ModuleImport {
|
||||
before_name: &[],
|
||||
name: self.name.remove_spaces(arena),
|
||||
params: self.params.remove_spaces(arena),
|
||||
alias: self.alias.remove_spaces(arena),
|
||||
exposed: self.exposed.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleImportParams<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ModuleImportParams {
|
||||
before: &[],
|
||||
params: self.params.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
IngestedFileImport {
|
||||
before_path: &[],
|
||||
path: self.path.remove_spaces(arena),
|
||||
name: self.name.remove_spaces(arena),
|
||||
annotation: self.annotation.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ImportedModuleName {
|
||||
package: self.package.remove_spaces(arena),
|
||||
name: self.name.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportAlias<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportAsKeyword {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportExposingKeyword {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for IngestedFileAnnotation<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
IngestedFileAnnotation {
|
||||
before_colon: &[],
|
||||
annotation: self.annotation.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Implements<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
Implements::Implements
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for AbilityMember<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
AbilityMember {
|
||||
name: self.name.remove_spaces(arena),
|
||||
typ: self.typ.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for WhenBranch<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
WhenBranch {
|
||||
patterns: self.patterns.remove_spaces(arena),
|
||||
value: self.value.remove_spaces(arena),
|
||||
guard: self.guard.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue(
|
||||
a.remove_spaces(arena),
|
||||
arena.alloc([]),
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue(
|
||||
a.remove_spaces(arena),
|
||||
arena.alloc([]),
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)),
|
||||
AssignedField::Malformed(a) => AssignedField::Malformed(a),
|
||||
AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value(
|
||||
a.remove_spaces(arena),
|
||||
&[],
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue(
|
||||
a.remove_spaces(arena),
|
||||
&[],
|
||||
&[],
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
OldRecordBuilderField::LabelOnly(a) => {
|
||||
OldRecordBuilderField::LabelOnly(a.remove_spaces(arena))
|
||||
}
|
||||
OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a),
|
||||
OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for StrLiteral<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t),
|
||||
StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)),
|
||||
StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for StrSegment<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
StrSegment::Plaintext(t) => StrSegment::Plaintext(t),
|
||||
StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)),
|
||||
StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c),
|
||||
StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)),
|
||||
StrSegment::DeprecatedInterpolated(t) => {
|
||||
StrSegment::DeprecatedInterpolated(t.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Expr::Float(a) => Expr::Float(a),
|
||||
Expr::Num(a) => Expr::Num(a),
|
||||
Expr::NonBase10Int {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
} => Expr::NonBase10Int {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
},
|
||||
Expr::Str(a) => Expr::Str(a.remove_spaces(arena)),
|
||||
Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b),
|
||||
Expr::AccessorFunction(a) => Expr::AccessorFunction(a),
|
||||
Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b),
|
||||
Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))),
|
||||
Expr::List(a) => Expr::List(a.remove_spaces(arena)),
|
||||
Expr::RecordUpdate { update, fields } => Expr::RecordUpdate {
|
||||
update: arena.alloc(update.remove_spaces(arena)),
|
||||
fields: fields.remove_spaces(arena),
|
||||
},
|
||||
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
|
||||
Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)),
|
||||
Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder {
|
||||
mapper: arena.alloc(mapper.remove_spaces(arena)),
|
||||
fields: fields.remove_spaces(arena),
|
||||
},
|
||||
Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)),
|
||||
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
||||
Expr::Underscore(a) => Expr::Underscore(a),
|
||||
Expr::Tag(a) => Expr::Tag(a),
|
||||
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
||||
Expr::Closure(a, b) => Expr::Closure(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Expr::Crash => Expr::Crash,
|
||||
Expr::Defs(a, b) => {
|
||||
let mut defs = a.clone();
|
||||
defs.space_before = vec![Default::default(); defs.len()];
|
||||
defs.space_after = vec![Default::default(); defs.len()];
|
||||
defs.regions = vec![Region::zero(); defs.len()];
|
||||
defs.spaces.clear();
|
||||
|
||||
for type_def in defs.type_defs.iter_mut() {
|
||||
*type_def = type_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = value_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Expr::Backpassing(a, b, c) => Expr::Backpassing(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
Expr::Expect(a, b) => Expr::Expect(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Expr::Dbg(a, b) => Expr::Dbg(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Expr::LowLevelDbg(_, _, _) => unreachable!(
|
||||
"LowLevelDbg should only exist after desugaring, not during formatting"
|
||||
),
|
||||
Expr::Apply(a, b, c) => Expr::Apply(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
b.remove_spaces(arena),
|
||||
c,
|
||||
),
|
||||
Expr::BinOps(a, b) => {
|
||||
Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Expr::UnaryOp(a, b) => {
|
||||
Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
||||
}
|
||||
Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))),
|
||||
Expr::When(a, b) => {
|
||||
Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
||||
}
|
||||
Expr::ParensAround(a) => {
|
||||
// The formatter can remove redundant parentheses, so also remove these when normalizing for comparison.
|
||||
a.remove_spaces(arena)
|
||||
}
|
||||
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||
Expr::MalformedClosure => Expr::MalformedClosure,
|
||||
Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a),
|
||||
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
||||
Expr::MultipleOldRecordBuilders(a) => Expr::MultipleOldRecordBuilders(a),
|
||||
Expr::UnappliedOldRecordBuilder(a) => Expr::UnappliedOldRecordBuilder(a),
|
||||
Expr::EmptyRecordBuilder(a) => Expr::EmptyRecordBuilder(a),
|
||||
Expr::SingleFieldRecordBuilder(a) => Expr::SingleFieldRecordBuilder(a),
|
||||
Expr::OptionalFieldInRecordBuilder(name, a) => {
|
||||
Expr::OptionalFieldInRecordBuilder(name, a)
|
||||
}
|
||||
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Expr::SingleQuote(a) => Expr::Num(a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
|
||||
match ident {
|
||||
BadIdent::Start(_) => BadIdent::Start(Position::zero()),
|
||||
BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()),
|
||||
BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()),
|
||||
BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()),
|
||||
BadIdent::UnderscoreAtStart {
|
||||
position: _,
|
||||
declaration_region,
|
||||
} => BadIdent::UnderscoreAtStart {
|
||||
position: Position::zero(),
|
||||
declaration_region,
|
||||
},
|
||||
BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()),
|
||||
BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()),
|
||||
BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()),
|
||||
BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()),
|
||||
BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()),
|
||||
BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()),
|
||||
BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Pattern::Identifier { ident } => Pattern::Identifier { ident },
|
||||
Pattern::Tag(a) => Pattern::Tag(a),
|
||||
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
||||
Pattern::Apply(a, b) => Pattern::Apply(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)),
|
||||
Pattern::RequiredField(a, b) => {
|
||||
Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Pattern::OptionalField(a, b) => {
|
||||
Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Pattern::As(pattern, pattern_as) => Pattern::As(
|
||||
arena.alloc(pattern.remove_spaces(arena)),
|
||||
pattern_as.remove_spaces(arena),
|
||||
),
|
||||
Pattern::NumLiteral(a) => Pattern::NumLiteral(a),
|
||||
Pattern::NonBase10Literal {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
} => Pattern::NonBase10Literal {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
},
|
||||
Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a),
|
||||
Pattern::StrLiteral(a) => Pattern::StrLiteral(a),
|
||||
Pattern::Underscore(a) => Pattern::Underscore(a),
|
||||
Pattern::Malformed(a) => Pattern::Malformed(a),
|
||||
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||
Pattern::QualifiedIdentifier { module_name, ident } => {
|
||||
Pattern::QualifiedIdentifier { module_name, ident }
|
||||
}
|
||||
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Pattern::SingleQuote(a) => Pattern::SingleQuote(a),
|
||||
Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)),
|
||||
Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)),
|
||||
Pattern::ListRest(opt_pattern_as) => Pattern::ListRest(
|
||||
opt_pattern_as
|
||||
.map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
TypeAnnotation::Function(a, b) => TypeAnnotation::Function(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)),
|
||||
TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a),
|
||||
TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
&[],
|
||||
TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
),
|
||||
TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple {
|
||||
elems: fields.remove_spaces(arena),
|
||||
ext: ext.remove_spaces(arena),
|
||||
},
|
||||
TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record {
|
||||
fields: fields.remove_spaces(arena),
|
||||
ext: ext.remove_spaces(arena),
|
||||
},
|
||||
TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion {
|
||||
ext: ext.remove_spaces(arena),
|
||||
tags: tags.remove_spaces(arena),
|
||||
},
|
||||
TypeAnnotation::Inferred => TypeAnnotation::Inferred,
|
||||
TypeAnnotation::Wildcard => TypeAnnotation::Wildcard,
|
||||
TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where(
|
||||
arena.alloc(annot.remove_spaces(arena)),
|
||||
arena.alloc(has_clauses.remove_spaces(arena)),
|
||||
),
|
||||
TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ImplementsClause {
|
||||
var: self.var.remove_spaces(arena),
|
||||
abilities: self.abilities.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Tag::Apply { name, args } => Tag::Apply {
|
||||
name: name.remove_spaces(arena),
|
||||
args: args.remove_spaces(arena),
|
||||
},
|
||||
Tag::Malformed(a) => Tag::Malformed(a),
|
||||
Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
AbilityImpls::AbilityImpls(impls) => {
|
||||
AbilityImpls::AbilityImpls(impls.remove_spaces(arena))
|
||||
}
|
||||
AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => {
|
||||
has.remove_spaces(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
ImplementsAbility::ImplementsAbility { ability, impls } => {
|
||||
ImplementsAbility::ImplementsAbility {
|
||||
ability: ability.remove_spaces(arena),
|
||||
impls: impls.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => {
|
||||
has.remove_spaces(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
ImplementsAbilities::Implements(derived) => {
|
||||
ImplementsAbilities::Implements(derived.remove_spaces(arena))
|
||||
}
|
||||
ImplementsAbilities::SpaceBefore(derived, _)
|
||||
| ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PatternAs<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
PatternAs {
|
||||
spaces_before: &[],
|
||||
identifier: self.identifier.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2318,6 +2318,22 @@ impl<
|
|||
refcount_proc_name
|
||||
}
|
||||
|
||||
fn build_indirect_copy(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
let (refcount_proc_name, linker_data) =
|
||||
self.helper_proc_gen
|
||||
.gen_copy_proc(ident_ids, self.layout_interner, layout);
|
||||
|
||||
self.helper_proc_symbols_mut().extend(linker_data);
|
||||
|
||||
refcount_proc_name
|
||||
}
|
||||
|
||||
fn build_higher_order_lowlevel(
|
||||
&mut self,
|
||||
dst: &Symbol,
|
||||
|
@ -2413,8 +2429,9 @@ impl<
|
|||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(element_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(element_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(element_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(element_layout);
|
||||
let dec_fn_ptr = self.decrement_fn_pointer(element_layout);
|
||||
let copy_fn_ptr = self.copy_fn_pointer(element_layout);
|
||||
|
||||
// input: RocList,
|
||||
// caller: CompareFn,
|
||||
|
@ -2426,6 +2443,7 @@ impl<
|
|||
// element_refcounted: bool,
|
||||
// inc: Inc,
|
||||
// dec: Dec,
|
||||
// copy: CopyFn,
|
||||
|
||||
let arguments = [
|
||||
xs,
|
||||
|
@ -2436,8 +2454,9 @@ impl<
|
|||
alignment,
|
||||
element_width,
|
||||
Symbol::DEV_TMP3,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
inc_fn_ptr,
|
||||
dec_fn_ptr,
|
||||
copy_fn_ptr,
|
||||
];
|
||||
|
||||
let layouts = [
|
||||
|
@ -2451,6 +2470,7 @@ impl<
|
|||
Layout::BOOL,
|
||||
usize_,
|
||||
usize_,
|
||||
usize_,
|
||||
];
|
||||
|
||||
self.build_fn_call_stack_return(
|
||||
|
@ -2493,8 +2513,8 @@ impl<
|
|||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(elem_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(elem_layout);
|
||||
let dec_fn_ptr = self.decrement_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
|
@ -2510,9 +2530,9 @@ impl<
|
|||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
// inc
|
||||
inc_elem_fn,
|
||||
inc_fn_ptr,
|
||||
// dec
|
||||
dec_elem_fn,
|
||||
dec_fn_ptr,
|
||||
];
|
||||
let usize_layout = Layout::U64;
|
||||
let lowlevel_arg_layouts = [
|
||||
|
@ -2564,7 +2584,7 @@ impl<
|
|||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
|
@ -2580,7 +2600,7 @@ impl<
|
|||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
// Inc element fn
|
||||
inc_elem_fn,
|
||||
inc_fn_ptr,
|
||||
];
|
||||
let layout_usize = Layout::U64;
|
||||
let lowlevel_arg_layouts = [
|
||||
|
@ -2637,7 +2657,7 @@ impl<
|
|||
self.load_layout_stack_size(element_layout, Symbol::DEV_TMP2);
|
||||
self.load_layout_refcounted(element_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(element_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(element_layout);
|
||||
|
||||
// Load UpdateMode.Immutable argument (0u8)
|
||||
let u8_layout = Layout::U8;
|
||||
|
@ -2664,7 +2684,7 @@ impl<
|
|||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
// Inc element fn
|
||||
inc_elem_fn,
|
||||
inc_fn_ptr,
|
||||
// update_mode
|
||||
Symbol::DEV_TMP4,
|
||||
];
|
||||
|
@ -2733,6 +2753,7 @@ impl<
|
|||
let base_offset =
|
||||
self.storage_manager
|
||||
.claim_stack_area_layout(self.layout_interner, *dst, *ret_layout);
|
||||
let copy_fn_ptr = self.copy_fn_pointer(elem_layout);
|
||||
|
||||
let lowlevel_args = [
|
||||
list,
|
||||
|
@ -2740,8 +2761,11 @@ impl<
|
|||
Symbol::DEV_TMP,
|
||||
// element_width
|
||||
Symbol::DEV_TMP2,
|
||||
// copy
|
||||
copy_fn_ptr,
|
||||
];
|
||||
let lowlevel_arg_layouts = [list_layout, Layout::U64, Layout::U64];
|
||||
let usize_layout = Layout::U64;
|
||||
let lowlevel_arg_layouts = [list_layout, Layout::U64, Layout::U64, usize_layout];
|
||||
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP3,
|
||||
|
@ -2844,8 +2868,9 @@ impl<
|
|||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP4);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(elem_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(elem_layout);
|
||||
let dec_fn_ptr = self.decrement_fn_pointer(elem_layout);
|
||||
let copy_fn_ptr = self.copy_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
|
@ -2891,9 +2916,10 @@ impl<
|
|||
Symbol::DEV_TMP2,
|
||||
Symbol::DEV_TMP3,
|
||||
Symbol::DEV_TMP4,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
inc_fn_ptr,
|
||||
dec_fn_ptr,
|
||||
Symbol::DEV_TMP5,
|
||||
copy_fn_ptr,
|
||||
];
|
||||
let lowlevel_arg_layouts = [
|
||||
list_layout,
|
||||
|
@ -2905,6 +2931,7 @@ impl<
|
|||
u64_layout,
|
||||
u64_layout,
|
||||
u64_layout,
|
||||
u64_layout,
|
||||
];
|
||||
|
||||
let out = self.debug_symbol("out");
|
||||
|
@ -2955,8 +2982,8 @@ impl<
|
|||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(elem_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(elem_layout);
|
||||
let dec_fn_ptr = self.decrement_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
|
@ -2973,8 +3000,8 @@ impl<
|
|||
Symbol::DEV_TMP2,
|
||||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
inc_fn_ptr,
|
||||
dec_fn_ptr,
|
||||
];
|
||||
let lowlevel_arg_layouts = [
|
||||
list_a_layout,
|
||||
|
@ -3042,7 +3069,8 @@ impl<
|
|||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP4);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(elem_layout);
|
||||
let copy_fn_ptr = self.copy_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
|
@ -3060,7 +3088,9 @@ impl<
|
|||
// element_refcounted
|
||||
Symbol::DEV_TMP4,
|
||||
// inc
|
||||
inc_elem_fn,
|
||||
inc_fn_ptr,
|
||||
// copy
|
||||
copy_fn_ptr,
|
||||
];
|
||||
let usize_layout = Layout::U64;
|
||||
let lowlevel_arg_layouts = [
|
||||
|
@ -3070,6 +3100,7 @@ impl<
|
|||
Layout::U64,
|
||||
Layout::BOOL,
|
||||
usize_layout,
|
||||
usize_layout,
|
||||
];
|
||||
|
||||
self.build_fn_call(
|
||||
|
|
|
@ -485,6 +485,26 @@ trait Backend<'a> {
|
|||
element_decrement
|
||||
}
|
||||
|
||||
fn copy_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let box_layout = self
|
||||
.interner_mut()
|
||||
.insert_direct_no_semantic(LayoutRepr::Ptr(layout));
|
||||
|
||||
let element_copy = self.debug_symbol("element_copy");
|
||||
let element_copy_symbol = self.build_indirect_copy(layout);
|
||||
|
||||
let element_copy_string = self.lambda_name_to_string(
|
||||
LambdaName::no_niche(element_copy_symbol),
|
||||
[box_layout, box_layout].into_iter(),
|
||||
None,
|
||||
Layout::UNIT,
|
||||
);
|
||||
|
||||
self.build_fn_pointer(&element_copy, element_copy_string);
|
||||
|
||||
element_copy
|
||||
}
|
||||
|
||||
fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a>;
|
||||
|
||||
fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
@ -1955,8 +1975,9 @@ trait Backend<'a> {
|
|||
let update_mode = self.debug_symbol("update_mode");
|
||||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(list_argument.element_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(list_argument.element_layout);
|
||||
let dec_fn_ptr = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
let copy_fn_ptr = self.copy_fn_pointer(list_argument.element_layout);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
|
@ -1969,6 +1990,7 @@ trait Backend<'a> {
|
|||
// inc: Inc
|
||||
// dec: Dec
|
||||
// update_mode: UpdateMode,
|
||||
// copy: CopyFn
|
||||
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
|
@ -1980,9 +2002,10 @@ trait Backend<'a> {
|
|||
i,
|
||||
j,
|
||||
list_argument.element_refcounted,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
inc_fn_ptr,
|
||||
dec_fn_ptr,
|
||||
update_mode,
|
||||
copy_fn_ptr,
|
||||
],
|
||||
&[
|
||||
list_layout,
|
||||
|
@ -1994,6 +2017,7 @@ trait Backend<'a> {
|
|||
layout_usize,
|
||||
layout_usize,
|
||||
Layout::U8,
|
||||
layout_usize,
|
||||
],
|
||||
ret_layout,
|
||||
);
|
||||
|
@ -2007,8 +2031,8 @@ trait Backend<'a> {
|
|||
let update_mode = self.debug_symbol("update_mode");
|
||||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(list_argument.element_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
let inc_fn_ptr = self.increment_fn_pointer(list_argument.element_layout);
|
||||
let dec_fn_ptr = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
|
@ -2016,8 +2040,8 @@ trait Backend<'a> {
|
|||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// element_refcounted: bool,
|
||||
// inc_elem_fn: Inc,
|
||||
// dec_elem_fn: Dec,
|
||||
// inc_fn_ptr: Inc,
|
||||
// dec_fn_ptr: Dec,
|
||||
// update_mode: UpdateMode,
|
||||
|
||||
self.build_fn_call(
|
||||
|
@ -2028,8 +2052,8 @@ trait Backend<'a> {
|
|||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
list_argument.element_refcounted,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
inc_fn_ptr,
|
||||
dec_fn_ptr,
|
||||
update_mode,
|
||||
],
|
||||
&[
|
||||
|
@ -2082,7 +2106,7 @@ trait Backend<'a> {
|
|||
let list_layout = arg_layouts[0];
|
||||
let list_argument = self.list_argument(list_layout);
|
||||
|
||||
let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
let dec_fn_ptr = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
|
@ -2090,7 +2114,7 @@ trait Backend<'a> {
|
|||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// element_refcounted: bool,
|
||||
// dec_elem_fn: Dec,
|
||||
// dec_fn_ptr: Dec,
|
||||
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
|
@ -2100,7 +2124,7 @@ trait Backend<'a> {
|
|||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
list_argument.element_refcounted,
|
||||
dec_elem_fn,
|
||||
dec_fn_ptr,
|
||||
],
|
||||
&[
|
||||
list_layout,
|
||||
|
@ -2582,6 +2606,7 @@ trait Backend<'a> {
|
|||
fn build_indirect_inc(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
fn build_indirect_inc_n(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
fn build_indirect_dec(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
fn build_indirect_copy(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
|
||||
fn build_list_clone(
|
||||
&mut self,
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::llvm::build::{
|
|||
FAST_CALL_CONV,
|
||||
};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::memcpy::build_memcpy;
|
||||
use crate::llvm::refcounting::{
|
||||
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
|
||||
};
|
||||
|
@ -72,18 +73,8 @@ fn call_bitcode_fn_help<'ctx>(
|
|||
.map(|x| {
|
||||
if env.target.operating_system() == roc_target::OperatingSystem::Windows {
|
||||
if x.get_type() == env.context.i128_type().into() {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
let alloca = create_entry_block_alloca(
|
||||
env,
|
||||
parent,
|
||||
x.get_type(),
|
||||
"pass_u128_by_reference",
|
||||
);
|
||||
let alloca =
|
||||
create_entry_block_alloca(env, x.get_type(), "pass_u128_by_reference");
|
||||
|
||||
env.builder.build_store(alloca, *x).unwrap();
|
||||
|
||||
|
@ -163,7 +154,8 @@ pub fn call_bitcode_fn_fixing_for_convention<'a, 'ctx, 'env>(
|
|||
|
||||
// when we write an i128 into this (happens in NumToInt), zig expects this pointer to
|
||||
// be 16-byte aligned. Not doing so is UB and will immediately fail on CI
|
||||
let cc_return_value_ptr = env.builder.new_build_alloca(cc_return_type, "return_value");
|
||||
let cc_return_value_ptr =
|
||||
create_entry_block_alloca(env, cc_return_type, "return_value");
|
||||
cc_return_value_ptr
|
||||
.as_instruction()
|
||||
.unwrap()
|
||||
|
@ -745,6 +737,78 @@ pub fn build_compare_wrapper<'a, 'ctx>(
|
|||
function_value
|
||||
}
|
||||
|
||||
pub fn build_copy_wrapper<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: InLayout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let symbol = Symbol::GENERIC_COPY_REF;
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &layout_interner.get_repr(layout))
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
|
||||
let function_value = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let arg_type = env.context.i8_type().ptr_type(AddressSpace::default());
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
env.context.void_type().into(),
|
||||
&[arg_type.into(), arg_type.into()],
|
||||
);
|
||||
|
||||
// called from zig, must use C calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 0);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let mut it = function_value.get_param_iter();
|
||||
let dst_ptr = it.next().unwrap().into_pointer_value();
|
||||
let src_ptr = it.next().unwrap().into_pointer_value();
|
||||
|
||||
dst_ptr.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
src_ptr.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
|
||||
let repr = layout_interner.get_repr(layout);
|
||||
let value_type = basic_type_from_layout(env, layout_interner, repr)
|
||||
.ptr_type(AddressSpace::default());
|
||||
|
||||
let dst_cast = env
|
||||
.builder
|
||||
.new_build_pointer_cast(dst_ptr, value_type, "load_opaque");
|
||||
|
||||
let src_cast = env
|
||||
.builder
|
||||
.new_build_pointer_cast(src_ptr, value_type, "load_opaque");
|
||||
|
||||
build_memcpy(env, layout_interner, repr, dst_cast, src_cast);
|
||||
|
||||
env.builder.new_build_return(None);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder.set_current_debug_location(di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
|
||||
enum BitcodeReturnValue<'ctx> {
|
||||
List(PointerValue<'ctx>),
|
||||
Str(PointerValue<'ctx>),
|
||||
|
@ -818,14 +882,7 @@ impl BitcodeReturns {
|
|||
BitcodeReturns::List => {
|
||||
let list_type = super::convert::zig_list_type(env);
|
||||
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
let result =
|
||||
create_entry_block_alloca(env, parent, list_type.into(), "list_alloca");
|
||||
let result = create_entry_block_alloca(env, list_type, "list_alloca");
|
||||
|
||||
arguments.push(result.into());
|
||||
|
||||
|
@ -834,13 +891,7 @@ impl BitcodeReturns {
|
|||
BitcodeReturns::Str => {
|
||||
let str_type = super::convert::zig_str_type(env);
|
||||
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
let result = create_entry_block_alloca(env, parent, str_type.into(), "str_alloca");
|
||||
let result = create_entry_block_alloca(env, str_type, "str_alloca");
|
||||
|
||||
arguments.push(result.into());
|
||||
|
||||
|
@ -957,14 +1008,8 @@ pub(crate) fn pass_list_to_zig_64bit<'ctx>(
|
|||
env: &Env<'_, 'ctx, '_>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
let list_type = super::convert::zig_list_type(env);
|
||||
let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca");
|
||||
let list_alloca = create_entry_block_alloca(env, list_type, "list_alloca");
|
||||
|
||||
env.builder.new_build_store(list_alloca, list);
|
||||
|
||||
|
@ -975,14 +1020,8 @@ pub(crate) fn pass_list_to_zig_wasm<'ctx>(
|
|||
env: &Env<'_, 'ctx, '_>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
let list_type = super::convert::zig_list_type(env);
|
||||
let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca");
|
||||
let list_alloca = create_entry_block_alloca(env, list_type, "list_alloca");
|
||||
|
||||
env.builder.new_build_store(list_alloca, list);
|
||||
|
||||
|
@ -993,14 +1032,8 @@ pub(crate) fn pass_string_to_zig_wasm<'ctx>(
|
|||
env: &Env<'_, 'ctx, '_>,
|
||||
string: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
let string_type = super::convert::zig_str_type(env);
|
||||
let string_alloca = create_entry_block_alloca(env, parent, string_type.into(), "string_alloca");
|
||||
let string_alloca = create_entry_block_alloca(env, string_type, "string_alloca");
|
||||
|
||||
env.builder.new_build_store(string_alloca, string);
|
||||
|
||||
|
@ -1225,9 +1258,7 @@ pub(crate) fn call_bitcode_fn_with_record_arg<'ctx>(
|
|||
arg: BasicValueEnum<'ctx>,
|
||||
fn_name: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let roc_call_alloca = env
|
||||
.builder
|
||||
.new_build_alloca(arg.get_type(), "roc_call_alloca");
|
||||
let roc_call_alloca = create_entry_block_alloca(env, arg.get_type(), "roc_call_alloca");
|
||||
env.builder.new_build_store(roc_call_alloca, arg);
|
||||
|
||||
let fn_val = env.module.get_function(fn_name).unwrap();
|
||||
|
@ -1235,9 +1266,7 @@ pub(crate) fn call_bitcode_fn_with_record_arg<'ctx>(
|
|||
let mut args: Vec<BasicValueEnum<'ctx>> = Vec::with_capacity(fn_val.count_params() as usize);
|
||||
if fn_val.get_first_param().unwrap().is_pointer_value() {
|
||||
// call by pointer
|
||||
let zig_call_alloca = env
|
||||
.builder
|
||||
.new_build_alloca(arg.get_type(), "zig_return_alloca");
|
||||
let zig_call_alloca = create_entry_block_alloca(env, arg.get_type(), "zig_return_alloca");
|
||||
env.builder.new_build_store(zig_call_alloca, arg);
|
||||
args.push(zig_call_alloca.into());
|
||||
} else if fn_val.count_params() == 1 {
|
||||
|
@ -1283,16 +1312,14 @@ pub(crate) fn call_bitcode_fn_returning_record<'ctx>(
|
|||
.module
|
||||
.get_struct_type(bitcode_return_type_name)
|
||||
.unwrap();
|
||||
zig_return_alloca = env
|
||||
.builder
|
||||
.new_build_alloca(bitcode_return_type, "zig_return_alloca");
|
||||
zig_return_alloca =
|
||||
create_entry_block_alloca(env, bitcode_return_type, "zig_return_alloca");
|
||||
call_void_bitcode_fn(env, &[zig_return_alloca.into(), arg], fn_name);
|
||||
} else {
|
||||
// direct return
|
||||
let zig_result = call_bitcode_fn(env, &[arg], fn_name);
|
||||
zig_return_alloca = env
|
||||
.builder
|
||||
.new_build_alloca(zig_result.get_type(), "zig_return_alloca");
|
||||
zig_return_alloca =
|
||||
create_entry_block_alloca(env, zig_result.get_type(), "zig_return_alloca");
|
||||
env.builder.new_build_store(zig_return_alloca, zig_result);
|
||||
}
|
||||
|
||||
|
|
|
@ -936,9 +936,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
|||
match env.target.ptr_width() {
|
||||
PtrWidth::Bytes4 => {
|
||||
// we need to pass the string by reference, but we currently hold the value.
|
||||
let alloca = env
|
||||
.builder
|
||||
.new_build_alloca(string.get_type(), "alloca_string");
|
||||
let alloca = create_entry_block_alloca(env, string.get_type(), "alloca_string");
|
||||
env.builder.new_build_store(alloca, string);
|
||||
alloca.into()
|
||||
}
|
||||
|
@ -1345,7 +1343,6 @@ fn float_with_precision<'ctx>(
|
|||
pub fn build_exp_literal<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
layout: InLayout<'_>,
|
||||
literal: &roc_mono::ir::Literal<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
|
@ -1382,18 +1379,14 @@ pub fn build_exp_literal<'a, 'ctx>(
|
|||
}
|
||||
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(),
|
||||
Str(str_literal) => build_string_literal(env, parent, str_literal),
|
||||
Str(str_literal) => build_string_literal(env, str_literal),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_string_literal<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
str_literal: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
fn build_string_literal<'ctx>(env: &Env<'_, 'ctx, '_>, str_literal: &str) -> BasicValueEnum<'ctx> {
|
||||
if str_literal.len() < env.small_str_bytes() as usize {
|
||||
match env.small_str_bytes() {
|
||||
24 => small_str_ptr_width_8(env, parent, str_literal).into(),
|
||||
24 => small_str_ptr_width_8(env, str_literal).into(),
|
||||
12 => small_str_ptr_width_4(env, str_literal).into(),
|
||||
_ => unreachable!("incorrect small_str_bytes"),
|
||||
}
|
||||
|
@ -1401,7 +1394,7 @@ fn build_string_literal<'ctx>(
|
|||
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 alloca = const_str_alloca_ptr(env, parent, ptr, number_of_elements, number_of_elements);
|
||||
let alloca = const_str_alloca_ptr(env, ptr, number_of_elements, number_of_elements);
|
||||
|
||||
match env.target.ptr_width() {
|
||||
PtrWidth::Bytes4 => {
|
||||
|
@ -1415,7 +1408,6 @@ fn build_string_literal<'ctx>(
|
|||
|
||||
fn const_str_alloca_ptr<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
len: IntValue<'ctx>,
|
||||
cap: IntValue<'ctx>,
|
||||
|
@ -1424,18 +1416,14 @@ fn const_str_alloca_ptr<'ctx>(
|
|||
|
||||
let value = typ.const_named_struct(&[ptr.into(), len.into(), cap.into()]);
|
||||
|
||||
let alloca = create_entry_block_alloca(env, parent, typ.into(), "const_str_store");
|
||||
let alloca = create_entry_block_alloca(env, typ, "const_str_store");
|
||||
|
||||
env.builder.new_build_store(alloca, value);
|
||||
|
||||
alloca
|
||||
}
|
||||
|
||||
fn small_str_ptr_width_8<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
str_literal: &str,
|
||||
) -> PointerValue<'ctx> {
|
||||
fn small_str_ptr_width_8<'ctx>(env: &Env<'_, 'ctx, '_>, str_literal: &str) -> PointerValue<'ctx> {
|
||||
debug_assert_eq!(env.target.ptr_width() as u8, 8);
|
||||
|
||||
let mut array = [0u8; 24];
|
||||
|
@ -1456,7 +1444,7 @@ fn small_str_ptr_width_8<'ctx>(
|
|||
let ptr_type = env.context.i8_type().ptr_type(address_space);
|
||||
let ptr = env.builder.new_build_int_to_ptr(ptr, ptr_type, "to_u8_ptr");
|
||||
|
||||
const_str_alloca_ptr(env, parent, ptr, len, cap)
|
||||
const_str_alloca_ptr(env, ptr, len, cap)
|
||||
}
|
||||
|
||||
fn small_str_ptr_width_4<'ctx>(env: &Env<'_, 'ctx, '_>, str_literal: &str) -> StructValue<'ctx> {
|
||||
|
@ -1653,7 +1641,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
|
|||
use roc_mono::ir::Expr::*;
|
||||
|
||||
match expr {
|
||||
Literal(literal) => build_exp_literal(env, layout_interner, parent, layout, literal),
|
||||
Literal(literal) => build_exp_literal(env, layout_interner, layout, literal),
|
||||
NullPointer => {
|
||||
let basic_type =
|
||||
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout));
|
||||
|
@ -1918,7 +1906,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
|
|||
|
||||
EmptyArray => empty_polymorphic_list(env),
|
||||
Array { elem_layout, elems } => {
|
||||
list_literal(env, layout_interner, parent, scope, *elem_layout, elems)
|
||||
list_literal(env, layout_interner, scope, *elem_layout, elems)
|
||||
}
|
||||
RuntimeErrorFunction(_) => todo!(),
|
||||
|
||||
|
@ -2204,7 +2192,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
|
|||
layout_interner,
|
||||
layout_interner.get_repr(*element_layout),
|
||||
);
|
||||
let ptr = entry_block_alloca_zerofill(env, element_type, "stack_value");
|
||||
let ptr = create_entry_block_alloca(env, element_type, "stack_value");
|
||||
|
||||
if let Some(initializer) = initializer {
|
||||
env.builder
|
||||
|
@ -2295,21 +2283,6 @@ fn build_wrapped_tag<'a, 'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn entry_block_alloca_zerofill<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
basic_type: BasicTypeEnum<'ctx>,
|
||||
name: &str,
|
||||
) -> PointerValue<'ctx> {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.unwrap()
|
||||
.get_parent()
|
||||
.unwrap();
|
||||
|
||||
create_entry_block_alloca(env, parent, basic_type, name)
|
||||
}
|
||||
|
||||
fn build_tag_field_value<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
|
@ -2395,10 +2368,7 @@ fn build_tag<'a, 'ctx>(
|
|||
|
||||
let roc_union = RocUnion::tagged_from_slices(layout_interner, env.context, tags);
|
||||
|
||||
let tag_alloca = env
|
||||
.builder
|
||||
.new_build_alloca(roc_union.struct_type(), "tag_alloca");
|
||||
|
||||
let tag_alloca = create_entry_block_alloca(env, roc_union.struct_type(), "tag_alloca");
|
||||
roc_union.write_struct_data(
|
||||
env,
|
||||
layout_interner,
|
||||
|
@ -2692,8 +2662,6 @@ pub fn get_tag_id<'a, 'ctx>(
|
|||
union_layout: &UnionLayout<'a>,
|
||||
argument: BasicValueEnum<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let tag_id_layout = union_layout.tag_id_layout();
|
||||
let tag_id_int_type = basic_type_from_layout(
|
||||
env,
|
||||
|
@ -2728,7 +2696,7 @@ pub fn get_tag_id<'a, 'ctx>(
|
|||
let else_block = ctx.append_basic_block(parent, "else");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
|
||||
let result = builder.new_build_alloca(tag_id_int_type, "result");
|
||||
let result = create_entry_block_alloca(env, tag_id_int_type, "result");
|
||||
|
||||
env.builder
|
||||
.new_build_conditional_branch(is_null, then_block, else_block);
|
||||
|
@ -2801,7 +2769,7 @@ fn lookup_at_index_ptr<'a, 'ctx>(
|
|||
|
||||
// A recursive pointer in the loaded structure is stored as a `i64*`, but the loaded layout
|
||||
// might want a more precise structure. As such, cast it to the refined type if needed.
|
||||
cast_if_necessary_for_opaque_recursive_pointers(env.builder, result, target_loaded_type)
|
||||
cast_if_necessary_for_opaque_recursive_pointers(env, result, target_loaded_type)
|
||||
}
|
||||
|
||||
fn union_field_ptr_at_index_help<'a, 'ctx>(
|
||||
|
@ -2858,8 +2826,7 @@ fn union_field_ptr_at_index<'a, 'ctx>(
|
|||
// might want a more precise structure. As such, cast it to the refined type if needed.
|
||||
let from_value: BasicValueEnum = result.into();
|
||||
let to_type: BasicTypeEnum = target_loaded_type;
|
||||
cast_if_necessary_for_opaque_recursive_pointers(env.builder, from_value, to_type)
|
||||
.into_pointer_value()
|
||||
cast_if_necessary_for_opaque_recursive_pointers(env, from_value, to_type).into_pointer_value()
|
||||
}
|
||||
|
||||
fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx>(
|
||||
|
@ -2932,7 +2899,6 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
|||
fn list_literal<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
elems: &[ListLiteralElement],
|
||||
|
@ -2989,13 +2955,7 @@ fn list_literal<'a, 'ctx>(
|
|||
for (index, element) in elems.iter().enumerate() {
|
||||
match element {
|
||||
ListLiteralElement::Literal(literal) => {
|
||||
let val = build_exp_literal(
|
||||
env,
|
||||
layout_interner,
|
||||
parent,
|
||||
element_layout,
|
||||
literal,
|
||||
);
|
||||
let val = build_exp_literal(env, layout_interner, element_layout, literal);
|
||||
global_elements.push(val.into_int_value());
|
||||
}
|
||||
ListLiteralElement::Symbol(symbol) => {
|
||||
|
@ -3086,7 +3046,7 @@ fn list_literal<'a, 'ctx>(
|
|||
for (index, element) in elems.iter().enumerate() {
|
||||
let val = match element {
|
||||
ListLiteralElement::Literal(literal) => {
|
||||
build_exp_literal(env, layout_interner, parent, element_layout, literal)
|
||||
build_exp_literal(env, layout_interner, element_layout, literal)
|
||||
}
|
||||
ListLiteralElement::Symbol(symbol) => scope.load_symbol(symbol),
|
||||
};
|
||||
|
@ -3118,7 +3078,7 @@ pub fn load_roc_value<'a, 'ctx>(
|
|||
let basic_type = basic_type_from_layout(env, layout_interner, layout);
|
||||
|
||||
if layout.is_passed_by_reference(layout_interner) {
|
||||
let alloca = entry_block_alloca_zerofill(env, basic_type, name);
|
||||
let alloca = create_entry_block_alloca(env, basic_type, name);
|
||||
|
||||
store_roc_value(env, layout_interner, layout, alloca, source.into());
|
||||
|
||||
|
@ -3136,7 +3096,7 @@ pub fn use_roc_value<'a, 'ctx>(
|
|||
name: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
if layout.is_passed_by_reference(layout_interner) {
|
||||
let alloca = entry_block_alloca_zerofill(
|
||||
let alloca = create_entry_block_alloca(
|
||||
env,
|
||||
basic_type_from_layout(env, layout_interner, layout),
|
||||
name,
|
||||
|
@ -3332,14 +3292,18 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
layout_interner.get_repr(param.layout),
|
||||
);
|
||||
|
||||
let phi_type = if layout_interner.is_passed_by_reference(param.layout) {
|
||||
basic_type.ptr_type(AddressSpace::default()).into()
|
||||
use crate::llvm::scope::JoinPointArg::*;
|
||||
let joinpoint_arg = if layout_interner.is_passed_by_reference(param.layout) {
|
||||
Alloca(create_entry_block_alloca(
|
||||
env,
|
||||
basic_type,
|
||||
"joinpoint_arg_alloca",
|
||||
))
|
||||
} else {
|
||||
basic_type
|
||||
Phi(env.builder.new_build_phi(basic_type, "joinpointarg"))
|
||||
};
|
||||
|
||||
let phi_node = env.builder.new_build_phi(phi_type, "joinpointarg");
|
||||
joinpoint_args.push(phi_node);
|
||||
joinpoint_args.push(joinpoint_arg);
|
||||
}
|
||||
|
||||
builder.position_at_end(current);
|
||||
|
@ -3391,14 +3355,57 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
Jump(join_point, arguments) => {
|
||||
let builder = env.builder;
|
||||
let context = env.context;
|
||||
let (cont_block, argument_phi_values) = scope.get_join_point(*join_point).unwrap();
|
||||
let (cont_block, joinpoint_args) = scope.get_join_point(*join_point).unwrap();
|
||||
|
||||
let current_block = builder.get_insert_block().unwrap();
|
||||
|
||||
for (phi_value, argument) in argument_phi_values.iter().zip(arguments.iter()) {
|
||||
let (value, _) = scope.load_symbol_and_layout(argument);
|
||||
let mut to_resolve = bumpalo::vec![in env.arena];
|
||||
for (joinpoint_arg, argument) in joinpoint_args.iter().zip(arguments.iter()) {
|
||||
let (value, layout) = scope.load_symbol_and_layout(argument);
|
||||
|
||||
phi_value.add_incoming(&[(&value, current_block)]);
|
||||
match joinpoint_arg {
|
||||
crate::llvm::scope::JoinPointArg::Alloca(alloca) => {
|
||||
let (size, alignment) = layout_interner.stack_size_and_alignment(layout);
|
||||
// I would like to just do a direct memcpy here, but it is not valid.
|
||||
// Imagine the case where I just swap two referenced args.
|
||||
// (a, b) -> jump (b, a)
|
||||
// This would generate `b` overwriting `a` then `a` overwriting `b`.
|
||||
// That would lead to both values being `b`
|
||||
|
||||
// Instead we copy to a tmp alloca here.
|
||||
// After we have all tmp allocas, we copy those to the final output.
|
||||
let basic_type = basic_type_from_layout(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_interner.get_repr(layout),
|
||||
);
|
||||
let tmp = create_entry_block_alloca(env, basic_type, "tmp_output_for_jmp");
|
||||
builder
|
||||
.build_memcpy(
|
||||
tmp,
|
||||
alignment,
|
||||
value.into_pointer_value(),
|
||||
alignment,
|
||||
env.ptr_int().const_int(size as _, false),
|
||||
)
|
||||
.unwrap();
|
||||
to_resolve.push((alloca, tmp, alignment, size));
|
||||
}
|
||||
crate::llvm::scope::JoinPointArg::Phi(phi) => {
|
||||
phi.add_incoming(&[(&value, current_block)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (alloca, tmp, alignment, size) in to_resolve {
|
||||
builder
|
||||
.build_memcpy(
|
||||
*alloca,
|
||||
alignment,
|
||||
tmp,
|
||||
alignment,
|
||||
env.ptr_int().const_int(size as _, false),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
builder.new_build_unconditional_branch(*cont_block);
|
||||
|
@ -3576,8 +3583,8 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
remainder,
|
||||
} => {
|
||||
if env.mode.runs_expects() {
|
||||
let location = build_string_literal(env, parent, source_location);
|
||||
let source = build_string_literal(env, parent, source);
|
||||
let location = build_string_literal(env, source_location);
|
||||
let source = build_string_literal(env, source);
|
||||
let message = scope.load_symbol(symbol);
|
||||
env.call_dbg(env, location, source, message);
|
||||
}
|
||||
|
@ -3644,7 +3651,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
}
|
||||
roc_target::PtrWidth::Bytes4 => {
|
||||
// temporary WASM implementation
|
||||
throw_internal_exception(env, parent, "An expectation failed!");
|
||||
throw_internal_exception(env, "An expectation failed!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -3712,7 +3719,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
}
|
||||
roc_target::PtrWidth::Bytes4 => {
|
||||
// temporary WASM implementation
|
||||
throw_internal_exception(env, parent, "An expectation failed!");
|
||||
throw_internal_exception(env, "An expectation failed!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -3844,7 +3851,7 @@ fn equivalent_type_constructors(t1: &BasicTypeEnum, t2: &BasicTypeEnum) -> bool
|
|||
/// This will no longer be necessary and should be removed after we employ opaque pointers from
|
||||
/// LLVM.
|
||||
pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
from_value: BasicValueEnum<'ctx>,
|
||||
to_type: BasicTypeEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
|
@ -3853,7 +3860,7 @@ pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>(
|
|||
&& equivalent_type_constructors(&from_value.get_type(), &to_type)
|
||||
{
|
||||
complex_bitcast(
|
||||
builder,
|
||||
env,
|
||||
from_value,
|
||||
to_type,
|
||||
"bitcast_for_opaque_recursive_pointer",
|
||||
|
@ -3865,39 +3872,33 @@ pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>(
|
|||
|
||||
/// Cast a value to another value of the same (or smaller?) size
|
||||
pub fn cast_basic_basic<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
from_value: BasicValueEnum<'ctx>,
|
||||
to_type: BasicTypeEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
complex_bitcast(builder, from_value, to_type, "cast_basic_basic")
|
||||
complex_bitcast(env, from_value, to_type, "cast_basic_basic")
|
||||
}
|
||||
|
||||
pub fn complex_bitcast_struct_struct<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
from_value: StructValue<'ctx>,
|
||||
to_type: StructType<'ctx>,
|
||||
name: &str,
|
||||
) -> StructValue<'ctx> {
|
||||
complex_bitcast(builder, from_value.into(), to_type.into(), name).into_struct_value()
|
||||
complex_bitcast(env, from_value.into(), to_type.into(), name).into_struct_value()
|
||||
}
|
||||
|
||||
pub fn cast_block_of_memory_to_tag<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
from_value: StructValue<'ctx>,
|
||||
to_type: BasicTypeEnum<'ctx>,
|
||||
) -> StructValue<'ctx> {
|
||||
complex_bitcast(
|
||||
builder,
|
||||
from_value.into(),
|
||||
to_type,
|
||||
"block_of_memory_to_tag",
|
||||
)
|
||||
.into_struct_value()
|
||||
complex_bitcast(env, from_value.into(), to_type, "block_of_memory_to_tag").into_struct_value()
|
||||
}
|
||||
|
||||
/// Cast a value to another value of the same (or smaller?) size
|
||||
pub fn complex_bitcast<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
from_value: BasicValueEnum<'ctx>,
|
||||
to_type: BasicTypeEnum<'ctx>,
|
||||
name: &str,
|
||||
|
@ -3908,7 +3909,8 @@ pub fn complex_bitcast<'ctx>(
|
|||
// we can't use the more straightforward bitcast in all cases
|
||||
// it seems like a bitcast only works on integers and pointers
|
||||
// and crucially does not work not on arrays
|
||||
return builder
|
||||
return env
|
||||
.builder
|
||||
.new_build_pointer_cast(
|
||||
from_value.into_pointer_value(),
|
||||
to_type.into_pointer_type(),
|
||||
|
@ -3917,7 +3919,7 @@ pub fn complex_bitcast<'ctx>(
|
|||
.into();
|
||||
}
|
||||
|
||||
complex_bitcast_from_bigger_than_to(builder, from_value, to_type, name)
|
||||
complex_bitcast_from_bigger_than_to(env, from_value, to_type, name)
|
||||
}
|
||||
|
||||
/// Check the size of the input and output types. Pretending we have more bytes at a pointer than
|
||||
|
@ -3965,14 +3967,14 @@ pub fn complex_bitcast_check_size<'ctx>(
|
|||
|
||||
let then_answer = {
|
||||
env.builder.position_at_end(then_block);
|
||||
let result = complex_bitcast_from_bigger_than_to(env.builder, from_value, to_type, name);
|
||||
let result = complex_bitcast_from_bigger_than_to(env, from_value, to_type, name);
|
||||
env.builder.new_build_unconditional_branch(cont_block);
|
||||
result
|
||||
};
|
||||
|
||||
let else_answer = {
|
||||
env.builder.position_at_end(else_block);
|
||||
let result = complex_bitcast_to_bigger_than_from(env.builder, from_value, to_type, name);
|
||||
let result = complex_bitcast_to_bigger_than_from(env, from_value, to_type, name);
|
||||
env.builder.new_build_unconditional_branch(cont_block);
|
||||
result
|
||||
};
|
||||
|
@ -3987,13 +3989,15 @@ pub fn complex_bitcast_check_size<'ctx>(
|
|||
}
|
||||
|
||||
fn complex_bitcast_from_bigger_than_to<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
from_value: BasicValueEnum<'ctx>,
|
||||
to_type: BasicTypeEnum<'ctx>,
|
||||
name: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
// store the value in memory
|
||||
let argument_pointer = builder.new_build_alloca(from_value.get_type(), "cast_alloca");
|
||||
let argument_pointer = create_entry_block_alloca(env, from_value.get_type(), "cast_alloca");
|
||||
builder.new_build_store(argument_pointer, from_value);
|
||||
|
||||
// then read it back as a different type
|
||||
|
@ -4007,15 +4011,16 @@ fn complex_bitcast_from_bigger_than_to<'ctx>(
|
|||
}
|
||||
|
||||
fn complex_bitcast_to_bigger_than_from<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
from_value: BasicValueEnum<'ctx>,
|
||||
to_type: BasicTypeEnum<'ctx>,
|
||||
name: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
// reserve space in memory with the return type. This way, if the return type is bigger
|
||||
// than the input type, we don't access invalid memory when later taking a pointer to
|
||||
// the cast value
|
||||
let storage = builder.new_build_alloca(to_type, "cast_alloca");
|
||||
let storage = create_entry_block_alloca(env, to_type, "cast_alloca");
|
||||
|
||||
// then cast the pointer to our desired type
|
||||
let from_type_pointer = builder.new_build_pointer_cast(
|
||||
|
@ -4035,7 +4040,7 @@ fn complex_bitcast_to_bigger_than_from<'ctx>(
|
|||
|
||||
/// get the tag id out of a pointer to a wrapped (i.e. stores the tag id at runtime) layout
|
||||
fn get_tag_id_wrapped<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
from_value: PointerValue<'ctx>,
|
||||
|
@ -4308,12 +4313,18 @@ fn build_switch_ir<'a, 'ctx>(
|
|||
}
|
||||
|
||||
/// Creates a new stack allocation instruction in the entry block of the function.
|
||||
pub fn create_entry_block_alloca<'ctx>(
|
||||
pub fn create_entry_block_alloca<'ctx, T: BasicType<'ctx>>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
parent: FunctionValue<'_>,
|
||||
basic_type: BasicTypeEnum<'ctx>,
|
||||
basic_type: T,
|
||||
name: &str,
|
||||
) -> PointerValue<'ctx> {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.expect("builder to be in a block")
|
||||
.get_parent()
|
||||
.expect("block to be in a function");
|
||||
|
||||
let builder = env.context.create_builder();
|
||||
let entry = parent.get_first_basic_block().unwrap();
|
||||
|
||||
|
@ -5212,7 +5223,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
|
|||
RocReturn::from_layout(layout_interner, layout)
|
||||
};
|
||||
let call_result_type = roc_call_result_type(env, return_type.as_basic_type_enum());
|
||||
let result_alloca = builder.new_build_alloca(call_result_type, "result");
|
||||
let result_alloca = create_entry_block_alloca(env, call_result_type, "result");
|
||||
|
||||
let then_block = context.append_basic_block(parent, "then_block");
|
||||
let catch_block = context.append_basic_block(parent, "catch_block");
|
||||
|
@ -6392,7 +6403,7 @@ fn call_roc_function_help<'a, 'ctx>(
|
|||
arguments.pop();
|
||||
|
||||
let result_type = basic_type_from_layout(env, layout_interner, result_layout);
|
||||
let result_alloca = env.builder.new_build_alloca(result_type, "result_value");
|
||||
let result_alloca = create_entry_block_alloca(env, result_type, "result_value");
|
||||
|
||||
arguments.push(result_alloca.into());
|
||||
|
||||
|
@ -6410,7 +6421,7 @@ fn call_roc_function_help<'a, 'ctx>(
|
|||
let mut arguments = Vec::from_iter_in(it, env.arena);
|
||||
|
||||
let result_type = basic_type_from_layout(env, layout_interner, result_layout);
|
||||
let result_alloca = entry_block_alloca_zerofill(env, result_type, "result_value");
|
||||
let result_alloca = create_entry_block_alloca(env, result_type, "result_value");
|
||||
|
||||
arguments.push(result_alloca.into());
|
||||
|
||||
|
@ -6494,9 +6505,7 @@ pub(crate) fn roc_function_call<'a, 'ctx>(
|
|||
layout_interner.get_repr(lambda_set.runtime_representation()),
|
||||
);
|
||||
|
||||
let closure_data_ptr = env
|
||||
.builder
|
||||
.new_build_alloca(closure_data_type, "closure_data_ptr");
|
||||
let closure_data_ptr = create_entry_block_alloca(env, closure_data_type, "closure_data_ptr");
|
||||
|
||||
store_roc_value(
|
||||
env,
|
||||
|
@ -6864,7 +6873,9 @@ fn build_foreign_symbol<'a, 'ctx>(
|
|||
Vec::with_capacity_in(fastcc_parameters.len() + 1, env.arena);
|
||||
|
||||
let return_pointer = match roc_return {
|
||||
RocReturn::Return => env.builder.new_build_alloca(return_type, "return_value"),
|
||||
RocReturn::Return => {
|
||||
create_entry_block_alloca(env, return_type, "return_value")
|
||||
}
|
||||
RocReturn::ByPointer => fastcc_parameters.pop().unwrap().into_pointer_value(),
|
||||
};
|
||||
|
||||
|
@ -6882,9 +6893,8 @@ fn build_foreign_symbol<'a, 'ctx>(
|
|||
// we need to pass this value by-reference; put it into an alloca
|
||||
// and bitcast the reference
|
||||
|
||||
let param_alloca = env
|
||||
.builder
|
||||
.new_build_alloca(param.get_type(), "param_alloca");
|
||||
let param_alloca =
|
||||
create_entry_block_alloca(env, param.get_type(), "param_alloca");
|
||||
env.builder.new_build_store(param_alloca, param);
|
||||
|
||||
let as_cc_type = env.builder.new_build_pointer_cast(
|
||||
|
@ -7042,14 +7052,10 @@ fn define_global_str_literal<'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn throw_internal_exception<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
message: &str,
|
||||
) {
|
||||
pub(crate) fn throw_internal_exception(env: &Env<'_, '_, '_>, message: &str) {
|
||||
let builder = env.builder;
|
||||
|
||||
let str = build_string_literal(env, parent, message);
|
||||
let str = build_string_literal(env, message);
|
||||
|
||||
env.call_panic(env, str, CrashTag::Roc);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use roc_mono::layout::{
|
|||
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
|
||||
};
|
||||
|
||||
use super::bitcode::{build_inc_wrapper, call_list_bitcode_fn, BitcodeReturns};
|
||||
use super::bitcode::{build_copy_wrapper, build_inc_wrapper, call_list_bitcode_fn, BitcodeReturns};
|
||||
use super::build::{
|
||||
create_entry_block_alloca, load_roc_value, store_roc_value, use_roc_value, BuilderExt,
|
||||
};
|
||||
|
@ -34,14 +34,8 @@ pub(crate) fn list_symbol_to_c_abi<'a, 'ctx>(
|
|||
scope: &Scope<'a, 'ctx>,
|
||||
symbol: Symbol,
|
||||
) -> PointerValue<'ctx> {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
let list_type = zig_list_type(env);
|
||||
let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca");
|
||||
let list_alloca = create_entry_block_alloca(env, list_type, "list_alloca");
|
||||
|
||||
let list = scope.load_symbol(&symbol);
|
||||
env.builder.new_build_store(list_alloca, list);
|
||||
|
@ -67,9 +61,7 @@ fn pass_element_as_opaque<'a, 'ctx>(
|
|||
) -> BasicValueEnum<'ctx> {
|
||||
let element_type =
|
||||
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout));
|
||||
let element_ptr = env
|
||||
.builder
|
||||
.new_build_alloca(element_type, "element_to_pass_as_opaque");
|
||||
let element_ptr = create_entry_block_alloca(env, element_type, "element_to_pass_as_opaque");
|
||||
store_roc_value(
|
||||
env,
|
||||
layout_interner,
|
||||
|
@ -165,7 +157,7 @@ pub(crate) fn list_get_unsafe<'a, 'ctx>(
|
|||
let elem_index = builder.new_build_int_cast(elem_index, env.ptr_int(), "u64_to_usize");
|
||||
let ptr_type = elem_type.ptr_type(AddressSpace::default());
|
||||
// Load the pointer to the array data
|
||||
let array_data_ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
|
||||
let array_data_ptr = load_list_ptr(env, wrapper_struct, ptr_type);
|
||||
|
||||
// Assume the bounds have already been checked earlier
|
||||
// (e.g. by List.get or List.first, which wrap List.#getUnsafe)
|
||||
|
@ -243,16 +235,19 @@ pub(crate) fn list_release_excess_capacity<'a, 'ctx>(
|
|||
pub(crate) fn list_append_unsafe<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
original_wrapper: StructValue<'ctx>,
|
||||
element: BasicValueEnum<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
original_wrapper,
|
||||
&[
|
||||
pass_element_as_opaque(env, layout_interner, element, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
copy_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_APPEND_UNSAFE,
|
||||
)
|
||||
|
@ -268,6 +263,7 @@ pub(crate) fn list_prepend<'a, 'ctx>(
|
|||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
original_wrapper,
|
||||
|
@ -277,6 +273,7 @@ pub(crate) fn list_prepend<'a, 'ctx>(
|
|||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
copy_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_PREPEND,
|
||||
)
|
||||
|
@ -318,6 +315,7 @@ pub(crate) fn list_swap<'a, 'ctx>(
|
|||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
|
@ -331,6 +329,7 @@ pub(crate) fn list_swap<'a, 'ctx>(
|
|||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_update_mode(env, update_mode),
|
||||
copy_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_SWAP,
|
||||
)
|
||||
|
@ -404,9 +403,8 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>(
|
|||
layout_interner,
|
||||
layout_interner.get_repr(element_layout),
|
||||
);
|
||||
let element_ptr = env
|
||||
.builder
|
||||
.new_build_alloca(element_type, "output_element_as_opaque");
|
||||
let element_ptr = create_entry_block_alloca(env, element_type, "output_element_as_opaque");
|
||||
let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
|
||||
// Assume the bounds have already been checked earlier
|
||||
// (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe)
|
||||
|
@ -419,6 +417,7 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>(
|
|||
pass_element_as_opaque(env, layout_interner, element, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
pass_as_opaque(env, element_ptr),
|
||||
copy_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_REPLACE_IN_PLACE,
|
||||
),
|
||||
|
@ -439,6 +438,7 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>(
|
|||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_as_opaque(env, element_ptr),
|
||||
copy_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_REPLACE,
|
||||
)
|
||||
|
@ -546,6 +546,7 @@ pub(crate) fn list_sort_with<'a, 'ctx>(
|
|||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
list.into_struct_value(),
|
||||
|
@ -559,6 +560,7 @@ pub(crate) fn list_sort_with<'a, 'ctx>(
|
|||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
copy_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_SORT_WITH,
|
||||
)
|
||||
|
@ -657,7 +659,7 @@ where
|
|||
let zero = env.ptr_int().const_zero();
|
||||
|
||||
// allocate a stack slot for the current index
|
||||
let index_alloca = builder.new_build_alloca(env.ptr_int(), index_name);
|
||||
let index_alloca = create_entry_block_alloca(env, env.ptr_int(), index_name);
|
||||
builder.new_build_store(index_alloca, zero);
|
||||
|
||||
let loop_bb = ctx.append_basic_block(parent, "loop");
|
||||
|
@ -698,18 +700,19 @@ pub(crate) fn empty_polymorphic_list<'ctx>(env: &Env<'_, 'ctx, '_>) -> BasicValu
|
|||
}
|
||||
|
||||
pub(crate) fn load_list_ptr<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
ptr_type: PointerType<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
// a `*mut u8` pointer
|
||||
let generic_ptr = builder
|
||||
let generic_ptr = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
// cast to the expected pointer type
|
||||
cast_basic_basic(builder, generic_ptr.into(), ptr_type.into()).into_pointer_value()
|
||||
cast_basic_basic(env, generic_ptr.into(), ptr_type.into()).into_pointer_value()
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_list<'a, 'ctx>(
|
||||
|
|
|
@ -14,7 +14,7 @@ use roc_mono::layout::{
|
|||
Builtin, InLayout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||
};
|
||||
|
||||
use super::build::{load_roc_value, BuilderExt};
|
||||
use super::build::{create_entry_block_alloca, load_roc_value, BuilderExt};
|
||||
use super::convert::{argument_type_from_layout, argument_type_from_union_layout};
|
||||
use super::lowlevel::dec_binop_with_unchecked;
|
||||
use super::struct_;
|
||||
|
@ -529,14 +529,14 @@ fn build_list_eq_help<'a, 'ctx>(
|
|||
let builder = env.builder;
|
||||
let element_type = basic_type_from_layout(env, layout_interner, element_layout);
|
||||
let ptr_type = element_type.ptr_type(AddressSpace::default());
|
||||
let ptr1 = load_list_ptr(env.builder, list1, ptr_type);
|
||||
let ptr2 = load_list_ptr(env.builder, list2, ptr_type);
|
||||
let ptr1 = load_list_ptr(env, list1, ptr_type);
|
||||
let ptr2 = load_list_ptr(env, list2, ptr_type);
|
||||
|
||||
// we know that len1 == len2
|
||||
let end = len1;
|
||||
|
||||
// allocate a stack slot for the current index
|
||||
let index_alloca = builder.new_build_alloca(env.ptr_int(), "index");
|
||||
let index_alloca = create_entry_block_alloca(env, env.ptr_int(), "index");
|
||||
builder.new_build_store(index_alloca, env.ptr_int().const_zero());
|
||||
|
||||
let loop_bb = ctx.append_basic_block(parent, "loop");
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_str_bitcode_fn;
|
||||
use crate::llvm::build::{get_tag_id, store_roc_value, tag_pointer_clear_tag_id, Env};
|
||||
use crate::llvm::build::{
|
||||
create_entry_block_alloca, get_tag_id, store_roc_value, tag_pointer_clear_tag_id, Env,
|
||||
};
|
||||
use crate::llvm::build_list::{self, incrementing_elem_loop};
|
||||
use crate::llvm::convert::{basic_type_from_layout, RocUnion};
|
||||
use inkwell::builder::Builder;
|
||||
|
@ -1030,7 +1032,7 @@ fn build_clone_builtin<'a, 'ctx>(
|
|||
);
|
||||
|
||||
// if the element has any pointers, we clone them to this offset
|
||||
let rest_offset = bd.new_build_alloca(env.ptr_int(), "rest_offset");
|
||||
let rest_offset = create_entry_block_alloca(env, env.ptr_int(), "rest_offset");
|
||||
|
||||
let element_stack_size = env
|
||||
.ptr_int()
|
||||
|
|
|
@ -225,12 +225,8 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
|
||||
let return_type = zig_num_parse_result_type(env, return_type_name);
|
||||
|
||||
let zig_return_alloca = create_entry_block_alloca(
|
||||
env,
|
||||
parent,
|
||||
return_type.into(),
|
||||
"str_to_num",
|
||||
);
|
||||
let zig_return_alloca =
|
||||
create_entry_block_alloca(env, return_type, "str_to_num");
|
||||
|
||||
let (a, b) =
|
||||
pass_list_or_string_to_zig_32bit(env, string.into_struct_value());
|
||||
|
@ -324,7 +320,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
let return_type = zig_num_parse_result_type(env, return_type_name);
|
||||
|
||||
let zig_return_alloca =
|
||||
create_entry_block_alloca(env, parent, return_type.into(), "str_to_num");
|
||||
create_entry_block_alloca(env, return_type, "str_to_num");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
|
@ -411,9 +407,8 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
}
|
||||
StrFromUtf8 => {
|
||||
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
||||
let result_ptr = env
|
||||
.builder
|
||||
.new_build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
||||
let result_ptr =
|
||||
create_entry_block_alloca(env, result_type, "alloca_utf8_validate_bytes_result");
|
||||
|
||||
use roc_target::Architecture::*;
|
||||
match env.target.architecture() {
|
||||
|
@ -675,7 +670,14 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
let original_wrapper = scope.load_symbol(&args[0]).into_struct_value();
|
||||
let (elem, elem_layout) = scope.load_symbol_and_layout(&args[1]);
|
||||
|
||||
list_append_unsafe(env, layout_interner, original_wrapper, elem, elem_layout)
|
||||
list_append_unsafe(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
original_wrapper,
|
||||
elem,
|
||||
elem_layout,
|
||||
)
|
||||
}
|
||||
ListPrepend => {
|
||||
// List.prepend : List elem, elem -> List elem
|
||||
|
@ -947,7 +949,6 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
build_int_unary_op(
|
||||
env,
|
||||
layout_interner,
|
||||
parent,
|
||||
arg.into_int_value(),
|
||||
int_width,
|
||||
int_type,
|
||||
|
@ -1396,9 +1397,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
layout_interner,
|
||||
layout_interner.get_repr(layout),
|
||||
);
|
||||
let ptr = env
|
||||
.builder
|
||||
.new_build_alloca(basic_type, "unreachable_alloca");
|
||||
let ptr = create_entry_block_alloca(env, basic_type, "unreachable_alloca");
|
||||
env.builder.new_build_store(ptr, basic_type.const_zero());
|
||||
|
||||
ptr.into()
|
||||
|
@ -1933,7 +1932,7 @@ fn throw_because_overflow(env: &Env<'_, '_, '_>, message: &str) {
|
|||
env.builder.position_at_end(entry);
|
||||
|
||||
// ends in unreachable, so no return is needed
|
||||
throw_internal_exception(env, function_value, message);
|
||||
throw_internal_exception(env, message);
|
||||
|
||||
function_value
|
||||
}
|
||||
|
@ -1973,7 +1972,7 @@ fn dec_alloca<'ctx>(env: &Env<'_, 'ctx, '_>, value: IntValue<'ctx>) -> BasicValu
|
|||
Windows => {
|
||||
let dec_type = zig_dec_type(env);
|
||||
|
||||
let alloca = env.builder.new_build_alloca(dec_type, "dec_alloca");
|
||||
let alloca = create_entry_block_alloca(env, dec_type, "dec_alloca");
|
||||
|
||||
let instruction = alloca.as_instruction_value().unwrap();
|
||||
instruction.set_alignment(16).unwrap();
|
||||
|
@ -2079,11 +2078,7 @@ fn dec_binary_op<'ctx>(
|
|||
fn_name,
|
||||
);
|
||||
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let parent = block.get_parent().expect("to be in a function");
|
||||
|
||||
let ptr =
|
||||
create_entry_block_alloca(env, parent, env.context.i128_type().into(), "to_i128");
|
||||
let ptr = create_entry_block_alloca(env, env.context.i128_type(), "to_i128");
|
||||
env.builder.build_store(ptr, lowr_highr).unwrap();
|
||||
|
||||
env.builder
|
||||
|
@ -2109,7 +2104,7 @@ fn dec_binop_with_overflow<'ctx>(
|
|||
let rhs = rhs.into_int_value();
|
||||
|
||||
let return_type = zig_with_overflow_roc_dec(env);
|
||||
let return_alloca = env.builder.new_build_alloca(return_type, "return_alloca");
|
||||
let return_alloca = create_entry_block_alloca(env, return_type, "return_alloca");
|
||||
|
||||
match env.target {
|
||||
Target::LinuxX32 | Target::LinuxX64 | Target::MacX64 => {
|
||||
|
@ -2195,7 +2190,7 @@ fn change_with_overflow_to_roc_type<'a, 'ctx>(
|
|||
layout_interner,
|
||||
layout_interner.get_repr(return_layout),
|
||||
);
|
||||
let casted = cast_basic_basic(env.builder, val.as_basic_value_enum(), return_type);
|
||||
let casted = cast_basic_basic(env, val.as_basic_value_enum(), return_type);
|
||||
|
||||
use_roc_value(
|
||||
env,
|
||||
|
@ -2341,7 +2336,6 @@ fn int_type_signed_min(int_type: IntType) -> IntValue {
|
|||
fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
arg: IntValue<'ctx>,
|
||||
arg_width: IntWidth,
|
||||
arg_int_type: IntType<'ctx>,
|
||||
|
@ -2477,12 +2471,8 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
|||
target_int_width.type_name(),
|
||||
);
|
||||
|
||||
let zig_return_alloca = create_entry_block_alloca(
|
||||
env,
|
||||
parent,
|
||||
return_type.into(),
|
||||
"num_to_int",
|
||||
);
|
||||
let zig_return_alloca =
|
||||
create_entry_block_alloca(env, return_type, "num_to_int");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
|
@ -2584,7 +2574,6 @@ fn int_neg_raise_on_overflow<'ctx>(
|
|||
|
||||
throw_internal_exception(
|
||||
env,
|
||||
parent,
|
||||
"Integer negation overflowed because its argument is the minimum value",
|
||||
);
|
||||
|
||||
|
@ -2615,7 +2604,6 @@ fn int_abs_raise_on_overflow<'ctx>(
|
|||
|
||||
throw_internal_exception(
|
||||
env,
|
||||
parent,
|
||||
"Integer absolute overflowed because its argument is the minimum value",
|
||||
);
|
||||
|
||||
|
@ -2642,7 +2630,7 @@ fn int_abs_with_overflow<'ctx>(
|
|||
let bits_to_shift = int_type.get_bit_width() as u64 - 1;
|
||||
let shift_val = int_type.const_int(bits_to_shift, false);
|
||||
let shifted = bd.new_build_right_shift(arg, shift_val, true, shifted_name);
|
||||
let alloca = bd.new_build_alloca(int_type, "#int_abs_help");
|
||||
let alloca = create_entry_block_alloca(env, int_type, "#int_abs_help");
|
||||
|
||||
// shifted = arg >>> 63
|
||||
bd.new_build_store(alloca, shifted);
|
||||
|
|
|
@ -646,7 +646,7 @@ fn call_help<'ctx>(
|
|||
value: BasicValueEnum<'ctx>,
|
||||
) -> inkwell::values::CallSiteValue<'ctx> {
|
||||
let value = cast_if_necessary_for_opaque_recursive_pointers(
|
||||
env.builder,
|
||||
env,
|
||||
value,
|
||||
function.get_params()[0].get_type(),
|
||||
);
|
||||
|
@ -1350,7 +1350,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx>(
|
|||
// therefore we must cast it to our desired type
|
||||
let union_layout = LayoutRepr::Union(union_layout);
|
||||
let union_type = basic_type_from_layout(env, layout_interner, union_layout);
|
||||
let recursive_field_ptr = cast_basic_basic(env.builder, ptr_as_i64_ptr, union_type);
|
||||
let recursive_field_ptr = cast_basic_basic(env, ptr_as_i64_ptr, union_type);
|
||||
|
||||
deferred_rec.push(recursive_field_ptr);
|
||||
} else if layout_interner.contains_refcounted(*field_layout) {
|
||||
|
@ -1823,8 +1823,7 @@ fn modify_refcount_nonrecursive_help<'a, 'ctx>(
|
|||
layout_interner,
|
||||
layout_interner.get_repr(union_layout),
|
||||
);
|
||||
let recursive_ptr_field_value =
|
||||
cast_basic_basic(env.builder, field_value, union_type);
|
||||
let recursive_ptr_field_value = cast_basic_basic(env, field_value, union_type);
|
||||
|
||||
modify_refcount_layout_help(
|
||||
env,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use inkwell::{
|
||||
basic_block::BasicBlock,
|
||||
values::{BasicValueEnum, FunctionValue, PhiValue},
|
||||
values::{BasicValue, BasicValueEnum, FunctionValue, PhiValue, PointerValue},
|
||||
};
|
||||
use roc_collections::ImMap;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
|
@ -13,12 +13,18 @@ use roc_mono::{
|
|||
pub(crate) struct Scope<'a, 'ctx> {
|
||||
symbols: ImMap<Symbol, (InLayout<'a>, BasicValueEnum<'ctx>)>,
|
||||
top_level_thunks: ImMap<Symbol, (ProcLayout<'a>, FunctionValue<'ctx>)>,
|
||||
join_points: ImMap<JoinPointId, (BasicBlock<'ctx>, Vec<PhiValue<'ctx>>)>,
|
||||
join_points: ImMap<JoinPointId, (BasicBlock<'ctx>, Vec<JoinPointArg<'ctx>>)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct JoinPointNotFound;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum JoinPointArg<'ctx> {
|
||||
Alloca(PointerValue<'ctx>),
|
||||
Phi(PhiValue<'ctx>),
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Scope<'a, 'ctx> {
|
||||
pub fn insert(&mut self, symbol: Symbol, layout: InLayout<'a>, value: BasicValueEnum<'ctx>) {
|
||||
self.symbols.insert(symbol, (layout, value));
|
||||
|
@ -62,7 +68,7 @@ impl<'a, 'ctx> Scope<'a, 'ctx> {
|
|||
&mut self,
|
||||
join_point_id: JoinPointId,
|
||||
bb: BasicBlock<'ctx>,
|
||||
phis: Vec<PhiValue<'ctx>>,
|
||||
phis: Vec<JoinPointArg<'ctx>>,
|
||||
) {
|
||||
self.join_points.insert(join_point_id, (bb, phis));
|
||||
}
|
||||
|
@ -74,7 +80,7 @@ impl<'a, 'ctx> Scope<'a, 'ctx> {
|
|||
pub fn get_join_point(
|
||||
&self,
|
||||
join_point_id: JoinPointId,
|
||||
) -> Option<&(BasicBlock<'ctx>, Vec<PhiValue<'ctx>>)> {
|
||||
) -> Option<&(BasicBlock<'ctx>, Vec<JoinPointArg<'ctx>>)> {
|
||||
self.join_points.get(&join_point_id)
|
||||
}
|
||||
|
||||
|
@ -89,9 +95,17 @@ impl<'a, 'ctx> Scope<'a, 'ctx> {
|
|||
.ok_or(JoinPointNotFound)?
|
||||
.1;
|
||||
|
||||
for (phi_value, param) in ref_join_points.iter().zip(parameters.into_iter()) {
|
||||
let value = phi_value.as_basic_value();
|
||||
self.symbols.insert(param.symbol, (param.layout, value));
|
||||
for (joinpoint_arg, param) in ref_join_points.iter().zip(parameters.into_iter()) {
|
||||
match joinpoint_arg {
|
||||
crate::llvm::scope::JoinPointArg::Alloca(alloca) => {
|
||||
self.symbols
|
||||
.insert(param.symbol, (param.layout, alloca.as_basic_value_enum()));
|
||||
}
|
||||
crate::llvm::scope::JoinPointArg::Phi(phi) => {
|
||||
self.symbols
|
||||
.insert(param.symbol, (param.layout, phi.as_basic_value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -11,7 +11,7 @@ use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner};
|
|||
use crate::llvm::build::{load_roc_value, use_roc_value};
|
||||
|
||||
use super::{
|
||||
build::{store_roc_value, BuilderExt, Env},
|
||||
build::{create_entry_block_alloca, store_roc_value, BuilderExt, Env},
|
||||
convert::basic_type_from_layout,
|
||||
scope::Scope,
|
||||
};
|
||||
|
@ -253,7 +253,7 @@ fn build_struct_alloca_helper<'a, 'ctx>(
|
|||
|
||||
// Create the struct_type
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let alloca = env.builder.new_build_alloca(struct_type, "struct_alloca");
|
||||
let alloca = create_entry_block_alloca(env, struct_type, "struct_alloca");
|
||||
|
||||
for (i, (field_expr, field_repr)) in field_expr_repr.into_iter().enumerate() {
|
||||
let dst =
|
||||
|
|
|
@ -1981,12 +1981,33 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
self.get_existing_refcount_fn_index(proc_symbol, layout, op)
|
||||
self.get_existing_helper_fn_index(proc_symbol, layout, op)
|
||||
}
|
||||
|
||||
/// Generate a copy helper procedure and return a pointer (table index) to it
|
||||
/// This allows it to be indirectly called from Zig code
|
||||
pub fn get_copy_fn_index(&mut self, layout: InLayout<'a>) -> u32 {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
let (proc_symbol, new_specializations) =
|
||||
self.helper_proc_gen
|
||||
.gen_copy_proc(ident_ids, self.layout_interner, layout);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
self.get_existing_helper_fn_index(proc_symbol, layout, HelperOp::IndirectCopy)
|
||||
}
|
||||
|
||||
/// return a pointer (table index) to a refcount helper procedure.
|
||||
/// This allows it to be indirectly called from Zig code
|
||||
pub fn get_existing_refcount_fn_index(
|
||||
pub fn get_existing_helper_fn_index(
|
||||
&mut self,
|
||||
proc_symbol: Symbol,
|
||||
layout: InLayout<'a>,
|
||||
|
|
|
@ -352,7 +352,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let dec_fn = backend.get_existing_refcount_fn_index(
|
||||
let dec_fn = backend.get_existing_helper_fn_index(
|
||||
dec_fn_sym,
|
||||
elem_in_layout,
|
||||
HelperOp::IndirectDec,
|
||||
|
@ -476,6 +476,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout);
|
||||
|
||||
// Load all the arguments for Zig
|
||||
// (List return pointer) i32
|
||||
|
@ -488,6 +489,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
// out_element: ?[*]u8, i32
|
||||
// copy: CopyFn, i32
|
||||
|
||||
let code_builder = &mut backend.code_builder;
|
||||
|
||||
|
@ -517,6 +519,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
code_builder.i32_const((ret_offset + ret_elem_offset) as i32);
|
||||
code_builder.i32_add();
|
||||
}
|
||||
code_builder.i32_const(copy_fn_ptr);
|
||||
|
||||
// There is an in-place version of this but we don't use it for dev backends. No morphic_lib analysis.
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_REPLACE);
|
||||
|
@ -689,16 +692,18 @@ impl<'a> LowLevelCall<'a> {
|
|||
let list: Symbol = self.arguments[0];
|
||||
let elem: Symbol = self.arguments[1];
|
||||
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_width = backend.layout_interner.stack_size(elem_layout);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_width = backend.layout_interner.stack_size(elem_in_layout);
|
||||
let (elem_local, elem_offset, _) =
|
||||
ensure_symbol_is_in_memory(backend, elem, elem_layout, backend.env.arena);
|
||||
ensure_symbol_is_in_memory(backend, elem, elem_in_layout, backend.env.arena);
|
||||
let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// list: RocList i32
|
||||
// element: Opaque i32
|
||||
// element_width: usize i32
|
||||
// copy: CopyFn, i32
|
||||
|
||||
// return pointer and list
|
||||
backend.storage.load_symbols_for_call(
|
||||
|
@ -715,6 +720,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(copy_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_APPEND_UNSAFE);
|
||||
}
|
||||
|
@ -734,6 +740,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
|
@ -743,6 +750,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// copy: CopyFn, i32
|
||||
|
||||
// return pointer and list
|
||||
backend.storage.load_symbols_for_call(
|
||||
|
@ -762,6 +770,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(copy_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_PREPEND);
|
||||
}
|
||||
|
@ -870,6 +879,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
|
@ -882,6 +892,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
// update_mode: UpdateMode, i32
|
||||
// copy: CopyFn, i32
|
||||
|
||||
// Load the return pointer and the list
|
||||
backend.storage.load_symbols_for_call(
|
||||
|
@ -900,6 +911,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(dec_fn_ptr);
|
||||
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
||||
backend.code_builder.i32_const(copy_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_SWAP);
|
||||
}
|
||||
|
@ -2784,6 +2796,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout);
|
||||
|
||||
let cb = &mut backend.code_builder;
|
||||
|
||||
|
@ -2798,6 +2811,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
// copy: CopyFn i32
|
||||
|
||||
backend.storage.load_symbols(cb, &[return_sym, *xs]);
|
||||
cb.i32_const(wrapper_fn_ptr);
|
||||
|
@ -2817,6 +2831,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
cb.i32_const(elem_refcounted as i32);
|
||||
cb.i32_const(inc_fn_ptr);
|
||||
cb.i32_const(dec_fn_ptr);
|
||||
cb.i32_const(copy_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_SORT_WITH);
|
||||
}
|
||||
|
@ -2869,3 +2884,8 @@ fn build_refcount_element_fn<'a>(
|
|||
let rc_fn = backend.get_refcount_fn_index(elem_layout, rc_op);
|
||||
backend.get_fn_ptr(rc_fn)
|
||||
}
|
||||
|
||||
fn build_copy_element_fn<'a>(backend: &mut WasmBackend<'a, '_>, elem_layout: InLayout<'a>) -> i32 {
|
||||
let copy_fn = backend.get_copy_fn_index(elem_layout);
|
||||
backend.get_fn_ptr(copy_fn)
|
||||
}
|
||||
|
|
|
@ -3374,7 +3374,7 @@ mod test_reporting {
|
|||
f x y = x
|
||||
"
|
||||
),
|
||||
@r#"
|
||||
@r###"
|
||||
── ARGUMENTS BEFORE EQUALS in tmp/elm_function_syntax/Test.roc ─────────────────
|
||||
|
||||
I am partway through parsing a definition, but I got stuck here:
|
||||
|
@ -3385,9 +3385,9 @@ mod test_reporting {
|
|||
4│ f x y = x
|
||||
^^^
|
||||
|
||||
Looks like you are trying to define a function. In roc, functions are
|
||||
Looks like you are trying to define a function. In Roc, functions are
|
||||
always written as a lambda, like increment = \n -> n + 1.
|
||||
"#
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -4320,16 +4320,26 @@ mod test_reporting {
|
|||
{ x, y }
|
||||
"
|
||||
),
|
||||
@r"
|
||||
── TOO MANY ARGS in /code/proj/Main.roc ────────────────────────────────────────
|
||||
@r###"
|
||||
── STATEMENT AFTER EXPRESSION in tmp/double_equals_in_def/Test.roc ─────────────
|
||||
|
||||
This value is not a function, but it was given 3 arguments:
|
||||
I just finished parsing an expression with a series of definitions,
|
||||
|
||||
and this line is indented as if it's intended to be part of that
|
||||
expression:
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ x = 3
|
||||
5│ y =
|
||||
6│ x == 5
|
||||
^
|
||||
7│ Num.add 1 2
|
||||
^
|
||||
|
||||
Are there any missing commas? Or missing parentheses?
|
||||
"
|
||||
However, I already saw the final expression in that series of
|
||||
definitions.
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -5018,9 +5028,9 @@ mod test_reporting {
|
|||
I was partway through parsing an `import`, but I got stuck here:
|
||||
|
||||
4│ import svg.Path a
|
||||
^
|
||||
^
|
||||
|
||||
I was expecting to see the `as` keyword, like:
|
||||
I was expecting to see the `as` keyword next, like:
|
||||
|
||||
import svg.Path as SvgPath
|
||||
|
||||
|
@ -5417,14 +5427,25 @@ mod test_reporting {
|
|||
2 -> 2
|
||||
"
|
||||
),
|
||||
@r"
|
||||
── NOT END OF FILE in tmp/when_outdented_branch/Test.roc ───────────────────────
|
||||
@r###"
|
||||
── UNKNOWN OPERATOR in tmp/when_outdented_branch/Test.roc ──────────────────────
|
||||
|
||||
I expected to reach the end of the file, but got stuck here:
|
||||
This looks like an operator, but it's not one I recognize!
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ when 4 is
|
||||
5│ 5 -> 2
|
||||
6│ 2 -> 2
|
||||
^
|
||||
"
|
||||
^^
|
||||
|
||||
Looks like you are trying to define a function.
|
||||
|
||||
In Roc, functions are always written as a lambda, like
|
||||
|
||||
increment = \n -> n + 1
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -5436,12 +5457,13 @@ mod test_reporting {
|
|||
_ -> 2
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── UNEXPECTED ARROW in tmp/when_over_indented_underscore/Test.roc ──────────────
|
||||
|
||||
I am parsing a `when` expression right now, but this arrow is confusing
|
||||
me:
|
||||
|
||||
4│ when 4 is
|
||||
5│ 5 -> 2
|
||||
6│ _ -> 2
|
||||
^^
|
||||
|
@ -5461,7 +5483,7 @@ mod test_reporting {
|
|||
|
||||
Notice the indentation. All patterns are aligned, and each branch is
|
||||
indented a bit more than the corresponding pattern. That is important!
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -5473,12 +5495,13 @@ mod test_reporting {
|
|||
2 -> 2
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── UNEXPECTED ARROW in tmp/when_over_indented_int/Test.roc ─────────────────────
|
||||
|
||||
I am parsing a `when` expression right now, but this arrow is confusing
|
||||
me:
|
||||
|
||||
4│ when 4 is
|
||||
5│ 5 -> Num.neg
|
||||
6│ 2 -> 2
|
||||
^^
|
||||
|
@ -5498,7 +5521,7 @@ mod test_reporting {
|
|||
|
||||
Notice the indentation. All patterns are aligned, and each branch is
|
||||
indented a bit more than the corresponding pattern. That is important!
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
// TODO I think we can do better here
|
||||
|
@ -6136,27 +6159,21 @@ All branches in an `if` must have the same type!
|
|||
main = 5 -> 3
|
||||
"
|
||||
),
|
||||
|golden| pretty_assertions::assert_eq!(
|
||||
golden,
|
||||
&format!(
|
||||
r#"── UNKNOWN OPERATOR in tmp/wild_case_arrow/Test.roc ────────────────────────────
|
||||
@r###"
|
||||
── SYNTAX PROBLEM in tmp/wild_case_arrow/Test.roc ──────────────────────────────
|
||||
|
||||
This looks like an operator, but it's not one I recognize!
|
||||
I got stuck here:
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ main = 5 -> 3
|
||||
^^
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ main = 5 -> 3
|
||||
^
|
||||
|
||||
Looks like you are trying to define a function.{}
|
||||
|
||||
In roc, functions are always written as a lambda, like{}
|
||||
|
||||
increment = \n -> n + 1"#,
|
||||
' ', ' '
|
||||
)
|
||||
)
|
||||
Whatever I am running into is confusing me a lot! Normally I can give
|
||||
fairly specific hints, but something is really tripping me up this
|
||||
time.
|
||||
"###
|
||||
);
|
||||
|
||||
#[test]
|
||||
|
@ -10972,13 +10989,13 @@ In roc, functions are always written as a lambda, like{}
|
|||
0
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
||||
|
||||
This destructure assignment doesn't introduce any new variables:
|
||||
|
||||
4│ Pair _ _ = Pair 0 1
|
||||
^^^^
|
||||
^^^^^^^^
|
||||
|
||||
If you don't need to use the value on the right-hand-side of this
|
||||
assignment, consider removing the assignment. Since Roc is purely
|
||||
|
@ -11020,7 +11037,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
assignment, consider removing the assignment. Since Roc is purely
|
||||
functional, assignments that don't introduce variables cannot affect a
|
||||
program's behavior!
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -14504,4 +14521,38 @@ In roc, functions are always written as a lambda, like{}
|
|||
make partial application explicit.
|
||||
"
|
||||
);
|
||||
|
||||
// TODO: add the following tests after built-in Tasks are added
|
||||
// https://github.com/roc-lang/roc/pull/6836
|
||||
|
||||
// test_report!(
|
||||
// suffixed_stmt_invalid_type,
|
||||
// indoc!(
|
||||
// r###"
|
||||
// app "test" provides [main] to "./platform"
|
||||
|
||||
// main : Task U64 _ -> _
|
||||
// main = \task ->
|
||||
// task!
|
||||
// 42
|
||||
// "###
|
||||
// ),
|
||||
// @r""
|
||||
// );
|
||||
|
||||
// test_report!(
|
||||
// suffixed_expr_invalid_type,
|
||||
// indoc!(
|
||||
// r###"
|
||||
// app "test" provides [main] to "./platform"
|
||||
|
||||
// main : Task U64 _ -> _
|
||||
// main = \task ->
|
||||
// result : U32
|
||||
// result = task!
|
||||
// result
|
||||
// "###
|
||||
// ),
|
||||
// @r""
|
||||
// );
|
||||
}
|
||||
|
|
|
@ -833,7 +833,7 @@ fn platform_parse_error() {
|
|||
|
||||
match multiple_modules("platform_parse_error", modules) {
|
||||
Err(report) => {
|
||||
assert!(report.contains("NOT END OF FILE"));
|
||||
assert!(report.contains("STATEMENT AFTER EXPRESSION"));
|
||||
assert!(report.contains("blah 1 2 3 # causing a parse error on purpose"));
|
||||
}
|
||||
Ok(_) => unreachable!("we expect failure here"),
|
||||
|
|
|
@ -3,7 +3,7 @@ use self::BinOp::*;
|
|||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
const PRECEDENCES: [(BinOp, u8); 20] = [
|
||||
const PRECEDENCES: [(BinOp, u8); 16] = [
|
||||
(Caret, 8),
|
||||
(Star, 7),
|
||||
(Slash, 7),
|
||||
|
@ -20,14 +20,9 @@ const PRECEDENCES: [(BinOp, u8); 20] = [
|
|||
(GreaterThanOrEq, 2),
|
||||
(And, 1),
|
||||
(Or, 0),
|
||||
// These should never come up
|
||||
(Assignment, 255),
|
||||
(IsAliasType, 255),
|
||||
(IsOpaqueType, 255),
|
||||
(Backpassing, 255),
|
||||
];
|
||||
|
||||
const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [
|
||||
const ASSOCIATIVITIES: [(BinOp, Associativity); 16] = [
|
||||
(Caret, RightAssociative),
|
||||
(Star, LeftAssociative),
|
||||
(Slash, LeftAssociative),
|
||||
|
@ -44,14 +39,9 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [
|
|||
(GreaterThanOrEq, NonAssociative),
|
||||
(And, RightAssociative),
|
||||
(Or, RightAssociative),
|
||||
// These should never come up
|
||||
(Assignment, LeftAssociative),
|
||||
(IsAliasType, LeftAssociative),
|
||||
(IsOpaqueType, LeftAssociative),
|
||||
(Backpassing, LeftAssociative),
|
||||
];
|
||||
|
||||
const DISPLAY_STRINGS: [(BinOp, &str); 20] = [
|
||||
const DISPLAY_STRINGS: [(BinOp, &str); 16] = [
|
||||
(Caret, "^"),
|
||||
(Star, "*"),
|
||||
(Slash, "/"),
|
||||
|
@ -68,10 +58,6 @@ const DISPLAY_STRINGS: [(BinOp, &str); 20] = [
|
|||
(GreaterThanOrEq, ">="),
|
||||
(And, "&&"),
|
||||
(Or, "||"),
|
||||
(Assignment, "="),
|
||||
(IsAliasType, ":"),
|
||||
(IsOpaqueType, ":="),
|
||||
(Backpassing, "<-"),
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
@ -147,10 +133,6 @@ pub enum BinOp {
|
|||
GreaterThanOrEq,
|
||||
And,
|
||||
Or,
|
||||
Assignment,
|
||||
IsAliasType,
|
||||
IsOpaqueType,
|
||||
Backpassing,
|
||||
// lowest precedence
|
||||
}
|
||||
|
||||
|
@ -161,7 +143,6 @@ impl BinOp {
|
|||
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
|
||||
DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or
|
||||
| Pizza => 2,
|
||||
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,25 +176,13 @@ pub enum Associativity {
|
|||
|
||||
impl BinOp {
|
||||
pub fn associativity(self) -> Associativity {
|
||||
// The compiler should never pass any of these to this function!
|
||||
debug_assert_ne!(self, Assignment);
|
||||
debug_assert_ne!(self, IsAliasType);
|
||||
debug_assert_ne!(self, IsOpaqueType);
|
||||
debug_assert_ne!(self, Backpassing);
|
||||
|
||||
const ASSOCIATIVITY_TABLE: [Associativity; 20] = generate_associativity_table();
|
||||
const ASSOCIATIVITY_TABLE: [Associativity; 16] = generate_associativity_table();
|
||||
|
||||
ASSOCIATIVITY_TABLE[self as usize]
|
||||
}
|
||||
|
||||
fn precedence(self) -> u8 {
|
||||
// The compiler should never pass any of these to this function!
|
||||
debug_assert_ne!(self, Assignment);
|
||||
debug_assert_ne!(self, IsAliasType);
|
||||
debug_assert_ne!(self, IsOpaqueType);
|
||||
debug_assert_ne!(self, Backpassing);
|
||||
|
||||
const PRECEDENCE_TABLE: [u8; 20] = generate_precedence_table();
|
||||
const PRECEDENCE_TABLE: [u8; 16] = generate_precedence_table();
|
||||
|
||||
PRECEDENCE_TABLE[self as usize]
|
||||
}
|
||||
|
@ -233,19 +202,14 @@ impl Ord for BinOp {
|
|||
|
||||
impl std::fmt::Display for BinOp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
debug_assert_ne!(*self, Assignment);
|
||||
debug_assert_ne!(*self, IsAliasType);
|
||||
debug_assert_ne!(*self, IsOpaqueType);
|
||||
debug_assert_ne!(*self, Backpassing);
|
||||
|
||||
const DISPLAY_TABLE: [&str; 20] = generate_display_table();
|
||||
const DISPLAY_TABLE: [&str; 16] = generate_display_table();
|
||||
|
||||
write!(f, "{}", DISPLAY_TABLE[*self as usize])
|
||||
}
|
||||
}
|
||||
|
||||
const fn generate_precedence_table() -> [u8; 20] {
|
||||
let mut table = [0u8; 20];
|
||||
const fn generate_precedence_table() -> [u8; 16] {
|
||||
let mut table = [0u8; 16];
|
||||
let mut i = 0;
|
||||
|
||||
while i < PRECEDENCES.len() {
|
||||
|
@ -256,8 +220,8 @@ const fn generate_precedence_table() -> [u8; 20] {
|
|||
table
|
||||
}
|
||||
|
||||
const fn generate_associativity_table() -> [Associativity; 20] {
|
||||
let mut table = [NonAssociative; 20];
|
||||
const fn generate_associativity_table() -> [Associativity; 16] {
|
||||
let mut table = [NonAssociative; 16];
|
||||
let mut i = 0;
|
||||
|
||||
while i < ASSOCIATIVITIES.len() {
|
||||
|
@ -268,8 +232,8 @@ const fn generate_associativity_table() -> [Associativity; 20] {
|
|||
table
|
||||
}
|
||||
|
||||
const fn generate_display_table() -> [&'static str; 20] {
|
||||
let mut table = [""; 20];
|
||||
const fn generate_display_table() -> [&'static str; 16] {
|
||||
let mut table = [""; 16];
|
||||
let mut i = 0;
|
||||
|
||||
while i < DISPLAY_STRINGS.len() {
|
||||
|
|
|
@ -1175,41 +1175,42 @@ define_builtins! {
|
|||
|
||||
16 GENERIC_EQ_REF: "#generic_eq_by_ref" // equality of arbitrary layouts, passed as an opaque pointer
|
||||
17 GENERIC_RC_REF: "#generic_rc_by_ref" // refcount of arbitrary layouts, passed as an opaque pointer
|
||||
18 GENERIC_COPY_REF: "#generic_copy_by_ref" // copy of arbitrary layouts, passed as an opaque pointer
|
||||
|
||||
18 GENERIC_EQ: "#generic_eq" // internal function that checks generic equality
|
||||
19 GENERIC_EQ: "#generic_eq" // internal function that checks generic equality
|
||||
|
||||
// a user-defined function that we need to capture in a closure
|
||||
// see e.g. Set.walk
|
||||
19 USER_FUNCTION: "#user_function"
|
||||
20 USER_FUNCTION: "#user_function"
|
||||
|
||||
// A caller (wrapper) that we pass to zig for it to be able to call Roc functions
|
||||
20 ZIG_FUNCTION_CALLER: "#zig_function_caller"
|
||||
21 ZIG_FUNCTION_CALLER: "#zig_function_caller"
|
||||
|
||||
// a caller (wrapper) for comparison
|
||||
21 GENERIC_COMPARE_REF: "#generic_compare_ref"
|
||||
22 GENERIC_COMPARE_REF: "#generic_compare_ref"
|
||||
|
||||
// used to initialize parameters in borrow.rs
|
||||
22 EMPTY_PARAM: "#empty_param"
|
||||
23 EMPTY_PARAM: "#empty_param"
|
||||
|
||||
// used by the dev backend to store the pointer to where to store large return types
|
||||
23 RET_POINTER: "#ret_pointer"
|
||||
24 RET_POINTER: "#ret_pointer"
|
||||
|
||||
// used in wasm dev backend to mark temporary values in the VM stack
|
||||
24 WASM_TMP: "#wasm_tmp"
|
||||
25 WASM_TMP: "#wasm_tmp"
|
||||
|
||||
// the _ used in mono when a specialized symbol is deleted
|
||||
25 REMOVED_SPECIALIZATION: "#removed_specialization"
|
||||
26 REMOVED_SPECIALIZATION: "#removed_specialization"
|
||||
|
||||
// used in dev backend
|
||||
26 DEV_TMP: "#dev_tmp"
|
||||
27 DEV_TMP2: "#dev_tmp2"
|
||||
28 DEV_TMP3: "#dev_tmp3"
|
||||
29 DEV_TMP4: "#dev_tmp4"
|
||||
30 DEV_TMP5: "#dev_tmp5"
|
||||
27 DEV_TMP: "#dev_tmp"
|
||||
28 DEV_TMP2: "#dev_tmp2"
|
||||
29 DEV_TMP3: "#dev_tmp3"
|
||||
30 DEV_TMP4: "#dev_tmp4"
|
||||
31 DEV_TMP5: "#dev_tmp5"
|
||||
|
||||
31 ATTR_INVALID: "#attr_invalid"
|
||||
32 ATTR_INVALID: "#attr_invalid"
|
||||
|
||||
32 CLONE: "#clone" // internal function that clones a value into a buffer
|
||||
33 CLONE: "#clone" // internal function that clones a value into a buffer
|
||||
}
|
||||
// Fake module for synthesizing and storing derived implementations
|
||||
1 DERIVED_SYNTH: "#Derived" => {
|
||||
|
|
38
crates/compiler/mono/src/code_gen_help/copy.rs
Normal file
38
crates/compiler/mono/src/code_gen_help/copy.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
use roc_module::symbol::{IdentIds, Symbol};
|
||||
|
||||
use crate::ir::{Expr, Stmt};
|
||||
use crate::layout::{InLayout, Layout, STLayoutInterner};
|
||||
|
||||
use super::{CodeGenHelp, Context};
|
||||
|
||||
const ARG_1: Symbol = Symbol::ARG_1;
|
||||
const ARG_2: Symbol = Symbol::ARG_2;
|
||||
|
||||
pub fn copy_indirect<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
_ctx: &mut Context<'a>,
|
||||
_layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let arena = root.arena;
|
||||
let unit = root.create_symbol(ident_ids, "unit");
|
||||
let loaded = root.create_symbol(ident_ids, "loaded");
|
||||
Stmt::Let(
|
||||
loaded,
|
||||
Expr::ptr_load(arena.alloc(ARG_2)),
|
||||
layout,
|
||||
arena.alloc(
|
||||
//
|
||||
Stmt::Let(
|
||||
unit,
|
||||
Expr::ptr_store(arena.alloc([ARG_1, loaded])),
|
||||
Layout::UNIT,
|
||||
arena.alloc(
|
||||
//
|
||||
Stmt::Ret(unit),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -14,6 +14,7 @@ use crate::layout::{
|
|||
STLayoutInterner, UnionLayout,
|
||||
};
|
||||
|
||||
mod copy;
|
||||
mod equality;
|
||||
mod refcount;
|
||||
|
||||
|
@ -43,6 +44,7 @@ pub enum HelperOp {
|
|||
Reset,
|
||||
ResetRef,
|
||||
Eq,
|
||||
IndirectCopy,
|
||||
}
|
||||
|
||||
impl HelperOp {
|
||||
|
@ -57,7 +59,7 @@ impl HelperOp {
|
|||
pub fn is_indirect(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::IndirectInc | Self::IndirectIncN | Self::IndirectDec
|
||||
Self::IndirectInc | Self::IndirectIncN | Self::IndirectDec | Self::IndirectCopy
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +277,25 @@ impl<'a> CodeGenHelp<'a> {
|
|||
(expr, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
/// Generate a copy procedure, *without* a Call expression.
|
||||
/// *This method should be rarely used* - only when the proc is to be called from Zig.
|
||||
pub fn gen_copy_proc(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut ctx = Context {
|
||||
new_linker_data: Vec::new_in(self.arena),
|
||||
recursive_union: None,
|
||||
op: HelperOp::IndirectCopy,
|
||||
};
|
||||
|
||||
let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout_interner, layout);
|
||||
|
||||
(proc_name, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
//
|
||||
// CALL SPECIALIZED OP
|
||||
|
@ -321,6 +342,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
IndirectIncN => (LAYOUT_UNIT, arena.alloc([ptr_arg, self.layout_isize])),
|
||||
IndirectInc => (LAYOUT_UNIT, arena.alloc([ptr_arg])),
|
||||
Eq => (LAYOUT_BOOL, self.arena.alloc([arg, arg])),
|
||||
IndirectCopy => (LAYOUT_UNIT, self.arena.alloc([ptr_arg, ptr_arg])),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -430,6 +452,10 @@ impl<'a> CodeGenHelp<'a> {
|
|||
LAYOUT_BOOL,
|
||||
equality::eq_generic(self, ident_ids, ctx, layout_interner, layout),
|
||||
),
|
||||
IndirectCopy => (
|
||||
LAYOUT_UNIT,
|
||||
copy::copy_indirect(self, ident_ids, ctx, layout_interner, layout),
|
||||
),
|
||||
};
|
||||
|
||||
let args: &'a [(InLayout<'a>, Symbol)] = {
|
||||
|
@ -452,6 +478,11 @@ impl<'a> CodeGenHelp<'a> {
|
|||
self.arena.alloc([(ptr_layout, ARG_1)])
|
||||
}
|
||||
Eq => self.arena.alloc([roc_value, (layout, ARG_2)]),
|
||||
IndirectCopy => {
|
||||
let ptr_layout =
|
||||
layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout));
|
||||
self.arena.alloc([(ptr_layout, ARG_1), (ptr_layout, ARG_2)])
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -524,6 +555,15 @@ impl<'a> CodeGenHelp<'a> {
|
|||
result: LAYOUT_BOOL,
|
||||
niche: Niche::NONE,
|
||||
},
|
||||
HelperOp::IndirectCopy => {
|
||||
let ptr_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout));
|
||||
|
||||
ProcLayout {
|
||||
arguments: self.arena.alloc([ptr_layout, ptr_layout]),
|
||||
result: LAYOUT_UNIT,
|
||||
niche: Niche::NONE,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(proc_symbol, proc_layout)
|
||||
|
|
|
@ -21,6 +21,12 @@ pub struct Spaces<'a, T> {
|
|||
pub after: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct SpacesBefore<'a, T> {
|
||||
pub before: &'a [CommentOrNewline<'a>],
|
||||
pub item: T,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Spaced<'a, T> {
|
||||
Item(T),
|
||||
|
@ -1204,6 +1210,21 @@ impl<'a> Defs<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn loc_defs<'b>(
|
||||
&'b self,
|
||||
) -> impl Iterator<Item = Result<Loc<TypeDef<'a>>, Loc<ValueDef<'a>>>> + 'b {
|
||||
self.tags
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, tag)| match tag.split() {
|
||||
Ok(type_index) => Ok(Loc::at(self.regions[i], self.type_defs[type_index.index()])),
|
||||
Err(value_index) => Err(Loc::at(
|
||||
self.regions[i],
|
||||
self.value_defs[value_index.index()],
|
||||
)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_value_defs(&self) -> impl Iterator<Item = (usize, &ValueDef<'a>)> {
|
||||
self.tags
|
||||
.iter()
|
||||
|
@ -2072,6 +2093,28 @@ pub trait Spaceable<'a> {
|
|||
fn before(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self;
|
||||
fn after(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self;
|
||||
|
||||
fn maybe_before(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self
|
||||
where
|
||||
Self: Sized + 'a,
|
||||
{
|
||||
if spaces.is_empty() {
|
||||
self
|
||||
} else {
|
||||
arena.alloc(self).before(spaces)
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_after(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self
|
||||
where
|
||||
Self: Sized + 'a,
|
||||
{
|
||||
if spaces.is_empty() {
|
||||
self
|
||||
} else {
|
||||
arena.alloc(self).after(spaces)
|
||||
}
|
||||
}
|
||||
|
||||
fn with_spaces_before(&'a self, spaces: &'a [CommentOrNewline<'a>], region: Region) -> Loc<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
|
|
|
@ -333,7 +333,7 @@ where
|
|||
let start = state.pos();
|
||||
match spaces().parse(arena, state, min_indent) {
|
||||
Ok((progress, spaces, state)) => {
|
||||
if progress == NoProgress || state.column() >= min_indent {
|
||||
if spaces.is_empty() || state.column() >= min_indent {
|
||||
Ok((progress, spaces, state))
|
||||
} else {
|
||||
Err((progress, indent_problem(start)))
|
||||
|
@ -344,6 +344,60 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn require_newline_or_eof<'a, E>(newline_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
|
||||
where
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
move |arena: &'a Bump, state: State<'a>, min_indent| {
|
||||
// TODO: we can do this more efficiently by stopping as soon as we see a '#' or a newline
|
||||
let (_, res, _) = space0_e(newline_problem).parse(arena, state.clone(), min_indent)?;
|
||||
|
||||
if !res.is_empty() || state.has_reached_end() {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, newline_problem(state.pos())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loc_space0_e<'a, E>(
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<&'a [CommentOrNewline<'a>]>, E>
|
||||
where
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
move |arena, state: State<'a>, min_indent: u32| {
|
||||
let mut newlines = Vec::new_in(arena);
|
||||
let start = state.pos();
|
||||
let mut comment_start = None;
|
||||
let mut comment_end = None;
|
||||
|
||||
let res = consume_spaces(state, |start, space, end| {
|
||||
newlines.push(space);
|
||||
if !matches!(space, CommentOrNewline::Newline) {
|
||||
if comment_start.is_none() {
|
||||
comment_start = Some(start);
|
||||
}
|
||||
comment_end = Some(end);
|
||||
}
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok((progress, state)) => {
|
||||
if newlines.is_empty() || state.column() >= min_indent {
|
||||
let start = comment_start.unwrap_or(state.pos());
|
||||
let end = comment_end.unwrap_or(state.pos());
|
||||
let region = Region::new(start, end);
|
||||
Ok((progress, Loc::at(region, newlines.into_bump_slice()), state))
|
||||
} else {
|
||||
Err((progress, indent_problem(start)))
|
||||
}
|
||||
}
|
||||
Err((progress, err)) => Err((progress, err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn begins_with_crlf(bytes: &[u8]) -> bool {
|
||||
bytes.len() >= 2 && bytes[0] == b'\r' && bytes[1] == b'\n'
|
||||
}
|
||||
|
@ -387,7 +441,7 @@ where
|
|||
F: FnMut(Position, CommentOrNewline<'a>, Position),
|
||||
{
|
||||
let mut progress = NoProgress;
|
||||
let mut found_newline = false;
|
||||
let mut found_newline = state.is_at_start_of_file();
|
||||
loop {
|
||||
let whitespace = fast_eat_whitespace(state.bytes());
|
||||
if whitespace > 0 {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,7 @@ pub mod module;
|
|||
pub mod number_literal;
|
||||
pub mod pattern;
|
||||
pub mod problems;
|
||||
pub mod remove_spaces;
|
||||
pub mod src64;
|
||||
pub mod state;
|
||||
pub mod string_literal;
|
||||
|
|
|
@ -302,6 +302,7 @@ pub enum EExpr<'a> {
|
|||
Start(Position),
|
||||
End(Position),
|
||||
BadExprEnd(Position),
|
||||
StmtAfterExpr(Position),
|
||||
Space(BadInputError, Position),
|
||||
|
||||
Dot(Position),
|
||||
|
@ -327,6 +328,8 @@ pub enum EExpr<'a> {
|
|||
QualifiedTag(Position),
|
||||
BackpassComma(Position),
|
||||
BackpassArrow(Position),
|
||||
BackpassContinue(Position),
|
||||
DbgContinue(Position),
|
||||
|
||||
When(EWhen<'a>, Position),
|
||||
If(EIf<'a>, Position),
|
||||
|
@ -355,6 +358,7 @@ pub enum EExpr<'a> {
|
|||
IndentEnd(Position),
|
||||
|
||||
UnexpectedComma(Position),
|
||||
UnexpectedTopLevelExpr(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -823,8 +827,9 @@ where
|
|||
let cur_indent = INDENT.with(|i| *i.borrow());
|
||||
|
||||
println!(
|
||||
"{:<5?}: {}{:<50}",
|
||||
"{:<5?}:{:<2} {}{:<50}",
|
||||
state.pos(),
|
||||
min_indent,
|
||||
&indent_text[..cur_indent * 2],
|
||||
self.message
|
||||
);
|
||||
|
@ -840,8 +845,9 @@ where
|
|||
};
|
||||
|
||||
println!(
|
||||
"{:<5?}: {}{:<50} {:<15} {:?}",
|
||||
"{:<5?}:{:<2} {}{:<50} {:<15} {:?}",
|
||||
state.pos(),
|
||||
min_indent,
|
||||
&indent_text[..cur_indent * 2],
|
||||
self.message,
|
||||
format!("{:?}", progress),
|
||||
|
|
1749
crates/compiler/parse/src/remove_spaces.rs
Normal file
1749
crates/compiler/parse/src/remove_spaces.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -129,6 +129,10 @@ impl<'a> State<'a> {
|
|||
pub fn len_region(&self, length: u32) -> Region {
|
||||
Region::new(self.pos(), self.pos().bump_column(length))
|
||||
}
|
||||
|
||||
pub fn is_at_start_of_file(&self) -> bool {
|
||||
self.offset == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for State<'a> {
|
||||
|
|
|
@ -22,7 +22,7 @@ pub fn parse_loc_with<'a>(
|
|||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Loc<ast::Expr<'a>>, SourceError<'a, SyntaxError<'a>>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
let state = State::new(input.as_bytes());
|
||||
|
||||
match crate::expr::test_parse_expr(0, arena, state.clone()) {
|
||||
Ok(loc_expr) => Ok(loc_expr),
|
||||
|
@ -31,7 +31,7 @@ pub fn parse_loc_with<'a>(
|
|||
}
|
||||
|
||||
pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
let state = State::new(input.as_bytes());
|
||||
|
||||
parse_module_defs(arena, state, Defs::default())
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ pub fn parse_header_with<'a>(
|
|||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<ast::Module<'a>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
let state = State::new(input.as_bytes());
|
||||
|
||||
match crate::module::parse_header(arena, state.clone()) {
|
||||
Ok((header, _)) => Ok(header),
|
||||
|
|
|
@ -4664,3 +4664,67 @@ fn multiple_uses_of_bool_true_tag_union() {
|
|||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn issue_6139_contains() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
buggy = \node, seen ->
|
||||
if List.contains seen node then
|
||||
seen
|
||||
else
|
||||
# node = "B"
|
||||
nextNode = stepNode node
|
||||
|
||||
# node = "C"
|
||||
buggy nextNode (List.append seen node)
|
||||
|
||||
stepNode = \node ->
|
||||
when node is
|
||||
"A" -> "B"
|
||||
"B" -> "C"
|
||||
"C" -> "D"
|
||||
"D" -> "A"
|
||||
_ -> crash ""
|
||||
|
||||
buggy "A" []
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[
|
||||
RocStr::from("A"),
|
||||
RocStr::from("B"),
|
||||
RocStr::from("C"),
|
||||
RocStr::from("D"),
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn issue_6139_prefixes() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
prefixes = \str, soFar ->
|
||||
if Str.isEmpty str then
|
||||
soFar
|
||||
|
||||
else
|
||||
graphemes =
|
||||
Str.toUtf8 str
|
||||
|> List.map \c -> Str.fromUtf8 [c] |> Result.withDefault ""
|
||||
remaining = List.dropFirst graphemes 1
|
||||
next = Str.joinWith remaining ""
|
||||
|
||||
prefixes next (List.append soFar str)
|
||||
|
||||
prefixes "abc" []
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[RocStr::from("abc"), RocStr::from("bc"), RocStr::from("c")]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2176,8 +2176,8 @@ fn refcount_nullable_unwrapped_needing_no_refcount_issue_5027() {
|
|||
await : Effect, (Str -> Effect) -> Effect
|
||||
await = \fx, cont ->
|
||||
after
|
||||
fx
|
||||
cont
|
||||
fx
|
||||
cont
|
||||
|
||||
succeed : {} -> Effect
|
||||
succeed = \{} -> (\{} -> "success")
|
||||
|
|
|
@ -11,6 +11,7 @@ cargo-fuzz = true
|
|||
|
||||
[dependencies]
|
||||
test_syntax = { path = "../../test_syntax" }
|
||||
roc_parse = { path = "../../parse" }
|
||||
|
||||
bumpalo = { version = "3.12.0", features = ["collections"] }
|
||||
libfuzzer-sys = "0.4"
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use bumpalo::Bump;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use roc_parse::ast::Malformed;
|
||||
use test_syntax::test_helpers::Input;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(input) = std::str::from_utf8(data) {
|
||||
let input = Input::Expr(input);
|
||||
let arena = Bump::new();
|
||||
if input.parse_in(&arena).is_ok() {
|
||||
input.check_invariants(|_| (), true);
|
||||
let ast = input.parse_in(&arena);
|
||||
if let Ok(ast) = ast {
|
||||
if !ast.is_malformed() {
|
||||
input.check_invariants(|_| (), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
35
crates/compiler/test_syntax/src/bin/minimize.rs
Normal file
35
crates/compiler/test_syntax/src/bin/minimize.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
//! Generate a minimized version of a given input, by removing parts of it.
|
||||
//! This is useful for debugging, when you have a large input that causes a failure,
|
||||
//! and you want to find the smallest input that still causes the failure.
|
||||
//!
|
||||
//! Typical usage:
|
||||
//! `cargo run --release --bin minimize -- full <file_that_triggers_parsing_bug>`
|
||||
//!
|
||||
//! This tool will churn on that for a while, and eventually print out a minimized version
|
||||
//! of the input that still triggers the bug.
|
||||
//!
|
||||
//! Note that `--release` is important, as this tool is very slow in debug mode.
|
||||
|
||||
use test_syntax::{minimize::print_minimizations, test_helpers::InputKind};
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
if args.len() != 3 {
|
||||
eprintln!("Usage: {} [expr|full|moduledefs|header] <input>", args[0]);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let kind = match args[1].as_str() {
|
||||
"expr" => InputKind::Expr,
|
||||
"full" => InputKind::Full,
|
||||
"moduledefs" => InputKind::ModuleDefs,
|
||||
"header" => InputKind::Header,
|
||||
_ => {
|
||||
eprintln!("Invalid input kind: {}", args[1]);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let text = std::fs::read_to_string(&args[2]).unwrap();
|
||||
print_minimizations(&text, kind);
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
pub mod minimize;
|
||||
pub mod test_helpers;
|
||||
|
|
211
crates/compiler/test_syntax/src/minimize.rs
Normal file
211
crates/compiler/test_syntax/src/minimize.rs
Normal file
|
@ -0,0 +1,211 @@
|
|||
//! Generate a minimized version of a given input, by removing parts of it.
|
||||
//! This is useful for debugging, when you have a large input that causes a failure,
|
||||
//! and you want to find the smallest input that still causes the failure.
|
||||
//!
|
||||
//! Most users will want to use the binary instead of this module directly.
|
||||
//! e.g. `cargo run --release --bin minimize -- full <file_that_triggers_parsing_bug>`
|
||||
|
||||
use crate::test_helpers::{Input, InputKind};
|
||||
use bumpalo::Bump;
|
||||
use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces};
|
||||
|
||||
pub fn print_minimizations(text: &str, kind: InputKind) {
|
||||
let Some(original_error) = round_trip_once_and_extract_error(text, kind) else {
|
||||
eprintln!("No error found");
|
||||
return;
|
||||
};
|
||||
|
||||
eprintln!("Error found: {}", original_error);
|
||||
eprintln!("Proceeding with minimization");
|
||||
|
||||
let mut s = text.to_string();
|
||||
|
||||
loop {
|
||||
let mut found = false;
|
||||
for update in candidate_minimizations(s.clone()) {
|
||||
let mut new_s = String::with_capacity(s.len());
|
||||
let mut offset = 0;
|
||||
for (start, end, replacement) in update.replacements.clone() {
|
||||
new_s.push_str(&s[offset..start]);
|
||||
new_s.push_str(&replacement);
|
||||
offset = end;
|
||||
}
|
||||
new_s.push_str(&s[offset..]);
|
||||
|
||||
assert!(
|
||||
new_s.len() < s.len(),
|
||||
"replacements: {:?}",
|
||||
update.replacements
|
||||
);
|
||||
|
||||
if let Some(result) = round_trip_once_and_extract_error(&new_s, kind) {
|
||||
if result == original_error {
|
||||
eprintln!("Successfully minimized, new length: {}", new_s.len());
|
||||
s = new_s;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
eprintln!("No more minimizations found");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("Final result:");
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
fn round_trip_once_and_extract_error(text: &str, kind: InputKind) -> Option<String> {
|
||||
let input = kind.with_text(text);
|
||||
let res = std::panic::catch_unwind(|| round_trip_once(input));
|
||||
|
||||
match res {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
if let Some(s) = e.downcast_ref::<&'static str>() {
|
||||
return Some(s.to_string());
|
||||
}
|
||||
if let Some(s) = e.downcast_ref::<String>() {
|
||||
return Some(s.clone());
|
||||
}
|
||||
Some("Panic during parsing".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn round_trip_once(input: Input<'_>) -> Option<String> {
|
||||
let arena = Bump::new();
|
||||
|
||||
let actual = match input.parse_in(&arena) {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
return Some(format!(
|
||||
"Initial parse failed: {:?}",
|
||||
e.remove_spaces(&arena)
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if actual.is_malformed() {
|
||||
return Some("Initial parse is malformed".to_string());
|
||||
}
|
||||
|
||||
let output = actual.format();
|
||||
|
||||
let reparsed_ast = match output.as_ref().parse_in(&arena) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Some(format!("Reparse failed: {:?}", e.remove_spaces(&arena))),
|
||||
};
|
||||
|
||||
let ast_normalized = actual.remove_spaces(&arena);
|
||||
let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena);
|
||||
|
||||
if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") {
|
||||
return Some("Different ast".to_string());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
struct Update {
|
||||
replacements: Vec<(usize, usize, String)>,
|
||||
}
|
||||
|
||||
fn candidate_minimizations(s: String) -> Box<dyn Iterator<Item = Update>> {
|
||||
let mut line_offsets = vec![0];
|
||||
line_offsets.extend(s.match_indices('\n').map(|(i, _)| i + 1));
|
||||
let line_count = line_offsets.len();
|
||||
let s_len = s.len();
|
||||
|
||||
let line_indents = line_offsets
|
||||
.iter()
|
||||
.map(|&offset| s[offset..].chars().take_while(|&c| c == ' ').count())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
|
||||
// first, try to remove every group of 1, 2, 3, ... lines - in reverse order (so, trying removing n lines first, then n-1, etc)
|
||||
let line_removals = (1..=line_count).rev().flat_map(move |n| {
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
(0..line_count - n).map(move |start| {
|
||||
let end = start + n;
|
||||
let start_offset = line_offsets_clone[start];
|
||||
let end_offset = line_offsets_clone[end];
|
||||
let replacement = String::new();
|
||||
let replacements = vec![(start_offset, end_offset, replacement)];
|
||||
Update { replacements }
|
||||
})
|
||||
});
|
||||
|
||||
let line_offsets = line_offsets_clone;
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
|
||||
// then, try to dedent every group of 1, 2, 3, ... lines - in reverse order (so, trying dedenting n lines first, then n-1, etc)
|
||||
// just remove one space at a time, for now
|
||||
let line_dedents = (1..=line_count).rev().flat_map(move |n| {
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
let line_indents_clone = line_indents.clone();
|
||||
(0..line_count - n).filter_map(move |start| {
|
||||
// first check if all lines are either zero-width or have greater than zero indent
|
||||
let end = start + n;
|
||||
for i in start..end {
|
||||
if line_indents_clone[i] == 0
|
||||
&& line_offsets_clone[i] + 1
|
||||
< line_offsets_clone.get(i + 1).cloned().unwrap_or(s_len)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let mut replacements = vec![];
|
||||
for i in start..end {
|
||||
let offset = line_offsets_clone[i];
|
||||
let indent = line_indents_clone[i];
|
||||
if indent > 0 {
|
||||
replacements.push((offset, offset + 1, String::new()));
|
||||
}
|
||||
}
|
||||
Some(Update { replacements })
|
||||
})
|
||||
});
|
||||
|
||||
// then, try to select every range of 1, 2, 3, ... lines - in normal order this time!
|
||||
// we remove the lines before and after the range
|
||||
let line_selects = (1..line_count - 1).flat_map(move |n| {
|
||||
assert!(n > 0);
|
||||
let line_offsets_clone = line_offsets_clone.clone();
|
||||
(0..line_count - n).map(move |start| {
|
||||
let end = start + n;
|
||||
let start_offset = line_offsets_clone[start];
|
||||
let end_offset = line_offsets_clone[end];
|
||||
assert!(end_offset > start_offset);
|
||||
assert!(start_offset > 0 || end_offset < s_len);
|
||||
let replacements = vec![
|
||||
(0, start_offset, String::new()),
|
||||
(end_offset, s_len, String::new()),
|
||||
];
|
||||
Update { replacements }
|
||||
})
|
||||
});
|
||||
|
||||
// then, try to remove every range of 1, 2, 3, ... characters - in reverse order (so, trying removing n characters first, then n-1, etc)
|
||||
let charseq_removals = (1..s.len()).rev().flat_map(move |n| {
|
||||
(0..s.len() - n).map(move |start| {
|
||||
let end = start + n;
|
||||
let replacement = String::new();
|
||||
let replacements = vec![(start, end, replacement)];
|
||||
Update { replacements }
|
||||
})
|
||||
});
|
||||
|
||||
Box::new(
|
||||
line_removals
|
||||
.chain(line_dedents)
|
||||
.chain(line_selects)
|
||||
.chain(charseq_removals)
|
||||
.filter(|u| !u.replacements.is_empty()),
|
||||
)
|
||||
}
|
|
@ -4,12 +4,12 @@ use roc_parse::{
|
|||
ast::{Defs, Expr, Malformed, Module},
|
||||
module::parse_module_defs,
|
||||
parser::{Parser, SyntaxError},
|
||||
remove_spaces::RemoveSpaces,
|
||||
state::State,
|
||||
test_helpers::{parse_defs_with, parse_expr_with, parse_header_with},
|
||||
};
|
||||
use roc_test_utils::assert_multiline_str_eq;
|
||||
|
||||
use roc_fmt::spaces::RemoveSpaces;
|
||||
use roc_fmt::Buf;
|
||||
|
||||
/// Source code to parse. Usually in the form of a test case.
|
||||
|
@ -28,6 +28,25 @@ pub enum Input<'a> {
|
|||
Full(&'a str),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum InputKind {
|
||||
Header,
|
||||
ModuleDefs,
|
||||
Expr,
|
||||
Full,
|
||||
}
|
||||
|
||||
impl InputKind {
|
||||
pub fn with_text(self, text: &str) -> Input {
|
||||
match self {
|
||||
InputKind::Header => Input::Header(text),
|
||||
InputKind::ModuleDefs => Input::ModuleDefs(text),
|
||||
InputKind::Expr => Input::Expr(text),
|
||||
InputKind::Full => Input::Full(text),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Owned version of `Input`
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InputOwned {
|
||||
|
@ -38,7 +57,7 @@ pub enum InputOwned {
|
|||
}
|
||||
|
||||
impl InputOwned {
|
||||
fn as_ref(&self) -> Input {
|
||||
pub fn as_ref(&self) -> Input {
|
||||
match self {
|
||||
InputOwned::Header(s) => Input::Header(s),
|
||||
InputOwned::ModuleDefs(s) => Input::ModuleDefs(s),
|
||||
|
@ -64,7 +83,7 @@ pub enum Output<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Output<'a> {
|
||||
fn format(&self) -> InputOwned {
|
||||
pub fn format(&self) -> InputOwned {
|
||||
let arena = Bump::new();
|
||||
let mut buf = Buf::new_in(&arena);
|
||||
match self {
|
||||
|
@ -172,7 +191,7 @@ impl<'a> Input<'a> {
|
|||
|
||||
let (header, defs) = header.upgrade_header_imports(arena);
|
||||
|
||||
let module_defs = parse_module_defs(arena, state, defs).unwrap();
|
||||
let module_defs = parse_module_defs(arena, state, defs)?;
|
||||
|
||||
Ok(Output::Full {
|
||||
header,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Expr(BackpassContinue(@8), @0)
|
|
@ -0,0 +1,2 @@
|
|||
u:i
|
||||
e<-x
|
|
@ -0,0 +1 @@
|
|||
Expr(BadExprEnd(@4), @0)
|
|
@ -1 +1 @@
|
|||
Expr(DefMissingFinalExpr2(Start(@11), @11), @0)
|
||||
Expr(IndentEnd(@11), @0)
|
|
@ -0,0 +1 @@
|
|||
Expr(BadExprEnd(@3), @0)
|
|
@ -1 +1 @@
|
|||
Expr(Start(@0), @0)
|
||||
Expr(IndentEnd(@12), @0)
|
|
@ -1 +1 @@
|
|||
Expr(If(Else(@16), @0), @0)
|
||||
Expr(If(Else(@17), @0), @0)
|
|
@ -1 +1 @@
|
|||
Expr(List(End(@6), @0), @0)
|
||||
Expr(List(End(@7), @0), @0)
|
|
@ -0,0 +1 @@
|
|||
Expr(InParens(Expr(BadExprEnd(@8), @5), @4), @0)
|
|
@ -0,0 +1,3 @@
|
|||
a : e
|
||||
Na := e
|
||||
e0
|
|
@ -0,0 +1 @@
|
|||
Expr(BadExprEnd(@11), @0)
|
|
@ -1 +1 @@
|
|||
Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0)
|
||||
Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0)
|
|
@ -1 +1 @@
|
|||
Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0)
|
||||
Expr(Closure(Pattern(PInParens(End(@6), @1), @1), @0), @0)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue