mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
add Expect ir::Stmt node
This commit is contained in:
parent
70df1ff9c7
commit
b29c7d6fb2
10 changed files with 239 additions and 17 deletions
|
@ -439,6 +439,7 @@ fn stmt_spec<'a>(
|
||||||
|
|
||||||
builder.add_choice(block, &cases)
|
builder.add_choice(block, &cases)
|
||||||
}
|
}
|
||||||
|
Expect { remainder, .. } => stmt_spec(builder, env, block, layout, remainder),
|
||||||
Ret(symbol) => Ok(env.symbols[symbol]),
|
Ret(symbol) => Ok(env.symbols[symbol]),
|
||||||
Refcounting(modify_rc, continuation) => match modify_rc {
|
Refcounting(modify_rc, continuation) => match modify_rc {
|
||||||
ModifyRc::Inc(symbol, _) => {
|
ModifyRc::Inc(symbol, _) => {
|
||||||
|
|
|
@ -979,6 +979,9 @@ trait Backend<'a> {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"),
|
||||||
|
|
||||||
Stmt::RuntimeError(_) => {}
|
Stmt::RuntimeError(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2737,6 +2737,80 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition: cond,
|
||||||
|
lookups: _,
|
||||||
|
layouts: _,
|
||||||
|
remainder,
|
||||||
|
} => {
|
||||||
|
// do stuff
|
||||||
|
|
||||||
|
let bd = env.builder;
|
||||||
|
let context = env.context;
|
||||||
|
|
||||||
|
let (cond, _cond_layout) = load_symbol_and_layout(scope, cond);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
{
|
||||||
|
bd.position_at_end(throw_block);
|
||||||
|
|
||||||
|
match env.target_info.ptr_width() {
|
||||||
|
roc_target::PtrWidth::Bytes8 => {
|
||||||
|
let func = env
|
||||||
|
.module
|
||||||
|
.get_function(bitcode::UTILS_EXPECT_FAILED)
|
||||||
|
.unwrap();
|
||||||
|
// TODO get the actual line info instead of
|
||||||
|
// hardcoding as zero!
|
||||||
|
let callable = CallableValue::try_from(func).unwrap();
|
||||||
|
let start_line = context.i32_type().const_int(0, false);
|
||||||
|
let end_line = context.i32_type().const_int(0, false);
|
||||||
|
let start_col = context.i16_type().const_int(0, false);
|
||||||
|
let end_col = context.i16_type().const_int(0, false);
|
||||||
|
|
||||||
|
bd.build_call(
|
||||||
|
callable,
|
||||||
|
&[
|
||||||
|
start_line.into(),
|
||||||
|
end_line.into(),
|
||||||
|
start_col.into(),
|
||||||
|
end_col.into(),
|
||||||
|
],
|
||||||
|
"call_expect_failed",
|
||||||
|
);
|
||||||
|
|
||||||
|
bd.build_unconditional_branch(then_block);
|
||||||
|
}
|
||||||
|
roc_target::PtrWidth::Bytes4 => {
|
||||||
|
// temporary WASM implementation
|
||||||
|
throw_exception(env, "An expectation failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bd.position_at_end(then_block);
|
||||||
|
|
||||||
|
build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
remainder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
RuntimeError(error_msg) => {
|
RuntimeError(error_msg) => {
|
||||||
throw_exception(env, error_msg);
|
throw_exception(env, error_msg);
|
||||||
|
|
||||||
|
@ -6128,8 +6202,6 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
set
|
set
|
||||||
}
|
}
|
||||||
ExpectTrue => {
|
ExpectTrue => {
|
||||||
debug_assert_eq!(args.len(), 1);
|
|
||||||
|
|
||||||
let context = env.context;
|
let context = env.context;
|
||||||
let bd = env.builder;
|
let bd = env.builder;
|
||||||
|
|
||||||
|
|
|
@ -457,6 +457,8 @@ impl<'a> WasmBackend<'a> {
|
||||||
|
|
||||||
Stmt::Refcounting(modify, following) => self.stmt_refcounting(modify, following),
|
Stmt::Refcounting(modify, following) => self.stmt_refcounting(modify, following),
|
||||||
|
|
||||||
|
Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"),
|
||||||
|
|
||||||
Stmt::RuntimeError(msg) => self.stmt_runtime_error(msg),
|
Stmt::RuntimeError(msg) => self.stmt_runtime_error(msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,6 +307,9 @@ impl<'a> ParamMap<'a> {
|
||||||
Let(_, _, _, cont) => {
|
Let(_, _, _, cont) => {
|
||||||
stack.push(cont);
|
stack.push(cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect { remainder, .. } => stack.push(remainder),
|
||||||
|
|
||||||
Switch {
|
Switch {
|
||||||
branches,
|
branches,
|
||||||
default_branch,
|
default_branch,
|
||||||
|
@ -835,6 +838,11 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
self.collect_stmt(param_map, default_branch.1);
|
self.collect_stmt(param_map, default_branch.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect { remainder, .. } => {
|
||||||
|
self.collect_stmt(param_map, remainder);
|
||||||
|
}
|
||||||
|
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | RuntimeError(_) => {
|
Ret(_) | RuntimeError(_) => {
|
||||||
|
@ -1027,6 +1035,9 @@ fn call_info_stmt<'a>(arena: &'a Bump, stmt: &Stmt<'a>, info: &mut CallInfo<'a>)
|
||||||
stack.extend(branches.iter().map(|b| &b.2));
|
stack.extend(branches.iter().map(|b| &b.2));
|
||||||
stack.push(default_branch.1);
|
stack.push(default_branch.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect { remainder, .. } => stack.push(remainder),
|
||||||
|
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||||
|
|
|
@ -108,6 +108,15 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
||||||
stack.push(cont);
|
stack.push(cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition,
|
||||||
|
remainder,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
result.insert(*condition);
|
||||||
|
stack.push(remainder);
|
||||||
|
}
|
||||||
|
|
||||||
Jump(_, arguments) => {
|
Jump(_, arguments) => {
|
||||||
result.extend(arguments.iter().copied());
|
result.extend(arguments.iter().copied());
|
||||||
}
|
}
|
||||||
|
@ -1196,6 +1205,8 @@ impl<'a> Context<'a> {
|
||||||
(switch, case_live_vars)
|
(switch, case_live_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect { remainder, .. } => self.visit_stmt(codegen, remainder),
|
||||||
|
|
||||||
RuntimeError(_) | Refcounting(_, _) => (stmt, MutSet::default()),
|
RuntimeError(_) | Refcounting(_, _) => (stmt, MutSet::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1299,6 +1310,15 @@ pub fn collect_stmt(
|
||||||
collect_stmt(cont, jp_live_vars, vars)
|
collect_stmt(cont, jp_live_vars, vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition,
|
||||||
|
remainder,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
vars.insert(*condition);
|
||||||
|
collect_stmt(remainder, jp_live_vars, vars)
|
||||||
|
}
|
||||||
|
|
||||||
Join {
|
Join {
|
||||||
id: j,
|
id: j,
|
||||||
parameters,
|
parameters,
|
||||||
|
|
|
@ -1326,6 +1326,13 @@ pub enum Stmt<'a> {
|
||||||
},
|
},
|
||||||
Ret(Symbol),
|
Ret(Symbol),
|
||||||
Refcounting(ModifyRc, &'a Stmt<'a>),
|
Refcounting(ModifyRc, &'a Stmt<'a>),
|
||||||
|
Expect {
|
||||||
|
condition: Symbol,
|
||||||
|
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`
|
/// a join point `join f <params> = <continuation> in remainder`
|
||||||
Join {
|
Join {
|
||||||
id: JoinPointId,
|
id: JoinPointId,
|
||||||
|
@ -1912,6 +1919,10 @@ impl<'a> Stmt<'a> {
|
||||||
.append(alloc.hardline())
|
.append(alloc.hardline())
|
||||||
.append(cont.to_doc(alloc)),
|
.append(cont.to_doc(alloc)),
|
||||||
|
|
||||||
|
Expect{condition, .. } =>
|
||||||
|
alloc.text("expect ")
|
||||||
|
.append(symbol_to_doc(alloc, *condition)),
|
||||||
|
|
||||||
Ret(symbol) => alloc
|
Ret(symbol) => alloc
|
||||||
.text("ret ")
|
.text("ret ")
|
||||||
.append(symbol_to_doc(alloc, *symbol))
|
.append(symbol_to_doc(alloc, *symbol))
|
||||||
|
@ -5931,22 +5942,20 @@ pub fn from_can<'a>(
|
||||||
Expect {
|
Expect {
|
||||||
loc_condition,
|
loc_condition,
|
||||||
loc_continuation,
|
loc_continuation,
|
||||||
lookups_in_cond: _ ,
|
lookups_in_cond,
|
||||||
} => {
|
} => {
|
||||||
let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache);
|
let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache);
|
||||||
let cond_symbol = env.unique_symbol();
|
let cond_symbol = env.unique_symbol();
|
||||||
let mut stmt = Stmt::Let(
|
|
||||||
env.unique_symbol(),
|
let lookups = Vec::from_iter_in(lookups_in_cond.iter().map(|t| t.0), env.arena);
|
||||||
Expr::Call(self::Call {
|
// let layouts = Vec::from_iter_in(lookups_in_cond.iter().map(|t| t.1), env.arena);
|
||||||
call_type: CallType::LowLevel {
|
|
||||||
op: LowLevel::ExpectTrue,
|
let mut stmt = Stmt::Expect {
|
||||||
update_mode: env.next_update_mode_id(),
|
condition: cond_symbol,
|
||||||
},
|
lookups: lookups.into_bump_slice(),
|
||||||
arguments: env.arena.alloc([cond_symbol]),
|
layouts: &[],
|
||||||
}),
|
remainder: env.arena.alloc(rest),
|
||||||
Layout::Builtin(Builtin::Bool),
|
};
|
||||||
env.arena.alloc(rest),
|
|
||||||
);
|
|
||||||
|
|
||||||
stmt = with_hole(
|
stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
|
@ -5958,7 +5967,6 @@ pub fn from_can<'a>(
|
||||||
env.arena.alloc(stmt),
|
env.arena.alloc(stmt),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6300,6 +6308,14 @@ fn substitute_in_stmt_help<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect { condition, lookups, layouts, remainder } => {
|
||||||
|
// TODO should we substitute in the ModifyRc?
|
||||||
|
match substitute_in_stmt_help(arena, remainder, subs) {
|
||||||
|
Some(cont) => Some(arena.alloc(Expect { condition: *condition , lookups, layouts, remainder: cont} )),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Jump(id, args) => {
|
Jump(id, args) => {
|
||||||
let mut did_change = false;
|
let mut did_change = false;
|
||||||
let new_args = Vec::from_iter_in(
|
let new_args = Vec::from_iter_in(
|
||||||
|
|
|
@ -192,6 +192,30 @@ fn function_s<'a, 'i>(
|
||||||
arena.alloc(new_refcounting)
|
arena.alloc(new_refcounting)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition,
|
||||||
|
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 = Expect {
|
||||||
|
condition: *condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder: new_continuation,
|
||||||
|
};
|
||||||
|
|
||||||
|
arena.alloc(new_refcounting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,6 +412,37 @@ fn function_d_main<'a, 'i>(
|
||||||
(arena.alloc(refcounting), found)
|
(arena.alloc(refcounting), found)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder,
|
||||||
|
} => {
|
||||||
|
let (b, found) = function_d_main(env, x, c, remainder);
|
||||||
|
|
||||||
|
if found || *condition != x {
|
||||||
|
let refcounting = Expect {
|
||||||
|
condition: *condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder: b,
|
||||||
|
};
|
||||||
|
|
||||||
|
(arena.alloc(refcounting), found)
|
||||||
|
} else {
|
||||||
|
let b = try_function_s(env, x, c, b);
|
||||||
|
|
||||||
|
let refcounting = Expect {
|
||||||
|
condition: *condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder: b,
|
||||||
|
};
|
||||||
|
|
||||||
|
(arena.alloc(refcounting), found)
|
||||||
|
}
|
||||||
|
}
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -540,6 +595,24 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||||
arena.alloc(Refcounting(*modify_rc, b))
|
arena.alloc(Refcounting(*modify_rc, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder,
|
||||||
|
} => {
|
||||||
|
let b = function_r(env, remainder);
|
||||||
|
|
||||||
|
let expect = Expect {
|
||||||
|
condition: *condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder: b,
|
||||||
|
};
|
||||||
|
|
||||||
|
arena.alloc(expect)
|
||||||
|
}
|
||||||
|
|
||||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||||
// terminals
|
// terminals
|
||||||
stmt
|
stmt
|
||||||
|
@ -570,6 +643,11 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
||||||
Refcounting(modify_rc, cont) => {
|
Refcounting(modify_rc, cont) => {
|
||||||
modify_rc.get_symbol() == needle || has_live_var(jp_live_vars, cont, needle)
|
modify_rc.get_symbol() == needle || has_live_var(jp_live_vars, cont, needle)
|
||||||
}
|
}
|
||||||
|
Expect {
|
||||||
|
condition,
|
||||||
|
remainder,
|
||||||
|
..
|
||||||
|
} => *condition == needle || has_live_var(jp_live_vars, remainder, needle),
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
|
|
|
@ -191,6 +191,21 @@ fn insert_jumps<'a>(
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder,
|
||||||
|
} => match insert_jumps(arena, remainder, goal_id, needle) {
|
||||||
|
Some(cont) => Some(arena.alloc(Expect {
|
||||||
|
condition: *condition,
|
||||||
|
lookups,
|
||||||
|
layouts,
|
||||||
|
remainder: cont,
|
||||||
|
})),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
|
||||||
Ret(_) => None,
|
Ret(_) => None,
|
||||||
Jump(_, _) => None,
|
Jump(_, _) => None,
|
||||||
RuntimeError(_) => None,
|
RuntimeError(_) => None,
|
||||||
|
|
|
@ -3,4 +3,8 @@ app "helloZig"
|
||||||
imports []
|
imports []
|
||||||
provides [main] to pf
|
provides [main] to pf
|
||||||
|
|
||||||
main = "Hello, World!\n"
|
x = 42
|
||||||
|
|
||||||
|
main =
|
||||||
|
expect x != x
|
||||||
|
"Hello, World!\n"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue