mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into faster-str-eq
This commit is contained in:
commit
77d73f35eb
68 changed files with 1345 additions and 1098 deletions
|
@ -16,7 +16,7 @@ const InPlace = enum(u8) {
|
|||
};
|
||||
|
||||
const SMALL_STR_MAX_LENGTH = small_string_size - 1;
|
||||
const small_string_size = 2 * @sizeOf(usize);
|
||||
const small_string_size = @sizeOf(RocStr);
|
||||
const blank_small_string: [@sizeOf(RocStr)]u8 = init_blank_small_string(small_string_size);
|
||||
|
||||
fn init_blank_small_string(comptime n: usize) [n]u8 {
|
||||
|
@ -37,8 +37,9 @@ pub const RocStr = extern struct {
|
|||
pub const alignment = @alignOf(usize);
|
||||
|
||||
pub inline fn empty() RocStr {
|
||||
const small_str_flag: isize = std.math.minInt(isize);
|
||||
return RocStr{
|
||||
.str_len = 0,
|
||||
.str_len = @bitCast(usize, small_str_flag),
|
||||
.str_bytes = null,
|
||||
};
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
pub fn deinit(self: RocStr) void {
|
||||
if (!self.isSmallStr() and !self.isEmpty()) {
|
||||
if (!self.isSmallStr()) {
|
||||
utils.decref(self.str_bytes, self.str_len, RocStr.alignment);
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
pub fn clone(in_place: InPlace, str: RocStr) RocStr {
|
||||
if (str.isSmallStr() or str.isEmpty()) {
|
||||
if (str.isSmallStr()) {
|
||||
// just return the bytes
|
||||
return str;
|
||||
} else {
|
||||
|
@ -217,7 +218,8 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
pub fn isEmpty(self: RocStr) bool {
|
||||
return (self.str_len << 1) == 0;
|
||||
comptime const empty_len = RocStr.empty().str_len;
|
||||
return self.str_len == empty_len;
|
||||
}
|
||||
|
||||
// If a string happens to be null-terminated already, then we can pass its
|
||||
|
@ -297,11 +299,6 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
pub fn isUnique(self: RocStr) bool {
|
||||
// the empty string is unique (in the sense that copying it will not leak memory)
|
||||
if (self.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// small strings can be copied
|
||||
if (self.isSmallStr()) {
|
||||
return true;
|
||||
|
@ -324,8 +321,8 @@ pub const RocStr = extern struct {
|
|||
|
||||
// Since this conditional would be prone to branch misprediction,
|
||||
// make sure it will compile to a cmov.
|
||||
// return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||
if (self.isSmallStr() or self.isEmpty()) {
|
||||
// return if (self.isSmallStr()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||
if (self.isSmallStr()) {
|
||||
const as_int = @ptrToInt(&self);
|
||||
const as_ptr = @intToPtr([*]u8, as_int);
|
||||
return as_ptr;
|
||||
|
|
|
@ -446,9 +446,6 @@ drop : List elem, Nat -> List elem
|
|||
## To replace the element at a given index, instead of dropping it, see [List.set].
|
||||
dropAt : List elem, Nat -> List elem
|
||||
|
||||
## Drops the last element in a List.
|
||||
dropLast : List elem -> List elem
|
||||
|
||||
## Adds a new element to the end of the list.
|
||||
##
|
||||
## >>> List.append [ "a", "b" ] "c"
|
||||
|
@ -685,8 +682,6 @@ startsWith : List elem, List elem -> Bool
|
|||
|
||||
endsWith : List elem, List elem -> Bool
|
||||
|
||||
all : List elem, (elem -> Bool) -> Bool
|
||||
|
||||
## Run the given predicate on each element of the list, returning `True` if
|
||||
## any of the elements satisfy it.
|
||||
any : List elem, (elem -> Bool) -> Bool
|
||||
|
@ -698,3 +693,11 @@ all : List elem, (elem -> Bool) -> Bool
|
|||
## Returns the first element of the list satisfying a predicate function.
|
||||
## If no satisfying element is found, an `Err NotFound` is returned.
|
||||
find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
||||
|
||||
## Apply a function that returns a Result on a list, only successful
|
||||
## Results are kept and returned unwrapped.
|
||||
keepOks : List before, (before -> Result after *) -> List after
|
||||
|
||||
## Apply a function that returns a Result on a list, only unsuccessful
|
||||
## Results are kept and returned unwrapped.
|
||||
keepErrs : List before, (before -> Result * after) -> List after
|
||||
|
|
|
@ -621,13 +621,13 @@ toStr : Num * -> Str
|
|||
## Examples:
|
||||
##
|
||||
## In some countries (e.g. USA and UK), a comma is used to separate thousands:
|
||||
## >>> Num.format 1_000_000 { base: Decimal, wholeSep: { mark: ",", places: 3 } }
|
||||
## >>> Num.format 1_000_000 { pf: Decimal, wholeSep: { mark: ",", places: 3 } }
|
||||
##
|
||||
## Sometimes when rendering bits, it's nice to group them into groups of 4:
|
||||
## >>> Num.format 1_000_000 { base: Binary, wholeSep: { mark: " ", places: 4 } }
|
||||
## >>> Num.format 1_000_000 { pf: Binary, wholeSep: { mark: " ", places: 4 } }
|
||||
##
|
||||
## It's also common to render hexadecimal in groups of 2:
|
||||
## >>> Num.format 1_000_000 { base: Hexadecimal, wholeSep: { mark: " ", places: 2 } }
|
||||
## >>> Num.format 1_000_000 { pf: Hexadecimal, wholeSep: { mark: " ", places: 2 } }
|
||||
format :
|
||||
Num *,
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@ interface Result
|
|||
exposes
|
||||
[
|
||||
Result,
|
||||
isOk,
|
||||
isErr,
|
||||
map,
|
||||
mapErr,
|
||||
withDefault,
|
||||
|
@ -13,6 +15,16 @@ interface Result
|
|||
## okay, or else there was an error of some sort.
|
||||
Result ok err : [ @Result ok err ]
|
||||
|
||||
## Return True if the result indicates a success, else return False
|
||||
##
|
||||
## >>> Result.isOk (Ok 5)
|
||||
isOk : Result * * -> bool
|
||||
|
||||
## Return True if the result indicates a failure, else return False
|
||||
##
|
||||
## >>> Result.isErr (Err "uh oh")
|
||||
isErr : Result * * -> bool
|
||||
|
||||
## If the result is `Ok`, return the value it holds. Otherwise, return
|
||||
## the given default value.
|
||||
##
|
||||
|
|
|
@ -29,6 +29,8 @@ isEmpty : Set * -> Bool
|
|||
|
||||
len : Set * -> Nat
|
||||
|
||||
## Modify
|
||||
|
||||
# TODO: removed `'` from signature because parser does not support it yet
|
||||
# Original signature: `add : Set 'elem, 'elem -> Set 'elem`
|
||||
## Make sure never to add a *NaN* to a [Set]! Because *NaN* is defined to be
|
||||
|
@ -41,6 +43,8 @@ add : Set elem, elem -> Set elem
|
|||
# Original signature: `drop : Set 'elem, 'elem -> Set 'elem`
|
||||
drop : Set elem, elem -> Set elem
|
||||
|
||||
## Transform
|
||||
|
||||
## Convert each element in the set to something new, by calling a conversion
|
||||
## function on each of them. Then return a new set of the converted values.
|
||||
##
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{ClosureData, Expr::*};
|
||||
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
||||
use crate::expr::{self, ClosureData, Expr::*};
|
||||
use crate::expr::{Expr, Field, Recursive};
|
||||
use crate::pattern::Pattern;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::called_via::CalledVia;
|
||||
|
@ -3091,7 +3091,7 @@ fn list_keep_errs(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
lowlevel_2(symbol, LowLevel::ListKeepErrs, var_store)
|
||||
}
|
||||
|
||||
/// List.keepErrs: List before, (before -> Result * after) -> List after
|
||||
/// List.range: Int a, Int a -> List (Int a)
|
||||
fn list_range(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_2(symbol, LowLevel::ListRange, var_store)
|
||||
}
|
||||
|
@ -4107,7 +4107,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(ok),
|
||||
guard: None,
|
||||
|
@ -4137,7 +4137,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(err),
|
||||
guard: None,
|
||||
|
@ -4204,7 +4204,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(ok),
|
||||
guard: None,
|
||||
|
@ -4234,7 +4234,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(err),
|
||||
guard: None,
|
||||
|
@ -4277,7 +4277,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![(ret_var, no_region(Pattern::Identifier(Symbol::ARG_3)))],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(Var(Symbol::ARG_3)),
|
||||
guard: None,
|
||||
|
@ -4297,7 +4297,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![(var_store.fresh(), no_region(Pattern::Underscore))],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(Var(Symbol::ARG_2)),
|
||||
guard: None,
|
||||
|
@ -4347,7 +4347,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(false_expr),
|
||||
guard: None,
|
||||
|
@ -4374,7 +4374,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(true_expr),
|
||||
guard: None,
|
||||
|
@ -4424,7 +4424,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(true_expr),
|
||||
guard: None,
|
||||
|
@ -4451,7 +4451,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(false_expr),
|
||||
guard: None,
|
||||
|
@ -4513,7 +4513,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(ok),
|
||||
guard: None,
|
||||
|
@ -4543,7 +4543,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(err),
|
||||
guard: None,
|
||||
|
|
|
@ -40,12 +40,12 @@ pub enum Newlines {
|
|||
No,
|
||||
}
|
||||
|
||||
pub trait Formattable<'a> {
|
||||
pub trait Formattable {
|
||||
fn is_multiline(&self) -> bool;
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -53,23 +53,23 @@ pub trait Formattable<'a> {
|
|||
self.format(buf, indent);
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||
self.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a formattable value is also formattable
|
||||
impl<'a, T> Formattable<'a> for &'a T
|
||||
impl<'a, T> Formattable for &'a T
|
||||
where
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
fn is_multiline(&self) -> bool {
|
||||
(*self).is_multiline()
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -77,23 +77,23 @@ where
|
|||
(*self).format_with_options(buf, parens, newlines, indent)
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||
(*self).format(buf, indent)
|
||||
}
|
||||
}
|
||||
|
||||
/// A Located formattable value is also formattable
|
||||
impl<'a, T> Formattable<'a> for Located<T>
|
||||
impl<T> Formattable for Located<T>
|
||||
where
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
fn is_multiline(&self) -> bool {
|
||||
self.value.is_multiline()
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -102,12 +102,12 @@ where
|
|||
.format_with_options(buf, parens, newlines, indent)
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||
self.value.format(buf, indent)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||
impl<'a> Formattable for TypeAnnotation<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use roc_parse::ast::TypeAnnotation::*;
|
||||
|
||||
|
@ -148,9 +148,9 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -275,14 +275,14 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
|||
/// > term: { x: 100, y: True }
|
||||
///
|
||||
/// So we need two instances, each having the specific separator
|
||||
impl<'a> Formattable<'a> for AssignedField<'a, TypeAnnotation<'a>> {
|
||||
impl<'a> Formattable for AssignedField<'a, TypeAnnotation<'a>> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
is_multiline_assigned_field_help(self)
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -292,14 +292,14 @@ impl<'a> Formattable<'a> for AssignedField<'a, TypeAnnotation<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for AssignedField<'a, Expr<'a>> {
|
||||
impl<'a> Formattable for AssignedField<'a, Expr<'a>> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
is_multiline_assigned_field_help(self)
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -309,7 +309,7 @@ impl<'a> Formattable<'a> for AssignedField<'a, Expr<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_multiline_assigned_field_help<'a, T: Formattable<'a>>(afield: &AssignedField<'a, T>) -> bool {
|
||||
fn is_multiline_assigned_field_help<T: Formattable>(afield: &AssignedField<'_, T>) -> bool {
|
||||
use self::AssignedField::*;
|
||||
|
||||
match afield {
|
||||
|
@ -322,15 +322,15 @@ fn is_multiline_assigned_field_help<'a, T: Formattable<'a>>(afield: &AssignedFie
|
|||
}
|
||||
}
|
||||
|
||||
fn format_assigned_field_help<'a, T>(
|
||||
fn format_assigned_field_help<'a, 'buf, T>(
|
||||
zelf: &AssignedField<'a, T>,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
indent: u16,
|
||||
separator_prefix: &str,
|
||||
is_multiline: bool,
|
||||
) where
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
use self::AssignedField::*;
|
||||
|
||||
|
@ -403,7 +403,7 @@ fn format_assigned_field_help<'a, T>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for Tag<'a> {
|
||||
impl<'a> Formattable for Tag<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use self::Tag::*;
|
||||
|
||||
|
@ -416,9 +416,9 @@ impl<'a> Formattable<'a> for Tag<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
|
|
|
@ -6,15 +6,15 @@ use crate::{
|
|||
Buf,
|
||||
};
|
||||
|
||||
pub fn fmt_collection<'a, 'b, T: ExtractSpaces<'a> + Formattable<'b>>(
|
||||
buf: &mut Buf<'b>,
|
||||
pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
||||
buf: &mut Buf<'buf>,
|
||||
indent: u16,
|
||||
start: char,
|
||||
end: char,
|
||||
items: Collection<'a, T>,
|
||||
newline: Newlines,
|
||||
) where
|
||||
<T as ExtractSpaces<'a>>::Item: Formattable<'b>,
|
||||
<T as ExtractSpaces<'a>>::Item: Formattable,
|
||||
{
|
||||
buf.indent(indent);
|
||||
let is_multiline =
|
||||
|
|
|
@ -6,7 +6,7 @@ use roc_parse::ast::{Def, Expr, Pattern};
|
|||
use roc_region::all::Located;
|
||||
|
||||
/// A Located formattable value is also formattable
|
||||
impl<'a> Formattable<'a> for Def<'a> {
|
||||
impl<'a> Formattable for Def<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use roc_parse::ast::Def::*;
|
||||
|
||||
|
@ -25,9 +25,9 @@ impl<'a> Formattable<'a> for Def<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -107,8 +107,8 @@ impl<'a> Formattable<'a> for Def<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_expect<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_expect<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
condition: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
indent: u16,
|
||||
|
@ -123,11 +123,16 @@ fn fmt_expect<'a>(
|
|||
condition.format(buf, return_indent);
|
||||
}
|
||||
|
||||
pub fn fmt_def<'a>(buf: &mut Buf<'a>, def: &Def<'a>, indent: u16) {
|
||||
pub fn fmt_def<'a, 'buf>(buf: &mut Buf<'buf>, def: &Def<'a>, indent: u16) {
|
||||
def.format(buf, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_body<'a>(buf: &mut Buf<'a>, pattern: &'a Pattern<'a>, body: &'a Expr<'a>, indent: u16) {
|
||||
pub fn fmt_body<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
pattern: &'a Pattern<'a>,
|
||||
body: &'a Expr<'a>,
|
||||
indent: u16,
|
||||
) {
|
||||
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||
buf.push_str(" =");
|
||||
if body.is_multiline() {
|
||||
|
|
|
@ -11,7 +11,7 @@ use roc_parse::ast::{
|
|||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
use roc_region::all::Located;
|
||||
|
||||
impl<'a> Formattable<'a> for Expr<'a> {
|
||||
impl<'a> Formattable for Expr<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use roc_parse::ast::Expr::*;
|
||||
// TODO cache these answers using a Map<Pointer, bool>, so
|
||||
|
@ -106,9 +106,9 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -289,7 +289,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_str_segment<'a>(seg: &StrSegment<'a>, buf: &mut Buf<'a>, indent: u16) {
|
||||
fn format_str_segment<'a, 'buf>(seg: &StrSegment<'a>, buf: &mut Buf<'buf>, indent: u16) {
|
||||
use StrSegment::*;
|
||||
|
||||
match seg {
|
||||
|
@ -344,7 +344,7 @@ fn push_op(buf: &mut Buf, op: BinOp) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fmt_str_literal<'a>(buf: &mut Buf<'a>, literal: StrLiteral<'a>, indent: u16) {
|
||||
pub fn fmt_str_literal<'buf>(buf: &mut Buf<'buf>, literal: StrLiteral, indent: u16) {
|
||||
use roc_parse::ast::StrLiteral::*;
|
||||
|
||||
buf.indent(indent);
|
||||
|
@ -395,8 +395,8 @@ pub fn fmt_str_literal<'a>(buf: &mut Buf<'a>, literal: StrLiteral<'a>, indent: u
|
|||
buf.push('"');
|
||||
}
|
||||
|
||||
fn fmt_bin_ops<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_bin_ops<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
lefts: &'a [(Located<Expr<'a>>, Located<BinOp>)],
|
||||
loc_right_side: &'a Located<Expr<'a>>,
|
||||
part_of_multi_line_bin_ops: bool,
|
||||
|
@ -454,8 +454,8 @@ fn empty_line_before_expr<'a>(expr: &'a Expr<'a>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_when<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_when<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_condition: &'a Located<Expr<'a>>,
|
||||
branches: &[&'a WhenBranch<'a>],
|
||||
indent: u16,
|
||||
|
@ -569,8 +569,8 @@ fn fmt_when<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_expect<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_expect<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
condition: &'a Located<Expr<'a>>,
|
||||
continuation: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
|
@ -588,8 +588,8 @@ fn fmt_expect<'a>(
|
|||
continuation.format(buf, return_indent);
|
||||
}
|
||||
|
||||
fn fmt_if<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_if<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
branches: &'a [(Located<Expr<'a>>, Located<Expr<'a>>)],
|
||||
final_else: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
|
@ -709,8 +709,8 @@ fn fmt_if<'a>(
|
|||
final_else.format(buf, return_indent);
|
||||
}
|
||||
|
||||
fn fmt_closure<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_closure<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_patterns: &'a [Located<Pattern<'a>>],
|
||||
loc_ret: &'a Located<Expr<'a>>,
|
||||
indent: u16,
|
||||
|
@ -782,8 +782,8 @@ fn fmt_closure<'a>(
|
|||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||
}
|
||||
|
||||
fn fmt_backpassing<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_backpassing<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_patterns: &'a [Located<Pattern<'a>>],
|
||||
loc_body: &'a Located<Expr<'a>>,
|
||||
loc_ret: &'a Located<Expr<'a>>,
|
||||
|
@ -876,8 +876,8 @@ fn pattern_needs_parens_when_backpassing(pat: &Pattern) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_record<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_record<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
update: Option<&'a Located<Expr<'a>>>,
|
||||
fields: Collection<'a, Located<AssignedField<'a, Expr<'a>>>>,
|
||||
indent: u16,
|
||||
|
@ -945,13 +945,13 @@ fn fmt_record<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn format_field_multiline<'a, T>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn format_field_multiline<'a, 'buf, T>(
|
||||
buf: &mut Buf<'buf>,
|
||||
field: &AssignedField<'a, T>,
|
||||
indent: u16,
|
||||
separator_prefix: &str,
|
||||
) where
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
use self::AssignedField::*;
|
||||
match field {
|
||||
|
|
|
@ -3,14 +3,14 @@ use crate::collection::fmt_collection;
|
|||
use crate::expr::fmt_str_literal;
|
||||
use crate::spaces::{fmt_default_spaces, fmt_spaces, INDENT};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{Collection, Module};
|
||||
use roc_parse::ast::{Collection, Module, Spaced};
|
||||
use roc_parse::header::{
|
||||
AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
||||
AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
||||
PackageName, PackageOrPath, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent,
|
||||
};
|
||||
use roc_region::all::Located;
|
||||
|
||||
pub fn fmt_module<'a>(buf: &mut Buf<'a>, module: &'a Module<'a>) {
|
||||
pub fn fmt_module<'a, 'buf>(buf: &mut Buf<'buf>, module: &'a Module<'a>) {
|
||||
match module {
|
||||
Module::Interface { header } => {
|
||||
fmt_interface_header(buf, header);
|
||||
|
@ -24,7 +24,7 @@ pub fn fmt_module<'a>(buf: &mut Buf<'a>, module: &'a Module<'a>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fmt_interface_header<'a>(buf: &mut Buf<'a>, header: &'a InterfaceHeader<'a>) {
|
||||
pub fn fmt_interface_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a InterfaceHeader<'a>) {
|
||||
let indent = INDENT;
|
||||
|
||||
buf.indent(0);
|
||||
|
@ -49,7 +49,7 @@ pub fn fmt_interface_header<'a>(buf: &mut Buf<'a>, header: &'a InterfaceHeader<'
|
|||
fmt_imports(buf, header.imports, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_app_header<'a>(buf: &mut Buf<'a>, header: &'a AppHeader<'a>) {
|
||||
pub fn fmt_app_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a AppHeader<'a>) {
|
||||
let indent = INDENT;
|
||||
buf.indent(0);
|
||||
buf.push_str("app");
|
||||
|
@ -84,7 +84,7 @@ pub fn fmt_app_header<'a>(buf: &mut Buf<'a>, header: &'a AppHeader<'a>) {
|
|||
fmt_to(buf, header.to.value, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_platform_header<'a>(buf: &mut Buf<'a>, header: &'a PlatformHeader<'a>) {
|
||||
pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHeader<'a>) {
|
||||
let indent = INDENT;
|
||||
|
||||
buf.indent(0);
|
||||
|
@ -131,15 +131,15 @@ pub fn fmt_platform_header<'a>(buf: &mut Buf<'a>, header: &'a PlatformHeader<'a>
|
|||
fmt_effects(buf, &header.effects, indent);
|
||||
}
|
||||
|
||||
fn fmt_requires<'a>(buf: &mut Buf<'a>, requires: &PlatformRequires<'a>, indent: u16) {
|
||||
fn fmt_requires<'a, 'buf>(buf: &mut Buf<'buf>, requires: &PlatformRequires<'a>, indent: u16) {
|
||||
fmt_collection(buf, indent, '{', '}', requires.rigids, Newlines::No);
|
||||
|
||||
buf.push_str(" { ");
|
||||
fmt_typed_ident(buf, &requires.signature.value, indent);
|
||||
requires.signature.value.format(buf, indent);
|
||||
buf.push_str(" }");
|
||||
}
|
||||
|
||||
fn fmt_effects<'a>(buf: &mut Buf<'a>, effects: &Effects<'a>, indent: u16) {
|
||||
fn fmt_effects<'a, 'buf>(buf: &mut Buf<'buf>, effects: &Effects<'a>, indent: u16) {
|
||||
fmt_default_spaces(buf, effects.spaces_before_effects_keyword, " ", indent);
|
||||
buf.indent(indent);
|
||||
buf.push_str("effects");
|
||||
|
@ -155,95 +155,84 @@ fn fmt_effects<'a>(buf: &mut Buf<'a>, effects: &Effects<'a>, indent: u16) {
|
|||
fmt_collection(buf, indent, '{', '}', effects.entries, Newlines::No)
|
||||
}
|
||||
|
||||
fn fmt_typed_ident<'a>(buf: &mut Buf<'a>, entry: &TypedIdent<'a>, indent: u16) {
|
||||
use TypedIdent::*;
|
||||
match entry {
|
||||
Entry {
|
||||
ident,
|
||||
spaces_before_colon,
|
||||
ann,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(ident.value);
|
||||
fmt_default_spaces(buf, spaces_before_colon, " ", indent);
|
||||
buf.push_str(": ");
|
||||
ann.value.format(buf, indent);
|
||||
}
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fmt_typed_ident(buf, sub_entry, indent);
|
||||
}
|
||||
SpaceAfter(sub_entry, spaces) => {
|
||||
fmt_typed_ident(buf, sub_entry, indent);
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for TypedIdent<'a> {
|
||||
impl<'a> Formattable for TypedIdent<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fmt_typed_ident(buf, self, indent);
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||
buf.indent(indent);
|
||||
buf.push_str(self.ident.value);
|
||||
fmt_default_spaces(buf, self.spaces_before_colon, " ", indent);
|
||||
buf.push_str(": ");
|
||||
self.ann.value.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for PlatformRigid<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fmt_platform_rigid(buf, self, indent);
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_package_name<'a>(buf: &mut Buf<'a>, name: PackageName) {
|
||||
fn fmt_package_name<'buf>(buf: &mut Buf<'buf>, name: PackageName) {
|
||||
buf.push_str(name.account);
|
||||
buf.push('/');
|
||||
buf.push_str(name.pkg);
|
||||
}
|
||||
|
||||
fn fmt_platform_rigid<'a>(buf: &mut Buf<'a>, entry: &PlatformRigid<'a>, indent: u16) {
|
||||
use roc_parse::header::PlatformRigid::*;
|
||||
impl<'a, T: Formattable> Formattable for Spaced<'a, T> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
// TODO
|
||||
false
|
||||
}
|
||||
|
||||
match entry {
|
||||
Entry { rigid, alias } => {
|
||||
buf.push_str(rigid);
|
||||
buf.push_str("=>");
|
||||
buf.push_str(alias);
|
||||
}
|
||||
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fmt_platform_rigid(buf, sub_entry, indent);
|
||||
}
|
||||
SpaceAfter(sub_entry, spaces) => {
|
||||
fmt_platform_rigid(buf, sub_entry, indent);
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: crate::annotation::Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
match self {
|
||||
Spaced::Item(item) => {
|
||||
item.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
Spaced::SpaceBefore(item, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
item.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
Spaced::SpaceAfter(item, spaces) => {
|
||||
item.format_with_options(buf, parens, newlines, indent);
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_imports<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
impl<'a> Formattable for PlatformRigid<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||
buf.push_str(self.rigid);
|
||||
buf.push_str("=>");
|
||||
buf.push_str(self.alias);
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_imports<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_entries: Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
||||
}
|
||||
|
||||
fn fmt_provides<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
fn fmt_provides<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_entries: Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
||||
}
|
||||
|
||||
fn fmt_to<'a>(buf: &mut Buf<'a>, to: To<'a>, indent: u16) {
|
||||
fn fmt_to<'buf>(buf: &mut Buf<'buf>, to: To, indent: u16) {
|
||||
match to {
|
||||
To::ExistingPackage(name) => {
|
||||
buf.push_str(name);
|
||||
|
@ -252,113 +241,95 @@ fn fmt_to<'a>(buf: &mut Buf<'a>, to: To<'a>, indent: u16) {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_exposes<'a, N: FormatName + Copy + 'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'_, Located<ExposesEntry<'_, N>>>,
|
||||
fn fmt_exposes<'buf, N: Formattable + Copy>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_entries: Collection<'_, Located<Spaced<'_, N>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
||||
}
|
||||
|
||||
impl<'a, 'b, N: FormatName> Formattable<'a> for ExposesEntry<'b, N> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fmt_exposes_entry(buf, self, indent);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FormatName {
|
||||
fn format<'a>(&self, buf: &mut Buf<'a>);
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>);
|
||||
}
|
||||
|
||||
impl<'a> FormatName for &'a str {
|
||||
fn format<'b>(&self, buf: &mut Buf<'b>) {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>) {
|
||||
buf.push_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatName for ModuleName<'a> {
|
||||
fn format<'b>(&self, buf: &mut Buf<'b>) {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>) {
|
||||
buf.push_str(self.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_exposes_entry<'a, 'b, N: FormatName>(
|
||||
buf: &mut Buf<'a>,
|
||||
entry: &ExposesEntry<'b, N>,
|
||||
indent: u16,
|
||||
) {
|
||||
use roc_parse::header::ExposesEntry::*;
|
||||
impl<'a> Formattable for ModuleName<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
match entry {
|
||||
Exposed(ident) => ident.format(buf),
|
||||
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fmt_exposes_entry(buf, sub_entry, indent);
|
||||
}
|
||||
SpaceAfter(sub_entry, spaces) => {
|
||||
fmt_exposes_entry(buf, sub_entry, indent);
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||
buf.push_str(self.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_packages<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'a, Located<PackageEntry<'a>>>,
|
||||
impl<'a> Formattable for ExposedName<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||
buf.push_str(self.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatName for ExposedName<'a> {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>) {
|
||||
buf.push_str(self.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_packages<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_entries: Collection<'a, Located<Spaced<'a, PackageEntry<'a>>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
fmt_collection(buf, indent, '{', '}', loc_entries, Newlines::No)
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for PackageEntry<'a> {
|
||||
impl<'a> Formattable for PackageEntry<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||
fmt_packages_entry(buf, self, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for ImportsEntry<'a> {
|
||||
impl<'a> Formattable for ImportsEntry<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||
fmt_imports_entry(buf, self, indent);
|
||||
}
|
||||
}
|
||||
fn fmt_packages_entry<'a>(buf: &mut Buf<'a>, entry: &PackageEntry<'a>, indent: u16) {
|
||||
use PackageEntry::*;
|
||||
match entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_or_path,
|
||||
} => {
|
||||
buf.push_str(shorthand);
|
||||
buf.push(':');
|
||||
fmt_default_spaces(buf, spaces_after_shorthand, " ", indent);
|
||||
fmt_package_or_path(buf, &package_or_path.value, indent);
|
||||
}
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fmt_packages_entry(buf, sub_entry, indent);
|
||||
}
|
||||
SpaceAfter(sub_entry, spaces) => {
|
||||
fmt_packages_entry(buf, sub_entry, indent);
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
}
|
||||
fn fmt_packages_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &PackageEntry<'a>, indent: u16) {
|
||||
buf.push_str(entry.shorthand);
|
||||
buf.push(':');
|
||||
fmt_default_spaces(buf, entry.spaces_after_shorthand, " ", indent);
|
||||
fmt_package_or_path(buf, &entry.package_or_path.value, indent);
|
||||
}
|
||||
|
||||
fn fmt_package_or_path<'a>(buf: &mut Buf<'a>, package_or_path: &PackageOrPath<'a>, indent: u16) {
|
||||
fn fmt_package_or_path<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
package_or_path: &PackageOrPath<'a>,
|
||||
indent: u16,
|
||||
) {
|
||||
match package_or_path {
|
||||
PackageOrPath::Package(_name, _version) => {
|
||||
todo!("format package");
|
||||
|
@ -367,7 +338,7 @@ fn fmt_package_or_path<'a>(buf: &mut Buf<'a>, package_or_path: &PackageOrPath<'a
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_imports_entry<'a>(buf: &mut Buf<'a>, entry: &ImportsEntry<'a>, indent: u16) {
|
||||
fn fmt_imports_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &ImportsEntry<'a>, indent: u16) {
|
||||
use roc_parse::header::ImportsEntry::*;
|
||||
|
||||
match entry {
|
||||
|
@ -392,14 +363,5 @@ fn fmt_imports_entry<'a>(buf: &mut Buf<'a>, entry: &ImportsEntry<'a>, indent: u1
|
|||
fmt_collection(buf, indent, '{', '}', *entries, Newlines::No)
|
||||
}
|
||||
}
|
||||
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fmt_imports_entry(buf, sub_entry, indent);
|
||||
}
|
||||
SpaceAfter(sub_entry, spaces) => {
|
||||
fmt_imports_entry(buf, sub_entry, indent);
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,16 @@ use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt};
|
|||
use crate::Buf;
|
||||
use roc_parse::ast::{Base, Pattern};
|
||||
|
||||
pub fn fmt_pattern<'a>(buf: &mut Buf<'a>, pattern: &'a Pattern<'a>, indent: u16, parens: Parens) {
|
||||
pub fn fmt_pattern<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
pattern: &'a Pattern<'a>,
|
||||
indent: u16,
|
||||
parens: Parens,
|
||||
) {
|
||||
pattern.format_with_options(buf, parens, Newlines::No, indent);
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for Pattern<'a> {
|
||||
impl<'a> Formattable for Pattern<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
// Theory: a pattern should only be multiline when it contains a comment
|
||||
match self {
|
||||
|
@ -37,9 +42,9 @@ impl<'a> Formattable<'a> for Pattern<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
|
|
@ -18,8 +18,8 @@ pub fn add_spaces(buf: &mut String<'_>, spaces: u16) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fmt_default_spaces<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
pub fn fmt_default_spaces<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
spaces: &[CommentOrNewline<'a>],
|
||||
default: &str,
|
||||
indent: u16,
|
||||
|
@ -31,9 +31,9 @@ pub fn fmt_default_spaces<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fmt_spaces<'a, 'b, I>(buf: &mut Buf<'a>, spaces: I, indent: u16)
|
||||
pub fn fmt_spaces<'a, 'buf, I>(buf: &mut Buf<'buf>, spaces: I, indent: u16)
|
||||
where
|
||||
I: Iterator<Item = &'b CommentOrNewline<'b>>,
|
||||
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
||||
{
|
||||
use self::CommentOrNewline::*;
|
||||
|
||||
|
@ -84,13 +84,13 @@ pub enum NewlineAt {
|
|||
/// The `new_line_at` argument describes how new lines should be inserted
|
||||
/// 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, 'b, I>(
|
||||
buf: &mut Buf<'a>,
|
||||
pub fn fmt_comments_only<'a, 'buf, I>(
|
||||
buf: &mut Buf<'buf>,
|
||||
spaces: I,
|
||||
new_line_at: NewlineAt,
|
||||
indent: u16,
|
||||
) where
|
||||
I: Iterator<Item = &'b CommentOrNewline<'b>>,
|
||||
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
||||
{
|
||||
use self::CommentOrNewline::*;
|
||||
use NewlineAt::*;
|
||||
|
@ -123,7 +123,7 @@ pub fn fmt_comments_only<'a, 'b, I>(
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_comment<'a>(buf: &mut Buf<'a>, comment: &str) {
|
||||
fn fmt_comment<'buf>(buf: &mut Buf<'buf>, comment: &str) {
|
||||
buf.push('#');
|
||||
if !comment.starts_with(' ') {
|
||||
buf.push(' ');
|
||||
|
@ -131,7 +131,7 @@ fn fmt_comment<'a>(buf: &mut Buf<'a>, comment: &str) {
|
|||
buf.push_str(comment);
|
||||
}
|
||||
|
||||
fn fmt_docs<'a>(buf: &mut Buf<'a>, docs: &str) {
|
||||
fn fmt_docs<'buf>(buf: &mut Buf<'buf>, docs: &str) {
|
||||
buf.push_str("##");
|
||||
if !docs.starts_with(' ') {
|
||||
buf.push(' ');
|
||||
|
|
|
@ -15,7 +15,7 @@ mod test_fmt {
|
|||
use roc_test_utils::assert_multiline_str_eq;
|
||||
|
||||
// Not intended to be used directly in tests; please use expr_formats_to or expr_formats_same
|
||||
fn expect_format_helper(input: &str, expected: &str) {
|
||||
fn expect_format_expr_helper(input: &str, expected: &str) {
|
||||
let arena = Bump::new();
|
||||
match roc_parse::test_helpers::parse_expr_with(&arena, input.trim()) {
|
||||
Ok(actual) => {
|
||||
|
@ -34,21 +34,20 @@ mod test_fmt {
|
|||
let expected = expected.trim_end();
|
||||
|
||||
// First check that input formats to the expected version
|
||||
expect_format_helper(input, expected);
|
||||
expect_format_expr_helper(input, expected);
|
||||
|
||||
// Parse the expected result format it, asserting that it doesn't change
|
||||
// It's important that formatting be stable / idempotent
|
||||
expect_format_helper(expected, expected);
|
||||
expect_format_expr_helper(expected, expected);
|
||||
}
|
||||
|
||||
fn expr_formats_same(input: &str) {
|
||||
expr_formats_to(input, input);
|
||||
}
|
||||
|
||||
fn module_formats_to(src: &str, expected: &str) {
|
||||
// Not intended to be used directly in tests; please use module_formats_to or module_formats_same
|
||||
fn expect_format_module_helper(src: &str, expected: &str) {
|
||||
let arena = Bump::new();
|
||||
let src = src.trim_end();
|
||||
|
||||
match module::parse_header(&arena, State::new(src.as_bytes())) {
|
||||
Ok((actual, state)) => {
|
||||
let mut buf = Buf::new_in(&arena);
|
||||
|
@ -70,6 +69,17 @@ mod test_fmt {
|
|||
};
|
||||
}
|
||||
|
||||
fn module_formats_to(input: &str, expected: &str) {
|
||||
let input = input.trim_end();
|
||||
|
||||
// First check that input formats to the expected version
|
||||
expect_format_module_helper(input, expected);
|
||||
|
||||
// Parse the expected result format it, asserting that it doesn't change
|
||||
// It's important that formatting be stable / idempotent
|
||||
expect_format_module_helper(expected, expected);
|
||||
}
|
||||
|
||||
fn module_formats_same(input: &str) {
|
||||
module_formats_to(input, input);
|
||||
}
|
||||
|
@ -2625,7 +2635,7 @@ mod test_fmt {
|
|||
fn single_line_app() {
|
||||
module_formats_same(indoc!(
|
||||
r#"
|
||||
app "Foo" packages { base: "platform" } imports [] provides [ main ] to base"#
|
||||
app "Foo" packages { pf: "platform" } imports [] provides [ main ] to pf"#
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use bumpalo::collections::Vec;
|
|||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
||||
use roc_mono::code_gen_help::CodeGenHelp;
|
||||
use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt};
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
use roc_reporting::internal_error;
|
||||
|
@ -256,8 +256,8 @@ pub struct Backend64Bit<
|
|||
phantom_cc: PhantomData<CC>,
|
||||
env: &'a Env<'a>,
|
||||
interns: &'a mut Interns,
|
||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
||||
refcount_proc_symbols: Vec<'a, (Symbol, ProcLayout<'a>)>,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
helper_proc_symbols: Vec<'a, (Symbol, ProcLayout<'a>)>,
|
||||
buf: Vec<'a, u8>,
|
||||
relocs: Vec<'a, Relocation>,
|
||||
proc_name: Option<String>,
|
||||
|
@ -308,8 +308,8 @@ pub fn new_backend_64bit<
|
|||
phantom_cc: PhantomData,
|
||||
env,
|
||||
interns,
|
||||
refcount_proc_gen: RefcountProcGenerator::new(env.arena, IntWidth::I64, env.module_id),
|
||||
refcount_proc_symbols: bumpalo::vec![in env.arena],
|
||||
helper_proc_gen: CodeGenHelp::new(env.arena, IntWidth::I64, env.module_id),
|
||||
helper_proc_symbols: bumpalo::vec![in env.arena],
|
||||
proc_name: None,
|
||||
is_self_recursive: None,
|
||||
buf: bumpalo::vec![in env.arena],
|
||||
|
@ -346,19 +346,17 @@ impl<
|
|||
fn interns(&self) -> &Interns {
|
||||
self.interns
|
||||
}
|
||||
fn env_interns_refcount_mut(
|
||||
&mut self,
|
||||
) -> (&Env<'a>, &mut Interns, &mut RefcountProcGenerator<'a>) {
|
||||
(self.env, self.interns, &mut self.refcount_proc_gen)
|
||||
fn env_interns_helpers_mut(&mut self) -> (&Env<'a>, &mut Interns, &mut CodeGenHelp<'a>) {
|
||||
(self.env, self.interns, &mut self.helper_proc_gen)
|
||||
}
|
||||
fn refcount_proc_gen_mut(&mut self) -> &mut RefcountProcGenerator<'a> {
|
||||
&mut self.refcount_proc_gen
|
||||
fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a> {
|
||||
&mut self.helper_proc_gen
|
||||
}
|
||||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&mut self.refcount_proc_symbols
|
||||
fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&mut self.helper_proc_symbols
|
||||
}
|
||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&self.refcount_proc_symbols
|
||||
fn helper_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&self.helper_proc_symbols
|
||||
}
|
||||
|
||||
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive) {
|
||||
|
@ -383,7 +381,7 @@ impl<
|
|||
self.float_used_regs.clear();
|
||||
self.float_free_regs
|
||||
.extend_from_slice(CC::FLOAT_DEFAULT_FREE_REGS);
|
||||
self.refcount_proc_symbols.clear();
|
||||
self.helper_proc_symbols.clear();
|
||||
}
|
||||
|
||||
fn literal_map(&mut self) -> &mut MutMap<Symbol, (*const Literal<'a>, *const Layout<'a>)> {
|
||||
|
|
|
@ -8,7 +8,7 @@ use roc_collections::all::{MutMap, MutSet};
|
|||
use roc_module::ident::{ModuleName, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
||||
use roc_mono::code_gen_help::CodeGenHelp;
|
||||
use roc_mono::ir::{
|
||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
||||
SelfRecursive, Stmt,
|
||||
|
@ -62,9 +62,7 @@ trait Backend<'a> {
|
|||
// This method is suboptimal, but it seems to be the only way to make rust understand
|
||||
// that all of these values can be mutable at the same time. By returning them together,
|
||||
// rust understands that they are part of a single use of mutable self.
|
||||
fn env_interns_refcount_mut(
|
||||
&mut self,
|
||||
) -> (&Env<'a>, &mut Interns, &mut RefcountProcGenerator<'a>);
|
||||
fn env_interns_helpers_mut(&mut self) -> (&Env<'a>, &mut Interns, &mut CodeGenHelp<'a>);
|
||||
|
||||
fn symbol_to_string(&self, symbol: Symbol, layout_id: LayoutId) -> String {
|
||||
layout_id.to_symbol_string(symbol, self.interns())
|
||||
|
@ -76,11 +74,11 @@ trait Backend<'a> {
|
|||
.starts_with(ModuleName::APP)
|
||||
}
|
||||
|
||||
fn refcount_proc_gen_mut(&mut self) -> &mut RefcountProcGenerator<'a>;
|
||||
fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a>;
|
||||
|
||||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
fn helper_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
||||
/// reset resets any registers or other values that may be occupied at the end of a procedure.
|
||||
/// It also passes basic procedure information to the builder for setup of the next function.
|
||||
|
@ -116,17 +114,17 @@ trait Backend<'a> {
|
|||
self.scan_ast(&proc.body);
|
||||
self.create_free_map();
|
||||
self.build_stmt(&proc.body, &proc.ret_layout);
|
||||
let mut rc_proc_names = bumpalo::vec![in self.env().arena];
|
||||
rc_proc_names.reserve(self.refcount_proc_symbols().len());
|
||||
for (rc_proc_sym, rc_proc_layout) in self.refcount_proc_symbols() {
|
||||
let mut helper_proc_names = bumpalo::vec![in self.env().arena];
|
||||
helper_proc_names.reserve(self.helper_proc_symbols().len());
|
||||
for (rc_proc_sym, rc_proc_layout) in self.helper_proc_symbols() {
|
||||
let name = layout_ids
|
||||
.get_toplevel(*rc_proc_sym, rc_proc_layout)
|
||||
.to_symbol_string(*rc_proc_sym, self.interns());
|
||||
|
||||
rc_proc_names.push((*rc_proc_sym, name));
|
||||
helper_proc_names.push((*rc_proc_sym, name));
|
||||
}
|
||||
let (bytes, relocs) = self.finalize();
|
||||
(bytes, relocs, rc_proc_names)
|
||||
(bytes, relocs, helper_proc_names)
|
||||
}
|
||||
|
||||
/// build_stmt builds a statement and outputs at the end of the buffer.
|
||||
|
@ -150,17 +148,16 @@ trait Backend<'a> {
|
|||
// Expand the Refcounting statement into more detailed IR with a function call
|
||||
// If this layout requires a new RC proc, we get enough info to create a linker symbol
|
||||
// for it. Here we don't create linker symbols at this time, but in Wasm backend, we do.
|
||||
let (rc_stmt, new_proc_info) = {
|
||||
let (env, interns, rc_proc_gen) = self.env_interns_refcount_mut();
|
||||
let (rc_stmt, new_specializations) = {
|
||||
let (env, interns, rc_proc_gen) = self.env_interns_helpers_mut();
|
||||
let module_id = env.module_id;
|
||||
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||
|
||||
rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following)
|
||||
};
|
||||
|
||||
if let Some((rc_proc_symbol, rc_proc_layout)) = new_proc_info {
|
||||
self.refcount_proc_symbols_mut()
|
||||
.push((rc_proc_symbol, rc_proc_layout));
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.helper_proc_symbols_mut().push(spec);
|
||||
}
|
||||
|
||||
self.build_stmt(rc_stmt, ret_layout)
|
||||
|
@ -538,7 +535,7 @@ trait Backend<'a> {
|
|||
debug_assert_eq!(
|
||||
1,
|
||||
args.len(),
|
||||
"RefCountGetPtr: expected to have exactly two argument"
|
||||
"RefCountGetPtr: expected to have exactly one argument"
|
||||
);
|
||||
self.build_refcount_getptr(sym, &args[0])
|
||||
}
|
||||
|
|
|
@ -240,38 +240,38 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
)
|
||||
}
|
||||
|
||||
let rc_procs = {
|
||||
// Generate IR for specialized helper procs (refcounting & equality)
|
||||
let helper_procs = {
|
||||
let module_id = backend.env().module_id;
|
||||
|
||||
let (env, interns, rc_proc_gen) = backend.env_interns_refcount_mut();
|
||||
let (env, interns, helper_proc_gen) = backend.env_interns_helpers_mut();
|
||||
|
||||
// Generate IR for refcounting procedures
|
||||
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||
let rc_procs = rc_proc_gen.generate_refcount_procs(arena, ident_ids);
|
||||
let helper_procs = helper_proc_gen.generate_procs(arena, ident_ids);
|
||||
env.module_id.register_debug_idents(ident_ids);
|
||||
|
||||
rc_procs
|
||||
helper_procs
|
||||
};
|
||||
|
||||
let empty = bumpalo::collections::Vec::new_in(arena);
|
||||
let rc_symbols_and_layouts = std::mem::replace(backend.refcount_proc_symbols_mut(), empty);
|
||||
let mut rc_names_symbols_procs = Vec::with_capacity_in(rc_procs.len(), arena);
|
||||
let helper_symbols_and_layouts = std::mem::replace(backend.helper_proc_symbols_mut(), empty);
|
||||
let mut helper_names_symbols_procs = Vec::with_capacity_in(helper_procs.len(), arena);
|
||||
|
||||
// Names and linker data for refcounting procedures
|
||||
for ((sym, layout), proc) in rc_symbols_and_layouts.into_iter().zip(rc_procs) {
|
||||
// Names and linker data for helpers
|
||||
for ((sym, layout), proc) in helper_symbols_and_layouts.into_iter().zip(helper_procs) {
|
||||
let layout_id = layout_ids.get_toplevel(sym, &layout);
|
||||
let fn_name = backend.symbol_to_string(sym, layout_id);
|
||||
if let Some(proc_id) = output.symbol_id(fn_name.as_bytes()) {
|
||||
if let SymbolSection::Section(section_id) = output.symbol(proc_id).section {
|
||||
rc_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
||||
helper_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
internal_error!("failed to create rc fn for symbol {:?}", sym);
|
||||
}
|
||||
|
||||
// Build refcounting procedures
|
||||
for (fn_name, section_id, proc_id, proc) in rc_names_symbols_procs {
|
||||
// Build helpers
|
||||
for (fn_name, section_id, proc_id, proc) in helper_names_symbols_procs {
|
||||
build_proc(
|
||||
&mut output,
|
||||
&mut backend,
|
||||
|
@ -285,7 +285,7 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
)
|
||||
}
|
||||
|
||||
// Relocations for all procedures (user code & refcounting)
|
||||
// Relocations for all procedures (user code & helpers)
|
||||
for (section_id, reloc) in relocations {
|
||||
match output.add_relocation(section_id, reloc) {
|
||||
Ok(obj) => obj,
|
||||
|
|
|
@ -15,8 +15,8 @@ use crate::llvm::build_list::{
|
|||
list_single, list_sort_with, list_sublist, list_swap,
|
||||
};
|
||||
use crate::llvm::build_str::{
|
||||
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
||||
str_from_utf8, str_from_utf8_range, str_join_with, str_number_of_bytes, str_repeat, str_split,
|
||||
str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8,
|
||||
str_from_utf8_range, str_join_with, str_number_of_bytes, str_repeat, str_split,
|
||||
str_starts_with, str_starts_with_code_point, str_to_utf8, str_trim, str_trim_left,
|
||||
str_trim_right,
|
||||
};
|
||||
|
@ -779,118 +779,109 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
|||
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
||||
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
||||
Str(str_literal) => {
|
||||
if str_literal.is_empty() {
|
||||
empty_str(env)
|
||||
} else {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
let number_of_chars = str_literal.len() as u64;
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
let number_of_chars = str_literal.len() as u64;
|
||||
|
||||
let str_type = super::convert::zig_str_type(env);
|
||||
let str_type = super::convert::zig_str_type(env);
|
||||
|
||||
if str_literal.len() < env.small_str_bytes() as usize {
|
||||
// TODO support big endian systems
|
||||
if str_literal.len() < env.small_str_bytes() as usize {
|
||||
// TODO support big endian systems
|
||||
|
||||
let array_alloca = builder.build_array_alloca(
|
||||
ctx.i8_type(),
|
||||
ctx.i8_type().const_int(env.small_str_bytes() as u64, false),
|
||||
"alloca_small_str",
|
||||
);
|
||||
let array_alloca = builder.build_array_alloca(
|
||||
ctx.i8_type(),
|
||||
ctx.i8_type().const_int(env.small_str_bytes() as u64, false),
|
||||
"alloca_small_str",
|
||||
);
|
||||
|
||||
// Zero out all the bytes. If we don't do this, then
|
||||
// small strings would have uninitialized bytes, which could
|
||||
// cause string equality checks to fail randomly.
|
||||
//
|
||||
// We're running memset over *all* the bytes, even though
|
||||
// the final one is about to be manually overridden, on
|
||||
// the theory that LLVM will optimize the memset call
|
||||
// into two instructions to move appropriately-sized
|
||||
// zero integers into the appropriate locations instead
|
||||
// of doing any iteration.
|
||||
//
|
||||
// TODO: look at the compiled output to verify this theory!
|
||||
env.call_memset(
|
||||
// Zero out all the bytes. If we don't do this, then
|
||||
// small strings would have uninitialized bytes, which could
|
||||
// cause string equality checks to fail randomly.
|
||||
//
|
||||
// We're running memset over *all* the bytes, even though
|
||||
// the final one is about to be manually overridden, on
|
||||
// the theory that LLVM will optimize the memset call
|
||||
// into two instructions to move appropriately-sized
|
||||
// zero integers into the appropriate locations instead
|
||||
// of doing any iteration.
|
||||
//
|
||||
// TODO: look at the compiled output to verify this theory!
|
||||
env.call_memset(
|
||||
array_alloca,
|
||||
ctx.i8_type().const_zero(),
|
||||
env.ptr_int().const_int(env.small_str_bytes() as u64, false),
|
||||
);
|
||||
|
||||
let final_byte = (str_literal.len() as u8) | 0b1000_0000;
|
||||
|
||||
let final_byte_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
array_alloca,
|
||||
ctx.i8_type().const_zero(),
|
||||
env.ptr_int().const_int(env.small_str_bytes() as u64, false),
|
||||
);
|
||||
|
||||
let final_byte = (str_literal.len() as u8) | 0b1000_0000;
|
||||
|
||||
let final_byte_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
array_alloca,
|
||||
&[ctx
|
||||
.i8_type()
|
||||
.const_int(env.small_str_bytes() as u64 - 1, false)],
|
||||
"str_literal_final_byte",
|
||||
)
|
||||
};
|
||||
|
||||
builder.build_store(
|
||||
final_byte_ptr,
|
||||
ctx.i8_type().const_int(final_byte as u64, false),
|
||||
);
|
||||
|
||||
// Copy the elements from the list literal into the array
|
||||
for (index, character) in str_literal.as_bytes().iter().enumerate() {
|
||||
let val = env
|
||||
.context
|
||||
&[ctx
|
||||
.i8_type()
|
||||
.const_int(*character as u64, false)
|
||||
.as_basic_value_enum();
|
||||
let index_val = ctx.i64_type().const_int(index as u64, false);
|
||||
let elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(array_alloca, &[index_val], "index")
|
||||
};
|
||||
|
||||
builder.build_store(elem_ptr, val);
|
||||
}
|
||||
|
||||
builder.build_load(
|
||||
builder
|
||||
.build_bitcast(
|
||||
array_alloca,
|
||||
str_type.ptr_type(AddressSpace::Generic),
|
||||
"cast_collection",
|
||||
)
|
||||
.into_pointer_value(),
|
||||
"small_str_array",
|
||||
.const_int(env.small_str_bytes() as u64 - 1, false)],
|
||||
"str_literal_final_byte",
|
||||
)
|
||||
} else {
|
||||
let ptr = define_global_str_literal_ptr(env, *str_literal);
|
||||
let number_of_elements = env.ptr_int().const_int(number_of_chars, false);
|
||||
};
|
||||
|
||||
let struct_type = str_type;
|
||||
builder.build_store(
|
||||
final_byte_ptr,
|
||||
ctx.i8_type().const_int(final_byte as u64, false),
|
||||
);
|
||||
|
||||
let mut struct_val;
|
||||
// Copy the elements from the list literal into the array
|
||||
for (index, character) in str_literal.as_bytes().iter().enumerate() {
|
||||
let val = env
|
||||
.context
|
||||
.i8_type()
|
||||
.const_int(*character as u64, false)
|
||||
.as_basic_value_enum();
|
||||
let index_val = ctx.i64_type().const_int(index as u64, false);
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(array_alloca, &[index_val], "index") };
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr_str_literal",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_val,
|
||||
number_of_elements,
|
||||
Builtin::WRAPPER_LEN,
|
||||
"insert_len",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
str_type,
|
||||
"cast_collection",
|
||||
)
|
||||
builder.build_store(elem_ptr, val);
|
||||
}
|
||||
|
||||
builder.build_load(
|
||||
builder
|
||||
.build_bitcast(
|
||||
array_alloca,
|
||||
str_type.ptr_type(AddressSpace::Generic),
|
||||
"cast_collection",
|
||||
)
|
||||
.into_pointer_value(),
|
||||
"small_str_array",
|
||||
)
|
||||
} else {
|
||||
let ptr = define_global_str_literal_ptr(env, *str_literal);
|
||||
let number_of_elements = env.ptr_int().const_int(number_of_chars, false);
|
||||
|
||||
let struct_type = str_type;
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr_str_literal",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_val,
|
||||
number_of_elements,
|
||||
Builtin::WRAPPER_LEN,
|
||||
"insert_len",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(struct_val.into_struct_value(), str_type, "cast_collection")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -432,12 +432,3 @@ pub fn str_equal<'a, 'ctx, 'env>(
|
|||
bitcode::STR_EQUAL,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO investigate: does this cause problems when the layout is known? this value is now not refcounted!
|
||||
pub fn empty_str<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
||||
let struct_type = super::convert::zig_str_type(env);
|
||||
|
||||
// The pointer should be null (aka zero) and the length should be zero,
|
||||
// so the whole struct should be a const_zero
|
||||
BasicValueEnum::StructValue(struct_type.const_zero())
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use bumpalo::{self, collections::Vec};
|
||||
|
||||
use code_builder::Align;
|
||||
use roc_builtins::bitcode::IntWidth;
|
||||
use roc_builtins::bitcode::{self, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::gen_refcount::{RefcountProcGenerator, REFCOUNT_MAX};
|
||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||
use roc_mono::code_gen_help::{CodeGenHelp, REFCOUNT_MAX};
|
||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, ProcLayout, Stmt};
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
||||
use roc_reporting::internal_error;
|
||||
|
||||
|
@ -49,7 +49,7 @@ pub struct WasmBackend<'a> {
|
|||
builtin_sym_index_map: MutMap<&'a str, usize>,
|
||||
proc_symbols: Vec<'a, (Symbol, u32)>,
|
||||
linker_symbols: Vec<'a, SymInfo>,
|
||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
|
||||
// Function-level data
|
||||
code_builder: CodeBuilder<'a>,
|
||||
|
@ -71,7 +71,7 @@ impl<'a> WasmBackend<'a> {
|
|||
proc_symbols: Vec<'a, (Symbol, u32)>,
|
||||
mut linker_symbols: Vec<'a, SymInfo>,
|
||||
mut exports: Vec<'a, Export>,
|
||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
) -> Self {
|
||||
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
|
||||
let arena = env.arena;
|
||||
|
@ -144,7 +144,7 @@ impl<'a> WasmBackend<'a> {
|
|||
builtin_sym_index_map: MutMap::default(),
|
||||
proc_symbols,
|
||||
linker_symbols,
|
||||
refcount_proc_gen,
|
||||
helper_proc_gen,
|
||||
|
||||
// Function-level data
|
||||
block_depth: 0,
|
||||
|
@ -157,15 +157,34 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_refcount_procs(&mut self) -> Vec<'a, Proc<'a>> {
|
||||
pub fn generate_helpers(&mut self) -> Vec<'a, Proc<'a>> {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
self.refcount_proc_gen
|
||||
.generate_refcount_procs(self.env.arena, ident_ids)
|
||||
self.helper_proc_gen
|
||||
.generate_procs(self.env.arena, ident_ids)
|
||||
}
|
||||
|
||||
fn register_helper_proc(&mut self, new_proc_info: (Symbol, ProcLayout<'a>)) {
|
||||
let (new_proc_sym, new_proc_layout) = new_proc_info;
|
||||
let wasm_fn_index = self.proc_symbols.len() as u32;
|
||||
let linker_sym_index = self.linker_symbols.len() as u32;
|
||||
|
||||
let name = self
|
||||
.layout_ids
|
||||
.get_toplevel(new_proc_sym, &new_proc_layout)
|
||||
.to_symbol_string(new_proc_sym, self.interns);
|
||||
|
||||
self.proc_symbols.push((new_proc_sym, linker_sym_index));
|
||||
self.linker_symbols
|
||||
.push(SymInfo::Function(WasmObjectSymbol::Defined {
|
||||
flags: 0,
|
||||
index: wasm_fn_index,
|
||||
name,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn finalize_module(mut self) -> WasmModule<'a> {
|
||||
|
@ -500,8 +519,8 @@ impl<'a> WasmBackend<'a> {
|
|||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
let (rc_stmt, new_proc_info) = self
|
||||
.refcount_proc_gen
|
||||
let (rc_stmt, new_specializations) = self
|
||||
.helper_proc_gen
|
||||
.expand_refcount_stmt(ident_ids, *layout, modify, *following);
|
||||
|
||||
if false {
|
||||
|
@ -509,24 +528,9 @@ impl<'a> WasmBackend<'a> {
|
|||
println!("## rc_stmt:\n{}\n{:?}", rc_stmt.to_pretty(200), rc_stmt);
|
||||
}
|
||||
|
||||
// If we're creating a new RC procedure, we need to store its symbol data,
|
||||
// so that we can correctly generate calls to it.
|
||||
if let Some((rc_proc_sym, rc_proc_layout)) = new_proc_info {
|
||||
let wasm_fn_index = self.proc_symbols.len() as u32;
|
||||
let linker_sym_index = self.linker_symbols.len() as u32;
|
||||
|
||||
let name = self
|
||||
.layout_ids
|
||||
.get_toplevel(rc_proc_sym, &rc_proc_layout)
|
||||
.to_symbol_string(rc_proc_sym, self.interns);
|
||||
|
||||
self.proc_symbols.push((rc_proc_sym, linker_sym_index));
|
||||
self.linker_symbols
|
||||
.push(SymInfo::Function(WasmObjectSymbol::Defined {
|
||||
flags: 0,
|
||||
index: wasm_fn_index,
|
||||
name,
|
||||
}));
|
||||
// If any new specializations were created, register their symbol data
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec);
|
||||
}
|
||||
|
||||
self.build_stmt(rc_stmt, ret_layout);
|
||||
|
@ -560,7 +564,13 @@ impl<'a> WasmBackend<'a> {
|
|||
CallType::ByName { name: func_sym, .. } => {
|
||||
// If this function is just a lowlevel wrapper, then inline it
|
||||
if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
|
||||
return self.build_low_level(lowlevel, arguments, *sym, wasm_layout);
|
||||
return self.build_low_level(
|
||||
lowlevel,
|
||||
arguments,
|
||||
*sym,
|
||||
wasm_layout,
|
||||
storage,
|
||||
);
|
||||
}
|
||||
|
||||
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
||||
|
@ -596,7 +606,7 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
CallType::LowLevel { op: lowlevel, .. } => {
|
||||
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout)
|
||||
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout, storage)
|
||||
}
|
||||
|
||||
x => todo!("call type {:?}", x),
|
||||
|
@ -925,6 +935,7 @@ impl<'a> WasmBackend<'a> {
|
|||
arguments: &'a [Symbol],
|
||||
return_sym: Symbol,
|
||||
return_layout: WasmLayout,
|
||||
storage: &StoredValue,
|
||||
) {
|
||||
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
||||
self.env.arena,
|
||||
|
@ -949,6 +960,37 @@ impl<'a> WasmBackend<'a> {
|
|||
BuiltinCall(name) => {
|
||||
self.call_zig_builtin(name, param_types, ret_type);
|
||||
}
|
||||
SpecializedEq | SpecializedNotEq => {
|
||||
let layout = self.symbol_layouts[&arguments[0]];
|
||||
|
||||
if layout == Layout::Builtin(Builtin::Str) {
|
||||
self.call_zig_builtin(bitcode::STR_EQUAL, param_types, ret_type);
|
||||
} else {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
let (replacement_expr, new_specializations) = self
|
||||
.helper_proc_gen
|
||||
.specialize_equals(ident_ids, &layout, arguments);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec);
|
||||
}
|
||||
|
||||
self.build_expr(&return_sym, replacement_expr, &layout, storage);
|
||||
}
|
||||
|
||||
if matches!(build_result, SpecializedNotEq) {
|
||||
self.code_builder.i32_eqz();
|
||||
}
|
||||
}
|
||||
SpecializedHash => {
|
||||
todo!("Specialized hash functions")
|
||||
}
|
||||
NotImplemented => {
|
||||
todo!("Low level operation {:?}", lowlevel)
|
||||
}
|
||||
|
@ -977,14 +1019,11 @@ impl<'a> WasmBackend<'a> {
|
|||
};
|
||||
}
|
||||
|
||||
StoredValue::StackMemory { location, .. } => match lit {
|
||||
Literal::Decimal(decimal) => {
|
||||
StoredValue::StackMemory { location, .. } => {
|
||||
let mut write128 = |lower_bits, upper_bits| {
|
||||
let (local_id, offset) =
|
||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||
|
||||
let lower_bits = decimal.0 as i64;
|
||||
let upper_bits = (decimal.0 >> 64) as i64;
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i64_const(lower_bits);
|
||||
self.code_builder.i64_store(Align::Bytes8, offset);
|
||||
|
@ -992,39 +1031,56 @@ impl<'a> WasmBackend<'a> {
|
|||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i64_const(upper_bits);
|
||||
self.code_builder.i64_store(Align::Bytes8, offset + 8);
|
||||
};
|
||||
|
||||
match lit {
|
||||
Literal::Decimal(decimal) => {
|
||||
let lower_bits = (decimal.0 & 0xffff_ffff_ffff_ffff) as i64;
|
||||
let upper_bits = (decimal.0 >> 64) as i64;
|
||||
write128(lower_bits, upper_bits);
|
||||
}
|
||||
Literal::Int(x) => {
|
||||
let lower_bits = (*x & 0xffff_ffff_ffff_ffff) as i64;
|
||||
let upper_bits = (*x >> 64) as i64;
|
||||
write128(lower_bits, upper_bits);
|
||||
}
|
||||
Literal::Float(_) => {
|
||||
// Also not implemented in LLVM backend (nor in Rust!)
|
||||
todo!("f128 type");
|
||||
}
|
||||
Literal::Str(string) => {
|
||||
let (local_id, offset) =
|
||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||
|
||||
let len = string.len();
|
||||
if len < 8 {
|
||||
let mut stack_mem_bytes = [0; 8];
|
||||
stack_mem_bytes[0..len].clone_from_slice(string.as_bytes());
|
||||
stack_mem_bytes[7] = 0x80 | (len as u8);
|
||||
let str_as_int = i64::from_le_bytes(stack_mem_bytes);
|
||||
|
||||
// Write all 8 bytes at once using an i64
|
||||
// Str is normally two i32's, but in this special case, we can get away with fewer instructions
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i64_const(str_as_int);
|
||||
self.code_builder.i64_store(Align::Bytes4, offset);
|
||||
} else {
|
||||
let (linker_sym_index, elements_addr) =
|
||||
self.lookup_string_constant(string, sym, layout);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder
|
||||
.i32_const_mem_addr(elements_addr, linker_sym_index);
|
||||
self.code_builder.i32_store(Align::Bytes4, offset);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i32_const(string.len() as i32);
|
||||
self.code_builder.i32_store(Align::Bytes4, offset + 4);
|
||||
};
|
||||
}
|
||||
_ => not_supported_error(),
|
||||
}
|
||||
Literal::Str(string) => {
|
||||
let (local_id, offset) =
|
||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||
|
||||
let len = string.len();
|
||||
if len < 8 {
|
||||
let mut stack_mem_bytes = [0; 8];
|
||||
stack_mem_bytes[0..len].clone_from_slice(string.as_bytes());
|
||||
stack_mem_bytes[7] = 0x80 | (len as u8);
|
||||
let str_as_int = i64::from_le_bytes(stack_mem_bytes);
|
||||
|
||||
// Write all 8 bytes at once using an i64
|
||||
// Str is normally two i32's, but in this special case, we can get away with fewer instructions
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i64_const(str_as_int);
|
||||
self.code_builder.i64_store(Align::Bytes4, offset);
|
||||
} else {
|
||||
let (linker_sym_index, elements_addr) =
|
||||
self.lookup_string_constant(string, sym, layout);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder
|
||||
.i32_const_mem_addr(elements_addr, linker_sym_index);
|
||||
self.code_builder.i32_store(Align::Bytes4, offset);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i32_const(string.len() as i32);
|
||||
self.code_builder.i32_store(Align::Bytes4, offset + 4);
|
||||
};
|
||||
}
|
||||
_ => not_supported_error(),
|
||||
},
|
||||
}
|
||||
|
||||
_ => not_supported_error(),
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ use roc_builtins::bitcode::IntWidth;
|
|||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
||||
use roc_mono::code_gen_help::CodeGenHelp;
|
||||
use roc_mono::ir::{Proc, ProcLayout};
|
||||
use roc_mono::layout::LayoutIds;
|
||||
use roc_reporting::internal_error;
|
||||
|
@ -94,7 +94,7 @@ pub fn build_module_help<'a>(
|
|||
proc_symbols,
|
||||
linker_symbols,
|
||||
exports,
|
||||
RefcountProcGenerator::new(env.arena, IntWidth::I32, env.module_id),
|
||||
CodeGenHelp::new(env.arena, IntWidth::I32, env.module_id),
|
||||
);
|
||||
|
||||
if false {
|
||||
|
@ -110,21 +110,21 @@ pub fn build_module_help<'a>(
|
|||
backend.build_proc(proc);
|
||||
}
|
||||
|
||||
// Generate IR for refcounting procs
|
||||
let refcount_procs = backend.generate_refcount_procs();
|
||||
// Generate specialized helpers for refcounting & equality
|
||||
let helper_procs = backend.generate_helpers();
|
||||
|
||||
backend.register_symbol_debug_names();
|
||||
|
||||
if false {
|
||||
println!("## refcount_procs");
|
||||
for proc in refcount_procs.iter() {
|
||||
println!("## helper_procs");
|
||||
for proc in helper_procs.iter() {
|
||||
println!("{}", proc.to_pretty(200));
|
||||
println!("{:#?}", proc);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Wasm for refcounting procs
|
||||
for proc in refcount_procs.iter() {
|
||||
for proc in helper_procs.iter() {
|
||||
backend.build_proc(proc);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,14 @@ use roc_reporting::internal_error;
|
|||
|
||||
use crate::layout::{StackMemoryFormat::*, WasmLayout};
|
||||
use crate::storage::{Storage, StoredValue};
|
||||
use crate::wasm_module::{CodeBuilder, ValueType::*};
|
||||
use crate::wasm_module::{Align, CodeBuilder, ValueType::*};
|
||||
|
||||
pub enum LowlevelBuildResult {
|
||||
Done,
|
||||
BuiltinCall(&'static str),
|
||||
SpecializedEq,
|
||||
SpecializedNotEq,
|
||||
SpecializedHash,
|
||||
NotImplemented,
|
||||
}
|
||||
|
||||
|
@ -17,7 +20,7 @@ pub fn decode_low_level<'a>(
|
|||
code_builder: &mut CodeBuilder<'a>,
|
||||
storage: &mut Storage<'a>,
|
||||
lowlevel: LowLevel,
|
||||
args: &'a [Symbol],
|
||||
args: &[Symbol],
|
||||
ret_layout: &WasmLayout,
|
||||
) -> LowlevelBuildResult {
|
||||
use LowlevelBuildResult::*;
|
||||
|
@ -81,8 +84,6 @@ pub fn decode_low_level<'a>(
|
|||
WasmLayout::Primitive(value_type, size) => match value_type {
|
||||
I32 => {
|
||||
code_builder.i32_add();
|
||||
// TODO: is *deliberate* wrapping really in the spirit of things?
|
||||
// The point of choosing NumAddWrap is to go fast by skipping checks, but we're making it slower.
|
||||
wrap_i32(code_builder, *size);
|
||||
}
|
||||
I64 => code_builder.i64_add(),
|
||||
|
@ -347,27 +348,56 @@ pub fn decode_low_level<'a>(
|
|||
},
|
||||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||
},
|
||||
NumIsFinite => match ret_layout {
|
||||
WasmLayout::Primitive(value_type, _) => match value_type {
|
||||
I32 => code_builder.i32_const(1),
|
||||
I64 => code_builder.i32_const(1),
|
||||
F32 => {
|
||||
code_builder.i32_reinterpret_f32();
|
||||
code_builder.i32_const(0x7f800000);
|
||||
code_builder.i32_and();
|
||||
code_builder.i32_const(0x7f800000);
|
||||
code_builder.i32_ne();
|
||||
NumIsFinite => {
|
||||
use StoredValue::*;
|
||||
match storage.get(&args[0]) {
|
||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||
match value_type {
|
||||
I32 | I64 => code_builder.i32_const(1), // always true for integers
|
||||
F32 => {
|
||||
code_builder.i32_reinterpret_f32();
|
||||
code_builder.i32_const(0x7f80_0000);
|
||||
code_builder.i32_and();
|
||||
code_builder.i32_const(0x7f80_0000);
|
||||
code_builder.i32_ne();
|
||||
}
|
||||
F64 => {
|
||||
code_builder.i64_reinterpret_f64();
|
||||
code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||
code_builder.i64_and();
|
||||
code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||
code_builder.i64_ne();
|
||||
}
|
||||
}
|
||||
}
|
||||
F64 => {
|
||||
code_builder.i64_reinterpret_f64();
|
||||
code_builder.i64_const(0x7ff0000000000000);
|
||||
code_builder.i64_and();
|
||||
code_builder.i64_const(0x7ff0000000000000);
|
||||
code_builder.i64_ne();
|
||||
StackMemory {
|
||||
format, location, ..
|
||||
} => {
|
||||
let (local_id, offset) = location.local_and_offset(storage.stack_frame_pointer);
|
||||
|
||||
match format {
|
||||
Int128 => code_builder.i32_const(1),
|
||||
Float128 => {
|
||||
code_builder.get_local(local_id);
|
||||
code_builder.i64_load(Align::Bytes4, offset + 8);
|
||||
code_builder.i64_const(0x7fff_0000_0000_0000);
|
||||
code_builder.i64_and();
|
||||
code_builder.i64_const(0x7fff_0000_0000_0000);
|
||||
code_builder.i64_ne();
|
||||
}
|
||||
Decimal => {
|
||||
code_builder.get_local(local_id);
|
||||
code_builder.i64_load(Align::Bytes4, offset + 8);
|
||||
code_builder.i64_const(0x7100_0000_0000_0000);
|
||||
code_builder.i64_and();
|
||||
code_builder.i64_const(0x7100_0000_0000_0000);
|
||||
code_builder.i64_ne();
|
||||
}
|
||||
DataStructure => return NotImplemented,
|
||||
}
|
||||
}
|
||||
},
|
||||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||
},
|
||||
}
|
||||
}
|
||||
NumAtan => {
|
||||
let width = float_width_from_layout(ret_layout);
|
||||
return BuiltinCall(&bitcode::NUM_ATAN[width]);
|
||||
|
@ -468,16 +498,80 @@ pub fn decode_low_level<'a>(
|
|||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||
}
|
||||
}
|
||||
Eq => match storage.get(&args[0]) {
|
||||
StoredValue::VirtualMachineStack { value_type, .. }
|
||||
| StoredValue::Local { value_type, .. } => match value_type {
|
||||
I32 => code_builder.i32_eq(),
|
||||
I64 => code_builder.i64_eq(),
|
||||
F32 => code_builder.f32_eq(),
|
||||
F64 => code_builder.f64_eq(),
|
||||
},
|
||||
StoredValue::StackMemory { .. } => return NotImplemented,
|
||||
},
|
||||
Eq => {
|
||||
use StoredValue::*;
|
||||
match storage.get(&args[0]).to_owned() {
|
||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||
match value_type {
|
||||
I32 => code_builder.i32_eq(),
|
||||
I64 => code_builder.i64_eq(),
|
||||
F32 => code_builder.f32_eq(),
|
||||
F64 => code_builder.f64_eq(),
|
||||
}
|
||||
}
|
||||
StackMemory {
|
||||
format,
|
||||
location: location0,
|
||||
..
|
||||
} => {
|
||||
if let StackMemory {
|
||||
location: location1,
|
||||
..
|
||||
} = storage.get(&args[1]).to_owned()
|
||||
{
|
||||
let stack_frame_pointer = storage.stack_frame_pointer;
|
||||
let compare_bytes = |code_builder: &mut CodeBuilder| {
|
||||
let (local0, offset0) = location0.local_and_offset(stack_frame_pointer);
|
||||
let (local1, offset1) = location1.local_and_offset(stack_frame_pointer);
|
||||
|
||||
code_builder.get_local(local0);
|
||||
code_builder.i64_load(Align::Bytes8, offset0);
|
||||
code_builder.get_local(local1);
|
||||
code_builder.i64_load(Align::Bytes8, offset1);
|
||||
code_builder.i64_eq();
|
||||
|
||||
code_builder.get_local(local0);
|
||||
code_builder.i64_load(Align::Bytes8, offset0 + 8);
|
||||
code_builder.get_local(local1);
|
||||
code_builder.i64_load(Align::Bytes8, offset1 + 8);
|
||||
code_builder.i64_eq();
|
||||
|
||||
code_builder.i32_and();
|
||||
};
|
||||
|
||||
match format {
|
||||
Decimal => {
|
||||
// Both args are finite
|
||||
let first = [args[0]];
|
||||
let second = [args[1]];
|
||||
decode_low_level(
|
||||
code_builder,
|
||||
storage,
|
||||
LowLevel::NumIsFinite,
|
||||
&first,
|
||||
ret_layout,
|
||||
);
|
||||
decode_low_level(
|
||||
code_builder,
|
||||
storage,
|
||||
LowLevel::NumIsFinite,
|
||||
&second,
|
||||
ret_layout,
|
||||
);
|
||||
code_builder.i32_and();
|
||||
|
||||
// AND they have the same bytes
|
||||
compare_bytes(code_builder);
|
||||
code_builder.i32_and();
|
||||
}
|
||||
Int128 => compare_bytes(code_builder),
|
||||
Float128 => return NotImplemented,
|
||||
DataStructure => return SpecializedEq,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NotEq => match storage.get(&args[0]) {
|
||||
StoredValue::VirtualMachineStack { value_type, .. }
|
||||
| StoredValue::Local { value_type, .. } => match value_type {
|
||||
|
@ -486,12 +580,19 @@ pub fn decode_low_level<'a>(
|
|||
F32 => code_builder.f32_ne(),
|
||||
F64 => code_builder.f64_ne(),
|
||||
},
|
||||
StoredValue::StackMemory { .. } => return NotImplemented,
|
||||
StoredValue::StackMemory { format, .. } => {
|
||||
if matches!(format, DataStructure) {
|
||||
return SpecializedNotEq;
|
||||
} else {
|
||||
decode_low_level(code_builder, storage, LowLevel::Eq, args, ret_layout);
|
||||
code_builder.i32_eqz();
|
||||
}
|
||||
}
|
||||
},
|
||||
And => code_builder.i32_and(),
|
||||
Or => code_builder.i32_or(),
|
||||
Not => code_builder.i32_eqz(),
|
||||
Hash => return NotImplemented,
|
||||
Hash => return SpecializedHash,
|
||||
ExpectTrue => return NotImplemented,
|
||||
RefCountGetPtr => {
|
||||
code_builder.i32_const(4);
|
||||
|
|
|
@ -23,14 +23,13 @@ use roc_mono::ir::{
|
|||
UpdateModeIds,
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
||||
use roc_parse::ast::{self, ExtractSpaces, StrLiteral, TypeAnnotation};
|
||||
use roc_parse::ast::{self, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation};
|
||||
use roc_parse::header::{
|
||||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
ExposedName, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{self, ParseProblem, Parser, SyntaxError};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_reporting::internal_error;
|
||||
use roc_solve::module::SolvedModule;
|
||||
use roc_solve::solve;
|
||||
use roc_types::solved_types::Solved;
|
||||
|
@ -685,7 +684,7 @@ enum HeaderFor<'a> {
|
|||
to_platform: To<'a>,
|
||||
},
|
||||
PkgConfig {
|
||||
/// usually `base`
|
||||
/// usually `pf`
|
||||
config_shorthand: &'a str,
|
||||
/// the type scheme of the main function (required by the platform)
|
||||
/// (currently unused)
|
||||
|
@ -2554,8 +2553,8 @@ fn parse_header<'a>(
|
|||
opt_shorthand,
|
||||
header_src,
|
||||
packages: &[],
|
||||
exposes: header.exposes.items,
|
||||
imports: header.imports.items,
|
||||
exposes: unspace(arena, header.exposes.items),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
to_platform: None,
|
||||
};
|
||||
|
||||
|
@ -2576,7 +2575,7 @@ fn parse_header<'a>(
|
|||
std::str::from_utf8_unchecked(&src_bytes[..chomped])
|
||||
};
|
||||
|
||||
let packages = header.packages.items;
|
||||
let packages = unspace(arena, header.packages.items);
|
||||
|
||||
let info = HeaderInfo {
|
||||
loc_name: Located {
|
||||
|
@ -2588,8 +2587,8 @@ fn parse_header<'a>(
|
|||
opt_shorthand,
|
||||
header_src,
|
||||
packages,
|
||||
exposes: header.provides.items,
|
||||
imports: header.imports.items,
|
||||
exposes: unspace(arena, header.provides.items),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
to_platform: Some(header.to.value),
|
||||
};
|
||||
|
||||
|
@ -2603,62 +2602,56 @@ fn parse_header<'a>(
|
|||
|
||||
match header.to.value {
|
||||
To::ExistingPackage(existing_package) => {
|
||||
let opt_base_package = packages.iter().find(|loc_package_entry| {
|
||||
let opt_base_package = packages.iter().find_map(|loc_package_entry| {
|
||||
let Located { value, .. } = loc_package_entry;
|
||||
|
||||
match value.extract_spaces().item {
|
||||
PackageEntry::Entry { shorthand, .. } => shorthand == existing_package,
|
||||
_ => internal_error!(),
|
||||
if value.shorthand == existing_package {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
match opt_base_package {
|
||||
Some(Located {
|
||||
value:
|
||||
PackageEntry::Entry {
|
||||
shorthand,
|
||||
package_or_path:
|
||||
Located {
|
||||
value: package_or_path,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
match package_or_path {
|
||||
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
|
||||
// check whether we can find a Package-Config.roc file
|
||||
let mut pkg_config_roc = pkg_config_dir;
|
||||
pkg_config_roc.push(package);
|
||||
pkg_config_roc.push(PKG_CONFIG_FILE_NAME);
|
||||
pkg_config_roc.set_extension(ROC_FILE_EXTENSION);
|
||||
if let Some(PackageEntry {
|
||||
shorthand,
|
||||
package_or_path:
|
||||
Located {
|
||||
value: package_or_path,
|
||||
..
|
||||
},
|
||||
..
|
||||
}) = opt_base_package
|
||||
{
|
||||
match package_or_path {
|
||||
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
|
||||
// check whether we can find a Package-Config.roc file
|
||||
let mut pkg_config_roc = pkg_config_dir;
|
||||
pkg_config_roc.push(package);
|
||||
pkg_config_roc.push(PKG_CONFIG_FILE_NAME);
|
||||
pkg_config_roc.set_extension(ROC_FILE_EXTENSION);
|
||||
|
||||
if pkg_config_roc.as_path().exists() {
|
||||
let load_pkg_config_msg = load_pkg_config(
|
||||
arena,
|
||||
&pkg_config_roc,
|
||||
shorthand,
|
||||
module_id,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
)?;
|
||||
if pkg_config_roc.as_path().exists() {
|
||||
let load_pkg_config_msg = load_pkg_config(
|
||||
arena,
|
||||
&pkg_config_roc,
|
||||
shorthand,
|
||||
module_id,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
module_id,
|
||||
Msg::Many(vec![
|
||||
app_module_header_msg,
|
||||
load_pkg_config_msg,
|
||||
]),
|
||||
))
|
||||
} else {
|
||||
Ok((module_id, app_module_header_msg))
|
||||
}
|
||||
Ok((
|
||||
module_id,
|
||||
Msg::Many(vec![app_module_header_msg, load_pkg_config_msg]),
|
||||
))
|
||||
} else {
|
||||
Ok((module_id, app_module_header_msg))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
_ => panic!("could not find base"),
|
||||
} else {
|
||||
panic!("could not find base")
|
||||
}
|
||||
}
|
||||
To::NewPackage(package_or_path) => match package_or_path {
|
||||
|
@ -2766,7 +2759,7 @@ struct HeaderInfo<'a> {
|
|||
opt_shorthand: Option<&'a str>,
|
||||
header_src: &'a str,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
exposes: &'a [Located<ExposedName<'a>>],
|
||||
imports: &'a [Located<ImportsEntry<'a>>],
|
||||
to_platform: Option<To<'a>>,
|
||||
}
|
||||
|
@ -2846,7 +2839,7 @@ fn send_header<'a>(
|
|||
|
||||
// For each of our imports, add an entry to deps_by_name
|
||||
//
|
||||
// e.g. for `imports [ base.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||
// e.g. for `imports [ pf.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||
//
|
||||
// Also build a list of imported_values_to_expose (like `bar` above.)
|
||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||
|
@ -2913,24 +2906,13 @@ fn send_header<'a>(
|
|||
ident_ids.clone()
|
||||
};
|
||||
|
||||
let mut parse_entries: Vec<_> = packages.iter().map(|x| &x.value).collect();
|
||||
let mut package_entries = MutMap::default();
|
||||
|
||||
while let Some(parse_entry) = parse_entries.pop() {
|
||||
use PackageEntry::*;
|
||||
match parse_entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
package_or_path,
|
||||
..
|
||||
} => {
|
||||
package_entries.insert(*shorthand, package_or_path.value);
|
||||
}
|
||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||
parse_entries.push(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
let package_entries = packages
|
||||
.iter()
|
||||
.map(|pkg| {
|
||||
let pkg = pkg.value;
|
||||
(pkg.shorthand, pkg.package_or_path.value)
|
||||
})
|
||||
.collect::<MutMap<_, _>>();
|
||||
|
||||
// Send the deps to the coordinator thread for processing,
|
||||
// then continue on to parsing and canonicalizing defs.
|
||||
|
@ -2989,7 +2971,7 @@ struct PlatformHeaderInfo<'a> {
|
|||
header_src: &'a str,
|
||||
app_module_id: ModuleId,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
provides: &'a [Located<ExposedName<'a>>],
|
||||
requires: &'a [Located<TypedIdent<'a>>],
|
||||
imports: &'a [Located<ImportsEntry<'a>>],
|
||||
}
|
||||
|
@ -2997,7 +2979,6 @@ struct PlatformHeaderInfo<'a> {
|
|||
// TODO refactor so more logic is shared with `send_header`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn send_header_two<'a>(
|
||||
arena: &'a Bump,
|
||||
info: PlatformHeaderInfo<'a>,
|
||||
parse_state: parser::State<'a>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
|
@ -3067,7 +3048,7 @@ fn send_header_two<'a>(
|
|||
|
||||
// For each of our imports, add an entry to deps_by_name
|
||||
//
|
||||
// e.g. for `imports [ base.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||
// e.g. for `imports [ pf.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||
//
|
||||
// Also build a list of imported_values_to_expose (like `bar` above.)
|
||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||
|
@ -3105,15 +3086,17 @@ fn send_header_two<'a>(
|
|||
.entry(app_module_id)
|
||||
.or_insert_with(IdentIds::default);
|
||||
|
||||
for (loc_ident, _) in unpack_exposes_entries(arena, requires) {
|
||||
let ident: Ident = loc_ident.value.into();
|
||||
for entry in requires {
|
||||
let entry = entry.value;
|
||||
|
||||
let ident: Ident = entry.ident.value.into();
|
||||
let ident_id = ident_ids.get_or_insert(&ident);
|
||||
let symbol = Symbol::new(app_module_id, ident_id);
|
||||
|
||||
// Since this value is exposed, add it to our module's default scope.
|
||||
debug_assert!(!scope.contains_key(&ident.clone()));
|
||||
|
||||
scope.insert(ident, (symbol, loc_ident.region));
|
||||
scope.insert(ident, (symbol, entry.ident.region));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3146,24 +3129,10 @@ fn send_header_two<'a>(
|
|||
ident_ids.clone()
|
||||
};
|
||||
|
||||
let mut parse_entries: Vec<_> = packages.iter().map(|x| &x.value).collect();
|
||||
let mut package_entries = MutMap::default();
|
||||
|
||||
while let Some(parse_entry) = parse_entries.pop() {
|
||||
use PackageEntry::*;
|
||||
match parse_entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
package_or_path,
|
||||
..
|
||||
} => {
|
||||
package_entries.insert(*shorthand, package_or_path.value);
|
||||
}
|
||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||
parse_entries.push(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
let package_entries = packages
|
||||
.iter()
|
||||
.map(|pkg| (pkg.value.shorthand, pkg.value.package_or_path.value))
|
||||
.collect::<MutMap<_, _>>();
|
||||
|
||||
// Send the deps to the coordinator thread for processing,
|
||||
// then continue on to parsing and canonicalizing defs.
|
||||
|
@ -3342,6 +3311,16 @@ fn run_solve<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn unspace<'a, T: Copy>(arena: &'a Bump, items: &[Located<Spaced<'a, T>>]) -> &'a [Located<T>] {
|
||||
bumpalo::collections::Vec::from_iter_in(
|
||||
items
|
||||
.iter()
|
||||
.map(|item| Located::at(item.region, item.value.extract_spaces().item)),
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn fabricate_pkg_config_module<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -3355,8 +3334,6 @@ fn fabricate_pkg_config_module<'a>(
|
|||
header_src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] = header.provides.items;
|
||||
|
||||
let info = PlatformHeaderInfo {
|
||||
filename,
|
||||
is_root_module: false,
|
||||
|
@ -3364,13 +3341,15 @@ fn fabricate_pkg_config_module<'a>(
|
|||
header_src,
|
||||
app_module_id,
|
||||
packages: &[],
|
||||
provides,
|
||||
requires: arena.alloc([header.requires.signature]),
|
||||
imports: header.imports.items,
|
||||
provides: unspace(arena, header.provides.items),
|
||||
requires: &*arena.alloc([Located::at(
|
||||
header.requires.signature.region,
|
||||
header.requires.signature.extract_spaces().item,
|
||||
)]),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
};
|
||||
|
||||
send_header_two(
|
||||
arena,
|
||||
info,
|
||||
parse_state,
|
||||
module_ids,
|
||||
|
@ -3413,12 +3392,12 @@ fn fabricate_effects_module<'a>(
|
|||
let mut module_ids = (*module_ids).lock();
|
||||
|
||||
for exposed in header.exposes.iter() {
|
||||
if let ExposesEntry::Exposed(module_name) = exposed.value {
|
||||
module_ids.get_or_insert(&PQModuleName::Qualified(
|
||||
shorthand,
|
||||
module_name.as_str().into(),
|
||||
));
|
||||
}
|
||||
let module_name = exposed.value.extract_spaces().item;
|
||||
|
||||
module_ids.get_or_insert(&PQModuleName::Qualified(
|
||||
shorthand,
|
||||
module_name.as_str().into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3633,33 +3612,16 @@ fn fabricate_effects_module<'a>(
|
|||
|
||||
fn unpack_exposes_entries<'a>(
|
||||
arena: &'a Bump,
|
||||
entries: &'a [Located<TypedIdent<'a>>],
|
||||
) -> bumpalo::collections::Vec<'a, (&'a Located<&'a str>, &'a Located<TypeAnnotation<'a>>)> {
|
||||
entries: &'a [Located<Spaced<'a, TypedIdent<'a>>>],
|
||||
) -> bumpalo::collections::Vec<'a, (Located<&'a str>, Located<TypeAnnotation<'a>>)> {
|
||||
use bumpalo::collections::Vec;
|
||||
|
||||
let mut stack: Vec<&TypedIdent> = Vec::with_capacity_in(entries.len(), arena);
|
||||
let mut output = Vec::with_capacity_in(entries.len(), arena);
|
||||
let iter = entries.iter().map(|entry| {
|
||||
let entry: TypedIdent<'a> = entry.value.extract_spaces().item;
|
||||
(entry.ident, entry.ann)
|
||||
});
|
||||
|
||||
for entry in entries.iter() {
|
||||
stack.push(&entry.value);
|
||||
}
|
||||
|
||||
while let Some(effects_entry) = stack.pop() {
|
||||
match effects_entry {
|
||||
TypedIdent::Entry {
|
||||
ident,
|
||||
spaces_before_colon: _,
|
||||
ann,
|
||||
} => {
|
||||
output.push((ident, ann));
|
||||
}
|
||||
TypedIdent::SpaceAfter(nested, _) | TypedIdent::SpaceBefore(nested, _) => {
|
||||
stack.push(nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
Vec::from_iter_in(iter, arena)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -3849,21 +3811,11 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>
|
|||
|
||||
(qualified_module_name, exposed)
|
||||
}
|
||||
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
|
||||
// Ignore spaces.
|
||||
exposed_from_import(*sub_entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ident_from_exposed(entry: &ExposesEntry<'_, &str>) -> Ident {
|
||||
use roc_parse::header::ExposesEntry::*;
|
||||
|
||||
match entry {
|
||||
Exposed(ident) => (*ident).into(),
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => ident_from_exposed(sub_entry),
|
||||
}
|
||||
fn ident_from_exposed(entry: &Spaced<'_, ExposedName<'_>>) -> Ident {
|
||||
entry.extract_spaces().item.as_str().into()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -4429,7 +4381,7 @@ fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> Strin
|
|||
alloc.reflow("I could not find a platform based on your input file."),
|
||||
alloc.reflow(r"Does the module header contain an entry that looks like this:"),
|
||||
alloc
|
||||
.parser_suggestion(" packages { base: \"platform\" }")
|
||||
.parser_suggestion(" packages { pf: \"platform\" }")
|
||||
.indent(4),
|
||||
alloc.reflow("See also TODO."),
|
||||
]);
|
||||
|
|
|
@ -359,7 +359,7 @@ impl fmt::Debug for ModuleId {
|
|||
}
|
||||
}
|
||||
|
||||
/// base.Task
|
||||
/// pf.Task
|
||||
/// 1. build mapping from short name to package
|
||||
/// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId
|
||||
/// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::ir::{
|
|||
BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, Literal, ModifyRc, Proc,
|
||||
ProcLayout, SelfRecursive, Stmt, UpdateModeId,
|
||||
};
|
||||
use crate::layout::{Builtin, Layout};
|
||||
use crate::layout::{Builtin, Layout, UnionLayout};
|
||||
|
||||
const LAYOUT_BOOL: Layout = Layout::Builtin(Builtin::Bool);
|
||||
const LAYOUT_UNIT: Layout = Layout::Struct(&[]);
|
||||
|
@ -21,71 +21,79 @@ const LAYOUT_U32: Layout = Layout::Builtin(Builtin::Int(IntWidth::U32));
|
|||
pub const REFCOUNT_MAX: usize = 0;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum RefcountOp {
|
||||
pub enum HelperOp {
|
||||
Inc,
|
||||
Dec,
|
||||
DecRef,
|
||||
Eq,
|
||||
}
|
||||
|
||||
/// Generate specialized refcounting code in mono IR format
|
||||
/// -------------------------------------------------------
|
||||
impl From<&ModifyRc> for HelperOp {
|
||||
fn from(modify: &ModifyRc) -> Self {
|
||||
match modify {
|
||||
ModifyRc::Inc(..) => Self::Inc,
|
||||
ModifyRc::Dec(_) => Self::Dec,
|
||||
ModifyRc::DecRef(_) => Self::DecRef,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate specialized helper procs for code gen
|
||||
/// ----------------------------------------------
|
||||
///
|
||||
/// Any backend that wants to use this, needs a field of type `RefcountProcGenerator`.
|
||||
/// Some low level operations need specialized helper procs to traverse data structures at runtime.
|
||||
/// This includes refcounting, hashing, and equality checks.
|
||||
///
|
||||
/// Whenever the backend sees a `Stmt::Refcounting`, it calls
|
||||
/// `RefcountProcGenerator::expand_refcount_stmt()`, which returns IR statements
|
||||
/// to call a refcounting procedure. The backend can then generate target code
|
||||
/// for those IR statements instead of the original `Refcounting` statement.
|
||||
/// For example, when checking List equality, we need to visit each element and compare them.
|
||||
/// Depending on the type of the list elements, we may need to recurse deeper into each element.
|
||||
/// For tag unions, we may need branches for different tag IDs, etc.
|
||||
///
|
||||
/// Essentially we are expanding the `Refcounting` statement into a more detailed
|
||||
/// form that's more suitable for code generation.
|
||||
/// This module creates specialized helper procs for all such operations and types used in the program.
|
||||
///
|
||||
/// But so far, we've only mentioned _calls_ to the refcounting procedures.
|
||||
/// The procedures themselves don't exist yet!
|
||||
/// The backend drives the process, in two steps:
|
||||
/// 1) When it sees the relevant node, it calls CodeGenHelp to get the replacement IR.
|
||||
/// CodeGenHelp returns IR for a call to the helper proc, and remembers the specialization.
|
||||
/// 2) After the backend has generated code for all user procs, it takes the IR for all of the
|
||||
/// specialized helpers procs, and generates target code for them too.
|
||||
///
|
||||
/// So when the backend has finished with all the `Proc`s from user code,
|
||||
/// it's time to call `RefcountProcGenerator::generate_refcount_procs()`,
|
||||
/// which generates the `Procs` for refcounting helpers. The backend can
|
||||
/// simply generate target code for these `Proc`s just like any other Proc.
|
||||
///
|
||||
pub struct RefcountProcGenerator<'a> {
|
||||
pub struct CodeGenHelp<'a> {
|
||||
arena: &'a Bump,
|
||||
home: ModuleId,
|
||||
ptr_size: u32,
|
||||
layout_isize: Layout<'a>,
|
||||
/// List of refcounting procs to generate, specialised by Layout and RefCountOp
|
||||
/// Specializations to generate
|
||||
/// Order of insertion is preserved, since it is important for Wasm backend
|
||||
procs_to_generate: Vec<'a, (Layout<'a>, RefcountOp, Symbol)>,
|
||||
specs: Vec<'a, (Layout<'a>, HelperOp, Symbol)>,
|
||||
}
|
||||
|
||||
impl<'a> RefcountProcGenerator<'a> {
|
||||
impl<'a> CodeGenHelp<'a> {
|
||||
pub fn new(arena: &'a Bump, intwidth_isize: IntWidth, home: ModuleId) -> Self {
|
||||
RefcountProcGenerator {
|
||||
CodeGenHelp {
|
||||
arena,
|
||||
home,
|
||||
ptr_size: intwidth_isize.stack_size(),
|
||||
layout_isize: Layout::Builtin(Builtin::Int(intwidth_isize)),
|
||||
procs_to_generate: Vec::with_capacity_in(16, arena),
|
||||
specs: Vec::with_capacity_in(16, arena),
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands the IR node Stmt::Refcounting to a more detailed IR Stmt that calls a helper proc.
|
||||
/// The helper procs themselves can be generated later by calling `generate_refcount_procs`
|
||||
/// Expand a `Refcounting` node to a `Let` node that calls a specialized helper proc.
|
||||
/// The helper procs themselves are to be generated later with `generate_procs`
|
||||
pub fn expand_refcount_stmt(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: Layout<'a>,
|
||||
modify: &ModifyRc,
|
||||
following: &'a Stmt<'a>,
|
||||
) -> (&'a Stmt<'a>, Option<(Symbol, ProcLayout<'a>)>) {
|
||||
if !Self::layout_is_supported(&layout) {
|
||||
) -> (&'a Stmt<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
if !Self::is_rc_implemented_yet(&layout) {
|
||||
// Just a warning, so we can decouple backend development from refcounting development.
|
||||
// When we are closer to completion, we can change it to a panic.
|
||||
println!(
|
||||
"WARNING! MEMORY LEAK! Refcounting not yet implemented for Layout {:?}",
|
||||
layout
|
||||
);
|
||||
return (following, None);
|
||||
return (following, Vec::new_in(self.arena));
|
||||
}
|
||||
|
||||
let arena = self.arena;
|
||||
|
@ -94,8 +102,8 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
ModifyRc::Inc(structure, amount) => {
|
||||
let layout_isize = self.layout_isize;
|
||||
|
||||
let (is_existing, proc_name) =
|
||||
self.get_proc_symbol(ident_ids, layout, RefcountOp::Inc);
|
||||
let (proc_name, new_procs_info) =
|
||||
self.get_or_create_proc_symbols_recursive(ident_ids, &layout, HelperOp::Inc);
|
||||
|
||||
// Define a constant for the amount to increment
|
||||
let amount_sym = self.create_symbol(ident_ids, "amount");
|
||||
|
@ -117,28 +125,14 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
|
||||
let rc_stmt = arena.alloc(amount_stmt(arena.alloc(call_stmt)));
|
||||
|
||||
// Create a linker symbol for the helper proc if this is the first usage
|
||||
let new_proc_info = if is_existing {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
proc_name,
|
||||
ProcLayout {
|
||||
arguments: arg_layouts,
|
||||
result: LAYOUT_UNIT,
|
||||
},
|
||||
))
|
||||
};
|
||||
|
||||
(rc_stmt, new_proc_info)
|
||||
(rc_stmt, new_procs_info)
|
||||
}
|
||||
|
||||
ModifyRc::Dec(structure) => {
|
||||
let (is_existing, proc_name) =
|
||||
self.get_proc_symbol(ident_ids, layout, RefcountOp::Dec);
|
||||
let (proc_name, new_procs_info) =
|
||||
self.get_or_create_proc_symbols_recursive(ident_ids, &layout, HelperOp::Dec);
|
||||
|
||||
// Call helper proc, passing the Roc structure
|
||||
let arg_layouts = arena.alloc([layout, self.layout_isize]);
|
||||
let call_result_empty = self.create_symbol(ident_ids, "call_result_empty");
|
||||
let call_expr = Expr::Call(Call {
|
||||
call_type: CallType::ByName {
|
||||
|
@ -157,20 +151,7 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
following,
|
||||
));
|
||||
|
||||
// Create a linker symbol for the helper proc if this is the first usage
|
||||
let new_proc_info = if is_existing {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
proc_name,
|
||||
ProcLayout {
|
||||
arguments: arg_layouts,
|
||||
result: LAYOUT_UNIT,
|
||||
},
|
||||
))
|
||||
};
|
||||
|
||||
(rc_stmt, new_proc_info)
|
||||
(rc_stmt, new_procs_info)
|
||||
}
|
||||
|
||||
ModifyRc::DecRef(structure) => {
|
||||
|
@ -199,67 +180,202 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
|
||||
let rc_stmt = arena.alloc(rc_ptr_stmt(arena.alloc(call_stmt)));
|
||||
|
||||
(rc_stmt, None)
|
||||
(rc_stmt, Vec::new_in(self.arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider refactoring so that we have just one place to define what's supported
|
||||
// (Probably by generating procs on the fly instead of all at the end)
|
||||
fn layout_is_supported(layout: &Layout) -> bool {
|
||||
/// Replace a generic `Lowlevel::Eq` call with a specialized helper proc.
|
||||
/// The helper procs themselves are to be generated later with `generate_procs`
|
||||
pub fn specialize_equals(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: &Layout<'a>,
|
||||
arguments: &'a [Symbol],
|
||||
) -> (&'a Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
// Record a specialization and get its name
|
||||
let (proc_name, new_procs_info) =
|
||||
self.get_or_create_proc_symbols_recursive(ident_ids, layout, HelperOp::Eq);
|
||||
|
||||
// Call the specialized helper
|
||||
let arg_layouts = self.arena.alloc([*layout, *layout]);
|
||||
let expr = self.arena.alloc(Expr::Call(Call {
|
||||
call_type: CallType::ByName {
|
||||
name: proc_name,
|
||||
ret_layout: &LAYOUT_BOOL,
|
||||
arg_layouts,
|
||||
specialization_id: CallSpecId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments,
|
||||
}));
|
||||
|
||||
(expr, new_procs_info)
|
||||
}
|
||||
|
||||
// Check if refcounting is implemented yet. In the long term, this will be deleted.
|
||||
// In the short term, it helps us to skip refcounting and let it leak, so we can make
|
||||
// progress incrementally. Kept in sync with generate_procs using assertions.
|
||||
fn is_rc_implemented_yet(layout: &Layout) -> bool {
|
||||
matches!(layout, Layout::Builtin(Builtin::Str))
|
||||
}
|
||||
|
||||
/// Generate refcounting helper procs, each specialized to a particular Layout.
|
||||
/// For example `List (Result { a: Str, b: Int } Str)` would get its own helper
|
||||
/// to update the refcounts on the List, the Result and the strings.
|
||||
pub fn generate_refcount_procs(
|
||||
pub fn generate_procs(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
ident_ids: &mut IdentIds,
|
||||
) -> Vec<'a, Proc<'a>> {
|
||||
// Move the vector out of self, so we can loop over it safely
|
||||
let mut procs_to_generate =
|
||||
std::mem::replace(&mut self.procs_to_generate, Vec::with_capacity_in(0, arena));
|
||||
use HelperOp::*;
|
||||
|
||||
let procs_iter = procs_to_generate
|
||||
.drain(0..)
|
||||
.map(|(layout, op, proc_symbol)| {
|
||||
debug_assert!(Self::layout_is_supported(&layout));
|
||||
// Move the vector out of self, so we can loop over it safely
|
||||
let mut specs = std::mem::replace(&mut self.specs, Vec::with_capacity_in(0, arena));
|
||||
|
||||
let procs_iter = specs.drain(0..).map(|(layout, op, proc_symbol)| match op {
|
||||
Inc | Dec | DecRef => {
|
||||
debug_assert!(Self::is_rc_implemented_yet(&layout));
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
self.gen_modify_str(ident_ids, op, proc_symbol)
|
||||
}
|
||||
|
||||
_ => todo!("Please update layout_is_supported for {:?}", layout),
|
||||
_ => todo!("Please update is_rc_implemented_yet for `{:?}`", layout),
|
||||
}
|
||||
});
|
||||
}
|
||||
Eq => match layout {
|
||||
Layout::Builtin(
|
||||
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal,
|
||||
) => panic!(
|
||||
"No generated helper proc. Use direct code gen for {:?}",
|
||||
layout
|
||||
),
|
||||
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
panic!("No generated helper proc. Use Zig builtin for Str.")
|
||||
}
|
||||
|
||||
_ => todo!("Specialized equality check for `{:?}`", layout),
|
||||
},
|
||||
});
|
||||
|
||||
Vec::from_iter_in(procs_iter, arena)
|
||||
}
|
||||
|
||||
/// Find the Symbol of the procedure for this layout and refcount operation,
|
||||
/// or create one if needed.
|
||||
fn get_proc_symbol(
|
||||
/// Find the Symbol of the procedure for this layout and operation
|
||||
/// If any new helper procs are needed for this layout or its children,
|
||||
/// return their details in a vector.
|
||||
fn get_or_create_proc_symbols_recursive(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: Layout<'a>,
|
||||
op: RefcountOp,
|
||||
) -> (bool, Symbol) {
|
||||
let found = self
|
||||
.procs_to_generate
|
||||
.iter()
|
||||
.find(|(l, o, _)| *l == layout && *o == op);
|
||||
layout: &Layout<'a>,
|
||||
op: HelperOp,
|
||||
) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut new_procs_info = Vec::new_in(self.arena);
|
||||
|
||||
let proc_symbol =
|
||||
self.get_or_create_proc_symbols_visit(ident_ids, &mut new_procs_info, op, layout);
|
||||
|
||||
(proc_symbol, new_procs_info)
|
||||
}
|
||||
|
||||
fn get_or_create_proc_symbols_visit(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
new_procs_info: &mut Vec<'a, (Symbol, ProcLayout<'a>)>,
|
||||
op: HelperOp,
|
||||
layout: &Layout<'a>,
|
||||
) -> Symbol {
|
||||
if let Layout::LambdaSet(lambda_set) = layout {
|
||||
return self.get_or_create_proc_symbols_visit(
|
||||
ident_ids,
|
||||
new_procs_info,
|
||||
op,
|
||||
&lambda_set.runtime_representation(),
|
||||
);
|
||||
}
|
||||
|
||||
let (symbol, new_proc_layout) = self.get_or_create_proc_symbol(ident_ids, layout, op);
|
||||
|
||||
if let Some(proc_layout) = new_proc_layout {
|
||||
new_procs_info.push((symbol, proc_layout));
|
||||
|
||||
let mut visit_child = |child| {
|
||||
self.get_or_create_proc_symbols_visit(ident_ids, new_procs_info, op, child);
|
||||
};
|
||||
|
||||
let mut visit_children = |children: &'a [Layout]| {
|
||||
for child in children {
|
||||
visit_child(child);
|
||||
}
|
||||
};
|
||||
|
||||
let mut visit_tags = |tags: &'a [&'a [Layout]]| {
|
||||
for tag in tags {
|
||||
visit_children(tag);
|
||||
}
|
||||
};
|
||||
|
||||
match layout {
|
||||
Layout::Builtin(builtin) => match builtin {
|
||||
Builtin::Dict(key, value) => {
|
||||
visit_child(key);
|
||||
visit_child(value);
|
||||
}
|
||||
Builtin::Set(element) | Builtin::List(element) => visit_child(element),
|
||||
_ => {}
|
||||
},
|
||||
Layout::Struct(fields) => visit_children(fields),
|
||||
Layout::Union(union_layout) => match union_layout {
|
||||
UnionLayout::NonRecursive(tags) => visit_tags(tags),
|
||||
UnionLayout::Recursive(tags) => visit_tags(tags),
|
||||
UnionLayout::NonNullableUnwrapped(fields) => visit_children(fields),
|
||||
UnionLayout::NullableWrapped { other_tags, .. } => visit_tags(other_tags),
|
||||
UnionLayout::NullableUnwrapped { other_fields, .. } => {
|
||||
visit_children(other_fields)
|
||||
}
|
||||
},
|
||||
Layout::LambdaSet(_) => unreachable!(),
|
||||
Layout::RecursivePointer => {}
|
||||
}
|
||||
}
|
||||
|
||||
symbol
|
||||
}
|
||||
|
||||
fn get_or_create_proc_symbol(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: &Layout<'a>,
|
||||
op: HelperOp,
|
||||
) -> (Symbol, Option<ProcLayout<'a>>) {
|
||||
let found = self.specs.iter().find(|(l, o, _)| l == layout && *o == op);
|
||||
|
||||
if let Some((_, _, existing_symbol)) = found {
|
||||
(true, *existing_symbol)
|
||||
(*existing_symbol, None)
|
||||
} else {
|
||||
let layout_name = layout_debug_name(&layout);
|
||||
let unique_idx = self.procs_to_generate.len();
|
||||
let debug_name = format!("#rc{:?}_{}_{}", op, layout_name, unique_idx);
|
||||
let layout_name = layout_debug_name(layout);
|
||||
let debug_name = format!("#help{:?}_{}", op, layout_name);
|
||||
let new_symbol: Symbol = self.create_symbol(ident_ids, &debug_name);
|
||||
self.procs_to_generate.push((layout, op, new_symbol));
|
||||
(false, new_symbol)
|
||||
self.specs.push((*layout, op, new_symbol));
|
||||
|
||||
let new_proc_layout = match op {
|
||||
HelperOp::Inc => Some(ProcLayout {
|
||||
arguments: self.arena.alloc([*layout, self.layout_isize]),
|
||||
result: LAYOUT_UNIT,
|
||||
}),
|
||||
HelperOp::Dec => Some(ProcLayout {
|
||||
arguments: self.arena.alloc([*layout]),
|
||||
result: LAYOUT_UNIT,
|
||||
}),
|
||||
HelperOp::DecRef => None,
|
||||
HelperOp::Eq => Some(ProcLayout {
|
||||
arguments: self.arena.alloc([*layout, *layout]),
|
||||
result: LAYOUT_BOOL,
|
||||
}),
|
||||
};
|
||||
|
||||
(new_symbol, new_proc_layout)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,14 +390,15 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
Stmt::Let(unit, Expr::Struct(&[]), LAYOUT_UNIT, ret_stmt)
|
||||
}
|
||||
|
||||
fn gen_args(&self, op: RefcountOp, layout: Layout<'a>) -> &'a [(Layout<'a>, Symbol)] {
|
||||
fn gen_args(&self, op: HelperOp, layout: Layout<'a>) -> &'a [(Layout<'a>, Symbol)] {
|
||||
let roc_value = (layout, Symbol::ARG_1);
|
||||
match op {
|
||||
RefcountOp::Inc => {
|
||||
HelperOp::Inc => {
|
||||
let inc_amount = (self.layout_isize, Symbol::ARG_2);
|
||||
self.arena.alloc([roc_value, inc_amount])
|
||||
}
|
||||
RefcountOp::Dec | RefcountOp::DecRef => self.arena.alloc([roc_value]),
|
||||
HelperOp::Dec | HelperOp::DecRef => self.arena.alloc([roc_value]),
|
||||
HelperOp::Eq => self.arena.alloc([roc_value, (layout, Symbol::ARG_2)]),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,7 +406,7 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
fn gen_modify_str(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
op: RefcountOp,
|
||||
op: HelperOp,
|
||||
proc_name: Symbol,
|
||||
) -> Proc<'a> {
|
||||
let string = Symbol::ARG_1;
|
||||
|
@ -349,20 +466,21 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
// Call the relevant Zig lowlevel to actually modify the refcount
|
||||
let zig_call_result = self.create_symbol(ident_ids, "zig_call_result");
|
||||
let zig_call_expr = match op {
|
||||
RefcountOp::Inc => Expr::Call(Call {
|
||||
HelperOp::Inc => Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::RefCountInc,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: self.arena.alloc([rc_ptr, Symbol::ARG_2]),
|
||||
}),
|
||||
RefcountOp::Dec | RefcountOp::DecRef => Expr::Call(Call {
|
||||
HelperOp::Dec | HelperOp::DecRef => Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::RefCountDec,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: self.arena.alloc([rc_ptr, alignment]),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let zig_call_stmt = |next| Stmt::Let(zig_call_result, zig_call_expr, LAYOUT_UNIT, next);
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
pub mod alias_analysis;
|
||||
pub mod borrow;
|
||||
pub mod gen_refcount;
|
||||
pub mod code_gen_help;
|
||||
pub mod inc_dec;
|
||||
pub mod ir;
|
||||
pub mod layout;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::header::{
|
||||
AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PackageEntry, PlatformHeader,
|
||||
PlatformRigid, TypedIdent,
|
||||
};
|
||||
use crate::header::{AppHeader, InterfaceHeader, PlatformHeader};
|
||||
use crate::ident::Ident;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
|
@ -17,6 +14,33 @@ pub struct Spaces<'a, T> {
|
|||
pub after: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Spaced<'a, T> {
|
||||
Item(T),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a Spaced<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Spaced<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a, T: Debug> Debug for Spaced<'a, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Item(item) => item.fmt(f),
|
||||
Self::SpaceBefore(item, space) => f
|
||||
.debug_tuple("SpaceBefore")
|
||||
.field(item)
|
||||
.field(space)
|
||||
.finish(),
|
||||
Self::SpaceAfter(item, space) => f
|
||||
.debug_tuple("SpaceAfter")
|
||||
.field(item)
|
||||
.field(space)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtractSpaces<'a>: Sized + Copy {
|
||||
type Item;
|
||||
fn extract_spaces(&self) -> Spaces<'a, Self::Item>;
|
||||
|
@ -674,6 +698,15 @@ pub trait Spaceable<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Spaceable<'a> for Spaced<'a, T> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Spaced::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Spaced::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for Expr<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Expr::SpaceBefore(self, spaces)
|
||||
|
@ -701,24 +734,6 @@ impl<'a> Spaceable<'a> for TypeAnnotation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for ImportsEntry<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ImportsEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ImportsEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for TypedIdent<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
TypedIdent::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
TypedIdent::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Val> Spaceable<'a> for AssignedField<'a, Val> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
AssignedField::SpaceBefore(self, spaces)
|
||||
|
@ -823,8 +838,55 @@ macro_rules! impl_extract_spaces {
|
|||
impl_extract_spaces!(Expr);
|
||||
impl_extract_spaces!(Tag);
|
||||
impl_extract_spaces!(AssignedField<T>);
|
||||
impl_extract_spaces!(PlatformRigid);
|
||||
impl_extract_spaces!(TypedIdent);
|
||||
impl_extract_spaces!(ImportsEntry);
|
||||
impl_extract_spaces!(ExposesEntry<T>);
|
||||
impl_extract_spaces!(PackageEntry);
|
||||
|
||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||
type Item = T;
|
||||
|
||||
fn extract_spaces(&self) -> Spaces<'a, T> {
|
||||
match self {
|
||||
Spaced::SpaceBefore(item, before) => match item {
|
||||
Spaced::SpaceBefore(_, _) => todo!(),
|
||||
Spaced::SpaceAfter(item, after) => {
|
||||
if let Spaced::Item(item) = item {
|
||||
Spaces {
|
||||
before,
|
||||
item: *item,
|
||||
after,
|
||||
}
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
Spaced::Item(item) => Spaces {
|
||||
before,
|
||||
item: *item,
|
||||
after: &[],
|
||||
},
|
||||
},
|
||||
Spaced::SpaceAfter(item, after) => match item {
|
||||
Spaced::SpaceBefore(item, before) => {
|
||||
if let Spaced::Item(item) = item {
|
||||
Spaces {
|
||||
before,
|
||||
item: *item,
|
||||
after,
|
||||
}
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
Spaced::SpaceAfter(_, _) => todo!(),
|
||||
Spaced::Item(item) => Spaces {
|
||||
before: &[],
|
||||
item: *item,
|
||||
after,
|
||||
},
|
||||
},
|
||||
Spaced::Item(item) => Spaces {
|
||||
before: &[],
|
||||
item: *item,
|
||||
after: &[],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ast::{Collection, CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
|
||||
use crate::ast::{Collection, CommentOrNewline, Spaced, StrLiteral, TypeAnnotation};
|
||||
use crate::blankspace::space0_e;
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::parser::Progress::{self, *};
|
||||
|
@ -57,11 +57,30 @@ impl<'a> ModuleName<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ExposedName<'a>(&'a str);
|
||||
|
||||
impl<'a> From<ExposedName<'a>> for &'a str {
|
||||
fn from(name: ExposedName<'a>) -> Self {
|
||||
name.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExposedName<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
ExposedName(name)
|
||||
}
|
||||
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
|
@ -81,9 +100,9 @@ pub enum To<'a> {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppHeader<'a> {
|
||||
pub name: Loc<StrLiteral<'a>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub to: Loc<To<'a>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
|
@ -102,7 +121,7 @@ pub struct AppHeader<'a> {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PackageHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageOrPath<'a>>)>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
|
@ -118,37 +137,25 @@ pub struct PackageHeader<'a> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PlatformRigid<'a> {
|
||||
Entry { rigid: &'a str, alias: &'a str },
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for PlatformRigid<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PlatformRigid::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PlatformRigid::SpaceAfter(self, spaces)
|
||||
}
|
||||
pub struct PlatformRigid<'a> {
|
||||
pub rigid: &'a str,
|
||||
pub alias: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformRequires<'a> {
|
||||
pub rigids: Collection<'a, Loc<PlatformRigid<'a>>>,
|
||||
pub signature: Loc<TypedIdent<'a>>,
|
||||
pub rigids: Collection<'a, Loc<Spaced<'a, PlatformRigid<'a>>>>,
|
||||
pub signature: Loc<Spaced<'a, TypedIdent<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub requires: PlatformRequires<'a>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
|
@ -174,26 +181,7 @@ pub struct Effects<'a> {
|
|||
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
||||
pub effect_shortname: &'a str,
|
||||
pub effect_type_name: &'a str,
|
||||
pub entries: Collection<'a, Loc<TypedIdent<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ExposesEntry<'a, T> {
|
||||
/// e.g. `Task`
|
||||
Exposed(T),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a, T> Spaceable<'a> for ExposesEntry<'a, T> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
pub entries: Collection<'a, Loc<Spaced<'a, TypedIdent<'a>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
@ -201,71 +189,35 @@ pub enum ImportsEntry<'a> {
|
|||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
Module(
|
||||
ModuleName<'a>,
|
||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
|
||||
/// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }`
|
||||
/// e.g. `pf.Task` or `pf.Task.{ after }` or `pf.{ Task.{ Task, after } }`
|
||||
Package(
|
||||
&'a str,
|
||||
ModuleName<'a>,
|
||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> ExposesEntry<'a, &'a str> {
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
use ExposesEntry::*;
|
||||
|
||||
match self {
|
||||
Exposed(string) => string,
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
|
||||
}
|
||||
}
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct TypedIdent<'a> {
|
||||
pub ident: Loc<&'a str>,
|
||||
pub spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
pub ann: Loc<TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum TypedIdent<'a> {
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
Entry {
|
||||
ident: Loc<&'a str>,
|
||||
spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
ann: Loc<TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
pub struct PackageEntry<'a> {
|
||||
pub shorthand: &'a str,
|
||||
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||
pub package_or_path: Loc<PackageOrPath<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PackageEntry<'a> {
|
||||
Entry {
|
||||
shorthand: &'a str,
|
||||
spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||
package_or_path: Loc<PackageOrPath<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for PackageEntry<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PackageEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PackageEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, EPackageEntry<'a>> {
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
||||
move |arena, state| {
|
||||
// You may optionally have a package shorthand,
|
||||
// e.g. "uc" in `uc: roc/unicode 1.0.0`
|
||||
|
@ -293,19 +245,19 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, EPackageEntry<'a
|
|||
.parse(arena, state)?;
|
||||
|
||||
let entry = match opt_shorthand {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_or_path,
|
||||
},
|
||||
None => PackageEntry::Entry {
|
||||
None => PackageEntry {
|
||||
shorthand: "",
|
||||
spaces_after_shorthand: &[],
|
||||
package_or_path,
|
||||
},
|
||||
};
|
||||
|
||||
Ok((MadeProgress, entry, state))
|
||||
Ok((MadeProgress, Spaced::Item(entry), state))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::ast::{Collection, CommentOrNewline, Def, Module};
|
||||
use crate::ast::{Collection, CommentOrNewline, Def, Module, Spaced};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::header::{
|
||||
package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
|
||||
package_entry, package_name, package_or_path, AppHeader, Effects, ExposedName, ImportsEntry,
|
||||
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, PlatformRigid, To,
|
||||
TypedIdent,
|
||||
};
|
||||
|
@ -220,7 +220,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
|||
#[allow(clippy::type_complexity)]
|
||||
let opt_imports: Option<(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
)> = opt_imports;
|
||||
|
||||
let ((before_imports, after_imports), imports) =
|
||||
|
@ -303,7 +303,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct ProvidesTo<'a> {
|
||||
entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
entries: Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
to: Located<To<'a>>,
|
||||
|
||||
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
|
@ -362,7 +362,7 @@ fn provides_without_to<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
EProvides<'a>,
|
||||
> {
|
||||
|
@ -385,14 +385,14 @@ fn provides_without_to<'a>() -> impl Parser<
|
|||
EProvides::Open,
|
||||
EProvides::Space,
|
||||
EProvides::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn exposes_entry<'a, F, E>(
|
||||
to_expectation: F,
|
||||
) -> impl Parser<'a, Located<ExposesEntry<'a, &'a str>>, E>
|
||||
) -> impl Parser<'a, Located<Spaced<'a, ExposedName<'a>>>, E>
|
||||
where
|
||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||
F: Copy,
|
||||
|
@ -400,7 +400,7 @@ where
|
|||
{
|
||||
loc!(map!(
|
||||
specialize(|_, r, c| to_expectation(r, c), unqualified_ident()),
|
||||
ExposesEntry::Exposed
|
||||
|n| Spaced::Item(ExposedName::new(n))
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -444,7 +444,7 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
|
|||
#[inline(always)]
|
||||
fn requires_rigids<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Collection<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> {
|
||||
) -> impl Parser<'a, Collection<'a, Located<Spaced<'a, PlatformRigid<'a>>>>, ERequires<'a>> {
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', ERequires::ListStart),
|
||||
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
|
||||
|
@ -454,23 +454,24 @@ fn requires_rigids<'a>(
|
|||
ERequires::Open,
|
||||
ERequires::Space,
|
||||
ERequires::IndentListEnd,
|
||||
PlatformRigid::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn requires_rigid<'a>() -> impl Parser<'a, PlatformRigid<'a>, ()> {
|
||||
fn requires_rigid<'a>() -> impl Parser<'a, Spaced<'a, PlatformRigid<'a>>, ()> {
|
||||
map!(
|
||||
and!(
|
||||
lowercase_ident(),
|
||||
skip_first!(word2(b'=', b'>', |_, _| ()), uppercase_ident())
|
||||
),
|
||||
|(rigid, alias)| PlatformRigid::Entry { rigid, alias }
|
||||
|(rigid, alias)| Spaced::Item(PlatformRigid { rigid, alias })
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn requires_typed_ident<'a>() -> impl Parser<'a, Located<TypedIdent<'a>>, ERequires<'a>> {
|
||||
fn requires_typed_ident<'a>() -> impl Parser<'a, Located<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>>
|
||||
{
|
||||
skip_first!(
|
||||
word1(b'{', ERequires::ListStart),
|
||||
skip_second!(
|
||||
|
@ -491,7 +492,7 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
|
@ -515,7 +516,7 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -545,7 +546,7 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
Collection<'a, Located<Spaced<'a, ModuleName<'a>>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
|
@ -569,14 +570,14 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn exposes_module<'a, F, E>(
|
||||
to_expectation: F,
|
||||
) -> impl Parser<'a, Located<ExposesEntry<'a, ModuleName<'a>>>, E>
|
||||
) -> impl Parser<'a, Located<Spaced<'a, ModuleName<'a>>>, E>
|
||||
where
|
||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||
F: Copy,
|
||||
|
@ -584,13 +585,13 @@ where
|
|||
{
|
||||
loc!(map!(
|
||||
specialize(|_, r, c| to_expectation(r, c), module_name()),
|
||||
ExposesEntry::Exposed
|
||||
Spaced::Item
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Packages<'a> {
|
||||
entries: Collection<'a, Located<PackageEntry<'a>>>,
|
||||
entries: Collection<'a, Located<Spaced<'a, PackageEntry<'a>>>>,
|
||||
before_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
@ -618,7 +619,7 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
|
|||
EPackages::Open,
|
||||
EPackages::Space,
|
||||
EPackages::IndentListEnd,
|
||||
PackageEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
),
|
||||
|((before_packages_keyword, after_packages_keyword), entries): (
|
||||
|
@ -639,7 +640,7 @@ fn imports<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
),
|
||||
EImports,
|
||||
> {
|
||||
|
@ -663,7 +664,7 @@ fn imports<'a>() -> impl Parser<
|
|||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentListEnd,
|
||||
ImportsEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -706,7 +707,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
|||
EEffects::Open,
|
||||
EEffects::Space,
|
||||
EEffects::IndentListEnd,
|
||||
TypedIdent::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
|
@ -726,7 +727,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
||||
fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
|
||||
// e.g.
|
||||
//
|
||||
// printLine : Str -> Effect {}
|
||||
|
@ -752,11 +753,11 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
|||
)
|
||||
),
|
||||
|((ident, spaces_before_colon), ann)| {
|
||||
TypedIdent::Entry {
|
||||
Spaced::Item(TypedIdent {
|
||||
ident,
|
||||
spaces_before_colon,
|
||||
ann,
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -775,18 +776,18 @@ where
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
||||
fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> {
|
||||
let min_indent = 1;
|
||||
|
||||
type Temp<'a> = (
|
||||
(Option<&'a str>, ModuleName<'a>),
|
||||
Option<Collection<'a, Located<ExposesEntry<'a, &'a str>>>>,
|
||||
Option<Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>>,
|
||||
);
|
||||
|
||||
map_with_arena!(
|
||||
and!(
|
||||
and!(
|
||||
// e.g. `base.`
|
||||
// e.g. `pf.`
|
||||
maybe!(skip_second!(
|
||||
shortname(),
|
||||
word1(b'.', EImports::ShorthandDot)
|
||||
|
@ -806,18 +807,20 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
|||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentSetEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
))
|
||||
),
|
||||
|_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
|
||||
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
|
||||
|
||||
match opt_shortname {
|
||||
let entry = match opt_shortname {
|
||||
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
|
||||
|
||||
None => ImportsEntry::Module(module_name, exposed_values),
|
||||
}
|
||||
};
|
||||
|
||||
Spaced::Item(entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ Platform {
|
|||
},
|
||||
requires: PlatformRequires {
|
||||
rigids: [],
|
||||
signature: |L 0-0, C 38-47| Entry {
|
||||
signature: |L 0-0, C 38-47| TypedIdent {
|
||||
ident: |L 0-0, C 38-42| "main",
|
||||
spaces_before_colon: [],
|
||||
ann: |L 0-0, C 45-47| Record {
|
||||
|
|
|
@ -4,10 +4,10 @@ App {
|
|||
"quicksort",
|
||||
),
|
||||
packages: [
|
||||
|L 1-1, C 15-33| Entry {
|
||||
shorthand: "base",
|
||||
|L 1-1, C 15-31| PackageEntry {
|
||||
shorthand: "pf",
|
||||
spaces_after_shorthand: [],
|
||||
package_or_path: |L 1-1, C 21-33| Path(
|
||||
package_or_path: |L 1-1, C 19-31| Path(
|
||||
PlainLine(
|
||||
"./platform",
|
||||
),
|
||||
|
@ -24,12 +24,12 @@ App {
|
|||
),
|
||||
],
|
||||
provides: [
|
||||
|L 3-3, C 15-24| Exposed(
|
||||
|L 3-3, C 15-24| ExposedName(
|
||||
"quicksort",
|
||||
),
|
||||
],
|
||||
to: |L 3-3, C 30-34| ExistingPackage(
|
||||
"base",
|
||||
to: |L 3-3, C 30-32| ExistingPackage(
|
||||
"pf",
|
||||
),
|
||||
before_header: [],
|
||||
after_app_keyword: [],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app "quicksort"
|
||||
packages { base: "./platform" }
|
||||
packages { pf: "./platform" }
|
||||
imports [ foo.Bar.Baz ]
|
||||
provides [ quicksort ] to base
|
||||
provides [ quicksort ] to pf
|
||||
|
|
|
@ -4,10 +4,10 @@ App {
|
|||
"quicksort",
|
||||
),
|
||||
packages: [
|
||||
|L 1-1, C 15-33| Entry {
|
||||
shorthand: "base",
|
||||
|L 1-1, C 15-31| PackageEntry {
|
||||
shorthand: "pf",
|
||||
spaces_after_shorthand: [],
|
||||
package_or_path: |L 1-1, C 21-33| Path(
|
||||
package_or_path: |L 1-1, C 19-31| Path(
|
||||
PlainLine(
|
||||
"./platform",
|
||||
),
|
||||
|
@ -23,7 +23,7 @@ App {
|
|||
Collection {
|
||||
items: [
|
||||
|L 3-3, C 8-11| SpaceBefore(
|
||||
Exposed(
|
||||
ExposedName(
|
||||
"Baz",
|
||||
),
|
||||
[
|
||||
|
@ -31,7 +31,7 @@ App {
|
|||
],
|
||||
),
|
||||
|L 4-4, C 8-16| SpaceBefore(
|
||||
Exposed(
|
||||
ExposedName(
|
||||
"FortyTwo",
|
||||
),
|
||||
[
|
||||
|
@ -49,12 +49,12 @@ App {
|
|||
),
|
||||
],
|
||||
provides: [
|
||||
|L 7-7, C 15-24| Exposed(
|
||||
|L 7-7, C 15-24| ExposedName(
|
||||
"quicksort",
|
||||
),
|
||||
],
|
||||
to: |L 7-7, C 31-35| ExistingPackage(
|
||||
"base",
|
||||
to: |L 7-7, C 31-33| ExistingPackage(
|
||||
"pf",
|
||||
),
|
||||
before_header: [],
|
||||
after_app_keyword: [],
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
app "quicksort"
|
||||
packages { base: "./platform", }
|
||||
packages { pf: "./platform", }
|
||||
imports [ foo.Bar.{
|
||||
Baz,
|
||||
FortyTwo,
|
||||
# I'm a happy comment
|
||||
} ]
|
||||
provides [ quicksort, ] to base
|
||||
provides [ quicksort, ] to pf
|
||||
|
|
|
@ -6,12 +6,12 @@ Platform {
|
|||
},
|
||||
requires: PlatformRequires {
|
||||
rigids: [
|
||||
|L 1-1, C 14-26| Entry {
|
||||
|L 1-1, C 14-26| PlatformRigid {
|
||||
rigid: "model",
|
||||
alias: "Model",
|
||||
},
|
||||
],
|
||||
signature: |L 1-1, C 30-39| Entry {
|
||||
signature: |L 1-1, C 30-39| TypedIdent {
|
||||
ident: |L 1-1, C 30-34| "main",
|
||||
spaces_before_colon: [],
|
||||
ann: |L 1-1, C 37-39| Record {
|
||||
|
@ -22,7 +22,7 @@ Platform {
|
|||
},
|
||||
exposes: [],
|
||||
packages: [
|
||||
|L 3-3, C 15-27| Entry {
|
||||
|L 3-3, C 15-27| PackageEntry {
|
||||
shorthand: "foo",
|
||||
spaces_after_shorthand: [],
|
||||
package_or_path: |L 3-3, C 20-27| Path(
|
||||
|
@ -34,7 +34,7 @@ Platform {
|
|||
],
|
||||
imports: [],
|
||||
provides: [
|
||||
|L 5-5, C 15-26| Exposed(
|
||||
|L 5-5, C 15-26| ExposedName(
|
||||
"mainForHost",
|
||||
),
|
||||
],
|
||||
|
|
|
@ -31,7 +31,7 @@ fn nat_alias() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn i128_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -115,7 +115,7 @@ fn i8_signed_int_alias() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn i128_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -741,7 +741,7 @@ fn gen_int_less_than() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_dec_eq() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -761,7 +761,7 @@ fn gen_dec_eq() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_dec_neq() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -864,7 +864,7 @@ fn gen_sub_i64() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_mul_dec() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1830,7 +1830,7 @@ fn shift_right_zf_by() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn max_i128() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
|
|
@ -665,17 +665,17 @@ fn str_starts_with_false_small_str() {
|
|||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_equality() {
|
||||
// assert_evals_to!(r#""a" == "a""#, true, bool);
|
||||
// assert_evals_to!(
|
||||
// r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// assert_evals_to!(r#""a" != "b""#, true, bool);
|
||||
// assert_evals_to!(r#""a" == "b""#, false, bool);
|
||||
// }
|
||||
#[test]
|
||||
fn str_equality() {
|
||||
assert_evals_to!(r#""a" == "a""#, true, bool);
|
||||
assert_evals_to!(
|
||||
r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
||||
true,
|
||||
bool
|
||||
);
|
||||
assert_evals_to!(r#""a" != "b""#, true, bool);
|
||||
assert_evals_to!(r#""a" == "b""#, false, bool);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn nested_recursive_literal() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue