fix(vararg): va_arg/arg 类型解析改走注解解析同源并 fail-fast
- _resolve_vararg_type 覆盖 builtins/库/符号表 typedef 与 T* 指针后缀,不再对手抄名单之外的名字(如 uint64)静默回退 i32 - i32 回退会让 Win64 8 字节变参槽被 trunc 丢高 32 位:viperlib vsnprintf 的 %016llx 因此只打印低 32 位,ConstFloat 的浮点常量名(即常量值)退化为真值低 32 位零扩展,double 字面量全变 0.0 - _HandleArgLlvm / _infer_vararg_type_from_context 统一调同一入口,消除三份同源重复解析链
This commit is contained in:
1 parent
2cc816ac13
commit
f7ace9876d
1 file changed
+68
-88
@@ -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 '{enum_type_name}' size=4 signed=true, ptr=false>"
|
||||
_enum_bits: int = Gen._enum_int_type(enum_type_name).width
|
||||
type_str = f"<enum '{enum_type_name}' size={_enum_bits // 8} signed=true, ptr=false>"
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user