///| Keep scalar and composite parameter rewrites on the same callee cleanup
///|
/// contract. Other batch callers retain their existing cleanup mode.
fn dae_finalize_removed_parameter_callee(
evidence : DaeRemovedParamCalleeRewriteEvidence,
scalar_cleanup : Bool,
) -> @lib.Func {
let written_locals = dae_collect_written_locals(
evidence.body,
evidence.kept_params.length() + evidence.locals.length(),
)
let body = dae_simplify_rewritten_add_zero_instrs(
dae_simplify_rewritten_control_instrs(
dae_simplify_default_zero_high_guard_instrs(
evidence.body,
evidence.kept_params.length(),
written_locals,
),
scalar_cleanup,
),
)
let body = if scalar_cleanup {
dae_fold_const_eq_if_instrs_until_stable(body)
} else {
body
}
dae_simplify_to_unreachable_when_prefix_unobservable(
@lib.Func::new(evidence.locals, @lib.Expr::new(body)),
)
}
///|
fn dae_trim_parameter_rewrite_func(func : @lib.Func) -> @lib.Func {
let @lib.Func(locals, @lib.Expr(body)) = func
let (cleaned_body, _) = dae_trim_instrs_after_unreachable_tail(body)
@lib.Func::new(locals, @lib.Expr::new(cleaned_body))
}
///| Commit the complete private candidate once. The family plan checks original
///| ownership for every changed signature, then verifies the source, epochs,
///|
/// call identities, and complete module validation before publication.
fn dae_commit_composite_parameter_batch(
original : DaeOriginalBoundarySnapshot,
current : @lib.Module,
touched : Array[Bool],
changed : Bool,
graph : DaeCurrentBoundaryGraph,
candidate : DaePassResult,
materialized_defs : Array[Bool],
) -> DaeExactLiteralWorklistApplication {
let defined = graph.snapshot.defined
let unchanged : DaeExactLiteralWorklistApplication = {
mod_out: current,
touched,
changed,
productive_defs: [],
effects: dae_commit_effects_empty(defined),
}
if !candidate.changed || candidate.touched.length() != defined {
return unchanged
}
let candidate_funcs = match candidate.mod_out.code_sec {
Some(@lib.CodeSec(funcs)) if funcs.length() == defined => funcs
_ => return unchanged
}
let indices = dae_collect_func_type_indices(candidate.mod_out)
let candidate_signatures = dae_collect_defined_function_types(
candidate.mod_out,
graph.snapshot.imported,
defined,
indices,
)
let facts = dae_boundary_facts_from_snapshots(original, graph)
let effects = dae_commit_effects_empty(defined)
let productive_defs : Array[Int32] = []
let mut scratch_locals_added = 0
let mut changed_type_index_targets = 0
for def_idx in 0..
()
_ => return unchanged
}
}
if before_signature != after_signature {
if !fully_materialized {
return unchanged
}
if !dae_can_change_parameters(facts, func_abs) {
return unchanged
}
match (before_signature, after_signature) {
(
Some(@lib.FuncType(before_params, before_results)),
Some(@lib.FuncType(after_params, after_results)),
) if before_results == after_results &&
after_params.length() < before_params.length() => ()
_ => return unchanged
}
productive_defs.push(def_idx)
if func_abs < indices.length() &&
graph.snapshot.type_indices[func_abs] != indices[func_abs] {
changed_type_index_targets += 1
}
effects.parameter_changed[def_idx] = true
effects.local_projection_changed[def_idx] = true
effects.worth_decided[def_idx] = true
effects.worth_optimizing[def_idx] = true
effects.body_materialized[def_idx] = def_idx < materialized_defs.length() &&
materialized_defs[def_idx]
}
let @lib.Func(before_locals, _) = graph.snapshot.funcs[def_idx]
let @lib.Func(after_locals, _) = candidate_funcs[def_idx]
let body_changed = !dae_snapshot_func_unchanged(
graph.snapshot.funcs[def_idx],
candidate_funcs[def_idx],
)
effects.structurally_touched[def_idx] = body_changed ||
before_signature != after_signature
if before_locals != after_locals {
effects.local_projection_changed[def_idx] = true
}
if after_locals.length() > before_locals.length() {
scratch_locals_added += after_locals.length() - before_locals.length()
effects.body_localized[def_idx] = true
effects.worth_decided[def_idx] = true
effects.worth_optimizing[def_idx] = true
}
}
if productive_defs.is_empty() {
return unchanged
}
let count = productive_defs.length()
let before_type_count = match current.type_sec {
Some(types) => dae_type_sec_flattened_length(types)
None => return unchanged
}
let after_type_count = match candidate.mod_out.type_sec {
Some(types) => dae_type_sec_flattened_length(types)
None => return unchanged
}
let appended = after_type_count - before_type_count
let reasons : Array[DaeDecisionReason] = []
for _index in 0..= 0 &&
appended <= changed_type_index_targets {
changed_type_index_targets - appended
} else {
0
},
type_entries_appended: if appended > 0 {
appended
} else {
0
},
type_section_changed: current.type_sec != candidate.mod_out.type_sec,
metadata_changed: current.name_sec != candidate.mod_out.name_sec,
invalidation_domains: dae_invalidation_domains_for_work_family(
DaeUniformActualWork,
),
decision_reasons: reasons,
}
let normalized_candidate = {
..candidate,
touched: effects.structurally_touched,
primary_def: productive_defs[0],
}
let plan = match
dae_build_family_rewrite_plan(
original,
current,
graph,
normalized_candidate.primary_def,
normalized_candidate,
effects=Some(effects),
) {
Some(plan) => plan
None => return unchanged
}
let application = dae_apply_family_rewrite_plan(current, graph, plan)
if !application.applied {
return unchanged
}
let applied_reasons : Array[DaeDecisionReason] = []
for _index in 0.. DaeExactLiteralWorklistApplication {
let unchanged : DaeExactLiteralWorklistApplication = {
mod_out: current,
touched,
changed,
productive_defs: [],
effects: dae_commit_effects_empty(dae_defined_func_count(current)),
}
let snapshot = graph.snapshot
if original.imported != snapshot.imported ||
original.defined != snapshot.defined ||
candidate_defs.is_empty() {
return unchanged
}
let mut previous_def = -1
for def_idx in candidate_defs {
if def_idx <= previous_def {
// The producer applies plans in definition order. Other list orders and
// repeated definitions must keep their existing scalar transaction order.
return unchanged
}
previous_def = def_idx
}
let facts = dae_boundary_facts_from_snapshots(original, graph)
let active = Array::make(snapshot.defined, false)
let mut any = false
for def_idx in candidate_defs {
let callee_abs = snapshot.imported + def_idx
if def_idx < 0 ||
def_idx >= snapshot.defined ||
!dae_can_change_parameters(facts, callee_abs) {
break
}
let params = match snapshot.function_types[callee_abs] {
Some(@lib.FuncType(params, _)) if !params.is_empty() => params
_ => break
}
let @lib.Func(locals, _) = snapshot.funcs[def_idx]
let local_use = dae_local_use_for_callee(
snapshot.funcs[def_idx],
params.length() + locals.length(),
callee_abs,
params.length(),
)
let mut ordinary_parameters = true
for param_idx in 0.. values
None => break
}
let mut supported = values.length() == params.length()
for value in values {
match value {
Some(value) =>
if !dae_batchable_uniform_literal(value) {
supported = false
break
}
None => {
supported = false
break
}
}
}
if !supported {
break
}
active[def_idx] = true
any = true
}
if !any {
return unchanged
}
let candidate = dae_try_apply_unread_param_batch(
current,
active_defs=Some(active),
allow_uniform_params=true,
scalar_callee_cleanup=true,
boundary_snapshot=Some(snapshot),
)
dae_commit_composite_parameter_batch(
original, current, touched, changed, graph, candidate, active,
)
}
///| Retry the remaining ordered work after its first scalar commit. Admission
///|
/// and transaction checks remain those of the ordinary composite helper.
fn dae_apply_composite_literal_suffix_after_scalar(
original : DaeOriginalBoundarySnapshot,
current : @lib.Module,
touched : Array[Bool],
changed : Bool,
graph : DaeCurrentBoundaryGraph,
candidate_defs : Array[Int32],
completed_index : Int32,
) -> DaeExactLiteralWorklistApplication {
if completed_index < 0 || completed_index >= candidate_defs.length() - 2 {
// One remaining scalar candidate has no transaction gate to combine.
return {
mod_out: current,
touched,
changed,
productive_defs: [],
effects: dae_commit_effects_empty(dae_defined_func_count(current)),
}
}
let suffix = Array::makei(candidate_defs.length() - completed_index - 1, fn(
index,
) {
candidate_defs[completed_index + index + 1]
})
dae_apply_composite_exact_literal_batch(
original, current, touched, changed, graph, suffix,
)
}