From c57c9310995e698fecfaca5bd3fb58c96db2f97e Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 16 Oct 2026 11:23:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(cdefine):=20=E6=94=AF=E6=8C=81=E6=B5=AE?= =?UTF-8?q?=E7=82=B9=E5=B8=B8=E9=87=8F=E8=A1=A8=E8=BE=BE=E5=BC=8F(define?= =?UTF-8?q?=20X=20=3D=20160.0=20*=204)=20-=20=E5=90=AB=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E5=AD=97=E9=9D=A2=E9=87=8F=E6=97=B6=E6=8C=89=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E6=B1=82=E5=80=BC=E7=99=BB=E8=AE=B0=20double?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/core/Gen/AssignGenMixin.vp | 108 ++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 6db9e18..487ce50 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -631,6 +631,86 @@ novirtual class AssignGenMixin: 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") + # 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) 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).""" @@ -689,6 +769,17 @@ novirtual class AssignGenMixin: # 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") 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(注解 → 右侧显式强转 → 值的最小可存宽度)。 # 有注解而存不下时:右侧带显式强转截断则不报错(左侧只看到截断值),否则由 # require_cdefine_storable 报"存不下"。 @@ -2725,7 +2816,22 @@ novirtual class AssignGenMixin: 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 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) if new_alloca is not None: v_entry_rb: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)