cure_engine/lang.rs
1//! `Lang` trait:语言适配层。引擎的一切通用逻辑只通过它访问语言 AST。
2//!
3//! 结构契约:
4//! - 节点用稳定句柄 `Id`(语言侧 arena 索引),arena 只追加不回收,`Id` 永不失效;
5//! - children 顺序 = 书写/求值顺序(各 kind 的布局见 [`crate::kind::NodeKind`]);
6//! - `check` 阶段允许**追加**新节点(arena push),但不得改动既有结构;
7//! 结构变更只允许通过 `Edit` 在应用阶段发生。
8
9use std::fmt::Debug;
10use std::hash::Hash;
11
12use crate::effect::Effect;
13use crate::kind::{BinOp, LitRef, NodeKind, UnOp};
14
15/// Result of scanning one statement for a name: any-path reads keep the
16/// init alive, shadowing declarations reject, and a dominating simple
17/// assignment kills it. `None` means the statement neither reads, shadows,
18/// nor dominantly writes the name.
19#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20pub enum StmtKill<Id> {
21 None,
22 Read,
23 Shadow,
24 DomWrite(Id),
25}
26
27pub trait Lang {
28 /// 稳定节点句柄(通常是 `Copy` 的索引 newtype)。
29 type Id: Copy + Eq + Hash + Ord + Debug;
30 /// 名字等价类句柄(Java:u32 intern id)。区域扫描的全部名字比较
31 /// 走它——语言侧应保证同名字同键、比较为整数等值(无字符串 memcmp)。
32 type NameKey: Copy + Eq + Ord + Debug;
33 /// 节点在语言 arena 中的稠密下标(供引擎位图/数组索引)。
34 fn node_index(&self, id: Self::Id) -> usize;
35 /// `node_index` 的逆映射(下标 → Id)。cure-tree 工具包的线性
36 /// 扫描(效果表重建、名字键扩展)需要按 arena 下标遍历。
37 fn id_of_index(&self, idx: usize) -> Self::Id;
38
39 // ---- 结构 ----
40
41 fn kind(&self, id: Self::Id) -> NodeKind;
42 fn children(&self, id: Self::Id) -> &[Self::Id];
43
44 /// 把 `parent` 的第 `index` 个孩子替换为 `new`。
45 fn set_child(&mut self, parent: Self::Id, index: usize, new: Self::Id);
46 /// 删除 `parent` 的第 `index` 个孩子。
47 fn remove_child(&mut self, parent: Self::Id, index: usize);
48 /// 从 `node` 的第 `index` 个孩子开始删 `remove` 个、插入 `insert`。
49 fn splice(&mut self, node: Self::Id, index: usize, remove: usize, insert: Vec<Self::Id>);
50
51 // ---- 构造 ----
52
53 fn build_return(&mut self, value: Option<Self::Id>) -> Self::Id;
54 fn build_block(&mut self, stmts: Vec<Self::Id>) -> Self::Id;
55
56 /// if→三元合并前的类型上下文校验。窄基本类型(Java short/byte/char)
57 /// 的 return/赋值允许**常量表达式**收窄;`if` 的两支各自是常量表达式,
58 /// 合并成非常量三元后收窄失效(`return cond ? A|B : C|D;` 对 short
59 /// 返回不可编译)。默认放行;Java 侧按返回类型/目标类型裁决。
60 fn ternary_merge_ok(&self, _target: Option<Self::Id>, _branches: &[Self::Id]) -> bool {
61 true
62 }
63
64 /// 局部传播把值内联进 cast 操作数时的语言类型守卫(Java 装箱:
65 /// `Boolean b = call(); (T) b` → `(T) call()` 把基本 boolean 塞进
66 /// 类型变量 cast,非法)。默认放行。
67 fn propagation_cast_unsound(&self, _decl: Self::Id, _value: Self::Id) -> bool {
68 false
69 }
70
71 /// 三元值内联进 Call/New 实参位是否可能丢失泛型推断(声明为参数化
72 /// 引用类型时目标类型是推断锚点)。默认 false。
73 fn ternary_arg_unsound(&self, _decl: Self::Id, _value: Self::Id) -> bool {
74 false
75 }
76 fn build_unary(&mut self, op: UnOp, operand: Self::Id) -> Self::Id;
77 fn build_bool(&mut self, v: bool) -> Self::Id;
78 /// 构造整数/long 字面量(常量折叠产物)。`long` 为 true 时带 L 语义。
79 fn build_int(&mut self, v: i64, long: bool) -> Self::Id;
80 /// 构造二元运算节点(常量折叠 / 取反重写等需要重建比较运算时使用)。
81 fn build_bin(&mut self, op: BinOp, l: Self::Id, r: Self::Id) -> Self::Id;
82 /// 构造三元表达式 `c ? a : b`(if→ternary 归并)。
83 fn build_ternary(&mut self, c: Self::Id, a: Self::Id, b: Self::Id) -> Self::Id;
84 /// 构造简单赋值 `target = value`(if→三元赋值归并)。
85 fn build_assign(&mut self, target: Self::Id, value: Self::Id) -> Self::Id;
86 /// 构造字符串字面量(字符串常量折叠产物)。
87 fn build_str(&mut self, s: &str) -> Self::Id;
88 /// 构造字符字面量(部分求值产物:`(char)('a'+1)` → 'b')。
89 fn build_char(&mut self, c: char) -> Self::Id;
90 /// 深拷贝子树(返回新 Id)。
91 fn copy_subtree(&mut self, id: Self::Id) -> Self::Id;
92
93 // ---- 语义查询 ----
94
95 /// 整棵子树的聚合效果(最坏情况)。语言侧应缓存(见 `prepare`)。
96 fn effect(&self, id: Self::Id) -> Effect;
97 /// 使 `id` 的聚合效果缓存失效(若有)。引擎在同 pass 批量应用编辑后
98 /// 沿祖先链调用——祖先的聚合效果含被改子树,必须失效,
99 /// 后续 `effect()` 读取回退为按需重算(子缓存仍有效,代价 O(孩子数))。
100 /// 默认空实现:无缓存的语言每次现算,天然正确。
101 fn invalidate_effect(&mut self, _id: Self::Id) {}
102
103 /// `id` 子树的区域事件序列(使用索引,prepare 期间预计算)。
104 /// 事件序必须等于 scan_region 原递归的遍历序;返回 None 表示该
105 /// 节点无索引(未构建/已失效)——scan_region 回退为原递归遍历。
106 /// 引擎在应用编辑后沿祖先链调用 invalidate_effect 使索引失效。
107 /// 事件索引一致性自检(惰性重建 B3 调试):默认空实现。
108 fn debug_verify_events(&self, _stmt: Self::Id) {}
109
110 fn region_events(
111 &self,
112 _id: Self::Id,
113 ) -> Option<&[crate::kind::RegionEvent<Self::Id, Self::NameKey>]> {
114 None
115 }
116 /// 节点的名字键(VarRef/VarDecl/ForEach/Catch 等命名节点);
117 /// 无名节点返回 None。
118 fn var_key(&self, _id: Self::Id) -> Option<Self::NameKey> {
119 None
120 }
121
122 /// 两个节点是否指向**同一个存储位置**。默认按名字键(Java 的
123 /// 作用域信息由前端 facts 提供);有绑定解析的后端(TS/JS)应覆盖
124 /// 为 BindingId 等值,避免同名遮蔽被当成同一变量。
125 fn same_binding(&self, a: Self::Id, b: Self::Id) -> bool {
126 match (self.var_key(a), self.var_key(b)) {
127 (Some(x), Some(y)) => x == y,
128 _ => false,
129 }
130 }
131 /// Syntactic VarRef count for `key` inside this statement subtree.
132 /// `None` = no fresh index (unbuilt/stale) — callers fall back to a
133 /// recursive walk. Indexed languages return `Some(0)` for a statement
134 /// that simply does not mention the name.
135 fn stmt_var_ref_count(&self, _stmt: Self::Id, _key: Self::NameKey) -> Option<u32> {
136 None
137 }
138 /// Whether a lambda inside this statement subtree captures `key`.
139 /// Same `None` contract as [`Self::stmt_var_ref_count`].
140 fn stmt_lambda_captures(&self, _stmt: Self::Id, _key: Self::NameKey) -> Option<bool> {
141 None
142 }
143 /// Precomputed statement-level kill event for `key`. `None` = no fresh
144 /// index (callers fall back to [`crate::rules::scan_kill_event`]).
145 fn stmt_kill_event(&self, _stmt: Self::Id, _key: Self::NameKey) -> Option<StmtKill<Self::Id>> {
146 None
147 }
148 /// Whether the statement syntactically mentions `key` (VarRef) or
149 /// declares it (VarDecl/ForEach/Catch). `None` = no index.
150 fn stmt_mentions(&self, _stmt: Self::Id, _key: Self::NameKey) -> Option<bool> {
151 None
152 }
153 /// Whether the statement subtree contains an opaque region. `None` =
154 /// no index.
155 fn stmt_opaque(&self, _stmt: Self::Id) -> Option<bool> {
156 None
157 }
158 /// 节点是否不透明(内部含不可解析原文,读/写集不可证明):
159 /// Java 的 Raw 节点与匿名类体(anon_raw)。区域用量分析的可靠性
160 /// 前提——漏判会导致捕获变量被误删(对抗波 4 抓获)。
161 fn is_opaque(&self, _id: Self::Id) -> bool {
162 false
163 }
164 /// 节点自身(不含 children)的效果贡献。
165 fn own_effect(&self, id: Self::Id) -> Effect {
166 match self.kind(id) {
167 NodeKind::Literal
168 | NodeKind::Paren
169 | NodeKind::This
170 | NodeKind::Block
171 | NodeKind::Empty
172 | NodeKind::VarDecl => Effect::Pure,
173 NodeKind::VarRef => Effect::MayRead,
174 NodeKind::Member => Effect::MayThrow,
175 NodeKind::Index | NodeKind::New => Effect::MayThrow,
176 NodeKind::Call => Effect::Unknown,
177 NodeKind::Assign => Effect::MayWrite,
178 NodeKind::Unary => {
179 if self.un_op(id).is_some_and(|o| o.is_incdec()) {
180 Effect::MayWrite
181 } else {
182 Effect::Pure
183 }
184 }
185 NodeKind::Binary => {
186 if self.bin_op(id).is_some_and(|o| o.may_div_zero()) {
187 Effect::MayThrow
188 } else {
189 Effect::Pure
190 }
191 }
192 NodeKind::Return | NodeKind::Throw => Effect::MayThrow,
193 NodeKind::Try | NodeKind::ForEach | NodeKind::Assert => Effect::MayThrow,
194 NodeKind::Catch | NodeKind::NewArray => Effect::MayThrow,
195 NodeKind::Synchronized => Effect::MayWrite,
196 NodeKind::Raw => Effect::Unknown,
197 _ => Effect::Unknown,
198 }
199 }
200
201 /// 表达式是否为**原始 boolean** 类型(装箱 Boolean 必须返回 false)。
202 fn is_bool(&self, id: Self::Id) -> bool;
203 /// 表达式是否具有精确整数类型(byte/short/int/long/char),用于算术恒等式守卫。
204 fn is_exact_int(&self, id: Self::Id) -> bool;
205 /// 是否为语言层的普通局部变量(可安全删除赋值;字段/全局必须 false)。
206 fn is_local_var(&self, id: Self::Id) -> bool {
207 self.kind(id) == NodeKind::VarRef
208 }
209
210 /// 声明(VarDecl 节点)的类型域是否容纳 int 算术结果。窄类型
211 /// (Java byte/short/char)的复合赋值含**隐式收窄**
212 /// (`b += K` ≡ `b = (byte)(b + K)`)——把 delta 折回声明会产出
213 /// 非法(超域常量)或变义(丢静态类型:`char ch = 99` 内联进
214 /// println 打数字而非字符)的字面量。默认 true(宽域语言)。
215 fn is_long_decl(&self, _decl: Self::Id) -> bool {
216 false
217 }
218 fn is_wide_decl(&self, _decl: Self::Id) -> bool {
219 true
220 }
221
222 /// 声明是否为**推断类型**(Java 10 `var`)。推断声明剥除 init 后
223 /// 变成 `var x;`——非法源码(obf_fuzz 差分抓获)。剥 init 类规则
224 /// 必须拒绝。默认 false。
225 fn is_inferred_decl(&self, _decl: Self::Id) -> bool {
226 false
227 }
228
229 /// 声明是否为字符类型。字符类型变量对 **int 字面量内联类型敏感**:
230 /// `char c = 98; println(c)` 打印 'b',而内联成 `println(98)` 打印
231 /// 98——静态类型可被 println / 字符串拼接观察。窄域规则(如
232 /// local_propagation)须拒绝非 Char 字面量值出字符声明。
233 fn is_char_decl(&self, _decl: Self::Id) -> bool {
234 false
235 }
236
237 /// 局部传播到**成员访问接收位**是否类型不可靠:声明的静态类型与
238 /// init 表达式的静态类型在成员解析上可能不等价(Java raw 泛型:
239 /// `List l = m(); l.add("x")` 的 raw 擦除收 Object,内联成
240 /// `m().add("x")` 后按泛型收 CAP#1 → javac 硬错——mockito
241 /// ReturningDefaultValuesTest 抓获)。仅接收位敏感(实参位 raw↔
242 /// 泛型双向兼容)。默认 false(无 raw 泛型的语言)。
243 fn receiver_propagation_unsound(
244 &self,
245 _decl: Self::Id,
246 _value: Self::Id,
247 _use_parent: Option<Self::Id>,
248 _use_at_head: bool,
249 ) -> bool {
250 false
251 }
252
253 // ---- 外部事实(front-end facts)----------------------------------
254 //
255 // 语言无关的「前端断言」词汇:持有 IR/类型/数据流信息的调用方
256 //(反编译前端等)对源码层**可见变量与被调成员**的权威陈述。引擎
257 // 规则保持形状驱动——事实只用于**放宽守卫**(替代源码层保守扫
258 // 描/回退),永不引入源码无法自证的新改写方向,语义保持承诺不
259 // 变。默认实现 = 无事实(保守,纯源码模式)。
260 //
261 // 携带方式由各语言 crate 自定(本 trait 只定义查询面);事实的
262 // 生命周期是**编译单元**——换单元必须清空/替换。
263
264 /// 变量(按名字键)在源码外**无隐藏写**且全部可见写恰为一次
265 /// (单一定义)。传播/钻石折叠据此跳过写窗口扫描。
266 fn fact_single_def(&self, _key: Self::NameKey) -> bool {
267 false
268 }
269 /// 变量(按名字键)的读点恰为一次(前端权威计数)。传播据此
270 /// 免句法引用计数兜底。
271 fn fact_single_use(&self, _key: Self::NameKey) -> bool {
272 false
273 }
274 /// 变量(按名字键)的静态类型为原始布尔。类型敏感的布尔折叠
275 /// 据此放行 int 形状。
276 fn fact_bool_valued(&self, _key: Self::NameKey) -> bool {
277 false
278 }
279 /// 按被调成员名的副作用上限(池级/IR 分析:纯方法调用、无抛
280 /// 字段读等)。`None` = 未知(保守)。条件折叠/短路重建据此
281 /// 放行含调用的条件。
282 fn fact_call_effect(&self, _name: &str) -> Option<Effect> {
283 None
284 }
285
286 fn bin_op(&self, id: Self::Id) -> Option<BinOp>;
287 fn un_op(&self, id: Self::Id) -> Option<UnOp>;
288 fn literal(&self, id: Self::Id) -> Option<LitRef<'_>>;
289 /// 变量相关名字:VarRef / VarDecl 读写的名字,以及 ForEach / Catch 的**声明绑定名**。
290 fn var_name(&self, id: Self::Id) -> Option<&str>;
291 /// 成员访问/方法引用的**成员名**(x.f / x.m() / X::m 的 f/m 部分)。
292 /// 结构相等性比较用(var_name 只覆盖 VarRef——x.a 与 x.b 曾被误判
293 /// 相等 → ternary_fold 删除活分支,Types.java Rewriter.high 抓获)。
294 fn member_name(&self, _id: Self::Id) -> Option<&str> {
295 None
296 }
297 /// VarRef 值替换进 cast/instanceof 类型判定位后的**可编译性**裁决
298 /// (传播守卫)。引擎只判形状(沿 Paren 链首遇 Cast/InstanceOf,
299 /// 见 typed_position)——类型相容性归语言侧:值的声明类型未解析
300 /// (自由名——反编译残缺作用域常态)→ 未知类型可 cast 到任何
301 /// 引用类型,恒可编译;声明类型与目标类型同名 → 恒合法;其余
302 /// 交给语言的类型知识(Java:Object 任一侧放行,否则保守拒绝——
303 /// interface/子类关系不可判)。默认 true(无静态类型判定位的语言)。
304 fn var_cast_position_ok(&self, _value: Self::Id, _typed: Self::Id) -> bool {
305 true
306 }
307 /// 子树是否含**可提升/条件初始化声明**(JS 的 `if (c) function f(){}`
308 /// ——Annex B:绑定被 var 提升但初始化条件化;搬移/删除该声明改变
309 /// 语义)。ConstCondition 的 then 解包 / else 丢弃需拒绝。
310 /// 默认 false(Java 无嵌套函数声明)。
311 fn contains_hoisted_decl(&self, _root: Self::Id) -> bool {
312 false
313 }
314 /// 块展开是否改变 Annex B 块级函数声明的提升语义(TS 判直接孩子;
315 /// Java 恒 false)。
316 fn block_hoists_decl(&self, _block: Self::Id) -> bool {
317 false
318 }
319 /// 是否为**标签模板**节点(tagged template literal)。标签位的
320 /// `this` 绑定取决于 tag 是 Reference 还是值——折臂/短路改写会
321 /// 改变 this(`(true ? obj.f : null)\`\`` vs `obj.f\`\``)。
322 fn is_tagged_template(&self, _id: Self::Id) -> bool {
323 false
324 }
325 /// 赋值目标位的 Paren 是否**语义显著**(JS 具名求值:`(fn) = function(){}`
326 /// 不推断 name,删括号会改变 Function.prototype.name)。
327 fn assign_target_paren_required(&self, _inner: Self::Id) -> bool {
328 false
329 }
330 /// 局部传播的值形态是否**稳定**(复制到使用点不改变求值时间语义)。
331 /// Java:任意表达式(类型系统/纯读假设——引擎既有扫描保证)。
332 /// JS:仅字面量(全局/闭包变量可在外层被改写;`var local2 = y; …
333 /// y 变化 …; return {local2}` 内联会读到新值——Test262 eval 家族抓获)。
334 fn local_propagation_value_ok(&self, _decl: Self::Id, _value: Self::Id) -> bool {
335 true
336 }
337 /// 语句子树是否含**规则不可见**的原文区域(Raw/类表达式/解构模式)。
338 fn stmt_has_opaque_region(&self, _stmt: Self::Id) -> bool {
339 false
340 }
341 /// 值子树是否读取**可能未解析**的名字(未声明全局 → 求值抛
342 /// ReferenceError)。死存储/死声明删除会把这次抛异常一并删掉
343 ///(JS `var o = {notDefined};`——Test262 object/not-defined 抓获)。
344 fn value_may_throw_unresolved(&self, _value: Self::Id) -> bool {
345 false
346 }
347 /// 声明删除规则(dead_decl)是否允许删除该声明。Java:允许;
348 /// TS:仅 `var`(let/const/using 的 TDZ/词法遮蔽/dispose 语义会因
349 /// 删除而改变——Test262 annexB block-decl 家族抓获)。
350 fn dead_decl_allowed(&self, _decl: Self::Id) -> bool {
351 true
352 }
353 /// 函数边界(λ 捕获扫描用):Java 为 Lambda;TS 还含函数声明
354 /// (FuncStmt——`function i(){ use(x) }` 对 x 是按引用捕获)。
355 fn is_function_like(&self, id: Self::Id) -> bool {
356 self.kind(id) == NodeKind::Lambda
357 }
358 /// Paren 的**语法必要性**(paren_removal 守卫):该内层形态在
359 /// 紧位置(二元/一元运算数位)是否必须带括号——产生式约束,
360 /// 优先级序表达不了。JS:箭头/对象字面量/Raw 形态在运算数位
361 /// 语法必需括号(`a ?? (() => true)`)。默认 false(打印层
362 /// 优先级模型完备的语言——如 Java)。
363 fn grammar_paren_required(&self, _inner: Self::Id) -> bool {
364 false
365 }
366 /// `!!x ≡ x` 恒等式在**本语言**是否成立(DoubleNot 规则守卫)。
367 /// 蕴含 x 为布尔值。Java:`!` 操作数必须 boolean——可编译的 !!x
368 /// 其 x 恒 boolean → 恒成立。JS:!x 是布尔胁迫(!!0 === false
369 /// ≠ 0)→ 仅语法级布尔(is_bool)成立。默认取 is_bool(保守)。
370 fn double_not_sound(&self, operand: Self::Id) -> bool {
371 self.is_bool(operand)
372 }
373 /// 节点**负载**等价(NodeData 中的非孩子字段:New.ty/anon_raw、
374 /// Cast.ty、InstanceOf.ty/bind、Lambda.params_raw、VarDecl.ty 等)。
375 /// structurally_equal 用——负载不比较曾把 `cond ? new FairSync() :
376 /// new NonfairSync()`(参数相同、类型不同)判相等折叠:JDK 语料
377 /// 90 处真实误折(ReentrantReadWriteLock 锁公平性、DirectByteBuffer
378 /// 字节序、instanceof 守卫 cast → CCE,5 例运行时行为改变证实)。
379 fn payload_equal(&self, _a: Self::Id, _b: Self::Id) -> bool {
380 true
381 }
382 /// 名字是否是本单元声明的字段(字段读的可移动性判定——局部/参数
383 /// 不可被调用改变,字段可以)。默认 false(名字未声明 → 当作可移动,
384 /// 手工构建的 AST 无作用域解析时不会误伤)。
385 fn is_field_name(&self, _name: &str) -> bool {
386 false
387 }
388 /// 子树内是否含**模式绑定**(Java instanceof pattern:绑定作用域按
389 /// JLS 6.3 流可达性可越出语句本身到 enclosing block——块展平/语句
390 /// 移动类规则须拒绝。默认 false(无模式绑定的语言)。
391 fn has_pattern_bindings(&self, _root: Self::Id) -> bool {
392 false
393 }
394 /// 赋值运算(`=` 之外的复合赋值也在此表达;None 表示简单赋值)。
395 fn assign_op(&self, _id: Self::Id) -> Option<BinOp> {
396 None
397 }
398
399 // ---- 字面量折叠语义(数值/字符串语义全权归属语言侧)----
400 //
401 // 引擎规则只做**形态**匹配(双字面量、算符类别、代价门槛);一切
402 // 数值语义——宽度与环绕、除零策略、移位掩码、字符串拼接的强制
403 // 转换规则——由这些钩子的语言实现决定。默认全部不折(保守)。
404
405 /// 字面量二元折叠:算术/位运算/移位(`+ - * / % << >> >>> & | ^`)
406 /// 与字符串拼接。操作数为字面量节点 id;返回折叠产物节点。
407 fn fold_lit_bin(&mut self, _op: BinOp, _l: Self::Id, _r: Self::Id) -> Option<Self::Id> {
408 None
409 }
410
411 /// 字面量比较折叠(如 `1 < 2`)。可比较的字面量种类与字符串比较
412 /// 语义(引用比较 vs 值比较)由语言侧决定;返回布尔结果。
413 fn fold_lit_cmp(&mut self, _op: BinOp, _l: Self::Id, _r: Self::Id) -> Option<bool> {
414 None
415 }
416
417 /// 负数值字面量的取反(`-(-lit)` 外层负消除)。宽度溢出时必须
418 /// 返回 None 拒绝折叠(如 Java i32 的 `-(-2147483648)`)。
419 /// 默认实现按 i64 拒绝 `i64::MIN` 取反(防 debug panic / release 回绕)。
420 fn fold_lit_neg(&mut self, v: i64, _wide: bool) -> Option<i64> {
421 v.checked_neg()
422 }
423
424 /// 常量重结合合并:`(x op1 K1) op2 K2` 的常量合并结果。
425 /// 语言侧负责宽度回绕(Java i32 的 `K1+K2` 溢出必须回绕,否则
426 /// 产物是非法字面量)。
427 fn reassoc_delta(
428 &mut self,
429 _op1: BinOp,
430 _k1: i64,
431 _op2: BinOp,
432 _k2: i64,
433 _wide: bool,
434 ) -> Option<ReassocOutcome<Self::Id>> {
435 None
436 }
437
438 /// 每轮扫描前由引擎调用;语言侧在此刷新缓存(效果表、变量类型表等)。
439 /// `root` 是本轮简化的根(通常是方法体),语言侧可据此做作用域分析。
440 fn prepare(&mut self, _root: Self::Id) {}
441
442 /// 节点成本权重(用于“改写必须严格降本”的门槛)。
443 fn cost_weight(&self, kind: NodeKind) -> u64 {
444 crate::cost::default_weight(kind)
445 }
446}
447
448/// [`Lang::reassoc_delta`] 的合并结果。
449#[derive(Clone, Copy, Debug)]
450pub enum ReassocOutcome<Id> {
451 /// 合并为中性元(`(x+K1)-K1` 类)→ 直接替换为 `x`。
452 Neutral,
453 /// 合并后的常量字面量节点 + 建议算符(`x op lit`;负常量由语言侧
454 /// 决定用 `Sub` 正字面量还是 `Add` 负字面量表达)。
455 Lit(Id, BinOp),
456}