mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Merge branch 'main' into constrain-early-return-functions
This commit is contained in:
commit
20ba4a92de
88 changed files with 155 additions and 518 deletions
2
.github/workflows/nix_macos_x86_64.yml
vendored
2
.github/workflows/nix_macos_x86_64.yml
vendored
|
@ -9,7 +9,7 @@ env:
|
|||
jobs:
|
||||
nix-macos-x86-64:
|
||||
name: nix-macos-x86-64
|
||||
runs-on: [macos-12]
|
||||
runs-on: [macos-13]
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
4
.github/workflows/test_nightly_many_os.yml
vendored
4
.github/workflows/test_nightly_many_os.yml
vendored
|
@ -5,11 +5,11 @@ name: Test latest nightly releases for macOS and Linux x86_64
|
|||
|
||||
jobs:
|
||||
test-nightly:
|
||||
name: test nightly macos 11/12/13, ubuntu 20.04/22.04
|
||||
name: test nightly macos 13 (x64), ubuntu 20.04/22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ macos-12, macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
|
||||
os: [ macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3007,7 +3007,6 @@ dependencies = [
|
|||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"signal-hook",
|
||||
"strip-ansi-escapes",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
|
|
|
@ -29,14 +29,15 @@ mv roc_nightly* roc_nightly
|
|||
cd roc_nightly
|
||||
|
||||
# test rust platform (first prebuild the host)
|
||||
examples/platform-switching/rust-platform/build.sh
|
||||
./roc examples/platform-switching/rocLovesRust.roc
|
||||
# temp disabled
|
||||
# examples/platform-switching/rust-platform/build.sh
|
||||
# ./roc examples/platform-switching/rocLovesRust.roc
|
||||
|
||||
# test zig platform
|
||||
./roc examples/platform-switching/rocLovesZig.roc
|
||||
./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesZig.roc
|
||||
|
||||
# test C platform
|
||||
./roc examples/platform-switching/rocLovesC.roc
|
||||
./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesC.roc
|
||||
|
||||
# test repl
|
||||
cd ../ci/repl_basic_test
|
||||
|
|
|
@ -62,7 +62,7 @@ mod cli_tests {
|
|||
.unwrap();
|
||||
|
||||
let cli_build = ExecCli::new(
|
||||
roc_cli::CMD_RUN,
|
||||
roc_cli::CMD_DEV,
|
||||
file_from_root("examples/platform-switching", "rocLovesRust.roc"),
|
||||
);
|
||||
|
||||
|
@ -359,7 +359,7 @@ mod cli_tests {
|
|||
mod test_platform_basic_cli {
|
||||
|
||||
use super::*;
|
||||
use roc_cli::CMD_RUN;
|
||||
use roc_cli::CMD_DEV;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
|
@ -418,7 +418,7 @@ mod cli_tests {
|
|||
)]
|
||||
fn module_params_issue_7116() {
|
||||
let cli_build = ExecCli::new(
|
||||
CMD_RUN,
|
||||
CMD_DEV,
|
||||
file_from_root(
|
||||
"crates/cli/tests/test-projects/module_params",
|
||||
"issue_7116.roc",
|
||||
|
@ -439,7 +439,7 @@ mod cli_tests {
|
|||
)]
|
||||
fn module_params_pass_task() {
|
||||
let cli_build = ExecCli::new(
|
||||
CMD_RUN,
|
||||
CMD_DEV,
|
||||
file_from_root(
|
||||
"crates/cli/tests/test-projects/module_params",
|
||||
"pass_task.roc",
|
||||
|
|
|
@ -872,7 +872,7 @@ fn build_loaded_file<'a>(
|
|||
|
||||
let built_host_opt =
|
||||
// Not sure if this is correct for all calls with LinkType::Dylib...
|
||||
if link_type == LinkType::Dylib || target == Target::Wasm32 {
|
||||
if link_type == LinkType::None || link_type == LinkType::Dylib || target == Target::Wasm32 {
|
||||
BuiltHostOpt::None
|
||||
} else {
|
||||
let prebuilt_host = determine_built_host_path(&platform_main_roc_path, target, build_host_requested, link_type, linking_strategy, suppress_build_host_warning);
|
||||
|
|
|
@ -62,7 +62,6 @@ fn print_declarations_help<'a>(
|
|||
toplevel_function(c, f, symbol, function_def, &body.value)
|
||||
}
|
||||
DeclarationTag::Expectation => todo!(),
|
||||
DeclarationTag::ExpectationFx => todo!(),
|
||||
DeclarationTag::Destructure(_) => todo!(),
|
||||
DeclarationTag::MutualRecursion { .. } => {
|
||||
// the defs will be printed next
|
||||
|
|
|
@ -342,7 +342,6 @@ pub enum Declaration {
|
|||
DeclareRec(Vec<Def>, IllegalCycleMark),
|
||||
Builtin(Def),
|
||||
Expects(ExpectsOrDbgs),
|
||||
ExpectsFx(ExpectsOrDbgs),
|
||||
/// If we know a cycle is illegal during canonicalization.
|
||||
/// Otherwise we will try to detect this during solving; see [`IllegalCycleMark`].
|
||||
InvalidCycle(Vec<CycleEntry>),
|
||||
|
@ -357,7 +356,6 @@ impl Declaration {
|
|||
InvalidCycle { .. } => 0,
|
||||
Builtin(_) => 0,
|
||||
Expects(_) => 0,
|
||||
ExpectsFx(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,7 +371,7 @@ impl Declaration {
|
|||
&cycles.first().unwrap().expr_region,
|
||||
&cycles.last().unwrap().expr_region,
|
||||
),
|
||||
Declaration::Expects(expects) | Declaration::ExpectsFx(expects) => Region::span_across(
|
||||
Declaration::Expects(expects) => Region::span_across(
|
||||
expects.regions.first().unwrap(),
|
||||
expects.regions.last().unwrap(),
|
||||
),
|
||||
|
@ -2835,10 +2833,6 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
|
|||
|
||||
loc_ret
|
||||
}
|
||||
Declaration::ExpectsFx(expects) => {
|
||||
// Expects should only be added to top-level decls, not to let-exprs!
|
||||
unreachable!("{:?}", &expects)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1100,15 +1100,6 @@ pub fn desugar_expr<'a>(
|
|||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
Expect(condition, continuation) => {
|
||||
let desugared_condition = &*env.arena.alloc(desugar_expr(env, scope, condition));
|
||||
let desugared_continuation = &*env.arena.alloc(desugar_expr(env, scope, continuation));
|
||||
|
||||
env.arena.alloc(Loc {
|
||||
value: Expect(desugared_condition, desugared_continuation),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
Dbg => {
|
||||
// Allow naked dbg, necessary for piping values into dbg with the `Pizza` binop
|
||||
loc_expr
|
||||
|
|
|
@ -1188,36 +1188,6 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::Expr::Expect(condition, continuation) => {
|
||||
let mut output = Output::default();
|
||||
|
||||
let (loc_condition, output1) =
|
||||
canonicalize_expr(env, var_store, scope, condition.region, &condition.value);
|
||||
|
||||
// Get all the lookups that were referenced in the condition,
|
||||
// so we can print their values later.
|
||||
let lookups_in_cond = get_lookup_symbols(&loc_condition.value);
|
||||
|
||||
let (loc_continuation, output2) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
continuation.region,
|
||||
&continuation.value,
|
||||
);
|
||||
|
||||
output.union(output1);
|
||||
output.union(output2);
|
||||
|
||||
(
|
||||
Expect {
|
||||
loc_condition: Box::new(loc_condition),
|
||||
loc_continuation: Box::new(loc_continuation),
|
||||
lookups_in_cond,
|
||||
},
|
||||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::Dbg => {
|
||||
// Dbg was not desugared as either part of an `Apply` or a `Pizza` binop, so it's
|
||||
// invalid.
|
||||
|
@ -2534,7 +2504,6 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
// Newlines are disallowed inside interpolation, and these all require newlines
|
||||
ast::Expr::DbgStmt(_, _)
|
||||
| ast::Expr::LowLevelDbg(_, _, _)
|
||||
| ast::Expr::Expect(_, _)
|
||||
| ast::Expr::Return(_, _)
|
||||
| ast::Expr::When(_, _)
|
||||
| ast::Expr::Backpassing(_, _, _)
|
||||
|
@ -3241,12 +3210,6 @@ impl Declarations {
|
|||
|
||||
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_expr =
|
||||
toplevel_expect_to_inline_expect_fx(self.expressions[index].clone());
|
||||
|
||||
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3265,7 +3228,6 @@ roc_error_macros::assert_sizeof_default!(DeclarationTag, 8);
|
|||
pub enum DeclarationTag {
|
||||
Value,
|
||||
Expectation,
|
||||
ExpectationFx,
|
||||
Function(Index<Loc<FunctionDef>>),
|
||||
Recursive(Index<Loc<FunctionDef>>),
|
||||
TailRecursive(Index<Loc<FunctionDef>>),
|
||||
|
@ -3283,7 +3245,7 @@ impl DeclarationTag {
|
|||
match self {
|
||||
Function(_) | Recursive(_) | TailRecursive(_) => 1,
|
||||
Value => 1,
|
||||
Expectation | ExpectationFx => 1,
|
||||
Expectation => 1,
|
||||
Destructure(_) => 1,
|
||||
MutualRecursion { length, .. } => length as usize + 1,
|
||||
}
|
||||
|
@ -3484,15 +3446,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
/// This is supposed to happen just before monomorphization:
|
||||
/// all type errors and such are generated from the user source,
|
||||
/// but this transformation means that we don't need special codegen for toplevel expects
|
||||
pub fn toplevel_expect_to_inline_expect_pure(loc_expr: Loc<Expr>) -> Loc<Expr> {
|
||||
toplevel_expect_to_inline_expect_help(loc_expr, false)
|
||||
}
|
||||
|
||||
pub fn toplevel_expect_to_inline_expect_fx(loc_expr: Loc<Expr>) -> Loc<Expr> {
|
||||
toplevel_expect_to_inline_expect_help(loc_expr, true)
|
||||
}
|
||||
|
||||
fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: bool) -> Loc<Expr> {
|
||||
pub fn toplevel_expect_to_inline_expect_pure(mut loc_expr: Loc<Expr>) -> Loc<Expr> {
|
||||
enum StoredDef {
|
||||
NonRecursive(Region, Box<Def>),
|
||||
Recursive(Region, Vec<Def>, IllegalCycleMark),
|
||||
|
@ -3530,7 +3484,6 @@ fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: b
|
|||
}
|
||||
|
||||
let expect_region = loc_expr.region;
|
||||
assert!(!has_effects);
|
||||
let expect = Expr::Expect {
|
||||
loc_condition: Box::new(loc_expr),
|
||||
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),
|
||||
|
|
|
@ -609,7 +609,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// the declarations of this group will be treaded individually by later iterations
|
||||
}
|
||||
Expectation => { /* ignore */ }
|
||||
ExpectationFx => { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -747,14 +746,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_expr = &mut declarations.expressions[index];
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,49 +155,6 @@ pub fn unwrap_suffixed_expression<'a>(
|
|||
|
||||
Expr::LowLevelDbg(..) => unwrap_low_level_dbg(arena, loc_expr, maybe_def_pat),
|
||||
|
||||
Expr::Expect(condition, continuation) => {
|
||||
if is_expr_suffixed(&condition.value) {
|
||||
// we cannot unwrap a suffixed expression within expect
|
||||
// e.g. expect (foo! "bar")
|
||||
return Err(EUnwrapped::Malformed);
|
||||
}
|
||||
|
||||
match unwrap_suffixed_expression(arena, continuation, maybe_def_pat) {
|
||||
Ok(unwrapped_expr) => {
|
||||
let new_expect = arena
|
||||
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
|
||||
return Ok(new_expect);
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr {
|
||||
loc_expr: unwrapped_expr,
|
||||
target,
|
||||
}) => {
|
||||
let new_expect = arena
|
||||
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
|
||||
Err(EUnwrapped::UnwrappedDefExpr {
|
||||
loc_expr: new_expect,
|
||||
target,
|
||||
})
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg: unwrapped_expr,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
target,
|
||||
}) => {
|
||||
let new_expect = arena
|
||||
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg: new_expect,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
target,
|
||||
})
|
||||
}
|
||||
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
|
||||
}
|
||||
}
|
||||
|
||||
// we only need to unwrap some expressions, leave the rest as is
|
||||
_ => Ok(loc_expr),
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
|||
annotation: decls.annotations[index].as_ref(),
|
||||
}
|
||||
}
|
||||
Expectation | ExpectationFx => {
|
||||
Expectation => {
|
||||
let loc_condition = &decls.expressions[index];
|
||||
|
||||
DeclarationInfo::Expectation { loc_condition }
|
||||
|
|
|
@ -2754,34 +2754,6 @@ pub fn constrain_decls(
|
|||
expected,
|
||||
);
|
||||
|
||||
constraint = constraints.let_constraint(
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
expect_constraint,
|
||||
constraint,
|
||||
Generalizable(false),
|
||||
)
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_expr = &declarations.expressions[index];
|
||||
|
||||
let bool_type = constraints.push_variable(Variable::BOOL);
|
||||
let expected = constraints.push_expected_type(Expected::ForReason(
|
||||
Reason::ExpectCondition,
|
||||
bool_type,
|
||||
loc_expr.region,
|
||||
));
|
||||
|
||||
let expect_constraint = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
loc_expr.region,
|
||||
&loc_expr.value,
|
||||
expected,
|
||||
);
|
||||
|
||||
constraint = constraints.let_constraint(
|
||||
[],
|
||||
[],
|
||||
|
|
|
@ -64,9 +64,6 @@ impl<'a> Formattable for Expr<'a> {
|
|||
loc_expr.is_multiline() || args.iter().any(|loc_arg| loc_arg.is_multiline())
|
||||
}
|
||||
|
||||
Expect(condition, continuation) => {
|
||||
condition.is_multiline() || continuation.is_multiline()
|
||||
}
|
||||
DbgStmt(condition, _) => condition.is_multiline(),
|
||||
LowLevelDbg(_, _, _) => unreachable!(
|
||||
"LowLevelDbg should only exist after desugaring, not during formatting"
|
||||
|
@ -445,9 +442,6 @@ impl<'a> Formattable for Expr<'a> {
|
|||
buf.push(')');
|
||||
}
|
||||
}
|
||||
Expect(condition, continuation) => {
|
||||
fmt_expect(buf, condition, continuation, self.is_multiline(), indent);
|
||||
}
|
||||
Dbg => {
|
||||
buf.indent(indent);
|
||||
buf.push_str("dbg");
|
||||
|
@ -1071,33 +1065,6 @@ fn fmt_dbg_stmt<'a>(
|
|||
continuation.format(buf, indent);
|
||||
}
|
||||
|
||||
fn fmt_expect<'a>(
|
||||
buf: &mut Buf,
|
||||
condition: &'a Loc<Expr<'a>>,
|
||||
continuation: &'a Loc<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.ensure_ends_with_newline();
|
||||
buf.indent(indent);
|
||||
buf.push_str("expect");
|
||||
|
||||
let return_indent = if is_multiline {
|
||||
buf.newline();
|
||||
indent + INDENT
|
||||
} else {
|
||||
buf.spaces(1);
|
||||
indent
|
||||
};
|
||||
|
||||
condition.format(buf, return_indent);
|
||||
|
||||
// Always put a blank line after the `expect` line(s)
|
||||
buf.ensure_ends_with_blank_line();
|
||||
|
||||
continuation.format(buf, indent);
|
||||
}
|
||||
|
||||
fn fmt_return<'a>(
|
||||
buf: &mut Buf,
|
||||
return_value: &'a Loc<Expr<'a>>,
|
||||
|
|
|
@ -2657,7 +2657,7 @@ fn update<'a>(
|
|||
|
||||
let subs = solved_subs.into_inner();
|
||||
|
||||
if !toplevel_expects.pure.is_empty() || !toplevel_expects.fx.is_empty() {
|
||||
if !toplevel_expects.pure.is_empty() {
|
||||
state.toplevel_expects.insert(module_id, toplevel_expects);
|
||||
}
|
||||
|
||||
|
@ -6010,78 +6010,6 @@ fn build_pending_specializations<'a>(
|
|||
toplevel_expects.pure.insert(symbol, region);
|
||||
procs_base.partial_procs.insert(symbol, proc);
|
||||
}
|
||||
ExpectationFx => {
|
||||
// skip expectations if we're not going to run them
|
||||
if !build_expects {
|
||||
continue;
|
||||
}
|
||||
|
||||
// mark this symbol as a top-level thunk before any other work on the procs
|
||||
module_thunks.push(symbol);
|
||||
|
||||
let expr_var = Variable::EMPTY_RECORD;
|
||||
|
||||
let is_host_exposed = true;
|
||||
|
||||
// If this is an exposed symbol, we need to
|
||||
// register it as such. Otherwise, since it
|
||||
// never gets called by Roc code, it will never
|
||||
// get specialized!
|
||||
if is_host_exposed {
|
||||
let layout_result =
|
||||
layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs);
|
||||
|
||||
// cannot specialize when e.g. main's type contains type variables
|
||||
if let Err(e) = layout_result {
|
||||
match e {
|
||||
LayoutProblem::Erroneous => {
|
||||
let message = "top level function has erroneous type";
|
||||
procs_base.runtime_errors.insert(symbol, message);
|
||||
continue;
|
||||
}
|
||||
LayoutProblem::UnresolvedTypeVar(v) => {
|
||||
let message = format!(
|
||||
"top level function has unresolved type variable {v:?}"
|
||||
);
|
||||
procs_base
|
||||
.runtime_errors
|
||||
.insert(symbol, mono_env.arena.alloc(message));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
procs_base.host_specializations.insert_host_exposed(
|
||||
mono_env.subs,
|
||||
LambdaName::no_niche(symbol),
|
||||
None,
|
||||
expr_var,
|
||||
);
|
||||
}
|
||||
|
||||
let body = roc_can::expr::toplevel_expect_to_inline_expect_fx(body);
|
||||
|
||||
let proc = PartialProc {
|
||||
annotation: expr_var,
|
||||
// This is a 0-arity thunk, so it has no arguments.
|
||||
pattern_symbols: &[],
|
||||
// This is a top-level definition, so it cannot capture anything
|
||||
captured_symbols: CapturedSymbols::None,
|
||||
body: body.value,
|
||||
body_var: expr_var,
|
||||
// This is a 0-arity thunk, so it cannot be recursive
|
||||
is_self_recursive: false,
|
||||
};
|
||||
|
||||
// extend the region of the expect expression with the region of the preceding
|
||||
// comment, so it is shown in failure/panic messages
|
||||
let name_region = declarations.symbols[index].region;
|
||||
let expr_region = declarations.expressions[index].region;
|
||||
let region = Region::span_across(&name_region, &expr_region);
|
||||
|
||||
toplevel_expects.fx.insert(symbol, region);
|
||||
procs_base.partial_procs.insert(symbol, proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,7 +167,6 @@ pub(crate) struct LateSpecializationsModule<'a> {
|
|||
#[derive(Debug, Default)]
|
||||
pub struct ToplevelExpects {
|
||||
pub pure: VecMap<Symbol, Region>,
|
||||
pub fx: VecMap<Symbol, Region>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -359,10 +359,6 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st
|
|||
// at least at the moment this does not happen
|
||||
panic!("Unexpected expectation in module declarations");
|
||||
}
|
||||
ExpectationFx => {
|
||||
// at least at the moment this does not happen
|
||||
panic!("Unexpected expectation in module declarations");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -462,7 +458,7 @@ fn module_with_deps() {
|
|||
def_count += 1;
|
||||
}
|
||||
MutualRecursion { .. } => { /* do nothing, not a def */ }
|
||||
Expectation | ExpectationFx => { /* do nothing, not a def */ }
|
||||
Expectation => { /* do nothing, not a def */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ impl<'a> LowerParams<'a> {
|
|||
self.lower_expr(&mut decls.expressions[index].value);
|
||||
}
|
||||
|
||||
Destructure(_) | Expectation | ExpectationFx => {
|
||||
Destructure(_) | Expectation => {
|
||||
self.lower_expr(&mut decls.expressions[index].value);
|
||||
}
|
||||
MutualRecursion { .. } => {}
|
||||
|
|
|
@ -489,7 +489,6 @@ pub enum Expr<'a> {
|
|||
Defs(&'a Defs<'a>, &'a Loc<Expr<'a>>),
|
||||
|
||||
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
Expect(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
|
||||
Dbg,
|
||||
DbgStmt(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
|
@ -671,7 +670,6 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
|
|||
Expr::Tag(_) => false,
|
||||
Expr::OpaqueRef(_) => false,
|
||||
Expr::Backpassing(_, _, _) => false, // TODO: we might want to check this?
|
||||
Expr::Expect(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
|
||||
Expr::Dbg => false,
|
||||
Expr::DbgStmt(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
|
||||
Expr::LowLevelDbg(_, a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
|
||||
|
@ -932,11 +930,6 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
|
|||
expr_stack.push(&a.value);
|
||||
expr_stack.push(&b.value);
|
||||
}
|
||||
Expect(condition, cont) => {
|
||||
expr_stack.reserve(2);
|
||||
expr_stack.push(&condition.value);
|
||||
expr_stack.push(&cont.value);
|
||||
}
|
||||
DbgStmt(condition, cont) => {
|
||||
expr_stack.reserve(2);
|
||||
expr_stack.push(&condition.value);
|
||||
|
@ -2482,7 +2475,6 @@ impl<'a> Malformed for Expr<'a> {
|
|||
Closure(args, body) => args.iter().any(|arg| arg.is_malformed()) || body.is_malformed(),
|
||||
Defs(defs, body) => defs.is_malformed() || body.is_malformed(),
|
||||
Backpassing(args, call, body) => args.iter().any(|arg| arg.is_malformed()) || call.is_malformed() || body.is_malformed(),
|
||||
Expect(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
|
||||
Dbg => false,
|
||||
DbgStmt(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
|
||||
LowLevelDbg(_, condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
|
||||
|
|
|
@ -2175,7 +2175,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
|||
| Expr::Defs(_, _)
|
||||
| Expr::If { .. }
|
||||
| Expr::When(_, _)
|
||||
| Expr::Expect(_, _)
|
||||
| Expr::Dbg
|
||||
| Expr::DbgStmt(_, _)
|
||||
| Expr::LowLevelDbg(_, _, _)
|
||||
|
|
|
@ -735,10 +735,6 @@ impl<'a> Normalize<'a> for Expr<'a> {
|
|||
arena.alloc(b.normalize(arena)),
|
||||
arena.alloc(c.normalize(arena)),
|
||||
),
|
||||
Expr::Expect(a, b) => Expr::Expect(
|
||||
arena.alloc(a.normalize(arena)),
|
||||
arena.alloc(b.normalize(arena)),
|
||||
),
|
||||
Expr::Dbg => Expr::Dbg,
|
||||
Expr::DbgStmt(a, b) => Expr::DbgStmt(
|
||||
arena.alloc(a.normalize(arena)),
|
||||
|
|
|
@ -49,8 +49,8 @@ pub enum Architecture {
|
|||
impl std::fmt::Display for Architecture {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let arch_str = match self {
|
||||
Architecture::Aarch32 => "arm",
|
||||
Architecture::Aarch64 => "arm64",
|
||||
Architecture::Aarch32 => "aarch32",
|
||||
Architecture::Aarch64 => "aarch64",
|
||||
Architecture::Wasm32 => "wasm32",
|
||||
Architecture::X86_32 => "x86_32",
|
||||
Architecture::X86_64 => "x86_64",
|
||||
|
|
32
crates/glue/src/CGlue.roc
Normal file
32
crates/glue/src/CGlue.roc
Normal file
|
@ -0,0 +1,32 @@
|
|||
app [makeGlue] { pf: platform "../platform/main.roc" }
|
||||
|
||||
import pf.Types exposing [Types]
|
||||
# import pf.Shape exposing [Shape, RocFn]
|
||||
import pf.File exposing [File]
|
||||
# import pf.TypeId exposing [TypeId]
|
||||
|
||||
## generate placeholder glue for now that only works for our one C test
|
||||
makeGlue : List Types -> Result (List File) Str
|
||||
makeGlue = \_typesByArch ->
|
||||
Ok ([{ name: "roc_app.h", content: placeholderGlue }])
|
||||
|
||||
placeholderGlue =
|
||||
"""
|
||||
#ifndef ROC_APP_H
|
||||
#define ROC_APP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern void roc__mainForHost_1_exposed_generic(uint8_t *ret);
|
||||
|
||||
uint8_t roc_mainForHost()
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
roc__mainForHost_1_exposed_generic(&ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
"""
|
|
@ -1,13 +1,10 @@
|
|||
app "zig-glue"
|
||||
packages { pf: "../platform/main.roc" }
|
||||
imports [
|
||||
pf.Types.{ Types },
|
||||
pf.File.{ File },
|
||||
"../../compiler/builtins/bitcode/src/list.zig" as rocStdList : Str,
|
||||
"../../compiler/builtins/bitcode/src/str.zig" as rocStdStr : Str,
|
||||
"../../compiler/builtins/bitcode/src/utils.zig" as rocStdUtils : Str,
|
||||
]
|
||||
provides [makeGlue] to pf
|
||||
app [makeGlue] { pf: platform "../platform/main.roc" }
|
||||
|
||||
import pf.Types exposing [Types]
|
||||
import pf.File exposing [File]
|
||||
import "../../compiler/builtins/bitcode/src/list.zig" as rocStdList : Str
|
||||
import "../../compiler/builtins/bitcode/src/str.zig" as rocStdStr : Str
|
||||
import "../../compiler/builtins/bitcode/src/utils.zig" as rocStdUtils : Str
|
||||
|
||||
makeGlue : List Types -> Result (List File) Str
|
||||
makeGlue = \typesByArch ->
|
||||
|
|
13
crates/glue/tests/fixtures/.gitignore
vendored
Normal file
13
crates/glue/tests/fixtures/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
*/*/test_glue/
|
||||
*/*/target/
|
||||
*/*/src/main.rs
|
||||
*/*/app
|
||||
*/*/Cargo.lock
|
||||
*/*/Cargo.toml
|
||||
*/*/host.c
|
||||
*/*/build.rs
|
||||
*/*/dynhost
|
||||
*/*/.so
|
||||
*/*/*.o
|
||||
*/*/*.rh
|
||||
*/*/*.rm
|
3
crates/glue/tests/fixtures/c/hello-world/app.roc
vendored
Normal file
3
crates/glue/tests/fixtures/c/hello-world/app.roc
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
app [main] { pf: platform "platform.roc" }
|
||||
|
||||
main = 42
|
14
crates/glue/tests/fixtures/c/hello-world/host.c
vendored
Normal file
14
crates/glue/tests/fixtures/c/hello-world/host.c
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "test_glue/roc_app.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t mainForHost = roc_mainForHost();
|
||||
|
||||
printf("mainForHost = %i\n", mainForHost);
|
||||
|
||||
assert(mainForHost == 42);
|
||||
}
|
9
crates/glue/tests/fixtures/c/hello-world/platform.roc
vendored
Normal file
9
crates/glue/tests/fixtures/c/hello-world/platform.roc
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
platform "test-platform"
|
||||
requires {} { main : _ }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : U8
|
||||
mainForHost = main
|
|
@ -67,7 +67,7 @@ mod glue_cli_tests {
|
|||
let test_name_str = stringify!($test_name);
|
||||
|
||||
// TODO after #5924 is fixed; remove this
|
||||
let skip_on_linux_surgical_linker = ["closures", "option", "nullable_wrapped", "nullable_unwrapped", "nonnullable_unwrapped", "enumeration", "nested_record", "advanced_recursive_union"];
|
||||
let skip_on_linux_surgical_linker = ["rust_closures", "rust_option", "rust_nullable_wrapped", "rust_nullable_unwrapped", "rust_nonnullable_unwrapped", "rust_enumeration", "rust_nested_record", "rust_advanced_recursive_union"];
|
||||
|
||||
// Validate linux with the default linker.
|
||||
if !(cfg!(target_os = "linux") && (skip_on_linux_surgical_linker.contains(&test_name_str))) {
|
||||
|
@ -97,13 +97,13 @@ mod glue_cli_tests {
|
|||
}
|
||||
|
||||
fixtures! {
|
||||
basic_record:"basic-record" => "Record was: MyRcd { b: 42, a: 1995 }\n",
|
||||
nested_record:"nested-record" => "Record was: Outer { y: \"foo\", z: [1, 2], x: Inner { b: 24.0, a: 5 } }\n",
|
||||
enumeration:"enumeration" => "tag_union was: MyEnum::Foo, Bar is: MyEnum::Bar, Baz is: MyEnum::Baz\n",
|
||||
single_tag_union:"single-tag-union" => indoc!(r#"
|
||||
rust_basic_record:"rust/basic-record" => "Record was: MyRcd { b: 42, a: 1995 }\n",
|
||||
rust_nested_record:"rust/nested-record" => "Record was: Outer { y: \"foo\", z: [1, 2], x: Inner { b: 24.0, a: 5 } }\n",
|
||||
rust_enumeration:"rust/enumeration" => "tag_union was: MyEnum::Foo, Bar is: MyEnum::Bar, Baz is: MyEnum::Baz\n",
|
||||
rust_single_tag_union:"rust/single-tag-union" => indoc!(r#"
|
||||
tag_union was: SingleTagUnion::OneTag
|
||||
"#),
|
||||
union_with_padding:"union-with-padding" => indoc!(r#"
|
||||
rust_union_with_padding:"rust/union-with-padding" => indoc!(r#"
|
||||
tag_union was: NonRecursive::Foo("This is a test")
|
||||
`Foo "small str"` is: NonRecursive::Foo("small str")
|
||||
`Foo "A long enough string to not be small"` is: NonRecursive::Foo("A long enough string to not be small")
|
||||
|
@ -111,41 +111,41 @@ mod glue_cli_tests {
|
|||
`Baz` is: NonRecursive::Baz(())
|
||||
`Blah 456` is: NonRecursive::Blah(456)
|
||||
"#),
|
||||
union_without_padding:"union-without-padding" => indoc!(r#"
|
||||
rust_union_without_padding:"rust/union-without-padding" => indoc!(r#"
|
||||
tag_union was: NonRecursive::Foo("This is a test")
|
||||
`Foo "small str"` is: NonRecursive::Foo("small str")
|
||||
`Bar 123` is: NonRecursive::Bar(123)
|
||||
`Baz` is: NonRecursive::Baz(())
|
||||
`Blah 456` is: NonRecursive::Blah(456)
|
||||
"#),
|
||||
nullable_wrapped:"nullable-wrapped" => indoc!(r#"
|
||||
rust_nullable_wrapped:"rust/nullable-wrapped" => indoc!(r#"
|
||||
tag_union was: StrFingerTree::More("foo", StrFingerTree::More("bar", StrFingerTree::Empty))
|
||||
`More "small str" (Single "other str")` is: StrFingerTree::More("small str", StrFingerTree::Single("other str"))
|
||||
`More "small str" Empty` is: StrFingerTree::More("small str", StrFingerTree::Empty)
|
||||
`Single "small str"` is: StrFingerTree::Single("small str")
|
||||
`Empty` is: StrFingerTree::Empty
|
||||
"#),
|
||||
nullable_unwrapped:"nullable-unwrapped" => indoc!(r#"
|
||||
rust_nullable_unwrapped:"rust/nullable-unwrapped" => indoc!(r#"
|
||||
tag_union was: StrConsList::Cons("World!", StrConsList::Cons("Hello ", StrConsList::Nil))
|
||||
`Cons "small str" Nil` is: StrConsList::Cons("small str", StrConsList::Nil)
|
||||
`Nil` is: StrConsList::Nil
|
||||
"#),
|
||||
nonnullable_unwrapped:"nonnullable-unwrapped" => indoc!(r#"
|
||||
rust_nonnullable_unwrapped:"rust/nonnullable-unwrapped" => indoc!(r#"
|
||||
tag_union was: StrRoseTree::Tree("root", [StrRoseTree::Tree("leaf1", []), StrRoseTree::Tree("leaf2", [])])
|
||||
Tree "foo" [] is: StrRoseTree::Tree("foo", [])
|
||||
"#),
|
||||
basic_recursive_union:"basic-recursive-union" => indoc!(r#"
|
||||
rust_basic_recursive_union:"rust/basic-recursive-union" => indoc!(r#"
|
||||
tag_union was: Expr::Concat(Expr::String("Hello, "), Expr::String("World!"))
|
||||
`Concat (String "Hello, ") (String "World!")` is: Expr::Concat(Expr::String("Hello, "), Expr::String("World!"))
|
||||
`String "this is a test"` is: Expr::String("this is a test")
|
||||
"#),
|
||||
advanced_recursive_union:"advanced-recursive-union" => indoc!(r#"
|
||||
rust_advanced_recursive_union:"rust/advanced-recursive-union" => indoc!(r#"
|
||||
rbt was: Rbt { default: Job::Job(R1 { command: Command::Command(R2 { tool: Tool::SystemTool(R4 { name: "test", num: 42 }) }), inputFiles: ["foo"] }) }
|
||||
"#),
|
||||
list_recursive_union:"list-recursive-union" => indoc!(r#"
|
||||
rust_list_recursive_union:"rust/list-recursive-union" => indoc!(r#"
|
||||
rbt was: Rbt { default: Job::Job(R1 { command: Command::Command(R2 { args: [], tool: Tool::SystemTool(R3 { name: "test" }) }), inputFiles: ["foo"], job: [] }) }
|
||||
"#),
|
||||
multiple_modules:"multiple-modules" => indoc!(r#"
|
||||
rust_multiple_modules:"rust/multiple-modules" => indoc!(r#"
|
||||
combined was: Combined { s1: DepStr1::S("hello"), s2: DepStr2::R("world") }
|
||||
"#),
|
||||
// issue https://github.com/roc-lang/roc/issues/6121
|
||||
|
@ -155,17 +155,20 @@ mod glue_cli_tests {
|
|||
// arguments:"arguments" => indoc!(r#"
|
||||
// Answer was: 84
|
||||
// "#),
|
||||
closures:"closures" => indoc!(r#"
|
||||
rust_closures:"rust/closures" => indoc!(r#"
|
||||
Answer was: 672
|
||||
"#),
|
||||
rocresult:"rocresult" => indoc!(r#"
|
||||
rust_rocresult:"rust/rocresult" => indoc!(r#"
|
||||
Answer was: RocOk(ManuallyDrop { value: "Hello World!" })
|
||||
Answer was: RocErr(ManuallyDrop { value: 42 })
|
||||
"#),
|
||||
option:"option" => indoc!(r#"
|
||||
rust_option:"rust/option" => indoc!(r#"
|
||||
Answer was: "Hello World!"
|
||||
Answer was: discriminant_U1::None
|
||||
"#),
|
||||
c_hello_world:"c/hello-world" => indoc!(r#"
|
||||
mainForHost = 42
|
||||
"#),
|
||||
}
|
||||
|
||||
fn check_for_tests(all_fixtures: &mut roc_collections::VecSet<String>) {
|
||||
|
@ -204,32 +207,46 @@ mod glue_cli_tests {
|
|||
) -> CmdOut {
|
||||
let platform_module_path = platform_dir.join("platform.roc");
|
||||
let glue_dir = platform_dir.join("test_glue");
|
||||
let fixture_templates_dir = platform_dir
|
||||
let tests_dir = platform_dir
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("fixture-templates");
|
||||
|
||||
// Copy the rust template from the templates directory into the fixture dir.
|
||||
dircpy::CopyBuilder::new(fixture_templates_dir.join("rust"), platform_dir)
|
||||
.overwrite(true) // overwrite any files that were already present
|
||||
.run()
|
||||
.parent()
|
||||
.unwrap();
|
||||
|
||||
// Delete the glue file to make sure we're actually regenerating it!
|
||||
let fixture_templates_dir = tests_dir.join("fixture-templates");
|
||||
|
||||
let fixtures_subfolder_name = platform_dir.parent().unwrap().file_name().unwrap();
|
||||
|
||||
let fixture_template_dir = fixture_templates_dir.join(fixtures_subfolder_name);
|
||||
|
||||
if fixture_template_dir.exists() {
|
||||
// Copy the template from the templates directory into the fixture dir if it exists
|
||||
dircpy::CopyBuilder::new(fixture_template_dir, platform_dir)
|
||||
.overwrite(true) // overwrite any files that were already present
|
||||
.run()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Delete the glue files to make sure we're actually regenerating it!
|
||||
if glue_dir.exists() {
|
||||
std::fs::remove_dir_all(&glue_dir)
|
||||
.expect("Unable to remove test_glue dir in order to regenerate it in the test");
|
||||
}
|
||||
|
||||
let rust_glue_spec = fixture_templates_dir
|
||||
.parent()
|
||||
.unwrap()
|
||||
let glue_spec_filename = match fixtures_subfolder_name.to_str().unwrap() {
|
||||
"rust" => "RustGlue.roc",
|
||||
"zig" => "ZigGlue.roc",
|
||||
"c" => "CGlue.roc",
|
||||
unknown_subfolder => panic!("I don't know which glue file to use for tests in the `{}` subfolder! Please add one here!", unknown_subfolder),
|
||||
};
|
||||
|
||||
let rust_glue_spec = tests_dir
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("src")
|
||||
.join("RustGlue.roc");
|
||||
.join(glue_spec_filename);
|
||||
|
||||
// Generate a fresh test_glue for this platform
|
||||
let all_args : Vec<_> =
|
||||
|
|
|
@ -686,9 +686,6 @@ impl IterTokens for Loc<Expr<'_>> {
|
|||
.chain(e1.iter_tokens(arena))
|
||||
.chain(e2.iter_tokens(arena))
|
||||
.collect_in(arena),
|
||||
Expr::Expect(e1, e2) => (e1.iter_tokens(arena).into_iter())
|
||||
.chain(e2.iter_tokens(arena))
|
||||
.collect_in(arena),
|
||||
Expr::Dbg => onetoken(Token::Keyword, region, arena),
|
||||
Expr::DbgStmt(e1, e2) => (e1.iter_tokens(arena).into_iter())
|
||||
.chain(e2.iter_tokens(arena))
|
||||
|
|
|
@ -30,7 +30,6 @@ bumpalo.workspace = true
|
|||
inkwell.workspace = true
|
||||
libc.workspace = true
|
||||
libloading.workspace = true
|
||||
signal-hook.workspace = true
|
||||
target-lexicon.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::{
|
||||
os::unix::process::parent_id,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU32},
|
||||
Arc,
|
||||
},
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, AtomicU32},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
|
@ -32,7 +29,6 @@ use roc_types::subs::Subs;
|
|||
pub struct ExpectMemory<'a> {
|
||||
ptr: *mut u8,
|
||||
length: usize,
|
||||
shm_name: Option<std::ffi::CString>,
|
||||
_marker: std::marker::PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
|
@ -44,7 +40,6 @@ impl<'a> ExpectMemory<'a> {
|
|||
Self {
|
||||
ptr: slice.as_mut_ptr(),
|
||||
length: slice.len(),
|
||||
shm_name: None,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +49,6 @@ impl<'a> ExpectMemory<'a> {
|
|||
Self::mmap_help(cstring, libc::O_RDWR | libc::O_CREAT)
|
||||
}
|
||||
|
||||
fn reuse_mmap(&mut self) -> Option<Self> {
|
||||
let shm_name = self.shm_name.as_ref()?.clone();
|
||||
Some(Self::mmap_help(shm_name, libc::O_RDWR))
|
||||
}
|
||||
|
||||
fn mmap_help(cstring: std::ffi::CString, shm_flags: i32) -> Self {
|
||||
let ptr = unsafe {
|
||||
let shared_fd = libc::shm_open(cstring.as_ptr().cast(), shm_flags, 0o666);
|
||||
|
@ -102,7 +92,6 @@ impl<'a> ExpectMemory<'a> {
|
|||
Self {
|
||||
ptr: ptr.cast(),
|
||||
length: Self::SHM_SIZE,
|
||||
shm_name: Some(cstring),
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -124,33 +113,6 @@ impl<'a> ExpectMemory<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn run_inline_expects<'a, W: std::io::Write>(
|
||||
writer: &mut W,
|
||||
render_target: RenderTarget,
|
||||
arena: &'a Bump,
|
||||
interns: &'a Interns,
|
||||
layout_interner: &GlobalLayoutInterner<'a>,
|
||||
lib: &libloading::Library,
|
||||
expectations: &mut VecMap<ModuleId, Expectations>,
|
||||
expects: ExpectFunctions<'_>,
|
||||
) -> std::io::Result<(usize, usize)> {
|
||||
let shm_name = format!("/roc_expect_buffer_{}", std::process::id());
|
||||
let mut memory = ExpectMemory::create_or_reuse_mmap(&shm_name);
|
||||
|
||||
run_expects_with_memory(
|
||||
writer,
|
||||
render_target,
|
||||
arena,
|
||||
interns,
|
||||
layout_interner,
|
||||
lib,
|
||||
expectations,
|
||||
expects,
|
||||
&mut memory,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn run_toplevel_expects<'a, W: std::io::Write>(
|
||||
writer: &mut W,
|
||||
|
@ -193,25 +155,6 @@ pub(crate) fn run_expects_with_memory<'a, W: std::io::Write>(
|
|||
let mut failed = 0;
|
||||
let mut passed = 0;
|
||||
|
||||
for expect in expects.fx {
|
||||
let result = run_expect_fx(
|
||||
writer,
|
||||
render_target,
|
||||
arena,
|
||||
interns,
|
||||
layout_interner,
|
||||
lib,
|
||||
expectations,
|
||||
memory,
|
||||
expect,
|
||||
)?;
|
||||
|
||||
match result {
|
||||
true => passed += 1,
|
||||
false => failed += 1,
|
||||
}
|
||||
}
|
||||
|
||||
memory.set_shared_buffer(lib);
|
||||
|
||||
for expect in expects.pure {
|
||||
|
@ -294,107 +237,6 @@ fn run_expect_pure<'a, W: std::io::Write>(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_expect_fx<'a, W: std::io::Write>(
|
||||
writer: &mut W,
|
||||
render_target: RenderTarget,
|
||||
arena: &'a Bump,
|
||||
interns: &'a Interns,
|
||||
layout_interner: &GlobalLayoutInterner<'a>,
|
||||
lib: &libloading::Library,
|
||||
expectations: &mut VecMap<ModuleId, Expectations>,
|
||||
parent_memory: &mut ExpectMemory,
|
||||
expect: ToplevelExpect<'_>,
|
||||
) -> std::io::Result<bool> {
|
||||
use signal_hook::{consts::signal::SIGCHLD, consts::signal::SIGUSR1, iterator::Signals};
|
||||
|
||||
let mut signals = Signals::new([SIGCHLD, SIGUSR1]).unwrap();
|
||||
|
||||
match unsafe { libc::fork() } {
|
||||
0 => unsafe {
|
||||
// we are the child
|
||||
|
||||
use roc_gen_llvm::try_run_jit_function;
|
||||
|
||||
let mut child_memory = parent_memory.reuse_mmap().unwrap();
|
||||
|
||||
let sequence = ExpectSequence::new(child_memory.ptr);
|
||||
|
||||
child_memory.set_shared_buffer(lib);
|
||||
|
||||
let result: Result<(), (String, _)> =
|
||||
try_run_jit_function!(lib, expect.name, (), |v: ()| v);
|
||||
|
||||
if let Err((msg, _)) = result {
|
||||
internal_error!("roc panic {msg}");
|
||||
}
|
||||
|
||||
if sequence.count_failures() > 0 {
|
||||
libc::kill(parent_id() as _, SIGUSR1);
|
||||
}
|
||||
|
||||
std::process::exit(0)
|
||||
},
|
||||
-1 => {
|
||||
// something failed
|
||||
|
||||
// Display a human-friendly error message
|
||||
println!("Error {:?}", std::io::Error::last_os_error());
|
||||
|
||||
std::process::exit(1)
|
||||
}
|
||||
1.. => {
|
||||
let mut has_succeeded = true;
|
||||
|
||||
for sig in &mut signals {
|
||||
match sig {
|
||||
SIGCHLD => {
|
||||
// done!
|
||||
return Ok(has_succeeded);
|
||||
}
|
||||
SIGUSR1 => {
|
||||
// this is the signal we use for an expect failure. Let's see what the child told us
|
||||
has_succeeded = false;
|
||||
|
||||
let frame =
|
||||
ExpectFrame::at_offset(parent_memory.ptr, ExpectSequence::START_OFFSET);
|
||||
let module_id = frame.module_id;
|
||||
|
||||
let data = expectations.get_mut(&module_id).unwrap();
|
||||
let filename = data.path.to_owned();
|
||||
let source = std::fs::read_to_string(&data.path).unwrap();
|
||||
|
||||
let renderer = Renderer::new(
|
||||
arena,
|
||||
interns,
|
||||
render_target,
|
||||
module_id,
|
||||
filename,
|
||||
&source,
|
||||
);
|
||||
|
||||
render_expect_failure(
|
||||
writer,
|
||||
&renderer,
|
||||
arena,
|
||||
None,
|
||||
expectations,
|
||||
interns,
|
||||
layout_interner,
|
||||
parent_memory.ptr,
|
||||
ExpectSequence::START_OFFSET,
|
||||
)?;
|
||||
}
|
||||
_ => println!("received signal {sig}"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_expects_in_memory<'a>(
|
||||
writer: &mut impl std::io::Write,
|
||||
arena: &'a Bump,
|
||||
|
@ -607,7 +449,6 @@ pub struct ToplevelExpect<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct ExpectFunctions<'a> {
|
||||
pub pure: BumpVec<'a, ToplevelExpect<'a>>,
|
||||
pub fx: BumpVec<'a, ToplevelExpect<'a>>,
|
||||
}
|
||||
|
||||
pub fn expect_mono_module_to_dylib<'a>(
|
||||
|
@ -668,14 +509,7 @@ pub fn expect_mono_module_to_dylib<'a>(
|
|||
.map(|(module_id, expects)| {
|
||||
(
|
||||
*module_id,
|
||||
bumpalo::collections::Vec::from_iter_in(
|
||||
expects
|
||||
.pure
|
||||
.keys()
|
||||
.copied()
|
||||
.chain(expects.fx.keys().copied()),
|
||||
env.arena,
|
||||
),
|
||||
bumpalo::collections::Vec::from_iter_in(expects.pure.keys().copied(), env.arena),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
@ -693,19 +527,6 @@ pub fn expect_mono_module_to_dylib<'a>(
|
|||
for (module_id, expects) in toplevel_expects.into_iter() {
|
||||
let expect_names = expect_names.get(&module_id).unwrap();
|
||||
|
||||
let expects_fx = bumpalo::collections::Vec::from_iter_in(
|
||||
expects
|
||||
.fx
|
||||
.into_iter()
|
||||
.zip(expect_names.iter().skip(expects.pure.len()))
|
||||
.map(|((symbol, region), name)| ToplevelExpect {
|
||||
symbol,
|
||||
region,
|
||||
name,
|
||||
}),
|
||||
env.arena,
|
||||
);
|
||||
|
||||
let expects_pure =
|
||||
bumpalo::collections::Vec::from_iter_in(
|
||||
expects.pure.into_iter().zip(expect_names.iter()).map(
|
||||
|
@ -718,10 +539,7 @@ pub fn expect_mono_module_to_dylib<'a>(
|
|||
env.arena,
|
||||
);
|
||||
|
||||
let expect_funs = ExpectFunctions {
|
||||
pure: expects_pure,
|
||||
fx: expects_fx,
|
||||
};
|
||||
let expect_funs = ExpectFunctions { pure: expects_pure };
|
||||
|
||||
modules_expects.insert(module_id, expect_funs);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
app [main] { pf: platform "c-platform/main.roc" }
|
||||
|
||||
# run with `roc --build-host --suppress-build-host-warning rocLovesZig.roc`
|
||||
|
||||
main = "Roc <3 C!\n"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
app [main] { pf: platform "zig-platform/main.roc" }
|
||||
|
||||
# run with `roc --build-host --suppress-build-host-warning rocLovesZig.roc`
|
||||
|
||||
main = "Roc <3 Zig!\n"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue