Merge branch 'trunk' of github.com:rtfeldman/roc into faster-str-eq

This commit is contained in:
Brian Carroll 2021-12-17 13:47:25 +00:00
commit 77d73f35eb
68 changed files with 1345 additions and 1098 deletions

View file

@ -22,6 +22,7 @@ Earthly may temporarily use a lot of disk space, up to 90 GB. This disk space is
- Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip](https://roc.zulipchat.com) first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation.
- It's a good idea to open a work-in-progress pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Be sure to include "WIP" in the title of the PR as long as it's not ready for review!
- Make sure to create a branch on the roc repository for your changes. We do not allow CI to be run on forks for security.
- You find good first issues [here](https://github.com/rtfeldman/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
## Can we do better?

View file

@ -7,11 +7,11 @@ use roc_fmt::module::fmt_module;
use roc_fmt::Buf;
use roc_module::called_via::{BinOp, UnaryOp};
use roc_parse::ast::{
AssignedField, Collection, Expr, Pattern, StrLiteral, StrSegment, Tag, TypeAnnotation,
AssignedField, Collection, Expr, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation,
WhenBranch,
};
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_parse::{
@ -228,16 +228,22 @@ impl<'a> RemoveSpaces<'a> for &'a str {
}
}
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for ExposesEntry<'a, T> {
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
ExposesEntry::Exposed(a) => ExposesEntry::Exposed(a.remove_spaces(arena)),
ExposesEntry::SpaceBefore(a, _) => a.remove_spaces(arena),
ExposesEntry::SpaceAfter(a, _) => a.remove_spaces(arena),
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
Spaced::SpaceBefore(a, _) => a.remove_spaces(arena),
Spaced::SpaceAfter(a, _) => a.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for ExposedName<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
@ -261,18 +267,10 @@ impl<'a> RemoveSpaces<'a> for To<'a> {
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
TypedIdent::Entry {
ident,
spaces_before_colon: _,
ann,
} => TypedIdent::Entry {
ident: ident.remove_spaces(arena),
TypedIdent {
ident: self.ident.remove_spaces(arena),
spaces_before_colon: &[],
ann: ann.remove_spaces(arena),
},
TypedIdent::SpaceBefore(a, _) => a.remove_spaces(arena),
TypedIdent::SpaceAfter(a, _) => a.remove_spaces(arena),
ann: self.ann.remove_spaces(arena),
}
}
}
@ -287,29 +285,17 @@ impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
}
impl<'a> RemoveSpaces<'a> for PlatformRigid<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
PlatformRigid::Entry { rigid, alias } => PlatformRigid::Entry { rigid, alias },
PlatformRigid::SpaceBefore(a, _) => a.remove_spaces(arena),
PlatformRigid::SpaceAfter(a, _) => a.remove_spaces(arena),
}
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
PackageEntry::Entry {
shorthand,
spaces_after_shorthand: _,
package_or_path,
} => PackageEntry::Entry {
shorthand,
PackageEntry {
shorthand: self.shorthand,
spaces_after_shorthand: &[],
package_or_path: package_or_path.remove_spaces(arena),
},
PackageEntry::SpaceBefore(a, _) => a.remove_spaces(arena),
PackageEntry::SpaceAfter(a, _) => a.remove_spaces(arena),
package_or_path: self.package_or_path.remove_spaces(arena),
}
}
}
@ -328,8 +314,6 @@ impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
match *self {
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
ImportsEntry::SpaceBefore(a, _) => a.remove_spaces(arena),
ImportsEntry::SpaceAfter(a, _) => a.remove_spaces(arena),
}
}
}

View file

@ -1,7 +1,7 @@
app "multi-dep-str"
packages { base: "platform" }
packages { pf: "platform" }
imports [ Dep1 ]
provides [ main ] to base
provides [ main ] to pf
main : Str
main = Dep1.str1

View file

@ -1,7 +1,7 @@
app "multi-dep-thunk"
packages { base: "platform" }
packages { pf: "platform" }
imports [ Dep1 ]
provides [ main ] to base
provides [ main ] to pf
main : Str
main = Dep1.value1 {}

10
cli_utils/Cargo.lock generated
View file

@ -1310,13 +1310,13 @@ dependencies = [
name = "inkwell"
version = "0.1.0"
dependencies = [
"inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8)",
"inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1)",
]
[[package]]
name = "inkwell"
version = "0.1.0"
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8#14b78d96d2dbc95694e181be66e4cd53df3fc02f"
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1#e15d665227b2acad4ca949820d80048e09f3f4e5"
dependencies = [
"either",
"inkwell_internals",
@ -1324,13 +1324,12 @@ dependencies = [
"llvm-sys",
"once_cell",
"parking_lot",
"regex",
]
[[package]]
name = "inkwell_internals"
version = "0.3.0"
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8#14b78d96d2dbc95694e181be66e4cd53df3fc02f"
version = "0.5.0"
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1#e15d665227b2acad4ca949820d80048e09f3f4e5"
dependencies = [
"proc-macro2",
"quote",
@ -2666,6 +2665,7 @@ dependencies = [
"roc_collections",
"roc_module",
"roc_mono",
"roc_reporting",
"roc_std",
"target-lexicon",
]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,
} => {
impl<'a> Formattable for TypedIdent<'a> {
fn is_multiline(&self) -> bool {
false
}
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
buf.indent(indent);
buf.push_str(ident.value);
fmt_default_spaces(buf, spaces_before_colon, " ", indent);
buf.push_str(self.ident.value);
fmt_default_spaces(buf, self.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);
}
self.ann.value.format(buf, indent);
}
}
impl<'a> Formattable<'a> for TypedIdent<'a> {
fn is_multiline(&self) -> bool {
false
}
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
fmt_typed_ident(buf, self, 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);
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);
}
}
}
}
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(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);
}
buf.push_str(self.alias);
}
}
fn fmt_imports<'a>(
buf: &mut Buf<'a>,
loc_entries: Collection<'a, Located<ImportsEntry<'a>>>,
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::*;
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);
impl<'a> Formattable for ModuleName<'a> {
fn is_multiline(&self) -> bool {
false
}
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);
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, 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);
}
}
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);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,9 +779,6 @@ 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;
@ -840,9 +837,8 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
.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")
};
let elem_ptr =
unsafe { builder.build_in_bounds_gep(array_alloca, &[index_val], "index") };
builder.build_store(elem_ptr, val);
}
@ -885,12 +881,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
)
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
str_type,
"cast_collection",
)
}
builder.build_bitcast(struct_val.into_struct_value(), str_type, "cast_collection")
}
}
}

View file

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

View file

@ -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,6 +1031,22 @@ 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) =
@ -1024,7 +1079,8 @@ impl<'a> WasmBackend<'a> {
};
}
_ => not_supported_error(),
},
}
}
_ => not_supported_error(),
};

View file

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

View file

@ -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),
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(0x7f800000);
code_builder.i32_const(0x7f80_0000);
code_builder.i32_and();
code_builder.i32_const(0x7f800000);
code_builder.i32_const(0x7f80_0000);
code_builder.i32_ne();
}
F64 => {
code_builder.i64_reinterpret_f64();
code_builder.i64_const(0x7ff0000000000000);
code_builder.i64_const(0x7ff0_0000_0000_0000);
code_builder.i64_and();
code_builder.i64_const(0x7ff0000000000000);
code_builder.i64_const(0x7ff0_0000_0000_0000);
code_builder.i64_ne();
}
},
WasmLayout::StackMemory { .. } => return NotImplemented,
},
}
}
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,
}
}
}
}
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 {
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(),
},
StoredValue::StackMemory { .. } => return NotImplemented,
},
}
}
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);

View file

@ -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,19 +2602,17 @@ 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 {
if let Some(PackageEntry {
shorthand,
package_or_path:
Located {
@ -2623,9 +2620,8 @@ fn parse_header<'a>(
..
},
..
},
..
}) => {
}) = opt_base_package
{
match package_or_path {
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
// check whether we can find a Package-Config.roc file
@ -2646,10 +2642,7 @@ fn parse_header<'a>(
Ok((
module_id,
Msg::Many(vec![
app_module_header_msg,
load_pkg_config_msg,
]),
Msg::Many(vec![app_module_header_msg, load_pkg_config_msg]),
))
} else {
Ok((module_id, app_module_header_msg))
@ -2657,8 +2650,8 @@ fn parse_header<'a>(
}
_ => 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,14 +3392,14 @@ 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 {
let module_name = exposed.value.extract_spaces().item;
module_ids.get_or_insert(&PQModuleName::Qualified(
shorthand,
module_name.as_str().into(),
));
}
}
}
let exposed_ident_ids = {
// Lock just long enough to perform the minimal operations necessary.
@ -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."),
]);

View file

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

View file

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

View file

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

View file

@ -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: &[],
},
}
}
}

View file

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

View file

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

View file

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

View file

@ -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: [],

View file

@ -1,4 +1,4 @@
app "quicksort"
packages { base: "./platform" }
packages { pf: "./platform" }
imports [ foo.Bar.Baz ]
provides [ quicksort ] to base
provides [ quicksort ] to pf

View file

@ -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: [],

View file

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

View file

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

View file

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

View file

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

View file

@ -58,9 +58,9 @@ mod insert_doc_syntax_highlighting {
pub const HELLO_WORLD: &str = r#"
app "test-app"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
main = "Hello, world!"

View file

@ -1,7 +1,7 @@
The editor is a work in progress, only a limited subset of Roc expressions are currently supported.
Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete and refactoring.
Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete, refactoring and never needing to format your code.
## Getting started
@ -29,3 +29,46 @@ We thank the following open source projects in particular for inspiring us when
- [rgx](https://github.com/cloudhead/rgx)
- [elm-editor](https://github.com/jxxcarlson/elm-editor)
- [iced](https://github.com/hecrj/iced)
## How does it work?
To take a look behind the scenes, open the editor with `./roc edit` or `cargo run edit` and press F11.
This debug view shows important data structures that can be found in `editor/src/editor/mvc/ed_model.rs`.
Add or delete some code to see how these data structures are updated.
From roc to render:
- `./roc edit` or `cargo run edit` is first handled in `cli/src/main.rs`, from there the editor's launch function is called (`editor/src/editor/main.rs`).
- In `run_event_loop` we initialize the winit window, wgpu, the editor's model(`EdModel`) and start the rendering loop.
- The `ed_model` is filled in part with data obtained by loading and typechecking the roc file with the same function (`load_and_typecheck`) that is used by the compiler.
- `ed_model` also contains an `EdModule`, which holds the parsed abstract syntax tree (AST).
- In the `init_model` function:
+ The AST is converted into a tree of `MarkupNode`. The different types of `MarkupNode` are similar to the elements/nodes in HTML. A line of roc code is represented as a nested `MarkupNode` containing mostly text `MarkupNode`s. The line `foo = "bar"` is represented as
three text `MarkupNode` representing `foo`, ` = ` and `bar`. Multiple lines of roc code are represented as nested `MarkupNode` that contain other nested `MarkupNode`.
+ `CodeLines` holds a `Vec` of `String`, each line of code is a `String`. When saving the file, the content of `CodeLines` is written to disk.
+ `GridNodeMap` maps every position of a char of roc code to a `MarkNodeId`, for easy interaction with the caret.
- Back in `editor/src/editor/main.rs` we convert the `EdModel` to `RenderedWgpu` by calling `model_to_wgpu`.
- The `RenderedWgpu` is passed to the `glyph_brush` to draw the characters(glyphs) on the screen.
### Important files
To understand how the editor works it is useful to know the most important files:
- editor/src/editor/main.rs
- editor/src/editor/mvc/ed_update.rs
- editor/src/editor/mvc/ed_model.rs
- editor/src/editor/mvc/ed_view.rs
- editor/src/editor/render_ast.rs
- editor/src/editor/render_debug.rs
Important folders/files outside the editor folder:
- code_markup/src/markup/convert
- code_markup/src/markup/nodes.rs
- ast/src/lang/core/def
- ast/src/lang/core/expr
- ast/src/lang/core/ast.rs
- ast/src/lang/env.rs
## Contributing
We welcome new contributors :heart: and are happy to help you get started.
Check [CONTRIBUTING.md](../CONTRIBUTING.md) for more info.

View file

@ -17,9 +17,9 @@ For convenience and consistency, there is only one way to format roc.
pub const HELLO_WORLD: &str = r#"
app "test-app"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
main = "Hello, world!"

View file

@ -1,7 +1,7 @@
app "cfold"
packages { base: "platform" }
imports [base.Task]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task]
provides [ main ] to pf
# adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs

View file

@ -1,7 +1,7 @@
app "closure"
packages { base: "platform" }
imports [base.Task]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task]
provides [ main ] to pf
# see https://github.com/rtfeldman/roc/issues/985

View file

@ -1,7 +1,7 @@
app "deriv"
packages { base: "platform" }
imports [base.Task]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task]
provides [ main ] to pf
# based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs

View file

@ -1,7 +1,7 @@
app "nqueens"
packages { base: "platform" }
imports [base.Task]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task]
provides [ main ] to pf
main : Task.Task {} []
main =

