mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-15 21:23:57 +00:00
Merge branch 'resetref' into Frame-Limited
This commit is contained in:
commit
87abe8df7d
13 changed files with 387 additions and 16 deletions
|
|
@ -1450,6 +1450,10 @@ fn expr_spec<'a>(
|
|||
Reset {
|
||||
symbol,
|
||||
update_mode,
|
||||
}
|
||||
| ResetRef {
|
||||
symbol,
|
||||
update_mode,
|
||||
} => {
|
||||
let tag_value_id = env.symbols[symbol];
|
||||
|
||||
|
|
|
|||
|
|
@ -1545,7 +1545,7 @@ trait Backend<'a> {
|
|||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
}
|
||||
Expr::Reset { symbol, .. } => {
|
||||
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
}
|
||||
Expr::EmptyArray => {}
|
||||
|
|
|
|||
|
|
@ -1235,6 +1235,59 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
phi.as_basic_value()
|
||||
}
|
||||
}
|
||||
ResetRef {
|
||||
symbol,
|
||||
update_mode,
|
||||
} => {
|
||||
let bytes = update_mode.to_bytes();
|
||||
let update_var = UpdateModeVar(&bytes);
|
||||
let update_mode = func_spec_solutions
|
||||
.update_mode(update_var)
|
||||
.unwrap_or(UpdateMode::Immutable);
|
||||
|
||||
let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol);
|
||||
let tag_ptr = tag_ptr.into_pointer_value();
|
||||
|
||||
// reset is only generated for union values
|
||||
let union_layout = match layout_interner.get(layout) {
|
||||
Layout::Union(ul) => ul,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ctx = env.context;
|
||||
let not_unique_block = ctx.append_basic_block(parent, "else_decref");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
|
||||
let refcount_ptr =
|
||||
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
|
||||
|
||||
let is_unique = match update_mode {
|
||||
UpdateMode::InPlace => env.context.bool_type().const_int(1, false),
|
||||
UpdateMode::Immutable => refcount_ptr.is_1(env),
|
||||
};
|
||||
|
||||
let parent_block = env.builder.get_insert_block().unwrap();
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_unique, cont_block, not_unique_block);
|
||||
|
||||
{
|
||||
// If reset is used on a shared, non-reusable reference, it behaves
|
||||
// like dec and returns NULL, which instructs reuse to behave like ctor
|
||||
env.builder.position_at_end(not_unique_block);
|
||||
refcount_ptr.decrement(env, layout_interner, layout);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
{
|
||||
env.builder.position_at_end(cont_block);
|
||||
let phi = env.builder.build_phi(tag_ptr.get_type(), "branch");
|
||||
|
||||
let null_ptr = tag_ptr.get_type().const_null();
|
||||
phi.add_incoming(&[(&tag_ptr, parent_block), (&null_ptr, not_unique_block)]);
|
||||
|
||||
phi.as_basic_value()
|
||||
}
|
||||
}
|
||||
|
||||
StructAtIndex {
|
||||
index, structure, ..
|
||||
|
|
|
|||
|
|
@ -1095,6 +1095,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
|
||||
Expr::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage),
|
||||
|
||||
Expr::ResetRef { symbol: arg, .. } => self.expr_resetref(*arg, sym, storage),
|
||||
|
||||
Expr::RuntimeErrorFunction(_) => {
|
||||
todo!("Expression `{}`", expr.to_pretty(100, false))
|
||||
}
|
||||
|
|
@ -2017,6 +2019,33 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
);
|
||||
}
|
||||
|
||||
fn expr_resetref(&mut self, argument: Symbol, ret_symbol: Symbol, ret_storage: &StoredValue) {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
// Get an IR expression for the call to the specialized procedure
|
||||
let layout = self.storage.symbol_layouts[&argument];
|
||||
let (specialized_call_expr, new_specializations) = self
|
||||
.helper_proc_gen
|
||||
.call_resetref_refcount(ident_ids, self.layout_interner, layout, argument);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
// Generate Wasm code for the IR call expression
|
||||
self.expr(
|
||||
ret_symbol,
|
||||
self.env.arena.alloc(specialized_call_expr),
|
||||
Layout::BOOL,
|
||||
ret_storage,
|
||||
);
|
||||
}
|
||||
|
||||
/// Generate a refcount helper procedure and return a pointer (table index) to it
|
||||
/// This allows it to be indirectly called from Zig code
|
||||
pub fn get_refcount_fn_index(&mut self, layout: InLayout<'a>, op: HelperOp) -> u32 {
|
||||
|
|
|
|||
|
|
@ -742,7 +742,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
self.if_is_owned_then_own(z, *x);
|
||||
}
|
||||
|
||||
Reset { symbol: x, .. } => {
|
||||
Reset { symbol: x, .. } | ResetRef { symbol: x, .. } => {
|
||||
self.own_var(z);
|
||||
self.own_var(*x);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ pub enum HelperOp {
|
|||
Dec,
|
||||
DecRef(JoinPointId),
|
||||
Reset,
|
||||
ResetRef,
|
||||
Eq,
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +184,38 @@ impl<'a> CodeGenHelp<'a> {
|
|||
(expr, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
// TODO update to not decrement children.
|
||||
pub fn call_resetref_refcount(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
argument: Symbol,
|
||||
) -> (Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut ctx = Context {
|
||||
new_linker_data: Vec::new_in(self.arena),
|
||||
recursive_union: None,
|
||||
op: HelperOp::Reset,
|
||||
};
|
||||
|
||||
let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout_interner, layout);
|
||||
|
||||
let arguments = self.arena.alloc([argument]);
|
||||
let ret_layout = layout;
|
||||
let arg_layouts = self.arena.alloc([layout]);
|
||||
let expr = Expr::Call(Call {
|
||||
call_type: CallType::ByName {
|
||||
name: LambdaName::no_niche(proc_name),
|
||||
ret_layout,
|
||||
arg_layouts,
|
||||
specialization_id: CallSpecId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments,
|
||||
});
|
||||
|
||||
(expr, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
/// Generate a refcount increment procedure, *without* a Call expression.
|
||||
/// *This method should be rarely used* - only when the proc is to be called from Zig.
|
||||
/// Otherwise you want to generate the Proc and the Call together, using another method.
|
||||
|
|
@ -262,7 +295,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
let arg = self.replace_rec_ptr(ctx, layout_interner, layout);
|
||||
match ctx.op {
|
||||
Dec | DecRef(_) => (LAYOUT_UNIT, self.arena.alloc([arg])),
|
||||
Reset => (layout, self.arena.alloc([layout])),
|
||||
Reset | ResetRef => (layout, self.arena.alloc([layout])),
|
||||
Inc => (LAYOUT_UNIT, self.arena.alloc([arg, self.layout_isize])),
|
||||
Eq => (LAYOUT_BOOL, self.arena.alloc([arg, arg])),
|
||||
}
|
||||
|
|
@ -347,6 +380,17 @@ impl<'a> CodeGenHelp<'a> {
|
|||
Symbol::ARG_1,
|
||||
),
|
||||
),
|
||||
ResetRef => (
|
||||
layout,
|
||||
refcount::refcount_resetref_proc_body(
|
||||
self,
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
layout,
|
||||
Symbol::ARG_1,
|
||||
),
|
||||
),
|
||||
Eq => (
|
||||
LAYOUT_BOOL,
|
||||
equality::eq_generic(self, ident_ids, ctx, layout_interner, layout),
|
||||
|
|
@ -360,7 +404,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
let inc_amount = (self.layout_isize, ARG_2);
|
||||
self.arena.alloc([roc_value, inc_amount])
|
||||
}
|
||||
Dec | DecRef(_) | Reset => self.arena.alloc([roc_value]),
|
||||
Dec | DecRef(_) | Reset | ResetRef => self.arena.alloc([roc_value]),
|
||||
Eq => self.arena.alloc([roc_value, (layout, ARG_2)]),
|
||||
}
|
||||
};
|
||||
|
|
@ -411,6 +455,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
niche: Niche::NONE,
|
||||
},
|
||||
HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"),
|
||||
HelperOp::ResetRef => unreachable!("No generated Proc for ResetRef"),
|
||||
HelperOp::Eq => ProcLayout {
|
||||
arguments: self.arena.alloc([layout, layout]),
|
||||
result: LAYOUT_BOOL,
|
||||
|
|
|
|||
|
|
@ -413,6 +413,206 @@ pub fn refcount_reset_proc_body<'a>(
|
|||
rc_ptr_stmt
|
||||
}
|
||||
|
||||
pub fn refcount_resetref_proc_body<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
ctx: &mut Context<'a>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
structure: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
|
||||
let rc = root.create_symbol(ident_ids, "rc");
|
||||
let refcount_1 = root.create_symbol(ident_ids, "refcount_1");
|
||||
let is_unique = root.create_symbol(ident_ids, "is_unique");
|
||||
let addr = root.create_symbol(ident_ids, "addr");
|
||||
|
||||
let union_layout = match layout_interner.get(layout) {
|
||||
Layout::Union(u) => u,
|
||||
_ => unimplemented!("Reset is only implemented for UnionLayout"),
|
||||
};
|
||||
|
||||
// Whenever we recurse into a child layout we will want to Decrement
|
||||
ctx.op = HelperOp::Dec;
|
||||
ctx.recursive_union = Some(union_layout);
|
||||
let recursion_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
|
||||
|
||||
// Reset structure is unique. Return a pointer to the allocation.
|
||||
let then_stmt = {
|
||||
let alignment = root.create_symbol(ident_ids, "alignment");
|
||||
let alignment_expr = Expr::Literal(Literal::Int(
|
||||
(layout_interner
|
||||
.get(layout)
|
||||
.alignment_bytes(layout_interner, root.target_info) as i128)
|
||||
.to_ne_bytes(),
|
||||
));
|
||||
let alloc_addr = root.create_symbol(ident_ids, "alloc_addr");
|
||||
let alloc_addr_expr = Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::NumSubWrap,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: root.arena.alloc([addr, alignment]),
|
||||
});
|
||||
|
||||
Stmt::Let(
|
||||
alignment,
|
||||
alignment_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Let(
|
||||
alloc_addr,
|
||||
alloc_addr_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Ret(alloc_addr),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
// Reset structure is not unique. Decrement it and return a NULL pointer.
|
||||
let else_stmt = {
|
||||
let decrement_unit = root.create_symbol(ident_ids, "decrement_unit");
|
||||
let decrement_expr = root
|
||||
.call_specialized_op(
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
layout,
|
||||
root.arena.alloc([structure]),
|
||||
)
|
||||
.unwrap();
|
||||
let decrement_stmt = |next| Stmt::Let(decrement_unit, decrement_expr, LAYOUT_UNIT, next);
|
||||
|
||||
// Zero
|
||||
let zero = root.create_symbol(ident_ids, "zero");
|
||||
let zero_expr = Expr::Literal(Literal::Int(0i128.to_ne_bytes()));
|
||||
let zero_stmt = |next| Stmt::Let(zero, zero_expr, root.layout_isize, next);
|
||||
|
||||
// Null pointer with union layout
|
||||
let null = root.create_symbol(ident_ids, "null");
|
||||
let null_stmt =
|
||||
|next| let_lowlevel(root.arena, root.layout_isize, null, PtrCast, &[zero], next);
|
||||
|
||||
decrement_stmt(root.arena.alloc(
|
||||
//
|
||||
zero_stmt(root.arena.alloc(
|
||||
//
|
||||
null_stmt(root.arena.alloc(
|
||||
//
|
||||
Stmt::Ret(null),
|
||||
)),
|
||||
)),
|
||||
))
|
||||
};
|
||||
|
||||
let if_stmt = Stmt::Switch {
|
||||
cond_symbol: is_unique,
|
||||
cond_layout: LAYOUT_BOOL,
|
||||
branches: root.arena.alloc([(1, BranchInfo::None, then_stmt)]),
|
||||
default_branch: (BranchInfo::None, root.arena.alloc(else_stmt)),
|
||||
ret_layout: layout,
|
||||
};
|
||||
|
||||
// Uniqueness test
|
||||
let is_unique_stmt = {
|
||||
let_lowlevel(
|
||||
root.arena,
|
||||
LAYOUT_BOOL,
|
||||
is_unique,
|
||||
Eq,
|
||||
&[rc, refcount_1],
|
||||
root.arena.alloc(if_stmt),
|
||||
)
|
||||
};
|
||||
|
||||
// Constant for unique refcount
|
||||
let refcount_1_encoded = match root.target_info.ptr_width() {
|
||||
PtrWidth::Bytes4 => i32::MIN as i128,
|
||||
PtrWidth::Bytes8 => i64::MIN as i128,
|
||||
}
|
||||
.to_ne_bytes();
|
||||
let refcount_1_expr = Expr::Literal(Literal::Int(refcount_1_encoded));
|
||||
let refcount_1_stmt = Stmt::Let(
|
||||
refcount_1,
|
||||
refcount_1_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(is_unique_stmt),
|
||||
);
|
||||
|
||||
// Refcount value
|
||||
let rc_expr = Expr::UnionAtIndex {
|
||||
structure: rc_ptr,
|
||||
tag_id: 0,
|
||||
union_layout: root.union_refcount,
|
||||
index: 0,
|
||||
};
|
||||
let rc_stmt = Stmt::Let(
|
||||
rc,
|
||||
rc_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(refcount_1_stmt),
|
||||
);
|
||||
|
||||
// Refcount pointer
|
||||
let rc_ptr_stmt = {
|
||||
rc_ptr_from_data_ptr_help(
|
||||
root,
|
||||
ident_ids,
|
||||
structure,
|
||||
rc_ptr,
|
||||
union_layout.stores_tag_id_in_pointer(root.target_info),
|
||||
root.arena.alloc(rc_stmt),
|
||||
addr,
|
||||
recursion_ptr,
|
||||
)
|
||||
};
|
||||
|
||||
rc_ptr_stmt
|
||||
}
|
||||
|
||||
// Check if refcounting is implemented yet. In the long term, this will be deleted.
|
||||
// In the short term, it helps us to skip refcounting and let it leak, so we can make
|
||||
// progress incrementally. Kept in sync with generate_procs using assertions.
|
||||
pub fn is_rc_implemented_yet<'a, I>(interner: &I, layout: InLayout<'a>) -> bool
|
||||
where
|
||||
I: LayoutInterner<'a>,
|
||||
{
|
||||
use UnionLayout::*;
|
||||
|
||||
match interner.get(layout) {
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => is_rc_implemented_yet(interner, elem_layout),
|
||||
Layout::Builtin(_) => true,
|
||||
Layout::Struct { field_layouts, .. } => field_layouts
|
||||
.iter()
|
||||
.all(|l| is_rc_implemented_yet(interner, *l)),
|
||||
Layout::Union(union_layout) => match union_layout {
|
||||
NonRecursive(tags) => tags
|
||||
.iter()
|
||||
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
|
||||
Recursive(tags) => tags
|
||||
.iter()
|
||||
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
|
||||
NonNullableUnwrapped(fields) => {
|
||||
fields.iter().all(|l| is_rc_implemented_yet(interner, *l))
|
||||
}
|
||||
NullableWrapped { other_tags, .. } => other_tags
|
||||
.iter()
|
||||
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
|
||||
NullableUnwrapped { other_fields, .. } => other_fields
|
||||
.iter()
|
||||
.all(|l| is_rc_implemented_yet(interner, *l)),
|
||||
},
|
||||
Layout::LambdaSet(lambda_set) => is_rc_implemented_yet(interner, lambda_set.representation),
|
||||
Layout::RecursivePointer(_) => true,
|
||||
Layout::Boxed(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn rc_return_stmt<'a>(
|
||||
root: &CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
|
|
|
|||
|
|
@ -474,6 +474,10 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
|||
&Expr::Reset {
|
||||
symbol,
|
||||
update_mode: _,
|
||||
}
|
||||
| &Expr::ResetRef {
|
||||
symbol,
|
||||
update_mode: _,
|
||||
} => {
|
||||
self.check_sym_exists(symbol);
|
||||
None
|
||||
|
|
|
|||
|
|
@ -310,7 +310,14 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
|||
update_mode_id: update_mode_ids.next_id(),
|
||||
};
|
||||
environment.push_reuse_token(&layout_clone, reuse_token);
|
||||
Some((layout_clone, *symbol, reuse_token))
|
||||
|
||||
let dec_ref = match rc {
|
||||
ModifyRc::Dec(_) => false,
|
||||
ModifyRc::DecRef(_) => true,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Some((layout_clone, *symbol, reuse_token, dec_ref))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -328,7 +335,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
|||
);
|
||||
|
||||
// If we inserted a reuse token, we need to insert a reset reuse operation if the reuse token is consumed.
|
||||
if let Some((layout, symbol, reuse_token)) = reuse_pair {
|
||||
if let Some((layout, symbol, reuse_token, dec_ref)) = reuse_pair {
|
||||
let stack_reuse_token = environment.peek_reuse_token(&layout);
|
||||
|
||||
match stack_reuse_token {
|
||||
|
|
@ -340,9 +347,17 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
|||
_ => {
|
||||
// The token we inserted is no longer on the stack, it must have been consumed.
|
||||
// So we need to insert a reset operation.
|
||||
let reset_expr = Expr::Reset {
|
||||
symbol,
|
||||
update_mode: reuse_token.update_mode_id,
|
||||
let reset_expr = match dec_ref {
|
||||
// A decref will be replaced by a resetref.
|
||||
true => Expr::ResetRef {
|
||||
symbol,
|
||||
update_mode: reuse_token.update_mode_id,
|
||||
},
|
||||
// And a dec will be replaced by a reset.
|
||||
false => Expr::Reset {
|
||||
symbol,
|
||||
update_mode: reuse_token.update_mode_id,
|
||||
},
|
||||
};
|
||||
|
||||
// If we generate a reuse token, we no longer want to use the drop statement anymore. So we just return the reset expression.
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ pub fn occurring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
|||
result.extend(arguments.iter().copied());
|
||||
result.insert(*symbol);
|
||||
}
|
||||
Reset { symbol: x, .. } => {
|
||||
Reset { symbol: x, .. } | ResetRef { symbol: x, .. } => {
|
||||
result.insert(*x);
|
||||
}
|
||||
|
||||
|
|
@ -946,7 +946,12 @@ impl<'a, 'i> Context<'a, 'i> {
|
|||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
EmptyArray | Literal(_) | Reset { .. } | NullPointer | RuntimeErrorFunction(_) => {
|
||||
EmptyArray
|
||||
| Literal(_)
|
||||
| Reset { .. }
|
||||
| ResetRef { .. }
|
||||
| NullPointer
|
||||
| RuntimeErrorFunction(_) => {
|
||||
// EmptyArray is always stack-allocated function pointers are persistent
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1926,6 +1926,13 @@ pub enum Expr<'a> {
|
|||
update_mode: UpdateModeId,
|
||||
},
|
||||
|
||||
// Just like Reset, but does not recursively decrement the children.
|
||||
// Used in reuse analysis to replace a decref with a resetRef to avoid decrementing when the dec ref didn't.
|
||||
ResetRef {
|
||||
symbol: Symbol,
|
||||
update_mode: UpdateModeId,
|
||||
},
|
||||
|
||||
RuntimeErrorFunction(&'a str),
|
||||
}
|
||||
|
||||
|
|
@ -2049,7 +2056,13 @@ impl<'a> Expr<'a> {
|
|||
"Reset {{ symbol: {:?}, id: {} }}",
|
||||
symbol, update_mode.id
|
||||
)),
|
||||
|
||||
ResetRef {
|
||||
symbol,
|
||||
update_mode,
|
||||
} => alloc.text(format!(
|
||||
"ResetRef {{ symbol: {:?}, id: {} }}",
|
||||
symbol, update_mode.id
|
||||
)),
|
||||
Struct(args) => {
|
||||
let it = args.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
|
||||
|
||||
|
|
@ -7464,7 +7477,9 @@ fn substitute_in_expr<'a>(
|
|||
|
||||
NullPointer => None,
|
||||
|
||||
Reuse { .. } | Reset { .. } => unreachable!("reset/reuse have not been introduced yet"),
|
||||
Reuse { .. } | Reset { .. } | ResetRef { .. } => {
|
||||
unreachable!("reset/resetref/reuse have not been introduced yet")
|
||||
}
|
||||
|
||||
Struct(args) => {
|
||||
let mut did_change = false;
|
||||
|
|
|
|||
|
|
@ -430,8 +430,8 @@ impl VariableUsage {
|
|||
borrowed: MutSet::default(),
|
||||
}
|
||||
}
|
||||
Expr::Reuse { .. } | Expr::Reset { .. } => {
|
||||
unreachable!("Reset and reuse should not exist at this point")
|
||||
Expr::Reuse { .. } | Expr::Reset { .. } | Expr::ResetRef { .. } => {
|
||||
unreachable!("Reset(ref) and reuse should not exist at this point")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@ fn insert_reset<'a>(
|
|||
| EmptyArray
|
||||
| Reuse { .. }
|
||||
| Reset { .. }
|
||||
| ResetRef { .. }
|
||||
| RuntimeErrorFunction(_) => break,
|
||||
}
|
||||
}
|
||||
|
|
@ -864,7 +865,7 @@ fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool {
|
|||
Expr::Reuse {
|
||||
symbol, arguments, ..
|
||||
} => needle == *symbol || arguments.iter().any(|s| *s == needle),
|
||||
Expr::Reset { symbol, .. } => needle == *symbol,
|
||||
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => needle == *symbol,
|
||||
Expr::ExprBox { symbol, .. } => needle == *symbol,
|
||||
Expr::ExprUnbox { symbol, .. } => needle == *symbol,
|
||||
Expr::RuntimeErrorFunction(_) => false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue