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 run: nix-build
- name: execute tests with --release - 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 run: nix develop -c cargo test --locked --release -- --skip glue_cli_tests
- name: 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 run: nix develop -c cargo test --locked --release glue_cli_tests -- --test-threads=1
- name: roc test all builtins - name: roc test all builtins
run: nix develop -c ./ci/roc_test_builtins.sh run: nix develop -c ./ci/roc_test_builtins.sh
- name: test wasm32 cli_tests - 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 run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" -- --skip glue_cli_tests
- name: wasm32 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 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 - name: test the dev backend # these tests require an explicit feature flag

View file

@ -63,7 +63,7 @@ jobs:
- name: test website build script - name: test website build script
run: bash www/build.sh run: bash www/build.sh
- name: run fuzz tests - ok to merge if this one fails # for now! #- name: run fuzz tests - ok to merge if this one fails # for now!
run: | # run: |
cargo +nightly-2024-02-03 install --locked cargo-fuzz # 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 # 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 - name: Check if debug flag files are in sync
run: ./ci/check_debug_vars.sh run: ./ci/check_debug_vars.sh
# for skipped tests; see #6946, #6947
- name: cargo test without --release - 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> Ch1n3du <danielonyesoh@gmail.com>
Elias Mulhall <eli.mulhall@gmail.com> Elias Mulhall <eli.mulhall@gmail.com>
ABuffSeagull <reecevanatta@hey.com> ABuffSeagull <reecevanatta@hey.com>
Timon Krebs <timonkrebs@hotmail.com>

View file

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

View file

@ -80,6 +80,8 @@ impl ExecCli {
app_stdin_opt: Option<&str>, app_stdin_opt: Option<&str>,
app_args_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(); let build_cmd_out = self.run();
build_cmd_out.assert_clean_success(); build_cmd_out.assert_clean_success();

View file

@ -456,7 +456,7 @@ fn gen_from_mono_module_dev<'a>(
#[cfg(feature = "target-wasm32")] #[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) => { (_, Architecture::Aarch32) => {

View file

@ -283,10 +283,16 @@ dbg_bool = \b ->
dbg_str : Str -> Inspector DbgFormatter dbg_str : Str -> Inspector DbgFormatter
dbg_str = \s -> 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 -> custom_dbg_str = \f0 ->
f0 dbg_write(f0, "\"")
|> dbg_write("\"") |> dbg_write(escape_s)
|> dbg_write(s) # TODO: Should we be escaping strings for dbg/logging?
|> dbg_write("\"") |> dbg_write("\"")
custom(custom_dbg_str) custom(custom_dbg_str)

View file

@ -538,14 +538,14 @@ to_utf8 : Str -> List U8
## expect Str.from_utf8([]) == Ok("") ## expect Str.from_utf8([]) == Ok("")
## expect Str.from_utf8([255]) |> Result.is_err ## 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 -> from_utf8 = \bytes ->
result = from_utf8_lowlevel(bytes) result = from_utf8_lowlevel bytes
if result.c_is_ok then if result.c_is_ok then
Ok(result.b_string) Ok(result.b_string)
else 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([82, 111, 99])) == Ok("Roc")
expect (Str.from_utf8([224, 174, 154, 224, 174, 191])) == Ok("சி") expect (Str.from_utf8([224, 174, 154, 224, 174, 191])) == Ok("சி")

View file

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

View file

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

View file

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

View file

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

View file