View file

@ -1,7 +1,7 @@
app "quicksortapp"
packages { base: "platform" }
imports [base.Task, Quicksort]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task, Quicksort]
provides [ main ] to pf
main : Task.Task {} []
main =

View file

@ -1,7 +1,7 @@
app "rbtree-ck"
packages { base: "platform" }
imports [base.Task]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task]
provides [ main ] to pf
Color : [ Red, Black ]

View file

@ -1,7 +1,7 @@
app "rbtree-del"
packages { base: "platform" }
imports [base.Task]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task]
provides [ main ] to pf
Color : [ Red, Black ]

View file

@ -1,7 +1,7 @@
app "rbtree-insert"
packages { base: "platform" }
imports [base.Task]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task]
provides [ main ] to pf
main : Task.Task {} []
main =

View file

@ -1,7 +1,7 @@
app "test-astar"
packages { base: "platform" }
imports [base.Task, AStar]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task, AStar]
provides [ main ] to pf
main : Task.Task {} []
main =

View file

@ -1,7 +1,7 @@
app "test-base64"
packages { base: "platform" }
imports [base.Task, Base64 ]
provides [ main ] to base
packages { pf: "platform" }
imports [pf.Task, Base64 ]
provides [ main ] to pf
IO a : Task.Task a []

View file

@ -1,9 +1,9 @@
#!/usr/bin/env roc
app "echo"
packages { base: "platform" }
imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ]
provides [ main ] to base
packages { pf: "platform" }
imports [ pf.Task.{ Task, await }, pf.Stdout, pf.Stdin ]
provides [ main ] to pf
main : Task {} *
main =

View file

@ -1,7 +1,7 @@
app "effect-example"
packages { base: "thing/platform-dir" }
packages { pf: "thing/platform-dir" }
imports [fx.Effect]
provides [ main ] to base
provides [ main ] to pf
main : Effect.Effect {}
main =

View file

@ -1,6 +1,6 @@
interface Context
exposes [ Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope ]
imports [ base.File, base.Task.{ Task }, Variable.{ Variable } ]
imports [ pf.File, pf.Task.{ Task }, Variable.{ Variable } ]
Option a : [ Some a, None ]

View file

