Merge branch 'main' into auto-snake-case

This commit is contained in:
Sam Mohr 2025-01-07 13:18:41 -08:00
commit 7a2f8bfa71
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
44 changed files with 650 additions and 464 deletions

View file

@ -18,22 +18,22 @@ jobs:
run: nix-build
- name: execute tests with --release
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step, see #7476
run: nix develop -c cargo test --locked --release -- --skip glue_cli_tests
- name: glue_cli_tests
# single threaded due to difficult bug when multithreading
# single threaded due to difficult bug when multithreading, see #7476
run: nix develop -c cargo test --locked --release glue_cli_tests -- --test-threads=1
- name: roc test all builtins
run: nix develop -c ./ci/roc_test_builtins.sh
- name: test wasm32 cli_tests
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step, see #7476
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" -- --skip glue_cli_tests
- name: wasm32 glue_cli_tests
# single threaded due to difficult bug when multithreading
# single threaded due to difficult bug when multithreading, see #7476
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" glue_cli_tests -- --test-threads=1
- name: test the dev backend # these tests require an explicit feature flag

View file

@ -63,7 +63,7 @@ jobs:
- name: test website build script
run: bash www/build.sh
- name: run fuzz tests - ok to merge if this one fails # for now!
run: |
cargo +nightly-2024-02-03 install --locked cargo-fuzz
cd crates/compiler/test_syntax/fuzz && cargo +nightly-2024-02-03 fuzz run -j4 fuzz_expr --sanitizer=none -- -dict=dict.txt -max_total_time=60
#- name: run fuzz tests - ok to merge if this one fails # for now!
# run: |
# cargo +nightly-2024-02-03 install --locked cargo-fuzz
# cd crates/compiler/test_syntax/fuzz && cargo +nightly-2024-02-03 fuzz run -j4 fuzz_expr --sanitizer=none -- -dict=dict.txt -max_total_time=60

View file

@ -22,6 +22,10 @@ jobs:
- name: Check if debug flag files are in sync
run: ./ci/check_debug_vars.sh
# for skipped tests; see #6946, #6947
- name: cargo test without --release
run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test'
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step, see #7476
run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test --locked -- --skip glue_cli_tests'
- name: glue_cli_tests
# single threaded due to difficult bug when multithreading, see #7476
run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test --locked glue_cli_tests -- --test-threads=1'

View file

@ -170,3 +170,4 @@ Isak Jones <isak.jones.980@gmail.com>
Ch1n3du <danielonyesoh@gmail.com>
Elias Mulhall <eli.mulhall@gmail.com>
ABuffSeagull <reecevanatta@hey.com>
Timon Krebs <timonkrebs@hotmail.com>

View file

