mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 00:09:33 +03:00
Merge branch 'trunk' into refactor-builtin-list-drop
This commit is contained in:
commit
c253273490
@ -132,7 +132,7 @@ pub fn expr_to_expr2<'a>(
|
||||
|
||||
Str(literal) => flatten_str_literal(env, scope, literal),
|
||||
|
||||
List { items, .. } => {
|
||||
List(items) => {
|
||||
let mut output = Output::default();
|
||||
let output_ref = &mut output;
|
||||
|
||||
@ -185,13 +185,12 @@ pub fn expr_to_expr2<'a>(
|
||||
RecordUpdate {
|
||||
fields,
|
||||
update: loc_update,
|
||||
final_comments: _,
|
||||
} => {
|
||||
let (can_update, update_out) =
|
||||
expr_to_expr2(env, scope, &loc_update.value, loc_update.region);
|
||||
|
||||
if let Expr2::Var(symbol) = &can_update {
|
||||
match canonicalize_fields(env, scope, fields) {
|
||||
match canonicalize_fields(env, scope, fields.items) {
|
||||
Ok((can_fields, mut output)) => {
|
||||
output.references.union_mut(update_out.references);
|
||||
|
||||
@ -236,14 +235,11 @@ pub fn expr_to_expr2<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
Record {
|
||||
fields,
|
||||
final_comments: _,
|
||||
} => {
|
||||
Record(fields) => {
|
||||
if fields.is_empty() {
|
||||
(Expr2::EmptyRecord, Output::default())
|
||||
} else {
|
||||
match canonicalize_fields(env, scope, fields) {
|
||||
match canonicalize_fields(env, scope, fields.items) {
|
||||
Ok((can_fields, output)) => (
|
||||
Expr2::Record {
|
||||
record_var: env.var_store.fresh(),
|
||||
|
@ -428,7 +428,7 @@ pub fn to_type2<'a>(
|
||||
Type2::Record(field_types, ext_type)
|
||||
}
|
||||
TagUnion { tags, ext, .. } => {
|
||||
let tag_types_vec = can_tags(env, scope, references, tags, region);
|
||||
let tag_types_vec = can_tags(env, scope, references, tags.items, region);
|
||||
|
||||
let tag_types = PoolVec::with_capacity(tag_types_vec.len() as u32, env.pool);
|
||||
|
||||
|
@ -247,7 +247,7 @@ pub fn build_file<'a>(
|
||||
link(
|
||||
target,
|
||||
binary_path.clone(),
|
||||
&inputs,
|
||||
&inputs,
|
||||
link_type
|
||||
)
|
||||
.map_err(|_| {
|
||||
|
@ -7,7 +7,7 @@ use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::ir::ProcLayout;
|
||||
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant};
|
||||
use roc_parse::ast::{AssignedField, Expr, StrLiteral};
|
||||
use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable};
|
||||
|
||||
@ -133,10 +133,7 @@ fn jit_to_ast_help<'a>(
|
||||
),
|
||||
Layout::Builtin(Builtin::EmptyList) => {
|
||||
Ok(run_jit_function!(lib, main_fn_name, &'static str, |_| {
|
||||
Expr::List {
|
||||
items: &[],
|
||||
final_comments: &[],
|
||||
}
|
||||
Expr::List(Collection::empty())
|
||||
}))
|
||||
}
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => Ok(run_jit_function!(
|
||||
@ -431,10 +428,7 @@ fn ptr_to_ast<'a>(
|
||||
|
||||
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
|
||||
}
|
||||
Layout::Builtin(Builtin::EmptyList) => Expr::List {
|
||||
items: &[],
|
||||
final_comments: &[],
|
||||
},
|
||||
Layout::Builtin(Builtin::EmptyList) => Expr::List(Collection::empty()),
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => {
|
||||
// Turn the (ptr, len) wrapper struct into actual ptr and len values.
|
||||
let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) };
|
||||
@ -522,10 +516,7 @@ fn list_to_ast<'a>(
|
||||
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::List {
|
||||
items: output,
|
||||
final_comments: &[],
|
||||
}
|
||||
Expr::List(Collection::with_items(output))
|
||||
}
|
||||
|
||||
fn single_tag_union_to_ast<'a>(
|
||||
@ -621,10 +612,7 @@ fn struct_to_ast<'a>(
|
||||
|
||||
let output = env.arena.alloc([loc_field]);
|
||||
|
||||
Expr::Record {
|
||||
fields: output,
|
||||
final_comments: &[],
|
||||
}
|
||||
Expr::Record(Collection::with_items(output))
|
||||
} else {
|
||||
debug_assert_eq!(sorted_fields.len(), field_layouts.len());
|
||||
|
||||
@ -658,10 +646,7 @@ fn struct_to_ast<'a>(
|
||||
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::Record {
|
||||
fields: output,
|
||||
final_comments: &[],
|
||||
}
|
||||
Expr::Record(Collection::with_items(output))
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,10 +720,7 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
Expr::Record {
|
||||
fields: arena.alloc([loc_assigned_field]),
|
||||
final_comments: arena.alloc([]),
|
||||
}
|
||||
Expr::Record(Collection::with_items(arena.alloc([loc_assigned_field])))
|
||||
}
|
||||
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
||||
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
|
||||
@ -850,10 +832,7 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a>
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
Expr::Record {
|
||||
fields: arena.alloc([loc_assigned_field]),
|
||||
final_comments: &[],
|
||||
}
|
||||
Expr::Record(Collection::with_items(arena.alloc([loc_assigned_field])))
|
||||
}
|
||||
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
||||
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
|
||||
@ -972,10 +951,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
Expr::Record {
|
||||
fields: arena.alloc([loc_assigned_field]),
|
||||
final_comments: arena.alloc([]),
|
||||
}
|
||||
Expr::Record(Collection::with_items(arena.alloc([loc_assigned_field])))
|
||||
}
|
||||
FlatType::TagUnion(tags, _) => {
|
||||
// This was a single-tag union that got unwrapped at runtime.
|
||||
|
@ -218,8 +218,8 @@ pub fn gen_and_eval<'a>(
|
||||
// Verify the module
|
||||
if let Err(errors) = env.module.verify() {
|
||||
panic!(
|
||||
"Errors defining module: {}\n\nUncomment things nearby to see more details.",
|
||||
errors
|
||||
"Errors defining module:\n{}\n\nUncomment things nearby to see more details.",
|
||||
errors.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
|
24
cli_utils/Cargo.lock
generated
24
cli_utils/Cargo.lock
generated
@ -2398,8 +2398,6 @@ name = "roc_build"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"im",
|
||||
"im-rc",
|
||||
"inkwell 0.1.0",
|
||||
"libloading 0.7.1",
|
||||
"roc_builtins",
|
||||
@ -2440,8 +2438,6 @@ name = "roc_can"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"im",
|
||||
"im-rc",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
@ -2459,8 +2455,6 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"clap 3.0.0-beta.5",
|
||||
"const_format",
|
||||
"im",
|
||||
"im-rc",
|
||||
"inkwell 0.1.0",
|
||||
"libloading 0.7.1",
|
||||
"mimalloc",
|
||||
@ -2562,8 +2556,6 @@ dependencies = [
|
||||
"fs_extra",
|
||||
"futures",
|
||||
"glyph_brush",
|
||||
"im",
|
||||
"im-rc",
|
||||
"libc",
|
||||
"log",
|
||||
"nonempty",
|
||||
@ -2599,8 +2591,6 @@ name = "roc_fmt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"im",
|
||||
"im-rc",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
@ -2612,8 +2602,6 @@ name = "roc_gen_dev"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"im",
|
||||
"im-rc",
|
||||
"object 0.26.2",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
@ -2632,20 +2620,13 @@ name = "roc_gen_llvm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"im",
|
||||
"im-rc",
|
||||
"inkwell 0.1.0",
|
||||
"morphic_lib",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
@ -2654,6 +2635,7 @@ name = "roc_gen_wasm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
@ -2726,7 +2708,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"hashbrown 0.11.2",
|
||||
"linked-hash-map",
|
||||
"morphic_lib",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
@ -2737,7 +2718,6 @@ dependencies = [
|
||||
"roc_std",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"ven_ena",
|
||||
"ven_graph",
|
||||
"ven_pretty",
|
||||
]
|
||||
@ -2773,8 +2753,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"distance",
|
||||
"im",
|
||||
"im-rc",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
|
@ -126,6 +126,7 @@ comptime {
|
||||
exportStrFn(str.repeat, "repeat");
|
||||
exportStrFn(str.strTrim, "trim");
|
||||
exportStrFn(str.strTrimLeft, "trim_left");
|
||||
exportStrFn(str.strTrimRight, "trim_right");
|
||||
|
||||
inline for (INTEGERS) |T| {
|
||||
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");
|
||||
|
@ -1584,6 +1584,41 @@ pub fn strTrimLeft(string: RocStr) callconv(.C) RocStr {
|
||||
return RocStr.empty();
|
||||
}
|
||||
|
||||
pub fn strTrimRight(string: RocStr) callconv(.C) RocStr {
|
||||
if (string.str_bytes) |bytes_ptr| {
|
||||
const trailing_bytes = countTrailingWhitespaceBytes(string);
|
||||
const original_len = string.len();
|
||||
|
||||
if (original_len == trailing_bytes) {
|
||||
string.deinit();
|
||||
return RocStr.empty();
|
||||
}
|
||||
|
||||
const new_len = original_len - trailing_bytes;
|
||||
|
||||
const small_or_shared = new_len <= SMALL_STR_MAX_LENGTH or !string.isRefcountOne();
|
||||
if (small_or_shared) {
|
||||
return RocStr.init(string.asU8ptr(), new_len);
|
||||
}
|
||||
|
||||
// nonempty, large, and unique:
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < new_len) : (i += 1) {
|
||||
const dest = bytes_ptr + i;
|
||||
const source = dest;
|
||||
@memcpy(dest, source, 1);
|
||||
}
|
||||
|
||||
var new_string = string;
|
||||
new_string.str_len = new_len;
|
||||
|
||||
return new_string;
|
||||
}
|
||||
|
||||
return RocStr.empty();
|
||||
}
|
||||
|
||||
fn countLeadingWhitespaceBytes(string: RocStr) usize {
|
||||
var byte_count: usize = 0;
|
||||
|
||||
@ -1820,6 +1855,77 @@ test "strTrimLeft: small to small" {
|
||||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimRight: empty" {
|
||||
const trimmedEmpty = strTrimRight(RocStr.empty());
|
||||
try expect(trimmedEmpty.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimRight: blank" {
|
||||
const original_bytes = " ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimRight: large to large" {
|
||||
const original_bytes = " hello giant world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
try expect(!original.isSmallStr());
|
||||
|
||||
const expected_bytes = " hello giant world";
|
||||
const expected = RocStr.init(expected_bytes, expected_bytes.len);
|
||||
defer expected.deinit();
|
||||
|
||||
try expect(!expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
}
|
||||
|
||||
test "strTrimRight: large to small" {
|
||||
const original_bytes = " hello world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
try expect(!original.isSmallStr());
|
||||
|
||||
const expected_bytes = " hello world";
|
||||
const expected = RocStr.init(expected_bytes, expected_bytes.len);
|
||||
defer expected.deinit();
|
||||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimRight: small to small" {
|
||||
const original_bytes = " hello world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
try expect(original.isSmallStr());
|
||||
|
||||
const expected_bytes = " hello world";
|
||||
const expected = RocStr.init(expected_bytes, expected_bytes.len);
|
||||
defer expected.deinit();
|
||||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "ReverseUtf8View: hello world" {
|
||||
const original_bytes = "hello world";
|
||||
const expected_bytes = "dlrow olleh";
|
||||
|
@ -149,6 +149,7 @@ pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
||||
pub const STR_REPEAT: &str = "roc_builtins.str.repeat";
|
||||
pub const STR_TRIM: &str = "roc_builtins.str.trim";
|
||||
pub const STR_TRIM_LEFT: &str = "roc_builtins.str.trim_left";
|
||||
pub const STR_TRIM_RIGHT: &str = "roc_builtins.str.trim_right";
|
||||
|
||||
pub const DICT_HASH: &str = "roc_builtins.dict.hash";
|
||||
pub const DICT_HASH_STR: &str = "roc_builtins.dict.hash_str";
|
||||
|
@ -639,6 +639,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
Box::new(str_type())
|
||||
);
|
||||
|
||||
// trimRight : Str -> Str
|
||||
add_top_level_function_type!(
|
||||
Symbol::STR_TRIM_RIGHT,
|
||||
vec![str_type()],
|
||||
Box::new(str_type())
|
||||
);
|
||||
|
||||
// trim : Str -> Str
|
||||
add_top_level_function_type!(Symbol::STR_TRIM, vec![str_type()], Box::new(str_type()));
|
||||
|
||||
|
@ -417,7 +417,7 @@ fn can_annotation_help(
|
||||
TagUnion { tags, ext, .. } => {
|
||||
let tag_types = can_tags(
|
||||
env,
|
||||
tags,
|
||||
tags.items,
|
||||
region,
|
||||
scope,
|
||||
var_store,
|
||||
|
@ -69,6 +69,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||
STR_REPEAT => str_repeat,
|
||||
STR_TRIM => str_trim,
|
||||
STR_TRIM_LEFT => str_trim_left,
|
||||
STR_TRIM_RIGHT => str_trim_right,
|
||||
LIST_LEN => list_len,
|
||||
LIST_GET => list_get,
|
||||
LIST_SET => list_set,
|
||||
@ -1295,6 +1296,11 @@ fn str_trim_left(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_1(symbol, LowLevel::StrTrimLeft, var_store)
|
||||
}
|
||||
|
||||
/// Str.trimRight : Str -> Str
|
||||
fn str_trim_right(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_1(symbol, LowLevel::StrTrimRight, var_store)
|
||||
}
|
||||
|
||||
/// Str.repeat : Str, Nat -> Str
|
||||
fn str_repeat(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let str_var = var_store.fresh();
|
||||
|
@ -228,14 +228,11 @@ pub fn canonicalize_expr<'a>(
|
||||
|
||||
(answer, Output::default())
|
||||
}
|
||||
ast::Expr::Record {
|
||||
fields,
|
||||
final_comments: _,
|
||||
} => {
|
||||
ast::Expr::Record(fields) => {
|
||||
if fields.is_empty() {
|
||||
(EmptyRecord, Output::default())
|
||||
} else {
|
||||
match canonicalize_fields(env, var_store, scope, region, fields) {
|
||||
match canonicalize_fields(env, var_store, scope, region, fields.items) {
|
||||
Ok((can_fields, output)) => (
|
||||
Record {
|
||||
record_var: var_store.fresh(),
|
||||
@ -261,12 +258,11 @@ pub fn canonicalize_expr<'a>(
|
||||
ast::Expr::RecordUpdate {
|
||||
fields,
|
||||
update: loc_update,
|
||||
final_comments: _,
|
||||
} => {
|
||||
let (can_update, update_out) =
|
||||
canonicalize_expr(env, var_store, scope, loc_update.region, &loc_update.value);
|
||||
if let Var(symbol) = &can_update.value {
|
||||
match canonicalize_fields(env, var_store, scope, region, fields) {
|
||||
match canonicalize_fields(env, var_store, scope, region, fields.items) {
|
||||
Ok((can_fields, mut output)) => {
|
||||
output.references = output.references.union(update_out.references);
|
||||
|
||||
@ -307,9 +303,7 @@ pub fn canonicalize_expr<'a>(
|
||||
}
|
||||
}
|
||||
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
|
||||
ast::Expr::List {
|
||||
items: loc_elems, ..
|
||||
} => {
|
||||
ast::Expr::List(loc_elems) => {
|
||||
if loc_elems.is_empty() {
|
||||
(
|
||||
List {
|
||||
|
@ -144,77 +144,46 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
||||
|
||||
arena.alloc(Located { region, value })
|
||||
}
|
||||
List {
|
||||
items,
|
||||
final_comments,
|
||||
} => {
|
||||
List(items) => {
|
||||
let mut new_items = Vec::with_capacity_in(items.len(), arena);
|
||||
|
||||
for item in items.iter() {
|
||||
new_items.push(desugar_expr(arena, item));
|
||||
}
|
||||
let new_items = new_items.into_bump_slice();
|
||||
let value: Expr<'a> = List {
|
||||
items: new_items,
|
||||
final_comments,
|
||||
};
|
||||
let value: Expr<'a> = List(items.replace_items(new_items));
|
||||
|
||||
arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value,
|
||||
})
|
||||
}
|
||||
Record {
|
||||
fields,
|
||||
final_comments,
|
||||
} => {
|
||||
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
||||
for field in fields.iter() {
|
||||
Record(fields) => arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value: Record(fields.map_items(arena, |field| {
|
||||
let value = desugar_field(arena, &field.value);
|
||||
|
||||
new_fields.push(Located {
|
||||
Located {
|
||||
value,
|
||||
region: field.region,
|
||||
});
|
||||
}
|
||||
}
|
||||
})),
|
||||
}),
|
||||
|
||||
let new_fields = new_fields.into_bump_slice();
|
||||
|
||||
arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value: Record {
|
||||
fields: new_fields,
|
||||
final_comments,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
RecordUpdate {
|
||||
fields,
|
||||
update,
|
||||
final_comments,
|
||||
} => {
|
||||
RecordUpdate { fields, update } => {
|
||||
// NOTE the `update` field is always a `Var { .. }` and does not need to be desugared
|
||||
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
||||
for field in fields.iter() {
|
||||
let new_fields = fields.map_items(arena, |field| {
|
||||
let value = desugar_field(arena, &field.value);
|
||||
|
||||
new_fields.push(Located {
|
||||
Located {
|
||||
value,
|
||||
region: field.region,
|
||||
});
|
||||
}
|
||||
|
||||
let new_fields = new_fields.into_bump_slice();
|
||||
}
|
||||
});
|
||||
|
||||
arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value: RecordUpdate {
|
||||
update: *update,
|
||||
fields: new_fields,
|
||||
final_comments,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ pub fn canonicalize_pattern<'a>(
|
||||
let mut destructs = Vec::with_capacity(patterns.len());
|
||||
let mut opt_erroneous = None;
|
||||
|
||||
for loc_pattern in *patterns {
|
||||
for loc_pattern in patterns.iter() {
|
||||
match loc_pattern.value {
|
||||
Identifier(label) => {
|
||||
match scope.introduce(
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::spaces::{fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
|
||||
use bumpalo::collections::String;
|
||||
use roc_parse::ast::{AssignedField, Collection, Expr, Tag, TypeAnnotation};
|
||||
use roc_parse::ast::{AssignedField, Expr, Tag, TypeAnnotation};
|
||||
use roc_region::all::Located;
|
||||
|
||||
/// Does an AST node need parens around it?
|
||||
@ -81,9 +81,9 @@ where
|
||||
}
|
||||
|
||||
macro_rules! format_sequence {
|
||||
($buf: expr, $indent:expr, $start:expr, $end:expr, $items:expr, $final_comments:expr, $newline:expr, $t:ident) => {
|
||||
let is_multiline =
|
||||
$items.iter().any(|item| item.value.is_multiline()) || !$final_comments.is_empty();
|
||||
($buf: expr, $indent:expr, $start:expr, $end:expr, $items:expr, $newline:expr, $t:ident) => {
|
||||
let is_multiline = $items.iter().any(|item| item.value.is_multiline())
|
||||
|| !$items.final_comments().is_empty();
|
||||
|
||||
if is_multiline {
|
||||
let braces_indent = $indent + INDENT;
|
||||
@ -138,7 +138,12 @@ macro_rules! format_sequence {
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt_comments_only($buf, $final_comments.iter(), NewlineAt::Top, item_indent);
|
||||
fmt_comments_only(
|
||||
$buf,
|
||||
$items.final_comments().iter(),
|
||||
NewlineAt::Top,
|
||||
item_indent,
|
||||
);
|
||||
newline($buf, braces_indent);
|
||||
$buf.push($end);
|
||||
} else {
|
||||
@ -192,11 +197,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||
fields.items.iter().any(|field| field.value.is_multiline())
|
||||
}
|
||||
|
||||
TagUnion {
|
||||
tags,
|
||||
ext,
|
||||
final_comments: _,
|
||||
} => {
|
||||
TagUnion { tags, ext } => {
|
||||
match ext {
|
||||
Some(ann) if ann.value.is_multiline() => return true,
|
||||
_ => {}
|
||||
@ -279,36 +280,16 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||
BoundVariable(v) => buf.push_str(v),
|
||||
Wildcard => buf.push('*'),
|
||||
|
||||
TagUnion {
|
||||
tags,
|
||||
ext,
|
||||
final_comments,
|
||||
} => {
|
||||
format_sequence!(buf, indent, '[', ']', tags, final_comments, newlines, Tag);
|
||||
TagUnion { tags, ext } => {
|
||||
format_sequence!(buf, indent, '[', ']', tags, newlines, Tag);
|
||||
|
||||
if let Some(loc_ext_ann) = *ext {
|
||||
loc_ext_ann.value.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
Record {
|
||||
fields:
|
||||
Collection {
|
||||
items,
|
||||
final_comments,
|
||||
},
|
||||
ext,
|
||||
} => {
|
||||
format_sequence!(
|
||||
buf,
|
||||
indent,
|
||||
'{',
|
||||
'}',
|
||||
items,
|
||||
final_comments,
|
||||
newlines,
|
||||
AssignedField
|
||||
);
|
||||
Record { fields, ext } => {
|
||||
format_sequence!(buf, indent, '{', '}', fields, newlines, AssignedField);
|
||||
|
||||
if let Some(loc_ext_ann) = *ext {
|
||||
loc_ext_ann.value.format(buf, indent);
|
||||
|
@ -5,7 +5,9 @@ use crate::spaces::{add_spaces, fmt_comments_only, fmt_spaces, newline, NewlineA
|
||||
use bumpalo::collections::String;
|
||||
use roc_module::operator::{self, BinOp};
|
||||
use roc_parse::ast::StrSegment;
|
||||
use roc_parse::ast::{AssignedField, Base, CommentOrNewline, Expr, Pattern, WhenBranch};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Base, Collection, CommentOrNewline, Expr, Pattern, WhenBranch,
|
||||
};
|
||||
use roc_region::all::Located;
|
||||
|
||||
impl<'a> Formattable<'a> for Expr<'a> {
|
||||
@ -39,7 +41,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||
// These expressions always have newlines
|
||||
Defs(_, _) | When(_, _) => true,
|
||||
|
||||
List { items, .. } => items.iter().any(|loc_expr| loc_expr.is_multiline()),
|
||||
List(items) => items.iter().any(|loc_expr| loc_expr.is_multiline()),
|
||||
|
||||
Str(literal) => {
|
||||
use roc_parse::ast::StrLiteral::*;
|
||||
@ -98,7 +100,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||
.any(|loc_pattern| loc_pattern.is_multiline())
|
||||
}
|
||||
|
||||
Record { fields, .. } => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
Record(fields) => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
RecordUpdate { fields, .. } => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
}
|
||||
}
|
||||
@ -250,18 +252,11 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||
|
||||
buf.push_str(string);
|
||||
}
|
||||
Record {
|
||||
fields,
|
||||
final_comments,
|
||||
} => {
|
||||
fmt_record(buf, None, fields, final_comments, indent);
|
||||
Record(fields) => {
|
||||
fmt_record(buf, None, *fields, indent);
|
||||
}
|
||||
RecordUpdate {
|
||||
fields,
|
||||
update,
|
||||
final_comments,
|
||||
} => {
|
||||
fmt_record(buf, Some(*update), fields, final_comments, indent);
|
||||
RecordUpdate { update, fields } => {
|
||||
fmt_record(buf, Some(*update), *fields, indent);
|
||||
}
|
||||
Closure(loc_patterns, loc_ret) => {
|
||||
fmt_closure(buf, loc_patterns, loc_ret, indent);
|
||||
@ -295,11 +290,8 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||
fmt_if(buf, branches, final_else, self.is_multiline(), indent);
|
||||
}
|
||||
When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent),
|
||||
List {
|
||||
items,
|
||||
final_comments,
|
||||
} => {
|
||||
fmt_list(buf, items, final_comments, indent);
|
||||
List(items) => {
|
||||
fmt_list(buf, *items, indent);
|
||||
}
|
||||
BinOps(lefts, right) => fmt_bin_ops(buf, lefts, right, false, parens, indent),
|
||||
UnaryOp(sub_expr, unary_op) => {
|
||||
@ -416,12 +408,9 @@ fn fmt_bin_ops<'a>(
|
||||
loc_right_side.format_with_options(buf, apply_needs_parens, Newlines::Yes, indent);
|
||||
}
|
||||
|
||||
fn fmt_list<'a>(
|
||||
buf: &mut String<'a>,
|
||||
loc_items: &[&Located<Expr<'a>>],
|
||||
final_comments: &'a [CommentOrNewline<'a>],
|
||||
indent: u16,
|
||||
) {
|
||||
fn fmt_list<'a>(buf: &mut String<'a>, items: Collection<'a, &'a Located<Expr<'a>>>, indent: u16) {
|
||||
let loc_items = items.items;
|
||||
let final_comments = items.final_comments();
|
||||
if loc_items.is_empty() && final_comments.iter().all(|c| c.is_newline()) {
|
||||
buf.push_str("[]");
|
||||
} else {
|
||||
@ -917,10 +906,11 @@ fn fmt_backpassing<'a>(
|
||||
fn fmt_record<'a>(
|
||||
buf: &mut String<'a>,
|
||||
update: Option<&'a Located<Expr<'a>>>,
|
||||
loc_fields: &[Located<AssignedField<'a, Expr<'a>>>],
|
||||
final_comments: &'a [CommentOrNewline<'a>],
|
||||
fields: Collection<'a, Located<AssignedField<'a, Expr<'a>>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
let loc_fields = fields.items;
|
||||
let final_comments = fields.final_comments();
|
||||
if loc_fields.is_empty() && final_comments.iter().all(|c| c.is_newline()) {
|
||||
buf.push_str("{}");
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::spaces::{fmt_spaces, INDENT};
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use roc_parse::ast::Module;
|
||||
use bumpalo::collections::String;
|
||||
use roc_parse::ast::{Collection, Module};
|
||||
use roc_parse::header::{AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PlatformHeader};
|
||||
use roc_region::all::Located;
|
||||
|
||||
@ -64,7 +64,7 @@ pub fn fmt_interface_header<'a>(buf: &mut String<'a>, header: &'a InterfaceHeade
|
||||
fmt_spaces(buf, header.after_imports.iter(), indent);
|
||||
}
|
||||
|
||||
fmt_imports(buf, &header.imports, indent);
|
||||
fmt_imports(buf, header.imports, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_app_header<'a>(buf: &mut String<'a>, header: &'a AppHeader<'a>) {
|
||||
@ -76,7 +76,7 @@ pub fn fmt_app_header<'a>(buf: &mut String<'a>, header: &'a AppHeader<'a>) {
|
||||
buf.push_str("imports");
|
||||
|
||||
fmt_spaces(buf, header.before_imports.iter(), indent);
|
||||
fmt_imports(buf, &header.imports, indent);
|
||||
fmt_imports(buf, header.imports, indent);
|
||||
fmt_spaces(buf, header.after_imports.iter(), indent);
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ pub fn fmt_platform_header<'a>(_buf: &mut String<'a>, _header: &'a PlatformHeade
|
||||
|
||||
fn fmt_imports<'a>(
|
||||
buf: &mut String<'a>,
|
||||
loc_entries: &'a Vec<'a, Located<ImportsEntry<'a>>>,
|
||||
loc_entries: Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.push('[');
|
||||
@ -112,7 +112,7 @@ fn fmt_imports<'a>(
|
||||
|
||||
fn fmt_exposes<'a>(
|
||||
buf: &mut String<'a>,
|
||||
loc_entries: &'a Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
loc_entries: &'a Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.push('[');
|
||||
|
@ -128,20 +128,19 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
|
||||
[tag_id, tag_value_ptr] => {
|
||||
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||
|
||||
let argument_cast = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
*tag_value_ptr,
|
||||
tag_type.ptr_type(AddressSpace::Generic),
|
||||
"load_opaque",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
let tag_value = env.builder.build_load(argument_cast, "get_value");
|
||||
let tag_value = env.builder.build_pointer_cast(
|
||||
tag_value_ptr.into_pointer_value(),
|
||||
tag_type.ptr_type(AddressSpace::Generic),
|
||||
"load_opaque_get_tag_id",
|
||||
);
|
||||
|
||||
let actual_tag_id = {
|
||||
let tag_id_i64 =
|
||||
crate::llvm::build::get_tag_id(env, function_value, &union_layout, tag_value);
|
||||
let tag_id_i64 = crate::llvm::build::get_tag_id(
|
||||
env,
|
||||
function_value,
|
||||
&union_layout,
|
||||
tag_value.into(),
|
||||
);
|
||||
|
||||
env.builder
|
||||
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
|
||||
@ -155,18 +154,11 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
|
||||
);
|
||||
|
||||
let tag_data_ptr = {
|
||||
let data_index = env
|
||||
.context
|
||||
.i64_type()
|
||||
.const_int(TAG_DATA_INDEX as u64, false);
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_struct_gep(tag_value, TAG_DATA_INDEX, "get_data_ptr")
|
||||
.unwrap();
|
||||
|
||||
let ptr = unsafe {
|
||||
env.builder.build_gep(
|
||||
tag_value_ptr.into_pointer_value(),
|
||||
&[data_index],
|
||||
"get_data_ptr",
|
||||
)
|
||||
};
|
||||
env.builder.build_bitcast(ptr, i8_ptr_type, "to_opaque")
|
||||
};
|
||||
|
||||
@ -191,6 +183,7 @@ pub fn build_transform_caller<'a, 'ctx, 'env>(
|
||||
function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'a>,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name: &str = &format!(
|
||||
"{}_zig_function_caller",
|
||||
@ -204,6 +197,7 @@ pub fn build_transform_caller<'a, 'ctx, 'env>(
|
||||
function,
|
||||
closure_data_layout,
|
||||
argument_layouts,
|
||||
result_layout,
|
||||
fn_name,
|
||||
),
|
||||
}
|
||||
@ -214,6 +208,7 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'a>,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: Layout<'a>,
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
debug_assert!(argument_layouts.len() <= 7);
|
||||
@ -260,12 +255,22 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||
for (argument_ptr, layout) in arguments.iter().zip(argument_layouts) {
|
||||
let basic_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let argument_cast = env
|
||||
.builder
|
||||
.build_bitcast(*argument_ptr, basic_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
let argument = if layout.is_passed_by_reference() {
|
||||
env.builder
|
||||
.build_pointer_cast(
|
||||
argument_ptr.into_pointer_value(),
|
||||
basic_type,
|
||||
"cast_ptr_to_tag_build_transform_caller_help",
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
let argument_cast = env
|
||||
.builder
|
||||
.build_bitcast(*argument_ptr, basic_type, "load_opaque_1")
|
||||
.into_pointer_value();
|
||||
|
||||
let argument = env.builder.build_load(argument_cast, "load_opaque");
|
||||
env.builder.build_load(argument_cast, "load_opaque_2")
|
||||
};
|
||||
|
||||
arguments_cast.push(argument);
|
||||
}
|
||||
@ -288,31 +293,19 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
let call = {
|
||||
env.builder
|
||||
.build_call(roc_function, arguments_cast.as_slice(), "tmp")
|
||||
};
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
let result = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
|
||||
let result = crate::llvm::build::call_roc_function(
|
||||
env,
|
||||
roc_function,
|
||||
&result_layout,
|
||||
arguments_cast.as_slice(),
|
||||
);
|
||||
|
||||
let result_u8_ptr = function_value
|
||||
.get_nth_param(argument_layouts.len() as u32 + 1)
|
||||
.unwrap();
|
||||
let result_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
result_u8_ptr,
|
||||
result.get_type().ptr_type(AddressSpace::Generic),
|
||||
"write_result",
|
||||
)
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder.build_store(result_ptr, result);
|
||||
crate::llvm::build::store_roc_value_opaque(env, result_layout, result_u8_ptr, result);
|
||||
env.builder.build_return(None);
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
@ -414,12 +407,18 @@ fn build_rc_wrapper<'a, 'ctx, 'env>(
|
||||
|
||||
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let value_cast = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr, value_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
let value = if layout.is_passed_by_reference() {
|
||||
env.builder
|
||||
.build_pointer_cast(value_ptr, value_type, "cast_ptr_to_tag_build_rc_wrapper")
|
||||
.into()
|
||||
} else {
|
||||
let value_cast = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr, value_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let value = env.builder.build_load(value_cast, "load_opaque");
|
||||
env.builder.build_load(value_cast, "load_opaque")
|
||||
};
|
||||
|
||||
match rc_operation {
|
||||
Mode::Inc => {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,9 +2,9 @@ use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build::tag_pointer_clear_tag_id;
|
||||
use crate::llvm::build::Env;
|
||||
use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
|
||||
use crate::llvm::build::{get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
|
||||
use crate::llvm::build_str;
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::values::{
|
||||
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
|
||||
@ -339,7 +339,8 @@ fn build_hash_tag<'a, 'ctx, 'env>(
|
||||
None => {
|
||||
let seed_type = env.context.i64_type();
|
||||
|
||||
let arg_type = basic_type_from_layout(env, layout);
|
||||
let arg_type = basic_type_from_layout_1(env, layout);
|
||||
dbg!(layout, arg_type);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
@ -423,14 +424,6 @@ fn hash_tag<'a, 'ctx, 'env>(
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let struct_layout = Layout::Struct(field_layouts);
|
||||
|
||||
let wrapper_type = basic_type_from_layout(env, &struct_layout);
|
||||
debug_assert!(wrapper_type.is_struct_type());
|
||||
|
||||
let as_struct =
|
||||
cast_block_of_memory_to_tag(env.builder, tag.into_struct_value(), wrapper_type);
|
||||
|
||||
// hash the tag id
|
||||
let hash_bytes = store_and_use_as_u8_ptr(
|
||||
env,
|
||||
@ -440,7 +433,6 @@ fn hash_tag<'a, 'ctx, 'env>(
|
||||
.into(),
|
||||
&tag_id_layout,
|
||||
);
|
||||
|
||||
let seed = hash_bitcode_fn(
|
||||
env,
|
||||
seed,
|
||||
@ -449,14 +441,9 @@ fn hash_tag<'a, 'ctx, 'env>(
|
||||
);
|
||||
|
||||
// hash the tag data
|
||||
let answer = build_hash_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
field_layouts,
|
||||
WhenRecursive::Unreachable,
|
||||
seed,
|
||||
as_struct,
|
||||
);
|
||||
let tag = tag.into_pointer_value();
|
||||
let answer =
|
||||
hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
@ -793,7 +780,15 @@ fn hash_list<'a, 'ctx, 'env>(
|
||||
env.builder.build_store(result, answer);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(env, parent, ptr, length, "current_index", loop_fn);
|
||||
incrementing_elem_loop(
|
||||
env,
|
||||
parent,
|
||||
*element_layout,
|
||||
ptr,
|
||||
length,
|
||||
"current_index",
|
||||
loop_fn,
|
||||
);
|
||||
|
||||
env.builder.build_unconditional_branch(done_block);
|
||||
|
||||
@ -822,12 +817,12 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
|
||||
) -> IntValue<'ctx> {
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let wrapper_type = basic_type_from_layout(env, &Layout::Union(*union_layout));
|
||||
let wrapper_type = basic_type_from_layout_1(env, &Layout::Union(*union_layout));
|
||||
|
||||
// cast the opaque pointer to a pointer of the correct shape
|
||||
let wrapper_ptr = env
|
||||
.builder
|
||||
.build_bitcast(tag, wrapper_type, "opaque_to_correct")
|
||||
.build_bitcast(tag, wrapper_type, "hash_ptr_to_struct_opaque_to_correct")
|
||||
.into_pointer_value();
|
||||
|
||||
let struct_ptr = env
|
||||
|
@ -17,6 +17,8 @@ use morphic_lib::UpdateMode;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||
|
||||
use super::build::{load_roc_value, store_roc_value};
|
||||
|
||||
pub fn pass_update_mode<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
update_mode: UpdateMode,
|
||||
@ -53,9 +55,13 @@ pub fn call_bitcode_fn_returns_list<'a, 'ctx, 'env>(
|
||||
fn pass_element_as_opaque<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
element: BasicValueEnum<'ctx>,
|
||||
layout: Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let element_ptr = env.builder.build_alloca(element.get_type(), "element");
|
||||
env.builder.build_store(element_ptr, element);
|
||||
let element_type = basic_type_from_layout(env, &layout);
|
||||
let element_ptr = env
|
||||
.builder
|
||||
.build_alloca(element_type, "element_to_pass_as_opaque");
|
||||
store_roc_value(env, layout, element_ptr, element);
|
||||
|
||||
env.builder.build_bitcast(
|
||||
element_ptr,
|
||||
@ -106,7 +112,7 @@ pub fn list_single<'a, 'ctx, 'env>(
|
||||
env,
|
||||
&[
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
],
|
||||
bitcode::LIST_SINGLE,
|
||||
@ -128,7 +134,7 @@ pub fn list_repeat<'a, 'ctx, 'env>(
|
||||
&[
|
||||
list_len.into(),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
@ -216,10 +222,11 @@ pub fn list_get_unsafe<'a, 'ctx, 'env>(
|
||||
|
||||
// Assume the bounds have already been checked earlier
|
||||
// (e.g. by List.get or List.first, which wrap List.#getUnsafe)
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(array_data_ptr, &[elem_index], "elem") };
|
||||
let elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(array_data_ptr, &[elem_index], "list_get_element")
|
||||
};
|
||||
|
||||
let result = builder.build_load(elem_ptr, "List.get");
|
||||
let result = load_roc_value(env, **elem_layout, elem_ptr, "list_get_load_element");
|
||||
|
||||
increment_refcount_layout(env, parent, layout_ids, 1, result, elem_layout);
|
||||
|
||||
@ -247,7 +254,7 @@ pub fn list_append<'a, 'ctx, 'env>(
|
||||
&[
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
pass_update_mode(env, update_mode),
|
||||
],
|
||||
@ -267,7 +274,7 @@ pub fn list_prepend<'a, 'ctx, 'env>(
|
||||
&[
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
],
|
||||
bitcode::LIST_PREPEND,
|
||||
@ -367,7 +374,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
||||
&[
|
||||
bytes.into(),
|
||||
index.into(),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
@ -380,7 +387,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
||||
length.into(),
|
||||
env.alignment_intvalue(element_layout),
|
||||
index.into(),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
@ -556,7 +563,7 @@ pub fn list_contains<'a, 'ctx, 'env>(
|
||||
env,
|
||||
&[
|
||||
pass_list_cc(env, list),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
eq_fn,
|
||||
],
|
||||
@ -1115,6 +1122,7 @@ where
|
||||
pub fn incrementing_elem_loop<'a, 'ctx, 'env, LoopFn>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
element_layout: Layout<'a>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
len: IntValue<'ctx>,
|
||||
index_name: &str,
|
||||
@ -1127,9 +1135,14 @@ where
|
||||
|
||||
incrementing_index_loop(env, parent, len, index_name, |index| {
|
||||
// The pointer to the element in the list
|
||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index], "load_index") };
|
||||
let element_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index], "load_index") };
|
||||
|
||||
let elem = builder.build_load(elem_ptr, "get_elem");
|
||||
let elem = load_roc_value(
|
||||
env,
|
||||
element_layout,
|
||||
element_ptr,
|
||||
"incrementing_element_loop_load",
|
||||
);
|
||||
|
||||
loop_fn(index, elem);
|
||||
})
|
||||
|
@ -269,6 +269,16 @@ pub fn str_trim_left<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn(env, &[str_i128.into()], bitcode::STR_TRIM_LEFT)
|
||||
}
|
||||
|
||||
/// Str.trimRight : Str -> Str
|
||||
pub fn str_trim_right<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
str_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
call_bitcode_fn(env, &[str_i128.into()], bitcode::STR_TRIM_RIGHT)
|
||||
}
|
||||
|
||||
/// Str.fromInt : Int -> Str
|
||||
pub fn str_from_int<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -1,10 +1,8 @@
|
||||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build::{
|
||||
cast_block_of_memory_to_tag, get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV,
|
||||
};
|
||||
use crate::llvm::build::{get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV};
|
||||
use crate::llvm::build_list::{list_len, load_list_ptr};
|
||||
use crate::llvm::build_str::str_equal;
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::{
|
||||
@ -751,7 +749,7 @@ fn build_tag_eq<'a, 'ctx, 'env>(
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let arg_type = basic_type_from_layout(env, tag_layout);
|
||||
let arg_type = basic_type_from_layout_1(env, tag_layout);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
@ -844,9 +842,29 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
||||
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
let ptr_equal = env.builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
env.builder
|
||||
.build_ptr_to_int(tag1.into_pointer_value(), env.ptr_int(), "pti"),
|
||||
env.builder
|
||||
.build_ptr_to_int(tag2.into_pointer_value(), env.ptr_int(), "pti"),
|
||||
"compare_pointers",
|
||||
);
|
||||
|
||||
let compare_tag_ids = ctx.append_basic_block(parent, "compare_tag_ids");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(ptr_equal, return_true, compare_tag_ids);
|
||||
|
||||
env.builder.position_at_end(compare_tag_ids);
|
||||
|
||||
let id1 = get_tag_id(env, parent, union_layout, tag1);
|
||||
let id2 = get_tag_id(env, parent, union_layout, tag2);
|
||||
|
||||
// clear the tag_id so we get a pointer to the actual data
|
||||
let tag1 = tag1.into_pointer_value();
|
||||
let tag2 = tag2.into_pointer_value();
|
||||
|
||||
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
|
||||
|
||||
let same_tag =
|
||||
@ -866,30 +884,14 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
// TODO drop tag id?
|
||||
let struct_layout = Layout::Struct(field_layouts);
|
||||
|
||||
let wrapper_type = basic_type_from_layout(env, &struct_layout);
|
||||
debug_assert!(wrapper_type.is_struct_type());
|
||||
|
||||
let struct1 = cast_block_of_memory_to_tag(
|
||||
env.builder,
|
||||
tag1.into_struct_value(),
|
||||
wrapper_type,
|
||||
);
|
||||
let struct2 = cast_block_of_memory_to_tag(
|
||||
env.builder,
|
||||
tag2.into_struct_value(),
|
||||
wrapper_type,
|
||||
);
|
||||
|
||||
let answer = build_struct_eq(
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
Some(when_recursive.clone()),
|
||||
field_layouts,
|
||||
when_recursive.clone(),
|
||||
struct1,
|
||||
struct2,
|
||||
tag1,
|
||||
tag2,
|
||||
);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
@ -946,8 +948,15 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let answer =
|
||||
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2);
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
field_layouts,
|
||||
tag1,
|
||||
tag2,
|
||||
);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
@ -1003,6 +1012,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
other_fields,
|
||||
tag1.into_pointer_value(),
|
||||
tag2.into_pointer_value(),
|
||||
@ -1093,8 +1103,15 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let answer =
|
||||
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2);
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
field_layouts,
|
||||
tag1,
|
||||
tag2,
|
||||
);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
@ -1128,6 +1145,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
field_layouts,
|
||||
tag1.into_pointer_value(),
|
||||
tag2.into_pointer_value(),
|
||||
@ -1142,6 +1160,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
opt_when_recursive: Option<WhenRecursive<'a>>,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
tag1: PointerValue<'ctx>,
|
||||
tag2: PointerValue<'ctx>,
|
||||
@ -1184,7 +1203,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
|
||||
env,
|
||||
layout_ids,
|
||||
field_layouts,
|
||||
WhenRecursive::Loop(*union_layout),
|
||||
opt_when_recursive.unwrap_or(WhenRecursive::Loop(*union_layout)),
|
||||
struct1,
|
||||
struct2,
|
||||
)
|
||||
|
@ -76,6 +76,66 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_type_from_layout_1<'a, 'ctx, 'env>(
|
||||
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'_>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
use Layout::*;
|
||||
|
||||
match layout {
|
||||
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
|
||||
LambdaSet(lambda_set) => {
|
||||
basic_type_from_layout_1(env, &lambda_set.runtime_representation())
|
||||
}
|
||||
Union(union_layout) => {
|
||||
use UnionLayout::*;
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
|
||||
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||
let struct_type = env.context.struct_type(&[data, tag_id_type], false);
|
||||
|
||||
struct_type.ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
Recursive(tags)
|
||||
| NullableWrapped {
|
||||
other_tags: tags, ..
|
||||
} => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||
|
||||
if union_layout.stores_tag_id_as_data(env.ptr_bytes) {
|
||||
env.context
|
||||
.struct_type(&[data, tag_id_type], false)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
} else {
|
||||
data.ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
}
|
||||
NullableUnwrapped { other_fields, .. } => {
|
||||
let block = block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes);
|
||||
block.ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
NonNullableUnwrapped(fields) => {
|
||||
let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes);
|
||||
block.ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
RecursivePointer => {
|
||||
// TODO make this dynamic
|
||||
env.context
|
||||
.i64_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum()
|
||||
}
|
||||
|
||||
Builtin(builtin) => basic_type_from_builtin(env, builtin),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
|
||||
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||
builtin: &Builtin<'_>,
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||
use crate::llvm::build::{
|
||||
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
|
||||
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, TAG_DATA_INDEX,
|
||||
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, use_roc_value, Env,
|
||||
FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
|
||||
};
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||
use crate::llvm::convert::{basic_type_from_layout, ptr_int};
|
||||
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1, ptr_int};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::basic_block::BasicBlock;
|
||||
use inkwell::context::Context;
|
||||
@ -139,8 +139,10 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let parent = block.get_parent().unwrap();
|
||||
|
||||
let modify_block = env.context.append_basic_block(parent, "inc_str_modify");
|
||||
let cont_block = env.context.append_basic_block(parent, "inc_str_cont");
|
||||
let modify_block = env
|
||||
.context
|
||||
.append_basic_block(parent, "inc_refcount_modify");
|
||||
let cont_block = env.context.append_basic_block(parent, "inc_refcount_cont");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_static_allocation, cont_block, modify_block);
|
||||
@ -349,18 +351,25 @@ fn modify_refcount_struct_help<'a, 'ctx, 'env>(
|
||||
|
||||
for (i, field_layout) in layouts.iter().enumerate() {
|
||||
if field_layout.contains_refcounted() {
|
||||
let field_ptr = env
|
||||
let raw_value = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||
.unwrap();
|
||||
|
||||
let field_value = use_roc_value(
|
||||
env,
|
||||
*field_layout,
|
||||
raw_value,
|
||||
"load_struct_tag_field_for_decrement",
|
||||
);
|
||||
|
||||
modify_refcount_layout_help(
|
||||
env,
|
||||
parent,
|
||||
layout_ids,
|
||||
mode.to_call_mode(fn_val),
|
||||
when_recursive,
|
||||
field_ptr,
|
||||
field_value,
|
||||
field_layout,
|
||||
);
|
||||
}
|
||||
@ -753,7 +762,15 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
||||
);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(env, parent, ptr, len, "modify_rc_index", loop_fn);
|
||||
incrementing_elem_loop(
|
||||
env,
|
||||
parent,
|
||||
*element_layout,
|
||||
ptr,
|
||||
len,
|
||||
"modify_rc_index",
|
||||
loop_fn,
|
||||
);
|
||||
}
|
||||
|
||||
let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper);
|
||||
@ -1289,7 +1306,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
||||
.build_bitcast(
|
||||
value_ptr,
|
||||
wrapper_type.ptr_type(AddressSpace::Generic),
|
||||
"opaque_to_correct",
|
||||
"opaque_to_correct_recursive_decrement",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
@ -1602,7 +1619,7 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let basic_type = basic_type_from_layout(env, &layout);
|
||||
let basic_type = basic_type_from_layout_1(env, &layout);
|
||||
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||
|
||||
modify_refcount_union_help(
|
||||
@ -1647,18 +1664,24 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||
|
||||
// Add args to scope
|
||||
let arg_symbol = Symbol::ARG_1;
|
||||
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||
let arg_ptr = fn_val.get_param_iter().next().unwrap().into_pointer_value();
|
||||
|
||||
arg_val.set_name(arg_symbol.as_str(&env.interns));
|
||||
arg_ptr.set_name(arg_symbol.as_str(&env.interns));
|
||||
|
||||
let parent = fn_val;
|
||||
|
||||
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||
|
||||
let wrapper_struct = arg_val.into_struct_value();
|
||||
|
||||
// read the tag_id
|
||||
let tag_id = get_tag_id_non_recursive(env, wrapper_struct);
|
||||
let tag_id_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(arg_ptr, TAG_ID_INDEX, "tag_id_ptr")
|
||||
.unwrap();
|
||||
|
||||
let tag_id = env
|
||||
.builder
|
||||
.build_load(tag_id_ptr, "load_tag_id")
|
||||
.into_int_value();
|
||||
|
||||
let tag_id_u8 = env
|
||||
.builder
|
||||
@ -1686,12 +1709,16 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
|
||||
|
||||
debug_assert!(wrapper_type.is_struct_type());
|
||||
let data_bytes = env
|
||||
let opaque_tag_data_ptr = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, TAG_DATA_INDEX, "read_tag_id")
|
||||
.unwrap()
|
||||
.into_struct_value();
|
||||
let wrapper_struct = cast_block_of_memory_to_tag(env.builder, data_bytes, wrapper_type);
|
||||
.build_struct_gep(arg_ptr, TAG_DATA_INDEX, "field_ptr")
|
||||
.unwrap();
|
||||
|
||||
let cast_tag_data_pointer = env.builder.build_pointer_cast(
|
||||
opaque_tag_data_ptr,
|
||||
wrapper_type.ptr_type(AddressSpace::Generic),
|
||||
"cast_to_concrete_tag",
|
||||
);
|
||||
|
||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||
if let Layout::RecursivePointer = field_layout {
|
||||
@ -1699,16 +1726,22 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||
} else if field_layout.contains_refcounted() {
|
||||
let field_ptr = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, i as u32, "modify_tag_field")
|
||||
.build_struct_gep(cast_tag_data_pointer, i as u32, "modify_tag_field")
|
||||
.unwrap();
|
||||
|
||||
let field_value = if field_layout.is_passed_by_reference() {
|
||||
field_ptr.into()
|
||||
} else {
|
||||
env.builder.build_load(field_ptr, "field_value")
|
||||
};
|
||||
|
||||
modify_refcount_layout_help(
|
||||
env,
|
||||
parent,
|
||||
layout_ids,
|
||||
mode.to_call_mode(fn_val),
|
||||
when_recursive,
|
||||
field_ptr,
|
||||
field_value,
|
||||
field_layout,
|
||||
);
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ use crate::wasm_module::{
|
||||
LocalId, Signature, SymInfo, ValueType,
|
||||
};
|
||||
use crate::{
|
||||
copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_TYPE,
|
||||
STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME,
|
||||
copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_SIZE,
|
||||
PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME,
|
||||
};
|
||||
|
||||
/// The memory address where the constants data will be loaded during module instantiation.
|
||||
@ -540,6 +540,29 @@ impl<'a> WasmBackend<'a> {
|
||||
|
||||
Expr::Struct(fields) => self.create_struct(sym, layout, fields),
|
||||
|
||||
Expr::StructAtIndex {
|
||||
index,
|
||||
field_layouts,
|
||||
structure,
|
||||
} => {
|
||||
if let StoredValue::StackMemory { location, .. } = self.storage.get(structure) {
|
||||
let (local_id, mut offset) =
|
||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||
for field in field_layouts.iter().take(*index as usize) {
|
||||
offset += field.stack_size(PTR_SIZE);
|
||||
}
|
||||
self.storage.copy_value_from_memory(
|
||||
&mut self.code_builder,
|
||||
*sym,
|
||||
local_id,
|
||||
offset,
|
||||
);
|
||||
} else {
|
||||
unreachable!("Unexpected storage for {:?}", structure)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
x => Err(format!("Expression is not yet implemented {:?}", x)),
|
||||
}
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ pub fn build_call_low_level<'a>(
|
||||
match lowlevel {
|
||||
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
|
||||
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft
|
||||
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen
|
||||
| ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
||||
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
|
||||
| ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil
|
||||
| ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListSublist
|
||||
| ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty | DictInsert
|
||||
| DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues | DictUnion
|
||||
| DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||
| StrTrimRight | StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim
|
||||
| ListLen | ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse
|
||||
| ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap
|
||||
| ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||
| ListSublist | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty
|
||||
| DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
|
||||
| DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||
return NotImplemented;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use roc_collections::all::MutMap;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::layout::WasmLayout;
|
||||
use crate::wasm_module::{CodeBuilder, LocalId, ValueType, VirtualMachineSymbolState};
|
||||
use crate::wasm_module::{CodeBuilder, LocalId, ValueType, VmSymbolState};
|
||||
use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, PTR_SIZE, PTR_TYPE};
|
||||
|
||||
pub enum StoredValueKind {
|
||||
@ -33,7 +33,7 @@ impl StackMemoryLocation {
|
||||
pub enum StoredValue {
|
||||
/// A value stored implicitly in the VM stack (primitives only)
|
||||
VirtualMachineStack {
|
||||
vm_state: VirtualMachineSymbolState,
|
||||
vm_state: VmSymbolState,
|
||||
value_type: ValueType,
|
||||
size: u32,
|
||||
},
|
||||
@ -126,7 +126,7 @@ impl<'a> Storage<'a> {
|
||||
}
|
||||
}
|
||||
_ => StoredValue::VirtualMachineStack {
|
||||
vm_state: VirtualMachineSymbolState::NotYetPushed,
|
||||
vm_state: VmSymbolState::NotYetPushed,
|
||||
value_type: *value_type,
|
||||
size: *size,
|
||||
},
|
||||
@ -319,6 +319,67 @@ impl<'a> Storage<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate code to copy a StoredValue from an arbitrary memory location
|
||||
/// (defined by a pointer and offset).
|
||||
pub fn copy_value_from_memory(
|
||||
&mut self,
|
||||
code_builder: &mut CodeBuilder,
|
||||
to_symbol: Symbol,
|
||||
from_ptr: LocalId,
|
||||
from_offset: u32,
|
||||
) -> u32 {
|
||||
let to_storage = self.get(&to_symbol).to_owned();
|
||||
match to_storage {
|
||||
StoredValue::StackMemory {
|
||||
location,
|
||||
size,
|
||||
alignment_bytes,
|
||||
} => {
|
||||
let (to_ptr, to_offset) = location.local_and_offset(self.stack_frame_pointer);
|
||||
copy_memory(
|
||||
code_builder,
|
||||
CopyMemoryConfig {
|
||||
from_ptr,
|
||||
from_offset,
|
||||
to_ptr,
|
||||
to_offset,
|
||||
size,
|
||||
alignment_bytes,
|
||||
},
|
||||
);
|
||||
size
|
||||
}
|
||||
|
||||
StoredValue::VirtualMachineStack {
|
||||
value_type, size, ..
|
||||
}
|
||||
| StoredValue::Local {
|
||||
value_type, size, ..
|
||||
} => {
|
||||
use crate::wasm_module::Align::*;
|
||||
|
||||
code_builder.get_local(from_ptr);
|
||||
match (value_type, size) {
|
||||
(ValueType::I64, 8) => code_builder.i64_load(Bytes8, from_offset),
|
||||
(ValueType::I32, 4) => code_builder.i32_load(Bytes4, from_offset),
|
||||
(ValueType::I32, 2) => code_builder.i32_load16_s(Bytes2, from_offset),
|
||||
(ValueType::I32, 1) => code_builder.i32_load8_s(Bytes1, from_offset),
|
||||
(ValueType::F32, 4) => code_builder.f32_load(Bytes4, from_offset),
|
||||
(ValueType::F64, 8) => code_builder.f64_load(Bytes8, from_offset),
|
||||
_ => {
|
||||
panic!("Cannot store {:?} with alignment of {:?}", value_type, size);
|
||||
}
|
||||
};
|
||||
|
||||
if let StoredValue::Local { local_id, .. } = to_storage {
|
||||
code_builder.set_local(local_id);
|
||||
}
|
||||
|
||||
size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate code to copy from one StoredValue to another
|
||||
/// Copies the _entire_ value. For struct fields etc., see `copy_value_to_memory`
|
||||
pub fn clone_value(
|
||||
@ -422,7 +483,7 @@ impl<'a> Storage<'a> {
|
||||
} = storage
|
||||
{
|
||||
let local_id = self.get_next_local_id();
|
||||
if vm_state != VirtualMachineSymbolState::NotYetPushed {
|
||||
if vm_state != VmSymbolState::NotYetPushed {
|
||||
code_builder.load_symbol(symbol, vm_state, local_id);
|
||||
code_builder.set_local(local_id);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
use core::panic;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
@ -10,6 +9,13 @@ use super::opcodes::{OpCode, OpCode::*};
|
||||
use super::serialize::{SerialBuffer, Serialize};
|
||||
use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
||||
|
||||
const ENABLE_DEBUG_LOG: bool = true;
|
||||
macro_rules! log_instruction {
|
||||
($($x: expr),+) => {
|
||||
if ENABLE_DEBUG_LOG { println!($($x,)*); }
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct LocalId(pub u32);
|
||||
|
||||
@ -29,6 +35,7 @@ impl Serialize for ValueType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum BlockType {
|
||||
NoResult,
|
||||
Value(ValueType),
|
||||
@ -43,6 +50,31 @@ impl BlockType {
|
||||
}
|
||||
}
|
||||
|
||||
/// A control block in our model of the VM
|
||||
/// Child blocks cannot "see" values from their parent block
|
||||
struct VmBlock<'a> {
|
||||
/// opcode indicating what kind of block this is
|
||||
opcode: OpCode,
|
||||
/// the stack of values for this block
|
||||
value_stack: Vec<'a, Symbol>,
|
||||
/// whether this block pushes a result value to its parent
|
||||
has_result: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for VmBlock<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!(
|
||||
"{:?} {}",
|
||||
self.opcode,
|
||||
if self.has_result {
|
||||
"Result"
|
||||
} else {
|
||||
"NoResult"
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wasm memory alignment. (Rust representation matches Wasm encoding)
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@ -73,7 +105,7 @@ impl From<u32> for Align {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||
pub enum VirtualMachineSymbolState {
|
||||
pub enum VmSymbolState {
|
||||
/// Value doesn't exist yet
|
||||
NotYetPushed,
|
||||
|
||||
@ -113,6 +145,8 @@ macro_rules! instruction_memargs {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CodeBuilder<'a> {
|
||||
arena: &'a Bump,
|
||||
|
||||
/// The main container for the instructions
|
||||
code: Vec<'a, u8>,
|
||||
|
||||
@ -135,8 +169,8 @@ pub struct CodeBuilder<'a> {
|
||||
inner_length: Vec<'a, u8>,
|
||||
|
||||
/// Our simulation model of the Wasm stack machine
|
||||
/// Keeps track of where Symbol values are in the VM stack
|
||||
vm_stack: Vec<'a, Symbol>,
|
||||
/// Nested blocks of instructions. A child block can't "see" the stack of its parent block
|
||||
vm_block_stack: Vec<'a, VmBlock<'a>>,
|
||||
|
||||
/// Linker info to help combine the Roc module with builtin & platform modules,
|
||||
/// e.g. to modify call instructions when function indices change
|
||||
@ -146,13 +180,22 @@ pub struct CodeBuilder<'a> {
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl<'a> CodeBuilder<'a> {
|
||||
pub fn new(arena: &'a Bump) -> Self {
|
||||
let mut vm_block_stack = Vec::with_capacity_in(8, arena);
|
||||
let function_block = VmBlock {
|
||||
opcode: BLOCK,
|
||||
has_result: true,
|
||||
value_stack: Vec::with_capacity_in(8, arena),
|
||||
};
|
||||
vm_block_stack.push(function_block);
|
||||
|
||||
CodeBuilder {
|
||||
arena,
|
||||
code: Vec::with_capacity_in(1024, arena),
|
||||
insertions: Vec::with_capacity_in(32, arena),
|
||||
insert_bytes: Vec::with_capacity_in(64, arena),
|
||||
preamble: Vec::with_capacity_in(32, arena),
|
||||
inner_length: Vec::with_capacity_in(5, arena),
|
||||
vm_stack: Vec::with_capacity_in(32, arena),
|
||||
vm_block_stack,
|
||||
relocations: Vec::with_capacity_in(32, arena),
|
||||
}
|
||||
}
|
||||
@ -167,35 +210,39 @@ impl<'a> CodeBuilder<'a> {
|
||||
|
||||
***********************************************************/
|
||||
|
||||
fn current_stack(&self) -> &Vec<'a, Symbol> {
|
||||
let block = self.vm_block_stack.last().unwrap();
|
||||
&block.value_stack
|
||||
}
|
||||
|
||||
fn current_stack_mut(&mut self) -> &mut Vec<'a, Symbol> {
|
||||
let block = self.vm_block_stack.last_mut().unwrap();
|
||||
&mut block.value_stack
|
||||
}
|
||||
|
||||
/// Set the Symbol that is at the top of the VM stack right now
|
||||
/// We will use this later when we need to load the Symbol
|
||||
pub fn set_top_symbol(&mut self, sym: Symbol) -> VirtualMachineSymbolState {
|
||||
let len = self.vm_stack.len();
|
||||
pub fn set_top_symbol(&mut self, sym: Symbol) -> VmSymbolState {
|
||||
let current_stack = &mut self.vm_block_stack.last_mut().unwrap().value_stack;
|
||||
let pushed_at = self.code.len();
|
||||
let top_symbol: &mut Symbol = current_stack.last_mut().unwrap();
|
||||
*top_symbol = sym;
|
||||
|
||||
if len == 0 {
|
||||
panic!(
|
||||
"trying to set symbol with nothing on stack, code = {:?}",
|
||||
self.code
|
||||
);
|
||||
}
|
||||
|
||||
self.vm_stack[len - 1] = sym;
|
||||
|
||||
VirtualMachineSymbolState::Pushed { pushed_at }
|
||||
VmSymbolState::Pushed { pushed_at }
|
||||
}
|
||||
|
||||
/// Verify if a sequence of symbols is at the top of the stack
|
||||
pub fn verify_stack_match(&self, symbols: &[Symbol]) -> bool {
|
||||
let current_stack = self.current_stack();
|
||||
let n_symbols = symbols.len();
|
||||
let stack_depth = self.vm_stack.len();
|
||||
let stack_depth = current_stack.len();
|
||||
if n_symbols > stack_depth {
|
||||
return false;
|
||||
}
|
||||
let offset = stack_depth - n_symbols;
|
||||
|
||||
for (i, sym) in symbols.iter().enumerate() {
|
||||
if self.vm_stack[offset + i] != *sym {
|
||||
if current_stack[offset + i] != *sym {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -214,7 +261,12 @@ impl<'a> CodeBuilder<'a> {
|
||||
end: self.insert_bytes.len(),
|
||||
});
|
||||
|
||||
// println!("insert {:?} {} at byte offset {} ", opcode, immediate, insert_at);
|
||||
log_instruction!(
|
||||
"**insert {:?} {} at byte offset {}**",
|
||||
opcode,
|
||||
immediate,
|
||||
insert_at
|
||||
);
|
||||
}
|
||||
|
||||
/// Load a Symbol that is stored in the VM stack
|
||||
@ -227,41 +279,56 @@ impl<'a> CodeBuilder<'a> {
|
||||
pub fn load_symbol(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
vm_state: VirtualMachineSymbolState,
|
||||
vm_state: VmSymbolState,
|
||||
next_local_id: LocalId,
|
||||
) -> Option<VirtualMachineSymbolState> {
|
||||
use VirtualMachineSymbolState::*;
|
||||
) -> Option<VmSymbolState> {
|
||||
use VmSymbolState::*;
|
||||
|
||||
match vm_state {
|
||||
NotYetPushed => panic!("Symbol {:?} has no value yet. Nothing to load.", symbol),
|
||||
NotYetPushed => unreachable!("Symbol {:?} has no value yet. Nothing to load.", symbol),
|
||||
|
||||
Pushed { pushed_at } => {
|
||||
let &top = self.vm_stack.last().unwrap();
|
||||
if top == symbol {
|
||||
// We're lucky, the symbol is already on top of the VM stack
|
||||
// No code to generate! (This reduces code size by up to 25% in tests.)
|
||||
// Just let the caller know what happened
|
||||
Some(Popped { pushed_at })
|
||||
} else {
|
||||
// Symbol is not on top of the stack. Find it.
|
||||
if let Some(found_index) = self.vm_stack.iter().rposition(|&s| s == symbol) {
|
||||
// Insert a local.set where the value was created
|
||||
self.add_insertion(pushed_at, SETLOCAL, next_local_id.0);
|
||||
match self.current_stack().last() {
|
||||
Some(top_symbol) if *top_symbol == symbol => {
|
||||
// We're lucky, the symbol is already on top of the current block's stack.
|
||||
// No code to generate! (This reduces code size by up to 25% in tests.)
|
||||
// Just let the caller know what happened
|
||||
Some(Popped { pushed_at })
|
||||
}
|
||||
_ => {
|
||||
// Symbol is not on top of the stack.
|
||||
// We should have saved it to a local, so go back and do that now.
|
||||
|
||||
// Take the value out of the stack where local.set was inserted
|
||||
self.vm_stack.remove(found_index);
|
||||
// It should still be on the stack in the block where it was assigned. Remove it.
|
||||
let mut found = false;
|
||||
for block in self.vm_block_stack.iter_mut() {
|
||||
if let Some(found_index) =
|
||||
block.value_stack.iter().position(|&s| s == symbol)
|
||||
{
|
||||
block.value_stack.remove(found_index);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert a local.get at the current position
|
||||
// Go back to the code position where it was pushed, and save it to a local
|
||||
if found {
|
||||
self.add_insertion(pushed_at, SETLOCAL, next_local_id.0);
|
||||
} else {
|
||||
if ENABLE_DEBUG_LOG {
|
||||
println!(
|
||||
"{:?} has been popped implicitly. Leaving it on the stack.",
|
||||
symbol
|
||||
);
|
||||
}
|
||||
self.add_insertion(pushed_at, TEELOCAL, next_local_id.0);
|
||||
}
|
||||
|
||||
// Recover the value again at the current position
|
||||
self.get_local(next_local_id);
|
||||
self.set_top_symbol(symbol);
|
||||
|
||||
// This Symbol is no longer stored in the VM stack, but in a local
|
||||
None
|
||||
} else {
|
||||
panic!(
|
||||
"{:?} has state {:?} but not found in VM stack",
|
||||
symbol, vm_state
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,7 +351,7 @@ impl<'a> CodeBuilder<'a> {
|
||||
|
||||
/**********************************************************
|
||||
|
||||
FINALIZE AND SERIALIZE
|
||||
FUNCTION HEADER
|
||||
|
||||
***********************************************************/
|
||||
|
||||
@ -377,6 +444,12 @@ impl<'a> CodeBuilder<'a> {
|
||||
self.insertions.sort_by_key(|ins| ins.at);
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
|
||||
SERIALIZE
|
||||
|
||||
***********************************************************/
|
||||
|
||||
/// Serialize all byte vectors in the right order
|
||||
/// Also update relocation offsets relative to the base offset (code section body start)
|
||||
pub fn serialize_with_relocs<T: SerialBuffer>(
|
||||
@ -435,33 +508,68 @@ impl<'a> CodeBuilder<'a> {
|
||||
|
||||
/// Base method for generating instructions
|
||||
/// Emits the opcode and simulates VM stack push/pop
|
||||
fn inst(&mut self, opcode: OpCode, pops: usize, push: bool) {
|
||||
let new_len = self.vm_stack.len() - pops as usize;
|
||||
self.vm_stack.truncate(new_len);
|
||||
fn inst_base(&mut self, opcode: OpCode, pops: usize, push: bool) {
|
||||
let current_stack = self.current_stack_mut();
|
||||
let new_len = current_stack.len() - pops as usize;
|
||||
current_stack.truncate(new_len);
|
||||
if push {
|
||||
self.vm_stack.push(Symbol::WASM_TMP);
|
||||
current_stack.push(Symbol::WASM_TMP);
|
||||
}
|
||||
|
||||
self.code.push(opcode as u8);
|
||||
|
||||
// println!("{:10}\t{:?}", format!("{:?}", opcode), &self.vm_stack);
|
||||
}
|
||||
|
||||
fn inst_imm8(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u8) {
|
||||
self.inst(opcode, pops, push);
|
||||
self.code.push(immediate);
|
||||
/// Plain instruction without any immediates
|
||||
fn inst(&mut self, opcode: OpCode, pops: usize, push: bool) {
|
||||
self.inst_base(opcode, pops, push);
|
||||
log_instruction!(
|
||||
"{:10}\t\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
self.current_stack()
|
||||
);
|
||||
}
|
||||
|
||||
// public for use in test code
|
||||
pub fn inst_imm32(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u32) {
|
||||
self.inst(opcode, pops, push);
|
||||
/// Block instruction
|
||||
fn inst_block(&mut self, opcode: OpCode, pops: usize, block_type: BlockType) {
|
||||
self.inst_base(opcode, pops, false);
|
||||
self.code.push(block_type.as_byte());
|
||||
|
||||
// Start a new block with a fresh value stack
|
||||
self.vm_block_stack.push(VmBlock {
|
||||
opcode,
|
||||
value_stack: Vec::with_capacity_in(8, self.arena),
|
||||
has_result: block_type != BlockType::NoResult,
|
||||
});
|
||||
|
||||
log_instruction!(
|
||||
"{:10} {:?}\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
block_type,
|
||||
&self.vm_block_stack
|
||||
);
|
||||
}
|
||||
|
||||
fn inst_imm32(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u32) {
|
||||
self.inst_base(opcode, pops, push);
|
||||
self.code.encode_u32(immediate);
|
||||
log_instruction!(
|
||||
"{:10}\t{}\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
immediate,
|
||||
self.current_stack()
|
||||
);
|
||||
}
|
||||
|
||||
fn inst_mem(&mut self, opcode: OpCode, pops: usize, push: bool, align: Align, offset: u32) {
|
||||
self.inst(opcode, pops, push);
|
||||
self.inst_base(opcode, pops, push);
|
||||
self.code.push(align as u8);
|
||||
self.code.encode_u32(offset);
|
||||
log_instruction!(
|
||||
"{:10} {:?} {}\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
align,
|
||||
offset,
|
||||
self.current_stack()
|
||||
);
|
||||
}
|
||||
|
||||
/// Insert a linker relocation for a memory address
|
||||
@ -488,22 +596,38 @@ impl<'a> CodeBuilder<'a> {
|
||||
instruction_no_args!(nop, NOP, 0, false);
|
||||
|
||||
pub fn block(&mut self, ty: BlockType) {
|
||||
self.inst_imm8(BLOCK, 0, false, ty.as_byte());
|
||||
self.inst_block(BLOCK, 0, ty);
|
||||
}
|
||||
pub fn loop_(&mut self, ty: BlockType) {
|
||||
self.inst_imm8(LOOP, 0, false, ty.as_byte());
|
||||
self.inst_block(LOOP, 0, ty);
|
||||
}
|
||||
pub fn if_(&mut self, ty: BlockType) {
|
||||
self.inst_imm8(IF, 1, false, ty.as_byte());
|
||||
self.inst_block(IF, 1, ty);
|
||||
}
|
||||
pub fn else_(&mut self) {
|
||||
// Reuse the 'then' block but clear its value stack
|
||||
self.current_stack_mut().clear();
|
||||
self.inst(ELSE, 0, false);
|
||||
}
|
||||
|
||||
instruction_no_args!(else_, ELSE, 0, false);
|
||||
instruction_no_args!(end, END, 0, false);
|
||||
pub fn end(&mut self) {
|
||||
self.inst_base(END, 0, false);
|
||||
|
||||
let ended_block = self.vm_block_stack.pop().unwrap();
|
||||
if ended_block.has_result {
|
||||
let result = ended_block.value_stack.last().unwrap();
|
||||
self.current_stack_mut().push(*result)
|
||||
}
|
||||
|
||||
log_instruction!("END \t\t{:?}", &self.vm_block_stack);
|
||||
}
|
||||
pub fn br(&mut self, levels: u32) {
|
||||
self.inst_imm32(BR, 0, false, levels);
|
||||
}
|
||||
pub fn br_if(&mut self, levels: u32) {
|
||||
// In dynamic execution, br_if can pop 2 values if condition is true and the target block has a result.
|
||||
// But our stack model is for *static* analysis and we need it to be correct at the next instruction,
|
||||
// where the branch was not taken. So we only pop 1 value, the condition.
|
||||
self.inst_imm32(BRIF, 1, false, levels);
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
@ -520,7 +644,7 @@ impl<'a> CodeBuilder<'a> {
|
||||
n_args: usize,
|
||||
has_return_val: bool,
|
||||
) {
|
||||
self.inst(CALL, n_args, has_return_val);
|
||||
self.inst_base(CALL, n_args, has_return_val);
|
||||
|
||||
let offset = self.code.len() as u32;
|
||||
self.code.encode_padded_u32(function_index);
|
||||
@ -533,6 +657,13 @@ impl<'a> CodeBuilder<'a> {
|
||||
offset,
|
||||
symbol_index,
|
||||
});
|
||||
|
||||
log_instruction!(
|
||||
"{:10}\t{}\t{:?}",
|
||||
format!("{:?}", CALL),
|
||||
function_index,
|
||||
self.current_stack()
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -584,26 +715,44 @@ impl<'a> CodeBuilder<'a> {
|
||||
instruction_memargs!(i64_store32, I64STORE32, 2, false);
|
||||
|
||||
pub fn memory_size(&mut self) {
|
||||
self.inst_imm8(CURRENTMEMORY, 0, true, 0);
|
||||
self.inst(CURRENTMEMORY, 0, true);
|
||||
self.code.push(0);
|
||||
}
|
||||
pub fn memory_grow(&mut self) {
|
||||
self.inst_imm8(GROWMEMORY, 1, true, 0);
|
||||
self.inst(GROWMEMORY, 1, true);
|
||||
self.code.push(0);
|
||||
}
|
||||
|
||||
fn log_const<T>(&self, opcode: OpCode, x: T)
|
||||
where
|
||||
T: std::fmt::Debug + std::fmt::Display,
|
||||
{
|
||||
log_instruction!(
|
||||
"{:10}\t{}\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
x,
|
||||
self.current_stack()
|
||||
);
|
||||
}
|
||||
pub fn i32_const(&mut self, x: i32) {
|
||||
self.inst(I32CONST, 0, true);
|
||||
self.inst_base(I32CONST, 0, true);
|
||||
self.code.encode_i32(x);
|
||||
self.log_const(I32CONST, x);
|
||||
}
|
||||
pub fn i64_const(&mut self, x: i64) {
|
||||
self.inst(I64CONST, 0, true);
|
||||
self.inst_base(I64CONST, 0, true);
|
||||
self.code.encode_i64(x);
|
||||
self.log_const(I64CONST, x);
|
||||
}
|
||||
pub fn f32_const(&mut self, x: f32) {
|
||||
self.inst(F32CONST, 0, true);
|
||||
self.inst_base(F32CONST, 0, true);
|
||||
self.code.encode_f32(x);
|
||||
self.log_const(F32CONST, x);
|
||||
}
|
||||
pub fn f64_const(&mut self, x: f64) {
|
||||
self.inst(F64CONST, 0, true);
|
||||
self.inst_base(F64CONST, 0, true);
|
||||
self.code.encode_f64(x);
|
||||
self.log_const(F64CONST, x);
|
||||
}
|
||||
|
||||
// TODO: Consider creating unified methods for numerical ops like 'eq' and 'add',
|
||||
|
@ -4,8 +4,6 @@ pub mod opcodes;
|
||||
pub mod sections;
|
||||
pub mod serialize;
|
||||
|
||||
pub use code_builder::{
|
||||
Align, BlockType, CodeBuilder, LocalId, ValueType, VirtualMachineSymbolState,
|
||||
};
|
||||
pub use code_builder::{Align, BlockType, CodeBuilder, LocalId, ValueType, VmSymbolState};
|
||||
pub use linking::{LinkingSubSection, SymInfo};
|
||||
pub use sections::{ConstExpr, Export, ExportType, Global, GlobalType, Signature, WasmModule};
|
||||
|
@ -1,5 +1,5 @@
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum OpCode {
|
||||
UNREACHABLE = 0x00,
|
||||
NOP = 0x01,
|
||||
|
@ -235,16 +235,12 @@ fn generate_entry_doc<'a>(
|
||||
|
||||
fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> TypeAnnotation {
|
||||
match type_annotation {
|
||||
ast::TypeAnnotation::TagUnion {
|
||||
tags,
|
||||
ext,
|
||||
final_comments: _,
|
||||
} => {
|
||||
ast::TypeAnnotation::TagUnion { tags, ext } => {
|
||||
let mut tags_to_render: Vec<Tag> = Vec::new();
|
||||
|
||||
let mut any_tags_are_private = false;
|
||||
|
||||
for tag in tags {
|
||||
for tag in tags.iter() {
|
||||
match tag_to_doc(in_func_type_ann, tag.value) {
|
||||
None => {
|
||||
any_tags_are_private = true;
|
||||
|
@ -2608,8 +2608,8 @@ fn parse_header<'a>(
|
||||
opt_shorthand,
|
||||
header_src,
|
||||
packages: &[],
|
||||
exposes: header.exposes.into_bump_slice(),
|
||||
imports: header.imports.into_bump_slice(),
|
||||
exposes: header.exposes.items,
|
||||
imports: header.imports.items,
|
||||
to_platform: None,
|
||||
};
|
||||
|
||||
@ -2642,8 +2642,8 @@ fn parse_header<'a>(
|
||||
opt_shorthand,
|
||||
header_src,
|
||||
packages,
|
||||
exposes: header.provides.into_bump_slice(),
|
||||
imports: header.imports.into_bump_slice(),
|
||||
exposes: header.provides.items,
|
||||
imports: header.imports.items,
|
||||
to_platform: Some(header.to.value.clone()),
|
||||
};
|
||||
|
||||
@ -3236,7 +3236,7 @@ fn send_header_two<'a>(
|
||||
|
||||
let extra = HeaderFor::PkgConfig {
|
||||
config_shorthand: shorthand,
|
||||
platform_main_type: requires[0].value.clone(),
|
||||
platform_main_type: requires[0].value,
|
||||
main_for_host,
|
||||
};
|
||||
|
||||
@ -3409,8 +3409,7 @@ fn fabricate_pkg_config_module<'a>(
|
||||
header_src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] =
|
||||
header.provides.clone().into_bump_slice();
|
||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] = header.provides.items;
|
||||
|
||||
let info = PlatformHeaderInfo {
|
||||
filename,
|
||||
@ -3420,8 +3419,8 @@ fn fabricate_pkg_config_module<'a>(
|
||||
app_module_id,
|
||||
packages: &[],
|
||||
provides,
|
||||
requires: arena.alloc([header.requires.signature.clone()]),
|
||||
imports: header.imports.clone().into_bump_slice(),
|
||||
requires: arena.alloc([header.requires.signature]),
|
||||
imports: header.imports.items,
|
||||
};
|
||||
|
||||
send_header_two(
|
||||
@ -3467,7 +3466,7 @@ fn fabricate_effects_module<'a>(
|
||||
{
|
||||
let mut module_ids = (*module_ids).lock();
|
||||
|
||||
for exposed in header.exposes {
|
||||
for exposed in header.exposes.iter() {
|
||||
if let ExposesEntry::Exposed(module_name) = exposed.value {
|
||||
module_ids.get_or_insert(&PQModuleName::Qualified(
|
||||
shorthand,
|
||||
@ -3886,7 +3885,7 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>
|
||||
Module(module_name, exposes) => {
|
||||
let mut exposed = Vec::with_capacity(exposes.len());
|
||||
|
||||
for loc_entry in exposes {
|
||||
for loc_entry in exposes.iter() {
|
||||
exposed.push(ident_from_exposed(&loc_entry.value));
|
||||
}
|
||||
|
||||
@ -3901,7 +3900,7 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>
|
||||
Package(package_name, module_name, exposes) => {
|
||||
let mut exposed = Vec::with_capacity(exposes.len());
|
||||
|
||||
for loc_entry in exposes {
|
||||
for loc_entry in exposes.iter() {
|
||||
exposed.push(ident_from_exposed(&loc_entry.value));
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ pub enum LowLevel {
|
||||
StrFromFloat,
|
||||
StrTrim,
|
||||
StrTrimLeft,
|
||||
StrTrimRight,
|
||||
ListLen,
|
||||
ListGetUnsafe,
|
||||
ListSet,
|
||||
@ -130,6 +131,7 @@ macro_rules! first_order {
|
||||
| StrRepeat
|
||||
| StrTrim
|
||||
| StrTrimLeft
|
||||
| StrTrimRight
|
||||
| StrFromFloat
|
||||
| ListLen
|
||||
| ListGetUnsafe
|
||||
|
@ -1019,6 +1019,7 @@ define_builtins! {
|
||||
19 STR_REPEAT: "repeat"
|
||||
20 STR_TRIM: "trim"
|
||||
21 STR_TRIM_LEFT: "trimLeft"
|
||||
22 STR_TRIM_RIGHT: "trimRight"
|
||||
}
|
||||
4 LIST: "List" => {
|
||||
0 LIST_LIST: "List" imported // the List.List type alias
|
||||
|
@ -941,6 +941,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||
StrTrim => arena.alloc_slice_copy(&[owned]),
|
||||
StrTrimLeft => arena.alloc_slice_copy(&[owned]),
|
||||
StrTrimRight => arena.alloc_slice_copy(&[owned]),
|
||||
StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||
ListSingle => arena.alloc_slice_copy(&[irrelevant]),
|
||||
ListRepeat => arena.alloc_slice_copy(&[irrelevant, borrowed]),
|
||||
|
@ -863,6 +863,16 @@ impl<'a> Layout<'a> {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_passed_by_reference(&self) -> bool {
|
||||
match self {
|
||||
Layout::Union(UnionLayout::NonRecursive(_)) => true,
|
||||
Layout::LambdaSet(lambda_set) => {
|
||||
lambda_set.runtime_representation().is_passed_by_reference()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stack_size(&self, pointer_size: u32) -> u32 {
|
||||
let width = self.stack_size_without_alignment(pointer_size);
|
||||
let alignment = self.alignment_bytes(pointer_size);
|
||||
@ -941,16 +951,16 @@ impl<'a> Layout<'a> {
|
||||
})
|
||||
.max();
|
||||
|
||||
let tag_id_builtin = variant.tag_id_builtin();
|
||||
match max_alignment {
|
||||
Some(align) => {
|
||||
let tag_id_builtin = variant.tag_id_builtin();
|
||||
|
||||
round_up_to_alignment(
|
||||
align,
|
||||
tag_id_builtin.alignment_bytes(pointer_size),
|
||||
)
|
||||
Some(align) => round_up_to_alignment(
|
||||
align.max(tag_id_builtin.alignment_bytes(pointer_size)),
|
||||
tag_id_builtin.alignment_bytes(pointer_size),
|
||||
),
|
||||
None => {
|
||||
// none of the tags had any payload, but the tag id still contains information
|
||||
tag_id_builtin.alignment_bytes(pointer_size)
|
||||
}
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
Recursive(_)
|
||||
@ -2674,3 +2684,27 @@ impl<'a> std::convert::TryFrom<&Layout<'a>> for ListLayout<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn width_and_alignment_union_empty_struct() {
|
||||
let lambda_set = LambdaSet {
|
||||
set: &[(Symbol::LIST_MAP, &[])],
|
||||
representation: &Layout::Struct(&[]),
|
||||
};
|
||||
|
||||
let a = &[Layout::Struct(&[])] as &[_];
|
||||
let b = &[Layout::LambdaSet(lambda_set)] as &[_];
|
||||
let tt = [a, b];
|
||||
|
||||
let layout = Layout::Union(UnionLayout::NonRecursive(&tt));
|
||||
|
||||
// at the moment, the tag id uses an I64, so
|
||||
let ptr_width = 8;
|
||||
assert_eq!(layout.stack_size(ptr_width), 8);
|
||||
assert_eq!(layout.alignment_bytes(ptr_width), 8);
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,12 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::header::{AppHeader, ImportsEntry, InterfaceHeader, PlatformHeader, TypedIdent};
|
||||
use crate::ident::Ident;
|
||||
use bumpalo::collections::String;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
use roc_module::operator::{BinOp, CalledVia, UnaryOp};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Collection<'a, T> {
|
||||
pub items: &'a [T],
|
||||
pub final_comments: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
impl<'a, T> Collection<'a, T> {
|
||||
pub fn empty() -> Collection<'a, T> {
|
||||
Collection {
|
||||
items: &[],
|
||||
final_comments: &[],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_items(items: &'a [T]) -> Collection<'a, T> {
|
||||
Collection {
|
||||
items,
|
||||
final_comments: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Module<'a> {
|
||||
Interface { header: InterfaceHeader<'a> },
|
||||
@ -115,21 +95,14 @@ pub enum Expr<'a> {
|
||||
AccessorFunction(&'a str),
|
||||
|
||||
// Collection Literals
|
||||
List {
|
||||
items: &'a [&'a Loc<Expr<'a>>],
|
||||
final_comments: &'a [CommentOrNewline<'a>],
|
||||
},
|
||||
List(Collection<'a, &'a Loc<Expr<'a>>>),
|
||||
|
||||
RecordUpdate {
|
||||
update: &'a Loc<Expr<'a>>,
|
||||
fields: &'a [Loc<AssignedField<'a, Expr<'a>>>],
|
||||
final_comments: &'a &'a [CommentOrNewline<'a>],
|
||||
fields: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>,
|
||||
},
|
||||
|
||||
Record {
|
||||
fields: &'a [Loc<AssignedField<'a, Expr<'a>>>],
|
||||
final_comments: &'a [CommentOrNewline<'a>],
|
||||
},
|
||||
Record(Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>),
|
||||
|
||||
// Lookups
|
||||
Var {
|
||||
@ -257,16 +230,14 @@ pub enum TypeAnnotation<'a> {
|
||||
/// The row type variable in an open record, e.g. the `r` in `{ name: Str }r`.
|
||||
/// This is None if it's a closed record annotation like `{ name: Str }`.
|
||||
ext: Option<&'a Loc<TypeAnnotation<'a>>>,
|
||||
// final_comments: &'a [CommentOrNewline<'a>],
|
||||
},
|
||||
|
||||
/// A tag union, e.g. `[
|
||||
TagUnion {
|
||||
tags: &'a [Loc<Tag<'a>>],
|
||||
/// The row type variable in an open tag union, e.g. the `a` in `[ Foo, Bar ]a`.
|
||||
/// This is None if it's a closed tag union like `[ Foo, Bar]`.
|
||||
ext: Option<&'a Loc<TypeAnnotation<'a>>>,
|
||||
final_comments: &'a [CommentOrNewline<'a>],
|
||||
tags: Collection<'a, Loc<Tag<'a>>>,
|
||||
},
|
||||
|
||||
/// The `*` type variable, e.g. in (List *)
|
||||
@ -357,10 +328,11 @@ pub enum Pattern<'a> {
|
||||
GlobalTag(&'a str),
|
||||
PrivateTag(&'a str),
|
||||
Apply(&'a Loc<Pattern<'a>>, &'a [Loc<Pattern<'a>>]),
|
||||
|
||||
/// This is Loc<Pattern> rather than Loc<str> so we can record comments
|
||||
/// around the destructured names, e.g. { x ### x does stuff ###, y }
|
||||
/// In practice, these patterns will always be Identifier
|
||||
RecordDestructure(&'a [Loc<Pattern<'a>>]),
|
||||
RecordDestructure(Collection<'a, Loc<Pattern<'a>>>),
|
||||
|
||||
/// A required field pattern, e.g. { x: Just 0 } -> ...
|
||||
/// Can only occur inside of a RecordDestructure
|
||||
@ -519,6 +491,126 @@ impl<'a> Pattern<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Collection<'a, T> {
|
||||
pub items: &'a [T],
|
||||
// Use a pointer to a slice (rather than just a slice), in order to avoid bloating
|
||||
// Ast variants. The final_comments field is rarely accessed in the hot path, so
|
||||
// this shouldn't matter much for perf.
|
||||
// Use an Option, so it's possible to initialize without allocating.
|
||||
final_comments: Option<&'a &'a [CommentOrNewline<'a>]>,
|
||||
}
|
||||
|
||||
impl<'a, T> Collection<'a, T> {
|
||||
pub fn empty() -> Collection<'a, T> {
|
||||
Collection {
|
||||
items: &[],
|
||||
final_comments: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_items(items: &'a [T]) -> Collection<'a, T> {
|
||||
Collection {
|
||||
items,
|
||||
final_comments: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_items_and_comments(
|
||||
arena: &'a Bump,
|
||||
items: &'a [T],
|
||||
comments: &'a [CommentOrNewline<'a>],
|
||||
) -> Collection<'a, T> {
|
||||
Collection {
|
||||
items,
|
||||
final_comments: if comments.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(arena.alloc(comments))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_items<V>(&self, new_items: &'a [V]) -> Collection<'a, V> {
|
||||
Collection {
|
||||
items: new_items,
|
||||
final_comments: self.final_comments,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ptrify_items(&self, arena: &'a Bump) -> Collection<'a, &'a T> {
|
||||
let mut allocated = Vec::with_capacity_in(self.len(), arena);
|
||||
|
||||
for parsed_elem in self.items {
|
||||
allocated.push(parsed_elem);
|
||||
}
|
||||
|
||||
self.replace_items(allocated.into_bump_slice())
|
||||
}
|
||||
|
||||
pub fn map_items<V: 'a>(&self, arena: &'a Bump, f: impl Fn(&'a T) -> V) -> Collection<'a, V> {
|
||||
let mut allocated = Vec::with_capacity_in(self.len(), arena);
|
||||
|
||||
for parsed_elem in self.items {
|
||||
allocated.push(f(parsed_elem));
|
||||
}
|
||||
|
||||
self.replace_items(allocated.into_bump_slice())
|
||||
}
|
||||
|
||||
pub fn map_items_result<V: 'a, E>(
|
||||
&self,
|
||||
arena: &'a Bump,
|
||||
f: impl Fn(&T) -> Result<V, E>,
|
||||
) -> Result<Collection<'a, V>, E> {
|
||||
let mut allocated = Vec::with_capacity_in(self.len(), arena);
|
||||
|
||||
for parsed_elem in self.items {
|
||||
allocated.push(f(parsed_elem)?);
|
||||
}
|
||||
|
||||
Ok(self.replace_items(allocated.into_bump_slice()))
|
||||
}
|
||||
|
||||
pub fn final_comments(&self) -> &'a [CommentOrNewline<'a>] {
|
||||
if let Some(final_comments) = self.final_comments {
|
||||
*final_comments
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &'a T> {
|
||||
self.items.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PartialEq> PartialEq for Collection<'a, T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.items == other.items && self.final_comments() == other.final_comments()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Debug> Debug for Collection<'a, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.final_comments().is_empty() {
|
||||
f.debug_list().entries(self.items.iter()).finish()
|
||||
} else {
|
||||
f.debug_struct("Collection")
|
||||
.field("items", &self.items)
|
||||
.field("final_comments", &self.final_comments())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Spaceable<'a> {
|
||||
fn before(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self;
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::ast::{AssignedField, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation};
|
||||
use crate::ast::{
|
||||
AssignedField, Collection, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation,
|
||||
};
|
||||
use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::{lowercase_ident, parse_ident, Ident};
|
||||
use crate::keyword;
|
||||
@ -1446,20 +1448,14 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||
|
||||
Expr::ParensAround(sub_expr) => expr_to_pattern_help(arena, sub_expr),
|
||||
|
||||
Expr::Record {
|
||||
fields,
|
||||
final_comments: _,
|
||||
} => {
|
||||
let mut loc_patterns = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
||||
for loc_assigned_field in fields.iter() {
|
||||
Expr::Record(fields) => {
|
||||
let patterns = fields.map_items_result(arena, |loc_assigned_field| {
|
||||
let region = loc_assigned_field.region;
|
||||
let value = assigned_expr_field_to_pattern_help(arena, &loc_assigned_field.value)?;
|
||||
Ok(Located { region, value })
|
||||
})?;
|
||||
|
||||
loc_patterns.push(Located { region, value });
|
||||
}
|
||||
|
||||
Ok(Pattern::RecordDestructure(loc_patterns.into_bump_slice()))
|
||||
Ok(Pattern::RecordDestructure(patterns))
|
||||
}
|
||||
|
||||
Expr::Float(string) => Ok(Pattern::FloatLiteral(string)),
|
||||
@ -2165,16 +2161,8 @@ fn list_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EList<'a>
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
let mut allocated = Vec::with_capacity_in(elements.items.len(), arena);
|
||||
|
||||
for parsed_elem in elements.items {
|
||||
allocated.push(parsed_elem);
|
||||
}
|
||||
|
||||
let expr = Expr::List {
|
||||
items: allocated.into_bump_slice(),
|
||||
final_comments: elements.final_comments,
|
||||
};
|
||||
let elements = elements.ptrify_items(arena);
|
||||
let expr = Expr::List(elements);
|
||||
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
@ -2312,13 +2300,17 @@ fn record_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'
|
||||
let mut value = match opt_update {
|
||||
Some(update) => Expr::RecordUpdate {
|
||||
update: &*arena.alloc(update),
|
||||
fields: loc_assigned_fields_with_comments.value.0.into_bump_slice(),
|
||||
final_comments: arena.alloc(loc_assigned_fields_with_comments.value.1),
|
||||
},
|
||||
None => Expr::Record {
|
||||
fields: loc_assigned_fields_with_comments.value.0.into_bump_slice(),
|
||||
final_comments: loc_assigned_fields_with_comments.value.1,
|
||||
fields: Collection::with_items_and_comments(
|
||||
arena,
|
||||
loc_assigned_fields_with_comments.value.0.into_bump_slice(),
|
||||
arena.alloc(loc_assigned_fields_with_comments.value.1),
|
||||
),
|
||||
},
|
||||
None => Expr::Record(Collection::with_items_and_comments(
|
||||
arena,
|
||||
loc_assigned_fields_with_comments.value.0.into_bump_slice(),
|
||||
loc_assigned_fields_with_comments.value.1,
|
||||
)),
|
||||
};
|
||||
|
||||
// there can be field access, e.g. `{ x : 4 }.x`
|
||||
|
@ -38,7 +38,7 @@ pub enum PackageOrPath<'a> {
|
||||
Path(StrLiteral<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ModuleName<'a>(&'a str);
|
||||
|
||||
impl<'a> From<ModuleName<'a>> for &'a str {
|
||||
@ -60,8 +60,8 @@ impl<'a> ModuleName<'a> {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
@ -82,8 +82,8 @@ pub enum To<'a> {
|
||||
pub struct AppHeader<'a> {
|
||||
pub name: Loc<StrLiteral<'a>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub to: Loc<To<'a>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
@ -117,7 +117,7 @@ pub struct PackageHeader<'a> {
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PlatformRigid<'a> {
|
||||
Entry { rigid: &'a str, alias: &'a str },
|
||||
|
||||
@ -137,7 +137,7 @@ impl<'a> Spaceable<'a> for PlatformRigid<'a> {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformRequires<'a> {
|
||||
pub rigids: Vec<'a, Loc<PlatformRigid<'a>>>,
|
||||
pub rigids: Collection<'a, Loc<PlatformRigid<'a>>>,
|
||||
pub signature: Loc<TypedIdent<'a>>,
|
||||
}
|
||||
|
||||
@ -145,10 +145,10 @@ pub struct PlatformRequires<'a> {
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub requires: PlatformRequires<'a>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
@ -177,7 +177,7 @@ pub struct Effects<'a> {
|
||||
pub entries: &'a [Loc<TypedIdent<'a>>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ExposesEntry<'a, T> {
|
||||
/// e.g. `Task`
|
||||
Exposed(T),
|
||||
@ -196,16 +196,19 @@ impl<'a, T> Spaceable<'a> for ExposesEntry<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ImportsEntry<'a> {
|
||||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
Module(ModuleName<'a>, Vec<'a, Loc<ExposesEntry<'a, &'a str>>>),
|
||||
Module(
|
||||
ModuleName<'a>,
|
||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
|
||||
/// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }`
|
||||
Package(
|
||||
&'a str,
|
||||
ModuleName<'a>,
|
||||
Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
|
||||
// Spaces
|
||||
@ -224,7 +227,7 @@ impl<'a> ExposesEntry<'a, &'a str> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum TypedIdent<'a> {
|
||||
/// e.g.
|
||||
///
|
||||
|
@ -220,11 +220,11 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
let opt_imports: Option<(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
)> = opt_imports;
|
||||
|
||||
let ((before_imports, after_imports), imports) =
|
||||
opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Vec::new_in(arena)));
|
||||
opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Collection::empty()));
|
||||
let provides: ProvidesTo<'a> = provides; // rustc must be told the type here
|
||||
|
||||
let header = AppHeader {
|
||||
@ -303,7 +303,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProvidesTo<'a> {
|
||||
entries: Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
to: Located<To<'a>>,
|
||||
|
||||
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
@ -362,7 +362,7 @@ fn provides_without_to<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
EProvides<'a>,
|
||||
> {
|
||||
@ -376,14 +376,16 @@ fn provides_without_to<'a>() -> impl Parser<
|
||||
EProvides::IndentProvides,
|
||||
EProvides::IndentListStart
|
||||
),
|
||||
collection_e!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', EProvides::ListStart),
|
||||
exposes_entry(EProvides::Identifier),
|
||||
word1(b',', EProvides::ListEnd),
|
||||
word1(b']', EProvides::ListEnd),
|
||||
min_indent,
|
||||
EProvides::Open,
|
||||
EProvides::Space,
|
||||
EProvides::IndentListEnd
|
||||
EProvides::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -442,15 +444,17 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
|
||||
#[inline(always)]
|
||||
fn requires_rigids<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Vec<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> {
|
||||
collection_e!(
|
||||
) -> impl Parser<'a, Collection<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> {
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', ERequires::ListStart),
|
||||
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
|
||||
word1(b',', ERequires::ListEnd),
|
||||
word1(b'}', ERequires::ListEnd),
|
||||
min_indent,
|
||||
ERequires::Open,
|
||||
ERequires::Space,
|
||||
ERequires::IndentListEnd
|
||||
ERequires::IndentListEnd,
|
||||
PlatformRigid::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
@ -487,7 +491,7 @@ fn exposes_values<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
@ -502,14 +506,16 @@ fn exposes_values<'a>() -> impl Parser<
|
||||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart
|
||||
),
|
||||
collection_e!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', EExposes::ListStart),
|
||||
exposes_entry(EExposes::Identifier),
|
||||
word1(b',', EExposes::ListEnd),
|
||||
word1(b']', EExposes::ListEnd),
|
||||
min_indent,
|
||||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -539,7 +545,7 @@ fn exposes_modules<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
Collection<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
@ -554,14 +560,16 @@ fn exposes_modules<'a>() -> impl Parser<
|
||||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart
|
||||
),
|
||||
collection_e!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', EExposes::ListStart),
|
||||
exposes_module(EExposes::Identifier),
|
||||
word1(b',', EExposes::ListEnd),
|
||||
word1(b']', EExposes::ListEnd),
|
||||
min_indent,
|
||||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -631,7 +639,7 @@ fn imports<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
),
|
||||
EImports,
|
||||
> {
|
||||
@ -646,14 +654,16 @@ fn imports<'a>() -> impl Parser<
|
||||
EImports::IndentImports,
|
||||
EImports::IndentListStart
|
||||
),
|
||||
collection_e!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', EImports::ListStart),
|
||||
loc!(imports_entry()),
|
||||
word1(b',', EImports::ListEnd),
|
||||
word1(b']', EImports::ListEnd),
|
||||
min_indent,
|
||||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentListEnd
|
||||
EImports::IndentListEnd,
|
||||
ImportsEntry::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -687,14 +697,16 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
||||
space0_e(min_indent, EEffects::Space, EEffects::IndentListStart)
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
let (_, entries, state) = collection_e!(
|
||||
let (_, entries, state) = collection_trailing_sep_e!(
|
||||
word1(b'{', EEffects::ListStart),
|
||||
specialize(EEffects::TypedIdent, loc!(typed_ident())),
|
||||
word1(b',', EEffects::ListEnd),
|
||||
word1(b'}', EEffects::ListEnd),
|
||||
min_indent,
|
||||
EEffects::Open,
|
||||
EEffects::Space,
|
||||
EEffects::IndentListEnd
|
||||
EEffects::IndentListEnd,
|
||||
TypedIdent::SpaceBefore
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
@ -706,7 +718,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
||||
spaces_after_type_name,
|
||||
effect_shortname: type_shortname,
|
||||
effect_type_name: type_name,
|
||||
entries: entries.into_bump_slice(),
|
||||
entries: entries.items,
|
||||
},
|
||||
state,
|
||||
))
|
||||
@ -768,7 +780,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
||||
|
||||
type Temp<'a> = (
|
||||
(Option<&'a str>, ModuleName<'a>),
|
||||
Option<Vec<'a, Located<ExposesEntry<'a, &'a str>>>>,
|
||||
Option<Collection<'a, Located<ExposesEntry<'a, &'a str>>>>,
|
||||
);
|
||||
|
||||
map_with_arena!(
|
||||
@ -785,19 +797,21 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
||||
// e.g. `.{ Task, after}`
|
||||
maybe!(skip_first!(
|
||||
word1(b'.', EImports::ExposingDot),
|
||||
collection_e!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', EImports::SetStart),
|
||||
exposes_entry(EImports::Identifier),
|
||||
word1(b',', EImports::SetEnd),
|
||||
word1(b'}', EImports::SetEnd),
|
||||
min_indent,
|
||||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentSetEnd
|
||||
EImports::IndentSetEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
)
|
||||
))
|
||||
),
|
||||
|arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
|
||||
let exposed_values = opt_values.unwrap_or_else(|| Vec::new_in(arena));
|
||||
|_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
|
||||
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
|
||||
|
||||
match opt_shortname {
|
||||
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
|
||||
|
@ -214,6 +214,7 @@ pub enum EHeader<'a> {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EProvides<'a> {
|
||||
Provides(Row, Col),
|
||||
Open(Row, Col),
|
||||
To(Row, Col),
|
||||
IndentProvides(Row, Col),
|
||||
IndentTo(Row, Col),
|
||||
@ -230,6 +231,7 @@ pub enum EProvides<'a> {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EExposes {
|
||||
Exposes(Row, Col),
|
||||
Open(Row, Col),
|
||||
IndentExposes(Row, Col),
|
||||
IndentListStart(Row, Col),
|
||||
IndentListEnd(Row, Col),
|
||||
@ -242,6 +244,7 @@ pub enum EExposes {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ERequires<'a> {
|
||||
Requires(Row, Col),
|
||||
Open(Row, Col),
|
||||
IndentRequires(Row, Col),
|
||||
IndentListStart(Row, Col),
|
||||
IndentListEnd(Row, Col),
|
||||
@ -302,6 +305,7 @@ pub enum EPackageEntry<'a> {
|
||||
pub enum EEffects<'a> {
|
||||
Space(BadInputError, Row, Col),
|
||||
Effects(Row, Col),
|
||||
Open(Row, Col),
|
||||
IndentEffects(Row, Col),
|
||||
ListStart(Row, Col),
|
||||
ListEnd(Row, Col),
|
||||
@ -315,6 +319,7 @@ pub enum EEffects<'a> {
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EImports {
|
||||
Open(Row, Col),
|
||||
Imports(Row, Col),
|
||||
IndentImports(Row, Col),
|
||||
IndentListStart(Row, Col),
|
||||
@ -1183,83 +1188,6 @@ macro_rules! collection {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! collection_e {
|
||||
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $space_problem:expr, $indent_problem:expr) => {
|
||||
skip_first!(
|
||||
$opening_brace,
|
||||
skip_first!(
|
||||
// We specifically allow space characters inside here, so that
|
||||
// `[ ]` can be successfully parsed as an empty list, and then
|
||||
// changed by the formatter back into `[]`.
|
||||
//
|
||||
// We don't allow newlines or comments in the middle of empty
|
||||
// roc_collections because those are normally stored in an Expr,
|
||||
// and there's no Expr in which to store them in an empty collection!
|
||||
//
|
||||
// We could change the AST to add extra storage specifically to
|
||||
// support empty literals containing newlines or comments, but this
|
||||
// does not seem worth even the tiniest regression in compiler performance.
|
||||
zero_or_more!($crate::parser::word1(b' ', |row, col| $space_problem(
|
||||
crate::parser::BadInputError::LineTooLong,
|
||||
row,
|
||||
col
|
||||
))),
|
||||
skip_second!(
|
||||
$crate::parser::sep_by0(
|
||||
$delimiter,
|
||||
$crate::blankspace::space0_around_ee(
|
||||
$elem,
|
||||
$min_indent,
|
||||
$space_problem,
|
||||
$indent_problem,
|
||||
$indent_problem
|
||||
)
|
||||
),
|
||||
$closing_brace
|
||||
)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Parse zero or more elements between two braces (e.g. square braces).
|
||||
/// Elements can be optionally surrounded by spaces, and are separated by a
|
||||
/// delimiter (e.g comma-separated) with optionally a trailing delimiter.
|
||||
/// Braces and delimiters get discarded.
|
||||
#[macro_export]
|
||||
macro_rules! collection_trailing_sep {
|
||||
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr) => {
|
||||
skip_first!(
|
||||
$opening_brace,
|
||||
skip_first!(
|
||||
// We specifically allow space characters inside here, so that
|
||||
// `[ ]` can be successfully parsed as an empty list, and then
|
||||
// changed by the formatter back into `[]`.
|
||||
//
|
||||
// We don't allow newlines or comments in the middle of empty
|
||||
// roc_collections because those are normally stored in an Expr,
|
||||
// and there's no Expr in which to store them in an empty collection!
|
||||
//
|
||||
// We could change the AST to add extra storage specifically to
|
||||
// support empty literals containing newlines or comments, but this
|
||||
// does not seem worth even the tiniest regression in compiler performance.
|
||||
zero_or_more!($crate::parser::ascii_char(b' ')),
|
||||
skip_second!(
|
||||
and!(
|
||||
$crate::parser::trailing_sep_by0(
|
||||
$delimiter,
|
||||
$crate::blankspace::space0_around($elem, $min_indent)
|
||||
),
|
||||
$crate::blankspace::space0($min_indent)
|
||||
),
|
||||
$closing_brace
|
||||
)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! collection_trailing_sep_e {
|
||||
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $space_problem:expr, $indent_problem:expr, $space_before:expr) => {
|
||||
@ -1300,10 +1228,10 @@ macro_rules! collection_trailing_sep_e {
|
||||
}
|
||||
}
|
||||
|
||||
let collection = $crate::ast::Collection {
|
||||
items: parsed_elems.into_bump_slice(),
|
||||
final_comments,
|
||||
};
|
||||
let collection = $crate::ast::Collection::with_items_and_comments(
|
||||
arena,
|
||||
parsed_elems.into_bump_slice(),
|
||||
final_comments);
|
||||
|
||||
Ok((MadeProgress, collection, state))
|
||||
}
|
||||
|
@ -331,10 +331,7 @@ fn record_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRec
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// TODO
|
||||
let _unused = fields.final_comments;
|
||||
|
||||
let result = Pattern::RecordDestructure(fields.items);
|
||||
let result = Pattern::RecordDestructure(fields);
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::ast::{AssignedField, Collection, Tag, TypeAnnotation};
|
||||
use crate::ast::{AssignedField, Tag, TypeAnnotation};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
@ -40,11 +40,7 @@ fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ET
|
||||
)))
|
||||
.parse(arena, state)?;
|
||||
|
||||
let result = TypeAnnotation::TagUnion {
|
||||
tags: tags.items,
|
||||
ext,
|
||||
final_comments: tags.final_comments,
|
||||
};
|
||||
let result = TypeAnnotation::TagUnion { tags, ext };
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
}
|
||||
@ -295,13 +291,7 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, EType
|
||||
let field_term = specialize_ref(ETypeRecord::Type, term(min_indent));
|
||||
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state)?;
|
||||
|
||||
let result = Record {
|
||||
fields: Collection {
|
||||
items: fields.items,
|
||||
final_comments: fields.final_comments,
|
||||
},
|
||||
ext,
|
||||
};
|
||||
let result = Record { fields, ext };
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
}
|
||||
|
@ -460,10 +460,7 @@ mod test_parse {
|
||||
#[test]
|
||||
fn empty_record() {
|
||||
let arena = Bump::new();
|
||||
let expected = Record {
|
||||
fields: &[],
|
||||
final_comments: &[],
|
||||
};
|
||||
let expected = Record(Collection::empty());
|
||||
let actual = parse_expr_with(&arena, "{}");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -493,8 +490,7 @@ mod test_parse {
|
||||
let update_target = Located::new(0, 0, 2, 13, var);
|
||||
let expected = RecordUpdate {
|
||||
update: &*arena.alloc(update_target),
|
||||
fields,
|
||||
final_comments: &(&[] as &[_]),
|
||||
fields: Collection::with_items(fields),
|
||||
};
|
||||
|
||||
let actual = parse_expr_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }");
|
||||
@ -526,10 +522,7 @@ mod test_parse {
|
||||
Located::new(0, 0, 1, 26, label1),
|
||||
Located::new(0, 0, 28, 32, label2),
|
||||
];
|
||||
let expected = Record {
|
||||
fields,
|
||||
final_comments: &[],
|
||||
};
|
||||
let expected = Record(Collection::with_items(fields));
|
||||
|
||||
let actual = parse_expr_with(&arena, "{x : if True then 1 else 2, y: 3 }");
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1173,10 +1166,7 @@ mod test_parse {
|
||||
#[test]
|
||||
fn empty_list() {
|
||||
let arena = Bump::new();
|
||||
let expected = List {
|
||||
items: &[],
|
||||
final_comments: &[],
|
||||
};
|
||||
let expected = List(Collection::empty());
|
||||
let actual = parse_expr_with(&arena, "[]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1186,10 +1176,7 @@ mod test_parse {
|
||||
fn spaces_inside_empty_list() {
|
||||
// This is a regression test!
|
||||
let arena = Bump::new();
|
||||
let expected = List {
|
||||
items: &[],
|
||||
final_comments: &[],
|
||||
};
|
||||
let expected = List(Collection::empty());
|
||||
let actual = parse_expr_with(&arena, "[ ]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1198,10 +1185,7 @@ mod test_parse {
|
||||
#[test]
|
||||
fn newline_inside_empty_list() {
|
||||
let arena = Bump::new();
|
||||
let expected = List {
|
||||
items: &[],
|
||||
final_comments: &[Newline],
|
||||
};
|
||||
let expected = List(Collection::with_items_and_comments(&arena, &[], &[Newline]));
|
||||
let actual = parse_expr_with(&arena, "[\n]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1210,10 +1194,11 @@ mod test_parse {
|
||||
#[test]
|
||||
fn comment_inside_empty_list() {
|
||||
let arena = Bump::new();
|
||||
let expected = List {
|
||||
items: &[],
|
||||
final_comments: &[LineComment("comment")],
|
||||
};
|
||||
let expected = List(Collection::with_items_and_comments(
|
||||
&arena,
|
||||
&[],
|
||||
&[LineComment("comment")],
|
||||
));
|
||||
let actual = parse_expr_with(&arena, "[#comment\n]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1223,10 +1208,7 @@ mod test_parse {
|
||||
fn packed_singleton_list() {
|
||||
let arena = Bump::new();
|
||||
let items = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
|
||||
let expected = List {
|
||||
items,
|
||||
final_comments: &[],
|
||||
};
|
||||
let expected = List(Collection::with_items(items));
|
||||
let actual = parse_expr_with(&arena, "[1]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1236,10 +1218,7 @@ mod test_parse {
|
||||
fn spaced_singleton_list() {
|
||||
let arena = Bump::new();
|
||||
let items = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
|
||||
let expected = List {
|
||||
items,
|
||||
final_comments: &[],
|
||||
};
|
||||
let expected = List(Collection::with_items(items));
|
||||
let actual = parse_expr_with(&arena, "[ 1 ]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1254,10 +1233,7 @@ mod test_parse {
|
||||
));
|
||||
let item = Expr::SpaceBefore(item, arena.alloc([Newline]));
|
||||
let items = [&*arena.alloc(Located::new(1, 1, 0, 1, item))];
|
||||
let expected = List {
|
||||
items: &items,
|
||||
final_comments: &[],
|
||||
};
|
||||
let expected = List(Collection::with_items(&items));
|
||||
let actual = parse_expr_with(&arena, "[\n1\n]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
@ -1897,7 +1873,13 @@ mod test_parse {
|
||||
Located::new(1, 1, 5, 7, Identifier("y"))
|
||||
];
|
||||
let def1 = Def::Body(
|
||||
arena.alloc(Located::new(1, 1, 0, 8, RecordDestructure(&fields))),
|
||||
arena.alloc(Located::new(
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
8,
|
||||
RecordDestructure(Collection::with_items(&fields)),
|
||||
)),
|
||||
arena.alloc(Located::new(1, 1, 11, 12, Num("5"))),
|
||||
);
|
||||
let loc_def1 = &*arena.alloc(Located::new(1, 1, 0, 12, def1));
|
||||
@ -2028,10 +2010,7 @@ mod test_parse {
|
||||
let inner_backpassing = {
|
||||
let identifier_z = Located::new(2, 2, 0, 1, Identifier("z"));
|
||||
|
||||
let empty_record = Record {
|
||||
fields: &[],
|
||||
final_comments: &[],
|
||||
};
|
||||
let empty_record = Record(Collection::empty());
|
||||
let loc_empty_record = Located::new(2, 2, 5, 7, empty_record);
|
||||
|
||||
let var_x = Var {
|
||||
@ -2115,8 +2094,8 @@ mod test_parse {
|
||||
let apply = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 8, 17, var_list_map2)),
|
||||
bumpalo::vec![ in &arena;
|
||||
&*arena.alloc(Located::new(0, 0, 18, 20, Expr::List{ items: &[], final_comments: &[] })),
|
||||
&*arena.alloc(Located::new(0, 0, 21, 23, Expr::List{ items: &[], final_comments: &[] })),
|
||||
&*arena.alloc(Located::new(0, 0, 18, 20, Expr::List(Collection::empty()))),
|
||||
&*arena.alloc(Located::new(0, 0, 21, 23, Expr::List(Collection::empty()))),
|
||||
]
|
||||
.into_bump_slice(),
|
||||
CalledVia::Space,
|
||||
@ -2950,7 +2929,10 @@ mod test_parse {
|
||||
let arena = Bump::new();
|
||||
let newlines = &[Newline];
|
||||
let identifiers1 = &[Located::new(1, 1, 3, 4, Identifier("y"))];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(RecordDestructure(identifiers1)), newlines);
|
||||
let pattern1 = Pattern::SpaceBefore(
|
||||
arena.alloc(RecordDestructure(Collection::with_items(identifiers1))),
|
||||
newlines,
|
||||
);
|
||||
let loc_pattern1 = Located::new(1, 1, 1, 6, pattern1);
|
||||
let expr1 = Num("2");
|
||||
let loc_expr1 = Located::new(1, 1, 10, 11, expr1);
|
||||
@ -2964,7 +2946,10 @@ mod test_parse {
|
||||
Located::new(2, 2, 3, 4, Identifier("z")),
|
||||
Located::new(2, 2, 6, 7, Identifier("w")),
|
||||
];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(RecordDestructure(identifiers2)), newlines);
|
||||
let pattern2 = Pattern::SpaceBefore(
|
||||
arena.alloc(RecordDestructure(Collection::with_items(identifiers2))),
|
||||
newlines,
|
||||
);
|
||||
let loc_pattern2 = Located::new(2, 2, 1, 9, pattern2);
|
||||
let expr2 = Num("4");
|
||||
let loc_expr2 = Located::new(2, 2, 13, 14, expr2);
|
||||
@ -3092,8 +3077,8 @@ mod test_parse {
|
||||
fn empty_app_header() {
|
||||
let arena = Bump::new();
|
||||
let packages = Collection::empty();
|
||||
let imports = Vec::new_in(&arena);
|
||||
let provides = Vec::new_in(&arena);
|
||||
let imports = Collection::empty();
|
||||
let provides = Collection::empty();
|
||||
let module_name = StrLiteral::PlainLine("test-app");
|
||||
let header = AppHeader {
|
||||
name: Located::new(0, 0, 4, 14, module_name),
|
||||
@ -3132,8 +3117,8 @@ mod test_parse {
|
||||
|
||||
let arena = Bump::new();
|
||||
let packages = Collection::empty();
|
||||
let imports = Vec::new_in(&arena);
|
||||
let provides = Vec::new_in(&arena);
|
||||
let imports = Collection::empty();
|
||||
let provides = Collection::empty();
|
||||
let module_name = StrLiteral::PlainLine("test-app");
|
||||
let header = AppHeader {
|
||||
before_header: &[],
|
||||
@ -3181,11 +3166,11 @@ mod test_parse {
|
||||
let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry);
|
||||
let arena = Bump::new();
|
||||
let packages = Collection::with_items(arena.alloc([loc_pkg_entry]));
|
||||
let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Vec::new_in(&arena));
|
||||
let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Collection::empty());
|
||||
let loc_import = Located::new(2, 2, 14, 25, import);
|
||||
let imports = bumpalo::vec![in &arena; loc_import];
|
||||
let imports = Collection::with_items(arena.alloc([loc_import]));
|
||||
let provide_entry = Located::new(3, 3, 15, 24, Exposed("quicksort"));
|
||||
let provides = bumpalo::vec![in &arena; provide_entry];
|
||||
let provides = Collection::with_items(arena.alloc([provide_entry]));
|
||||
let module_name = StrLiteral::PlainLine("quicksort");
|
||||
|
||||
let header = AppHeader {
|
||||
@ -3237,11 +3222,40 @@ mod test_parse {
|
||||
let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry);
|
||||
let arena = Bump::new();
|
||||
let packages = Collection::with_items(arena.alloc([loc_pkg_entry]));
|
||||
let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Vec::new_in(&arena));
|
||||
let loc_import = Located::new(2, 2, 14, 25, import);
|
||||
let imports = bumpalo::vec![in &arena; loc_import];
|
||||
let provide_entry = Located::new(3, 3, 15, 24, Exposed("quicksort"));
|
||||
let provides = bumpalo::vec![in &arena; provide_entry];
|
||||
let import = ImportsEntry::Package(
|
||||
"foo",
|
||||
ModuleName::new("Bar"),
|
||||
Collection::with_items_and_comments(
|
||||
&arena,
|
||||
arena.alloc([
|
||||
Located::new(
|
||||
3,
|
||||
3,
|
||||
8,
|
||||
11,
|
||||
ExposesEntry::SpaceBefore(
|
||||
arena.alloc(ExposesEntry::Exposed("Baz")),
|
||||
arena.alloc([Newline]),
|
||||
),
|
||||
),
|
||||
Located::new(
|
||||
4,
|
||||
4,
|
||||
8,
|
||||
17,
|
||||
ExposesEntry::SpaceBefore(
|
||||
arena.alloc(ExposesEntry::Exposed("FourtyTwo")),
|
||||
arena.alloc([Newline]),
|
||||
),
|
||||
),
|
||||
]),
|
||||
arena.alloc([Newline, LineComment(" I'm a happy comment")]),
|
||||
),
|
||||
);
|
||||
let loc_import = Located::new(2, 6, 14, 5, import);
|
||||
let imports = Collection::with_items(arena.alloc([loc_import]));
|
||||
let provide_entry = Located::new(7, 7, 15, 24, Exposed("quicksort"));
|
||||
let provides = Collection::with_items(arena.alloc([provide_entry]));
|
||||
let module_name = StrLiteral::PlainLine("quicksort");
|
||||
|
||||
let header = AppHeader {
|
||||
@ -3250,7 +3264,7 @@ mod test_parse {
|
||||
packages,
|
||||
imports,
|
||||
provides,
|
||||
to: Located::new(3, 3, 30, 34, To::ExistingPackage("base")),
|
||||
to: Located::new(7, 7, 31, 35, To::ExistingPackage("base")),
|
||||
after_app_keyword: &[],
|
||||
before_packages: newlines,
|
||||
after_packages: &[],
|
||||
@ -3268,8 +3282,12 @@ mod test_parse {
|
||||
r#"
|
||||
app "quicksort"
|
||||
packages { base: "./platform", }
|
||||
imports [ foo.Bar.Baz ]
|
||||
provides [ quicksort ] to base
|
||||
imports [ foo.Bar.{
|
||||
Baz,
|
||||
FourtyTwo,
|
||||
# I'm a happy comment
|
||||
} ]
|
||||
provides [ quicksort, ] to base
|
||||
"#
|
||||
);
|
||||
|
||||
@ -3300,7 +3318,7 @@ mod test_parse {
|
||||
let region2 = Region::new(0, 0, 45, 47);
|
||||
|
||||
PlatformRequires {
|
||||
rigids: Vec::new_in(&arena),
|
||||
rigids: Collection::empty(),
|
||||
signature: Located::at(
|
||||
region1,
|
||||
TypedIdent::Entry {
|
||||
@ -3322,10 +3340,10 @@ mod test_parse {
|
||||
before_header: &[],
|
||||
name: Located::new(0, 0, 9, 23, pkg_name),
|
||||
requires,
|
||||
exposes: Vec::new_in(&arena),
|
||||
exposes: Collection::empty(),
|
||||
packages: Collection::empty(),
|
||||
imports: Vec::new_in(&arena),
|
||||
provides: Vec::new_in(&arena),
|
||||
imports: Collection::empty(),
|
||||
provides: Collection::empty(),
|
||||
effects,
|
||||
after_platform_keyword: &[],
|
||||
before_requires: &[],
|
||||
@ -3367,9 +3385,9 @@ mod test_parse {
|
||||
let loc_pkg_entry = Located::new(3, 3, 15, 27, pkg_entry);
|
||||
let arena = Bump::new();
|
||||
let packages = Collection::with_items(arena.alloc([loc_pkg_entry]));
|
||||
let imports = Vec::new_in(&arena);
|
||||
let imports = Collection::empty();
|
||||
let provide_entry = Located::new(5, 5, 15, 26, Exposed("mainForHost"));
|
||||
let provides = bumpalo::vec![in &arena; provide_entry];
|
||||
let provides = Collection::with_items(arena.alloc([provide_entry]));
|
||||
let effects = Effects {
|
||||
effect_type_name: "Effect",
|
||||
effect_shortname: "fx",
|
||||
@ -3385,7 +3403,13 @@ mod test_parse {
|
||||
let region3 = Region::new(1, 1, 14, 26);
|
||||
|
||||
PlatformRequires {
|
||||
rigids: bumpalo::vec![ in &arena; Located::at(region3, PlatformRigid::Entry { alias: "Model", rigid: "model" }) ],
|
||||
rigids: Collection::with_items(arena.alloc([Located::at(
|
||||
region3,
|
||||
PlatformRigid::Entry {
|
||||
alias: "Model",
|
||||
rigid: "model",
|
||||
},
|
||||
)])),
|
||||
signature: Located::at(
|
||||
region1,
|
||||
TypedIdent::Entry {
|
||||
@ -3407,7 +3431,7 @@ mod test_parse {
|
||||
before_header: &[],
|
||||
name: Located::new(0, 0, 9, 19, pkg_name),
|
||||
requires,
|
||||
exposes: Vec::new_in(&arena),
|
||||
exposes: Collection::empty(),
|
||||
packages,
|
||||
imports,
|
||||
provides,
|
||||
@ -3447,8 +3471,8 @@ mod test_parse {
|
||||
#[test]
|
||||
fn empty_interface_header() {
|
||||
let arena = Bump::new();
|
||||
let exposes = Vec::new_in(&arena);
|
||||
let imports = Vec::new_in(&arena);
|
||||
let exposes = Collection::empty();
|
||||
let imports = Collection::empty();
|
||||
let module_name = ModuleName::new("Foo");
|
||||
let header = InterfaceHeader {
|
||||
before_header: &[],
|
||||
@ -3479,8 +3503,8 @@ mod test_parse {
|
||||
#[test]
|
||||
fn nested_module() {
|
||||
let arena = Bump::new();
|
||||
let exposes = Vec::new_in(&arena);
|
||||
let imports = Vec::new_in(&arena);
|
||||
let exposes = Collection::empty();
|
||||
let imports = Collection::empty();
|
||||
let module_name = ModuleName::new("Foo.Bar.Baz");
|
||||
let header = InterfaceHeader {
|
||||
before_header: &[],
|
||||
|
@ -3093,6 +3093,7 @@ fn to_provides_report<'a>(
|
||||
use roc_parse::parser::EProvides;
|
||||
|
||||
match *parse_problem {
|
||||
EProvides::ListEnd(row, col) | // TODO: give this its own error message
|
||||
EProvides::Identifier(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
@ -3158,6 +3159,7 @@ fn to_exposes_report<'a>(
|
||||
use roc_parse::parser::EExposes;
|
||||
|
||||
match *parse_problem {
|
||||
EExposes::ListEnd(row, col) | // TODO: give this its own error message
|
||||
EExposes::Identifier(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
@ -385,7 +385,7 @@ fn gen_basic_def() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_multiple_defs() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -11,7 +11,7 @@ use crate::helpers::wasm::assert_evals_to;
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn basic_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -45,7 +45,7 @@ fn basic_record() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn f64_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -137,7 +137,7 @@ fn fn_record() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn def_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -192,7 +192,7 @@ fn when_on_record() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn when_record_with_guard_pattern() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -207,7 +207,7 @@ fn when_record_with_guard_pattern() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn let_with_record_pattern() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -223,7 +223,7 @@ fn let_with_record_pattern() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn record_guard_pattern() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -239,7 +239,7 @@ fn record_guard_pattern() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn twice_record_access() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -254,7 +254,7 @@ fn twice_record_access() {
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-dev"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
fn empty_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -873,7 +873,7 @@ fn update_single_element_record() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn booleans_in_record() {
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 == 1, y: 1 == 1 }"),
|
||||
@ -908,7 +908,7 @@ fn alignment_in_record() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn blue_and_present() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -927,7 +927,7 @@ fn blue_and_present() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn blue_and_absent() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1244,3 +1244,96 @@ fn str_trim_left_small_to_small_shared() {
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_small_blank_string() {
|
||||
assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_small_to_small() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trimRight " hello world ""#),
|
||||
RocStr::from(" hello world"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_large_unique() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trimRight (Str.concat " hello world from a large string" " ")"#),
|
||||
RocStr::from(" hello world from a large string"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_small_unique() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trimRight (Str.concat " hello world" " ")"#),
|
||||
RocStr::from(" hello world"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_large_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world world "
|
||||
|
||||
{ trimmed: Str.trimRight original, original: original }
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from(" hello world world "),
|
||||
RocStr::from(" hello world world"),
|
||||
),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_small_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world "
|
||||
|
||||
{ trimmed: Str.trimRight original, original: original }
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from(" hello world "),
|
||||
RocStr::from(" hello world"),
|
||||
),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_small_to_small_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world "
|
||||
|
||||
{ trimmed: Str.trimRight original, original: original }
|
||||
"#
|
||||
),
|
||||
(RocStr::from(" hello world "), RocStr::from(" hello world"),),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
@ -453,7 +453,7 @@ fn result_with_guard_pattern() {
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn maybe_is_just() {
|
||||
fn maybe_is_just_not_nested() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -198,6 +198,10 @@ fn create_llvm_module<'a>(
|
||||
if name.starts_with("roc_builtins.dict") {
|
||||
function.add_attribute(AttributeLoc::Function, attr);
|
||||
}
|
||||
|
||||
if name.starts_with("roc_builtins.list") {
|
||||
function.add_attribute(AttributeLoc::Function, attr);
|
||||
}
|
||||
}
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
|
Loading…
Reference in New Issue
Block a user