mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
boilerplate
This commit is contained in:
parent
07eed2c4a6
commit
10b6f33ed2
9 changed files with 295 additions and 19 deletions
|
@ -463,6 +463,7 @@ fn stmt_spec<'a>(
|
|||
builder.add_choice(block, &cases)
|
||||
}
|
||||
Expect { remainder, .. } => stmt_spec(builder, env, block, layout, remainder),
|
||||
ExpectFx { remainder, .. } => stmt_spec(builder, env, block, layout, remainder),
|
||||
Ret(symbol) => Ok(env.symbols[symbol]),
|
||||
Refcounting(modify_rc, continuation) => match modify_rc {
|
||||
ModifyRc::Inc(symbol, _) => {
|
||||
|
|
|
@ -1004,7 +1004,8 @@ trait Backend<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"),
|
||||
Stmt::Expect { .. } => todo!("expect is not implemented in the dev backend"),
|
||||
Stmt::ExpectFx { .. } => todo!("expect-fx is not implemented in the dev backend"),
|
||||
|
||||
Stmt::RuntimeError(_) => {}
|
||||
}
|
||||
|
|
|
@ -2836,6 +2836,78 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
condition: cond_symbol,
|
||||
region,
|
||||
lookups,
|
||||
layouts: _,
|
||||
remainder,
|
||||
} => {
|
||||
let bd = env.builder;
|
||||
let context = env.context;
|
||||
|
||||
let (cond, _cond_layout) = load_symbol_and_layout(scope, cond_symbol);
|
||||
|
||||
let condition = bd.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
cond.into_int_value(),
|
||||
context.bool_type().const_int(1, false),
|
||||
"is_true",
|
||||
);
|
||||
|
||||
let then_block = context.append_basic_block(parent, "then_block");
|
||||
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||
|
||||
bd.build_conditional_branch(condition, then_block, throw_block);
|
||||
|
||||
if env.mode.runs_expects() {
|
||||
bd.position_at_end(throw_block);
|
||||
|
||||
match env.target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes8 => {
|
||||
clone_to_shared_memory(
|
||||
env,
|
||||
scope,
|
||||
layout_ids,
|
||||
*cond_symbol,
|
||||
*region,
|
||||
lookups,
|
||||
);
|
||||
|
||||
// NOTE: signals to the parent process that an expect failed
|
||||
if env.mode.runs_expects_in_separate_process() {
|
||||
let func = env
|
||||
.module
|
||||
.get_function(bitcode::UTILS_EXPECT_FAILED_FINALIZE)
|
||||
.unwrap();
|
||||
|
||||
bd.build_call(func, &[], "call_expect_finalize_failed");
|
||||
}
|
||||
|
||||
bd.build_unconditional_branch(then_block);
|
||||
}
|
||||
roc_target::PtrWidth::Bytes4 => {
|
||||
// temporary WASM implementation
|
||||
throw_exception(env, "An expectation failed!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bd.position_at_end(throw_block);
|
||||
bd.build_unconditional_branch(then_block);
|
||||
}
|
||||
|
||||
bd.position_at_end(then_block);
|
||||
|
||||
build_exp_stmt(
|
||||
env,
|
||||
layout_ids,
|
||||
func_spec_solutions,
|
||||
scope,
|
||||
parent,
|
||||
remainder,
|
||||
)
|
||||
}
|
||||
|
||||
RuntimeError(error_msg) => {
|
||||
throw_exception(env, error_msg);
|
||||
|
||||
|
|
|
@ -692,6 +692,7 @@ impl<'a> WasmBackend<'a> {
|
|||
Stmt::Refcounting(modify, following) => self.stmt_refcounting(modify, following),
|
||||
|
||||
Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"),
|
||||
Stmt::ExpectFx { .. } => todo!("expect-fx is not implemented in the wasm backend"),
|
||||
|
||||
Stmt::RuntimeError(msg) => self.stmt_runtime_error(msg),
|
||||
}
|
||||
|
|
|
@ -309,6 +309,7 @@ impl<'a> ParamMap<'a> {
|
|||
}
|
||||
|
||||
Expect { remainder, .. } => stack.push(remainder),
|
||||
ExpectFx { remainder, .. } => stack.push(remainder),
|
||||
|
||||
Switch {
|
||||
branches,
|
||||
|
@ -820,6 +821,10 @@ impl<'a> BorrowInfState<'a> {
|
|||
self.collect_stmt(param_map, remainder);
|
||||
}
|
||||
|
||||
ExpectFx { remainder, .. } => {
|
||||
self.collect_stmt(param_map, remainder);
|
||||
}
|
||||
|
||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||
|
||||
Ret(_) | RuntimeError(_) => {
|
||||
|
@ -988,6 +993,7 @@ fn call_info_stmt<'a>(arena: &'a Bump, stmt: &Stmt<'a>, info: &mut CallInfo<'a>)
|
|||
}
|
||||
|
||||
Expect { remainder, .. } => stack.push(remainder),
|
||||
ExpectFx { remainder, .. } => stack.push(remainder),
|
||||
|
||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||
|
||||
|
|
|
@ -119,6 +119,17 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
|||
stack.push(remainder);
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
remainder,
|
||||
lookups,
|
||||
..
|
||||
} => {
|
||||
result.insert(*condition);
|
||||
result.extend(lookups.iter().copied());
|
||||
stack.push(remainder);
|
||||
}
|
||||
|
||||
Jump(_, arguments) => {
|
||||
result.extend(arguments.iter().copied());
|
||||
}
|
||||
|
@ -671,8 +682,8 @@ impl<'a> Context<'a> {
|
|||
|
||||
match ownership {
|
||||
DataBorrowedFunctionOwns => {
|
||||
// the data is borrowed;
|
||||
// increment it to own the values so the function can use them
|
||||
// the data is borrowed; increment it to own the values so the function
|
||||
// can use them
|
||||
let rc = Stmt::Refcounting(ModifyRc::Inc(argument, 1), stmt);
|
||||
|
||||
stmt = self.arena.alloc(rc);
|
||||
|
@ -690,8 +701,8 @@ impl<'a> Context<'a> {
|
|||
}};
|
||||
}
|
||||
|
||||
// incrementing/consuming the closure (if needed) is done by the zig implementation.
|
||||
// We don't want to touch the RC on the roc side, so treat these as borrowed.
|
||||
// incrementing/consuming the closure (if needed) is done by the zig implementation. We
|
||||
// don't want to touch the RC on the roc side, so treat these as borrowed.
|
||||
const FUNCTION: bool = BORROWED;
|
||||
const CLOSURE_DATA: bool = BORROWED;
|
||||
|
||||
|
@ -753,9 +764,9 @@ impl<'a> Context<'a> {
|
|||
handle_ownerships_pre!(Stmt::Let(z, v, l, b), ownerships)
|
||||
}
|
||||
ListSortWith { xs } => {
|
||||
// NOTE: we may apply the function to the same argument multiple times.
|
||||
// for that to be valid, the function must borrow its argument. This is not
|
||||
// enforced at the moment
|
||||
// NOTE: we may apply the function to the same argument multiple times. for that to
|
||||
// be valid, the function must borrow its argument. This is not enforced at the
|
||||
// moment
|
||||
//
|
||||
// we also don't check that both arguments have the same ownership characteristics
|
||||
let ownerships = [(xs, function_ps[0])];
|
||||
|
@ -768,7 +779,8 @@ impl<'a> Context<'a> {
|
|||
|
||||
match ownership {
|
||||
DataOwnedFunctionOwns => {
|
||||
// if non-unique, elements have been consumed, must still consume the list itself
|
||||
// if non-unique, elements have been consumed, must still consume the
|
||||
// list itself
|
||||
let rc = Stmt::Refcounting(ModifyRc::DecRef(xs), b);
|
||||
|
||||
let condition_stmt = branch_on_list_uniqueness(
|
||||
|
@ -914,8 +926,7 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
|
||||
EmptyArray | Literal(_) | Reset { .. } | RuntimeErrorFunction(_) => {
|
||||
// EmptyArray is always stack-allocated
|
||||
// function pointers are persistent
|
||||
// EmptyArray is always stack-allocated function pointers are persistent
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
};
|
||||
|
@ -924,8 +935,7 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
|
||||
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
|
||||
// is this value a constant?
|
||||
// TODO do function pointers also fall into this category?
|
||||
// is this value a constant? TODO do function pointers also fall into this category?
|
||||
let persistent = false;
|
||||
|
||||
// must this value be consumed?
|
||||
|
@ -979,9 +989,7 @@ impl<'a> Context<'a> {
|
|||
|
||||
// Add `dec` instructions for parameters that are
|
||||
//
|
||||
// - references
|
||||
// - not alive in `b`
|
||||
// - not borrow.
|
||||
// - references - not alive in `b` - not borrow.
|
||||
//
|
||||
// That is, we must make sure these parameters are consumed.
|
||||
fn add_dec_for_dead_params(
|
||||
|
@ -1021,9 +1029,9 @@ impl<'a> Context<'a> {
|
|||
) -> (&'a Stmt<'a>, LiveVarSet) {
|
||||
use Stmt::*;
|
||||
|
||||
// let-chains can be very long, especially for large (list) literals
|
||||
// in (rust) debug mode, this function can overflow the stack for such values
|
||||
// so we have to write an explicit loop.
|
||||
// let-chains can be very long, especially for large (list) literals in (rust) debug mode,
|
||||
// this function can overflow the stack for such values so we have to write an explicit
|
||||
// loop.
|
||||
{
|
||||
let mut cont = stmt;
|
||||
let mut triples = Vec::new_in(self.arena);
|
||||
|
@ -1199,6 +1207,30 @@ impl<'a> Context<'a> {
|
|||
(expect, b_live_vars)
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
remainder,
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
} => {
|
||||
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
||||
|
||||
let expect = self.arena.alloc(Stmt::ExpectFx {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder: b,
|
||||
});
|
||||
|
||||
let expect = self.add_inc_before_consume_all(lookups, expect, &b_live_vars);
|
||||
|
||||
b_live_vars.extend(lookups.iter().copied());
|
||||
|
||||
(expect, b_live_vars)
|
||||
}
|
||||
|
||||
RuntimeError(_) | Refcounting(_, _) => (stmt, MutSet::default()),
|
||||
}
|
||||
}
|
||||
|
@ -1313,6 +1345,17 @@ pub fn collect_stmt(
|
|||
collect_stmt(remainder, jp_live_vars, vars)
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
remainder,
|
||||
lookups,
|
||||
..
|
||||
} => {
|
||||
vars.insert(*condition);
|
||||
vars.extend(lookups.iter().copied());
|
||||
collect_stmt(remainder, jp_live_vars, vars)
|
||||
}
|
||||
|
||||
Join {
|
||||
id: j,
|
||||
parameters,
|
||||
|
|
|
@ -1430,6 +1430,14 @@ pub enum Stmt<'a> {
|
|||
/// what happens after the expect
|
||||
remainder: &'a Stmt<'a>,
|
||||
},
|
||||
ExpectFx {
|
||||
condition: Symbol,
|
||||
region: Region,
|
||||
lookups: &'a [Symbol],
|
||||
layouts: &'a [Layout<'a>],
|
||||
/// what happens after the expect
|
||||
remainder: &'a Stmt<'a>,
|
||||
},
|
||||
/// a join point `join f <params> = <continuation> in remainder`
|
||||
Join {
|
||||
id: JoinPointId,
|
||||
|
@ -2017,6 +2025,17 @@ impl<'a> Stmt<'a> {
|
|||
.append(alloc.hardline())
|
||||
.append(remainder.to_doc(alloc)),
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
remainder,
|
||||
..
|
||||
} => alloc
|
||||
.text("expect-fx ")
|
||||
.append(symbol_to_doc(alloc, *condition))
|
||||
.append(";")
|
||||
.append(alloc.hardline())
|
||||
.append(remainder.to_doc(alloc)),
|
||||
|
||||
Ret(symbol) => alloc
|
||||
.text("ret ")
|
||||
.append(symbol_to_doc(alloc, *symbol))
|
||||
|
@ -6382,6 +6401,32 @@ fn substitute_in_stmt_help<'a>(
|
|||
Some(arena.alloc(expect))
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder,
|
||||
} => {
|
||||
let new_remainder =
|
||||
substitute_in_stmt_help(arena, remainder, subs).unwrap_or(remainder);
|
||||
|
||||
let new_lookups = Vec::from_iter_in(
|
||||
lookups.iter().map(|s| substitute(subs, *s).unwrap_or(*s)),
|
||||
arena,
|
||||
);
|
||||
|
||||
let expect = ExpectFx {
|
||||
condition: substitute(subs, *condition).unwrap_or(*condition),
|
||||
region: *region,
|
||||
lookups: new_lookups.into_bump_slice(),
|
||||
layouts,
|
||||
remainder: new_remainder,
|
||||
};
|
||||
|
||||
Some(arena.alloc(expect))
|
||||
}
|
||||
|
||||
Jump(id, args) => {
|
||||
let mut did_change = false;
|
||||
let new_args = Vec::from_iter_in(
|
||||
|
|
|
@ -216,6 +216,31 @@ fn function_s<'a, 'i>(
|
|||
}
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder,
|
||||
} => {
|
||||
let continuation: &Stmt = *remainder;
|
||||
let new_continuation = function_s(env, w, c, continuation);
|
||||
|
||||
if std::ptr::eq(continuation, new_continuation) || continuation == new_continuation {
|
||||
stmt
|
||||
} else {
|
||||
let new_refcounting = ExpectFx {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder: new_continuation,
|
||||
};
|
||||
|
||||
arena.alloc(new_refcounting)
|
||||
}
|
||||
}
|
||||
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||
}
|
||||
}
|
||||
|
@ -446,6 +471,39 @@ fn function_d_main<'a, 'i>(
|
|||
(arena.alloc(refcounting), found)
|
||||
}
|
||||
}
|
||||
ExpectFx {
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder,
|
||||
} => {
|
||||
let (b, found) = function_d_main(env, x, c, remainder);
|
||||
|
||||
if found || *condition != x {
|
||||
let refcounting = ExpectFx {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
(arena.alloc(refcounting), found)
|
||||
} else {
|
||||
let b = try_function_s(env, x, c, b);
|
||||
|
||||
let refcounting = ExpectFx {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
(arena.alloc(refcounting), found)
|
||||
}
|
||||
}
|
||||
Join {
|
||||
id,
|
||||
parameters,
|
||||
|
@ -618,6 +676,26 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
|||
arena.alloc(expect)
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder,
|
||||
} => {
|
||||
let b = function_r(env, remainder);
|
||||
|
||||
let expect = ExpectFx {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
arena.alloc(expect)
|
||||
}
|
||||
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
// terminals
|
||||
stmt
|
||||
|
@ -653,6 +731,11 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
|||
remainder,
|
||||
..
|
||||
} => *condition == needle || has_live_var(jp_live_vars, remainder, needle),
|
||||
ExpectFx {
|
||||
condition,
|
||||
remainder,
|
||||
..
|
||||
} => *condition == needle || has_live_var(jp_live_vars, remainder, needle),
|
||||
Join {
|
||||
id,
|
||||
parameters,
|
||||
|
|
|
@ -273,6 +273,30 @@ fn insert_jumps<'a>(
|
|||
None => None,
|
||||
},
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder,
|
||||
} => match insert_jumps(
|
||||
arena,
|
||||
remainder,
|
||||
goal_id,
|
||||
needle,
|
||||
needle_arguments,
|
||||
needle_result,
|
||||
) {
|
||||
Some(cont) => Some(arena.alloc(ExpectFx {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
remainder: cont,
|
||||
})),
|
||||
None => None,
|
||||
},
|
||||
|
||||
Ret(_) => None,
|
||||
Jump(_, _) => None,
|
||||
RuntimeError(_) => None,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue