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

This commit is contained in:
Luke Boswell 2024-07-29 16:05:51 +10:00
commit eca453d07f
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
367 changed files with 14084 additions and 12080 deletions

View file

@ -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)]

View file

@ -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 {

View file

@ -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;
}

View file

@ -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"),
}
}

View file

@ -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,
}
}

View file

@ -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,

View file

@ -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,

View file

@ -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",
},
),
],

View file

@ -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",
},
),
[

View file

@ -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(

View file

@ -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(

View file

@ -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",
},
),
[

View file

@ -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(

View file

@ -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,
),
),
},

View file

@ -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(

View file

@ -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",
" ",

View file

@ -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,

View file

@ -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(

View file

@ -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 {

View file

@ -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(

View file

@ -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",

View file

@ -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(

View file

@ -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 {

View file

@ -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,

View file

@ -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 {

View file

@ -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",
},

View file

@ -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(

View file

@ -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",
},

View file

@ -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,

View file

@ -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(

View file

@ -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(

View file

@ -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",
},
),
],

View file

@ -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(

View file

@ -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(

View file

@ -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(

View file

@ -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,
),
),
),
],
}

View file

@ -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",

View file

@ -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(

View file

@ -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 {

View file

@ -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]

View file

@ -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));
}
}

View file

@ -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,

View file

@ -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
}
}
)*
}
}

View file

@ -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 {

View file

@ -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),
}
}
}

View file

@ -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(

View file

@ -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,

View file

@ -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);
}

View file

@ -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);

View file

@ -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>(

View file

@ -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");

View file

@ -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()

View file

@ -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);

View file

@ -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,

View file

@ -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(())

View file

@ -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 =

View file

@ -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>,

View file

@ -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)
}

View file

@ -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""
// );
}

View file

@ -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"),

View file

@ -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() {

View file

@ -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" => {

View 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),
),
),
),
)
}

View file

@ -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)

View file

@ -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,

View file

@ -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

View file

@ -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;

View file

@ -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),

File diff suppressed because it is too large Load diff

View file

@ -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> {

View file

@ -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),

View file

@ -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>
);
}

View file

@ -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")

View file

@ -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"

View file

@ -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);
}
}
}
});

View 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);
}

View file

@ -1 +1,2 @@
pub mod minimize;
pub mod test_helpers;

View 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()),
)
}

View file

@ -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,

View file

@ -0,0 +1 @@
Expr(BackpassContinue(@8), @0)

View file

@ -0,0 +1 @@
Expr(BadExprEnd(@4), @0)

View file

@ -1 +1 @@
Expr(DefMissingFinalExpr2(Start(@11), @11), @0)
Expr(IndentEnd(@11), @0)

View file

@ -0,0 +1 @@
Expr(BadExprEnd(@3), @0)

View file

@ -1 +1 @@
Expr(Start(@0), @0)
Expr(IndentEnd(@12), @0)

View file

@ -1 +1 @@
Expr(If(Else(@16), @0), @0)
Expr(If(Else(@17), @0), @0)

View file

@ -1 +1 @@
Expr(List(End(@6), @0), @0)
Expr(List(End(@7), @0), @0)

View file

@ -0,0 +1 @@
Expr(InParens(Expr(BadExprEnd(@8), @5), @4), @0)

View file

@ -0,0 +1 @@
Expr(BadExprEnd(@11), @0)

View file

@ -1 +1 @@
Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0)
Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0)

View file

@ -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