fix(cdefine): 支持浮点常量表达式(define X = 160.0 * 4) - 含浮点字面量时按浮点求值登记 double

This commit is contained in:
TermiNexus committed 2026-10-16 11:23:41 +08:00
1 parent 832b0a5706
commit c57c931099
1 file changed
+107 -1
+107 -1
View File
@@ -631,6 +631,86 @@ novirtual class AssignGenMixin:
if bits >= dw: return if bits >= dw: return
self.Gen.TypeGenErr.fatal_error(value_node, f"[VPC] define constant use-site width insufficient: constant '{rn.id}' is {dw} bits wide " f"(declared width; actual value {cdef_val}), but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {dw} bits (such as int{dw}T / CUInt{dw}T), " f"or narrow the constant at its define with an explicit cast.", "TypeError") self.Gen.TypeGenErr.fatal_error(value_node, f"[VPC] define constant use-site width insufficient: constant '{rn.id}' is {dw} bits wide " f"(declared width; actual value {cdef_val}), but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {dw} bits (such as int{dw}T / CUInt{dw}T), " f"or narrow the constant at its define with an explicit cast.", "TypeError")
# eval_cdefine_float_expr - 编译期**浮点**常量表达式求值(CDefine 值为浮点表达式时用)。
# 支持 Constant(float/int)、UnaryOp(+/-)、Call 强转包裹(float(x)/double(x)/t.CDouble(x))、
# BinOp(+ - * /)。成功写 out[0] 并返回 1;不是纯浮点常量表达式时返回 0(交回整型路径)。
# 与 extract_cdefine_int_value_bits 的分工:后者只服务整型 CDefine,遇到 float 字面量会 fail-fast。
def eval_cdefine_float_expr(self, node: ast.AST*, out: double*, pool: memhub.MemBuddy*) -> int:
if node is None: return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_FLOAT:
out[0] = cn.float_val
return 1
if cn.const_kind == ast.CONST_INT:
out[0] = double(cn.int_val)
return 1
return 0
if k == ast.ASTKind.UnaryOp:
uu: ast.UnaryOp* = ast.UnaryOp*(node)
ov: double = 0.0
if self.eval_cdefine_float_expr(uu.operand, &ov, pool) == 0: return 0
if uu.op == ast.OpKind.USub:
out[0] = -ov
return 1
if uu.op == ast.OpKind.UAdd:
out[0] = ov
return 1
return 0
if k == ast.ASTKind.Call:
# 强转包裹(float(3.14) / double(x) / t.CDouble(x)):取首实参的值
cl: ast.Call* = ast.Call*(node)
if cl.args is not None and len(cl.args) > 0:
return self.eval_cdefine_float_expr(cl.args.get(0), out, pool)
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
lv: double = 0.0
rv: double = 0.0
if self.eval_cdefine_float_expr(bop.left, &lv, pool) == 0: return 0
if self.eval_cdefine_float_expr(bop.right, &rv, pool) == 0: return 0
if bop.op == ast.OpKind.Add:
out[0] = lv + rv
return 1
if bop.op == ast.OpKind.Sub:
out[0] = lv - rv
return 1
if bop.op == ast.OpKind.Mult:
out[0] = lv * rv
return 1
if bop.op == ast.OpKind.Div:
if rv == 0.0:
self.Gen.TypeGenErr.fatal_error(node, "constant expression division by zero: the right operand of '/' evaluates to 0 at compile time (float CDefine).", "ValueError")
out[0] = lv / rv
return 1
return 0
return 0
# expr_has_float_literal - 表达式里是否**含浮点字面量**(递归)。
# 用于把"浮点 CDefine"与"整型 CDefine"分开:只有含浮点字面量时才允许走浮点求值,
# 否则纯整型表达式会被登记成 double,破坏整型位宽与整型语义。
def expr_has_float_literal(self, node: ast.AST*) -> int:
if node is None: return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_FLOAT: return 1
return 0
if k == ast.ASTKind.UnaryOp:
uu: ast.UnaryOp* = ast.UnaryOp*(node)
return self.expr_has_float_literal(uu.operand)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if self.expr_has_float_literal(bop.left) != 0: return 1
return self.expr_has_float_literal(bop.right)
if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(node)
if cl.args is not None and len(cl.args) > 0:
return self.expr_has_float_literal(cl.args.get(0))
return 0
return 0
# _register_cdefine_from_value - register a CDefine constant (float or int) # _register_cdefine_from_value - register a CDefine constant (float or int)
def _register_cdefine_from_value(self, pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None) -> None: def _register_cdefine_from_value(self, pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None) -> None:
"""Register a CDefine constant (double for floats, else integer).""" """Register a CDefine constant (double for floats, else integer)."""
@@ -689,6 +769,17 @@ novirtual class AssignGenMixin:
# CDefine declaration without a value node: fail fast # CDefine declaration without a value node: fail fast
self.Gen.TypeGenErr.fatal_error(None, f"CDefine value extraction failed: '{name}' is missing an initial value", "TypeError") self.Gen.TypeGenErr.fatal_error(None, f"CDefine value extraction failed: '{name}' is missing an initial value", "TypeError")
return return
# 浮点常量表达式(如 `define GW_F = 160.0 * 4`):整型提取器只认整型,遇到其中的 float
# 字面量会 fail-fast("cannot parse a compile-time constant")。这里先按浮点求值;
# 仅当表达式**含浮点字面量**时才走此路,纯整型表达式仍走下面的整型路径(保住位宽/整型语义)。
if self.expr_has_float_literal(val_node) != 0:
fv_try: double = 0.0
if self.eval_cdefine_float_expr(val_node, &fv_try, pool) != 0:
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, fv_try, f32_kind)
return
# 含浮点字面量但不是编译期常量(含变量/函数调用)⇒ fail-fast,不静默降级
self.Gen.TypeGenErr.fatal_error(val_node, f"CDefine float expression is not a compile-time constant: '{name}'", "TypeError")
return
# 位宽裁决:与跨模块使用方共用 cdefine_decl_bits(注解 → 右侧显式强转 → 值的最小可存宽度)。 # 位宽裁决:与跨模块使用方共用 cdefine_decl_bits(注解 → 右侧显式强转 → 值的最小可存宽度)。
# 有注解而存不下时:右侧带显式强转截断则不报错(左侧只看到截断值),否则由 # 有注解而存不下时:右侧带显式强转截断则不报错(左侧只看到截断值),否则由
# require_cdefine_storable 报"存不下"。 # require_cdefine_storable 报"存不下"。
@@ -2725,7 +2816,22 @@ novirtual class AssignGenMixin:
rhs_pt_up: mlirlite.LLVMType* = precise_rhs_ty.Pointee rhs_pt_up: mlirlite.LLVMType* = precise_rhs_ty.Pointee
if ann_pt_rb is not None and rhs_pt_up is not None and uint64(ann_pt_rb) != uint64(rhs_pt_up): if ann_pt_rb is not None and rhs_pt_up is not None and uint64(ann_pt_rb) != uint64(rhs_pt_up):
if self.Gen.ExprGenBase._is_inheritance_upcast(pool, rhs_pt_up, ann_pt_rb) != 0: rb_is_upcast = 1 if self.Gen.ExprGenBase._is_inheritance_upcast(pool, rhs_pt_up, ann_pt_rb) != 0: rb_is_upcast = 1
if alloca_pt is not None and uint64(alloca_pt) != uint64(precise_rhs_ty) and rb_is_bare_generic == 0 and rb_is_upcast == 0: # 注解解析为**无 vtable 的聚合值**(纯数据 struct / array)时,绝不把槽重建为指针:
# `x: V3 = V3(...)` 的 RHS 是构造器返回的 Ptr(V3)(只是 ABI 细节,`_translate_struct_ctor`
# 一律返回指针),若据此重建,x 的槽会变成 Ptr(V3),于是 `x = y` 编译成 `store ptr &y, x_slot`
# —— x 成为 y 的**别名**,y 的后续写入会污染 x(现场:Gargantua 的 pos = npos 之后
# `pos.y * npos.y < 0` 恒不成立,吸积盘永不绘制)。值语义槽必须保持聚合类型,
# 由下方的 coerce_to_type 把 Ptr(T) 做 load 得到一份独立的值拷贝。
# 边界:**有 vtable 的多态类必须继续用指针槽** —— 虚调用/闭包捕获等路径依赖
# 「槽里就是对象指针」(对聚合值 bitcast 到 ptr 是非法 IR),故只对无 vtable 的类放宽。
ann_agg_value: int = 0
if alloca_pt is not None and self.Gen.ExprGenBase.is_ptr_type(alloca_pt) == 0:
ak_av: int = alloca_pt.kind()
if ak_av == mlirlite.LLVMType.Struct:
av_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(alloca_pt)
if av_entry is None or av_entry.HasVTable == 0: ann_agg_value = 1
elif ak_av == mlirlite.LLVMType.Array: ann_agg_value = 1
if alloca_pt is not None and uint64(alloca_pt) != uint64(precise_rhs_ty) and rb_is_bare_generic == 0 and rb_is_upcast == 0 and ann_agg_value == 0:
new_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, precise_rhs_ty) new_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, precise_rhs_ty)
if new_alloca is not None: if new_alloca is not None:
v_entry_rb: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id) v_entry_rb: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)