@ -895,7 +895,7 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root("crates/cli/tests/test-projects/effectful", "form.roc"),
);
@ -915,14 +915,14 @@ mod cli_tests {
fn effectful_hello() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root("crates/cli/tests/test-projects/effectful/", "hello.roc"),
);
let expected_out = "I'm an effect 👻\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -930,14 +930,14 @@ mod cli_tests {
fn effectful_loops() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root("crates/cli/tests/test-projects/effectful/", "loops.roc"),
);
let expected_out = "Lu\nMarce\nJoaquin\nChloé\nMati\nPedro\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -945,7 +945,7 @@ mod cli_tests {
fn effectful_untyped_passed_fx() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root(
"crates/cli/tests/test-projects/effectful/",
@ -955,7 +955,7 @@ mod cli_tests {
let expected_out = "Before hello\nHello, World!\nAfter hello\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -963,7 +963,7 @@ mod cli_tests {
fn effectful_ignore_result() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root(
"crates/cli/tests/test-projects/effectful/",
@ -973,7 +973,7 @@ mod cli_tests {
let expected_out = "I asked for input and I ignored it. Deal with it! 😎\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -982,7 +982,7 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root(
"crates/cli/tests/test-projects/effectful",
"suffixed_record_field.roc",
@ -1005,7 +1005,7 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root("crates/cli/tests/test-projects/effectful", "on_err.roc"),
);
@ -1020,7 +1020,7 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root(
"crates/cli/tests/test-projects/effectful",
"for_each_try.roc",

View file

@ -80,6 +80,8 @@ impl ExecCli {
app_stdin_opt: Option<&str>,
app_args_opt: Option<&[&str]>,
) {
assert!(self.sub_command == roc_cli::CMD_BUILD, "check_build_and_run should be run with the build command, instead it was run with '{}'.", self.sub_command);
let build_cmd_out = self.run();
build_cmd_out.assert_clean_success();

View file

@ -456,7 +456,7 @@ fn gen_from_mono_module_dev<'a>(
#[cfg(feature = "target-wasm32")]
{
internal_error!("Compiler was not built with feature 'target-wasm32'.")
internal_error!("Compiler was built with feature 'target-wasm32'.")
}
}
(_, Architecture::Aarch32) => {

View file

@ -283,10 +283,16 @@ dbg_bool = \b ->
dbg_str : Str -> Inspector DbgFormatter
dbg_str = \s ->
# escape invisible unicode characters as in fmt_str_body crates/compiler/fmt/src/expr.rs
escape_s =
Str.replace_each(s, "\u(feff)", "\\u(feff)")
|> Str.replace_each("\u(200b)", "\\u(200b)")
|> Str.replace_each("\u(200c)", "\\u(200c)")
|> Str.replace_each("\u(200d)", "\\u(200d)")
custom_dbg_str = \f0 ->
f0
|> dbg_write("\"")
|> dbg_write(s) # TODO: Should we be escaping strings for dbg/logging?
dbg_write(f0, "\"")
|> dbg_write(escape_s)
|> dbg_write("\"")
custom(custom_dbg_str)

View file

@ -538,14 +538,14 @@ to_utf8 : Str -> List U8
## expect Str.from_utf8([]) == Ok("")
## expect Str.from_utf8([255]) |> Result.is_err
## ```
from_utf8 : List U8 -> Result Str [BadUtf8 Utf8ByteProblem U64]
from_utf8 : List U8 -> Result Str [BadUtf8 { problem : Utf8ByteProblem, index : U64 }]
from_utf8 = \bytes ->
result = from_utf8_lowlevel(bytes)
result = from_utf8_lowlevel bytes
if result.c_is_ok then
Ok(result.b_string)
else
Err(BadUtf8(result.d_problem_code, result.a_byte_index))
Err (BadUtf8 { problem: result.d_problem_code, index: result.a_byte_index })
expect (Str.from_utf8([82, 111, 99])) == Ok("Roc")
expect (Str.from_utf8([224, 174, 154, 224, 174, 191])) == Ok("சி")

View file

@ -272,7 +272,7 @@ enum PendingTypeDef<'a> {
name: Loc<Symbol>,
vars: Vec<Loc<Lowercase>>,
ann: &'a Loc<ast::TypeAnnotation<'a>>,
derived: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
derived: Option<&'a ast::ImplementsAbilities<'a>>,
},
Ability {
@ -319,7 +319,7 @@ impl PendingTypeDef<'_> {
ann,
derived,
} => {
let end = derived.map(|d| d.region).unwrap_or(ann.region);
let end = derived.map(|d| d.item.region).unwrap_or(ann.region);
let region = Region::span_across(&name.region, &end);
Some((name.value, region))
@ -761,12 +761,11 @@ fn canonicalize_opaque<'a>(
var_store: &mut VarStore,
scope: &mut Scope,
pending_abilities_in_scope: &PendingAbilitiesInScope,
name: Loc<Symbol>,
name_str: &'a str,
ann: &'a Loc<ast::TypeAnnotation<'a>>,
vars: &[Loc<Lowercase>],
has_abilities: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
has_abilities: Option<&'a ast::ImplementsAbilities<'a>>,
) -> Result<CanonicalizedOpaque<'a>, ()> {
let alias = canonicalize_alias(
env,
@ -784,11 +783,11 @@ fn canonicalize_opaque<'a>(
let mut derived_defs = Vec::new();
if let Some(has_abilities) = has_abilities {
let has_abilities = has_abilities.value.collection();
let has_abilities = has_abilities.item;
let mut derived_abilities = vec![];
for has_ability in has_abilities.items {
for has_ability in has_abilities.value.items {
let region = has_ability.region;
let (ability, opt_impls) = match has_ability.value.extract_spaces().item {
ast::ImplementsAbility::ImplementsAbility { ability, impls } => (ability, impls),
@ -1303,7 +1302,7 @@ fn canonicalize_type_defs<'a>(
Loc<Symbol>,
Vec<Loc<Lowercase>>,
&'a Loc<ast::TypeAnnotation<'a>>,
Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
Option<&'a ast::ImplementsAbilities<'a>>,
),
Ability(Loc<Symbol>, Vec<PendingAbilityMember<'a>>),
}
@ -2805,7 +2804,7 @@ fn to_pending_alias_or_opaque<'a>(
name: &'a Loc<&'a str>,
vars: &'a [Loc<ast::Pattern<'a>>],
ann: &'a Loc<ast::TypeAnnotation<'a>>,
opt_derived: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
opt_derived: Option<&'a ast::ImplementsAbilities<'a>>,
kind: AliasKind,
) -> PendingTypeDef<'a> {
let region = Region::span_across(&name.region, &ann.region);
@ -2898,15 +2897,7 @@ fn to_pending_type_def<'a>(
header: TypeHeader { name, vars },
typ: ann,
derived,
} => to_pending_alias_or_opaque(
env,
scope,
name,
vars,
ann,
derived.as_ref(),
AliasKind::Opaque,
),
} => to_pending_alias_or_opaque(env, scope, name, vars, ann, *derived, AliasKind::Opaque),
Ability {
header, members, ..

View file

@ -703,36 +703,37 @@ impl<'a> Formattable for ImplementsAbility<'a> {
impl<'a> Formattable for ImplementsAbilities<'a> {
fn is_multiline(&self) -> bool {
match self {
ImplementsAbilities::SpaceAfter(..) | ImplementsAbilities::SpaceBefore(..) => true,
ImplementsAbilities::Implements(has_abilities) => {
is_collection_multiline(has_abilities)
}
}
self.before_implements_kw.iter().any(|s| s.is_comment())
|| self.after_implements_kw.iter().any(|s| s.is_comment())
|| is_collection_multiline(&self.item.value)
}
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
match self {
ImplementsAbilities::Implements(has_abilities) => {
if newlines == Newlines::Yes {
fn format_with_options(&self, buf: &mut Buf, _parens: Parens, newlines: Newlines, indent: u16) {
if !self.before_implements_kw.is_empty() {
buf.newline();
buf.indent(indent);
fmt_comments_only(
buf,
self.before_implements_kw.iter(),
NewlineAt::Bottom,
indent,
);
}
if newlines == Newlines::Yes {
buf.ensure_ends_with_newline();
}
buf.indent(indent);
buf.push_str(roc_parse::keyword::IMPLEMENTS);
buf.spaces(1);
fmt_collection(buf, indent, Braces::Square, *has_abilities, Newlines::No);
}
ImplementsAbilities::SpaceBefore(has_abilities, spaces) => {
buf.newline();
buf.indent(indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
has_abilities.format_with_options(buf, parens, Newlines::No, indent)
}
ImplementsAbilities::SpaceAfter(has_abilities, spaces) => {
has_abilities.format_with_options(buf, parens, newlines, indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
}
if !self.after_implements_kw.is_empty() {
fmt_comments_only(
buf,
self.after_implements_kw.iter(),
NewlineAt::Bottom,
indent,
);
}
buf.ensure_ends_with_whitespace();
fmt_collection(buf, indent, Braces::Square, self.item.value, Newlines::No);
}
}
@ -1217,7 +1218,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> {
},
after: last_after,
needs_indent,
prec: Prec::AsType,
prec: Prec::FunctionType,
}
}
}

View file

@ -19,8 +19,8 @@ use roc_error_macros::internal_error;
use roc_parse::ast::{
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, Pattern, PatternApplyStyle, Spaceable, Spaces, SpacesAfter, SpacesBefore,
StrLiteral, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
ModuleImportParams, Pattern, PatternApplyStyle, Spaces, SpacesBefore, StrLiteral,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
};
use roc_parse::expr::merge_spaces;
use roc_parse::header::Keyword;
@ -94,21 +94,6 @@ pub fn def_lift_spaces<'a, 'b: 'a>(
}
}
fn lift_spaces_after<'a, 'b: 'a, T: 'b + ExtractSpaces<'a> + Spaceable<'a>>(
arena: &'a Bump,
item: T,
) -> SpacesAfter<'a, <T as ExtractSpaces<'a>>::Item>
where
<T as ExtractSpaces<'a>>::Item: Spaceable<'a>,
{
let spaces = item.extract_spaces();
SpacesAfter {
item: spaces.item.maybe_before(arena, spaces.before),
after: spaces.after,
}
}
pub fn tydef_lift_spaces<'a, 'b: 'a>(arena: &'a Bump, def: TypeDef<'b>) -> Spaces<'a, TypeDef<'a>> {
match def {
TypeDef::Alias { header, ann } => {
@ -128,17 +113,12 @@ pub fn tydef_lift_spaces<'a, 'b: 'a>(arena: &'a Bump, def: TypeDef<'b>) -> Space
typ,
derived,
} => {
if let Some(derived) = derived {
let derived_lifted = lift_spaces_after(arena, derived.value);
if derived.is_some() {
// It's structurally impossible for a derived clause to have spaces after
Spaces {
before: &[],
item: TypeDef::Opaque {
header,
typ,
derived: Some(Loc::at(derived.region, derived_lifted.item)),
},
after: derived_lifted.after,
item: def,
after: &[],
}
} else {
let typ_lifted = ann_lift_spaces_after(arena, &typ.value);
@ -461,7 +441,7 @@ impl<'a> Formattable for TypeDef<'a> {
// Always put the has-abilities clause on a newline if the opaque annotation
// contains a where-has clause.
let has_abilities_multiline = if let Some(has_abilities) = has_abilities {
!has_abilities.value.is_empty() && ann_is_where_clause
!has_abilities.item.value.is_empty() && ann_is_where_clause
} else {
false
};
@ -481,7 +461,7 @@ impl<'a> Formattable for TypeDef<'a> {
if let Some(has_abilities) = has_abilities {
buf.spaces(1);
has_abilities.format_with_options(
(*has_abilities).format_with_options(
buf,
Parens::NotNeeded,
Newlines::from_bool(make_multiline),

View file

@ -1814,14 +1814,17 @@ fn fmt_return<'a>(
buf.indent(indent);
buf.push_str(keyword::RETURN);
let return_indent = if return_value.is_multiline() {
let value = expr_lift_spaces(parens, buf.text.bump(), &return_value.value);
let return_indent = if value.item.is_multiline()
|| (newlines == Newlines::Yes && !value.before.is_empty())
|| value.before.iter().any(|s| s.is_comment())
{
indent + INDENT
} else {
indent
};
let value = expr_lift_spaces(parens, buf.text.bump(), &return_value.value);
if !value.before.is_empty() {
format_spaces(buf, value.before, newlines, return_indent);
}
@ -2057,15 +2060,17 @@ fn fmt_record_like<'a, 'b: 'a, Field, ToSpacesAround>(
// doesnt make sense.
Some(RecordPrefix::Update(record_var)) => {
buf.spaces(1);
record_var.format(buf, indent);
buf.indent(indent);
buf.push_str(" &");
record_var.format(buf, indent + INDENT);
buf.indent(indent + INDENT);
buf.ensure_ends_with_whitespace();
buf.push_str("&");
}
Some(RecordPrefix::Mapper(mapper_var)) => {
buf.spaces(1);
mapper_var.format(buf, indent);
buf.indent(indent);
buf.push_str(" <-");
mapper_var.format(buf, indent + INDENT);
buf.indent(indent + INDENT);
buf.ensure_ends_with_whitespace();
buf.push_str("<-");
}
}

View file

@ -841,7 +841,7 @@ pub enum TypeDef<'a> {
Opaque {
header: TypeHeader<'a>,
typ: Loc<TypeAnnotation<'a>>,
derived: Option<Loc<ImplementsAbilities<'a>>>,
derived: Option<&'a ImplementsAbilities<'a>>,
},
/// An ability definition. E.g.
@ -1552,31 +1552,11 @@ pub enum ImplementsAbility<'a> {
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ImplementsAbilities<'a> {
/// `implements [Eq { eq: myEq }, Hash]`
Implements(Collection<'a, Loc<ImplementsAbility<'a>>>),
// We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a ImplementsAbilities<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a ImplementsAbilities<'a>, &'a [CommentOrNewline<'a>]),
}
impl ImplementsAbilities<'_> {
pub fn collection(&self) -> &Collection<Loc<ImplementsAbility>> {
let mut it = self;
loop {
match it {
Self::SpaceBefore(inner, _) | Self::SpaceAfter(inner, _) => {
it = inner;
}
Self::Implements(collection) => return collection,
}
}
}
pub fn is_empty(&self) -> bool {
self.collection().is_empty()
}
pub struct ImplementsAbilities<'a> {
pub before_implements_kw: &'a [CommentOrNewline<'a>],
pub implements: Region,
pub after_implements_kw: &'a [CommentOrNewline<'a>],
pub item: Loc<Collection<'a, Loc<ImplementsAbility<'a>>>>,
}
#[derive(Debug, Copy, Clone, PartialEq)]
@ -2278,15 +2258,6 @@ impl<'a> Spaceable<'a> for ImplementsAbility<'a> {
}
}
impl<'a> Spaceable<'a> for ImplementsAbilities<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
ImplementsAbilities::SpaceBefore(self, spaces)
}
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
ImplementsAbilities::SpaceAfter(self, spaces)
}
}
impl<'a> Expr<'a> {
pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var {
module_name: "",
@ -2396,7 +2367,6 @@ impl_extract_spaces!(Tag);
impl_extract_spaces!(AssignedField<T>);
impl_extract_spaces!(TypeAnnotation);
impl_extract_spaces!(ImplementsAbility);
impl_extract_spaces!(ImplementsAbilities);
impl_extract_spaces!(Implements);
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
@ -2720,7 +2690,11 @@ impl<'a> Malformed for TypeDef<'a> {
header,
typ,
derived,
} => header.is_malformed() || typ.is_malformed() || derived.is_malformed(),
} => {
header.is_malformed()
|| typ.is_malformed()
|| derived.map(|d| d.item.is_malformed()).unwrap_or_default()
}
TypeDef::Ability {
header,
loc_implements,
@ -2762,19 +2736,6 @@ impl<'a> Malformed for ImplementsAbility<'a> {
}
}
impl<'a> Malformed for ImplementsAbilities<'a> {
fn is_malformed(&self) -> bool {
match self {
ImplementsAbilities::Implements(abilities) => {
abilities.iter().any(|ability| ability.is_malformed())
}
ImplementsAbilities::SpaceBefore(has, _) | ImplementsAbilities::SpaceAfter(has, _) => {
has.is_malformed()
}
}
}
}
impl<'a> Malformed for AbilityImpls<'a> {
fn is_malformed(&self) -> bool {
match self {

View file

@ -1,5 +1,6 @@
use crate::ast::CommentOrNewline;
use crate::ast::Spaceable;
use crate::ast::SpacesBefore;
use crate::parser::succeed;
use crate::parser::Progress;
use crate::parser::SpaceProblem;
@ -175,6 +176,24 @@ where
)
}
pub fn plain_spaces_before<'a, P, S, E>(
parser: P,
indent_problem: fn(Position) -> E,
) -> impl Parser<'a, SpacesBefore<'a, S>, E>
where
S: 'a,
P: 'a + Parser<'a, S, E>,
E: 'a + SpaceProblem,
{
parser::map(
and(backtrackable(space0_e(indent_problem)), parser),
|(space_list, item): (&'a [CommentOrNewline<'a>], S)| SpacesBefore {
before: space_list,
item,
},
)
}
pub fn space0_after_e<'a, P, S, E>(
parser: P,
indent_problem: fn(Position) -> E,

View file

@ -1170,20 +1170,14 @@ fn alias_signature<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EExpr<'a>>
increment_min_indent(specialize_err(EExpr::Type, type_annotation::located(false)))
}
fn opaque_signature<'a>() -> impl Parser<
'a,
(
Loc<TypeAnnotation<'a>>,
Option<Loc<ImplementsAbilities<'a>>>,
),
EExpr<'a>,
> {
fn opaque_signature<'a>(
) -> impl Parser<'a, (Loc<TypeAnnotation<'a>>, Option<&'a ImplementsAbilities<'a>>), EExpr<'a>> {
and(
specialize_err(EExpr::Type, type_annotation::located_opaque_signature(true)),
optional(backtrackable(specialize_err(
EExpr::Type,
space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart),
))),
optional(map_with_arena(
specialize_err(EExpr::Type, type_annotation::implements_abilities()),
|arena, item| &*arena.alloc(item),
)),
)
}

View file

@ -3,14 +3,14 @@ use bumpalo::Bump;
use roc_module::called_via::{BinOp, UnaryOp};
use roc_region::all::{Loc, Position, Region};
use crate::ast::ImplementsAbilities;
use crate::{
ast::{
AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, FullAst, Header,
Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias,
ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation,
IngestedFileImport, ModuleImport, ModuleImportParams, Pattern, PatternAs, Spaced, Spaces,
SpacesBefore, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
WhenBranch,
Implements, ImplementsAbility, ImplementsClause, ImportAlias, ImportAsKeyword,
ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport,
ModuleImport, ModuleImportParams, Pattern, PatternAs, Spaced, Spaces, SpacesBefore,
StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch,
},
header::{
AppHeader, ExposedName, ExposesKeyword, HostedHeader, ImportsEntry, ImportsKeyword,
@ -372,7 +372,7 @@ impl<'a> Normalize<'a> for TypeDef<'a> {
vars: vars.normalize(arena),
},
typ: typ.normalize(arena),
derived: derived.normalize(arena),
derived: derived.map(|item| &*arena.alloc(item.normalize(arena))),
},
Ability {
header: TypeHeader { name, vars },
@ -1001,6 +1001,17 @@ impl<'a> Normalize<'a> for AbilityImpls<'a> {
}
}
impl<'a> Normalize<'a> for ImplementsAbilities<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
ImplementsAbilities {
before_implements_kw: &[],
implements: Region::zero(),
after_implements_kw: &[],
item: self.item.normalize(arena),
}
}
}
impl<'a> Normalize<'a> for ImplementsAbility<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match *self {
@ -1017,18 +1028,6 @@ impl<'a> Normalize<'a> for ImplementsAbility<'a> {
}
}
impl<'a> Normalize<'a> for ImplementsAbilities<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match *self {
ImplementsAbilities::Implements(derived) => {
ImplementsAbilities::Implements(derived.normalize(arena))
}
ImplementsAbilities::SpaceBefore(derived, _)
| ImplementsAbilities::SpaceAfter(derived, _) => derived.normalize(arena),
}
}
}
impl<'a> Normalize<'a> for PatternAs<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
PatternAs {

View file

@ -4,8 +4,8 @@ use crate::ast::{
TypeAnnotation, TypeHeader,
};
use crate::blankspace::{
self, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
spaces_before_optional_after,
self, plain_spaces_before, space0_around_ee, space0_before_e, space0_before_optional_after,
space0_e, spaces_before_optional_after,
};
use crate::expr::record_field;
use crate::ident::{lowercase_ident, lowercase_ident_keyword_e};
@ -787,25 +787,39 @@ fn parse_implements_clause_chain_after_where<'a>(
}
/// Parse a implements-abilities clause, e.g. `implements [Eq, Hash]`.
pub fn implements_abilities<'a>() -> impl Parser<'a, Loc<ImplementsAbilities<'a>>, EType<'a>> {
increment_min_indent(skip_first(
pub fn implements_abilities<'a>() -> impl Parser<'a, ImplementsAbilities<'a>, EType<'a>> {
map(
plain_spaces_before(
increment_min_indent(and(
// Parse "implements"; we don't care about this keyword
crate::parser::keyword(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
map(
loc(crate::parser::keyword(
crate::keyword::IMPLEMENTS,
EType::TImplementsClause,
)),
|item| item.region,
),
// Parse "Hash"; this may be qualified from another module like "Hash.Hash"
space0_before_e(
loc(map(
collection_trailing_sep_e(
plain_spaces_before(
loc(collection_trailing_sep_e(
byte(b'[', EType::TStart),
loc(parse_implements_ability()),
byte(b',', EType::TEnd),
byte(b']', EType::TEnd),
ImplementsAbility::SpaceBefore,
),
ImplementsAbilities::Implements,
)),
EType::TIndentEnd,
),
))
)),
EType::TIndentStart,
),
|item| ImplementsAbilities {
before_implements_kw: item.before,
implements: item.item.0,
after_implements_kw: item.item.1.before,
item: item.item.1.item,
},
)
}
fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, EType<'a>> {

View file

@ -165,7 +165,7 @@ mod solve_expr {
Str.from_utf8
"
),
"List U8 -> Result Str [BadUtf8 Utf8ByteProblem U64]",
"List U8 -> Result Str [BadUtf8 { index : U64, problem : Utf8ByteProblem }]",
);
}

View file

@ -692,7 +692,7 @@ fn str_from_utf8_fail_invalid_start_byte() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 0x80, 99] is
Err (BadUtf8 InvalidStartByte byte_index) ->
Err (BadUtf8 {problem: InvalidStartByte, index: byte_index}) ->
if byte_index == 2 then
"a"
else
@ -712,7 +712,7 @@ fn str_from_utf8_fail_unexpected_end_of_sequence() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 99, 0xC2] is
Err (BadUtf8 UnexpectedEndOfSequence byte_index) ->
Err (BadUtf8 {problem: UnexpectedEndOfSequence, index: byte_index}) ->
if byte_index == 3 then
"a"
else
@ -732,7 +732,7 @@ fn str_from_utf8_fail_expected_continuation() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 99, 0xC2, 0x00] is
Err (BadUtf8 ExpectedContinuation byte_index) ->
Err (BadUtf8 {problem: ExpectedContinuation, index: byte_index}) ->
if byte_index == 3 then
"a"
else
@ -752,7 +752,7 @@ fn str_from_utf8_fail_overlong_encoding() {
indoc!(
r#"
when Str.from_utf8 [97, 0xF0, 0x80, 0x80, 0x80] is
Err (BadUtf8 OverlongEncoding byte_index) ->
Err (BadUtf8 {problem: OverlongEncoding, index: byte_index}) ->
if byte_index == 1 then
"a"
else
@ -772,7 +772,7 @@ fn str_from_utf8_fail_codepoint_too_large() {
indoc!(
r#"
when Str.from_utf8 [97, 0xF4, 0x90, 0x80, 0x80] is
Err (BadUtf8 CodepointTooLarge byte_index) ->
Err (BadUtf8 {problem: CodepointTooLarge, index: byte_index}) ->
if byte_index == 1 then
"a"
else
@ -792,7 +792,7 @@ fn str_from_utf8_fail_surrogate_half() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 0xED, 0xA0, 0x80] is
Err (BadUtf8 EncodesSurrogateHalf byte_index) ->
Err (BadUtf8 {problem: EncodesSurrogateHalf, index: byte_index}) ->
if byte_index == 2 then
"a"
else

View file

@ -522,7 +522,7 @@ fn str_from_utf8_fail_invalid_start_byte() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 0x80, 99] is
Err (BadUtf8 InvalidStartByte byte_index) ->
Err (BadUtf8 {problem: InvalidStartByte, index: byte_index}) ->
if byte_index == 2 then
"a"
else
@ -541,7 +541,7 @@ fn str_from_utf8_fail_unexpected_end_of_sequence() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 99, 0xC2] is
Err (BadUtf8 UnexpectedEndOfSequence byte_index) ->
Err (BadUtf8 {problem: UnexpectedEndOfSequence, index: byte_index}) ->
if byte_index == 3 then
"a"
else
@ -560,7 +560,7 @@ fn str_from_utf8_fail_expected_continuation() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 99, 0xC2, 0x00] is
Err (BadUtf8 ExpectedContinuation byte_index) ->
Err (BadUtf8 {problem: ExpectedContinuation, index: byte_index}) ->
if byte_index == 3 then
"a"
else
@ -579,7 +579,7 @@ fn str_from_utf8_fail_overlong_encoding() {
indoc!(
r#"
when Str.from_utf8 [97, 0xF0, 0x80, 0x80, 0x80] is
Err (BadUtf8 OverlongEncoding byte_index) ->
Err (BadUtf8 {problem: OverlongEncoding, index: byte_index}) ->
if byte_index == 1 then
"a"
else
@ -598,7 +598,7 @@ fn str_from_utf8_fail_codepoint_too_large() {
indoc!(
r#"
when Str.from_utf8 [97, 0xF4, 0x90, 0x80, 0x80] is
Err (BadUtf8 CodepointTooLarge byte_index) ->
Err (BadUtf8 {problem: CodepointTooLarge, index: byte_index}) ->
if byte_index == 1 then
"a"
else
@ -617,7 +617,7 @@ fn str_from_utf8_fail_surrogate_half() {
indoc!(
r#"
when Str.from_utf8 [97, 98, 0xED, 0xA0, 0x80] is
Err (BadUtf8 EncodesSurrogateHalf byte_index) ->
Err (BadUtf8 {problem: EncodesSurrogateHalf, index: byte_index}) ->
if byte_index == 2 then
"a"
else

View file

@ -164,31 +164,31 @@ procedure Num.96 (#Attr.2):
ret Num.287;
procedure Str.12 (#Attr.2):
let Str.258 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.258;
procedure Str.36 (#Attr.2):
let Str.259 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
let Str.259 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.259;
procedure Str.36 (#Attr.2):
let Str.260 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.260;
procedure Str.43 (#Attr.2):
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
else
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.45 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.45;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
procedure Test.20 (Test.56):

View file

@ -105,31 +105,31 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
procedure Str.43 (#Attr.2):
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
else
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.24 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.24;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
procedure Test.20 (Test.56):

View file

@ -112,31 +112,31 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
procedure Str.43 (#Attr.2):
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
else
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.28;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
procedure Test.20 (Test.56):

View file

@ -38,31 +38,31 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
procedure Str.43 (#Attr.2):
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
else
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.3 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.3;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
procedure Test.20 (Test.56):

View file

@ -110,31 +110,31 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
procedure Str.43 (#Attr.2):
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
else
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.27 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.27;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
procedure Test.20 (Test.56):

View file

@ -113,31 +113,31 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
procedure Str.43 (#Attr.2):
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253;
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then
let Str.252 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252;
ret Str.251;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
else
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.28;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
procedure Test.20 (Test.56):

View file

@ -0,0 +1,38 @@
//! Dump a syntax tree for a given input file.
//!
//! Typical usage:
//! `cargo run --bin dump_syntax -- full file.roc`
use std::process::exit;
use bumpalo::Bump;
use test_syntax::test_helpers::InputKind;
fn main() {
let args = std::env::args().collect::<Vec<String>>();
if args.len() != 3 {
eprintln!("Usage: {} [expr|full|moduledefs|header] <input>", args[0]);
std::process::exit(1);
}
let kind = match args[1].as_str() {
"expr" => InputKind::Expr,
"full" => InputKind::Full,
"moduledefs" => InputKind::ModuleDefs,
"header" => InputKind::Header,
_ => {
eprintln!("Invalid input kind: {}", args[1]);
std::process::exit(1);
}
};
let text = std::fs::read_to_string(&args[2]).unwrap_or_else(|e| {
eprintln!("Error reading file: {}", e);
exit(1);
});
let input = kind.with_text(&text);
let arena = Bump::new();
let output = input.parse_in(&arena);
eprintln!("memory used: {}", arena.allocated_bytes());
println!("{:#?}", output);
}

View file

@ -0,0 +1,5 @@
x :
(a where e
implements K)
-> Z
s

View file

@ -0,0 +1,81 @@
@0-31 SpaceAfter(
Defs(
Defs {
tags: [
EitherIndex(2147483648),
],
regions: [
@0-29,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [],
value_defs: [
Annotation(
@0-1 Identifier {
ident: "x",
},
@3-28 Function(
[
@3-25 Where(
@3-4 SpaceAfter(
BoundVariable(
"a",
),
[
Newline,
],
),
[
@11-25 ImplementsClause {
var: @11-12 SpaceBefore(
SpaceAfter(
"e",
[
Newline,
],
),
[
Newline,
],
),
abilities: [
@24-25 Apply(
"",
"K",
[],
),
],
},
],
),
],
Pure,
@27-28 Apply(
"",
"Z",
[],
),
),
),
],
},
@30-31 SpaceBefore(
Var {
module_name: "",
ident: "s",
},
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -0,0 +1,5 @@
x:(a
where
e
implements K->Z)
s

View file

@ -24,16 +24,16 @@
"p",
),
derived: Some(
@18-20 SpaceBefore(
Implements(
[],
),
[
ImplementsAbilities {
before_implements_kw: [
LineComment(
"",
),
],
),
implements: @8-18,
after_implements_kw: [],
item: @18-20 [],
},
),
},
],

View file

@ -0,0 +1,57 @@
@0-25 SpaceAfter(
Defs(
Defs {
tags: [
EitherIndex(0),
],
regions: [
@0-23,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [
Opaque {
header: TypeHeader {
name: @0-1 "P",
vars: [],
},
typ: @3-4 Apply(
"",
"W",
[],
),
derived: Some(
ImplementsAbilities {
before_implements_kw: [
Newline,
],
implements: @7-17,
after_implements_kw: [
Newline,
],
item: @21-23 [],
},
),
},
],
value_defs: [],
},
@24-25 SpaceBefore(
Var {
module_name: "",
ident: "t",
},
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -81,8 +81,11 @@
[],
),
derived: Some(
@19-29 Implements(
[
ImplementsAbilities {
before_implements_kw: [],
implements: @8-18,
after_implements_kw: [],
item: @19-29 [
@20-22 ImplementsAbility {
ability: @20-22 Apply(
"",
@ -100,7 +103,7 @@
impls: None,
},
],
),
},
),
},
Opaque {
@ -126,8 +129,11 @@
],
),
derived: Some(
@74-84 Implements(
[
ImplementsAbilities {
before_implements_kw: [],
implements: @63-73,
after_implements_kw: [],
item: @74-84 [
@75-77 ImplementsAbility {
ability: @75-77 Apply(
"",
@ -145,7 +151,7 @@
impls: None,
},
],
),
},
),
},
Opaque {
@ -171,9 +177,13 @@
],
),
derived: Some(
@134-144 SpaceBefore(
Implements(
[
ImplementsAbilities {
before_implements_kw: [
Newline,
],
implements: @123-133,
after_implements_kw: [],
item: @134-144 [
@135-137 ImplementsAbility {
ability: @135-137 Apply(
"",
@ -191,11 +201,7 @@
impls: None,
},
],
),
[
Newline,
],
),
},
),
},
Opaque {
@ -209,8 +215,11 @@
[],
),
derived: Some(
@165-187 Implements(
[
ImplementsAbilities {
before_implements_kw: [],
implements: @154-164,
after_implements_kw: [],
item: @165-187 [
@166-173 ImplementsAbility {
ability: @166-168 Apply(
"",
@ -244,7 +253,7 @@
),
},
],
),
},
),
},
Opaque {
@ -258,8 +267,11 @@
[],
),
derived: Some(
@208-222 Implements(
[
ImplementsAbilities {
before_implements_kw: [],
implements: @197-207,
after_implements_kw: [],
item: @208-222 [
@209-221 ImplementsAbility {
ability: @209-211 Apply(
"",
@ -280,7 +292,7 @@
),
},
],
),
},
),
},
Opaque {
@ -294,8 +306,11 @@
[],
),
derived: Some(
@243-263 Implements(
[
ImplementsAbilities {
before_implements_kw: [],
implements: @232-242,
after_implements_kw: [],
item: @243-263 [
@244-256 ImplementsAbility {
ability: @244-246 Apply(
"",
@ -324,7 +339,7 @@
impls: None,
},
],
),
},
),
},
Opaque {
@ -338,8 +353,11 @@
[],
),
derived: Some(
@284-304 Implements(
[
ImplementsAbilities {
before_implements_kw: [],
implements: @273-283,
after_implements_kw: [],
item: @284-304 [
@285-289 ImplementsAbility {
ability: @285-289 Apply(
"",
@ -368,7 +386,7 @@
),
},
],
),
},
),
},
Opaque {
@ -382,9 +400,12 @@
[],
),
derived: Some(
@325-327 Implements(
[],
),
ImplementsAbilities {
before_implements_kw: [],
implements: @314-324,
after_implements_kw: [],
item: @325-327 [],
},
),
},
Opaque {
@ -410,9 +431,13 @@
],
),
derived: Some(
@377-399 SpaceBefore(
Implements(
[
ImplementsAbilities {
before_implements_kw: [
Newline,
],
implements: @366-376,
after_implements_kw: [],
item: @377-399 [
@378-385 ImplementsAbility {
ability: @378-380 Apply(
"",
@ -446,11 +471,7 @@
),
},
],
),
[
Newline,
],
),
},
),
},
Opaque {
@ -464,8 +485,11 @@
[],
),
derived: Some(
@420-427 Implements(
[
ImplementsAbilities {
before_implements_kw: [],
implements: @409-419,
after_implements_kw: [],
item: @420-427 [
@421-426 ImplementsAbility {
ability: @421-423 Apply(
"",
@ -479,7 +503,7 @@
),
},
],
),
},
),
},
],

View file

@ -0,0 +1,36 @@
@0-15 SpaceAfter(
Return(
@0-15 SpaceBefore(
Apply(
@8-14 RecordUpdate {
update: @11-12 SpaceBefore(
Var {
module_name: "",
ident: "g",
},
[
LineComment(
"",
),
],
),
fields: [],
},
[
@14-15 Var {
module_name: "",
ident: "e",
},
],
Space,
),
[
Newline,
],
),
None,
),
[
Newline,
],
)

View file

@ -301,6 +301,7 @@ mod test_snapshots {
pass/ann_effectful_fn.expr,
pass/ann_open_union.expr,
pass/ann_parens_comments.expr,
pass/ann_parens_where_implements_func.expr,
pass/ann_pattern_comment_before_body.expr,
pass/ann_record_pat_with_comment.expr,
pass/ann_tag_union_newline_comment.expr,
@ -552,6 +553,7 @@ mod test_snapshots {
pass/newline_after_sub.expr,
pass/newline_and_spaces_before_less_than.expr,
pass/newline_before_add.expr,
pass/newline_before_and_after_implements_opaque.expr,
pass/newline_before_import_curlies.expr,
pass/newline_before_sub.expr,
pass/newline_in_packages.full,
@ -666,6 +668,7 @@ mod test_snapshots {
pass/return_multiline.expr,
pass/return_only_statement.expr,
pass/return_parens_comments.expr,
pass/return_record_update_comment_empty_fields.expr,
pass/return_then_nested_parens.expr,
pass/return_with_after.expr,
pass/separate_defs.moduledefs,

View file

@ -535,12 +535,7 @@ impl IterTokens for Loc<ImplementsAbilities<'_>> {
impl IterTokens for ImplementsAbilities<'_> {
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
match self {
ImplementsAbilities::Implements(impls) => impls.iter_tokens(arena),
ImplementsAbilities::SpaceBefore(i, _) | ImplementsAbilities::SpaceAfter(i, _) => {
i.iter_tokens(arena)
}
}
self.item.value.iter_tokens(arena)
}
}

View file

@ -1321,59 +1321,9 @@ fn surgery_macho_help(
continue;
} else if matches!(app_obj.symbol_by_index(index), Ok(sym) if ["_longjmp", "_setjmp"].contains(&sym.name().unwrap_or_default()))
{
// These symbols have to stay undefined as we dynamically link them from libSystem.dylib at runtime.
// TODO have a table of all known symbols; perhaps parse and use an Apple provided libSystem.tbd stub file?
let name = app_obj
.symbol_by_index(index)
.and_then(|sym| sym.name())
.ok()
.unwrap();
match rel.1.kind() {
RelocationKind::PltRelative => {
if verbose {
println!("\t\tTODO synthesise __stub entry for {name}")
}
}
RelocationKind::Got => {
if verbose {
println!("\t\tTODO synthesise __got entry for {name}")
}
}
RelocationKind::Unknown => {
if let RelocationFlags::MachO { r_type, .. } = rel.1.flags() {
match r_type {
macho::ARM64_RELOC_GOT_LOAD_PAGE21
| macho::ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
if verbose {
println!(
"\t\tTODO synthesise __got entry for {name}"
)
}
}
macho::ARM64_RELOC_BRANCH26 => {
if verbose {
println!(
"\t\tTODO synthesise __stub entry for {name}"
)
}
}
_ => internal_error!(
"Invalid relocation for libc symbol, {:+x?}: {name}",
rel
),
}
} else {
internal_error!(
"Invalid relocation found for Mach-O: {:?}",
rel
);
}
}
_ => internal_error!(
"Invalid relocation for libc symbol, {:+x?}: {name}",
rel
),
}
// Explicitly ignore `longjmp` and `setjmp` which are used only in `roc test` mode and thus are unreferenced
// by the app and can be safely skipped.
// In the future, `longjmp` and `setjmp` will be obsoleted and thus this prong can be safely deleted.
continue;
} else {
internal_error!(

View file

@ -59,6 +59,7 @@
xorg.libXi
xorg.libxcb
cargo-llvm-cov # to visualize code coverage
];
# DevInputs are not necessary to build roc as a user
@ -75,6 +76,8 @@
cmake
# provides llvm
llvmPkgs.dev
# for debugging:
# lldb
# faster builds - see https://github.com/roc-lang/roc/blob/main/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker
# provides lld
pkgs.lld_18