diff --git a/lib/core/Handles/HandlesExprBuiltin.py b/lib/core/Handles/HandlesExprBuiltin.py index a3ecbab..f8001de 100644 --- a/lib/core/Handles/HandlesExprBuiltin.py +++ b/lib/core/Handles/HandlesExprBuiltin.py @@ -5,11 +5,12 @@ if TYPE_CHECKING: from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin import vast as ast import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes.t import CTypeRegistry -from lib.core.SymbolUtils import IsTModule, ExtractTypeNameFromBinOp +from lib.includes import t as _t +from lib.core.SymbolUtils import IsTModule class ExprBuiltinHandle(BaseHandle): @@ -631,7 +632,20 @@ class ExprBuiltinHandle(BaseHandle): if offset is not None: list_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{attr_name}_ptr") list_val = Gen._load(list_ptr, name=f"{obj_name}_{attr_name}") - result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen) + # 诊断增强(不改变任何成功路径):字段身份(class_member_element_class) + # 说是 list 容器,但按 _get_member_offset 取出的字段静态类型不是 + # list 特化接收者(典型为 i32,即偏移落在 lineno/col_offset 等 i32 字段) + # 时,llvmlite 只会抛出无上下文的 + # "Type of #1 arg mismatch: %"…list[…]"* != i32"。 + # 此处仅在"该调用本就会抛 TypeError 终止"时生效,把类/字段/偏移/ + # 接收者类型一并报出,用于判定 class_members 偏移与 struct 布局不一致。 + try: + result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen) + except TypeError as _e_len: + raise TypeError( + f"[fast-fail] len({obj_name}.{attr_name}) 分派 '{field_cls}.__len__' 失败:" + f"类='{bare_cls}' 偏移={offset} 接收者类型={list_val.type};" + f"(成员偏移与 struct 布局不一致)原错误: {_e_len}") if result is not None: return result val = self.HandleExprLlvm(arg_node) @@ -850,7 +864,8 @@ class ExprBuiltinHandle(BaseHandle): if enum_member_found: enum_type_name = enum_class_name if enum_type_name: - type_str = f"" + _enum_bits: int = Gen._enum_int_type(enum_type_name).width + type_str = f"" else: llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg) if isinstance(llvm_type, ir.PointerType): @@ -1048,68 +1063,11 @@ class ExprBuiltinHandle(BaseHandle): va_list_ptr = Gen.variables[vararg_name] if not va_list_ptr: return ir.Constant(ir.IntType(32), 0) - target_type: ir.Type = ir.IntType(32) - if args: - type_arg: ast.expr = args[0] - if isinstance(type_arg, ast.Name): - type_name: str = type_arg.id - if type_name in ('str', 'bytes'): - target_type = ir.PointerType(ir.IntType(8)) - elif type_name == 'CPtr': - target_type = ir.PointerType(ir.IntType(8)) - elif type_name in Gen.structs: - target_type = ir.PointerType(Gen.structs[type_name]) - else: - ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name) - if ctype_cls is not None: - llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - else: - resolved: tuple | None = CTypeRegistry.ResolveName(type_name) - if resolved is not None: - ctype_cls, ptr_level = resolved - llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if llvm_str: - base: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if base is not None: - for _ in range(ptr_level): - if isinstance(base, ir.VoidType): - base = ir.IntType(8).as_pointer() - else: - base = ir.PointerType(base) - target_type = base - elif isinstance(type_arg, ast.Attribute): - if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable): - attr_name: str = type_arg.attr - ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name) - if ctype_cls is not None: - llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - else: - assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None) - if assign_node: - ann: ast.AST | None = getattr(assign_node, 'annotation', None) - if ann: - ann_name: str | None = None - if isinstance(ann, ast.Name): - ann_name = ann.id - elif isinstance(ann, ast.Attribute): - ann_name = ann.attr - elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr): - ann_name = ExtractTypeNameFromBinOp(ann) - if ann_name: - ctype_cls: type | None = CTypeRegistry.GetClassByName(ann_name) - if ctype_cls is not None: - llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - elif ann_name in Gen.structs: - target_type = ir.PointerType(Gen.structs[ann_name]) + # 类型参数解析与 va_arg(ap, T) 共用同一入口:显式类型走 _resolve_vararg_type + # (typedef/T*/struct 同一规则源,解析不出即 fail-fast),无参数走 + # _infer_vararg_type_from_context(无注解可推断时保持 i32 默认)。 + target_type: ir.Type = self._resolve_vararg_type(args[0], Gen) if args \ + else self._infer_vararg_type_from_context(Gen) if not getattr(Gen, '_va_arg_counter', None): Gen._va_arg_counter = 0 Gen._va_arg_counter += 1 @@ -1119,8 +1077,24 @@ class ExprBuiltinHandle(BaseHandle): return result def _resolve_vararg_type(self, type_arg: ast.expr, Gen: LlvmGeneratorMixin) -> ir.Type: - """将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。""" - target_type: ir.Type = ir.IntType(32) + """将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。 + + 解析顺序(与注解解析同一规则源,避免各处手抄一份而漏项): + 1) str/bytes/CPtr → i8*;类名(Gen.structs)→ 该类指针 + 2) CTypeRegistry.GetClassByName/ResolveName(t.* 类型类与 C 基本类型名) + 3) CTypeInfo.FromNode:builtins/库/符号表 typedef(uint64/int64/sizet/ + size_t…)、`T*` 指针后缀(parser 已展开为 `T | t.CPtr`)等 + + fail-fast:以上均解析不出 LLVM 类型时直接报错终止,**绝不回退 i32**。 + 回退 i32 的后果不是"精度差一点"而是**读错变参槽**:Win64 变参槽固定 8 字节, + emit_va_arg 对 <64 位整数会生成 "va_arg i64 → trunc to i32",于是 + `va_arg(ap, uint64)` 只保留低 32 位、高 32 位被丢弃。viperlib vsnprintf 的 + `%016llx` 分支(`u_num_l: uint64 = va_arg(ap, uint64)`)因此输出 + 0x00000000XXXXXXXX —— llvmlite.ConstFloat 用该文本作"浮点常量位模式名", + 所有 double 字面量遂退化为「真值低 32 位零扩展」(1.0/2.5/3.0 的低 32 位 + 全 0 ⇒ 全变成 0.0,除法/比较结果错乱)。 + """ + target_type: ir.Type | None = None if isinstance(type_arg, ast.Name): type_name: str = type_arg.id if type_name in ('str', 'bytes'): @@ -1159,32 +1133,38 @@ class ExprBuiltinHandle(BaseHandle): resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) if resolved: target_type = resolved + # 常规分支未命中:走注解解析同一规则源(typedef / `T*` 指针后缀 / t.* 组合写法)。 + # 该规则源对未收录的名字会退化成 `%"{mod}.名"*` 假结构体(CStruct),故显式判掉。 + if target_type is None: + type_info: CTypeInfo | None = CTypeInfo.FromNode(type_arg, self.Trans.SymbolTable) + if type_info is not None and type_info.BaseType is not None \ + and not isinstance(type_info.BaseType, _t.CStruct): + target_type = Gen._ctype_to_llvm(type_info) + if target_type is None: + type_name_dbg: str = type(type_arg).__name__ + if isinstance(type_arg, ast.Name): + type_name_dbg = type_arg.id + elif isinstance(type_arg, ast.Attribute): + type_name_dbg = type_arg.attr + raise TypeError( + f"[fast-fail] va_arg/arg 的类型参数 '{type_name_dbg}' 解析不出 LLVM 类型:" + f"拒绝回退 i32(Win64 变参槽 8 字节,i32 会 trunc 掉高 32 位," + f"如 va_arg(ap, uint64) 只留低 32 位)。请检查该类型名是否已声明/import。") return target_type def _infer_vararg_type_from_context(self, Gen: LlvmGeneratorMixin) -> ir.Type: - """从当前赋值上下文推断 va_arg/arg 的目标类型。""" - target_type: ir.Type = ir.IntType(32) + """从当前赋值上下文推断 va_arg/arg 的目标类型(无显式类型参数时用) + + 有赋值注解时与显式类型参数走同一解析入口(含 typedef/T*/struct;解析不出即 + fail-fast,避免 i32 回退读错 8 字节变参槽);无注解可推断时保持 i32 默认 + (`va_arg(ap)` 读 i32 的既有语义,见 viperlib vsnprintf)。 + """ assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None) if assign_node: ann: ast.AST | None = getattr(assign_node, 'annotation', None) - if ann: - ann_name: str | None = None - if isinstance(ann, ast.Name): - ann_name = ann.id - elif isinstance(ann, ast.Attribute): - ann_name = ann.attr - elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr): - ann_name = ExtractTypeNameFromBinOp(ann) - if ann_name: - ctype_cls: type | None = CTypeRegistry.GetClassByName(ann_name) - if ctype_cls is not None: - llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - elif ann_name in Gen.structs: - target_type = ir.PointerType(Gen.structs[ann_name]) - return target_type + if ann is not None: + return self._resolve_vararg_type(ann, Gen) + return ir.IntType(32) def _HandleVaArgLlvm(self, args: list[ast.expr]) -> ir.Value: Gen: LlvmGeneratorMixin = self.Trans.LlvmGen