From 21d5cd1cd1d8c3f77fb36cf52b41b6aed9231548 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 1 Oct 2026 21:17:49 +0800 Subject: [PATCH] =?UTF-8?q?fix(ExprGenBase):=20REnum=20=E5=8F=98=E4=BD=93?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E7=BA=B3=E5=85=A5=E3=80=8C=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E7=89=A9=E5=8C=96=E3=80=8D=E8=B1=81=E5=85=8D=EF=BC=9B=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=8A=A5=E6=96=87=E5=8F=AA=E6=8A=A5=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E6=9C=AC=E8=BA=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) _ctor_call_agg_ty 增加 REnum 变体构造识别:`EnumType.Variant(...)` 的变体名不是结构体名 (find_struct 必 miss)⇒ 取属性链最右裸名,若判为基础枚举则返回该枚举的聚合类型。 否则 `*槽 = ` 会被「指针 → 非基本类型值」禁令拦下—— 现场 ClassGenDef.vp:128 `*existing.Ty = llvmlite.LLVMType.Struct(...)`。 2) 按用户要求简化 Error 输出:CastError 只报问题(原因 + 源/目标类型),去掉 ";出错表达式: <形态>(行/列)源码: ..." 后缀(文件与行号已由 TypeGenErr 的 Traceback 帧给出,属重复展示),并删除随之失去调用点的 _cast_fail_where / _source_snippet。 --- App/lib/core/Gen/Expr/ExprGenBase.vp | 123 ++++++--------------------- App/lib/core/Gen/Expr/ExprGenFstr.vp | 2 +- 2 files changed, 29 insertions(+), 96 deletions(-) diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index c0b944b..32cc92d 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -362,91 +362,8 @@ novirtual class ExprGenBase: v = (ast.Return*(node)).value if v is None: return node - return v - # ============================================================ - # _source_snippet - 尽力取「源码片段」(行文本),失败返回空串 - # - # 报文风格对齐本仓既有「源码片段 + 行:列」(源行由 TypeGenErr.fatal_error 的 - # Traceback 帧也会打印;此处再在报文内附一份,便于单行错误消息自洽可读)。 - # 行文本由 stdlib.malloc 分配,拼进 f-string 后立即 free。 - # ============================================================ - def _source_snippet(self, lineno: int) -> str: - """尽力读取第 lineno 行源码文本,拼成片段;取不到返回空串""" - if lineno <= 0: - return "" - file_name: str = self.Gen.TypeGenResolve.get_current_file() - if file_name is None: - return "" - line: str = self.Gen.TypeGenErr.read_source_line(file_name, lineno) - if line is None: - return "" - body: str = GenApi.escape_fstring_copy(f"\n 源码: {line}") - stdlib.free(line) - return body - # ============================================================ - # _cast_fail_where - 隐式转换失败时报出**出错的表达式**(→ 定位到具体实参) - # - # 调用点只传 AST 节点,报错原本只有"调用所在行",多实参时看不出是哪一个 - # (如 `_td_set_token(td, TD_TOKEN_IN, dev_addr, endpoint, toggle_ptr, max_pkt)` - # 报指针→整数,无法判断是哪个实参)。此处按节点形态给出可读名: - # Name→'id'、Attribute→'.attr'、Call/Subscript→形态名,并附**行:列**。 - # - # 诊断错位修复(2026-09-28): - # ① 先经 _value_node_of 把外层语句节点(Assign kind=3 等)下沉到真正被转换的 - # 值子表达式节点,再按形态描述; - # ② 列号 col_offset 缺省为 -1,此前按无符号格式化成 4294967295;现按有符号 - # 打印,未知(<0)一律显示 '-',绝不出现 4294967295; - # ③ 尽力附源码片段(行 + 列 + 源行文本)。 - # ============================================================ - def _cast_fail_where(self, node: ast.AST*) -> str: - """取隐式转换失败的定位文本(出错表达式 + 行:列 + 源码片段) - - 返回值会**逃逸本函数**(被 coerce_to_type 拼进更大的报文),而 f-string 结果 - 指向当前函数的栈缓冲 ⇒ 每处都经 GenApi.escape_fstring_copy 显式拷到 stdlib 堆 - (历史事故:直接 return f"..." 让调用方读到已弹栈的帧,ASan - stack-buffer-underflow in vsnprintf)。 - """ - node = self._value_node_of(node) - if node is None: - return "节点未知(无 AST 信息)" - k: int = node.kind() - pos: int = -1 - where: str = None - if k == ast.ASTKind.Name: - nm: ast.Name* = ast.Name*(node) - if nm.id is not None: - pos = nm.col_offset - where = f"Name '{nm.id}'" - elif k == ast.ASTKind.Attribute: - at: ast.Attribute* = ast.Attribute*(node) - if at.attr is not None: - pos = at.col_offset - where = f"属性 '.{at.attr}'" - elif k == ast.ASTKind.Call: - pos = (ast.Call*(node)).col_offset - where = "调用结果 Call" - elif k == ast.ASTKind.Subscript: - pos = (ast.Subscript*(node)).col_offset - where = "下标表达式 Subscript" - elif k == ast.ASTKind.Constant: - pos = (ast.Constant*(node)).col_offset - where = "常量 Constant" - if where is None: - where = f"表达式 kind={k}" - # 列号按**有符号**打印:col_offset 未知/缺省为 -1,若按无符号会打成 - # 4294967295;未知一律显示 '-'。 - col_txt: str = "-" - if pos >= 0: - col_txt = f"{pos}" - # 行号:node.lineno 缺省为 0/负,未知显示 '-'。 - ln: int = node.lineno - ln_txt: str = "-" - if ln > 0: - ln_txt = f"{ln}" - snippet: str = self._source_snippet(ln) - return GenApi.escape_fstring_copy(f"{where}(行 {ln_txt},列 {col_txt}){snippet}") - - # ============================================================ + return v + # ============================================================ # IsNoneValue - 判据:源值是否为 None(空值常量),返回 1=是 / 0=否 # # None 是特殊的空值常量:语义上「无值」,按语言规则**允许隐式转换为任意 @@ -780,9 +697,25 @@ novirtual class ExprGenBase: if callee is None: return None ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(callee) - if ent is None or ent.Ty is None: - return None - return ent.Ty + if ent is not None and ent.Ty is not None: + return ent.Ty + # REnum 变体构造 `EnumType.Variant(...)`:被调方是「基础枚举.变体」,变体名不是 + # 结构体名(find_struct 必 miss);但构造结果物化的就是**基础枚举**的整对象 + # (boxed 指针指向该枚举结构体)⇒ 返回该 REnum 的聚合类型,使「构造物化」豁免 + # 覆盖 `*槽 = `。否则该写法会被「指针 → 非基本类型值」禁令拦下 + # (现场 ClassGenDef.vp:128 `*existing.Ty = llvmlite.LLVMType.Struct(...)`)。 + if cl.func.kind() == ast.ASTKind.Attribute: + base_v: ast.AST* = (ast.Attribute*(cl.func)).value + # 取属性链最右的裸名(llvmlite.LLVMType → LLVMType) + while base_v is not None and base_v.kind() == ast.ASTKind.Attribute: + base_v = (ast.Attribute*(base_v)).value + if base_v is not None and base_v.kind() == ast.ASTKind.Name: + base_nm: str = (ast.Name*(base_v)).id + if base_nm is not None and self.Gen.ClassGenEnum._is_renum_class_name(base_nm) != 0: + rn_ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(base_nm) + if rn_ent is not None and rn_ent.Ty is not None: + return rn_ent.Ty + return None # ============================================================ # _same_agg_ty - 两个聚合类型是否为「同一非基本类型」(构造物化豁免的类型匹配) # ============================================================ @@ -851,7 +784,7 @@ novirtual class ExprGenBase: return llvmlite.build_inttoptr(builder, llvmlite.const_int64(builder.Pool, 0), target_ty) m_i2p: str = "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型" self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), - f"{m_i2p};出错表达式: {self._cast_fail_where(node)}", "CastError") + m_i2p, "CastError") return val # Pointer -> integer: rejected (symmetric with the integer -> pointer rule above). # No implicit pointer <-> integer conversion is allowed; an explicit cast is @@ -860,7 +793,7 @@ novirtual class ExprGenBase: if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and self.is_ptr_type(target_ty) == 0: m_p2i: str = "指针隐式转整数已禁止:必须显式强转(如 uint64(p) / t.CInt64T(p) / int(p))才能把指针赋给整数类型" self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), - f"{m_p2i};出错表达式: {self._cast_fail_where(node)}", "CastError") + m_p2i, "CastError") return val if val_bits != 0 and target_bits != 0: if val_bits == target_bits: @@ -906,7 +839,7 @@ novirtual class ExprGenBase: if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or \ (self.is_ptr_type(val.Ty) != 0 and target_fbits != 0): self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), - f"unsupported type conversion: float <-> ptr;出错表达式: {self._cast_fail_where(node)}", "CastError") + "unsupported type conversion: float <-> ptr", "CastError") return val if val_fbits != 0 and target_fbits != 0: if val_fbits == target_fbits: @@ -932,7 +865,7 @@ novirtual class ExprGenBase: v2p_txt: str = GenApi.escape_fstring_copy( f";源值类型 {self._type_text(builder.Pool, val.Ty)} → 目标类型 {self._type_text(builder.Pool, target_ty)}") self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), - f"{m_v2p}{v2p_txt};出错表达式: {self._cast_fail_where(node)}", "CastError") + f"{m_v2p}{v2p_txt}", "CastError") return val tmp_agg: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, val.Ty) if tmp_agg is not None: @@ -962,11 +895,11 @@ novirtual class ExprGenBase: p2v_txt: str = GenApi.escape_fstring_copy( f";源类型 {self._type_text(builder.Pool, val.Ty)} → 目标值类型 {self._type_text(builder.Pool, target_ty)}") self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), - f"{m_p2v}{p2v_txt};出错表达式: {self._cast_fail_where(node)}", "CastError") + f"{m_p2v}{p2v_txt}", "CastError") return val return llvmlite.build_load(builder, target_ty, val) self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), - f"指针隐式转非指针值已禁止:必须显式解引用(c.Deref(p) / *p)或显式强转(uint64(p));出错表达式: {self._cast_fail_where(node)}", "CastError") + f"指针隐式转非指针值已禁止:必须显式解引用(c.Deref(p) / *p)或显式强转(uint64(p))", "CastError") return val # 指针 → 指针: 隐式转换须满足两条新规则(见 _ptr_cast_reason 单一真源): # 规则 1 通用指针(t.CPtr / i8*)与任何 T* 双向互转都放行; @@ -979,7 +912,7 @@ novirtual class ExprGenBase: if pc != PC_OK: reason: str = self._ptr_cast_reason_text(pc, builder.Pool, val.Ty, target_ty) self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), - f"{reason};出错表达式: {self._cast_fail_where(node)}", "CastError") + reason, "CastError") return val return llvmlite.build_bitcast(builder, val, target_ty) return val diff --git a/App/lib/core/Gen/Expr/ExprGenFstr.vp b/App/lib/core/Gen/Expr/ExprGenFstr.vp index 94d2507..97f2e46 100644 --- a/App/lib/core/Gen/Expr/ExprGenFstr.vp +++ b/App/lib/core/Gen/Expr/ExprGenFstr.vp @@ -166,7 +166,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase): # ASan stack-buffer-underflow in vsnprintf(READ size 1,栈左红区)。 # · 确需逃逸时**显式分配后拷贝**:GenApi.escape_fstring_copy # (stdlib.malloc + string.strcpy);编译器自身的报文构造即此写法 - # (ExprGenBase._cast_fail_where / AssignGenMixin.const_assign_msg)。 + # (AssignGenMixin.const_assign_msg)。 buf_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE) buf_alloca: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty) if buf_alloca is None: return None