Merge remote-tracking branch 'origin/trunk' into recursive-tag-segfault

This commit is contained in:
Folkert 2020-11-17 02:52:13 +01:00
commit c11d97f3e7
25 changed files with 577 additions and 192 deletions

View file

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

View file

@ -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("{}", "{} : {}");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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) { fn fmt_comment<'a>(buf: &mut String<'a>, comment: &'a str) {
buf.push('#'); buf.push('#');
buf.push_str(comment); if !comment.starts_with(' ') {
buf.push(' ');
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,
} }
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);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,6 +22,7 @@ pub enum LowLevel {
ListMap, ListMap,
ListKeepIf, ListKeepIf,
ListWalkRight, ListWalkRight,
ListSum,
NumAdd, NumAdd,
NumAddWrap, NumAddWrap,
NumAddChecked, NumAddChecked,

View file

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

View file

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

View file

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

View file

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