@ -841,7 +841,7 @@ pub enum TypeDef<'a> {
Opaque { Opaque {
header: TypeHeader<'a>, header: TypeHeader<'a>,
typ: Loc<TypeAnnotation<'a>>, typ: Loc<TypeAnnotation<'a>>,
derived: Option<Loc<ImplementsAbilities<'a>>>, derived: Option<&'a ImplementsAbilities<'a>>,
}, },
/// An ability definition. E.g. /// An ability definition. E.g.
@ -1552,31 +1552,11 @@ pub enum ImplementsAbility<'a> {
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum ImplementsAbilities<'a> { pub struct ImplementsAbilities<'a> {
/// `implements [Eq { eq: myEq }, Hash]` pub before_implements_kw: &'a [CommentOrNewline<'a>],
Implements(Collection<'a, Loc<ImplementsAbility<'a>>>), pub implements: Region,
pub after_implements_kw: &'a [CommentOrNewline<'a>],
// We preserve this for the formatter; canonicalization ignores it. pub item: Loc<Collection<'a, Loc<ImplementsAbility<'a>>>>,
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()
}
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[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> { impl<'a> Expr<'a> {
pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var { pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var {
module_name: "", module_name: "",
@ -2396,7 +2367,6 @@ impl_extract_spaces!(Tag);
impl_extract_spaces!(AssignedField<T>); impl_extract_spaces!(AssignedField<T>);
impl_extract_spaces!(TypeAnnotation); impl_extract_spaces!(TypeAnnotation);
impl_extract_spaces!(ImplementsAbility); impl_extract_spaces!(ImplementsAbility);
impl_extract_spaces!(ImplementsAbilities);
impl_extract_spaces!(Implements); impl_extract_spaces!(Implements);
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> { impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
@ -2720,7 +2690,11 @@ impl<'a> Malformed for TypeDef<'a> {
header, header,
typ, typ,
derived, 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 { TypeDef::Ability {
header, header,
loc_implements, 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> { impl<'a> Malformed for AbilityImpls<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
match self { match self {

View file

@ -1,5 +1,6 @@
use crate::ast::CommentOrNewline; use crate::ast::CommentOrNewline;
use crate::ast::Spaceable; use crate::ast::Spaceable;
use crate::ast::SpacesBefore;
use crate::parser::succeed; use crate::parser::succeed;
use crate::parser::Progress; use crate::parser::Progress;
use crate::parser::SpaceProblem; 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>( pub fn space0_after_e<'a, P, S, E>(
parser: P, parser: P,
indent_problem: fn(Position) -> E, 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))) increment_min_indent(specialize_err(EExpr::Type, type_annotation::located(false)))
} }
fn opaque_signature<'a>() -> impl Parser< fn opaque_signature<'a>(
'a, ) -> impl Parser<'a, (Loc<TypeAnnotation<'a>>, Option<&'a ImplementsAbilities<'a>>), EExpr<'a>> {
(
Loc<TypeAnnotation<'a>>,
Option<Loc<ImplementsAbilities<'a>>>,
),
EExpr<'a>,
> {
and( and(
specialize_err(EExpr::Type, type_annotation::located_opaque_signature(true)), specialize_err(EExpr::Type, type_annotation::located_opaque_signature(true)),
optional(backtrackable(specialize_err( optional(map_with_arena(
EExpr::Type, specialize_err(EExpr::Type, type_annotation::implements_abilities()),
space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart), |arena, item| &*arena.alloc(item),
))), )),
) )
} }

View file

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

View file

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

View file

@ -165,7 +165,7 @@ mod solve_expr {
Str.from_utf8 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!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 0x80, 99] is 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 if byte_index == 2 then
"a" "a"
else else
@ -712,7 +712,7 @@ fn str_from_utf8_fail_unexpected_end_of_sequence() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 99, 0xC2] is 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 if byte_index == 3 then
"a" "a"
else else
@ -732,7 +732,7 @@ fn str_from_utf8_fail_expected_continuation() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 99, 0xC2, 0x00] is 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 if byte_index == 3 then
"a" "a"
else else
@ -752,7 +752,7 @@ fn str_from_utf8_fail_overlong_encoding() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 0xF0, 0x80, 0x80, 0x80] is 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 if byte_index == 1 then
"a" "a"
else else
@ -772,7 +772,7 @@ fn str_from_utf8_fail_codepoint_too_large() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 0xF4, 0x90, 0x80, 0x80] is 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 if byte_index == 1 then
"a" "a"
else else
@ -792,7 +792,7 @@ fn str_from_utf8_fail_surrogate_half() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 0xED, 0xA0, 0x80] is 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 if byte_index == 2 then
"a" "a"
else else

View file

@ -522,7 +522,7 @@ fn str_from_utf8_fail_invalid_start_byte() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 0x80, 99] is 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 if byte_index == 2 then
"a" "a"
else else
@ -541,7 +541,7 @@ fn str_from_utf8_fail_unexpected_end_of_sequence() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 99, 0xC2] is 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 if byte_index == 3 then
"a" "a"
else else
@ -560,7 +560,7 @@ fn str_from_utf8_fail_expected_continuation() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 99, 0xC2, 0x00] is 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 if byte_index == 3 then
"a" "a"
else else
@ -579,7 +579,7 @@ fn str_from_utf8_fail_overlong_encoding() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 0xF0, 0x80, 0x80, 0x80] is 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 if byte_index == 1 then
"a" "a"
else else
@ -598,7 +598,7 @@ fn str_from_utf8_fail_codepoint_too_large() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 0xF4, 0x90, 0x80, 0x80] is 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 if byte_index == 1 then
"a" "a"
else else
@ -617,7 +617,7 @@ fn str_from_utf8_fail_surrogate_half() {
indoc!( indoc!(
r#" r#"
when Str.from_utf8 [97, 98, 0xED, 0xA0, 0x80] is 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 if byte_index == 2 then
"a" "a"
else else

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -113,31 +113,31 @@ procedure Num.96 (#Attr.2):
ret Num.283; ret Num.283;
procedure Str.12 (#Attr.2): procedure Str.12 (#Attr.2):
let Str.255 : List U8 = lowlevel StrToUtf8 #Attr.2; let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.255;
procedure Str.36 (#Attr.2):
let Str.256 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.256; ret Str.256;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
procedure Str.43 (#Attr.2): procedure Str.43 (#Attr.2):
let Str.253 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.253; ret Str.254;
procedure Str.9 (Str.73): procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73; let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.250 : Int1 = StructAtIndex 2 Str.74; let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.250 then if Str.251 then
let Str.252 : Str = StructAtIndex 1 Str.74; let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.251 : [C {U64, U8}, C Str] = TagId(1) Str.252; let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.251; ret Str.252;
else else
let Str.248 : U8 = StructAtIndex 3 Str.74;
let Str.249 : U64 = StructAtIndex 0 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; let #Derived_gen.28 : Str = StructAtIndex 1 Str.74;
dec #Derived_gen.28; dec #Derived_gen.28;
let Str.247 : {U64, U8} = Struct {Str.249, Str.248}; let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.247; let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246; ret Str.246;
procedure Test.20 (Test.56): 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", "p",
), ),
derived: Some( derived: Some(
@18-20 SpaceBefore( ImplementsAbilities {
Implements( before_implements_kw: [
[],
),
[
LineComment( 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( derived: Some(
@19-29 Implements( ImplementsAbilities {
[ before_implements_kw: [],
implements: @8-18,
after_implements_kw: [],
item: @19-29 [
@20-22 ImplementsAbility { @20-22 ImplementsAbility {
ability: @20-22 Apply( ability: @20-22 Apply(
"", "",
@ -100,7 +103,7 @@
impls: None, impls: None,
}, },
], ],
), },
), ),
}, },
Opaque { Opaque {
@ -126,8 +129,11 @@
], ],
), ),
derived: Some( derived: Some(
@74-84 Implements( ImplementsAbilities {
[ before_implements_kw: [],
implements: @63-73,
after_implements_kw: [],
item: @74-84 [
@75-77 ImplementsAbility { @75-77 ImplementsAbility {
ability: @75-77 Apply( ability: @75-77 Apply(
"", "",
@ -145,7 +151,7 @@
impls: None, impls: None,
}, },
], ],
), },
), ),
}, },
Opaque { Opaque {
@ -171,31 +177,31 @@
], ],
), ),
derived: Some( derived: Some(
@134-144 SpaceBefore( ImplementsAbilities {
Implements( before_implements_kw: [
[
@135-137 ImplementsAbility {
ability: @135-137 Apply(
"",
"Eq",
[],
),
impls: None,
},
@139-143 ImplementsAbility {
ability: @139-143 Apply(
"",
"Hash",
[],
),
impls: None,
},
],
),
[
Newline, Newline,
], ],
), implements: @123-133,
after_implements_kw: [],
item: @134-144 [
@135-137 ImplementsAbility {
ability: @135-137 Apply(
"",
"Eq",
[],
),
impls: None,
},
@139-143 ImplementsAbility {
ability: @139-143 Apply(
"",
"Hash",
[],
),
impls: None,
},
],
},
), ),
}, },
Opaque { Opaque {
@ -209,8 +215,11 @@
[], [],
), ),
derived: Some( derived: Some(
@165-187 Implements( ImplementsAbilities {
[ before_implements_kw: [],
implements: @154-164,
after_implements_kw: [],
item: @165-187 [
@166-173 ImplementsAbility { @166-173 ImplementsAbility {
ability: @166-168 Apply( ability: @166-168 Apply(
"", "",
@ -244,7 +253,7 @@
), ),
}, },
], ],
), },
), ),
}, },
Opaque { Opaque {
@ -258,8 +267,11 @@
[], [],
), ),
derived: Some( derived: Some(
@208-222 Implements( ImplementsAbilities {
[ before_implements_kw: [],
implements: @197-207,
after_implements_kw: [],
item: @208-222 [
@209-221 ImplementsAbility { @209-221 ImplementsAbility {
ability: @209-211 Apply( ability: @209-211 Apply(
"", "",
@ -280,7 +292,7 @@
), ),
}, },
], ],
), },
), ),
}, },
Opaque { Opaque {
@ -294,8 +306,11 @@
[], [],
), ),
derived: Some( derived: Some(
@243-263 Implements( ImplementsAbilities {
[ before_implements_kw: [],
implements: @232-242,
after_implements_kw: [],
item: @243-263 [
@244-256 ImplementsAbility { @244-256 ImplementsAbility {
ability: @244-246 Apply( ability: @244-246 Apply(
"", "",
@ -324,7 +339,7 @@
impls: None, impls: None,
}, },
], ],
), },
), ),
}, },
Opaque { Opaque {
@ -338,8 +353,11 @@
[], [],
), ),
derived: Some( derived: Some(
@284-304 Implements( ImplementsAbilities {
[ before_implements_kw: [],
implements: @273-283,
after_implements_kw: [],
item: @284-304 [
@285-289 ImplementsAbility { @285-289 ImplementsAbility {
ability: @285-289 Apply( ability: @285-289 Apply(
"", "",
@ -368,7 +386,7 @@
), ),
}, },
], ],
), },
), ),
}, },
Opaque { Opaque {
@ -382,9 +400,12 @@
[], [],
), ),
derived: Some( derived: Some(
@325-327 Implements( ImplementsAbilities {
[], before_implements_kw: [],
), implements: @314-324,
after_implements_kw: [],
item: @325-327 [],
},
), ),
}, },
Opaque { Opaque {
@ -410,47 +431,47 @@
], ],
), ),
derived: Some( derived: Some(
@377-399 SpaceBefore( ImplementsAbilities {
Implements( before_implements_kw: [
[
@378-385 ImplementsAbility {
ability: @378-380 Apply(
"",
"Eq",
[],
),
impls: Some(
@381-385 AbilityImpls(
[
@382-384 LabelOnly(
@382-384 "eq",
),
],
),
),
},
@387-398 ImplementsAbility {
ability: @387-391 Apply(
"",
"Hash",
[],
),
impls: Some(
@392-398 AbilityImpls(
[
@393-397 LabelOnly(
@393-397 "hash",
),
],
),
),
},
],
),
[
Newline, Newline,
], ],
), implements: @366-376,
after_implements_kw: [],
item: @377-399 [
@378-385 ImplementsAbility {
ability: @378-380 Apply(
"",
"Eq",
[],
),
impls: Some(
@381-385 AbilityImpls(
[
@382-384 LabelOnly(
@382-384 "eq",
),
],
),
),
},
@387-398 ImplementsAbility {
ability: @387-391 Apply(
"",
"Hash",
[],
),
impls: Some(
@392-398 AbilityImpls(
[
@393-397 LabelOnly(
@393-397 "hash",
),
],
),
),
},
],
},
), ),
}, },
Opaque { Opaque {
@ -464,8 +485,11 @@
[], [],
), ),
derived: Some( derived: Some(
@420-427 Implements( ImplementsAbilities {
[ before_implements_kw: [],
implements: @409-419,
after_implements_kw: [],
item: @420-427 [
@421-426 ImplementsAbility { @421-426 ImplementsAbility {
ability: @421-423 Apply( 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_effectful_fn.expr,
pass/ann_open_union.expr, pass/ann_open_union.expr,
pass/ann_parens_comments.expr, pass/ann_parens_comments.expr,
pass/ann_parens_where_implements_func.expr,
pass/ann_pattern_comment_before_body.expr, pass/ann_pattern_comment_before_body.expr,
pass/ann_record_pat_with_comment.expr, pass/ann_record_pat_with_comment.expr,
pass/ann_tag_union_newline_comment.expr, pass/ann_tag_union_newline_comment.expr,
@ -552,6 +553,7 @@ mod test_snapshots {
pass/newline_after_sub.expr, pass/newline_after_sub.expr,
pass/newline_and_spaces_before_less_than.expr, pass/newline_and_spaces_before_less_than.expr,
pass/newline_before_add.expr, pass/newline_before_add.expr,
pass/newline_before_and_after_implements_opaque.expr,
pass/newline_before_import_curlies.expr, pass/newline_before_import_curlies.expr,
pass/newline_before_sub.expr, pass/newline_before_sub.expr,
pass/newline_in_packages.full, pass/newline_in_packages.full,
@ -666,6 +668,7 @@ mod test_snapshots {
pass/return_multiline.expr, pass/return_multiline.expr,
pass/return_only_statement.expr, pass/return_only_statement.expr,
pass/return_parens_comments.expr, pass/return_parens_comments.expr,
pass/return_record_update_comment_empty_fields.expr,
pass/return_then_nested_parens.expr, pass/return_then_nested_parens.expr,
pass/return_with_after.expr, pass/return_with_after.expr,
pass/separate_defs.moduledefs, pass/separate_defs.moduledefs,

View file

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

View file

@ -1321,59 +1321,9 @@ fn surgery_macho_help(
continue; continue;
} else if matches!(app_obj.symbol_by_index(index), Ok(sym) if ["_longjmp", "_setjmp"].contains(&sym.name().unwrap_or_default())) } 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. // Explicitly ignore `longjmp` and `setjmp` which are used only in `roc test` mode and thus are unreferenced
// TODO have a table of all known symbols; perhaps parse and use an Apple provided libSystem.tbd stub file? // by the app and can be safely skipped.
let name = app_obj // In the future, `longjmp` and `setjmp` will be obsoleted and thus this prong can be safely deleted.
.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
),
}
continue; continue;
} else { } else {
internal_error!( internal_error!(

View file

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