mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge remote-tracking branch 'origin/trunk' into recursive-tag-segfault
This commit is contained in:
commit
c11d97f3e7
25 changed files with 577 additions and 192 deletions
|
@ -296,7 +296,7 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut layout_ids = roc_gen::layout_id::LayoutIds::default();
|
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||||
let mut headers = Vec::with_capacity(procedures.len());
|
let mut headers = Vec::with_capacity(procedures.len());
|
||||||
|
|
||||||
// Add all the Proc headers to the module.
|
// Add all the Proc headers to the module.
|
||||||
|
|
|
@ -261,6 +261,13 @@ mod repl_eval {
|
||||||
expect_success("List.contains [ 1, 2, 3 ] 4", "False : Bool");
|
expect_success("List.contains [ 1, 2, 3 ] 4", "False : Bool");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_sum() {
|
||||||
|
expect_success("List.sum []", "0 : Num *");
|
||||||
|
expect_success("List.sum [ 1, 2, 3 ]", "6 : Num *");
|
||||||
|
expect_success("List.sum [ 1.1, 2.2, 3.3 ]", "6.6 : Float");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_record() {
|
fn empty_record() {
|
||||||
expect_success("{}", "{} : {}");
|
expect_success("{}", "{} : {}");
|
||||||
|
|
|
@ -263,6 +263,7 @@ fn link_macos(
|
||||||
.args(&[
|
.args(&[
|
||||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
||||||
// for discussion and further references
|
// for discussion and further references
|
||||||
|
"-L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib",
|
||||||
"-lSystem",
|
"-lSystem",
|
||||||
"-lresolv",
|
"-lresolv",
|
||||||
"-lpthread",
|
"-lpthread",
|
||||||
|
|
|
@ -2,9 +2,9 @@ use crate::target;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||||
use roc_gen::layout_id::LayoutIds;
|
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope};
|
use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope};
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
|
use roc_mono::layout::LayoutIds;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
|
|
|
@ -474,6 +474,15 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// sum : List (Num a) -> Num a
|
||||||
|
add_type(
|
||||||
|
Symbol::LIST_SUM,
|
||||||
|
top_level_function(
|
||||||
|
vec![list_type(num_type(flex(TVAR1)))],
|
||||||
|
Box::new(num_type(flex(TVAR1))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_WALK_RIGHT,
|
Symbol::LIST_WALK_RIGHT,
|
||||||
|
|
|
@ -683,6 +683,23 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
unique_function(vec![list_type(star1, a), flex(a)], bool_type(star2))
|
unique_function(vec![list_type(star1, a), flex(a)], bool_type(star2))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// sum : Attr * (List (Attr u (Num (Attr u num))))
|
||||||
|
// -> Attr v (Num (Attr v num))
|
||||||
|
add_type(Symbol::LIST_SUM, {
|
||||||
|
let_tvars! { star1, u, v, num };
|
||||||
|
|
||||||
|
unique_function(
|
||||||
|
vec![SolvedType::Apply(
|
||||||
|
Symbol::ATTR_ATTR,
|
||||||
|
vec![
|
||||||
|
flex(star1),
|
||||||
|
SolvedType::Apply(Symbol::LIST_LIST, vec![num_type(u, num)]),
|
||||||
|
],
|
||||||
|
)],
|
||||||
|
num_type(v, num),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// join : Attr * (List (Attr * (List a)))
|
// join : Attr * (List (Attr * (List a)))
|
||||||
// -> Attr * (List a)
|
// -> Attr * (List a)
|
||||||
add_type(Symbol::LIST_JOIN, {
|
add_type(Symbol::LIST_JOIN, {
|
||||||
|
|
|
@ -65,6 +65,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::LIST_REVERSE => list_reverse,
|
Symbol::LIST_REVERSE => list_reverse,
|
||||||
Symbol::LIST_CONCAT => list_concat,
|
Symbol::LIST_CONCAT => list_concat,
|
||||||
Symbol::LIST_CONTAINS => list_contains,
|
Symbol::LIST_CONTAINS => list_contains,
|
||||||
|
Symbol::LIST_SUM => list_sum,
|
||||||
Symbol::LIST_PREPEND => list_prepend,
|
Symbol::LIST_PREPEND => list_prepend,
|
||||||
Symbol::LIST_JOIN => list_join,
|
Symbol::LIST_JOIN => list_join,
|
||||||
Symbol::LIST_MAP => list_map,
|
Symbol::LIST_MAP => list_map,
|
||||||
|
@ -1320,6 +1321,26 @@ fn list_walk_right(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.sum : List (Num a) -> Num a
|
||||||
|
fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let list_var = var_store.fresh();
|
||||||
|
let result_var = var_store.fresh();
|
||||||
|
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op: LowLevel::ListSum,
|
||||||
|
args: vec![(list_var, Var(Symbol::ARG_1))],
|
||||||
|
ret_var: result_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(list_var, Symbol::ARG_1)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
result_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.keepIf : List elem, (elem -> Bool) -> List elem
|
/// List.keepIf : List elem, (elem -> Bool) -> List elem
|
||||||
fn list_keep_if(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_keep_if(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let list_var = var_store.fresh();
|
let list_var = var_store.fresh();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::spaces::{fmt_comments_only, fmt_condition_spaces, fmt_spaces, newline, INDENT};
|
use crate::spaces::{fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
|
||||||
use bumpalo::collections::String;
|
use bumpalo::collections::String;
|
||||||
use roc_parse::ast::{AssignedField, Expr, Tag, TypeAnnotation};
|
use roc_parse::ast::{AssignedField, Expr, Tag, TypeAnnotation};
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
|
@ -333,7 +333,7 @@ fn format_assigned_field_help<'a, T>(
|
||||||
buf.push_str(name.value);
|
buf.push_str(name.value);
|
||||||
}
|
}
|
||||||
AssignedField::SpaceBefore(sub_field, spaces) => {
|
AssignedField::SpaceBefore(sub_field, spaces) => {
|
||||||
fmt_comments_only(buf, spaces.iter(), indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
format_assigned_field_help(
|
format_assigned_field_help(
|
||||||
sub_field,
|
sub_field,
|
||||||
buf,
|
buf,
|
||||||
|
@ -352,7 +352,7 @@ fn format_assigned_field_help<'a, T>(
|
||||||
separator_prefix,
|
separator_prefix,
|
||||||
is_multiline,
|
is_multiline,
|
||||||
);
|
);
|
||||||
fmt_comments_only(buf, spaces.iter(), indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
}
|
}
|
||||||
Malformed(raw) => {
|
Malformed(raw) => {
|
||||||
buf.push_str(raw);
|
buf.push_str(raw);
|
||||||
|
@ -448,7 +448,12 @@ macro_rules! implement_format_sequence {
|
||||||
match &item.value {
|
match &item.value {
|
||||||
$t::SpaceBefore(expr_below, spaces_above_expr) => {
|
$t::SpaceBefore(expr_below, spaces_above_expr) => {
|
||||||
newline(buf, item_indent);
|
newline(buf, item_indent);
|
||||||
fmt_comments_only(buf, spaces_above_expr.iter(), item_indent);
|
fmt_comments_only(
|
||||||
|
buf,
|
||||||
|
spaces_above_expr.iter(),
|
||||||
|
NewlineAt::Bottom,
|
||||||
|
item_indent,
|
||||||
|
);
|
||||||
|
|
||||||
match &expr_below {
|
match &expr_below {
|
||||||
$t::SpaceAfter(expr_above, spaces_below_expr) => {
|
$t::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||||
|
@ -458,9 +463,10 @@ macro_rules! implement_format_sequence {
|
||||||
buf.push(',');
|
buf.push(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt_condition_spaces(
|
fmt_comments_only(
|
||||||
buf,
|
buf,
|
||||||
spaces_below_expr.iter(),
|
spaces_below_expr.iter(),
|
||||||
|
NewlineAt::Top,
|
||||||
item_indent,
|
item_indent,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -481,7 +487,7 @@ macro_rules! implement_format_sequence {
|
||||||
buf.push(',');
|
buf.push(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt_condition_spaces(buf, spaces.iter(), item_indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, item_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::annotation::{Formattable, Newlines, Parens};
|
use crate::annotation::{Formattable, Newlines, Parens};
|
||||||
use crate::pattern::fmt_pattern;
|
use crate::pattern::fmt_pattern;
|
||||||
use crate::spaces::{fmt_spaces, is_comment, newline, INDENT};
|
use crate::spaces::{fmt_spaces, newline, INDENT};
|
||||||
use bumpalo::collections::String;
|
use bumpalo::collections::String;
|
||||||
use roc_parse::ast::{Def, Expr, Pattern};
|
use roc_parse::ast::{Def, Expr, Pattern};
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ impl<'a> Formattable<'a> for Def<'a> {
|
||||||
Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(),
|
Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(),
|
||||||
AnnotatedBody { .. } => true,
|
AnnotatedBody { .. } => true,
|
||||||
SpaceBefore(sub_def, spaces) | SpaceAfter(sub_def, spaces) => {
|
SpaceBefore(sub_def, spaces) | SpaceAfter(sub_def, spaces) => {
|
||||||
spaces.iter().any(|s| is_comment(s)) || sub_def.is_multiline()
|
spaces.iter().any(|s| s.is_comment()) || sub_def.is_multiline()
|
||||||
}
|
}
|
||||||
Nested(def) => def.is_multiline(),
|
Nested(def) => def.is_multiline(),
|
||||||
NotYetImplemented(s) => todo!("{}", s),
|
NotYetImplemented(s) => todo!("{}", s),
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::annotation::{Formattable, Newlines, Parens};
|
use crate::annotation::{Formattable, Newlines, Parens};
|
||||||
use crate::def::fmt_def;
|
use crate::def::fmt_def;
|
||||||
use crate::pattern::fmt_pattern;
|
use crate::pattern::fmt_pattern;
|
||||||
use crate::spaces::{
|
use crate::spaces::{add_spaces, fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
|
||||||
add_spaces, fmt_comments_only, fmt_condition_spaces, fmt_spaces, newline, INDENT,
|
|
||||||
};
|
|
||||||
use bumpalo::collections::String;
|
use bumpalo::collections::String;
|
||||||
use roc_module::operator::{self, BinOp};
|
use roc_module::operator::{self, BinOp};
|
||||||
use roc_parse::ast::StrSegment;
|
use roc_parse::ast::StrSegment;
|
||||||
|
@ -108,7 +106,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||||
if format_newlines {
|
if format_newlines {
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
} else {
|
} else {
|
||||||
fmt_comments_only(buf, spaces.iter(), indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
}
|
}
|
||||||
sub_expr.format_with_options(buf, parens, newlines, indent);
|
sub_expr.format_with_options(buf, parens, newlines, indent);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +115,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||||
if format_newlines {
|
if format_newlines {
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
} else {
|
} else {
|
||||||
fmt_comments_only(buf, spaces.iter(), indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParensAround(sub_expr) => {
|
ParensAround(sub_expr) => {
|
||||||
|
@ -416,7 +414,12 @@ pub fn fmt_list<'a>(buf: &mut String<'a>, loc_items: &[&Located<Expr<'a>>], inde
|
||||||
match &item.value {
|
match &item.value {
|
||||||
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
||||||
newline(buf, item_indent);
|
newline(buf, item_indent);
|
||||||
fmt_comments_only(buf, spaces_above_expr.iter(), item_indent);
|
fmt_comments_only(
|
||||||
|
buf,
|
||||||
|
spaces_above_expr.iter(),
|
||||||
|
NewlineAt::Bottom,
|
||||||
|
item_indent,
|
||||||
|
);
|
||||||
|
|
||||||
match &expr_below {
|
match &expr_below {
|
||||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||||
|
@ -426,7 +429,12 @@ pub fn fmt_list<'a>(buf: &mut String<'a>, loc_items: &[&Located<Expr<'a>>], inde
|
||||||
buf.push(',');
|
buf.push(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt_condition_spaces(buf, spaces_below_expr.iter(), item_indent);
|
fmt_comments_only(
|
||||||
|
buf,
|
||||||
|
spaces_below_expr.iter(),
|
||||||
|
NewlineAt::Top,
|
||||||
|
item_indent,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
expr_below.format(buf, item_indent);
|
expr_below.format(buf, item_indent);
|
||||||
|
@ -446,7 +454,7 @@ pub fn fmt_list<'a>(buf: &mut String<'a>, loc_items: &[&Located<Expr<'a>>], inde
|
||||||
buf.push(',');
|
buf.push(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt_condition_spaces(buf, spaces.iter(), item_indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, item_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -521,12 +529,22 @@ fn fmt_when<'a>(
|
||||||
|
|
||||||
match &loc_condition.value {
|
match &loc_condition.value {
|
||||||
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
||||||
fmt_condition_spaces(buf, spaces_above_expr.iter(), condition_indent);
|
fmt_comments_only(
|
||||||
|
buf,
|
||||||
|
spaces_above_expr.iter(),
|
||||||
|
NewlineAt::Top,
|
||||||
|
condition_indent,
|
||||||
|
);
|
||||||
newline(buf, condition_indent);
|
newline(buf, condition_indent);
|
||||||
match &expr_below {
|
match &expr_below {
|
||||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||||
expr_above.format(buf, condition_indent);
|
expr_above.format(buf, condition_indent);
|
||||||
fmt_condition_spaces(buf, spaces_below_expr.iter(), condition_indent);
|
fmt_comments_only(
|
||||||
|
buf,
|
||||||
|
spaces_below_expr.iter(),
|
||||||
|
NewlineAt::Top,
|
||||||
|
condition_indent,
|
||||||
|
);
|
||||||
newline(buf, indent);
|
newline(buf, indent);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -585,7 +603,7 @@ fn fmt_when<'a>(
|
||||||
add_spaces(buf, indent + (INDENT * 2));
|
add_spaces(buf, indent + (INDENT * 2));
|
||||||
match expr.value {
|
match expr.value {
|
||||||
Expr::SpaceBefore(nested, spaces) => {
|
Expr::SpaceBefore(nested, spaces) => {
|
||||||
fmt_comments_only(buf, spaces.iter(), indent + (INDENT * 2));
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent + (INDENT * 2));
|
||||||
nested.format_with_options(
|
nested.format_with_options(
|
||||||
buf,
|
buf,
|
||||||
Parens::NotNeeded,
|
Parens::NotNeeded,
|
||||||
|
@ -633,13 +651,18 @@ fn fmt_if<'a>(
|
||||||
if is_multiline_condition {
|
if is_multiline_condition {
|
||||||
match &loc_condition.value {
|
match &loc_condition.value {
|
||||||
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
||||||
fmt_condition_spaces(buf, spaces_above_expr.iter(), return_indent);
|
fmt_comments_only(buf, spaces_above_expr.iter(), NewlineAt::Top, return_indent);
|
||||||
newline(buf, return_indent);
|
newline(buf, return_indent);
|
||||||
|
|
||||||
match &expr_below {
|
match &expr_below {
|
||||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||||
expr_above.format(buf, return_indent);
|
expr_above.format(buf, return_indent);
|
||||||
fmt_condition_spaces(buf, spaces_below_expr.iter(), return_indent);
|
fmt_comments_only(
|
||||||
|
buf,
|
||||||
|
spaces_below_expr.iter(),
|
||||||
|
NewlineAt::Top,
|
||||||
|
return_indent,
|
||||||
|
);
|
||||||
newline(buf, indent);
|
newline(buf, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,7 +675,7 @@ fn fmt_if<'a>(
|
||||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||||
newline(buf, return_indent);
|
newline(buf, return_indent);
|
||||||
expr_above.format(buf, return_indent);
|
expr_above.format(buf, return_indent);
|
||||||
fmt_condition_spaces(buf, spaces_below_expr.iter(), return_indent);
|
fmt_comments_only(buf, spaces_below_expr.iter(), NewlineAt::Top, return_indent);
|
||||||
newline(buf, indent);
|
newline(buf, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,13 +698,13 @@ fn fmt_if<'a>(
|
||||||
Expr::SpaceBefore(expr_below, spaces_below) => {
|
Expr::SpaceBefore(expr_below, spaces_below) => {
|
||||||
// we want exactly one newline, user-inserted extra newlines are ignored.
|
// we want exactly one newline, user-inserted extra newlines are ignored.
|
||||||
newline(buf, return_indent);
|
newline(buf, return_indent);
|
||||||
fmt_comments_only(buf, spaces_below.iter(), return_indent);
|
fmt_comments_only(buf, spaces_below.iter(), NewlineAt::Bottom, return_indent);
|
||||||
|
|
||||||
match &expr_below {
|
match &expr_below {
|
||||||
Expr::SpaceAfter(expr_above, spaces_above) => {
|
Expr::SpaceAfter(expr_above, spaces_above) => {
|
||||||
expr_above.format(buf, return_indent);
|
expr_above.format(buf, return_indent);
|
||||||
|
|
||||||
fmt_condition_spaces(buf, spaces_above.iter(), return_indent);
|
fmt_comments_only(buf, spaces_above.iter(), NewlineAt::Top, return_indent);
|
||||||
newline(buf, indent);
|
newline(buf, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -783,7 +806,7 @@ pub fn fmt_record<'a>(
|
||||||
buf: &mut String<'a>,
|
buf: &mut String<'a>,
|
||||||
update: Option<&'a Located<Expr<'a>>>,
|
update: Option<&'a Located<Expr<'a>>>,
|
||||||
loc_fields: &[Located<AssignedField<'a, Expr<'a>>>],
|
loc_fields: &[Located<AssignedField<'a, Expr<'a>>>],
|
||||||
final_comments: &[CommentOrNewline<'a>],
|
final_comments: &'a [CommentOrNewline<'a>],
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) {
|
) {
|
||||||
if loc_fields.is_empty() {
|
if loc_fields.is_empty() {
|
||||||
|
@ -810,12 +833,14 @@ pub fn fmt_record<'a>(
|
||||||
if is_multiline {
|
if is_multiline {
|
||||||
let field_indent = indent + INDENT;
|
let field_indent = indent + INDENT;
|
||||||
for field in loc_fields.iter() {
|
for field in loc_fields.iter() {
|
||||||
field.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, field_indent);
|
format_field_multiline(buf, &field.value, field_indent, "");
|
||||||
buf.push(',');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt_comments_only(buf, final_comments.iter(), NewlineAt::Top, field_indent);
|
||||||
|
|
||||||
newline(buf, indent);
|
newline(buf, indent);
|
||||||
} else {
|
} else {
|
||||||
// is_multiline == false
|
// is_multiline == false */
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
let field_indent = indent;
|
let field_indent = indent;
|
||||||
let mut iter = loc_fields.iter().peekable();
|
let mut iter = loc_fields.iter().peekable();
|
||||||
|
@ -827,7 +852,67 @@ pub fn fmt_record<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
|
// if we are here, that means that `final_comments` is empty, thus we don't have
|
||||||
|
// to add a comment. Anyway, it is not possible to have a single line record with
|
||||||
|
// a comment in it.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// closes the initial bracket
|
||||||
buf.push('}');
|
buf.push('}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_field_multiline<'a, T>(
|
||||||
|
buf: &mut String<'a>,
|
||||||
|
field: &AssignedField<'a, T>,
|
||||||
|
indent: u16,
|
||||||
|
separator_prefix: &str,
|
||||||
|
) where
|
||||||
|
T: Formattable<'a>,
|
||||||
|
{
|
||||||
|
use self::AssignedField::*;
|
||||||
|
match field {
|
||||||
|
RequiredValue(name, spaces, ann) => {
|
||||||
|
newline(buf, indent);
|
||||||
|
buf.push_str(name.value);
|
||||||
|
|
||||||
|
if !spaces.is_empty() {
|
||||||
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str(separator_prefix);
|
||||||
|
buf.push_str(": ");
|
||||||
|
ann.value.format(buf, indent);
|
||||||
|
buf.push(',');
|
||||||
|
}
|
||||||
|
OptionalValue(name, spaces, ann) => {
|
||||||
|
newline(buf, indent);
|
||||||
|
buf.push_str(name.value);
|
||||||
|
|
||||||
|
if !spaces.is_empty() {
|
||||||
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str(separator_prefix);
|
||||||
|
buf.push('?');
|
||||||
|
ann.value.format(buf, indent);
|
||||||
|
buf.push(',');
|
||||||
|
}
|
||||||
|
LabelOnly(name) => {
|
||||||
|
newline(buf, indent);
|
||||||
|
buf.push_str(name.value);
|
||||||
|
buf.push(',');
|
||||||
|
}
|
||||||
|
AssignedField::SpaceBefore(sub_field, spaces) => {
|
||||||
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, indent);
|
||||||
|
format_field_multiline(buf, sub_field, indent, separator_prefix);
|
||||||
|
}
|
||||||
|
AssignedField::SpaceAfter(sub_field, spaces) => {
|
||||||
|
format_field_multiline(buf, sub_field, indent, separator_prefix);
|
||||||
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, indent);
|
||||||
|
}
|
||||||
|
Malformed(raw) => {
|
||||||
|
buf.push_str(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::annotation::{Formattable, Newlines, Parens};
|
use crate::annotation::{Formattable, Newlines, Parens};
|
||||||
use crate::spaces::{fmt_comments_only, fmt_spaces, is_comment};
|
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt};
|
||||||
use bumpalo::collections::String;
|
use bumpalo::collections::String;
|
||||||
use roc_parse::ast::{Base, Pattern};
|
use roc_parse::ast::{Base, Pattern};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ impl<'a> Formattable<'a> for Pattern<'a> {
|
||||||
Pattern::SpaceBefore(_, spaces) | Pattern::SpaceAfter(_, spaces) => {
|
Pattern::SpaceBefore(_, spaces) | Pattern::SpaceAfter(_, spaces) => {
|
||||||
debug_assert!(!spaces.is_empty());
|
debug_assert!(!spaces.is_empty());
|
||||||
|
|
||||||
spaces.iter().any(|s| is_comment(s))
|
spaces.iter().any(|s| s.is_comment())
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern::Nested(nested_pat) => nested_pat.is_multiline(),
|
Pattern::Nested(nested_pat) => nested_pat.is_multiline(),
|
||||||
|
@ -133,7 +133,7 @@ impl<'a> Formattable<'a> for Pattern<'a> {
|
||||||
// Space
|
// Space
|
||||||
SpaceBefore(sub_pattern, spaces) => {
|
SpaceBefore(sub_pattern, spaces) => {
|
||||||
if !sub_pattern.is_multiline() {
|
if !sub_pattern.is_multiline() {
|
||||||
fmt_comments_only(buf, spaces.iter(), indent)
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent)
|
||||||
} else {
|
} else {
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ impl<'a> Formattable<'a> for Pattern<'a> {
|
||||||
sub_pattern.format_with_options(buf, parens, newlines, indent);
|
sub_pattern.format_with_options(buf, parens, newlines, indent);
|
||||||
// if only_comments {
|
// if only_comments {
|
||||||
if !sub_pattern.is_multiline() {
|
if !sub_pattern.is_multiline() {
|
||||||
fmt_comments_only(buf, spaces.iter(), indent)
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent)
|
||||||
} else {
|
} else {
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,12 +45,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LineComment(comment) => {
|
LineComment(comment) => {
|
||||||
fmt_comment(buf, comment, indent);
|
fmt_comment(buf, comment);
|
||||||
|
newline(buf, indent);
|
||||||
|
|
||||||
encountered_comment = true;
|
encountered_comment = true;
|
||||||
}
|
}
|
||||||
DocComment(docs) => {
|
DocComment(docs) => {
|
||||||
fmt_docs(buf, docs, indent);
|
fmt_docs(buf, docs);
|
||||||
|
newline(buf, indent);
|
||||||
|
|
||||||
encountered_comment = true;
|
encountered_comment = true;
|
||||||
}
|
}
|
||||||
|
@ -58,78 +60,67 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to fmt_comments_only, but does not finish with a newline()
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
/// Used to format when and if statement conditions
|
pub enum NewlineAt {
|
||||||
pub fn fmt_condition_spaces<'a, I>(buf: &mut String<'a>, spaces: I, indent: u16)
|
Top,
|
||||||
where
|
Bottom,
|
||||||
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
Both,
|
||||||
{
|
None,
|
||||||
use self::CommentOrNewline::*;
|
|
||||||
|
|
||||||
let mut iter = spaces.peekable();
|
|
||||||
|
|
||||||
while let Some(space) = iter.next() {
|
|
||||||
match space {
|
|
||||||
Newline => {}
|
|
||||||
LineComment(comment) => {
|
|
||||||
buf.push('#');
|
|
||||||
buf.push_str(comment);
|
|
||||||
}
|
|
||||||
DocComment(docs) => {
|
|
||||||
buf.push_str("##");
|
|
||||||
buf.push_str(docs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match iter.peek() {
|
|
||||||
None => {}
|
|
||||||
Some(next_space) => match next_space {
|
|
||||||
Newline => {}
|
|
||||||
LineComment(_) | DocComment(_) => {
|
|
||||||
newline(buf, indent);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like format_spaces, but remove newlines and keep only comments.
|
/// Like format_spaces, but remove newlines and keep only comments.
|
||||||
pub fn fmt_comments_only<'a, I>(buf: &mut String<'a>, spaces: I, indent: u16)
|
/// The `new_line_at` argument describes how new lines should be inserted
|
||||||
where
|
/// at the beginning or at the end of the block
|
||||||
|
/// in the case of there is some comment in the `spaces` argument.
|
||||||
|
pub fn fmt_comments_only<'a, I>(
|
||||||
|
buf: &mut String<'a>,
|
||||||
|
spaces: I,
|
||||||
|
new_line_at: NewlineAt,
|
||||||
|
indent: u16,
|
||||||
|
) where
|
||||||
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
||||||
{
|
{
|
||||||
use self::CommentOrNewline::*;
|
use self::CommentOrNewline::*;
|
||||||
|
use NewlineAt::*;
|
||||||
|
|
||||||
|
let mut comment_seen = false;
|
||||||
|
|
||||||
for space in spaces {
|
for space in spaces {
|
||||||
match space {
|
match space {
|
||||||
Newline => {}
|
Newline => {}
|
||||||
LineComment(comment) => {
|
LineComment(comment) => {
|
||||||
fmt_comment(buf, comment, indent);
|
if comment_seen || new_line_at == Top || new_line_at == Both {
|
||||||
|
newline(buf, indent);
|
||||||
|
}
|
||||||
|
fmt_comment(buf, comment);
|
||||||
|
comment_seen = true;
|
||||||
}
|
}
|
||||||
DocComment(docs) => {
|
DocComment(docs) => {
|
||||||
fmt_docs(buf, docs, indent);
|
if comment_seen || new_line_at == Top || new_line_at == Both {
|
||||||
|
newline(buf, indent);
|
||||||
|
}
|
||||||
|
fmt_docs(buf, docs);
|
||||||
|
comment_seen = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if comment_seen && (new_line_at == Bottom || new_line_at == Both) {
|
||||||
|
newline(buf, indent);
|
||||||
fn fmt_comment<'a>(buf: &mut String<'a>, comment: &'a str, indent: u16) {
|
|
||||||
buf.push('#');
|
|
||||||
buf.push_str(comment);
|
|
||||||
|
|
||||||
newline(buf, indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_docs<'a>(buf: &mut String<'a>, docs: &'a str, indent: u16) {
|
|
||||||
buf.push_str("##");
|
|
||||||
buf.push_str(docs);
|
|
||||||
|
|
||||||
newline(buf, indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_comment<'a>(space: &'a CommentOrNewline<'a>) -> bool {
|
|
||||||
match space {
|
|
||||||
CommentOrNewline::Newline => false,
|
|
||||||
CommentOrNewline::LineComment(_) => true,
|
|
||||||
CommentOrNewline::DocComment(_) => true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fmt_comment<'a>(buf: &mut String<'a>, comment: &'a str) {
|
||||||
|
buf.push('#');
|
||||||
|
if !comment.starts_with(' ') {
|
||||||
|
buf.push(' ');
|
||||||
|
}
|
||||||
|
buf.push_str(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_docs<'a>(buf: &mut String<'a>, docs: &'a str) {
|
||||||
|
buf.push_str("##");
|
||||||
|
if !docs.starts_with(' ') {
|
||||||
|
buf.push(' ');
|
||||||
|
}
|
||||||
|
buf.push_str(docs);
|
||||||
|
}
|
||||||
|
|
|
@ -129,6 +129,24 @@ mod test_fmt {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn force_space_at_begining_of_comment() {
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
#comment
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
# comment
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn func_def() {
|
fn func_def() {
|
||||||
expr_formats_same(indoc!(
|
expr_formats_same(indoc!(
|
||||||
|
@ -616,6 +634,129 @@ mod test_fmt {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn final_comments_in_records() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
x: 42,
|
||||||
|
# comment
|
||||||
|
}"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
x: 42,
|
||||||
|
# comment
|
||||||
|
# other comment
|
||||||
|
}"#
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn final_comments_without_comma_in_records() {
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
y: 41,
|
||||||
|
# comment 1
|
||||||
|
x: 42 # comment 2
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
y: 41,
|
||||||
|
# comment 1
|
||||||
|
x: 42,
|
||||||
|
# comment 2
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_final_comments_without_comma_in_records() {
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
y: 41,
|
||||||
|
x: 42 # comment 1
|
||||||
|
# comment 2
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
y: 41,
|
||||||
|
x: 42,
|
||||||
|
# comment 1
|
||||||
|
# comment 2
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn comments_with_newlines_in_records() {
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
z: 44 #comment 0
|
||||||
|
,
|
||||||
|
y: 41, # comment 1
|
||||||
|
|
||||||
|
# comment 2
|
||||||
|
x: 42
|
||||||
|
|
||||||
|
# comment 3
|
||||||
|
|
||||||
|
# comment 4
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
z: 44,
|
||||||
|
# comment 0
|
||||||
|
y: 41,
|
||||||
|
# comment 1
|
||||||
|
# comment 2
|
||||||
|
x: 42,
|
||||||
|
# comment 3
|
||||||
|
# comment 4
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_final_comments_with_comma_in_records() {
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
y: 41,
|
||||||
|
x: 42, # comment 1
|
||||||
|
# comment 2
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
y: 41,
|
||||||
|
x: 42,
|
||||||
|
# comment 1
|
||||||
|
# comment 2
|
||||||
|
}"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn def_closure() {
|
fn def_closure() {
|
||||||
expr_formats_same(indoc!(
|
expr_formats_same(indoc!(
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
use roc_collections::all::{default_hasher, MutMap};
|
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
|
||||||
use roc_mono::layout::Layout;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct LayoutId(u32);
|
|
||||||
|
|
||||||
impl LayoutId {
|
|
||||||
// Returns something like "foo#1" when given a symbol that interns to "foo"
|
|
||||||
// and a LayoutId of 1.
|
|
||||||
pub fn to_symbol_string(self, symbol: Symbol, interns: &Interns) -> String {
|
|
||||||
let ident_string = symbol.ident_string(interns);
|
|
||||||
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
|
|
||||||
format!("{}_{}_{}", module_string, ident_string, self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IdsByLayout<'a> {
|
|
||||||
by_id: MutMap<Layout<'a>, u32>,
|
|
||||||
next_id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct LayoutIds<'a> {
|
|
||||||
by_symbol: MutMap<Symbol, IdsByLayout<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LayoutIds<'a> {
|
|
||||||
/// Returns a LayoutId which is unique for the given symbol and layout.
|
|
||||||
/// If given the same symbol and same layout, returns the same LayoutId.
|
|
||||||
pub fn get(&mut self, symbol: Symbol, layout: &Layout<'a>) -> LayoutId {
|
|
||||||
// Note: this function does some weird stuff to satisfy the borrow checker.
|
|
||||||
// There's probably a nicer way to write it that still works.
|
|
||||||
let ids = self.by_symbol.entry(symbol).or_insert_with(|| IdsByLayout {
|
|
||||||
by_id: HashMap::with_capacity_and_hasher(1, default_hasher()),
|
|
||||||
next_id: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get the id associated with this layout, or default to next_id.
|
|
||||||
let answer = ids.by_id.get(layout).copied().unwrap_or(ids.next_id);
|
|
||||||
|
|
||||||
// If we had to default to next_id, it must not have been found;
|
|
||||||
// store the ID we're going to return and increment next_id.
|
|
||||||
if answer == ids.next_id {
|
|
||||||
ids.by_id.insert(layout.clone(), ids.next_id);
|
|
||||||
|
|
||||||
ids.next_id += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LayoutId(answer)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,6 @@
|
||||||
// re-enable this when working on performance optimizations than have it block PRs.
|
// re-enable this when working on performance optimizations than have it block PRs.
|
||||||
#![allow(clippy::large_enum_variant)]
|
#![allow(clippy::large_enum_variant)]
|
||||||
|
|
||||||
pub mod layout_id;
|
|
||||||
pub mod llvm;
|
pub mod llvm;
|
||||||
|
|
||||||
pub mod run_roc;
|
pub mod run_roc;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::layout_id::LayoutIds;
|
|
||||||
use crate::llvm::build_list::{
|
use crate::llvm::build_list::{
|
||||||
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
||||||
list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat,
|
list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat,
|
||||||
list_reverse, list_set, list_single, list_walk_right,
|
list_reverse, list_set, list_single, list_sum, list_walk_right,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{str_concat, str_count_graphemes, str_len, str_split, CHAR_LAYOUT};
|
use crate::llvm::build_str::{str_concat, str_count_graphemes, str_len, str_split, CHAR_LAYOUT};
|
||||||
use crate::llvm::compare::{build_eq, build_neq};
|
use crate::llvm::compare::{build_eq, build_neq};
|
||||||
|
@ -34,7 +33,7 @@ use roc_collections::all::{ImMap, MutSet};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::ir::{JoinPointId, Wrapped};
|
use roc_mono::ir::{JoinPointId, Wrapped};
|
||||||
use roc_mono::layout::{Builtin, ClosureLayout, Layout, MemoryMode};
|
use roc_mono::layout::{Builtin, ClosureLayout, Layout, LayoutIds, MemoryMode};
|
||||||
use target_lexicon::CallingConvention;
|
use target_lexicon::CallingConvention;
|
||||||
|
|
||||||
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
|
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
|
||||||
|
@ -2362,6 +2361,13 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
default_layout,
|
default_layout,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ListSum => {
|
||||||
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
|
let list = load_symbol(env, scope, &args[0]);
|
||||||
|
|
||||||
|
list_sum(env, parent, list, layout)
|
||||||
|
}
|
||||||
ListAppend => {
|
ListAppend => {
|
||||||
// List.append : List elem, elem -> List elem
|
// List.append : List elem, elem -> List elem
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
@ -2512,40 +2518,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
let (rhs_arg, rhs_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
let (rhs_arg, rhs_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||||
|
|
||||||
match (lhs_layout, rhs_layout) {
|
build_num_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op)
|
||||||
(Layout::Builtin(lhs_builtin), Layout::Builtin(rhs_builtin))
|
|
||||||
if lhs_builtin == rhs_builtin =>
|
|
||||||
{
|
|
||||||
use roc_mono::layout::Builtin::*;
|
|
||||||
|
|
||||||
match lhs_builtin {
|
|
||||||
Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
|
||||||
env,
|
|
||||||
parent,
|
|
||||||
lhs_arg.into_int_value(),
|
|
||||||
lhs_layout,
|
|
||||||
rhs_arg.into_int_value(),
|
|
||||||
rhs_layout,
|
|
||||||
op,
|
|
||||||
),
|
|
||||||
Float128 | Float64 | Float32 | Float16 => build_float_binop(
|
|
||||||
env,
|
|
||||||
parent,
|
|
||||||
lhs_arg.into_float_value(),
|
|
||||||
lhs_layout,
|
|
||||||
rhs_arg.into_float_value(),
|
|
||||||
rhs_layout,
|
|
||||||
op,
|
|
||||||
),
|
|
||||||
_ => {
|
|
||||||
unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid builtin layout: ({:?})", op, lhs_layout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid layouts. The 2 layouts were: ({:?}) and ({:?})", op, lhs_layout, rhs_layout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Eq => {
|
Eq => {
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
@ -2797,14 +2770,57 @@ fn call_bitcode_fn_help<'a, 'ctx, 'env>(
|
||||||
.get_function(fn_name)
|
.get_function(fn_name)
|
||||||
.unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", fn_name));
|
.unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", fn_name));
|
||||||
|
|
||||||
dbg!(fn_val);
|
|
||||||
|
|
||||||
let call = env.builder.build_call(fn_val, args, "call_builtin");
|
let call = env.builder.build_call(fn_val, args, "call_builtin");
|
||||||
|
|
||||||
call.set_call_convention(fn_val.get_call_conventions());
|
call.set_call_convention(fn_val.get_call_conventions());
|
||||||
call
|
call
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_num_binop<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
lhs_arg: BasicValueEnum<'ctx>,
|
||||||
|
lhs_layout: &Layout<'a>,
|
||||||
|
rhs_arg: BasicValueEnum<'ctx>,
|
||||||
|
rhs_layout: &Layout<'a>,
|
||||||
|
op: LowLevel,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
match (lhs_layout, rhs_layout) {
|
||||||
|
(Layout::Builtin(lhs_builtin), Layout::Builtin(rhs_builtin))
|
||||||
|
if lhs_builtin == rhs_builtin =>
|
||||||
|
{
|
||||||
|
use roc_mono::layout::Builtin::*;
|
||||||
|
|
||||||
|
match lhs_builtin {
|
||||||
|
Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
lhs_arg.into_int_value(),
|
||||||
|
lhs_layout,
|
||||||
|
rhs_arg.into_int_value(),
|
||||||
|
rhs_layout,
|
||||||
|
op,
|
||||||
|
),
|
||||||
|
Float128 | Float64 | Float32 | Float16 => build_float_binop(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
lhs_arg.into_float_value(),
|
||||||
|
lhs_layout,
|
||||||
|
rhs_arg.into_float_value(),
|
||||||
|
rhs_layout,
|
||||||
|
op,
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid builtin layout: ({:?})", op, lhs_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid layouts. The 2 layouts were: ({:?}) and ({:?})", op, lhs_layout, rhs_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_float_binop<'a, 'ctx, 'env>(
|
fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::llvm::build::{Env, InPlace};
|
use crate::llvm::build::{build_num_binop, Env, InPlace};
|
||||||
use crate::llvm::compare::build_eq;
|
use crate::llvm::compare::build_eq;
|
||||||
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
|
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
|
||||||
use inkwell::builder::Builder;
|
use inkwell::builder::Builder;
|
||||||
|
@ -728,6 +728,81 @@ pub fn list_len<'ctx>(
|
||||||
.into_int_value()
|
.into_int_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.sum : List (Num a) -> Num a
|
||||||
|
pub fn list_sum<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
list: BasicValueEnum<'ctx>,
|
||||||
|
default_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let ctx = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let list_wrapper = list.into_struct_value();
|
||||||
|
let len = list_len(env.builder, list_wrapper);
|
||||||
|
|
||||||
|
let accum_type = basic_type_from_layout(env.arena, ctx, default_layout, env.ptr_bytes);
|
||||||
|
let accum_alloca = builder.build_alloca(accum_type, "alloca_walk_right_accum");
|
||||||
|
|
||||||
|
let default: BasicValueEnum = match accum_type {
|
||||||
|
BasicTypeEnum::IntType(int_type) => int_type.const_zero().into(),
|
||||||
|
BasicTypeEnum::FloatType(float_type) => float_type.const_zero().into(),
|
||||||
|
_ => unreachable!(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.build_store(accum_alloca, default);
|
||||||
|
|
||||||
|
let then_block = ctx.append_basic_block(parent, "then");
|
||||||
|
let cont_block = ctx.append_basic_block(parent, "branchcont");
|
||||||
|
|
||||||
|
let condition = builder.build_int_compare(
|
||||||
|
IntPredicate::UGT,
|
||||||
|
len,
|
||||||
|
ctx.i64_type().const_zero(),
|
||||||
|
"list_non_empty",
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_conditional_branch(condition, then_block, cont_block);
|
||||||
|
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
|
let elem_ptr_type = get_ptr_type(&accum_type, AddressSpace::Generic);
|
||||||
|
let list_ptr = load_list_ptr(builder, list_wrapper, elem_ptr_type);
|
||||||
|
|
||||||
|
let walk_right_loop = |_, elem: BasicValueEnum<'ctx>| {
|
||||||
|
// load current accumulator
|
||||||
|
let current = builder.build_load(accum_alloca, "retrieve_accum");
|
||||||
|
|
||||||
|
let new_current = build_num_binop(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
current,
|
||||||
|
default_layout,
|
||||||
|
elem,
|
||||||
|
default_layout,
|
||||||
|
roc_module::low_level::LowLevel::NumAdd,
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_store(accum_alloca, new_current);
|
||||||
|
};
|
||||||
|
|
||||||
|
incrementing_elem_loop(
|
||||||
|
builder,
|
||||||
|
ctx,
|
||||||
|
parent,
|
||||||
|
list_ptr,
|
||||||
|
len,
|
||||||
|
"#index",
|
||||||
|
walk_right_loop,
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
|
||||||
|
builder.position_at_end(cont_block);
|
||||||
|
|
||||||
|
builder.build_load(accum_alloca, "load_final_acum")
|
||||||
|
}
|
||||||
|
|
||||||
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn list_walk_right<'a, 'ctx, 'env>(
|
pub fn list_walk_right<'a, 'ctx, 'env>(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::layout_id::LayoutIds;
|
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
cast_basic_basic, cast_struct_struct, create_entry_block_alloca, set_name, Env, Scope,
|
cast_basic_basic, cast_struct_struct, create_entry_block_alloca, set_name, Env, Scope,
|
||||||
FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64,
|
FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64,
|
||||||
|
@ -12,7 +11,7 @@ use inkwell::module::Linkage;
|
||||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
use inkwell::{AddressSpace, IntPredicate};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{Builtin, Layout, MemoryMode};
|
use roc_mono::layout::{Builtin, Layout, LayoutIds, MemoryMode};
|
||||||
|
|
||||||
pub const REFCOUNT_MAX: usize = 0 as usize;
|
pub const REFCOUNT_MAX: usize = 0 as usize;
|
||||||
|
|
||||||
|
|
|
@ -1612,4 +1612,11 @@ mod gen_list {
|
||||||
|
|
||||||
assert_evals_to!(indoc!("List.contains [] 4"), false, bool);
|
assert_evals_to!(indoc!("List.contains [] 4"), false, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_sum() {
|
||||||
|
assert_evals_to!("List.sum []", 0, i64);
|
||||||
|
assert_evals_to!("List.sum [ 1, 2, 3 ]", 6, i64);
|
||||||
|
assert_evals_to!("List.sum [ 1.1, 2.2, 3.3 ]", 6.6, f64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@ pub fn helper<'a>(
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut layout_ids = roc_gen::layout_id::LayoutIds::default();
|
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||||
let mut headers = Vec::with_capacity(procedures.len());
|
let mut headers = Vec::with_capacity(procedures.len());
|
||||||
|
|
||||||
// Add all the Proc headers to the module.
|
// Add all the Proc headers to the module.
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub enum LowLevel {
|
||||||
ListMap,
|
ListMap,
|
||||||
ListKeepIf,
|
ListKeepIf,
|
||||||
ListWalkRight,
|
ListWalkRight,
|
||||||
|
ListSum,
|
||||||
NumAdd,
|
NumAdd,
|
||||||
NumAddWrap,
|
NumAddWrap,
|
||||||
NumAddChecked,
|
NumAddChecked,
|
||||||
|
|
|
@ -693,6 +693,7 @@ define_builtins! {
|
||||||
16 LIST_JOIN: "join"
|
16 LIST_JOIN: "join"
|
||||||
17 LIST_KEEP_IF: "keepIf"
|
17 LIST_KEEP_IF: "keepIf"
|
||||||
18 LIST_CONTAINS: "contains"
|
18 LIST_CONTAINS: "contains"
|
||||||
|
19 LIST_SUM: "sum"
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
|
|
|
@ -536,6 +536,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
||||||
|
ListSum => arena.alloc_slice_copy(&[borrowed]),
|
||||||
|
|
||||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt
|
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt
|
||||||
| NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow
|
| NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{default_hasher, MutMap, MutSet};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<u8>() * 8) as usize;
|
pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<u8>() * 8) as usize;
|
||||||
|
|
||||||
|
@ -1298,3 +1299,52 @@ pub fn mode_from_var(var: Variable, subs: &Subs) -> MemoryMode {
|
||||||
_ => MemoryMode::Refcounted,
|
_ => MemoryMode::Refcounted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct LayoutId(u32);
|
||||||
|
|
||||||
|
impl LayoutId {
|
||||||
|
// Returns something like "foo#1" when given a symbol that interns to "foo"
|
||||||
|
// and a LayoutId of 1.
|
||||||
|
pub fn to_symbol_string(self, symbol: Symbol, interns: &Interns) -> String {
|
||||||
|
let ident_string = symbol.ident_string(interns);
|
||||||
|
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
|
||||||
|
format!("{}_{}_{}", module_string, ident_string, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IdsByLayout<'a> {
|
||||||
|
by_id: MutMap<Layout<'a>, u32>,
|
||||||
|
next_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct LayoutIds<'a> {
|
||||||
|
by_symbol: MutMap<Symbol, IdsByLayout<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LayoutIds<'a> {
|
||||||
|
/// Returns a LayoutId which is unique for the given symbol and layout.
|
||||||
|
/// If given the same symbol and same layout, returns the same LayoutId.
|
||||||
|
pub fn get(&mut self, symbol: Symbol, layout: &Layout<'a>) -> LayoutId {
|
||||||
|
// Note: this function does some weird stuff to satisfy the borrow checker.
|
||||||
|
// There's probably a nicer way to write it that still works.
|
||||||
|
let ids = self.by_symbol.entry(symbol).or_insert_with(|| IdsByLayout {
|
||||||
|
by_id: HashMap::with_capacity_and_hasher(1, default_hasher()),
|
||||||
|
next_id: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the id associated with this layout, or default to next_id.
|
||||||
|
let answer = ids.by_id.get(layout).copied().unwrap_or(ids.next_id);
|
||||||
|
|
||||||
|
// If we had to default to next_id, it must not have been found;
|
||||||
|
// store the ID we're going to return and increment next_id.
|
||||||
|
if answer == ids.next_id {
|
||||||
|
ids.by_id.insert(layout.clone(), ids.next_id);
|
||||||
|
|
||||||
|
ids.next_id += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutId(answer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -392,6 +392,17 @@ pub enum CommentOrNewline<'a> {
|
||||||
DocComment(&'a str),
|
DocComment(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> CommentOrNewline<'a> {
|
||||||
|
pub fn is_comment(&self) -> bool {
|
||||||
|
use CommentOrNewline::*;
|
||||||
|
match self {
|
||||||
|
Newline => false,
|
||||||
|
LineComment(_) => true,
|
||||||
|
DocComment(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Pattern<'a> {
|
pub enum Pattern<'a> {
|
||||||
// Identifier
|
// Identifier
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue