Decompile an Ixon environment back to Lean format. Single-pass parallel work-stealing scheduler. Computes SCCs over the name-level reference graph, then processes SCC blocks in topological order. For each block: - Phase A: decompile all non-aux_gen constants (`decompile_named_const`) - Phase B: regenerate aux_gen constants if the block has any (`decompile_block_aux_gen`) - Phase C: resolve deps t
( stt: &CompileState, )
| 4261 | .iter() |
| 4262 | .zip(original_all.iter()) |
| 4263 | .any(|(class, orig)| class[0] != *orig)); |
| 4264 | let aux_layout_changed = block.aux_layout.as_ref().is_some_and(|layout| { |
| 4265 | // Keep identical to the compile-side predicate in `compile_mutual` |
| 4266 | // — evaporated positions need their head-rewrite plans even when |
| 4267 | // no canonical slot moved. |
| 4268 | layout.evaporated.iter().any(|&b| b) |
| 4269 | || layout.perm.iter().enumerate().any(|(source_j, &canonical_i)| { |
| 4270 | canonical_i != aux_gen::nested::PERM_OUT_OF_SCC |
| 4271 | && canonical_i != source_j |
| 4272 | }) |
| 4273 | }); |
| 4274 | |
| 4275 | if !user_layout_changed && !aux_layout_changed { |
| 4276 | continue; |
| 4277 | } |
| 4278 | |
| 4279 | let plans = surgery::compute_call_site_plans( |
| 4280 | &block.class_names, |
| 4281 | &original_all, |
| 4282 | env, |
| 4283 | block.aux_layout.as_ref(), |
| 4284 | ) |
| 4285 | .map_err(|e| DecompileError::BadConstantFormat { |
| 4286 | msg: format!("decompile aux plan compute_call_site_plans: {e}"), |
| 4287 | })?; |
| 4288 | |
| 4289 | for (name, plan) in plans { |
| 4290 | // First-wins per name, but a DIFFERING later plan means two stored |
| 4291 | // blocks claim one source-indexed aux name — the same collision |
| 4292 | // class the compile side now rejects; surface it rather than |
| 4293 | // decompiling with whichever block's plan happened to install |
| 4294 | // first (plans/aux-recursor-alias-collision.md §2.4). |
| 4295 | if let Some(brecon_name) = surgery::rec_name_to_brecon_name(&name) |
| 4296 | && (aux_member_names.contains(&brecon_name) |
| 4297 | || env.contains_key(&brecon_name)) |
| 4298 | { |
| 4299 | let new_plan = surgery::BRecOnCallSitePlan::from_rec_plan(&plan); |
| 4300 | // Mirror the compile side: Type-level `.brecOn.go` / `.brecOn.eq` |
| 4301 | // share `.brecOn`'s telescope and are referenced directly by |
| 4302 | // equation-lemma proofs, so they carry the same plan keys. |
| 4303 | let mut plan_keys = vec![brecon_name.clone()]; |
| 4304 | for sub in ["go", "eq"] { |
| 4305 | let sub_name = Name::str(brecon_name.clone(), sub.to_string()); |
| 4306 | if aux_member_names.contains(&sub_name) || env.contains_key(&sub_name) |
| 4307 | { |
| 4308 | plan_keys.push(sub_name); |
| 4309 | } |
| 4310 | } |
| 4311 | for key in plan_keys { |
| 4312 | // Probe-then-act: the DashMap read guard must drop before |
| 4313 | // insert. |
| 4314 | let existing_differs = |
| 4315 | stt.brec_on_call_site_plans.get(&key).map(|e| *e != new_plan); |
| 4316 | match existing_differs { |
| 4317 | Some(true) => { |
| 4318 | return Err(DecompileError::BadConstantFormat { |
| 4319 | msg: format!( |
| 4320 | "conflicting brecOn call-site plans for '{}' across \ |