unluac 1.2.2

Multi-dialect Lua decompiler written in Rust.
Documentation
# StructureFacts

> **职责**:把 CFG + GraphFacts + Dataflow 翻译成源码恢复候选(branch / loop / short-circuit / goto / scope)。
>
> **不负责**:最终 HIR 语法决策、AST 糖化。
>
> **例子**`cfg.branch_edges(B3) + dataflow.phi_candidates_in(B5)``BranchCandidate { header: B3, merge: B5, arms: [B4a, B4b] }`

## 入口

```
src/structure/mod.rs
  analyze_structure(proto, cfg, graph_facts, dataflow, children) -> StructureFacts
```

## 模块布局

```
src/structure/
  mod.rs          入口 + 所有类型导出
  common.rs       所有结构候选类型(StructureFacts / BranchCandidate / LoopCandidate 等)
  analyze.rs      analyze_structure 骨架,协调各子模块
  helpers.rs      共享 query/cache:region entry/exit/predecessor/reducible 判定
  phi_facts.rs    phi → 结构候选翻译(merge arm def 提取 / generic phi fallback)
  branches.rs     branch 候选识别
  branch_values.rs  branch value merge 候选识别
  loops.rs        loop 候选识别(含 WhileLike 判定)
  goto.rs         GotoRequirement 识别
  regions.rs      region block 收集
  scope.rs        ScopeCandidate 识别
  short_circuit/
    branch_exit.rs   短路条件出口识别
    value_merge.rs   短路值合流提取
    shared.rs        短路共享设施
  debug.rs        dump_structure
```

## 数据流

```
LoweredProto + Cfg + GraphFacts + DataflowFacts
  └─ analyze_structure
       ├─ branches.rs   → Vec<BranchCandidate>
       ├─ loops.rs      → Vec<LoopCandidate>
       ├─ short_circuit/ → Vec<ShortCircuitCandidate>
       ├─ goto.rs       → Vec<GotoRequirement>
       ├─ scope.rs      → Vec<ScopeCandidate>
       └─ phi_facts.rs  → Vec<GenericPhiMaterialization>
            └─ StructureFacts { branches, loops, short_circuits, gotos, scopes, generic_phis, ... }
```

## 关键类型(`src/structure/common.rs`

| 类型 | 说明 |
|---|---|
| `StructureFacts` | 所有候选的统一容器 |
| `BranchCandidate` | if / if-else 候选:header block + merge block + arms |
| `BranchValueMergeCandidate` | branch 两臂合流成值的候选 |
| `LoopCandidate` | while / repeat 候选:header + back edge + exit |
| `LoopSourceBindings` | loop 携带状态绑定信息 |
| `LoopValueMerge` / `LoopExitValueMergeCandidate` | loop 出口值合流 |
| `ShortCircuitCandidate` | 短路逻辑候选 |
| `GotoRequirement` | 必须保留的 goto/label 对 |
| `ScopeCandidate` | TBC close 作用域候选 |
| `RegionFact` / `RegionKind` | region 边界描述 |
| `GenericPhiMaterialization` | fallback phi 物化候选 |

## 应优先复用的共享设施

| 设施 | 用途 |
|---|---|
| `helpers.rs` | region entry/exit edge、reducible 判定、branch region block 收集、forward region 扫描 |
| `phi_facts.rs` | phi/def/incoming 翻译成结构候选,避免在 loops / branch_values / short_circuit 各处散落 |
| `short_circuit/` | 短路条件出口识别 + 值合流提取;包含 `if a or b then ... else ... end` 这类共享 then/else body 的条件 DAG,不跑到 HIR 再重新解释 DAG |

### 短路条件出口补充

`short_circuit/branch_exit.rs` 会同时提取三类条件出口 DAG:

- 线性 guard / if-then 链,例如 `if a and b then body end`- if-else 共享 body 链,例如 `if a or b then shared else gated end`,其中后续 header 可能把一臂接到共享 body,另一臂接到 loop continue / merge。
- 嵌套 if-else 节点组成的 guard DAG。遇到多前驱目标时会先把目标视为出口,避免把共享 continuation 或 body header 继续 follow 进后面的 merge。

在 loop 内,单纯的全图 `can_reach` 会穿过回边,不能作为“两个出口属于自然 fallthrough”的唯一依据。这里优先结合后支配关系判断出口方向;对同一共享出口在不同节点上极性不同的链,允许 relaxed linear inference 只要求最终收敛到两个出口。

## 维护规范

1. **结构证据应尽量前移**:HIR 需要的 phi 分类 / merge arm 信息优先补进 `StructureFacts`,不让 HIR 再从 dataflow 裸存储推断。
2. **候选与最终决策分开**:这一层只回答"像不像 branch/loop/short-circuit",不决定"HIR 应该选哪种语法"。  
   - `loops.rs``WhileLike` 判定应接受无副作用条件前缀(位运算、表访问等),不应只接受"一条裸 branch"。
3. **共享事实放 `common.rs`**,算法放子模块。
4. **复用 CFG/Dataflow query**:不私养"小型 CFG API"或"小型 Dataflow API"。

## 向后提供的事实

HIR 消费的所有结构候选,见"关键类型"一节。

## 排错指引

| 症状 | 检查点 |
|---|---|
| branch 合流 block 识别错误 | `helpers.rs` region exit 逻辑 |
| loop 类型判断错误(while vs repeat) | `loops.rs` WhileLike 识别 |
| 短路候选丢失 | `short_circuit/branch_exit.rs`,尤其检查 guard/if-then 链和 if-else 共享 body 两类条件出口 |
| phi 归属错误(branch vs loop vs generic) | `phi_facts.rs` incoming 分类 |
| 想看结构候选 | `unluac --debug --target-stage structure-facts <file>` |