@ -1,8 +1,8 @@
#!/usr/bin/env roc
app "false"
packages { base: "platform" }
imports [ base.Task.{ Task }, base.Stdout, base.Stdin, Context.{ Context }, Variable.{ Variable } ]
provides [ main ] to base
packages { pf: "platform" }
imports [ pf.Task.{ Task }, pf.Stdout, pf.Stdin, Context.{ Context }, Variable.{ Variable } ]
provides [ main ] to pf
# An interpreter for the False programming language: https://strlen.com/false-language/
# This is just a silly example to test this variety of program.

View file

@ -1,7 +1,7 @@
app "fib"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
main = \n -> fib n 0 1

View file

@ -1,7 +1,7 @@
app "hello-rust"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
greeting =
hi = "Hello"

View file

@ -1,7 +1,7 @@
app "hello-swift"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
main =
host = "Swift"

View file

@ -1,7 +1,7 @@
app "hello-web"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
greeting =
hi = "Hello"

View file

@ -1,6 +1,6 @@
app "hello-world"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
main = "Hello, World!\n"

View file

@ -1,7 +1,7 @@
app "hello-world"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ main ] to base
provides [ main ] to pf
greeting =
hi = "Hello"

View file

@ -1,7 +1,7 @@
app "quicksort"
packages { base: "platform" }
packages { pf: "platform" }
imports []
provides [ quicksort ] to base
provides [ quicksort ] to pf
quicksort = \originalList ->
n = List.len originalList

View file

@ -6048,9 +6048,9 @@ I need all branches in an `if` to have the same type!
indoc!(
r#"
app "test-base64"
packages { base: "platform" }
imports [base.Task, Base64 ]
provides [ main, @Foo ] to base
packages { pf: "platform" }
imports [pf.Task, Base64 ]
provides [ main, @Foo ] to pf
"#
),
indoc!(
@ -6059,8 +6059,8 @@ I need all branches in an `if` to have the same type!
I am partway through parsing a provides list, but I got stuck here:
3 imports [base.Task, Base64 ]
4 provides [ main, @Foo ] to base
3 imports [pf.Task, Base64 ]
4 provides [ main, @Foo ] to pf
^
I was expecting a type name, value name or function name next, like
@ -6116,7 +6116,7 @@ I need all branches in an `if` to have the same type!
r#"
interface Foobar
exposes [ main, @Foo ]
imports [base.Task, Base64 ]
imports [pf.Task, Base64 ]
"#
),
indoc!(
@ -6144,7 +6144,7 @@ I need all branches in an `if` to have the same type!
r#"
interface foobar
exposes [ main, @Foo ]
imports [base.Task, Base64 ]
imports [pf.Task, Base64 ]
"#
),
indoc!(
@ -6170,7 +6170,7 @@ I need all branches in an `if` to have the same type!
r#"
app foobar
exposes [ main, @Foo ]
imports [base.Task, Base64 ]
imports [pf.Task, Base64 ]
"#
),
indoc!(

View file

@ -507,7 +507,7 @@ impl RocStr {
pub fn storage(&self) -> Option<Storage> {
use core::cmp::Ordering::*;
if self.is_small_str() || self.length == 0 {
if self.is_small_str() {
return None;
}
@ -660,7 +660,7 @@ impl RocStr {
impl Default for RocStr {
fn default() -> Self {
Self {
length: 0,
length: isize::MIN as usize,
elements: core::ptr::null_mut(),
}
}
@ -693,7 +693,7 @@ impl Eq for RocStr {}
impl Clone for RocStr {
fn clone(&self) -> Self {
if self.is_small_str() || self.is_empty() {
if self.is_small_str() {
Self {
elements: self.elements,
length: self.length,
@ -730,7 +730,7 @@ impl Clone for RocStr {
impl Drop for RocStr {
fn drop(&mut self) {
if !self.is_small_str() && !self.is_empty() {
if !self.is_small_str() {
let storage_ptr = self.get_storage_ptr_mut();
unsafe {