boilerplate

This commit is contained in:
Folkert 2022-08-12 13:34:36 +02:00
parent 07eed2c4a6
commit 10b6f33ed2
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
9 changed files with 295 additions and 19 deletions

View file

@ -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, _) => {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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