From 23b47053d9ed7590589f5578db521aa3ad16679b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 12 Oct 2026 20:59:39 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=90=8C=E6=AD=A5=20TransPyV=E2=86=92?= =?UTF-8?q?ViperC=20=E6=94=B9=E5=90=8D=E3=80=81new=20=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E6=94=AF=E6=8C=81=E4=B8=8E=E5=AE=9A=E5=AE=BD=E5=AD=97?= =?UTF-8?q?=E9=9D=A2=E9=87=8F/=E7=AA=84=E5=8C=96=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/Handles/HandlesAssign.py | 4554 +++++------ lib/core/Handles/HandlesExpr.py | 1226 +-- lib/core/Handles/HandlesExprCall.py | 11057 +++++++++++++------------- lib/core/LlvmCodeGenerator.py | 6 +- lib/core/SymbolUtils.py | 3277 ++++---- lib/vast/ExprParser.py | 95 +- lib/vast/Nodes.py | 11 +- lib/vast/Tokenizer.py | 64 +- lib/vast/Unparser.py | 21 + lib/vast/__init__.py | 13 +- 10 files changed, 10283 insertions(+), 10041 deletions(-) diff --git a/lib/core/Handles/HandlesAssign.py b/lib/core/Handles/HandlesAssign.py index e5134d6..8ab1fc9 100644 --- a/lib/core/Handles/HandlesAssign.py +++ b/lib/core/Handles/HandlesAssign.py @@ -1,1623 +1,1625 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: - from lib.core.translator import Translator -import vast as ast -import sys -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta -from lib.core.VLogger import get_logger as _vlog -from lib.core.ConstEvaluator import ConstEvaluator, EvalContext -from lib.constants.config import mode as _config_mode -from lib.includes import t -from lib.includes.t import CTypeRegistry -from lib.core.SymbolUtils import IsTModule, ParseListAnnotation, AnnotationContainsName - -class AssignHandle(BaseHandle): - def __init__(self, translator: "Translator") -> None: - super().__init__(translator) - self._CurrentCpythonObjectClass: str | None = None - - @staticmethod - def _DeclaredClassBlocksOverride(Gen: Any, VarName: str) -> bool: - """该变量是否**已由类型注解声明**了类名(是 ⇒ 本次赋值不得改掉它的类)。 - - 判据(正向事实):`Gen.var_annotated_class[VarName]` 由 HandlesAnnAssign 在 - **带注解的声明完成时**登记(取注解解析的终值)。有它就意味着这个变量的类型是 - **声明**出来的 ⇒ 赋值只能把右值**转换进**声明类型,不能反过来把变量重新定型。 - - 为什么要这条:`node: rawptr[AST] = Name(...)` 里右值构造调用推出的是 `Name`, - 若覆盖声明的 `AST`,下一处 `node = `(函数返回类型 AST)就变成被禁止的 - **向下转型** —— 现场 includes/ast/parser.vp:1919,TPV 报 - "指针隐式转指针已禁止:源类型 %AST* → 目标类型 %Name*"(本判据是两侧同源的唯一真源)。 - """ - if not VarName: - return False - return VarName in (getattr(Gen, 'var_annotated_class', None) or {}) - - @staticmethod - def _contains_identified_struct(var_type: ir.Type) -> bool: - if isinstance(var_type, ir.IdentifiedStructType): - return True - if isinstance(var_type, ir.ArrayType): - return AssignHandle._contains_identified_struct(var_type.element) - if isinstance(var_type, ir.PointerType): - return AssignHandle._contains_identified_struct(var_type.pointee) - if isinstance(var_type, ir.BaseStructType): - if var_type.elements: - return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements) - return False - - def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None, - ConstWidthHint: int | None = None) -> ir.Value | None: - return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint) - - def _HandleAssignLlvm(self, Node: ast.Assign) -> None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - Gen._set_node_info(Node, "Assign") - if len(Node.targets) == 1: - Target: ast.expr = Node.targets[0] - if isinstance(Target, ast.Name): - VarName: str = Target.id - if Gen._IsVarConst(VarName): - _vlog().error(Gen._ConstAssignMessage("赋值", VarName)) - sys.exit(1) - if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and IsTModule(Node.value.value.id, self.Trans.SymbolTable): - AttrName: str = Node.value.attr - Gen.var_type_info[VarName] = {'type': 't_type', 'name': AttrName} - Gen.var_type_assignments.setdefault(VarName, []).append({'type': 't_type', 'name': AttrName}) - c_type_name: str = '' - resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(AttrName) - if resolved is not None: - ctype_cls: type = resolved[0] - ptr_level: int = resolved[1] - c_type_name = CTypeRegistry.CTypeToName(ctype_cls) - if ptr_level > 0 and c_type_name: - c_type_name = c_type_name + ' *' - if not c_type_name: - if AttrName == 'CPtr': - c_type_name = 'void *' - is_unsigned: bool = Gen._is_type_unsigned(c_type_name) if c_type_name else False - fmt_str: str = "%u\n" if is_unsigned else "%d\n" - fmt_ptr: ir.Value = Gen._create_string_global(fmt_str) - fmt_var_name: str = f"__{VarName}_fmt" - if fmt_var_name not in Gen.variables: - fmt_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name) - Gen.variables[fmt_var_name] = fmt_alloca - Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name]) - kind_var_name: str = f"__{VarName}_kind" - if kind_var_name not in Gen.variables: - kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=kind_var_name) - Gen.variables[kind_var_name] = kind_alloca - Gen.builder.store(ir.Constant(ir.IntType(32), 0), Gen.variables[kind_var_name]) - if VarName not in Gen.variables: - dummy_var: ir.AllocaInstr = Gen._alloca(ir.IntType(8), name=VarName) - Gen.variables[VarName] = dummy_var - return - if isinstance(Node.value, ast.Name) and Node.value.id in Gen.structs: - ClassName: str = Node.value.id - Gen.var_type_info[VarName] = {'type': 'class_cast', 'name': ClassName} - Gen.var_type_assignments.setdefault(VarName, []).append({'type': 'class_cast', 'name': ClassName}) - fmt_str: str = "%p\n" - fmt_ptr: ir.Value = Gen._create_string_global(fmt_str) - fmt_var_name: str = f"__{VarName}_fmt" - if fmt_var_name not in Gen.variables: - fmt_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name) - Gen.variables[fmt_var_name] = fmt_alloca - Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name]) - kind_var_name: str = f"__{VarName}_kind" - if kind_var_name not in Gen.variables: - kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=kind_var_name) - Gen.variables[kind_var_name] = kind_alloca - Gen.builder.store(ir.Constant(ir.IntType(32), 1), Gen.variables[kind_var_name]) - if VarName not in Gen.variables: - dummy_var: ir.AllocaInstr = Gen._alloca(ir.IntType(8), name=VarName) - Gen.variables[VarName] = dummy_var - return - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == 'va_list': - va_list_ptr: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(8).as_pointer(), name="va_list") - Gen.variables[VarName] = va_list_ptr - return - IsStructCtor: bool = (isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) - and Node.value.func.id in Gen.structs) - if IsStructCtor: - ClassName: str = Node.value.func.id - StructType: ir.Type = Gen.structs[ClassName] - - IsUnion: bool = False - TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(ClassName) - if TypeInfo and TypeInfo.IsUnion: - IsUnion = True - - if IsUnion: - Value: ir.Value | None = self.HandleExprLlvm(Node.value) - if Value: - var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) - Gen._store(Value, var) - Gen.variables[VarName] = var - return - - ExistingPtr: ir.Value | None = None - # BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明) - if (VarName in Gen.variables and VarName not in Gen.global_vars - and isinstance(Gen.variables[VarName], ir.GlobalVariable)): - del Gen.variables[VarName] - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: ir.Value = Gen.variables[VarName] - if isinstance(VarPtr.type.pointee, ir.PointerType): - ExistingPtr = VarPtr - elif VarName in Gen._reg_values: - OldVal: ir.Value = Gen._reg_values[VarName] - if isinstance(OldVal.type, ir.PointerType): - var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) - Gen._store(OldVal, var) - Gen.variables[VarName] = var - del Gen._reg_values[VarName] - ExistingPtr = var - elif VarName in Gen._direct_values: - OldVal: ir.Value = Gen._direct_values.pop(VarName) - if isinstance(OldVal.type, ir.PointerType): - var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) - Gen._store(OldVal, var) - Gen.variables[VarName] = var - ExistingPtr = var - if ExistingPtr: - pointee: ir.Type = ExistingPtr.type.pointee - # 仅当 alloca 直接存储结构体值(pointee 不是指针)时才 in-place 初始化。 - # 当 pointee 是 PointerType 时,alloca 存储的是堆对象指针: - # - 若 AnnAssign 分支未执行,alloca 内容是未初始化的栈垃圾 - # (可能恰好等于 pool 地址),_InitStructAtPtr 会向垃圾地址写字段 → 内存损坏 - # - 即使指针有效,node = Class(...) 的语义也是创建新对象并更新指针, - # 而非在旧对象位置 in-place 重初始化 - # 此时必须 fall through 到通用路径 HandleExprLlvm → _HandleClassNewLlvm - # 以正确调用 __new__/__before_init__/__init__ 构造新对象。 - if not isinstance(pointee, ir.PointerType) and pointee == StructType: - self._InitStructAtPtr(Node.value, ClassName, ExistingPtr) - return - if isinstance(Node.value, ast.Attribute): - enum_type_name: str | None = self._CheckEnumMemberAssignment(Node.value) - if enum_type_name: - Gen.var_type_info[VarName] = {'type': 'enum', 'name': enum_type_name} - var_type_for_list: ir.Type | None = None - if isinstance(Node.value, ast.List) and VarName in Gen.var_type_info: - type_info: dict[str, str] = Gen.var_type_info[VarName] - if type_info.get('type') == 'CArray': - var_type_for_list = type_info.get('full_type') - Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=var_type_for_list) - # 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型 - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): - CalledName: str = Node.value.func.id - if CalledName in Gen._closure_return_types: - Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName] - if not Value: - # 防静默:右侧是属性链(如 blk = func.Blocks.Head)却求值失败时 - # 必须报错,禁止跳过 store 留下未初始化变量(随机崩溃)。 - if isinstance(Node.value, ast.Attribute): - self._RaiseUnresolvedExpr("赋值", Node.value, Gen) - return - if isinstance(Value.type, ir.VoidType): - return - Gen._UnregisterTempPtr(Value) - # 【右值形态判定】只有"取地址形态"的右值才需要自动解一层引用:Name/Attribute/ - # Subscript 的求值结果是**存储地址**(变量 alloca / 字段地址 /"对象即指针"), - # 赋给指针变量时必须 load 出内层指针。而 Call(显式 cast 如 `str*(addr)`、 - # `t.CPtr(x)`,以及构造/普通函数调用)与其它表达式给出的是**值本身**, - # 再 load 会多解一层 → 静默取到野指针(现场 includes/argparse.vp 的 - # `entry = str*(uint64(argv) + idx*8)`:i8** 右值被多解一次,entry 变成 - # argv[idx] 本身,随后 entry[0] 去读字符串头 8 字节 → ASan access-violation)。 - _rhs_addr_form: bool = isinstance(Node.value, (ast.Name, ast.Attribute, ast.Subscript)) - # 大端局部变量:赋值时 bswap - Value = Gen._apply_bswap_if_big(Value, Gen.local_var_byteorders.get(VarName, ""), f"bswap_assign_{VarName}") - if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - found_class: str | None = None - found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(Value.type.pointee) - if found: - CN: str = found[0] - ST: ir.Type = found[1] - found_class = CN - # 同类特化覆盖保护:简单赋值(如 dbody = dbody2)会经 - # find_struct_by_pointee 拿到带 sha1 前缀的 IR 特化名 - # (list[f467...ASTptr]),覆盖声明注解解析出的裸名 - # (list[ast.ASTptr])→ 方法调用(dbody.get)按该名查 - # SymbolTable/functions 时 miss → fallback 到 list[int].get - # → i32 返回值被 zext+inttoptr 当指针 → 崩溃。因此变量已有 - # 同 base 的声明类型时不覆盖(保留声明名,方法解析走声明名)。 - _prev_cls: str | None = Gen.var_struct_class.get(VarName) - if self._DeclaredClassBlocksOverride(Gen, VarName): - # 注解声明的类是权威类型(见 _DeclaredClassBlocksOverride) - pass - elif _prev_cls and '[' in _prev_cls and '[' in CN and _prev_cls.split('[')[0] == CN.split('[')[0]: - pass - else: - Gen.var_struct_class[VarName] = CN - Gen._var_to_heap_ptr[VarName] = Value - if found_class is None and isinstance(Value.type.pointee, ir.IdentifiedStructType): - pointee_name: str = Value.type.pointee.name - pointee_short: str = Gen._extract_short_name(pointee_name) - best_elem_count: int = 0 - for CN, ST in Gen.structs.items(): - if isinstance(ST, ir.IdentifiedStructType): - st_short: str = Gen._extract_short_name(ST.name) - if st_short == pointee_short: - elem_count: int = len(ST.elements) if ST.elements else 0 - if elem_count > best_elem_count: - found_class = CN - best_elem_count = elem_count - if found_class: - # 同类特化覆盖保护(见上方 L189 注释):短名匹配的 - # sha1 前缀变体不覆盖声明注解类型 - _prev_cls2: str | None = Gen.var_struct_class.get(VarName) - if self._DeclaredClassBlocksOverride(Gen, VarName): - # 注解声明的类是权威类型(见 _DeclaredClassBlocksOverride) - pass - elif _prev_cls2 and '[' in _prev_cls2 and '[' in found_class and _prev_cls2.split('[')[0] == found_class.split('[')[0]: - pass - else: - Gen.var_struct_class[VarName] = found_class - Gen._var_to_heap_ptr[VarName] = Value - # 泛型类特化触发:返回类型是泛型类特化(如 list[str])但结构体尚未创建, - # 或虽然找到了结构体但它是 opaque(来自 stub 声明,尚未发射方法体) - _fd_struct_opaque: bool = False - if found_class and found_class in Gen.structs: - _fd_st: ir.IdentifiedStructType = Gen.structs[found_class] - if isinstance(_fd_st, ir.IdentifiedStructType) and (_fd_st.elements is None or len(_fd_st.elements) == 0): - _fd_struct_opaque = True - if (found_class is None or _fd_struct_opaque) and isinstance(Value.type.pointee, ir.IdentifiedStructType): - pn: str = Value.type.pointee.name - short_pn: str = pn - if '.' in pn: - pn_parts: list[str] = pn.split('.', 1) - if len(pn_parts[0]) >= 8 and all(c in '0123456789abcdef' for c in pn_parts[0]): - short_pn = pn_parts[1] - if '[' in short_pn and short_pn.endswith(']'): - gc_base: str = short_pn.split('[')[0] - gc_args_raw: str = short_pn[len(gc_base):] - gc_type_args: list[str] = [] - for gc_arg_part in gc_args_raw.split(']['): - gc_arg_part = gc_arg_part.strip('[]') - if gc_arg_part: - gc_type_args.append(gc_arg_part) - if (gc_type_args - and hasattr(self.Trans.ClassHandler, '_generic_class_templates') - and gc_base in self.Trans.ClassHandler._generic_class_templates): - gc_type_names: list[str] = [] - for gc_ta in gc_type_args: - gc_tn: str = gc_ta - if gc_ta == 'int': - gc_tn = 'CInt' - elif gc_ta == 'double': - gc_tn = 'CDouble' - elif gc_ta == 'float': - gc_tn = 'CFloat' - elif gc_ta == 'char': - gc_tn = 'CChar' - elif gc_ta == 'bool': - gc_tn = 'CBool' - gc_type_names.append(gc_tn) - gc_spec: str | None = self.Trans.ClassHandler._specialize_generic_class( - gc_base, gc_type_args, Gen, type_names=gc_type_names) - if gc_spec and gc_spec in Gen.structs: - found_class = gc_spec - # 同类特化覆盖保护(见上方 L189 注释): - # gc_spec 与声明注解类型同 base 时不覆盖 - _prev_cls3: str | None = Gen.var_struct_class.get(VarName) - if _prev_cls3 and '[' in _prev_cls3 and '[' in gc_spec and _prev_cls3.split('[')[0] == gc_spec.split('[')[0]: - pass - else: - Gen.var_struct_class[VarName] = gc_spec - Gen._var_to_heap_ptr[VarName] = Value - else: - if VarName in ('mctx', 's1ctx', 's2ctx', 's5ctx'): - pass - # 【联合返回注解的被指类恢复】RHS 是调用、且值的 LLVM 类型是**通用指针** - # (i8*/i8**,pointee 不是结构体)时,上面那块类名推断整块失效 —— 现场 - # `parts = s1.split(',')`:`split` 的返回注解 `list[str]*` ≡ - # `list[str] | t.CPtr`(只读探针确认二者 AST 逐字同形)被映射成 i8* - # ⇒ 类名丢失 ⇒ 随后 `parts[0]` 不走 `list[str].__getitem__` 而按通用 - # 指针/字节索引 ⇒ 得到整数 ⇒ 赋给 `str` 时报"整数隐式转指针已禁止" - # (真因在 4 行之前,报文完全指不到)。此处从被调者的**返回注解** - # 取回被指类名(判据与 HandlesAnnAssign 的同类恢复同源)。 - if (not Gen.var_struct_class.get(VarName) - and isinstance(Node.value, ast.Call) - and isinstance(Value.type, ir.PointerType) - and not isinstance(Value.type.pointee, - (ir.IdentifiedStructType, ir.LiteralStructType))): - _rc: str | None = self.Trans.ExprCallHandle._CallReturnClass(Node.value, Gen) - if _rc: - Gen.var_struct_class[VarName] = _rc - if getattr(Gen, 'global_struct_class', None) is not None: - Gen.global_struct_class[VarName] = _rc - Gen._var_to_heap_ptr[VarName] = Value - if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - # BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明) - if (VarName in Gen.variables and VarName not in Gen.global_vars - and isinstance(Gen.variables[VarName], ir.GlobalVariable)): - del Gen.variables[VarName] - if VarName in Gen.variables and Gen.variables[VarName] is not None: - Gen._store(Value, Gen.variables[VarName]) - else: - var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) - Gen._store(Value, var) - Gen.variables[VarName] = var - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): - call_func_name: str = Node.value.func.id - if call_func_name in Gen.var_type_info: - type_info: dict[str, str] = Gen.var_type_info[call_func_name] - if type_info['type'] in ('t_type', 'class_cast'): - src_fmt_var: str = f"__{call_func_name}_fmt" - dst_fmt_var: str = f"__{VarName}_fmt" - if src_fmt_var in Gen.variables: - if dst_fmt_var not in Gen.variables: - dst_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var) - Gen.variables[dst_fmt_var] = dst_alloca - src_fmt_ptr: ir.Value = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}") - Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var]) - src_kind_var: str = f"__{call_func_name}_kind" - dst_kind_var: str = f"__{VarName}_kind" - if src_kind_var in Gen.variables: - if dst_kind_var not in Gen.variables: - dst_kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var) - Gen.variables[dst_kind_var] = dst_kind_alloca - src_kind_val: ir.Value = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}") - Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var]) - Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name} - return - if VarName in Gen.global_vars: - # 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局 - # (注解无初始值的全局变量),此处自动创建零初始化全局变量 - GVar: ir.GlobalVariable - if VarName not in Gen.module.globals: - _gv_type: ir.Type = Value.type if isinstance(Value.type, (ir.IntType, ir.PointerType)) else ir.IntType(64) - GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName) - GVar.initializer = ir.Constant(_gv_type, None) if isinstance(_gv_type, ir.PointerType) else ir.Constant(_gv_type, 0) - else: - GVar = Gen.module.globals[VarName] - # 栈地址逃逸:`G = &local`,或 `G = arr`(G 为指针时的数组退让)——禁止把本帧栈 - # 地址写入模块级全局(函数返回后悬垂);目标为同型聚合时按值拷贝,不在此列。 - TgtTy1: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None - if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy1): - from lib.core.LLVMCG.FuncGen import LifetimeError - raise LifetimeError( - f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName}," - f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配" - ) - Gen._store(Value, GVar) - Gen.variables[VarName] = GVar - if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(Value.type.pointee) - if found and not self._DeclaredClassBlocksOverride(Gen, VarName): - CN: str = found[0] - ST: ir.Type = found[1] - Gen.var_struct_class[VarName] = CN - return - if VarName in Gen._reg_values: - OldVal: ir.Value = Gen._reg_values[VarName] - var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) - Gen._store(OldVal, var) - Gen.variables[VarName] = var - del Gen._reg_values[VarName] - if VarName in Gen._direct_values: - OldVal: ir.Value = Gen._direct_values.pop(VarName) - var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) - Gen._store(OldVal, var) - Gen.variables[VarName] = var - # BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明)。 - # Python 语义:函数内无 global 声明时赋值应创建局部变量, - # 而非修改被读取缓存的全局变量。清除后走 L339 的正确逻辑。 - if (VarName in Gen.variables and VarName not in Gen.global_vars - and isinstance(Gen.variables[VarName], ir.GlobalVariable)): - del Gen.variables[VarName] - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: ir.Value = Gen.variables[VarName] - TargetType: ir.Type | None = VarPtr.type.pointee if isinstance(VarPtr.type, ir.PointerType) else None - types_differ: bool - try: - types_differ = TargetType and Value.type != TargetType - except AssertionError: - types_differ = False - if _rhs_addr_form and isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType): - LoadedValue: ir.Value = Gen._load(Value, name=f"Load_{VarName}") - Loaded_differ: bool - try: - Loaded_differ = TargetType and LoadedValue.type != TargetType - except AssertionError: - Loaded_differ = False - if Loaded_differ: - try: - Coerced: ir.Value | None = Gen._coerce_value(LoadedValue, TargetType, Node.value) - if Coerced is not None: - LoadedValue = Coerced - except AssertionError: - pass - Gen._store(LoadedValue, VarPtr, Node=Node.value) - elif types_differ: - try: - Coerced: ir.Value | None = Gen._coerce_value(Value, TargetType, Node.value) - if Coerced is not None: - Gen._store(Coerced, VarPtr, Node=Node.value) - else: - Gen._store(Value, VarPtr, Node=Node.value) - except AssertionError: - Gen._store(Value, VarPtr, Node=Node.value) - else: - Gen._store(Value, VarPtr, Node=Node.value) - if VarName not in Gen.variables: - # 检查是否为模块级全局变量 - # main 函数代表模块级作用域,直接修改全局变量 - # 其他函数中,未声明 global 且未先读取的赋值创建局部变量(Python 作用域语义) - # 先读取过的变量已通过 HandleExprLlvm 加入 Gen.variables,会走 L289 分支存储到全局 - CurrentFuncName: str = getattr(Gen.func, 'name', '') if Gen.func else '' - IsMainScope: bool = CurrentFuncName == 'main' or CurrentFuncName.endswith('.main') - if VarName in Gen.module.globals and (VarName in Gen.global_vars or IsMainScope): - GVar: ir.GlobalVariable = Gen.module.globals[VarName] - # 栈地址逃逸:main 作用域下的全局写入同口径(含 `G = arr` 的数组退让)。 - TgtTy2: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None - if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy2): - from lib.core.LLVMCG.FuncGen import LifetimeError - raise LifetimeError( - f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName}," - f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配" - ) - Gen._store(Value, GVar, Node=Node.value) - Gen.variables[VarName] = GVar - elif _rhs_addr_form and isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType): - LoadedValue: ir.Value = Gen._load(Value, name=f"Load_{VarName}") - var: ir.AllocaInstr = Gen._allocaEntry(LoadedValue.type, name=VarName) - Gen._store(LoadedValue, var, Node=Node.value) - Gen.variables[VarName] = var - else: - var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) - Gen._store(Value, var) - Gen.variables[VarName] = var - is_u: bool = Gen._check_node_unsigned(Node.value) - Gen._record_var_signedness(VarName, 'unsigned int' if is_u else 'int') - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): - call_func_name: str = Node.value.func.id - if call_func_name in Gen.var_type_info: - type_info: dict[str, str] = Gen.var_type_info[call_func_name] - if type_info['type'] in ('t_type', 'class_cast'): - src_fmt_var: str = f"__{call_func_name}_fmt" - dst_fmt_var: str = f"__{VarName}_fmt" - if src_fmt_var in Gen.variables: - if dst_fmt_var not in Gen.variables: - dst_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var) - Gen.variables[dst_fmt_var] = dst_alloca - src_fmt_ptr: ir.Value = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}") - Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var]) - src_kind_var: str = f"__{call_func_name}_kind" - dst_kind_var: str = f"__{VarName}_kind" - if src_kind_var in Gen.variables: - if dst_kind_var not in Gen.variables: - dst_kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var) - Gen.variables[dst_kind_var] = dst_kind_alloca - src_kind_val: ir.Value = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}") - Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var]) - Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name} - else: - Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name} - elif isinstance(Target, ast.Attribute): - TargetType: ir.Type | None = None - AttrName: str = Target.attr - VarName: str | None = None - if isinstance(Target.value, ast.Name): - VarName = Target.value.id - if VarName: - ClassName: str | None = Gen.var_struct_class.get(VarName) - if not ClassName and Gen.global_struct_class: - ClassName = Gen.global_struct_class.get(VarName) - if ClassName: - Gen.var_struct_class[VarName] = ClassName - if not ClassName: - for CN in Gen.structs: - if VarName == CN.lower() or VarName.startswith(CN): - ClassName = CN - break - if ClassName and ClassName in Gen.class_members: - for m_name, m_type in Gen.class_members[ClassName]: - if m_name == AttrName: - TargetType = m_type - break - Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType) - if not Value: - if isinstance(Node.value, ast.Attribute): - self._RaiseUnresolvedExpr("属性写入", Node.value, Gen) - return - self._HandleAttributeStoreLlvm(Target, Value) - elif isinstance(Target, ast.Subscript): - TargetType: ir.Type | None = None - SubVarName: str | None = None - if isinstance(Target.value, ast.Name): - SubVarName = Target.value.id - if SubVarName in Gen.var_struct_class: - ClassName: str = Gen.var_struct_class[SubVarName] - if ClassName in Gen.class_members: - for m_name, m_type in Gen.class_members[ClassName]: - if m_name == SubVarName: - if isinstance(m_type, ir.PointerType): - pointee: ir.Type = m_type.pointee - if isinstance(pointee, ir.ArrayType): - TargetType = pointee.element - elif isinstance(m_type, ir.ArrayType): - TargetType = m_type.element - break - if not TargetType and isinstance(Target.value, ast.Name): - SubVarName = Target.value.id - var_val: ir.Value | None = Gen.variables.get(SubVarName) - if var_val is None and SubVarName in Gen._direct_values: - var_val = Gen._direct_values[SubVarName] - if var_val is not None: - var_type: ir.Type = var_val.type - if isinstance(var_type, ir.PointerType): - pointee: ir.Type = var_type.pointee - if isinstance(pointee, ir.ArrayType): - TargetType = pointee.element - elif isinstance(var_type, ir.ArrayType): - TargetType = var_type.element - Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType) - if not Value: - if isinstance(Node.value, ast.Attribute): - self._RaiseUnresolvedExpr("下标写入", Node.value, Gen) - return - self._HandleSubscriptStoreLlvm(Target, Value) - elif isinstance(Target, ast.UnaryOp): - # 解引用写入 `*p = v`(与读侧同一节点形态)。 - # 解析链:语句级 Assign 走 vast_c.c ParseSimpleStmt 的 - # ParseExprList + SetStoreContext;`*p` 由 ParseFactor 产出 - # UnaryOp(op=Deref)(与 `*p` 读侧完全同形),而 SetStoreContext 只改 - # Attribute/Subscript/Name/Tuple/List/Starred 的 ctx,**不改 UnaryOp** - # ⇒ 赋值目标就是 UnaryOp(Deref)。 - # 此前本分派(Name/Attribute/Subscript/Tuple)无 UnaryOp 分支且无兜底 - # ⇒ 该语句被整条静默丢弃(既不落盘也不报错):memhub 的块头 - # `(order<<1)|1`、空闲链 next、canary 三处写入全不发射 ⇒ 块头恒为 0 - # (free 被拒)、free_lists 链读到未初始化 0xBE、池单调漏满 ⇒ 1 GiB OOM。 - # `&x`/`-x`/`~x` 作目标不是合法左值,故除 Deref 外一律 fail-fast。 - if not isinstance(Target.op, ast.Deref): - BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Target.op) - raise Exception( - f"赋值目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)" - f"{Gen._get_node_info()}" - ) - Ptr: ir.Value | None = self.HandleExprLlvm(Target.operand) - if Ptr is None: - self._RaiseUnresolvedExpr("解引用写入", Target.operand, Gen) - return - if not isinstance(Ptr.type, ir.PointerType): - raise Exception( - f"解引用赋值 '*' 的操作数必须是指针类型(类型推断退化," - f"实际 {Ptr.type}){Gen._get_node_info()}" - ) - # `*a = *b` is the c.Load form: copy the raw bytes (width = target - # pointee), never convert the value. Without it a bit-pun such as - # `*&bits = *&dbl` degrades to fptosi/trunc and loses the IEEE bits. - if isinstance(Node.value, ast.UnaryOp) and isinstance(Node.value.op, ast.Deref): - SrcPtr: ir.Value | None = self.HandleExprLlvm(Node.value.operand) - if SrcPtr is None: - self._RaiseUnresolvedExpr("解引用写入", Node.value.operand, Gen) - return - if not isinstance(SrcPtr.type, ir.PointerType): - raise Exception( - f"解引用赋值右侧 '*' 的操作数必须是指针类型(类型推断退化," - f"实际 {SrcPtr.type}){Gen._get_node_info()}" - ) - if self.Trans.CSpecialCallHandle.EmitRawCopyLlvm(Ptr, SrcPtr, Gen): - return - DerefValue: ir.Value | None = self.HandleExprLlvm(Node.value) - if DerefValue is None: - self._RaiseUnresolvedExpr("解引用写入", Node.value, Gen) - return - # 方案1(DerefAs 的安全语义):`*p = v` 只在本会被"指针↔pointee 类型不匹配" - # 拒绝时才改用 `*(T*(p)) = v`(T = v 的类型):把 **指针** p bitcast 成 T* 再按值 - # 存 v。这正是 c.DerefAs 期望的写法(旧 DerefAs 的 `*(intptr_t*(p))=(intptr_t)v` - # 把"值"bitcast 成指针大小整数,是错实现)。标量宽度等其余情形维持原 - # `pointee(p)` 语义不变(不引入标量宽度漂移)。 - PtrPointee: ir.Type | None = Ptr.type.pointee if isinstance(Ptr.type, ir.PointerType) else None - if (isinstance(DerefValue.type, ir.PointerType) and PtrPointee is not None - and PtrPointee != DerefValue.type): - Ptr = Gen.builder.bitcast(Ptr, ir.PointerType(DerefValue.type), name="deref_store_as") - self._StoreWithCoerce(DerefValue, Ptr, Node.value) +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator +import vast as ast +import sys +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta +from lib.core.VLogger import get_logger as _vlog +from lib.core.ConstEvaluator import ConstEvaluator, EvalContext +from lib.constants.config import mode as _config_mode +from lib.includes import t +from lib.includes.t import CTypeRegistry +from lib.core.SymbolUtils import IsTModule, ParseListAnnotation, AnnotationContainsName + +class AssignHandle(BaseHandle): + def __init__(self, translator: "Translator") -> None: + super().__init__(translator) + self._CurrentCpythonObjectClass: str | None = None + + @staticmethod + def _DeclaredClassBlocksOverride(Gen: Any, VarName: str) -> bool: + """该变量是否**已由类型注解声明**了类名(是 ⇒ 本次赋值不得改掉它的类)。 + + 判据(正向事实):`Gen.var_annotated_class[VarName]` 由 HandlesAnnAssign 在 + **带注解的声明完成时**登记(取注解解析的终值)。有它就意味着这个变量的类型是 + **声明**出来的 ⇒ 赋值只能把右值**转换进**声明类型,不能反过来把变量重新定型。 + + 为什么要这条:`node: rawptr[AST] = Name(...)` 里右值构造调用推出的是 `Name`, + 若覆盖声明的 `AST`,下一处 `node = `(函数返回类型 AST)就变成被禁止的 + **向下转型** —— 现场 includes/ast/parser.vp:1919,VPC 报 + "指针隐式转指针已禁止:源类型 %AST* → 目标类型 %Name*"(本判据是两侧同源的唯一真源)。 + """ + if not VarName: + return False + return VarName in (getattr(Gen, 'var_annotated_class', None) or {}) + + @staticmethod + def _contains_identified_struct(var_type: ir.Type) -> bool: + if isinstance(var_type, ir.IdentifiedStructType): + return True + if isinstance(var_type, ir.ArrayType): + return AssignHandle._contains_identified_struct(var_type.element) + if isinstance(var_type, ir.PointerType): + return AssignHandle._contains_identified_struct(var_type.pointee) + if isinstance(var_type, ir.BaseStructType): + if var_type.elements: + return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements) + return False + + def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None, + ConstWidthHint: int | None = None) -> ir.Value | None: + return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint) + + def _HandleAssignLlvm(self, Node: ast.Assign) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + Gen._set_node_info(Node, "Assign") + if len(Node.targets) == 1: + Target: ast.expr = Node.targets[0] + if isinstance(Target, ast.Name): + VarName: str = Target.id + if Gen._IsVarConst(VarName): + _vlog().error(Gen._ConstAssignMessage("赋值", VarName)) + sys.exit(1) + if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and IsTModule(Node.value.value.id, self.Trans.SymbolTable): + AttrName: str = Node.value.attr + Gen.var_type_info[VarName] = {'type': 't_type', 'name': AttrName} + Gen.var_type_assignments.setdefault(VarName, []).append({'type': 't_type', 'name': AttrName}) + c_type_name: str = '' + resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(AttrName) + if resolved is not None: + ctype_cls: type = resolved[0] + ptr_level: int = resolved[1] + c_type_name = CTypeRegistry.CTypeToName(ctype_cls) + if ptr_level > 0 and c_type_name: + c_type_name = c_type_name + ' *' + if not c_type_name: + if AttrName == 'CPtr': + c_type_name = 'void *' + is_unsigned: bool = Gen._is_type_unsigned(c_type_name) if c_type_name else False + fmt_str: str = "%u\n" if is_unsigned else "%d\n" + fmt_ptr: ir.Value = Gen._create_string_global(fmt_str) + fmt_var_name: str = f"__{VarName}_fmt" + if fmt_var_name not in Gen.variables: + fmt_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name) + Gen.variables[fmt_var_name] = fmt_alloca + Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name]) + kind_var_name: str = f"__{VarName}_kind" + if kind_var_name not in Gen.variables: + kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=kind_var_name) + Gen.variables[kind_var_name] = kind_alloca + Gen.builder.store(ir.Constant(ir.IntType(32), 0), Gen.variables[kind_var_name]) + if VarName not in Gen.variables: + dummy_var: ir.AllocaInstr = Gen._alloca(ir.IntType(8), name=VarName) + Gen.variables[VarName] = dummy_var + return + if isinstance(Node.value, ast.Name) and Node.value.id in Gen.structs: + ClassName: str = Node.value.id + Gen.var_type_info[VarName] = {'type': 'class_cast', 'name': ClassName} + Gen.var_type_assignments.setdefault(VarName, []).append({'type': 'class_cast', 'name': ClassName}) + fmt_str: str = "%p\n" + fmt_ptr: ir.Value = Gen._create_string_global(fmt_str) + fmt_var_name: str = f"__{VarName}_fmt" + if fmt_var_name not in Gen.variables: + fmt_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name) + Gen.variables[fmt_var_name] = fmt_alloca + Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name]) + kind_var_name: str = f"__{VarName}_kind" + if kind_var_name not in Gen.variables: + kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=kind_var_name) + Gen.variables[kind_var_name] = kind_alloca + Gen.builder.store(ir.Constant(ir.IntType(32), 1), Gen.variables[kind_var_name]) + if VarName not in Gen.variables: + dummy_var: ir.AllocaInstr = Gen._alloca(ir.IntType(8), name=VarName) + Gen.variables[VarName] = dummy_var + return + if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == 'va_list': + va_list_ptr: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(8).as_pointer(), name="va_list") + Gen.variables[VarName] = va_list_ptr + return + IsStructCtor: bool = (isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) + and Node.value.func.id in Gen.structs) + if IsStructCtor: + ClassName: str = Node.value.func.id + StructType: ir.Type = Gen.structs[ClassName] + + IsUnion: bool = False + TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(ClassName) + if TypeInfo and TypeInfo.IsUnion: + IsUnion = True + + if IsUnion: + Value: ir.Value | None = self.HandleExprLlvm(Node.value) + if Value: + var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) + Gen._store(Value, var) + Gen.variables[VarName] = var + return + + ExistingPtr: ir.Value | None = None + # BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明) + if (VarName in Gen.variables and VarName not in Gen.global_vars + and isinstance(Gen.variables[VarName], ir.GlobalVariable)): + del Gen.variables[VarName] + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: ir.Value = Gen.variables[VarName] + if isinstance(VarPtr.type.pointee, ir.PointerType): + ExistingPtr = VarPtr + elif VarName in Gen._reg_values: + OldVal: ir.Value = Gen._reg_values[VarName] + if isinstance(OldVal.type, ir.PointerType): + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) + Gen._store(OldVal, var) + Gen.variables[VarName] = var + del Gen._reg_values[VarName] + ExistingPtr = var + elif VarName in Gen._direct_values: + OldVal: ir.Value = Gen._direct_values.pop(VarName) + if isinstance(OldVal.type, ir.PointerType): + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) + Gen._store(OldVal, var) + Gen.variables[VarName] = var + ExistingPtr = var + if ExistingPtr: + pointee: ir.Type = ExistingPtr.type.pointee + # 仅当 alloca 直接存储结构体值(pointee 不是指针)时才 in-place 初始化。 + # 当 pointee 是 PointerType 时,alloca 存储的是堆对象指针: + # - 若 AnnAssign 分支未执行,alloca 内容是未初始化的栈垃圾 + # (可能恰好等于 pool 地址),_InitStructAtPtr 会向垃圾地址写字段 → 内存损坏 + # - 即使指针有效,node = Class(...) 的语义也是创建新对象并更新指针, + # 而非在旧对象位置 in-place 重初始化 + # 此时必须 fall through 到通用路径 HandleExprLlvm → _HandleClassNewLlvm + # 以正确调用 __new__/__before_init__/__init__ 构造新对象。 + if not isinstance(pointee, ir.PointerType) and pointee == StructType: + self._InitStructAtPtr(Node.value, ClassName, ExistingPtr) + return + if isinstance(Node.value, ast.Attribute): + enum_type_name: str | None = self._CheckEnumMemberAssignment(Node.value) + if enum_type_name: + Gen.var_type_info[VarName] = {'type': 'enum', 'name': enum_type_name} + var_type_for_list: ir.Type | None = None + if isinstance(Node.value, ast.List) and VarName in Gen.var_type_info: + type_info: dict[str, str] = Gen.var_type_info[VarName] + if type_info.get('type') == 'CArray': + var_type_for_list = type_info.get('full_type') + Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=var_type_for_list) + # 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型 + if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): + CalledName: str = Node.value.func.id + if CalledName in Gen._closure_return_types: + Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName] + if not Value: + # 防静默:右侧是属性链(如 blk = func.Blocks.Head)却求值失败时 + # 必须报错,禁止跳过 store 留下未初始化变量(随机崩溃)。 + if isinstance(Node.value, ast.Attribute): + self._RaiseUnresolvedExpr("赋值", Node.value, Gen) + return + if isinstance(Value.type, ir.VoidType): + return + Gen._UnregisterTempPtr(Value) + # 【右值形态判定】只有"取地址形态"的右值才需要自动解一层引用:Name/Attribute/ + # Subscript 的求值结果是**存储地址**(变量 alloca / 字段地址 /"对象即指针"), + # 赋给指针变量时必须 load 出内层指针。而 Call(显式 cast 如 `str*(addr)`、 + # `t.CPtr(x)`,以及构造/普通函数调用)与其它表达式给出的是**值本身**, + # 再 load 会多解一层 → 静默取到野指针(现场 includes/argparse.vp 的 + # `entry = str*(uint64(argv) + idx*8)`:i8** 右值被多解一次,entry 变成 + # argv[idx] 本身,随后 entry[0] 去读字符串头 8 字节 → ASan access-violation)。 + _rhs_addr_form: bool = isinstance(Node.value, (ast.Name, ast.Attribute, ast.Subscript)) + # 大端局部变量:赋值时 bswap + Value = Gen._apply_bswap_if_big(Value, Gen.local_var_byteorders.get(VarName, ""), f"bswap_assign_{VarName}") + if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + found_class: str | None = None + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(Value.type.pointee) + if found: + CN: str = found[0] + ST: ir.Type = found[1] + found_class = CN + # 同类特化覆盖保护:简单赋值(如 dbody = dbody2)会经 + # find_struct_by_pointee 拿到带 sha1 前缀的 IR 特化名 + # (list[f467...ASTptr]),覆盖声明注解解析出的裸名 + # (list[ast.ASTptr])→ 方法调用(dbody.get)按该名查 + # SymbolTable/functions 时 miss → fallback 到 list[int].get + # → i32 返回值被 zext+inttoptr 当指针 → 崩溃。因此变量已有 + # 同 base 的声明类型时不覆盖(保留声明名,方法解析走声明名)。 + _prev_cls: str | None = Gen.var_struct_class.get(VarName) + if self._DeclaredClassBlocksOverride(Gen, VarName): + # 注解声明的类是权威类型(见 _DeclaredClassBlocksOverride) + pass + elif _prev_cls and '[' in _prev_cls and '[' in CN and _prev_cls.split('[')[0] == CN.split('[')[0]: + pass + else: + Gen.var_struct_class[VarName] = CN + Gen._var_to_heap_ptr[VarName] = Value + if found_class is None and isinstance(Value.type.pointee, ir.IdentifiedStructType): + pointee_name: str = Value.type.pointee.name + pointee_short: str = Gen._extract_short_name(pointee_name) + best_elem_count: int = 0 + for CN, ST in Gen.structs.items(): + if isinstance(ST, ir.IdentifiedStructType): + st_short: str = Gen._extract_short_name(ST.name) + if st_short == pointee_short: + elem_count: int = len(ST.elements) if ST.elements else 0 + if elem_count > best_elem_count: + found_class = CN + best_elem_count = elem_count + if found_class: + # 同类特化覆盖保护(见上方 L189 注释):短名匹配的 + # sha1 前缀变体不覆盖声明注解类型 + _prev_cls2: str | None = Gen.var_struct_class.get(VarName) + if self._DeclaredClassBlocksOverride(Gen, VarName): + # 注解声明的类是权威类型(见 _DeclaredClassBlocksOverride) + pass + elif _prev_cls2 and '[' in _prev_cls2 and '[' in found_class and _prev_cls2.split('[')[0] == found_class.split('[')[0]: + pass + else: + Gen.var_struct_class[VarName] = found_class + Gen._var_to_heap_ptr[VarName] = Value + # 泛型类特化触发:返回类型是泛型类特化(如 list[str])但结构体尚未创建, + # 或虽然找到了结构体但它是 opaque(来自 stub 声明,尚未发射方法体) + _fd_struct_opaque: bool = False + if found_class and found_class in Gen.structs: + _fd_st: ir.IdentifiedStructType = Gen.structs[found_class] + if isinstance(_fd_st, ir.IdentifiedStructType) and (_fd_st.elements is None or len(_fd_st.elements) == 0): + _fd_struct_opaque = True + if (found_class is None or _fd_struct_opaque) and isinstance(Value.type.pointee, ir.IdentifiedStructType): + pn: str = Value.type.pointee.name + short_pn: str = pn + if '.' in pn: + pn_parts: list[str] = pn.split('.', 1) + if len(pn_parts[0]) >= 8 and all(c in '0123456789abcdef' for c in pn_parts[0]): + short_pn = pn_parts[1] + if '[' in short_pn and short_pn.endswith(']'): + gc_base: str = short_pn.split('[')[0] + gc_args_raw: str = short_pn[len(gc_base):] + gc_type_args: list[str] = [] + for gc_arg_part in gc_args_raw.split(']['): + gc_arg_part = gc_arg_part.strip('[]') + if gc_arg_part: + gc_type_args.append(gc_arg_part) + if (gc_type_args + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and gc_base in self.Trans.ClassHandler._generic_class_templates): + gc_type_names: list[str] = [] + for gc_ta in gc_type_args: + gc_tn: str = gc_ta + if gc_ta == 'int': + gc_tn = 'CInt' + elif gc_ta == 'double': + gc_tn = 'CDouble' + elif gc_ta == 'float': + gc_tn = 'CFloat' + elif gc_ta == 'char': + gc_tn = 'CChar' + elif gc_ta == 'bool': + gc_tn = 'CBool' + gc_type_names.append(gc_tn) + gc_spec: str | None = self.Trans.ClassHandler._specialize_generic_class( + gc_base, gc_type_args, Gen, type_names=gc_type_names) + if gc_spec and gc_spec in Gen.structs: + found_class = gc_spec + # 同类特化覆盖保护(见上方 L189 注释): + # gc_spec 与声明注解类型同 base 时不覆盖 + _prev_cls3: str | None = Gen.var_struct_class.get(VarName) + if _prev_cls3 and '[' in _prev_cls3 and '[' in gc_spec and _prev_cls3.split('[')[0] == gc_spec.split('[')[0]: + pass + else: + Gen.var_struct_class[VarName] = gc_spec + Gen._var_to_heap_ptr[VarName] = Value + else: + if VarName in ('mctx', 's1ctx', 's2ctx', 's5ctx'): + pass + # 【联合返回注解的被指类恢复】RHS 是调用、且值的 LLVM 类型是**通用指针** + # (i8*/i8**,pointee 不是结构体)时,上面那块类名推断整块失效 —— 现场 + # `parts = s1.split(',')`:`split` 的返回注解 `list[str]*` ≡ + # `list[str] | t.CPtr`(只读探针确认二者 AST 逐字同形)被映射成 i8* + # ⇒ 类名丢失 ⇒ 随后 `parts[0]` 不走 `list[str].__getitem__` 而按通用 + # 指针/字节索引 ⇒ 得到整数 ⇒ 赋给 `str` 时报"整数隐式转指针已禁止" + # (真因在 4 行之前,报文完全指不到)。此处从被调者的**返回注解** + # 取回被指类名(判据与 HandlesAnnAssign 的同类恢复同源)。 + if (not Gen.var_struct_class.get(VarName) + and isinstance(Node.value, ast.Call) + and isinstance(Value.type, ir.PointerType) + and not isinstance(Value.type.pointee, + (ir.IdentifiedStructType, ir.LiteralStructType))): + _rc: str | None = self.Trans.ExprCallHandle._CallReturnClass(Node.value, Gen) + if _rc: + Gen.var_struct_class[VarName] = _rc + if getattr(Gen, 'global_struct_class', None) is not None: + Gen.global_struct_class[VarName] = _rc + Gen._var_to_heap_ptr[VarName] = Value + if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + # BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明) + if (VarName in Gen.variables and VarName not in Gen.global_vars + and isinstance(Gen.variables[VarName], ir.GlobalVariable)): + del Gen.variables[VarName] + if VarName in Gen.variables and Gen.variables[VarName] is not None: + Gen._store(Value, Gen.variables[VarName]) + else: + var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) + Gen._store(Value, var) + Gen.variables[VarName] = var + if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): + call_func_name: str = Node.value.func.id + if call_func_name in Gen.var_type_info: + type_info: dict[str, str] = Gen.var_type_info[call_func_name] + if type_info['type'] in ('t_type', 'class_cast'): + src_fmt_var: str = f"__{call_func_name}_fmt" + dst_fmt_var: str = f"__{VarName}_fmt" + if src_fmt_var in Gen.variables: + if dst_fmt_var not in Gen.variables: + dst_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var) + Gen.variables[dst_fmt_var] = dst_alloca + src_fmt_ptr: ir.Value = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}") + Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var]) + src_kind_var: str = f"__{call_func_name}_kind" + dst_kind_var: str = f"__{VarName}_kind" + if src_kind_var in Gen.variables: + if dst_kind_var not in Gen.variables: + dst_kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var) + Gen.variables[dst_kind_var] = dst_kind_alloca + src_kind_val: ir.Value = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}") + Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var]) + Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name} + return + if VarName in Gen.global_vars: + # 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局 + # (注解无初始值的全局变量),此处自动创建零初始化全局变量 + GVar: ir.GlobalVariable + if VarName not in Gen.module.globals: + _gv_type: ir.Type = Value.type if isinstance(Value.type, (ir.IntType, ir.PointerType)) else ir.IntType(64) + GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName) + GVar.initializer = ir.Constant(_gv_type, None) if isinstance(_gv_type, ir.PointerType) else ir.Constant(_gv_type, 0) + else: + GVar = Gen.module.globals[VarName] + # 栈地址逃逸:`G = &local`,或 `G = arr`(G 为指针时的数组退让)——禁止把本帧栈 + # 地址写入模块级全局(函数返回后悬垂);目标为同型聚合时按值拷贝,不在此列。 + TgtTy1: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None + if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy1): + from lib.core.LLVMCG.FuncGen import LifetimeError + raise LifetimeError( + f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName}," + f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配" + ) + Gen._store(Value, GVar) + Gen.variables[VarName] = GVar + if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(Value.type.pointee) + if found and not self._DeclaredClassBlocksOverride(Gen, VarName): + CN: str = found[0] + ST: ir.Type = found[1] + Gen.var_struct_class[VarName] = CN + return + if VarName in Gen._reg_values: + OldVal: ir.Value = Gen._reg_values[VarName] + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) + Gen._store(OldVal, var) + Gen.variables[VarName] = var + del Gen._reg_values[VarName] + if VarName in Gen._direct_values: + OldVal: ir.Value = Gen._direct_values.pop(VarName) + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) + Gen._store(OldVal, var) + Gen.variables[VarName] = var + # BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明)。 + # Python 语义:函数内无 global 声明时赋值应创建局部变量, + # 而非修改被读取缓存的全局变量。清除后走 L339 的正确逻辑。 + if (VarName in Gen.variables and VarName not in Gen.global_vars + and isinstance(Gen.variables[VarName], ir.GlobalVariable)): + del Gen.variables[VarName] + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: ir.Value = Gen.variables[VarName] + TargetType: ir.Type | None = VarPtr.type.pointee if isinstance(VarPtr.type, ir.PointerType) else None + types_differ: bool + try: + types_differ = TargetType and Value.type != TargetType + except AssertionError: + types_differ = False + if _rhs_addr_form and isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType): + LoadedValue: ir.Value = Gen._load(Value, name=f"Load_{VarName}") + Loaded_differ: bool + try: + Loaded_differ = TargetType and LoadedValue.type != TargetType + except AssertionError: + Loaded_differ = False + if Loaded_differ: + try: + Coerced: ir.Value | None = Gen._coerce_value(LoadedValue, TargetType, Node.value) + if Coerced is not None: + LoadedValue = Coerced + except AssertionError: + pass + Gen._store(LoadedValue, VarPtr, Node=Node.value) + elif types_differ: + try: + Coerced: ir.Value | None = Gen._coerce_value(Value, TargetType, Node.value) + if Coerced is not None: + Gen._store(Coerced, VarPtr, Node=Node.value) + else: + Gen._store(Value, VarPtr, Node=Node.value) + except AssertionError: + Gen._store(Value, VarPtr, Node=Node.value) + else: + Gen._store(Value, VarPtr, Node=Node.value) + if VarName not in Gen.variables: + # 检查是否为模块级全局变量 + # main 函数代表模块级作用域,直接修改全局变量 + # 其他函数中,未声明 global 且未先读取的赋值创建局部变量(Python 作用域语义) + # 先读取过的变量已通过 HandleExprLlvm 加入 Gen.variables,会走 L289 分支存储到全局 + CurrentFuncName: str = getattr(Gen.func, 'name', '') if Gen.func else '' + IsMainScope: bool = CurrentFuncName == 'main' or CurrentFuncName.endswith('.main') + if VarName in Gen.module.globals and (VarName in Gen.global_vars or IsMainScope): + GVar: ir.GlobalVariable = Gen.module.globals[VarName] + # 栈地址逃逸:main 作用域下的全局写入同口径(含 `G = arr` 的数组退让)。 + TgtTy2: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None + if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy2): + from lib.core.LLVMCG.FuncGen import LifetimeError + raise LifetimeError( + f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName}," + f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配" + ) + Gen._store(Value, GVar, Node=Node.value) + Gen.variables[VarName] = GVar + elif _rhs_addr_form and isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType): + LoadedValue: ir.Value = Gen._load(Value, name=f"Load_{VarName}") + var: ir.AllocaInstr = Gen._allocaEntry(LoadedValue.type, name=VarName) + Gen._store(LoadedValue, var, Node=Node.value) + Gen.variables[VarName] = var + else: + var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) + Gen._store(Value, var) + Gen.variables[VarName] = var + is_u: bool = Gen._check_node_unsigned(Node.value) + Gen._record_var_signedness(VarName, 'unsigned int' if is_u else 'int') + if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): + call_func_name: str = Node.value.func.id + if call_func_name in Gen.var_type_info: + type_info: dict[str, str] = Gen.var_type_info[call_func_name] + if type_info['type'] in ('t_type', 'class_cast'): + src_fmt_var: str = f"__{call_func_name}_fmt" + dst_fmt_var: str = f"__{VarName}_fmt" + if src_fmt_var in Gen.variables: + if dst_fmt_var not in Gen.variables: + dst_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var) + Gen.variables[dst_fmt_var] = dst_alloca + src_fmt_ptr: ir.Value = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}") + Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var]) + src_kind_var: str = f"__{call_func_name}_kind" + dst_kind_var: str = f"__{VarName}_kind" + if src_kind_var in Gen.variables: + if dst_kind_var not in Gen.variables: + dst_kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var) + Gen.variables[dst_kind_var] = dst_kind_alloca + src_kind_val: ir.Value = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}") + Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var]) + Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name} + else: + Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name} + elif isinstance(Target, ast.Attribute): + TargetType: ir.Type | None = None + AttrName: str = Target.attr + VarName: str | None = None + if isinstance(Target.value, ast.Name): + VarName = Target.value.id + if VarName: + ClassName: str | None = Gen.var_struct_class.get(VarName) + if not ClassName and Gen.global_struct_class: + ClassName = Gen.global_struct_class.get(VarName) + if ClassName: + Gen.var_struct_class[VarName] = ClassName + if not ClassName: + for CN in Gen.structs: + if VarName == CN.lower() or VarName.startswith(CN): + ClassName = CN + break + if ClassName and ClassName in Gen.class_members: + for m_name, m_type in Gen.class_members[ClassName]: + if m_name == AttrName: + TargetType = m_type + break + Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType) + if not Value: + if isinstance(Node.value, ast.Attribute): + self._RaiseUnresolvedExpr("属性写入", Node.value, Gen) + return + self._HandleAttributeStoreLlvm(Target, Value) + elif isinstance(Target, ast.Subscript): + TargetType: ir.Type | None = None + SubVarName: str | None = None + if isinstance(Target.value, ast.Name): + SubVarName = Target.value.id + if SubVarName in Gen.var_struct_class: + ClassName: str = Gen.var_struct_class[SubVarName] + if ClassName in Gen.class_members: + for m_name, m_type in Gen.class_members[ClassName]: + if m_name == SubVarName: + if isinstance(m_type, ir.PointerType): + pointee: ir.Type = m_type.pointee + if isinstance(pointee, ir.ArrayType): + TargetType = pointee.element + elif isinstance(m_type, ir.ArrayType): + TargetType = m_type.element + break + if not TargetType and isinstance(Target.value, ast.Name): + SubVarName = Target.value.id + var_val: ir.Value | None = Gen.variables.get(SubVarName) + if var_val is None and SubVarName in Gen._direct_values: + var_val = Gen._direct_values[SubVarName] + if var_val is not None: + var_type: ir.Type = var_val.type + if isinstance(var_type, ir.PointerType): + pointee: ir.Type = var_type.pointee + if isinstance(pointee, ir.ArrayType): + TargetType = pointee.element + elif isinstance(var_type, ir.ArrayType): + TargetType = var_type.element + Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType) + if not Value: + if isinstance(Node.value, ast.Attribute): + self._RaiseUnresolvedExpr("下标写入", Node.value, Gen) + return + self._HandleSubscriptStoreLlvm(Target, Value) + elif isinstance(Target, ast.UnaryOp): + # 解引用写入 `*p = v`(与读侧同一节点形态)。 + # 解析链:语句级 Assign 走 vast_c.c ParseSimpleStmt 的 + # ParseExprList + SetStoreContext;`*p` 由 ParseFactor 产出 + # UnaryOp(op=Deref)(与 `*p` 读侧完全同形),而 SetStoreContext 只改 + # Attribute/Subscript/Name/Tuple/List/Starred 的 ctx,**不改 UnaryOp** + # ⇒ 赋值目标就是 UnaryOp(Deref)。 + # 此前本分派(Name/Attribute/Subscript/Tuple)无 UnaryOp 分支且无兜底 + # ⇒ 该语句被整条静默丢弃(既不落盘也不报错):memhub 的块头 + # `(order<<1)|1`、空闲链 next、canary 三处写入全不发射 ⇒ 块头恒为 0 + # (free 被拒)、free_lists 链读到未初始化 0xBE、池单调漏满 ⇒ 1 GiB OOM。 + # `&x`/`-x`/`~x` 作目标不是合法左值,故除 Deref 外一律 fail-fast。 + if not isinstance(Target.op, ast.Deref): + BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Target.op) + raise Exception( + f"赋值目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)" + f"{Gen._get_node_info()}" + ) + Ptr: ir.Value | None = self.HandleExprLlvm(Target.operand) + if Ptr is None: + self._RaiseUnresolvedExpr("解引用写入", Target.operand, Gen) + return + if not isinstance(Ptr.type, ir.PointerType): + raise Exception( + f"解引用赋值 '*' 的操作数必须是指针类型(类型推断退化," + f"实际 {Ptr.type}){Gen._get_node_info()}" + ) + # `*a = *b` is the c.Load form: copy the raw bytes (width = target + # pointee), never convert the value. Without it a bit-pun such as + # `*&bits = *&dbl` degrades to fptosi/trunc and loses the IEEE bits. + if isinstance(Node.value, ast.UnaryOp) and isinstance(Node.value.op, ast.Deref): + SrcPtr: ir.Value | None = self.HandleExprLlvm(Node.value.operand) + if SrcPtr is None: + self._RaiseUnresolvedExpr("解引用写入", Node.value.operand, Gen) + return + if not isinstance(SrcPtr.type, ir.PointerType): + raise Exception( + f"解引用赋值右侧 '*' 的操作数必须是指针类型(类型推断退化," + f"实际 {SrcPtr.type}){Gen._get_node_info()}" + ) + if self.Trans.CSpecialCallHandle.EmitRawCopyLlvm(Ptr, SrcPtr, Gen): + return + DerefValue: ir.Value | None = self.HandleExprLlvm(Node.value) + if DerefValue is None: + self._RaiseUnresolvedExpr("解引用写入", Node.value, Gen) + return + # 方案1(收窄版):`*p = v` 仅当【p 的 pointee 是 8 位原始指针目标(i8/rawptr)】 + # 且 v 是指针时,才 bitcast p 成 typeof(v)* 后按值存 v(`*(T*(p)) = v`)—— + # 即"把指针位写进原始字节缓冲"这类合法位写。 + # **不得**对结构槽(pointee 为 renum/struct)生效:那类 `*X = <构造调用>` 必须 + # 交给 _StoreWithCoerce 的"构造物化"路径做 load+store 整对象拷贝,否则会把 + # 栈 alloca 的地址当指针存进去(栈逃逸 → 变体损坏,SpecTypeBits/TypePrint 崩)。 + PtrPointee: ir.Type | None = Ptr.type.pointee if isinstance(Ptr.type, ir.PointerType) else None + if (isinstance(DerefValue.type, ir.PointerType) and PtrPointee is not None + and PtrPointee != DerefValue.type + and isinstance(PtrPointee, ir.IntType) and PtrPointee.width == 8): + Ptr = Gen.builder.bitcast(Ptr, ir.PointerType(DerefValue.type), name="deref_store_as") + self._StoreWithCoerce(DerefValue, Ptr, Node.value) elif isinstance(Target, (ast.Tuple, ast.List)): # ast.List 与 ast.Tuple 字段表逐字相同(Nodes.py:119-120 均为 # ('elts', 'ctx')),故 `[a, b] = f()` 与 `a, b = f()` 共用本分支。 - # 此前只认 Tuple ⇒ List 目标落入无分支区被静默丢弃(不落盘、不报错)。 - if isinstance(Node.value, ast.Call): - FuncName: str | None = None - ModulePath: str | None = None - if isinstance(Node.value.func, ast.Name): - FuncName = Node.value.func.id - elif isinstance(Node.value.func, ast.Attribute): - FuncName = Node.value.func.attr - ModulePath = self.Trans.ExprCallHandle._get_ModulePath(Node.value.func.value) - MultiRetTypes: list[ast.expr] = [] - FuncDef: ast.FunctionDef | None = self.Trans.FunctionDefCache.get(FuncName) if FuncName else None - if FuncDef: - if FuncDef.returns and isinstance(FuncDef.returns, ast.Subscript): - if isinstance(FuncDef.returns.value, ast.Name) and FuncDef.returns.value.id == 'tuple': - slice_node: ast.expr = FuncDef.returns.slice - if isinstance(slice_node, ast.Tuple): - MultiRetTypes = slice_node.elts - else: - MultiRetTypes = [slice_node] - if FuncDef.decorator_list: - for decorator in FuncDef.decorator_list: - if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): - # fail-fast:`@c.CReturn` 已移除,扫到即报错。 - if decorator.func.attr == 'CReturn': - raise SyntaxError( - f"函数 '{FuncName}':`@c.CReturn` 已移除:请改用返回注解 " - f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" - f"{Gen._get_node_info()}" - ) - if not MultiRetTypes and FuncName: - func: ir.Function | None = Gen.functions.get(FuncName) - if func: - func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) - if func_ft: - ret_type: ir.Type = func_ft.return_type - if isinstance(ret_type, ir.LiteralStructType): - MultiRetTypes = [None] * len(ret_type.elements) - if not MultiRetTypes: - sym_key: str = FuncName - if not self.Trans.SymbolTable.has(sym_key): - if ModulePath: - sym_key = f"{ModulePath}.{FuncName}" - sym_info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(sym_key) - if sym_info and sym_info.IsFunction: - ret_type_info: CTypeInfo | None = sym_info.FuncPtrReturn - param_type_infos: list[tuple[str, CTypeInfo]] = [pt for _, pt in (sym_info.FuncPtrParams or [])] - if ret_type_info: - if isinstance(ret_type_info, CTypeInfo) and ret_type_info.BaseType: - ret_type: ir.Type = ret_type_info.ToLLVM(Gen) - else: - ret_type = Gen._type_str_to_llvm(str(ret_type_info) if ret_type_info else 'i32', False) - if isinstance(ret_type, ir.LiteralStructType): - MultiRetTypes = [None] * len(ret_type.elements) - if FuncName not in Gen.functions: - if isinstance(ret_type, ir.VoidType): - ret_type = ir.IntType(32) - llvm_param_types: list[ir.Type] = [] - for pt in param_type_infos: - if isinstance(pt, CTypeInfo) and pt.BaseType: - lp: ir.Type = pt.ToLLVM(Gen) - else: - lp = Gen._type_str_to_llvm(str(pt) if pt else 'i32', '*' in str(pt) if isinstance(pt, str) else False) - if isinstance(lp, ir.VoidType): - lp = ir.IntType(8).as_pointer() - llvm_param_types.append(lp) - func_type: ir.FunctionType = ir.FunctionType(ret_type, llvm_param_types) - MangledName: str = Gen._mangle_func_name(FuncName) - # 走唯一幂等入口(module.globals 已有同名声明复用 / - # 类型冲突 fail-fast),避免裸建抛 DuplicatedNameError。 - func_decl: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, func_type) - Gen.functions[FuncName] = func_decl - if not MultiRetTypes and FuncName: - func: ir.Function | None = Gen.functions.get(FuncName) - if func: - func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) - if func_ft: - ret_type: ir.Type = func_ft.return_type - if isinstance(ret_type, ir.LiteralStructType): - MultiRetTypes = [None] * len(ret_type.elements) - if not MultiRetTypes: - # 跨模块多返回值兜底:短名未注册时(函数以 ModulePath.名 / - # sha1.名 全名注册),从其 LiteralStructType 返回类型恢复 MultiRetTypes。 - # 否则 MultiRetTypes 为空会走下方静默 return,目标变量未初始化, - # 运行期 free(垃圾指针) 触发堆损坏(HashLibTest 0xC0000374)。 - func = None - if ModulePath: - func = Gen.functions.get(f"{ModulePath}.{FuncName}") - if func is None: - for key in Gen.functions: - if key.endswith(f".{FuncName}"): - func = Gen.functions[key] - break - if func: - func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) - if func_ft: - ret_type: ir.Type = func_ft.return_type - if isinstance(ret_type, ir.LiteralStructType): - MultiRetTypes = [None] * len(ret_type.elements) - if MultiRetTypes and len(Target.elts) == len(MultiRetTypes): - # 注:此处原有两行 `if func is None: func = ...`,但 func 只在上面 - # 「not MultiRetTypes」分支里绑定;当 MultiRetTypes 来自返回注解 - # (`-> tuple[T1, T2]`)时该分支未执行 ⇒ UnboundLocalError 崩溃。 - # 且紧接着的 `func = Gen.functions.get(FuncName)` 本就覆盖它, - # 故直接删除(`a, b = f()` 因此从未真正跑通)。 - CallArgs: list[ir.Value | None] = [] - for arg in Node.value.args: - ArgVal: ir.Value | None = self.HandleExprLlvm(arg) - if ArgVal: - CallArgs.append(ArgVal) - for kw in Node.value.keywords: - pass - func: ir.Function | None = Gen.functions.get(FuncName) - func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) if func else None - func_params: list[ir.Type] = list(getattr(func_ft, 'args', []) or []) if func_ft else [] - while len(CallArgs) < len(func_params): - FuncDef: ast.FunctionDef | None = self.Trans.FunctionDefCache.get(FuncName) - if FuncDef and len(CallArgs) < len(FuncDef.args.args): - arg_def: list[ast.expr] = FuncDef.args.defaults - arg_idx: int = len(CallArgs) - total_args: int = len(FuncDef.args.args) - default_start: int = total_args - len(arg_def) - if arg_idx >= default_start: - def_idx: int = arg_idx - default_start - if def_idx < len(arg_def): - DefVal: ir.Value | None = self.HandleExprLlvm(arg_def[def_idx]) - if DefVal: - expected_type: ir.Type = func_params[arg_idx] - if DefVal.type != expected_type: - if isinstance(expected_type, ir.IntType) and isinstance(DefVal.type, ir.IntType): - if DefVal.type.width < expected_type.width: - DefVal = Gen.builder.zext(DefVal, expected_type, name=f"zext_default_{arg_idx}") - elif DefVal.type.width > expected_type.width: - DefVal = Gen.builder.trunc(DefVal, expected_type, name=f"trunc_default_{arg_idx}") - CallArgs.append(DefVal) - continue - if len(CallArgs) < len(func_params): - CallArgs.append(ir.Constant(func_params[len(CallArgs)], 0)) - else: - break - adjusted: list[ir.Value] = Gen._adjust_args(CallArgs, func) - call_result: ir.Value = Gen.builder.call(func, adjusted, name=f"call_{FuncName}") - func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) - ret_type: ir.Type | None = getattr(func_ft, 'return_type', None) if func_ft else None - for j, elt in enumerate(Target.elts): - if isinstance(elt, ast.Name): - VarName: str = elt.id - field_val: ir.Value = Gen.builder.extract_value(call_result, j, name=f"extract_{FuncName}_{j}") - VarType: ir.Type - if MultiRetTypes[j] is not None: - ReturnTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(MultiRetTypes[j], self.Trans.SymbolTable) - if ReturnTypeInfo is None: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - VarType = Gen._ctype_to_llvm(ReturnTypeInfo) - elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements): - VarType = ret_type.elements[j] - else: - VarType = field_val.type - if isinstance(VarType, ir.VoidType): - VarType = ir.IntType(32) - var: ir.AllocaInstr = Gen._alloca(VarType, name=VarName) - Gen.variables[VarName] = var - if MultiRetTypes[j] is not None: - Gen._record_var_signedness(VarName, str(ReturnTypeInfo) if ReturnTypeInfo else '') - elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements): - elem_type: ir.Type = ret_type.elements[j] - if isinstance(elem_type, ir.IntType): - if elem_type.width == 16: - Gen._record_var_signedness(VarName, 'unsigned short') - elif elem_type.width == 32: - Gen._record_var_signedness(VarName, 'unsigned int') - elif elem_type.width == 64: - Gen._record_var_signedness(VarName, 'unsigned long long') - Gen._store(field_val, var) - elif isinstance(Node.value, ast.Call): - # fail-fast:多返回值赋值但 MultiRetTypes 解析失败(跨模块签名丢失), - # 绝不能静默跳过——目标变量不会被初始化,运行期 free(垃圾指针) - # 触发堆损坏(HashLibTest 0xC0000374)。 - target_names: list[str] = [ - e.id if isinstance(e, ast.Name) else '' for e in Target.elts - ] - raise Exception( - f"多返回值赋值解析失败: '{FuncName}' 的返回类型无法解析为" - f"返回注解 `-> tuple[...]`(ModulePath={ModulePath}),目标 {target_names} 未被初始化" - f"{Gen._get_node_info()}" - ) - return - - def _InitStructAtPtr(self, call_node: ast.Call, ClassName: str, VarPtr: ir.Value) -> None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - if ClassName not in Gen.structs: - return - StructType: ir.Type = Gen.structs[ClassName] - StructPtrType: ir.PointerType = ir.PointerType(StructType) - RawPtr: ir.Value = Gen._load(VarPtr, name=getattr(VarPtr, 'name', 'ptr')) - StructPtr: ir.Value = Gen.builder.bitcast(RawPtr, StructPtrType, name=ClassName) - members: list[tuple[str, ir.Type]] = Gen.class_members.get(ClassName, []) - defaults: dict[str, ir.Value] = Gen.class_member_defaults.get(ClassName, {}) - member_values: dict[str, ir.Value] = {} - for member_name, member_type in members: - if member_name in defaults: - member_values[member_name] = defaults[member_name] - if call_node.args: - for i, arg in enumerate(call_node.args): - if i < len(members): - member_name: str = members[i][0] - val: ir.Value | None = self.HandleExprLlvm(arg) - if val: - member_values[member_name] = val - if call_node.keywords: - for kw in call_node.keywords: - val: ir.Value | None = self.HandleExprLlvm(kw.value) - if val: - member_values[kw.arg] = val - # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源), - # 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。 - base: int = Gen._member_base(ClassName) - for i, (member_name, member_type) in enumerate(members): - if isinstance(member_type, ir.VoidType): - continue - if member_name in member_values: - ElemPtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + i)], name=f"{ClassName}_{member_name}") - try: - Gen._store(member_values[member_name], ElemPtr) - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - InitFuncName: str = f'{ClassName}.__init__' - if Gen._has_function(InitFuncName): - Args: list[ir.Value | None] = [self.HandleExprLlvm(arg) for arg in call_node.args] - Args = [a for a in Args if a] - Gen.builder.call(Gen._get_function(InitFuncName), [StructPtr] + Args, name=f"call_{InitFuncName}") - - def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - AttrName: str = Target.attr - # const 类字段:给 `obj.字段 = ...` / `self.字段 = ...` 直接赋值同样拒绝 - # (判据 = 字段自身 const,见 BaseGen._IsFieldConst / Debugger/type-descriptors.md)。 - # 类名能解析时按 `类名.字段名` 精确判定;解析不到时退化裸名(保守报错)。 - _TargetCN: str | None = None - if isinstance(Target.value, ast.Name): - _TargetCN = self.Trans.ExprHandler._get_var_class(Target.value, Gen) - if Gen._IsFieldConst(_TargetCN, AttrName): - _vlog().error(Gen._ConstAssignMessage("字段赋值", AttrName)) - sys.exit(1) - if isinstance(Target.value, ast.Name) and Target.value.id == 'self': - ClassName: str | None = self.Trans._CurrentCpythonObjectClass - if ClassName and ClassName in Gen.structs: - PropKey: str = f'{ClassName}.{AttrName}' - PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey) - if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList: - SelfVar: ir.Value | None = Gen._get_var_ptr('self') - if SelfVar: - SelfPtr: ir.Value = Gen._load(SelfVar, name="self") - SetterFunc: ir.Function | None = Gen._get_function(PropKey + '$set') - if SetterFunc and SelfPtr: - Gen.builder.call(SetterFunc, [SelfPtr, Value], name=f"prop_set_{PropKey}") - return - SelfVar: ir.Value | None = Gen._get_var_ptr('self') - if not SelfVar: - return - if SelfVar: - SelfPtr: ir.Value = Gen._load(SelfVar, name="self") - is_vtable_method: bool = False - method_idx: int = 0 - if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes: - if ClassName in Gen.class_methods: - for mi, mn in enumerate(Gen.class_methods[ClassName]): - method_short: str = mn.split('.')[-1] if '.' in mn else mn - if method_short == AttrName or mn == AttrName: - is_vtable_method = True - method_idx = mi - break - if is_vtable_method: - VtableSlotPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}") - VtablePtr: ir.Value = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}") - methods: list[str] = Gen.class_methods[ClassName] - VtableArrayType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) - ClassVtable: ir.GlobalVariable | None = Gen.Vtables.get(ClassName) - if ClassVtable: - ClassVtableAddr: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}") - IsSameVtable: ir.Value = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}") - CopyBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_copy_self_{AttrName}") - SkipBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_skip_self_{AttrName}") - Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock) - Gen.builder.position_at_end(CopyBlock) - Gen._vtable_copy_counter += 1 - CopyName: str = f"{Gen._mangle_name(ClassName)}_vtable_copy_self_{Gen._vtable_copy_counter}" - VtableCopyGlobal: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName) - VtableCopyGlobal.linkage = 'internal' - VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) - VtableCopyTyped: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed") - SrcTyped: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed") - VtableSize: int = len(methods) * Gen.ptr_size - MemcpyFunc: ir.Function | None = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64') - if not MemcpyFunc: - MemcpyType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) - MemcpyFunc = Gen._CreateOrReuseFuncDecl('llvm.memcpy.p0i8.p0i8.i64', MemcpyType) - Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc - Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)]) - VtableCopyGlobalAddr: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr") - Gen._store(VtableCopyGlobalAddr, VtableSlotPtr) - Gen.builder.branch(SkipBlock) - Gen.builder.position_at_end(SkipBlock) - VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy") - VtableTyped: ir.Value = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}") - MethodPtrAddr: ir.Value = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}") - FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}") - Gen._store(FuncPtrI8, MethodPtrAddr) - return - # 自指针基址收口:与**读路径同一判据源**(HandlesExprAttr - # `_ConcretizeGepBase` / `_ConcretePointeeFor`),三步同源: - # ① 类键那份 opaque ⇒ 先 `_TryLoadStructFromStub` 补 body; - # ② 仍不可用 ⇒ 按同短名取一份已具化实例(`_FindConcreteTwin`); - # ③ 都拿不到 ⇒ 原样返回,由下方 opaque fail-fast 报根因。 - # 写路径此前是**内联**版本、缺 ①:类键那份仍 opaque 且 `Gen.structs` - # 里也还没有孪生体时(body 只在 stub 文件里待加载),内联版会一路走到 - # GEP 撞 opaque(现场 2026-09-30:includes/ast/astaux.vp:165 - # `self.arg = _copy_str(pool, arg)` 的基址 pointee 是 opaque 的 - # %"8312ce86a2fd82c0.Keyword"*,而同短名 'Keyword' 键那份已具化 10 槽)。 - # 偏移与 GEP 必须同源:返回的实例同时作为 `_get_member_offset` 的 StructHint。 - SelfPtr, _SelfPointee = self.Trans.ExprAttrHandle._ConcretizeGepBase( - Gen, SelfPtr, ClassName, f"concrete_self_{ClassName}") - struct_type: ir.Type = (_SelfPointee - if isinstance(_SelfPointee, ir.IdentifiedStructType) - else Gen.structs[ClassName]) - if (isinstance(_SelfPointee, ir.IdentifiedStructType) - and _SelfPointee.elements is None): - # 收口失败:类键那份、同短名孪生体、以及 `_TryLoadStructFromStub` - # 三条路都没拿到 body。**不得**再落回下方 `offset >= max_offset` - # 的「存回临时变量」分支 —— 该临时变量不在对象布局里,后续 - # `self.attr` 读路径按 GEP 取值,写入会被**静默丢弃**。此处 fail-fast。 - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE0 - raise _FFE0( - f"属性写入 '{ClassName}.{AttrName}' 的 self 基址指向**未具化**" - f"结构体:'{getattr(_SelfPointee, 'name', '?')}'(opaque)。" - f"已尝试:类键 {ClassName!r} 的实例 → 同短名孪生体 → " - f"_TryLoadStructFromStub 补 body,均未拿到 elements。" - f"请查该类 body 为何从未 set_body(StructGen._generate_structs)。" - + (f"\n {Gen._get_node_info()}" if hasattr(Gen, '_get_node_info') else "")) - offset: int = Gen._get_member_offset(AttrName, ClassName, _SelfPointee) - max_offset: int = (len(struct_type.elements) - if isinstance(struct_type, ir.IdentifiedStructType) - and struct_type.elements is not None else 0) - if offset >= max_offset: - VarName: str = f"self.{AttrName}" - if VarName not in Gen.variables: - NewVar: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=AttrName) - Gen.variables[VarName] = NewVar - Gen._store(Value, Gen.variables[VarName]) - return - MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - # 成员槽写入失败时补「来源明细」:槽类型的两个来源(class_members - # 声明侧 / struct body 布局侧)是两条独立链,不一致时会静默写进 - # 类型完全不同的槽。报文里逐项列出两侧数据,直接判定谁退化了 - # (现场:Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。 - try: - self._StoreWithCoerce(Value, MemberPtr) - except Exception as _Me: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if not isinstance(_Me, _FFE): - raise - raise _FFE( - f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, SelfPtr)}" - ) from _Me - elif isinstance(Target.value, ast.Name): - VarName: str = Target.value.id - - # 模块全局变量写入(治本修复 2026-08-27):X.attr = ... 且 X 是 import - # 模块(含别名:resolve_alias 命中 _ImportedModules)时,必须优先按 - # "模块全局变量"处理——直接 store 到该模块的裸名/带 sha1 前缀全局符号。 - # 该检查必须放在 ClassName heuristic(下方 startswith(CN) 类名匹配)之前: - # 模块名与类名同名(如 CompilerFactory)时,heuristic 会把模块误判为类 - # 而走 _get_member_offset → fail-fast KeyError"属性在类中找不到"。 - # 读端(_lookup_module_global/_get_attr_ptr)已有此判定,写端此前缺失 - # → main.vp 的 `CompilerFactory.factory = CompilerFactory(...)` 从未写入 - # → Generator.__init__ 读 @factory=NULL → ASan 崩 0x8。 - _mod_imported: list[str] | None = getattr(self.Trans, '_ImportedModules', None) - _resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) - # 模块判定:VarName/ResolvedName 直等命中,或与某个已导入模块全名 - # 后缀匹配(如 VarName=CompilerFactory 命中 lib.core.CompilerFactory, - # 与 read 端 _lookup_module_global L212 的 endswith 判定保持一致)。 - _is_mod_write: bool = False - if _mod_imported: - _is_mod_write = (VarName in _mod_imported or _resolved_mod in _mod_imported) - if not _is_mod_write: - for _mn in _mod_imported: - if (_mn == VarName or _mn.endswith(f".{VarName}") - or _mn == _resolved_mod or _mn.endswith(f".{_resolved_mod}")): - _is_mod_write = True - break - if _is_mod_write: - # 优先带前缀:{module_sha1}.{AttrName}(TPC 编译定义端为裸名,TPV 自举 - # 后端为 sha1 前缀名,两代都要覆盖),再回退裸名 {AttrName}。 - _mod_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) - _cands: list[str] = [VarName, _resolved_mod] - for _mn in _mod_imported: - if _mn.endswith(f".{VarName}") or _mn == VarName: - if _mn not in _cands: - _cands.append(_mn) - for _cand in _cands: - _sha1: str | None = _mod_sha1_map.get(_cand) - if _sha1: - _prefixed: str = f"{_sha1}.{AttrName}" - if _prefixed in Gen.module.globals: - _pgv: ir.GlobalVariable = Gen.module.globals[_prefixed] - if isinstance(_pgv, ir.Function): - return - self._StoreWithCoerce(Value, _pgv) - return - if AttrName in Gen.module.globals: - _gv: ir.GlobalVariable = Gen.module.globals[AttrName] - if isinstance(_gv, ir.Function): - return - self._StoreWithCoerce(Value, _gv) - return - - # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) - # 直接通过 a.__field__ = value 访问影子结构体字段 (语法糖) - _fd_meta_name: str = f"{VarName}__meta__" - if _fd_meta_name in Gen.variables and '_str' in Gen.structs: - _fd_offset = Gen._get_member_offset(AttrName, '_str') - if _fd_offset is not None: - _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] - _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") - self._StoreWithCoerce(Value, _fd_field_ptr) - return - - ClassName: str | None = Gen.var_struct_class.get(VarName) - - if not ClassName and Gen.global_struct_class: - ClassName = Gen.global_struct_class.get(VarName) - if ClassName: - Gen.var_struct_class[VarName] = ClassName - - if not ClassName: - for CN in Gen.structs: - if VarName == CN.lower() or VarName.startswith(CN): - ClassName = CN - break - - if not ClassName and VarName in Gen.module.globals: - GVar: ir.GlobalVariable = Gen.module.globals[VarName] - if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(GVar.type.pointee) - if found: - ClassName = found[0] - Gen.var_struct_class[VarName] = ClassName - if Gen.global_struct_class: - Gen.global_struct_class[VarName] = ClassName - - if not ClassName and VarName in Gen.variables: - VarPtr: ir.Value = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType): - pointee: ir.Type = VarPtr.type.pointee - if isinstance(pointee, ir.PointerType): - pointee = pointee.pointee - found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee) - if found: - CN: str = found[0] - ST: ir.Type = found[1] - ClassName = CN - elif isinstance(pointee, ir.IdentifiedStructType): - # sha1 冲突 fallback:find_struct_by_pointee 因 sha1 前缀不匹配失败时, - # 用短名(去掉 sha1 前缀)匹配,优先选字段最多的完整版本 - pointee_short: str = Gen._extract_short_name(pointee.name) - best_elem_count: int = 0 - for CN, ST in Gen.structs.items(): - if isinstance(ST, ir.IdentifiedStructType): - st_short: str = Gen._extract_short_name(ST.name) - if st_short == pointee_short: - elem_count: int = len(ST.elements) if ST.elements else 0 - if elem_count > best_elem_count: - ClassName = CN - best_elem_count = elem_count - - IsUnion: bool = False - if ClassName: - TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(ClassName) - if TypeInfo and TypeInfo.IsUnion: - IsUnion = True - - if ClassName and ClassName in Gen.structs: - PropKey: str = f'{ClassName}.{AttrName}' - PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey) - if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList: - ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) - SetterFunc: ir.Function | None = Gen._get_function(PropKey + '$set') - if SetterFunc and ObjVal: - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}") - Gen.builder.call(SetterFunc, [ObjVal, Value], name=f"prop_set_{PropKey}") - return - ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) - if ObjVal: - if BaseHandle._is_char_pointer(ObjVal): - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}") - # 跨模块 opaque 类型:ObjVal.type.pointee 与 Gen.structs[ClassName] 可能是 - # 不同的 IdentifiedStructType 对象(虽然 name 相同),== 返回 False。 - # 使用 name 匹配作为 fallback,与链式赋值路径(第 926-927 行)一致。 - # sha1 冲突时还需短名匹配(如 6bc91356c652f8ca.Function vs 8ad0c0a2934ca5b4.Function) - _ObjPointee = ObjVal.type.pointee if isinstance(ObjVal.type, ir.PointerType) else None - _TargetStruct = Gen.structs.get(ClassName) - _MatchesStruct = (_ObjPointee is not None and _TargetStruct is not None and - (_ObjPointee == _TargetStruct or - (isinstance(_ObjPointee, ir.IdentifiedStructType) and isinstance(_TargetStruct, ir.IdentifiedStructType) and - (_ObjPointee.name == _TargetStruct.name or - Gen._extract_short_name(_ObjPointee.name) == Gen._extract_short_name(_TargetStruct.name))))) - # opaque 泛型结构体 → 特化版本 bitcast - # 当 ObjVal 的 pointee 是 opaque 基础泛型结构体(如 GSList,来自跨模块 stub), - # 而 ClassName 是特化版本(如 GSList[BasicBlock])时,两者 short name 不同 - # (GSList vs GSList[BasicBlock]),_MatchesStruct 失败。 - # 验证特化 short name 以 基础名+'[' 开头,bitcast 到特化版本使后续 GEP 能正确访问字段。 - if (not _MatchesStruct - and isinstance(ObjVal.type, ir.PointerType) - and isinstance(_ObjPointee, ir.IdentifiedStructType) - and isinstance(_TargetStruct, ir.IdentifiedStructType) - and '[' in ClassName): - _BaseShort: str = Gen._extract_short_name(_ObjPointee.name) - _SpecShort: str = Gen._extract_short_name(_TargetStruct.name) - if _BaseShort and _SpecShort.startswith(_BaseShort + '['): - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(_TargetStruct), name=f"cast_spec_{ClassName}") - _MatchesStruct = True - if isinstance(ObjVal.type, ir.PointerType) and _MatchesStruct: - if IsUnion: - NestedStructName: str = f"{ClassName}_{AttrName}" - if NestedStructName in Gen.structs: - NestedStructType: ir.Type = Gen.structs[NestedStructName] - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") - offset: int | None = Gen._get_member_offset(AttrName, NestedStructName) - if offset is not None and offset < len(NestedStructType.elements): - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - return - bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(ClassName, {}) - if AttrName in bitfield_offsets: - self._store_bitfield_member(ObjVal, ClassName, AttrName, Value) - return - bitfields: dict[str, int] = Gen.class_member_bitfields.get(ClassName, {}) - if AttrName in bitfields and bitfields[AttrName] > 0: - if ClassName not in Gen.class_member_bitoffsets: - Gen.class_member_bitoffsets[ClassName] = {} - if AttrName not in Gen.class_member_bitoffsets[ClassName]: - bo: int = 0 - for bf_name, bf_width in bitfields.items(): - if bf_name == AttrName: - break - bo += bf_width - Gen.class_member_bitoffsets[ClassName][AttrName] = bo - self._store_bitfield_member(ObjVal, ClassName, AttrName, Value) - return - is_vtable_method: bool = False - method_idx: int = 0 - if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes: - if ClassName in Gen.class_methods: - for mi, mn in enumerate(Gen.class_methods[ClassName]): - method_short: str = mn.split('.')[-1] if '.' in mn else mn - if method_short == AttrName or mn == AttrName: - is_vtable_method = True - method_idx = mi - break - if is_vtable_method: - VtableSlotPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}") - VtablePtr: ir.Value = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}") - methods: list[str] = Gen.class_methods[ClassName] - VtableArrayType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) - ClassVtable: ir.GlobalVariable | None = Gen.Vtables.get(ClassName) - if ClassVtable: - ClassVtableAddr: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}") - IsSameVtable: ir.Value = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}") - CopyBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_copy_{AttrName}") - SkipBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_skip_{AttrName}") - Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock) - Gen.builder.position_at_end(CopyBlock) - Gen._vtable_copy_counter += 1 - CopyName: str = f"{Gen._mangle_name(ClassName)}_vtable_copy_{Gen._vtable_copy_counter}" - VtableCopyGlobal: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName) - VtableCopyGlobal.linkage = 'internal' - VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) - VtableCopyTyped: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed") - SrcTyped: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed") - VtableSize: int = len(methods) * Gen.ptr_size - MemcpyFunc: ir.Function | None = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64') - if not MemcpyFunc: - MemcpyType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) - MemcpyFunc = Gen._CreateOrReuseFuncDecl('llvm.memcpy.p0i8.p0i8.i64', MemcpyType) - Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc - Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)]) - VtableCopyGlobalAddr: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr") - Gen._store(VtableCopyGlobalAddr, VtableSlotPtr) - Gen.builder.branch(SkipBlock) - Gen.builder.position_at_end(SkipBlock) - VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy") - VtableTyped: ir.Value = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}") - MethodPtrAddr: ir.Value = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}") - FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}") - Gen._store(FuncPtrI8, MethodPtrAddr) - return - offset: int | None = Gen._get_member_offset(AttrName, ClassName) - StructType: ir.Type | None = Gen.structs.get(ClassName) - if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) - StructType = Gen.structs.get(ClassName) - if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0): - return - # 重新计算 offset:_TryLoadStructFromStub 刚加载了 struct body, - # 之前 offset 可能因 struct opaque / class_members 不完整而返回 None - offset = Gen._get_member_offset(AttrName, ClassName) - # sha1 冲突修复:Gen.structs[ClassName] 可能是字段不全的版本(如 2 字段 Function - # 来自 MetaType.py scope,而正确版本是 8 字段 Function 来自 __function.py)。 - # _get_member_offset 通过 class_sha1_map 已返回正确的 offset(如 5), - # 但 StructType 可能只有 2 个 elements,导致 offset < len(elements) 失败。 - # 在所有 structs 中查找同短名且字段最多的版本。 - if offset is not None and isinstance(StructType, ir.IdentifiedStructType) and StructType.elements is not None and offset >= len(StructType.elements): - _short: str = Gen._extract_short_name(StructType.name) - _best: int = len(StructType.elements) - for _cn, _st in Gen.structs.items(): - if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: - if Gen._extract_short_name(_st.name) == _short and len(_st.elements) > _best: - StructType = _st - _best = len(_st.elements) - # bitcast:当 ObjVal 的 pointee 是 opaque 或与 StructType 不是同一个类型对象 - # (sha1 前缀不同但短名相同)时,需要 bitcast 到 StructType 才能正确 GEP - if StructType is not None and StructType.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and (ObjVal.type.pointee.elements is None or ObjVal.type.pointee != StructType): - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(StructType), name=f"cast_{ClassName}") - if offset is not None and StructType is not None and StructType.elements is not None and offset < len(StructType.elements): - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - else: - return - - byte_order: str = Gen.class_member_byteorders.get(ClassName, {}).get(AttrName, "") - - if byte_order == 'big': - if StructType is None or StructType.elements is None or len(StructType.elements) == 0: - return - member_type: ir.Type = StructType.elements[offset] - if isinstance(member_type, ir.IntType): - Value = Gen._apply_bswap_if_big(Value, byte_order, f"bswap_store_{AttrName}") - - # 成员槽写入失败时补「来源明细」(与 `self.x = ...` 路径 :897-905 - # 同一套 `_DescribeMemberSlot`):槽类型的两条独立来源 - # (class_members 声明侧 / struct body 布局侧)不一致时,值会被 - # 写进类型完全不同的槽(现场:`self.Gen.TypeGenCType. - # LastBuiltinsTypedefResult = "t.CPtr"` 落到 i32 槽 → 指针隐式 - # 转整数 fail-fast);报文里逐项列出两侧数据即可直接判定谁退化 - # (此处目标形态是 `对象.字段 = 值`,Base 侧同理取 ObjVal 所指布局)。 - try: - self._StoreWithCoerce(Value, MemberPtr) - except Exception as _Me: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if not isinstance(_Me, _FFE): - raise - raise _FFE( - f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, ObjVal)}" - ) from _Me - if not ClassName: - imported_modules: list[str] | None = getattr(self.Trans, '_ImportedModules', None) - resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) - if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules): - PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] - for mod_name in imported_modules: - if mod_name.endswith(f".{VarName}") or mod_name == VarName: - key: str = f"{mod_name}.{AttrName}" - if key not in PossibleKeys: - PossibleKeys.append(key) - for key in PossibleKeys: - if key in Gen.module.globals: - GVar: ir.GlobalVariable = Gen.module.globals[key] - if isinstance(GVar, ir.Function): - return - if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - return - self._StoreWithCoerce(Value, GVar) - return - ModuleSha1Map: dict[str, str] = Gen.ModuleSha1Map - sha1_candidates: list[str] = [VarName, resolved_mod] - for mod_name in imported_modules: - if mod_name.endswith(f".{VarName}") or mod_name == VarName: - if mod_name not in sha1_candidates: - sha1_candidates.append(mod_name) - for cand in sha1_candidates: - sha1: str | None = ModuleSha1Map.get(cand) - if sha1: - prefixed: str = f"{sha1}.{AttrName}" - if prefixed in Gen.module.globals: - GVar2: ir.GlobalVariable = Gen.module.globals[prefixed] - if isinstance(GVar2, ir.Function): - return - if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - return - self._StoreWithCoerce(Value, GVar2) - return - elif isinstance(Target.value, ast.Attribute): - ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) - if ObjVal and isinstance(ObjVal.type, ir.PointerType): - pointee: ir.Type = ObjVal.type.pointee - if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: - for CN, ST in Gen.structs.items(): - if isinstance(ST, ir.IdentifiedStructType) and ST.name == pointee.name and ST.elements is not None: - pointee = ST - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") - break - found_match: bool = False - for CN, ST in Gen.structs.items(): - if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name): - found_match = True - if ST.elements is None: - self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen) - ST = Gen.structs.get(CN, ST) - if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None: - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") - bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(CN, {}) - if AttrName in bitfield_offsets: - self._store_bitfield_member(ObjVal, CN, AttrName, Value) - return - offset: int = Gen._get_member_offset(AttrName, CN) - if offset is not None and ST.elements is not None and offset < len(ST.elements): - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - break - if not found_match: - if isinstance(pointee, ir.IdentifiedStructType): - matched_cn: str | None = None - for CN in Gen.class_members: - members: list[tuple[str, ir.Type]] = Gen.class_members[CN] - for mname, mtype in members: - if mname == AttrName: - matched_cn = CN - break - if matched_cn: - break - if matched_cn and matched_cn in Gen.structs: - ST: ir.Type = Gen.structs[matched_cn] - if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(matched_cn, Gen) - ST = Gen.structs.get(matched_cn, ST) - if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None: - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{matched_cn}") - offset: int = Gen._get_member_offset(AttrName, matched_cn) - if offset is not None and ST.elements is not None and offset < len(ST.elements): - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - return - elif isinstance(Target.value, ast.Call): - # Check if this is c.Deref(...) — we need the pointer, not the Loaded value - ObjVal: ir.Value | None = None - is_cderef: bool = (isinstance(Target.value.func, ast.Attribute) and - isinstance(Target.value.func.value, ast.Name) and - Target.value.func.value.id == 'c' and - Target.value.func.attr == 'Deref' and - Target.value.args) - if is_cderef: - # Get the pointer from c.Deref argument, don't Load the value - deref_arg: ast.expr = Target.value.args[0] - inner_val: ir.Value | None = self.HandleExprLlvm(deref_arg) - if inner_val and isinstance(inner_val.type, ir.PointerType): - if isinstance(inner_val.type.pointee, ir.PointerType): - ObjVal = Gen._load(inner_val, name="deref_load_ptr") - else: - ObjVal = inner_val - if ObjVal is None: - ObjVal = self.Trans.ExprHandler.HandleExprLlvm(Target.value) - if ObjVal and isinstance(ObjVal.type, ir.PointerType): - pointee: ir.Type = ObjVal.type.pointee - if isinstance(pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr") - pointee = ObjVal.type.pointee - for CN, ST in Gen.structs.items(): - if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name): - if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen) - ST = Gen.structs.get(CN, ST) - if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None: - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") - bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(CN, {}) - if AttrName in bitfield_offsets: - self._store_bitfield_member(ObjVal, CN, AttrName, Value) - return - offset: int = Gen._get_member_offset(AttrName, CN) - if offset is not None and ST.elements is not None and offset < len(ST.elements): - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - break - elif isinstance(Target.value, ast.Subscript): - SubPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value) - if SubPtr and isinstance(SubPtr.type, ir.PointerType): - pointee: ir.Type = SubPtr.type.pointee - - if isinstance(Target.value.value, ast.Attribute): - AttrNameSub: str = Target.value.value.attr - if AttrNameSub == 'items' and 'Item' in Gen.structs: - ObjValCast: ir.Value = Gen.builder.bitcast(SubPtr, ir.PointerType(Gen.structs['Item']), name=f"cast_Item") - offset: int = Gen._get_member_offset(AttrName, 'Item') - MemberPtr: ir.Value = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - return - - if BaseHandle._is_char_pointer(SubPtr): - for CN, ST in Gen.structs.items(): - ObjValCast: ir.Value = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}") - offset: int = Gen._get_member_offset(AttrName, CN) - MemberPtr: ir.Value = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - break - elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee) - if found: - CN: str = found[0] - ST: ir.Type = found[1] - if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen) - ST = Gen.structs.get(CN, ST) - if ST.elements is not None and isinstance(SubPtr.type, ir.PointerType) and isinstance(SubPtr.type.pointee, ir.IdentifiedStructType) and SubPtr.type.pointee.elements is None: - SubPtr = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}") - offset: int = Gen._get_member_offset(AttrName, CN) - if offset is not None and ST.elements is not None and offset < len(ST.elements): - MemberPtr: ir.Value = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - elif isinstance(pointee, ir.PointerType): - LoadedPtr: ir.Value = Gen._load(SubPtr, name=f"Load_{AttrName}_ptr") - if isinstance(LoadedPtr.type, ir.PointerType) and isinstance(LoadedPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(LoadedPtr.type.pointee) - if found: - CN: str = found[0] - ST: ir.Type = found[1] - offset: int = Gen._get_member_offset(AttrName, CN) - MemberPtr: ir.Value = Gen.builder.gep(LoadedPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - - def _StoreArrElemWithCoerceBswap(self, Value: ir.Value, Ptr: ir.Value, arr_byte_order: str, - Node: ast.AST | None = None) -> None: - """对数组元素存储应用 bswap 后再 coerce 存储""" - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - Value = Gen._apply_bswap_if_big(Value, arr_byte_order, "bswap_arr_store") - self._StoreWithCoerce(Value, Ptr, Node) - - def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value, - Node: ast.AST | None = None) -> None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - # pointee-volatile 基址名后缀(与 HandlesExprAttr._HandleSubscriptLlvm 同款); - # 空后缀 ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。 - _PVSuffix: str = Gen._SubscriptSuffix(Target.value) - # 查询数组元素字节序 - arr_byte_order: str = '' - if isinstance(Target.value, ast.Name) and Target.value.id in Gen.local_var_byteorders: - arr_byte_order = Gen.local_var_byteorders[Target.value.id] - if isinstance(Target.value, ast.Name): - SubVarName: str = Target.value.id - if Gen._IsVarConst(SubVarName): - # 与 Name 赋值/增量赋值同一条 fail-fast(原先此处 LogError 后继续, - # 属静默继续;现统一 error + exit,判据与报文两侧一致)。 - _vlog().error(Gen._ConstAssignMessage("的元素赋值", SubVarName)) - sys.exit(1) - ClassName: str | None = self.Trans.ExprHandler._get_var_class(Target.value, Gen) - if ClassName and Gen._has_function(f'{ClassName}.__setitem__'): - obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) - idx_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.slice) - if obj_val and idx_val: - # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 - if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: - obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") - if BaseHandle._is_char_pointer(obj_val): - obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - setitem_args: list[ir.Value] = [obj_val, idx_val, Value] - setitem_func_name: str = f'{ClassName}.__setitem__' - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - setitem_func: ir.Function = Gen._get_function(setitem_func_name) - setitem_args = Gen._adjust_args(setitem_args, setitem_func) - Gen.builder.call(setitem_func, setitem_args, name=f"call_{setitem_func_name}") - return - ValueVal: ir.Value | None = self.HandleExprLlvm(Target.value) - IndexVal: ir.Value | None = self.HandleExprLlvm(Target.slice) - if not ValueVal or not IndexVal: - return - # 当变量本身是二级指针(如 elem_ptr: T | t.CPtr = AST**), - # 且存储的值也是指针时,elem_ptr[i] = item 应直接存储指针(8 字节), - # 而非解引用后复制结构体(如 48 字节的 AST),避免对象切片 - if isinstance(Target.value, ast.Name): - VarName: str = Target.value.id - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarAlloca: ir.Value = Gen.variables[VarName] - if isinstance(VarAlloca.type, ir.PointerType) and isinstance(VarAlloca.type.pointee, ir.PointerType): - if (isinstance(ValueVal.type, ir.PointerType) and isinstance(Value.type, ir.PointerType) - and isinstance(Value.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store") - # ValueVal 是 AST*(加载自 elem_ptr 的基指针), - # 但 GEP 默认将其视为 AST 结构体数组。需要先 bitcast 为 - # AST**(指向 AST* 的指针),让 GEP 正确索引 AST* 指针数组, - # 而非 AST 结构体数组。然后用 builder.store 直接存储指针, - # 避免 _store 的类型强制转换将指针解引用为结构体。 - PtrToStruct: ir.Type = Value.type.pointee - BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal, - ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp") - ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name=f"subscript{_PVSuffix}") - Gen.builder.store(Value, ElemPtr) - return - if BaseHandle._is_char_pointer(IndexVal): - Loaded_byte: ir.Value = Gen._load(IndexVal, name="Load_char_idx_store") - IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_store") - if isinstance(ValueVal.type, ir.IntType): - var_ptr: ir.Value | None = self._get_int_ptr(Target.value) - if var_ptr: - i8_ptr: ir.Value = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr_store") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index_store") - ptr: ir.Value = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr_store") - self._StoreWithCoerce(Value, ptr) - return - if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store") - ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") - self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order, Node) - return - if isinstance(ValueVal.type, ir.PointerType): - pointee: ir.Type = ValueVal.type.pointee - if isinstance(pointee, ir.ArrayType): - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}") - elif (isinstance(pointee, ir.IntType) and pointee.width == 8 - and isinstance(Target.value, ast.Name) - and (Gen.var_ptr_element.get(Target.value.id, False) - or Gen.global_var_ptr_element.get(Target.value.id, False))): - # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr) - # elem_ptr 是 i8* 但实际指向 i8* (字符串指针), elem_ptr[0] = item 应存储 8 字节 - CastedPtrPtr: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_store") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_store") - ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") - self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) - return - else: - ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") - # fail-fast:rawptr[T](基座是指针存储)的元素为对象 T、而 RHS 是指向 T - # 的指针时,下游 _StoreWithCoerce/_coerce_value 会静默 load 整对象再按 - # sizeof(T) 存储(写 sizeof(T) 而非 8 字节指针)。数组槽(t.CArray[T,N]) - # 本就按 sizeof(T) 布局,整对象赋值合法 ⇒ 由 IsArrayBase 排除。 - self._RejectWholeObjectCopyIntoPtrSlot( - Target, Value, ElemPtr, isinstance(pointee, ir.ArrayType)) - self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) - elif isinstance(ValueVal.type, ir.ArrayType): - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - if isinstance(Target.value, ast.Name): - VarName: str = Target.value.id - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: ir.Value = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr: ir.Value = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) - return - if isinstance(Target.value, ast.Subscript): - InnerPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value) - if InnerPtr: - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr: ir.Value = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) - return - if isinstance(Target.value, ast.Attribute): - AttrPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value) - if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr: ir.Value = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) - return - arr_alloc: ir.AllocaInstr = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") - Gen._store(ValueVal, arr_alloc) - ElemPtr: ir.Value = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}") - self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) - ModifiedArr: ir.Value = Gen._load(arr_alloc, name="modified_arr") - if isinstance(Target.value, ast.Subscript): - OuterPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value) - if OuterPtr: - Gen._store(ModifiedArr, OuterPtr) - elif isinstance(Target.value, ast.Attribute): - OuterPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value) - if OuterPtr: - Gen._store(ModifiedArr, OuterPtr) - + # 此前只认 Tuple ⇒ List 目标落入无分支区被静默丢弃(不落盘、不报错)。 + if isinstance(Node.value, ast.Call): + FuncName: str | None = None + ModulePath: str | None = None + if isinstance(Node.value.func, ast.Name): + FuncName = Node.value.func.id + elif isinstance(Node.value.func, ast.Attribute): + FuncName = Node.value.func.attr + ModulePath = self.Trans.ExprCallHandle._get_ModulePath(Node.value.func.value) + MultiRetTypes: list[ast.expr] = [] + FuncDef: ast.FunctionDef | None = self.Trans.FunctionDefCache.get(FuncName) if FuncName else None + if FuncDef: + if FuncDef.returns and isinstance(FuncDef.returns, ast.Subscript): + if isinstance(FuncDef.returns.value, ast.Name) and FuncDef.returns.value.id == 'tuple': + slice_node: ast.expr = FuncDef.returns.slice + if isinstance(slice_node, ast.Tuple): + MultiRetTypes = slice_node.elts + else: + MultiRetTypes = [slice_node] + if FuncDef.decorator_list: + for decorator in FuncDef.decorator_list: + if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): + # fail-fast:`@c.CReturn` 已移除,扫到即报错。 + if decorator.func.attr == 'CReturn': + raise SyntaxError( + f"函数 '{FuncName}':`@c.CReturn` 已移除:请改用返回注解 " + f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" + f"{Gen._get_node_info()}" + ) + if not MultiRetTypes and FuncName: + func: ir.Function | None = Gen.functions.get(FuncName) + if func: + func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) + if func_ft: + ret_type: ir.Type = func_ft.return_type + if isinstance(ret_type, ir.LiteralStructType): + MultiRetTypes = [None] * len(ret_type.elements) + if not MultiRetTypes: + sym_key: str = FuncName + if not self.Trans.SymbolTable.has(sym_key): + if ModulePath: + sym_key = f"{ModulePath}.{FuncName}" + sym_info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(sym_key) + if sym_info and sym_info.IsFunction: + ret_type_info: CTypeInfo | None = sym_info.FuncPtrReturn + param_type_infos: list[tuple[str, CTypeInfo]] = [pt for _, pt in (sym_info.FuncPtrParams or [])] + if ret_type_info: + if isinstance(ret_type_info, CTypeInfo) and ret_type_info.BaseType: + ret_type: ir.Type = ret_type_info.ToLLVM(Gen) + else: + ret_type = Gen._type_str_to_llvm(str(ret_type_info) if ret_type_info else 'i32', False) + if isinstance(ret_type, ir.LiteralStructType): + MultiRetTypes = [None] * len(ret_type.elements) + if FuncName not in Gen.functions: + if isinstance(ret_type, ir.VoidType): + ret_type = ir.IntType(32) + llvm_param_types: list[ir.Type] = [] + for pt in param_type_infos: + if isinstance(pt, CTypeInfo) and pt.BaseType: + lp: ir.Type = pt.ToLLVM(Gen) + else: + lp = Gen._type_str_to_llvm(str(pt) if pt else 'i32', '*' in str(pt) if isinstance(pt, str) else False) + if isinstance(lp, ir.VoidType): + lp = ir.IntType(8).as_pointer() + llvm_param_types.append(lp) + func_type: ir.FunctionType = ir.FunctionType(ret_type, llvm_param_types) + MangledName: str = Gen._mangle_func_name(FuncName) + # 走唯一幂等入口(module.globals 已有同名声明复用 / + # 类型冲突 fail-fast),避免裸建抛 DuplicatedNameError。 + func_decl: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, func_type) + Gen.functions[FuncName] = func_decl + if not MultiRetTypes and FuncName: + func: ir.Function | None = Gen.functions.get(FuncName) + if func: + func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) + if func_ft: + ret_type: ir.Type = func_ft.return_type + if isinstance(ret_type, ir.LiteralStructType): + MultiRetTypes = [None] * len(ret_type.elements) + if not MultiRetTypes: + # 跨模块多返回值兜底:短名未注册时(函数以 ModulePath.名 / + # sha1.名 全名注册),从其 LiteralStructType 返回类型恢复 MultiRetTypes。 + # 否则 MultiRetTypes 为空会走下方静默 return,目标变量未初始化, + # 运行期 free(垃圾指针) 触发堆损坏(HashLibTest 0xC0000374)。 + func = None + if ModulePath: + func = Gen.functions.get(f"{ModulePath}.{FuncName}") + if func is None: + for key in Gen.functions: + if key.endswith(f".{FuncName}"): + func = Gen.functions[key] + break + if func: + func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) + if func_ft: + ret_type: ir.Type = func_ft.return_type + if isinstance(ret_type, ir.LiteralStructType): + MultiRetTypes = [None] * len(ret_type.elements) + if MultiRetTypes and len(Target.elts) == len(MultiRetTypes): + # 注:此处原有两行 `if func is None: func = ...`,但 func 只在上面 + # 「not MultiRetTypes」分支里绑定;当 MultiRetTypes 来自返回注解 + # (`-> tuple[T1, T2]`)时该分支未执行 ⇒ UnboundLocalError 崩溃。 + # 且紧接着的 `func = Gen.functions.get(FuncName)` 本就覆盖它, + # 故直接删除(`a, b = f()` 因此从未真正跑通)。 + CallArgs: list[ir.Value | None] = [] + for arg in Node.value.args: + ArgVal: ir.Value | None = self.HandleExprLlvm(arg) + if ArgVal: + CallArgs.append(ArgVal) + for kw in Node.value.keywords: + pass + func: ir.Function | None = Gen.functions.get(FuncName) + func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) if func else None + func_params: list[ir.Type] = list(getattr(func_ft, 'args', []) or []) if func_ft else [] + while len(CallArgs) < len(func_params): + FuncDef: ast.FunctionDef | None = self.Trans.FunctionDefCache.get(FuncName) + if FuncDef and len(CallArgs) < len(FuncDef.args.args): + arg_def: list[ast.expr] = FuncDef.args.defaults + arg_idx: int = len(CallArgs) + total_args: int = len(FuncDef.args.args) + default_start: int = total_args - len(arg_def) + if arg_idx >= default_start: + def_idx: int = arg_idx - default_start + if def_idx < len(arg_def): + DefVal: ir.Value | None = self.HandleExprLlvm(arg_def[def_idx]) + if DefVal: + expected_type: ir.Type = func_params[arg_idx] + if DefVal.type != expected_type: + if isinstance(expected_type, ir.IntType) and isinstance(DefVal.type, ir.IntType): + if DefVal.type.width < expected_type.width: + DefVal = Gen.builder.zext(DefVal, expected_type, name=f"zext_default_{arg_idx}") + elif DefVal.type.width > expected_type.width: + DefVal = Gen.builder.trunc(DefVal, expected_type, name=f"trunc_default_{arg_idx}") + CallArgs.append(DefVal) + continue + if len(CallArgs) < len(func_params): + CallArgs.append(ir.Constant(func_params[len(CallArgs)], 0)) + else: + break + adjusted: list[ir.Value] = Gen._adjust_args(CallArgs, func) + call_result: ir.Value = Gen.builder.call(func, adjusted, name=f"call_{FuncName}") + func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) + ret_type: ir.Type | None = getattr(func_ft, 'return_type', None) if func_ft else None + for j, elt in enumerate(Target.elts): + if isinstance(elt, ast.Name): + VarName: str = elt.id + field_val: ir.Value = Gen.builder.extract_value(call_result, j, name=f"extract_{FuncName}_{j}") + VarType: ir.Type + if MultiRetTypes[j] is not None: + ReturnTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(MultiRetTypes[j], self.Trans.SymbolTable) + if ReturnTypeInfo is None: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + VarType = Gen._ctype_to_llvm(ReturnTypeInfo) + elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements): + VarType = ret_type.elements[j] + else: + VarType = field_val.type + if isinstance(VarType, ir.VoidType): + VarType = ir.IntType(32) + var: ir.AllocaInstr = Gen._alloca(VarType, name=VarName) + Gen.variables[VarName] = var + if MultiRetTypes[j] is not None: + Gen._record_var_signedness(VarName, str(ReturnTypeInfo) if ReturnTypeInfo else '') + elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements): + elem_type: ir.Type = ret_type.elements[j] + if isinstance(elem_type, ir.IntType): + if elem_type.width == 16: + Gen._record_var_signedness(VarName, 'unsigned short') + elif elem_type.width == 32: + Gen._record_var_signedness(VarName, 'unsigned int') + elif elem_type.width == 64: + Gen._record_var_signedness(VarName, 'unsigned long long') + Gen._store(field_val, var) + elif isinstance(Node.value, ast.Call): + # fail-fast:多返回值赋值但 MultiRetTypes 解析失败(跨模块签名丢失), + # 绝不能静默跳过——目标变量不会被初始化,运行期 free(垃圾指针) + # 触发堆损坏(HashLibTest 0xC0000374)。 + target_names: list[str] = [ + e.id if isinstance(e, ast.Name) else '' for e in Target.elts + ] + raise Exception( + f"多返回值赋值解析失败: '{FuncName}' 的返回类型无法解析为" + f"返回注解 `-> tuple[...]`(ModulePath={ModulePath}),目标 {target_names} 未被初始化" + f"{Gen._get_node_info()}" + ) + return + + def _InitStructAtPtr(self, call_node: ast.Call, ClassName: str, VarPtr: ir.Value) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + if ClassName not in Gen.structs: + return + StructType: ir.Type = Gen.structs[ClassName] + StructPtrType: ir.PointerType = ir.PointerType(StructType) + RawPtr: ir.Value = Gen._load(VarPtr, name=getattr(VarPtr, 'name', 'ptr')) + StructPtr: ir.Value = Gen.builder.bitcast(RawPtr, StructPtrType, name=ClassName) + members: list[tuple[str, ir.Type]] = Gen.class_members.get(ClassName, []) + defaults: dict[str, ir.Value] = Gen.class_member_defaults.get(ClassName, {}) + member_values: dict[str, ir.Value] = {} + for member_name, member_type in members: + if member_name in defaults: + member_values[member_name] = defaults[member_name] + if call_node.args: + for i, arg in enumerate(call_node.args): + if i < len(members): + member_name: str = members[i][0] + val: ir.Value | None = self.HandleExprLlvm(arg) + if val: + member_values[member_name] = val + if call_node.keywords: + for kw in call_node.keywords: + val: ir.Value | None = self.HandleExprLlvm(kw.value) + if val: + member_values[kw.arg] = val + # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源), + # 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。 + base: int = Gen._member_base(ClassName) + for i, (member_name, member_type) in enumerate(members): + if isinstance(member_type, ir.VoidType): + continue + if member_name in member_values: + ElemPtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + i)], name=f"{ClassName}_{member_name}") + try: + Gen._store(member_values[member_name], ElemPtr) + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + InitFuncName: str = f'{ClassName}.__init__' + if Gen._has_function(InitFuncName): + Args: list[ir.Value | None] = [self.HandleExprLlvm(arg) for arg in call_node.args] + Args = [a for a in Args if a] + Gen.builder.call(Gen._get_function(InitFuncName), [StructPtr] + Args, name=f"call_{InitFuncName}") + + def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + AttrName: str = Target.attr + # const 类字段:给 `obj.字段 = ...` / `self.字段 = ...` 直接赋值同样拒绝 + # (判据 = 字段自身 const,见 BaseGen._IsFieldConst / Debugger/type-descriptors.md)。 + # 类名能解析时按 `类名.字段名` 精确判定;解析不到时退化裸名(保守报错)。 + _TargetCN: str | None = None + if isinstance(Target.value, ast.Name): + _TargetCN = self.Trans.ExprHandler._get_var_class(Target.value, Gen) + if Gen._IsFieldConst(_TargetCN, AttrName): + _vlog().error(Gen._ConstAssignMessage("字段赋值", AttrName)) + sys.exit(1) + if isinstance(Target.value, ast.Name) and Target.value.id == 'self': + ClassName: str | None = self.Trans._CurrentCpythonObjectClass + if ClassName and ClassName in Gen.structs: + PropKey: str = f'{ClassName}.{AttrName}' + PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList: + SelfVar: ir.Value | None = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: ir.Value = Gen._load(SelfVar, name="self") + SetterFunc: ir.Function | None = Gen._get_function(PropKey + '$set') + if SetterFunc and SelfPtr: + Gen.builder.call(SetterFunc, [SelfPtr, Value], name=f"prop_set_{PropKey}") + return + SelfVar: ir.Value | None = Gen._get_var_ptr('self') + if not SelfVar: + return + if SelfVar: + SelfPtr: ir.Value = Gen._load(SelfVar, name="self") + is_vtable_method: bool = False + method_idx: int = 0 + if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes: + if ClassName in Gen.class_methods: + for mi, mn in enumerate(Gen.class_methods[ClassName]): + method_short: str = mn.split('.')[-1] if '.' in mn else mn + if method_short == AttrName or mn == AttrName: + is_vtable_method = True + method_idx = mi + break + if is_vtable_method: + VtableSlotPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}") + VtablePtr: ir.Value = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}") + methods: list[str] = Gen.class_methods[ClassName] + VtableArrayType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) + ClassVtable: ir.GlobalVariable | None = Gen.Vtables.get(ClassName) + if ClassVtable: + ClassVtableAddr: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}") + IsSameVtable: ir.Value = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}") + CopyBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_copy_self_{AttrName}") + SkipBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_skip_self_{AttrName}") + Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock) + Gen.builder.position_at_end(CopyBlock) + Gen._vtable_copy_counter += 1 + CopyName: str = f"{Gen._mangle_name(ClassName)}_vtable_copy_self_{Gen._vtable_copy_counter}" + VtableCopyGlobal: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName) + VtableCopyGlobal.linkage = 'internal' + VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) + VtableCopyTyped: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed") + SrcTyped: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed") + VtableSize: int = len(methods) * Gen.ptr_size + MemcpyFunc: ir.Function | None = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64') + if not MemcpyFunc: + MemcpyType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) + MemcpyFunc = Gen._CreateOrReuseFuncDecl('llvm.memcpy.p0i8.p0i8.i64', MemcpyType) + Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc + Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)]) + VtableCopyGlobalAddr: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr") + Gen._store(VtableCopyGlobalAddr, VtableSlotPtr) + Gen.builder.branch(SkipBlock) + Gen.builder.position_at_end(SkipBlock) + VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy") + VtableTyped: ir.Value = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}") + MethodPtrAddr: ir.Value = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}") + FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}") + Gen._store(FuncPtrI8, MethodPtrAddr) + return + # 自指针基址收口:与**读路径同一判据源**(HandlesExprAttr + # `_ConcretizeGepBase` / `_ConcretePointeeFor`),三步同源: + # ① 类键那份 opaque ⇒ 先 `_TryLoadStructFromStub` 补 body; + # ② 仍不可用 ⇒ 按同短名取一份已具化实例(`_FindConcreteTwin`); + # ③ 都拿不到 ⇒ 原样返回,由下方 opaque fail-fast 报根因。 + # 写路径此前是**内联**版本、缺 ①:类键那份仍 opaque 且 `Gen.structs` + # 里也还没有孪生体时(body 只在 stub 文件里待加载),内联版会一路走到 + # GEP 撞 opaque(现场 2026-09-30:includes/ast/astaux.vp:165 + # `self.arg = _copy_str(pool, arg)` 的基址 pointee 是 opaque 的 + # %"8312ce86a2fd82c0.Keyword"*,而同短名 'Keyword' 键那份已具化 10 槽)。 + # 偏移与 GEP 必须同源:返回的实例同时作为 `_get_member_offset` 的 StructHint。 + SelfPtr, _SelfPointee = self.Trans.ExprAttrHandle._ConcretizeGepBase( + Gen, SelfPtr, ClassName, f"concrete_self_{ClassName}") + struct_type: ir.Type = (_SelfPointee + if isinstance(_SelfPointee, ir.IdentifiedStructType) + else Gen.structs[ClassName]) + if (isinstance(_SelfPointee, ir.IdentifiedStructType) + and _SelfPointee.elements is None): + # 收口失败:类键那份、同短名孪生体、以及 `_TryLoadStructFromStub` + # 三条路都没拿到 body。**不得**再落回下方 `offset >= max_offset` + # 的「存回临时变量」分支 —— 该临时变量不在对象布局里,后续 + # `self.attr` 读路径按 GEP 取值,写入会被**静默丢弃**。此处 fail-fast。 + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE0 + raise _FFE0( + f"属性写入 '{ClassName}.{AttrName}' 的 self 基址指向**未具化**" + f"结构体:'{getattr(_SelfPointee, 'name', '?')}'(opaque)。" + f"已尝试:类键 {ClassName!r} 的实例 → 同短名孪生体 → " + f"_TryLoadStructFromStub 补 body,均未拿到 elements。" + f"请查该类 body 为何从未 set_body(StructGen._generate_structs)。" + + (f"\n {Gen._get_node_info()}" if hasattr(Gen, '_get_node_info') else "")) + offset: int = Gen._get_member_offset(AttrName, ClassName, _SelfPointee) + max_offset: int = (len(struct_type.elements) + if isinstance(struct_type, ir.IdentifiedStructType) + and struct_type.elements is not None else 0) + if offset >= max_offset: + VarName: str = f"self.{AttrName}" + if VarName not in Gen.variables: + NewVar: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=AttrName) + Gen.variables[VarName] = NewVar + Gen._store(Value, Gen.variables[VarName]) + return + MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + # 成员槽写入失败时补「来源明细」:槽类型的两个来源(class_members + # 声明侧 / struct body 布局侧)是两条独立链,不一致时会静默写进 + # 类型完全不同的槽。报文里逐项列出两侧数据,直接判定谁退化了 + # (现场:Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。 + try: + self._StoreWithCoerce(Value, MemberPtr) + except Exception as _Me: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if not isinstance(_Me, _FFE): + raise + raise _FFE( + f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, SelfPtr)}" + ) from _Me + elif isinstance(Target.value, ast.Name): + VarName: str = Target.value.id + + # 模块全局变量写入(治本修复 2026-08-27):X.attr = ... 且 X 是 import + # 模块(含别名:resolve_alias 命中 _ImportedModules)时,必须优先按 + # "模块全局变量"处理——直接 store 到该模块的裸名/带 sha1 前缀全局符号。 + # 该检查必须放在 ClassName heuristic(下方 startswith(CN) 类名匹配)之前: + # 模块名与类名同名(如 CompilerFactory)时,heuristic 会把模块误判为类 + # 而走 _get_member_offset → fail-fast KeyError"属性在类中找不到"。 + # 读端(_lookup_module_global/_get_attr_ptr)已有此判定,写端此前缺失 + # → main.vp 的 `CompilerFactory.factory = CompilerFactory(...)` 从未写入 + # → Generator.__init__ 读 @factory=NULL → ASan 崩 0x8。 + _mod_imported: list[str] | None = getattr(self.Trans, '_ImportedModules', None) + _resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) + # 模块判定:VarName/ResolvedName 直等命中,或与某个已导入模块全名 + # 后缀匹配(如 VarName=CompilerFactory 命中 lib.core.CompilerFactory, + # 与 read 端 _lookup_module_global L212 的 endswith 判定保持一致)。 + _is_mod_write: bool = False + if _mod_imported: + _is_mod_write = (VarName in _mod_imported or _resolved_mod in _mod_imported) + if not _is_mod_write: + for _mn in _mod_imported: + if (_mn == VarName or _mn.endswith(f".{VarName}") + or _mn == _resolved_mod or _mn.endswith(f".{_resolved_mod}")): + _is_mod_write = True + break + if _is_mod_write: + # 优先带前缀:{module_sha1}.{AttrName}(TPC 编译定义端为裸名,VPC 自举 + # 后端为 sha1 前缀名,两代都要覆盖),再回退裸名 {AttrName}。 + _mod_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) + _cands: list[str] = [VarName, _resolved_mod] + for _mn in _mod_imported: + if _mn.endswith(f".{VarName}") or _mn == VarName: + if _mn not in _cands: + _cands.append(_mn) + for _cand in _cands: + _sha1: str | None = _mod_sha1_map.get(_cand) + if _sha1: + _prefixed: str = f"{_sha1}.{AttrName}" + if _prefixed in Gen.module.globals: + _pgv: ir.GlobalVariable = Gen.module.globals[_prefixed] + if isinstance(_pgv, ir.Function): + return + self._StoreWithCoerce(Value, _pgv) + return + if AttrName in Gen.module.globals: + _gv: ir.GlobalVariable = Gen.module.globals[AttrName] + if isinstance(_gv, ir.Function): + return + self._StoreWithCoerce(Value, _gv) + return + + # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) + # 直接通过 a.__field__ = value 访问影子结构体字段 (语法糖) + _fd_meta_name: str = f"{VarName}__meta__" + if _fd_meta_name in Gen.variables and '_str' in Gen.structs: + _fd_offset = Gen._get_member_offset(AttrName, '_str') + if _fd_offset is not None: + _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] + _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") + self._StoreWithCoerce(Value, _fd_field_ptr) + return + + ClassName: str | None = Gen.var_struct_class.get(VarName) + + if not ClassName and Gen.global_struct_class: + ClassName = Gen.global_struct_class.get(VarName) + if ClassName: + Gen.var_struct_class[VarName] = ClassName + + if not ClassName: + for CN in Gen.structs: + if VarName == CN.lower() or VarName.startswith(CN): + ClassName = CN + break + + if not ClassName and VarName in Gen.module.globals: + GVar: ir.GlobalVariable = Gen.module.globals[VarName] + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(GVar.type.pointee) + if found: + ClassName = found[0] + Gen.var_struct_class[VarName] = ClassName + if Gen.global_struct_class: + Gen.global_struct_class[VarName] = ClassName + + if not ClassName and VarName in Gen.variables: + VarPtr: ir.Value = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType): + pointee: ir.Type = VarPtr.type.pointee + if isinstance(pointee, ir.PointerType): + pointee = pointee.pointee + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee) + if found: + CN: str = found[0] + ST: ir.Type = found[1] + ClassName = CN + elif isinstance(pointee, ir.IdentifiedStructType): + # sha1 冲突 fallback:find_struct_by_pointee 因 sha1 前缀不匹配失败时, + # 用短名(去掉 sha1 前缀)匹配,优先选字段最多的完整版本 + pointee_short: str = Gen._extract_short_name(pointee.name) + best_elem_count: int = 0 + for CN, ST in Gen.structs.items(): + if isinstance(ST, ir.IdentifiedStructType): + st_short: str = Gen._extract_short_name(ST.name) + if st_short == pointee_short: + elem_count: int = len(ST.elements) if ST.elements else 0 + if elem_count > best_elem_count: + ClassName = CN + best_elem_count = elem_count + + IsUnion: bool = False + if ClassName: + TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(ClassName) + if TypeInfo and TypeInfo.IsUnion: + IsUnion = True + + if ClassName and ClassName in Gen.structs: + PropKey: str = f'{ClassName}.{AttrName}' + PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList: + ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) + SetterFunc: ir.Function | None = Gen._get_function(PropKey + '$set') + if SetterFunc and ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}") + Gen.builder.call(SetterFunc, [ObjVal, Value], name=f"prop_set_{PropKey}") + return + ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) + if ObjVal: + if BaseHandle._is_char_pointer(ObjVal): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}") + # 跨模块 opaque 类型:ObjVal.type.pointee 与 Gen.structs[ClassName] 可能是 + # 不同的 IdentifiedStructType 对象(虽然 name 相同),== 返回 False。 + # 使用 name 匹配作为 fallback,与链式赋值路径(第 926-927 行)一致。 + # sha1 冲突时还需短名匹配(如 6bc91356c652f8ca.Function vs 8ad0c0a2934ca5b4.Function) + _ObjPointee = ObjVal.type.pointee if isinstance(ObjVal.type, ir.PointerType) else None + _TargetStruct = Gen.structs.get(ClassName) + _MatchesStruct = (_ObjPointee is not None and _TargetStruct is not None and + (_ObjPointee == _TargetStruct or + (isinstance(_ObjPointee, ir.IdentifiedStructType) and isinstance(_TargetStruct, ir.IdentifiedStructType) and + (_ObjPointee.name == _TargetStruct.name or + Gen._extract_short_name(_ObjPointee.name) == Gen._extract_short_name(_TargetStruct.name))))) + # opaque 泛型结构体 → 特化版本 bitcast + # 当 ObjVal 的 pointee 是 opaque 基础泛型结构体(如 GSList,来自跨模块 stub), + # 而 ClassName 是特化版本(如 GSList[BasicBlock])时,两者 short name 不同 + # (GSList vs GSList[BasicBlock]),_MatchesStruct 失败。 + # 验证特化 short name 以 基础名+'[' 开头,bitcast 到特化版本使后续 GEP 能正确访问字段。 + if (not _MatchesStruct + and isinstance(ObjVal.type, ir.PointerType) + and isinstance(_ObjPointee, ir.IdentifiedStructType) + and isinstance(_TargetStruct, ir.IdentifiedStructType) + and '[' in ClassName): + _BaseShort: str = Gen._extract_short_name(_ObjPointee.name) + _SpecShort: str = Gen._extract_short_name(_TargetStruct.name) + if _BaseShort and _SpecShort.startswith(_BaseShort + '['): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(_TargetStruct), name=f"cast_spec_{ClassName}") + _MatchesStruct = True + if isinstance(ObjVal.type, ir.PointerType) and _MatchesStruct: + if IsUnion: + NestedStructName: str = f"{ClassName}_{AttrName}" + if NestedStructName in Gen.structs: + NestedStructType: ir.Type = Gen.structs[NestedStructName] + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") + offset: int | None = Gen._get_member_offset(AttrName, NestedStructName) + if offset is not None and offset < len(NestedStructType.elements): + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + return + bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(ClassName, {}) + if AttrName in bitfield_offsets: + self._store_bitfield_member(ObjVal, ClassName, AttrName, Value) + return + bitfields: dict[str, int] = Gen.class_member_bitfields.get(ClassName, {}) + if AttrName in bitfields and bitfields[AttrName] > 0: + if ClassName not in Gen.class_member_bitoffsets: + Gen.class_member_bitoffsets[ClassName] = {} + if AttrName not in Gen.class_member_bitoffsets[ClassName]: + bo: int = 0 + for bf_name, bf_width in bitfields.items(): + if bf_name == AttrName: + break + bo += bf_width + Gen.class_member_bitoffsets[ClassName][AttrName] = bo + self._store_bitfield_member(ObjVal, ClassName, AttrName, Value) + return + is_vtable_method: bool = False + method_idx: int = 0 + if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes: + if ClassName in Gen.class_methods: + for mi, mn in enumerate(Gen.class_methods[ClassName]): + method_short: str = mn.split('.')[-1] if '.' in mn else mn + if method_short == AttrName or mn == AttrName: + is_vtable_method = True + method_idx = mi + break + if is_vtable_method: + VtableSlotPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}") + VtablePtr: ir.Value = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}") + methods: list[str] = Gen.class_methods[ClassName] + VtableArrayType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) + ClassVtable: ir.GlobalVariable | None = Gen.Vtables.get(ClassName) + if ClassVtable: + ClassVtableAddr: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}") + IsSameVtable: ir.Value = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}") + CopyBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_copy_{AttrName}") + SkipBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_skip_{AttrName}") + Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock) + Gen.builder.position_at_end(CopyBlock) + Gen._vtable_copy_counter += 1 + CopyName: str = f"{Gen._mangle_name(ClassName)}_vtable_copy_{Gen._vtable_copy_counter}" + VtableCopyGlobal: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName) + VtableCopyGlobal.linkage = 'internal' + VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) + VtableCopyTyped: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed") + SrcTyped: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed") + VtableSize: int = len(methods) * Gen.ptr_size + MemcpyFunc: ir.Function | None = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64') + if not MemcpyFunc: + MemcpyType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) + MemcpyFunc = Gen._CreateOrReuseFuncDecl('llvm.memcpy.p0i8.p0i8.i64', MemcpyType) + Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc + Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)]) + VtableCopyGlobalAddr: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr") + Gen._store(VtableCopyGlobalAddr, VtableSlotPtr) + Gen.builder.branch(SkipBlock) + Gen.builder.position_at_end(SkipBlock) + VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy") + VtableTyped: ir.Value = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}") + MethodPtrAddr: ir.Value = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}") + FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}") + Gen._store(FuncPtrI8, MethodPtrAddr) + return + offset: int | None = Gen._get_member_offset(AttrName, ClassName) + StructType: ir.Type | None = Gen.structs.get(ClassName) + if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + StructType = Gen.structs.get(ClassName) + if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0): + return + # 重新计算 offset:_TryLoadStructFromStub 刚加载了 struct body, + # 之前 offset 可能因 struct opaque / class_members 不完整而返回 None + offset = Gen._get_member_offset(AttrName, ClassName) + # sha1 冲突修复:Gen.structs[ClassName] 可能是字段不全的版本(如 2 字段 Function + # 来自 MetaType.py scope,而正确版本是 8 字段 Function 来自 __function.py)。 + # _get_member_offset 通过 class_sha1_map 已返回正确的 offset(如 5), + # 但 StructType 可能只有 2 个 elements,导致 offset < len(elements) 失败。 + # 在所有 structs 中查找同短名且字段最多的版本。 + if offset is not None and isinstance(StructType, ir.IdentifiedStructType) and StructType.elements is not None and offset >= len(StructType.elements): + _short: str = Gen._extract_short_name(StructType.name) + _best: int = len(StructType.elements) + for _cn, _st in Gen.structs.items(): + if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: + if Gen._extract_short_name(_st.name) == _short and len(_st.elements) > _best: + StructType = _st + _best = len(_st.elements) + # bitcast:当 ObjVal 的 pointee 是 opaque 或与 StructType 不是同一个类型对象 + # (sha1 前缀不同但短名相同)时,需要 bitcast 到 StructType 才能正确 GEP + if StructType is not None and StructType.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and (ObjVal.type.pointee.elements is None or ObjVal.type.pointee != StructType): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(StructType), name=f"cast_{ClassName}") + if offset is not None and StructType is not None and StructType.elements is not None and offset < len(StructType.elements): + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + else: + return + + byte_order: str = Gen.class_member_byteorders.get(ClassName, {}).get(AttrName, "") + + if byte_order == 'big': + if StructType is None or StructType.elements is None or len(StructType.elements) == 0: + return + member_type: ir.Type = StructType.elements[offset] + if isinstance(member_type, ir.IntType): + Value = Gen._apply_bswap_if_big(Value, byte_order, f"bswap_store_{AttrName}") + + # 成员槽写入失败时补「来源明细」(与 `self.x = ...` 路径 :897-905 + # 同一套 `_DescribeMemberSlot`):槽类型的两条独立来源 + # (class_members 声明侧 / struct body 布局侧)不一致时,值会被 + # 写进类型完全不同的槽(现场:`self.Gen.TypeGenCType. + # LastBuiltinsTypedefResult = "t.CPtr"` 落到 i32 槽 → 指针隐式 + # 转整数 fail-fast);报文里逐项列出两侧数据即可直接判定谁退化 + # (此处目标形态是 `对象.字段 = 值`,Base 侧同理取 ObjVal 所指布局)。 + try: + self._StoreWithCoerce(Value, MemberPtr) + except Exception as _Me: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if not isinstance(_Me, _FFE): + raise + raise _FFE( + f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, ObjVal)}" + ) from _Me + if not ClassName: + imported_modules: list[str] | None = getattr(self.Trans, '_ImportedModules', None) + resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) + if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules): + PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + key: str = f"{mod_name}.{AttrName}" + if key not in PossibleKeys: + PossibleKeys.append(key) + for key in PossibleKeys: + if key in Gen.module.globals: + GVar: ir.GlobalVariable = Gen.module.globals[key] + if isinstance(GVar, ir.Function): + return + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return + self._StoreWithCoerce(Value, GVar) + return + ModuleSha1Map: dict[str, str] = Gen.ModuleSha1Map + sha1_candidates: list[str] = [VarName, resolved_mod] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + if mod_name not in sha1_candidates: + sha1_candidates.append(mod_name) + for cand in sha1_candidates: + sha1: str | None = ModuleSha1Map.get(cand) + if sha1: + prefixed: str = f"{sha1}.{AttrName}" + if prefixed in Gen.module.globals: + GVar2: ir.GlobalVariable = Gen.module.globals[prefixed] + if isinstance(GVar2, ir.Function): + return + if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return + self._StoreWithCoerce(Value, GVar2) + return + elif isinstance(Target.value, ast.Attribute): + ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) + if ObjVal and isinstance(ObjVal.type, ir.PointerType): + pointee: ir.Type = ObjVal.type.pointee + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: + for CN, ST in Gen.structs.items(): + if isinstance(ST, ir.IdentifiedStructType) and ST.name == pointee.name and ST.elements is not None: + pointee = ST + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") + break + found_match: bool = False + for CN, ST in Gen.structs.items(): + if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name): + found_match = True + if ST.elements is None: + self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen) + ST = Gen.structs.get(CN, ST) + if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") + bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(CN, {}) + if AttrName in bitfield_offsets: + self._store_bitfield_member(ObjVal, CN, AttrName, Value) + return + offset: int = Gen._get_member_offset(AttrName, CN) + if offset is not None and ST.elements is not None and offset < len(ST.elements): + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + break + if not found_match: + if isinstance(pointee, ir.IdentifiedStructType): + matched_cn: str | None = None + for CN in Gen.class_members: + members: list[tuple[str, ir.Type]] = Gen.class_members[CN] + for mname, mtype in members: + if mname == AttrName: + matched_cn = CN + break + if matched_cn: + break + if matched_cn and matched_cn in Gen.structs: + ST: ir.Type = Gen.structs[matched_cn] + if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(matched_cn, Gen) + ST = Gen.structs.get(matched_cn, ST) + if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{matched_cn}") + offset: int = Gen._get_member_offset(AttrName, matched_cn) + if offset is not None and ST.elements is not None and offset < len(ST.elements): + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + return + elif isinstance(Target.value, ast.Call): + # Check if this is c.Deref(...) — we need the pointer, not the Loaded value + ObjVal: ir.Value | None = None + is_cderef: bool = (isinstance(Target.value.func, ast.Attribute) and + isinstance(Target.value.func.value, ast.Name) and + Target.value.func.value.id == 'c' and + Target.value.func.attr == 'Deref' and + Target.value.args) + if is_cderef: + # Get the pointer from c.Deref argument, don't Load the value + deref_arg: ast.expr = Target.value.args[0] + inner_val: ir.Value | None = self.HandleExprLlvm(deref_arg) + if inner_val and isinstance(inner_val.type, ir.PointerType): + if isinstance(inner_val.type.pointee, ir.PointerType): + ObjVal = Gen._load(inner_val, name="deref_load_ptr") + else: + ObjVal = inner_val + if ObjVal is None: + ObjVal = self.Trans.ExprHandler.HandleExprLlvm(Target.value) + if ObjVal and isinstance(ObjVal.type, ir.PointerType): + pointee: ir.Type = ObjVal.type.pointee + if isinstance(pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr") + pointee = ObjVal.type.pointee + for CN, ST in Gen.structs.items(): + if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name): + if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen) + ST = Gen.structs.get(CN, ST) + if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") + bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(CN, {}) + if AttrName in bitfield_offsets: + self._store_bitfield_member(ObjVal, CN, AttrName, Value) + return + offset: int = Gen._get_member_offset(AttrName, CN) + if offset is not None and ST.elements is not None and offset < len(ST.elements): + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + break + elif isinstance(Target.value, ast.Subscript): + SubPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value) + if SubPtr and isinstance(SubPtr.type, ir.PointerType): + pointee: ir.Type = SubPtr.type.pointee + + if isinstance(Target.value.value, ast.Attribute): + AttrNameSub: str = Target.value.value.attr + if AttrNameSub == 'items' and 'Item' in Gen.structs: + ObjValCast: ir.Value = Gen.builder.bitcast(SubPtr, ir.PointerType(Gen.structs['Item']), name=f"cast_Item") + offset: int = Gen._get_member_offset(AttrName, 'Item') + MemberPtr: ir.Value = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + return + + if BaseHandle._is_char_pointer(SubPtr): + for CN, ST in Gen.structs.items(): + ObjValCast: ir.Value = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}") + offset: int = Gen._get_member_offset(AttrName, CN) + MemberPtr: ir.Value = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + break + elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee) + if found: + CN: str = found[0] + ST: ir.Type = found[1] + if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen) + ST = Gen.structs.get(CN, ST) + if ST.elements is not None and isinstance(SubPtr.type, ir.PointerType) and isinstance(SubPtr.type.pointee, ir.IdentifiedStructType) and SubPtr.type.pointee.elements is None: + SubPtr = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}") + offset: int = Gen._get_member_offset(AttrName, CN) + if offset is not None and ST.elements is not None and offset < len(ST.elements): + MemberPtr: ir.Value = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + elif isinstance(pointee, ir.PointerType): + LoadedPtr: ir.Value = Gen._load(SubPtr, name=f"Load_{AttrName}_ptr") + if isinstance(LoadedPtr.type, ir.PointerType) and isinstance(LoadedPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(LoadedPtr.type.pointee) + if found: + CN: str = found[0] + ST: ir.Type = found[1] + offset: int = Gen._get_member_offset(AttrName, CN) + MemberPtr: ir.Value = Gen.builder.gep(LoadedPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + + def _StoreArrElemWithCoerceBswap(self, Value: ir.Value, Ptr: ir.Value, arr_byte_order: str, + Node: ast.AST | None = None) -> None: + """对数组元素存储应用 bswap 后再 coerce 存储""" + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + Value = Gen._apply_bswap_if_big(Value, arr_byte_order, "bswap_arr_store") + self._StoreWithCoerce(Value, Ptr, Node) + + def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value, + Node: ast.AST | None = None) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + # pointee-volatile 基址名后缀(与 HandlesExprAttr._HandleSubscriptLlvm 同款); + # 空后缀 ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。 + _PVSuffix: str = Gen._SubscriptSuffix(Target.value) + # 查询数组元素字节序 + arr_byte_order: str = '' + if isinstance(Target.value, ast.Name) and Target.value.id in Gen.local_var_byteorders: + arr_byte_order = Gen.local_var_byteorders[Target.value.id] + if isinstance(Target.value, ast.Name): + SubVarName: str = Target.value.id + if Gen._IsVarConst(SubVarName): + # 与 Name 赋值/增量赋值同一条 fail-fast(原先此处 LogError 后继续, + # 属静默继续;现统一 error + exit,判据与报文两侧一致)。 + _vlog().error(Gen._ConstAssignMessage("的元素赋值", SubVarName)) + sys.exit(1) + ClassName: str | None = self.Trans.ExprHandler._get_var_class(Target.value, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__setitem__'): + obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) + idx_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.slice) + if obj_val and idx_val: + # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 + if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: + obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") + if BaseHandle._is_char_pointer(obj_val): + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + setitem_args: list[ir.Value] = [obj_val, idx_val, Value] + setitem_func_name: str = f'{ClassName}.__setitem__' + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + setitem_func: ir.Function = Gen._get_function(setitem_func_name) + setitem_args = Gen._adjust_args(setitem_args, setitem_func) + Gen.builder.call(setitem_func, setitem_args, name=f"call_{setitem_func_name}") + return + ValueVal: ir.Value | None = self.HandleExprLlvm(Target.value) + IndexVal: ir.Value | None = self.HandleExprLlvm(Target.slice) + if not ValueVal or not IndexVal: + return + # 当变量本身是二级指针(如 elem_ptr: T | t.CPtr = AST**), + # 且存储的值也是指针时,elem_ptr[i] = item 应直接存储指针(8 字节), + # 而非解引用后复制结构体(如 48 字节的 AST),避免对象切片 + if isinstance(Target.value, ast.Name): + VarName: str = Target.value.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarAlloca: ir.Value = Gen.variables[VarName] + if isinstance(VarAlloca.type, ir.PointerType) and isinstance(VarAlloca.type.pointee, ir.PointerType): + if (isinstance(ValueVal.type, ir.PointerType) and isinstance(Value.type, ir.PointerType) + and isinstance(Value.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store") + # ValueVal 是 AST*(加载自 elem_ptr 的基指针), + # 但 GEP 默认将其视为 AST 结构体数组。需要先 bitcast 为 + # AST**(指向 AST* 的指针),让 GEP 正确索引 AST* 指针数组, + # 而非 AST 结构体数组。然后用 builder.store 直接存储指针, + # 避免 _store 的类型强制转换将指针解引用为结构体。 + PtrToStruct: ir.Type = Value.type.pointee + BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal, + ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp") + ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name=f"subscript{_PVSuffix}") + Gen.builder.store(Value, ElemPtr) + return + if BaseHandle._is_char_pointer(IndexVal): + Loaded_byte: ir.Value = Gen._load(IndexVal, name="Load_char_idx_store") + IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_store") + if isinstance(ValueVal.type, ir.IntType): + var_ptr: ir.Value | None = self._get_int_ptr(Target.value) + if var_ptr: + i8_ptr: ir.Value = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr_store") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index_store") + ptr: ir.Value = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr_store") + self._StoreWithCoerce(Value, ptr) + return + if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store") + ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") + self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order, Node) + return + if isinstance(ValueVal.type, ir.PointerType): + pointee: ir.Type = ValueVal.type.pointee + if isinstance(pointee, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}") + elif (isinstance(pointee, ir.IntType) and pointee.width == 8 + and isinstance(Target.value, ast.Name) + and (Gen.var_ptr_element.get(Target.value.id, False) + or Gen.global_var_ptr_element.get(Target.value.id, False))): + # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr) + # elem_ptr 是 i8* 但实际指向 i8* (字符串指针), elem_ptr[0] = item 应存储 8 字节 + CastedPtrPtr: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_store") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_store") + ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") + self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) + return + else: + ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") + # fail-fast:rawptr[T](基座是指针存储)的元素为对象 T、而 RHS 是指向 T + # 的指针时,下游 _StoreWithCoerce/_coerce_value 会静默 load 整对象再按 + # sizeof(T) 存储(写 sizeof(T) 而非 8 字节指针)。数组槽(t.CArray[T,N]) + # 本就按 sizeof(T) 布局,整对象赋值合法 ⇒ 由 IsArrayBase 排除。 + self._RejectWholeObjectCopyIntoPtrSlot( + Target, Value, ElemPtr, isinstance(pointee, ir.ArrayType)) + self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) + elif isinstance(ValueVal.type, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + if isinstance(Target.value, ast.Name): + VarName: str = Target.value.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: ir.Value = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr: ir.Value = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) + return + if isinstance(Target.value, ast.Subscript): + InnerPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value) + if InnerPtr: + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr: ir.Value = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) + return + if isinstance(Target.value, ast.Attribute): + AttrPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value) + if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr: ir.Value = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) + return + arr_alloc: ir.AllocaInstr = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") + Gen._store(ValueVal, arr_alloc) + ElemPtr: ir.Value = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}") + self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) + ModifiedArr: ir.Value = Gen._load(arr_alloc, name="modified_arr") + if isinstance(Target.value, ast.Subscript): + OuterPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value) + if OuterPtr: + Gen._store(ModifiedArr, OuterPtr) + elif isinstance(Target.value, ast.Attribute): + OuterPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value) + if OuterPtr: + Gen._store(ModifiedArr, OuterPtr) + @staticmethod def _StructTypeKey(ty: ir.Type) -> str: """结构体类型的稳定标识(identified 用 name,literal 用类型文本)""" @@ -1642,7 +1644,7 @@ class AssignHandle(BaseHandle): # · 元素是标量(rawptr[int]):ElemPtr 元素不是结构体; # · 元素本身是指针(rawptr[rawptr[T]] / X|t.CPtr):元素类型是 Ptr; # · RHS 所指类型与元素类型不一致(非本形态)。 - # 与 TPV AssignGenMixin._reject_whole_object_copy_into_ptr_slot 同口径。 + # 与 VPC AssignGenMixin._reject_whole_object_copy_into_ptr_slot 同口径。 # ============================================================ def _RejectWholeObjectCopyIntoPtrSlot(self, Target: ast.expr, Value: ir.Value, ElemPtr: ir.Value, IsArrayBase: bool) -> None: @@ -1743,69 +1745,69 @@ class AssignHandle(BaseHandle): pass return f"\n [成员槽来源] self.{FieldName} = ...\n" + "\n".join(Lines) - def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - if isinstance(Ptr.type, ir.PointerType): - TargetType: ir.Type = Ptr.type.pointee - if Value.type != TargetType: - if Gen._IsNoneValue(Value, Node): - # None(空值常量)允许隐式转换为任意目标类型:指针→空指针,其余→零值。 - Value = (ir.Constant(TargetType, None) if isinstance(TargetType, ir.PointerType) - else Gen._ZeroConst(TargetType)) - elif isinstance(TargetType, ir.ArrayType) and isinstance(Value.type, ir.PointerType): - ElemType: ir.Type = TargetType.element - ElemSize: int = Gen._get_struct_size(ElemType) - for i in range(TargetType.count): - DstElem: ir.Value = Gen.builder.gep(Ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"arr_dst_{i}") - SrcPtr: ir.Value - if ElemSize == 1: - SrcPtr = Gen.builder.gep(Value, [ir.Constant(ir.IntType(32), i)], name=f"arr_src_{i}") - else: - PtrInt: ir.Value = Gen.builder.ptrtoint(Value, ir.IntType(64)) - Offset: ir.Constant = ir.Constant(ir.IntType(64), i * ElemSize) - NewPtrInt: ir.Value = Gen.builder.add(PtrInt, Offset) - SrcPtr = Gen.builder.inttoptr(NewPtrInt, ir.PointerType(ElemType), name=f"arr_src_{i}") - SrcVal: ir.Value = Gen._load(SrcPtr, name=f"arr_elem_{i}") - if SrcVal.type != ElemType: - try: - SrcVal = Gen.builder.bitcast(SrcVal, ElemType, name=f"arr_cast_{i}") - except Exception: # 回退:bitcast 失败时跳过该元素 - continue - Gen._store(SrcVal, DstElem) - return - elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType): - # 新增指针隐式转换规则①/②:通用指针通配放行;非通用 T*→U* 仅在 - # 被指类型无损时放行,非基本/有损/层数不同 fail-fast。 - Allowed: bool - Reason: str - Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType, Node) - if not Allowed: - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转换已禁止({Reason}):不能把指针类型 {Value.type} 隐式转为 {TargetType};" - f"如需转换请显式 cast(如 t.CPtr(...))" - f"{Gen._DescribeValueNode(Node)}" - ) - Value = Gen.builder.bitcast(Value, TargetType, name="ptr_bitcast") - else: - Coerced: ir.Value | None = Gen._coerce_value(Value, TargetType, Node) - if Coerced is not None: - Value = Coerced - Gen._store(Value, Ptr, Node=Node) - - - def _get_int_ptr(self, target: ast.expr) -> ir.Value | None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - if isinstance(target, ast.Name): - VarName: str = target.id - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: ir.Value = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType): - pointee: ir.Type = VarPtr.type.pointee - if isinstance(pointee, ir.ArrayType): - return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}") - return None - + def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + if isinstance(Ptr.type, ir.PointerType): + TargetType: ir.Type = Ptr.type.pointee + if Value.type != TargetType: + if Gen._IsNoneValue(Value, Node): + # None(空值常量)允许隐式转换为任意目标类型:指针→空指针,其余→零值。 + Value = (ir.Constant(TargetType, None) if isinstance(TargetType, ir.PointerType) + else Gen._ZeroConst(TargetType)) + elif isinstance(TargetType, ir.ArrayType) and isinstance(Value.type, ir.PointerType): + ElemType: ir.Type = TargetType.element + ElemSize: int = Gen._get_struct_size(ElemType) + for i in range(TargetType.count): + DstElem: ir.Value = Gen.builder.gep(Ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"arr_dst_{i}") + SrcPtr: ir.Value + if ElemSize == 1: + SrcPtr = Gen.builder.gep(Value, [ir.Constant(ir.IntType(32), i)], name=f"arr_src_{i}") + else: + PtrInt: ir.Value = Gen.builder.ptrtoint(Value, ir.IntType(64)) + Offset: ir.Constant = ir.Constant(ir.IntType(64), i * ElemSize) + NewPtrInt: ir.Value = Gen.builder.add(PtrInt, Offset) + SrcPtr = Gen.builder.inttoptr(NewPtrInt, ir.PointerType(ElemType), name=f"arr_src_{i}") + SrcVal: ir.Value = Gen._load(SrcPtr, name=f"arr_elem_{i}") + if SrcVal.type != ElemType: + try: + SrcVal = Gen.builder.bitcast(SrcVal, ElemType, name=f"arr_cast_{i}") + except Exception: # 回退:bitcast 失败时跳过该元素 + continue + Gen._store(SrcVal, DstElem) + return + elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType): + # 新增指针隐式转换规则①/②:通用指针通配放行;非通用 T*→U* 仅在 + # 被指类型无损时放行,非基本/有损/层数不同 fail-fast。 + Allowed: bool + Reason: str + Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType, Node) + if not Allowed: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针隐式转换已禁止({Reason}):不能把指针类型 {Value.type} 隐式转为 {TargetType};" + f"如需转换请显式 cast(如 t.CPtr(...))" + f"{Gen._DescribeValueNode(Node)}" + ) + Value = Gen.builder.bitcast(Value, TargetType, name="ptr_bitcast") + else: + Coerced: ir.Value | None = Gen._coerce_value(Value, TargetType, Node) + if Coerced is not None: + Value = Coerced + Gen._store(Value, Ptr, Node=Node) + + + def _get_int_ptr(self, target: ast.expr) -> ir.Value | None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + if isinstance(target, ast.Name): + VarName: str = target.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: ir.Value = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType): + pointee: ir.Type = VarPtr.type.pointee + if isinstance(pointee, ir.ArrayType): + return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}") + return None + def _BuildStringPtrInitializer(self, Gen: "Translator.LlvmGen", VarName: str, str_value: str) -> ir.Constant: """模块级 `X: str = "字面量"` 全局的初始化器:指向常量字符数组**首元素** @@ -1830,599 +1832,599 @@ class AssignHandle(BaseHandle): zero: ir.Constant = ir.Constant(ir.IntType(32), 0) return ir.Constant.gep(str_gv, [zero, zero]) - def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None: - # 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列): - # 模块级全局注解不经过 ResolveAnnotationType,故在此显式校验。 - self._CheckFixedArrayKeyword(Node.annotation) - if isinstance(Node.target, ast.Name): - VarName: str = Node.target.id - Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName) - if Info: - if Info.IsTypedef: - return - # 模块级全局 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`): - # 先解析(空/缺段名、非正整数字面量 aligned → fail-fast 终止),再在各 - # 全局创建点用 _ApplyGlobalAttrs 施加。此前 TPC 全局路径完全没有 - # section/align/weak 代码(函数侧 section 只来自装饰器,见 - # HandlesFunctions.py:1361-1362 → :1767)。 - GlobalAttrs: dict[str, Any] = self._ExtractGlobalAttrs(Node, Gen) - parse_result = ParseListAnnotation(Node.annotation) - if parse_result: - elem_type_node: ast.expr = parse_result.elem_type_node - ElemType, ElemTypeInfo = self.ResolveListElementType( - elem_type_node, Gen, - check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True, - ) - if parse_result.is_pointer: - # 单参数 t.CArray[T]:与 t.CArray[T, None] 统一为"长度自动推导数组"。 - # 有可推导初值(列表字面量 / 字符串字面量)时不再生成裸指针,而是落入 - # 下方数组路径:由 ParseArrayCount(count_node=None) 从字面量推导长度、 - # 字节内联到变量自身(与类成员 DeclarationGenerator.py:775-784、 - # 局部 HandlesAnnAssign.py:446-489 同一口径;用户语义:单参数即 - # "长度自动推导",不再是隐式指针)。 - # 无可推导初值(纯声明 / 真正的指针用法)时保持既有指针语义逐字不变。 - HasDerivableInit: bool = bool( - Node.value and ( - isinstance(Node.value, ast.List) - or (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str)) - ) - ) - if not HasDerivableInit: - VarType: ir.Type = ir.PointerType(ElemType) - if VarName not in Gen.variables and VarName not in Gen.module.globals: - GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) - GlobalVar.initializer = ir.Constant(VarType, None) - self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) - Gen.variables[VarName] = GlobalVar - if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: - Gen.global_struct_class[VarName] = elem_type_node.id - return - # 有初值 → 落入下方数组路径(count_node=None → 从字面量推导长度) - count_node: ast.expr | None = parse_result.count_node - ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global') - VarType = ir.ArrayType(ElemType, ArrayCount) - if VarName not in Gen.variables and VarName not in Gen.module.globals: - GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName) - if isinstance(ElemType, ir.IdentifiedStructType): - GlobalVar.linkage = 'common' - elif Node.value and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str): - str_val: str = Node.value.value + '\x00' - str_bytes: bytearray = bytearray(str_val, 'utf-8') - if len(str_bytes) < ArrayCount: - str_bytes.extend(b'\x00' * (ArrayCount - len(str_bytes))) - try: - GlobalVar.initializer = ir.Constant(VarType, str_bytes[:ArrayCount]) - except Exception: # 回退:初始化常量失败时用零值 - GlobalVar.initializer = Gen._zero_value_for_type(VarType) - else: - InitConstants: list[ir.Constant] | None = self.Trans._BuildArrayInitConstants(Node.value, ElemType, elem_type_node, ArrayCount, Gen, byte_order=ElemTypeInfo.ByteOrder if ElemTypeInfo else '') - if InitConstants: - GlobalVar.initializer = ir.Constant(VarType, InitConstants) - elif ArrayCount > 256: - GlobalVar.linkage = 'common' - else: - if self._contains_identified_struct(VarType): - GlobalVar.linkage = 'common' - else: - GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount) - self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) - Gen.variables[VarName] = GlobalVar - if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: - Gen.global_struct_class[VarName] = elem_type_node.id - return - if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs: - StructName: str = Node.annotation.id - VarType: ir.Type = Gen.structs[StructName] - if VarName not in Gen.variables and VarName not in Gen.module.globals: - GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) - if Node.value and isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == StructName: - const: ir.Constant | None = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen) - if const: - GlobalVar.initializer = const - else: - GlobalVar.initializer = ir.Constant(VarType, None) - else: - GlobalVar.initializer = ir.Constant(VarType, None) - self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) - Gen.variables[VarName] = GlobalVar - Gen.global_struct_class[VarName] = StructName - return - TypeInfo: CTypeInfo | None = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) - IsCDefine: bool = False - if TypeInfo and TypeInfo.BaseType: - if isinstance(TypeInfo.BaseType, t._CTypedef): - return - if isinstance(TypeInfo.BaseType, t.CDefine): - IsCDefine = True - if not IsCDefine and self._IsTypedefAnnotation(Node.annotation): - IsCDefine = True - TTypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation) - if TTypeInfo: - TTypeInfo.IsTypedef = True - TTypeInfo.Name = VarName - self.Trans.SymbolTable.insert(VarName, TTypeInfo) - if IsCDefine: - if Node.value: - define_constants: dict[str, int | float] = vars(Gen).setdefault('_define_constants', {}) - try: - const_value: int | float | None = self._eval_const_expr(Node.value, Gen) - if const_value is not None: - define_constants[VarName] = const_value - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - return - if TypeInfo is None: - TypeInfo = CTypeInfo() - TypeInfo.BaseType = t.CInt() - VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo) - if isinstance(VarType, ir.VoidType): - VarType = ir.IntType(32) - # t.State 标记的模块级全局(`extern 名字: 类型` 的展开产物,见 - # SymbolUtils._ExpandExportExternVar):**只声明不定义**(external linkage), - # 符号定义在外部(C 库 / 汇编 / 其它翻译单元),本模块不得再生成定义 - # (否则 ld.lld duplicate symbol)。与 import 侧 - # _EmitExternalGlobalDeclLlvm 的同名分支(HandlesImports.py:1211-1217)同口径。 - # 位置必须早于下方「VarName 已在 module.globals → 升级 common 定义」分支: - # 否则 extern 会被反向升级成定义,抵消 external 声明语义。 - if TypeInfo is not None and TypeInfo.IsState: - if VarName not in Gen.module.globals: - _ext_gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) - _ext_gv.linkage = 'external' - Gen._export_funcs.add(VarName) - Gen.variables[VarName] = Gen.module.globals[VarName] - return - # const / volatile 模块级全局登记(统一登记入口,持久层 global_*)。 - # 局限(明确记录,见 Debugger/type-descriptors.md 已知差异):本分支只覆盖 - # 标量/指针型全局;数组(t.CArray[...])与结构体型全局在更早的分支 - # (上方 1568-1627)已 return,不在此登记 —— 全仓普查无这类带 const/ - # volatile 注解的全局,故无可观测差异。 - Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=True) - # 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考, - # 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。 - if not hasattr(Gen, '_module_ann_types'): - Gen._module_ann_types = {} - Gen._module_ann_types[VarName] = VarType - if VarName in Gen.variables or VarName in Gen.module.globals: - # 根因修复:import 端 _EmitExternalGlobalDeclLlvm 会把被 import 模块 - # 的模块级全局(如下划线私有 _g_gen)以裸名 external 声明注入本模块 - # module.globals,导致本模块同名全局定义被此分支跳过 → 全工程无定义 - # (ld.lld undefined symbol: _g_gen)。external 声明遇到本模块定义时 - # 应升级为 common 定义(多模块同名合并为零值,对齐 None 语义), - # 而非静默跳过。 - existing_gv = Gen.module.globals.get(VarName) if VarName in Gen.module.globals else None - # 注:llvmlite.ir.GlobalVariable 无 is_declaration 属性(binding 层才有), - # 声明/定义靠 initializer 区分:external 声明 initializer 恒为 None。 - if isinstance(existing_gv, ir.GlobalVariable) and getattr(existing_gv, 'initializer', None) is None: - try: - if (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) - and isinstance(getattr(existing_gv, 'value_type', None), ir.PointerType)): - # 字符串字面量全局:补"指向常量数组首元素"的初始化器。 - # **不能**设 common —— LLVM 要求 common 全局的初始化器必须是 - # 零值(verifier: "'common' global must have a zero - # initializer!"),配非空初值直接非法;保持默认 external - # 强定义即可跨模块共享(Config.BackendTarget / - # _inc_bin_suffix 都在运行时被赋值/读取,符号必须唯一且可见)。 - existing_gv.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value) - else: - existing_gv.linkage = 'common' - if Node.value is None or (isinstance(Node.value, ast.Constant) and Node.value.value is None): - existing_gv.initializer = ir.Constant(existing_gv.type.pointee, None) - Gen.variables[VarName] = existing_gv - except Exception: - pass - return - # 时序修复:第一遍创建全局时类未处理(SymbolTable 无枚举成员), - # initializer 回退为 0。第二遍类已处理,尝试用解析出的枚举值更新 initializer。 - if VarName in Gen.module.globals and Node.value: - existing: Any = Gen.module.globals[VarName] - if isinstance(existing, ir.GlobalVariable) and isinstance(VarType, ir.IntType): - new_init: ir.Constant | None = self._eval_global_const(Node.value, VarType, Gen) - if new_init is not None: - existing.initializer = new_init - return - if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)): - GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) - GlobalVar.initializer = ir.Constant(VarType, None) - self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) - Gen.variables[VarName] = GlobalVar - if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name: - Gen.global_struct_class[VarName] = TypeInfo.Name - return - GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) - # 全局变量指针元素标志:bytes|t.CPtr 或 str|t.CPtr 注解的变量, - # 使 var[index] 使用 8 字节指针步长(而非 1 字节字符步长) - if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): - if AnnotationContainsName(Node.annotation, 'str') or AnnotationContainsName(Node.annotation, 'bytes'): - Gen.global_var_ptr_element[VarName] = True - if Node.value: - if isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) and isinstance(VarType, ir.PointerType): - # 字符串字面量全局:初始化器 = 指向 @{VarName}_str 常量数组首元素 - # 的 GEP。此前这里在创建 GlobalVar(上方数行)之后又查 - # `VarName not in Gen.module.globals` —— 该条件恒为假(GlobalVar - # 刚被登记),于是永远走 else 分支、**从不写初始化器**:全工程模块级 - # `str = "字面量"` 全局退化成无初值的声明,第二遍再被升级成 - # `common global i8* null`,字符串常量沦为无人引用的 @NAME_str。 - # 只读常量(如 BuildPipeline.MLIR_OPT_CMD)在 mlir 后端 - # strlen(NULL) 直接崩(ASan 0x0)。 - if getattr(GlobalVar, 'initializer', None) is None: - GlobalVar.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value) - Gen.variables[VarName] = GlobalVar - elif isinstance(VarType, ir.PointerType) and isinstance(VarType.pointee, ir.FunctionType) and isinstance(Node.value, ast.Name): - # 函数指针赋值:f3: t.Callable[[int], int] = f - RefName: str = Node.value.id - SymInfo: Any = self.Trans.SymbolTable.lookup(RefName) - if SymInfo and (SymInfo.IsFunction or SymInfo.IsFuncPtr): - MangledName: str = Gen._mangle_func_name(RefName) - if MangledName in Gen.module.globals: - func: Any = Gen.module.globals[MangledName] - if isinstance(func, ir.Function): - GlobalVar.initializer = func - else: - if isinstance(VarType, ir.ArrayType) and isinstance(Node.value, (ast.List, ast.Set)): - InitConstants: list[ir.Constant] | None = self.Trans._BuildMultiDimArrayInitConstants(Node.value, VarType, Gen) - if InitConstants: - GlobalVar.initializer = ir.Constant(VarType, InitConstants) - else: - if self._contains_identified_struct(VarType): - GlobalVar.linkage = 'common' - else: - zv: ir.Constant | None = Gen._zero_value_for_type(VarType) - if zv: - GlobalVar.initializer = zv - else: - GlobalVar.linkage = 'common' - else: - init_val: ir.Constant | None = self._eval_global_const(Node.value, VarType, Gen) - if init_val: - GlobalVar.initializer = init_val - elif isinstance(VarType, ir.BaseStructType) or (isinstance(Node.value, ast.Constant) and Node.value.value is None): - # None 初值全局(如各 Handles 模块的 _g_gen: t.CVoid|t.CPtr = None): - # 多模块同名定义用 common linkage 让链接器合并(避免 - # ld.lld duplicate symbol)。common 无 initializer,合并为零值, - # 符合 None(=null) 语义。 - GlobalVar.linkage = 'common' - else: - GlobalVar.initializer = Gen._zero_value_for_type(VarType) - else: - if isinstance(VarType, ir.BaseStructType) or self._contains_identified_struct(VarType): - GlobalVar.linkage = 'common' - else: - GlobalVar.initializer = Gen._zero_value_for_type(VarType) - # 属性最后施加:weak 需覆盖分支内可能设过的 'common'(对齐 TransPyV - # TranslatorGenMixin.vp:926-929 先清 Linkage 再设 weak 的语义)。 - self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) - Gen.variables[VarName] = GlobalVar - - # ====================================================================== - # 模块级全局的 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`) - # - # 镜像 TransPyV(Viper 自举侧)已有实现: - # TranslatorGenMixin._scan_global_tattr / _extract_global_attr_section / - # _extract_global_attr_aligned / _extract_global_attr_weak(.vp:590-651) - # + ClassGenBase._tattr_name(.vp:341-358)+ _class_has_tattr 的 - # `c.Attribute(t.attr.xxx)` 解包(.vp:359-394)。 - # TPC(Python 侧)此前完全没有全局 section/align/weak 代码。 - # ====================================================================== - def _TAttrName(self, node: ast.AST | None) -> str | None: - """从 t.attr. / t.attr.(args) 节点提取最外层属性名。 - - 对应 TransPyV ClassGenBase._tattr_name:Call → 递归取 func; - Attribute → 取 attr。 - """ - if isinstance(node, ast.Call): - return self._TAttrName(node.func) - if isinstance(node, ast.Attribute): - return node.attr - return None - - def _ScanGlobalTAttr(self, node: ast.AST | None, want_name: str) -> ast.AST | None: - """在全局变量注解中查找 t.attr. 节点,命中返回该节点。 - - 注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)), - 递归左右子节点。命中节点形态:Call(t.attr.aligned(16))或 - Attribute(t.attr.weak)。同时兼容包装写法 - `T | c.Attribute(t.attr.section("..."))`(解包参考 TransPyV - ClassGenBase._class_has_tattr,.vp:375-389)。 - """ - if node is None: - return None - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - hit_left: ast.AST | None = self._ScanGlobalTAttr(node.left, want_name) - if hit_left is not None: - return hit_left - return self._ScanGlobalTAttr(node.right, want_name) - # 包装写法:c.Attribute(t.attr.xxx / t.attr.xxx(...)) → 拆 args 再扫 - # (与 HandlesFunctions.py:1354 装饰器侧同一判据:func.attr=='Attribute' - # 且 func.value 为 Name('c')) - if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) - and node.func.attr == 'Attribute' - and isinstance(node.func.value, ast.Name) and node.func.value.id == 'c'): - for arg in node.args: - hit_arg: ast.AST | None = self._ScanGlobalTAttr(arg, want_name) - if hit_arg is not None: - return hit_arg - return None - if self._TAttrName(node) == want_name: - return node - return None - - @staticmethod - def _TAttrArgStr(node: ast.AST | None) -> str | None: - """取 t.attr.("...") 的字符串字面量参数(缺参/非字面量返回 None)。""" - if isinstance(node, ast.Call) and node.args: - arg0: ast.AST = node.args[0] - if isinstance(arg0, ast.Constant) and isinstance(arg0.value, str): - return arg0.value - return None - - @staticmethod - def _TAttrAlignInt(node: ast.AST | None) -> int | None: - """取 t.attr.aligned(N) 的整数字面量 N(缺参/非字面量返回 None)。 - - 对应 TransPyV ClassGenBase._tattr_aligned_int(.vp:395-415)。 - """ - if isinstance(node, ast.Call) and node.args: - arg0: ast.AST = node.args[0] - if (isinstance(arg0, ast.Constant) and isinstance(arg0.value, int) - and not isinstance(arg0.value, bool)): - return arg0.value - return None - - def _RaiseGlobalAttrError(self, Msg: str, Node: ast.AST, Gen: "Translator.LlvmGen", VarName: str) -> None: - """fail-fast:全局 t.attr.* 属性非法(缺/空段名、非正整数字面量 aligned)。 - - 定位串(文件:行:列)与报错出口对齐 HandlesBase._RaiseUnresolvedExpr - (HandlesBase.py:552-615)——错误信息含源码位置后 sys.exit(1)。 - """ - raw_lineno: int = getattr(Node, 'lineno', 0) or 0 - lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 - col: int = getattr(Node, 'col_offset', -1) - src_file: str = getattr(Gen, '_current_source_file', '') or '' - # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的行数 - _line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - loc: str = src_file - if lineno > 0: - loc += f":{lineno}" - if col >= 0: - loc += f":{col}" - _vlog().compile_error( - f"模块级变量 '{VarName}' 的 {Msg}(位置 {loc})", src_file, lineno, '') - sys.exit(1) - - def _ExtractGlobalAttrs(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> dict[str, Any]: - """从模块级全局注解提取 t.attr.section/aligned/weak。 - - 返回 {'section': str|None, 'align': int, 'weak': bool}。 - 非法值(空/缺段名、非正整数 aligned)→ fail-fast 终止(对齐 TransPyV - TranslatorGenMixin._extract_global_attr_section/aligned 的致命错误语义)。 - """ - Attrs: dict[str, Any] = {'section': None, 'align': 0, 'weak': False} - Ann: ast.AST = Node.annotation - VarName: str = Node.target.id if isinstance(Node.target, ast.Name) else '' - sec_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'section') - if sec_node is not None: - sec_val: str | None = self._TAttrArgStr(sec_node) - if not sec_val: - self._RaiseGlobalAttrError( - "t.attr.section(...) 段名缺失或为空(空段名会生成非法的 section \"\")", - sec_node, Gen, VarName) - Attrs['section'] = sec_val - align_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'aligned') - if align_node is not None: - align_val: int | None = self._TAttrAlignInt(align_node) - if align_val is None or align_val <= 0: - self._RaiseGlobalAttrError( - "t.attr.aligned(N) 需要正整数字面量 N", align_node, Gen, VarName) - Attrs['align'] = align_val - if self._ScanGlobalTAttr(Ann, 'weak') is not None: - Attrs['weak'] = True - return Attrs - - @staticmethod - def _ApplyGlobalAttrs(GlobalVar: ir.GlobalVariable, Attrs: dict[str, Any]) -> None: - """把提取出的 section/align/weak 施加到已创建的 LLVM 全局变量上。 - - llvmlite 口径(llvmlite/ir/values.py): - - section 写**裸**段名(序列化时自动加引号,:843-844); - - align 需 int(:846-847);GlobalVariable.align 默认 None(:807); - - weak 用 linkage='weak'(GlobalVariable 无 weak 属性,对照函数侧 - HandlesFunctions.py:1772)。 - """ - if not Attrs: - return - sec: str | None = Attrs.get('section') - if sec: - GlobalVar.section = sec - align_val: int = int(Attrs.get('align') or 0) - if align_val > 0: - GlobalVar.align = align_val - if Attrs.get('weak'): - GlobalVar.linkage = 'weak' - - def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None: - if isinstance(value_node, ast.Constant): - if isinstance(value_node.value, bool): - if isinstance(var_type, ir.IntType): - return ir.Constant(var_type, int(value_node.value)) - elif isinstance(value_node.value, int): - if isinstance(var_type, ir.IntType): - return ir.Constant(var_type, value_node.value) - elif isinstance(var_type, (ir.FloatType, ir.DoubleType)): - return ir.Constant(var_type, float(value_node.value)) - elif isinstance(value_node.value, float): - if isinstance(var_type, (ir.FloatType, ir.DoubleType)): - return ir.Constant(var_type, value_node.value) - elif isinstance(var_type, ir.IntType): - return ir.Constant(var_type, int(value_node.value)) - elif isinstance(value_node.value, str): - return None - # Handle type constructor calls like t.CDouble(1e-9), t.CFloat(3.14), t.CInt(42) - if isinstance(value_node, ast.Call): - inner_val: ir.Constant | None = self._eval_type_ctor_const(value_node, var_type, Gen) - if inner_val is not None: - return inner_val - if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub): - inner: ir.Constant | None = self._eval_global_const(value_node.operand, var_type, Gen) - if inner: - return ir.Constant(var_type, -inner.constant) - # Handle enum member references like ASTVType.Module (ast.Attribute) - if isinstance(value_node, ast.Attribute): - attr_name: str = value_node.attr - # 优先用带前缀的 key 查找(避免短名被同名函数/变量覆盖) - if isinstance(value_node.value, ast.Name): - FullKey2: str = f"{value_node.value.id}.{attr_name}" - FullInfo: Any = self.Trans.SymbolTable.lookup(FullKey2) - if FullInfo and FullInfo.IsEnumMember and isinstance(FullInfo.value, int): - if isinstance(var_type, ir.IntType): - return ir.Constant(var_type, FullInfo.value) - SymInfo: Any = self.Trans.SymbolTable.lookup(attr_name) - if SymInfo: - if SymInfo.IsEnumMember and isinstance(SymInfo.value, int): - if isinstance(var_type, ir.IntType): - return ir.Constant(var_type, SymInfo.value) - return None - - def _eval_type_ctor_const(self, call_node: ast.Call, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None: - """Try to evaluate t.CDouble(val), t.CFloat(val), t.CInt(val) etc. as a compile-time constant.""" - if not isinstance(call_node.func, ast.Attribute): - return None - if not isinstance(call_node.func.value, ast.Name): - return None - if call_node.func.value.id != 't': - return None - if not call_node.args or len(call_node.args) != 1: - return None - arg: ast.expr = call_node.args[0] - # Recursively evaluate the argument as a constant - arg_val: ir.Constant | None = self._eval_global_const(arg, var_type, Gen) - if arg_val is not None: - return arg_val - return None - - def _store_bitfield_member(self, struct_ptr: ir.Value, ClassName: str, field_name: str, Value: ir.Value) -> bool: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(ClassName, {}) - bitfield_widths: dict[str, int] = Gen.class_member_bitfields.get(ClassName, {}) - if field_name not in bitfield_offsets: - return False - bit_offset: int = bitfield_offsets[field_name] - bit_width: int = bitfield_widths.get(field_name, 0) - if bit_width == 0: - return False - struct_type: ir.Type | None = Gen.structs.get(ClassName) - if struct_type is None or struct_type.elements is None or len(struct_type.elements) == 0: - return False - storage_type: ir.Type = struct_type.elements[0] - member_ptr: ir.Value = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{field_name}_storage_ptr") - old_val: ir.Value = Gen._load(member_ptr, name=f"{field_name}_old_storage") - mask: int = (1 << bit_width) - 1 - shifted_mask: int = mask << bit_offset - inverted_mask: ir.Constant = ir.Constant(storage_type, ~shifted_mask & ((1 << storage_type.width) - 1)) - cleared: ir.Value = Gen.builder.and_(old_val, inverted_mask, name=f"{field_name}_cleared") - if isinstance(Value.type, ir.IntType) and Value.type.width < storage_type.width: - if getattr(Value.type, 'is_signed', False): - Value = Gen.builder.sext(Value, storage_type, name=f"{field_name}_sext_store") - else: - Value = Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store") - elif Value.type.width != storage_type.width: - Value = Gen.builder.trunc(Value, storage_type, name=f"{field_name}_trunc_store") if Value.type.width > storage_type.width else Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store") - shifted_value: ir.Value = Gen.builder.shl(Value, ir.Constant(storage_type, bit_offset), name=f"{field_name}_shift_store") - masked_value: ir.Value = Gen.builder.and_(shifted_value, ir.Constant(storage_type, shifted_mask), name=f"{field_name}_mask_store") - new_val: ir.Value = Gen.builder.or_(cleared, masked_value, name=f"{field_name}_new_storage") - Gen._store(new_val, member_ptr) - return True - - def _CheckEnumMemberAssignment(self, node: ast.expr) -> str | None: - def _get_attr_path(n: ast.expr) -> str | None: - if isinstance(n, ast.Name): - return n.id - elif isinstance(n, ast.Attribute): - return f"{_get_attr_path(n.value)}.{n.attr}" - return None - attr_path: str | None = _get_attr_path(node) - if not attr_path: - return None - parts: list[str] = attr_path.split('.') - if len(parts) >= 2: - enum_class_name: str = parts[-2] - member_name: str = parts[-1] - qualified_name: str = f"{enum_class_name}.{member_name}" - info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(qualified_name) - if info: - if info.IsEnumMember and info.EnumName: - return info.EnumName - under_name: str = f"{enum_class_name}_{member_name}" - if self.Trans.SymbolTable.has(under_name): - info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable[under_name] - if info.IsEnumMember and info.EnumName: - return info.EnumName - for key in self.Trans.SymbolTable: - if key == member_name: - info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable[key] - if info.IsEnumMember and info.EnumName and info.EnumName == enum_class_name: - return enum_class_name - return None - - def _IsTypedefAnnotation(self, annotation: ast.expr) -> bool: - if isinstance(annotation, ast.Attribute): - if annotation.attr in ('CTypedef', 'CDefine'): - return True - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - return self._IsTypedefAnnotation(annotation.left) or self._IsTypedefAnnotation(annotation.right) - if isinstance(annotation, ast.Subscript): - return self._IsTypedefAnnotation(annotation.value) - return False - - def _EmitGlobalAssignLlvm(self, Node: ast.Assign, Gen: "Translator.LlvmGen") -> None: - if len(Node.targets) == 1 and isinstance(Node.targets[0], ast.Name): - VarName: str = Node.targets[0].id - if VarName in Gen.variables or VarName in Gen.module.globals: - return - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): - FuncName: str = Node.value.func.id - if FuncName in Gen.structs: - IsUnion: bool = False - TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(FuncName) - if TypeInfo: - if TypeInfo.IsUnion: - IsUnion = True - if IsUnion: - StructType: ir.Type = Gen.structs[FuncName] - StructPtrType: ir.PointerType = ir.PointerType(StructType) - GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, StructPtrType, name=VarName) - GlobalVar.initializer = ir.Constant(StructPtrType, None) - Gen.variables[VarName] = GlobalVar - Gen.global_struct_class[VarName] = FuncName - return - StructName: str = FuncName - VarType: ir.Type = Gen.structs[StructName] - GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) - const: ir.Constant | None = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen) - if const: - GlobalVar.initializer = const - else: - GlobalVar.linkage = 'common' - Gen.variables[VarName] = GlobalVar - Gen.global_struct_class[VarName] = StructName - return - GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName) - init_val: ir.Constant | None = self._eval_global_const(Node.value, ir.IntType(32), Gen) - GlobalVar.initializer = init_val if init_val else ir.Constant(ir.IntType(32), 0) - Gen.variables[VarName] = GlobalVar - - def _eval_const_expr(self, node: ast.expr, Gen: "Translator.LlvmGen") -> int | float | None: - """计算常量表达式的值(委托到 ConstEvaluator)""" - ctx: EvalContext = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans) - return ConstEvaluator.eval_full(node, ctx) - - def _eval_global_count(self, node: ast.expr, Gen: "Translator.LlvmGen") -> int | None: - val: int | float | None = self._eval_const_expr(node, Gen) - if val is not None: - if isinstance(val, float): - return int(val) - if isinstance(val, int): - return val + def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None: + # 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列): + # 模块级全局注解不经过 ResolveAnnotationType,故在此显式校验。 + self._CheckFixedArrayKeyword(Node.annotation) + if isinstance(Node.target, ast.Name): + VarName: str = Node.target.id + Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName) + if Info: + if Info.IsTypedef: + return + # 模块级全局 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`): + # 先解析(空/缺段名、非正整数字面量 aligned → fail-fast 终止),再在各 + # 全局创建点用 _ApplyGlobalAttrs 施加。此前 TPC 全局路径完全没有 + # section/align/weak 代码(函数侧 section 只来自装饰器,见 + # HandlesFunctions.py:1361-1362 → :1767)。 + GlobalAttrs: dict[str, Any] = self._ExtractGlobalAttrs(Node, Gen) + parse_result = ParseListAnnotation(Node.annotation) + if parse_result: + elem_type_node: ast.expr = parse_result.elem_type_node + ElemType, ElemTypeInfo = self.ResolveListElementType( + elem_type_node, Gen, + check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True, + ) + if parse_result.is_pointer: + # 单参数 t.CArray[T]:与 t.CArray[T, None] 统一为"长度自动推导数组"。 + # 有可推导初值(列表字面量 / 字符串字面量)时不再生成裸指针,而是落入 + # 下方数组路径:由 ParseArrayCount(count_node=None) 从字面量推导长度、 + # 字节内联到变量自身(与类成员 DeclarationGenerator.py:775-784、 + # 局部 HandlesAnnAssign.py:446-489 同一口径;用户语义:单参数即 + # "长度自动推导",不再是隐式指针)。 + # 无可推导初值(纯声明 / 真正的指针用法)时保持既有指针语义逐字不变。 + HasDerivableInit: bool = bool( + Node.value and ( + isinstance(Node.value, ast.List) + or (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str)) + ) + ) + if not HasDerivableInit: + VarType: ir.Type = ir.PointerType(ElemType) + if VarName not in Gen.variables and VarName not in Gen.module.globals: + GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) + GlobalVar.initializer = ir.Constant(VarType, None) + self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) + Gen.variables[VarName] = GlobalVar + if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: + Gen.global_struct_class[VarName] = elem_type_node.id + return + # 有初值 → 落入下方数组路径(count_node=None → 从字面量推导长度) + count_node: ast.expr | None = parse_result.count_node + ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global') + VarType = ir.ArrayType(ElemType, ArrayCount) + if VarName not in Gen.variables and VarName not in Gen.module.globals: + GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName) + if isinstance(ElemType, ir.IdentifiedStructType): + GlobalVar.linkage = 'common' + elif Node.value and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str): + str_val: str = Node.value.value + '\x00' + str_bytes: bytearray = bytearray(str_val, 'utf-8') + if len(str_bytes) < ArrayCount: + str_bytes.extend(b'\x00' * (ArrayCount - len(str_bytes))) + try: + GlobalVar.initializer = ir.Constant(VarType, str_bytes[:ArrayCount]) + except Exception: # 回退:初始化常量失败时用零值 + GlobalVar.initializer = Gen._zero_value_for_type(VarType) + else: + InitConstants: list[ir.Constant] | None = self.Trans._BuildArrayInitConstants(Node.value, ElemType, elem_type_node, ArrayCount, Gen, byte_order=ElemTypeInfo.ByteOrder if ElemTypeInfo else '') + if InitConstants: + GlobalVar.initializer = ir.Constant(VarType, InitConstants) + elif ArrayCount > 256: + GlobalVar.linkage = 'common' + else: + if self._contains_identified_struct(VarType): + GlobalVar.linkage = 'common' + else: + GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount) + self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) + Gen.variables[VarName] = GlobalVar + if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: + Gen.global_struct_class[VarName] = elem_type_node.id + return + if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs: + StructName: str = Node.annotation.id + VarType: ir.Type = Gen.structs[StructName] + if VarName not in Gen.variables and VarName not in Gen.module.globals: + GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) + if Node.value and isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == StructName: + const: ir.Constant | None = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen) + if const: + GlobalVar.initializer = const + else: + GlobalVar.initializer = ir.Constant(VarType, None) + else: + GlobalVar.initializer = ir.Constant(VarType, None) + self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) + Gen.variables[VarName] = GlobalVar + Gen.global_struct_class[VarName] = StructName + return + TypeInfo: CTypeInfo | None = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) + IsCDefine: bool = False + if TypeInfo and TypeInfo.BaseType: + if isinstance(TypeInfo.BaseType, t._CTypedef): + return + if isinstance(TypeInfo.BaseType, t.CDefine): + IsCDefine = True + if not IsCDefine and self._IsTypedefAnnotation(Node.annotation): + IsCDefine = True + TTypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation) + if TTypeInfo: + TTypeInfo.IsTypedef = True + TTypeInfo.Name = VarName + self.Trans.SymbolTable.insert(VarName, TTypeInfo) + if IsCDefine: + if Node.value: + define_constants: dict[str, int | float] = vars(Gen).setdefault('_define_constants', {}) + try: + const_value: int | float | None = self._eval_const_expr(Node.value, Gen) + if const_value is not None: + define_constants[VarName] = const_value + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + return + if TypeInfo is None: + TypeInfo = CTypeInfo() + TypeInfo.BaseType = t.CInt() + VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo) + if isinstance(VarType, ir.VoidType): + VarType = ir.IntType(32) + # t.State 标记的模块级全局(`extern 名字: 类型` 的展开产物,见 + # SymbolUtils._ExpandExportExternVar):**只声明不定义**(external linkage), + # 符号定义在外部(C 库 / 汇编 / 其它翻译单元),本模块不得再生成定义 + # (否则 ld.lld duplicate symbol)。与 import 侧 + # _EmitExternalGlobalDeclLlvm 的同名分支(HandlesImports.py:1211-1217)同口径。 + # 位置必须早于下方「VarName 已在 module.globals → 升级 common 定义」分支: + # 否则 extern 会被反向升级成定义,抵消 external 声明语义。 + if TypeInfo is not None and TypeInfo.IsState: + if VarName not in Gen.module.globals: + _ext_gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) + _ext_gv.linkage = 'external' + Gen._export_funcs.add(VarName) + Gen.variables[VarName] = Gen.module.globals[VarName] + return + # const / volatile 模块级全局登记(统一登记入口,持久层 global_*)。 + # 局限(明确记录,见 Debugger/type-descriptors.md 已知差异):本分支只覆盖 + # 标量/指针型全局;数组(t.CArray[...])与结构体型全局在更早的分支 + # (上方 1568-1627)已 return,不在此登记 —— 全仓普查无这类带 const/ + # volatile 注解的全局,故无可观测差异。 + Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=True) + # 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考, + # 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。 + if not hasattr(Gen, '_module_ann_types'): + Gen._module_ann_types = {} + Gen._module_ann_types[VarName] = VarType + if VarName in Gen.variables or VarName in Gen.module.globals: + # 根因修复:import 端 _EmitExternalGlobalDeclLlvm 会把被 import 模块 + # 的模块级全局(如下划线私有 _g_gen)以裸名 external 声明注入本模块 + # module.globals,导致本模块同名全局定义被此分支跳过 → 全工程无定义 + # (ld.lld undefined symbol: _g_gen)。external 声明遇到本模块定义时 + # 应升级为 common 定义(多模块同名合并为零值,对齐 None 语义), + # 而非静默跳过。 + existing_gv = Gen.module.globals.get(VarName) if VarName in Gen.module.globals else None + # 注:llvmlite.ir.GlobalVariable 无 is_declaration 属性(binding 层才有), + # 声明/定义靠 initializer 区分:external 声明 initializer 恒为 None。 + if isinstance(existing_gv, ir.GlobalVariable) and getattr(existing_gv, 'initializer', None) is None: + try: + if (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) + and isinstance(getattr(existing_gv, 'value_type', None), ir.PointerType)): + # 字符串字面量全局:补"指向常量数组首元素"的初始化器。 + # **不能**设 common —— LLVM 要求 common 全局的初始化器必须是 + # 零值(verifier: "'common' global must have a zero + # initializer!"),配非空初值直接非法;保持默认 external + # 强定义即可跨模块共享(Config.BackendTarget / + # _inc_bin_suffix 都在运行时被赋值/读取,符号必须唯一且可见)。 + existing_gv.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value) + else: + existing_gv.linkage = 'common' + if Node.value is None or (isinstance(Node.value, ast.Constant) and Node.value.value is None): + existing_gv.initializer = ir.Constant(existing_gv.type.pointee, None) + Gen.variables[VarName] = existing_gv + except Exception: + pass + return + # 时序修复:第一遍创建全局时类未处理(SymbolTable 无枚举成员), + # initializer 回退为 0。第二遍类已处理,尝试用解析出的枚举值更新 initializer。 + if VarName in Gen.module.globals and Node.value: + existing: Any = Gen.module.globals[VarName] + if isinstance(existing, ir.GlobalVariable) and isinstance(VarType, ir.IntType): + new_init: ir.Constant | None = self._eval_global_const(Node.value, VarType, Gen) + if new_init is not None: + existing.initializer = new_init + return + if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)): + GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) + GlobalVar.initializer = ir.Constant(VarType, None) + self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) + Gen.variables[VarName] = GlobalVar + if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name: + Gen.global_struct_class[VarName] = TypeInfo.Name + return + GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) + # 全局变量指针元素标志:bytes|t.CPtr 或 str|t.CPtr 注解的变量, + # 使 var[index] 使用 8 字节指针步长(而非 1 字节字符步长) + if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): + if AnnotationContainsName(Node.annotation, 'str') or AnnotationContainsName(Node.annotation, 'bytes'): + Gen.global_var_ptr_element[VarName] = True + if Node.value: + if isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) and isinstance(VarType, ir.PointerType): + # 字符串字面量全局:初始化器 = 指向 @{VarName}_str 常量数组首元素 + # 的 GEP。此前这里在创建 GlobalVar(上方数行)之后又查 + # `VarName not in Gen.module.globals` —— 该条件恒为假(GlobalVar + # 刚被登记),于是永远走 else 分支、**从不写初始化器**:全工程模块级 + # `str = "字面量"` 全局退化成无初值的声明,第二遍再被升级成 + # `common global i8* null`,字符串常量沦为无人引用的 @NAME_str。 + # 只读常量(如 BuildPipeline.MLIR_OPT_CMD)在 mlir 后端 + # strlen(NULL) 直接崩(ASan 0x0)。 + if getattr(GlobalVar, 'initializer', None) is None: + GlobalVar.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value) + Gen.variables[VarName] = GlobalVar + elif isinstance(VarType, ir.PointerType) and isinstance(VarType.pointee, ir.FunctionType) and isinstance(Node.value, ast.Name): + # 函数指针赋值:f3: t.Callable[[int], int] = f + RefName: str = Node.value.id + SymInfo: Any = self.Trans.SymbolTable.lookup(RefName) + if SymInfo and (SymInfo.IsFunction or SymInfo.IsFuncPtr): + MangledName: str = Gen._mangle_func_name(RefName) + if MangledName in Gen.module.globals: + func: Any = Gen.module.globals[MangledName] + if isinstance(func, ir.Function): + GlobalVar.initializer = func + else: + if isinstance(VarType, ir.ArrayType) and isinstance(Node.value, (ast.List, ast.Set)): + InitConstants: list[ir.Constant] | None = self.Trans._BuildMultiDimArrayInitConstants(Node.value, VarType, Gen) + if InitConstants: + GlobalVar.initializer = ir.Constant(VarType, InitConstants) + else: + if self._contains_identified_struct(VarType): + GlobalVar.linkage = 'common' + else: + zv: ir.Constant | None = Gen._zero_value_for_type(VarType) + if zv: + GlobalVar.initializer = zv + else: + GlobalVar.linkage = 'common' + else: + init_val: ir.Constant | None = self._eval_global_const(Node.value, VarType, Gen) + if init_val: + GlobalVar.initializer = init_val + elif isinstance(VarType, ir.BaseStructType) or (isinstance(Node.value, ast.Constant) and Node.value.value is None): + # None 初值全局(如各 Handles 模块的 _g_gen: t.CVoid|t.CPtr = None): + # 多模块同名定义用 common linkage 让链接器合并(避免 + # ld.lld duplicate symbol)。common 无 initializer,合并为零值, + # 符合 None(=null) 语义。 + GlobalVar.linkage = 'common' + else: + GlobalVar.initializer = Gen._zero_value_for_type(VarType) + else: + if isinstance(VarType, ir.BaseStructType) or self._contains_identified_struct(VarType): + GlobalVar.linkage = 'common' + else: + GlobalVar.initializer = Gen._zero_value_for_type(VarType) + # 属性最后施加:weak 需覆盖分支内可能设过的 'common'(对齐 ViperC + # TranslatorGenMixin.vp:926-929 先清 Linkage 再设 weak 的语义)。 + self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) + Gen.variables[VarName] = GlobalVar + + # ====================================================================== + # 模块级全局的 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`) + # + # 镜像 ViperC(Viper 自举侧)已有实现: + # TranslatorGenMixin._scan_global_tattr / _extract_global_attr_section / + # _extract_global_attr_aligned / _extract_global_attr_weak(.vp:590-651) + # + ClassGenBase._tattr_name(.vp:341-358)+ _class_has_tattr 的 + # `c.Attribute(t.attr.xxx)` 解包(.vp:359-394)。 + # TPC(Python 侧)此前完全没有全局 section/align/weak 代码。 + # ====================================================================== + def _TAttrName(self, node: ast.AST | None) -> str | None: + """从 t.attr. / t.attr.(args) 节点提取最外层属性名。 + + 对应 ViperC ClassGenBase._tattr_name:Call → 递归取 func; + Attribute → 取 attr。 + """ + if isinstance(node, ast.Call): + return self._TAttrName(node.func) + if isinstance(node, ast.Attribute): + return node.attr + return None + + def _ScanGlobalTAttr(self, node: ast.AST | None, want_name: str) -> ast.AST | None: + """在全局变量注解中查找 t.attr. 节点,命中返回该节点。 + + 注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)), + 递归左右子节点。命中节点形态:Call(t.attr.aligned(16))或 + Attribute(t.attr.weak)。同时兼容包装写法 + `T | c.Attribute(t.attr.section("..."))`(解包参考 ViperC + ClassGenBase._class_has_tattr,.vp:375-389)。 + """ + if node is None: + return None + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + hit_left: ast.AST | None = self._ScanGlobalTAttr(node.left, want_name) + if hit_left is not None: + return hit_left + return self._ScanGlobalTAttr(node.right, want_name) + # 包装写法:c.Attribute(t.attr.xxx / t.attr.xxx(...)) → 拆 args 再扫 + # (与 HandlesFunctions.py:1354 装饰器侧同一判据:func.attr=='Attribute' + # 且 func.value 为 Name('c')) + if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) + and node.func.attr == 'Attribute' + and isinstance(node.func.value, ast.Name) and node.func.value.id == 'c'): + for arg in node.args: + hit_arg: ast.AST | None = self._ScanGlobalTAttr(arg, want_name) + if hit_arg is not None: + return hit_arg + return None + if self._TAttrName(node) == want_name: + return node + return None + + @staticmethod + def _TAttrArgStr(node: ast.AST | None) -> str | None: + """取 t.attr.("...") 的字符串字面量参数(缺参/非字面量返回 None)。""" + if isinstance(node, ast.Call) and node.args: + arg0: ast.AST = node.args[0] + if isinstance(arg0, ast.Constant) and isinstance(arg0.value, str): + return arg0.value + return None + + @staticmethod + def _TAttrAlignInt(node: ast.AST | None) -> int | None: + """取 t.attr.aligned(N) 的整数字面量 N(缺参/非字面量返回 None)。 + + 对应 ViperC ClassGenBase._tattr_aligned_int(.vp:395-415)。 + """ + if isinstance(node, ast.Call) and node.args: + arg0: ast.AST = node.args[0] + if (isinstance(arg0, ast.Constant) and isinstance(arg0.value, int) + and not isinstance(arg0.value, bool)): + return arg0.value + return None + + def _RaiseGlobalAttrError(self, Msg: str, Node: ast.AST, Gen: "Translator.LlvmGen", VarName: str) -> None: + """fail-fast:全局 t.attr.* 属性非法(缺/空段名、非正整数字面量 aligned)。 + + 定位串(文件:行:列)与报错出口对齐 HandlesBase._RaiseUnresolvedExpr + (HandlesBase.py:552-615)——错误信息含源码位置后 sys.exit(1)。 + """ + raw_lineno: int = getattr(Node, 'lineno', 0) or 0 + lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 + col: int = getattr(Node, 'col_offset', -1) + src_file: str = getattr(Gen, '_current_source_file', '') or '' + # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的行数 + _line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + loc: str = src_file + if lineno > 0: + loc += f":{lineno}" + if col >= 0: + loc += f":{col}" + _vlog().compile_error( + f"模块级变量 '{VarName}' 的 {Msg}(位置 {loc})", src_file, lineno, '') + sys.exit(1) + + def _ExtractGlobalAttrs(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> dict[str, Any]: + """从模块级全局注解提取 t.attr.section/aligned/weak。 + + 返回 {'section': str|None, 'align': int, 'weak': bool}。 + 非法值(空/缺段名、非正整数 aligned)→ fail-fast 终止(对齐 ViperC + TranslatorGenMixin._extract_global_attr_section/aligned 的致命错误语义)。 + """ + Attrs: dict[str, Any] = {'section': None, 'align': 0, 'weak': False} + Ann: ast.AST = Node.annotation + VarName: str = Node.target.id if isinstance(Node.target, ast.Name) else '' + sec_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'section') + if sec_node is not None: + sec_val: str | None = self._TAttrArgStr(sec_node) + if not sec_val: + self._RaiseGlobalAttrError( + "t.attr.section(...) 段名缺失或为空(空段名会生成非法的 section \"\")", + sec_node, Gen, VarName) + Attrs['section'] = sec_val + align_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'aligned') + if align_node is not None: + align_val: int | None = self._TAttrAlignInt(align_node) + if align_val is None or align_val <= 0: + self._RaiseGlobalAttrError( + "t.attr.aligned(N) 需要正整数字面量 N", align_node, Gen, VarName) + Attrs['align'] = align_val + if self._ScanGlobalTAttr(Ann, 'weak') is not None: + Attrs['weak'] = True + return Attrs + + @staticmethod + def _ApplyGlobalAttrs(GlobalVar: ir.GlobalVariable, Attrs: dict[str, Any]) -> None: + """把提取出的 section/align/weak 施加到已创建的 LLVM 全局变量上。 + + llvmlite 口径(llvmlite/ir/values.py): + - section 写**裸**段名(序列化时自动加引号,:843-844); + - align 需 int(:846-847);GlobalVariable.align 默认 None(:807); + - weak 用 linkage='weak'(GlobalVariable 无 weak 属性,对照函数侧 + HandlesFunctions.py:1772)。 + """ + if not Attrs: + return + sec: str | None = Attrs.get('section') + if sec: + GlobalVar.section = sec + align_val: int = int(Attrs.get('align') or 0) + if align_val > 0: + GlobalVar.align = align_val + if Attrs.get('weak'): + GlobalVar.linkage = 'weak' + + def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None: + if isinstance(value_node, ast.Constant): + if isinstance(value_node.value, bool): + if isinstance(var_type, ir.IntType): + return ir.Constant(var_type, int(value_node.value)) + elif isinstance(value_node.value, int): + if isinstance(var_type, ir.IntType): + return ir.Constant(var_type, value_node.value) + elif isinstance(var_type, (ir.FloatType, ir.DoubleType)): + return ir.Constant(var_type, float(value_node.value)) + elif isinstance(value_node.value, float): + if isinstance(var_type, (ir.FloatType, ir.DoubleType)): + return ir.Constant(var_type, value_node.value) + elif isinstance(var_type, ir.IntType): + return ir.Constant(var_type, int(value_node.value)) + elif isinstance(value_node.value, str): + return None + # Handle type constructor calls like t.CDouble(1e-9), t.CFloat(3.14), t.CInt(42) + if isinstance(value_node, ast.Call): + inner_val: ir.Constant | None = self._eval_type_ctor_const(value_node, var_type, Gen) + if inner_val is not None: + return inner_val + if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub): + inner: ir.Constant | None = self._eval_global_const(value_node.operand, var_type, Gen) + if inner: + return ir.Constant(var_type, -inner.constant) + # Handle enum member references like ASTVType.Module (ast.Attribute) + if isinstance(value_node, ast.Attribute): + attr_name: str = value_node.attr + # 优先用带前缀的 key 查找(避免短名被同名函数/变量覆盖) + if isinstance(value_node.value, ast.Name): + FullKey2: str = f"{value_node.value.id}.{attr_name}" + FullInfo: Any = self.Trans.SymbolTable.lookup(FullKey2) + if FullInfo and FullInfo.IsEnumMember and isinstance(FullInfo.value, int): + if isinstance(var_type, ir.IntType): + return ir.Constant(var_type, FullInfo.value) + SymInfo: Any = self.Trans.SymbolTable.lookup(attr_name) + if SymInfo: + if SymInfo.IsEnumMember and isinstance(SymInfo.value, int): + if isinstance(var_type, ir.IntType): + return ir.Constant(var_type, SymInfo.value) + return None + + def _eval_type_ctor_const(self, call_node: ast.Call, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None: + """Try to evaluate t.CDouble(val), t.CFloat(val), t.CInt(val) etc. as a compile-time constant.""" + if not isinstance(call_node.func, ast.Attribute): + return None + if not isinstance(call_node.func.value, ast.Name): + return None + if call_node.func.value.id != 't': + return None + if not call_node.args or len(call_node.args) != 1: + return None + arg: ast.expr = call_node.args[0] + # Recursively evaluate the argument as a constant + arg_val: ir.Constant | None = self._eval_global_const(arg, var_type, Gen) + if arg_val is not None: + return arg_val + return None + + def _store_bitfield_member(self, struct_ptr: ir.Value, ClassName: str, field_name: str, Value: ir.Value) -> bool: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(ClassName, {}) + bitfield_widths: dict[str, int] = Gen.class_member_bitfields.get(ClassName, {}) + if field_name not in bitfield_offsets: + return False + bit_offset: int = bitfield_offsets[field_name] + bit_width: int = bitfield_widths.get(field_name, 0) + if bit_width == 0: + return False + struct_type: ir.Type | None = Gen.structs.get(ClassName) + if struct_type is None or struct_type.elements is None or len(struct_type.elements) == 0: + return False + storage_type: ir.Type = struct_type.elements[0] + member_ptr: ir.Value = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{field_name}_storage_ptr") + old_val: ir.Value = Gen._load(member_ptr, name=f"{field_name}_old_storage") + mask: int = (1 << bit_width) - 1 + shifted_mask: int = mask << bit_offset + inverted_mask: ir.Constant = ir.Constant(storage_type, ~shifted_mask & ((1 << storage_type.width) - 1)) + cleared: ir.Value = Gen.builder.and_(old_val, inverted_mask, name=f"{field_name}_cleared") + if isinstance(Value.type, ir.IntType) and Value.type.width < storage_type.width: + if getattr(Value.type, 'is_signed', False): + Value = Gen.builder.sext(Value, storage_type, name=f"{field_name}_sext_store") + else: + Value = Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store") + elif Value.type.width != storage_type.width: + Value = Gen.builder.trunc(Value, storage_type, name=f"{field_name}_trunc_store") if Value.type.width > storage_type.width else Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store") + shifted_value: ir.Value = Gen.builder.shl(Value, ir.Constant(storage_type, bit_offset), name=f"{field_name}_shift_store") + masked_value: ir.Value = Gen.builder.and_(shifted_value, ir.Constant(storage_type, shifted_mask), name=f"{field_name}_mask_store") + new_val: ir.Value = Gen.builder.or_(cleared, masked_value, name=f"{field_name}_new_storage") + Gen._store(new_val, member_ptr) + return True + + def _CheckEnumMemberAssignment(self, node: ast.expr) -> str | None: + def _get_attr_path(n: ast.expr) -> str | None: + if isinstance(n, ast.Name): + return n.id + elif isinstance(n, ast.Attribute): + return f"{_get_attr_path(n.value)}.{n.attr}" + return None + attr_path: str | None = _get_attr_path(node) + if not attr_path: + return None + parts: list[str] = attr_path.split('.') + if len(parts) >= 2: + enum_class_name: str = parts[-2] + member_name: str = parts[-1] + qualified_name: str = f"{enum_class_name}.{member_name}" + info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(qualified_name) + if info: + if info.IsEnumMember and info.EnumName: + return info.EnumName + under_name: str = f"{enum_class_name}_{member_name}" + if self.Trans.SymbolTable.has(under_name): + info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable[under_name] + if info.IsEnumMember and info.EnumName: + return info.EnumName + for key in self.Trans.SymbolTable: + if key == member_name: + info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable[key] + if info.IsEnumMember and info.EnumName and info.EnumName == enum_class_name: + return enum_class_name + return None + + def _IsTypedefAnnotation(self, annotation: ast.expr) -> bool: + if isinstance(annotation, ast.Attribute): + if annotation.attr in ('CTypedef', 'CDefine'): + return True + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + return self._IsTypedefAnnotation(annotation.left) or self._IsTypedefAnnotation(annotation.right) + if isinstance(annotation, ast.Subscript): + return self._IsTypedefAnnotation(annotation.value) + return False + + def _EmitGlobalAssignLlvm(self, Node: ast.Assign, Gen: "Translator.LlvmGen") -> None: + if len(Node.targets) == 1 and isinstance(Node.targets[0], ast.Name): + VarName: str = Node.targets[0].id + if VarName in Gen.variables or VarName in Gen.module.globals: + return + if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): + FuncName: str = Node.value.func.id + if FuncName in Gen.structs: + IsUnion: bool = False + TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(FuncName) + if TypeInfo: + if TypeInfo.IsUnion: + IsUnion = True + if IsUnion: + StructType: ir.Type = Gen.structs[FuncName] + StructPtrType: ir.PointerType = ir.PointerType(StructType) + GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, StructPtrType, name=VarName) + GlobalVar.initializer = ir.Constant(StructPtrType, None) + Gen.variables[VarName] = GlobalVar + Gen.global_struct_class[VarName] = FuncName + return + StructName: str = FuncName + VarType: ir.Type = Gen.structs[StructName] + GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) + const: ir.Constant | None = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen) + if const: + GlobalVar.initializer = const + else: + GlobalVar.linkage = 'common' + Gen.variables[VarName] = GlobalVar + Gen.global_struct_class[VarName] = StructName + return + GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName) + init_val: ir.Constant | None = self._eval_global_const(Node.value, ir.IntType(32), Gen) + GlobalVar.initializer = init_val if init_val else ir.Constant(ir.IntType(32), 0) + Gen.variables[VarName] = GlobalVar + + def _eval_const_expr(self, node: ast.expr, Gen: "Translator.LlvmGen") -> int | float | None: + """计算常量表达式的值(委托到 ConstEvaluator)""" + ctx: EvalContext = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans) + return ConstEvaluator.eval_full(node, ctx) + + def _eval_global_count(self, node: ast.expr, Gen: "Translator.LlvmGen") -> int | None: + val: int | float | None = self._eval_const_expr(node, Gen) + if val is not None: + if isinstance(val, float): + return int(val) + if isinstance(val, int): + return val return None \ No newline at end of file diff --git a/lib/core/Handles/HandlesExpr.py b/lib/core/Handles/HandlesExpr.py index 46f1d6a..3ed9a31 100644 --- a/lib/core/Handles/HandlesExpr.py +++ b/lib/core/Handles/HandlesExpr.py @@ -1,612 +1,622 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -import vast as ast -import re -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle -from lib.constants.config import mode as _config_mode -from lib.includes.t import CTypeRegistry as _CR +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +import vast as ast +import re +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle +from lib.constants.config import mode as _config_mode +from lib.includes.t import CTypeRegistry as _CR from lib.core.SymbolUtils import (RequireDefineUseWidthAt, RequireIntLiteralStorable, RequireNameDefined, - DefineDeclTypeValue, GlobalDeclShadowsDefine) - - -# --------------------------------------------------------------- -# 注解/常量折叠里的正则**预编译**(性能:避免每次 `re.match`/`re.search(字面量, ...)` -# 的"模式串哈希 → re 模块级缓存查找"。语义与逐字写法完全一致,模式串一字未改)。 -# --------------------------------------------------------------- -# 从 `list[...]` 注解串里取出元素类型与可选长度 -_RE_LIST_ELEM_TYPE: re.Pattern[str] = re.compile(r'list\[([^,\]]+)(?:,\s*(\d+))?\]') -# LLVM 整型类型名 `i8`/`i32`/... 的位宽提取 -_RE_LLVM_INT_TYPE: re.Pattern[str] = re.compile(r'^i(\d+)$') - - -def DefineUseWidthFromLLVMType(Ty: object) -> int | None: - """LLVM 类型 → CDefine 常量的"使用处位宽"提示;其余 → None。 - - 只认 i8/i16/i32/i64(与 SymbolUtils.DEFINE_WIDTH_LADDER 同集合): - i1(比较/逻辑结果)与指针不构成"整数使用处宽度",必须落回"取不到 ⇒ - 默认 32/64",否则 `C == 0` 之类会拿到位宽 1 而误报"使用处宽度不足"。 - """ - if isinstance(Ty, ir.IntType) and Ty.width in (8, 16, 32, 64): - return int(Ty.width) - return None - - -class ExprHandle(BaseHandle): - - # list 元素类型 → (字节大小, LLVM 元素类型) 映射表 - # 用于 _HandleListLlvm 中 alloc_size / byte_offset / elem_ptr 类型统一查表, - # 消除原代码三处重复的 if-elif 链。 - _ELEM_TYPE_INFO: dict[str, tuple[int, ir.Type]] = { - 'i8': (1, ir.IntType(8)), - 'i16': (2, ir.IntType(16)), - 'i32': (4, ir.IntType(32)), - 'float': (4, ir.FloatType()), - 'double': (8, ir.DoubleType()), - } - - def __init__(self, translator: "Translator") -> None: - super().__init__(translator) - - def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None: - return self.Trans.ExprUtils.GetOpSymbol(Op) - - def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None: - return self.Trans.ExprUtils.GetUnaryOpSymbol(Op) - - def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None: - return self.Trans.ExprUtils.GetComparatorSymbol(Op) - - def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: - return self.Trans.ExprUtils._get_var_class(node, Gen) - - def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None: - return self.Trans.ExprUtils._try_operator_overLoad(ClassName, op_name, obj_val, Gen, other_val) - - def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: - return self.Trans.ExprAttrHandle._get_llvm_member_offset(field_name, ClassName, Gen) - - def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None, - ConstWidthHint: int | None = None) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if not Gen or not Gen.builder: - return None - if isinstance(Node, ast.Constant): - return self._HandleConstantLlvm(Node, VarType) - elif isinstance(Node, ast.Name): - # ConstWidthHint 是**窄通道**:只有 Name(CDefine 常量物化)读取它, - # 其余分支(字面量/列表/运算…)逐字沿用既有 VarType 语义 ⇒ 非常量 - # 路径的既有 IR 不变。 - return self._HandleNameLlvm(Node, VarType, ConstWidthHint) - elif isinstance(Node, ast.BinOp): - return self.Trans.ExprOpsHandle._HandleBinOpLlvm(Node, VarType) - elif isinstance(Node, ast.BoolOp): - return self.Trans.ExprOpsHandle._HandleBoolOpLlvm(Node) - elif isinstance(Node, ast.UnaryOp): - return self.Trans.ExprOpsHandle._HandleUnaryOpLlvm(Node) - elif isinstance(Node, ast.Starred): - # `*p` 出现在值位置(典型:调用实参 `f(*p)`):C/Python 解析器都按 - # Python `*args` 语义归类为 Starred(Load)(vast_c.c ParseCallTail、 - # ExprParser.py:543-548)。Viper 未实现解包 ⇒ 与 `*p` 读侧对齐,一律 - # 归一为 UnaryOp(Deref) 解引用读取(与 TransPyV ExprGenMixin 的同类归一 - # 逐字同口径)。此前无本分支 ⇒ 落到函数末尾兜底 `return i32 0`, - # 把 `f(*p)` 静默变成 `f(0)`。 - StarredInner: ast.expr | None = Node.value - if StarredInner is None: - raise Exception(f"值位置的单目 '*' 缺少操作数{Gen._get_node_info()}") - return self.Trans.ExprOpsHandle._HandleUnaryOpLlvm( - ast.UnaryOp(op=ast.Deref(), operand=StarredInner)) - elif isinstance(Node, ast.Call): - return self.Trans.ExprCallHandle._HandleCallLlvm(Node) - elif isinstance(Node, ast.Await): - return self.Trans.AsyncHandler._HandleAwaitLlvm(Node, Gen) - elif isinstance(Node, ast.Compare): - return self.Trans.ExprOpsHandle._HandleCompareLlvm(Node) - elif isinstance(Node, ast.Attribute): - return self.Trans.ExprAttrHandle._HandleAttributeLlvm(Node) - elif isinstance(Node, ast.Subscript): - return self.Trans.ExprAttrHandle._HandleSubscriptLlvm(Node) - elif isinstance(Node, ast.IfExp): - return self.Trans.ExprLambdaHandle._HandleIfExpLlvm(Node) - elif isinstance(Node, ast.NamedExpr): - return self.Trans.ExprLambdaHandle._HandleNamedExprLlvm(Node) - elif isinstance(Node, ast.JoinedStr): - return self.Trans.ExprFormatHandle._HandleJoinedStrLlvm(Node) - elif isinstance(Node, ast.Lambda): - return self.Trans.ExprLambdaHandle._HandleLambdaLlvm(Node) - elif isinstance(Node, ast.List): - return self._HandleListLlvm(Node, VarType) - return ir.Constant(ir.IntType(32), 0) - - def _HandleListLlvm(self, Node: ast.List, VarType: ir.Type | str | None = None) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - elements: list[ast.expr] = Node.elts - if not elements: - return ir.Constant(ir.IntType(8).as_pointer(), None) - - array_len: int = len(elements) - elem_type: str = 'i8' - - if VarType and isinstance(VarType, str) and 'list[' in VarType: - match: re.Match | None = _RE_LIST_ELEM_TYPE.search(VarType) - if match: - type_str: str = match.group(1).strip() - if match.group(2): - array_len = int(match.group(2)) - if 'CChar' in type_str or 'char' in type_str.lower(): - elem_type = 'i8' - elif 'CFloat' in type_str or type_str.lower() == 'float': - elem_type = 'float' - elif 'CDouble' in type_str or type_str.lower() == 'double': - elem_type = 'double' - elif 'CInt' in type_str or ('int' in type_str.lower() and 'unsigned' not in type_str.lower()): - elem_type = 'i32' - elif 'CUnsignedInt' in type_str or 'unsigned' in type_str.lower(): - elem_type = 'i32' - elif 'CUnsignedChar' in type_str or 'unsigned char' in type_str.lower(): - elem_type = 'i8' - elif 'CShort' in type_str or 'short' in type_str.lower(): - elem_type = 'i16' - elif 'CUnsignedShort' in type_str: - elem_type = 'i16' - elif elements and all(isinstance(e, (ast.Constant,)) and isinstance(getattr(e, 'value', None), float) for e in elements): - elem_type = 'float' - - alloc_size: int = array_len - elem_info: tuple[int, ir.Type] = self._ELEM_TYPE_INFO.get(elem_type, (1, ir.IntType(8))) - elem_size: int = elem_info[0] - elem_llvm_type: ir.Type = elem_info[1] - alloc_size *= elem_size - - malloc_fn: Any = None - malloc_arg_type: ir.IntType = ir.IntType(64) - for fn in Gen.module.global_values: - if fn.name == 'malloc': - malloc_fn = fn - func_ftype: Any = getattr(fn, 'ftype', None) - if func_ftype and getattr(func_ftype, 'args', None): - if func_ftype.args: - malloc_arg_type = fn.ftype.args[0] - break - - if not malloc_fn: - try: - malloc_fn_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) - malloc_fn = Gen._CreateOrReuseFuncDecl('malloc', malloc_fn_type) - Gen.functions['malloc'] = malloc_fn - except Exception as _e: # 回退:malloc 函数创建失败时返回 null - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - raise - return ir.Constant(ir.IntType(8).as_pointer(), None) - - alloc_ptr: Any = Gen.builder.call(malloc_fn, [ir.Constant(malloc_arg_type, alloc_size)]) - cast_ptr: Any = Gen.builder.bitcast(alloc_ptr, ir.PointerType(ir.IntType(8))) - - for i, elem in enumerate(elements): - if i >= array_len: - break - - elem_val: Any = self.HandleExprLlvm(elem, VarType=elem_type) - if elem_val is None: - continue - # None(空值常量)允许隐式转换为任意元素类型 ⇒ 物化为目标元素类型零值。 - if Gen._IsNoneValue(elem_val, elem): - elem_val = Gen._ZeroConst(elem_llvm_type) - - byte_offset: int = i - if elem_type == 'i32': - byte_offset = i * 4 - elif elem_type == 'i16': - byte_offset = i * 2 - elif elem_type == 'float': - byte_offset = i * 4 - elif elem_type == 'double': - byte_offset = i * 8 - - # 元素地址:以 i8* 基址按字节 GEP,再转成元素指针类型 - # (不再 ptr→uint64 手动加偏移再 inttoptr,见新规范第 10 条) - elem_addr: Any = Gen.builder.gep(cast_ptr, [ir.Constant(ir.IntType(64), byte_offset)], name=f"elem_addr_{i}") - if elem_type == 'i32': - elem_ptr: Any = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(32))) - elif elem_type == 'i16': - elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(16))) - elif elem_type == 'float': - elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.FloatType())) - elif elem_type == 'double': - elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.DoubleType())) - else: - elem_ptr = elem_addr - - if elem_type == 'i8': - if isinstance(elem_val.type, ir.IntType) and elem_val.type.width > 8: - elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}") - elif isinstance(elem_val.type, ir.PointerType): - # 指针 → 整数:隐式转换一律禁止(元素值是指针、元素类型是整数) - # (None 已在上方短路为零值,不会落到这里)。 - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type}," - f"但目标元素类型是 i8;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" - f"{Gen._DescribeValueNode(elem)}" - ) - elif elem_type == 'i32': - if isinstance(elem_val.type, ir.IntType): - if elem_val.type.width < 32: - elem_val = Gen.builder.zext(elem_val, ir.IntType(32), name=f"zext_elem_{i}") - elif elem_val.type.width > 32: - elem_val = Gen.builder.trunc(elem_val, ir.IntType(32), name=f"trunc_elem_{i}") - elif isinstance(elem_val.type, ir.PointerType): - # 指针 → 整数:隐式转换一律禁止(ptrtoint 到 i32 还会截断地址) - # (None 已在上方短路为零值,不会落到这里)。 - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type}," - f"但目标元素类型是 i32;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" - f"{Gen._DescribeValueNode(elem)}" - ) - elif elem_type == 'i16': - if isinstance(elem_val.type, ir.IntType): - if elem_val.type.width < 16: - elem_val = Gen.builder.zext(elem_val, ir.IntType(16), name=f"zext_elem_{i}") - elif elem_val.type.width > 16: - elem_val = Gen.builder.trunc(elem_val, ir.IntType(16), name=f"trunc_elem_{i}") - elif elem_type == 'float': - if isinstance(elem_val.type, ir.DoubleType): - elem_val = Gen.builder.fptrunc(elem_val, ir.FloatType(), name=f"fptrunc_elem_{i}") - elif isinstance(elem_val.type, ir.IntType): - elem_val = Gen.builder.sitofp(elem_val, ir.FloatType(), name=f"sitofp_elem_{i}") - elif elem_type == 'double': - if isinstance(elem_val.type, ir.FloatType): - elem_val = Gen.builder.fpext(elem_val, ir.DoubleType(), name=f"fpext_elem_{i}") - elif isinstance(elem_val.type, ir.IntType): - elem_val = Gen.builder.sitofp(elem_val, ir.DoubleType(), name=f"sitofp_elem_{i}") - - Gen.builder.store(elem_val, elem_ptr) - - if elem_type in ('float', 'double'): - return Gen.builder.bitcast(alloc_ptr, ir.PointerType(elem_llvm_type), name=f"list_{elem_type}_ptr") - return cast_ptr - - def _HandleConstantLlvm(self, Node: ast.Constant, VarType: ir.Type | str | None = None) -> ir.Value: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - Value: Any = Node.value - if Value is None: - if VarType is not None and isinstance(VarType, ir.PointerType): - return ir.Constant(VarType, None) - return ir.Constant(ir.IntType(8).as_pointer(), None) - elif isinstance(Value, bool): - return ir.Constant(ir.IntType(32), 1 if Value else 0) - elif isinstance(Value, int): - # 整数字面量越界 fail-fast(字面量本身无符号,0..2^64-1):与 TPV 词法层 - # `_lex_int_exceeds_u64` 同口径。负数侧由"一元减 + 字面量"的联合判定负责 - # (HandlesExprOps._HandleUnaryOpLlvm),此处只判字面量本身。 - RequireIntLiteralStorable(Gen, self.Trans, Node, Value) - if VarType is not None: - if isinstance(VarType, ir.IntType): - return ir.Constant(VarType, Value & ((1 << VarType.width) - 1)) - if isinstance(VarType, ir.PointerType): - return ir.Constant(ir.IntType(64), Value) - if isinstance(VarType, (ir.FloatType, ir.DoubleType)): - return ir.Constant(VarType, float(Value)) - if isinstance(VarType, str) and '*' in VarType: - return ir.Constant(ir.IntType(64), Value) - if isinstance(VarType, str): - resolved: Any = _CR.ResolveName(VarType) or _CR.LLVMToCType(VarType) - if resolved: - ctype_cls: type = resolved[0] if isinstance(resolved, tuple) else resolved - inst: Any = ctype_cls() - size: int = getattr(inst, 'Size', 32) - is_unsigned: bool = getattr(inst, 'IsSigned', True) is False - mask: int = (1 << size) - 1 - return ir.Constant(ir.IntType(size), Value & mask) - llvm_match: re.Match | None = _RE_LLVM_INT_TYPE.match(VarType) - if llvm_match: - width: int = int(llvm_match.group(1)) - return ir.Constant(ir.IntType(width), Value & ((1 << width) - 1)) - if Value < 0 or Value > 0xFFFFFFFF: - return ir.Constant(ir.IntType(64), Value) - return ir.Constant(ir.IntType(32), Value & 0xFFFFFFFF) - elif isinstance(Value, float): - if VarType and isinstance(VarType, ir.FloatType): - return ir.Constant(VarType, Value) - elif VarType and isinstance(VarType, ir.DoubleType): - return ir.Constant(VarType, Value) - return ir.Constant(ir.DoubleType(), Value) - elif isinstance(Value, str): - is_wide: bool = getattr(Node, 'kind', None) == 'u' - # 引号类型推断:单引号单字符 → i8,双引号/多字符/三重 → i8*,空 → 0(None) - quote_char: str | None = None - is_triple: bool = False - if not is_wide: - col_offset: int = getattr(Node, 'col_offset', 0) - node_lineno: int = getattr(Node, 'lineno', 0) - # AST 的 lineno 可能不准确(常量节点可能指向下一行), - # 因此在 lineno 和 lineno-1 两行中查找引号 - for try_lineno in (node_lineno, node_lineno - 1): - # AST 行号含 _inject_auto_imports 注入的 N 行,须经 _DiskLineno - # 还原成磁盘行号,否则取到 N 行之后的错行:引号判定只靠"错行同列 - # 恰好也是引号"的巧合命中,一旦源码行数变动(单行化/增删行)就会 - # 把 'x' 误判成字符串(i8*)→ 传给 int 形参时 fail-fast。 - source_line: str | None = Gen._get_source_line(Gen._DiskLineno(try_lineno)) - if not source_line: - continue - # col_offset 是 UTF-8 字节偏移(vast Tokenizer 口径)→ 转字符偏移; - # 前缀含多字节字符时二者不等,直接按字节偏移取字符会取错列。 - char_col: int = len(source_line.encode('utf-8')[:col_offset].decode('utf-8', 'ignore')) - if char_col < len(source_line): - ch: str = source_line[char_col] - if ch in ("'", '"'): - quote_char = ch - if char_col + 2 < len(source_line) and source_line[char_col:char_col + 3] in ("'''", '"""'): - is_triple = True - break - # 空字符串 → 创建指向 \0 的全局变量 (而非 null, 避免 strcmp 解引用空指针崩溃) - # 走与非空字符串相同的路径, 由下方 else 分支处理 - # 单引号单字符(非三重)→ i8 - is_char_literal: bool = (not is_triple) and (quote_char == "'") and (len(Value) == 1) and not is_wide - if is_char_literal: - if VarType is not None and isinstance(VarType, ir.PointerType): - pass # 类型不匹配:目标是 ptr 但值是 char,走 string 路径 - elif isinstance(VarType, ir.IntType): - return ir.Constant(VarType, ord(Value[0])) - elif isinstance(VarType, str): - resolved = _CR.ResolveName(VarType) or _CR.LLVMToCType(VarType) - if resolved: - ctype_cls = resolved[0] if isinstance(resolved, tuple) else resolved - inst = ctype_cls() - size = getattr(inst, 'Size', 32) - return ir.Constant(ir.IntType(size), ord(Value[0])) - llvm_match = _RE_LLVM_INT_TYPE.match(VarType) - if llvm_match: - return ir.Constant(ir.IntType(int(llvm_match.group(1))), ord(Value[0])) - elif VarType is not None: - return ir.Constant(ir.IntType(8), ord(Value[0])) - else: - # 无类型提示:单引号单字符默认为 i8 - return ir.Constant(ir.IntType(8), ord(Value[0])) - if is_wide: - str_value: str = Value + '\x00' - wide_chars: list[int] = [ord(c) for c in str_value] - target_count: int = len(wide_chars) - str_type: ir.ArrayType = ir.ArrayType(ir.IntType(16), target_count) - gv_name: str = f"str_const_{Gen.string_const_counter}" - gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, str_type, name=gv_name) - gv.initializer = ir.Constant(str_type, wide_chars) - gv.linkage = 'internal' - Gen.string_const_counter += 1 - ptr: Any = Gen.builder.gep(gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{gv_name}_cast") - return ptr - else: - str_value = Value + '\x00' - str_bytes: bytes = str_value.encode('utf-8') - target_count = len(str_bytes) - if isinstance(VarType, ir.ArrayType) and isinstance(VarType.element, ir.IntType) and VarType.element.width == 8: - target_count = VarType.count - if len(str_bytes) < target_count: - str_bytes = str_bytes + b'\x00' * (target_count - len(str_bytes)) - else: - str_bytes = str_bytes[:target_count] - str_type = ir.ArrayType(ir.IntType(8), target_count) - gv_name = f"str_const_{Gen.string_const_counter}" - gv = ir.GlobalVariable(Gen.module, str_type, name=gv_name) - gv.initializer = ir.Constant(str_type, bytearray(str_bytes)) - gv.linkage = 'internal' - Gen.string_const_counter += 1 - ptr = Gen.builder.gep(gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{gv_name}_cast") - return ptr - return ir.Constant(ir.IntType(32), 0) - - def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None, - ConstWidthHint: int | None = None) -> ir.Value: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - VarName: str = Node.id - # 治本修复:不把 module.globals 的 initializer 注入 _define_constants。 - # 原因:CDefine(t.CDefine 注解)在 AnnAssign/Assign 处理时已注册到 - # _define_constants 且不创建 LLVM global(不在 module.globals); - # module.globals 里全是可变全局变量(如 _sha1_store_count,有 += 写入), - # 若把其 initializer 当常量传播,会导致 range(_sha1_store_count) 被求值成 - # range(0)、printf 印 0 等问题(读取处不生成 load)。 - # CDefine 的本模块优先靠下方 _define_constants 查询保证,双点 bug 不复发。 - define_constants: dict[str, Any] = getattr(Gen, '_define_constants', {}) - # 治本防护:module.globals 里的都是可变全局变量(CDefine 不创建 LLVM global, - # 仅注册到 _define_constants)。可变全局有 += 等写入,不能当常量传播, - # 跳过 _define_constants 查询,直接走下方 variables/global_vars/module.globals 的 load。 - if VarName in define_constants and not (hasattr(Gen, 'module') and VarName in Gen.module.globals): - val: Any = define_constants[VarName] - # CDefine 常量注册值(用于使用处宽度判据;下方 _gvar 覆盖是"同名全局变量" - # 的既有特例,不应把变量值当常量做宽度判据) - _cdefine_val: Any = val - # 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过 - # 共享 _define_constants 覆盖本模块常量 - # (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug) - _gvar: Any = None - try: - if hasattr(Gen, 'module') and VarName in Gen.module.globals: - _gvar = Gen.module.globals[VarName] - except Exception: - _gvar = None - if _gvar is not None and getattr(_gvar, 'initializer', None) is not None: - _init: Any = _gvar.initializer - if isinstance(_init, ir.Constant) and isinstance(_init.constant, (int, float, str)): - val = _init.constant - if isinstance(val, int): - # 使用处宽度(唯一判据 SymbolUtils.RequireDefineUseWidth): - # 上下文(赋值目标 / 形参 / 返回注解 / 运算对象…)给出的位宽更宽 ⇒ - # 就地按该宽度物化(立即数升宽无损);更窄 ⇒ fail-fast(替换此前的 - # `val & mask` 静默截断)。 - # 宽度来源优先级:**窄通道 ConstWidthHint**(仅常量物化读取,由调用 - # 装配处按形参/返回类型下传)> 既有尽宽通道 VarType(赋值目标 / - # 二元运算对象)。两者都取不到 ⇒ 落到下方既有 32/64 二值口径。 - _use_ty: ir.Type | None = None - # 报文的"使用处"标签按**通道**取真实站点种类(不再写笼统的 - # "由调用方下传的类型提示"):窄通道 ConstWidthHint 只由 - # 调用装配处(形参)与返回处下传;既有 VarType 通道承载的是 - # 赋值目标 / 二元运算对象类型。 - _use_ctx: str = '赋值目标 / 运算对象类型' - if ConstWidthHint in (8, 16, 32, 64): - _use_ty = ir.IntType(int(ConstWidthHint)) - _use_ctx = '形参 / 返回注解' - elif VarType is not None and isinstance(VarType, ir.IntType): - _use_ty = VarType - if isinstance(_use_ty, ir.IntType): - RequireDefineUseWidthAt(Gen, self.Trans, Node, _cdefine_val, - int(_use_ty.width), VarName, _use_ctx) - return ir.Constant(_use_ty, val & ((1 << _use_ty.width) - 1)) - # 带类型注册:无使用处宽度提示时,按 define 的**声明类型**物化 - # (`define X: DWORD = -11` → i32 位型 0xFFFFFFF5,而不是按值大小猜 i64)。 + RequireExplicitWidthLiteral, + DefineDeclTypeValue, GlobalDeclShadowsDefine) + + +# --------------------------------------------------------------- +# 注解/常量折叠里的正则**预编译**(性能:避免每次 `re.match`/`re.search(字面量, ...)` +# 的"模式串哈希 → re 模块级缓存查找"。语义与逐字写法完全一致,模式串一字未改)。 +# --------------------------------------------------------------- +# 从 `list[...]` 注解串里取出元素类型与可选长度 +_RE_LIST_ELEM_TYPE: re.Pattern[str] = re.compile(r'list\[([^,\]]+)(?:,\s*(\d+))?\]') +# LLVM 整型类型名 `i8`/`i32`/... 的位宽提取 +_RE_LLVM_INT_TYPE: re.Pattern[str] = re.compile(r'^i(\d+)$') + + +def DefineUseWidthFromLLVMType(Ty: object) -> int | None: + """LLVM 类型 → CDefine 常量的"使用处位宽"提示;其余 → None。 + + 只认 i8/i16/i32/i64(与 SymbolUtils.DEFINE_WIDTH_LADDER 同集合): + i1(比较/逻辑结果)与指针不构成"整数使用处宽度",必须落回"取不到 ⇒ + 默认 32/64",否则 `C == 0` 之类会拿到位宽 1 而误报"使用处宽度不足"。 + """ + if isinstance(Ty, ir.IntType) and Ty.width in (8, 16, 32, 64): + return int(Ty.width) + return None + + +class ExprHandle(BaseHandle): + + # list 元素类型 → (字节大小, LLVM 元素类型) 映射表 + # 用于 _HandleListLlvm 中 alloc_size / byte_offset / elem_ptr 类型统一查表, + # 消除原代码三处重复的 if-elif 链。 + _ELEM_TYPE_INFO: dict[str, tuple[int, ir.Type]] = { + 'i8': (1, ir.IntType(8)), + 'i16': (2, ir.IntType(16)), + 'i32': (4, ir.IntType(32)), + 'float': (4, ir.FloatType()), + 'double': (8, ir.DoubleType()), + } + + def __init__(self, translator: "Translator") -> None: + super().__init__(translator) + + def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None: + return self.Trans.ExprUtils.GetOpSymbol(Op) + + def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None: + return self.Trans.ExprUtils.GetUnaryOpSymbol(Op) + + def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None: + return self.Trans.ExprUtils.GetComparatorSymbol(Op) + + def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + return self.Trans.ExprUtils._get_var_class(node, Gen) + + def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None: + return self.Trans.ExprUtils._try_operator_overLoad(ClassName, op_name, obj_val, Gen, other_val) + + def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: + return self.Trans.ExprAttrHandle._get_llvm_member_offset(field_name, ClassName, Gen) + + def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None, + ConstWidthHint: int | None = None) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if not Gen or not Gen.builder: + return None + if isinstance(Node, ast.Constant): + return self._HandleConstantLlvm(Node, VarType) + elif isinstance(Node, ast.Name): + # ConstWidthHint 是**窄通道**:只有 Name(CDefine 常量物化)读取它, + # 其余分支(字面量/列表/运算…)逐字沿用既有 VarType 语义 ⇒ 非常量 + # 路径的既有 IR 不变。 + return self._HandleNameLlvm(Node, VarType, ConstWidthHint) + elif isinstance(Node, ast.BinOp): + return self.Trans.ExprOpsHandle._HandleBinOpLlvm(Node, VarType) + elif isinstance(Node, ast.BoolOp): + return self.Trans.ExprOpsHandle._HandleBoolOpLlvm(Node) + elif isinstance(Node, ast.UnaryOp): + return self.Trans.ExprOpsHandle._HandleUnaryOpLlvm(Node) + elif isinstance(Node, ast.Starred): + # `*p` 出现在值位置(典型:调用实参 `f(*p)`):C/Python 解析器都按 + # Python `*args` 语义归类为 Starred(Load)(vast_c.c ParseCallTail、 + # ExprParser.py:543-548)。Viper 未实现解包 ⇒ 与 `*p` 读侧对齐,一律 + # 归一为 UnaryOp(Deref) 解引用读取(与 ViperC ExprGenMixin 的同类归一 + # 逐字同口径)。此前无本分支 ⇒ 落到函数末尾兜底 `return i32 0`, + # 把 `f(*p)` 静默变成 `f(0)`。 + StarredInner: ast.expr | None = Node.value + if StarredInner is None: + raise Exception(f"值位置的单目 '*' 缺少操作数{Gen._get_node_info()}") + return self.Trans.ExprOpsHandle._HandleUnaryOpLlvm( + ast.UnaryOp(op=ast.Deref(), operand=StarredInner)) + elif isinstance(Node, ast.Call): + return self.Trans.ExprCallHandle._HandleCallLlvm(Node) + elif isinstance(Node, ast.New): + # Viper `new [描述符...] T(args)`:取存储 + 构造(见 _HandleNewExprLlvm) + return self.Trans.ExprCallHandle._HandleNewExprLlvm(Node) + elif isinstance(Node, ast.Await): + return self.Trans.AsyncHandler._HandleAwaitLlvm(Node, Gen) + elif isinstance(Node, ast.Compare): + return self.Trans.ExprOpsHandle._HandleCompareLlvm(Node) + elif isinstance(Node, ast.Attribute): + return self.Trans.ExprAttrHandle._HandleAttributeLlvm(Node) + elif isinstance(Node, ast.Subscript): + return self.Trans.ExprAttrHandle._HandleSubscriptLlvm(Node) + elif isinstance(Node, ast.IfExp): + return self.Trans.ExprLambdaHandle._HandleIfExpLlvm(Node) + elif isinstance(Node, ast.NamedExpr): + return self.Trans.ExprLambdaHandle._HandleNamedExprLlvm(Node) + elif isinstance(Node, ast.JoinedStr): + return self.Trans.ExprFormatHandle._HandleJoinedStrLlvm(Node) + elif isinstance(Node, ast.Lambda): + return self.Trans.ExprLambdaHandle._HandleLambdaLlvm(Node) + elif isinstance(Node, ast.List): + return self._HandleListLlvm(Node, VarType) + return ir.Constant(ir.IntType(32), 0) + + def _HandleListLlvm(self, Node: ast.List, VarType: ir.Type | str | None = None) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + elements: list[ast.expr] = Node.elts + if not elements: + return ir.Constant(ir.IntType(8).as_pointer(), None) + + array_len: int = len(elements) + elem_type: str = 'i8' + + if VarType and isinstance(VarType, str) and 'list[' in VarType: + match: re.Match | None = _RE_LIST_ELEM_TYPE.search(VarType) + if match: + type_str: str = match.group(1).strip() + if match.group(2): + array_len = int(match.group(2)) + if 'CChar' in type_str or 'char' in type_str.lower(): + elem_type = 'i8' + elif 'CFloat' in type_str or type_str.lower() == 'float': + elem_type = 'float' + elif 'CDouble' in type_str or type_str.lower() == 'double': + elem_type = 'double' + elif 'CInt' in type_str or ('int' in type_str.lower() and 'unsigned' not in type_str.lower()): + elem_type = 'i32' + elif 'CUnsignedInt' in type_str or 'unsigned' in type_str.lower(): + elem_type = 'i32' + elif 'CUnsignedChar' in type_str or 'unsigned char' in type_str.lower(): + elem_type = 'i8' + elif 'CShort' in type_str or 'short' in type_str.lower(): + elem_type = 'i16' + elif 'CUnsignedShort' in type_str: + elem_type = 'i16' + elif elements and all(isinstance(e, (ast.Constant,)) and isinstance(getattr(e, 'value', None), float) for e in elements): + elem_type = 'float' + + alloc_size: int = array_len + elem_info: tuple[int, ir.Type] = self._ELEM_TYPE_INFO.get(elem_type, (1, ir.IntType(8))) + elem_size: int = elem_info[0] + elem_llvm_type: ir.Type = elem_info[1] + alloc_size *= elem_size + + malloc_fn: Any = None + malloc_arg_type: ir.IntType = ir.IntType(64) + for fn in Gen.module.global_values: + if fn.name == 'malloc': + malloc_fn = fn + func_ftype: Any = getattr(fn, 'ftype', None) + if func_ftype and getattr(func_ftype, 'args', None): + if func_ftype.args: + malloc_arg_type = fn.ftype.args[0] + break + + if not malloc_fn: + try: + malloc_fn_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) + malloc_fn = Gen._CreateOrReuseFuncDecl('malloc', malloc_fn_type) + Gen.functions['malloc'] = malloc_fn + except Exception as _e: # 回退:malloc 函数创建失败时返回 null + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise + return ir.Constant(ir.IntType(8).as_pointer(), None) + + alloc_ptr: Any = Gen.builder.call(malloc_fn, [ir.Constant(malloc_arg_type, alloc_size)]) + cast_ptr: Any = Gen.builder.bitcast(alloc_ptr, ir.PointerType(ir.IntType(8))) + + for i, elem in enumerate(elements): + if i >= array_len: + break + + elem_val: Any = self.HandleExprLlvm(elem, VarType=elem_type) + if elem_val is None: + continue + # None(空值常量)允许隐式转换为任意元素类型 ⇒ 物化为目标元素类型零值。 + if Gen._IsNoneValue(elem_val, elem): + elem_val = Gen._ZeroConst(elem_llvm_type) + + byte_offset: int = i + if elem_type == 'i32': + byte_offset = i * 4 + elif elem_type == 'i16': + byte_offset = i * 2 + elif elem_type == 'float': + byte_offset = i * 4 + elif elem_type == 'double': + byte_offset = i * 8 + + # 元素地址:以 i8* 基址按字节 GEP,再转成元素指针类型 + # (不再 ptr→uint64 手动加偏移再 inttoptr,见新规范第 10 条) + elem_addr: Any = Gen.builder.gep(cast_ptr, [ir.Constant(ir.IntType(64), byte_offset)], name=f"elem_addr_{i}") + if elem_type == 'i32': + elem_ptr: Any = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(32))) + elif elem_type == 'i16': + elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(16))) + elif elem_type == 'float': + elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.FloatType())) + elif elem_type == 'double': + elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.DoubleType())) + else: + elem_ptr = elem_addr + + if elem_type == 'i8': + if isinstance(elem_val.type, ir.IntType) and elem_val.type.width > 8: + elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}") + elif isinstance(elem_val.type, ir.PointerType): + # 指针 → 整数:隐式转换一律禁止(元素值是指针、元素类型是整数) + # (None 已在上方短路为零值,不会落到这里)。 + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type}," + f"但目标元素类型是 i8;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" + f"{Gen._DescribeValueNode(elem)}" + ) + elif elem_type == 'i32': + if isinstance(elem_val.type, ir.IntType): + if elem_val.type.width < 32: + elem_val = Gen.builder.zext(elem_val, ir.IntType(32), name=f"zext_elem_{i}") + elif elem_val.type.width > 32: + elem_val = Gen.builder.trunc(elem_val, ir.IntType(32), name=f"trunc_elem_{i}") + elif isinstance(elem_val.type, ir.PointerType): + # 指针 → 整数:隐式转换一律禁止(ptrtoint 到 i32 还会截断地址) + # (None 已在上方短路为零值,不会落到这里)。 + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type}," + f"但目标元素类型是 i32;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" + f"{Gen._DescribeValueNode(elem)}" + ) + elif elem_type == 'i16': + if isinstance(elem_val.type, ir.IntType): + if elem_val.type.width < 16: + elem_val = Gen.builder.zext(elem_val, ir.IntType(16), name=f"zext_elem_{i}") + elif elem_val.type.width > 16: + elem_val = Gen.builder.trunc(elem_val, ir.IntType(16), name=f"trunc_elem_{i}") + elif elem_type == 'float': + if isinstance(elem_val.type, ir.DoubleType): + elem_val = Gen.builder.fptrunc(elem_val, ir.FloatType(), name=f"fptrunc_elem_{i}") + elif isinstance(elem_val.type, ir.IntType): + elem_val = Gen.builder.sitofp(elem_val, ir.FloatType(), name=f"sitofp_elem_{i}") + elif elem_type == 'double': + if isinstance(elem_val.type, ir.FloatType): + elem_val = Gen.builder.fpext(elem_val, ir.DoubleType(), name=f"fpext_elem_{i}") + elif isinstance(elem_val.type, ir.IntType): + elem_val = Gen.builder.sitofp(elem_val, ir.DoubleType(), name=f"sitofp_elem_{i}") + + Gen.builder.store(elem_val, elem_ptr) + + if elem_type in ('float', 'double'): + return Gen.builder.bitcast(alloc_ptr, ir.PointerType(elem_llvm_type), name=f"list_{elem_type}_ptr") + return cast_ptr + + def _HandleConstantLlvm(self, Node: ast.Constant, VarType: ir.Type | str | None = None) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + Value: Any = Node.value + if Value is None: + if VarType is not None and isinstance(VarType, ir.PointerType): + return ir.Constant(VarType, None) + return ir.Constant(ir.IntType(8).as_pointer(), None) + elif isinstance(Value, bool): + return ir.Constant(ir.IntType(32), 1 if Value else 0) + elif isinstance(Value, int): + # 整数字面量越界 fail-fast(字面量本身无符号,0..2^64-1):与 VPC 词法层 + # `_lex_int_exceeds_u64` 同口径。负数侧由"一元减 + 字面量"的联合判定负责 + # (HandlesExprOps._HandleUnaryOpLlvm),此处只判字面量本身。 + RequireIntLiteralStorable(Gen, self.Trans, Node, Value) + # 显式宽度字面量(_int/_uint/_i/_u):先严格校验「不截断」,再按声明宽度出常量 + ExpW: int = int(getattr(Node, 'int_width', 0) or 0) + if ExpW > 0: + ExpU: int = int(getattr(Node, 'int_unsigned', 0) or 0) + RequireExplicitWidthLiteral(Gen, self.Trans, Node, Value, ExpW, ExpU) + return ir.Constant(ir.IntType(ExpW), Value & ((1 << ExpW) - 1)) + if VarType is not None: + if isinstance(VarType, ir.IntType): + return ir.Constant(VarType, Value & ((1 << VarType.width) - 1)) + if isinstance(VarType, ir.PointerType): + return ir.Constant(ir.IntType(64), Value) + if isinstance(VarType, (ir.FloatType, ir.DoubleType)): + return ir.Constant(VarType, float(Value)) + if isinstance(VarType, str) and '*' in VarType: + return ir.Constant(ir.IntType(64), Value) + if isinstance(VarType, str): + resolved: Any = _CR.ResolveName(VarType) or _CR.LLVMToCType(VarType) + if resolved: + ctype_cls: type = resolved[0] if isinstance(resolved, tuple) else resolved + inst: Any = ctype_cls() + size: int = getattr(inst, 'Size', 32) + is_unsigned: bool = getattr(inst, 'IsSigned', True) is False + mask: int = (1 << size) - 1 + return ir.Constant(ir.IntType(size), Value & mask) + llvm_match: re.Match | None = _RE_LLVM_INT_TYPE.match(VarType) + if llvm_match: + width: int = int(llvm_match.group(1)) + return ir.Constant(ir.IntType(width), Value & ((1 << width) - 1)) + if Value < 0 or Value > 0xFFFFFFFF: + return ir.Constant(ir.IntType(64), Value) + return ir.Constant(ir.IntType(32), Value & 0xFFFFFFFF) + elif isinstance(Value, float): + if VarType and isinstance(VarType, ir.FloatType): + return ir.Constant(VarType, Value) + elif VarType and isinstance(VarType, ir.DoubleType): + return ir.Constant(VarType, Value) + return ir.Constant(ir.DoubleType(), Value) + elif isinstance(Value, str): + is_wide: bool = getattr(Node, 'kind', None) == 'u' + # 引号类型推断:单引号单字符 → i8,双引号/多字符/三重 → i8*,空 → 0(None) + quote_char: str | None = None + is_triple: bool = False + if not is_wide: + col_offset: int = getattr(Node, 'col_offset', 0) + node_lineno: int = getattr(Node, 'lineno', 0) + # AST 的 lineno 可能不准确(常量节点可能指向下一行), + # 因此在 lineno 和 lineno-1 两行中查找引号 + for try_lineno in (node_lineno, node_lineno - 1): + # AST 行号含 _inject_auto_imports 注入的 N 行,须经 _DiskLineno + # 还原成磁盘行号,否则取到 N 行之后的错行:引号判定只靠"错行同列 + # 恰好也是引号"的巧合命中,一旦源码行数变动(单行化/增删行)就会 + # 把 'x' 误判成字符串(i8*)→ 传给 int 形参时 fail-fast。 + source_line: str | None = Gen._get_source_line(Gen._DiskLineno(try_lineno)) + if not source_line: + continue + # col_offset 是 UTF-8 字节偏移(vast Tokenizer 口径)→ 转字符偏移; + # 前缀含多字节字符时二者不等,直接按字节偏移取字符会取错列。 + char_col: int = len(source_line.encode('utf-8')[:col_offset].decode('utf-8', 'ignore')) + if char_col < len(source_line): + ch: str = source_line[char_col] + if ch in ("'", '"'): + quote_char = ch + if char_col + 2 < len(source_line) and source_line[char_col:char_col + 3] in ("'''", '"""'): + is_triple = True + break + # 空字符串 → 创建指向 \0 的全局变量 (而非 null, 避免 strcmp 解引用空指针崩溃) + # 走与非空字符串相同的路径, 由下方 else 分支处理 + # 单引号单字符(非三重)→ i8 + is_char_literal: bool = (not is_triple) and (quote_char == "'") and (len(Value) == 1) and not is_wide + if is_char_literal: + if VarType is not None and isinstance(VarType, ir.PointerType): + pass # 类型不匹配:目标是 ptr 但值是 char,走 string 路径 + elif isinstance(VarType, ir.IntType): + return ir.Constant(VarType, ord(Value[0])) + elif isinstance(VarType, str): + resolved = _CR.ResolveName(VarType) or _CR.LLVMToCType(VarType) + if resolved: + ctype_cls = resolved[0] if isinstance(resolved, tuple) else resolved + inst = ctype_cls() + size = getattr(inst, 'Size', 32) + return ir.Constant(ir.IntType(size), ord(Value[0])) + llvm_match = _RE_LLVM_INT_TYPE.match(VarType) + if llvm_match: + return ir.Constant(ir.IntType(int(llvm_match.group(1))), ord(Value[0])) + elif VarType is not None: + return ir.Constant(ir.IntType(8), ord(Value[0])) + else: + # 无类型提示:单引号单字符默认为 i8 + return ir.Constant(ir.IntType(8), ord(Value[0])) + if is_wide: + str_value: str = Value + '\x00' + wide_chars: list[int] = [ord(c) for c in str_value] + target_count: int = len(wide_chars) + str_type: ir.ArrayType = ir.ArrayType(ir.IntType(16), target_count) + gv_name: str = f"str_const_{Gen.string_const_counter}" + gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, str_type, name=gv_name) + gv.initializer = ir.Constant(str_type, wide_chars) + gv.linkage = 'internal' + Gen.string_const_counter += 1 + ptr: Any = Gen.builder.gep(gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{gv_name}_cast") + return ptr + else: + str_value = Value + '\x00' + str_bytes: bytes = str_value.encode('utf-8') + target_count = len(str_bytes) + if isinstance(VarType, ir.ArrayType) and isinstance(VarType.element, ir.IntType) and VarType.element.width == 8: + target_count = VarType.count + if len(str_bytes) < target_count: + str_bytes = str_bytes + b'\x00' * (target_count - len(str_bytes)) + else: + str_bytes = str_bytes[:target_count] + str_type = ir.ArrayType(ir.IntType(8), target_count) + gv_name = f"str_const_{Gen.string_const_counter}" + gv = ir.GlobalVariable(Gen.module, str_type, name=gv_name) + gv.initializer = ir.Constant(str_type, bytearray(str_bytes)) + gv.linkage = 'internal' + Gen.string_const_counter += 1 + ptr = Gen.builder.gep(gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{gv_name}_cast") + return ptr + return ir.Constant(ir.IntType(32), 0) + + def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None, + ConstWidthHint: int | None = None) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + VarName: str = Node.id + # 治本修复:不把 module.globals 的 initializer 注入 _define_constants。 + # 原因:CDefine(t.CDefine 注解)在 AnnAssign/Assign 处理时已注册到 + # _define_constants 且不创建 LLVM global(不在 module.globals); + # module.globals 里全是可变全局变量(如 _sha1_store_count,有 += 写入), + # 若把其 initializer 当常量传播,会导致 range(_sha1_store_count) 被求值成 + # range(0)、printf 印 0 等问题(读取处不生成 load)。 + # CDefine 的本模块优先靠下方 _define_constants 查询保证,双点 bug 不复发。 + define_constants: dict[str, Any] = getattr(Gen, '_define_constants', {}) + # 治本防护:module.globals 里的都是可变全局变量(CDefine 不创建 LLVM global, + # 仅注册到 _define_constants)。可变全局有 += 等写入,不能当常量传播, + # 跳过 _define_constants 查询,直接走下方 variables/global_vars/module.globals 的 load。 + if VarName in define_constants and not (hasattr(Gen, 'module') and VarName in Gen.module.globals): + val: Any = define_constants[VarName] + # CDefine 常量注册值(用于使用处宽度判据;下方 _gvar 覆盖是"同名全局变量" + # 的既有特例,不应把变量值当常量做宽度判据) + _cdefine_val: Any = val + # 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过 + # 共享 _define_constants 覆盖本模块常量 + # (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug) + _gvar: Any = None + try: + if hasattr(Gen, 'module') and VarName in Gen.module.globals: + _gvar = Gen.module.globals[VarName] + except Exception: + _gvar = None + if _gvar is not None and getattr(_gvar, 'initializer', None) is not None: + _init: Any = _gvar.initializer + if isinstance(_init, ir.Constant) and isinstance(_init.constant, (int, float, str)): + val = _init.constant + if isinstance(val, int): + # 使用处宽度(唯一判据 SymbolUtils.RequireDefineUseWidth): + # 上下文(赋值目标 / 形参 / 返回注解 / 运算对象…)给出的位宽更宽 ⇒ + # 就地按该宽度物化(立即数升宽无损);更窄 ⇒ fail-fast(替换此前的 + # `val & mask` 静默截断)。 + # 宽度来源优先级:**窄通道 ConstWidthHint**(仅常量物化读取,由调用 + # 装配处按形参/返回类型下传)> 既有尽宽通道 VarType(赋值目标 / + # 二元运算对象)。两者都取不到 ⇒ 落到下方既有 32/64 二值口径。 + _use_ty: ir.Type | None = None + # 报文的"使用处"标签按**通道**取真实站点种类(不再写笼统的 + # "由调用方下传的类型提示"):窄通道 ConstWidthHint 只由 + # 调用装配处(形参)与返回处下传;既有 VarType 通道承载的是 + # 赋值目标 / 二元运算对象类型。 + _use_ctx: str = '赋值目标 / 运算对象类型' + if ConstWidthHint in (8, 16, 32, 64): + _use_ty = ir.IntType(int(ConstWidthHint)) + _use_ctx = '形参 / 返回注解' + elif VarType is not None and isinstance(VarType, ir.IntType): + _use_ty = VarType + if isinstance(_use_ty, ir.IntType): + RequireDefineUseWidthAt(Gen, self.Trans, Node, _cdefine_val, + int(_use_ty.width), VarName, _use_ctx) + return ir.Constant(_use_ty, val & ((1 << _use_ty.width) - 1)) + # 带类型注册:无使用处宽度提示时,按 define 的**声明类型**物化 + # (`define X: DWORD = -11` → i32 位型 0xFFFFFFF5,而不是按值大小猜 i64)。 _dtv: tuple[int, int] | None = DefineDeclTypeValue( - getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None), VarName, val) - if _dtv is not None: - return ir.Constant(ir.IntType(_dtv[0]), _dtv[1]) - if VarName in Gen.var_signedness and Gen.var_signedness[VarName]: - if val < -(1 << 31) or val > 0xFFFFFFFF: - return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF) - return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF) - if val < -(1 << 31) or val > 0xFFFFFFFF: - return ir.Constant(ir.IntType(64), val) - return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF) - elif isinstance(val, float): - return ir.Constant(ir.DoubleType(), val) - elif isinstance(val, str): - return Gen._create_string_global(val) - # 治本修复(方案C):CDefine 不通过共享 SymbolTable 跨模块裸名共享。 - # 此处不再消费 SymbolTable 中的 IsDefine 符号,避免其他模块 CDefine - # 覆盖本模块同名全局变量(VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖)。 - # 本模块 CDefine 已在步骤1(_define_constants)解析; - # 跨模块 CDefine 通过 _RegisterFromImportAliases 显式导入注册到 _define_constants。 - # 治本修复:如果符号表也没有,回退查询 _all_define_constants - # _all_define_constants 由 Phase2Translator 从所有 .pyi 文件预提取, - # 包含跨模块导入的 CDefine 常量(如 FOREGROUND_GREEN、STD_OUTPUT_HANDLE) - if VarName not in define_constants and VarName not in Gen.variables and not GlobalDeclShadowsDefine(Gen, VarName): - all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} - if VarName in all_dc: - val: Any = all_dc[VarName] - if isinstance(val, int): - # 跨模块 CDefine 常量同口径:窄通道 ConstWidthHint(形参/返回 - # 类型下传)优先,其次既有 VarType;更宽 ⇒ 按该宽度物化, - # 更窄 ⇒ fail-fast(判据唯一源 SymbolUtils.RequireDefineUseWidth) - _use_ty2: ir.Type | None = None - _use_ctx2: str = '赋值目标 / 运算对象类型' - if ConstWidthHint in (8, 16, 32, 64): - _use_ty2 = ir.IntType(int(ConstWidthHint)) - _use_ctx2 = '形参 / 返回注解' - elif VarType is not None and isinstance(VarType, ir.IntType): - _use_ty2 = VarType - if isinstance(_use_ty2, ir.IntType): - RequireDefineUseWidthAt(Gen, self.Trans, Node, val, - int(_use_ty2.width), VarName, _use_ctx2) - return ir.Constant(_use_ty2, val & ((1 << _use_ty2.width) - 1)) - # 带类型注册:同本模块路径——按声明类型物化(见上)。 + getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None), VarName, val) + if _dtv is not None: + return ir.Constant(ir.IntType(_dtv[0]), _dtv[1]) + if VarName in Gen.var_signedness and Gen.var_signedness[VarName]: + if val < -(1 << 31) or val > 0xFFFFFFFF: + return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF) + return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF) + if val < -(1 << 31) or val > 0xFFFFFFFF: + return ir.Constant(ir.IntType(64), val) + return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF) + elif isinstance(val, float): + return ir.Constant(ir.DoubleType(), val) + elif isinstance(val, str): + return Gen._create_string_global(val) + # 治本修复(方案C):CDefine 不通过共享 SymbolTable 跨模块裸名共享。 + # 此处不再消费 SymbolTable 中的 IsDefine 符号,避免其他模块 CDefine + # 覆盖本模块同名全局变量(VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖)。 + # 本模块 CDefine 已在步骤1(_define_constants)解析; + # 跨模块 CDefine 通过 _RegisterFromImportAliases 显式导入注册到 _define_constants。 + # 治本修复:如果符号表也没有,回退查询 _all_define_constants + # _all_define_constants 由 Phase2Translator 从所有 .pyi 文件预提取, + # 包含跨模块导入的 CDefine 常量(如 FOREGROUND_GREEN、STD_OUTPUT_HANDLE) + if VarName not in define_constants and VarName not in Gen.variables and not GlobalDeclShadowsDefine(Gen, VarName): + all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} + if VarName in all_dc: + val: Any = all_dc[VarName] + if isinstance(val, int): + # 跨模块 CDefine 常量同口径:窄通道 ConstWidthHint(形参/返回 + # 类型下传)优先,其次既有 VarType;更宽 ⇒ 按该宽度物化, + # 更窄 ⇒ fail-fast(判据唯一源 SymbolUtils.RequireDefineUseWidth) + _use_ty2: ir.Type | None = None + _use_ctx2: str = '赋值目标 / 运算对象类型' + if ConstWidthHint in (8, 16, 32, 64): + _use_ty2 = ir.IntType(int(ConstWidthHint)) + _use_ctx2 = '形参 / 返回注解' + elif VarType is not None and isinstance(VarType, ir.IntType): + _use_ty2 = VarType + if isinstance(_use_ty2, ir.IntType): + RequireDefineUseWidthAt(Gen, self.Trans, Node, val, + int(_use_ty2.width), VarName, _use_ctx2) + return ir.Constant(_use_ty2, val & ((1 << _use_ty2.width) - 1)) + # 带类型注册:同本模块路径——按声明类型物化(见上)。 _dtv2: tuple[int, int] | None = DefineDeclTypeValue( - getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None), VarName, val) - if _dtv2 is not None: - return ir.Constant(ir.IntType(_dtv2[0]), _dtv2[1]) - if val < -(1 << 31) or val > 0xFFFFFFFF: - return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF) - return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF) - elif isinstance(val, float): - return ir.Constant(ir.DoubleType(), val) - elif isinstance(val, str): - return Gen._create_string_global(val) - # 优先检查 variables(alloca),因为变量可能已被 += 等操作迁移到 variables - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: Any = Gen.variables[VarName] - if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - if VarName in Gen.global_struct_class: - Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] - return VarPtr - if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - if VarName in Gen.global_struct_class: - Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] - return VarPtr - # 防御性恢复:对于 double-pointer(如 ndarray**,pointee 是 PointerType) - # 或其他非结构体类型,若 global_struct_class 已记录该变量的类名, - # 也恢复 var_struct_class。这确保方法调用能查找到正确的特化类 - # (如 ndarray[double]),而非 fallback 到 opaque 基础类型。 - if VarName in Gen.global_struct_class and VarName not in Gen.var_struct_class: - Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] - loaded: ir.Value = Gen._load(VarPtr, name=VarName) - # 大端局部变量:读取时 bswap 还原 - loaded = Gen._apply_bswap_if_big(loaded, getattr(Gen, 'local_var_byteorders', {}).get(VarName, ""), f"bswap_load_{VarName}") - return loaded - if VarName in Gen._reg_values: - return Gen._reg_values[VarName] - if VarName in Gen._direct_values: - return Gen._direct_values[VarName] - if VarName in Gen.global_vars and VarName in Gen.module.globals: - GVar: Any = Gen.module.globals[VarName] - Gen.variables[VarName] = GVar - if isinstance(GVar, ir.Function): - return GVar - if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - return GVar - return Gen._load(GVar, name=VarName) - if VarName in Gen.module.globals: - GVar = Gen.module.globals[VarName] - Gen.variables[VarName] = GVar - if isinstance(GVar, ir.Function): - return GVar - if VarName in Gen.global_struct_class: - Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] - if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - return GVar - return Gen._load(GVar, name=VarName) - if VarName == 'self': - # 治本修复:方法名为 _get_var_ptr(小写),原 GetVarPtr 不存在会抛 AttributeError。 - # 当 self 未注册到 variables 时(如 opaque struct 参数走了 else 分支), - # 通过 _get_var_ptr 兜底返回 None,由调用方处理。 - SelfPtr: ir.Value | None = Gen._get_var_ptr('self') - if SelfPtr is not None: - return SelfPtr - # 最终兜底:若 _get_var_ptr 返回 None(self 既不在 _reg_values 也不在 variables), - # 直接返回函数参数本身(LLVM Argument)。这避免了 AttributeError 导致的异常路径。 - for arg in getattr(Gen.func, 'args', []): - if getattr(arg, 'name', None) == 'self': - return arg - return ir.Constant(ir.IntType(32), 0) - if Gen._has_function(VarName): - func: Any = Gen._get_function(VarName) - if func: - return Gen.builder.bitcast(func, ir.IntType(8).as_pointer(), name=f"funcptr_{VarName}") - if VarName in Gen.class_methods: - return ir.Constant(ir.IntType(8).as_pointer(), None) - if VarName == 'True' or VarName == 'False': - return ir.Constant(ir.IntType(32), 1 if VarName == 'True' else 0) - SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) - if SymInfo: - if SymInfo.IsEnumMember and isinstance(SymInfo.value, int): - return ir.Constant(Gen._enum_int_type(SymInfo.EnumName, SymInfo), SymInfo.value) - if SymInfo.IsFunction or SymInfo.IsFuncPtr: - MangledName: str = Gen._mangle_func_name(VarName) - if MangledName in Gen.module.globals: - g: Any = Gen.module.globals[MangledName] - if isinstance(g, ir.Function): - Gen.functions[VarName] = g - return Gen.builder.bitcast(g, ir.IntType(8).as_pointer(), name=f"funcptr_{VarName}") - for sha1 in Gen.ModuleSha1Map.values(): - prefixed: str = f"{sha1}.{VarName}" - if prefixed in Gen.module.globals: - g = Gen.module.globals[prefixed] - if isinstance(g, ir.Function): - Gen.functions[VarName] = g - return Gen.builder.bitcast(g, ir.IntType(8).as_pointer(), name=f"funcptr_{VarName}") - t_c_imported: dict[str, tuple[str, str]] = getattr(self.Trans, '_t_c_imported_names', {}) - if VarName in t_c_imported: - src_module: str - src_name: str - src_module, src_name = t_c_imported[VarName] - virtual_attr: ast.Attribute = ast.Attribute( - value=ast.Name(id=src_module, ctx=ast.Load()), - attr=src_name, - ctx=ast.Load() - ) - ast.copy_location(virtual_attr, Node) - return self.Trans.ExprAttrHandle._HandleAttributeLlvm(virtual_attr) - # fail-fast: 未定义名称禁止静默物化为 i32 0(会把常量名写错 / define 丢失 / - # 变量漏声明这类上游缺陷藏到运行期,见 RequireNameDefined 的头注释)。 - RequireNameDefined(Gen, self.Trans, Node, VarName) + getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None), VarName, val) + if _dtv2 is not None: + return ir.Constant(ir.IntType(_dtv2[0]), _dtv2[1]) + if val < -(1 << 31) or val > 0xFFFFFFFF: + return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF) + return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF) + elif isinstance(val, float): + return ir.Constant(ir.DoubleType(), val) + elif isinstance(val, str): + return Gen._create_string_global(val) + # 优先检查 variables(alloca),因为变量可能已被 += 等操作迁移到 variables + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: Any = Gen.variables[VarName] + if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if VarName in Gen.global_struct_class: + Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] + return VarPtr + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if VarName in Gen.global_struct_class: + Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] + return VarPtr + # 防御性恢复:对于 double-pointer(如 ndarray**,pointee 是 PointerType) + # 或其他非结构体类型,若 global_struct_class 已记录该变量的类名, + # 也恢复 var_struct_class。这确保方法调用能查找到正确的特化类 + # (如 ndarray[double]),而非 fallback 到 opaque 基础类型。 + if VarName in Gen.global_struct_class and VarName not in Gen.var_struct_class: + Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] + loaded: ir.Value = Gen._load(VarPtr, name=VarName) + # 大端局部变量:读取时 bswap 还原 + loaded = Gen._apply_bswap_if_big(loaded, getattr(Gen, 'local_var_byteorders', {}).get(VarName, ""), f"bswap_load_{VarName}") + return loaded + if VarName in Gen._reg_values: + return Gen._reg_values[VarName] + if VarName in Gen._direct_values: + return Gen._direct_values[VarName] + if VarName in Gen.global_vars and VarName in Gen.module.globals: + GVar: Any = Gen.module.globals[VarName] + Gen.variables[VarName] = GVar + if isinstance(GVar, ir.Function): + return GVar + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar + return Gen._load(GVar, name=VarName) + if VarName in Gen.module.globals: + GVar = Gen.module.globals[VarName] + Gen.variables[VarName] = GVar + if isinstance(GVar, ir.Function): + return GVar + if VarName in Gen.global_struct_class: + Gen.var_struct_class[VarName] = Gen.global_struct_class[VarName] + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar + return Gen._load(GVar, name=VarName) + if VarName == 'self': + # 治本修复:方法名为 _get_var_ptr(小写),原 GetVarPtr 不存在会抛 AttributeError。 + # 当 self 未注册到 variables 时(如 opaque struct 参数走了 else 分支), + # 通过 _get_var_ptr 兜底返回 None,由调用方处理。 + SelfPtr: ir.Value | None = Gen._get_var_ptr('self') + if SelfPtr is not None: + return SelfPtr + # 最终兜底:若 _get_var_ptr 返回 None(self 既不在 _reg_values 也不在 variables), + # 直接返回函数参数本身(LLVM Argument)。这避免了 AttributeError 导致的异常路径。 + for arg in getattr(Gen.func, 'args', []): + if getattr(arg, 'name', None) == 'self': + return arg + return ir.Constant(ir.IntType(32), 0) + if Gen._has_function(VarName): + func: Any = Gen._get_function(VarName) + if func: + return Gen.builder.bitcast(func, ir.IntType(8).as_pointer(), name=f"funcptr_{VarName}") + if VarName in Gen.class_methods: + return ir.Constant(ir.IntType(8).as_pointer(), None) + if VarName == 'True' or VarName == 'False': + return ir.Constant(ir.IntType(32), 1 if VarName == 'True' else 0) + SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) + if SymInfo: + if SymInfo.IsEnumMember and isinstance(SymInfo.value, int): + return ir.Constant(Gen._enum_int_type(SymInfo.EnumName, SymInfo), SymInfo.value) + if SymInfo.IsFunction or SymInfo.IsFuncPtr: + MangledName: str = Gen._mangle_func_name(VarName) + if MangledName in Gen.module.globals: + g: Any = Gen.module.globals[MangledName] + if isinstance(g, ir.Function): + Gen.functions[VarName] = g + return Gen.builder.bitcast(g, ir.IntType(8).as_pointer(), name=f"funcptr_{VarName}") + for sha1 in Gen.ModuleSha1Map.values(): + prefixed: str = f"{sha1}.{VarName}" + if prefixed in Gen.module.globals: + g = Gen.module.globals[prefixed] + if isinstance(g, ir.Function): + Gen.functions[VarName] = g + return Gen.builder.bitcast(g, ir.IntType(8).as_pointer(), name=f"funcptr_{VarName}") + t_c_imported: dict[str, tuple[str, str]] = getattr(self.Trans, '_t_c_imported_names', {}) + if VarName in t_c_imported: + src_module: str + src_name: str + src_module, src_name = t_c_imported[VarName] + virtual_attr: ast.Attribute = ast.Attribute( + value=ast.Name(id=src_module, ctx=ast.Load()), + attr=src_name, + ctx=ast.Load() + ) + ast.copy_location(virtual_attr, Node) + return self.Trans.ExprAttrHandle._HandleAttributeLlvm(virtual_attr) + # fail-fast: 未定义名称禁止静默物化为 i32 0(会把常量名写错 / define 丢失 / + # 变量漏声明这类上游缺陷藏到运行期,见 RequireNameDefined 的头注释)。 + RequireNameDefined(Gen, self.Trans, Node, VarName) return ir.Constant(ir.IntType(32), 0) \ No newline at end of file diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index fa5118d..1697b1a 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -1,4121 +1,4127 @@ -from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin -import vast as ast -import os -import re -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode -from lib.includes import t -from lib.includes.t import CTypeRegistry +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +import vast as ast +import os +import re +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.includes import t +from lib.includes.t import CTypeRegistry from lib.core.SymbolUtils import ( IsTModule, ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, ResolveTypeArgStr, GenericArgRawName, ClassHasFuncLevelCVTable, IsVtableSlotMethod) -from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType - - -# len() 参数形态不支持时的共享提示语(与 TransPyV App/lib/core/Gen/Call/ -# CallGenDispatch.vp 的 len() 分支同口径,措辞逐字一致,便于用户对照两侧报文)。 -_LEN_SUPPORTED_HINT: str = ( - "len() 只支持定长数组/数组指针(t.CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;" - "非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度," - "或改用 t.CPtr[元素] 并显式维护元素计数" -) - -# 已删除的用户可见 CIf 族(c.CIf / c.CIfdef / c.CIfndef / c.CElif / c.CElse / c.CEndif): -# 用户再写即在调用点 FailFast,指向原生 cif/celif/celse 与 defined(NAME)(英文报文, -# 与 TransPyV CallGenDispatch.vp 同口径)。内部标记 c.@CIf 族不受影响。 -_REMOVED_CIF_CALL_MSG: str = ( - "c.CIf / c.CIfdef / c.CIfndef / c.CElif / c.CElse / c.CEndif were removed; use the cif / " - "celif / celse statements together with the defined(NAME) predicate instead " - "(e.g. `cif defined(NAME):` ... `celif ...:` ... `celse:` ...)") - - -# PROF 热点:_EmitLLVMIRInstruction 对每条 IR 模板都要做多次 re.match / re.split, -# 未预编译的模块级入口每次都要「模式串哈希 → re 缓存查找 → 未命中就重新编译」, -# 512 条缓存被打穿后开销巨大。这些模式全是字面量 → 提升为模块级预编译常量。 -# 模式串与调用点处的字面量逐字一致,语义不变。 -# _EmitLLVMIRInstruction 用:剥离 IR 模板前置的结果赋值 `%__OPn__ = ` -_RE_OP_ASSIGN: re.Pattern[str] = re.compile(r'%__OP(\d+)__\s*=\s*(.+)') -# _EmitLLVMIRInstruction 用:整数比较指令 icmp -_RE_ICMP: re.Pattern[str] = re.compile(r'icmp\s+(\w+)\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:浮点比较指令 fcmp -_RE_FCMP: re.Pattern[str] = re.compile(r'fcmp\s+([\w\s]+?)\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:select 指令 -_RE_SELECT: re.Pattern[str] = re.compile(r'select\s+i1\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:Load 指令 -_RE_LOAD: re.Pattern[str] = re.compile(r'Load\s+(\w+),\s*(\w+)\s+%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:store 指令 -_RE_STORE: re.Pattern[str] = re.compile(r'store\s+(\w+)\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:getelementptr 指令 -_RE_GEP: re.Pattern[str] = re.compile(r'getelementptr\s+(?:inbounds\s+)?(\w+),\s*(\w+)\s+%__OP(\d+)__,\s*(.+)') -# _EmitLLVMIRInstruction 用:单个 %__OPn__ 操作数占位引用 -_RE_OP_REF: re.Pattern[str] = re.compile(r'%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:取负形式 sub 指令 -_RE_NEG: re.Pattern[str] = re.compile(r'sub\s+(\w+)\s+(\d+),\s*%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:按位取反形式 xor 指令 -_RE_NOT: re.Pattern[str] = re.compile(r'xor\s+(\w+)\s+(-1|\d+),\s*%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:call 指令 -_RE_CALL: re.Pattern[str] = re.compile(r'call\s+(\w+)\s+@([\w.]+)\((.+)\)') -# _EmitLLVMIRInstruction 用:调用参数中的「类型 %__OPn__」片段 -_RE_TYPED_OP: re.Pattern[str] = re.compile(r'(\w+\*?)\s+%__OP(\d+)__') -# _EmitLLVMIRInstruction 用:调用参数中的「类型 常量」片段 -_RE_CONST: re.Pattern[str] = re.compile(r'(\w+)\s+(-?\d+)') -# _EmitLLVMIRInstruction 用:以逗号切分操作数列表(逗号 + 任意空白) -_RE_COMMA_WS: re.Pattern[str] = re.compile(r',\s*') - - -def _safe_ptr_type(ty: ir.Type) -> ir.Type: - """LLVM IR 中不允许 void* 指针,Ptr(VoidType) 统一替换为 i8*(对应 C 的 void* 语义)""" - if isinstance(ty, ir.PointerType): - cur: ir.Type = ty - while isinstance(cur, ir.PointerType): - if isinstance(cur.pointee, ir.VoidType): - return ir.IntType(8).as_pointer() - cur = cur.pointee - return ty - - -def _BinOpContainsCPtr(node: ast.AST) -> bool: - """递归检查 BinOp(BitOr) 联合类型中是否含 t.CPtr(指针标记)""" - if isinstance(node, ast.Attribute): - return node.attr == 'CPtr' - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - return _BinOpContainsCPtr(node.left) or _BinOpContainsCPtr(node.right) - return False - - -class ExprCallHandle(BaseHandle): - # vtable on-demand 缓存(类级,跨 worker 任务共享,进程内单线程无竞争)。 - # 原实现 pyi_cache 是 _try_register_vtable_ondemand 函数局部变量,每次调用都 - # 重建 → 437 次调用 = 437 次 open+ast.parse 同一批 pyi(prof 38.5s)。 - # vpi 以 sha1 命名、单次编译内只增不减,路径不变则内容不可变,进程内缓存安全。 - # _vtable_pyi_cache:pyi 文件绝对路径 → {类名: ClassDef 节点} - # _vtable_methods_cache:(sha1, class_name) → (methods, is_cvtable, is_novtable, parent_name) - _vtable_pyi_cache: dict[str, dict[str, ast.ClassDef]] = {} - _vtable_methods_cache: dict[tuple[str, str], tuple[list[str], bool, bool, str | None]] = {} - def _append_eh_msg_out_arg(self, CallArgs: list[ir.Value], func: ir.Function | None, Gen: LlvmGeneratorMixin) -> None: - if not func: - return - func_args: list[ir.Type] = func.function_type.args - n_user_args: int = len(CallArgs) - has_eh_msg: bool = False - has_eh_code: bool = False - if len(func_args) > n_user_args: - last_type: ir.Type = func_args[-1] - if (isinstance(last_type, ir.PointerType) - and isinstance(last_type.pointee, ir.IntType) - and last_type.pointee.width == 32): - has_eh_code = True - if len(func_args) > n_user_args + (1 if has_eh_code else 0): - check_idx: int = len(func_args) - 1 - (1 if has_eh_code else 0) - check_type: ir.Type = func_args[check_idx] - if (isinstance(check_type, ir.PointerType) - and isinstance(check_type.pointee, ir.PointerType) - and isinstance(check_type.pointee.pointee, ir.IntType) - and check_type.pointee.pointee.width == 8): - has_eh_msg = True - if not has_eh_msg: - return - eh_msg_ptr: ir.Value | None = None - eh_code_ptr: ir.Value | None = None - if Gen.eh_except_block_stack: - _, _, exception_code, eh_message = Gen.eh_except_block_stack[-1] - if eh_message is not None: - eh_msg_ptr = eh_message - if exception_code is not None: - eh_code_ptr = exception_code - if eh_msg_ptr is None and Gen.func and len(Gen.func.args) > 0: - for arg in Gen.func.args: - if arg.name == '__eh_msg_out__' and eh_msg_ptr is None: - eh_msg_ptr = arg - elif arg.name == '__eh_code_out__' and eh_code_ptr is None: - eh_code_ptr = arg - if eh_msg_ptr is None: - eh_msg_ptr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name="eh_msg_out_null") - if eh_code_ptr is None: - eh_code_ptr = Gen._allocaEntry(ir.IntType(32), name="eh_code_out_null") - Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), eh_msg_ptr) - Gen._store(ir.Constant(ir.IntType(32), 0), eh_code_ptr) - CallArgs.append(eh_msg_ptr) - if has_eh_code: - CallArgs.append(eh_code_ptr) - - def _check_eh_return(self, result: ir.Value, func_name: str, Gen: LlvmGeneratorMixin, called_func: ir.Function | None = None) -> ir.Value: - has_eh_msg_out: bool = False - has_eh_code_out: bool = False - if called_func and hasattr(called_func, 'args') and len(called_func.args) > 0: - for arg in called_func.args: - if arg.name == '__eh_msg_out__': - has_eh_msg_out = True - elif arg.name == '__eh_code_out__': - has_eh_code_out = True - if not has_eh_msg_out: - return result - eh_msg_ptr: ir.Value | None = None - eh_code_ptr: ir.Value | None = None - if Gen.eh_except_block_stack: - _, _, exception_code, eh_message = Gen.eh_except_block_stack[-1] - if eh_message is not None: - eh_msg_ptr = eh_message - if exception_code is not None: - eh_code_ptr = exception_code - if eh_msg_ptr is None and Gen.func and len(Gen.func.args) > 0: - for arg in Gen.func.args: - if arg.name == '__eh_msg_out__' and eh_msg_ptr is None: - eh_msg_ptr = arg - elif arg.name == '__eh_code_out__' and eh_code_ptr is None: - eh_code_ptr = arg - if eh_msg_ptr is not None: - eh_msg_val: ir.Value = Gen._load(eh_msg_ptr, name=f"Load_eh_msg_{func_name}") - null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) - is_error: ir.Value = Gen.builder.icmp_signed('!=', eh_msg_val, null_ptr, name=f"check_eh_msg_{func_name}") - OkBB: ir.Block = Gen.func.append_basic_block(name=f"ok_after_{func_name}") - ErrorBB: ir.Block = Gen.func.append_basic_block(name=f"error_after_{func_name}") - Gen.builder.cbranch(is_error, ErrorBB, OkBB) - Gen.builder.position_at_start(ErrorBB) - ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None - error_ret_val: ir.Constant | None = None - if isinstance(ret_type, ir.IdentifiedStructType): - if ret_type.elements: - error_ret_val = ir.Constant(ret_type, [ - ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) - else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) - else ir.Constant(et, ir.Undefined) for et in ret_type.elements]) - else: - error_ret_val = ir.Constant(ret_type, None) - elif isinstance(ret_type, ir.PointerType): - error_ret_val = ir.Constant(ret_type, None) - elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)): - error_ret_val = ir.Constant(ret_type, 0.0) - elif isinstance(ret_type, ir.IntType): - error_ret_val = ir.Constant(ret_type, 1) - elif isinstance(ret_type, ir.VoidType): - pass - else: - error_ret_val = ir.Constant(ir.IntType(32), 1) - if Gen.eh_except_block_stack: - ExceptBB, _, exception_code, _ = Gen.eh_except_block_stack[-1] - if ExceptBB is not None: - if has_eh_code_out and eh_code_ptr is not None: - eh_code_val = Gen._load(eh_code_ptr, name=f"Load_eh_code_{func_name}") - Gen._store(eh_code_val, exception_code) - else: - Gen._store(ir.Constant(ir.IntType(32), 1), exception_code) - Gen.builder.branch(ExceptBB) - else: - if isinstance(ret_type, ir.VoidType): - Gen.builder.ret_void() - else: - Gen.builder.ret(error_ret_val) - else: - if isinstance(ret_type, ir.VoidType): - Gen.builder.ret_void() - else: - Gen.builder.ret(error_ret_val) - Gen.builder.position_at_start(OkBB) - - def _fill_default_args(self, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None = None) -> None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - global_defaults: dict = self.Trans._global_function_default_args - func_defaults: list | None = None - if func_name: - func_defaults = global_defaults.get(func_name) - if not func_defaults and func: - func_defaults = global_defaults.get(func.name) - if not func_defaults and func_name: - for gk, gv in global_defaults.items(): - if func_name in gk or gk.endswith(func_name) or gk.endswith('.' + func_name): - func_defaults = gv - break - if func and len(CallArgs) < len(func.args): - total_params: int = len(func.args) - if func_defaults: - num_defaults: int = len(func_defaults) - default_start: int = total_params - num_defaults - else: - num_defaults = 0 - default_start = total_params - while len(CallArgs) < total_params: - arg_idx: int = len(CallArgs) - arg = func.args[arg_idx] - param_type: ir.Type = arg.type if hasattr(arg, 'type') else arg - if func_defaults and arg_idx >= default_start: - def_idx: int = arg_idx - default_start - if def_idx < num_defaults and func_defaults[def_idx] is not None: - default_val = func_defaults[def_idx] - if isinstance(param_type, ir.IntType): - max_val: int = (1 << param_type.width) - 1 - if isinstance(default_val, bool): - default_val = 1 if default_val else 0 - if isinstance(default_val, int) and default_val > max_val: - default_val = default_val & max_val - if isinstance(default_val, int) and default_val < 0: - default_val = default_val & max_val - CallArgs.append(ir.Constant(param_type, default_val)) - elif isinstance(param_type, ir.PointerType): - CallArgs.append(ir.Constant(param_type, None)) - else: - try: - _zv = Gen._zero_value_for_type(param_type) - CallArgs.append(_zv if _zv is not None else ir.Constant(ir.IntType(32), 0)) - except (AssertionError, TypeError): - CallArgs.append(ir.Constant(ir.IntType(32), 0)) - else: - if isinstance(param_type, ir.PointerType): - CallArgs.append(ir.Constant(param_type, None)) - elif isinstance(param_type, ir.IntType): - CallArgs.append(ir.Constant(param_type, 0)) - else: - try: - _zv = Gen._zero_value_for_type(param_type) - CallArgs.append(_zv if _zv is not None else ir.Constant(ir.IntType(32), 0)) - except (AssertionError, TypeError): - CallArgs.append(ir.Constant(ir.IntType(32), 0)) - else: - if isinstance(param_type, ir.PointerType): - CallArgs.append(ir.Constant(param_type, None)) - elif isinstance(param_type, ir.IntType): - CallArgs.append(ir.Constant(param_type, 0)) - else: - try: - _zv = Gen._zero_value_for_type(param_type) - CallArgs.append(_zv if _zv is not None else ir.Constant(ir.IntType(32), 0)) - except (AssertionError, TypeError): - CallArgs.append(ir.Constant(ir.IntType(32), 0)) - - def _LookupGlobalDefaults(self, func_name: str | None) -> list | None: - """按函数名查询全局默认参数表(跨模块调用点用)。 - - 查找顺序:精确键 → 去 mangled 前缀后的裸名 → 以 '.<裸名>' 结尾的键 - (如 {sha1}.name)。精确键优先,避免"同名不同模块"的默认值互相乱配。 - """ - if not func_name: - return None - table: dict = getattr(self.Trans, '_global_function_default_args', None) or {} - if not table: - return None - hit = table.get(func_name) - if hit: - return hit - base: str = func_name.split('.')[-1] - if base != func_name: - hit = table.get(base) - if hit: - return hit - for key, value in table.items(): - if key.endswith('.' + base): - return value - return None - - def _LookupGlobalParamNames(self, func_name: str | None) -> list | None: - """按函数名查询全局参数名表;查找顺序同 _LookupGlobalDefaults。""" - if not func_name: - return None - table: dict = getattr(self.Trans, '_global_function_param_names', None) or {} - if not table: - return None - hit = table.get(func_name) - if hit: - return hit - base: str = func_name.split('.')[-1] - if base != func_name: - hit = table.get(base) - if hit: - return hit - for key, value in table.items(): - if key.endswith('.' + base): - return value - return None - - @staticmethod - def _ConstantToIrValue(raw: object, param_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Value | None: - """把全局默认参数表里的 Python 常量转成形参类型的 IR 常量。 - - 只处理整数/布尔(跨模块默认值里最常见,如 cap: t.CSizeT = 4096)。字符串等 - 常量需要在调用点引用定义模块的全局对象,无法安全重建 → 返回 None,调用点 - 退回原有零值填充(与修复前行为一致)。 - """ - if isinstance(param_type, ir.IntType): - if isinstance(raw, bool): - raw = 1 if raw else 0 - if isinstance(raw, int): - max_val: int = (1 << param_type.width) - 1 - if raw > max_val or raw < 0: - raw = raw & max_val - return ir.Constant(param_type, raw) - return ir.Constant(param_type, 0) - if isinstance(param_type, ir.PointerType): - return ir.Constant(param_type, None) - return None - - def _ApplyKeywordArgs(self, Node: ast.Call, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None, self_offset: int = 1) -> None: - """在 _fill_default_args 之后调用:用 keyword args 按 param_names 覆盖 CallArgs 对应位置。 - self_offset=1 表示 CallArgs[0] 是 self,参数从 CallArgs[1] 开始(__init__/__new__/__call__/method)。 - self_offset=0 表示无 self,参数从 CallArgs[0] 开始(普通函数)。 - """ - if not Node.keywords: - return - global_param_names: dict = self.Trans._global_function_param_names - param_names: list[str] | None = None - if func_name: - param_names = global_param_names.get(func_name) - if not param_names and func is not None: - param_names = global_param_names.get(func.name) - if not param_names and func is not None and hasattr(func, 'args'): - llvm_names: list[str] = [a.name for a in func.args] if func.args else [] - if llvm_names and not all(n.isdigit() for n in llvm_names): - param_names = llvm_names - if not param_names: - # 无法获取参数名,回退:按顺序追加(保持旧行为,避免静默丢弃) - for kw in Node.keywords: - if kw.arg is None: - continue - ArgVal = self.HandleExprLlvm(kw.value) - if ArgVal: - CallArgs.append(ArgVal) - return - for kw in Node.keywords: - if kw.arg is None: - continue - ArgVal = self.HandleExprLlvm(kw.value) - if not ArgVal: - continue - if kw.arg in param_names: - idx: int = param_names.index(kw.arg) - target_idx: int = idx - self_offset - if 0 <= target_idx < len(CallArgs) - self_offset: - CallArgs[self_offset + target_idx] = ArgVal - - def _CheckFuncPtrArity(self, Node: ast.Call, FnPtr: ir.Value, Args: list[ir.Value], - FuncName: str, Gen: LlvmGeneratorMixin) -> None: - """函数指针间接调用前的 arity 校验(fail-fast,紧邻 builder.call 之前)。 - - 期望形参个数取自 FnPtr 的静态 LLVM 函数类型的形参表:来源是函数指针槽 - (模块级/局部 t.Callable[...] 注解)的 CTypeInfo.FuncPtrParams → - CTypeInfo.ToLLVM/_ctype_to_llvm → ir.FunctionType(ret, params);变参位由 - FunctionType.var_arg 承载。不校验时,实参个数少于形参个数会让 llvmlite - CallInstr 按形参个数索引 args[i] 抛裸 `IndexError: list index out of range` - (无函数名、无源位置,栈顶落在被调函数 docstring 行,无法定位真因)。 - - 变参函数(var_arg=True,如 C printf 形态)只要求实参不少于固定形参个数: - 多出的实参按变参传递,不报错;仅 len(Args) < 固定形参个数 时报错。 - """ - FnType: ir.Type = FnPtr.type.pointee if isinstance(FnPtr.type, ir.PointerType) else FnPtr.type - if not isinstance(FnType, ir.FunctionType): - return - Expected: int = len(FnType.args) - Actual: int = len(Args) - IsVariadic: bool = bool(getattr(FnType, 'var_arg', False)) - if IsVariadic: - if Actual >= Expected: - return - elif Actual == Expected: - return - # 源位置:按本文件既有写法取 Node 自身的 lineno/col_offset(不用 - # _current_node_info/_current_lineno —— 二者可能停留在上一条语句), - # 源文件取 Gen._current_source_file,源码行取 Gen._get_source_line。 - Lineno: int = getattr(Node, 'lineno', 0) or 0 - # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, - # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator); - # 报文行号与源码行必须同用还原后的 Lineno,二者一致(否则会显示下一行源码)。 - _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _line_offset > 0 and Lineno > _line_offset: - Lineno -= _line_offset - Col: int = getattr(Node, 'col_offset', -1) - SrcFile: str = getattr(Gen, '_current_source_file', '') or 'unknown' - Loc: str = f"{SrcFile}:{Lineno}" - if Col >= 0: - Loc += f":{Col}" - SourceLine: str = '' - if Lineno > 0 and hasattr(Gen, '_get_source_line'): - SourceLine = (Gen._get_source_line(Lineno) or '').strip() - ExpectText: str = (f"至少 {Expected} 个形参(变参函数,仅固定形参)" - if IsVariadic else f" {Expected} 个形参") - raise Exception( - f"[TransPyC] 函数指针调用实参个数不匹配: '{FuncName}' 期望{ExpectText}," - f"实际传入 {Actual} 个实参;请检查该函数指针(t.Callable[...] 注解槽)的" - f"形参表是否与调用处实参个数一致。位置 {Loc}" - f"{(',源码: ' + SourceLine) if SourceLine else ''}" - ) - - def _RequireArgValue(self, ArgVal: ir.Value | None, Callee: str, ArgIndex: int, - ArgNode: ast.AST, CallNode: ast.Call) -> ir.Value: - """fail-fast:调用实参编译期求值为空(`HandleExprLlvm` 返回 None)时终止装配。 - - 调用装配处原写作 `ArgVal = self.HandleExprLlvm(arg); if ArgVal: 追加(ArgVal)`: - 实参求值为 None 时它被**静默整条丢弃**,导致后续实参整体前移一格,最终由 - `FuncGen._adjust_args` 报出误导性的"指针类型不能隐式转换为整数参数"(真因是 - "少了一个实参")。实例:`ast.OpKind.BitOr` 因枚举成员未登记而求值 None ⇒ - `ast.BinOp(...)` 的 4 个实参退化为 3 个 ⇒ `AST*` 落到 `op: i32`(见 - `Debugger/rawptr-migration-and-fastfail.md` §27、`Debugger/enum-semantics.md` - 的「逗号分组成员被静默丢弃」一节)。 - - `HandleExprLlvm` 返回 None 只有一种语义——该表达式编译期**求值失败** - (名称/属性链/枚举成员未登记、跨模块符号查找失败、类构造在声明期结构体不完整等); - "按类型本无值"的形态(以返回 void 的调用收尾)返回的是 CallInstr 对象而非 None, - 故不存在需要跳过的合法实参。此处改为当场终止,报文含被调者、实参序号与源表达式、 - 根因提示与源码位置;**不丢弃该实参**。 - """ - if ArgVal is not None: - return ArgVal - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - expr_text: str = self._ExprText(ArgNode) - ArgKind: str = type(ArgNode).__name__ - raw_lineno: int = getattr(ArgNode, 'lineno', 0) or getattr(CallNode, 'lineno', 0) or 0 - lineno: int = raw_lineno - # 行号还原:`_inject_auto_imports` 会在解析前往文件头注入 import 行,AST 行号 - # 比磁盘真实行号大 N;与 HandlesBase._RaiseUnresolvedExpr 同口径。 - _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - col: int = getattr(ArgNode, 'col_offset', -1) - src_file: str = getattr(Gen, '_current_source_file', '') or 'unknown' - code_line: str = '' - if lineno > 0 and hasattr(Gen, '_get_source_line'): - try: - code_line = (Gen._get_source_line(lineno) or '').strip() - except Exception: - code_line = '' - loc: str = f"{src_file}:{lineno}" - if col >= 0: - loc += f":{col}" - # 实参是属性读时补同一份基址事实(与 HandlesBase._RaiseUnresolvedExpr 共用 - # `_DescribeAttrBase`):属性读取值失败的两类根因(类名登记退化 / 键不同源) - # 必须一眼可见,不再从"求值失败"反推。 - _base_facts: str = '' - if isinstance(ArgNode, ast.Attribute): - try: - _base_facts = self._DescribeAttrBase(Gen, ArgNode) - except Exception: # noqa: BLE001 - _base_facts = '' - raise Exception( - f"[TransPyC] 调用实参编译期求值为空: 被调者 '{Callee}' 的第 {ArgIndex} 个实参 " - f"'{expr_text}'({ArgKind} 形态)求值失败;已终止编译,不丢弃该实参" - f"(丢弃会使后续实参前移一格,报出与真因无关的类型不匹配)。" - f"常见根因:枚举成员/名称未登记、跨模块符号查找失败、类定义在声明期结构体不完整。" - f"位置 {loc}" - f"{(',源码: ' + code_line) if code_line else ''}" - f"{chr(10) + '│ 基址诊断: ' + _base_facts if _base_facts else ''}" - ) - - def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - # 协程:coro.coro_run(帧) 内联翻译为 resume 循环 - if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_run': - if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): - return self.Trans.AsyncHandler._HandleCoroRunLlvm(Node, Gen) - # 协程:coro.coro_destroy(帧) 内联翻译为 free - if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_destroy': - if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): - return self.Trans.AsyncHandler._HandleCoroDestroyLlvm(Node, Gen) - # 协程:coro.coro_exception(帧) 内联翻译为帧 exc 槽读取 - if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_exception': - if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): - return self.Trans.AsyncHandler._HandleCoroExceptionLlvm(Node, Gen) - # 协程:coro.coro_exception_msg(帧) 内联翻译为帧 msg 槽读取 - if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_exception_msg': - if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): - return self.Trans.AsyncHandler._HandleCoroExceptionMsgLlvm(Node, Gen) - # 协程:coro.coro_gather(帧...) 内联翻译为并发推进循环 - if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_gather': - if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): - return self.Trans.AsyncHandler._HandleCoroGatherLlvm(Node, Gen) - FuncAttr: str | None = None - ModulePath: str | None = None - if isinstance(Node.func, ast.Name): - FuncName = Node.func.id - # __asm__(...) 语法糖:等价 c.Asm(...)(裸名调用,无需 import c) - if FuncName == '__asm__': - return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) - # __llvmir__(...) 语法糖:等价 c.LLVMIR(...) - if FuncName == '__llvmir__': - return self._HandleLLVMIRLlvm(Node) - # irinp/irout 等价 c.LInp/c.LOut(仅作模板占位符,独立调用返回 0) - if FuncName in ('irinp', 'irout'): - return ir.Constant(ir.IntType(32), 0) - # M1 协程:普通函数调用 async 函数 → create_()(返回帧指针) - if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(FuncName): - return self.Trans.AsyncHandler._HandleAsyncCreateCallLlvm(Node, FuncName) - if hasattr(self.Trans.FunctionHandler, '_generic_templates') and FuncName in self.Trans.FunctionHandler._generic_templates: - return self._HandleGenericCallLlvm(Node, FuncName) - result = self.Trans.ExprBuiltinHandle._HandleBuiltinCallLlvm(Node) - if result is not None: - return result - if Gen._has_function(FuncName): - return self._HandleClosureCallLlvm(Node, FuncName) - SymInfo = self.Trans.SymbolTable.lookup(FuncName) - if SymInfo: - # 名字冲突处理:类名可能与 CEnum 成员名冲突(如 ASTKind.Name 枚举成员与 Name 类同名) - # 若该名字同时是类(在 Gen.structs 中或存在 __before_init__),优先作为类构造函数处理 - IsAlsoClass: bool = (FuncName in Gen.structs) or Gen._has_function(f'{FuncName}.__before_init__') - if SymInfo.IsEnum and not (IsAlsoClass and getattr(SymInfo, 'IsEnumMember', False)): - # 枚举类型转换 `EnumName(v)`:结果宽度 = 该枚举的存储宽度 - # (最宽成员类型),与成员常量宽度同点(ABI 一致)。 - _enum_ty: ir.IntType = Gen._enum_int_type(FuncName, SymInfo) - if Node.args: - arg_val = self.HandleExprLlvm(Node.args[0]) - if arg_val: - if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != _enum_ty.width: - if arg_val.type.width < _enum_ty.width: - arg_val = Gen.builder.zext(arg_val, _enum_ty, name="zext_enum_arg") - else: - arg_val = Gen.builder.trunc(arg_val, _enum_ty, name="trunc_enum_arg") - return arg_val - return ir.Constant(_enum_ty, 0) - if SymInfo.IsTypedef: - return self._HandleTypedefCastLlvm(Node, FuncName) - NewFuncName = f'{FuncName}.__before_init__' - InitFuncName = f'{FuncName}.__init__' - if Gen._has_function(NewFuncName): - return self._HandleClassNewLlvm(Node, FuncName) - if Gen._has_function(InitFuncName) and FuncName in Gen.structs: - return self._HandleClassNewLlvm(Node, FuncName) - if FuncName in Gen.structs: - return self._HandleClassNewLlvm(Node, FuncName) - if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and FuncName in self.Trans.ClassHandler._generic_class_templates: - return self._HandleClassNewLlvm(Node, FuncName) - if self.Trans.FunctionDefCache.get(FuncName): - return self._HandleClosureCallLlvm(Node, FuncName) - if FuncName in self._C_LIB_FUNCS: - return self._HandleClosureCallLlvm(Node, FuncName) - ClassName = Gen.var_struct_class.get(FuncName) - if ClassName and Gen._has_function(f'{ClassName}.__call__'): - return self._HandleInstanceCallLlvm(Node, FuncName, ClassName) - t_c_imported = getattr(self.Trans, '_t_c_imported_names', {}) - if FuncName in t_c_imported: - src_module, src_name = t_c_imported[FuncName] - virtual_node = self._make_virtual_attr_call(Node, src_module, src_name) - return self._HandleCallLlvm(virtual_node) - # 检查是否是全局变量中的函数指针(t.Callable 类型在模块级定义) - if FuncName not in Gen.variables and FuncName in Gen.global_vars: - Gen._Load_var(FuncName) - if FuncName not in Gen.variables and FuncName in Gen.module.globals: - GVar = Gen.module.globals[FuncName] - if not isinstance(GVar, ir.Function): - StoredType = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else GVar.type - if isinstance(StoredType, ir.PointerType) and isinstance(StoredType.pointee, ir.FunctionType): - Gen.variables[FuncName] = GVar - # 检查是否是闭包变量(lambda 赋值给变量后调用) - if FuncName in Gen.variables and Gen.variables[FuncName] is not None: - VarPtr = Gen.variables[FuncName] - # 检查是否是函数指针变量(t.Callable 类型),直接加载并调用 - if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(VarPtr.type.pointee.pointee, ir.FunctionType): - fn_ptr = Gen._load(VarPtr, name=f"Load_fn_{FuncName}") - args = [] - for arg in Node.args: - ArgVal = self.HandleExprLlvm(arg) - if ArgVal: - args.append(ArgVal) - self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen) - return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}") - closure_ptr = Gen._load(VarPtr, name=f"Load_closure_{FuncName}") - if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.FunctionType): - fn_ptr = closure_ptr - args = [] - for arg in Node.args: - ArgVal = self.HandleExprLlvm(arg) - if ArgVal: - args.append(ArgVal) - self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen) - return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}") - # 处理 t.CPtr (i8*) 函数指针调用 - # 当函数被赋值给 t.CPtr 变量时,原始函数类型退化为 i8* - # 检查是否是闭包变量(通过 _closure_var_types 追踪) - if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.IntType) and closure_ptr.type.pointee.width == 8: - ClosureInfo: tuple[ir.Type, list[ir.Type]] | None = Gen._closure_var_types.get(FuncName) - if ClosureInfo is not None: - # 闭包结构体解包:{i8* env, i8* fn} -> 加载 env 和 fn,构造正确函数签名 - FnRetType: ir.Type = ClosureInfo[0] - NonlocalArgTypes: list[ir.Type] = [_safe_ptr_type(t) for t in ClosureInfo[1]] - I8PtrType: ir.PointerType = ir.IntType(8).as_pointer() - ClosureStructType: ir.LiteralStructType = ir.LiteralStructType([I8PtrType, I8PtrType]) - ClosureStructPtr: ir.Value = Gen.builder.bitcast(closure_ptr, ir.PointerType(ClosureStructType), name=f"cast_closure_{FuncName}") - # 加载 env(field 0)和 fn(field 1) - EnvGep: ir.Value = Gen.builder.gep(ClosureStructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"env_gep_{FuncName}") - EnvVal: ir.Value = Gen._load(EnvGep, name=f"env_{FuncName}") - FnGep: ir.Value = Gen.builder.gep(ClosureStructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 1)], name=f"fn_gep_{FuncName}") - FnVal: ir.Value = Gen._load(FnGep, name=f"fn_{FuncName}") - # 收集用户调用参数 - UserArgTypes: list[ir.Type] = [] - UserCallArgs: list[ir.Value] = [] - for arg in Node.args: - ArgVal = self.HandleExprLlvm(arg) - if ArgVal: - UserCallArgs.append(ArgVal) - UserArgTypes.append(ArgVal.type) - # 构造完整函数类型:[nonlocal_types..., user_arg_types...] - FullArgTypes: list[ir.Type] = list(NonlocalArgTypes) + UserArgTypes - FullFnType: ir.FunctionType = ir.FunctionType(FnRetType, FullArgTypes) - FullFnPtr: ir.Value = Gen.builder.bitcast(FnVal, ir.PointerType(FullFnType), name=f"cast_fn_{FuncName}") - # 调用参数:env 对应 nonlocal 参数,bitcast 到正确类型(需 _safe_ptr_type 兜底避免 void*) - CallArgs: list[ir.Value] = [] - if NonlocalArgTypes: - CallArgs.append(Gen.builder.bitcast(EnvVal, _safe_ptr_type(NonlocalArgTypes[0]), name=f"cast_env_{FuncName}")) - CallArgs.extend(UserCallArgs) - return Gen.builder.call(FullFnPtr, CallArgs, name=f"call_{FuncName}") - # 普通函数指针调用(无闭包):从调用参数构造函数类型,默认返回 i32 - cptr_arg_types: list[ir.Type] = [] - cptr_call_args: list[ir.Value] = [] - for arg in Node.args: - ArgVal = self.HandleExprLlvm(arg) - if ArgVal: - cptr_call_args.append(ArgVal) - cptr_arg_types.append(ArgVal.type) - cptr_fn_type = ir.FunctionType(ir.IntType(32), cptr_arg_types) - cptr_fn_ptr = Gen.builder.bitcast(closure_ptr, ir.PointerType(cptr_fn_type), name=f"cast_fn_{FuncName}") - return Gen.builder.call(cptr_fn_ptr, cptr_call_args, name=f"call_{FuncName}") - if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.LiteralStructType): - st = closure_ptr.type.pointee - if len(st.elements) == 2 and isinstance(st.elements[1], ir.PointerType) and isinstance(st.elements[1].pointee, ir.FunctionType): - fn_ptr_type = st.elements[1] - fn_ptr = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 1)], name=f"fn_ptr_{FuncName}") - fn_val = Gen._load(fn_ptr, name=f"fn_{FuncName}") - args = [] - for arg in Node.args: - ArgVal = self.HandleExprLlvm(arg) - if ArgVal: - args.append(ArgVal) - env_ptr = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"env_ptr_{FuncName}") - env_val = Gen._load(env_ptr, name=f"env_{FuncName}") - call_args = [env_val] + args - expected_fn_type = fn_ptr_type.pointee - if len(call_args) < len(expected_fn_type.args): - while len(call_args) < len(expected_fn_type.args): - call_args.append(ir.Constant(expected_fn_type.args[len(call_args)], 0)) - # 如果函数期望的 env 类型与闭包中存储的 i8* 不同,做 bitcast(避免 void*) - if len(expected_fn_type.args) > 0 and call_args[0].type != expected_fn_type.args[0]: - safe_env_type: ir.Type = _safe_ptr_type(expected_fn_type.args[0]) - call_args[0] = Gen.builder.bitcast(call_args[0], safe_env_type, name=f"cast_env_{FuncName}") - return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}") - # 编译器内置裸指针强转 rawptr(x):rawptr 是 TPC/TPV 双端**语法保留字** - # (无 SymbolTable typedef 条目、无泛型模板;builtins.vp 已不再声明它, - # 见 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME 与 TPV 同口径内置分支 - # TypeGenResolve.vp:921)。落到下方 typedef 表判据必然 miss ⇒ 误报 - # "Undefined function: 'rawptr'"(TransPyV/App/lib/core/Gen/ - # StructGenMixin.vp `vt_ptr: rawptr = rawptr(*(uint64*)(default_val))` 根因)。 - # 单独 rawptr 等价 t.CPtr(void* → i8*):目标类型取内置 rawptr 的权威 - # CTypeInfo(_builtin_rawptr_info,与注解侧同源),转换复用 (rawptr[X])(x) - # / (t.CPtr)(x) 的 _emit_rawptr_cast——不新造第二套 rawptr 表示。 - # 不照抄 uchar 的做法(往 builtins.vp 加 `uchar: t.CTypedef = ...`): - # rawptr 不是普通 typedef 名而是内置语法,TPV register_ctypedef 显式拒绝 - # 重定义(TypeGenCType.vp:157-160)、TPC builtins_typedef_value 亦硬编码 - # 返回内置值(TypeAnnotationResolver.py:313-314)⇒ 该声明不生效。 - # 本分支置于下方 try 之外:解析/转换异常必须外抛,不得被吞成 - # "Undefined function: 'rawptr'" 的误导报文。 - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_RP - if FuncName == _TAR_RP._BUILTIN_ROWPTR_NAME: - _rawptr_target: ir.Type = Gen._ctype_to_llvm(_TAR_RP._builtin_rawptr_info()) - return self._emit_rawptr_cast(Node, Gen, _rawptr_target) - # builtins 隐式 CTypedef 作为类型转换调用(uint64(x)/int64(x)/size_t(x) 等, - # builtins.vp 声明、无需 import,不注册进 SymbolTable):常规 miss 后在此 - # 尝试 typedef cast(uint64(buf) = t.CUInt64T(buf)),否则报 - # "Undefined function: 'uint64'"(includes/ast/base.vp bootstrap 根因)。 - _td_diag: str = '' - try: - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_TD - _td_table: dict = _TAR_TD._scan_builtins()['typedefs'] - if FuncName not in _td_table: - # 典型成因:includes/builtins.vp 未定位到(PROJECT_INCLUDE_DIRS 尚未 - # 装配)、或声明写法未被文本扫描识别 ⇒ 整表为空/缺项。 - _td_diag = (f"(builtins typedef 表 {len(_td_table)} 项,无 '{FuncName}';" - f"includes 目录={_TAR_TD._builtins_include_dirs()})") - if FuncName in _td_table: - # from_type_name 对裸 typedef 名(uint64 等)解析不出真实类型类 - # (BuiltinTypeMap 无此键,CreateFromTypeName 落空成空 CType), - # 因此改走权威 typedef 值:'t.CUInt64T' → CTypeRegistry.NameToLLVM - # → 'i64'(与 DeclarationGenerator 的 stub 兜底同机制)。 - _bv_td: str | None = _TAR_TD.builtins_typedef_value(FuncName) - _lls_td: str | None = None - if _bv_td: - _nm_td: str = _bv_td[2:] if _bv_td.startswith('t.') else _bv_td - from lib.includes.t import CTypeRegistry as _CR_TD - _lls_td = _CR_TD.NameToLLVM(_nm_td) - if not _lls_td: - # 声明值本身也是短别名(新语法:`typedef short = int16`、 - # `typedef longlong = int64`)⇒ 单步 NameToLLVM(不认短别名) - # 落空。改走权威链式解析(builtins.vp 声明 → 逐级展开到 t.C*), - # 与注解位置 LLVMTypeMapper._resolve_name 同一来源。 - _td_info = _TAR_TD._lookup_builtins_typedef(FuncName, self.Trans.SymbolTable) - if _td_info is not None and _td_info.BaseType: - _tg_td2: ir.Type = Gen._ctype_to_llvm(_td_info) - if isinstance(_tg_td2, (ir.IntType, ir.FloatType, ir.DoubleType, ir.PointerType)): - if len(Node.args) >= 2: - _p2b: ast.AST = Node.args[1] - _p2b_cptr: bool = ( - (isinstance(_p2b, ast.Attribute) and _p2b.attr == 'CPtr' - and isinstance(_p2b.value, ast.Name) and _p2b.value.id in ('t', 'c')) or - (isinstance(_p2b, ast.Name) and _p2b.id == 'CPtr') - ) - if _p2b_cptr: - if isinstance(_tg_td2, ir.PointerType): - return self._emit_rawptr_cast(Node, Gen, _tg_td2) - return self._emit_rawptr_cast(Node, Gen, ir.PointerType(_tg_td2)) - return self._emit_rawptr_cast(Node, Gen, _tg_td2) - if _lls_td: - _tg_td: ir.Type = Gen._ctype_to_llvm(_lls_td) - if isinstance(_tg_td, (ir.IntType, ir.FloatType, ir.DoubleType, ir.PointerType)): - # 双参数形式 uint8(s, t.CPtr) / int64(v, CPtr):第二参是 - # CPtr 标记 → 指针包装(等价旧写法 t.CUInt8T(s, t.CPtr), - # 对齐 Attribute 形式 t.CXXX(v, t.CPtr) 的 IsPtr_cast 语义)。 - # 单参形式 uint64(buf) 才是标量转换。忽略第二参会把 - # hashlib/__sha1.vp 的 data 编译成标量 → 运行时 sha1 - # transform 读越界崩溃。 - if len(Node.args) >= 2: - _p2_td: ast.AST = Node.args[1] - _p2_cptr: bool = ( - (isinstance(_p2_td, ast.Attribute) and _p2_td.attr == 'CPtr' - and isinstance(_p2_td.value, ast.Name) and _p2_td.value.id in ('t', 'c')) or - (isinstance(_p2_td, ast.Name) and _p2_td.id == 'CPtr') - ) - if _p2_cptr: - if isinstance(_tg_td, ir.PointerType): - return self._emit_rawptr_cast(Node, Gen, _tg_td) - _tg_ptr_td: ir.Type = ir.PointerType(_tg_td) - return self._emit_rawptr_cast(Node, Gen, _tg_ptr_td) - return self._emit_rawptr_cast(Node, Gen, _tg_td) - except Exception as _e_td: - # 不再静默:兜底链任何一步抛错都要带出来(此前 pass 让真正的根因 - # 只剩一句误导性的 "Undefined function")。 - _td_diag = _td_diag + f"(typedef cast 求值异常: {type(_e_td).__name__}: {_e_td})" - raise Exception(f"Undefined function: '{FuncName}'{_td_diag}") - if isinstance(Node.func, ast.Subscript): - # Handle list[int](mb) syntax — generic class constructor with explicit type args - # Handle xxx[T](x) syntax — generic function call with explicit type args - sub: ast.Subscript = Node.func - # (rawptr[X])(expr) / (t.CPtr[X])(expr):内建裸指针转换(无泛型模板)。 - # 不拦截 → 泛型模板查不到 → 落 L850 "Unsupported call expression" - # (StubMergerCollect.vp L271 bootstrap 根因)。 - _sub_nm: str | None = None - if isinstance(sub.value, ast.Name): - _sub_nm = sub.value.id - elif isinstance(sub.value, ast.Attribute) and isinstance(sub.value.value, ast.Name) and sub.value.value.id == 't' and sub.value.attr == 'CPtr': - _sub_nm = 't.CPtr' - if _sub_nm in ('rawptr', 't.CPtr'): - return self._HandleSubscriptPtrCastLlvm(Node, sub) - if isinstance(sub.value, ast.Name): - FuncName: str = sub.value.id - # 优先检查泛型类模板 - if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and FuncName in self.Trans.ClassHandler._generic_class_templates: - return self._HandleGenericClassNewLlvm(Node, FuncName, explicit_slice=sub.slice) - # 检查泛型函数模板(如 xxx[T](x)) - if hasattr(self.Trans.FunctionHandler, '_generic_templates') and FuncName in self.Trans.FunctionHandler._generic_templates: - return self._HandleGenericCallExplicitLlvm(Node, FuncName, explicit_slice=sub.slice) - if isinstance(Node.func, ast.Attribute): - if Node.func.attr in ('update', 'final', 'transform'): - pass - if Node.func.attr == '__sizeof__': - if isinstance(Node.func.value, ast.Subscript): - spec_name = self._ResolveGenericSlice(Node.func.value) - if spec_name: - return self._HandleSizeofLlvm(ast.Name(id=spec_name, ctx=ast.Load())) - # 对于 BinOp/BitOr 联合类型(如 AST | t.CPtr), - # sizeof 应返回指针大小 8,而非结构体大小 - # 必须在 HandleExprLlvm 之前检查,避免对类型名求值产生副作用 - if isinstance(Node.func.value, ast.BinOp) and isinstance(Node.func.value.op, ast.BitOr): - if self._AnnotationContainsCPtr(Node.func.value): - return ir.Constant(ir.IntType(64), 8) - # 对于 ast.Attribute(如 ast.ClassDef.__sizeof__() / t.CInt.__sizeof__()), - # 直接调用 _HandleSizeofLlvm,不走 HandleExprLlvm - # 因为 ast.Attribute 在 sizeof 上下文中一定是类型名,不是变量 - if isinstance(Node.func.value, ast.Attribute): - type_name = Node.func.value.attr - SizeofNode = ast.Name(id=type_name, ctx=ast.Load()) - _r = self._HandleSizeofLlvm(SizeofNode) - return _r - # 对于 ast.Name,需要区分变量和类型名: - # - 如果是已知变量(Gen.variables/_reg_values/_direct_values/self), - # 走 HandleExprLlvm 路径(如 myvar.__sizeof__()) - # - 否则视为类型名,直接调用 _HandleSizeofLlvm - # 这避免了 HandleExprLlvm 对类型名求值产生副作用 - # (如修改 builder 状态、污染 variables 缓存等), - # 这些副作用会导致泛型特化后 then 块为空 - if isinstance(Node.func.value, ast.Name): - nm: str = Node.func.value.id - is_variable: bool = ( - nm == 'self' - or nm in Gen.variables - or nm in getattr(Gen, '_reg_values', {}) - or nm in getattr(Gen, '_direct_values', {}) - ) - if not is_variable: - SizeofNode = ast.Name(id=nm, ctx=ast.Load()) - _r = self._HandleSizeofLlvm(SizeofNode) - return _r - # 变量/实例的 sizeof: 通过 HandleExprLlvm 获取值 - ObjVal = self.HandleExprLlvm(Node.func.value) - if ObjVal and isinstance(ObjVal.type, ir.PointerType): - pointee = ObjVal.type.pointee - if isinstance(pointee, ir.ArrayType): - # 元素尺寸统一走 Gen._get_struct_size(含字段对齐/填充,与 __sizeof__ / - # memhub alloc 同源):_get_struct_size(ArrayType) = 元素尺寸 × 元素数。 - # 原实现手写「字段宽度直接相加 + 未知类型兜底 8」,会漏算 struct 的填充 - # ({i8,i64} 算成 9 而非 16),sizeof 偏小 → 调用方按过小尺寸 alloc → - # 越界写坏邻居块头(与 1 GiB 主池漏满同一根因链)。 - return ir.Constant(ir.IntType(64), Gen._get_struct_size(pointee)) - elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - # 结构体实例: 直接计算大小 - self._resolve_opaque_struct_members(pointee, Gen) - size = Gen._get_struct_size(pointee) - if size > 0: - return ir.Constant(ir.IntType(64), size) - # 回退: 尝试作为类型名处理 - if isinstance(Node.func.value, ast.Name): - type_name = Node.func.value.id - SizeofNode = ast.Name(id=type_name, ctx=ast.Load()) - return self._HandleSizeofLlvm(SizeofNode) - ModulePath = self._get_ModulePath(Node.func.value) - if ModulePath: - first_part = ModulePath.split('.')[0] - resolved_first = self.Trans.SymbolTable.resolve_alias(first_part) - if resolved_first != first_part: - ModulePath = resolved_first + ModulePath[len(first_part):] - # 统一获取模块 SHA1,用于跨模块类型引用(确保用源模块的 SHA1 而非当前模块) - # 放在 ModulePath 和 alias 解析之后,供所有 _HandleClassNewLlvm/_ensure_struct_declared 调用点使用 - _ctor_module_sha1: str | None = None - if ModulePath and ModulePath not in {'t', 'c'}: - _ctor_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) - _ctor_module_sha1 = _ctor_sha1_map.get(ModulePath) - if not _ctor_module_sha1 and '.' in ModulePath: - _ctor_module_sha1 = _ctor_sha1_map.get(ModulePath.split('.')[-1]) - if ModulePath: - is_instance_var = isinstance(Node.func.value, ast.Name) and ( - Node.func.value.id in Gen.var_struct_class - or Node.func.value.id in Gen.variables - or Node.func.value.id in Gen.global_vars - ) and ( - Node.func.value.id not in Gen.ModuleSha1Map - or Node.func.value.id in Gen.var_struct_class - or Node.func.value.id in Gen.variables - or Node.func.value.id in Gen.global_vars - ) - is_class_name = isinstance(Node.func.value, ast.Name) and (Node.func.value.id in Gen.structs or self.Trans.SymbolTable.is_struct(Node.func.value.id)) - # 链式方法调用:如 self.__mbuddy__.alloc() 或 trans.ImportsH.HandleImport(child) - # Node.func.value 是 ast.Attribute,基础是实例变量(如 self/trans) - _is_chained_instance: bool = False - if isinstance(Node.func.value, ast.Attribute): - _base_node: ast.AST = Node.func.value - while isinstance(_base_node, ast.Attribute): - _base_node = _base_node.value - if isinstance(_base_node, ast.Name): - _base_name: str = _base_node.id - # 检查基名是否为已知变量(self、局部变量、全局变量、已知 struct 变量) - # 且不在 ModuleSha1Map 中(避免把模块名误判为变量) - if _base_name == 'self' or ( - (_base_name in Gen.var_struct_class - or _base_name in Gen.variables - or _base_name in Gen.global_vars) - and (_base_name not in Gen.ModuleSha1Map - or _base_name in Gen.var_struct_class - or _base_name in Gen.variables - or _base_name in Gen.global_vars) - ): - _is_chained_instance = True - elif self._IsModuleQualifiedGlobalChain(_base_name, Node.func.value): - # 模块限定全局变量链:`Module.Global.field.method(...)` - # (如 GenApi._g_gen.ClassGenBase._collect_parents(...))。 - # 链根是模块别名、次段是该模块的全局变量 ⇒ 接收者是对象, - # 与 self/局部变量/全局变量同一条"实例链"判据(详见 - # _IsModuleQualifiedGlobalChain 的说明)。 - _is_chained_instance = True - if (is_instance_var or _is_chained_instance) and not is_class_name: - return self._HandleMethodCallLlvm(Node) - FuncAttr = Node.func.attr - # c 模块的特殊语法设施(Addr/Deref/DerefAs/Set/Load/Asm 等)必须优先处理, - # 即使它们在 Gen.structs 中(c.py 的类会被声明为结构体但不应被实例化)。 - # c.py 是语法部分,不应被编译,其中的类是编译器语法设施。 - # 必须在 Gen.structs 检查之前处理,否则 c.Deref/c.DerefAs 会被 - # _HandleClassNewLlvm 当成普通类实例化(根因:HandlesExprCall.py:497)。 - if ModulePath == 'c': - if FuncAttr == 'Asm': - return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) - elif FuncAttr == 'Addr': - return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node) - elif FuncAttr == 'Set': - return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node) - elif FuncAttr == 'Load': - return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node) - elif FuncAttr == 'Deref': - return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) - elif FuncAttr == 'DerefAs': - return self._HandleCDerefAsLlvm(Node) - elif FuncAttr == 'PtrToInt': - return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) - elif FuncAttr in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'): - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError(_REMOVED_CIF_CALL_MSG) - elif FuncAttr == 'CError': - msg = "compile-time error" - if Node.args: - arg = Node.args[0] - if isinstance(arg, ast.Constant) and isinstance(arg.value, str): - msg = arg.value - lineno = getattr(Node, 'lineno', 0) - # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 - _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - raise Exception(f"#error: {msg} (line {lineno})") - elif FuncAttr in ('AsmInp', 'AsmOut'): - return ir.Constant(ir.IntType(32), 0) - elif FuncAttr == 'LLVMIR': - return self._HandleLLVMIRLlvm(Node) - elif FuncAttr in ('LInp', 'LOut'): - return ir.Constant(ir.IntType(32), 0) - # stdarg 模块的变参糖:stdarg.arg / stdarg.va_arg / stdarg.va_start / - # stdarg.va_end 命中与裸名同名内建**完全相同**的处理(stdarg 是编译器 - # 拦截的糖模块,不是真符号,与 c 模块的语法设施同一口径)。仅当 - # ModulePath 恰为 'stdarg' 时进入,不会抢掉其它模块/用户模块的调用。 - if ModulePath == 'stdarg': - if FuncAttr == 'arg': - return self.Trans.ExprBuiltinHandle._HandleArgLlvm(Node.args) - elif FuncAttr == 'va_arg': - return self.Trans.ExprBuiltinHandle._HandleVaArgLlvm(Node.args) - elif FuncAttr == 'va_start': - return self.Trans.ExprBuiltinHandle._HandleVaStartLlvm(Node.args) - elif FuncAttr == 'va_end': - return self.Trans.ExprBuiltinHandle._HandleVaEndLlvm(Node.args) - if FuncAttr in Gen.structs: - result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) - if result is not None: - return result - if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and FuncAttr in self.Trans.ClassHandler._generic_class_templates: - return self._HandleGenericClassNewLlvm(Node, FuncAttr) - # 跨模块 REnum 变体构造:ClassName.Variant(args) - # 直接检查 Node.func.value 是否是 REnum 类名(不依赖变体短名注册) - if isinstance(Node.func.value, ast.Name): - RenumClassName: str = Node.func.value.id - if self.Trans.SymbolTable.has(RenumClassName): - RenumInfo = self.Trans.SymbolTable[RenumClassName] - if RenumInfo.IsRenum and hasattr(RenumInfo, 'RenumVariants') and RenumInfo.RenumVariants: - if FuncAttr in RenumInfo.RenumVariants: - TagValue: int = RenumInfo.RenumVariants.index(FuncAttr) - result = self._HandleREnumConstructLlvm(Node, RenumClassName, FuncAttr, TagValue) - if result is not None: - return result - # 跨模块 REnum 变体构造(**点号类名**写法):`mod.ClassName.Variant(args)` - # 现场:`llvmlite.LLVMType.Struct(fields, fcount, name)` —— Node.func.value - # 是 Attribute(llvmlite.LLVMType)而不是 Name,上一条判据(L903 的 - # isinstance(Node.func.value, ast.Name))漏掉 ⇒ 落到"模块函数 Struct"路径: - # 返回类型被当成普通函数返回值(实为 boxed 变体构造指针),且实参少一个 - # pool(arity 也不对)。此处与裸名写法同口径补齐,覆盖点号写法。 - if isinstance(Node.func.value, ast.Attribute): - _rc_attr: str = getattr(Node.func.value, 'attr', '') or '' - _rc_sym: Any = None - for _rc_key in (f"{ModulePath}.{_rc_attr}", _rc_attr): - if _rc_key and self.Trans.SymbolTable.has(_rc_key): - _rc_sym = self.Trans.SymbolTable[_rc_key] - break - if _rc_sym is not None and getattr(_rc_sym, 'IsRenum', False): - _rc_vars: Any = getattr(_rc_sym, 'RenumVariants', None) - if _rc_vars and FuncAttr in _rc_vars: - _rc_tag: int = _rc_vars.index(FuncAttr) - result = self._HandleREnumConstructLlvm(Node, _rc_attr, FuncAttr, _rc_tag) - if result is not None: - return result - if self.Trans.SymbolTable.has(FuncAttr): - SymInfo = self.Trans.SymbolTable[FuncAttr] - if SymInfo.IsStruct: - if FuncAttr not in Gen.structs: - self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) - if FuncAttr in Gen.structs: - result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) - if result is not None: - return result - if SymInfo.IsEnumMember and SymInfo.EnumName: - # When called as module.func(args), check if module.func is a - # function/variable first. If so, skip REnum construction and - # let the function call path handle it. This fixes cases where - # a factory function shares a name with an REnum variant - # (e.g., llvmlite.Ptr vs LLVMType.Ptr). - _SkipRenum: bool = False - if ModulePath: - _FullKey = f"{ModulePath}.{FuncAttr}" - _FullSym = self.Trans.SymbolTable.lookup(_FullKey) - if _FullSym and (_FullSym.IsFunction or _FullSym.IsVariable): - _SkipRenum = True - if not _SkipRenum: - EnumName = SymInfo.EnumName - if self.Trans.SymbolTable.has(EnumName): - EnumInfo = self.Trans.SymbolTable[EnumName] - if EnumInfo.IsRenum: - result = self._HandleREnumConstructLlvm(Node, EnumName, FuncAttr, SymInfo.value) - if result is not None: - return result - FullAttrKey = f"{ModulePath}.{FuncAttr}" - if self.Trans.SymbolTable.has(FullAttrKey): - SymInfo = self.Trans.SymbolTable[FullAttrKey] - if SymInfo.IsStruct: - if FuncAttr not in Gen.structs: - self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) - if FuncAttr in Gen.structs: - result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) - if result is not None: - return result - # 跨模块 REnum 变体构造:FullName 查找(如 LLVMType.Int) - if SymInfo.IsEnumMember and SymInfo.EnumName: - EnumName = SymInfo.EnumName - if self.Trans.SymbolTable.has(EnumName): - EnumInfo = self.Trans.SymbolTable[EnumName] - if EnumInfo.IsRenum: - result = self._HandleREnumConstructLlvm(Node, EnumName, FuncAttr, SymInfo.value) - if result is not None: - return result - # 模块名与类名同名优先规则:`import lib.core.X as X` 且模块内 - # 有同名 class X 时,`X.func()` 应解析为模块函数(mangle - # {sha1}.func,对应模块级 define)而非类静态方法(mangle - # {sha1}.X.func——模块函数没有带类名的 define,链接报 undefined - # symbol,如 CompilerFactory.get_perm_pool)。判断依据:SymbolTable - # 中 base 名是否注册了 IsModuleAlias(import 处理插入的别名标记)。 - # 类构造调用(X.X(args))不受影响:FuncAttr==类名 时上方 591/639 - # 行的 struct 路径已先行返回。 - _is_mod_alias_base = False - if isinstance(Node.func.value, ast.Name): - _base_alias_sym = self.Trans.SymbolTable.lookup(Node.func.value.id) - if _base_alias_sym is not None and getattr(_base_alias_sym, 'IsModuleAlias', False): - _is_mod_alias_base = True - if is_class_name and not _is_mod_alias_base: - ClassName = Node.func.value.id - result = self._HandleStaticMethodCallLlvm(Node, ClassName, FuncAttr) - if result is not None: - return result - FullAttrKey = f"{ModulePath}.{FuncAttr}" - if self.Trans.SymbolTable.has(FullAttrKey): - SymInfo = self.Trans.SymbolTable[FullAttrKey] - if SymInfo.IsTypedef: - return ir.Constant(ir.IntType(32), 0) - if SymInfo.IsStruct: - if FuncAttr not in Gen.structs: - self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) - if FuncAttr in Gen.structs: - result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) - if result is not None: - return result - if FuncAttr in Gen.structs: - result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) - if result is not None: - return result - if ModulePath == 't': - MethodResult_t = self._HandleMethodCallLlvm(Node) - if MethodResult_t is not None: - return MethodResult_t - raise Exception(f"Undefined method: 't.{FuncAttr}'") - if isinstance(Node.func.value, ast.Attribute): - inner_attr = Node.func.value.attr - if inner_attr in Gen.structs or self.Trans.SymbolTable.is_struct(inner_attr): - static_result = self._HandleStaticMethodCallLlvm(Node, inner_attr, FuncAttr) - if static_result is not None: - return static_result - MethodResult_attr = self._HandleMethodCallLlvm(Node) - is_zero_attr = isinstance(MethodResult_attr, ir.Constant) and isinstance(MethodResult_attr.type, ir.IntType) and MethodResult_attr.constant == 0 - if MethodResult_attr is not None and not is_zero_attr: - return MethodResult_attr - result = self._HandleExternalCallLlvm(Node, FuncAttr, ModulePath) - if result is not None: - return result - MethodResult = self._HandleMethodCallLlvm(Node) - IsZeroResult = isinstance(MethodResult, ir.Constant) and isinstance(MethodResult.type, ir.IntType) and MethodResult.constant == 0 - if IsZeroResult and FuncAttr is not None: - mangled_name = Gen._mangle_func_name(FuncAttr, ModulePath if ModulePath and ModulePath != 't' else None) - if Gen._has_function(mangled_name): - CallArgs = [] - for ArgIdx, arg in enumerate(Node.args, start=1): - # 禁止静默丢弃求值为空的实参(会导致后续整体前移一格, - # 报出与真因无关的类型不匹配,见 _RequireArgValue 说明) - CallArgs.append(self._RequireArgValue( - self.HandleExprLlvm(arg), FuncAttr, ArgIdx, arg, Node)) - return Gen.builder.call(Gen._get_function(mangled_name), CallArgs, name=f"call_{FuncAttr}") - if MethodResult is None or IsZeroResult: - imported = getattr(self.Trans, '_ImportedModules', set()) - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - IsKnown = (ModulePath in imported or ModulePath in self.Trans.SymbolTable.import_aliases or - ModulePath in ModuleSha1Map) - if not IsKnown and ModulePath and '.' in ModulePath: - LastPart = ModulePath.split('.')[-1] - IsKnown = (LastPart in imported or LastPart in self.Trans.SymbolTable.import_aliases or - LastPart in ModuleSha1Map) - # 最后尝试: 用 FuncAttr 直接查找 Gen.functions(_find_function 会遍历 SHA1 前缀) - if FuncAttr and ModulePath and ModulePath not in {'t', 'c'}: - # _ctor_module_sha1 已在函数顶部统一获取,此处无需重复 - # 先检查 FuncAttr 是否是函数/变量,如果是则跳过 struct 声明 - _attr_sym = self.Trans.SymbolTable.lookup(FuncAttr) - _is_func_or_var = _attr_sym and (_attr_sym.IsFunction or _attr_sym.IsVariable) - if not _is_func_or_var and ModulePath: - _full_key = f"{ModulePath}.{FuncAttr}" - _full_sym = self.Trans.SymbolTable.lookup(_full_key) - if _full_sym and (_full_sym.IsFunction or _full_sym.IsVariable): - _is_func_or_var = True - # 优先检查 FuncAttr 是否为 struct/类构造器 - if not _is_func_or_var and FuncAttr not in Gen.structs: - self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) - if not _is_func_or_var and FuncAttr in Gen.structs: - result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) - if result is not None: - return result - # 检查 FullAttrKey 是否为 struct(如 hashlib.md5) - if ModulePath: - FullAttrKey = f"{ModulePath}.{FuncAttr}" - if self.Trans.SymbolTable.has(FullAttrKey): - FullSymInfo = self.Trans.SymbolTable[FullAttrKey] - if FullSymInfo.IsStruct or FullSymInfo.IsCpythonObject: - if FuncAttr not in Gen.structs: - self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) - if FuncAttr in Gen.structs: - result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) - if result is not None: - return result - fallback_func = Gen._find_function(FuncAttr) - if fallback_func is not None: - CallArgs = [] - for ArgIdx, arg in enumerate(Node.args, start=1): - # 同 1039:禁止静默丢弃(会导致后续前移一格) - CallArgs.append(self._RequireArgValue( - self.HandleExprLlvm(arg), FuncAttr, ArgIdx, arg, Node)) - self._append_eh_msg_out_arg(CallArgs, fallback_func, Gen) - result = Gen.builder.call(fallback_func, CallArgs, name=f"call_{FuncAttr}") - return self._check_eh_return(result, FuncAttr, Gen, called_func=fallback_func) - # 尝试从 stub 按需加载函数声明 - if ModulePath in ModuleSha1Map: - sha1 = ModuleSha1Map[ModulePath] - stub_mangled = f"{sha1}.{FuncAttr}" - try: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(stub_mangled, Gen) - except Exception: - stub_func_type = None - if not stub_func_type: - try: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(FuncAttr, Gen) - except Exception: - stub_func_type = None - if stub_func_type: - decl_name = stub_mangled if stub_mangled in getattr(self.Trans.ImportHandler, '_stub_func_cache', {}) else FuncAttr - func_decl = self.GetOrCreateStubFuncDecl(decl_name, stub_func_type, Gen, alias=FuncAttr) - CallArgs = [] - for ArgIdx, arg in enumerate(Node.args, start=1): - # 同 1039:禁止静默丢弃(会导致后续前移一格) - CallArgs.append(self._RequireArgValue( - self.HandleExprLlvm(arg), FuncAttr, ArgIdx, arg, Node)) - self._append_eh_msg_out_arg(CallArgs, func_decl, Gen) - result = Gen.builder.call(func_decl, CallArgs, name=f"call_{FuncAttr}") - return self._check_eh_return(result, FuncAttr, Gen, called_func=func_decl) - if ModulePath and IsKnown: - raise Exception(f"Undefined symbol: '{ModulePath}.{FuncAttr}'") - if ModulePath and ModulePath not in {'t', 'c'} and not IsKnown: - raise Exception(f"Unknown module: '{ModulePath}' (undefined symbol: '{ModulePath}.{FuncAttr}')") - if not ModulePath and FuncAttr: - raise Exception(f"Undefined method: '{FuncAttr}'") - return MethodResult if MethodResult is not None else ir.Constant(ir.IntType(32), 0) - if isinstance(Node.func, ast.BinOp) and isinstance(Node.func.op, ast.BitOr): - return self._HandleTypeUnionCastLlvm(Node) - raise Exception(f"Unsupported call expression") - - def _ensure_struct_declared(self, class_name: str, module_sha1: str | None = None) -> None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - # 如果名称是函数或变量,不应创建 struct - SymInfo = self.Trans.SymbolTable.lookup(class_name) - if SymInfo and (SymInfo.IsFunction or SymInfo.IsVariable): - return - # 跨模块类型引用:如果提供了 module_sha1,优先检查带 SHA1 前缀的完整名 - full_key: str | None = f"{module_sha1}.{class_name}" if module_sha1 else None - if full_key and full_key in Gen.structs: - existing = Gen.structs[full_key] - if isinstance(existing, ir.IdentifiedStructType) and (existing.elements is None or len(existing.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) - return - if class_name in Gen.structs: - existing = Gen.structs[class_name] - if isinstance(existing, ir.IdentifiedStructType) and (existing.elements is None or len(existing.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) - existing = Gen.structs.get(class_name, existing) - if isinstance(existing, ir.IdentifiedStructType) and existing.elements is not None and len(existing.elements) > 0: - return - else: - # 短名已存在但 SHA1 可能不匹配:如果 module_sha1 与现有 struct 的 SHA1 不一致, - # 需要用正确的 SHA1 创建独立 struct - if module_sha1: - _existing_sha1: str = Gen._extract_struct_sha1(existing) - if _existing_sha1 and _existing_sha1 != module_sha1: - # SHA1 不匹配,用正确的 SHA1 创建 - correct_st = Gen._get_or_create_struct(class_name, source_sha1=module_sha1) - self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) - return - SymInfo = self.Trans.SymbolTable.lookup(class_name) - if SymInfo and SymInfo.Members: - member_types = [] - if isinstance(SymInfo.Members, dict): - for member_name, member_info in SymInfo.Members.items(): - if isinstance(member_info, CTypeInfo): - mt = member_info.ToLLVM(Gen) - elif isinstance(member_info, str): - mt = Gen._CType2LLVM(member_info, '*' in member_info) - else: - mt = Gen._CType2LLVM(str(member_info) if member_info else 'int', False) - if isinstance(mt, ir.VoidType): - mt = ir.IntType(8) - member_types.append(mt) - else: - for item in SymInfo.Members: - if isinstance(item, (list, tuple)) and len(item) == 2: - member_name, member_type_str = item - mt = Gen._CType2LLVM(member_type_str, '*' in member_type_str) - if isinstance(mt, ir.VoidType): - mt = ir.IntType(8) - member_types.append(mt) - if member_types: - st = Gen._get_or_create_struct(class_name, source_sha1=module_sha1) - if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): - st.set_body(*member_types) - else: - Gen._get_or_create_struct(class_name, source_sha1=module_sha1) - else: - if class_name not in Gen.structs and not (full_key and full_key in Gen.structs): - self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) - Gen._get_or_create_struct(class_name, source_sha1=module_sha1) - - def _HandleStaticMethodCallLlvm(self, Node: ast.Call, ClassName: str, MethodName: str) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - class_sha1 = struct_sha1_map.get(ClassName) - if class_sha1: - MangledName = f'{class_sha1}.{ClassName}.{MethodName}' - else: - MangledName = f'{ClassName}.{MethodName}' - func = Gen._find_function(MangledName) - if not func: - func = Gen._find_function(f'{MangledName}__') - if not func: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(MangledName, Gen) - if stub_func_type: - func = self.GetOrCreateStubFuncDecl(MangledName, stub_func_type, Gen) - if not func and class_sha1: - plain_name = f'{ClassName}.{MethodName}' - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen) - if stub_func_type: - func = self.GetOrCreateStubFuncDecl(MangledName, stub_func_type, Gen) - if not func: - if ClassName not in Gen.structs: - self._ensure_struct_declared(ClassName) - SymInfo = self.LookupFunctionSymbol(MethodName, module_path=ClassName) - if SymInfo and SymInfo.IsFunction: - sig = self.BuildLLVMFuncTypeFromSig(SymInfo, Gen) - if sig is not None: - ret_type, llvm_param_types, _ = sig - is_static = FuncMeta.STATIC_METHOD in SymInfo.MetaList - if ClassName in Gen.structs and not is_static: - llvm_param_types.insert(0, ir.PointerType(Gen.structs[ClassName])) - func_type = ir.FunctionType(ret_type, llvm_param_types) - # 走唯一幂等入口:module.globals 已有同名函数(stub / 调用点先建) - # 时复用,避免 llvmlite DuplicatedNameError;类型不同则 fail-fast。 - func = Gen._CreateOrReuseFuncDecl(MangledName, func_type) - # 方法实际定义类(多继承 this 调整用) - method_anc_st: str | None = None - # 若 MangledName 只有 stub 声明(无函数体),也走 MRO 回退找定义类方法 - if not func or len(list(func.blocks)) == 0: - # 多继承:沿 MRO 祖先链查找(先声明父类优先) - for anc in Gen._get_mro_chain(ClassName)[1:]: - parent_mangled = Gen._mangle_name(f'{anc}.{MethodName}') - parent_func = Gen.functions.get(parent_mangled) - if parent_func: - func = parent_func - method_anc_st = anc - break - parent_func = Gen._find_function(f'{anc}.{MethodName}') - if parent_func: - func = parent_func - method_anc_st = anc - break - if not func: - return None - CallArgs = [] - SymInfo = self.LookupFunctionSymbol(MethodName, module_path=ClassName) - is_static_call = SymInfo is not None and FuncMeta.STATIC_METHOD in SymInfo.MetaList - is_classmethod_call = SymInfo is not None and FuncMeta.CLASS_METHOD in SymInfo.MetaList - # @classmethod: 在参数列表前插入 cls(栈上分配的类实例指针) - if is_classmethod_call and ClassName in Gen.structs: - ClsPtr = Gen._alloca(Gen.structs[ClassName], name=f"cls_{ClassName}") - CallArgs.append(ClsPtr) - # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度 - # (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量实参走原路径。 - _arg_off: int = len(CallArgs) - for _ai, arg in enumerate(Node.args): - _pt: ir.Type | None = None - if func is not None: - _ft_args: list[ir.Type] = func.function_type.args - if _ai + _arg_off < len(_ft_args): - _pt = _ft_args[_ai + _arg_off] - ArgVal = self.HandleExprLlvm( - arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)) - if ArgVal: - CallArgs.append(ArgVal) - if (ClassName not in Gen.class_vtable - and ClassName not in Gen._cross_module_vtable_classes - and class_sha1): - self._try_register_vtable_ondemand(ClassName, class_sha1, Gen) - # 跨模块多继承:确保 class_parents 可用(MRO 回退需要)。 - # 如 HandlesType 委托调用 Generator 的方法——Generator 定义在 - # 另一模块,class_parents 未随 import 填充,需从 vpi 按需加载。 - self._EnsureClassParentsLoaded(ClassName, Gen) - if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods and CallArgs and not is_static_call: - methods = Gen.class_methods[ClassName] - if class_sha1: - FullMethodName = f'{class_sha1}.{ClassName}.{MethodName}' - else: - FullMethodName = f'{ClassName}.{MethodName}' - method_idx = -1 - for mi, mn in enumerate(methods): - if mn == FullMethodName or mn == MethodName or mn.endswith(f'.{MethodName}'): - method_idx = mi - break - if method_idx >= 0: - ObjVal = CallArgs[0] - if self._is_char_pointer(ObjVal): - if ClassName in Gen.structs: - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_static") - # 多继承 this 调整(仅调整 self 参数;vtable 槽位读取用原始 ObjVal, - # 因为 vptr 总在对象首字段,与 TPV _do_virtual_call 一致) - self_arg_st = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc_st) - CallArgs[0] = self_arg_st - VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}_static") - VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_static") - VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) - VtableTyped = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}_static") - MethodPtrAddr = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_ptr_addr_{MethodName}_static") - MethodPtrI8 = Gen._load(MethodPtrAddr, name=f"vmethod_ptr_{MethodName}_static") - FuncPtrType = ir.PointerType(func.function_type) - MethodPtrTyped = Gen.builder.bitcast(MethodPtrI8, FuncPtrType, name=f"vmethod_typed_{MethodName}_static") - self._fill_default_args(CallArgs, func, FullMethodName) - self._append_eh_msg_out_arg(CallArgs, func, Gen) - adjusted = Gen._adjust_args(CallArgs, func) - result = Gen.builder.call(MethodPtrTyped, adjusted, name=f"vcall_{FullMethodName}") - self._check_eh_return(result, FullMethodName, Gen, called_func=func) - return result - self._append_eh_msg_out_arg(CallArgs, func, Gen) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表), - # 此处的显式取址已删除,避免 'always' 模式被重复应用(不幂等)。 - adjusted = Gen._adjust_args(CallArgs, func) - result = Gen.builder.call(func, adjusted, name=f"call_{MangledName}") - self._check_eh_return(result, MangledName, Gen, called_func=func) - return result - - def _HandleInstanceCallLlvm(self, Node: ast.Call, VarName: str, ClassName: str) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - ObjVal = self.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load())) - if not ObjVal: - raise Exception(f"Cannot evaluate object '{VarName}' for __call__") - if self._is_char_pointer(ObjVal): - if ClassName in Gen.structs: - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - CallArgs = [ObjVal] - CallFuncName = f'{ClassName}.__call__' - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - class_sha1 = struct_sha1_map.get(ClassName) - if class_sha1: - CallFuncName = f'{class_sha1}.{ClassName}.__call__' - # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 - # CallArgs[0] 为 self ⇒ 第 i 个用户实参对应形参 i+1。 - _cfunc: ir.Function | None = (Gen._get_function(CallFuncName) - if Gen._has_function(CallFuncName) else None) - _ptypes: list[ir.Type] = _cfunc.function_type.args if _cfunc is not None else [] - for _ai, arg in enumerate(Node.args): - _pt: ir.Type | None = _ptypes[_ai + 1] if _ai + 1 < len(_ptypes) else None - ArgVal = self.HandleExprLlvm( - arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)) - if ArgVal: - CallArgs.append(ArgVal) - if not Gen._has_function(CallFuncName): - raise Exception(f"Class '{ClassName}' has no __call__ method") - func = Gen._get_function(CallFuncName) - self._fill_default_args(CallArgs, func, CallFuncName) - self._ApplyKeywordArgs(Node, CallArgs, func, CallFuncName, self_offset=1) - self._append_eh_msg_out_arg(CallArgs, func, Gen) - adjusted = Gen._adjust_args(CallArgs, func) - result = Gen.builder.call(func, adjusted, name=f"call_{CallFuncName}") - self._check_eh_return(result, CallFuncName, Gen, called_func=func) - return result - - def _make_virtual_attr_call(self, Node: ast.Call, module_name: str, attr_name: str) -> ast.Call: - virtual_func = ast.Attribute( - value=ast.Name(id=module_name, ctx=ast.Load()), - attr=attr_name, - ctx=ast.Load() - ) - new_node = ast.Call( - func=virtual_func, - args=Node.args, - keywords=Node.keywords - ) - ast.copy_location(new_node, Node) - return new_node - - def _get_ModulePath(self, node: ast.AST) -> str | None: - parts = [] - while isinstance(node, ast.Attribute): - parts.append(node.attr) - node = node.value - if isinstance(node, ast.Name): - parts.append(node.id) - parts.reverse() - return '.'.join(parts) if parts else None - - def _IsModuleQualifiedGlobalChain(self, base_name: str, receiver: ast.AST) -> bool: - """判定"模块限定全局变量"接收者链(链根=模块别名、次段=该模块全局变量)。 - - 与调用点既有的"链根是已知变量(self / 局部变量 / 全局变量 / struct 变量)" - 判据互补,补的是**模块限定全局变量**这一形态: - `import lib.core.Gen.GenApi as GenApi` 后写 - `GenApi._g_gen.ClassGenBase._collect_parents(...)`——链根 `GenApi` 是模块 - 别名(不在 var_struct_class / variables / global_vars 中),次段 `_g_gen` - 是该模块的全局变量。缺此判定时该接收者被当作"模块.类"引用,落到下方 - `inner_attr in Gen.structs` 分支走 _HandleStaticMethodCallLlvm——该路径按 - 静态方法口径处理(**不前置接收者 self**),实参整体错位一格 - (trans: Translator* 对上 max_parents: i32),触发 fail-fast - "指针类型 ... 不能隐式转换为整数参数 i32";不报错的同形调用(实参全为指针) - 则静默生成缺 self 的错误调用。 - - 判据只有两段:链根是模块(SymbolTable.import_aliases / IsModuleAlias 符号 / - ModuleSha1Map / _ImportedModules 任一命中),且**紧邻根名的那一段属性**不是 - 类型/函数类符号(类 / 枚举 / REnum / 枚举成员 / 函数 / CDefine / 异常类 / - 模块别名)——即该段是"对象"(全局变量;跨模块全局在本符号表里登记为 - IsTypedef 的空类型条目,故 IsTypedef 不在排除集内)。紧随段是类 - (`Module.Class.static_method(...)`)时返回 False,静态方法口径保持不变。 - """ - symbol_table: Any = self.Trans.SymbolTable - # 紧邻根名的那一段属性:GenApi._g_gen.ClassGenBase → '_g_gen' - first_attr: str | None = None - cursor: ast.AST = receiver - while isinstance(cursor, ast.Attribute): - first_attr = cursor.attr - cursor = cursor.value - if not first_attr or not isinstance(cursor, ast.Name): - return False - # 链根是不是模块:优先别名表(import ... as X 的 X → 模块全名), - # 其次符号表里的模块别名标记,最后模块表(其键含短名)兜底。 - module_path: str = symbol_table.resolve_alias(base_name) - if module_path != base_name: - is_module: bool = True - else: - alias_entry: Any = symbol_table.lookup(base_name) - if alias_entry is not None and getattr(alias_entry, 'IsModuleAlias', False): - module_path = getattr(alias_entry, 'ResolvedModule', '') or '' - is_module = bool(module_path) - else: - is_module = ( - base_name in getattr(self.Trans.LlvmGen, 'ModuleSha1Map', {}) - or base_name in getattr(self.Trans, '_ImportedModules', set())) - if not is_module: - return False - segment: Any = symbol_table.lookup(f'{module_path}.{first_attr}') if module_path else None - if segment is None: - segment = symbol_table.lookup(first_attr) - if segment is None: - return False - # 该段是"类型/函数类"符号 ⇒ 属于 `Module.Class...` 引用,不按实例链处理 - # (同名枚举成员/异常类在符号表里与类名冲突,一并排除,避免误判)。 - for _type_flag in ('IsStruct', 'IsEnum', 'IsRenum', 'IsUnion', 'IsEnumMember', - 'IsFunction', 'IsDefine', 'IsExceptionClass', 'IsModuleAlias'): - if getattr(segment, _type_flag, False): - return False - return True - - def _HandleLLVMIRLlvm(self, Node: ast.Call) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if not Node.args: - return ir.Constant(ir.IntType(32), 0) - - all_ops = [] - input_ops = [] - output_targets = [] - operand_seq = 0 - - ret_type = ir.IntType(32) - if len(Node.args) >= 2: - ret_type_arg = Node.args[1] - inferred = self.Trans.ExprAsmHandle._infer_expr_llvm_type(ret_type_arg) - if inferred: - ret_type = inferred - - ir_template = "" - first_arg = Node.args[0] - - if isinstance(first_arg, ast.JoinedStr): - parts = [] - for value in first_arg.values: - if isinstance(value, ast.Constant) and isinstance(value.value, str): - parts.append(value.value) - elif isinstance(value, ast.FormattedValue): - expr = value.value - # c.LInp/c.LOut 与裸名 irinp/irout 等价(见 _intrinsic_marker) - _marker: str | None = self.Trans.ExprAsmHandle._intrinsic_marker(expr) - _is_c_qualified: bool = (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute) - and isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c') - if _marker == 'LInp': - if expr.args: - val = self.HandleExprLlvm(expr.args[0]) - if val: - input_ops.append(val) - all_ops.append(('inp', len(input_ops) - 1)) - parts.append(f'%__OP{operand_seq}__') - operand_seq += 1 - elif _marker == 'LOut': - if expr.args: - target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0]) - if target_ptr: - val = target_ptr - else: - val = self.HandleExprLlvm(expr.args[0]) - if val: - output_targets.append(val) - all_ops.append(('out', len(output_targets) - 1)) - parts.append(f'%__OP{operand_seq}__') - operand_seq += 1 - elif not _is_c_qualified: - # c.* 中非 LInp/LOut 的形态不收集(保持原行为) - fallback = self.HandleExprLlvm(expr) - if fallback: - input_ops.append(fallback) - all_ops.append(('inp', len(input_ops) - 1)) - parts.append(f'%__OP{operand_seq}__') - operand_seq += 1 - ir_template = ''.join(parts) - elif isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): - ir_template = first_arg.value - - return self._EmitLLVMIRInstruction(Gen, ir_template, input_ops, output_targets, all_ops, ret_type) - - def _EmitLLVMIRInstruction(self, Gen: LlvmGeneratorMixin, ir_template: str, input_ops: list[ir.Value], output_targets: list[ir.Value], all_ops: list[tuple], ret_type: ir.Type) -> ir.Value: - template = ir_template.strip() - - def resolve_op(idx): - if idx < len(all_ops): - kind, local_idx = all_ops[idx] - if kind == 'inp' and local_idx < len(input_ops): - return input_ops[local_idx] - elif kind == 'out' and local_idx < len(output_targets): - return output_targets[local_idx] - return None - - assign_match = _RE_OP_ASSIGN.match(template) - if assign_match: - template = assign_match.group(2).strip() - - binop_map = { - 'add': 'add', 'fadd': 'fadd', 'sub': 'sub', 'fsub': 'fsub', - 'mul': 'mul', 'fmul': 'fmul', 'udiv': 'udiv', 'sdiv': 'sdiv', - 'fdiv': 'fdiv', 'urem': 'urem', 'srem': 'srem', 'frem': 'frem', - 'shl': 'shl', 'lshr': 'lshr', 'ashr': 'ashr', - 'and': 'and_', 'or': 'or_', 'xor': 'xor_', - } - - icmp_match = _RE_ICMP.match(template) - if icmp_match: - pred: str = icmp_match.group(1) - op1 = resolve_op(int(icmp_match.group(3))) - op2 = resolve_op(int(icmp_match.group(4))) - if op1 and op2: - # LLVM IR 谓词(eq/ne/sgt/sge/slt/sle/ugt/uge/ult/ule)映射到 llvmlite - # llvmlite 的 icmp_signed/icmp_unsigned 只接受 '=='/'!='/'<'/'<='/'>'/>=' - _ICMP_PRED_MAP: dict[str, tuple[str, str]] = { - 'eq': ('==', 'signed'), 'ne': ('!=', 'signed'), - 'sgt': ('>', 'signed'), 'sge': ('>=', 'signed'), - 'slt': ('<', 'signed'), 'sle': ('<=', 'signed'), - 'ugt': ('>', 'unsigned'), 'uge': ('>=', 'unsigned'), - 'ult': ('<', 'unsigned'), 'ule': ('<=', 'unsigned'), - } - pred_info: tuple[str, str] | None = _ICMP_PRED_MAP.get(pred) - if pred_info: - llvmlite_pred: str - cmp_kind: str - llvmlite_pred, cmp_kind = pred_info - icmp_method: Any = Gen.builder.icmp_signed if cmp_kind == 'signed' else Gen.builder.icmp_unsigned - result = icmp_method(llvmlite_pred, op1, op2, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - fcmp_match = _RE_FCMP.match(template) - if fcmp_match: - pred_raw: str = fcmp_match.group(1).strip() - # 分离 fast 修饰符(可能是 "oeq fast" 或 "fast oeq") - is_fast: bool = 'fast' in pred_raw - pred: str = pred_raw.replace('fast', '').strip() - op1 = resolve_op(int(fcmp_match.group(3))) - op2 = resolve_op(int(fcmp_match.group(4))) - if op1 and op2: - # LLVM IR fcmp 谓词(oeq/ogt/olt/ord/uno/ueq 等)映射到 llvmlite - _FCMP_PRED_MAP: dict[str, tuple[str, str]] = { - 'oeq': ('==', 'ordered'), 'ogt': ('>', 'ordered'), - 'oge': ('>=', 'ordered'), 'olt': ('<', 'ordered'), - 'ole': ('<=', 'ordered'), 'one': ('!=', 'ordered'), - 'ord': ('ord', 'ordered'), 'uno': ('uno', 'unordered'), - 'ueq': ('==', 'unordered'), 'ugt': ('>', 'unordered'), - 'uge': ('>=', 'unordered'), 'ult': ('<', 'unordered'), - 'ule': ('<=', 'unordered'), 'une': ('!=', 'unordered'), - } - fcmp_info: tuple[str, str] | None = _FCMP_PRED_MAP.get(pred) - if fcmp_info: - llvmlite_pred: str - cmp_kind: str - llvmlite_pred, cmp_kind = fcmp_info - fcmp_method: Any = Gen.builder.fcmp_ordered if cmp_kind == 'ordered' else Gen.builder.fcmp_unordered - result = fcmp_method(llvmlite_pred, op1, op2, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - for op_name, method_name in binop_map.items(): - pattern = re.compile( - rf'{op_name}\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__', - re.IGNORECASE if op_name in ('and', 'or', 'xor') else 0 - ) - m = pattern.match(template) - if m: - op1 = resolve_op(int(m.group(2))) - op2 = resolve_op(int(m.group(3))) - if op1 and op2: - method = getattr(Gen.builder, method_name, None) - if method: - result = method(op1, op2, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - select_match = _RE_SELECT.match(template) - if select_match: - cond = resolve_op(int(select_match.group(1))) - true_val = resolve_op(int(select_match.group(3))) - false_val = resolve_op(int(select_match.group(5))) - if cond and true_val and false_val: - result = Gen.builder.select(cond, true_val, false_val, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - cast_map = { - 'zext': 'zext', 'sext': 'sext', 'trunc': 'trunc', - 'bitcast': 'bitcast', 'inttoptr': 'inttoptr', 'ptrtoint': 'ptrtoint', - 'uitofp': 'uitofp', 'sitofp': 'sitofp', 'fptoui': 'fptoui', 'fptosi': 'fptosi', - 'fpext': 'fpext', 'fptrunc': 'fptrunc', - } - for op_name, method_name in cast_map.items(): - pattern = re.compile(rf'{op_name}\s+(\w+)\s+%__OP(\d+)__\s+to\s+(\w+)') - m = pattern.match(template) - if m: - src_val = resolve_op(int(m.group(2))) - dest_type_str = m.group(3) - dest_type = self._ParseLLVMType(Gen, dest_type_str) - if src_val and dest_type: - method = getattr(Gen.builder, method_name, None) - if method: - result = method(src_val, dest_type, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - Load_match = _RE_LOAD.match(template) - if Load_match: - ptr_val = resolve_op(int(Load_match.group(3))) - if ptr_val: - result = Gen.builder.load(ptr_val, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - store_match = _RE_STORE.match(template) - if store_match: - val = resolve_op(int(store_match.group(2))) - ptr = resolve_op(int(store_match.group(4))) - if val and ptr: - Gen.builder.store(val, ptr) - return ir.Constant(ir.IntType(32), 0) - - gep_match = _RE_GEP.match(template) - if gep_match: - ptr_val = resolve_op(int(gep_match.group(3))) - indices_str = gep_match.group(4) - if ptr_val: - indices = [] - for idx_part in _RE_COMMA_WS.split(indices_str): - idx_part = idx_part.strip() - idx_m = _RE_OP_REF.match(idx_part) - if idx_m: - idx_op = resolve_op(int(idx_m.group(1))) - if idx_op: - indices.append(idx_op) - else: - try: - indices.append(ir.Constant(ir.IntType(32), int(idx_part))) - except ValueError: - break - if indices: - result = Gen.builder.gep(ptr_val, indices, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - neg_match = _RE_NEG.match(template) - if neg_match: - op = resolve_op(int(neg_match.group(3))) - if op: - zero = ir.Constant(op.type, int(neg_match.group(2))) - result = Gen.builder.sub(zero, op, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - not_match = _RE_NOT.match(template) - if not_match: - op = resolve_op(int(not_match.group(3))) - if op: - mask = ir.Constant(op.type, int(not_match.group(2))) - result = Gen.builder.xor_(op, mask, name="llvmir_result") - return self._StoreLLVMIROutputs(Gen, result, output_targets) - - call_match = _RE_CALL.match(template) - if call_match: - call_ret_str = call_match.group(1) - func_name = call_match.group(2) - args_str = call_match.group(3) - call_args = [] - for arg_part in _RE_COMMA_WS.split(args_str): - arg_part = arg_part.strip() - typed_op = _RE_TYPED_OP.match(arg_part) - if typed_op: - expected_type_str = typed_op.group(1) - op_idx = int(typed_op.group(2)) - val = resolve_op(op_idx) - if val: - val = self._ConvertLLVMIRType(Gen, val, expected_type_str) - if val: - call_args.append(val) - else: - const_m = _RE_CONST.match(arg_part) - if const_m: - const_type = self._ParseLLVMType(Gen, const_m.group(1)) - if const_type: - call_args.append(ir.Constant(const_type, int(const_m.group(2)))) - if call_args: - call_ret_type = self._ParseLLVMType(Gen, call_ret_str) or ir.VoidType() - func_type = ir.FunctionType(call_ret_type, [a.type for a in call_args]) - if func_name.startswith('llvm.'): - existing = Gen.module.globals.get(func_name) - if existing: - func = existing - else: - try: - func = Gen.module.declare_intrinsic(func_name, [a.type for a in call_args]) - except Exception: - func = Gen._CreateOrReuseFuncDecl(func_name, func_type) - else: - existing = Gen.module.globals.get(func_name) - if existing: - func = existing - else: - func = Gen._CreateOrReuseFuncDecl(func_name, func_type) - result = Gen.builder.call(func, call_args, name="llvmir_result") - if output_targets: - return self._StoreLLVMIROutputs(Gen, result, output_targets) - if isinstance(call_ret_type, ir.VoidType): - return ir.Constant(ir.IntType(32), 0) - return result - - if output_targets: - for i, target in enumerate(output_targets): - if isinstance(target, ir.AllocaInstr): - Gen.builder.store(ir.Constant(target.type.pointee, 0), target) - return ir.Constant(ir.IntType(32), 0) - - return ir.Constant(ret_type, 0) - - def _StoreLLVMIROutputs(self, Gen: LlvmGeneratorMixin, result: ir.Value, output_targets: list[ir.Value]) -> ir.Value: - for target in output_targets: - if isinstance(target, ir.AllocaInstr): - if target.type.pointee == result.type: - Gen.builder.store(result, target) - else: - i64t = ir.IntType(64) - if isinstance(result.type, ir.IntType) and result.type.width == 64 and isinstance(target.type.pointee, ir.PointerType): - ptr_val = Gen.builder.inttoptr(result, target.type.pointee) - Gen.builder.store(ptr_val, target) - elif isinstance(result.type, ir.PointerType) and isinstance(target.type.pointee, ir.IntType) and target.type.pointee.width == 64: - int_val = Gen.builder.ptrtoint(result, i64t) - Gen.builder.store(int_val, target) - else: - try: - cast_val = Gen.builder.bitcast(result, target.type.pointee) - Gen.builder.store(cast_val, target) - except Exception: - Gen.builder.store(result, target) - elif isinstance(target, (ir.GlobalVariable, ir.GEPInstr)): - if isinstance(target.type, ir.PointerType): - if target.type.pointee == result.type: - Gen.builder.store(result, target) - else: - try: - cast_val = Gen.builder.bitcast(result, target.type.pointee) - Gen.builder.store(cast_val, target) - except Exception: - Gen.builder.store(result, target) - return result - - def _ParseLLVMType(self, Gen: LlvmGeneratorMixin, type_str: str) -> ir.Type | None: - type_map = { - 'i1': ir.IntType(1), 'i8': ir.IntType(8), 'i16': ir.IntType(16), - 'i32': ir.IntType(32), 'i64': ir.IntType(64), 'i128': ir.IntType(128), - 'float': ir.FloatType(), 'double': ir.DoubleType(), - 'void': ir.VoidType(), - } - if type_str in type_map: - return type_map[type_str] - if type_str.endswith('*'): - pointee = self._ParseLLVMType(Gen, type_str[:-1]) - if pointee: - return ir.PointerType(pointee) - return None - - def _ConvertLLVMIRType(self, Gen: LlvmGeneratorMixin, val: ir.Value, expected_type_str: str) -> ir.Value | None: - expected_type = self._ParseLLVMType(Gen, expected_type_str.rstrip('*')) - if not expected_type: - return val - IsPtr = expected_type_str.endswith('*') - if IsPtr: - expected_ptr_type = ir.PointerType(expected_type) - if isinstance(val.type, ir.PointerType) and val.type != expected_ptr_type: - try: - return Gen.builder.bitcast(val, expected_ptr_type) - except Exception: - return val - elif isinstance(val.type, ir.IntType) and val.type.width == 64: - try: - return Gen.builder.inttoptr(val, expected_ptr_type) - except Exception: - return val - return val - if val.type == expected_type: - return val - if isinstance(val.type, ir.IntType) and isinstance(expected_type, ir.IntType): - if expected_type.width < val.type.width: - return Gen.builder.trunc(val, expected_type) - else: - return Gen.builder.zext(val, expected_type) - if isinstance(val.type, ir.PointerType) and isinstance(expected_type, ir.IntType): - if expected_type.width == 64: - return Gen.builder.ptrtoint(val, expected_type) - if isinstance(val.type, ir.IntType) and isinstance(expected_type, ir.PointerType): - if val.type.width == 64: - return Gen.builder.inttoptr(val, expected_type) - return val - - def _HandleExternalCallLlvm(self, Node: ast.Call, func_name: str, ModulePath: str | None) -> ir.Value | None: - Gen = self.Trans.LlvmGen - - # 解析 import 别名: 如 window -> vpsdk.window - if ModulePath and ModulePath not in ('c', 't'): - ModulePath = self.Trans.SymbolTable.resolve_alias(ModulePath) - - is_user_module = (ModulePath and ModulePath not in ('c', 't') and - (ModulePath in Gen.ModuleSha1Map or - ModulePath in getattr(self.Trans, '_ImportedModules', set()) or - ModulePath in self.Trans.SymbolTable.import_aliases)) - if ModulePath and ModulePath not in ('c', 't') and not is_user_module: - is_user_module = (ModulePath in Gen.ModuleSha1Map or - ModulePath.split('.')[-1] in Gen.ModuleSha1Map or - ModulePath.split('.')[-1] in getattr(self.Trans, '_ImportedModules', set())) - if is_user_module and ModulePath not in Gen.ModuleSha1Map and ModulePath not in getattr(self.Trans, '_ImportedModules', set()): - ModulePath = ModulePath.split('.')[-1] - - # 模块别名识别:`import lib.core.X as X`(模块内有同名类时,类名会 - # 注册进 SymbolTable 遮蔽模块判定——如 CompilerFactory 模块 + 同名类)。 - # import 处理(HandlesImports._EmitImportDeclarationsLlvm)在 SymbolTable - # 插入了 IsModuleAlias 的 AliasInfo,据此识别并补全完整路径,使 - # `X.get_perm_pool()` 正确解析为模块函数({sha1}.get_perm_pool)而非 - # 类方法({sha1}.X.get_perm_pool,链接 undefined symbol)。 - if ModulePath and ModulePath not in ('c', 't') and not is_user_module: - _al_last = ModulePath.split('.')[-1] - _al_sym = self.Trans.SymbolTable.lookup(_al_last) - if _al_sym and getattr(_al_sym, 'IsModuleAlias', False): - is_user_module = True - if ModulePath in self.Trans.SymbolTable.import_aliases: - ModulePath = self.Trans.SymbolTable.import_aliases[ModulePath] - # 未命中模块别名时不截断:保持 is_user_module=False 走下方裸名回退 - - if is_user_module: - mangled_name = Gen._mangle_func_name(func_name, ModulePath) - sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath) - is_inline = sym_info and (sym_info.IsInline or isinstance(sym_info.Storage, t.CInline)) - if is_inline and sym_info.InlineBody: - self._HandleInlineExpandLlvm(Node, sym_info) - return ir.Constant(ir.IntType(32), 1) - sym_info_exact = self.Trans.SymbolTable.lookup(f'{ModulePath}.{func_name}') - if sym_info_exact: - exact_params = sym_info_exact.FuncPtrParams - exact_param_names = [pn for pn, _ in exact_params] - # stub/LLVM 侧形参名多为 arg0/arg1 占位(SymbolTable._InsertFuncSymbol), - # 用它们无法把命名实参 `f(cap = 8, x = 1)` 绑到正确位置,会误报"缺少必需 - # 参数"。优先替换为源码/vpi 扫描得到的语义参数名(长度一致才替换, - # 避免 shape 不符时错配)。 - semantic_names = (self._LookupGlobalParamNames(mangled_name) - or self._LookupGlobalParamNames(func_name)) - if semantic_names and len(semantic_names) == len(exact_param_names): - exact_param_names = semantic_names - if exact_param_names: - provided = len(Node.args) - kw_provided = set() - for kw in Node.keywords: - if kw.arg and kw.arg in exact_param_names: - kw_provided.add(exact_param_names.index(kw.arg)) - # 获取默认参数信息,计算必需参数数量(与 _fill_default_args 一致的查找逻辑) - func_defaults: list | None = self._LookupGlobalDefaults(mangled_name) - if func_defaults is None: - func_defaults = self._LookupGlobalDefaults(func_name) - total_params: int = len(exact_param_names) - if func_defaults is None: - # 默认参数信息缺失(跨模块 stub 加载:vpi/stub 只含参数 - # 类型不含默认值;或并行编译时序导致定义模块的默认参数 - # 尚未注册到共享表)→ 无法准确计算必需参数数量, - # 跳过严格检查避免误报(如 HandlesType.fatal_error 的 - # err_type/col_start/col_end 默认值未同步时误报"缺 arg3")。 - # 同文件函数的参数检查由 FunctionDef 路径(L1983)覆盖。 - pass - elif len(func_defaults) > total_params: - # 表里的条目与当前函数长度不符(同名不同模块)→ 不做严格检查 - pass - else: - num_defaults: int = len(func_defaults) - min_args: int = total_params - num_defaults - # 只检查必需参数(有默认值的参数允许缺失) - for i in range(min_args): - if i >= provided and i not in kw_provided: - raise Exception(f"调用 {ModulePath}.{func_name}() 缺少必需参数 '{exact_param_names[i]}',该参数没有默认值") - elif ModulePath in Gen.ModuleSha1Map and len(Node.args) == 0: - func = Gen.functions.get(mangled_name) - if func and len(func.function_type.args) > 0: - raise Exception(f"调用 {ModulePath}.{func_name}() 缺少必需参数,函数需要 {len(func.function_type.args)} 个参数但未传入任何参数") - if Gen._has_function(mangled_name): - return self._HandleClosureCallLlvm(Node, mangled_name) - try: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) - except Exception: - stub_func_type = None - if not stub_func_type and mangled_name != func_name: - try: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(func_name, Gen) - except Exception: - stub_func_type = None - if stub_func_type: - if is_user_module: - decl_name = mangled_name - else: - decl_name = mangled_name if mangled_name in self.Trans.ImportHandler._stub_func_cache else func_name - self.GetOrCreateStubFuncDecl(decl_name, stub_func_type, Gen, alias=func_name) - return self._HandleClosureCallLlvm(Node, decl_name) - sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath) - if sym_info: - sig = self.BuildLLVMFuncTypeFromSig(sym_info, Gen, mangled_name=mangled_name, use_infer_fallback=True) - if sig is not None: - ret_type, llvm_param_types, sym_is_variadic = sig - self.GetOrCreateFuncDecl(mangled_name, ret_type, llvm_param_types, Gen, - is_variadic=sym_is_variadic, replace_name=func_name) - return self._HandleClosureCallLlvm(Node, mangled_name) - # 回退: 直接用 func_name 查找(_find_function 会遍历 SHA1 前缀) - fallback_func = Gen._find_function(func_name) - if fallback_func is not None: - return self._HandleClosureCallLlvm(Node, func_name) - return None - - if ModulePath == 'c': - if func_name == 'Asm': - return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) - elif func_name == 'Addr': - return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node) - elif func_name == 'Set': - return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node) - elif func_name == 'Load': - return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node) - elif func_name == 'Deref': - return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) - elif func_name == 'DerefAs': - return self._HandleCDerefAsLlvm(Node) - elif func_name == 'PtrToInt': - return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) - elif func_name in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'): - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError(_REMOVED_CIF_CALL_MSG) - elif func_name == 'CError': - msg = "compile-time error" - if Node.args: - arg = Node.args[0] - if isinstance(arg, ast.Constant) and isinstance(arg.value, str): - msg = arg.value - lineno = getattr(Node, 'lineno', 0) - # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 - _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - raise Exception(f"#error: {msg} (line {lineno})") - elif func_name in ('AsmInp', 'AsmOut'): - return ir.Constant(ir.IntType(32), 0) - elif func_name == 'LLVMIR': - return self._HandleLLVMIRLlvm(Node) - elif func_name in ('LInp', 'LOut'): - return ir.Constant(ir.IntType(32), 0) - - win32_apis = { - 'SetConsoleOutputCP': (ir.IntType(32), [ir.IntType(32)]), - 'SetConsoleCP': (ir.IntType(32), [ir.IntType(32)]), - 'GetConsoleOutputCP': (ir.IntType(32), []), - 'GetConsoleCP': (ir.IntType(32), []), - } - if ModulePath and 'win32console' in ModulePath and func_name in win32_apis: - ret_type, param_types = win32_apis[func_name] - if func_name not in Gen.functions: - # module.globals 已有同名声明(其它路径/依赖 stub 先建)→ 直接登记复用, - # 不重建(llvmlite NameScope 按名字去重,重建必抛 DuplicatedNameError) - g_ex_w32: Any = Gen.module.globals.get(func_name) - if g_ex_w32 is not None: - Gen.functions[func_name] = g_ex_w32 - else: - func_type = ir.FunctionType(ret_type, param_types) - func_decl = Gen._CreateOrReuseFuncDecl(func_name, func_type) - func_decl.linkage = 'external' - Gen.functions[func_name] = func_decl - call_args = [] - if Node.args: - arg_val = self.HandleExprLlvm(Node.args[0]) - if arg_val: - target_type = param_types[0] if param_types else ir.IntType(32) - if isinstance(arg_val.type, ir.IntType) and arg_val.type.width < target_type.width: - arg_val = Gen.builder.zext(arg_val, target_type, name="zext_win32_arg") - elif isinstance(arg_val.type, ir.IntType) and arg_val.type.width > target_type.width: - arg_val = Gen.builder.trunc(arg_val, target_type, name="trunc_win32_arg") - call_args.append(arg_val) - return Gen.builder.call(Gen.functions[func_name], call_args, name=f"call_{func_name}") - - if Gen._has_function(func_name): - return self._HandleClosureCallLlvm(Node, func_name) - - if func_name in self.Trans.FunctionDefCache: - return self._HandleClosureCallLlvm(Node, func_name) - - if func_name in self._C_LIB_FUNCS: - return self._HandleClosureCallLlvm(Node, func_name) - - mangled_name = Gen._mangle_func_name(func_name, ModulePath) - try: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) - except Exception: - stub_func_type = None - if not stub_func_type and mangled_name != func_name: - try: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(func_name, Gen) - except Exception: - stub_func_type = None - if stub_func_type: - decl_name = mangled_name if mangled_name in self.Trans.ImportHandler._stub_func_cache else func_name - self.GetOrCreateStubFuncDecl(decl_name, stub_func_type, Gen, alias=func_name, check_existing=False) - return self._HandleClosureCallLlvm(Node, decl_name) - - sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath or None) - if sym_info: - sig = self.BuildLLVMFuncTypeFromSig(sym_info, Gen) - if sig is not None: - ret_type, llvm_param_types, sym_is_variadic = sig - mangled_name = Gen._mangle_func_name(func_name, ModulePath) - self.GetOrCreateFuncDecl(mangled_name, ret_type, llvm_param_types, Gen, - is_variadic=sym_is_variadic, replace_name=func_name) - return self._HandleClosureCallLlvm(Node, mangled_name) - - return None - - def _HandleTypeUnionCastLlvm(self, Node: ast.Call) -> ir.Value | None: - Gen = self.Trans.LlvmGen - if not Node.args: - return None - type_info = None - MergeException = None - try: - type_info = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.func) - except Exception as _e: - MergeException = _e - if _config_mode == "strict": - raise - _vlog().warning(f"解析类型失败: {_e}", "Exception") - if isinstance(type_info, CTypeInfo): - # 主路径:MergeTypes 成功返回 CTypeInfo,沿用原有逻辑生成类型字符串 - target_type_str = type_info.ToString() - IsPtr = type_info.IsPtr - if IsPtr and target_type_str and '*' not in target_type_str: - target_type_str += ' *' - if not target_type_str: - # Fallback:ToString 返回空字符串时,尝试手动从 BinOp 提取类型字符串 - _vlog().warning( - f"_HandleTypeUnionCastLlvm: ToString 返回空字符串,启用 fallback 手动解析 " - f"(type_info={type_info!r})" - ) - target_type_str = self._ExtractTypeStrFromUnionBinOp(Node.func) - if target_type_str and type_info.IsPtr and not IsPtr: - if '*' not in target_type_str: - target_type_str += ' *' - if isinstance(type_info.Storage, t.CExport) or isinstance(type_info.Storage, t.CExtern): - pass - if type_info.IsState: - Gen._export_funcs.add('_type_union_cast') - else: - # Fallback 路径:MergeTypes 失败(返回 None 或抛异常)时,手动从 BinOp 提取类型信息 - # 处理 (BaseType | t.CPtr) 这种模式,手动解析为 "{BaseType} *" - _vlog().warning( - f"_HandleTypeUnionCastLlvm: MergeTypes 未返回 CTypeInfo " - f"(type_info={type_info!r}, exception={MergeException!r}),启用 fallback 手动解析" - ) - target_type_str = self._ExtractTypeStrFromUnionBinOp(Node.func) - if not target_type_str: - # fail-fast(原为 _vlog().warning + return None): - # `(T | t.CPtr)(x)` 只可能来自指针强转——parser 把 `(T*)x` / `(T*)(x)` / - # `T*(x)`(含 `**`)统一产成 func = `T | t.CPtr` 的 Call(见 - # vast/ExprParser._ConsumePtrSuffixCall、vast_c.c ConsumePtrSuffixCall)。 - # 该形态必须有值产出;返回 None 会让调用方把整条表达式当"编译期求值为空" - # 丢掉(现场:TransPyV ExprGenBase.vp:360 `v = (ast.Expr*(node)).value` - # → TPC 报"赋值表达式 'value'(Attribute 形态)编译期求值为空",而同函数 - # 前三条 `(ast.Assign/AnnAssign/AugAssign*(node)).value` 同款写法正常)。 - # 故把"哪一步没解析出目标类型"逐项摊开,而不是静默丢弃整条表达式。 - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError(self._DescribeUnionCastFailure( - Node, Gen, type_info, MergeException, target_type_str)) - return self._HandleTypeCastLlvm(Node.args[0], target_type_str) - - def _DescribeUnionCastFailure(self, Node: ast.Call, Gen: Any, TypeInfo: Any, - MergeException: Any, TargetStr: Any) -> str: - """联合类型强转目标类型解析失败时的判定依据(逐项列出,供 fail-fast 报文)。 - - 只做取证,不做类型解析:把 MergeTypes / _ExtractTypeStrFromUnionBinOp 的 - 返回值、异常,以及联合中每个操作数在符号表与类登记表(Gen.structs / - _struct_sha1_map / class_sha1_map)里的命中情况全部列出,使"是基座类名 - 没被当成类"还是"CPtr 标记侧解析失败"一眼可分。 - """ - _Flat: list = [] - - def _Collect(n: ast.AST) -> None: - if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr): - _Collect(n.left) - _Collect(n.right) - else: - _Flat.append(n) - - _Collect(Node.func) - _Operands: list[str] = [] - for _op in _Flat: - _short: str = '' - _full: str = '' - if isinstance(_op, ast.Attribute): - _short = _op.attr - _cur: ast.AST = _op.value - while isinstance(_cur, ast.Attribute): - _cur = _cur.value - _full = f"{_cur.id}.{_short}" if isinstance(_cur, ast.Name) else _short - elif isinstance(_op, ast.Name): - _short = _op.id - _full = _short - else: - _Operands.append(f"<{type(_op).__name__}>(非类型名形态)") - continue - _sym: Any = None - try: - _sym = self.Trans.SymbolTable.lookup(_short) - except Exception: - _sym = None - _verdict: str = '符号表未命中' - if _sym is not None: - _verdict = (f"IsStruct={getattr(_sym, 'IsStruct', None)}" - f" IsRenum={getattr(_sym, 'IsRenum', None)}" - f" IsEnum={getattr(_sym, 'IsEnum', None)}" - f" IsEnumMember={getattr(_sym, 'IsEnumMember', None)}" - f" IsTypedef={getattr(_sym, 'IsTypedef', None)}" - f" Name={getattr(_sym, 'Name', None)!r}") - _Operands.append( - f"{_full}({_verdict};in structs={_short in Gen.structs}" - f";structs 同短名键={[k for k in Gen.structs if k.split('.')[-1] == _short][:4]}" - f";_struct_sha1_map={getattr(Gen, '_struct_sha1_map', {}).get(_short)!r}" - f";class_sha1_map={getattr(Gen, 'class_sha1_map', {}).get(_short)!r})") - - _ToStr: str = '<非 CTypeInfo>' - try: - if isinstance(TypeInfo, CTypeInfo): - _ToStr = repr(TypeInfo.ToString()) - elif TypeInfo is None: - _ToStr = 'None' - except Exception as _E: - _ToStr = f"" - return (f"联合类型强转 '({ast.unparse(Node.func)})(...)' 无法确定目标类型," - f"拒绝静默返回 None(会让整条表达式被判为『编译期求值为空』而丢弃):" - f"MergeTypes 返回={type(TypeInfo).__name__}(ToString()={_ToStr});" - f"_ExtractTypeStrFromUnionBinOp={TargetStr!r};" - f"MergeTypes 异常={MergeException!r};操作数判据:" + ";".join(_Operands)) - - def _ExtractTypeStrFromUnionBinOp(self, NodeFunc: ast.AST) -> str | None: - """Fallback:从类型联合 BinOp 手动提取类型字符串。 - - 处理 (BaseType | t.CPtr) 这种模式,手动解析为 "{BaseType} *"。 - 支持嵌套的 | 操作(如 (t.CInt | t.CPtr) | t.CStatic)。 - - 策略: - - 递归展平所有 | 操作数 - - t.CPtr 视为指针修饰符(生成 "{BaseType} *" 格式) - - 其他 CType 子类作为基础类型(取第一个) - - 跳过存储类/限定符修饰符(CExport/CExtern/CStatic/CConst/CVolatile/CInline 等) - """ - if not isinstance(NodeFunc, ast.BinOp) or not isinstance(NodeFunc.op, ast.BitOr): - return None - - # 存储类/限定符/修饰符类型名集合:这些类型在类型联合中只起修饰作用, - # 不是基础类型,应跳过(参考 HandlesTypeMerge._GetCTypeInfoInternal 的分类逻辑) - StorageQualifierNames = { - 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', - 'CStatic', 'CExtern', 'CExport', 'CAuto', 'CRegister', - 'CDefine', 'CPass', 'CTypeDefault', 'State', - 'BigEndian', 'LittleEndian', - } - - # 递归展平所有 | 操作数 - Operands = [] - - def CollectOperands(node: ast.AST) -> None: - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - CollectOperands(node.left) - CollectOperands(node.right) - else: - Operands.append(node) - - CollectOperands(NodeFunc) - - HasCPtr = False # 是否包含 t.CPtr(指针修饰符) - BaseTypeName = None # 基础类型名(如 "CInt"、"CChar") - - for Operand in Operands: - AttrName = None - if isinstance(Operand, ast.Attribute): - AttrName = Operand.attr - elif isinstance(Operand, ast.Name): - AttrName = Operand.id - else: - continue - - # t.CPtr 是指针修饰符,单独处理(增加一层 *) - if AttrName == 'CPtr': - HasCPtr = True - continue - - # 跳过存储类/限定符/修饰符类型,它们不是基础类型 - if AttrName in StorageQualifierNames: - continue - - # 通过 t 模块获取类型类,验证是否为 CType 子类 - TypeClass = getattr(t, AttrName, None) - if not (isinstance(TypeClass, type) and issubclass(TypeClass, t.CType)): - # 非 CType 类,跳过 - continue - - # 记录第一个非 CPtr 基础类型 - if BaseTypeName is None: - BaseTypeName = AttrName - - if not BaseTypeName: - return None - - # 包含 t.CPtr 时,生成 "{BaseType} *" 格式 - # _CType2LLVM 已验证能解析 "CInt *" → i32*(ir.PointerType(ir.IntType(32))) - if HasCPtr: - return f"{BaseTypeName} *" - return BaseTypeName - - def _HandleTypeCastLlvm(self, expr_node: ast.AST, target_type_str: str) -> ir.Value | None: - Gen = self.Trans.LlvmGen - val = self.HandleExprLlvm(expr_node) - if val is None: - return Gen.emit_constant(0, target_type_str) - target_type = Gen._CType2LLVM(target_type_str) - if not target_type: - return val - if val.type == target_type: - if Gen._is_type_unsigned(target_type_str): - Gen._unsigned_results[id(val)] = True - return val - src_is_float = isinstance(val.type, (ir.FloatType, ir.DoubleType)) - dst_is_float = isinstance(target_type, (ir.FloatType, ir.DoubleType)) - src_is_int = isinstance(val.type, ir.IntType) - dst_is_int = isinstance(target_type, ir.IntType) - src_IsPtr = isinstance(val.type, ir.PointerType) - dst_IsPtr = isinstance(target_type, ir.PointerType) - if src_is_int and dst_is_float: - is_unsigned = Gen._check_node_unsigned(expr_node) - if is_unsigned: - return Gen.builder.uitofp(val, target_type, name="uint2float") - return Gen.builder.sitofp(val, target_type, name="int2float") - if src_is_float and dst_is_int: - return Gen.builder.fptosi(val, target_type, name="float2int") - if src_is_int and dst_is_int: - is_dst_unsigned = Gen._is_type_unsigned(target_type_str) - if val.type.width < target_type.width: - if val.type.width == 1: - # i1 是布尔值(来自 icmp/fcmp/__contains__),无符号语义,必须 zext - result = Gen.builder.zext(val, target_type, name="zext_bool_cast") - else: - is_unsigned = Gen._check_node_unsigned(expr_node) - if is_unsigned or is_dst_unsigned: - result = Gen.builder.zext(val, target_type, name="zext_cast") - else: - result = Gen.builder.sext(val, target_type, name="sext_cast") - if is_dst_unsigned: - Gen._unsigned_results[id(result)] = True - return result - if val.type.width > target_type.width: - # 目标 i1:真值归一化 `x != 0`(与 bool(x) 内建 / if 条件 / 落存储 - # 同一判据),不是 trunc 取最低位(运行时值 2 会得 false) - if target_type.width == 1: - result = Gen._CoerceIntToI1(val, name="bool_norm_cast") - else: - result = Gen.builder.trunc(val, target_type, name="trunc_cast") - if is_dst_unsigned: - Gen._unsigned_results[id(result)] = True - return result - if is_dst_unsigned: - Gen._unsigned_results[id(val)] = True - return val - if src_is_float and dst_is_float: - if isinstance(val.type, ir.FloatType) and isinstance(target_type, ir.DoubleType): - return Gen.builder.fpext(val, target_type, name="fpext_cast") - if isinstance(val.type, ir.DoubleType) and isinstance(target_type, ir.FloatType): - return Gen.builder.fptrunc(val, target_type, name="fptrunc_cast") - return val - if src_IsPtr and dst_is_int: - if target_type.width < 64: - ptr_to_i64 = Gen.builder.ptrtoint(val, ir.IntType(64), name="ptr2i64") - return Gen.builder.trunc(ptr_to_i64, target_type, name="ptr2int_cast") - return Gen.builder.ptrtoint(val, target_type, name="ptr2int_cast") - if src_is_int and dst_IsPtr: - return Gen.builder.inttoptr(val, target_type, name="int2ptr_cast") - if src_IsPtr and dst_IsPtr: - if isinstance(val.type.pointee, ir.PointerType) and isinstance(target_type.pointee, ir.IntType): - Loaded = Gen._load(val, name="load_ptr") - return Loaded - return Gen.builder.bitcast(val, target_type, name="ptrcast") - try: - return Gen.builder.bitcast(val, target_type, name="cast") - except Exception: # 回退:bitcast 失败时返回原值 - return val - - def _HandleREnumConstructLlvm(self, Node: ast.Call, RenumName: str, VariantName: str, TagValue: int) -> ir.Value | None: - Gen = self.Trans.LlvmGen - if RenumName not in Gen.structs: - self.Trans.ImportHandler._TryLoadStructFromStub(RenumName, Gen) - if RenumName not in Gen.structs: - return None - RenumType = Gen.structs[RenumName] - result = Gen._allocaEntry(RenumType, name=f"{RenumName}_{VariantName}") - tag_ptr = Gen.builder.gep(result, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="renum_tag_ptr") - Gen._store(ir.Constant(ir.IntType(32), TagValue), tag_ptr) - NestedStructName = f"{RenumName}_{VariantName}" - if Node.args: - # 变体嵌套结构体按需补发:导入模块侧 stub 只声明主 REnum,不含 - # `_` 声明(见 BaseHandle._EnsureRenumVariantStruct)。 - # 缺它时历史行为是**静默丢弃全部载荷实参**(只写 tag)—— 属静默错码, - # 故此处补齐;补不上则响亮失败,绝不产出"只有 tag 没有载荷"的值。 - self._EnsureRenumVariantStruct(Gen, RenumName, VariantName) - if NestedStructName not in Gen.structs or not Gen.class_members.get(NestedStructName): - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"REnum 变体 '{RenumName}.{VariantName}' 的嵌套结构体/载荷成员缺失," - f"无法写入 {len(Node.args)} 个载荷实参(拒绝静默丢弃):" - f"structs 命中={'有' if NestedStructName in Gen.structs else '无'} " - f"class_members={len(Gen.class_members.get(NestedStructName, []))} 项;" - f"同前缀 structs 键=" - f"{[k for k in Gen.structs if k.startswith(RenumName + '_')][:8]}" - ) - if NestedStructName in Gen.structs and Node.args: - NestedStructType = Gen.structs[NestedStructName] - NestedStructPtrType = ir.PointerType(NestedStructType) - variant_ptr = Gen.builder.bitcast(result, NestedStructPtrType, name=f"cast_{NestedStructName}") - members = Gen.class_members.get(NestedStructName, []) - payLoad_members = [(n, t) for n, t in members if n != '__tag'] - for i, arg in enumerate(Node.args): - if i < len(payLoad_members): - member_name, member_type = payLoad_members[i] - ArgVal = self.HandleExprLlvm(arg) - if ArgVal: - Coerced = Gen._coerce_value(ArgVal, member_type) - if Coerced is not None: - ArgVal = Coerced - elem_ptr = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i + 1)], name=f"{VariantName}_{member_name}") - # REnum 嵌套结构体共享 max_variant_struct 布局,成员类型可能与 - # 结构体字段类型不一致(如 Bits:i32 与 Ret:LLVMType* 重叠)。 - # 需 bitcast elem_ptr 到成员类型,避免 _store 按 struct 字段类型做错误转换。 - if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type: - elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"{VariantName}_{member_name}_cast") - Gen._store(ArgVal, elem_ptr) - return result - - def _HandleTypedefCastLlvm(self, Node: ast.Call, TypedefName: str) -> ir.Value: - Gen = self.Trans.LlvmGen - SymInfo = self.Trans.SymbolTable.lookup(TypedefName) - if not SymInfo or not SymInfo.IsTypedef: - return ir.Constant(ir.IntType(32), 0) - # 确保 Name 已设置,以便 _resolve_typedef_ctype 能正确解析 - if not SymInfo.Name: - SymInfo.Name = TypedefName - TargetType = Gen._ctype_to_llvm(SymInfo) - if not Node.args: - if isinstance(TargetType, ir.IntType): - return ir.Constant(TargetType, 0) - elif isinstance(TargetType, ir.PointerType): - return ir.Constant(TargetType, None) - return ir.Constant(ir.IntType(32), 0) - ArgVal = self.HandleExprLlvm(Node.args[0]) - if not ArgVal: - if isinstance(TargetType, ir.IntType): - return ir.Constant(TargetType, 0) - return ir.Constant(ir.IntType(32), 0) - if ArgVal.type == TargetType: - return ArgVal - if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType): - if ArgVal.type.width < TargetType.width: - return Gen.builder.zext(ArgVal, TargetType, name=f"zext_{TypedefName}_cast") - elif ArgVal.type.width > TargetType.width: - return Gen.builder.trunc(ArgVal, TargetType, name=f"trunc_{TypedefName}_cast") - return ArgVal - if isinstance(TargetType, ir.PointerType): - if isinstance(ArgVal.type, ir.PointerType): - return Gen.builder.bitcast(ArgVal, TargetType, name=f"bitcast_{TypedefName}_cast") - if isinstance(ArgVal.type, ir.IntType): - return Gen.builder.inttoptr(ArgVal, TargetType, name=f"inttoptr_{TypedefName}_cast") - # 如果参数是结构体值,需要 alloca 并返回指针 - if isinstance(ArgVal.type, ir.BaseStructType): - alloca = Gen.builder.alloca(ArgVal.type, name=f"struct_alloca_{TypedefName}") - Gen.builder.store(ArgVal, alloca, align=8) - return Gen.builder.bitcast(alloca, TargetType, name=f"bitcast_struct_{TypedefName}") - if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.PointerType): - return Gen.builder.ptrtoint(ArgVal, TargetType, name=f"ptrtoint_{TypedefName}_cast") - if isinstance(TargetType, (ir.FloatType, ir.DoubleType)) and isinstance(ArgVal.type, ir.IntType): - return Gen.builder.sitofp(ArgVal, TargetType, name=f"sitofp_{TypedefName}_cast") - if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, (ir.FloatType, ir.DoubleType)): - return Gen.builder.fptosi(ArgVal, TargetType, name=f"fptosi_{TypedefName}_cast") - return ArgVal - - def _HandleSubscriptPtrCastLlvm(self, Node: ast.Call, sub: ast.Subscript) -> ir.Value: - """(rawptr[X])(expr) / (t.CPtr[X])(expr) 内建指针转换:expr → X* - - rawptr 是编译器内建裸指针语法(无 SymbolTable typedef 条目、无泛型模板), - 此前 Subscript 调用仅查泛型模板 → 落空后报 "Unsupported call expression" - (StubMergerCollect.vp `(rawptr[FileEntry])(t.CVoid(...))` bootstrap 根因)。 - 目标类型经 TypeMergeHandler.GetCTypeInfo 解析 Subscript 类型节点获得。 - """ - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - TargetType: ir.Type | None = None - try: - ti_cast: Any = self.Trans.TypeMergeHandler.GetCTypeInfo(sub) - if ti_cast is not None: - TargetType = Gen._ctype_to_llvm(ti_cast) - except Exception: - TargetType = None - if TargetType is None or isinstance(TargetType, ir.VoidType): - # 回退:rawptr[X] 语义 = X*(X 为已注册 struct/类,可能模块限定名) - s_name: str | None = None - _sl = getattr(sub, 'slice', None) - if isinstance(_sl, ast.Attribute): - s_name = _sl.attr - elif isinstance(_sl, ast.Name): - s_name = _sl.id - if s_name and s_name in Gen.structs: - TargetType = ir.PointerType(Gen.structs[s_name]) - else: - TargetType = ir.PointerType(ir.IntType(8)) - return self._emit_rawptr_cast(Node, Gen, TargetType) - - def _emit_rawptr_cast(self, Node: ast.Call, Gen: LlvmGeneratorMixin, TargetType: ir.Type) -> ir.Value: - """按 _HandleTypedefCastLlvm 的指针语义执行 bitcast/inttoptr 转换""" - if not Node.args: - if isinstance(TargetType, ir.PointerType): - return ir.Constant(TargetType, None) - return ir.Constant(ir.IntType(32), 0) - ArgVal: Any = self.HandleExprLlvm(Node.args[0]) - if not ArgVal: - return ir.Constant(ir.IntType(32), 0) - if ArgVal.type == TargetType: - return ArgVal - if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType): - # builtins typedef 数值转换(int64(bool) / uint64(i32) 等):按宽度 - # zext/trunc,与 _HandleTypedefCastLlvm 的语义一致。 - if ArgVal.type.width < TargetType.width: - return Gen.builder.zext(ArgVal, TargetType, name="zext_typedef_cast") - elif ArgVal.type.width > TargetType.width: - return Gen.builder.trunc(ArgVal, TargetType, name="trunc_typedef_cast") - return ArgVal - if isinstance(TargetType, ir.PointerType): - if isinstance(ArgVal.type, ir.PointerType): - return Gen.builder.bitcast(ArgVal, TargetType, name="bitcast_rawptr_cast") - if isinstance(ArgVal.type, ir.IntType): - return Gen.builder.inttoptr(ArgVal, TargetType, name="inttoptr_rawptr_cast") - if isinstance(ArgVal.type, ir.BaseStructType): - alloca = Gen.builder.alloca(ArgVal.type, name="struct_alloca_rawptr") - Gen.builder.store(ArgVal, alloca, align=8) - return Gen.builder.bitcast(alloca, TargetType, name="bitcast_struct_rawptr") - if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.PointerType): - return Gen.builder.ptrtoint(ArgVal, TargetType, name="ptrtoint_rawptr_cast") - return ArgVal - - def _HandleInlineExpandLlvm(self, Node: ast.Call, sym_info: CTypeInfo) -> None: - Gen = self.Trans.LlvmGen - inline_body = sym_info.InlineBody - inline_params = sym_info.InlineParams or [] - call_args = [] - for arg in Node.args: - call_args.append(self.Trans.ExprHandler.HandleExprLlvm(arg)) - saved_vars = {} - for i, param_name in enumerate(inline_params): - if i < len(call_args): - saved_vars[param_name] = call_args[i] - old_var_scopes = list(self.Trans.VarScopes) - for param_name, val in saved_vars.items(): - if val is not None: - alloca = Gen._allocaEntry(val.type, name=f"inline_arg_{param_name}") - Gen._store(val, alloca) - self.Trans.VarScopes.append((param_name, alloca)) - self.Trans.BodyHandler.HandleBodyLlvm(inline_body) - for param_name, _ in saved_vars.items(): - if self.Trans.VarScopes and self.Trans.VarScopes[-1][0] == param_name: - self.Trans.VarScopes.pop() - self.Trans.VarScopes = old_var_scopes - return None - - def _HandleClosureCallLlvm(self, Node: ast.Call, FuncName: str) -> ir.Value: - Gen = self.Trans.LlvmGen - func = Gen.functions.get(FuncName) - if not func: - if FuncName in self.Trans.FunctionDefCache: - FuncDef = self.Trans.FunctionDefCache[FuncName] - self.Trans.FunctionHandler._EmitFunctionForwardDeclLlvm(FuncDef, Gen) - func = Gen.functions.get(FuncName) - if not func: - func = self._try_declare_c_lib_func(FuncName, Gen) - if not func: - raise Exception(f"Undefined function: '{FuncName}'") - - FuncDef = self.Trans.FunctionDefCache.get(FuncName) - param_names = [] - FuncDef_args = getattr(FuncDef, 'args', None) if FuncDef else None - if FuncDef_args: - param_names = [arg.arg for arg in FuncDef.args.args] - # 回退到全局参数名表(支持跨模块 include 函数) - if not param_names: - global_param_names = getattr(self.Trans, '_global_function_param_names', {}) - param_names = global_param_names.get(FuncName) or [] - - num_params = len(func.function_type.args) - nonlocal_param_count = len(Gen.nonlocal_params.get(FuncName, [])) - eh_msg_out_count = 0 - if num_params > 0 and func.args: - for arg in func.args: - if arg.name in ('__eh_msg_out__', '__eh_code_out__'): - eh_msg_out_count += 1 - regular_param_count = num_params - nonlocal_param_count - eh_msg_out_count - is_variadic = False - func_ft = getattr(func, 'function_type', None) - is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False - - if not is_variadic and len(Node.args) > regular_param_count: - raise Exception(f"调用 {FuncName}() 传入了 {len(Node.args)} 个参数,但函数只需要 {regular_param_count} 个参数") - - # 变参函数:固定形参(含带默认值的)之外才是可变部分。被省略的固定形参 - # 也必须补进调用实参,否则实参个数少于固定形参 → 被调函数读到寄存器/栈 - # 垃圾(VET json.write 缺省 cap 读成指针量级 → pool.alloc 巨量 → OOM 的形态)。 - max_args = max(regular_param_count, len(Node.args)) if is_variadic else regular_param_count - resolved = [None] * max_args - - for i, arg in enumerate(Node.args): - if i < max_args: - arg_var_type = None - if i < num_params: - arg_var_type = func.function_type.args[i] - ArgVal = self.HandleExprLlvm(arg, VarType=arg_var_type) - if ArgVal: - resolved[i] = ArgVal - - for kw in Node.keywords: - if kw.arg is None: - if isinstance(kw.value, ast.Dict): - pass - elif isinstance(kw.value, ast.Call): - pass - continue - ArgVal = self.HandleExprLlvm(kw.value) - if not ArgVal: - continue - kw_name = kw.arg - if kw_name in param_names: - idx = param_names.index(kw_name) - if idx < regular_param_count: - resolved[idx] = ArgVal - - FuncDef_args_defaults = getattr(getattr(FuncDef, 'args', None), 'defaults', []) if FuncDef else [] - # 跨模块调用(被调函数不在本模块 FunctionDefCache:其它 App 模块 / includes - # 模块 / .vpi 声明)时 FuncDef 为 None,本模块没有被调方 AST,AST 默认值不可用。 - # 此时从全局默认参数表(Phase2 预扫描 App 与 includes 源码得到)取值,按 - # "尾部默认参数"语义补齐被省略的实参;缺失该表时保持原有零值填充。 - Global_defaults_vals: list | None = None - if not FuncDef_args_defaults: - Global_defaults_vals = self._LookupGlobalDefaults(FuncName) - if Global_defaults_vals is None and func is not None: - Global_defaults_vals = self._LookupGlobalDefaults(getattr(func, 'name', None)) - Num_defaults: int = len(FuncDef_args_defaults) if FuncDef_args_defaults else ( - len(Global_defaults_vals) if Global_defaults_vals else 0) - if Num_defaults > 0: - Min_args: int = regular_param_count - Num_defaults - for i in range(regular_param_count): - if resolved[i] is not None or i < Min_args: - continue - default_idx = i - Min_args - if default_idx >= Num_defaults: - continue - if FuncDef_args_defaults: - default_val = self.HandleExprLlvm(FuncDef_args_defaults[default_idx]) - else: - default_val = self._ConstantToIrValue(Global_defaults_vals[default_idx], - func.function_type.args[i], Gen) - if default_val: - param_type = func.function_type.args[i] - if default_val.type != param_type: - if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType): - if default_val.type.width < param_type.width: - default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}") - elif default_val.type.width > param_type.width: - default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}") - elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType): - default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}") - resolved[i] = default_val - - if FuncDef and param_names: - min_args = regular_param_count - Num_defaults - provided = len(Node.args) - kw_provided = set() - for kw in Node.keywords: - if kw.arg and kw.arg in param_names: - kw_provided.add(param_names.index(kw.arg)) - for i in range(min(min_args, len(param_names))): - if i >= provided and i not in kw_provided: - raise Exception(f"调用 {FuncName}() 缺少必需参数 '{param_names[i]}',该参数没有默认值") - - CallArgs = [] - for i in range(max_args): - if resolved[i] is not None: - CallArgs.append(resolved[i]) - elif i < regular_param_count: - arg_type = func.function_type.args[i] - if isinstance(arg_type, ir.PointerType): - CallArgs.append(ir.Constant(arg_type, None)) - elif isinstance(arg_type, (ir.IdentifiedStructType, ir.LiteralStructType)): - zero_val = Gen._zero_value_for_type(arg_type) - if zero_val is not None: - CallArgs.append(zero_val) - else: - CallArgs.append(ir.Constant(ir.PointerType(arg_type), None)) - elif isinstance(arg_type, ir.ArrayType): - zero_val = Gen._zero_value_for_type(arg_type) - if zero_val is not None: - CallArgs.append(zero_val) - else: - CallArgs.append(ir.Constant(arg_type, 0)) - else: - CallArgs.append(ir.Constant(arg_type, 0)) - - if FuncName in Gen.nonlocal_params: - for var_name, ptr_type in Gen.nonlocal_params[FuncName]: - if var_name in Gen.variables and Gen.variables[var_name] is not None: - CallArgs.append(Gen.variables[var_name]) - elif var_name in Gen._reg_values: - OldVal = Gen._reg_values[var_name] - var = Gen._allocaEntry(OldVal.type, name=var_name) - Gen._store(OldVal, var) - Gen.variables[var_name] = var - del Gen._reg_values[var_name] - CallArgs.append(var) - - self._append_eh_msg_out_arg(CallArgs, func, Gen) - - if is_variadic: - # 对于可变参数函数,需要手动处理参数类型转换 - adjusted = [] - for i, arg in enumerate(CallArgs): - if i < num_params: - # 固定参数:尝试类型转换 - param_type = func.function_type.args[i] - if arg.type != param_type: - try: - if isinstance(arg.type, ir.IntType) and isinstance(param_type, ir.PointerType): - adjusted.append(self.builder.inttoptr(arg, param_type)) - elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.IntType): - adjusted.append(Gen.builder.ptrtoint(arg, param_type)) - elif isinstance(arg.type, ir.IntType) and arg.type.width < param_type.width: - adjusted.append(Gen.builder.zext(arg, param_type)) - elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.PointerType): - adjusted.append(Gen.builder.bitcast(arg, param_type, name=f"cast_arg_{i}")) - else: - adjusted.append(arg) - except Exception: # 回退:参数类型调整失败时使用原值 - if isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.PointerType): - try: - adjusted.append(Gen.builder.bitcast(arg, param_type, name=f"cast_arg_{i}")) - except Exception: - adjusted.append(arg) - else: - adjusted.append(arg) - else: - adjusted.append(arg) - else: - # 可变参数:应用默认参数提升(float -> double, i32 -> i64) - if isinstance(arg.type, ir.FloatType): - arg = Gen.builder.fpext(arg, ir.DoubleType(), name=f"fpext_vararg_{FuncName}") - elif isinstance(arg.type, ir.IntType) and arg.type.width < 64: - # i1 是布尔值(来自 icmp/fcmp/__contains__),无符号语义,必须 zext - if arg.type.width == 1: - arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_bool_vararg_{FuncName}") - else: - # 有符号整数用 sext,无符号整数用 zext - is_signed = True - arg_node = Node.args[i] if i < len(Node.args) else None - if arg_node is not None: - is_u = Gen._check_node_unsigned(arg_node) - if is_u: - is_signed = False - if is_signed: - arg = Gen.builder.sext(arg, ir.IntType(64), name=f"sext_vararg_{FuncName}") - else: - arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_vararg_{FuncName}") - elif isinstance(arg.type, (ir.LiteralStructType, ir.IdentifiedStructType)): - # 治本修复:变参函数(如 printf)不支持 struct by value 传递。 - # 当 self 等 struct 值被错误地 load 为值(而非指针)传入变参时, - # ABI 不匹配会导致崩溃。修复:将 struct 值通过 alloca + bitcast - # 转换为 i8* 指针传递,确保与 %p / %s 等格式说明符兼容。 - try: - alloca_tmp = Gen.builder.alloca(arg.type, name=f"vararg_struct_{FuncName}_{i}") - Gen.builder.store(arg, alloca_tmp) - arg = Gen.builder.bitcast(alloca_tmp, ir.IntType(8).as_pointer(), name=f"vararg_struct_ptr_{FuncName}_{i}") - except Exception: - # 回退:alloca/store 失败时,尝试 ptrtoint 为 i64 - try: - arg = Gen.builder.ptrtoint(arg, ir.IntType(64), name=f"vararg_struct_i64_{FuncName}_{i}") - except Exception: - pass # 最终回退:保留原值(可能仍会导致崩溃,但至少不阻塞编译) - adjusted.append(arg) - result = Gen.builder.call(func, adjusted, name=f"call_{FuncName}") - else: - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted = Gen._adjust_args(CallArgs, func) - # 兜底:确保所有指针类型参数与函数签名匹配(如 struct* -> i8*) - for i, (arg, param) in enumerate(zip(adjusted, func.args)): - if arg.type != param.type: - if isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): - adjusted[i] = Gen.builder.bitcast(arg, param.type, name=f"cast_arg_{i}") - result = Gen.builder.call(func, adjusted, name=f"call_{FuncName}") - self._check_eh_return(result, FuncName, Gen, called_func=func) - return result - - @staticmethod - def _has_cvtable_decorator(node: ast.ClassDef) -> bool: - """检查 ClassDef 是否有 @t.CVTable 装饰器(或 @CVTable)""" - for decorator in node.decorator_list: - if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'CVTable': - return True - if isinstance(decorator, ast.Call): - inner = decorator.func - if isinstance(inner, ast.Attribute) and getattr(inner.value, 'id', None) == 't' and inner.attr == 'CVTable': - return True - if isinstance(inner, ast.Name) and inner.id == 'CVTable': - return True - if isinstance(decorator, ast.Name) and decorator.id == 'CVTable': - return True - return False - - @staticmethod - def _has_novtable_decorator(node: ast.ClassDef) -> bool: - """检查 ClassDef 是否有 @t.NoVTable 装饰器""" - for decorator in node.decorator_list: - if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'NoVTable': - return True - if isinstance(decorator, ast.Name) and decorator.id == 'NoVTable': - return True - return False - - def _try_register_vtable_ondemand(self, ClassName: str, class_sha1: str, Gen: "LlvmGeneratorMixin") -> None: - """按需 vtable 注册:从 .pyi stub 检查 @t.CVTable 并注册到 vtable 集合。 - - 沿继承链递归收集方法,保证 vtable 布局与 prescan 的 _EmitClassLlvm 一致: - - 基类方法在前(固定索引,保证多态分派正确) - - 子类新增方法在后 - - 子类覆盖的方法放在父类的位置(用子类实现) - - 与 prescan 的 _resolve_class_flags 逻辑一致: - - 类自身有 @t.CVTable → 注册 - - 类自身有 @t.NoVTable → 不注册 - - 类继承 @t.CVTable 基类(且无 @t.NoVTable)→ 注册(自动启用 CVTable) - """ - try: - import_handler = self.Trans.ImportHandler - project_root = import_handler._find_project_root() - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - - # 类级缓存已读取的 .pyi 文件(绝对路径 -> class_map),跨 worker 任务共享。 - # 原实现为函数局部缓存,每次调用重建 → 437 次 open+ast.parse 同一批 pyi。 - def load_pyi(sha1: str) -> dict[str, ast.ClassDef] | None: - pyi_path = os.path.join(project_root, 'temp', f'{sha1}.vpi') - if not os.path.isfile(pyi_path): - # 回退读取旧缓存 .pyi - pyi_path = os.path.join(project_root, 'temp', f'{sha1}.pyi') - if not os.path.isfile(pyi_path): - return None - abs_pyi = os.path.abspath(pyi_path) - cmap = self._vtable_pyi_cache.get(abs_pyi) - if cmap is not None: - return cmap - with open(pyi_path, 'r', encoding='utf-8') as f: - pyi_code = f.read() - tree = ast.parse(pyi_code, pyi_path) - cmap: dict[str, ast.ClassDef] = {} - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.ClassDef): - cmap[node.name] = node - self._vtable_pyi_cache[abs_pyi] = cmap - return cmap - - def collect_class_methods(class_name: str, sha1: str, visited: set[str]) -> tuple[list[str], bool, bool, str | None]: - """递归收集类的方法列表(基类方法在前,子类方法在后)。 - - Returns: - (methods, is_cvtable, is_novtable, parent_name) - """ - # 结果缓存(类级,键 (sha1, class_name)):同一类的 vtable 方法收集 - # 跨 worker 任务重复调用时直接复用(原 437 次顶层调用 + 递归每次都重走)。 - key = (sha1, class_name) - cached = self._vtable_methods_cache.get(key) - if cached is not None: - # 返回新 list:避免调用方(Gen.class_methods)后续修改污染缓存 - return (list(cached[0]), cached[1], cached[2], cached[3]) - if class_name in visited: - return ([], False, False, None) - visited.add(class_name) - - class_map = load_pyi(sha1) - if class_map is None: - return ([], False, False, None) - - target_node = class_map.get(class_name) - if target_node is None: - return ([], False, False, None) - - is_cvtable = self._has_cvtable_decorator(target_node) - is_novtable = self._has_novtable_decorator(target_node) - - # 收集自身方法(方法级修饰符的槽位过滤放在父类状态确定之后: - # 判据 SymbolUtils.IsVtableSlotMethod 需要 VtableByClassLevel) - self_method_pairs: list[tuple] = [] - for item in target_node.body: - if isinstance(item, ast.FunctionDef): - method_name = item.name - if method_name in ('__new__', '__init__', '__before_init__'): - continue - if method_name == '__call__': - self_method_pairs.append((item, f"{class_name}.__call__")) - else: - self_method_pairs.append((item, f"{class_name}.{method_name}")) - - # 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先) - parent_methods: list[str] = [] - parent_name: str | None = None - parent_names: list[str] = [] - parent_method_names_seen: set[str] = set() - # 第一个真实父类的 vtable 状态(None=无父类/未知) - first_parent_vtable: bool | None = None - for base in target_node.bases: - base_name: str | None = None - if hasattr(base, 'id'): - base_name = base.id - elif hasattr(base, 'attr'): - base_name = base.attr - if not base_name or base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): - continue - parent_names.append(base_name) - if parent_name is None: - parent_name = base_name - - # 检查同文件内的基类 - base_node = class_map.get(base_name) - if base_node is not None: - base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, sha1, visited) - # 仅第一个真实父类决定 vtable 继承状态(与 _resolve_class_flags 一致) - if parent_name == base_name: - first_parent_vtable = (base_cvtable and not base_novtable) - for bm in base_methods: - bmn = bm.split('.')[-1] if '.' in bm else bm - if bmn not in parent_method_names_seen: - parent_method_names_seen.add(bmn) - parent_methods.append(bm) - else: - # 跨模块基类 — 查找基类的 sha1 - base_sha1 = struct_sha1_map.get(base_name) - if base_sha1: - base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, base_sha1, visited) - if parent_name == base_name: - first_parent_vtable = (base_cvtable and not base_novtable) - for bm in base_methods: - bmn = bm.split('.')[-1] if '.' in bm else bm - if bmn not in parent_method_names_seen: - parent_method_names_seen.add(bmn) - parent_methods.append(bm) - else: - # 无法找到基类的 sha1 → 父类信息未知,保守启用 vtable - if parent_name == base_name and first_parent_vtable is None: - first_parent_vtable = True - if base_name in Gen._cross_module_novtable: - pass # 父类是 NoVTable,不继承 vtable - - # 继承链统一 vtable(与 _resolve_class_flags 一致): - # 类自身无 @t.CVTable 时,第一个父类有 vtable → 子类启用; - # 父类无 vtable → 子类静态;父类信息未知 → 保守启用。 - if not is_cvtable and not is_novtable and first_parent_vtable is not None: - if first_parent_vtable: - is_cvtable = True - - # VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态 - # (= 类级 CVTable / 继承链判定)——供槽位筛选与"方法级触发"共用。 - _VtByClassLevel: bool = is_cvtable - # 方法级修饰符筛选:`novirtual def`(⇒ @t.NoVTable)不进槽; - # `virtual def`(⇒ @t.CVTable)进槽。判据唯一源 = IsVtableSlotMethod - # (与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径), - # 故跨模块虚表槽序与定义模块逐槽一致。 - self_methods: list[str] = [] - for _nd, _full in self_method_pairs: - if (not is_cvtable) or IsVtableSlotMethod(_nd, _VtByClassLevel): - self_methods.append(_full) - # 方法级 virtual 触发类级 vtable(与 HandlesClassDef._resolve_class_flags / - # TPV _detect_vtable_status 的 `_has_any_func_cvtable` 分支对齐: - # 类级 NoVTable / 无真实父类这两种可判定形态;父类 vtable 已在上面处理) - if not is_cvtable and ClassHasFuncLevelCVTable(target_node): - if is_novtable or parent_name is None: - is_cvtable = True - - # 合并方法列表:基类方法在前,子类方法在后 - # 子类覆盖的方法放在父类的位置; - # 父类独有方法直接用父类方法原名(实际定义类.方法,与 TPV 一致) - child_method_map: dict[str, str] = {} - for m in self_methods: - mn = m.split('.')[-1] if '.' in m else m - child_method_map[mn] = m - - merged: list[str] = [] - covered_names: set[str] = set() - for pm in parent_methods: - mn = pm.split('.')[-1] if '.' in pm else pm - if mn in child_method_map: - merged.append(child_method_map[mn]) - covered_names.add(mn) - else: - # 父类独有方法 — 用父类方法原名(虚分派时函数查找沿 MRO 回退) - merged.append(pm) - - for m in self_methods: - mn = m.split('.')[-1] if '.' in m else m - if mn not in covered_names: - merged.append(m) - - result = (merged, is_cvtable, is_novtable, parent_name) - self._vtable_methods_cache[key] = result - return result - - methods, is_cvtable, is_novtable, parent_name = collect_class_methods(ClassName, class_sha1, set()) - - if is_novtable: - return - if not is_cvtable: - return - - # 注册到 vtable 集合(共享集合) - Gen._cross_module_vtable_classes.add(ClassName) - Gen.class_vtable.add(ClassName) - # 设置方法列表(与 prescan 逻辑一致:基类方法在前) - Gen.class_methods[ClassName] = methods - # 根本修复:注册 vtable 元数据后同步具化 struct body,从源头消除 - # "元数据已注册、本体仍 opaque" 的撕裂状态(配合 _HandleMethodCallLlvm - # 中 _EnsureReceiverStructConcrete 的使用前校验,双保险)。 - import_handler._TryLoadStructFromStub(ClassName, Gen, source_sha1=class_sha1) - # 设置 class_parent / class_parents(用于虚分派时的函数查找回退与 MRO) - if parent_name and ClassName not in Gen.class_parent: - Gen.class_parent[ClassName] = parent_name - parent_names_map = getattr(Gen, 'class_parents', None) - if parent_names_map is None: - parent_names_map = {} - Gen.class_parents = parent_names_map - if parent_name and ClassName not in parent_names_map: - parent_names_map[ClassName] = [parent_name] - except Exception: - pass - - def _try_complete_generic_specialization(self, ClassName: str, MethodName: str, Gen: LlvmGeneratorMixin) -> None: - """泛型特化类方法按需完整特化。 - - 当 ClassName 是泛型特化名(如 ndarray[double])且缓存中只有 - declare_only 版本时,补发方法体。这样方法调用能找到 define, - 避免链接时 undefined reference。 - - 触发场景:numpy 模块内部函数(如 np_sum)调用 a.sum(), - 其中 a 的类型注解 ndarray[t.CDouble] | t.CPtr 在类型解析时 - 只触发了 declare_only=True 特化(创建 struct + __before_init__), - 方法体(fill/delete/dot/sum/...)未被编译。 - """ - # 仅对特化名(含 '[' 且以 ']' 结尾)触发 - if '[' not in ClassName or not ClassName.endswith(']'): - return - # 反推模板类名(如 ndarray[double] → ndarray) - base_name: str = ClassName.split('[')[0] - # 检查是否是已注册的泛型模板 - if not (hasattr(self.Trans.ClassHandler, '_generic_class_templates') and base_name in self.Trans.ClassHandler._generic_class_templates): - return - # 构造 spec_key(与 _specialize_generic_class 一致:ClassName) - spec_key: str = base_name + '<' + ','.join(ClassName[len(base_name) + 1:-1].split('][')) + '>' - spec_cache = getattr(Gen, '_generic_class_specializations', {}) - cached = spec_cache.get(spec_key) - if not isinstance(cached, tuple) or len(cached) < 2: - return - cached_name: str = cached[0] - was_declare_only: bool = cached[1] - # 仅当缓存是 declare_only 版本时才需要补发 - if not was_declare_only: - return - # 检查方法是否已有 define(blocks 非空表示已编译方法体) - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - class_sha1 = struct_sha1_map.get(cached_name) - full_method_name: str = f'{class_sha1}.{cached_name}.{MethodName}' if class_sha1 else f'{cached_name}.{MethodName}' - existing_func = Gen._find_function(full_method_name) - if existing_func is not None: - existing_blocks = getattr(existing_func, 'blocks', []) - if existing_blocks: - return # 方法体已编译 - # 从缓存获取 type_args/type_names - type_info = getattr(Gen, '_generic_class_spec_type_info', {}).get(spec_key) - if type_info is None: - return - cached_type_args, cached_type_names = type_info - # 不预先标记:_specialize_generic_class 内部缓存逻辑会检测 - # cached_declare_only=True 且 declare_only=False,自动补发方法体 - # 并更新缓存为 (spec_name, False)。预注册机制(L117)防止递归。 - # - # 上下文快照:本函数**吞掉**特化异常、让外层方法继续编译,故必须在失败时把 - # 编译器上下文复位——`_EmitFunctionLlvm` 的类名/构建器恢复只在**成功路径** - # 执行(失败路径直接 re-raise),异常穿过多层后 `Gen.builder/func` 与 - # `_CurrentCpythonObjectClass` 会停在内层特化方法的中间状态:外层方法随后 - # 的 `self.X` 被按内层类解析(ExprGenFstr.vp 的 `self.Gen` 被按 - # `list[cfc4ca8da95a30e1.AST*]` 解析 → "属性 'Gen' 在类中找不到"的连带错误), - # 且后续指令会误插进内层函数的块。 - _saved_builder: object = Gen.builder - _saved_func: object = Gen.func - _saved_cpython_class: str | None = getattr(self.Trans, '_CurrentCpythonObjectClass', None) - _saved_class_name: object = getattr(Gen, '_current_class_name', None) - try: - self.Trans.ClassHandler._specialize_generic_class( - base_name, cached_type_args, Gen, - type_names=cached_type_names, declare_only=False - ) - except Exception: - # 失败时恢复 declare_only 标记,避免后续调用跳过补发 - Gen._generic_class_specializations[spec_key] = (cached_name, True) - Gen.builder = _saved_builder - Gen.func = _saved_func - self.Trans._CurrentCpythonObjectClass = _saved_cpython_class - Gen._current_class_name = _saved_class_name - - def _compute_field_byte_offset(self, Gen: LlvmGeneratorMixin, ClassName: str, field_name: str) -> int | None: - """计算字段在 struct 中的字节偏移(按 LLVM 布局含对齐)。 - - 遍历 struct elements 带对齐累加(与 _get_struct_size/_get_align 一致)。 - 有 vtable 的类 struct 首元素为 vtable 指针(i8*),class_members 不含它。 - 返回 None 表示无法计算(struct 未定义 / 字段不存在)。 - """ - # class_members 的 key 是裸类名,而 ClassName 可能带 sha1 前缀 - # (跨模块 ObjVal 类型带前缀,如 "e1ea7eaf63a9c3d1.Generator")。 - # 不剥离则 members 为空 → 找不到字段 → this_adjust 被跳过 → 继承方法 - # self 不调整 → 运行时错位崩溃(register_reexport 写 0x3f 越界即此)。 - lookup_cls: str = ClassName - if len(ClassName) > 17 and ClassName[16] == '.': - lookup_cls = ClassName[17:] - st = Gen.structs.get(ClassName) - # 注意:不能用 `not st` 做真值判断——IdentifiedStructType 定义 __len__, - # opaque(elements is None)时 bool(st) 触发 __len__ 的 assert(AssertionError)。 - # 必须用 `st is None` 身份比较。 - if (st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None) \ - and lookup_cls != ClassName: - st = Gen.structs.get(lookup_cls) - if st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None: - return None - members: list = Gen.class_members.get(lookup_cls, []) - # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源), - # 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。 - base: int = Gen._member_base(ClassName) - idx: int | None = None - for i, (name, _) in enumerate(members): - if name == field_name: - idx = base + i - break - if idx is None or idx >= len(st.elements): - return None - # ============================================================ - # diff_elems 修正(2026-08-13):class_members 与 struct.elements 数量 - # 不一致时对齐(前提:遗漏字段位于目标字段之前)。 - # ============================================================ - diff_elems: int = len(st.elements) - len(members) - if diff_elems > 0: - idx += diff_elems - if idx >= len(st.elements): - return None - # 临时诊断(2026-08-18,确定后移除):捕获 this_adjust 计算输入, - # 定位 TPC 并行 worker 间 class_members/struct 不一致导致的偏移抖动。 - if lookup_cls in ('Generator',): - try: - with open(r'd:\Users\TermiNexus\Desktop\TransPyC\_diag_offset.log', 'a', encoding='utf-8') as _f: - _f.write(f"{lookup_cls}|{field_name}|members={len(members)}|struct={len(st.elements)}" - f"|base={base}|idx={idx}|diff={diff_elems}\n") - except Exception: - pass - offset: int = 0 - for i in range(idx): - et = st.elements[i] - align: int = Gen._get_align(et) - size: int = Gen._get_struct_size(et) - if align > 1: - offset = ((offset + align - 1) // align) * align - offset += size - align = Gen._get_align(st.elements[idx]) - if align > 1: - offset = ((offset + align - 1) // align) * align - return offset - - def _adjust_this_for_inherited_method(self, Gen: LlvmGeneratorMixin, ObjVal: ir.Value, - ClassName: str, method_anc: str | None) -> ir.Value: - """多继承 this 调整(与 TPV _translate_method_call 一致): - - 继承方法体按"定义类自身布局"读 self 字段(如 B.get_b 内 - GEP(self_B, [0, 1]),字节偏移为 B 布局中 b 的 offset 8)。父类字段区 - 在子类中的**字节偏移**可能不同(如 AB={i8*,a@8,b@12} 中 b 偏移 12), - self 需按字节差前移,使父类方法内二次 GEP 恰好落到子类字段。 - - 修复(2026-08-10):字段索引法在"父类与子类元素大小不同"时错位 - (GEP 索引按元素个数计,非字节等价)。改用字节偏移: - delta = 字段在子类中字节偏移 − 字段在父类自身布局中字节偏移 - delta==0(第一父类,字段区前缀对齐)无需调整;delta>0 时按字节 - GEP 到父类字段区在子类中的等效基址,再 bitcast 成父类指针。 - """ - if method_anc is None or method_anc == ClassName: - return ObjVal - anc_lookup: str = method_anc - if len(method_anc) > 17 and method_anc[16] == '.': - anc_lookup = method_anc[17:] - anc_members: list = Gen.class_members.get(anc_lookup, []) - if not anc_members: - return ObjVal - first_field_name: str | None = None - for _fn, _ft in anc_members: - if _fn == '__vtable__': - continue - first_field_name = _fn - break - if first_field_name is None: - return ObjVal - sub_off: int | None = self._compute_field_byte_offset(Gen, ClassName, first_field_name) - if sub_off is None: - # 兄弟 Mixin 场景:调用类(ClassName)不含定义类字段——定义类在 - # 组合类 MRO 中位于调用类之前(如 Combined(BaseA, BaseB),BaseB 内 - # self.helper() 调 BaseA.helper)。此时 BaseB.this 指向组合类中 - # BaseB 字段区起点,BaseA 区在它前面,this 前移 sizeof(定义类 struct)。 - anc_st: Any = Gen.structs.get(method_anc) - if anc_st is None: - anc_st = Gen.structs.get(anc_lookup) - if anc_st is None or not isinstance(anc_st, ir.IdentifiedStructType) or anc_st.elements is None: - return ObjVal - sibling_delta: int = -Gen._get_struct_size(anc_st) - if sibling_delta >= 0: - return ObjVal - obj_i8_s: ir.Value = Gen.builder.bitcast( - ObjVal, ir.PointerType(ir.IntType(8)), name=f"this_sibling_i8_{anc_lookup}") - adj_s: ir.Value = Gen.builder.gep( - obj_i8_s, [ir.Constant(ir.IntType(64), sibling_delta)], - name=f"this_sibling_{anc_lookup}") - return Gen.builder.bitcast(adj_s, ir.PointerType(anc_st), - name=f"this_sibling_cast_{anc_lookup}") - anc_off: int | None = self._compute_field_byte_offset(Gen, method_anc, first_field_name) - if anc_off is None: - return ObjVal - delta: int = sub_off - anc_off - if delta <= 0: - return ObjVal - if method_anc not in Gen.structs: - return ObjVal - obj_i8: ir.Value = Gen.builder.bitcast(ObjVal, ir.PointerType(ir.IntType(8)), - name=f"this_adjust_i8_{method_anc}") - adj: ir.Value = Gen.builder.gep(obj_i8, [ir.Constant(ir.IntType(64), delta)], - name=f"this_adjust_{method_anc}") - return Gen.builder.bitcast(adj, ir.PointerType(Gen.structs[method_anc]), - name=f"this_adjust_cast_{method_anc}") - - def _EnsureClassParentsLoaded(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None: - """跨模块多继承:class_parents 缺失时从 vpi 按需加载继承信息。 - - 调用方(如 HandlesType 委托)引用的类定义在另一模块,其父类 - 信息未随 import 填充。_TryLoadClassMembersFromPyi 读取 .vpi 中 - 的 ClassDef 并经 _EmitExternalClassDeclLlvm 填充 class_parents, - 使 _get_mro_chain 能沿 MRO 回退到定义类(如 TypeGenMixin)。 - """ - if not ClassName: - return - if Gen.class_parents.get(ClassName): - return - # 归一化:ClassName 可能带 sha1 前缀(如 "c9b44d17cd621a75.CallGenLib", - # 跨模块 structs 表键),而 class_parents / ModuleSha1Map / _struct_sha1_map - # 的键是裸类名。不剥离则查不到父类 → 继承方法 MRO 回退失效 → fail-fast - # "Undefined method 'find_func_in_module' in class '...CallGenLib'"。 - lookup_cls: str = ClassName - if len(ClassName) > 17 and ClassName[16] == '.': - lookup_cls = ClassName[17:] - if Gen.class_parents.get(lookup_cls): - return - sha1_cls = Gen.ModuleSha1Map.get(lookup_cls) - if not sha1_cls: - sha1_cls = getattr(Gen, '_struct_sha1_map', {}).get(lookup_cls) - if not sha1_cls: - return - try: - self.Trans.ImportHandler._TryLoadClassMembersFromPyi(lookup_cls, f"{sha1_cls}.stub.ll", Gen) - except Exception: - pass - - def _HandleMethodCallLlvm(self, Node: ast.Call) -> ir.Value: - Gen = self.Trans.LlvmGen - MethodName = Node.func.attr - # 检查是否为 @staticmethod 或 @classmethod,如果是则走特殊路径 - if isinstance(Node.func.value, ast.Name): - VarName = Node.func.value.id - ClassName = Gen.var_struct_class.get(VarName) - if ClassName: - SymKey = f'{ClassName}.{MethodName}' - SymInfo = self.Trans.SymbolTable.lookup(SymKey) - if SymInfo and SymInfo.MetaList: - if FuncMeta.STATIC_METHOD in SymInfo.MetaList: - return self._HandleStaticMethodCallLlvm(Node, ClassName, MethodName) - if FuncMeta.CLASS_METHOD in SymInfo.MetaList: - return self._HandleStaticMethodCallLlvm(Node, ClassName, MethodName) - if isinstance(Node.func.value, ast.Name) and IsTModule(Node.func.value.id, self.Trans.SymbolTable): - result = self.Trans.TSpecialCallHandle._HandleTSpecialCallLlvm(Node) - if result is not None: - return result - ctype_cls = CTypeRegistry.GetClassByName(MethodName) - if ctype_cls is not None: - try: - ctype_inst = ctype_cls() - except Exception: - ctype_inst = None - target_bits = getattr(ctype_inst, 'Size', None) if ctype_inst else None - if target_bits is None: - target_bits = 0 - is_float = getattr(ctype_inst, 'IsSigned', False) is None and target_bits > 0 if ctype_inst else False - IsPtr_cast = len(Node.args) >= 2 and ( - (isinstance(Node.args[1], ast.Attribute) and Node.args[1].attr == 'CPtr' and isinstance(Node.args[1].value, ast.Name) and IsTModule(Node.args[1].value.id, self.Trans.SymbolTable)) or - (isinstance(Node.args[1], ast.Name) and Node.args[1].id == 'CPtr') - ) - if Node.args: - ArgNode = Node.args[0] - if isinstance(ArgNode, ast.Subscript): - SubscriptPtr = self._HandleSubscriptPtrLlvm(ArgNode) - if SubscriptPtr: - if MethodName == 'CPtr' or IsPtr_cast: - return SubscriptPtr - if isinstance(SubscriptPtr.type, ir.PointerType): - val = Gen._load(SubscriptPtr, name="Load_subscript_val") - else: - val = SubscriptPtr - if target_bits > 0 and isinstance(val.type, ir.IntType) and not is_float: - if val.type.width < target_bits: - val = Gen.builder.zext(val, ir.IntType(target_bits), name="zext_cast") - elif val.type.width > target_bits: - val = Gen.builder.trunc(val, ir.IntType(target_bits), name="trunc_cast") - return val - val = self.HandleExprLlvm(ArgNode) - if IsPtr_cast: - if target_bits > 0 and not is_float: - target_ptr_type = ir.PointerType(ir.IntType(target_bits)) - elif is_float and target_bits == 32: - target_ptr_type = ir.PointerType(ir.FloatType()) - elif is_float and target_bits == 64: - target_ptr_type = ir.PointerType(ir.DoubleType()) - else: - target_ptr_type = ir.PointerType(ir.IntType(8)) - if isinstance(val.type, ir.PointerType): - if val.type != target_ptr_type: - val = Gen.builder.bitcast(val, target_ptr_type, name="ptr_cast") - elif isinstance(val.type, ir.IntType): - if val.type.width < 64: - val = Gen.builder.zext(val, ir.IntType(64), name="zext_ptr_cast") - val = Gen.builder.inttoptr(val, target_ptr_type, name="int_to_ptr_cast") - return val - if val and target_bits > 0 and not is_float: - if isinstance(val.type, ir.IntType): - if val.type.width < target_bits: - val = Gen.builder.zext(val, ir.IntType(target_bits), name="zext_cast") - elif val.type.width > target_bits: - val = Gen.builder.trunc(val, ir.IntType(target_bits), name="trunc_cast") - elif isinstance(val.type, ir.PointerType): - val = Gen.builder.ptrtoint(val, ir.IntType(target_bits), name="ptrtoint_cast") - return val - if is_float: - return ir.Constant(ir.FloatType() if target_bits == 32 else ir.DoubleType(), 0.0) - return ir.Constant(ir.IntType(target_bits) if target_bits > 0 else ir.IntType(32), 0) - if self.Trans.SymbolTable.has(MethodName): - SymInfo = self.Trans.SymbolTable[MethodName] - if SymInfo.IsTypedef: - return self._HandleTypedefCastLlvm(Node, MethodName) - if isinstance(Node.func.value, ast.Name) and Node.func.value.id == 'c': - if MethodName == 'Asm': - return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) - if MethodName == 'Addr': - return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node) - elif MethodName == 'Set': - return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node) - elif MethodName == 'Load': - return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node) - elif MethodName == 'Deref': - return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) - elif MethodName == 'DerefAs': - return self._HandleCDerefAsLlvm(Node) - elif MethodName == 'PtrToInt': - return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) - elif MethodName in ('CIf', 'CIfdef', 'CIfndef', 'CElif'): - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError(_REMOVED_CIF_CALL_MSG) - elif MethodName == 'CError': - msg = "compile-time error" - if Node.args: - arg = Node.args[0] - if isinstance(arg, ast.Constant) and isinstance(arg.value, str): - msg = arg.value - else: - try: - val = self.HandleExprLlvm(arg) - if isinstance(val, str): - msg = val - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"处理函数调用失败: {_e}", "Exception") - lineno = getattr(Node, 'lineno', 0) - # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 - _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - raise Exception(f"#error: {msg} (line {lineno})") - elif MethodName in ('CElse', 'CEndif'): - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError(_REMOVED_CIF_CALL_MSG) - elif MethodName in ('CPragma', 'CUndef'): - return ir.Constant(ir.IntType(1), 1) - elif MethodName in ('LLVMIR', 'LInp', 'LOut'): - return None - ObjVal = self.HandleExprLlvm(Node.func.value) - if not ObjVal: - return None - if isinstance(ObjVal.type, (ir.IdentifiedStructType, ir.LiteralStructType)): - if isinstance(Node.func.value, ast.Name): - var_name = Node.func.value.id - if var_name in Gen.variables: - var_ptr = Gen.variables[var_name] - if isinstance(var_ptr.type, ir.PointerType) and isinstance(var_ptr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - ObjVal = var_ptr - else: - tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp") - Gen._store(ObjVal, tmp) - ObjVal = tmp - else: - tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp") - Gen._store(ObjVal, tmp) - ObjVal = tmp - else: - tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp") - Gen._store(ObjVal, tmp) - ObjVal = tmp - if isinstance(Node.func.value, ast.Name): - ModuleName = Node.func.value.id - imported = getattr(self.Trans, '_ImportedModules', set()) - actual_module = self.Trans.SymbolTable.resolve_alias(ModuleName) - if (ModuleName in imported or actual_module in imported) and ModuleName not in Gen.var_struct_class: - mangled_name = Gen._mangle_func_name(MethodName, actual_module) - if Gen._has_function(mangled_name): - func = Gen._get_function(mangled_name) - CallArgs = [] - # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 - _ptypes: list[ir.Type] = func.function_type.args if func is not None else [] - for _ai, arg in enumerate(Node.args): - _pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None - # 不再 `if ArgVal: append`(实参求值为空会被静默丢弃 ⇒ 后续整体 - # 前移一格 ⇒ 由 _adjust_args 报出与真因无关的类型不匹配,见 - # _RequireArgValue 的说明)。 - CallArgs.append(self._RequireArgValue( - self.HandleExprLlvm( - arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)), - mangled_name, _ai + 1, arg, Node)) - self._fill_default_args(CallArgs, func, MethodName) - func_ft = getattr(func, 'function_type', None) - is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False - num_fixed_params = len(func_ft.args) if func_ft else 0 - if is_variadic: - adjusted = [] - for i, arg in enumerate(CallArgs): - if i < num_fixed_params: - param_type = func_ft.args[i] - if arg.type != param_type: - try: - if isinstance(arg.type, ir.IntType) and isinstance(param_type, ir.PointerType): - adjusted.append(self.builder.inttoptr(arg, param_type)) - elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.IntType): - adjusted.append(Gen.builder.ptrtoint(arg, param_type)) - elif isinstance(arg.type, ir.IntType) and arg.type.width < param_type.width: - adjusted.append(Gen.builder.zext(arg, param_type)) - else: - adjusted.append(arg) - except Exception: # 回退:参数类型调整失败时使用原值 - adjusted.append(arg) - else: - adjusted.append(arg) - else: - if isinstance(arg.type, ir.FloatType): - arg = Gen.builder.fpext(arg, ir.DoubleType(), name=f"fpext_vararg_{MethodName}") - elif isinstance(arg.type, ir.IntType) and arg.type.width < 64: - if arg.type.width == 1: - arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_bool_vararg_{MethodName}") - else: - is_signed = True - arg_node = Node.args[i] if i < len(Node.args) else None - if arg_node is not None: - is_u = Gen._check_node_unsigned(arg_node) - if is_u: - is_signed = False - if is_signed: - arg = Gen.builder.sext(arg, ir.IntType(64), name=f"sext_vararg_{MethodName}") - else: - arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_vararg_{MethodName}") - adjusted.append(arg) - result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") - else: - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted = Gen._adjust_args(CallArgs, func) - result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") - return result - if Gen._has_function(MethodName): - _mf = Gen._get_function(MethodName) - if '.' not in _mf.name: - CallArgs = [] - # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 - _ptypes: list[ir.Type] = _mf.function_type.args - for _ai, arg in enumerate(Node.args): - _pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None - # 同 3357:禁止静默丢弃求值为空的实参(会导致后续前移一格) - CallArgs.append(self._RequireArgValue( - self.HandleExprLlvm( - arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)), - MethodName, _ai + 1, arg, Node)) - func = _mf - self._fill_default_args(CallArgs, func, MethodName) - func_ft = getattr(func, 'function_type', None) - is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False - num_fixed_params = len(func_ft.args) if func_ft else 0 - if is_variadic: - adjusted = [] - for i, arg in enumerate(CallArgs): - if i < num_fixed_params: - param_type = func_ft.args[i] - if arg.type != param_type: - try: - if isinstance(arg.type, ir.IntType) and isinstance(param_type, ir.PointerType): - adjusted.append(self.builder.inttoptr(arg, param_type)) - elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.IntType): - adjusted.append(Gen.builder.ptrtoint(arg, param_type)) - elif isinstance(arg.type, ir.IntType) and arg.type.width < param_type.width: - adjusted.append(Gen.builder.zext(arg, param_type)) - else: - adjusted.append(arg) - except Exception: # 回退:参数类型调整失败时使用原值 - adjusted.append(arg) - else: - adjusted.append(arg) - else: - if isinstance(arg.type, ir.FloatType): - arg = Gen.builder.fpext(arg, ir.DoubleType(), name=f"fpext_vararg_{MethodName}") - elif isinstance(arg.type, ir.IntType) and arg.type.width < 64: - if arg.type.width == 1: - arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_bool_vararg_{MethodName}") - else: - is_signed = True - arg_node = Node.args[i] if i < len(Node.args) else None - if arg_node is not None: - is_u = Gen._check_node_unsigned(arg_node) - if is_u: - is_signed = False - if is_signed: - arg = Gen.builder.sext(arg, ir.IntType(64), name=f"sext_vararg_{MethodName}") - else: - arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_vararg_{MethodName}") - adjusted.append(arg) - result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") - else: - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted = Gen._adjust_args(CallArgs, func) - result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") - return result - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) - if not stub_func_type: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(MethodName, Gen) - if not stub_func_type and mangled_name != MethodName: - for sha1 in getattr(Gen, 'ModuleSha1Map', {}).values(): - alt_name = f"{sha1}.{MethodName}" - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(alt_name, Gen) - if stub_func_type: - mangled_name = alt_name - break - if stub_func_type: - func = self.GetOrCreateStubFuncDecl(mangled_name, stub_func_type, Gen) - CallArgs = [] - # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 - _ptypes: list[ir.Type] = func.function_type.args if func is not None else [] - for _ai, arg in enumerate(Node.args): - _pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None - # 同 3357:禁止静默丢弃求值为空的实参(会导致后续前移一格) - CallArgs.append(self._RequireArgValue( - self.HandleExprLlvm( - arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)), - mangled_name, _ai + 1, arg, Node)) - self._fill_default_args(CallArgs, func, mangled_name) - self._append_eh_msg_out_arg(CallArgs, func, Gen) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted = Gen._adjust_args(CallArgs, func) - result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") - self._check_eh_return(result, mangled_name, Gen, called_func=func) - return result - return ir.Constant(ir.IntType(32), 0) - CallArgs = [] - for _ai, arg in enumerate(Node.args): - # 同 3357:禁止静默丢弃求值为空的实参(会导致后续整体前移一格) - CallArgs.append(self._RequireArgValue( - self.HandleExprLlvm(arg), self._ExprText(Node.func), _ai + 1, arg, Node)) - ClassName = None - if isinstance(Node.func.value, ast.Name): - ClassName = Gen.var_struct_class.get(Node.func.value.id) - if not ClassName and isinstance(Node.func.value, ast.Attribute): - # 链式属性访问(如 func.Params.append):通过 _get_var_class 推导泛型特化类名 - ClassName = self.Trans.ExprHandler._get_var_class(Node.func.value, Gen) - if not ClassName: - for CN, ST in Gen.structs.items(): - if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == ST: - ClassName = CN - break - if not ClassName and self._is_char_pointer(ObjVal): - MethodName = Node.func.attr - # FakeDuck: 检查是否有影子结构体 a__meta__ (per-instance 字段存储) - meta_ptr: ir.Value | None = None - if isinstance(Node.func.value, ast.Name): - meta_name = f"{Node.func.value.id}__meta__" - if meta_name in Gen.variables: - meta_ptr = Gen.variables[meta_name] - for CN, ST in Gen.structs.items(): - method_key = f'{CN}.{MethodName}' - if not Gen._has_function(method_key): - continue - # FakeDuck: 不跳过空结构体 (扩展类可能没有数据成员只有方法) - try: - # 如果有影子结构体且该类有字段, 使用影子结构体作为 self (而非 bitcast char*) - if meta_ptr is not None and hasattr(ST, 'elements') and len(ST.elements) > 0: - ObjVal = meta_ptr - elif CN == '_str' and hasattr(ST, 'elements') and len(ST.elements) > 0: - # FakeDuck: 字符串字面量/匿名 str 表达式调用 _str 方法时, - # 创建临时影子结构体 (栈上), 设置 __data__ 和 __mbuddy__ 字段。 - # bitcast i8* → _str* 会导致读取垃圾指针 → ACCESS_VIOLATION - tmp_meta: ir.AllocaInstr = Gen._allocaEntry(ST, name="tmp_str_meta") - _data_off = Gen._get_member_offset('__data__', '_str') - if _data_off is not None: - _data_ptr = Gen.builder.gep(tmp_meta, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _data_off)], name="tmp_str_data") - Gen._store(ObjVal, _data_ptr) - _mpool_off = Gen._get_member_offset('__mbuddy__', '_str') - if _mpool_off is not None: - _mpool_ptr = Gen.builder.gep(tmp_meta, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _mpool_off)], name="tmp_str_mbuddy") - Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), _mpool_ptr) - # 从 with 上下文自动注入 requires 字段 (如 __mbuddy__) - self._InjectRequiresFields(tmp_meta, '_str', Gen) - ObjVal = tmp_meta - else: - casted = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") - ObjVal = casted - ClassName = CN - break - except Exception: # 回退:bitcast 失败时尝试下一个类 - continue - if not ClassName and isinstance(Node.func.value, ast.Call): - if isinstance(Node.func.value.func, ast.Attribute): - InnerAttrName = Node.func.value.func.attr - for CN in Gen.structs: - if CN.endswith(f'__{InnerAttrName}') or f'__{InnerAttrName}' in CN: - continue - method_name = f'{CN}__{InnerAttrName}' - if Gen._has_function(method_name): - func = Gen._get_function(method_name) - ret_type = func.fnty.return_type - if isinstance(ret_type, ir.PointerType): - for CN2, ST in Gen.structs.items(): - if ret_type.pointee == ST: - ClassName = CN2 - break - elif isinstance(ret_type, ir.IdentifiedStructType): - for CN2, ST in Gen.structs.items(): - if ret_type == ST: - ClassName = CN2 - break - if ClassName: - break - if not ClassName: - return ir.Constant(ir.IntType(32), 0) - # REnum.kind() 编译期转 __tag 读取(位置 0,i32): - # REnum 无 kind 方法(源码无、编译器不生成),此前 .kind() 被"兄弟 Mixin - # 兜底"(L3203-3240)误绑到 AST.kind(this 前移 sizeof=-48,语义错误)。 - # 此处把 kind() 直接翻译为 bitcast 到 i32* + load —— 零函数调用, - # 返回变体 tag,与 match 读 __tag 语义一致。 - if MethodName == 'kind': - _renum_nm: str = ClassName[17:] if (len(ClassName) > 17 and ClassName[16] == '.') else ClassName - if '.' in _renum_nm: # 模块限定名 "llvmlite.LLVMType" → 裸名 - _renum_nm = _renum_nm.rsplit('.', 1)[1] - _renum_info: Any = self.Trans.SymbolTable.lookup(_renum_nm) if _renum_nm else None - if _renum_info is not None and getattr(_renum_info, 'IsRenum', False): - if isinstance(ObjVal.type, ir.PointerType): - _kind_ptr: ir.Value = Gen.builder.bitcast( - ObjVal, ir.PointerType(ir.IntType(32)), name="renum_kind_ptr") - return Gen._load(_kind_ptr, name="renum_kind") - # 泛型特化定位:当 ClassName 是 opaque 基础名(如 'ndarray'/'list',不含 '[') - # 时,按【接收者 LLVM 值类型】反查唯一的具体特化,而不再遍历 Gen.structs 按 - # 迭代顺序任选一个 'ClassName[' 开头的特化——任选会命中 list[int] 之类元素 - # 类型错误的特化,方法返回 i32 被 inttoptr 当指针 → 运行期崩溃。 - if '[' not in ClassName: - SpecClass: str | None = None - # 1) 以接收者 LLVM 值类型的 pointee 结构体反查真实类名(唯一解)。 - _recv_found: tuple[str, ir.IdentifiedStructType] | None = None - _recv_type: Any = getattr(ObjVal, 'type', None) - if isinstance(_recv_type, ir.PointerType): - _recv_found = Gen.find_struct_by_pointee(_recv_type.pointee) - if _recv_found is not None and '[' in _recv_found[0]: - SpecClass = _recv_found[0] - # 2) 接收者实型无法唯一确定具体特化(裸泛型基类 / 反查不到结构体): - # 仅当「基名匹配 + 该类型下存在目标方法」的特化唯一时才采用;存在 - # 多个候选 ⇒ fail-fast(禁止按 Gen.structs 顺序任选)。0 个候选表示 - # 该基名下没有任何带此方法的特化(普通非泛型类方法调用路径),保持 - # ClassName 不变、交由后续常规分派。 - if SpecClass is None: - _base_src: str = _recv_found[0] if _recv_found is not None else ClassName - _base_short: str = Gen._extract_short_name(_base_src) - _cands: list[str] = [] - for CN in Gen.structs.keys(): - if '[' not in CN or CN.split('.')[-1].split('[')[0] != _base_short: - continue - struct_sha1_tmp = getattr(Gen, '_struct_sha1_map', {}).get(CN) - if struct_sha1_tmp and Gen._has_function(f'{struct_sha1_tmp}.{CN}.{MethodName}'): - _cands.append(CN) - elif Gen._has_function(f'{CN}.{MethodName}'): - _cands.append(CN) - if len(_cands) == 1: - SpecClass = _cands[0] - elif len(_cands) > 1: - raise Exception( - f"方法调用 '{MethodName}' 的接收者无法唯一确定泛型特化:" - f"基名 '{_base_short}' 下存在多个带该方法的结构体 {_cands}," - f"禁止按 Gen.structs 顺序任选(会分派到错误元素类型)。") - if SpecClass: - ClassName = SpecClass - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - # 归一化:ClassName 可能带 sha1 前缀(跨模块 structs 表键,如 - # "940e5294667daf5a.ExprGenAttr"),其前 16 位即模块 sha1,直接用作 - # class_sha1 并免拼接(否则 struct_sha1_map.get(带前缀名)=None → class_sha1 - # 为空 → 下方 stub 查找路径被跳过 → fail-fast "Undefined method - # 'get_attribute_ptr' in class '...ExprGenAttr'")。裸类名则从映射表取 sha1。 - cls_has_prefix: bool = len(ClassName) > 17 and ClassName[16] == '.' - if cls_has_prefix: - class_sha1 = ClassName[:16] - FullMethodName = f'{ClassName}.{MethodName}' - else: - class_sha1 = struct_sha1_map.get(ClassName) - if class_sha1: - FullMethodName = f'{class_sha1}.{ClassName}.{MethodName}' - else: - FullMethodName = f'{ClassName}.{MethodName}' - - # 泛型特化类方法按需完整特化:当 ClassName 是特化名(如 ndarray[double]) - # 且缓存中只有 declare_only 版本时,补发方法体。否则方法调用会查找 - # stub declare(无 define),链接时报 undefined reference。 - self._try_complete_generic_specialization(ClassName, MethodName, Gen) - - # 提前查找函数以获取参数信息 - func_for_kw = Gen._find_function(FullMethodName) - if not func_for_kw: - func_for_kw = Gen._find_function(f'{FullMethodName}__') - if not func_for_kw and class_sha1: - func_for_kw = Gen._find_function(f'{ClassName}.{MethodName}') - - # 处理关键字参数: 先用 _fill_default_args 填充默认值,再用 keyword args 覆盖 - if Node.keywords and func_for_kw: - # 优先从全局参数名表获取 Python 参数名(支持跨模块 include 函数) - global_param_names = getattr(self.Trans, '_global_function_param_names', {}) - param_names = global_param_names.get(FullMethodName) - if not param_names: - param_names = global_param_names.get(f'{ClassName}.{MethodName}') - if not param_names: - # 回退到 LLVM 函数参数名(仅当参数名不是自动生成的数字名时有效) - llvm_names = [arg.name for arg in func_for_kw.args] if func_for_kw.args else [] - # 检测是否为 LLVM 自动生成的数字名(如 "0", "1", "2") - if llvm_names and not all(n.isdigit() for n in llvm_names): - param_names = llvm_names - if param_names: - # 构造临时 call_args (含 self) 调用 _fill_default_args 填充默认值 - temp_call_args = [ObjVal] + list(CallArgs) - self._fill_default_args(temp_call_args, func_for_kw, FullMethodName) - # 提取回非 self 参数 (现在已填充默认值) - CallArgs.clear() - CallArgs.extend(temp_call_args[1:]) - # 用 keyword args 覆盖对应位置 - for kw in Node.keywords: - if kw.arg is None: - continue - ArgVal = self.HandleExprLlvm(kw.value) - if not ArgVal: - continue - kw_name = kw.arg - if kw_name in param_names: - idx = param_names.index(kw_name) - # idx 0 是 self, 所以 CallArgs 中的偏移是 idx-1 - target_idx = idx - 1 - if 0 <= target_idx < len(CallArgs): - CallArgs[target_idx] = ArgVal - - # 按需 vtable 注册:当 ClassName 不在 vtable 集合中但有 class_sha1 时, - # 从 .pyi stub 检查 @t.CVTable 装饰器并注册。 - # 修复场景:list[str].__new__ 中 pool.alloc(48) 编译时, - # MemManager 可能因导入时序问题未注册到当前 Gen 的 vtable 集合, - # 导致生成 call_(直接调用)而非 vcall_(虚分派),运行时段错误。 - if (ClassName not in Gen.class_vtable - and ClassName not in Gen._cross_module_vtable_classes - and class_sha1): - self._try_register_vtable_ondemand(ClassName, class_sha1, Gen) - # 跨模块多继承:确保 class_parents 可用(MRO 回退需要), - # 如 HandlesType 委托调用 Generator 的方法(定义类在 TypeGenMixin)。 - self._EnsureClassParentsLoaded(ClassName, Gen) - - if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods: - # 根本修复:vtable 分派要求接收者 struct 已具化。元数据(vtable 集合 / - # class_methods)与 struct body 可能因并行 include 翻译时序撕裂—— - # _try_register_vtable_ondemand 只注册元数据不具化 body,若此时 struct - # 仍 opaque,下方 GEP [0,0] 取 vtable 指针会抛 llvmlite 裸 TypeError - # (典型:list[AST|t.CPtr].append 中 self.__pool__.alloc(...), - # __pool__ 类型 memhub.MemManager|t.CPtr,MemManager 已进 vtable 集合 - # 但 struct body 未从 stub 具化)。 - # 此处先主动具化(stub + class_members 双路径),仍失败则 fail-fast - # 暴露上游 stub 加载时序问题,而非静默崩溃。 - ObjVal = self._EnsureReceiverStructConcrete(ClassName, class_sha1, ObjVal, Gen) - methods = Gen.class_methods[ClassName] - method_idx = -1 - for mi, mn in enumerate(methods): - if mn == FullMethodName or mn == MethodName or mn.endswith(f'.{MethodName}'): - method_idx = mi - break - if method_idx >= 0: - func = Gen._find_function(FullMethodName) - # 方法实际定义类(多继承 this 调整用,None=本类或未知) - method_anc: str | None = None - # 仅当无函数或只有 stub 声明(无函数体)时,沿 MRO 祖先链回退, - # 否则 stub 会挡住回退导致 this 调整失效(与直接调用路径一致) - if not func or len(list(func.blocks)) == 0: - func = Gen._find_function(f'{FullMethodName}__') - if not func or len(list(func.blocks)) == 0: - # 多继承:沿 MRO 祖先链查找定义类方法(先声明父类优先) - for anc in Gen._get_mro_chain(ClassName)[1:]: - parent_full = f'{anc}.{MethodName}' - func = Gen._find_function(parent_full) - if not func: - func = Gen._find_function(f'{parent_full}__') - if func: - method_anc = anc - break - # 修复:vtable 分派需要 func 的函数类型。当 func=None 时(如 __new__ - # 编译较早,MemManager.alloc 的 declare 尚未加入 Gen.functions), - # 从 stub 获取函数类型并创建 declare,确保 vtable 分派不被跳过 - # 回退到直接调用(直接调用基类 MemManager.alloc 返回 NULL → 段错误)。 - if not func and class_sha1: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(FullMethodName, Gen) - if stub_func_type: - func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) - if not func and class_sha1: - plain_name = f'{ClassName}.{MethodName}' - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen) - if stub_func_type: - func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) - if not func: - plain_name = f'{ClassName}.{MethodName}' - func = Gen._find_function(plain_name) - if func: - # 多继承 this 调整:继承方法定义在无 vtable 父类且字段区偏移 > 0 时, - # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 - # receiver 为 i8*/泛型指针(CVTable 抽象基类如 ast.AST 无独立 - # struct 类型、联合字段存储为 i8*)时 vtable 槽恒在对象偏移 0: - # bitcast 到 ClassName 的 struct(无则建 opaque 占位)再 GEP, - # 否则 gep(i8) 报 "'IntType' object has no attribute 'gep'"。 - if (isinstance(ObjVal.type, ir.PointerType) - and not isinstance(ObjVal.type.pointee, - (ir.IdentifiedStructType, ir.LiteralStructType))): - _vt_st_rc: ir.Type = Gen.structs.get(ClassName) - if _vt_st_rc is None: - _vt_st_rc = Gen._get_or_create_struct(ClassName) - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(_vt_st_rc), - name=f"vt_cast_{ClassName}") - self_arg = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc) - VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}") - VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}") - VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) - VtableTyped = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}") - MethodPtrAddr = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_ptr_addr_{MethodName}") - MethodPtrI8 = Gen._load(MethodPtrAddr, name=f"vmethod_ptr_{MethodName}") - FuncPtrType = ir.PointerType(func.function_type) - MethodPtrTyped = Gen.builder.bitcast(MethodPtrI8, FuncPtrType, name=f"vmethod_typed_{MethodName}") - call_args = [self_arg] + CallArgs - self._fill_default_args(call_args, func, FullMethodName) - self._append_eh_msg_out_arg(call_args, func, Gen) - adjusted = Gen._adjust_args(call_args, func) - result = Gen.builder.call(MethodPtrTyped, adjusted, name=f"vcall_{FullMethodName}") - self._check_eh_return(result, FullMethodName, Gen, called_func=func) - return result - func = Gen._find_function(FullMethodName) - # 方法实际定义类(多继承 this 调整用,None=本类或未知) - method_anc: str | None = None - if not func: - func = Gen._find_function(f'{FullMethodName}__') - if not func and class_sha1: - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(FullMethodName, Gen) - if stub_func_type: - func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) - if not func and class_sha1: - plain_name = f'{ClassName}.{MethodName}' - stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen) - if stub_func_type: - func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) - # 最终回退:尝试无 sha1 前缀的函数名(与 func_for_kw 的双路径回退一致) - if not func: - plain_name = f'{ClassName}.{MethodName}' - func = Gen._find_function(plain_name) - # 若 FullMethodName 只有 stub 声明(无函数体),也走 MRO 回退找定义类方法 - if not func or len(list(func.blocks)) == 0: - # 多继承:沿 MRO 祖先链查找定义类方法(先声明父类优先), - # 继承方法用"定义类.方法"(如 B.get_b)直接调用,不再依赖子类包装器。 - for anc in Gen._get_mro_chain(ClassName)[1:]: - anc_full = f'{anc}.{MethodName}' - func = Gen._find_function(anc_full) - if not func: - func = Gen._find_function(f'{anc_full}__') - if func: - method_anc = anc - break - if not func or len(list(func.blocks)) == 0: - # 兄弟 Mixin 兜底:仅当"调用类自身未声明该方法"时启用。 - # 判断依据 class_methods[ClassName]: - # * BaseB.caller 调 self.helper()——BaseB 无 helper(源码未定义) - # → 跨 Mixin → 在其它注册类中找定义类(BaseA.helper)✓ - # * JsonValue.__getitem__ 源码定义存在但 stub 未命中(如 TPV 自身 - # 编译 Config.vp 时跨模块 stub 注册/时序问题)→ 不兜底, - # fail-fast(Undefined method)暴露上游 stub 问题——避免误绑 - # 无关类 HashTable.__getitem__(this 前移 sizeof(HashTable)=-48 - # → Load_project_config 读 -1 崩溃,1.LOG 根因;3fd875d 只 - # 过滤 list 特化,普通类仍被误绑)。 - # 注:不能用 MRO/组合类判断(Combined 定义在 BaseB 之后,BaseB.caller - # 翻译时组合类未注册,MRO 查询不可靠——780cd4a 引入 NoVTable 回归)。 - _self_has_method: bool = False - _cm_map = getattr(Gen, 'class_methods', None) - if isinstance(_cm_map, dict): - _self_methods = _cm_map.get(ClassName) - if _self_methods: - for _sm in _self_methods: - if _sm == MethodName or _sm.endswith(f'.{MethodName}'): - _self_has_method = True - break - if not _self_has_method: - for _cand_cls, _cand_methods in list(Gen.class_methods.items()): - if _cand_cls == ClassName: - continue - if '[' in _cand_cls: - continue - if not any(_cm.endswith(f'.{MethodName}') or _cm == MethodName for _cm in _cand_methods): - continue - _cand_full = f'{_cand_cls}.{MethodName}' - _cand_func = Gen._find_function(_cand_full) - if not _cand_func: - _cand_func = Gen._find_function(f'{_cand_full}__') - if _cand_func: - func = _cand_func - method_anc = _cand_cls - break - if func: - # 多继承 this 调整:继承方法定义在无 vtable 父类且字段区偏移 > 0 时, - # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 - self_arg = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc) - call_args = [self_arg] + CallArgs - self._fill_default_args(call_args, func, FullMethodName) - self._append_eh_msg_out_arg(call_args, func, Gen) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted = Gen._adjust_args(call_args, func) - result = Gen.builder.call(func, adjusted, name=f"call_{FullMethodName}") - self._check_eh_return(result, FullMethodName, Gen, called_func=func) - return result - i8ptr = ir.PointerType(ir.IntType(8)) - member_idx = -1 - if ClassName in Gen.class_members: - for mi, (mn, mt) in enumerate(Gen.class_members[ClassName]): - if mn == MethodName: - member_idx = mi - break - if member_idx >= 0 and isinstance(ObjVal.type, ir.PointerType): - # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 - base = Gen._member_base(ClassName) - member_ptr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + member_idx)], name=f"funcptr_{MethodName}_ptr") - raw_ptr = Gen._load(member_ptr, name=f"funcptr_{MethodName}_raw") - func_ptr_type = ir.PointerType(ir.FunctionType(ir.IntType(32), [i8ptr] + [a.type for a in CallArgs])) - typed_ptr = Gen.builder.bitcast(raw_ptr, func_ptr_type, name=f"funcptr_{MethodName}_typed") - obj_as_i8 = Gen.builder.bitcast(ObjVal, i8ptr, name=f"funcptr_self_{ClassName}") - call_args = [obj_as_i8] + CallArgs - result = Gen.builder.call(typed_ptr, call_args, name=f"call_funcptr_{ClassName}.{MethodName}") - return result - if class_sha1 and ClassName in Gen.structs: - struct_type = Gen.structs[ClassName] - self_ptr_type = ir.PointerType(struct_type) - # 修复 Bug 4: 从符号表获取完整参数列表(含默认参数)和正确返回类型, - # 而非仅使用 CallArgs(漏掉默认参数)和硬编码 i32 返回类型。 - # 这避免了 main.py 中 declare Logger.info(...,i8*) (2参数) - # 与 VLogger 中 define Logger.info(...,i8*,i8*) (3参数)的签名冲突。 - ret_type: Any = ir.IntType(32) - param_types: list[Any] = [self_ptr_type] + [a.type for a in CallArgs] - SymInfoFallback = self.LookupFunctionSymbol(MethodName, module_path=ClassName) - if SymInfoFallback and SymInfoFallback.IsFunction: - sig = self.BuildLLVMFuncTypeFromSig(SymInfoFallback, Gen) - if sig is not None: - sig_ret, sig_params, _ = sig - ret_type = sig_ret - is_static_fb = FuncMeta.STATIC_METHOD in SymInfoFallback.MetaList - if not is_static_fb: - sig_params = [self_ptr_type] + sig_params - param_types = sig_params - func_type = ir.FunctionType(ret_type, param_types) - # 复用已存在的声明(vpi 合并/LoadDepVpiIntoModule 可能已注册同名 declare), - # 避免 llvmlite DuplicatedNameError(如 gen.__init__ / ImportsHandle.HandleImport) - func = Gen._find_function(FullMethodName) - if func is None: - # 走唯一幂等入口(module.globals 同名函数复用 / 类型冲突 fail-fast), - # 不再裸建 ir.Function —— Guard 只看 Gen.functions 时漏掉 - # module.globals 已有声明会抛 llvmlite DuplicatedNameError。 - func = Gen._CreateOrReuseFuncDecl(FullMethodName, func_type) - if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == struct_type: - call_self = ObjVal - else: - call_self = Gen.builder.bitcast(ObjVal, self_ptr_type, name=f"method_self_{ClassName}") - call_args_fb = [call_self] + CallArgs - self._fill_default_args(call_args_fb, func, FullMethodName) - self._append_eh_msg_out_arg(call_args_fb, func, Gen) - adjusted = Gen._adjust_args(call_args_fb, func) - result = Gen.builder.call(func, adjusted, name=f"call_{FullMethodName}") - return result - raise Exception(f"Undefined method '{MethodName}' in class '{ClassName}' (no function declaration found for '{FullMethodName}')") - - def _EnsureReceiverStructConcrete(self, ClassName: str, class_sha1: str | None, ObjVal: Any, Gen: "LlvmGeneratorMixin") -> Any: - """根本修复:vtable 分派前保证接收者 struct 已具化(非 opaque)。 - - 背景:跨模块类的 vtable 元数据(class_vtable / _cross_module_vtable_classes / - class_methods)由 _precollect_vtable_state 预扫描或 _try_register_vtable_ondemand - 按需注册,而 struct body 依赖 _TryLoadStructFromStub 从 stub.ll 加载。 - 二者时序可能撕裂:body 仍 opaque 就对 struct 做 GEP [0,0] 取 vtable 指针, - llvmlite 在 types.py 的 elements[i] 处抛裸 TypeError('NoneType' object is - not subscriptable)。 - - 本方法: - 1. 先走 stub.ll 补 body(source_sha1 精确匹配优先) - 2. 再补 class_members(供 _generate_structs 用成员构造 body 的备选路径) - 3. 若 Gen.structs 中已具化版本与 ObjVal 指向的 opaque 占位对象不同, - bitcast 替换 ObjVal 到具体版本 - 4. 仍 opaque → fail-fast 报带源码上下文的编译错误(遵循"不兜底"原则, - 暴露上游 stub 加载/时序问题而非让 llvmlite 裸崩) - - Returns: - ObjVal 本身或 bitcast 到具体 struct 版本后的新值。 - """ - if not isinstance(ObjVal.type, ir.PointerType): - return ObjVal - pointee: ir.Type = ObjVal.type.pointee - if not isinstance(pointee, ir.IdentifiedStructType) or not pointee.is_opaque: - return ObjVal - - import_handler: Any = self.Trans.ImportHandler - # 1) stub.ll 补 body(source_sha1 精确匹配优先) - if class_sha1: - import_handler._TryLoadStructFromStub(ClassName, Gen, source_sha1=class_sha1) - else: - import_handler._TryLoadStructFromStub(ClassName, Gen) - # 2) 补 class_members(备选具化路径) - stub_basename: str = f'{class_sha1}.stub.ll' if class_sha1 else f'{ClassName}.stub.ll' - import_handler._TryLoadClassMembersFromPyi(ClassName, stub_basename, Gen) - - # 3) 从 Gen.structs 找已具化版本(短名优先,再按 sha1 全名) - concrete_st: ir.IdentifiedStructType | None = None - _st_short: Any = Gen.structs.get(ClassName) - if isinstance(_st_short, ir.IdentifiedStructType) and not _st_short.is_opaque: - concrete_st = _st_short - if concrete_st is None and class_sha1: - _st_full: Any = Gen.structs.get(f'{class_sha1}.{ClassName}') - if isinstance(_st_full, ir.IdentifiedStructType) and not _st_full.is_opaque: - concrete_st = _st_full - if concrete_st is None: - # 4) 仍 opaque → fail-fast(暴露上游 stub 加载/时序问题) - raise Exception( - f"vtable 分派失败: 类 '{ClassName}' 已注册虚表(class_vtable/class_methods)" - f"但 struct 类型无法从 stub 具化(仍为 opaque){Gen._get_node_info()}" - ) - if concrete_st is pointee: - return ObjVal - # ObjVal 指向旧 opaque 占位对象:bitcast 到具体版本 - return Gen.builder.bitcast(ObjVal, ir.PointerType(concrete_st), name=f"concrete_self_{ClassName}") - - _C_LIB_FUNCS = { - 'malloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.IntType(64)]), - 'calloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.IntType(64), ir.IntType(64)]), - 'realloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'free': lambda: (ir.VoidType(), [ir.PointerType(ir.IntType(8))]), - 'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), - 'strlength': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), - 'memset': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]), - 'memcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'memmove': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'memcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'strcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), - 'strncmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'strcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), - 'strncpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'strcat': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), - 'puts': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), - 'atoi': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), - 'atol': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), - 'abs': lambda: (ir.IntType(32), [ir.IntType(32)]), - 'labs': lambda: (ir.IntType(64), [ir.IntType(64)]), - 'system': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), - } - - def _try_declare_c_lib_func(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.Function | None: - if func_name not in self._C_LIB_FUNCS: - return None - ret_type, param_types = self._C_LIB_FUNCS[func_name]() - func_type = ir.FunctionType(ret_type, param_types) - # C 库声明名固定(malloc/strlen/...),同一模块内多次调用必须复用同一符号: - # 裸建会在第二次抛 llvmlite DuplicatedNameError。 - return Gen._CreateOrReuseFuncDecl(func_name, func_type) - +from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType +from lib.vast.ExprParser import NEW_SRC_MEMGR + + +# len() 参数形态不支持时的共享提示语(与 ViperC App/lib/core/Gen/Call/ +# CallGenDispatch.vp 的 len() 分支同口径,措辞逐字一致,便于用户对照两侧报文)。 +_LEN_SUPPORTED_HINT: str = ( + "len() 只支持定长数组/数组指针(t.CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;" + "非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度," + "或改用 t.CPtr[元素] 并显式维护元素计数" +) + +# 已删除的用户可见 CIf 族(c.CIf / c.CIfdef / c.CIfndef / c.CElif / c.CElse / c.CEndif): +# 用户再写即在调用点 FailFast,指向原生 cif/celif/celse 与 defined(NAME)(英文报文, +# 与 ViperC CallGenDispatch.vp 同口径)。内部标记 c.@CIf 族不受影响。 +_REMOVED_CIF_CALL_MSG: str = ( + "c.CIf / c.CIfdef / c.CIfndef / c.CElif / c.CElse / c.CEndif were removed; use the cif / " + "celif / celse statements together with the defined(NAME) predicate instead " + "(e.g. `cif defined(NAME):` ... `celif ...:` ... `celse:` ...)") + + +# PROF 热点:_EmitLLVMIRInstruction 对每条 IR 模板都要做多次 re.match / re.split, +# 未预编译的模块级入口每次都要「模式串哈希 → re 缓存查找 → 未命中就重新编译」, +# 512 条缓存被打穿后开销巨大。这些模式全是字面量 → 提升为模块级预编译常量。 +# 模式串与调用点处的字面量逐字一致,语义不变。 +# _EmitLLVMIRInstruction 用:剥离 IR 模板前置的结果赋值 `%__OPn__ = ` +_RE_OP_ASSIGN: re.Pattern[str] = re.compile(r'%__OP(\d+)__\s*=\s*(.+)') +# _EmitLLVMIRInstruction 用:整数比较指令 icmp +_RE_ICMP: re.Pattern[str] = re.compile(r'icmp\s+(\w+)\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:浮点比较指令 fcmp +_RE_FCMP: re.Pattern[str] = re.compile(r'fcmp\s+([\w\s]+?)\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:select 指令 +_RE_SELECT: re.Pattern[str] = re.compile(r'select\s+i1\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:Load 指令 +_RE_LOAD: re.Pattern[str] = re.compile(r'Load\s+(\w+),\s*(\w+)\s+%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:store 指令 +_RE_STORE: re.Pattern[str] = re.compile(r'store\s+(\w+)\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:getelementptr 指令 +_RE_GEP: re.Pattern[str] = re.compile(r'getelementptr\s+(?:inbounds\s+)?(\w+),\s*(\w+)\s+%__OP(\d+)__,\s*(.+)') +# _EmitLLVMIRInstruction 用:单个 %__OPn__ 操作数占位引用 +_RE_OP_REF: re.Pattern[str] = re.compile(r'%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:取负形式 sub 指令 +_RE_NEG: re.Pattern[str] = re.compile(r'sub\s+(\w+)\s+(\d+),\s*%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:按位取反形式 xor 指令 +_RE_NOT: re.Pattern[str] = re.compile(r'xor\s+(\w+)\s+(-1|\d+),\s*%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:call 指令 +_RE_CALL: re.Pattern[str] = re.compile(r'call\s+(\w+)\s+@([\w.]+)\((.+)\)') +# _EmitLLVMIRInstruction 用:调用参数中的「类型 %__OPn__」片段 +_RE_TYPED_OP: re.Pattern[str] = re.compile(r'(\w+\*?)\s+%__OP(\d+)__') +# _EmitLLVMIRInstruction 用:调用参数中的「类型 常量」片段 +_RE_CONST: re.Pattern[str] = re.compile(r'(\w+)\s+(-?\d+)') +# _EmitLLVMIRInstruction 用:以逗号切分操作数列表(逗号 + 任意空白) +_RE_COMMA_WS: re.Pattern[str] = re.compile(r',\s*') + + +def _safe_ptr_type(ty: ir.Type) -> ir.Type: + """LLVM IR 中不允许 void* 指针,Ptr(VoidType) 统一替换为 i8*(对应 C 的 void* 语义)""" + if isinstance(ty, ir.PointerType): + cur: ir.Type = ty + while isinstance(cur, ir.PointerType): + if isinstance(cur.pointee, ir.VoidType): + return ir.IntType(8).as_pointer() + cur = cur.pointee + return ty + + +def _BinOpContainsCPtr(node: ast.AST) -> bool: + """递归检查 BinOp(BitOr) 联合类型中是否含 t.CPtr(指针标记)""" + if isinstance(node, ast.Attribute): + return node.attr == 'CPtr' + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + return _BinOpContainsCPtr(node.left) or _BinOpContainsCPtr(node.right) + return False + + +class ExprCallHandle(BaseHandle): + # vtable on-demand 缓存(类级,跨 worker 任务共享,进程内单线程无竞争)。 + # 原实现 pyi_cache 是 _try_register_vtable_ondemand 函数局部变量,每次调用都 + # 重建 → 437 次调用 = 437 次 open+ast.parse 同一批 pyi(prof 38.5s)。 + # vpi 以 sha1 命名、单次编译内只增不减,路径不变则内容不可变,进程内缓存安全。 + # _vtable_pyi_cache:pyi 文件绝对路径 → {类名: ClassDef 节点} + # _vtable_methods_cache:(sha1, class_name) → (methods, is_cvtable, is_novtable, parent_name) + _vtable_pyi_cache: dict[str, dict[str, ast.ClassDef]] = {} + _vtable_methods_cache: dict[tuple[str, str], tuple[list[str], bool, bool, str | None]] = {} + def _append_eh_msg_out_arg(self, CallArgs: list[ir.Value], func: ir.Function | None, Gen: LlvmGeneratorMixin) -> None: + if not func: + return + func_args: list[ir.Type] = func.function_type.args + n_user_args: int = len(CallArgs) + has_eh_msg: bool = False + has_eh_code: bool = False + if len(func_args) > n_user_args: + last_type: ir.Type = func_args[-1] + if (isinstance(last_type, ir.PointerType) + and isinstance(last_type.pointee, ir.IntType) + and last_type.pointee.width == 32): + has_eh_code = True + if len(func_args) > n_user_args + (1 if has_eh_code else 0): + check_idx: int = len(func_args) - 1 - (1 if has_eh_code else 0) + check_type: ir.Type = func_args[check_idx] + if (isinstance(check_type, ir.PointerType) + and isinstance(check_type.pointee, ir.PointerType) + and isinstance(check_type.pointee.pointee, ir.IntType) + and check_type.pointee.pointee.width == 8): + has_eh_msg = True + if not has_eh_msg: + return + eh_msg_ptr: ir.Value | None = None + eh_code_ptr: ir.Value | None = None + if Gen.eh_except_block_stack: + _, _, exception_code, eh_message = Gen.eh_except_block_stack[-1] + if eh_message is not None: + eh_msg_ptr = eh_message + if exception_code is not None: + eh_code_ptr = exception_code + if eh_msg_ptr is None and Gen.func and len(Gen.func.args) > 0: + for arg in Gen.func.args: + if arg.name == '__eh_msg_out__' and eh_msg_ptr is None: + eh_msg_ptr = arg + elif arg.name == '__eh_code_out__' and eh_code_ptr is None: + eh_code_ptr = arg + if eh_msg_ptr is None: + eh_msg_ptr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name="eh_msg_out_null") + if eh_code_ptr is None: + eh_code_ptr = Gen._allocaEntry(ir.IntType(32), name="eh_code_out_null") + Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), eh_msg_ptr) + Gen._store(ir.Constant(ir.IntType(32), 0), eh_code_ptr) + CallArgs.append(eh_msg_ptr) + if has_eh_code: + CallArgs.append(eh_code_ptr) + + def _check_eh_return(self, result: ir.Value, func_name: str, Gen: LlvmGeneratorMixin, called_func: ir.Function | None = None) -> ir.Value: + has_eh_msg_out: bool = False + has_eh_code_out: bool = False + if called_func and hasattr(called_func, 'args') and len(called_func.args) > 0: + for arg in called_func.args: + if arg.name == '__eh_msg_out__': + has_eh_msg_out = True + elif arg.name == '__eh_code_out__': + has_eh_code_out = True + if not has_eh_msg_out: + return result + eh_msg_ptr: ir.Value | None = None + eh_code_ptr: ir.Value | None = None + if Gen.eh_except_block_stack: + _, _, exception_code, eh_message = Gen.eh_except_block_stack[-1] + if eh_message is not None: + eh_msg_ptr = eh_message + if exception_code is not None: + eh_code_ptr = exception_code + if eh_msg_ptr is None and Gen.func and len(Gen.func.args) > 0: + for arg in Gen.func.args: + if arg.name == '__eh_msg_out__' and eh_msg_ptr is None: + eh_msg_ptr = arg + elif arg.name == '__eh_code_out__' and eh_code_ptr is None: + eh_code_ptr = arg + if eh_msg_ptr is not None: + eh_msg_val: ir.Value = Gen._load(eh_msg_ptr, name=f"Load_eh_msg_{func_name}") + null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + is_error: ir.Value = Gen.builder.icmp_signed('!=', eh_msg_val, null_ptr, name=f"check_eh_msg_{func_name}") + OkBB: ir.Block = Gen.func.append_basic_block(name=f"ok_after_{func_name}") + ErrorBB: ir.Block = Gen.func.append_basic_block(name=f"error_after_{func_name}") + Gen.builder.cbranch(is_error, ErrorBB, OkBB) + Gen.builder.position_at_start(ErrorBB) + ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None + error_ret_val: ir.Constant | None = None + if isinstance(ret_type, ir.IdentifiedStructType): + if ret_type.elements: + error_ret_val = ir.Constant(ret_type, [ + ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) + else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) + else ir.Constant(et, ir.Undefined) for et in ret_type.elements]) + else: + error_ret_val = ir.Constant(ret_type, None) + elif isinstance(ret_type, ir.PointerType): + error_ret_val = ir.Constant(ret_type, None) + elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)): + error_ret_val = ir.Constant(ret_type, 0.0) + elif isinstance(ret_type, ir.IntType): + error_ret_val = ir.Constant(ret_type, 1) + elif isinstance(ret_type, ir.VoidType): + pass + else: + error_ret_val = ir.Constant(ir.IntType(32), 1) + if Gen.eh_except_block_stack: + ExceptBB, _, exception_code, _ = Gen.eh_except_block_stack[-1] + if ExceptBB is not None: + if has_eh_code_out and eh_code_ptr is not None: + eh_code_val = Gen._load(eh_code_ptr, name=f"Load_eh_code_{func_name}") + Gen._store(eh_code_val, exception_code) + else: + Gen._store(ir.Constant(ir.IntType(32), 1), exception_code) + Gen.builder.branch(ExceptBB) + else: + if isinstance(ret_type, ir.VoidType): + Gen.builder.ret_void() + else: + Gen.builder.ret(error_ret_val) + else: + if isinstance(ret_type, ir.VoidType): + Gen.builder.ret_void() + else: + Gen.builder.ret(error_ret_val) + Gen.builder.position_at_start(OkBB) + + def _fill_default_args(self, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None = None) -> None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + global_defaults: dict = self.Trans._global_function_default_args + func_defaults: list | None = None + if func_name: + func_defaults = global_defaults.get(func_name) + if not func_defaults and func: + func_defaults = global_defaults.get(func.name) + if not func_defaults and func_name: + for gk, gv in global_defaults.items(): + if func_name in gk or gk.endswith(func_name) or gk.endswith('.' + func_name): + func_defaults = gv + break + if func and len(CallArgs) < len(func.args): + total_params: int = len(func.args) + if func_defaults: + num_defaults: int = len(func_defaults) + default_start: int = total_params - num_defaults + else: + num_defaults = 0 + default_start = total_params + while len(CallArgs) < total_params: + arg_idx: int = len(CallArgs) + arg = func.args[arg_idx] + param_type: ir.Type = arg.type if hasattr(arg, 'type') else arg + if func_defaults and arg_idx >= default_start: + def_idx: int = arg_idx - default_start + if def_idx < num_defaults and func_defaults[def_idx] is not None: + default_val = func_defaults[def_idx] + if isinstance(param_type, ir.IntType): + max_val: int = (1 << param_type.width) - 1 + if isinstance(default_val, bool): + default_val = 1 if default_val else 0 + if isinstance(default_val, int) and default_val > max_val: + default_val = default_val & max_val + if isinstance(default_val, int) and default_val < 0: + default_val = default_val & max_val + CallArgs.append(ir.Constant(param_type, default_val)) + elif isinstance(param_type, ir.PointerType): + CallArgs.append(ir.Constant(param_type, None)) + else: + try: + _zv = Gen._zero_value_for_type(param_type) + CallArgs.append(_zv if _zv is not None else ir.Constant(ir.IntType(32), 0)) + except (AssertionError, TypeError): + CallArgs.append(ir.Constant(ir.IntType(32), 0)) + else: + if isinstance(param_type, ir.PointerType): + CallArgs.append(ir.Constant(param_type, None)) + elif isinstance(param_type, ir.IntType): + CallArgs.append(ir.Constant(param_type, 0)) + else: + try: + _zv = Gen._zero_value_for_type(param_type) + CallArgs.append(_zv if _zv is not None else ir.Constant(ir.IntType(32), 0)) + except (AssertionError, TypeError): + CallArgs.append(ir.Constant(ir.IntType(32), 0)) + else: + if isinstance(param_type, ir.PointerType): + CallArgs.append(ir.Constant(param_type, None)) + elif isinstance(param_type, ir.IntType): + CallArgs.append(ir.Constant(param_type, 0)) + else: + try: + _zv = Gen._zero_value_for_type(param_type) + CallArgs.append(_zv if _zv is not None else ir.Constant(ir.IntType(32), 0)) + except (AssertionError, TypeError): + CallArgs.append(ir.Constant(ir.IntType(32), 0)) + + def _LookupGlobalDefaults(self, func_name: str | None) -> list | None: + """按函数名查询全局默认参数表(跨模块调用点用)。 + + 查找顺序:精确键 → 去 mangled 前缀后的裸名 → 以 '.<裸名>' 结尾的键 + (如 {sha1}.name)。精确键优先,避免"同名不同模块"的默认值互相乱配。 + """ + if not func_name: + return None + table: dict = getattr(self.Trans, '_global_function_default_args', None) or {} + if not table: + return None + hit = table.get(func_name) + if hit: + return hit + base: str = func_name.split('.')[-1] + if base != func_name: + hit = table.get(base) + if hit: + return hit + for key, value in table.items(): + if key.endswith('.' + base): + return value + return None + + def _LookupGlobalParamNames(self, func_name: str | None) -> list | None: + """按函数名查询全局参数名表;查找顺序同 _LookupGlobalDefaults。""" + if not func_name: + return None + table: dict = getattr(self.Trans, '_global_function_param_names', None) or {} + if not table: + return None + hit = table.get(func_name) + if hit: + return hit + base: str = func_name.split('.')[-1] + if base != func_name: + hit = table.get(base) + if hit: + return hit + for key, value in table.items(): + if key.endswith('.' + base): + return value + return None + + @staticmethod + def _ConstantToIrValue(raw: object, param_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Value | None: + """把全局默认参数表里的 Python 常量转成形参类型的 IR 常量。 + + 只处理整数/布尔(跨模块默认值里最常见,如 cap: t.CSizeT = 4096)。字符串等 + 常量需要在调用点引用定义模块的全局对象,无法安全重建 → 返回 None,调用点 + 退回原有零值填充(与修复前行为一致)。 + """ + if isinstance(param_type, ir.IntType): + if isinstance(raw, bool): + raw = 1 if raw else 0 + if isinstance(raw, int): + max_val: int = (1 << param_type.width) - 1 + if raw > max_val or raw < 0: + raw = raw & max_val + return ir.Constant(param_type, raw) + return ir.Constant(param_type, 0) + if isinstance(param_type, ir.PointerType): + return ir.Constant(param_type, None) + return None + + def _ApplyKeywordArgs(self, Node: ast.Call, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None, self_offset: int = 1) -> None: + """在 _fill_default_args 之后调用:用 keyword args 按 param_names 覆盖 CallArgs 对应位置。 + self_offset=1 表示 CallArgs[0] 是 self,参数从 CallArgs[1] 开始(__init__/__new__/__call__/method)。 + self_offset=0 表示无 self,参数从 CallArgs[0] 开始(普通函数)。 + """ + if not Node.keywords: + return + global_param_names: dict = self.Trans._global_function_param_names + param_names: list[str] | None = None + if func_name: + param_names = global_param_names.get(func_name) + if not param_names and func is not None: + param_names = global_param_names.get(func.name) + if not param_names and func is not None and hasattr(func, 'args'): + llvm_names: list[str] = [a.name for a in func.args] if func.args else [] + if llvm_names and not all(n.isdigit() for n in llvm_names): + param_names = llvm_names + if not param_names: + # 无法获取参数名,回退:按顺序追加(保持旧行为,避免静默丢弃) + for kw in Node.keywords: + if kw.arg is None: + continue + ArgVal = self.HandleExprLlvm(kw.value) + if ArgVal: + CallArgs.append(ArgVal) + return + for kw in Node.keywords: + if kw.arg is None: + continue + ArgVal = self.HandleExprLlvm(kw.value) + if not ArgVal: + continue + if kw.arg in param_names: + idx: int = param_names.index(kw.arg) + target_idx: int = idx - self_offset + if 0 <= target_idx < len(CallArgs) - self_offset: + CallArgs[self_offset + target_idx] = ArgVal + + def _CheckFuncPtrArity(self, Node: ast.Call, FnPtr: ir.Value, Args: list[ir.Value], + FuncName: str, Gen: LlvmGeneratorMixin) -> None: + """函数指针间接调用前的 arity 校验(fail-fast,紧邻 builder.call 之前)。 + + 期望形参个数取自 FnPtr 的静态 LLVM 函数类型的形参表:来源是函数指针槽 + (模块级/局部 t.Callable[...] 注解)的 CTypeInfo.FuncPtrParams → + CTypeInfo.ToLLVM/_ctype_to_llvm → ir.FunctionType(ret, params);变参位由 + FunctionType.var_arg 承载。不校验时,实参个数少于形参个数会让 llvmlite + CallInstr 按形参个数索引 args[i] 抛裸 `IndexError: list index out of range` + (无函数名、无源位置,栈顶落在被调函数 docstring 行,无法定位真因)。 + + 变参函数(var_arg=True,如 C printf 形态)只要求实参不少于固定形参个数: + 多出的实参按变参传递,不报错;仅 len(Args) < 固定形参个数 时报错。 + """ + FnType: ir.Type = FnPtr.type.pointee if isinstance(FnPtr.type, ir.PointerType) else FnPtr.type + if not isinstance(FnType, ir.FunctionType): + return + Expected: int = len(FnType.args) + Actual: int = len(Args) + IsVariadic: bool = bool(getattr(FnType, 'var_arg', False)) + if IsVariadic: + if Actual >= Expected: + return + elif Actual == Expected: + return + # 源位置:按本文件既有写法取 Node 自身的 lineno/col_offset(不用 + # _current_node_info/_current_lineno —— 二者可能停留在上一条语句), + # 源文件取 Gen._current_source_file,源码行取 Gen._get_source_line。 + Lineno: int = getattr(Node, 'lineno', 0) or 0 + # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, + # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator); + # 报文行号与源码行必须同用还原后的 Lineno,二者一致(否则会显示下一行源码)。 + _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _line_offset > 0 and Lineno > _line_offset: + Lineno -= _line_offset + Col: int = getattr(Node, 'col_offset', -1) + SrcFile: str = getattr(Gen, '_current_source_file', '') or 'unknown' + Loc: str = f"{SrcFile}:{Lineno}" + if Col >= 0: + Loc += f":{Col}" + SourceLine: str = '' + if Lineno > 0 and hasattr(Gen, '_get_source_line'): + SourceLine = (Gen._get_source_line(Lineno) or '').strip() + ExpectText: str = (f"至少 {Expected} 个形参(变参函数,仅固定形参)" + if IsVariadic else f" {Expected} 个形参") + raise Exception( + f"[TransPyC] 函数指针调用实参个数不匹配: '{FuncName}' 期望{ExpectText}," + f"实际传入 {Actual} 个实参;请检查该函数指针(t.Callable[...] 注解槽)的" + f"形参表是否与调用处实参个数一致。位置 {Loc}" + f"{(',源码: ' + SourceLine) if SourceLine else ''}" + ) + + def _RequireArgValue(self, ArgVal: ir.Value | None, Callee: str, ArgIndex: int, + ArgNode: ast.AST, CallNode: ast.Call) -> ir.Value: + """fail-fast:调用实参编译期求值为空(`HandleExprLlvm` 返回 None)时终止装配。 + + 调用装配处原写作 `ArgVal = self.HandleExprLlvm(arg); if ArgVal: 追加(ArgVal)`: + 实参求值为 None 时它被**静默整条丢弃**,导致后续实参整体前移一格,最终由 + `FuncGen._adjust_args` 报出误导性的"指针类型不能隐式转换为整数参数"(真因是 + "少了一个实参")。实例:`ast.OpKind.BitOr` 因枚举成员未登记而求值 None ⇒ + `ast.BinOp(...)` 的 4 个实参退化为 3 个 ⇒ `AST*` 落到 `op: i32`(见 + `Debugger/rawptr-migration-and-fastfail.md` §27、`Debugger/enum-semantics.md` + 的「逗号分组成员被静默丢弃」一节)。 + + `HandleExprLlvm` 返回 None 只有一种语义——该表达式编译期**求值失败** + (名称/属性链/枚举成员未登记、跨模块符号查找失败、类构造在声明期结构体不完整等); + "按类型本无值"的形态(以返回 void 的调用收尾)返回的是 CallInstr 对象而非 None, + 故不存在需要跳过的合法实参。此处改为当场终止,报文含被调者、实参序号与源表达式、 + 根因提示与源码位置;**不丢弃该实参**。 + """ + if ArgVal is not None: + return ArgVal + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + expr_text: str = self._ExprText(ArgNode) + ArgKind: str = type(ArgNode).__name__ + raw_lineno: int = getattr(ArgNode, 'lineno', 0) or getattr(CallNode, 'lineno', 0) or 0 + lineno: int = raw_lineno + # 行号还原:`_inject_auto_imports` 会在解析前往文件头注入 import 行,AST 行号 + # 比磁盘真实行号大 N;与 HandlesBase._RaiseUnresolvedExpr 同口径。 + _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + col: int = getattr(ArgNode, 'col_offset', -1) + src_file: str = getattr(Gen, '_current_source_file', '') or 'unknown' + code_line: str = '' + if lineno > 0 and hasattr(Gen, '_get_source_line'): + try: + code_line = (Gen._get_source_line(lineno) or '').strip() + except Exception: + code_line = '' + loc: str = f"{src_file}:{lineno}" + if col >= 0: + loc += f":{col}" + # 实参是属性读时补同一份基址事实(与 HandlesBase._RaiseUnresolvedExpr 共用 + # `_DescribeAttrBase`):属性读取值失败的两类根因(类名登记退化 / 键不同源) + # 必须一眼可见,不再从"求值失败"反推。 + _base_facts: str = '' + if isinstance(ArgNode, ast.Attribute): + try: + _base_facts = self._DescribeAttrBase(Gen, ArgNode) + except Exception: # noqa: BLE001 + _base_facts = '' + raise Exception( + f"[TransPyC] 调用实参编译期求值为空: 被调者 '{Callee}' 的第 {ArgIndex} 个实参 " + f"'{expr_text}'({ArgKind} 形态)求值失败;已终止编译,不丢弃该实参" + f"(丢弃会使后续实参前移一格,报出与真因无关的类型不匹配)。" + f"常见根因:枚举成员/名称未登记、跨模块符号查找失败、类定义在声明期结构体不完整。" + f"位置 {loc}" + f"{(',源码: ' + code_line) if code_line else ''}" + f"{chr(10) + '│ 基址诊断: ' + _base_facts if _base_facts else ''}" + ) + + def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + # 协程:coro.coro_run(帧) 内联翻译为 resume 循环 + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_run': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroRunLlvm(Node, Gen) + # 协程:coro.coro_destroy(帧) 内联翻译为 free + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_destroy': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroDestroyLlvm(Node, Gen) + # 协程:coro.coro_exception(帧) 内联翻译为帧 exc 槽读取 + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_exception': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroExceptionLlvm(Node, Gen) + # 协程:coro.coro_exception_msg(帧) 内联翻译为帧 msg 槽读取 + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_exception_msg': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroExceptionMsgLlvm(Node, Gen) + # 协程:coro.coro_gather(帧...) 内联翻译为并发推进循环 + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_gather': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroGatherLlvm(Node, Gen) + FuncAttr: str | None = None + ModulePath: str | None = None + if isinstance(Node.func, ast.Name): + FuncName = Node.func.id + # __asm__(...) 语法糖:等价 c.Asm(...)(裸名调用,无需 import c) + if FuncName == '__asm__': + return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) + # __llvmir__(...) 语法糖:等价 c.LLVMIR(...) + if FuncName == '__llvmir__': + return self._HandleLLVMIRLlvm(Node) + # irinp/irout 等价 c.LInp/c.LOut(仅作模板占位符,独立调用返回 0) + if FuncName in ('irinp', 'irout'): + return ir.Constant(ir.IntType(32), 0) + # M1 协程:普通函数调用 async 函数 → create_()(返回帧指针) + if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(FuncName): + return self.Trans.AsyncHandler._HandleAsyncCreateCallLlvm(Node, FuncName) + if hasattr(self.Trans.FunctionHandler, '_generic_templates') and FuncName in self.Trans.FunctionHandler._generic_templates: + return self._HandleGenericCallLlvm(Node, FuncName) + result = self.Trans.ExprBuiltinHandle._HandleBuiltinCallLlvm(Node) + if result is not None: + return result + if Gen._has_function(FuncName): + return self._HandleClosureCallLlvm(Node, FuncName) + SymInfo = self.Trans.SymbolTable.lookup(FuncName) + if SymInfo: + # 名字冲突处理:类名可能与 CEnum 成员名冲突(如 ASTKind.Name 枚举成员与 Name 类同名) + # 若该名字同时是类(在 Gen.structs 中或存在 __before_init__),优先作为类构造函数处理 + IsAlsoClass: bool = (FuncName in Gen.structs) or Gen._has_function(f'{FuncName}.__before_init__') + if SymInfo.IsEnum and not (IsAlsoClass and getattr(SymInfo, 'IsEnumMember', False)): + # 枚举类型转换 `EnumName(v)`:结果宽度 = 该枚举的存储宽度 + # (最宽成员类型),与成员常量宽度同点(ABI 一致)。 + _enum_ty: ir.IntType = Gen._enum_int_type(FuncName, SymInfo) + if Node.args: + arg_val = self.HandleExprLlvm(Node.args[0]) + if arg_val: + if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != _enum_ty.width: + if arg_val.type.width < _enum_ty.width: + arg_val = Gen.builder.zext(arg_val, _enum_ty, name="zext_enum_arg") + else: + arg_val = Gen.builder.trunc(arg_val, _enum_ty, name="trunc_enum_arg") + return arg_val + return ir.Constant(_enum_ty, 0) + if SymInfo.IsTypedef: + return self._HandleTypedefCastLlvm(Node, FuncName) + NewFuncName = f'{FuncName}.__before_init__' + InitFuncName = f'{FuncName}.__init__' + if Gen._has_function(NewFuncName): + return self._HandleClassNewLlvm(Node, FuncName) + if Gen._has_function(InitFuncName) and FuncName in Gen.structs: + return self._HandleClassNewLlvm(Node, FuncName) + if FuncName in Gen.structs: + return self._HandleClassNewLlvm(Node, FuncName) + if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and FuncName in self.Trans.ClassHandler._generic_class_templates: + return self._HandleClassNewLlvm(Node, FuncName) + if self.Trans.FunctionDefCache.get(FuncName): + return self._HandleClosureCallLlvm(Node, FuncName) + if FuncName in self._C_LIB_FUNCS: + return self._HandleClosureCallLlvm(Node, FuncName) + ClassName = Gen.var_struct_class.get(FuncName) + if ClassName and Gen._has_function(f'{ClassName}.__call__'): + return self._HandleInstanceCallLlvm(Node, FuncName, ClassName) + t_c_imported = getattr(self.Trans, '_t_c_imported_names', {}) + if FuncName in t_c_imported: + src_module, src_name = t_c_imported[FuncName] + virtual_node = self._make_virtual_attr_call(Node, src_module, src_name) + return self._HandleCallLlvm(virtual_node) + # 检查是否是全局变量中的函数指针(t.Callable 类型在模块级定义) + if FuncName not in Gen.variables and FuncName in Gen.global_vars: + Gen._Load_var(FuncName) + if FuncName not in Gen.variables and FuncName in Gen.module.globals: + GVar = Gen.module.globals[FuncName] + if not isinstance(GVar, ir.Function): + StoredType = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else GVar.type + if isinstance(StoredType, ir.PointerType) and isinstance(StoredType.pointee, ir.FunctionType): + Gen.variables[FuncName] = GVar + # 检查是否是闭包变量(lambda 赋值给变量后调用) + if FuncName in Gen.variables and Gen.variables[FuncName] is not None: + VarPtr = Gen.variables[FuncName] + # 检查是否是函数指针变量(t.Callable 类型),直接加载并调用 + if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(VarPtr.type.pointee.pointee, ir.FunctionType): + fn_ptr = Gen._load(VarPtr, name=f"Load_fn_{FuncName}") + args = [] + for arg in Node.args: + ArgVal = self.HandleExprLlvm(arg) + if ArgVal: + args.append(ArgVal) + self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen) + return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}") + closure_ptr = Gen._load(VarPtr, name=f"Load_closure_{FuncName}") + if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.FunctionType): + fn_ptr = closure_ptr + args = [] + for arg in Node.args: + ArgVal = self.HandleExprLlvm(arg) + if ArgVal: + args.append(ArgVal) + self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen) + return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}") + # 处理 t.CPtr (i8*) 函数指针调用 + # 当函数被赋值给 t.CPtr 变量时,原始函数类型退化为 i8* + # 检查是否是闭包变量(通过 _closure_var_types 追踪) + if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.IntType) and closure_ptr.type.pointee.width == 8: + ClosureInfo: tuple[ir.Type, list[ir.Type]] | None = Gen._closure_var_types.get(FuncName) + if ClosureInfo is not None: + # 闭包结构体解包:{i8* env, i8* fn} -> 加载 env 和 fn,构造正确函数签名 + FnRetType: ir.Type = ClosureInfo[0] + NonlocalArgTypes: list[ir.Type] = [_safe_ptr_type(t) for t in ClosureInfo[1]] + I8PtrType: ir.PointerType = ir.IntType(8).as_pointer() + ClosureStructType: ir.LiteralStructType = ir.LiteralStructType([I8PtrType, I8PtrType]) + ClosureStructPtr: ir.Value = Gen.builder.bitcast(closure_ptr, ir.PointerType(ClosureStructType), name=f"cast_closure_{FuncName}") + # 加载 env(field 0)和 fn(field 1) + EnvGep: ir.Value = Gen.builder.gep(ClosureStructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"env_gep_{FuncName}") + EnvVal: ir.Value = Gen._load(EnvGep, name=f"env_{FuncName}") + FnGep: ir.Value = Gen.builder.gep(ClosureStructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 1)], name=f"fn_gep_{FuncName}") + FnVal: ir.Value = Gen._load(FnGep, name=f"fn_{FuncName}") + # 收集用户调用参数 + UserArgTypes: list[ir.Type] = [] + UserCallArgs: list[ir.Value] = [] + for arg in Node.args: + ArgVal = self.HandleExprLlvm(arg) + if ArgVal: + UserCallArgs.append(ArgVal) + UserArgTypes.append(ArgVal.type) + # 构造完整函数类型:[nonlocal_types..., user_arg_types...] + FullArgTypes: list[ir.Type] = list(NonlocalArgTypes) + UserArgTypes + FullFnType: ir.FunctionType = ir.FunctionType(FnRetType, FullArgTypes) + FullFnPtr: ir.Value = Gen.builder.bitcast(FnVal, ir.PointerType(FullFnType), name=f"cast_fn_{FuncName}") + # 调用参数:env 对应 nonlocal 参数,bitcast 到正确类型(需 _safe_ptr_type 兜底避免 void*) + CallArgs: list[ir.Value] = [] + if NonlocalArgTypes: + CallArgs.append(Gen.builder.bitcast(EnvVal, _safe_ptr_type(NonlocalArgTypes[0]), name=f"cast_env_{FuncName}")) + CallArgs.extend(UserCallArgs) + return Gen.builder.call(FullFnPtr, CallArgs, name=f"call_{FuncName}") + # 普通函数指针调用(无闭包):从调用参数构造函数类型,默认返回 i32 + cptr_arg_types: list[ir.Type] = [] + cptr_call_args: list[ir.Value] = [] + for arg in Node.args: + ArgVal = self.HandleExprLlvm(arg) + if ArgVal: + cptr_call_args.append(ArgVal) + cptr_arg_types.append(ArgVal.type) + cptr_fn_type = ir.FunctionType(ir.IntType(32), cptr_arg_types) + cptr_fn_ptr = Gen.builder.bitcast(closure_ptr, ir.PointerType(cptr_fn_type), name=f"cast_fn_{FuncName}") + return Gen.builder.call(cptr_fn_ptr, cptr_call_args, name=f"call_{FuncName}") + if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.LiteralStructType): + st = closure_ptr.type.pointee + if len(st.elements) == 2 and isinstance(st.elements[1], ir.PointerType) and isinstance(st.elements[1].pointee, ir.FunctionType): + fn_ptr_type = st.elements[1] + fn_ptr = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 1)], name=f"fn_ptr_{FuncName}") + fn_val = Gen._load(fn_ptr, name=f"fn_{FuncName}") + args = [] + for arg in Node.args: + ArgVal = self.HandleExprLlvm(arg) + if ArgVal: + args.append(ArgVal) + env_ptr = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"env_ptr_{FuncName}") + env_val = Gen._load(env_ptr, name=f"env_{FuncName}") + call_args = [env_val] + args + expected_fn_type = fn_ptr_type.pointee + if len(call_args) < len(expected_fn_type.args): + while len(call_args) < len(expected_fn_type.args): + call_args.append(ir.Constant(expected_fn_type.args[len(call_args)], 0)) + # 如果函数期望的 env 类型与闭包中存储的 i8* 不同,做 bitcast(避免 void*) + if len(expected_fn_type.args) > 0 and call_args[0].type != expected_fn_type.args[0]: + safe_env_type: ir.Type = _safe_ptr_type(expected_fn_type.args[0]) + call_args[0] = Gen.builder.bitcast(call_args[0], safe_env_type, name=f"cast_env_{FuncName}") + return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}") + # 编译器内置裸指针强转 rawptr(x):rawptr 是 TPC/VPC 双端**语法保留字** + # (无 SymbolTable typedef 条目、无泛型模板;builtins.vp 已不再声明它, + # 见 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME 与 VPC 同口径内置分支 + # TypeGenResolve.vp:921)。落到下方 typedef 表判据必然 miss ⇒ 误报 + # "Undefined function: 'rawptr'"(ViperC/App/lib/core/Gen/ + # StructGenMixin.vp `vt_ptr: rawptr = rawptr(*(uint64*)(default_val))` 根因)。 + # 单独 rawptr 等价 t.CPtr(void* → i8*):目标类型取内置 rawptr 的权威 + # CTypeInfo(_builtin_rawptr_info,与注解侧同源),转换复用 (rawptr[X])(x) + # / (t.CPtr)(x) 的 _emit_rawptr_cast——不新造第二套 rawptr 表示。 + # 不照抄 uchar 的做法(往 builtins.vp 加 `uchar: t.CTypedef = ...`): + # rawptr 不是普通 typedef 名而是内置语法,VPC register_ctypedef 显式拒绝 + # 重定义(TypeGenCType.vp:157-160)、TPC builtins_typedef_value 亦硬编码 + # 返回内置值(TypeAnnotationResolver.py:313-314)⇒ 该声明不生效。 + # 本分支置于下方 try 之外:解析/转换异常必须外抛,不得被吞成 + # "Undefined function: 'rawptr'" 的误导报文。 + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_RP + if FuncName == _TAR_RP._BUILTIN_ROWPTR_NAME: + _rawptr_target: ir.Type = Gen._ctype_to_llvm(_TAR_RP._builtin_rawptr_info()) + return self._emit_rawptr_cast(Node, Gen, _rawptr_target) + # builtins 隐式 CTypedef 作为类型转换调用(uint64(x)/int64(x)/size_t(x) 等, + # builtins.vp 声明、无需 import,不注册进 SymbolTable):常规 miss 后在此 + # 尝试 typedef cast(uint64(buf) = t.CUInt64T(buf)),否则报 + # "Undefined function: 'uint64'"(includes/ast/base.vp bootstrap 根因)。 + _td_diag: str = '' + try: + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_TD + _td_table: dict = _TAR_TD._scan_builtins()['typedefs'] + if FuncName not in _td_table: + # 典型成因:includes/builtins.vp 未定位到(PROJECT_INCLUDE_DIRS 尚未 + # 装配)、或声明写法未被文本扫描识别 ⇒ 整表为空/缺项。 + _td_diag = (f"(builtins typedef 表 {len(_td_table)} 项,无 '{FuncName}';" + f"includes 目录={_TAR_TD._builtins_include_dirs()})") + if FuncName in _td_table: + # from_type_name 对裸 typedef 名(uint64 等)解析不出真实类型类 + # (BuiltinTypeMap 无此键,CreateFromTypeName 落空成空 CType), + # 因此改走权威 typedef 值:'t.CUInt64T' → CTypeRegistry.NameToLLVM + # → 'i64'(与 DeclarationGenerator 的 stub 兜底同机制)。 + _bv_td: str | None = _TAR_TD.builtins_typedef_value(FuncName) + _lls_td: str | None = None + if _bv_td: + _nm_td: str = _bv_td[2:] if _bv_td.startswith('t.') else _bv_td + from lib.includes.t import CTypeRegistry as _CR_TD + _lls_td = _CR_TD.NameToLLVM(_nm_td) + if not _lls_td: + # 声明值本身也是短别名(新语法:`typedef short = int16`、 + # `typedef longlong = int64`)⇒ 单步 NameToLLVM(不认短别名) + # 落空。改走权威链式解析(builtins.vp 声明 → 逐级展开到 t.C*), + # 与注解位置 LLVMTypeMapper._resolve_name 同一来源。 + _td_info = _TAR_TD._lookup_builtins_typedef(FuncName, self.Trans.SymbolTable) + if _td_info is not None and _td_info.BaseType: + _tg_td2: ir.Type = Gen._ctype_to_llvm(_td_info) + if isinstance(_tg_td2, (ir.IntType, ir.FloatType, ir.DoubleType, ir.PointerType)): + if len(Node.args) >= 2: + _p2b: ast.AST = Node.args[1] + _p2b_cptr: bool = ( + (isinstance(_p2b, ast.Attribute) and _p2b.attr == 'CPtr' + and isinstance(_p2b.value, ast.Name) and _p2b.value.id in ('t', 'c')) or + (isinstance(_p2b, ast.Name) and _p2b.id == 'CPtr') + ) + if _p2b_cptr: + if isinstance(_tg_td2, ir.PointerType): + return self._emit_rawptr_cast(Node, Gen, _tg_td2) + return self._emit_rawptr_cast(Node, Gen, ir.PointerType(_tg_td2)) + return self._emit_rawptr_cast(Node, Gen, _tg_td2) + if _lls_td: + _tg_td: ir.Type = Gen._ctype_to_llvm(_lls_td) + if isinstance(_tg_td, (ir.IntType, ir.FloatType, ir.DoubleType, ir.PointerType)): + # 双参数形式 uint8(s, t.CPtr) / int64(v, CPtr):第二参是 + # CPtr 标记 → 指针包装(等价旧写法 t.CUInt8T(s, t.CPtr), + # 对齐 Attribute 形式 t.CXXX(v, t.CPtr) 的 IsPtr_cast 语义)。 + # 单参形式 uint64(buf) 才是标量转换。忽略第二参会把 + # hashlib/__sha1.vp 的 data 编译成标量 → 运行时 sha1 + # transform 读越界崩溃。 + if len(Node.args) >= 2: + _p2_td: ast.AST = Node.args[1] + _p2_cptr: bool = ( + (isinstance(_p2_td, ast.Attribute) and _p2_td.attr == 'CPtr' + and isinstance(_p2_td.value, ast.Name) and _p2_td.value.id in ('t', 'c')) or + (isinstance(_p2_td, ast.Name) and _p2_td.id == 'CPtr') + ) + if _p2_cptr: + if isinstance(_tg_td, ir.PointerType): + return self._emit_rawptr_cast(Node, Gen, _tg_td) + _tg_ptr_td: ir.Type = ir.PointerType(_tg_td) + return self._emit_rawptr_cast(Node, Gen, _tg_ptr_td) + return self._emit_rawptr_cast(Node, Gen, _tg_td) + except Exception as _e_td: + # 不再静默:兜底链任何一步抛错都要带出来(此前 pass 让真正的根因 + # 只剩一句误导性的 "Undefined function")。 + _td_diag = _td_diag + f"(typedef cast 求值异常: {type(_e_td).__name__}: {_e_td})" + raise Exception(f"Undefined function: '{FuncName}'{_td_diag}") + if isinstance(Node.func, ast.Subscript): + # Handle list[int](mb) syntax — generic class constructor with explicit type args + # Handle xxx[T](x) syntax — generic function call with explicit type args + sub: ast.Subscript = Node.func + # (rawptr[X])(expr) / (t.CPtr[X])(expr):内建裸指针转换(无泛型模板)。 + # 不拦截 → 泛型模板查不到 → 落 L850 "Unsupported call expression" + # (StubMergerCollect.vp L271 bootstrap 根因)。 + _sub_nm: str | None = None + if isinstance(sub.value, ast.Name): + _sub_nm = sub.value.id + elif isinstance(sub.value, ast.Attribute) and isinstance(sub.value.value, ast.Name) and sub.value.value.id == 't' and sub.value.attr == 'CPtr': + _sub_nm = 't.CPtr' + if _sub_nm in ('rawptr', 't.CPtr'): + return self._HandleSubscriptPtrCastLlvm(Node, sub) + if isinstance(sub.value, ast.Name): + FuncName: str = sub.value.id + # 优先检查泛型类模板 + if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and FuncName in self.Trans.ClassHandler._generic_class_templates: + return self._HandleGenericClassNewLlvm(Node, FuncName, explicit_slice=sub.slice) + # 检查泛型函数模板(如 xxx[T](x)) + if hasattr(self.Trans.FunctionHandler, '_generic_templates') and FuncName in self.Trans.FunctionHandler._generic_templates: + return self._HandleGenericCallExplicitLlvm(Node, FuncName, explicit_slice=sub.slice) + if isinstance(Node.func, ast.Attribute): + if Node.func.attr in ('update', 'final', 'transform'): + pass + if Node.func.attr == '__sizeof__': + if isinstance(Node.func.value, ast.Subscript): + spec_name = self._ResolveGenericSlice(Node.func.value) + if spec_name: + return self._HandleSizeofLlvm(ast.Name(id=spec_name, ctx=ast.Load())) + # 对于 BinOp/BitOr 联合类型(如 AST | t.CPtr), + # sizeof 应返回指针大小 8,而非结构体大小 + # 必须在 HandleExprLlvm 之前检查,避免对类型名求值产生副作用 + if isinstance(Node.func.value, ast.BinOp) and isinstance(Node.func.value.op, ast.BitOr): + if self._AnnotationContainsCPtr(Node.func.value): + return ir.Constant(ir.IntType(64), 8) + # 对于 ast.Attribute(如 ast.ClassDef.__sizeof__() / t.CInt.__sizeof__()), + # 直接调用 _HandleSizeofLlvm,不走 HandleExprLlvm + # 因为 ast.Attribute 在 sizeof 上下文中一定是类型名,不是变量 + if isinstance(Node.func.value, ast.Attribute): + type_name = Node.func.value.attr + SizeofNode = ast.Name(id=type_name, ctx=ast.Load()) + _r = self._HandleSizeofLlvm(SizeofNode) + return _r + # 对于 ast.Name,需要区分变量和类型名: + # - 如果是已知变量(Gen.variables/_reg_values/_direct_values/self), + # 走 HandleExprLlvm 路径(如 myvar.__sizeof__()) + # - 否则视为类型名,直接调用 _HandleSizeofLlvm + # 这避免了 HandleExprLlvm 对类型名求值产生副作用 + # (如修改 builder 状态、污染 variables 缓存等), + # 这些副作用会导致泛型特化后 then 块为空 + if isinstance(Node.func.value, ast.Name): + nm: str = Node.func.value.id + is_variable: bool = ( + nm == 'self' + or nm in Gen.variables + or nm in getattr(Gen, '_reg_values', {}) + or nm in getattr(Gen, '_direct_values', {}) + ) + if not is_variable: + SizeofNode = ast.Name(id=nm, ctx=ast.Load()) + _r = self._HandleSizeofLlvm(SizeofNode) + return _r + # 变量/实例的 sizeof: 通过 HandleExprLlvm 获取值 + ObjVal = self.HandleExprLlvm(Node.func.value) + if ObjVal and isinstance(ObjVal.type, ir.PointerType): + pointee = ObjVal.type.pointee + if isinstance(pointee, ir.ArrayType): + # 元素尺寸统一走 Gen._get_struct_size(含字段对齐/填充,与 __sizeof__ / + # memhub alloc 同源):_get_struct_size(ArrayType) = 元素尺寸 × 元素数。 + # 原实现手写「字段宽度直接相加 + 未知类型兜底 8」,会漏算 struct 的填充 + # ({i8,i64} 算成 9 而非 16),sizeof 偏小 → 调用方按过小尺寸 alloc → + # 越界写坏邻居块头(与 1 GiB 主池漏满同一根因链)。 + return ir.Constant(ir.IntType(64), Gen._get_struct_size(pointee)) + elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + # 结构体实例: 直接计算大小 + self._resolve_opaque_struct_members(pointee, Gen) + size = Gen._get_struct_size(pointee) + if size > 0: + return ir.Constant(ir.IntType(64), size) + # 回退: 尝试作为类型名处理 + if isinstance(Node.func.value, ast.Name): + type_name = Node.func.value.id + SizeofNode = ast.Name(id=type_name, ctx=ast.Load()) + return self._HandleSizeofLlvm(SizeofNode) + ModulePath = self._get_ModulePath(Node.func.value) + if ModulePath: + first_part = ModulePath.split('.')[0] + resolved_first = self.Trans.SymbolTable.resolve_alias(first_part) + if resolved_first != first_part: + ModulePath = resolved_first + ModulePath[len(first_part):] + # 统一获取模块 SHA1,用于跨模块类型引用(确保用源模块的 SHA1 而非当前模块) + # 放在 ModulePath 和 alias 解析之后,供所有 _HandleClassNewLlvm/_ensure_struct_declared 调用点使用 + _ctor_module_sha1: str | None = None + if ModulePath and ModulePath not in {'t', 'c'}: + _ctor_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) + _ctor_module_sha1 = _ctor_sha1_map.get(ModulePath) + if not _ctor_module_sha1 and '.' in ModulePath: + _ctor_module_sha1 = _ctor_sha1_map.get(ModulePath.split('.')[-1]) + if ModulePath: + is_instance_var = isinstance(Node.func.value, ast.Name) and ( + Node.func.value.id in Gen.var_struct_class + or Node.func.value.id in Gen.variables + or Node.func.value.id in Gen.global_vars + ) and ( + Node.func.value.id not in Gen.ModuleSha1Map + or Node.func.value.id in Gen.var_struct_class + or Node.func.value.id in Gen.variables + or Node.func.value.id in Gen.global_vars + ) + is_class_name = isinstance(Node.func.value, ast.Name) and (Node.func.value.id in Gen.structs or self.Trans.SymbolTable.is_struct(Node.func.value.id)) + # 链式方法调用:如 self.__mbuddy__.alloc() 或 trans.ImportsH.HandleImport(child) + # Node.func.value 是 ast.Attribute,基础是实例变量(如 self/trans) + _is_chained_instance: bool = False + if isinstance(Node.func.value, ast.Attribute): + _base_node: ast.AST = Node.func.value + while isinstance(_base_node, ast.Attribute): + _base_node = _base_node.value + if isinstance(_base_node, ast.Name): + _base_name: str = _base_node.id + # 检查基名是否为已知变量(self、局部变量、全局变量、已知 struct 变量) + # 且不在 ModuleSha1Map 中(避免把模块名误判为变量) + if _base_name == 'self' or ( + (_base_name in Gen.var_struct_class + or _base_name in Gen.variables + or _base_name in Gen.global_vars) + and (_base_name not in Gen.ModuleSha1Map + or _base_name in Gen.var_struct_class + or _base_name in Gen.variables + or _base_name in Gen.global_vars) + ): + _is_chained_instance = True + elif self._IsModuleQualifiedGlobalChain(_base_name, Node.func.value): + # 模块限定全局变量链:`Module.Global.field.method(...)` + # (如 GenApi._g_gen.ClassGenBase._collect_parents(...))。 + # 链根是模块别名、次段是该模块的全局变量 ⇒ 接收者是对象, + # 与 self/局部变量/全局变量同一条"实例链"判据(详见 + # _IsModuleQualifiedGlobalChain 的说明)。 + _is_chained_instance = True + if (is_instance_var or _is_chained_instance) and not is_class_name: + return self._HandleMethodCallLlvm(Node) + FuncAttr = Node.func.attr + # c 模块的特殊语法设施(Addr/Deref/DerefAs/Set/Load/Asm 等)必须优先处理, + # 即使它们在 Gen.structs 中(c.py 的类会被声明为结构体但不应被实例化)。 + # c.py 是语法部分,不应被编译,其中的类是编译器语法设施。 + # 必须在 Gen.structs 检查之前处理,否则 c.Deref/c.DerefAs 会被 + # _HandleClassNewLlvm 当成普通类实例化(根因:HandlesExprCall.py:497)。 + if ModulePath == 'c': + if FuncAttr == 'Asm': + return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) + elif FuncAttr == 'Addr': + return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node) + elif FuncAttr == 'Set': + return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node) + elif FuncAttr == 'Load': + return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node) + elif FuncAttr == 'Deref': + return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) + elif FuncAttr == 'DerefAs': + return self._HandleCDerefAsLlvm(Node) + elif FuncAttr == 'PtrToInt': + return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) + elif FuncAttr in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) + elif FuncAttr == 'CError': + msg = "compile-time error" + if Node.args: + arg = Node.args[0] + if isinstance(arg, ast.Constant) and isinstance(arg.value, str): + msg = arg.value + lineno = getattr(Node, 'lineno', 0) + # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 + _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + raise Exception(f"#error: {msg} (line {lineno})") + elif FuncAttr in ('AsmInp', 'AsmOut'): + return ir.Constant(ir.IntType(32), 0) + elif FuncAttr == 'LLVMIR': + return self._HandleLLVMIRLlvm(Node) + elif FuncAttr in ('LInp', 'LOut'): + return ir.Constant(ir.IntType(32), 0) + # stdarg 模块的变参糖:stdarg.arg / stdarg.va_arg / stdarg.va_start / + # stdarg.va_end 命中与裸名同名内建**完全相同**的处理(stdarg 是编译器 + # 拦截的糖模块,不是真符号,与 c 模块的语法设施同一口径)。仅当 + # ModulePath 恰为 'stdarg' 时进入,不会抢掉其它模块/用户模块的调用。 + if ModulePath == 'stdarg': + if FuncAttr == 'arg': + return self.Trans.ExprBuiltinHandle._HandleArgLlvm(Node.args) + elif FuncAttr == 'va_arg': + return self.Trans.ExprBuiltinHandle._HandleVaArgLlvm(Node.args) + elif FuncAttr == 'va_start': + return self.Trans.ExprBuiltinHandle._HandleVaStartLlvm(Node.args) + elif FuncAttr == 'va_end': + return self.Trans.ExprBuiltinHandle._HandleVaEndLlvm(Node.args) + if FuncAttr in Gen.structs: + result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) + if result is not None: + return result + if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and FuncAttr in self.Trans.ClassHandler._generic_class_templates: + return self._HandleGenericClassNewLlvm(Node, FuncAttr) + # 跨模块 REnum 变体构造:ClassName.Variant(args) + # 直接检查 Node.func.value 是否是 REnum 类名(不依赖变体短名注册) + if isinstance(Node.func.value, ast.Name): + RenumClassName: str = Node.func.value.id + if self.Trans.SymbolTable.has(RenumClassName): + RenumInfo = self.Trans.SymbolTable[RenumClassName] + if RenumInfo.IsRenum and hasattr(RenumInfo, 'RenumVariants') and RenumInfo.RenumVariants: + if FuncAttr in RenumInfo.RenumVariants: + TagValue: int = RenumInfo.RenumVariants.index(FuncAttr) + result = self._HandleREnumConstructLlvm(Node, RenumClassName, FuncAttr, TagValue) + if result is not None: + return result + # 跨模块 REnum 变体构造(**点号类名**写法):`mod.ClassName.Variant(args)` + # 现场:`llvmlite.LLVMType.Struct(fields, fcount, name)` —— Node.func.value + # 是 Attribute(llvmlite.LLVMType)而不是 Name,上一条判据(L903 的 + # isinstance(Node.func.value, ast.Name))漏掉 ⇒ 落到"模块函数 Struct"路径: + # 返回类型被当成普通函数返回值(实为 boxed 变体构造指针),且实参少一个 + # pool(arity 也不对)。此处与裸名写法同口径补齐,覆盖点号写法。 + if isinstance(Node.func.value, ast.Attribute): + _rc_attr: str = getattr(Node.func.value, 'attr', '') or '' + _rc_sym: Any = None + for _rc_key in (f"{ModulePath}.{_rc_attr}", _rc_attr): + if _rc_key and self.Trans.SymbolTable.has(_rc_key): + _rc_sym = self.Trans.SymbolTable[_rc_key] + break + if _rc_sym is not None and getattr(_rc_sym, 'IsRenum', False): + _rc_vars: Any = getattr(_rc_sym, 'RenumVariants', None) + if _rc_vars and FuncAttr in _rc_vars: + _rc_tag: int = _rc_vars.index(FuncAttr) + result = self._HandleREnumConstructLlvm(Node, _rc_attr, FuncAttr, _rc_tag) + if result is not None: + return result + if self.Trans.SymbolTable.has(FuncAttr): + SymInfo = self.Trans.SymbolTable[FuncAttr] + if SymInfo.IsStruct: + if FuncAttr not in Gen.structs: + self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) + if FuncAttr in Gen.structs: + result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) + if result is not None: + return result + if SymInfo.IsEnumMember and SymInfo.EnumName: + # When called as module.func(args), check if module.func is a + # function/variable first. If so, skip REnum construction and + # let the function call path handle it. This fixes cases where + # a factory function shares a name with an REnum variant + # (e.g., llvmlite.Ptr vs LLVMType.Ptr). + _SkipRenum: bool = False + if ModulePath: + _FullKey = f"{ModulePath}.{FuncAttr}" + _FullSym = self.Trans.SymbolTable.lookup(_FullKey) + if _FullSym and (_FullSym.IsFunction or _FullSym.IsVariable): + _SkipRenum = True + if not _SkipRenum: + EnumName = SymInfo.EnumName + if self.Trans.SymbolTable.has(EnumName): + EnumInfo = self.Trans.SymbolTable[EnumName] + if EnumInfo.IsRenum: + result = self._HandleREnumConstructLlvm(Node, EnumName, FuncAttr, SymInfo.value) + if result is not None: + return result + FullAttrKey = f"{ModulePath}.{FuncAttr}" + if self.Trans.SymbolTable.has(FullAttrKey): + SymInfo = self.Trans.SymbolTable[FullAttrKey] + if SymInfo.IsStruct: + if FuncAttr not in Gen.structs: + self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) + if FuncAttr in Gen.structs: + result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) + if result is not None: + return result + # 跨模块 REnum 变体构造:FullName 查找(如 LLVMType.Int) + if SymInfo.IsEnumMember and SymInfo.EnumName: + EnumName = SymInfo.EnumName + if self.Trans.SymbolTable.has(EnumName): + EnumInfo = self.Trans.SymbolTable[EnumName] + if EnumInfo.IsRenum: + result = self._HandleREnumConstructLlvm(Node, EnumName, FuncAttr, SymInfo.value) + if result is not None: + return result + # 模块名与类名同名优先规则:`import lib.core.X as X` 且模块内 + # 有同名 class X 时,`X.func()` 应解析为模块函数(mangle + # {sha1}.func,对应模块级 define)而非类静态方法(mangle + # {sha1}.X.func——模块函数没有带类名的 define,链接报 undefined + # symbol,如 CompilerFactory.get_perm_pool)。判断依据:SymbolTable + # 中 base 名是否注册了 IsModuleAlias(import 处理插入的别名标记)。 + # 类构造调用(X.X(args))不受影响:FuncAttr==类名 时上方 591/639 + # 行的 struct 路径已先行返回。 + _is_mod_alias_base = False + if isinstance(Node.func.value, ast.Name): + _base_alias_sym = self.Trans.SymbolTable.lookup(Node.func.value.id) + if _base_alias_sym is not None and getattr(_base_alias_sym, 'IsModuleAlias', False): + _is_mod_alias_base = True + if is_class_name and not _is_mod_alias_base: + ClassName = Node.func.value.id + result = self._HandleStaticMethodCallLlvm(Node, ClassName, FuncAttr) + if result is not None: + return result + FullAttrKey = f"{ModulePath}.{FuncAttr}" + if self.Trans.SymbolTable.has(FullAttrKey): + SymInfo = self.Trans.SymbolTable[FullAttrKey] + if SymInfo.IsTypedef: + return ir.Constant(ir.IntType(32), 0) + if SymInfo.IsStruct: + if FuncAttr not in Gen.structs: + self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) + if FuncAttr in Gen.structs: + result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) + if result is not None: + return result + if FuncAttr in Gen.structs: + result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) + if result is not None: + return result + if ModulePath == 't': + MethodResult_t = self._HandleMethodCallLlvm(Node) + if MethodResult_t is not None: + return MethodResult_t + raise Exception(f"Undefined method: 't.{FuncAttr}'") + if isinstance(Node.func.value, ast.Attribute): + inner_attr = Node.func.value.attr + if inner_attr in Gen.structs or self.Trans.SymbolTable.is_struct(inner_attr): + static_result = self._HandleStaticMethodCallLlvm(Node, inner_attr, FuncAttr) + if static_result is not None: + return static_result + MethodResult_attr = self._HandleMethodCallLlvm(Node) + is_zero_attr = isinstance(MethodResult_attr, ir.Constant) and isinstance(MethodResult_attr.type, ir.IntType) and MethodResult_attr.constant == 0 + if MethodResult_attr is not None and not is_zero_attr: + return MethodResult_attr + result = self._HandleExternalCallLlvm(Node, FuncAttr, ModulePath) + if result is not None: + return result + MethodResult = self._HandleMethodCallLlvm(Node) + IsZeroResult = isinstance(MethodResult, ir.Constant) and isinstance(MethodResult.type, ir.IntType) and MethodResult.constant == 0 + if IsZeroResult and FuncAttr is not None: + mangled_name = Gen._mangle_func_name(FuncAttr, ModulePath if ModulePath and ModulePath != 't' else None) + if Gen._has_function(mangled_name): + CallArgs = [] + for ArgIdx, arg in enumerate(Node.args, start=1): + # 禁止静默丢弃求值为空的实参(会导致后续整体前移一格, + # 报出与真因无关的类型不匹配,见 _RequireArgValue 说明) + CallArgs.append(self._RequireArgValue( + self.HandleExprLlvm(arg), FuncAttr, ArgIdx, arg, Node)) + return Gen.builder.call(Gen._get_function(mangled_name), CallArgs, name=f"call_{FuncAttr}") + if MethodResult is None or IsZeroResult: + imported = getattr(self.Trans, '_ImportedModules', set()) + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + IsKnown = (ModulePath in imported or ModulePath in self.Trans.SymbolTable.import_aliases or + ModulePath in ModuleSha1Map) + if not IsKnown and ModulePath and '.' in ModulePath: + LastPart = ModulePath.split('.')[-1] + IsKnown = (LastPart in imported or LastPart in self.Trans.SymbolTable.import_aliases or + LastPart in ModuleSha1Map) + # 最后尝试: 用 FuncAttr 直接查找 Gen.functions(_find_function 会遍历 SHA1 前缀) + if FuncAttr and ModulePath and ModulePath not in {'t', 'c'}: + # _ctor_module_sha1 已在函数顶部统一获取,此处无需重复 + # 先检查 FuncAttr 是否是函数/变量,如果是则跳过 struct 声明 + _attr_sym = self.Trans.SymbolTable.lookup(FuncAttr) + _is_func_or_var = _attr_sym and (_attr_sym.IsFunction or _attr_sym.IsVariable) + if not _is_func_or_var and ModulePath: + _full_key = f"{ModulePath}.{FuncAttr}" + _full_sym = self.Trans.SymbolTable.lookup(_full_key) + if _full_sym and (_full_sym.IsFunction or _full_sym.IsVariable): + _is_func_or_var = True + # 优先检查 FuncAttr 是否为 struct/类构造器 + if not _is_func_or_var and FuncAttr not in Gen.structs: + self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) + if not _is_func_or_var and FuncAttr in Gen.structs: + result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) + if result is not None: + return result + # 检查 FullAttrKey 是否为 struct(如 hashlib.md5) + if ModulePath: + FullAttrKey = f"{ModulePath}.{FuncAttr}" + if self.Trans.SymbolTable.has(FullAttrKey): + FullSymInfo = self.Trans.SymbolTable[FullAttrKey] + if FullSymInfo.IsStruct or FullSymInfo.IsCpythonObject: + if FuncAttr not in Gen.structs: + self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1) + if FuncAttr in Gen.structs: + result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) + if result is not None: + return result + fallback_func = Gen._find_function(FuncAttr) + if fallback_func is not None: + CallArgs = [] + for ArgIdx, arg in enumerate(Node.args, start=1): + # 同 1039:禁止静默丢弃(会导致后续前移一格) + CallArgs.append(self._RequireArgValue( + self.HandleExprLlvm(arg), FuncAttr, ArgIdx, arg, Node)) + self._append_eh_msg_out_arg(CallArgs, fallback_func, Gen) + result = Gen.builder.call(fallback_func, CallArgs, name=f"call_{FuncAttr}") + return self._check_eh_return(result, FuncAttr, Gen, called_func=fallback_func) + # 尝试从 stub 按需加载函数声明 + if ModulePath in ModuleSha1Map: + sha1 = ModuleSha1Map[ModulePath] + stub_mangled = f"{sha1}.{FuncAttr}" + try: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(stub_mangled, Gen) + except Exception: + stub_func_type = None + if not stub_func_type: + try: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(FuncAttr, Gen) + except Exception: + stub_func_type = None + if stub_func_type: + decl_name = stub_mangled if stub_mangled in getattr(self.Trans.ImportHandler, '_stub_func_cache', {}) else FuncAttr + func_decl = self.GetOrCreateStubFuncDecl(decl_name, stub_func_type, Gen, alias=FuncAttr) + CallArgs = [] + for ArgIdx, arg in enumerate(Node.args, start=1): + # 同 1039:禁止静默丢弃(会导致后续前移一格) + CallArgs.append(self._RequireArgValue( + self.HandleExprLlvm(arg), FuncAttr, ArgIdx, arg, Node)) + self._append_eh_msg_out_arg(CallArgs, func_decl, Gen) + result = Gen.builder.call(func_decl, CallArgs, name=f"call_{FuncAttr}") + return self._check_eh_return(result, FuncAttr, Gen, called_func=func_decl) + if ModulePath and IsKnown: + raise Exception(f"Undefined symbol: '{ModulePath}.{FuncAttr}'") + if ModulePath and ModulePath not in {'t', 'c'} and not IsKnown: + raise Exception(f"Unknown module: '{ModulePath}' (undefined symbol: '{ModulePath}.{FuncAttr}')") + if not ModulePath and FuncAttr: + raise Exception(f"Undefined method: '{FuncAttr}'") + return MethodResult if MethodResult is not None else ir.Constant(ir.IntType(32), 0) + if isinstance(Node.func, ast.BinOp) and isinstance(Node.func.op, ast.BitOr): + return self._HandleTypeUnionCastLlvm(Node) + raise Exception(f"Unsupported call expression") + + def _ensure_struct_declared(self, class_name: str, module_sha1: str | None = None) -> None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + # 如果名称是函数或变量,不应创建 struct + SymInfo = self.Trans.SymbolTable.lookup(class_name) + if SymInfo and (SymInfo.IsFunction or SymInfo.IsVariable): + return + # 跨模块类型引用:如果提供了 module_sha1,优先检查带 SHA1 前缀的完整名 + full_key: str | None = f"{module_sha1}.{class_name}" if module_sha1 else None + if full_key and full_key in Gen.structs: + existing = Gen.structs[full_key] + if isinstance(existing, ir.IdentifiedStructType) and (existing.elements is None or len(existing.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) + return + if class_name in Gen.structs: + existing = Gen.structs[class_name] + if isinstance(existing, ir.IdentifiedStructType) and (existing.elements is None or len(existing.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) + existing = Gen.structs.get(class_name, existing) + if isinstance(existing, ir.IdentifiedStructType) and existing.elements is not None and len(existing.elements) > 0: + return + else: + # 短名已存在但 SHA1 可能不匹配:如果 module_sha1 与现有 struct 的 SHA1 不一致, + # 需要用正确的 SHA1 创建独立 struct + if module_sha1: + _existing_sha1: str = Gen._extract_struct_sha1(existing) + if _existing_sha1 and _existing_sha1 != module_sha1: + # SHA1 不匹配,用正确的 SHA1 创建 + correct_st = Gen._get_or_create_struct(class_name, source_sha1=module_sha1) + self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) + return + SymInfo = self.Trans.SymbolTable.lookup(class_name) + if SymInfo and SymInfo.Members: + member_types = [] + if isinstance(SymInfo.Members, dict): + for member_name, member_info in SymInfo.Members.items(): + if isinstance(member_info, CTypeInfo): + mt = member_info.ToLLVM(Gen) + elif isinstance(member_info, str): + mt = Gen._CType2LLVM(member_info, '*' in member_info) + else: + mt = Gen._CType2LLVM(str(member_info) if member_info else 'int', False) + if isinstance(mt, ir.VoidType): + mt = ir.IntType(8) + member_types.append(mt) + else: + for item in SymInfo.Members: + if isinstance(item, (list, tuple)) and len(item) == 2: + member_name, member_type_str = item + mt = Gen._CType2LLVM(member_type_str, '*' in member_type_str) + if isinstance(mt, ir.VoidType): + mt = ir.IntType(8) + member_types.append(mt) + if member_types: + st = Gen._get_or_create_struct(class_name, source_sha1=module_sha1) + if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): + st.set_body(*member_types) + else: + Gen._get_or_create_struct(class_name, source_sha1=module_sha1) + else: + if class_name not in Gen.structs and not (full_key and full_key in Gen.structs): + self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1) + Gen._get_or_create_struct(class_name, source_sha1=module_sha1) + + def _HandleStaticMethodCallLlvm(self, Node: ast.Call, ClassName: str, MethodName: str) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + class_sha1 = struct_sha1_map.get(ClassName) + if class_sha1: + MangledName = f'{class_sha1}.{ClassName}.{MethodName}' + else: + MangledName = f'{ClassName}.{MethodName}' + func = Gen._find_function(MangledName) + if not func: + func = Gen._find_function(f'{MangledName}__') + if not func: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(MangledName, Gen) + if stub_func_type: + func = self.GetOrCreateStubFuncDecl(MangledName, stub_func_type, Gen) + if not func and class_sha1: + plain_name = f'{ClassName}.{MethodName}' + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen) + if stub_func_type: + func = self.GetOrCreateStubFuncDecl(MangledName, stub_func_type, Gen) + if not func: + if ClassName not in Gen.structs: + self._ensure_struct_declared(ClassName) + SymInfo = self.LookupFunctionSymbol(MethodName, module_path=ClassName) + if SymInfo and SymInfo.IsFunction: + sig = self.BuildLLVMFuncTypeFromSig(SymInfo, Gen) + if sig is not None: + ret_type, llvm_param_types, _ = sig + is_static = FuncMeta.STATIC_METHOD in SymInfo.MetaList + if ClassName in Gen.structs and not is_static: + llvm_param_types.insert(0, ir.PointerType(Gen.structs[ClassName])) + func_type = ir.FunctionType(ret_type, llvm_param_types) + # 走唯一幂等入口:module.globals 已有同名函数(stub / 调用点先建) + # 时复用,避免 llvmlite DuplicatedNameError;类型不同则 fail-fast。 + func = Gen._CreateOrReuseFuncDecl(MangledName, func_type) + # 方法实际定义类(多继承 this 调整用) + method_anc_st: str | None = None + # 若 MangledName 只有 stub 声明(无函数体),也走 MRO 回退找定义类方法 + if not func or len(list(func.blocks)) == 0: + # 多继承:沿 MRO 祖先链查找(先声明父类优先) + for anc in Gen._get_mro_chain(ClassName)[1:]: + parent_mangled = Gen._mangle_name(f'{anc}.{MethodName}') + parent_func = Gen.functions.get(parent_mangled) + if parent_func: + func = parent_func + method_anc_st = anc + break + parent_func = Gen._find_function(f'{anc}.{MethodName}') + if parent_func: + func = parent_func + method_anc_st = anc + break + if not func: + return None + CallArgs = [] + SymInfo = self.LookupFunctionSymbol(MethodName, module_path=ClassName) + is_static_call = SymInfo is not None and FuncMeta.STATIC_METHOD in SymInfo.MetaList + is_classmethod_call = SymInfo is not None and FuncMeta.CLASS_METHOD in SymInfo.MetaList + # @classmethod: 在参数列表前插入 cls(栈上分配的类实例指针) + if is_classmethod_call and ClassName in Gen.structs: + ClsPtr = Gen._alloca(Gen.structs[ClassName], name=f"cls_{ClassName}") + CallArgs.append(ClsPtr) + # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度 + # (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量实参走原路径。 + _arg_off: int = len(CallArgs) + for _ai, arg in enumerate(Node.args): + _pt: ir.Type | None = None + if func is not None: + _ft_args: list[ir.Type] = func.function_type.args + if _ai + _arg_off < len(_ft_args): + _pt = _ft_args[_ai + _arg_off] + ArgVal = self.HandleExprLlvm( + arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)) + if ArgVal: + CallArgs.append(ArgVal) + if (ClassName not in Gen.class_vtable + and ClassName not in Gen._cross_module_vtable_classes + and class_sha1): + self._try_register_vtable_ondemand(ClassName, class_sha1, Gen) + # 跨模块多继承:确保 class_parents 可用(MRO 回退需要)。 + # 如 HandlesType 委托调用 Generator 的方法——Generator 定义在 + # 另一模块,class_parents 未随 import 填充,需从 vpi 按需加载。 + self._EnsureClassParentsLoaded(ClassName, Gen) + if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods and CallArgs and not is_static_call: + methods = Gen.class_methods[ClassName] + if class_sha1: + FullMethodName = f'{class_sha1}.{ClassName}.{MethodName}' + else: + FullMethodName = f'{ClassName}.{MethodName}' + method_idx = -1 + for mi, mn in enumerate(methods): + if mn == FullMethodName or mn == MethodName or mn.endswith(f'.{MethodName}'): + method_idx = mi + break + if method_idx >= 0: + ObjVal = CallArgs[0] + if self._is_char_pointer(ObjVal): + if ClassName in Gen.structs: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_static") + # 多继承 this 调整(仅调整 self 参数;vtable 槽位读取用原始 ObjVal, + # 因为 vptr 总在对象首字段,与 VPC _do_virtual_call 一致) + self_arg_st = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc_st) + CallArgs[0] = self_arg_st + VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}_static") + VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_static") + VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) + VtableTyped = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}_static") + MethodPtrAddr = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_ptr_addr_{MethodName}_static") + MethodPtrI8 = Gen._load(MethodPtrAddr, name=f"vmethod_ptr_{MethodName}_static") + FuncPtrType = ir.PointerType(func.function_type) + MethodPtrTyped = Gen.builder.bitcast(MethodPtrI8, FuncPtrType, name=f"vmethod_typed_{MethodName}_static") + self._fill_default_args(CallArgs, func, FullMethodName) + self._append_eh_msg_out_arg(CallArgs, func, Gen) + adjusted = Gen._adjust_args(CallArgs, func) + result = Gen.builder.call(MethodPtrTyped, adjusted, name=f"vcall_{FullMethodName}") + self._check_eh_return(result, FullMethodName, Gen, called_func=func) + return result + self._append_eh_msg_out_arg(CallArgs, func, Gen) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表), + # 此处的显式取址已删除,避免 'always' 模式被重复应用(不幂等)。 + adjusted = Gen._adjust_args(CallArgs, func) + result = Gen.builder.call(func, adjusted, name=f"call_{MangledName}") + self._check_eh_return(result, MangledName, Gen, called_func=func) + return result + + def _HandleInstanceCallLlvm(self, Node: ast.Call, VarName: str, ClassName: str) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + ObjVal = self.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load())) + if not ObjVal: + raise Exception(f"Cannot evaluate object '{VarName}' for __call__") + if self._is_char_pointer(ObjVal): + if ClassName in Gen.structs: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + CallArgs = [ObjVal] + CallFuncName = f'{ClassName}.__call__' + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + class_sha1 = struct_sha1_map.get(ClassName) + if class_sha1: + CallFuncName = f'{class_sha1}.{ClassName}.__call__' + # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 + # CallArgs[0] 为 self ⇒ 第 i 个用户实参对应形参 i+1。 + _cfunc: ir.Function | None = (Gen._get_function(CallFuncName) + if Gen._has_function(CallFuncName) else None) + _ptypes: list[ir.Type] = _cfunc.function_type.args if _cfunc is not None else [] + for _ai, arg in enumerate(Node.args): + _pt: ir.Type | None = _ptypes[_ai + 1] if _ai + 1 < len(_ptypes) else None + ArgVal = self.HandleExprLlvm( + arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)) + if ArgVal: + CallArgs.append(ArgVal) + if not Gen._has_function(CallFuncName): + raise Exception(f"Class '{ClassName}' has no __call__ method") + func = Gen._get_function(CallFuncName) + self._fill_default_args(CallArgs, func, CallFuncName) + self._ApplyKeywordArgs(Node, CallArgs, func, CallFuncName, self_offset=1) + self._append_eh_msg_out_arg(CallArgs, func, Gen) + adjusted = Gen._adjust_args(CallArgs, func) + result = Gen.builder.call(func, adjusted, name=f"call_{CallFuncName}") + self._check_eh_return(result, CallFuncName, Gen, called_func=func) + return result + + def _make_virtual_attr_call(self, Node: ast.Call, module_name: str, attr_name: str) -> ast.Call: + virtual_func = ast.Attribute( + value=ast.Name(id=module_name, ctx=ast.Load()), + attr=attr_name, + ctx=ast.Load() + ) + new_node = ast.Call( + func=virtual_func, + args=Node.args, + keywords=Node.keywords + ) + ast.copy_location(new_node, Node) + return new_node + + def _get_ModulePath(self, node: ast.AST) -> str | None: + parts = [] + while isinstance(node, ast.Attribute): + parts.append(node.attr) + node = node.value + if isinstance(node, ast.Name): + parts.append(node.id) + parts.reverse() + return '.'.join(parts) if parts else None + + def _IsModuleQualifiedGlobalChain(self, base_name: str, receiver: ast.AST) -> bool: + """判定"模块限定全局变量"接收者链(链根=模块别名、次段=该模块全局变量)。 + + 与调用点既有的"链根是已知变量(self / 局部变量 / 全局变量 / struct 变量)" + 判据互补,补的是**模块限定全局变量**这一形态: + `import lib.core.Gen.GenApi as GenApi` 后写 + `GenApi._g_gen.ClassGenBase._collect_parents(...)`——链根 `GenApi` 是模块 + 别名(不在 var_struct_class / variables / global_vars 中),次段 `_g_gen` + 是该模块的全局变量。缺此判定时该接收者被当作"模块.类"引用,落到下方 + `inner_attr in Gen.structs` 分支走 _HandleStaticMethodCallLlvm——该路径按 + 静态方法口径处理(**不前置接收者 self**),实参整体错位一格 + (trans: Translator* 对上 max_parents: i32),触发 fail-fast + "指针类型 ... 不能隐式转换为整数参数 i32";不报错的同形调用(实参全为指针) + 则静默生成缺 self 的错误调用。 + + 判据只有两段:链根是模块(SymbolTable.import_aliases / IsModuleAlias 符号 / + ModuleSha1Map / _ImportedModules 任一命中),且**紧邻根名的那一段属性**不是 + 类型/函数类符号(类 / 枚举 / REnum / 枚举成员 / 函数 / CDefine / 异常类 / + 模块别名)——即该段是"对象"(全局变量;跨模块全局在本符号表里登记为 + IsTypedef 的空类型条目,故 IsTypedef 不在排除集内)。紧随段是类 + (`Module.Class.static_method(...)`)时返回 False,静态方法口径保持不变。 + """ + symbol_table: Any = self.Trans.SymbolTable + # 紧邻根名的那一段属性:GenApi._g_gen.ClassGenBase → '_g_gen' + first_attr: str | None = None + cursor: ast.AST = receiver + while isinstance(cursor, ast.Attribute): + first_attr = cursor.attr + cursor = cursor.value + if not first_attr or not isinstance(cursor, ast.Name): + return False + # 链根是不是模块:优先别名表(import ... as X 的 X → 模块全名), + # 其次符号表里的模块别名标记,最后模块表(其键含短名)兜底。 + module_path: str = symbol_table.resolve_alias(base_name) + if module_path != base_name: + is_module: bool = True + else: + alias_entry: Any = symbol_table.lookup(base_name) + if alias_entry is not None and getattr(alias_entry, 'IsModuleAlias', False): + module_path = getattr(alias_entry, 'ResolvedModule', '') or '' + is_module = bool(module_path) + else: + is_module = ( + base_name in getattr(self.Trans.LlvmGen, 'ModuleSha1Map', {}) + or base_name in getattr(self.Trans, '_ImportedModules', set())) + if not is_module: + return False + segment: Any = symbol_table.lookup(f'{module_path}.{first_attr}') if module_path else None + if segment is None: + segment = symbol_table.lookup(first_attr) + if segment is None: + return False + # 该段是"类型/函数类"符号 ⇒ 属于 `Module.Class...` 引用,不按实例链处理 + # (同名枚举成员/异常类在符号表里与类名冲突,一并排除,避免误判)。 + for _type_flag in ('IsStruct', 'IsEnum', 'IsRenum', 'IsUnion', 'IsEnumMember', + 'IsFunction', 'IsDefine', 'IsExceptionClass', 'IsModuleAlias'): + if getattr(segment, _type_flag, False): + return False + return True + + def _HandleLLVMIRLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not Node.args: + return ir.Constant(ir.IntType(32), 0) + + all_ops = [] + input_ops = [] + output_targets = [] + operand_seq = 0 + + ret_type = ir.IntType(32) + if len(Node.args) >= 2: + ret_type_arg = Node.args[1] + inferred = self.Trans.ExprAsmHandle._infer_expr_llvm_type(ret_type_arg) + if inferred: + ret_type = inferred + + ir_template = "" + first_arg = Node.args[0] + + if isinstance(first_arg, ast.JoinedStr): + parts = [] + for value in first_arg.values: + if isinstance(value, ast.Constant) and isinstance(value.value, str): + parts.append(value.value) + elif isinstance(value, ast.FormattedValue): + expr = value.value + # c.LInp/c.LOut 与裸名 irinp/irout 等价(见 _intrinsic_marker) + _marker: str | None = self.Trans.ExprAsmHandle._intrinsic_marker(expr) + _is_c_qualified: bool = (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute) + and isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c') + if _marker == 'LInp': + if expr.args: + val = self.HandleExprLlvm(expr.args[0]) + if val: + input_ops.append(val) + all_ops.append(('inp', len(input_ops) - 1)) + parts.append(f'%__OP{operand_seq}__') + operand_seq += 1 + elif _marker == 'LOut': + if expr.args: + target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0]) + if target_ptr: + val = target_ptr + else: + val = self.HandleExprLlvm(expr.args[0]) + if val: + output_targets.append(val) + all_ops.append(('out', len(output_targets) - 1)) + parts.append(f'%__OP{operand_seq}__') + operand_seq += 1 + elif not _is_c_qualified: + # c.* 中非 LInp/LOut 的形态不收集(保持原行为) + fallback = self.HandleExprLlvm(expr) + if fallback: + input_ops.append(fallback) + all_ops.append(('inp', len(input_ops) - 1)) + parts.append(f'%__OP{operand_seq}__') + operand_seq += 1 + ir_template = ''.join(parts) + elif isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): + ir_template = first_arg.value + + return self._EmitLLVMIRInstruction(Gen, ir_template, input_ops, output_targets, all_ops, ret_type) + + def _EmitLLVMIRInstruction(self, Gen: LlvmGeneratorMixin, ir_template: str, input_ops: list[ir.Value], output_targets: list[ir.Value], all_ops: list[tuple], ret_type: ir.Type) -> ir.Value: + template = ir_template.strip() + + def resolve_op(idx): + if idx < len(all_ops): + kind, local_idx = all_ops[idx] + if kind == 'inp' and local_idx < len(input_ops): + return input_ops[local_idx] + elif kind == 'out' and local_idx < len(output_targets): + return output_targets[local_idx] + return None + + assign_match = _RE_OP_ASSIGN.match(template) + if assign_match: + template = assign_match.group(2).strip() + + binop_map = { + 'add': 'add', 'fadd': 'fadd', 'sub': 'sub', 'fsub': 'fsub', + 'mul': 'mul', 'fmul': 'fmul', 'udiv': 'udiv', 'sdiv': 'sdiv', + 'fdiv': 'fdiv', 'urem': 'urem', 'srem': 'srem', 'frem': 'frem', + 'shl': 'shl', 'lshr': 'lshr', 'ashr': 'ashr', + 'and': 'and_', 'or': 'or_', 'xor': 'xor_', + } + + icmp_match = _RE_ICMP.match(template) + if icmp_match: + pred: str = icmp_match.group(1) + op1 = resolve_op(int(icmp_match.group(3))) + op2 = resolve_op(int(icmp_match.group(4))) + if op1 and op2: + # LLVM IR 谓词(eq/ne/sgt/sge/slt/sle/ugt/uge/ult/ule)映射到 llvmlite + # llvmlite 的 icmp_signed/icmp_unsigned 只接受 '=='/'!='/'<'/'<='/'>'/>=' + _ICMP_PRED_MAP: dict[str, tuple[str, str]] = { + 'eq': ('==', 'signed'), 'ne': ('!=', 'signed'), + 'sgt': ('>', 'signed'), 'sge': ('>=', 'signed'), + 'slt': ('<', 'signed'), 'sle': ('<=', 'signed'), + 'ugt': ('>', 'unsigned'), 'uge': ('>=', 'unsigned'), + 'ult': ('<', 'unsigned'), 'ule': ('<=', 'unsigned'), + } + pred_info: tuple[str, str] | None = _ICMP_PRED_MAP.get(pred) + if pred_info: + llvmlite_pred: str + cmp_kind: str + llvmlite_pred, cmp_kind = pred_info + icmp_method: Any = Gen.builder.icmp_signed if cmp_kind == 'signed' else Gen.builder.icmp_unsigned + result = icmp_method(llvmlite_pred, op1, op2, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + fcmp_match = _RE_FCMP.match(template) + if fcmp_match: + pred_raw: str = fcmp_match.group(1).strip() + # 分离 fast 修饰符(可能是 "oeq fast" 或 "fast oeq") + is_fast: bool = 'fast' in pred_raw + pred: str = pred_raw.replace('fast', '').strip() + op1 = resolve_op(int(fcmp_match.group(3))) + op2 = resolve_op(int(fcmp_match.group(4))) + if op1 and op2: + # LLVM IR fcmp 谓词(oeq/ogt/olt/ord/uno/ueq 等)映射到 llvmlite + _FCMP_PRED_MAP: dict[str, tuple[str, str]] = { + 'oeq': ('==', 'ordered'), 'ogt': ('>', 'ordered'), + 'oge': ('>=', 'ordered'), 'olt': ('<', 'ordered'), + 'ole': ('<=', 'ordered'), 'one': ('!=', 'ordered'), + 'ord': ('ord', 'ordered'), 'uno': ('uno', 'unordered'), + 'ueq': ('==', 'unordered'), 'ugt': ('>', 'unordered'), + 'uge': ('>=', 'unordered'), 'ult': ('<', 'unordered'), + 'ule': ('<=', 'unordered'), 'une': ('!=', 'unordered'), + } + fcmp_info: tuple[str, str] | None = _FCMP_PRED_MAP.get(pred) + if fcmp_info: + llvmlite_pred: str + cmp_kind: str + llvmlite_pred, cmp_kind = fcmp_info + fcmp_method: Any = Gen.builder.fcmp_ordered if cmp_kind == 'ordered' else Gen.builder.fcmp_unordered + result = fcmp_method(llvmlite_pred, op1, op2, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + for op_name, method_name in binop_map.items(): + pattern = re.compile( + rf'{op_name}\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__', + re.IGNORECASE if op_name in ('and', 'or', 'xor') else 0 + ) + m = pattern.match(template) + if m: + op1 = resolve_op(int(m.group(2))) + op2 = resolve_op(int(m.group(3))) + if op1 and op2: + method = getattr(Gen.builder, method_name, None) + if method: + result = method(op1, op2, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + select_match = _RE_SELECT.match(template) + if select_match: + cond = resolve_op(int(select_match.group(1))) + true_val = resolve_op(int(select_match.group(3))) + false_val = resolve_op(int(select_match.group(5))) + if cond and true_val and false_val: + result = Gen.builder.select(cond, true_val, false_val, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + cast_map = { + 'zext': 'zext', 'sext': 'sext', 'trunc': 'trunc', + 'bitcast': 'bitcast', 'inttoptr': 'inttoptr', 'ptrtoint': 'ptrtoint', + 'uitofp': 'uitofp', 'sitofp': 'sitofp', 'fptoui': 'fptoui', 'fptosi': 'fptosi', + 'fpext': 'fpext', 'fptrunc': 'fptrunc', + } + for op_name, method_name in cast_map.items(): + pattern = re.compile(rf'{op_name}\s+(\w+)\s+%__OP(\d+)__\s+to\s+(\w+)') + m = pattern.match(template) + if m: + src_val = resolve_op(int(m.group(2))) + dest_type_str = m.group(3) + dest_type = self._ParseLLVMType(Gen, dest_type_str) + if src_val and dest_type: + method = getattr(Gen.builder, method_name, None) + if method: + result = method(src_val, dest_type, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + Load_match = _RE_LOAD.match(template) + if Load_match: + ptr_val = resolve_op(int(Load_match.group(3))) + if ptr_val: + result = Gen.builder.load(ptr_val, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + store_match = _RE_STORE.match(template) + if store_match: + val = resolve_op(int(store_match.group(2))) + ptr = resolve_op(int(store_match.group(4))) + if val and ptr: + Gen.builder.store(val, ptr) + return ir.Constant(ir.IntType(32), 0) + + gep_match = _RE_GEP.match(template) + if gep_match: + ptr_val = resolve_op(int(gep_match.group(3))) + indices_str = gep_match.group(4) + if ptr_val: + indices = [] + for idx_part in _RE_COMMA_WS.split(indices_str): + idx_part = idx_part.strip() + idx_m = _RE_OP_REF.match(idx_part) + if idx_m: + idx_op = resolve_op(int(idx_m.group(1))) + if idx_op: + indices.append(idx_op) + else: + try: + indices.append(ir.Constant(ir.IntType(32), int(idx_part))) + except ValueError: + break + if indices: + result = Gen.builder.gep(ptr_val, indices, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + neg_match = _RE_NEG.match(template) + if neg_match: + op = resolve_op(int(neg_match.group(3))) + if op: + zero = ir.Constant(op.type, int(neg_match.group(2))) + result = Gen.builder.sub(zero, op, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + not_match = _RE_NOT.match(template) + if not_match: + op = resolve_op(int(not_match.group(3))) + if op: + mask = ir.Constant(op.type, int(not_match.group(2))) + result = Gen.builder.xor_(op, mask, name="llvmir_result") + return self._StoreLLVMIROutputs(Gen, result, output_targets) + + call_match = _RE_CALL.match(template) + if call_match: + call_ret_str = call_match.group(1) + func_name = call_match.group(2) + args_str = call_match.group(3) + call_args = [] + for arg_part in _RE_COMMA_WS.split(args_str): + arg_part = arg_part.strip() + typed_op = _RE_TYPED_OP.match(arg_part) + if typed_op: + expected_type_str = typed_op.group(1) + op_idx = int(typed_op.group(2)) + val = resolve_op(op_idx) + if val: + val = self._ConvertLLVMIRType(Gen, val, expected_type_str) + if val: + call_args.append(val) + else: + const_m = _RE_CONST.match(arg_part) + if const_m: + const_type = self._ParseLLVMType(Gen, const_m.group(1)) + if const_type: + call_args.append(ir.Constant(const_type, int(const_m.group(2)))) + if call_args: + call_ret_type = self._ParseLLVMType(Gen, call_ret_str) or ir.VoidType() + func_type = ir.FunctionType(call_ret_type, [a.type for a in call_args]) + if func_name.startswith('llvm.'): + existing = Gen.module.globals.get(func_name) + if existing: + func = existing + else: + try: + func = Gen.module.declare_intrinsic(func_name, [a.type for a in call_args]) + except Exception: + func = Gen._CreateOrReuseFuncDecl(func_name, func_type) + else: + existing = Gen.module.globals.get(func_name) + if existing: + func = existing + else: + func = Gen._CreateOrReuseFuncDecl(func_name, func_type) + result = Gen.builder.call(func, call_args, name="llvmir_result") + if output_targets: + return self._StoreLLVMIROutputs(Gen, result, output_targets) + if isinstance(call_ret_type, ir.VoidType): + return ir.Constant(ir.IntType(32), 0) + return result + + if output_targets: + for i, target in enumerate(output_targets): + if isinstance(target, ir.AllocaInstr): + Gen.builder.store(ir.Constant(target.type.pointee, 0), target) + return ir.Constant(ir.IntType(32), 0) + + return ir.Constant(ret_type, 0) + + def _StoreLLVMIROutputs(self, Gen: LlvmGeneratorMixin, result: ir.Value, output_targets: list[ir.Value]) -> ir.Value: + for target in output_targets: + if isinstance(target, ir.AllocaInstr): + if target.type.pointee == result.type: + Gen.builder.store(result, target) + else: + i64t = ir.IntType(64) + if isinstance(result.type, ir.IntType) and result.type.width == 64 and isinstance(target.type.pointee, ir.PointerType): + ptr_val = Gen.builder.inttoptr(result, target.type.pointee) + Gen.builder.store(ptr_val, target) + elif isinstance(result.type, ir.PointerType) and isinstance(target.type.pointee, ir.IntType) and target.type.pointee.width == 64: + int_val = Gen.builder.ptrtoint(result, i64t) + Gen.builder.store(int_val, target) + else: + try: + cast_val = Gen.builder.bitcast(result, target.type.pointee) + Gen.builder.store(cast_val, target) + except Exception: + Gen.builder.store(result, target) + elif isinstance(target, (ir.GlobalVariable, ir.GEPInstr)): + if isinstance(target.type, ir.PointerType): + if target.type.pointee == result.type: + Gen.builder.store(result, target) + else: + try: + cast_val = Gen.builder.bitcast(result, target.type.pointee) + Gen.builder.store(cast_val, target) + except Exception: + Gen.builder.store(result, target) + return result + + def _ParseLLVMType(self, Gen: LlvmGeneratorMixin, type_str: str) -> ir.Type | None: + type_map = { + 'i1': ir.IntType(1), 'i8': ir.IntType(8), 'i16': ir.IntType(16), + 'i32': ir.IntType(32), 'i64': ir.IntType(64), 'i128': ir.IntType(128), + 'float': ir.FloatType(), 'double': ir.DoubleType(), + 'void': ir.VoidType(), + } + if type_str in type_map: + return type_map[type_str] + if type_str.endswith('*'): + pointee = self._ParseLLVMType(Gen, type_str[:-1]) + if pointee: + return ir.PointerType(pointee) + return None + + def _ConvertLLVMIRType(self, Gen: LlvmGeneratorMixin, val: ir.Value, expected_type_str: str) -> ir.Value | None: + expected_type = self._ParseLLVMType(Gen, expected_type_str.rstrip('*')) + if not expected_type: + return val + IsPtr = expected_type_str.endswith('*') + if IsPtr: + expected_ptr_type = ir.PointerType(expected_type) + if isinstance(val.type, ir.PointerType) and val.type != expected_ptr_type: + try: + return Gen.builder.bitcast(val, expected_ptr_type) + except Exception: + return val + elif isinstance(val.type, ir.IntType) and val.type.width == 64: + try: + return Gen.builder.inttoptr(val, expected_ptr_type) + except Exception: + return val + return val + if val.type == expected_type: + return val + if isinstance(val.type, ir.IntType) and isinstance(expected_type, ir.IntType): + if expected_type.width < val.type.width: + return Gen.builder.trunc(val, expected_type) + else: + return Gen.builder.zext(val, expected_type) + if isinstance(val.type, ir.PointerType) and isinstance(expected_type, ir.IntType): + if expected_type.width == 64: + return Gen.builder.ptrtoint(val, expected_type) + if isinstance(val.type, ir.IntType) and isinstance(expected_type, ir.PointerType): + if val.type.width == 64: + return Gen.builder.inttoptr(val, expected_type) + return val + + def _HandleExternalCallLlvm(self, Node: ast.Call, func_name: str, ModulePath: str | None) -> ir.Value | None: + Gen = self.Trans.LlvmGen + + # 解析 import 别名: 如 window -> vpsdk.window + if ModulePath and ModulePath not in ('c', 't'): + ModulePath = self.Trans.SymbolTable.resolve_alias(ModulePath) + + is_user_module = (ModulePath and ModulePath not in ('c', 't') and + (ModulePath in Gen.ModuleSha1Map or + ModulePath in getattr(self.Trans, '_ImportedModules', set()) or + ModulePath in self.Trans.SymbolTable.import_aliases)) + if ModulePath and ModulePath not in ('c', 't') and not is_user_module: + is_user_module = (ModulePath in Gen.ModuleSha1Map or + ModulePath.split('.')[-1] in Gen.ModuleSha1Map or + ModulePath.split('.')[-1] in getattr(self.Trans, '_ImportedModules', set())) + if is_user_module and ModulePath not in Gen.ModuleSha1Map and ModulePath not in getattr(self.Trans, '_ImportedModules', set()): + ModulePath = ModulePath.split('.')[-1] + + # 模块别名识别:`import lib.core.X as X`(模块内有同名类时,类名会 + # 注册进 SymbolTable 遮蔽模块判定——如 CompilerFactory 模块 + 同名类)。 + # import 处理(HandlesImports._EmitImportDeclarationsLlvm)在 SymbolTable + # 插入了 IsModuleAlias 的 AliasInfo,据此识别并补全完整路径,使 + # `X.get_perm_pool()` 正确解析为模块函数({sha1}.get_perm_pool)而非 + # 类方法({sha1}.X.get_perm_pool,链接 undefined symbol)。 + if ModulePath and ModulePath not in ('c', 't') and not is_user_module: + _al_last = ModulePath.split('.')[-1] + _al_sym = self.Trans.SymbolTable.lookup(_al_last) + if _al_sym and getattr(_al_sym, 'IsModuleAlias', False): + is_user_module = True + if ModulePath in self.Trans.SymbolTable.import_aliases: + ModulePath = self.Trans.SymbolTable.import_aliases[ModulePath] + # 未命中模块别名时不截断:保持 is_user_module=False 走下方裸名回退 + + if is_user_module: + mangled_name = Gen._mangle_func_name(func_name, ModulePath) + sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath) + is_inline = sym_info and (sym_info.IsInline or isinstance(sym_info.Storage, t.CInline)) + if is_inline and sym_info.InlineBody: + self._HandleInlineExpandLlvm(Node, sym_info) + return ir.Constant(ir.IntType(32), 1) + sym_info_exact = self.Trans.SymbolTable.lookup(f'{ModulePath}.{func_name}') + if sym_info_exact: + exact_params = sym_info_exact.FuncPtrParams + exact_param_names = [pn for pn, _ in exact_params] + # stub/LLVM 侧形参名多为 arg0/arg1 占位(SymbolTable._InsertFuncSymbol), + # 用它们无法把命名实参 `f(cap = 8, x = 1)` 绑到正确位置,会误报"缺少必需 + # 参数"。优先替换为源码/vpi 扫描得到的语义参数名(长度一致才替换, + # 避免 shape 不符时错配)。 + semantic_names = (self._LookupGlobalParamNames(mangled_name) + or self._LookupGlobalParamNames(func_name)) + if semantic_names and len(semantic_names) == len(exact_param_names): + exact_param_names = semantic_names + if exact_param_names: + provided = len(Node.args) + kw_provided = set() + for kw in Node.keywords: + if kw.arg and kw.arg in exact_param_names: + kw_provided.add(exact_param_names.index(kw.arg)) + # 获取默认参数信息,计算必需参数数量(与 _fill_default_args 一致的查找逻辑) + func_defaults: list | None = self._LookupGlobalDefaults(mangled_name) + if func_defaults is None: + func_defaults = self._LookupGlobalDefaults(func_name) + total_params: int = len(exact_param_names) + if func_defaults is None: + # 默认参数信息缺失(跨模块 stub 加载:vpi/stub 只含参数 + # 类型不含默认值;或并行编译时序导致定义模块的默认参数 + # 尚未注册到共享表)→ 无法准确计算必需参数数量, + # 跳过严格检查避免误报(如 HandlesType.fatal_error 的 + # err_type/col_start/col_end 默认值未同步时误报"缺 arg3")。 + # 同文件函数的参数检查由 FunctionDef 路径(L1983)覆盖。 + pass + elif len(func_defaults) > total_params: + # 表里的条目与当前函数长度不符(同名不同模块)→ 不做严格检查 + pass + else: + num_defaults: int = len(func_defaults) + min_args: int = total_params - num_defaults + # 只检查必需参数(有默认值的参数允许缺失) + for i in range(min_args): + if i >= provided and i not in kw_provided: + raise Exception(f"调用 {ModulePath}.{func_name}() 缺少必需参数 '{exact_param_names[i]}',该参数没有默认值") + elif ModulePath in Gen.ModuleSha1Map and len(Node.args) == 0: + func = Gen.functions.get(mangled_name) + if func and len(func.function_type.args) > 0: + raise Exception(f"调用 {ModulePath}.{func_name}() 缺少必需参数,函数需要 {len(func.function_type.args)} 个参数但未传入任何参数") + if Gen._has_function(mangled_name): + return self._HandleClosureCallLlvm(Node, mangled_name) + try: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) + except Exception: + stub_func_type = None + if not stub_func_type and mangled_name != func_name: + try: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(func_name, Gen) + except Exception: + stub_func_type = None + if stub_func_type: + if is_user_module: + decl_name = mangled_name + else: + decl_name = mangled_name if mangled_name in self.Trans.ImportHandler._stub_func_cache else func_name + self.GetOrCreateStubFuncDecl(decl_name, stub_func_type, Gen, alias=func_name) + return self._HandleClosureCallLlvm(Node, decl_name) + sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath) + if sym_info: + sig = self.BuildLLVMFuncTypeFromSig(sym_info, Gen, mangled_name=mangled_name, use_infer_fallback=True) + if sig is not None: + ret_type, llvm_param_types, sym_is_variadic = sig + self.GetOrCreateFuncDecl(mangled_name, ret_type, llvm_param_types, Gen, + is_variadic=sym_is_variadic, replace_name=func_name) + return self._HandleClosureCallLlvm(Node, mangled_name) + # 回退: 直接用 func_name 查找(_find_function 会遍历 SHA1 前缀) + fallback_func = Gen._find_function(func_name) + if fallback_func is not None: + return self._HandleClosureCallLlvm(Node, func_name) + return None + + if ModulePath == 'c': + if func_name == 'Asm': + return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) + elif func_name == 'Addr': + return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node) + elif func_name == 'Set': + return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node) + elif func_name == 'Load': + return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node) + elif func_name == 'Deref': + return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) + elif func_name == 'DerefAs': + return self._HandleCDerefAsLlvm(Node) + elif func_name == 'PtrToInt': + return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) + elif func_name in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) + elif func_name == 'CError': + msg = "compile-time error" + if Node.args: + arg = Node.args[0] + if isinstance(arg, ast.Constant) and isinstance(arg.value, str): + msg = arg.value + lineno = getattr(Node, 'lineno', 0) + # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 + _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + raise Exception(f"#error: {msg} (line {lineno})") + elif func_name in ('AsmInp', 'AsmOut'): + return ir.Constant(ir.IntType(32), 0) + elif func_name == 'LLVMIR': + return self._HandleLLVMIRLlvm(Node) + elif func_name in ('LInp', 'LOut'): + return ir.Constant(ir.IntType(32), 0) + + win32_apis = { + 'SetConsoleOutputCP': (ir.IntType(32), [ir.IntType(32)]), + 'SetConsoleCP': (ir.IntType(32), [ir.IntType(32)]), + 'GetConsoleOutputCP': (ir.IntType(32), []), + 'GetConsoleCP': (ir.IntType(32), []), + } + if ModulePath and 'win32console' in ModulePath and func_name in win32_apis: + ret_type, param_types = win32_apis[func_name] + if func_name not in Gen.functions: + # module.globals 已有同名声明(其它路径/依赖 stub 先建)→ 直接登记复用, + # 不重建(llvmlite NameScope 按名字去重,重建必抛 DuplicatedNameError) + g_ex_w32: Any = Gen.module.globals.get(func_name) + if g_ex_w32 is not None: + Gen.functions[func_name] = g_ex_w32 + else: + func_type = ir.FunctionType(ret_type, param_types) + func_decl = Gen._CreateOrReuseFuncDecl(func_name, func_type) + func_decl.linkage = 'external' + Gen.functions[func_name] = func_decl + call_args = [] + if Node.args: + arg_val = self.HandleExprLlvm(Node.args[0]) + if arg_val: + target_type = param_types[0] if param_types else ir.IntType(32) + if isinstance(arg_val.type, ir.IntType) and arg_val.type.width < target_type.width: + arg_val = Gen.builder.zext(arg_val, target_type, name="zext_win32_arg") + elif isinstance(arg_val.type, ir.IntType) and arg_val.type.width > target_type.width: + arg_val = Gen.builder.trunc(arg_val, target_type, name="trunc_win32_arg") + call_args.append(arg_val) + return Gen.builder.call(Gen.functions[func_name], call_args, name=f"call_{func_name}") + + if Gen._has_function(func_name): + return self._HandleClosureCallLlvm(Node, func_name) + + if func_name in self.Trans.FunctionDefCache: + return self._HandleClosureCallLlvm(Node, func_name) + + if func_name in self._C_LIB_FUNCS: + return self._HandleClosureCallLlvm(Node, func_name) + + mangled_name = Gen._mangle_func_name(func_name, ModulePath) + try: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) + except Exception: + stub_func_type = None + if not stub_func_type and mangled_name != func_name: + try: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(func_name, Gen) + except Exception: + stub_func_type = None + if stub_func_type: + decl_name = mangled_name if mangled_name in self.Trans.ImportHandler._stub_func_cache else func_name + self.GetOrCreateStubFuncDecl(decl_name, stub_func_type, Gen, alias=func_name, check_existing=False) + return self._HandleClosureCallLlvm(Node, decl_name) + + sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath or None) + if sym_info: + sig = self.BuildLLVMFuncTypeFromSig(sym_info, Gen) + if sig is not None: + ret_type, llvm_param_types, sym_is_variadic = sig + mangled_name = Gen._mangle_func_name(func_name, ModulePath) + self.GetOrCreateFuncDecl(mangled_name, ret_type, llvm_param_types, Gen, + is_variadic=sym_is_variadic, replace_name=func_name) + return self._HandleClosureCallLlvm(Node, mangled_name) + + return None + + def _HandleTypeUnionCastLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen = self.Trans.LlvmGen + if not Node.args: + return None + type_info = None + MergeException = None + try: + type_info = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.func) + except Exception as _e: + MergeException = _e + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + if isinstance(type_info, CTypeInfo): + # 主路径:MergeTypes 成功返回 CTypeInfo,沿用原有逻辑生成类型字符串 + target_type_str = type_info.ToString() + IsPtr = type_info.IsPtr + if IsPtr and target_type_str and '*' not in target_type_str: + target_type_str += ' *' + if not target_type_str: + # Fallback:ToString 返回空字符串时,尝试手动从 BinOp 提取类型字符串 + _vlog().warning( + f"_HandleTypeUnionCastLlvm: ToString 返回空字符串,启用 fallback 手动解析 " + f"(type_info={type_info!r})" + ) + target_type_str = self._ExtractTypeStrFromUnionBinOp(Node.func) + if target_type_str and type_info.IsPtr and not IsPtr: + if '*' not in target_type_str: + target_type_str += ' *' + if isinstance(type_info.Storage, t.CExport) or isinstance(type_info.Storage, t.CExtern): + pass + if type_info.IsState: + Gen._export_funcs.add('_type_union_cast') + else: + # Fallback 路径:MergeTypes 失败(返回 None 或抛异常)时,手动从 BinOp 提取类型信息 + # 处理 (BaseType | t.CPtr) 这种模式,手动解析为 "{BaseType} *" + _vlog().warning( + f"_HandleTypeUnionCastLlvm: MergeTypes 未返回 CTypeInfo " + f"(type_info={type_info!r}, exception={MergeException!r}),启用 fallback 手动解析" + ) + target_type_str = self._ExtractTypeStrFromUnionBinOp(Node.func) + if not target_type_str: + # fail-fast(原为 _vlog().warning + return None): + # `(T | t.CPtr)(x)` 只可能来自指针强转——parser 把 `(T*)x` / `(T*)(x)` / + # `T*(x)`(含 `**`)统一产成 func = `T | t.CPtr` 的 Call(见 + # vast/ExprParser._ConsumePtrSuffixCall、vast_c.c ConsumePtrSuffixCall)。 + # 该形态必须有值产出;返回 None 会让调用方把整条表达式当"编译期求值为空" + # 丢掉(现场:ViperC ExprGenBase.vp:360 `v = (ast.Expr*(node)).value` + # → TPC 报"赋值表达式 'value'(Attribute 形态)编译期求值为空",而同函数 + # 前三条 `(ast.Assign/AnnAssign/AugAssign*(node)).value` 同款写法正常)。 + # 故把"哪一步没解析出目标类型"逐项摊开,而不是静默丢弃整条表达式。 + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(self._DescribeUnionCastFailure( + Node, Gen, type_info, MergeException, target_type_str)) + return self._HandleTypeCastLlvm(Node.args[0], target_type_str) + + def _DescribeUnionCastFailure(self, Node: ast.Call, Gen: Any, TypeInfo: Any, + MergeException: Any, TargetStr: Any) -> str: + """联合类型强转目标类型解析失败时的判定依据(逐项列出,供 fail-fast 报文)。 + + 只做取证,不做类型解析:把 MergeTypes / _ExtractTypeStrFromUnionBinOp 的 + 返回值、异常,以及联合中每个操作数在符号表与类登记表(Gen.structs / + _struct_sha1_map / class_sha1_map)里的命中情况全部列出,使"是基座类名 + 没被当成类"还是"CPtr 标记侧解析失败"一眼可分。 + """ + _Flat: list = [] + + def _Collect(n: ast.AST) -> None: + if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr): + _Collect(n.left) + _Collect(n.right) + else: + _Flat.append(n) + + _Collect(Node.func) + _Operands: list[str] = [] + for _op in _Flat: + _short: str = '' + _full: str = '' + if isinstance(_op, ast.Attribute): + _short = _op.attr + _cur: ast.AST = _op.value + while isinstance(_cur, ast.Attribute): + _cur = _cur.value + _full = f"{_cur.id}.{_short}" if isinstance(_cur, ast.Name) else _short + elif isinstance(_op, ast.Name): + _short = _op.id + _full = _short + else: + _Operands.append(f"<{type(_op).__name__}>(非类型名形态)") + continue + _sym: Any = None + try: + _sym = self.Trans.SymbolTable.lookup(_short) + except Exception: + _sym = None + _verdict: str = '符号表未命中' + if _sym is not None: + _verdict = (f"IsStruct={getattr(_sym, 'IsStruct', None)}" + f" IsRenum={getattr(_sym, 'IsRenum', None)}" + f" IsEnum={getattr(_sym, 'IsEnum', None)}" + f" IsEnumMember={getattr(_sym, 'IsEnumMember', None)}" + f" IsTypedef={getattr(_sym, 'IsTypedef', None)}" + f" Name={getattr(_sym, 'Name', None)!r}") + _Operands.append( + f"{_full}({_verdict};in structs={_short in Gen.structs}" + f";structs 同短名键={[k for k in Gen.structs if k.split('.')[-1] == _short][:4]}" + f";_struct_sha1_map={getattr(Gen, '_struct_sha1_map', {}).get(_short)!r}" + f";class_sha1_map={getattr(Gen, 'class_sha1_map', {}).get(_short)!r})") + + _ToStr: str = '<非 CTypeInfo>' + try: + if isinstance(TypeInfo, CTypeInfo): + _ToStr = repr(TypeInfo.ToString()) + elif TypeInfo is None: + _ToStr = 'None' + except Exception as _E: + _ToStr = f"" + return (f"联合类型强转 '({ast.unparse(Node.func)})(...)' 无法确定目标类型," + f"拒绝静默返回 None(会让整条表达式被判为『编译期求值为空』而丢弃):" + f"MergeTypes 返回={type(TypeInfo).__name__}(ToString()={_ToStr});" + f"_ExtractTypeStrFromUnionBinOp={TargetStr!r};" + f"MergeTypes 异常={MergeException!r};操作数判据:" + ";".join(_Operands)) + + def _ExtractTypeStrFromUnionBinOp(self, NodeFunc: ast.AST) -> str | None: + """Fallback:从类型联合 BinOp 手动提取类型字符串。 + + 处理 (BaseType | t.CPtr) 这种模式,手动解析为 "{BaseType} *"。 + 支持嵌套的 | 操作(如 (t.CInt | t.CPtr) | t.CStatic)。 + + 策略: + - 递归展平所有 | 操作数 + - t.CPtr 视为指针修饰符(生成 "{BaseType} *" 格式) + - 其他 CType 子类作为基础类型(取第一个) + - 跳过存储类/限定符修饰符(CExport/CExtern/CStatic/CConst/CVolatile/CInline 等) + """ + if not isinstance(NodeFunc, ast.BinOp) or not isinstance(NodeFunc.op, ast.BitOr): + return None + + # 存储类/限定符/修饰符类型名集合:这些类型在类型联合中只起修饰作用, + # 不是基础类型,应跳过(参考 HandlesTypeMerge._GetCTypeInfoInternal 的分类逻辑) + StorageQualifierNames = { + 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', + 'CStatic', 'CExtern', 'CExport', 'CAuto', 'CRegister', + 'CDefine', 'CPass', 'CTypeDefault', 'State', + 'BigEndian', 'LittleEndian', + } + + # 递归展平所有 | 操作数 + Operands = [] + + def CollectOperands(node: ast.AST) -> None: + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + CollectOperands(node.left) + CollectOperands(node.right) + else: + Operands.append(node) + + CollectOperands(NodeFunc) + + HasCPtr = False # 是否包含 t.CPtr(指针修饰符) + BaseTypeName = None # 基础类型名(如 "CInt"、"CChar") + + for Operand in Operands: + AttrName = None + if isinstance(Operand, ast.Attribute): + AttrName = Operand.attr + elif isinstance(Operand, ast.Name): + AttrName = Operand.id + else: + continue + + # t.CPtr 是指针修饰符,单独处理(增加一层 *) + if AttrName == 'CPtr': + HasCPtr = True + continue + + # 跳过存储类/限定符/修饰符类型,它们不是基础类型 + if AttrName in StorageQualifierNames: + continue + + # 通过 t 模块获取类型类,验证是否为 CType 子类 + TypeClass = getattr(t, AttrName, None) + if not (isinstance(TypeClass, type) and issubclass(TypeClass, t.CType)): + # 非 CType 类,跳过 + continue + + # 记录第一个非 CPtr 基础类型 + if BaseTypeName is None: + BaseTypeName = AttrName + + if not BaseTypeName: + return None + + # 包含 t.CPtr 时,生成 "{BaseType} *" 格式 + # _CType2LLVM 已验证能解析 "CInt *" → i32*(ir.PointerType(ir.IntType(32))) + if HasCPtr: + return f"{BaseTypeName} *" + return BaseTypeName + + def _HandleTypeCastLlvm(self, expr_node: ast.AST, target_type_str: str) -> ir.Value | None: + Gen = self.Trans.LlvmGen + val = self.HandleExprLlvm(expr_node) + if val is None: + return Gen.emit_constant(0, target_type_str) + target_type = Gen._CType2LLVM(target_type_str) + if not target_type: + return val + if val.type == target_type: + if Gen._is_type_unsigned(target_type_str): + Gen._unsigned_results[id(val)] = True + return val + src_is_float = isinstance(val.type, (ir.FloatType, ir.DoubleType)) + dst_is_float = isinstance(target_type, (ir.FloatType, ir.DoubleType)) + src_is_int = isinstance(val.type, ir.IntType) + dst_is_int = isinstance(target_type, ir.IntType) + src_IsPtr = isinstance(val.type, ir.PointerType) + dst_IsPtr = isinstance(target_type, ir.PointerType) + if src_is_int and dst_is_float: + is_unsigned = Gen._check_node_unsigned(expr_node) + if is_unsigned: + return Gen.builder.uitofp(val, target_type, name="uint2float") + return Gen.builder.sitofp(val, target_type, name="int2float") + if src_is_float and dst_is_int: + return Gen.builder.fptosi(val, target_type, name="float2int") + if src_is_int and dst_is_int: + is_dst_unsigned = Gen._is_type_unsigned(target_type_str) + if val.type.width < target_type.width: + if val.type.width == 1: + # i1 是布尔值(来自 icmp/fcmp/__contains__),无符号语义,必须 zext + result = Gen.builder.zext(val, target_type, name="zext_bool_cast") + else: + is_unsigned = Gen._check_node_unsigned(expr_node) + if is_unsigned or is_dst_unsigned: + result = Gen.builder.zext(val, target_type, name="zext_cast") + else: + result = Gen.builder.sext(val, target_type, name="sext_cast") + if is_dst_unsigned: + Gen._unsigned_results[id(result)] = True + return result + if val.type.width > target_type.width: + # 目标 i1:真值归一化 `x != 0`(与 bool(x) 内建 / if 条件 / 落存储 + # 同一判据),不是 trunc 取最低位(运行时值 2 会得 false) + if target_type.width == 1: + result = Gen._CoerceIntToI1(val, name="bool_norm_cast") + else: + result = Gen.builder.trunc(val, target_type, name="trunc_cast") + if is_dst_unsigned: + Gen._unsigned_results[id(result)] = True + return result + if is_dst_unsigned: + Gen._unsigned_results[id(val)] = True + return val + if src_is_float and dst_is_float: + if isinstance(val.type, ir.FloatType) and isinstance(target_type, ir.DoubleType): + return Gen.builder.fpext(val, target_type, name="fpext_cast") + if isinstance(val.type, ir.DoubleType) and isinstance(target_type, ir.FloatType): + return Gen.builder.fptrunc(val, target_type, name="fptrunc_cast") + return val + if src_IsPtr and dst_is_int: + if target_type.width < 64: + ptr_to_i64 = Gen.builder.ptrtoint(val, ir.IntType(64), name="ptr2i64") + return Gen.builder.trunc(ptr_to_i64, target_type, name="ptr2int_cast") + return Gen.builder.ptrtoint(val, target_type, name="ptr2int_cast") + if src_is_int and dst_IsPtr: + return Gen.builder.inttoptr(val, target_type, name="int2ptr_cast") + if src_IsPtr and dst_IsPtr: + if isinstance(val.type.pointee, ir.PointerType) and isinstance(target_type.pointee, ir.IntType): + Loaded = Gen._load(val, name="load_ptr") + return Loaded + return Gen.builder.bitcast(val, target_type, name="ptrcast") + try: + return Gen.builder.bitcast(val, target_type, name="cast") + except Exception: # 回退:bitcast 失败时返回原值 + return val + + def _HandleREnumConstructLlvm(self, Node: ast.Call, RenumName: str, VariantName: str, TagValue: int) -> ir.Value | None: + Gen = self.Trans.LlvmGen + if RenumName not in Gen.structs: + self.Trans.ImportHandler._TryLoadStructFromStub(RenumName, Gen) + if RenumName not in Gen.structs: + return None + RenumType = Gen.structs[RenumName] + # `new memgr(x) Qual.Variant(args)`:_HandleNewExprLlvm 已取好池存储 → 复用; + # 裸 `Qual.Variant(args)`(无 new)时 NewStorageOverride 为 None → 帧内 alloca。 + if Gen.NewStorageOverride is not None: + result = Gen.NewStorageOverride + else: + result = Gen._allocaEntry(RenumType, name=f"{RenumName}_{VariantName}") + tag_ptr = Gen.builder.gep(result, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="renum_tag_ptr") + Gen._store(ir.Constant(ir.IntType(32), TagValue), tag_ptr) + NestedStructName = f"{RenumName}_{VariantName}" + if Node.args: + # 变体嵌套结构体按需补发:导入模块侧 stub 只声明主 REnum,不含 + # `_` 声明(见 BaseHandle._EnsureRenumVariantStruct)。 + # 缺它时历史行为是**静默丢弃全部载荷实参**(只写 tag)—— 属静默错码, + # 故此处补齐;补不上则响亮失败,绝不产出"只有 tag 没有载荷"的值。 + self._EnsureRenumVariantStruct(Gen, RenumName, VariantName) + if NestedStructName not in Gen.structs or not Gen.class_members.get(NestedStructName): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"REnum 变体 '{RenumName}.{VariantName}' 的嵌套结构体/载荷成员缺失," + f"无法写入 {len(Node.args)} 个载荷实参(拒绝静默丢弃):" + f"structs 命中={'有' if NestedStructName in Gen.structs else '无'} " + f"class_members={len(Gen.class_members.get(NestedStructName, []))} 项;" + f"同前缀 structs 键=" + f"{[k for k in Gen.structs if k.startswith(RenumName + '_')][:8]}" + ) + if NestedStructName in Gen.structs and Node.args: + NestedStructType = Gen.structs[NestedStructName] + NestedStructPtrType = ir.PointerType(NestedStructType) + variant_ptr = Gen.builder.bitcast(result, NestedStructPtrType, name=f"cast_{NestedStructName}") + members = Gen.class_members.get(NestedStructName, []) + payLoad_members = [(n, t) for n, t in members if n != '__tag'] + for i, arg in enumerate(Node.args): + if i < len(payLoad_members): + member_name, member_type = payLoad_members[i] + ArgVal = self.HandleExprLlvm(arg) + if ArgVal: + Coerced = Gen._coerce_value(ArgVal, member_type) + if Coerced is not None: + ArgVal = Coerced + elem_ptr = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i + 1)], name=f"{VariantName}_{member_name}") + # REnum 嵌套结构体共享 max_variant_struct 布局,成员类型可能与 + # 结构体字段类型不一致(如 Bits:i32 与 Ret:LLVMType* 重叠)。 + # 需 bitcast elem_ptr 到成员类型,避免 _store 按 struct 字段类型做错误转换。 + if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type: + elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"{VariantName}_{member_name}_cast") + Gen._store(ArgVal, elem_ptr) + return result + + def _HandleTypedefCastLlvm(self, Node: ast.Call, TypedefName: str) -> ir.Value: + Gen = self.Trans.LlvmGen + SymInfo = self.Trans.SymbolTable.lookup(TypedefName) + if not SymInfo or not SymInfo.IsTypedef: + return ir.Constant(ir.IntType(32), 0) + # 确保 Name 已设置,以便 _resolve_typedef_ctype 能正确解析 + if not SymInfo.Name: + SymInfo.Name = TypedefName + TargetType = Gen._ctype_to_llvm(SymInfo) + if not Node.args: + if isinstance(TargetType, ir.IntType): + return ir.Constant(TargetType, 0) + elif isinstance(TargetType, ir.PointerType): + return ir.Constant(TargetType, None) + return ir.Constant(ir.IntType(32), 0) + ArgVal = self.HandleExprLlvm(Node.args[0]) + if not ArgVal: + if isinstance(TargetType, ir.IntType): + return ir.Constant(TargetType, 0) + return ir.Constant(ir.IntType(32), 0) + if ArgVal.type == TargetType: + return ArgVal + if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType): + if ArgVal.type.width < TargetType.width: + return Gen.builder.zext(ArgVal, TargetType, name=f"zext_{TypedefName}_cast") + elif ArgVal.type.width > TargetType.width: + return Gen.builder.trunc(ArgVal, TargetType, name=f"trunc_{TypedefName}_cast") + return ArgVal + if isinstance(TargetType, ir.PointerType): + if isinstance(ArgVal.type, ir.PointerType): + return Gen.builder.bitcast(ArgVal, TargetType, name=f"bitcast_{TypedefName}_cast") + if isinstance(ArgVal.type, ir.IntType): + return Gen.builder.inttoptr(ArgVal, TargetType, name=f"inttoptr_{TypedefName}_cast") + # 如果参数是结构体值,需要 alloca 并返回指针 + if isinstance(ArgVal.type, ir.BaseStructType): + alloca = Gen.builder.alloca(ArgVal.type, name=f"struct_alloca_{TypedefName}") + Gen.builder.store(ArgVal, alloca, align=8) + return Gen.builder.bitcast(alloca, TargetType, name=f"bitcast_struct_{TypedefName}") + if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.PointerType): + return Gen.builder.ptrtoint(ArgVal, TargetType, name=f"ptrtoint_{TypedefName}_cast") + if isinstance(TargetType, (ir.FloatType, ir.DoubleType)) and isinstance(ArgVal.type, ir.IntType): + return Gen.builder.sitofp(ArgVal, TargetType, name=f"sitofp_{TypedefName}_cast") + if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, (ir.FloatType, ir.DoubleType)): + return Gen.builder.fptosi(ArgVal, TargetType, name=f"fptosi_{TypedefName}_cast") + return ArgVal + + def _HandleSubscriptPtrCastLlvm(self, Node: ast.Call, sub: ast.Subscript) -> ir.Value: + """(rawptr[X])(expr) / (t.CPtr[X])(expr) 内建指针转换:expr → X* + + rawptr 是编译器内建裸指针语法(无 SymbolTable typedef 条目、无泛型模板), + 此前 Subscript 调用仅查泛型模板 → 落空后报 "Unsupported call expression" + (StubMergerCollect.vp `(rawptr[FileEntry])(t.CVoid(...))` bootstrap 根因)。 + 目标类型经 TypeMergeHandler.GetCTypeInfo 解析 Subscript 类型节点获得。 + """ + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + TargetType: ir.Type | None = None + try: + ti_cast: Any = self.Trans.TypeMergeHandler.GetCTypeInfo(sub) + if ti_cast is not None: + TargetType = Gen._ctype_to_llvm(ti_cast) + except Exception: + TargetType = None + if TargetType is None or isinstance(TargetType, ir.VoidType): + # 回退:rawptr[X] 语义 = X*(X 为已注册 struct/类,可能模块限定名) + s_name: str | None = None + _sl = getattr(sub, 'slice', None) + if isinstance(_sl, ast.Attribute): + s_name = _sl.attr + elif isinstance(_sl, ast.Name): + s_name = _sl.id + if s_name and s_name in Gen.structs: + TargetType = ir.PointerType(Gen.structs[s_name]) + else: + TargetType = ir.PointerType(ir.IntType(8)) + return self._emit_rawptr_cast(Node, Gen, TargetType) + + def _emit_rawptr_cast(self, Node: ast.Call, Gen: LlvmGeneratorMixin, TargetType: ir.Type) -> ir.Value: + """按 _HandleTypedefCastLlvm 的指针语义执行 bitcast/inttoptr 转换""" + if not Node.args: + if isinstance(TargetType, ir.PointerType): + return ir.Constant(TargetType, None) + return ir.Constant(ir.IntType(32), 0) + ArgVal: Any = self.HandleExprLlvm(Node.args[0]) + if not ArgVal: + return ir.Constant(ir.IntType(32), 0) + if ArgVal.type == TargetType: + return ArgVal + if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType): + # builtins typedef 数值转换(int64(bool) / uint64(i32) 等):按宽度 + # zext/trunc,与 _HandleTypedefCastLlvm 的语义一致。 + if ArgVal.type.width < TargetType.width: + return Gen.builder.zext(ArgVal, TargetType, name="zext_typedef_cast") + elif ArgVal.type.width > TargetType.width: + return Gen.builder.trunc(ArgVal, TargetType, name="trunc_typedef_cast") + return ArgVal + if isinstance(TargetType, ir.PointerType): + if isinstance(ArgVal.type, ir.PointerType): + return Gen.builder.bitcast(ArgVal, TargetType, name="bitcast_rawptr_cast") + if isinstance(ArgVal.type, ir.IntType): + return Gen.builder.inttoptr(ArgVal, TargetType, name="inttoptr_rawptr_cast") + if isinstance(ArgVal.type, ir.BaseStructType): + alloca = Gen.builder.alloca(ArgVal.type, name="struct_alloca_rawptr") + Gen.builder.store(ArgVal, alloca, align=8) + return Gen.builder.bitcast(alloca, TargetType, name="bitcast_struct_rawptr") + if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.PointerType): + return Gen.builder.ptrtoint(ArgVal, TargetType, name="ptrtoint_rawptr_cast") + return ArgVal + + def _HandleInlineExpandLlvm(self, Node: ast.Call, sym_info: CTypeInfo) -> None: + Gen = self.Trans.LlvmGen + inline_body = sym_info.InlineBody + inline_params = sym_info.InlineParams or [] + call_args = [] + for arg in Node.args: + call_args.append(self.Trans.ExprHandler.HandleExprLlvm(arg)) + saved_vars = {} + for i, param_name in enumerate(inline_params): + if i < len(call_args): + saved_vars[param_name] = call_args[i] + old_var_scopes = list(self.Trans.VarScopes) + for param_name, val in saved_vars.items(): + if val is not None: + alloca = Gen._allocaEntry(val.type, name=f"inline_arg_{param_name}") + Gen._store(val, alloca) + self.Trans.VarScopes.append((param_name, alloca)) + self.Trans.BodyHandler.HandleBodyLlvm(inline_body) + for param_name, _ in saved_vars.items(): + if self.Trans.VarScopes and self.Trans.VarScopes[-1][0] == param_name: + self.Trans.VarScopes.pop() + self.Trans.VarScopes = old_var_scopes + return None + + def _HandleClosureCallLlvm(self, Node: ast.Call, FuncName: str) -> ir.Value: + Gen = self.Trans.LlvmGen + func = Gen.functions.get(FuncName) + if not func: + if FuncName in self.Trans.FunctionDefCache: + FuncDef = self.Trans.FunctionDefCache[FuncName] + self.Trans.FunctionHandler._EmitFunctionForwardDeclLlvm(FuncDef, Gen) + func = Gen.functions.get(FuncName) + if not func: + func = self._try_declare_c_lib_func(FuncName, Gen) + if not func: + raise Exception(f"Undefined function: '{FuncName}'") + + FuncDef = self.Trans.FunctionDefCache.get(FuncName) + param_names = [] + FuncDef_args = getattr(FuncDef, 'args', None) if FuncDef else None + if FuncDef_args: + param_names = [arg.arg for arg in FuncDef.args.args] + # 回退到全局参数名表(支持跨模块 include 函数) + if not param_names: + global_param_names = getattr(self.Trans, '_global_function_param_names', {}) + param_names = global_param_names.get(FuncName) or [] + + num_params = len(func.function_type.args) + nonlocal_param_count = len(Gen.nonlocal_params.get(FuncName, [])) + eh_msg_out_count = 0 + if num_params > 0 and func.args: + for arg in func.args: + if arg.name in ('__eh_msg_out__', '__eh_code_out__'): + eh_msg_out_count += 1 + regular_param_count = num_params - nonlocal_param_count - eh_msg_out_count + is_variadic = False + func_ft = getattr(func, 'function_type', None) + is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False + + if not is_variadic and len(Node.args) > regular_param_count: + raise Exception(f"调用 {FuncName}() 传入了 {len(Node.args)} 个参数,但函数只需要 {regular_param_count} 个参数") + + # 变参函数:固定形参(含带默认值的)之外才是可变部分。被省略的固定形参 + # 也必须补进调用实参,否则实参个数少于固定形参 → 被调函数读到寄存器/栈 + # 垃圾(VET json.write 缺省 cap 读成指针量级 → pool.alloc 巨量 → OOM 的形态)。 + max_args = max(regular_param_count, len(Node.args)) if is_variadic else regular_param_count + resolved = [None] * max_args + + for i, arg in enumerate(Node.args): + if i < max_args: + arg_var_type = None + if i < num_params: + arg_var_type = func.function_type.args[i] + ArgVal = self.HandleExprLlvm(arg, VarType=arg_var_type) + if ArgVal: + resolved[i] = ArgVal + + for kw in Node.keywords: + if kw.arg is None: + if isinstance(kw.value, ast.Dict): + pass + elif isinstance(kw.value, ast.Call): + pass + continue + ArgVal = self.HandleExprLlvm(kw.value) + if not ArgVal: + continue + kw_name = kw.arg + if kw_name in param_names: + idx = param_names.index(kw_name) + if idx < regular_param_count: + resolved[idx] = ArgVal + + FuncDef_args_defaults = getattr(getattr(FuncDef, 'args', None), 'defaults', []) if FuncDef else [] + # 跨模块调用(被调函数不在本模块 FunctionDefCache:其它 App 模块 / includes + # 模块 / .vpi 声明)时 FuncDef 为 None,本模块没有被调方 AST,AST 默认值不可用。 + # 此时从全局默认参数表(Phase2 预扫描 App 与 includes 源码得到)取值,按 + # "尾部默认参数"语义补齐被省略的实参;缺失该表时保持原有零值填充。 + Global_defaults_vals: list | None = None + if not FuncDef_args_defaults: + Global_defaults_vals = self._LookupGlobalDefaults(FuncName) + if Global_defaults_vals is None and func is not None: + Global_defaults_vals = self._LookupGlobalDefaults(getattr(func, 'name', None)) + Num_defaults: int = len(FuncDef_args_defaults) if FuncDef_args_defaults else ( + len(Global_defaults_vals) if Global_defaults_vals else 0) + if Num_defaults > 0: + Min_args: int = regular_param_count - Num_defaults + for i in range(regular_param_count): + if resolved[i] is not None or i < Min_args: + continue + default_idx = i - Min_args + if default_idx >= Num_defaults: + continue + if FuncDef_args_defaults: + default_val = self.HandleExprLlvm(FuncDef_args_defaults[default_idx]) + else: + default_val = self._ConstantToIrValue(Global_defaults_vals[default_idx], + func.function_type.args[i], Gen) + if default_val: + param_type = func.function_type.args[i] + if default_val.type != param_type: + if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType): + if default_val.type.width < param_type.width: + default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}") + elif default_val.type.width > param_type.width: + default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}") + elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType): + default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}") + resolved[i] = default_val + + if FuncDef and param_names: + min_args = regular_param_count - Num_defaults + provided = len(Node.args) + kw_provided = set() + for kw in Node.keywords: + if kw.arg and kw.arg in param_names: + kw_provided.add(param_names.index(kw.arg)) + for i in range(min(min_args, len(param_names))): + if i >= provided and i not in kw_provided: + raise Exception(f"调用 {FuncName}() 缺少必需参数 '{param_names[i]}',该参数没有默认值") + + CallArgs = [] + for i in range(max_args): + if resolved[i] is not None: + CallArgs.append(resolved[i]) + elif i < regular_param_count: + arg_type = func.function_type.args[i] + if isinstance(arg_type, ir.PointerType): + CallArgs.append(ir.Constant(arg_type, None)) + elif isinstance(arg_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + zero_val = Gen._zero_value_for_type(arg_type) + if zero_val is not None: + CallArgs.append(zero_val) + else: + CallArgs.append(ir.Constant(ir.PointerType(arg_type), None)) + elif isinstance(arg_type, ir.ArrayType): + zero_val = Gen._zero_value_for_type(arg_type) + if zero_val is not None: + CallArgs.append(zero_val) + else: + CallArgs.append(ir.Constant(arg_type, 0)) + else: + CallArgs.append(ir.Constant(arg_type, 0)) + + if FuncName in Gen.nonlocal_params: + for var_name, ptr_type in Gen.nonlocal_params[FuncName]: + if var_name in Gen.variables and Gen.variables[var_name] is not None: + CallArgs.append(Gen.variables[var_name]) + elif var_name in Gen._reg_values: + OldVal = Gen._reg_values[var_name] + var = Gen._allocaEntry(OldVal.type, name=var_name) + Gen._store(OldVal, var) + Gen.variables[var_name] = var + del Gen._reg_values[var_name] + CallArgs.append(var) + + self._append_eh_msg_out_arg(CallArgs, func, Gen) + + if is_variadic: + # 对于可变参数函数,需要手动处理参数类型转换 + adjusted = [] + for i, arg in enumerate(CallArgs): + if i < num_params: + # 固定参数:尝试类型转换 + param_type = func.function_type.args[i] + if arg.type != param_type: + try: + if isinstance(arg.type, ir.IntType) and isinstance(param_type, ir.PointerType): + adjusted.append(self.builder.inttoptr(arg, param_type)) + elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.IntType): + adjusted.append(Gen.builder.ptrtoint(arg, param_type)) + elif isinstance(arg.type, ir.IntType) and arg.type.width < param_type.width: + adjusted.append(Gen.builder.zext(arg, param_type)) + elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.PointerType): + adjusted.append(Gen.builder.bitcast(arg, param_type, name=f"cast_arg_{i}")) + else: + adjusted.append(arg) + except Exception: # 回退:参数类型调整失败时使用原值 + if isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.PointerType): + try: + adjusted.append(Gen.builder.bitcast(arg, param_type, name=f"cast_arg_{i}")) + except Exception: + adjusted.append(arg) + else: + adjusted.append(arg) + else: + adjusted.append(arg) + else: + # 可变参数:应用默认参数提升(float -> double, i32 -> i64) + if isinstance(arg.type, ir.FloatType): + arg = Gen.builder.fpext(arg, ir.DoubleType(), name=f"fpext_vararg_{FuncName}") + elif isinstance(arg.type, ir.IntType) and arg.type.width < 64: + # i1 是布尔值(来自 icmp/fcmp/__contains__),无符号语义,必须 zext + if arg.type.width == 1: + arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_bool_vararg_{FuncName}") + else: + # 有符号整数用 sext,无符号整数用 zext + is_signed = True + arg_node = Node.args[i] if i < len(Node.args) else None + if arg_node is not None: + is_u = Gen._check_node_unsigned(arg_node) + if is_u: + is_signed = False + if is_signed: + arg = Gen.builder.sext(arg, ir.IntType(64), name=f"sext_vararg_{FuncName}") + else: + arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_vararg_{FuncName}") + elif isinstance(arg.type, (ir.LiteralStructType, ir.IdentifiedStructType)): + # 治本修复:变参函数(如 printf)不支持 struct by value 传递。 + # 当 self 等 struct 值被错误地 load 为值(而非指针)传入变参时, + # ABI 不匹配会导致崩溃。修复:将 struct 值通过 alloca + bitcast + # 转换为 i8* 指针传递,确保与 %p / %s 等格式说明符兼容。 + try: + alloca_tmp = Gen.builder.alloca(arg.type, name=f"vararg_struct_{FuncName}_{i}") + Gen.builder.store(arg, alloca_tmp) + arg = Gen.builder.bitcast(alloca_tmp, ir.IntType(8).as_pointer(), name=f"vararg_struct_ptr_{FuncName}_{i}") + except Exception: + # 回退:alloca/store 失败时,尝试 ptrtoint 为 i64 + try: + arg = Gen.builder.ptrtoint(arg, ir.IntType(64), name=f"vararg_struct_i64_{FuncName}_{i}") + except Exception: + pass # 最终回退:保留原值(可能仍会导致崩溃,但至少不阻塞编译) + adjusted.append(arg) + result = Gen.builder.call(func, adjusted, name=f"call_{FuncName}") + else: + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted = Gen._adjust_args(CallArgs, func) + # 兜底:确保所有指针类型参数与函数签名匹配(如 struct* -> i8*) + for i, (arg, param) in enumerate(zip(adjusted, func.args)): + if arg.type != param.type: + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): + adjusted[i] = Gen.builder.bitcast(arg, param.type, name=f"cast_arg_{i}") + result = Gen.builder.call(func, adjusted, name=f"call_{FuncName}") + self._check_eh_return(result, FuncName, Gen, called_func=func) + return result + + @staticmethod + def _has_cvtable_decorator(node: ast.ClassDef) -> bool: + """检查 ClassDef 是否有 @t.CVTable 装饰器(或 @CVTable)""" + for decorator in node.decorator_list: + if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'CVTable': + return True + if isinstance(decorator, ast.Call): + inner = decorator.func + if isinstance(inner, ast.Attribute) and getattr(inner.value, 'id', None) == 't' and inner.attr == 'CVTable': + return True + if isinstance(inner, ast.Name) and inner.id == 'CVTable': + return True + if isinstance(decorator, ast.Name) and decorator.id == 'CVTable': + return True + return False + + @staticmethod + def _has_novtable_decorator(node: ast.ClassDef) -> bool: + """检查 ClassDef 是否有 @t.NoVTable 装饰器""" + for decorator in node.decorator_list: + if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'NoVTable': + return True + if isinstance(decorator, ast.Name) and decorator.id == 'NoVTable': + return True + return False + + def _try_register_vtable_ondemand(self, ClassName: str, class_sha1: str, Gen: "LlvmGeneratorMixin") -> None: + """按需 vtable 注册:从 .pyi stub 检查 @t.CVTable 并注册到 vtable 集合。 + + 沿继承链递归收集方法,保证 vtable 布局与 prescan 的 _EmitClassLlvm 一致: + - 基类方法在前(固定索引,保证多态分派正确) + - 子类新增方法在后 + - 子类覆盖的方法放在父类的位置(用子类实现) + + 与 prescan 的 _resolve_class_flags 逻辑一致: + - 类自身有 @t.CVTable → 注册 + - 类自身有 @t.NoVTable → 不注册 + - 类继承 @t.CVTable 基类(且无 @t.NoVTable)→ 注册(自动启用 CVTable) + """ + try: + import_handler = self.Trans.ImportHandler + project_root = import_handler._find_project_root() + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + + # 类级缓存已读取的 .pyi 文件(绝对路径 -> class_map),跨 worker 任务共享。 + # 原实现为函数局部缓存,每次调用重建 → 437 次 open+ast.parse 同一批 pyi。 + def load_pyi(sha1: str) -> dict[str, ast.ClassDef] | None: + pyi_path = os.path.join(project_root, 'temp', f'{sha1}.vpi') + if not os.path.isfile(pyi_path): + # 回退读取旧缓存 .pyi + pyi_path = os.path.join(project_root, 'temp', f'{sha1}.pyi') + if not os.path.isfile(pyi_path): + return None + abs_pyi = os.path.abspath(pyi_path) + cmap = self._vtable_pyi_cache.get(abs_pyi) + if cmap is not None: + return cmap + with open(pyi_path, 'r', encoding='utf-8') as f: + pyi_code = f.read() + tree = ast.parse(pyi_code, pyi_path) + cmap: dict[str, ast.ClassDef] = {} + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.ClassDef): + cmap[node.name] = node + self._vtable_pyi_cache[abs_pyi] = cmap + return cmap + + def collect_class_methods(class_name: str, sha1: str, visited: set[str]) -> tuple[list[str], bool, bool, str | None]: + """递归收集类的方法列表(基类方法在前,子类方法在后)。 + + Returns: + (methods, is_cvtable, is_novtable, parent_name) + """ + # 结果缓存(类级,键 (sha1, class_name)):同一类的 vtable 方法收集 + # 跨 worker 任务重复调用时直接复用(原 437 次顶层调用 + 递归每次都重走)。 + key = (sha1, class_name) + cached = self._vtable_methods_cache.get(key) + if cached is not None: + # 返回新 list:避免调用方(Gen.class_methods)后续修改污染缓存 + return (list(cached[0]), cached[1], cached[2], cached[3]) + if class_name in visited: + return ([], False, False, None) + visited.add(class_name) + + class_map = load_pyi(sha1) + if class_map is None: + return ([], False, False, None) + + target_node = class_map.get(class_name) + if target_node is None: + return ([], False, False, None) + + is_cvtable = self._has_cvtable_decorator(target_node) + is_novtable = self._has_novtable_decorator(target_node) + + # 收集自身方法(方法级修饰符的槽位过滤放在父类状态确定之后: + # 判据 SymbolUtils.IsVtableSlotMethod 需要 VtableByClassLevel) + self_method_pairs: list[tuple] = [] + for item in target_node.body: + if isinstance(item, ast.FunctionDef): + method_name = item.name + if method_name in ('__new__', '__init__', '__before_init__'): + continue + if method_name == '__call__': + self_method_pairs.append((item, f"{class_name}.__call__")) + else: + self_method_pairs.append((item, f"{class_name}.{method_name}")) + + # 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先) + parent_methods: list[str] = [] + parent_name: str | None = None + parent_names: list[str] = [] + parent_method_names_seen: set[str] = set() + # 第一个真实父类的 vtable 状态(None=无父类/未知) + first_parent_vtable: bool | None = None + for base in target_node.bases: + base_name: str | None = None + if hasattr(base, 'id'): + base_name = base.id + elif hasattr(base, 'attr'): + base_name = base.attr + if not base_name or base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): + continue + parent_names.append(base_name) + if parent_name is None: + parent_name = base_name + + # 检查同文件内的基类 + base_node = class_map.get(base_name) + if base_node is not None: + base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, sha1, visited) + # 仅第一个真实父类决定 vtable 继承状态(与 _resolve_class_flags 一致) + if parent_name == base_name: + first_parent_vtable = (base_cvtable and not base_novtable) + for bm in base_methods: + bmn = bm.split('.')[-1] if '.' in bm else bm + if bmn not in parent_method_names_seen: + parent_method_names_seen.add(bmn) + parent_methods.append(bm) + else: + # 跨模块基类 — 查找基类的 sha1 + base_sha1 = struct_sha1_map.get(base_name) + if base_sha1: + base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, base_sha1, visited) + if parent_name == base_name: + first_parent_vtable = (base_cvtable and not base_novtable) + for bm in base_methods: + bmn = bm.split('.')[-1] if '.' in bm else bm + if bmn not in parent_method_names_seen: + parent_method_names_seen.add(bmn) + parent_methods.append(bm) + else: + # 无法找到基类的 sha1 → 父类信息未知,保守启用 vtable + if parent_name == base_name and first_parent_vtable is None: + first_parent_vtable = True + if base_name in Gen._cross_module_novtable: + pass # 父类是 NoVTable,不继承 vtable + + # 继承链统一 vtable(与 _resolve_class_flags 一致): + # 类自身无 @t.CVTable 时,第一个父类有 vtable → 子类启用; + # 父类无 vtable → 子类静态;父类信息未知 → 保守启用。 + if not is_cvtable and not is_novtable and first_parent_vtable is not None: + if first_parent_vtable: + is_cvtable = True + + # VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态 + # (= 类级 CVTable / 继承链判定)——供槽位筛选与"方法级触发"共用。 + _VtByClassLevel: bool = is_cvtable + # 方法级修饰符筛选:`novirtual def`(⇒ @t.NoVTable)不进槽; + # `virtual def`(⇒ @t.CVTable)进槽。判据唯一源 = IsVtableSlotMethod + # (与定义端 HandlesClassDef / VPC _should_method_be_virtual 同口径), + # 故跨模块虚表槽序与定义模块逐槽一致。 + self_methods: list[str] = [] + for _nd, _full in self_method_pairs: + if (not is_cvtable) or IsVtableSlotMethod(_nd, _VtByClassLevel): + self_methods.append(_full) + # 方法级 virtual 触发类级 vtable(与 HandlesClassDef._resolve_class_flags / + # VPC _detect_vtable_status 的 `_has_any_func_cvtable` 分支对齐: + # 类级 NoVTable / 无真实父类这两种可判定形态;父类 vtable 已在上面处理) + if not is_cvtable and ClassHasFuncLevelCVTable(target_node): + if is_novtable or parent_name is None: + is_cvtable = True + + # 合并方法列表:基类方法在前,子类方法在后 + # 子类覆盖的方法放在父类的位置; + # 父类独有方法直接用父类方法原名(实际定义类.方法,与 VPC 一致) + child_method_map: dict[str, str] = {} + for m in self_methods: + mn = m.split('.')[-1] if '.' in m else m + child_method_map[mn] = m + + merged: list[str] = [] + covered_names: set[str] = set() + for pm in parent_methods: + mn = pm.split('.')[-1] if '.' in pm else pm + if mn in child_method_map: + merged.append(child_method_map[mn]) + covered_names.add(mn) + else: + # 父类独有方法 — 用父类方法原名(虚分派时函数查找沿 MRO 回退) + merged.append(pm) + + for m in self_methods: + mn = m.split('.')[-1] if '.' in m else m + if mn not in covered_names: + merged.append(m) + + result = (merged, is_cvtable, is_novtable, parent_name) + self._vtable_methods_cache[key] = result + return result + + methods, is_cvtable, is_novtable, parent_name = collect_class_methods(ClassName, class_sha1, set()) + + if is_novtable: + return + if not is_cvtable: + return + + # 注册到 vtable 集合(共享集合) + Gen._cross_module_vtable_classes.add(ClassName) + Gen.class_vtable.add(ClassName) + # 设置方法列表(与 prescan 逻辑一致:基类方法在前) + Gen.class_methods[ClassName] = methods + # 根本修复:注册 vtable 元数据后同步具化 struct body,从源头消除 + # "元数据已注册、本体仍 opaque" 的撕裂状态(配合 _HandleMethodCallLlvm + # 中 _EnsureReceiverStructConcrete 的使用前校验,双保险)。 + import_handler._TryLoadStructFromStub(ClassName, Gen, source_sha1=class_sha1) + # 设置 class_parent / class_parents(用于虚分派时的函数查找回退与 MRO) + if parent_name and ClassName not in Gen.class_parent: + Gen.class_parent[ClassName] = parent_name + parent_names_map = getattr(Gen, 'class_parents', None) + if parent_names_map is None: + parent_names_map = {} + Gen.class_parents = parent_names_map + if parent_name and ClassName not in parent_names_map: + parent_names_map[ClassName] = [parent_name] + except Exception: + pass + + def _try_complete_generic_specialization(self, ClassName: str, MethodName: str, Gen: LlvmGeneratorMixin) -> None: + """泛型特化类方法按需完整特化。 + + 当 ClassName 是泛型特化名(如 ndarray[double])且缓存中只有 + declare_only 版本时,补发方法体。这样方法调用能找到 define, + 避免链接时 undefined reference。 + + 触发场景:numpy 模块内部函数(如 np_sum)调用 a.sum(), + 其中 a 的类型注解 ndarray[t.CDouble] | t.CPtr 在类型解析时 + 只触发了 declare_only=True 特化(创建 struct + __before_init__), + 方法体(fill/delete/dot/sum/...)未被编译。 + """ + # 仅对特化名(含 '[' 且以 ']' 结尾)触发 + if '[' not in ClassName or not ClassName.endswith(']'): + return + # 反推模板类名(如 ndarray[double] → ndarray) + base_name: str = ClassName.split('[')[0] + # 检查是否是已注册的泛型模板 + if not (hasattr(self.Trans.ClassHandler, '_generic_class_templates') and base_name in self.Trans.ClassHandler._generic_class_templates): + return + # 构造 spec_key(与 _specialize_generic_class 一致:ClassName) + spec_key: str = base_name + '<' + ','.join(ClassName[len(base_name) + 1:-1].split('][')) + '>' + spec_cache = getattr(Gen, '_generic_class_specializations', {}) + cached = spec_cache.get(spec_key) + if not isinstance(cached, tuple) or len(cached) < 2: + return + cached_name: str = cached[0] + was_declare_only: bool = cached[1] + # 仅当缓存是 declare_only 版本时才需要补发 + if not was_declare_only: + return + # 检查方法是否已有 define(blocks 非空表示已编译方法体) + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + class_sha1 = struct_sha1_map.get(cached_name) + full_method_name: str = f'{class_sha1}.{cached_name}.{MethodName}' if class_sha1 else f'{cached_name}.{MethodName}' + existing_func = Gen._find_function(full_method_name) + if existing_func is not None: + existing_blocks = getattr(existing_func, 'blocks', []) + if existing_blocks: + return # 方法体已编译 + # 从缓存获取 type_args/type_names + type_info = getattr(Gen, '_generic_class_spec_type_info', {}).get(spec_key) + if type_info is None: + return + cached_type_args, cached_type_names = type_info + # 不预先标记:_specialize_generic_class 内部缓存逻辑会检测 + # cached_declare_only=True 且 declare_only=False,自动补发方法体 + # 并更新缓存为 (spec_name, False)。预注册机制(L117)防止递归。 + # + # 上下文快照:本函数**吞掉**特化异常、让外层方法继续编译,故必须在失败时把 + # 编译器上下文复位——`_EmitFunctionLlvm` 的类名/构建器恢复只在**成功路径** + # 执行(失败路径直接 re-raise),异常穿过多层后 `Gen.builder/func` 与 + # `_CurrentCpythonObjectClass` 会停在内层特化方法的中间状态:外层方法随后 + # 的 `self.X` 被按内层类解析(ExprGenFstr.vp 的 `self.Gen` 被按 + # `list[cfc4ca8da95a30e1.AST*]` 解析 → "属性 'Gen' 在类中找不到"的连带错误), + # 且后续指令会误插进内层函数的块。 + _saved_builder: object = Gen.builder + _saved_func: object = Gen.func + _saved_cpython_class: str | None = getattr(self.Trans, '_CurrentCpythonObjectClass', None) + _saved_class_name: object = getattr(Gen, '_current_class_name', None) + try: + self.Trans.ClassHandler._specialize_generic_class( + base_name, cached_type_args, Gen, + type_names=cached_type_names, declare_only=False + ) + except Exception: + # 失败时恢复 declare_only 标记,避免后续调用跳过补发 + Gen._generic_class_specializations[spec_key] = (cached_name, True) + Gen.builder = _saved_builder + Gen.func = _saved_func + self.Trans._CurrentCpythonObjectClass = _saved_cpython_class + Gen._current_class_name = _saved_class_name + + def _compute_field_byte_offset(self, Gen: LlvmGeneratorMixin, ClassName: str, field_name: str) -> int | None: + """计算字段在 struct 中的字节偏移(按 LLVM 布局含对齐)。 + + 遍历 struct elements 带对齐累加(与 _get_struct_size/_get_align 一致)。 + 有 vtable 的类 struct 首元素为 vtable 指针(i8*),class_members 不含它。 + 返回 None 表示无法计算(struct 未定义 / 字段不存在)。 + """ + # class_members 的 key 是裸类名,而 ClassName 可能带 sha1 前缀 + # (跨模块 ObjVal 类型带前缀,如 "e1ea7eaf63a9c3d1.Generator")。 + # 不剥离则 members 为空 → 找不到字段 → this_adjust 被跳过 → 继承方法 + # self 不调整 → 运行时错位崩溃(register_reexport 写 0x3f 越界即此)。 + lookup_cls: str = ClassName + if len(ClassName) > 17 and ClassName[16] == '.': + lookup_cls = ClassName[17:] + st = Gen.structs.get(ClassName) + # 注意:不能用 `not st` 做真值判断——IdentifiedStructType 定义 __len__, + # opaque(elements is None)时 bool(st) 触发 __len__ 的 assert(AssertionError)。 + # 必须用 `st is None` 身份比较。 + if (st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None) \ + and lookup_cls != ClassName: + st = Gen.structs.get(lookup_cls) + if st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + return None + members: list = Gen.class_members.get(lookup_cls, []) + # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源), + # 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。 + base: int = Gen._member_base(ClassName) + idx: int | None = None + for i, (name, _) in enumerate(members): + if name == field_name: + idx = base + i + break + if idx is None or idx >= len(st.elements): + return None + # ============================================================ + # diff_elems 修正(2026-08-13):class_members 与 struct.elements 数量 + # 不一致时对齐(前提:遗漏字段位于目标字段之前)。 + # ============================================================ + diff_elems: int = len(st.elements) - len(members) + if diff_elems > 0: + idx += diff_elems + if idx >= len(st.elements): + return None + # 临时诊断(2026-08-18,确定后移除):捕获 this_adjust 计算输入, + # 定位 TPC 并行 worker 间 class_members/struct 不一致导致的偏移抖动。 + if lookup_cls in ('Generator',): + try: + with open(r'd:\Users\TermiNexus\Desktop\TransPyC\_diag_offset.log', 'a', encoding='utf-8') as _f: + _f.write(f"{lookup_cls}|{field_name}|members={len(members)}|struct={len(st.elements)}" + f"|base={base}|idx={idx}|diff={diff_elems}\n") + except Exception: + pass + offset: int = 0 + for i in range(idx): + et = st.elements[i] + align: int = Gen._get_align(et) + size: int = Gen._get_struct_size(et) + if align > 1: + offset = ((offset + align - 1) // align) * align + offset += size + align = Gen._get_align(st.elements[idx]) + if align > 1: + offset = ((offset + align - 1) // align) * align + return offset + + def _adjust_this_for_inherited_method(self, Gen: LlvmGeneratorMixin, ObjVal: ir.Value, + ClassName: str, method_anc: str | None) -> ir.Value: + """多继承 this 调整(与 VPC _translate_method_call 一致): + + 继承方法体按"定义类自身布局"读 self 字段(如 B.get_b 内 + GEP(self_B, [0, 1]),字节偏移为 B 布局中 b 的 offset 8)。父类字段区 + 在子类中的**字节偏移**可能不同(如 AB={i8*,a@8,b@12} 中 b 偏移 12), + self 需按字节差前移,使父类方法内二次 GEP 恰好落到子类字段。 + + 修复(2026-08-10):字段索引法在"父类与子类元素大小不同"时错位 + (GEP 索引按元素个数计,非字节等价)。改用字节偏移: + delta = 字段在子类中字节偏移 − 字段在父类自身布局中字节偏移 + delta==0(第一父类,字段区前缀对齐)无需调整;delta>0 时按字节 + GEP 到父类字段区在子类中的等效基址,再 bitcast 成父类指针。 + """ + if method_anc is None or method_anc == ClassName: + return ObjVal + anc_lookup: str = method_anc + if len(method_anc) > 17 and method_anc[16] == '.': + anc_lookup = method_anc[17:] + anc_members: list = Gen.class_members.get(anc_lookup, []) + if not anc_members: + return ObjVal + first_field_name: str | None = None + for _fn, _ft in anc_members: + if _fn == '__vtable__': + continue + first_field_name = _fn + break + if first_field_name is None: + return ObjVal + sub_off: int | None = self._compute_field_byte_offset(Gen, ClassName, first_field_name) + if sub_off is None: + # 兄弟 Mixin 场景:调用类(ClassName)不含定义类字段——定义类在 + # 组合类 MRO 中位于调用类之前(如 Combined(BaseA, BaseB),BaseB 内 + # self.helper() 调 BaseA.helper)。此时 BaseB.this 指向组合类中 + # BaseB 字段区起点,BaseA 区在它前面,this 前移 sizeof(定义类 struct)。 + anc_st: Any = Gen.structs.get(method_anc) + if anc_st is None: + anc_st = Gen.structs.get(anc_lookup) + if anc_st is None or not isinstance(anc_st, ir.IdentifiedStructType) or anc_st.elements is None: + return ObjVal + sibling_delta: int = -Gen._get_struct_size(anc_st) + if sibling_delta >= 0: + return ObjVal + obj_i8_s: ir.Value = Gen.builder.bitcast( + ObjVal, ir.PointerType(ir.IntType(8)), name=f"this_sibling_i8_{anc_lookup}") + adj_s: ir.Value = Gen.builder.gep( + obj_i8_s, [ir.Constant(ir.IntType(64), sibling_delta)], + name=f"this_sibling_{anc_lookup}") + return Gen.builder.bitcast(adj_s, ir.PointerType(anc_st), + name=f"this_sibling_cast_{anc_lookup}") + anc_off: int | None = self._compute_field_byte_offset(Gen, method_anc, first_field_name) + if anc_off is None: + return ObjVal + delta: int = sub_off - anc_off + if delta <= 0: + return ObjVal + if method_anc not in Gen.structs: + return ObjVal + obj_i8: ir.Value = Gen.builder.bitcast(ObjVal, ir.PointerType(ir.IntType(8)), + name=f"this_adjust_i8_{method_anc}") + adj: ir.Value = Gen.builder.gep(obj_i8, [ir.Constant(ir.IntType(64), delta)], + name=f"this_adjust_{method_anc}") + return Gen.builder.bitcast(adj, ir.PointerType(Gen.structs[method_anc]), + name=f"this_adjust_cast_{method_anc}") + + def _EnsureClassParentsLoaded(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None: + """跨模块多继承:class_parents 缺失时从 vpi 按需加载继承信息。 + + 调用方(如 HandlesType 委托)引用的类定义在另一模块,其父类 + 信息未随 import 填充。_TryLoadClassMembersFromPyi 读取 .vpi 中 + 的 ClassDef 并经 _EmitExternalClassDeclLlvm 填充 class_parents, + 使 _get_mro_chain 能沿 MRO 回退到定义类(如 TypeGenMixin)。 + """ + if not ClassName: + return + if Gen.class_parents.get(ClassName): + return + # 归一化:ClassName 可能带 sha1 前缀(如 "c9b44d17cd621a75.CallGenLib", + # 跨模块 structs 表键),而 class_parents / ModuleSha1Map / _struct_sha1_map + # 的键是裸类名。不剥离则查不到父类 → 继承方法 MRO 回退失效 → fail-fast + # "Undefined method 'find_func_in_module' in class '...CallGenLib'"。 + lookup_cls: str = ClassName + if len(ClassName) > 17 and ClassName[16] == '.': + lookup_cls = ClassName[17:] + if Gen.class_parents.get(lookup_cls): + return + sha1_cls = Gen.ModuleSha1Map.get(lookup_cls) + if not sha1_cls: + sha1_cls = getattr(Gen, '_struct_sha1_map', {}).get(lookup_cls) + if not sha1_cls: + return + try: + self.Trans.ImportHandler._TryLoadClassMembersFromPyi(lookup_cls, f"{sha1_cls}.stub.ll", Gen) + except Exception: + pass + + def _HandleMethodCallLlvm(self, Node: ast.Call) -> ir.Value: + Gen = self.Trans.LlvmGen + MethodName = Node.func.attr + # 检查是否为 @staticmethod 或 @classmethod,如果是则走特殊路径 + if isinstance(Node.func.value, ast.Name): + VarName = Node.func.value.id + ClassName = Gen.var_struct_class.get(VarName) + if ClassName: + SymKey = f'{ClassName}.{MethodName}' + SymInfo = self.Trans.SymbolTable.lookup(SymKey) + if SymInfo and SymInfo.MetaList: + if FuncMeta.STATIC_METHOD in SymInfo.MetaList: + return self._HandleStaticMethodCallLlvm(Node, ClassName, MethodName) + if FuncMeta.CLASS_METHOD in SymInfo.MetaList: + return self._HandleStaticMethodCallLlvm(Node, ClassName, MethodName) + if isinstance(Node.func.value, ast.Name) and IsTModule(Node.func.value.id, self.Trans.SymbolTable): + result = self.Trans.TSpecialCallHandle._HandleTSpecialCallLlvm(Node) + if result is not None: + return result + ctype_cls = CTypeRegistry.GetClassByName(MethodName) + if ctype_cls is not None: + try: + ctype_inst = ctype_cls() + except Exception: + ctype_inst = None + target_bits = getattr(ctype_inst, 'Size', None) if ctype_inst else None + if target_bits is None: + target_bits = 0 + is_float = getattr(ctype_inst, 'IsSigned', False) is None and target_bits > 0 if ctype_inst else False + IsPtr_cast = len(Node.args) >= 2 and ( + (isinstance(Node.args[1], ast.Attribute) and Node.args[1].attr == 'CPtr' and isinstance(Node.args[1].value, ast.Name) and IsTModule(Node.args[1].value.id, self.Trans.SymbolTable)) or + (isinstance(Node.args[1], ast.Name) and Node.args[1].id == 'CPtr') + ) + if Node.args: + ArgNode = Node.args[0] + if isinstance(ArgNode, ast.Subscript): + SubscriptPtr = self._HandleSubscriptPtrLlvm(ArgNode) + if SubscriptPtr: + if MethodName == 'CPtr' or IsPtr_cast: + return SubscriptPtr + if isinstance(SubscriptPtr.type, ir.PointerType): + val = Gen._load(SubscriptPtr, name="Load_subscript_val") + else: + val = SubscriptPtr + if target_bits > 0 and isinstance(val.type, ir.IntType) and not is_float: + if val.type.width < target_bits: + val = Gen.builder.zext(val, ir.IntType(target_bits), name="zext_cast") + elif val.type.width > target_bits: + val = Gen.builder.trunc(val, ir.IntType(target_bits), name="trunc_cast") + return val + val = self.HandleExprLlvm(ArgNode) + if IsPtr_cast: + if target_bits > 0 and not is_float: + target_ptr_type = ir.PointerType(ir.IntType(target_bits)) + elif is_float and target_bits == 32: + target_ptr_type = ir.PointerType(ir.FloatType()) + elif is_float and target_bits == 64: + target_ptr_type = ir.PointerType(ir.DoubleType()) + else: + target_ptr_type = ir.PointerType(ir.IntType(8)) + if isinstance(val.type, ir.PointerType): + if val.type != target_ptr_type: + val = Gen.builder.bitcast(val, target_ptr_type, name="ptr_cast") + elif isinstance(val.type, ir.IntType): + if val.type.width < 64: + val = Gen.builder.zext(val, ir.IntType(64), name="zext_ptr_cast") + val = Gen.builder.inttoptr(val, target_ptr_type, name="int_to_ptr_cast") + return val + if val and target_bits > 0 and not is_float: + if isinstance(val.type, ir.IntType): + if val.type.width < target_bits: + val = Gen.builder.zext(val, ir.IntType(target_bits), name="zext_cast") + elif val.type.width > target_bits: + val = Gen.builder.trunc(val, ir.IntType(target_bits), name="trunc_cast") + elif isinstance(val.type, ir.PointerType): + val = Gen.builder.ptrtoint(val, ir.IntType(target_bits), name="ptrtoint_cast") + return val + if is_float: + return ir.Constant(ir.FloatType() if target_bits == 32 else ir.DoubleType(), 0.0) + return ir.Constant(ir.IntType(target_bits) if target_bits > 0 else ir.IntType(32), 0) + if self.Trans.SymbolTable.has(MethodName): + SymInfo = self.Trans.SymbolTable[MethodName] + if SymInfo.IsTypedef: + return self._HandleTypedefCastLlvm(Node, MethodName) + if isinstance(Node.func.value, ast.Name) and Node.func.value.id == 'c': + if MethodName == 'Asm': + return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) + if MethodName == 'Addr': + return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node) + elif MethodName == 'Set': + return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node) + elif MethodName == 'Load': + return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node) + elif MethodName == 'Deref': + return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) + elif MethodName == 'DerefAs': + return self._HandleCDerefAsLlvm(Node) + elif MethodName == 'PtrToInt': + return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) + elif MethodName in ('CIf', 'CIfdef', 'CIfndef', 'CElif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) + elif MethodName == 'CError': + msg = "compile-time error" + if Node.args: + arg = Node.args[0] + if isinstance(arg, ast.Constant) and isinstance(arg.value, str): + msg = arg.value + else: + try: + val = self.HandleExprLlvm(arg) + if isinstance(val, str): + msg = val + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"处理函数调用失败: {_e}", "Exception") + lineno = getattr(Node, 'lineno', 0) + # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 + _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + raise Exception(f"#error: {msg} (line {lineno})") + elif MethodName in ('CElse', 'CEndif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) + elif MethodName in ('CPragma', 'CUndef'): + return ir.Constant(ir.IntType(1), 1) + elif MethodName in ('LLVMIR', 'LInp', 'LOut'): + return None + ObjVal = self.HandleExprLlvm(Node.func.value) + if not ObjVal: + return None + if isinstance(ObjVal.type, (ir.IdentifiedStructType, ir.LiteralStructType)): + if isinstance(Node.func.value, ast.Name): + var_name = Node.func.value.id + if var_name in Gen.variables: + var_ptr = Gen.variables[var_name] + if isinstance(var_ptr.type, ir.PointerType) and isinstance(var_ptr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + ObjVal = var_ptr + else: + tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp") + Gen._store(ObjVal, tmp) + ObjVal = tmp + else: + tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp") + Gen._store(ObjVal, tmp) + ObjVal = tmp + else: + tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp") + Gen._store(ObjVal, tmp) + ObjVal = tmp + if isinstance(Node.func.value, ast.Name): + ModuleName = Node.func.value.id + imported = getattr(self.Trans, '_ImportedModules', set()) + actual_module = self.Trans.SymbolTable.resolve_alias(ModuleName) + if (ModuleName in imported or actual_module in imported) and ModuleName not in Gen.var_struct_class: + mangled_name = Gen._mangle_func_name(MethodName, actual_module) + if Gen._has_function(mangled_name): + func = Gen._get_function(mangled_name) + CallArgs = [] + # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 + _ptypes: list[ir.Type] = func.function_type.args if func is not None else [] + for _ai, arg in enumerate(Node.args): + _pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None + # 不再 `if ArgVal: append`(实参求值为空会被静默丢弃 ⇒ 后续整体 + # 前移一格 ⇒ 由 _adjust_args 报出与真因无关的类型不匹配,见 + # _RequireArgValue 的说明)。 + CallArgs.append(self._RequireArgValue( + self.HandleExprLlvm( + arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)), + mangled_name, _ai + 1, arg, Node)) + self._fill_default_args(CallArgs, func, MethodName) + func_ft = getattr(func, 'function_type', None) + is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False + num_fixed_params = len(func_ft.args) if func_ft else 0 + if is_variadic: + adjusted = [] + for i, arg in enumerate(CallArgs): + if i < num_fixed_params: + param_type = func_ft.args[i] + if arg.type != param_type: + try: + if isinstance(arg.type, ir.IntType) and isinstance(param_type, ir.PointerType): + adjusted.append(self.builder.inttoptr(arg, param_type)) + elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.IntType): + adjusted.append(Gen.builder.ptrtoint(arg, param_type)) + elif isinstance(arg.type, ir.IntType) and arg.type.width < param_type.width: + adjusted.append(Gen.builder.zext(arg, param_type)) + else: + adjusted.append(arg) + except Exception: # 回退:参数类型调整失败时使用原值 + adjusted.append(arg) + else: + adjusted.append(arg) + else: + if isinstance(arg.type, ir.FloatType): + arg = Gen.builder.fpext(arg, ir.DoubleType(), name=f"fpext_vararg_{MethodName}") + elif isinstance(arg.type, ir.IntType) and arg.type.width < 64: + if arg.type.width == 1: + arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_bool_vararg_{MethodName}") + else: + is_signed = True + arg_node = Node.args[i] if i < len(Node.args) else None + if arg_node is not None: + is_u = Gen._check_node_unsigned(arg_node) + if is_u: + is_signed = False + if is_signed: + arg = Gen.builder.sext(arg, ir.IntType(64), name=f"sext_vararg_{MethodName}") + else: + arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_vararg_{MethodName}") + adjusted.append(arg) + result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") + else: + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted = Gen._adjust_args(CallArgs, func) + result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") + return result + if Gen._has_function(MethodName): + _mf = Gen._get_function(MethodName) + if '.' not in _mf.name: + CallArgs = [] + # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 + _ptypes: list[ir.Type] = _mf.function_type.args + for _ai, arg in enumerate(Node.args): + _pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None + # 同 3357:禁止静默丢弃求值为空的实参(会导致后续前移一格) + CallArgs.append(self._RequireArgValue( + self.HandleExprLlvm( + arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)), + MethodName, _ai + 1, arg, Node)) + func = _mf + self._fill_default_args(CallArgs, func, MethodName) + func_ft = getattr(func, 'function_type', None) + is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False + num_fixed_params = len(func_ft.args) if func_ft else 0 + if is_variadic: + adjusted = [] + for i, arg in enumerate(CallArgs): + if i < num_fixed_params: + param_type = func_ft.args[i] + if arg.type != param_type: + try: + if isinstance(arg.type, ir.IntType) and isinstance(param_type, ir.PointerType): + adjusted.append(self.builder.inttoptr(arg, param_type)) + elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.IntType): + adjusted.append(Gen.builder.ptrtoint(arg, param_type)) + elif isinstance(arg.type, ir.IntType) and arg.type.width < param_type.width: + adjusted.append(Gen.builder.zext(arg, param_type)) + else: + adjusted.append(arg) + except Exception: # 回退:参数类型调整失败时使用原值 + adjusted.append(arg) + else: + adjusted.append(arg) + else: + if isinstance(arg.type, ir.FloatType): + arg = Gen.builder.fpext(arg, ir.DoubleType(), name=f"fpext_vararg_{MethodName}") + elif isinstance(arg.type, ir.IntType) and arg.type.width < 64: + if arg.type.width == 1: + arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_bool_vararg_{MethodName}") + else: + is_signed = True + arg_node = Node.args[i] if i < len(Node.args) else None + if arg_node is not None: + is_u = Gen._check_node_unsigned(arg_node) + if is_u: + is_signed = False + if is_signed: + arg = Gen.builder.sext(arg, ir.IntType(64), name=f"sext_vararg_{MethodName}") + else: + arg = Gen.builder.zext(arg, ir.IntType(64), name=f"zext_vararg_{MethodName}") + adjusted.append(arg) + result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") + else: + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted = Gen._adjust_args(CallArgs, func) + result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") + return result + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) + if not stub_func_type: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(MethodName, Gen) + if not stub_func_type and mangled_name != MethodName: + for sha1 in getattr(Gen, 'ModuleSha1Map', {}).values(): + alt_name = f"{sha1}.{MethodName}" + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(alt_name, Gen) + if stub_func_type: + mangled_name = alt_name + break + if stub_func_type: + func = self.GetOrCreateStubFuncDecl(mangled_name, stub_func_type, Gen) + CallArgs = [] + # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。 + _ptypes: list[ir.Type] = func.function_type.args if func is not None else [] + for _ai, arg in enumerate(Node.args): + _pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None + # 同 3357:禁止静默丢弃求值为空的实参(会导致后续前移一格) + CallArgs.append(self._RequireArgValue( + self.HandleExprLlvm( + arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt)), + mangled_name, _ai + 1, arg, Node)) + self._fill_default_args(CallArgs, func, mangled_name) + self._append_eh_msg_out_arg(CallArgs, func, Gen) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted = Gen._adjust_args(CallArgs, func) + result = Gen.builder.call(func, adjusted, name=f"call_{MethodName}") + self._check_eh_return(result, mangled_name, Gen, called_func=func) + return result + return ir.Constant(ir.IntType(32), 0) + CallArgs = [] + for _ai, arg in enumerate(Node.args): + # 同 3357:禁止静默丢弃求值为空的实参(会导致后续整体前移一格) + CallArgs.append(self._RequireArgValue( + self.HandleExprLlvm(arg), self._ExprText(Node.func), _ai + 1, arg, Node)) + ClassName = None + if isinstance(Node.func.value, ast.Name): + ClassName = Gen.var_struct_class.get(Node.func.value.id) + if not ClassName and isinstance(Node.func.value, ast.Attribute): + # 链式属性访问(如 func.Params.append):通过 _get_var_class 推导泛型特化类名 + ClassName = self.Trans.ExprHandler._get_var_class(Node.func.value, Gen) + if not ClassName: + for CN, ST in Gen.structs.items(): + if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == ST: + ClassName = CN + break + if not ClassName and self._is_char_pointer(ObjVal): + MethodName = Node.func.attr + # FakeDuck: 检查是否有影子结构体 a__meta__ (per-instance 字段存储) + meta_ptr: ir.Value | None = None + if isinstance(Node.func.value, ast.Name): + meta_name = f"{Node.func.value.id}__meta__" + if meta_name in Gen.variables: + meta_ptr = Gen.variables[meta_name] + for CN, ST in Gen.structs.items(): + method_key = f'{CN}.{MethodName}' + if not Gen._has_function(method_key): + continue + # FakeDuck: 不跳过空结构体 (扩展类可能没有数据成员只有方法) + try: + # 如果有影子结构体且该类有字段, 使用影子结构体作为 self (而非 bitcast char*) + if meta_ptr is not None and hasattr(ST, 'elements') and len(ST.elements) > 0: + ObjVal = meta_ptr + elif CN == '_str' and hasattr(ST, 'elements') and len(ST.elements) > 0: + # FakeDuck: 字符串字面量/匿名 str 表达式调用 _str 方法时, + # 创建临时影子结构体 (栈上), 设置 __data__ 和 __mbuddy__ 字段。 + # bitcast i8* → _str* 会导致读取垃圾指针 → ACCESS_VIOLATION + tmp_meta: ir.AllocaInstr = Gen._allocaEntry(ST, name="tmp_str_meta") + _data_off = Gen._get_member_offset('__data__', '_str') + if _data_off is not None: + _data_ptr = Gen.builder.gep(tmp_meta, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _data_off)], name="tmp_str_data") + Gen._store(ObjVal, _data_ptr) + _mpool_off = Gen._get_member_offset('__mbuddy__', '_str') + if _mpool_off is not None: + _mpool_ptr = Gen.builder.gep(tmp_meta, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _mpool_off)], name="tmp_str_mbuddy") + Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), _mpool_ptr) + # 从 with 上下文自动注入 requires 字段 (如 __mbuddy__) + self._InjectRequiresFields(tmp_meta, '_str', Gen) + ObjVal = tmp_meta + else: + casted = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}") + ObjVal = casted + ClassName = CN + break + except Exception: # 回退:bitcast 失败时尝试下一个类 + continue + if not ClassName and isinstance(Node.func.value, ast.Call): + if isinstance(Node.func.value.func, ast.Attribute): + InnerAttrName = Node.func.value.func.attr + for CN in Gen.structs: + if CN.endswith(f'__{InnerAttrName}') or f'__{InnerAttrName}' in CN: + continue + method_name = f'{CN}__{InnerAttrName}' + if Gen._has_function(method_name): + func = Gen._get_function(method_name) + ret_type = func.fnty.return_type + if isinstance(ret_type, ir.PointerType): + for CN2, ST in Gen.structs.items(): + if ret_type.pointee == ST: + ClassName = CN2 + break + elif isinstance(ret_type, ir.IdentifiedStructType): + for CN2, ST in Gen.structs.items(): + if ret_type == ST: + ClassName = CN2 + break + if ClassName: + break + if not ClassName: + return ir.Constant(ir.IntType(32), 0) + # REnum.kind() 编译期转 __tag 读取(位置 0,i32): + # REnum 无 kind 方法(源码无、编译器不生成),此前 .kind() 被"兄弟 Mixin + # 兜底"(L3203-3240)误绑到 AST.kind(this 前移 sizeof=-48,语义错误)。 + # 此处把 kind() 直接翻译为 bitcast 到 i32* + load —— 零函数调用, + # 返回变体 tag,与 match 读 __tag 语义一致。 + if MethodName == 'kind': + _renum_nm: str = ClassName[17:] if (len(ClassName) > 17 and ClassName[16] == '.') else ClassName + if '.' in _renum_nm: # 模块限定名 "llvmlite.LLVMType" → 裸名 + _renum_nm = _renum_nm.rsplit('.', 1)[1] + _renum_info: Any = self.Trans.SymbolTable.lookup(_renum_nm) if _renum_nm else None + if _renum_info is not None and getattr(_renum_info, 'IsRenum', False): + if isinstance(ObjVal.type, ir.PointerType): + _kind_ptr: ir.Value = Gen.builder.bitcast( + ObjVal, ir.PointerType(ir.IntType(32)), name="renum_kind_ptr") + return Gen._load(_kind_ptr, name="renum_kind") + # 泛型特化定位:当 ClassName 是 opaque 基础名(如 'ndarray'/'list',不含 '[') + # 时,按【接收者 LLVM 值类型】反查唯一的具体特化,而不再遍历 Gen.structs 按 + # 迭代顺序任选一个 'ClassName[' 开头的特化——任选会命中 list[int] 之类元素 + # 类型错误的特化,方法返回 i32 被 inttoptr 当指针 → 运行期崩溃。 + if '[' not in ClassName: + SpecClass: str | None = None + # 1) 以接收者 LLVM 值类型的 pointee 结构体反查真实类名(唯一解)。 + _recv_found: tuple[str, ir.IdentifiedStructType] | None = None + _recv_type: Any = getattr(ObjVal, 'type', None) + if isinstance(_recv_type, ir.PointerType): + _recv_found = Gen.find_struct_by_pointee(_recv_type.pointee) + if _recv_found is not None and '[' in _recv_found[0]: + SpecClass = _recv_found[0] + # 2) 接收者实型无法唯一确定具体特化(裸泛型基类 / 反查不到结构体): + # 仅当「基名匹配 + 该类型下存在目标方法」的特化唯一时才采用;存在 + # 多个候选 ⇒ fail-fast(禁止按 Gen.structs 顺序任选)。0 个候选表示 + # 该基名下没有任何带此方法的特化(普通非泛型类方法调用路径),保持 + # ClassName 不变、交由后续常规分派。 + if SpecClass is None: + _base_src: str = _recv_found[0] if _recv_found is not None else ClassName + _base_short: str = Gen._extract_short_name(_base_src) + _cands: list[str] = [] + for CN in Gen.structs.keys(): + if '[' not in CN or CN.split('.')[-1].split('[')[0] != _base_short: + continue + struct_sha1_tmp = getattr(Gen, '_struct_sha1_map', {}).get(CN) + if struct_sha1_tmp and Gen._has_function(f'{struct_sha1_tmp}.{CN}.{MethodName}'): + _cands.append(CN) + elif Gen._has_function(f'{CN}.{MethodName}'): + _cands.append(CN) + if len(_cands) == 1: + SpecClass = _cands[0] + elif len(_cands) > 1: + raise Exception( + f"方法调用 '{MethodName}' 的接收者无法唯一确定泛型特化:" + f"基名 '{_base_short}' 下存在多个带该方法的结构体 {_cands}," + f"禁止按 Gen.structs 顺序任选(会分派到错误元素类型)。") + if SpecClass: + ClassName = SpecClass + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + # 归一化:ClassName 可能带 sha1 前缀(跨模块 structs 表键,如 + # "940e5294667daf5a.ExprGenAttr"),其前 16 位即模块 sha1,直接用作 + # class_sha1 并免拼接(否则 struct_sha1_map.get(带前缀名)=None → class_sha1 + # 为空 → 下方 stub 查找路径被跳过 → fail-fast "Undefined method + # 'get_attribute_ptr' in class '...ExprGenAttr'")。裸类名则从映射表取 sha1。 + cls_has_prefix: bool = len(ClassName) > 17 and ClassName[16] == '.' + if cls_has_prefix: + class_sha1 = ClassName[:16] + FullMethodName = f'{ClassName}.{MethodName}' + else: + class_sha1 = struct_sha1_map.get(ClassName) + if class_sha1: + FullMethodName = f'{class_sha1}.{ClassName}.{MethodName}' + else: + FullMethodName = f'{ClassName}.{MethodName}' + + # 泛型特化类方法按需完整特化:当 ClassName 是特化名(如 ndarray[double]) + # 且缓存中只有 declare_only 版本时,补发方法体。否则方法调用会查找 + # stub declare(无 define),链接时报 undefined reference。 + self._try_complete_generic_specialization(ClassName, MethodName, Gen) + + # 提前查找函数以获取参数信息 + func_for_kw = Gen._find_function(FullMethodName) + if not func_for_kw: + func_for_kw = Gen._find_function(f'{FullMethodName}__') + if not func_for_kw and class_sha1: + func_for_kw = Gen._find_function(f'{ClassName}.{MethodName}') + + # 处理关键字参数: 先用 _fill_default_args 填充默认值,再用 keyword args 覆盖 + if Node.keywords and func_for_kw: + # 优先从全局参数名表获取 Python 参数名(支持跨模块 include 函数) + global_param_names = getattr(self.Trans, '_global_function_param_names', {}) + param_names = global_param_names.get(FullMethodName) + if not param_names: + param_names = global_param_names.get(f'{ClassName}.{MethodName}') + if not param_names: + # 回退到 LLVM 函数参数名(仅当参数名不是自动生成的数字名时有效) + llvm_names = [arg.name for arg in func_for_kw.args] if func_for_kw.args else [] + # 检测是否为 LLVM 自动生成的数字名(如 "0", "1", "2") + if llvm_names and not all(n.isdigit() for n in llvm_names): + param_names = llvm_names + if param_names: + # 构造临时 call_args (含 self) 调用 _fill_default_args 填充默认值 + temp_call_args = [ObjVal] + list(CallArgs) + self._fill_default_args(temp_call_args, func_for_kw, FullMethodName) + # 提取回非 self 参数 (现在已填充默认值) + CallArgs.clear() + CallArgs.extend(temp_call_args[1:]) + # 用 keyword args 覆盖对应位置 + for kw in Node.keywords: + if kw.arg is None: + continue + ArgVal = self.HandleExprLlvm(kw.value) + if not ArgVal: + continue + kw_name = kw.arg + if kw_name in param_names: + idx = param_names.index(kw_name) + # idx 0 是 self, 所以 CallArgs 中的偏移是 idx-1 + target_idx = idx - 1 + if 0 <= target_idx < len(CallArgs): + CallArgs[target_idx] = ArgVal + + # 按需 vtable 注册:当 ClassName 不在 vtable 集合中但有 class_sha1 时, + # 从 .pyi stub 检查 @t.CVTable 装饰器并注册。 + # 修复场景:list[str].__new__ 中 pool.alloc(48) 编译时, + # MemManager 可能因导入时序问题未注册到当前 Gen 的 vtable 集合, + # 导致生成 call_(直接调用)而非 vcall_(虚分派),运行时段错误。 + if (ClassName not in Gen.class_vtable + and ClassName not in Gen._cross_module_vtable_classes + and class_sha1): + self._try_register_vtable_ondemand(ClassName, class_sha1, Gen) + # 跨模块多继承:确保 class_parents 可用(MRO 回退需要), + # 如 HandlesType 委托调用 Generator 的方法(定义类在 TypeGenMixin)。 + self._EnsureClassParentsLoaded(ClassName, Gen) + + if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods: + # 根本修复:vtable 分派要求接收者 struct 已具化。元数据(vtable 集合 / + # class_methods)与 struct body 可能因并行 include 翻译时序撕裂—— + # _try_register_vtable_ondemand 只注册元数据不具化 body,若此时 struct + # 仍 opaque,下方 GEP [0,0] 取 vtable 指针会抛 llvmlite 裸 TypeError + # (典型:list[AST|t.CPtr].append 中 self.__pool__.alloc(...), + # __pool__ 类型 memhub.MemManager|t.CPtr,MemManager 已进 vtable 集合 + # 但 struct body 未从 stub 具化)。 + # 此处先主动具化(stub + class_members 双路径),仍失败则 fail-fast + # 暴露上游 stub 加载时序问题,而非静默崩溃。 + ObjVal = self._EnsureReceiverStructConcrete(ClassName, class_sha1, ObjVal, Gen) + methods = Gen.class_methods[ClassName] + method_idx = -1 + for mi, mn in enumerate(methods): + if mn == FullMethodName or mn == MethodName or mn.endswith(f'.{MethodName}'): + method_idx = mi + break + if method_idx >= 0: + func = Gen._find_function(FullMethodName) + # 方法实际定义类(多继承 this 调整用,None=本类或未知) + method_anc: str | None = None + # 仅当无函数或只有 stub 声明(无函数体)时,沿 MRO 祖先链回退, + # 否则 stub 会挡住回退导致 this 调整失效(与直接调用路径一致) + if not func or len(list(func.blocks)) == 0: + func = Gen._find_function(f'{FullMethodName}__') + if not func or len(list(func.blocks)) == 0: + # 多继承:沿 MRO 祖先链查找定义类方法(先声明父类优先) + for anc in Gen._get_mro_chain(ClassName)[1:]: + parent_full = f'{anc}.{MethodName}' + func = Gen._find_function(parent_full) + if not func: + func = Gen._find_function(f'{parent_full}__') + if func: + method_anc = anc + break + # 修复:vtable 分派需要 func 的函数类型。当 func=None 时(如 __new__ + # 编译较早,MemManager.alloc 的 declare 尚未加入 Gen.functions), + # 从 stub 获取函数类型并创建 declare,确保 vtable 分派不被跳过 + # 回退到直接调用(直接调用基类 MemManager.alloc 返回 NULL → 段错误)。 + if not func and class_sha1: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(FullMethodName, Gen) + if stub_func_type: + func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) + if not func and class_sha1: + plain_name = f'{ClassName}.{MethodName}' + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen) + if stub_func_type: + func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) + if not func: + plain_name = f'{ClassName}.{MethodName}' + func = Gen._find_function(plain_name) + if func: + # 多继承 this 调整:继承方法定义在无 vtable 父类且字段区偏移 > 0 时, + # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 + # receiver 为 i8*/泛型指针(CVTable 抽象基类如 ast.AST 无独立 + # struct 类型、联合字段存储为 i8*)时 vtable 槽恒在对象偏移 0: + # bitcast 到 ClassName 的 struct(无则建 opaque 占位)再 GEP, + # 否则 gep(i8) 报 "'IntType' object has no attribute 'gep'"。 + if (isinstance(ObjVal.type, ir.PointerType) + and not isinstance(ObjVal.type.pointee, + (ir.IdentifiedStructType, ir.LiteralStructType))): + _vt_st_rc: ir.Type = Gen.structs.get(ClassName) + if _vt_st_rc is None: + _vt_st_rc = Gen._get_or_create_struct(ClassName) + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(_vt_st_rc), + name=f"vt_cast_{ClassName}") + self_arg = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc) + VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}") + VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}") + VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) + VtableTyped = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}") + MethodPtrAddr = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_ptr_addr_{MethodName}") + MethodPtrI8 = Gen._load(MethodPtrAddr, name=f"vmethod_ptr_{MethodName}") + FuncPtrType = ir.PointerType(func.function_type) + MethodPtrTyped = Gen.builder.bitcast(MethodPtrI8, FuncPtrType, name=f"vmethod_typed_{MethodName}") + call_args = [self_arg] + CallArgs + self._fill_default_args(call_args, func, FullMethodName) + self._append_eh_msg_out_arg(call_args, func, Gen) + adjusted = Gen._adjust_args(call_args, func) + result = Gen.builder.call(MethodPtrTyped, adjusted, name=f"vcall_{FullMethodName}") + self._check_eh_return(result, FullMethodName, Gen, called_func=func) + return result + func = Gen._find_function(FullMethodName) + # 方法实际定义类(多继承 this 调整用,None=本类或未知) + method_anc: str | None = None + if not func: + func = Gen._find_function(f'{FullMethodName}__') + if not func and class_sha1: + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(FullMethodName, Gen) + if stub_func_type: + func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) + if not func and class_sha1: + plain_name = f'{ClassName}.{MethodName}' + stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen) + if stub_func_type: + func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen) + # 最终回退:尝试无 sha1 前缀的函数名(与 func_for_kw 的双路径回退一致) + if not func: + plain_name = f'{ClassName}.{MethodName}' + func = Gen._find_function(plain_name) + # 若 FullMethodName 只有 stub 声明(无函数体),也走 MRO 回退找定义类方法 + if not func or len(list(func.blocks)) == 0: + # 多继承:沿 MRO 祖先链查找定义类方法(先声明父类优先), + # 继承方法用"定义类.方法"(如 B.get_b)直接调用,不再依赖子类包装器。 + for anc in Gen._get_mro_chain(ClassName)[1:]: + anc_full = f'{anc}.{MethodName}' + func = Gen._find_function(anc_full) + if not func: + func = Gen._find_function(f'{anc_full}__') + if func: + method_anc = anc + break + if not func or len(list(func.blocks)) == 0: + # 兄弟 Mixin 兜底:仅当"调用类自身未声明该方法"时启用。 + # 判断依据 class_methods[ClassName]: + # * BaseB.caller 调 self.helper()——BaseB 无 helper(源码未定义) + # → 跨 Mixin → 在其它注册类中找定义类(BaseA.helper)✓ + # * JsonValue.__getitem__ 源码定义存在但 stub 未命中(如 VPC 自身 + # 编译 Config.vp 时跨模块 stub 注册/时序问题)→ 不兜底, + # fail-fast(Undefined method)暴露上游 stub 问题——避免误绑 + # 无关类 HashTable.__getitem__(this 前移 sizeof(HashTable)=-48 + # → Load_project_config 读 -1 崩溃,1.LOG 根因;3fd875d 只 + # 过滤 list 特化,普通类仍被误绑)。 + # 注:不能用 MRO/组合类判断(Combined 定义在 BaseB 之后,BaseB.caller + # 翻译时组合类未注册,MRO 查询不可靠——780cd4a 引入 NoVTable 回归)。 + _self_has_method: bool = False + _cm_map = getattr(Gen, 'class_methods', None) + if isinstance(_cm_map, dict): + _self_methods = _cm_map.get(ClassName) + if _self_methods: + for _sm in _self_methods: + if _sm == MethodName or _sm.endswith(f'.{MethodName}'): + _self_has_method = True + break + if not _self_has_method: + for _cand_cls, _cand_methods in list(Gen.class_methods.items()): + if _cand_cls == ClassName: + continue + if '[' in _cand_cls: + continue + if not any(_cm.endswith(f'.{MethodName}') or _cm == MethodName for _cm in _cand_methods): + continue + _cand_full = f'{_cand_cls}.{MethodName}' + _cand_func = Gen._find_function(_cand_full) + if not _cand_func: + _cand_func = Gen._find_function(f'{_cand_full}__') + if _cand_func: + func = _cand_func + method_anc = _cand_cls + break + if func: + # 多继承 this 调整:继承方法定义在无 vtable 父类且字段区偏移 > 0 时, + # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 + self_arg = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc) + call_args = [self_arg] + CallArgs + self._fill_default_args(call_args, func, FullMethodName) + self._append_eh_msg_out_arg(call_args, func, Gen) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted = Gen._adjust_args(call_args, func) + result = Gen.builder.call(func, adjusted, name=f"call_{FullMethodName}") + self._check_eh_return(result, FullMethodName, Gen, called_func=func) + return result + i8ptr = ir.PointerType(ir.IntType(8)) + member_idx = -1 + if ClassName in Gen.class_members: + for mi, (mn, mt) in enumerate(Gen.class_members[ClassName]): + if mn == MethodName: + member_idx = mi + break + if member_idx >= 0 and isinstance(ObjVal.type, ir.PointerType): + # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 + base = Gen._member_base(ClassName) + member_ptr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + member_idx)], name=f"funcptr_{MethodName}_ptr") + raw_ptr = Gen._load(member_ptr, name=f"funcptr_{MethodName}_raw") + func_ptr_type = ir.PointerType(ir.FunctionType(ir.IntType(32), [i8ptr] + [a.type for a in CallArgs])) + typed_ptr = Gen.builder.bitcast(raw_ptr, func_ptr_type, name=f"funcptr_{MethodName}_typed") + obj_as_i8 = Gen.builder.bitcast(ObjVal, i8ptr, name=f"funcptr_self_{ClassName}") + call_args = [obj_as_i8] + CallArgs + result = Gen.builder.call(typed_ptr, call_args, name=f"call_funcptr_{ClassName}.{MethodName}") + return result + if class_sha1 and ClassName in Gen.structs: + struct_type = Gen.structs[ClassName] + self_ptr_type = ir.PointerType(struct_type) + # 修复 Bug 4: 从符号表获取完整参数列表(含默认参数)和正确返回类型, + # 而非仅使用 CallArgs(漏掉默认参数)和硬编码 i32 返回类型。 + # 这避免了 main.py 中 declare Logger.info(...,i8*) (2参数) + # 与 VLogger 中 define Logger.info(...,i8*,i8*) (3参数)的签名冲突。 + ret_type: Any = ir.IntType(32) + param_types: list[Any] = [self_ptr_type] + [a.type for a in CallArgs] + SymInfoFallback = self.LookupFunctionSymbol(MethodName, module_path=ClassName) + if SymInfoFallback and SymInfoFallback.IsFunction: + sig = self.BuildLLVMFuncTypeFromSig(SymInfoFallback, Gen) + if sig is not None: + sig_ret, sig_params, _ = sig + ret_type = sig_ret + is_static_fb = FuncMeta.STATIC_METHOD in SymInfoFallback.MetaList + if not is_static_fb: + sig_params = [self_ptr_type] + sig_params + param_types = sig_params + func_type = ir.FunctionType(ret_type, param_types) + # 复用已存在的声明(vpi 合并/LoadDepVpiIntoModule 可能已注册同名 declare), + # 避免 llvmlite DuplicatedNameError(如 gen.__init__ / ImportsHandle.HandleImport) + func = Gen._find_function(FullMethodName) + if func is None: + # 走唯一幂等入口(module.globals 同名函数复用 / 类型冲突 fail-fast), + # 不再裸建 ir.Function —— Guard 只看 Gen.functions 时漏掉 + # module.globals 已有声明会抛 llvmlite DuplicatedNameError。 + func = Gen._CreateOrReuseFuncDecl(FullMethodName, func_type) + if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == struct_type: + call_self = ObjVal + else: + call_self = Gen.builder.bitcast(ObjVal, self_ptr_type, name=f"method_self_{ClassName}") + call_args_fb = [call_self] + CallArgs + self._fill_default_args(call_args_fb, func, FullMethodName) + self._append_eh_msg_out_arg(call_args_fb, func, Gen) + adjusted = Gen._adjust_args(call_args_fb, func) + result = Gen.builder.call(func, adjusted, name=f"call_{FullMethodName}") + return result + raise Exception(f"Undefined method '{MethodName}' in class '{ClassName}' (no function declaration found for '{FullMethodName}')") + + def _EnsureReceiverStructConcrete(self, ClassName: str, class_sha1: str | None, ObjVal: Any, Gen: "LlvmGeneratorMixin") -> Any: + """根本修复:vtable 分派前保证接收者 struct 已具化(非 opaque)。 + + 背景:跨模块类的 vtable 元数据(class_vtable / _cross_module_vtable_classes / + class_methods)由 _precollect_vtable_state 预扫描或 _try_register_vtable_ondemand + 按需注册,而 struct body 依赖 _TryLoadStructFromStub 从 stub.ll 加载。 + 二者时序可能撕裂:body 仍 opaque 就对 struct 做 GEP [0,0] 取 vtable 指针, + llvmlite 在 types.py 的 elements[i] 处抛裸 TypeError('NoneType' object is + not subscriptable)。 + + 本方法: + 1. 先走 stub.ll 补 body(source_sha1 精确匹配优先) + 2. 再补 class_members(供 _generate_structs 用成员构造 body 的备选路径) + 3. 若 Gen.structs 中已具化版本与 ObjVal 指向的 opaque 占位对象不同, + bitcast 替换 ObjVal 到具体版本 + 4. 仍 opaque → fail-fast 报带源码上下文的编译错误(遵循"不兜底"原则, + 暴露上游 stub 加载/时序问题而非让 llvmlite 裸崩) + + Returns: + ObjVal 本身或 bitcast 到具体 struct 版本后的新值。 + """ + if not isinstance(ObjVal.type, ir.PointerType): + return ObjVal + pointee: ir.Type = ObjVal.type.pointee + if not isinstance(pointee, ir.IdentifiedStructType) or not pointee.is_opaque: + return ObjVal + + import_handler: Any = self.Trans.ImportHandler + # 1) stub.ll 补 body(source_sha1 精确匹配优先) + if class_sha1: + import_handler._TryLoadStructFromStub(ClassName, Gen, source_sha1=class_sha1) + else: + import_handler._TryLoadStructFromStub(ClassName, Gen) + # 2) 补 class_members(备选具化路径) + stub_basename: str = f'{class_sha1}.stub.ll' if class_sha1 else f'{ClassName}.stub.ll' + import_handler._TryLoadClassMembersFromPyi(ClassName, stub_basename, Gen) + + # 3) 从 Gen.structs 找已具化版本(短名优先,再按 sha1 全名) + concrete_st: ir.IdentifiedStructType | None = None + _st_short: Any = Gen.structs.get(ClassName) + if isinstance(_st_short, ir.IdentifiedStructType) and not _st_short.is_opaque: + concrete_st = _st_short + if concrete_st is None and class_sha1: + _st_full: Any = Gen.structs.get(f'{class_sha1}.{ClassName}') + if isinstance(_st_full, ir.IdentifiedStructType) and not _st_full.is_opaque: + concrete_st = _st_full + if concrete_st is None: + # 4) 仍 opaque → fail-fast(暴露上游 stub 加载/时序问题) + raise Exception( + f"vtable 分派失败: 类 '{ClassName}' 已注册虚表(class_vtable/class_methods)" + f"但 struct 类型无法从 stub 具化(仍为 opaque){Gen._get_node_info()}" + ) + if concrete_st is pointee: + return ObjVal + # ObjVal 指向旧 opaque 占位对象:bitcast 到具体版本 + return Gen.builder.bitcast(ObjVal, ir.PointerType(concrete_st), name=f"concrete_self_{ClassName}") + + _C_LIB_FUNCS = { + 'malloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.IntType(64)]), + 'calloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.IntType(64), ir.IntType(64)]), + 'realloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'free': lambda: (ir.VoidType(), [ir.PointerType(ir.IntType(8))]), + 'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'strlength': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'memset': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]), + 'memcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'memmove': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'memcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'strncmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'strncpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcat': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'puts': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), + 'atoi': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), + 'atol': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'abs': lambda: (ir.IntType(32), [ir.IntType(32)]), + 'labs': lambda: (ir.IntType(64), [ir.IntType(64)]), + 'system': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), + } + + def _try_declare_c_lib_func(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.Function | None: + if func_name not in self._C_LIB_FUNCS: + return None + ret_type, param_types = self._C_LIB_FUNCS[func_name]() + func_type = ir.FunctionType(ret_type, param_types) + # C 库声明名固定(malloc/strlen/...),同一模块内多次调用必须复用同一符号: + # 裸建会在第二次抛 llvmlite DuplicatedNameError。 + return Gen._CreateOrReuseFuncDecl(func_name, func_type) + def _FailFastLenUnsupported(self, arg_node: ast.AST, Gen: LlvmGeneratorMixin, val: ir.Value | None, reason: str) -> None: - """len(x) 参数形态不支持时的编译期 fail-fast 出口(TPC 与 TransPyV 同口径)。 + """len(x) 参数形态不支持时的编译期 fail-fast 出口(TPC 与 ViperC 同口径)。 - 规则(对齐 TransPyV App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支): + 规则(对齐 ViperC App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支): 定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen; 带 __len__ 的结构体 → 调用 __len__;其余一律编译期报错。 非 i8 裸指针(如 `[]T` 形参实为 `T*`)与标量指针**没有长度语义**,旧实现会 @@ -4146,217 +4152,217 @@ class ExprCallHandle(BaseHandle): + (f",源码: {code_line}" if code_line else '')) raise TypeError(msg) - def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None: - Gen = self.Trans.LlvmGen - if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name) and arg_node.value.id == 'self': - attr_name = arg_node.attr - len_member = f'{attr_name}__len' - current_class = self.Trans._CurrentCpythonObjectClass - # 关键:_CurrentCpythonObjectClass 可能带 sha1 前缀(如 - # 'a351387236c6ce1c.TypeGenCType'),而 class_members / - # class_member_element_class 的 key 全部是裸类名。必须先把前缀剥离 - # 再做 `in Gen.class_members` 检查——否则带前缀时 gate 直接 miss, - # 整个 self.attr 分支(含 list.__len__ 修复)被跳过,fall through - # 到 strlen(stdint.vp 0xC0000005 崩溃根因)。 - bare_cls: str | None = current_class - if bare_cls and len(bare_cls) > 17 and bare_cls[16] == '.': - bare_cls = bare_cls[17:] - if bare_cls and bare_cls in Gen.class_members: - for m_name, m_type in Gen.class_members[bare_cls]: - if m_name == len_member: - self_val = Gen._get_var_ptr('self') - if self_val: - self_ptr = Gen._load(self_val, name="self_for_len") - offset = Gen._get_member_offset(len_member, bare_cls) - len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"self_{len_member}_ptr") - return Gen._load(len_ptr, name=f"self_{len_member}") - # 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段 - # 的 LLVM 类型退化为 i8*,_HandleLenLlvm 无法识别为 list,fall through - # 到 strlen 分支 → 对 list 对象指针执行 strlen → 垃圾长度 → 越界 get - # → 垃圾指针 → strcmp 崩。此处按字段静态类型(class_member_element_class - # 记录的特化名,如 'list[str]',沿 MRO 链查找含父类)分派到 list.__len__。 - field_cls: str | None = None - _elem_map: dict = getattr(Gen, 'class_member_element_class', {}) - for _mro_cls in Gen._get_mro_chain(bare_cls): - # class_member_element_class 的 key 是裸类名(如 'TypeGenBase'), - # _get_mro_chain 在输入裸名时也返回裸名链;再保险剥一次前缀。 - _mro_key: str = _mro_cls - if len(_mro_cls) > 17 and _mro_cls[16] == '.': - _mro_key = _mro_cls[17:] - _sub: dict = _elem_map.get(_mro_key, {}) - if attr_name in _sub: - field_cls = _sub[attr_name] - break - if field_cls and (field_cls == 'list' or field_cls.startswith('list[')): - self_val = Gen._get_var_ptr('self') - if self_val: - self_ptr = Gen._load(self_val, name="self_for_len") - offset = Gen._get_member_offset(attr_name, bare_cls) - 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"self_{attr_name}_ptr") - list_val = Gen._load(list_ptr, name=f"self_{attr_name}") - result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen) - if result is not None: - return result - val = self.HandleExprLlvm(arg_node) - if not val: - return None - if isinstance(val.type, ir.PointerType): - pointee = val.type.pointee - if isinstance(pointee, ir.PointerType): - val = Gen._load(val, name="len_deref") - pointee = val.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - # struct(或任何非基本类型)→ 仍先走既有 __len__ 分派(保持不变); - # 没有 __len__ 时由旧实现的「回落 strlen」改为 fail-fast(与 TPV 同口径)。 - _matched_cn: str | None = None - for CN, ST in Gen.structs.items(): - if pointee == ST: - _matched_cn = CN - break - if _matched_cn is not None: - result = self.Trans.ExprUtils._try_operator_overLoad(_matched_cn, '__len__', val, Gen) - if result is not None: - return result - self._FailFastLenUnsupported(arg_node, Gen, val, f"len() 参数类型 '{_matched_cn}' 没有 __len__ 方法") - else: - # 未在 Gen.structs 登记的 struct(opaque/匿名)→ fail-fast。 - self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") - if isinstance(pointee, ir.ArrayType): - return ir.Constant(ir.IntType(64), pointee.count) - # i8*(含 str/bytes 指针形态)→ strlen(保持不变)。 - if isinstance(pointee, ir.IntType) and pointee.width == 8: - if val.type != ir.PointerType(ir.IntType(8)): - val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast") - correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))]) - strlen_func = None - if 'strlen' in Gen.functions: - existing = Gen.functions['strlen'] - if existing.ftype == correct_strlen_type: - strlen_func = existing - if not strlen_func: - try: - strlen_func = Gen._CreateOrReuseFuncDecl('strlen', correct_strlen_type) - Gen.functions['strlen'] = strlen_func - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - raise - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - if strlen_func: - result = Gen.builder.call(strlen_func, [val], name="strlen_call") - return result - return None - # 非 i8 裸指针 / 标量指针(i32*/i64*/float*/double*)→ fail-fast - # (旧实现静默返回常量 0 或回落 strlen,与 TPV 口径不符)。 - self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") - elif isinstance(val.type, ir.ArrayType): - return ir.Constant(ir.IntType(64), val.type.count) - # 其它不支持形态(标量值 / 结构体值等)→ fail-fast(旧实现静默返回常量 0)。 - self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") - - def _resolve_opaque_struct_members(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, _visited: set | None = None) -> None: - """Recursively resolve opaque struct members by loading from stub files.""" - if _visited is None: - _visited = set() - if not isinstance(struct_type, ir.IdentifiedStructType): - return - # 防止循环引用导致无限递归 - try: - st_id = id(struct_type) - except Exception: - st_id = struct_type - if st_id in _visited: - return - _visited.add(st_id) - if struct_type.elements is None or len(struct_type.elements) == 0: - # Find the class name for this opaque struct and try to resolve - for cn, st in Gen.structs.items(): - try: - if st == struct_type or (hasattr(st, 'name') and hasattr(struct_type, 'name') and st.name == struct_type.name): - self.Trans.ImportHandler._TryLoadStructFromStub(cn, Gen) - break - except (AssertionError, RuntimeError): - pass - # Re-get the struct in case it was resolved - if struct_type.elements: - for elem in struct_type.elements: - if isinstance(elem, ir.IdentifiedStructType): - self._resolve_opaque_struct_members(elem, Gen, _visited) - elif isinstance(elem, ir.PointerType): - pointee = elem.pointee - if isinstance(pointee, ir.IdentifiedStructType): - self._resolve_opaque_struct_members(pointee, Gen, _visited) - - def _resolve_type_arg_str(self, node: ast.AST) -> str: - """将类型参数 AST 节点解析为规范化的类型字符串(用于泛型特化的 mangling)。 - - 规则本体在 SymbolUtils.ResolveTypeArgStr(定义端/声明端唯一规则源, - 声明端 DeclarationGenerator 的泛型分支亦调用同一函数),此处仅注入 - 翻译器上下文: - - t/c 导入别名表(from t import CInt as myint → 't.CInt') - - 跨模块结构体 SHA1 表(ast.Name/Attribute 命中 → '{sha1}.{名}',与普通签名一致) - """ - Gen = self.Trans.LlvmGen - StructSha1Map: dict[str, str] = getattr(Gen, '_struct_sha1_map', {}) if Gen is not None else {} - return ResolveTypeArgStr( - node, - TCAliasMap=getattr(self.Trans, '_t_c_imported_names', {}), - StructSha1Map=StructSha1Map) - - def _get_raw_type_name(self, node: ast.AST) -> str: - """获取类型节点的原始名称(不带模块前缀),用于泛型 T 替换。 - - ast.Name → sn.id (如 'AST', 'CInt') - ast.Attribute → sn.attr (如 t.CInt → 'CInt') - ast.BinOp → 左侧的原始名称 (如 AST | CPtr → 'AST') - """ - if isinstance(node, ast.Name): - return node.id - if isinstance(node, ast.Attribute): - return node.attr - if isinstance(node, ast.BinOp): - return self._get_raw_type_name(node.left) - return ast.unparse(node) - - def _resolve_generic_slice_type(self, sn: ast.AST) -> tuple[str, str] | None: - """解析泛型 slice 中的单个类型参数节点,返回 (type_arg, type_name) 或 None。 - - type_arg: 用于 mangling,走**唯一规则源** _resolve_type_arg_str - (内层与参数注解同解析:'AST | t.CPtr' / 'rawptr[AST]' → - '{sha1}.AST*'、裸类名 'AST' → '{sha1}.AST'、标量按 - NormalizeGenericArgName 归一) - type_name: 用于替换泛型类型参数 T(原始源码名,如 'AST' / 'CInt', - _specialize_generic_class 会自动给类名加 | t.CPtr) - 指针下标 rawptr[T] 先折叠成等价的 `T | t.CPtr` 再解析, - 与源码联合写法得到同一 (type_arg, type_name):否则 list[rawptr[ast.AST]] - 会拼出 'rawptr[ast.AST]' 实参串(内层未混淆)→ 与 list[ast.AST | t.CPtr] - 的 '.AST*' 分裂成两个特化名(结构体/方法声明/调用点三端不一致)。 - """ - if IsPtrSubscriptNode(sn, self.Trans.SymbolTable): - sn = DesugarPtrSubscriptToUnion(sn, self.Trans.SymbolTable) - if isinstance(sn, ast.Name): - # type_arg 走唯一规则源(命中注册类加 SHA1 前缀,裸名与 - # `list[X]`/`rawptr[X]` 内层收敛到同一名字); - # type_name 保留源码名供 T 替换与内建/类类型判定(两张表收拢在 SymbolUtils) - return (self._resolve_type_arg_str(sn), GenericArgRawName(sn.id)) - # BinOp (如 AST | CPtr) 或 Attribute (如 t.CPtr / t.CInt) - type_str: str = self._resolve_type_arg_str(sn) - # type_name 保留模块前缀(如 't.CUnsignedChar'),避免 T(0) 替换为 - # CUnsignedChar(0) 后因 CUnsignedChar 不在顶层作用域而报 - # "Undefined function: 'CUnsignedChar'"。 - # t.CPtr 特殊处理:保留 't.CPtr' 带前缀(2026-08-19 根修:此前裸 - # 'CPtr' 作为 type_map['T'] 替换进模板(item: T → item: CPtr)时, - # CPtr 不在顶层作用域 → 方法声明注册失败 → "Undefined method - # 'append' in class 'list[CPtr]'"。带前缀 't.CPtr' 可解析。 - # 指针参数识别改由 _BUILTIN_TYPE_NAMES 的 tn_bare 检测('t.CPtr'→'CPtr')。 - if isinstance(sn, ast.Attribute) and isinstance(sn.value, ast.Name): - raw_name: str = f'{sn.value.id}.{sn.attr}' - else: - raw_name: str = self._get_raw_type_name(sn) - return (type_str, raw_name) - + def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None: + Gen = self.Trans.LlvmGen + if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name) and arg_node.value.id == 'self': + attr_name = arg_node.attr + len_member = f'{attr_name}__len' + current_class = self.Trans._CurrentCpythonObjectClass + # 关键:_CurrentCpythonObjectClass 可能带 sha1 前缀(如 + # 'a351387236c6ce1c.TypeGenCType'),而 class_members / + # class_member_element_class 的 key 全部是裸类名。必须先把前缀剥离 + # 再做 `in Gen.class_members` 检查——否则带前缀时 gate 直接 miss, + # 整个 self.attr 分支(含 list.__len__ 修复)被跳过,fall through + # 到 strlen(stdint.vp 0xC0000005 崩溃根因)。 + bare_cls: str | None = current_class + if bare_cls and len(bare_cls) > 17 and bare_cls[16] == '.': + bare_cls = bare_cls[17:] + if bare_cls and bare_cls in Gen.class_members: + for m_name, m_type in Gen.class_members[bare_cls]: + if m_name == len_member: + self_val = Gen._get_var_ptr('self') + if self_val: + self_ptr = Gen._load(self_val, name="self_for_len") + offset = Gen._get_member_offset(len_member, bare_cls) + len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"self_{len_member}_ptr") + return Gen._load(len_ptr, name=f"self_{len_member}") + # 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段 + # 的 LLVM 类型退化为 i8*,_HandleLenLlvm 无法识别为 list,fall through + # 到 strlen 分支 → 对 list 对象指针执行 strlen → 垃圾长度 → 越界 get + # → 垃圾指针 → strcmp 崩。此处按字段静态类型(class_member_element_class + # 记录的特化名,如 'list[str]',沿 MRO 链查找含父类)分派到 list.__len__。 + field_cls: str | None = None + _elem_map: dict = getattr(Gen, 'class_member_element_class', {}) + for _mro_cls in Gen._get_mro_chain(bare_cls): + # class_member_element_class 的 key 是裸类名(如 'TypeGenBase'), + # _get_mro_chain 在输入裸名时也返回裸名链;再保险剥一次前缀。 + _mro_key: str = _mro_cls + if len(_mro_cls) > 17 and _mro_cls[16] == '.': + _mro_key = _mro_cls[17:] + _sub: dict = _elem_map.get(_mro_key, {}) + if attr_name in _sub: + field_cls = _sub[attr_name] + break + if field_cls and (field_cls == 'list' or field_cls.startswith('list[')): + self_val = Gen._get_var_ptr('self') + if self_val: + self_ptr = Gen._load(self_val, name="self_for_len") + offset = Gen._get_member_offset(attr_name, bare_cls) + 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"self_{attr_name}_ptr") + list_val = Gen._load(list_ptr, name=f"self_{attr_name}") + result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen) + if result is not None: + return result + val = self.HandleExprLlvm(arg_node) + if not val: + return None + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, ir.PointerType): + val = Gen._load(val, name="len_deref") + pointee = val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + # struct(或任何非基本类型)→ 仍先走既有 __len__ 分派(保持不变); + # 没有 __len__ 时由旧实现的「回落 strlen」改为 fail-fast(与 VPC 同口径)。 + _matched_cn: str | None = None + for CN, ST in Gen.structs.items(): + if pointee == ST: + _matched_cn = CN + break + if _matched_cn is not None: + result = self.Trans.ExprUtils._try_operator_overLoad(_matched_cn, '__len__', val, Gen) + if result is not None: + return result + self._FailFastLenUnsupported(arg_node, Gen, val, f"len() 参数类型 '{_matched_cn}' 没有 __len__ 方法") + else: + # 未在 Gen.structs 登记的 struct(opaque/匿名)→ fail-fast。 + self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") + if isinstance(pointee, ir.ArrayType): + return ir.Constant(ir.IntType(64), pointee.count) + # i8*(含 str/bytes 指针形态)→ strlen(保持不变)。 + if isinstance(pointee, ir.IntType) and pointee.width == 8: + if val.type != ir.PointerType(ir.IntType(8)): + val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast") + correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))]) + strlen_func = None + if 'strlen' in Gen.functions: + existing = Gen.functions['strlen'] + if existing.ftype == correct_strlen_type: + strlen_func = existing + if not strlen_func: + try: + strlen_func = Gen._CreateOrReuseFuncDecl('strlen', correct_strlen_type) + Gen.functions['strlen'] = strlen_func + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + if strlen_func: + result = Gen.builder.call(strlen_func, [val], name="strlen_call") + return result + return None + # 非 i8 裸指针 / 标量指针(i32*/i64*/float*/double*)→ fail-fast + # (旧实现静默返回常量 0 或回落 strlen,与 VPC 口径不符)。 + self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") + elif isinstance(val.type, ir.ArrayType): + return ir.Constant(ir.IntType(64), val.type.count) + # 其它不支持形态(标量值 / 结构体值等)→ fail-fast(旧实现静默返回常量 0)。 + self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") + + def _resolve_opaque_struct_members(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, _visited: set | None = None) -> None: + """Recursively resolve opaque struct members by loading from stub files.""" + if _visited is None: + _visited = set() + if not isinstance(struct_type, ir.IdentifiedStructType): + return + # 防止循环引用导致无限递归 + try: + st_id = id(struct_type) + except Exception: + st_id = struct_type + if st_id in _visited: + return + _visited.add(st_id) + if struct_type.elements is None or len(struct_type.elements) == 0: + # Find the class name for this opaque struct and try to resolve + for cn, st in Gen.structs.items(): + try: + if st == struct_type or (hasattr(st, 'name') and hasattr(struct_type, 'name') and st.name == struct_type.name): + self.Trans.ImportHandler._TryLoadStructFromStub(cn, Gen) + break + except (AssertionError, RuntimeError): + pass + # Re-get the struct in case it was resolved + if struct_type.elements: + for elem in struct_type.elements: + if isinstance(elem, ir.IdentifiedStructType): + self._resolve_opaque_struct_members(elem, Gen, _visited) + elif isinstance(elem, ir.PointerType): + pointee = elem.pointee + if isinstance(pointee, ir.IdentifiedStructType): + self._resolve_opaque_struct_members(pointee, Gen, _visited) + + def _resolve_type_arg_str(self, node: ast.AST) -> str: + """将类型参数 AST 节点解析为规范化的类型字符串(用于泛型特化的 mangling)。 + + 规则本体在 SymbolUtils.ResolveTypeArgStr(定义端/声明端唯一规则源, + 声明端 DeclarationGenerator 的泛型分支亦调用同一函数),此处仅注入 + 翻译器上下文: + - t/c 导入别名表(from t import CInt as myint → 't.CInt') + - 跨模块结构体 SHA1 表(ast.Name/Attribute 命中 → '{sha1}.{名}',与普通签名一致) + """ + Gen = self.Trans.LlvmGen + StructSha1Map: dict[str, str] = getattr(Gen, '_struct_sha1_map', {}) if Gen is not None else {} + return ResolveTypeArgStr( + node, + TCAliasMap=getattr(self.Trans, '_t_c_imported_names', {}), + StructSha1Map=StructSha1Map) + + def _get_raw_type_name(self, node: ast.AST) -> str: + """获取类型节点的原始名称(不带模块前缀),用于泛型 T 替换。 + + ast.Name → sn.id (如 'AST', 'CInt') + ast.Attribute → sn.attr (如 t.CInt → 'CInt') + ast.BinOp → 左侧的原始名称 (如 AST | CPtr → 'AST') + """ + if isinstance(node, ast.Name): + return node.id + if isinstance(node, ast.Attribute): + return node.attr + if isinstance(node, ast.BinOp): + return self._get_raw_type_name(node.left) + return ast.unparse(node) + + def _resolve_generic_slice_type(self, sn: ast.AST) -> tuple[str, str] | None: + """解析泛型 slice 中的单个类型参数节点,返回 (type_arg, type_name) 或 None。 + + type_arg: 用于 mangling,走**唯一规则源** _resolve_type_arg_str + (内层与参数注解同解析:'AST | t.CPtr' / 'rawptr[AST]' → + '{sha1}.AST*'、裸类名 'AST' → '{sha1}.AST'、标量按 + NormalizeGenericArgName 归一) + type_name: 用于替换泛型类型参数 T(原始源码名,如 'AST' / 'CInt', + _specialize_generic_class 会自动给类名加 | t.CPtr) + 指针下标 rawptr[T] 先折叠成等价的 `T | t.CPtr` 再解析, + 与源码联合写法得到同一 (type_arg, type_name):否则 list[rawptr[ast.AST]] + 会拼出 'rawptr[ast.AST]' 实参串(内层未混淆)→ 与 list[ast.AST | t.CPtr] + 的 '.AST*' 分裂成两个特化名(结构体/方法声明/调用点三端不一致)。 + """ + if IsPtrSubscriptNode(sn, self.Trans.SymbolTable): + sn = DesugarPtrSubscriptToUnion(sn, self.Trans.SymbolTable) + if isinstance(sn, ast.Name): + # type_arg 走唯一规则源(命中注册类加 SHA1 前缀,裸名与 + # `list[X]`/`rawptr[X]` 内层收敛到同一名字); + # type_name 保留源码名供 T 替换与内建/类类型判定(两张表收拢在 SymbolUtils) + return (self._resolve_type_arg_str(sn), GenericArgRawName(sn.id)) + # BinOp (如 AST | CPtr) 或 Attribute (如 t.CPtr / t.CInt) + type_str: str = self._resolve_type_arg_str(sn) + # type_name 保留模块前缀(如 't.CUnsignedChar'),避免 T(0) 替换为 + # CUnsignedChar(0) 后因 CUnsignedChar 不在顶层作用域而报 + # "Undefined function: 'CUnsignedChar'"。 + # t.CPtr 特殊处理:保留 't.CPtr' 带前缀(2026-08-19 根修:此前裸 + # 'CPtr' 作为 type_map['T'] 替换进模板(item: T → item: CPtr)时, + # CPtr 不在顶层作用域 → 方法声明注册失败 → "Undefined method + # 'append' in class 'list[CPtr]'"。带前缀 't.CPtr' 可解析。 + # 指针参数识别改由 _BUILTIN_TYPE_NAMES 的 tn_bare 检测('t.CPtr'→'CPtr')。 + if isinstance(sn, ast.Attribute) and isinstance(sn.value, ast.Name): + raw_name: str = f'{sn.value.id}.{sn.attr}' + else: + raw_name: str = self._get_raw_type_name(sn) + return (type_str, raw_name) + @staticmethod def _IsCPtrMark(Node: ast.AST) -> bool: """`t.CPtr` / 裸名 `CPtr` 标记节点。""" @@ -4459,1169 +4465,1244 @@ class ExprCallHandle(BaseHandle): return None return None - def _ResolveGenericSlice(self, Sub: ast.Subscript) -> str | None: - """从泛型 Subscript (如 Stack[int]) 解析特化名,确保特化类型已生成。""" - if not isinstance(Sub.value, ast.Name): - return None - ClassName: str = Sub.value.id - if not (hasattr(self.Trans.ClassHandler, '_generic_class_templates') and ClassName in self.Trans.ClassHandler._generic_class_templates): - return None - Gen = self.Trans.LlvmGen - # 共享符号表构建阶段(_build_shared_symbol_data / ParsePythonFile)LlvmGen 为 None, - # 此时不应发射特化 IR;调用方(GetCTypeInfo)已用 mangled 名设置符号表信息 - if Gen is None: - return None - template: dict = self.Trans.ClassHandler._generic_class_templates[ClassName] - type_param_names: list[str] = template['type_params'] - type_args: list[str] = [] - type_names: list[str] = [] - slice_nodes: list[ast.AST] = [] - if isinstance(Sub.slice, ast.Tuple): - slice_nodes = list(Sub.slice.elts) - else: - slice_nodes = [Sub.slice] - for sn in slice_nodes: - result = self._resolve_generic_slice_type(sn) - if result is not None: - type_args.append(result[0]) - type_names.append(result[1]) - if len(type_args) < len(type_param_names): - raise Exception( - f"泛型类注解 '{ast.unparse(Sub)}' 无法解析全部类型实参:" - f"需要 {len(type_param_names)} 个,仅解析出 {len(type_args)} 个;" - f"禁止按 int 兜底。") - # 类型注解解析(如 Float64Array: t.CTypedef = ndarray[t.CDouble]) - # 只需 struct 定义,不需方法体;使用 declare_only=True 避免过早触发 - # 完整特化(此时被引用的函数如 printf 可能尚未由 from X import Y 注册, - # 导致 "Undefined function" 错误)。完整特化由后续类实例化 - # (_HandleClassNewLlvm → _specialize_generic_class(declare_only=False)) - # 触发,缓存机制会补发方法体。 - return self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names, declare_only=True) - - @staticmethod - def _AnnotationContainsCPtr(Node: ast.AST) -> bool: - """检查类型注解 AST 是否包含 t.CPtr(即是否为指针类型)""" - if isinstance(Node, ast.Attribute): - if Node.attr == 'CPtr': - return True - if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): - return (ExprCallHandle._AnnotationContainsCPtr(Node.left) - or ExprCallHandle._AnnotationContainsCPtr(Node.right)) - return False - - @staticmethod - def _is_sizeof_type_name(name: str, Gen: "LlvmGeneratorMixin") -> bool: - """检查名称是否是已知的类型名(用于 __sizeof__ 上下文)。 - 检查顺序: Gen.structs → SHA1 前缀 → 内建类型 → CTypeRegistry - """ - if not name: - return False - # 1. 直接在 Gen.structs 中查找 - if name in Gen.structs: - return True - # 2. 带 SHA1 前缀查找(如 ClassDef → 657e182b27c2a022.ClassDef) - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - if name in struct_sha1_map: - return True - # 遍历所有 SHA1 前缀 - for sha1 in struct_sha1_map.values(): - prefixed = f"{sha1}.{name}" - if prefixed in Gen.structs: - return True - # 3. 内建类型名 - if name in ('str', 'bytes', 'int', 'double', 'float', 'char', 'bool', - 'void', 'i8', 'i16', 'i32', 'i64', 'half', 'fp128'): - return True - # 4. CTypeRegistry 中的类型 - if CTypeRegistry.GetClassByName(name) is not None: - return True - if CTypeRegistry.ResolveName(name): - return True - return False - - def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Constant: - Gen = self.Trans.LlvmGen - if isinstance(Node, ast.Name): - type_name = Node.id - elif isinstance(Node, ast.Attribute): - type_name = Node.attr - elif isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): - # 联合类型(如 X | t.CPtr):TPV 注解中 | = and,带 t.CPtr 的联合 - # 是指针类型(X* 或 i8*),sizeof = 8(指针大小)。 - # list[T].__init__ 的 T.__sizeof__() 特化后 T 被还原为 'X | t.CPtr' - # (_make_replacement_node),此前落到 else 返回 0 → elem_size=0 → - # 所有元素存取同槽(get(0)==get(1),list[对象] join 误报根因)。 - if _BinOpContainsCPtr(Node): - return ir.Constant(ir.IntType(64), 8) - # 不带 t.CPtr 的联合(A | B)无法确定尺寸:曾静默返回 0 → - # 下游 elem_size=0 → alloc(0) 返回 NULL / 所有元素存取同槽。fail-fast。 - raise ValueError( - f"[FATAL][__sizeof__] 联合类型 '{ast.unparse(Node)}' 不含 t.CPtr," - f"无法确定尺寸,拒绝静默返回 0(会导致 alloc(0)/元素同槽)" - ) - else: - # 非 名称/属性/联合 的 sizeof 目标(如表达式、调用):曾静默返回 0。fail-fast。 - raise ValueError( - f"[FATAL][__sizeof__] 不支持的 sizeof 目标 {type(Node).__name__}" - f"({ast.unparse(Node)})," - f"拒绝静默返回 0(会导致 alloc(0))" - ) - - if type_name not in Gen.structs: - # SHA1 前缀查找:如 ClassDef → 657e182b27c2a022.ClassDef - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - resolved_name: str | None = None - if type_name in struct_sha1_map: - resolved_name = f"{struct_sha1_map[type_name]}.{type_name}" - else: - for sha1 in struct_sha1_map.values(): - prefixed = f"{sha1}.{type_name}" - if prefixed in Gen.structs: - resolved_name = prefixed - break - if resolved_name is not None and resolved_name in Gen.structs: - type_name = resolved_name - if type_name not in Gen.structs: - # str/bytes 是指针类型 (char*/void*),sizeof = 指针大小 - if type_name in ('str', 'bytes'): - return ir.Constant(ir.IntType(64), 8) - ctype_cls = CTypeRegistry.GetClassByName(type_name) - if ctype_cls is not None: - try: - ctype_inst = ctype_cls() - size_bits = getattr(ctype_inst, 'Size', 0) or 0 - size_bytes = size_bits // 8 - # POINTER 类型(CPtr 等)Size 为 None/0 → 指针大小 8。 - # 根因:t.CPtr.__sizeof__() 返回 0 → list[t.CPtr] - # elem_size=0 → append 全写同槽/alloc(0) → 崩溃。 - if getattr(ctype_inst, 'HasPosition', None) \ - and ctype_inst.HasPosition(t.CType.POINTER) \ - and size_bytes == 0: - size_bytes = 8 - return ir.Constant(ir.IntType(64), size_bytes) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"解析类型失败: {_e}", "Exception") - resolved = CTypeRegistry.ResolveName(type_name) - if resolved: - ctype_cls, ptr_level = resolved - try: - ctype_inst = ctype_cls() - size_bits = getattr(ctype_inst, 'Size', 0) or 0 - size_bytes = size_bits // 8 - if ptr_level > 0: - size_bytes = 8 - return ir.Constant(ir.IntType(64), size_bytes) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"解析类型失败: {_e}", "Exception") - # 所有查找策略均失败(不在 Gen.structs、SHA1 前缀查找失败、CTypeRegistry 也找不到): - # 报错终止,拒绝静默返回 0(会导致 malloc(0)/alloc(0) 堆溢出) - raise ValueError( - f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析" - f"(不在 Gen.structs、SHA1 前缀查找失败、CTypeRegistry 也找不到)," - f"拒绝静默返回 0" - ) - - struct_type = Gen.structs[type_name] - if isinstance(struct_type, ir.PointerType): - struct_type = struct_type.pointee - # 保存 builder 状态:_TryLoadStructFromStub / _resolve_opaque_struct_members - # 可能触发重入编译(编译其他模块的函数),这会改变 Gen.func/Gen.builder, - # 导致当前函数的指令被写到错误函数的 block 中(then 块为空的根因) - _saved_func = Gen.func - _saved_builder = Gen.builder - _saved_block = Gen.builder.block if (Gen.builder and Gen.builder.block) else None - _saved_variables = dict(Gen.variables) if Gen.variables else {} - _saved_reg_values = dict(getattr(Gen, '_reg_values', {})) - _saved_direct_values = dict(getattr(Gen, '_direct_values', {})) - _saved_var_type_info = dict(getattr(Gen, 'var_type_info', {})) - _saved_var_signedness = dict(getattr(Gen, 'var_signedness', {})) - _saved_global_vars = set(getattr(Gen, 'global_vars', set())) - _saved_var_scopes = [dict(s) for s in getattr(self.Trans, 'VarScopes', [])] - try: - if isinstance(struct_type, ir.IdentifiedStructType) and (struct_type.elements is None or len(struct_type.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(type_name, Gen) - struct_type = Gen.structs.get(type_name, struct_type) - # Recursively resolve opaque struct members - self._resolve_opaque_struct_members(struct_type, Gen) - finally: - Gen.func = _saved_func - Gen.builder = _saved_builder - if _saved_block is not None and Gen.builder is not None: - Gen.builder.position_at_end(_saved_block) - Gen.variables = _saved_variables - Gen._reg_values = _saved_reg_values - Gen._direct_values = _saved_direct_values - Gen.var_type_info = _saved_var_type_info - Gen.var_signedness = _saved_var_signedness - Gen.global_vars = _saved_global_vars - self.Trans.VarScopes = _saved_var_scopes - # _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。 - return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type)) - - def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None: - Gen = self.Trans.LlvmGen - val = self.HandleExprLlvm(arg_node) - if not val: - return None - if isinstance(val.type, ir.PointerType): - pointee = val.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - for CN, ST in Gen.structs.items(): - if pointee == ST: - result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__abs__', val, Gen) - if result is not None: - return result - break - if isinstance(val.type, ir.IntType): - neg_val = Gen.builder.neg(val, name="abs_neg") - is_neg = Gen.builder.icmp_signed('<', val, ir.Constant(val.type, 0), name="is_neg") - return Gen.builder.select(is_neg, neg_val, val, name="abs_result") - elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): - zero = ir.Constant(val.type, 0.0) - neg_val = Gen.builder.fneg(val, name="fabs_neg") - is_neg = Gen.builder.fcmp_ordered('<', val, zero, name="f_is_neg") - return Gen.builder.select(is_neg, neg_val, val, name="fabs_result") - return None - - def _HandleDirLlvm(self, Node: ast.Call) -> ir.Constant: - Gen = self.Trans.LlvmGen - return ir.Constant(ir.IntType(8).as_pointer(), None) - - def _HandleTypeLlvm(self, Node: ast.Call) -> ir.Value: - Gen = self.Trans.LlvmGen - if not Node.args: - type_str = "" - else: - arg = Node.args[0] - enum_type_name = None - - def _get_attr_path(node): - if isinstance(node, ast.Name): - return node.id - elif isinstance(node, ast.Attribute): - return f"{_get_attr_path(node.value)}.{node.attr}" - return None - - if isinstance(arg, ast.Name): - arg_name = arg.id - Gen = self.Trans.LlvmGen - if arg_name in Gen.var_type_info: - type_info = Gen.var_type_info[arg_name] - if isinstance(type_info, dict) and type_info.get('type') == 'enum': - enum_type_name = type_info.get('name') - if not enum_type_name: - TypeInfo = self.Trans.SymbolTable.lookup(arg_name) - if TypeInfo and TypeInfo.IsEnum: - enum_type_name = arg_name - elif isinstance(arg, ast.Attribute): - LastPart = None - enum_class_name = None - if isinstance(arg.value, ast.Name): - LastPart = arg.attr - AttrInfo = self.Trans.SymbolTable.lookup(LastPart) - if AttrInfo and AttrInfo.IsEnumMember and AttrInfo.EnumName: - enum_type_name = AttrInfo.EnumName - elif isinstance(arg.value, ast.Attribute): - attr_path = _get_attr_path(arg) - if attr_path: - parts = attr_path.split('.') - if len(parts) >= 2: - enum_class_name = parts[-2] - LastPart = parts[-1] - qualified_name_dot = f"{enum_class_name}.{LastPart}" - qualified_name_under = f"{enum_class_name}_{LastPart}" - enum_member_found = None - for qname in (qualified_name_dot, qualified_name_under): - info = self.Trans.SymbolTable.lookup(qname) - if info and info.IsEnumMember and info.EnumName == enum_class_name: - enum_member_found = info - break - if not enum_member_found: - for key in self.Trans.SymbolTable: - if key == LastPart: - info = self.Trans.SymbolTable[key] - if info.IsEnumMember and info.EnumName == enum_class_name: - enum_member_found = info - break - if enum_member_found: - enum_type_name = enum_class_name - if enum_type_name: - _enum_bits: int = self.Trans.LlvmGen._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): - pointee = llvm_type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - llvm_type = pointee - type_info = self.Trans.ExprUtils._llvm_type_to_detailed_string(llvm_type) - type_str = f"" - return Gen.emit_constant(type_str, 'string') - - def _HandleInputLlvm(self, Node: ast.Call) -> ir.Value: - Gen = self.Trans.LlvmGen - if Node.args and isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, str): - prompt = Node.args[0].value - prompt_ptr = Gen.emit_constant(prompt, 'string') - printf_func = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) - Gen.builder.call(printf_func, [prompt_ptr]) - scanf_func = Gen.get_or_declare_c_func('scanf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) - input_buffer = Gen._alloca(ir.ArrayType(ir.IntType(8), 256), name="input_buffer") - result = Gen.builder.call(scanf_func, [Gen.emit_constant("%255s", 'string'), input_buffer]) - return Gen.builder.gep(input_buffer, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="input_result") - - def _HandleAtoiLlvm(self, str_ptr: ir.Value) -> ir.Value: - Gen = self.Trans.LlvmGen - atoi_func = Gen.get_or_declare_c_func('atoi', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))])) - return Gen.builder.call(atoi_func, [str_ptr], name="atoi_result") - - def _HandleOrdLlvm(self, expr_node: ast.AST) -> ir.Value: - Gen = self.Trans.LlvmGen - val = self.HandleExprLlvm(expr_node) - if val and self._is_char_pointer(val): - return Gen._load(val, name="ord_result") - return ir.Constant(ir.IntType(32), 0) - - def _HandleChrLlvm(self, expr_node: ast.AST) -> ir.Value: - Gen = self.Trans.LlvmGen - val = self.HandleExprLlvm(expr_node) - if val: - char_ptr = Gen._alloca(ir.IntType(8), name="chr_char") - Gen.builder.store(Gen.builder.trunc(val, ir.IntType(8), name="trunc_chr"), char_ptr) - return Gen.builder.gep(char_ptr, [ir.Constant(ir.IntType(32), 0)], name="chr_result") - return ir.Constant(ir.IntType(8).as_pointer(), None) - - def _HandleMallocLlvm(self, args: list[ast.AST]) -> ir.Value: - Gen = self.Trans.LlvmGen - malloc_type = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) - malloc_func = Gen._get_or_declare_func('malloc', malloc_type) - param_type = malloc_func.type.pointee.args[0] - size_val = None - if args: - size_val = self.HandleExprLlvm(args[0]) - if size_val and isinstance(size_val.type, ir.IntType): - if isinstance(param_type, ir.IntType) and size_val.type.width != param_type.width: - if size_val.type.width < param_type.width: - size_val = Gen.builder.zext(size_val, param_type, name="zext_malloc_size") - else: - size_val = Gen.builder.trunc(size_val, param_type, name="trunc_malloc_size") - if not size_val: - size_val = ir.Constant(param_type, 1) - result = Gen.builder.call(malloc_func, [size_val], name="malloc_result") - return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="malloc_cast") - - def _HandleReallocLlvm(self, args: list[ast.AST]) -> ir.Value: - Gen = self.Trans.LlvmGen - realloc_type = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)]) - realloc_func = Gen._get_or_declare_func('realloc', realloc_type) - size_param_type = realloc_func.type.pointee.args[1] - ptr_val = None - size_val = None - if len(args) >= 2: - ptr_val = self.HandleExprLlvm(args[0]) - size_val = self.HandleExprLlvm(args[1]) - elif len(args) == 1: - size_val = self.HandleExprLlvm(args[0]) - if not ptr_val: - ptr_val = ir.Constant(ir.PointerType(ir.IntType(8)), None) - if isinstance(ptr_val.type, ir.PointerType) and not self._is_char_pointer(ptr_val): - ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="realloc_cast_ptr") - if size_val and isinstance(size_val.type, ir.IntType): - if isinstance(size_param_type, ir.IntType) and size_val.type.width != size_param_type.width: - if size_val.type.width < size_param_type.width: - size_val = Gen.builder.zext(size_val, size_param_type, name="zext_realloc_size") - else: - size_val = Gen.builder.trunc(size_val, size_param_type, name="trunc_realloc_size") - if not size_val: - size_val = ir.Constant(size_param_type, 0) - result = Gen.builder.call(realloc_func, [ptr_val, size_val], name="realloc_result") - return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="realloc_cast") - - def _HandleFreeLlvm(self, args: list[ast.AST]) -> ir.Constant: - Gen = self.Trans.LlvmGen - free_type = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]) - free_func = Gen._get_or_declare_func('free', free_type) - if args: - var_name = None - if isinstance(args[0], ast.Name): - var_name = args[0].id - ptr_val = self.HandleExprLlvm(args[0]) - if ptr_val: - if var_name and hasattr(Gen, '_var_to_heap_ptr') and var_name in Gen._var_to_heap_ptr: - registered_val = Gen._var_to_heap_ptr[var_name] - Gen._unregister_local_heap_ptr(registered_val) - del Gen._var_to_heap_ptr[var_name] - if isinstance(ptr_val.type, ir.PointerType) and not self._is_char_pointer(ptr_val): - ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="free_cast_ptr") - Gen.builder.call(free_func, [ptr_val], name="free_call") - return ir.Constant(ir.IntType(32), 0) - - def _HandleMemcpyLlvm(self, args: list[ast.AST]) -> None: - Gen = self.Trans.LlvmGen - if 'llvm.memcpy' not in Gen.functions: - memcpy_type = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) - Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type) - if len(args) >= 3: - dst = self.HandleExprLlvm(args[0]) - src = self.HandleExprLlvm(args[1]) - size = self.HandleExprLlvm(args[2]) - if dst and src and size: - i8ptr = ir.PointerType(ir.IntType(8)) - if isinstance(dst.type, ir.PointerType) and dst.type != i8ptr: - dst = Gen.builder.bitcast(dst, i8ptr, name="memcpy_dst_cast") - elif isinstance(dst.type, ir.IntType): - if dst.type.width < 64: - dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_dst_ptr") - dst = Gen.builder.inttoptr(dst, i8ptr, name="dst_int2ptr") - if isinstance(src.type, ir.PointerType) and src.type != i8ptr: - src = Gen.builder.bitcast(src, i8ptr, name="memcpy_src_cast") - elif isinstance(src.type, ir.IntType): - if src.type.width < 64: - src = Gen.builder.zext(src, ir.IntType(64), name="zext_src_ptr") - src = Gen.builder.inttoptr(src, i8ptr, name="src_int2ptr") - if isinstance(size.type, ir.IntType) and size.type.width < 64: - size = Gen.builder.zext(size, ir.IntType(64), name="zext_memcpy_size") - isvolatile = ir.Constant(ir.IntType(1), 0) - Gen.builder.call(Gen.functions['llvm.memcpy'], [dst, src, size, isvolatile], name="memcpy_call") - return None - - def _HandleMemsetLlvm(self, args: list[ast.AST]) -> None: - Gen = self.Trans.LlvmGen - memset_func = Gen.get_or_declare_c_func('memset', ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)])) - if len(args) >= 3: - dst = self.HandleExprLlvm(args[0]) - val = self.HandleExprLlvm(args[1]) - size = self.HandleExprLlvm(args[2]) - if dst and val is not None and size: - if isinstance(dst.type, ir.PointerType) and not self._is_char_pointer(dst): - dst = Gen.builder.bitcast(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_cast") - if isinstance(val.type, ir.IntType) and val.type.width != 32: - if val.type.width < 32: - val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val") - else: - val = Gen.builder.trunc(val, ir.IntType(32), name="trunc_memset_val") - if isinstance(size.type, ir.IntType) and size.type.width < 64: - size = Gen.builder.zext(size, ir.IntType(64), name="zext_memset_size") - elif isinstance(size.type, ir.IntType) and size.type.width > 64: - size = Gen.builder.trunc(size, ir.IntType(64), name="trunc_memset_size") - Gen.builder.call(memset_func, [dst, val, size], name="memset_call") - return None - - def _HandleVaStartLlvm(self, args: list[ast.AST]) -> None: - Gen = self.Trans.LlvmGen - return None - - def _HandleVaEndLlvm(self, args: list[ast.AST]) -> None: - Gen = self.Trans.LlvmGen - return None - - def _HandleVaArgLlvm(self, args: list[ast.AST]) -> ir.Constant: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - return ir.Constant(ir.IntType(32), 0) - - def _HandleCDerefAsLlvm(self, Node: ast.Call) -> ir.Constant: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if len(Node.args) < 2: - return ir.Constant(ir.IntType(32), 0) - ptr_val: ir.Value | None = self.HandleExprLlvm(Node.args[0]) - val_val: ir.Value | None = self.HandleExprLlvm(Node.args[1]) - if ptr_val is None or val_val is None: - return ir.Constant(ir.IntType(32), 0) - if isinstance(ptr_val.type, ir.PointerType): - store_val: ir.Value = val_val - # 结构体整对象拷贝判定(*ptr = *val): - # 1) val 指向结构体(REnum boxed 构造 / 结构体值引用),且 ptr 的 - # pointee 不是指针类型(ptr 指向"可容纳该结构体的槽"而非指针槽) - # → 整对象拷贝。典型:c.DerefAs(result_ty_ptr, result_ty),其中 - # result_ty = LLVMType.Ptr(...) 为 REnum boxed 值,result_ty_ptr - # 因 rawptr[REnum] 被解析成 i32*(LLVMType 首字段 i32)而 pointee - # 非结构体,原 pointee-equal 判断失配 → 误落"存储指针值"分支 - # (只写 8 字节),导致 REnum tag/字段全部丢失。 - # 2) ptr 与 val 指向同一结构体类型(原逻辑)→ 整对象拷贝。 - # 指针槽场景(ptr: T**,pointee 为指针,存 T* 指针值)不受影响。 - val_is_struct: bool = ( - isinstance(val_val.type, ir.PointerType) - and isinstance(val_val.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))) - ptr_pointee_is_ptr: bool = isinstance(ptr_val.type.pointee, ir.PointerType) - same_struct_ptrs: bool = ( - isinstance(ptr_val.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)) - and ptr_val.type.pointee == val_val.type.pointee) - if val_is_struct and (not ptr_pointee_is_ptr or same_struct_ptrs): - if ptr_val.type != val_val.type: - ptr_val = Gen.builder.bitcast(ptr_val, val_val.type, name="deref_as_ptr_align") - loaded_s: ir.Value = Gen._load(val_val, name="deref_as_struct_load") - Gen._store(loaded_s, ptr_val) - return ir.Constant(ir.IntType(32), 0) - if isinstance(val_val.type, ir.PointerType): - void_pp: ir.PointerType = ir.PointerType(ir.PointerType(ir.IntType(8))) - if ptr_val.type != void_pp: - ptr_val = Gen.builder.bitcast(ptr_val, void_pp, name="deref_as_pp") - if val_val.type.pointee != ir.IntType(8): - store_val = Gen.builder.bitcast(val_val, ir.PointerType(ir.IntType(8)), name="deref_as_vp") - elif isinstance(val_val.type, ir.IntType) and isinstance(ptr_val.type.pointee, ir.IntType): - if val_val.type.width != ptr_val.type.pointee.width: - if ptr_val.type.pointee.width == 8 and val_val.type.width > 8: - target_ptr_type = ir.PointerType(val_val.type) - ptr_val = Gen.builder.bitcast(ptr_val, target_ptr_type, name="deref_as_cast") - elif val_val.type.width < ptr_val.type.pointee.width: - store_val = Gen.builder.zext(val_val, ptr_val.type.pointee, name="deref_as_zext") - else: - store_val = Gen.builder.trunc(val_val, ptr_val.type.pointee, name="deref_as_trunc") - elif isinstance(val_val.type, ir.IntType) and isinstance(ptr_val.type.pointee, ir.PointerType): - if val_val.type.width == 64: - store_val = Gen.builder.inttoptr(val_val, ptr_val.type.pointee, name="deref_as_inttoptr") - else: - ext = Gen.builder.zext(val_val, ir.IntType(64), name="deref_as_zext") - store_val = Gen.builder.inttoptr(ext, ptr_val.type.pointee, name="deref_as_inttoptr") - elif val_val.type != ptr_val.type.pointee: - target_ptr_type = ir.PointerType(val_val.type) - ptr_val = Gen.builder.bitcast(ptr_val, target_ptr_type, name="deref_as_cast") - Gen.builder.store(store_val, ptr_val) - return ir.Constant(ir.IntType(32), 0) - - def _InjectRequiresFields(self, result: ir.Value, ClassName: str, Gen: LlvmGeneratorMixin) -> None: - """在 __new__ 后、__init__ 前自动注入 __requires__/__require_must__ 字段。 - - - 编译期:对 __require_must__ 做纯静态可达性检查(遍历 _with_provides_stack) - - 运行时:对每个 requires 字段调用 _find_provider,非 NULL 则 bitcast 写入 - """ - requires_all: list[str] = [] - requires_all.extend(Gen.class_requires.get(ClassName, [])) - requires_all.extend(Gen.class_require_must.get(ClassName, [])) - if not requires_all: - return - # 编译期 __require_must__ 静态检查 - must_fields: list[str] = Gen.class_require_must.get(ClassName, []) - if must_fields: - # 跳过类自身方法内的检查(类的实现内部不需要 with 上下文) - current_class = getattr(Gen, '_current_class_name', None) - if current_class != ClassName: - available: set[str] = set() - for provides_list in Gen._with_provides_stack: - available.update(provides_list) - for field in must_fields: - if field not in available: - raise SyntaxError( - f"[TransPyC] Class '{ClassName}' __require_must__ field '{field}' " - f"not provided by any enclosing 'with' context" - ) - # 运行时注入 - find_func = Gen.functions.get('_find_provider') - if not find_func: - return - members = Gen.class_members.get(ClassName, []) - # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 - base = Gen._member_base(ClassName) - field_map: dict[str, tuple[int, ir.Type]] = {} - for i, (mname, mtype) in enumerate(members): - if mname in requires_all: - field_map[mname] = (base + i, mtype) - for field_name in requires_all: - if field_name not in field_map: - continue - field_idx, field_type = field_map[field_name] - field_str = Gen._create_string_global(field_name) - ptr_val = Gen.builder.call(find_func, [field_str], name=f"find_{field_name}") - null_ptr = ir.Constant(ptr_val.type, None) - is_null = Gen.builder.icmp_unsigned('==', ptr_val, null_ptr, name=f"is_null_{field_name}") - skip_bb = Gen.func.append_basic_block(f"skip_inject_{field_name}") - inject_bb = Gen.func.append_basic_block(f"inject_{field_name}") - Gen.builder.cbranch(is_null, skip_bb, inject_bb) - Gen.builder.position_at_end(inject_bb) - if isinstance(field_type, ir.PointerType): - casted = Gen.builder.bitcast(ptr_val, field_type, name=f"cast_{field_name}") - elif isinstance(field_type, ir.IntType): - casted = Gen.builder.ptrtoint(ptr_val, field_type, name=f"cast_{field_name}") - else: - casted = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name=f"cast_{field_name}") - elem_ptr = Gen.builder.gep(result, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), field_idx)], name=f"field_{field_name}_ptr") - Gen._store(casted, elem_ptr) - Gen.builder.branch(skip_bb) - Gen.builder.position_at_end(skip_bb) - - def _HandleClassNewLlvm(self, Node: ast.Call, ClassName: str, module_sha1: str | None = None) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - # DEBUG: 追踪 Asm 类的构造 - if ClassName == 'Asm': - import sys as _dbg_sys - print(f"[DEBUG_CLASSNEW] _HandleClassNewLlvm called for Asm, module_sha1={module_sha1}", file=_dbg_sys.stderr) - # 跨模块类型引用:优先用 {module_sha1}.{ClassName} 查找 struct - _struct_full_key: str | None = f"{module_sha1}.{ClassName}" if module_sha1 else None - if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and ClassName in self.Trans.ClassHandler._generic_class_templates: - return self._HandleGenericClassNewLlvm(Node, ClassName) - NewFuncName = f'{ClassName}.__before_init__' - InitFuncName = f'{ClassName}.__init__' - NewMethodFuncName = f'{ClassName}.__new__' - has_new_method = Gen._find_function(NewMethodFuncName) is not None - - # 【self 的类型必须与 callee 同源(2026-09-30)】 - # `Gen.structs` 的键有"裸短名 / sha1 全名 / import 别名"三种:定义端恒以**短名** - # 作键(StructGen._generate_structs),别名注册只是**浅拷贝**(HandlesImports 对 - # `Gen.structs[asname] = Gen.structs[name]`)。因此"两个模块各定义同名类"时, - # `ClassName` 无论是别名(`from .astaux import Keyword as KwNode` ⇒ 'KwNode')、 - # 还是被别的模块先占用的短名('Keyword' 被 .tokens 占用),按名字查都可能拿到 - # **另一份实例**。唯一无歧义的判据是 **callee 自己**:构造函数 - # (__new__/__before_init__/__init__)第 0 个形参(self)的 pointee 就是编译器 - # 写下的期望类型,也正是 `_adjust_args` 用来比较的对象(llvmlite 的类型相等按 - # **IR 名字**判定,实测同名不同对象 `==` 为真,故取 callee 形参的 pointee 即与之 - # 逐字同名;按短名另取一份只会继续 fail-fast)。 - # 现场:includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)` - # —— callee 是 astaux 的 `8312ce86a2fd82c0.Keyword.__new__`,self 实参却按 - # 短名/别名叫到了 tokens 的 `fef783da4d24330d.Keyword` ⇒ - # "指针隐式转换已禁止(非基本类型(结构体/类/联合))"。 - def _CalleeSelfType() -> ir.Type | None: - """callee 第 0 个形参 self 的 pointee(未具化时按 IR 名补已具化的同类型)。 - - 按 __new__ → __before_init__ → __init__ 顺序取第一个可用的:三者都是 self - 的接收者,且同名类的多份实例只可能因"查名"分歧而不同源,取 callee 侧即可 - 消除分歧。取不到(无形参 / 非具名结构体)时返回 None,调用方退回按名字查。 - 未具化的实例不能用于 `_allocaEntry`(算不出尺寸),故按 **IR 名**换已具化的 - 同类型对象 —— IR 里类型的身份就是名字,同名即同类型(判据同 - `BaseGen._FindStructByIrName` 的文档)。 - """ - for _fn_name in (NewMethodFuncName, NewFuncName, InitFuncName): - _Fn: Any = Gen._find_function(_fn_name) - _Args: Any = getattr(getattr(_Fn, 'function_type', None), 'args', None) - if not _Args: - continue - _P0: Any = _Args[0] - if not isinstance(_P0, ir.PointerType): - continue - _PT: Any = _P0.pointee - if not isinstance(_PT, ir.IdentifiedStructType): - continue - if _PT.elements is not None: - return _PT - _Twin: Any = Gen._FindStructByIrName(getattr(_PT, 'name', '') or '') - if isinstance(_Twin, ir.IdentifiedStructType) and _Twin.elements is not None: - return _Twin - return None - - def _get_struct_type(name: str) -> ir.Type | None: - """self 用的 struct:callee 形参(无歧义)→ sha1 全名键 → 名字键""" - _SelfTy: ir.Type | None = _CalleeSelfType() - if _SelfTy is not None: - return _SelfTy - if _struct_full_key and _struct_full_key in Gen.structs: - return Gen.structs[_struct_full_key] - return Gen.structs.get(name) - - if ClassName == 'Asm': - import sys as _dbg_sys - print(f"[DEBUG_CLASSNEW] Asm: has_before_init={Gen._has_function(NewFuncName)}, has_new={has_new_method}, struct_in_gen={ClassName in Gen.structs}", file=_dbg_sys.stderr) - if Gen._has_function(NewFuncName): - NewFunc = Gen._get_function(NewFuncName) - StructType = _get_struct_type(ClassName) - if StructType: - result = Gen._allocaEntry(StructType, name=f"{ClassName}_alloca") - else: - return ir.Constant(ir.IntType(32), 0) - Gen.builder.call(NewFunc, [result], name=f"before_init_{ClassName}") - if has_new_method: - NewMethodFunc = Gen._find_function(NewMethodFuncName) - if NewMethodFunc: - NewMethodArgs = [result] - for ArgIdx, arg in enumerate(Node.args, start=1): - ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node) - NewMethodArgs.append(ArgVal) - self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName) - self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted_new = Gen._adjust_args(NewMethodArgs, NewMethodFunc) - heap_ptr = Gen.builder.call(NewMethodFunc, adjusted_new, name=f"new_{ClassName}") - if heap_ptr: - target_ptr_type = ir.PointerType(StructType) if StructType else None - if isinstance(heap_ptr.type, ir.PointerType): - # Bitcast the returned pointer to the struct pointer type if needed - if target_ptr_type and heap_ptr.type != target_ptr_type: - result = Gen.builder.bitcast(heap_ptr, target_ptr_type, name=f"new_{ClassName}_cast") - else: - result = heap_ptr - elif isinstance(heap_ptr.type, ir.IntType) and target_ptr_type: - # __new__ returned an integer (pointer as int), convert back to pointer - if heap_ptr.type.width < 64: - i64_val = Gen.builder.zext(heap_ptr, ir.IntType(64), name=f"new_{ClassName}_i64") - result = Gen.builder.inttoptr(i64_val, target_ptr_type, name=f"new_{ClassName}_ptr") - else: - result = Gen.builder.inttoptr(heap_ptr, target_ptr_type, name=f"new_{ClassName}_ptr") - # __new__ 后:对堆指针重新调用 __before_init__ 初始化默认值(alloca 的默认值已随 alloca 丢弃) - if has_new_method: - Gen.builder.call(NewFunc, [result], name=f"before_init_{ClassName}_heap") - # __new__ 后、__init__ 前:自动注入 requires 字段 - self._InjectRequiresFields(result, ClassName, Gen) - if Gen._has_function(InitFuncName): - InitFunc = Gen._get_function(InitFuncName) - InitArgs = [result] - for ArgIdx, arg in enumerate(Node.args, start=1): - ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__init__", ArgIdx, arg, Node) - InitArgs.append(ArgVal) - self._fill_default_args(InitArgs, InitFunc, InitFuncName) - self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1) - self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted_init = Gen._adjust_args(InitArgs, InitFunc) - Gen.builder.call(InitFunc, adjusted_init, name=f"init_{ClassName}") - return result - # 【两支合并(2026-09-30)】原实现的"sha1 全名键命中"那一支只解析 StructType、 - # **不产出 alloca / 构造调用**,控制流随后落到函数末尾 `return i32 0` —— 即跨模块 - # 同名类的构造会**静默不构造**("名字键命中"那一支才是完整实现)。`_get_struct_type` - # 现已统一「callee 形参 → sha1 全名键 → 名字键」的解析顺序,故两支可合并为一条 - # 条件;行为差异只剩"不再静默返回 i32 0"。 - if (_struct_full_key and _struct_full_key in Gen.structs) or ClassName in Gen.structs: - StructType = _get_struct_type(ClassName) - if isinstance(StructType, ir.PointerType): - StructType = StructType.pointee - is_opaque = isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0) - if is_opaque: - self._ensure_struct_declared(ClassName, module_sha1=module_sha1) - StructType = Gen.structs.get(ClassName, StructType) - if isinstance(StructType, ir.PointerType): - StructType = StructType.pointee - is_opaque = isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0) - if is_opaque: - return None - result = Gen._allocaEntry(StructType, name=f"{ClassName}_alloca") - # Call __new__ method if it exists (may replace self with heap pointer) - if has_new_method: - NewMethodFunc = Gen._find_function(NewMethodFuncName) - if NewMethodFunc: - NewMethodArgs = [result] - for ArgIdx, arg in enumerate(Node.args, start=1): - ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node) - NewMethodArgs.append(ArgVal) - self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName) - self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted_new = Gen._adjust_args(NewMethodArgs, NewMethodFunc) - heap_ptr = Gen.builder.call(NewMethodFunc, adjusted_new, name=f"new_{ClassName}") - if heap_ptr: - target_ptr_type = ir.PointerType(StructType) if StructType else None - if isinstance(heap_ptr.type, ir.PointerType): - # Bitcast the returned pointer to the struct pointer type if needed - if target_ptr_type and heap_ptr.type != target_ptr_type: - result = Gen.builder.bitcast(heap_ptr, target_ptr_type, name=f"new_{ClassName}_cast") - else: - result = heap_ptr - elif isinstance(heap_ptr.type, ir.IntType) and target_ptr_type: - # __new__ returned an integer (pointer as int), convert back to pointer - if heap_ptr.type.width < 64: - i64_val = Gen.builder.zext(heap_ptr, ir.IntType(64), name=f"new_{ClassName}_i64") - result = Gen.builder.inttoptr(i64_val, target_ptr_type, name=f"new_{ClassName}_ptr") - else: - result = Gen.builder.inttoptr(heap_ptr, target_ptr_type, name=f"new_{ClassName}_ptr") - # __new__ 后:对堆指针调用 __before_init__ 初始化默认值 - if has_new_method and Gen._has_function(NewFuncName): - NewFunc = Gen._get_function(NewFuncName) - Gen.builder.call(NewFunc, [result], name=f"before_init_{ClassName}_heap") - # __new__ 后、__init__ 前:自动注入 requires 字段 - self._InjectRequiresFields(result, ClassName, Gen) - InitFunc = Gen.functions.get(InitFuncName) - if not InitFunc: - g = Gen.module.globals.get(InitFuncName) - if g and isinstance(g, ir.Function): - Gen.functions[InitFuncName] = g - InitFunc = g - if InitFunc: - InitArgs = [result] - init_param_types = InitFunc.function_type.args if hasattr(InitFunc, 'function_type') else [] - for i, arg in enumerate(Node.args): - arg_var_type = init_param_types[i + 1] if i + 1 < len(init_param_types) else None - ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg, VarType=arg_var_type), f"{ClassName}.__init__", i + 1, arg, Node) - InitArgs.append(ArgVal) - self._fill_default_args(InitArgs, InitFunc, InitFuncName) - self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1) - self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted_init = Gen._adjust_args(InitArgs, InitFunc) - Gen.builder.call(InitFunc, adjusted_init, name=f"init_{ClassName}") - elif Node.args or Node.keywords: - self._InitStructDefaults(result, ClassName, StructType) - self._InitStructFromArgs(result, Node, ClassName, StructType) - else: - self._InitStructDefaults(result, ClassName, StructType) - return result - return ir.Constant(ir.IntType(32), 0) - - def _ZeroInitStruct(self, struct_ptr: ir.Value, StructType: ir.Type) -> None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if isinstance(StructType, ir.IdentifiedStructType) and StructType.elements: - for i, elem_type in enumerate(StructType.elements): - elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"zero_elem_{i}") - zero_val = self._GetZeroValue(elem_type) - if zero_val is not None: - Gen._store(zero_val, elem_ptr) - elif isinstance(StructType, ir.IntType): - Gen._store(ir.Constant(StructType, 0), struct_ptr) - elif isinstance(StructType, ir.ArrayType) and isinstance(StructType.pointee, ir.IntType): - zero_arr = ir.Constant(StructType, None) - Gen._store(zero_arr, struct_ptr) - - def _GetZeroValue(self, typ: ir.Type) -> ir.Constant | None: - if isinstance(typ, ir.IntType): - return ir.Constant(typ, 0) - elif isinstance(typ, ir.PointerType): - return ir.Constant(typ, None) - elif isinstance(typ, (ir.FloatType, ir.DoubleType)): - return ir.Constant(typ, 0.0) - elif isinstance(typ, ir.ArrayType): - return ir.Constant(typ, None) - return None - - def _InitStructDefaults(self, struct_ptr: ir.Value, ClassName: str, StructType: ir.Type) -> None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - defaults: dict = Gen.class_member_defaults.get(ClassName, {}) - members: list[tuple] = Gen.class_members.get(ClassName, []) - if not defaults or not members: - return - if not isinstance(StructType, ir.IdentifiedStructType): - return - # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 - base = Gen._member_base(ClassName) - for i, (member_name, member_type) in enumerate(members): - if member_name in defaults: - default_val = defaults[member_name] - if isinstance(default_val, ir.Constant): - elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + i)], name=f"default_{ClassName}_{member_name}") - Gen._store(default_val, elem_ptr) - - def _InitStructFromArgs(self, struct_ptr: ir.Value, Node: ast.Call, ClassName: str, StructType: ir.Type) -> None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if not isinstance(StructType, ir.IdentifiedStructType): - return - members = Gen.class_members.get(ClassName, []) - # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 - base = Gen._member_base(ClassName) - for i, arg in enumerate(Node.args): - if i >= len(members): - break - val = self.HandleExprLlvm(arg) - if not val: - continue - member_name, member_type = members[i] - elem_idx = base + i - if StructType.elements is None or elem_idx >= len(StructType.elements): - break - elem_type = StructType.elements[elem_idx] - if isinstance(elem_type, ir.IntType) and isinstance(val.type, ir.IntType): - if val.type.width < elem_type.width: - val = Gen.builder.zext(val, elem_type, name=f"zext_init_{ClassName}_{i}") - elif val.type.width > elem_type.width: - val = Gen.builder.trunc(val, elem_type, name=f"trunc_init_{ClassName}_{i}") - elif isinstance(elem_type, ir.PointerType) and isinstance(val.type, ir.PointerType): - if val.type.pointee != elem_type.pointee: - val = Gen.builder.bitcast(val, elem_type, name=f"bitcast_init_{ClassName}_{i}") - elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), elem_idx)], name=f"init_elem_{ClassName}_{member_name}") - Gen._store(val, elem_ptr) - if Node.keywords: - member_map = {name: (base + i) for i, (name, _) in enumerate(members)} - for kw in Node.keywords: - if kw.arg in member_map: - val = self.HandleExprLlvm(kw.value) - if not val: - continue - elem_idx = member_map[kw.arg] - if StructType.elements is None or elem_idx >= len(StructType.elements): - continue - elem_type = StructType.elements[elem_idx] - if isinstance(elem_type, ir.IntType) and isinstance(val.type, ir.IntType): - if val.type.width < elem_type.width: - val = Gen.builder.zext(val, elem_type, name=f"zext_init_{ClassName}_{kw.arg}") - elif val.type.width > elem_type.width: - val = Gen.builder.trunc(val, elem_type, name=f"trunc_init_{ClassName}_{kw.arg}") - elif isinstance(elem_type, ir.PointerType) and isinstance(val.type, ir.PointerType): - if val.type.pointee != elem_type.pointee: - val = Gen.builder.bitcast(val, elem_type, name=f"bitcast_init_{ClassName}_{kw.arg}") - elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), elem_idx)], name=f"init_elem_{ClassName}_{kw.arg}") - Gen._store(val, elem_ptr) - - def _HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - # pointee-volatile 基址名后缀(同 HandlesExprAttr._HandleSubscriptLlvm);空后缀 - # ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。 - _PVSuffix: str = Gen._SubscriptSuffix(Node.value) - ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen) - if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'): - obj_val = self.HandleExprLlvm(Node.value) - idx_val = self.HandleExprLlvm(Node.slice) - if obj_val and idx_val: - # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 - if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: - obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") - if self._is_char_pointer(obj_val): - obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - if isinstance(idx_val.type, ir.IntType) and idx_val.type.width < 64: - idx_val = Gen.builder.zext(idx_val, ir.IntType(64), name="zext_idx") - result = Gen.builder.call(Gen._get_function(f'{ClassName}.__getitem__'), [obj_val, idx_val], name=f"call_{ClassName}.__getitem__") - return result - ValueVal = self.HandleExprLlvm(Node.value) - if not ValueVal: - return None - IndexVal = self.HandleExprLlvm(Node.slice) - if not IndexVal: - return None - if isinstance(ValueVal.type, ir.IntType): - var_ptr = self._get_int_ptr(Node.value) - if var_ptr: - i8_ptr = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") - ptr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") - return ptr - return None - if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): - LoadedValueVal = Gen._load(ValueVal, name="Load_subscript") - if isinstance(LoadedValueVal.type, ir.PointerType): - ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name=f"subscript{_PVSuffix}") - return ElemPtr - if isinstance(ValueVal.type, ir.PointerType): - pointee = ValueVal.type.pointee - if isinstance(pointee, ir.ArrayType): - zero = ir.Constant(ir.IntType(32), 0) - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}") - return Gen._load(ElemPtr, name="arr_subscript_val") - else: - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") - ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") - if isinstance(pointee, ir.IntType): - return Gen._load(ElemPtr, name="subscript_val") - return ElemPtr - elif isinstance(ValueVal.type, ir.ArrayType): - zero = ir.Constant(ir.IntType(32), 0) - if isinstance(Node.value, ast.Name): - VarName = Node.value.id - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - return ElemPtr - arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") - Gen._store(ValueVal, arr_alloc) - ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}") - return ElemPtr - return None - - def _HandleGenericCallExplicitLlvm(self, Node: ast.Call, FuncName: str, explicit_slice: ast.AST) -> ir.Value | None: - """处理泛型函数手动传类型参数的调用 xxx[T](x)。 - - 与自动推导的 _HandleGenericCallLlvm 不同,这里从 explicit_slice - 提取类型参数,而不是从参数值推导。 - """ - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - template: dict = self.Trans.FunctionHandler._generic_templates[FuncName] - type_param_names: list[str] = template['type_params'] - # 从 slice 提取类型参数 - type_args: list[str] = [] - slice_nodes: list[ast.AST] = [] - if isinstance(explicit_slice, ast.Tuple): - slice_nodes = list(explicit_slice.elts) - else: - slice_nodes = [explicit_slice] - for sn in slice_nodes: - result = self._resolve_generic_slice_type(sn) - if result is not None: - type_args.append(result[0]) - # 无法解析全部类型实参时禁止按 int 兜底(否则会特化出错误元素类型的函数) - if len(type_args) < len(type_param_names): - raise Exception( - f"泛型函数 '{FuncName}' 的显式类型实参无法全部解析:" - f"需要 {len(type_param_names)} 个,仅解析出 {len(type_args)} 个;" - f"禁止按 int 兜底。") - type_args = type_args[:len(type_param_names)] - # 特化 - spec_name = self.Trans.FunctionHandler._specialize_generic_function(FuncName, type_args, Gen) - if spec_name is None: - return None - mangled_spec = Gen._mangle_func_name(spec_name) - func = None - if mangled_spec in Gen.functions: - func = Gen.functions[mangled_spec] - elif spec_name in Gen.functions: - func = Gen.functions[spec_name] - else: - for fname, fobj in Gen.functions.items(): - if spec_name in fname or fname.endswith(spec_name): - func = fobj - break - if func is None: - return None - # 处理调用参数 - CallArgs: list[ir.Value] = [] - for arg_node in Node.args: - arg_val = self.HandleExprLlvm(arg_node) - if arg_val: - CallArgs.append(arg_val) - if not CallArgs: - CallArgs = [ir.Constant(ir.IntType(32), 0) for _ in func.args] - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted = Gen._adjust_args(CallArgs, func) - return Gen.builder.call(func, adjusted, name=f"call_{spec_name}") - - def _HandleGenericCallLlvm(self, Node: ast.Call, FuncName: str) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - template: dict = self.Trans.FunctionHandler._generic_templates[FuncName] - type_param_names: list[str] = template['type_params'] - type_args: list[str] = [] - CallArgs: list[ir.Value] = [] - for i, arg_node in enumerate(Node.args): - arg_val = self.HandleExprLlvm(arg_node) - if arg_val: - CallArgs.append(arg_val) - type_str = self.Trans.FunctionHandler._infer_type_arg_from_value(arg_val, Gen) - type_args.append(type_str) - else: - # 实参未产生 LLVM 值 ⇒ 无法推导其类型实参,禁止按 int 兜底 - raise Exception( - f"泛型函数 '{FuncName}' 调用无法从第 {i + 1} 个实参推导类型实参;" - f"禁止按 int 兜底。") - if len(type_args) < len(type_param_names): - raise Exception( - f"泛型函数 '{FuncName}' 调用无法推导全部类型实参:" - f"需要 {len(type_param_names)} 个,仅推导出 {len(type_args)} 个;" - f"禁止按 int 兜底。") - type_args = type_args[:len(type_param_names)] - spec_name = self.Trans.FunctionHandler._specialize_generic_function(FuncName, type_args, Gen) - if spec_name is None: - return None - mangled_spec = Gen._mangle_func_name(spec_name) - func = None - if mangled_spec in Gen.functions: - func = Gen.functions[mangled_spec] - elif spec_name in Gen.functions: - func = Gen.functions[spec_name] - else: - for fname, fobj in Gen.functions.items(): - if spec_name in fname or fname.endswith(spec_name): - func = fobj - break - if func is None: - return None - if not CallArgs: - CallArgs = [ir.Constant(ir.IntType(32), 0) for _ in func.args] - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted = Gen._adjust_args(CallArgs, func) - return Gen.builder.call(func, adjusted, name=f"call_{spec_name}") - - def _HandleGenericClassNewLlvm(self, Node: ast.Call, ClassName: str, explicit_slice: ast.AST | None = None) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - template: dict = self.Trans.ClassHandler._generic_class_templates[ClassName] - type_param_names: list[str] = template['type_params'] - type_args: list[str] = [] - type_names: list[str] = [] - # 如果有显式类型参数 (如 list[int](mb)),直接从 slice 提取 - if explicit_slice is not None: - slice_nodes: list[ast.AST] = [] - if isinstance(explicit_slice, ast.Tuple): - slice_nodes = list(explicit_slice.elts) - else: - slice_nodes = [explicit_slice] - for sn in slice_nodes: - result = self._resolve_generic_slice_type(sn) - if result is not None: - type_args.append(result[0]) - type_names.append(result[1]) - # 无法解析全部类型实参时禁止按 int 兜底(否则特化出错误元素类型的类) - if len(type_args) < len(type_param_names): - raise Exception( - f"泛型类 '{ClassName}' 的显式类型实参无法全部解析:" - f"需要 {len(type_param_names)} 个,仅解析出 {len(type_args)} 个;" - f"禁止按 int 兜底。") - spec_name = self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names) - if spec_name is None: - return ir.Constant(ir.IntType(32), 0) - return self._HandleClassNewLlvm(Node, spec_name) - all_inferred: list[tuple[str, str]] = [] - for i, arg_node in enumerate(Node.args): - type_str = 'int' - type_name = 'CInt' - if isinstance(arg_node, ast.Constant): - if isinstance(arg_node.value, float): - type_str = 'double' - type_name = 'CDouble' - elif isinstance(arg_node.value, int): - type_str = 'int' - type_name = 'CInt' - elif isinstance(arg_node.value, bool): - type_str = 'i8' - type_name = 'CBool' - elif isinstance(arg_node, ast.Call): - if isinstance(arg_node.func, ast.Name): - func_id = arg_node.func.id - if func_id == 'float': - type_str = 'double' - type_name = 'CDouble' - elif func_id == 'int': - type_str = 'int' - type_name = 'CInt' - elif func_id == 'char': - # char() 内建 i8 强转(与 float()/double() 并列) - type_str = 'i8' - type_name = 'CChar' - else: - float_types = {'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T'} - int_types = {'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CShort', 'CLong'} - if func_id in float_types: - type_str = 'double' - type_name = func_id - elif func_id == 'CChar': - type_str = 'i8' - type_name = func_id - elif func_id in int_types: - type_str = 'int' - type_name = func_id - elif isinstance(arg_node.func, ast.Attribute): - attr_name = arg_node.func.attr - float_types = {'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T'} - int_types = {'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CShort', 'CLong'} - if attr_name in float_types: - type_str = 'double' - type_name = attr_name - elif attr_name == 'CChar': - type_str = 'i8' - type_name = attr_name - elif attr_name in int_types: - type_str = 'int' - type_name = attr_name - elif isinstance(arg_node, ast.Name): - var_info = self.Trans.SymbolTable.lookup(arg_node.id) - if var_info and isinstance(var_info, dict): - var_type = var_info.get('type', '') - if var_type in ('double', 'float'): - type_str = 'double' - type_name = 'CDouble' - elif var_type in ('char', 'i8', 'CChar'): - type_str = 'i8' - type_name = 'CChar' - all_inferred.append((type_str, type_name)) - hint_types = [(ts, tn) for ts, tn in all_inferred if tn != 'CInt' or ts != 'int'] - for i in range(len(type_param_names)): - if i < len(hint_types): - type_args.append(hint_types[i][0]) - type_names.append(hint_types[i][1]) - elif i < len(all_inferred): - type_args.append(all_inferred[i][0]) - type_names.append(all_inferred[i][1]) - else: - # 第 i 个类型参数既无对应实参也无法推断 ⇒ 禁止按 int 兜底 - raise Exception( - f"泛型类 '{ClassName}' 构造调用无法为第 {i + 1} 个类型参数推导实参;" - f"禁止按 int 兜底。") - spec_name = self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names) - if spec_name is None: - return ir.Constant(ir.IntType(32), 0) - return self._HandleClassNewLlvm(Node, spec_name) - + def _ResolveGenericSlice(self, Sub: ast.Subscript) -> str | None: + """从泛型 Subscript (如 Stack[int]) 解析特化名,确保特化类型已生成。""" + if not isinstance(Sub.value, ast.Name): + return None + ClassName: str = Sub.value.id + if not (hasattr(self.Trans.ClassHandler, '_generic_class_templates') and ClassName in self.Trans.ClassHandler._generic_class_templates): + return None + Gen = self.Trans.LlvmGen + # 共享符号表构建阶段(_build_shared_symbol_data / ParsePythonFile)LlvmGen 为 None, + # 此时不应发射特化 IR;调用方(GetCTypeInfo)已用 mangled 名设置符号表信息 + if Gen is None: + return None + template: dict = self.Trans.ClassHandler._generic_class_templates[ClassName] + type_param_names: list[str] = template['type_params'] + type_args: list[str] = [] + type_names: list[str] = [] + slice_nodes: list[ast.AST] = [] + if isinstance(Sub.slice, ast.Tuple): + slice_nodes = list(Sub.slice.elts) + else: + slice_nodes = [Sub.slice] + for sn in slice_nodes: + result = self._resolve_generic_slice_type(sn) + if result is not None: + type_args.append(result[0]) + type_names.append(result[1]) + if len(type_args) < len(type_param_names): + raise Exception( + f"泛型类注解 '{ast.unparse(Sub)}' 无法解析全部类型实参:" + f"需要 {len(type_param_names)} 个,仅解析出 {len(type_args)} 个;" + f"禁止按 int 兜底。") + # 类型注解解析(如 Float64Array: t.CTypedef = ndarray[t.CDouble]) + # 只需 struct 定义,不需方法体;使用 declare_only=True 避免过早触发 + # 完整特化(此时被引用的函数如 printf 可能尚未由 from X import Y 注册, + # 导致 "Undefined function" 错误)。完整特化由后续类实例化 + # (_HandleClassNewLlvm → _specialize_generic_class(declare_only=False)) + # 触发,缓存机制会补发方法体。 + return self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names, declare_only=True) + + @staticmethod + def _AnnotationContainsCPtr(Node: ast.AST) -> bool: + """检查类型注解 AST 是否包含 t.CPtr(即是否为指针类型)""" + if isinstance(Node, ast.Attribute): + if Node.attr == 'CPtr': + return True + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + return (ExprCallHandle._AnnotationContainsCPtr(Node.left) + or ExprCallHandle._AnnotationContainsCPtr(Node.right)) + return False + + @staticmethod + def _is_sizeof_type_name(name: str, Gen: "LlvmGeneratorMixin") -> bool: + """检查名称是否是已知的类型名(用于 __sizeof__ 上下文)。 + 检查顺序: Gen.structs → SHA1 前缀 → 内建类型 → CTypeRegistry + """ + if not name: + return False + # 1. 直接在 Gen.structs 中查找 + if name in Gen.structs: + return True + # 2. 带 SHA1 前缀查找(如 ClassDef → 657e182b27c2a022.ClassDef) + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + if name in struct_sha1_map: + return True + # 遍历所有 SHA1 前缀 + for sha1 in struct_sha1_map.values(): + prefixed = f"{sha1}.{name}" + if prefixed in Gen.structs: + return True + # 3. 内建类型名 + if name in ('str', 'bytes', 'int', 'double', 'float', 'char', 'bool', + 'void', 'i8', 'i16', 'i32', 'i64', 'half', 'fp128'): + return True + # 4. CTypeRegistry 中的类型 + if CTypeRegistry.GetClassByName(name) is not None: + return True + if CTypeRegistry.ResolveName(name): + return True + return False + + def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Constant: + Gen = self.Trans.LlvmGen + if isinstance(Node, ast.Name): + type_name = Node.id + elif isinstance(Node, ast.Attribute): + type_name = Node.attr + elif isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + # 联合类型(如 X | t.CPtr):VPC 注解中 | = and,带 t.CPtr 的联合 + # 是指针类型(X* 或 i8*),sizeof = 8(指针大小)。 + # list[T].__init__ 的 T.__sizeof__() 特化后 T 被还原为 'X | t.CPtr' + # (_make_replacement_node),此前落到 else 返回 0 → elem_size=0 → + # 所有元素存取同槽(get(0)==get(1),list[对象] join 误报根因)。 + if _BinOpContainsCPtr(Node): + return ir.Constant(ir.IntType(64), 8) + # 不带 t.CPtr 的联合(A | B)无法确定尺寸:曾静默返回 0 → + # 下游 elem_size=0 → alloc(0) 返回 NULL / 所有元素存取同槽。fail-fast。 + raise ValueError( + f"[FATAL][__sizeof__] 联合类型 '{ast.unparse(Node)}' 不含 t.CPtr," + f"无法确定尺寸,拒绝静默返回 0(会导致 alloc(0)/元素同槽)" + ) + else: + # 非 名称/属性/联合 的 sizeof 目标(如表达式、调用):曾静默返回 0。fail-fast。 + raise ValueError( + f"[FATAL][__sizeof__] 不支持的 sizeof 目标 {type(Node).__name__}" + f"({ast.unparse(Node)})," + f"拒绝静默返回 0(会导致 alloc(0))" + ) + + if type_name not in Gen.structs: + # SHA1 前缀查找:如 ClassDef → 657e182b27c2a022.ClassDef + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + resolved_name: str | None = None + if type_name in struct_sha1_map: + resolved_name = f"{struct_sha1_map[type_name]}.{type_name}" + else: + for sha1 in struct_sha1_map.values(): + prefixed = f"{sha1}.{type_name}" + if prefixed in Gen.structs: + resolved_name = prefixed + break + if resolved_name is not None and resolved_name in Gen.structs: + type_name = resolved_name + if type_name not in Gen.structs: + # str/bytes 是指针类型 (char*/void*),sizeof = 指针大小 + if type_name in ('str', 'bytes'): + return ir.Constant(ir.IntType(64), 8) + ctype_cls = CTypeRegistry.GetClassByName(type_name) + if ctype_cls is not None: + try: + ctype_inst = ctype_cls() + size_bits = getattr(ctype_inst, 'Size', 0) or 0 + size_bytes = size_bits // 8 + # POINTER 类型(CPtr 等)Size 为 None/0 → 指针大小 8。 + # 根因:t.CPtr.__sizeof__() 返回 0 → list[t.CPtr] + # elem_size=0 → append 全写同槽/alloc(0) → 崩溃。 + if getattr(ctype_inst, 'HasPosition', None) \ + and ctype_inst.HasPosition(t.CType.POINTER) \ + and size_bytes == 0: + size_bytes = 8 + return ir.Constant(ir.IntType(64), size_bytes) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + resolved = CTypeRegistry.ResolveName(type_name) + if resolved: + ctype_cls, ptr_level = resolved + try: + ctype_inst = ctype_cls() + size_bits = getattr(ctype_inst, 'Size', 0) or 0 + size_bytes = size_bits // 8 + if ptr_level > 0: + size_bytes = 8 + return ir.Constant(ir.IntType(64), size_bytes) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + # 所有查找策略均失败(不在 Gen.structs、SHA1 前缀查找失败、CTypeRegistry 也找不到): + # 报错终止,拒绝静默返回 0(会导致 malloc(0)/alloc(0) 堆溢出) + raise ValueError( + f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析" + f"(不在 Gen.structs、SHA1 前缀查找失败、CTypeRegistry 也找不到)," + f"拒绝静默返回 0" + ) + + struct_type = Gen.structs[type_name] + if isinstance(struct_type, ir.PointerType): + struct_type = struct_type.pointee + # 保存 builder 状态:_TryLoadStructFromStub / _resolve_opaque_struct_members + # 可能触发重入编译(编译其他模块的函数),这会改变 Gen.func/Gen.builder, + # 导致当前函数的指令被写到错误函数的 block 中(then 块为空的根因) + _saved_func = Gen.func + _saved_builder = Gen.builder + _saved_block = Gen.builder.block if (Gen.builder and Gen.builder.block) else None + _saved_variables = dict(Gen.variables) if Gen.variables else {} + _saved_reg_values = dict(getattr(Gen, '_reg_values', {})) + _saved_direct_values = dict(getattr(Gen, '_direct_values', {})) + _saved_var_type_info = dict(getattr(Gen, 'var_type_info', {})) + _saved_var_signedness = dict(getattr(Gen, 'var_signedness', {})) + _saved_global_vars = set(getattr(Gen, 'global_vars', set())) + _saved_var_scopes = [dict(s) for s in getattr(self.Trans, 'VarScopes', [])] + try: + if isinstance(struct_type, ir.IdentifiedStructType) and (struct_type.elements is None or len(struct_type.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(type_name, Gen) + struct_type = Gen.structs.get(type_name, struct_type) + # Recursively resolve opaque struct members + self._resolve_opaque_struct_members(struct_type, Gen) + finally: + Gen.func = _saved_func + Gen.builder = _saved_builder + if _saved_block is not None and Gen.builder is not None: + Gen.builder.position_at_end(_saved_block) + Gen.variables = _saved_variables + Gen._reg_values = _saved_reg_values + Gen._direct_values = _saved_direct_values + Gen.var_type_info = _saved_var_type_info + Gen.var_signedness = _saved_var_signedness + Gen.global_vars = _saved_global_vars + self.Trans.VarScopes = _saved_var_scopes + # _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。 + return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type)) + + def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None: + Gen = self.Trans.LlvmGen + val = self.HandleExprLlvm(arg_node) + if not val: + return None + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__abs__', val, Gen) + if result is not None: + return result + break + if isinstance(val.type, ir.IntType): + neg_val = Gen.builder.neg(val, name="abs_neg") + is_neg = Gen.builder.icmp_signed('<', val, ir.Constant(val.type, 0), name="is_neg") + return Gen.builder.select(is_neg, neg_val, val, name="abs_result") + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + zero = ir.Constant(val.type, 0.0) + neg_val = Gen.builder.fneg(val, name="fabs_neg") + is_neg = Gen.builder.fcmp_ordered('<', val, zero, name="f_is_neg") + return Gen.builder.select(is_neg, neg_val, val, name="fabs_result") + return None + + def _HandleDirLlvm(self, Node: ast.Call) -> ir.Constant: + Gen = self.Trans.LlvmGen + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _HandleTypeLlvm(self, Node: ast.Call) -> ir.Value: + Gen = self.Trans.LlvmGen + if not Node.args: + type_str = "" + else: + arg = Node.args[0] + enum_type_name = None + + def _get_attr_path(node): + if isinstance(node, ast.Name): + return node.id + elif isinstance(node, ast.Attribute): + return f"{_get_attr_path(node.value)}.{node.attr}" + return None + + if isinstance(arg, ast.Name): + arg_name = arg.id + Gen = self.Trans.LlvmGen + if arg_name in Gen.var_type_info: + type_info = Gen.var_type_info[arg_name] + if isinstance(type_info, dict) and type_info.get('type') == 'enum': + enum_type_name = type_info.get('name') + if not enum_type_name: + TypeInfo = self.Trans.SymbolTable.lookup(arg_name) + if TypeInfo and TypeInfo.IsEnum: + enum_type_name = arg_name + elif isinstance(arg, ast.Attribute): + LastPart = None + enum_class_name = None + if isinstance(arg.value, ast.Name): + LastPart = arg.attr + AttrInfo = self.Trans.SymbolTable.lookup(LastPart) + if AttrInfo and AttrInfo.IsEnumMember and AttrInfo.EnumName: + enum_type_name = AttrInfo.EnumName + elif isinstance(arg.value, ast.Attribute): + attr_path = _get_attr_path(arg) + if attr_path: + parts = attr_path.split('.') + if len(parts) >= 2: + enum_class_name = parts[-2] + LastPart = parts[-1] + qualified_name_dot = f"{enum_class_name}.{LastPart}" + qualified_name_under = f"{enum_class_name}_{LastPart}" + enum_member_found = None + for qname in (qualified_name_dot, qualified_name_under): + info = self.Trans.SymbolTable.lookup(qname) + if info and info.IsEnumMember and info.EnumName == enum_class_name: + enum_member_found = info + break + if not enum_member_found: + for key in self.Trans.SymbolTable: + if key == LastPart: + info = self.Trans.SymbolTable[key] + if info.IsEnumMember and info.EnumName == enum_class_name: + enum_member_found = info + break + if enum_member_found: + enum_type_name = enum_class_name + if enum_type_name: + _enum_bits: int = self.Trans.LlvmGen._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): + pointee = llvm_type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + llvm_type = pointee + type_info = self.Trans.ExprUtils._llvm_type_to_detailed_string(llvm_type) + type_str = f"" + return Gen.emit_constant(type_str, 'string') + + def _HandleInputLlvm(self, Node: ast.Call) -> ir.Value: + Gen = self.Trans.LlvmGen + if Node.args and isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, str): + prompt = Node.args[0].value + prompt_ptr = Gen.emit_constant(prompt, 'string') + printf_func = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + Gen.builder.call(printf_func, [prompt_ptr]) + scanf_func = Gen.get_or_declare_c_func('scanf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + input_buffer = Gen._alloca(ir.ArrayType(ir.IntType(8), 256), name="input_buffer") + result = Gen.builder.call(scanf_func, [Gen.emit_constant("%255s", 'string'), input_buffer]) + return Gen.builder.gep(input_buffer, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="input_result") + + def _HandleAtoiLlvm(self, str_ptr: ir.Value) -> ir.Value: + Gen = self.Trans.LlvmGen + atoi_func = Gen.get_or_declare_c_func('atoi', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))])) + return Gen.builder.call(atoi_func, [str_ptr], name="atoi_result") + + def _HandleOrdLlvm(self, expr_node: ast.AST) -> ir.Value: + Gen = self.Trans.LlvmGen + val = self.HandleExprLlvm(expr_node) + if val and self._is_char_pointer(val): + return Gen._load(val, name="ord_result") + return ir.Constant(ir.IntType(32), 0) + + def _HandleChrLlvm(self, expr_node: ast.AST) -> ir.Value: + Gen = self.Trans.LlvmGen + val = self.HandleExprLlvm(expr_node) + if val: + char_ptr = Gen._alloca(ir.IntType(8), name="chr_char") + Gen.builder.store(Gen.builder.trunc(val, ir.IntType(8), name="trunc_chr"), char_ptr) + return Gen.builder.gep(char_ptr, [ir.Constant(ir.IntType(32), 0)], name="chr_result") + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _HandleMallocLlvm(self, args: list[ast.AST]) -> ir.Value: + Gen = self.Trans.LlvmGen + malloc_type = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) + malloc_func = Gen._get_or_declare_func('malloc', malloc_type) + param_type = malloc_func.type.pointee.args[0] + size_val = None + if args: + size_val = self.HandleExprLlvm(args[0]) + if size_val and isinstance(size_val.type, ir.IntType): + if isinstance(param_type, ir.IntType) and size_val.type.width != param_type.width: + if size_val.type.width < param_type.width: + size_val = Gen.builder.zext(size_val, param_type, name="zext_malloc_size") + else: + size_val = Gen.builder.trunc(size_val, param_type, name="trunc_malloc_size") + if not size_val: + size_val = ir.Constant(param_type, 1) + result = Gen.builder.call(malloc_func, [size_val], name="malloc_result") + return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="malloc_cast") + + def _HandleReallocLlvm(self, args: list[ast.AST]) -> ir.Value: + Gen = self.Trans.LlvmGen + realloc_type = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)]) + realloc_func = Gen._get_or_declare_func('realloc', realloc_type) + size_param_type = realloc_func.type.pointee.args[1] + ptr_val = None + size_val = None + if len(args) >= 2: + ptr_val = self.HandleExprLlvm(args[0]) + size_val = self.HandleExprLlvm(args[1]) + elif len(args) == 1: + size_val = self.HandleExprLlvm(args[0]) + if not ptr_val: + ptr_val = ir.Constant(ir.PointerType(ir.IntType(8)), None) + if isinstance(ptr_val.type, ir.PointerType) and not self._is_char_pointer(ptr_val): + ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="realloc_cast_ptr") + if size_val and isinstance(size_val.type, ir.IntType): + if isinstance(size_param_type, ir.IntType) and size_val.type.width != size_param_type.width: + if size_val.type.width < size_param_type.width: + size_val = Gen.builder.zext(size_val, size_param_type, name="zext_realloc_size") + else: + size_val = Gen.builder.trunc(size_val, size_param_type, name="trunc_realloc_size") + if not size_val: + size_val = ir.Constant(size_param_type, 0) + result = Gen.builder.call(realloc_func, [ptr_val, size_val], name="realloc_result") + return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="realloc_cast") + + def _HandleFreeLlvm(self, args: list[ast.AST]) -> ir.Constant: + Gen = self.Trans.LlvmGen + free_type = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]) + free_func = Gen._get_or_declare_func('free', free_type) + if args: + var_name = None + if isinstance(args[0], ast.Name): + var_name = args[0].id + ptr_val = self.HandleExprLlvm(args[0]) + if ptr_val: + if var_name and hasattr(Gen, '_var_to_heap_ptr') and var_name in Gen._var_to_heap_ptr: + registered_val = Gen._var_to_heap_ptr[var_name] + Gen._unregister_local_heap_ptr(registered_val) + del Gen._var_to_heap_ptr[var_name] + if isinstance(ptr_val.type, ir.PointerType) and not self._is_char_pointer(ptr_val): + ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="free_cast_ptr") + Gen.builder.call(free_func, [ptr_val], name="free_call") + return ir.Constant(ir.IntType(32), 0) + + def _HandleMemcpyLlvm(self, args: list[ast.AST]) -> None: + Gen = self.Trans.LlvmGen + if 'llvm.memcpy' not in Gen.functions: + memcpy_type = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) + Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type) + if len(args) >= 3: + dst = self.HandleExprLlvm(args[0]) + src = self.HandleExprLlvm(args[1]) + size = self.HandleExprLlvm(args[2]) + if dst and src and size: + i8ptr = ir.PointerType(ir.IntType(8)) + if isinstance(dst.type, ir.PointerType) and dst.type != i8ptr: + dst = Gen.builder.bitcast(dst, i8ptr, name="memcpy_dst_cast") + elif isinstance(dst.type, ir.IntType): + if dst.type.width < 64: + dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_dst_ptr") + dst = Gen.builder.inttoptr(dst, i8ptr, name="dst_int2ptr") + if isinstance(src.type, ir.PointerType) and src.type != i8ptr: + src = Gen.builder.bitcast(src, i8ptr, name="memcpy_src_cast") + elif isinstance(src.type, ir.IntType): + if src.type.width < 64: + src = Gen.builder.zext(src, ir.IntType(64), name="zext_src_ptr") + src = Gen.builder.inttoptr(src, i8ptr, name="src_int2ptr") + if isinstance(size.type, ir.IntType) and size.type.width < 64: + size = Gen.builder.zext(size, ir.IntType(64), name="zext_memcpy_size") + isvolatile = ir.Constant(ir.IntType(1), 0) + Gen.builder.call(Gen.functions['llvm.memcpy'], [dst, src, size, isvolatile], name="memcpy_call") + return None + + def _HandleMemsetLlvm(self, args: list[ast.AST]) -> None: + Gen = self.Trans.LlvmGen + memset_func = Gen.get_or_declare_c_func('memset', ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)])) + if len(args) >= 3: + dst = self.HandleExprLlvm(args[0]) + val = self.HandleExprLlvm(args[1]) + size = self.HandleExprLlvm(args[2]) + if dst and val is not None and size: + if isinstance(dst.type, ir.PointerType) and not self._is_char_pointer(dst): + dst = Gen.builder.bitcast(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_cast") + if isinstance(val.type, ir.IntType) and val.type.width != 32: + if val.type.width < 32: + val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val") + else: + val = Gen.builder.trunc(val, ir.IntType(32), name="trunc_memset_val") + if isinstance(size.type, ir.IntType) and size.type.width < 64: + size = Gen.builder.zext(size, ir.IntType(64), name="zext_memset_size") + elif isinstance(size.type, ir.IntType) and size.type.width > 64: + size = Gen.builder.trunc(size, ir.IntType(64), name="trunc_memset_size") + Gen.builder.call(memset_func, [dst, val, size], name="memset_call") + return None + + def _HandleVaStartLlvm(self, args: list[ast.AST]) -> None: + Gen = self.Trans.LlvmGen + return None + + def _HandleVaEndLlvm(self, args: list[ast.AST]) -> None: + Gen = self.Trans.LlvmGen + return None + + def _HandleVaArgLlvm(self, args: list[ast.AST]) -> ir.Constant: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + return ir.Constant(ir.IntType(32), 0) + + def _HandleCDerefAsLlvm(self, Node: ast.Call) -> ir.Value | None: + """c.DerefAs(p, v):bitcast **指针** p 到 typeof(v)* 后按值存 v(`*(T*(p)) = v`)。 + + 与 ViperC CallGenCBuiltin.translate_c_derefas 逐字同口径: + - 单参形式(len<2)是**解引用读取**(等同 c.Deref(p)); + - 双参:p、v 均为指针且 p 的 pointee 是结构体且与 v 的 pointee **同一**时, + 按整对象拷贝(load v 指向的对象 + bitcast p + store);否则 bitcast p 到 + typeof(v)* 后按值 store v。 + 此前实现把**值** bitcast 成指针大小整数再写(`*(intptr_t*(p)) = (intptr_t)v`), + 与语义不符(R5 混淆了"bitcast 指针"与"bitcast 值")。 + """ + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if Node.args is None or len(Node.args) < 1: + return ir.Constant(ir.IntType(32), 0) + if len(Node.args) < 2: + # 单参:解引用读取(与 VPC translate_c_derefas 的转发口径一致) + return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) + ptr_val: ir.Value | None = self.HandleExprLlvm(Node.args[0]) + val_val: ir.Value | None = self.HandleExprLlvm(Node.args[1]) + if ptr_val is None or val_val is None: + return ir.Constant(ir.IntType(32), 0) + if not isinstance(ptr_val.type, ir.PointerType): + return val_val + # 整对象拷贝:p 的 pointee 是结构体且与 v 的 pointee 同一(REnum 变体 tag 才不会丢) + if isinstance(val_val.type, ir.PointerType): + p_pointee: ir.Type = ptr_val.type.pointee + v_pointee: ir.Type = val_val.type.pointee + if (isinstance(p_pointee, (ir.IdentifiedStructType, ir.LiteralStructType)) + and p_pointee == v_pointee): + loaded_s: ir.Value = Gen._load(val_val, name="deref_as_struct_load") + store_ptr: ir.Value = ptr_val + if store_ptr.type != val_val.type: + store_ptr = Gen.builder.bitcast(ptr_val, val_val.type, name="deref_as_ptr_align") + Gen._store(loaded_s, store_ptr) + return val_val + # 常规:bitcast p 到 typeof(v)* 后按值存 v(`*(T*(p)) = v`) + if ptr_val.type.pointee != val_val.type: + ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(val_val.type), name="deref_as_cast") + Gen._store(val_val, ptr_val) + return val_val + + def _InjectRequiresFields(self, result: ir.Value, ClassName: str, Gen: LlvmGeneratorMixin) -> None: + """在 __new__ 后、__init__ 前自动注入 __requires__/__require_must__ 字段。 + + - 编译期:对 __require_must__ 做纯静态可达性检查(遍历 _with_provides_stack) + - 运行时:对每个 requires 字段调用 _find_provider,非 NULL 则 bitcast 写入 + """ + requires_all: list[str] = [] + requires_all.extend(Gen.class_requires.get(ClassName, [])) + requires_all.extend(Gen.class_require_must.get(ClassName, [])) + if not requires_all: + return + # 编译期 __require_must__ 静态检查 + must_fields: list[str] = Gen.class_require_must.get(ClassName, []) + if must_fields: + # 跳过类自身方法内的检查(类的实现内部不需要 with 上下文) + current_class = getattr(Gen, '_current_class_name', None) + if current_class != ClassName: + available: set[str] = set() + for provides_list in Gen._with_provides_stack: + available.update(provides_list) + for field in must_fields: + if field not in available: + raise SyntaxError( + f"[TransPyC] Class '{ClassName}' __require_must__ field '{field}' " + f"not provided by any enclosing 'with' context" + ) + # 运行时注入 + find_func = Gen.functions.get('_find_provider') + if not find_func: + return + members = Gen.class_members.get(ClassName, []) + # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 + base = Gen._member_base(ClassName) + field_map: dict[str, tuple[int, ir.Type]] = {} + for i, (mname, mtype) in enumerate(members): + if mname in requires_all: + field_map[mname] = (base + i, mtype) + for field_name in requires_all: + if field_name not in field_map: + continue + field_idx, field_type = field_map[field_name] + field_str = Gen._create_string_global(field_name) + ptr_val = Gen.builder.call(find_func, [field_str], name=f"find_{field_name}") + null_ptr = ir.Constant(ptr_val.type, None) + is_null = Gen.builder.icmp_unsigned('==', ptr_val, null_ptr, name=f"is_null_{field_name}") + skip_bb = Gen.func.append_basic_block(f"skip_inject_{field_name}") + inject_bb = Gen.func.append_basic_block(f"inject_{field_name}") + Gen.builder.cbranch(is_null, skip_bb, inject_bb) + Gen.builder.position_at_end(inject_bb) + if isinstance(field_type, ir.PointerType): + casted = Gen.builder.bitcast(ptr_val, field_type, name=f"cast_{field_name}") + elif isinstance(field_type, ir.IntType): + casted = Gen.builder.ptrtoint(ptr_val, field_type, name=f"cast_{field_name}") + else: + casted = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name=f"cast_{field_name}") + elem_ptr = Gen.builder.gep(result, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), field_idx)], name=f"field_{field_name}_ptr") + Gen._store(casted, elem_ptr) + Gen.builder.branch(skip_bb) + Gen.builder.position_at_end(skip_bb) + + # _HandleNewExprLlvm - Viper `new [描述符...] T(args)`(vast ExprParser 产出 ast.New)。 + # - memgr(x):按 sizeof(T) 从 x.alloc 取存储(T*),置 Gen.NewStorageOverride 供 + # _HandleClassNewLlvm / _HandleREnumConstructLlvm 复用(否则它们各自 alloca); + # - 其余(stack/heap/hlife/slife/缺省):与旧 _ExpandNewExprs 容忍层同语义 —— 等价 + # 裸构造 T(args)(TPC 的类/REnum 构造自带存储策略)。 + def _HandleNewExprLlvm(self, Node: ast.New) -> ir.Value | None: + """`new [描述符...] T(args)`:取存储 + 构造;返回 T*(失败响亮失败)""" + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + # 合成裸构造 Call(VPC New 节点不携带 keywords) + ArgList: list = Node.args if Node.args is not None else [] + cl: ast.Call = ast.Call(func=Node.func, args=list(ArgList), keywords=[]) + cl.lineno = getattr(Node, 'lineno', 0) + cl.col_offset = getattr(Node, 'col_offset', 0) + cl.end_lineno = getattr(Node, 'end_lineno', 0) + cl.end_col_offset = getattr(Node, 'end_col_offset', 0) + if Node.Source != NEW_SRC_MEMGR or Node.memgr is None: + return self.HandleExprLlvm(cl) + # ---- memgr(x):池分配 ---- + StructType: ir.Type | None = self._ResolveNewMemgrType(Node) + if StructType is None: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"`new memgr(x) T(args)`:被构造类型 T 既不是已注册类/REnum 变体,也不是" + f"可解析的标量/数组(memgr 路径需要具体布局算尺寸){Gen._get_node_info()}") + size_bytes: int = Gen._get_struct_size(StructType) + # raw = .alloc(sizeof(T)) —— 复用既有方法调用通道(mlirlite._new_type 同款) + SizeNode: ast.Constant = ast.Constant(value=size_bytes, kind=None) + SizeNode.lineno = cl.lineno + SizeNode.col_offset = cl.col_offset + AllocAttr: ast.Attribute = ast.Attribute(value=Node.memgr, attr='alloc', ctx=ast.Load()) + AllocAttr.lineno = cl.lineno + AllocAttr.col_offset = cl.col_offset + AllocCall: ast.Call = ast.Call(func=AllocAttr, args=[SizeNode], keywords=[]) + AllocCall.lineno = cl.lineno + AllocCall.col_offset = cl.col_offset + Raw: ir.Value | None = self.HandleExprLlvm(AllocCall) + if Raw is None: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"`new memgr(x)`:x.alloc({size_bytes}) 求值失败(x 需为可 alloc 的内存管理器)" + f"{Gen._get_node_info()}") + TargetPtrType: ir.Type = ir.PointerType(StructType) + Storage: ir.Value + if isinstance(Raw.type, ir.PointerType) and Raw.type != TargetPtrType: + Storage = Gen.builder.bitcast(Raw, TargetPtrType, name="new_memgr_ptr_cast") + elif isinstance(Raw.type, ir.IntType): + Storage = Gen.builder.inttoptr(Raw, TargetPtrType, name="new_memgr_int2ptr") + else: + Storage = Raw + Prev: object = Gen.NewStorageOverride + Gen.NewStorageOverride = Storage + try: + return self.HandleExprLlvm(cl) + finally: + Gen.NewStorageOverride = Prev + + def _ResolveNewMemgrType(self, Node: ast.New) -> ir.Type | None: + """`new memgr(x) T(args)` 的 T 的 LLVM 结构类型(REnum 变体 / 普通类); + 非类/REnum(标量/数组等)返回 None(由调用方响亮失败)。""" + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + fn: ast.AST | None = Node.func + if fn is None: + return None + # REnum 变体:Qual.Variant / mod.Qual.Variant(Qual 为已注册 REnum 类) + if isinstance(fn, ast.Attribute): + Qual: str | None = None + Base: ast.AST | None = fn.value + if isinstance(Base, ast.Name): + Qual = Base.id + elif isinstance(Base, ast.Attribute): + Qual = Base.attr + if Qual is not None: + QSym = self.Trans.SymbolTable.lookup(Qual) + if QSym is not None and getattr(QSym, 'IsRenum', False): + if Qual not in Gen.structs: + self.Trans.ImportHandler._TryLoadStructFromStub(Qual, Gen) + return Gen.structs.get(Qual) + # 普通类构造:ClassName(...) / mod.ClassName(...) + ClsName: str | None = None + if isinstance(fn, ast.Name): + ClsName = fn.id + elif isinstance(fn, ast.Attribute): + ClsName = fn.attr + if ClsName is not None: + if ClsName not in Gen.structs: + self.Trans.ImportHandler._TryLoadStructFromStub(ClsName, Gen) + return Gen.structs.get(ClsName) + return None + + def _HandleClassNewLlvm(self, Node: ast.Call, ClassName: str, module_sha1: str | None = None) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + # DEBUG: 追踪 Asm 类的构造 + if ClassName == 'Asm': + import sys as _dbg_sys + print(f"[DEBUG_CLASSNEW] _HandleClassNewLlvm called for Asm, module_sha1={module_sha1}", file=_dbg_sys.stderr) + # 跨模块类型引用:优先用 {module_sha1}.{ClassName} 查找 struct + _struct_full_key: str | None = f"{module_sha1}.{ClassName}" if module_sha1 else None + if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and ClassName in self.Trans.ClassHandler._generic_class_templates: + return self._HandleGenericClassNewLlvm(Node, ClassName) + NewFuncName = f'{ClassName}.__before_init__' + InitFuncName = f'{ClassName}.__init__' + NewMethodFuncName = f'{ClassName}.__new__' + has_new_method = Gen._find_function(NewMethodFuncName) is not None + + # 【self 的类型必须与 callee 同源(2026-09-30)】 + # `Gen.structs` 的键有"裸短名 / sha1 全名 / import 别名"三种:定义端恒以**短名** + # 作键(StructGen._generate_structs),别名注册只是**浅拷贝**(HandlesImports 对 + # `Gen.structs[asname] = Gen.structs[name]`)。因此"两个模块各定义同名类"时, + # `ClassName` 无论是别名(`from .astaux import Keyword as KwNode` ⇒ 'KwNode')、 + # 还是被别的模块先占用的短名('Keyword' 被 .tokens 占用),按名字查都可能拿到 + # **另一份实例**。唯一无歧义的判据是 **callee 自己**:构造函数 + # (__new__/__before_init__/__init__)第 0 个形参(self)的 pointee 就是编译器 + # 写下的期望类型,也正是 `_adjust_args` 用来比较的对象(llvmlite 的类型相等按 + # **IR 名字**判定,实测同名不同对象 `==` 为真,故取 callee 形参的 pointee 即与之 + # 逐字同名;按短名另取一份只会继续 fail-fast)。 + # 现场:includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)` + # —— callee 是 astaux 的 `8312ce86a2fd82c0.Keyword.__new__`,self 实参却按 + # 短名/别名叫到了 tokens 的 `fef783da4d24330d.Keyword` ⇒ + # "指针隐式转换已禁止(非基本类型(结构体/类/联合))"。 + def _CalleeSelfType() -> ir.Type | None: + """callee 第 0 个形参 self 的 pointee(未具化时按 IR 名补已具化的同类型)。 + + 按 __new__ → __before_init__ → __init__ 顺序取第一个可用的:三者都是 self + 的接收者,且同名类的多份实例只可能因"查名"分歧而不同源,取 callee 侧即可 + 消除分歧。取不到(无形参 / 非具名结构体)时返回 None,调用方退回按名字查。 + 未具化的实例不能用于 `_allocaEntry`(算不出尺寸),故按 **IR 名**换已具化的 + 同类型对象 —— IR 里类型的身份就是名字,同名即同类型(判据同 + `BaseGen._FindStructByIrName` 的文档)。 + """ + for _fn_name in (NewMethodFuncName, NewFuncName, InitFuncName): + _Fn: Any = Gen._find_function(_fn_name) + _Args: Any = getattr(getattr(_Fn, 'function_type', None), 'args', None) + if not _Args: + continue + _P0: Any = _Args[0] + if not isinstance(_P0, ir.PointerType): + continue + _PT: Any = _P0.pointee + if not isinstance(_PT, ir.IdentifiedStructType): + continue + if _PT.elements is not None: + return _PT + _Twin: Any = Gen._FindStructByIrName(getattr(_PT, 'name', '') or '') + if isinstance(_Twin, ir.IdentifiedStructType) and _Twin.elements is not None: + return _Twin + return None + + def _get_struct_type(name: str) -> ir.Type | None: + """self 用的 struct:callee 形参(无歧义)→ sha1 全名键 → 名字键""" + _SelfTy: ir.Type | None = _CalleeSelfType() + if _SelfTy is not None: + return _SelfTy + if _struct_full_key and _struct_full_key in Gen.structs: + return Gen.structs[_struct_full_key] + return Gen.structs.get(name) + + if ClassName == 'Asm': + import sys as _dbg_sys + print(f"[DEBUG_CLASSNEW] Asm: has_before_init={Gen._has_function(NewFuncName)}, has_new={has_new_method}, struct_in_gen={ClassName in Gen.structs}", file=_dbg_sys.stderr) + if Gen._has_function(NewFuncName): + NewFunc = Gen._get_function(NewFuncName) + StructType = _get_struct_type(ClassName) + if StructType: + # `new memgr(x) ClassName(args)`:_HandleNewExprLlvm 已取好池存储 → 复用; + # 裸 `ClassName(args)` / 非 memgr 描述符时 NewStorageOverride 为 None → alloca。 + if Gen.NewStorageOverride is not None: + result = Gen.NewStorageOverride + else: + result = Gen._allocaEntry(StructType, name=f"{ClassName}_alloca") + else: + return ir.Constant(ir.IntType(32), 0) + Gen.builder.call(NewFunc, [result], name=f"before_init_{ClassName}") + if has_new_method: + NewMethodFunc = Gen._find_function(NewMethodFuncName) + if NewMethodFunc: + NewMethodArgs = [result] + for ArgIdx, arg in enumerate(Node.args, start=1): + ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node) + NewMethodArgs.append(ArgVal) + self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName) + self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted_new = Gen._adjust_args(NewMethodArgs, NewMethodFunc) + heap_ptr = Gen.builder.call(NewMethodFunc, adjusted_new, name=f"new_{ClassName}") + if heap_ptr: + target_ptr_type = ir.PointerType(StructType) if StructType else None + if isinstance(heap_ptr.type, ir.PointerType): + # Bitcast the returned pointer to the struct pointer type if needed + if target_ptr_type and heap_ptr.type != target_ptr_type: + result = Gen.builder.bitcast(heap_ptr, target_ptr_type, name=f"new_{ClassName}_cast") + else: + result = heap_ptr + elif isinstance(heap_ptr.type, ir.IntType) and target_ptr_type: + # __new__ returned an integer (pointer as int), convert back to pointer + if heap_ptr.type.width < 64: + i64_val = Gen.builder.zext(heap_ptr, ir.IntType(64), name=f"new_{ClassName}_i64") + result = Gen.builder.inttoptr(i64_val, target_ptr_type, name=f"new_{ClassName}_ptr") + else: + result = Gen.builder.inttoptr(heap_ptr, target_ptr_type, name=f"new_{ClassName}_ptr") + # __new__ 后:对堆指针重新调用 __before_init__ 初始化默认值(alloca 的默认值已随 alloca 丢弃) + if has_new_method: + Gen.builder.call(NewFunc, [result], name=f"before_init_{ClassName}_heap") + # __new__ 后、__init__ 前:自动注入 requires 字段 + self._InjectRequiresFields(result, ClassName, Gen) + if Gen._has_function(InitFuncName): + InitFunc = Gen._get_function(InitFuncName) + InitArgs = [result] + for ArgIdx, arg in enumerate(Node.args, start=1): + ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__init__", ArgIdx, arg, Node) + InitArgs.append(ArgVal) + self._fill_default_args(InitArgs, InitFunc, InitFuncName) + self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1) + self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted_init = Gen._adjust_args(InitArgs, InitFunc) + Gen.builder.call(InitFunc, adjusted_init, name=f"init_{ClassName}") + return result + # 【两支合并(2026-09-30)】原实现的"sha1 全名键命中"那一支只解析 StructType、 + # **不产出 alloca / 构造调用**,控制流随后落到函数末尾 `return i32 0` —— 即跨模块 + # 同名类的构造会**静默不构造**("名字键命中"那一支才是完整实现)。`_get_struct_type` + # 现已统一「callee 形参 → sha1 全名键 → 名字键」的解析顺序,故两支可合并为一条 + # 条件;行为差异只剩"不再静默返回 i32 0"。 + if (_struct_full_key and _struct_full_key in Gen.structs) or ClassName in Gen.structs: + StructType = _get_struct_type(ClassName) + if isinstance(StructType, ir.PointerType): + StructType = StructType.pointee + is_opaque = isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0) + if is_opaque: + self._ensure_struct_declared(ClassName, module_sha1=module_sha1) + StructType = Gen.structs.get(ClassName, StructType) + if isinstance(StructType, ir.PointerType): + StructType = StructType.pointee + is_opaque = isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0) + if is_opaque: + return None + result = Gen._allocaEntry(StructType, name=f"{ClassName}_alloca") + # Call __new__ method if it exists (may replace self with heap pointer) + if has_new_method: + NewMethodFunc = Gen._find_function(NewMethodFuncName) + if NewMethodFunc: + NewMethodArgs = [result] + for ArgIdx, arg in enumerate(Node.args, start=1): + ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node) + NewMethodArgs.append(ArgVal) + self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName) + self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted_new = Gen._adjust_args(NewMethodArgs, NewMethodFunc) + heap_ptr = Gen.builder.call(NewMethodFunc, adjusted_new, name=f"new_{ClassName}") + if heap_ptr: + target_ptr_type = ir.PointerType(StructType) if StructType else None + if isinstance(heap_ptr.type, ir.PointerType): + # Bitcast the returned pointer to the struct pointer type if needed + if target_ptr_type and heap_ptr.type != target_ptr_type: + result = Gen.builder.bitcast(heap_ptr, target_ptr_type, name=f"new_{ClassName}_cast") + else: + result = heap_ptr + elif isinstance(heap_ptr.type, ir.IntType) and target_ptr_type: + # __new__ returned an integer (pointer as int), convert back to pointer + if heap_ptr.type.width < 64: + i64_val = Gen.builder.zext(heap_ptr, ir.IntType(64), name=f"new_{ClassName}_i64") + result = Gen.builder.inttoptr(i64_val, target_ptr_type, name=f"new_{ClassName}_ptr") + else: + result = Gen.builder.inttoptr(heap_ptr, target_ptr_type, name=f"new_{ClassName}_ptr") + # __new__ 后:对堆指针调用 __before_init__ 初始化默认值 + if has_new_method and Gen._has_function(NewFuncName): + NewFunc = Gen._get_function(NewFuncName) + Gen.builder.call(NewFunc, [result], name=f"before_init_{ClassName}_heap") + # __new__ 后、__init__ 前:自动注入 requires 字段 + self._InjectRequiresFields(result, ClassName, Gen) + InitFunc = Gen.functions.get(InitFuncName) + if not InitFunc: + g = Gen.module.globals.get(InitFuncName) + if g and isinstance(g, ir.Function): + Gen.functions[InitFuncName] = g + InitFunc = g + if InitFunc: + InitArgs = [result] + init_param_types = InitFunc.function_type.args if hasattr(InitFunc, 'function_type') else [] + for i, arg in enumerate(Node.args): + arg_var_type = init_param_types[i + 1] if i + 1 < len(init_param_types) else None + ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg, VarType=arg_var_type), f"{ClassName}.__init__", i + 1, arg, Node) + InitArgs.append(ArgVal) + self._fill_default_args(InitArgs, InitFunc, InitFuncName) + self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1) + self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted_init = Gen._adjust_args(InitArgs, InitFunc) + Gen.builder.call(InitFunc, adjusted_init, name=f"init_{ClassName}") + elif Node.args or Node.keywords: + self._InitStructDefaults(result, ClassName, StructType) + self._InitStructFromArgs(result, Node, ClassName, StructType) + else: + self._InitStructDefaults(result, ClassName, StructType) + return result + return ir.Constant(ir.IntType(32), 0) + + def _ZeroInitStruct(self, struct_ptr: ir.Value, StructType: ir.Type) -> None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if isinstance(StructType, ir.IdentifiedStructType) and StructType.elements: + for i, elem_type in enumerate(StructType.elements): + elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"zero_elem_{i}") + zero_val = self._GetZeroValue(elem_type) + if zero_val is not None: + Gen._store(zero_val, elem_ptr) + elif isinstance(StructType, ir.IntType): + Gen._store(ir.Constant(StructType, 0), struct_ptr) + elif isinstance(StructType, ir.ArrayType) and isinstance(StructType.pointee, ir.IntType): + zero_arr = ir.Constant(StructType, None) + Gen._store(zero_arr, struct_ptr) + + def _GetZeroValue(self, typ: ir.Type) -> ir.Constant | None: + if isinstance(typ, ir.IntType): + return ir.Constant(typ, 0) + elif isinstance(typ, ir.PointerType): + return ir.Constant(typ, None) + elif isinstance(typ, (ir.FloatType, ir.DoubleType)): + return ir.Constant(typ, 0.0) + elif isinstance(typ, ir.ArrayType): + return ir.Constant(typ, None) + return None + + def _InitStructDefaults(self, struct_ptr: ir.Value, ClassName: str, StructType: ir.Type) -> None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + defaults: dict = Gen.class_member_defaults.get(ClassName, {}) + members: list[tuple] = Gen.class_members.get(ClassName, []) + if not defaults or not members: + return + if not isinstance(StructType, ir.IdentifiedStructType): + return + # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 + base = Gen._member_base(ClassName) + for i, (member_name, member_type) in enumerate(members): + if member_name in defaults: + default_val = defaults[member_name] + if isinstance(default_val, ir.Constant): + elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + i)], name=f"default_{ClassName}_{member_name}") + Gen._store(default_val, elem_ptr) + + def _InitStructFromArgs(self, struct_ptr: ir.Value, Node: ast.Call, ClassName: str, StructType: ir.Type) -> None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not isinstance(StructType, ir.IdentifiedStructType): + return + members = Gen.class_members.get(ClassName, []) + # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。 + base = Gen._member_base(ClassName) + for i, arg in enumerate(Node.args): + if i >= len(members): + break + val = self.HandleExprLlvm(arg) + if not val: + continue + member_name, member_type = members[i] + elem_idx = base + i + if StructType.elements is None or elem_idx >= len(StructType.elements): + break + elem_type = StructType.elements[elem_idx] + if isinstance(elem_type, ir.IntType) and isinstance(val.type, ir.IntType): + if val.type.width < elem_type.width: + val = Gen.builder.zext(val, elem_type, name=f"zext_init_{ClassName}_{i}") + elif val.type.width > elem_type.width: + val = Gen.builder.trunc(val, elem_type, name=f"trunc_init_{ClassName}_{i}") + elif isinstance(elem_type, ir.PointerType) and isinstance(val.type, ir.PointerType): + if val.type.pointee != elem_type.pointee: + val = Gen.builder.bitcast(val, elem_type, name=f"bitcast_init_{ClassName}_{i}") + elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), elem_idx)], name=f"init_elem_{ClassName}_{member_name}") + Gen._store(val, elem_ptr) + if Node.keywords: + member_map = {name: (base + i) for i, (name, _) in enumerate(members)} + for kw in Node.keywords: + if kw.arg in member_map: + val = self.HandleExprLlvm(kw.value) + if not val: + continue + elem_idx = member_map[kw.arg] + if StructType.elements is None or elem_idx >= len(StructType.elements): + continue + elem_type = StructType.elements[elem_idx] + if isinstance(elem_type, ir.IntType) and isinstance(val.type, ir.IntType): + if val.type.width < elem_type.width: + val = Gen.builder.zext(val, elem_type, name=f"zext_init_{ClassName}_{kw.arg}") + elif val.type.width > elem_type.width: + val = Gen.builder.trunc(val, elem_type, name=f"trunc_init_{ClassName}_{kw.arg}") + elif isinstance(elem_type, ir.PointerType) and isinstance(val.type, ir.PointerType): + if val.type.pointee != elem_type.pointee: + val = Gen.builder.bitcast(val, elem_type, name=f"bitcast_init_{ClassName}_{kw.arg}") + elem_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), elem_idx)], name=f"init_elem_{ClassName}_{kw.arg}") + Gen._store(val, elem_ptr) + + def _HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + # pointee-volatile 基址名后缀(同 HandlesExprAttr._HandleSubscriptLlvm);空后缀 + # ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。 + _PVSuffix: str = Gen._SubscriptSuffix(Node.value) + ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen) + if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'): + obj_val = self.HandleExprLlvm(Node.value) + idx_val = self.HandleExprLlvm(Node.slice) + if obj_val and idx_val: + # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 + if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: + obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") + if self._is_char_pointer(obj_val): + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + if isinstance(idx_val.type, ir.IntType) and idx_val.type.width < 64: + idx_val = Gen.builder.zext(idx_val, ir.IntType(64), name="zext_idx") + result = Gen.builder.call(Gen._get_function(f'{ClassName}.__getitem__'), [obj_val, idx_val], name=f"call_{ClassName}.__getitem__") + return result + ValueVal = self.HandleExprLlvm(Node.value) + if not ValueVal: + return None + IndexVal = self.HandleExprLlvm(Node.slice) + if not IndexVal: + return None + if isinstance(ValueVal.type, ir.IntType): + var_ptr = self._get_int_ptr(Node.value) + if var_ptr: + i8_ptr = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") + ptr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") + return ptr + return None + if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): + LoadedValueVal = Gen._load(ValueVal, name="Load_subscript") + if isinstance(LoadedValueVal.type, ir.PointerType): + ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name=f"subscript{_PVSuffix}") + return ElemPtr + if isinstance(ValueVal.type, ir.PointerType): + pointee = ValueVal.type.pointee + if isinstance(pointee, ir.ArrayType): + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}") + return Gen._load(ElemPtr, name="arr_subscript_val") + else: + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") + ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") + if isinstance(pointee, ir.IntType): + return Gen._load(ElemPtr, name="subscript_val") + return ElemPtr + elif isinstance(ValueVal.type, ir.ArrayType): + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(Node.value, ast.Name): + VarName = Node.value.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + return ElemPtr + arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") + Gen._store(ValueVal, arr_alloc) + ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}") + return ElemPtr + return None + + def _HandleGenericCallExplicitLlvm(self, Node: ast.Call, FuncName: str, explicit_slice: ast.AST) -> ir.Value | None: + """处理泛型函数手动传类型参数的调用 xxx[T](x)。 + + 与自动推导的 _HandleGenericCallLlvm 不同,这里从 explicit_slice + 提取类型参数,而不是从参数值推导。 + """ + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + template: dict = self.Trans.FunctionHandler._generic_templates[FuncName] + type_param_names: list[str] = template['type_params'] + # 从 slice 提取类型参数 + type_args: list[str] = [] + slice_nodes: list[ast.AST] = [] + if isinstance(explicit_slice, ast.Tuple): + slice_nodes = list(explicit_slice.elts) + else: + slice_nodes = [explicit_slice] + for sn in slice_nodes: + result = self._resolve_generic_slice_type(sn) + if result is not None: + type_args.append(result[0]) + # 无法解析全部类型实参时禁止按 int 兜底(否则会特化出错误元素类型的函数) + if len(type_args) < len(type_param_names): + raise Exception( + f"泛型函数 '{FuncName}' 的显式类型实参无法全部解析:" + f"需要 {len(type_param_names)} 个,仅解析出 {len(type_args)} 个;" + f"禁止按 int 兜底。") + type_args = type_args[:len(type_param_names)] + # 特化 + spec_name = self.Trans.FunctionHandler._specialize_generic_function(FuncName, type_args, Gen) + if spec_name is None: + return None + mangled_spec = Gen._mangle_func_name(spec_name) + func = None + if mangled_spec in Gen.functions: + func = Gen.functions[mangled_spec] + elif spec_name in Gen.functions: + func = Gen.functions[spec_name] + else: + for fname, fobj in Gen.functions.items(): + if spec_name in fname or fname.endswith(spec_name): + func = fobj + break + if func is None: + return None + # 处理调用参数 + CallArgs: list[ir.Value] = [] + for arg_node in Node.args: + arg_val = self.HandleExprLlvm(arg_node) + if arg_val: + CallArgs.append(arg_val) + if not CallArgs: + CallArgs = [ir.Constant(ir.IntType(32), 0) for _ in func.args] + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted = Gen._adjust_args(CallArgs, func) + return Gen.builder.call(func, adjusted, name=f"call_{spec_name}") + + def _HandleGenericCallLlvm(self, Node: ast.Call, FuncName: str) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + template: dict = self.Trans.FunctionHandler._generic_templates[FuncName] + type_param_names: list[str] = template['type_params'] + type_args: list[str] = [] + CallArgs: list[ir.Value] = [] + for i, arg_node in enumerate(Node.args): + arg_val = self.HandleExprLlvm(arg_node) + if arg_val: + CallArgs.append(arg_val) + type_str = self.Trans.FunctionHandler._infer_type_arg_from_value(arg_val, Gen) + type_args.append(type_str) + else: + # 实参未产生 LLVM 值 ⇒ 无法推导其类型实参,禁止按 int 兜底 + raise Exception( + f"泛型函数 '{FuncName}' 调用无法从第 {i + 1} 个实参推导类型实参;" + f"禁止按 int 兜底。") + if len(type_args) < len(type_param_names): + raise Exception( + f"泛型函数 '{FuncName}' 调用无法推导全部类型实参:" + f"需要 {len(type_param_names)} 个,仅推导出 {len(type_args)} 个;" + f"禁止按 int 兜底。") + type_args = type_args[:len(type_param_names)] + spec_name = self.Trans.FunctionHandler._specialize_generic_function(FuncName, type_args, Gen) + if spec_name is None: + return None + mangled_spec = Gen._mangle_func_name(spec_name) + func = None + if mangled_spec in Gen.functions: + func = Gen.functions[mangled_spec] + elif spec_name in Gen.functions: + func = Gen.functions[spec_name] + else: + for fname, fobj in Gen.functions.items(): + if spec_name in fname or fname.endswith(spec_name): + func = fobj + break + if func is None: + return None + if not CallArgs: + CallArgs = [ir.Constant(ir.IntType(32), 0) for _ in func.args] + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted = Gen._adjust_args(CallArgs, func) + return Gen.builder.call(func, adjusted, name=f"call_{spec_name}") + + def _HandleGenericClassNewLlvm(self, Node: ast.Call, ClassName: str, explicit_slice: ast.AST | None = None) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + template: dict = self.Trans.ClassHandler._generic_class_templates[ClassName] + type_param_names: list[str] = template['type_params'] + type_args: list[str] = [] + type_names: list[str] = [] + # 如果有显式类型参数 (如 list[int](mb)),直接从 slice 提取 + if explicit_slice is not None: + slice_nodes: list[ast.AST] = [] + if isinstance(explicit_slice, ast.Tuple): + slice_nodes = list(explicit_slice.elts) + else: + slice_nodes = [explicit_slice] + for sn in slice_nodes: + result = self._resolve_generic_slice_type(sn) + if result is not None: + type_args.append(result[0]) + type_names.append(result[1]) + # 无法解析全部类型实参时禁止按 int 兜底(否则特化出错误元素类型的类) + if len(type_args) < len(type_param_names): + raise Exception( + f"泛型类 '{ClassName}' 的显式类型实参无法全部解析:" + f"需要 {len(type_param_names)} 个,仅解析出 {len(type_args)} 个;" + f"禁止按 int 兜底。") + spec_name = self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names) + if spec_name is None: + return ir.Constant(ir.IntType(32), 0) + return self._HandleClassNewLlvm(Node, spec_name) + all_inferred: list[tuple[str, str]] = [] + for i, arg_node in enumerate(Node.args): + type_str = 'int' + type_name = 'CInt' + if isinstance(arg_node, ast.Constant): + if isinstance(arg_node.value, float): + type_str = 'double' + type_name = 'CDouble' + elif isinstance(arg_node.value, int): + type_str = 'int' + type_name = 'CInt' + elif isinstance(arg_node.value, bool): + type_str = 'i8' + type_name = 'CBool' + elif isinstance(arg_node, ast.Call): + if isinstance(arg_node.func, ast.Name): + func_id = arg_node.func.id + if func_id == 'float': + type_str = 'double' + type_name = 'CDouble' + elif func_id == 'int': + type_str = 'int' + type_name = 'CInt' + elif func_id == 'char': + # char() 内建 i8 强转(与 float()/double() 并列) + type_str = 'i8' + type_name = 'CChar' + else: + float_types = {'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T'} + int_types = {'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CShort', 'CLong'} + if func_id in float_types: + type_str = 'double' + type_name = func_id + elif func_id == 'CChar': + type_str = 'i8' + type_name = func_id + elif func_id in int_types: + type_str = 'int' + type_name = func_id + elif isinstance(arg_node.func, ast.Attribute): + attr_name = arg_node.func.attr + float_types = {'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T'} + int_types = {'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CShort', 'CLong'} + if attr_name in float_types: + type_str = 'double' + type_name = attr_name + elif attr_name == 'CChar': + type_str = 'i8' + type_name = attr_name + elif attr_name in int_types: + type_str = 'int' + type_name = attr_name + elif isinstance(arg_node, ast.Name): + var_info = self.Trans.SymbolTable.lookup(arg_node.id) + if var_info and isinstance(var_info, dict): + var_type = var_info.get('type', '') + if var_type in ('double', 'float'): + type_str = 'double' + type_name = 'CDouble' + elif var_type in ('char', 'i8', 'CChar'): + type_str = 'i8' + type_name = 'CChar' + all_inferred.append((type_str, type_name)) + hint_types = [(ts, tn) for ts, tn in all_inferred if tn != 'CInt' or ts != 'int'] + for i in range(len(type_param_names)): + if i < len(hint_types): + type_args.append(hint_types[i][0]) + type_names.append(hint_types[i][1]) + elif i < len(all_inferred): + type_args.append(all_inferred[i][0]) + type_names.append(all_inferred[i][1]) + else: + # 第 i 个类型参数既无对应实参也无法推断 ⇒ 禁止按 int 兜底 + raise Exception( + f"泛型类 '{ClassName}' 构造调用无法为第 {i + 1} 个类型参数推导实参;" + f"禁止按 int 兜底。") + spec_name = self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names) + if spec_name is None: + return ir.Constant(ir.IntType(32), 0) + return self._HandleClassNewLlvm(Node, spec_name) + diff --git a/lib/core/LlvmCodeGenerator.py b/lib/core/LlvmCodeGenerator.py index b09db65..012a334 100644 --- a/lib/core/LlvmCodeGenerator.py +++ b/lib/core/LlvmCodeGenerator.py @@ -38,7 +38,11 @@ class LlvmCodeGenerator( """ #builder: ir.IRBuilder = None #_Trans: LlvmGeneratorMixin = None - pass + + # `new memgr(x) T(args)` 的存储覆盖:_HandleNewExprLlvm 取好池存储(T*)后置入, + # _HandleClassNewLlvm / _HandleREnumConstructLlvm 有则复用之(否则各自 alloca)。 + # 默认 None = 无覆盖;构造结束由 try/finally 复位。 + NewStorageOverride: object = None # 向后兼容:重新导出常量和辅助类 diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index 5be8ca0..2d2d37b 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -1,218 +1,218 @@ -"""共享工具函数:符号表处理中使用的 AST 分析工具""" -from __future__ import annotations -import io -import tokenize -import vast as ast -import re -from lib.includes import t -from lib.includes.t import CTypeRegistry - -_T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__}) - -# Viper 软关键字 fn/fun/func/function 是 def 的别名(行首 + 空白 + 标识符才算函数定义, -# 变量名/调用/注解中的 fn 不受影响)。展开为 def 后行号不变,供 ast.parse 使用。 -_SOFT_KEYWORD_RE: re.Pattern[str] = re.compile( - r'^(\s*)(function|func|fun|fn)(\s+)(?=[A-Za-z_])', re.MULTILINE -) - -# enum 语句(Viper 语法糖):`enum Name:` + 成员行,与 -# `class Name(t.CEnum):` **完全等价**(同一实现路径——展开后再解析, -# 下游 EnumTable/符号表注册只看 ClassDef + t.CEnum 基类,不存在第二套枚举实现)。 -# 头行 enum Name: → class Name(t.CEnum): -# 裸成员 Name → Name: t.State(默认 i32;必须落为 AnnAssign, -# 否则 Enum/REnum 成员收集按 Assign/AnnAssign 过滤会漏掉裸名语句) -# 带注解 Name: T → 原样保留(自定义具体类型) -# 带值 Name = 0 → 原样保留(Assign 形态,两侧枚举收集均支持) +"""共享工具函数:符号表处理中使用的 AST 分析工具""" +from __future__ import annotations +import io +import tokenize +import vast as ast +import re +from lib.includes import t +from lib.includes.t import CTypeRegistry + +_T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__}) + +# Viper 软关键字 fn/fun/func/function 是 def 的别名(行首 + 空白 + 标识符才算函数定义, +# 变量名/调用/注解中的 fn 不受影响)。展开为 def 后行号不变,供 ast.parse 使用。 +_SOFT_KEYWORD_RE: re.Pattern[str] = re.compile( + r'^(\s*)(function|func|fun|fn)(\s+)(?=[A-Za-z_])', re.MULTILINE +) + +# enum 语句(Viper 语法糖):`enum Name:` + 成员行,与 +# `class Name(t.CEnum):` **完全等价**(同一实现路径——展开后再解析, +# 下游 EnumTable/符号表注册只看 ClassDef + t.CEnum 基类,不存在第二套枚举实现)。 +# 头行 enum Name: → class Name(t.CEnum): +# 裸成员 Name → Name: t.State(默认 i32;必须落为 AnnAssign, +# 否则 Enum/REnum 成员收集按 Assign/AnnAssign 过滤会漏掉裸名语句) +# 带注解 Name: T → 原样保留(自定义具体类型) +# 带值 Name = 0 → 原样保留(Assign 形态,两侧枚举收集均支持) # 多名行 A, B, C → A: t.State; B: t.State; C: t.State(同行分号分隔,见下; -# 可带尾随逗号 A, B,,与 TPV 的 AST 口径一致) -_ENUM_HEAD_RE: re.Pattern[str] = re.compile( - r'^(\s*)enum(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' -) -_ENUM_MEMBER_BARE_RE: re.Pattern[str] = re.compile( - r'^(\s*)([A-Za-z_]\w*)([ \t]*(?:#.*)?)$' -) -# 逗号分组成员行(`Add, Sub` / `A, B, C` / 尾随逗号 `A, B,`):与"每个名字各占一行"等价。 -# 只匹配"纯标识符列表 + 可选尾随逗号 + 可选注释",故 `A, B = 1`(Assign)、 -# `A, B: int`(带注解)与 `f(a, b)`(调用)都不命中,原样保留(由注册处的 -# EnumBodyMemberNames 兜底判据 fail-fast,不再静默丢成员)。 -# 尾随逗号必须支持:TPV 侧走 AST(`class X(t.CEnum): A, B,` 与 `A, B` 在 AST 上 -# 完全同形,无法区分),TPC 若在此拒绝会造成"同源两种编译器两种结果"。group(3) -# 只收注释正文(不含前导空白/逗号),展开时另行补一个空格。 -_ENUM_MEMBER_LIST_RE: re.Pattern[str] = re.compile( - r'^(\s*)([A-Za-z_]\w*(?:[ \t]*,[ \t]*[A-Za-z_]\w*)+)[ \t]*,?[ \t]*((?:#.*)?)$' -) -# 裸名语句保留集:这些行是语句而非枚举成员,展开时不得追加 `: t.State` -_ENUM_MEMBER_SKIP: frozenset[str] = frozenset({ - 'pass', 'return', 'break', 'continue', 'global', 'nonlocal', 'raise', - 'yield', 'import', 'from', 'del', 'assert', 'if', 'while', 'for', 'with', - 'try', 'match', 'case', 'lambda', 'def', 'class', 'enum', - 'None', 'True', 'False', -}) - - -# --------------------------------------------------------------- -# `_AttributeMarkerText` 的属性实参形态判定正则**预编译**(性能:避免每次 -# `re.fullmatch`/`re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。 -# 语义与逐字写法完全一致,模式串一字未改)。 -# --------------------------------------------------------------- -# 裸标识符(`always_inline` 形态) -_RE_ATTR_BARE_IDENT: re.Pattern[str] = re.compile(r'[A-Za-z_][A-Za-z0-9_]*') -# 标识符后紧跟调用括号(`section(...)` 形态) -_RE_ATTR_CALL_IDENT: re.Pattern[str] = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*\s*\(') - - -def _ExpandEnumBlocks(Code: str) -> str: - """把 `enum Name:` 块展开为等价的 `class Name(t.CEnum):` 块(行号不变)。 - - 只在"行首缩进 + enum + 空白 + 标识符 + 冒号(行尾)"的枚举头形态触发: - `enum = 5` / `import enum` / `x.enum` / 注解 `x: enum` 均不匹配。 - 成员行只对块内"更深缩进"的行做展开,块内空行与注释行原样保留; - 缩进回到枚举头同级(或更浅)即块结束。全程不增删换行,行号映射保持有效。 - - 成员行两种形态:单名(`Name`)追加 `: t.State`;逗号分组多名(`Add, Sub`) - 在同一行内展开为分号分隔的多个 `Name: t.State`(简单语句,`vast` 与 Python - 均按多个 AnnAssign 解析)。未展开的一行多名语句会被当作元组表达式,下游 - 枚举成员收集(仅认 Assign/AnnAssign)会静默漏掉这些成员。 - """ - lines: list[str] = Code.splitlines(keepends=True) - out: list[str] = [] - i: int = 0 - n: int = len(lines) - while i < n: - raw: str = lines[i] - # 行终止符单独拆出(兼容 LF / CRLF / CR):正文参与匹配,终止符原样回写, - # 保证"不增删换行、行号不变"。CRLF 源(本仓 .vp 大量为 CRLF,但 TPC 各 - # 调用点多为文本模式读入已被通用换行归一,双保险)若不做此拆分, - # 行尾 '\r' 会让 `$` 锚定的头行正则失配 → 静默不展开。 - k: int = len(raw) - while k > 0 and (raw[k - 1] == '\n' or raw[k - 1] == '\r'): - k -= 1 - body: str = raw[:k] - term: str = raw[k:] - head = _ENUM_HEAD_RE.match(body) - if head is None: - out.append(raw) - i += 1 - continue - indent: str = head.group(1) - out.append(f'{indent}class {head.group(3)}(t.CEnum):{head.group(5)}{term}') - i += 1 - while i < n: - raw2: str = lines[i] - k2: int = len(raw2) - while k2 > 0 and (raw2[k2 - 1] == '\n' or raw2[k2 - 1] == '\r'): - k2 -= 1 - body2: str = raw2[:k2] - term2: str = raw2[k2:] - stripped: str = body2.strip() - if stripped == '' or stripped.startswith('#'): - out.append(raw2) - i += 1 - continue - if len(body2) - len(body2.lstrip()) <= len(indent): - break # 缩进回到枚举头同级:块结束(该行留给外层循环) - member = _ENUM_MEMBER_BARE_RE.match(body2) - if member is not None and member.group(2) not in _ENUM_MEMBER_SKIP: - out.append(f'{member.group(1)}{member.group(2)}: t.State{member.group(3)}{term2}') - else: - # 逗号分组成员行:同一行展开为"分号分隔的多个 AnnAssign"。 - # `Name: t.State` 是简单语句,分号分隔在 Python 与 vast 中均合法 - # ⇒ 与单名成员行产出同一批 AnnAssign(枚举成员/宽度/编号全部一致), - # 且不增删换行,行号映射保持有效。group(3) 是注释正文(可为空), - # 需要时补一个空格再接回(尾随逗号已在正则里吃掉,不参与拼接)。 - member_list = _ENUM_MEMBER_LIST_RE.match(body2) - names: list[str] = ([nm.strip() for nm in member_list.group(2).split(',')] - if member_list is not None else []) - if names and all(nm not in _ENUM_MEMBER_SKIP for nm in names): - joined: str = '; '.join(f'{nm}: t.State' for nm in names) - tail: str = member_list.group(3) - comment: str = f' {tail}' if tail else '' - out.append(f'{member_list.group(1)}{joined}{comment}{term2}') - else: - out.append(raw2) - i += 1 +# 可带尾随逗号 A, B,,与 VPC 的 AST 口径一致) +_ENUM_HEAD_RE: re.Pattern[str] = re.compile( + r'^(\s*)enum(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' +) +_ENUM_MEMBER_BARE_RE: re.Pattern[str] = re.compile( + r'^(\s*)([A-Za-z_]\w*)([ \t]*(?:#.*)?)$' +) +# 逗号分组成员行(`Add, Sub` / `A, B, C` / 尾随逗号 `A, B,`):与"每个名字各占一行"等价。 +# 只匹配"纯标识符列表 + 可选尾随逗号 + 可选注释",故 `A, B = 1`(Assign)、 +# `A, B: int`(带注解)与 `f(a, b)`(调用)都不命中,原样保留(由注册处的 +# EnumBodyMemberNames 兜底判据 fail-fast,不再静默丢成员)。 +# 尾随逗号必须支持:VPC 侧走 AST(`class X(t.CEnum): A, B,` 与 `A, B` 在 AST 上 +# 完全同形,无法区分),TPC 若在此拒绝会造成"同源两种编译器两种结果"。group(3) +# 只收注释正文(不含前导空白/逗号),展开时另行补一个空格。 +_ENUM_MEMBER_LIST_RE: re.Pattern[str] = re.compile( + r'^(\s*)([A-Za-z_]\w*(?:[ \t]*,[ \t]*[A-Za-z_]\w*)+)[ \t]*,?[ \t]*((?:#.*)?)$' +) +# 裸名语句保留集:这些行是语句而非枚举成员,展开时不得追加 `: t.State` +_ENUM_MEMBER_SKIP: frozenset[str] = frozenset({ + 'pass', 'return', 'break', 'continue', 'global', 'nonlocal', 'raise', + 'yield', 'import', 'from', 'del', 'assert', 'if', 'while', 'for', 'with', + 'try', 'match', 'case', 'lambda', 'def', 'class', 'enum', + 'None', 'True', 'False', +}) + + +# --------------------------------------------------------------- +# `_AttributeMarkerText` 的属性实参形态判定正则**预编译**(性能:避免每次 +# `re.fullmatch`/`re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。 +# 语义与逐字写法完全一致,模式串一字未改)。 +# --------------------------------------------------------------- +# 裸标识符(`always_inline` 形态) +_RE_ATTR_BARE_IDENT: re.Pattern[str] = re.compile(r'[A-Za-z_][A-Za-z0-9_]*') +# 标识符后紧跟调用括号(`section(...)` 形态) +_RE_ATTR_CALL_IDENT: re.Pattern[str] = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*\s*\(') + + +def _ExpandEnumBlocks(Code: str) -> str: + """把 `enum Name:` 块展开为等价的 `class Name(t.CEnum):` 块(行号不变)。 + + 只在"行首缩进 + enum + 空白 + 标识符 + 冒号(行尾)"的枚举头形态触发: + `enum = 5` / `import enum` / `x.enum` / 注解 `x: enum` 均不匹配。 + 成员行只对块内"更深缩进"的行做展开,块内空行与注释行原样保留; + 缩进回到枚举头同级(或更浅)即块结束。全程不增删换行,行号映射保持有效。 + + 成员行两种形态:单名(`Name`)追加 `: t.State`;逗号分组多名(`Add, Sub`) + 在同一行内展开为分号分隔的多个 `Name: t.State`(简单语句,`vast` 与 Python + 均按多个 AnnAssign 解析)。未展开的一行多名语句会被当作元组表达式,下游 + 枚举成员收集(仅认 Assign/AnnAssign)会静默漏掉这些成员。 + """ + lines: list[str] = Code.splitlines(keepends=True) + out: list[str] = [] + i: int = 0 + n: int = len(lines) + while i < n: + raw: str = lines[i] + # 行终止符单独拆出(兼容 LF / CRLF / CR):正文参与匹配,终止符原样回写, + # 保证"不增删换行、行号不变"。CRLF 源(本仓 .vp 大量为 CRLF,但 TPC 各 + # 调用点多为文本模式读入已被通用换行归一,双保险)若不做此拆分, + # 行尾 '\r' 会让 `$` 锚定的头行正则失配 → 静默不展开。 + k: int = len(raw) + while k > 0 and (raw[k - 1] == '\n' or raw[k - 1] == '\r'): + k -= 1 + body: str = raw[:k] + term: str = raw[k:] + head = _ENUM_HEAD_RE.match(body) + if head is None: + out.append(raw) + i += 1 + continue + indent: str = head.group(1) + out.append(f'{indent}class {head.group(3)}(t.CEnum):{head.group(5)}{term}') + i += 1 + while i < n: + raw2: str = lines[i] + k2: int = len(raw2) + while k2 > 0 and (raw2[k2 - 1] == '\n' or raw2[k2 - 1] == '\r'): + k2 -= 1 + body2: str = raw2[:k2] + term2: str = raw2[k2:] + stripped: str = body2.strip() + if stripped == '' or stripped.startswith('#'): + out.append(raw2) + i += 1 + continue + if len(body2) - len(body2.lstrip()) <= len(indent): + break # 缩进回到枚举头同级:块结束(该行留给外层循环) + member = _ENUM_MEMBER_BARE_RE.match(body2) + if member is not None and member.group(2) not in _ENUM_MEMBER_SKIP: + out.append(f'{member.group(1)}{member.group(2)}: t.State{member.group(3)}{term2}') + else: + # 逗号分组成员行:同一行展开为"分号分隔的多个 AnnAssign"。 + # `Name: t.State` 是简单语句,分号分隔在 Python 与 vast 中均合法 + # ⇒ 与单名成员行产出同一批 AnnAssign(枚举成员/宽度/编号全部一致), + # 且不增删换行,行号映射保持有效。group(3) 是注释正文(可为空), + # 需要时补一个空格再接回(尾随逗号已在正则里吃掉,不参与拼接)。 + member_list = _ENUM_MEMBER_LIST_RE.match(body2) + names: list[str] = ([nm.strip() for nm in member_list.group(2).split(',')] + if member_list is not None else []) + if names and all(nm not in _ENUM_MEMBER_SKIP for nm in names): + joined: str = '; '.join(f'{nm}: t.State' for nm in names) + tail: str = member_list.group(3) + comment: str = f' {tail}' if tail else '' + out.append(f'{member_list.group(1)}{joined}{comment}{term2}') + else: + out.append(raw2) + i += 1 return ''.join(out) -# Viper soft keyword `union` (statement-level reserved word): `union X:` is exactly -# `class X(t.CUnion):` (same desugar target as TPV includes/ast/parser.vp:_parse_uniondef). -# Unlike enum, union members are ordinary annotated fields, so no member rewriting. -# `union` is not used as an identifier anywhere in .vp sources. -_UNION_HEAD_RE: re.Pattern[str] = re.compile( - r'^(\s*)union(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' -) -_UNION_PREFIX_RE: re.Pattern[str] = re.compile(r'^(\s*)union\b') - - -def _ExpandUnionBlocks(Code: str) -> str: - """Expand `union Name:` into `class Name(t.CUnion):` (same-line, line numbers kept). - - Mirrors TPV _parse_uniondef: the body is ordinary AnnAssign fields, so only the - head line is rewritten. A line starting with `union` that is not the exact - `union Name:` form fails loudly (union is never an identifier in .vp sources). - """ - if 'union' not in Code: - return Code - out: list[str] = [] - for Lineno, raw in enumerate(Code.splitlines(keepends=True), 1): - k: int = len(raw) - while k > 0 and (raw[k - 1] == '\n' or raw[k - 1] == '\r'): - k -= 1 - body: str = raw[:k] - term: str = raw[k:] - head = _UNION_HEAD_RE.match(body) - if head is not None: - out.append(f'{head.group(1)}class {head.group(3)}(t.CUnion):{head.group(5)}{term}') - continue - if _UNION_PREFIX_RE.match(body) is not None: - raise SyntaxError( - f"union 语法错误(行 {Lineno},原文: {body.strip()!r}):应为 `union 名字:`" - f"(等价于 `class 名字(t.CUnion):`)。union 是语句级保留字,成员必须写成 " - f"`名字: 类型`;非法形态一律终止编译(非静默)。" - ) - out.append(raw) - return ''.join(out) - - -# Viper soft keyword `renum` (statement-level reserved word): `renum X:` is exactly -# `class X(t.REnum):` (same desugar target as TPV includes/ast/parser.vp:_parse_renumdef). -# REnum members are ordinary class-body statements (nested `class V:` variants and -# bare assignments like `None_ = 0`), so only the head line is rewritten. -# `renum` is not used as an identifier anywhere in .vp sources. -_RENUM_HEAD_RE: re.Pattern[str] = re.compile( - r'^(\s*)renum(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' -) -_RENUM_PREFIX_RE: re.Pattern[str] = re.compile(r'^(\s*)renum\b') - - -def _ExpandRenumBlocks(Code: str) -> str: - """Expand `renum Name:` into `class Name(t.REnum):` (same-line, line numbers kept). - - Mirrors TPV _parse_renumdef: the body is an ordinary class body (variant classes - written as nested `class V:` plus payload-less assignments such as `None_ = 0`), - so only the head line is rewritten. A line starting with `renum` that is not the - exact `renum Name:` form fails loudly (renum is never an identifier in .vp sources). - """ - if 'renum' not in Code: - return Code - out: list[str] = [] - for Lineno, raw in enumerate(Code.splitlines(keepends=True), 1): - k: int = len(raw) - while k > 0 and (raw[k - 1] == '\n' or raw[k - 1] == '\r'): - k -= 1 - body: str = raw[:k] - term: str = raw[k:] - head = _RENUM_HEAD_RE.match(body) - if head is not None: - out.append(f'{head.group(1)}class {head.group(3)}(t.REnum):{head.group(5)}{term}') - continue - if _RENUM_PREFIX_RE.match(body) is not None: - raise SyntaxError( - f"renum 语法错误(行 {Lineno},原文: {body.strip()!r}):应为 `renum 名字:`" - f"(等价于 `class 名字(t.REnum):`)。renum 是语句级保留字,成员写成嵌套 " - f"`class 变体:` 或无载荷赋值(如 `None_ = 0`);非法形态一律终止编译(非静默)。" - ) - out.append(raw) +# Viper soft keyword `union` (statement-level reserved word): `union X:` is exactly +# `class X(t.CUnion):` (same desugar target as VPC includes/ast/parser.vp:_parse_uniondef). +# Unlike enum, union members are ordinary annotated fields, so no member rewriting. +# `union` is not used as an identifier anywhere in .vp sources. +_UNION_HEAD_RE: re.Pattern[str] = re.compile( + r'^(\s*)union(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' +) +_UNION_PREFIX_RE: re.Pattern[str] = re.compile(r'^(\s*)union\b') + + +def _ExpandUnionBlocks(Code: str) -> str: + """Expand `union Name:` into `class Name(t.CUnion):` (same-line, line numbers kept). + + Mirrors VPC _parse_uniondef: the body is ordinary AnnAssign fields, so only the + head line is rewritten. A line starting with `union` that is not the exact + `union Name:` form fails loudly (union is never an identifier in .vp sources). + """ + if 'union' not in Code: + return Code + out: list[str] = [] + for Lineno, raw in enumerate(Code.splitlines(keepends=True), 1): + k: int = len(raw) + while k > 0 and (raw[k - 1] == '\n' or raw[k - 1] == '\r'): + k -= 1 + body: str = raw[:k] + term: str = raw[k:] + head = _UNION_HEAD_RE.match(body) + if head is not None: + out.append(f'{head.group(1)}class {head.group(3)}(t.CUnion):{head.group(5)}{term}') + continue + if _UNION_PREFIX_RE.match(body) is not None: + raise SyntaxError( + f"union 语法错误(行 {Lineno},原文: {body.strip()!r}):应为 `union 名字:`" + f"(等价于 `class 名字(t.CUnion):`)。union 是语句级保留字,成员必须写成 " + f"`名字: 类型`;非法形态一律终止编译(非静默)。" + ) + out.append(raw) + return ''.join(out) + + +# Viper soft keyword `renum` (statement-level reserved word): `renum X:` is exactly +# `class X(t.REnum):` (same desugar target as VPC includes/ast/parser.vp:_parse_renumdef). +# REnum members are ordinary class-body statements (nested `class V:` variants and +# bare assignments like `None_ = 0`), so only the head line is rewritten. +# `renum` is not used as an identifier anywhere in .vp sources. +_RENUM_HEAD_RE: re.Pattern[str] = re.compile( + r'^(\s*)renum(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' +) +_RENUM_PREFIX_RE: re.Pattern[str] = re.compile(r'^(\s*)renum\b') + + +def _ExpandRenumBlocks(Code: str) -> str: + """Expand `renum Name:` into `class Name(t.REnum):` (same-line, line numbers kept). + + Mirrors VPC _parse_renumdef: the body is an ordinary class body (variant classes + written as nested `class V:` plus payload-less assignments such as `None_ = 0`), + so only the head line is rewritten. A line starting with `renum` that is not the + exact `renum Name:` form fails loudly (renum is never an identifier in .vp sources). + """ + if 'renum' not in Code: + return Code + out: list[str] = [] + for Lineno, raw in enumerate(Code.splitlines(keepends=True), 1): + k: int = len(raw) + while k > 0 and (raw[k - 1] == '\n' or raw[k - 1] == '\r'): + k -= 1 + body: str = raw[:k] + term: str = raw[k:] + head = _RENUM_HEAD_RE.match(body) + if head is not None: + out.append(f'{head.group(1)}class {head.group(3)}(t.REnum):{head.group(5)}{term}') + continue + if _RENUM_PREFIX_RE.match(body) is not None: + raise SyntaxError( + f"renum 语法错误(行 {Lineno},原文: {body.strip()!r}):应为 `renum 名字:`" + f"(等价于 `class 名字(t.REnum):`)。renum 是语句级保留字,成员写成嵌套 " + f"`class 变体:` 或无载荷赋值(如 `None_ = 0`);非法形态一律终止编译(非静默)。" + ) + out.append(raw) return ''.join(out) @@ -288,7 +288,7 @@ def _ExpandDefineConsts(Code: str) -> str: # 语义:与手写 `T1: t.CTypedef = T2` 落到**同一条** typedef 注册/查找路径,不新增 # 第二套实现(TPC:符号表 SymbolTypeResolver._resolve_ctypedef_typedef → # _resolve_typedef_from_value;翻译期 HandlesAnnAssign / DeclarationGenerator 的 -# typedef 表。TPV:AssignGenMixin.translate_annassign 的 CTypedef 分支 → +# typedef 表。VPC:AssignGenMixin.translate_annassign 的 CTypedef 分支 → # TypeGenCType.register_ctypedef)。RHS 即"类型表达式"(书写在值位): # 裸名 `int32` / `t.X` / 并集 `t.CChar | t.CPtr` / 泛型 `array[E, N]` / 链式 `A = B`。 # @@ -364,7 +364,7 @@ def _ExpandTypedefs(Code: str) -> str: # (与全仓既有真实写法一致:includes/stdio.vp:4 `fmt: t.CConst | str`、 # includes/spinlock.vp:6 `locked: t.CVolatile | int`、 # ViperApps/ViperOS/VKernel/Kernel/main.vp:301 `t.CConst | int | t.CPtr`)。 -# - TPV:includes/ast/parser.vp 的 `_parse_type_ann` 合成**同形** AST +# - VPC:includes/ast/parser.vp 的 `_parse_type_ann` 合成**同形** AST # (BinOp(BitOr, Attribute(t,'CConst'), <类型>)),产出与手写形态逐字段同形。 # # 软关键字边界(唯一识别面 = "注解位置 + token 相邻",与 array 那次同口径): @@ -376,7 +376,7 @@ def _ExpandTypedefs(Code: str) -> str: # (见 _ExpandExportExternVar 的 static 前置分支与 vast.Parser._ParseStaticDef); # 旧注解位置一律**响亮失败**(见下),不再脱糖为 `t.CStatic | T`。 # 非类型位置**原样保留**为普通标识符(不做词法保留字): -# - `t.attr.const()`(TransPyV/Test/App/attr_test.vp:142,全仓 .vp 代码内唯一出现) +# - `t.attr.const()`(ViperC/Test/App/attr_test.vp:142,全仓 .vp 代码内唯一出现) # - `const = 5` / `x = const` / `d = {"k": const}` # 注释与字符串内容**不参与**:本层用 `tokenize` 做词法切分(不是行正则), # 故 openssl/*.vp 等 C 原型注释里的 `const char *`(全仓 275 处)逐字不变。 @@ -386,7 +386,7 @@ def _ExpandTypedefs(Code: str) -> str: # **注解位置的 `static`**(如 `memory_regions: static memory_region* = None`): # 已迁移为前置形式,旧写法一律终止编译。 # 报文含 行:列 + 源码片段 + 期望形态 + 正确示例(文件名由调用方的 SyntaxError -# 上报路径补全,与 _ExpandTypedefs / TPV dump_parse_traceback 同约定)。 +# 上报路径补全,与 _ExpandTypedefs / VPC dump_parse_traceback 同约定)。 _TYPE_DESCRIPTOR_MARKERS: dict[str, str] = { 'const': 'CConst', 'volatile': 'CVolatile', } @@ -529,7 +529,7 @@ def _InStaticOldAnnotationPosition(Tokens: list, Index: int) -> bool: # C-style leading array declarator `const [N]T`: textual desugar into -# `t.CArray[T, N]` (same shape as TPV includes/ast/parser.vp:_parse_array_prefix_declarator). +# `t.CArray[T, N]` (same shape as VPC includes/ast/parser.vp:_parse_array_prefix_declarator). _TYPE_BRACKET_OPENERS: frozenset[str] = frozenset({'(', '[', '{'}) _TYPE_BRACKET_CLOSERS: frozenset[str] = frozenset({')', ']', '}'}) # Annotation type expression ends at these top-level operators. @@ -626,7 +626,7 @@ def _DesugarArrayPrefix(Tokens: list, BracketIdx: int, Count: int, # Bare C-style leading array declarator `[N]T` / `[]T` at annotation positions (no # descriptor), desugared to `t.CArray[T, N]` / `t.CArray[T, None]` — same shape as the -# const/volatile path and TPV _parse_array_prefix_declarator. +# const/volatile path and VPC _parse_array_prefix_declarator. _ARRAY_PREFIX_HINT: str = ( '`名字: [长度]元素类型`(长度可省略:`[]元素类型`);' '形参 / 返回值写作 `def f(p: [N]T) -> [M]U`' @@ -788,7 +788,7 @@ def _ExpandArrayPrefixAnnotations(Code: str) -> str: # --------------------------------------------------------------------------- -# 变量声明的存储类修饰符 export / extern / static(语句级软关键字)——与 TPV +# 变量声明的存储类修饰符 export / extern / static(语句级软关键字)——与 VPC # includes/ast/parser.vp **同一标记、同一消费路径**: # export 名字: T [= 初值] → 名字: T | t.CExport [= 初值] # (本模块**定义**:定义端裸名,不加模块 SHA1 头;内联汇编 / C 符号对接必需。 @@ -797,7 +797,7 @@ def _ExpandArrayPrefixAnnotations(Code: str) -> str: # (**仅声明**:t.State = t.CExtern | t.CExport ⇒ 定义端 `@裸名 = external global T`。 # extern 是外部定义,**不允许**初值 ⇒ 带 `= 初值` 一律终止编译。) # static 名字: T [= 初值] → 名字: t.CStatic | T [= 初值] -# (存储类标记**前置**在类型联合最左,与 TPV `_ParseStaticModified` 的 +# (存储类标记**前置**在类型联合最左,与 VPC `_ParseStaticModified` 的 # `pending_static` 通道同源——`static 名字: T` → `名字: t.CStatic | T`;允许初值。 # 产出与手写 `名字: t.CStatic | T` / 装饰器 `@t.CStatic` 逐字段同形。 # 旧注解位置写法 `名字: static T` 已删除,见 _StaticPositionSyntaxError。) @@ -832,9 +832,9 @@ def _ExpandExportExternVar(Code: str) -> str: """把 `export 名字: T [= 初值]` / `extern 名字: T` / `static 名字: T [= 初值]` 展开为带存储类标记的注解。 - 与 TPV 同口径:export → 类型**右侧追加** `| t.CExport`;extern → 追加 `| t.State` + 与 VPC 同口径:export → 类型**右侧追加** `| t.CExport`;extern → 追加 `| t.State` (extern 带初值 ⇒ **响亮失败** SyntaxError);static → 类型**左侧前置** `t.CStatic | ` - (与 TransPyV `_ParseStaticModified` / `pending_static` 通道同源——`static 名字: T` + (与 ViperC `_ParseStaticModified` / `pending_static` 通道同源——`static 名字: T` → `名字: t.CStatic | T`,亦与手写 `名字: t.CStatic | T` / `@t.CStatic` 逐字段同形; static **允许**初值)。 @@ -892,7 +892,7 @@ def _ExpandExportExternVar(Code: str) -> str: Edits.append((Line, DelStart, DelEnd, '', DelOrig)) if IsStatic: # 编辑 2(static):存储类标记**前置**在类型左侧(联合最左)。 - # 与 TransPyV `_ParseStaticModified` / `pending_static` 通道 + # 与 ViperC `_ParseStaticModified` / `pending_static` 通道 # 逐字同形:`static 名字: T` → `名字: t.CStatic | T`,产出与 # 手写 `名字: t.CStatic | T` / `@t.CStatic` 逐字段同形。 InsAt: tuple[int, int] = Tokens[TypeStart].start @@ -912,7 +912,7 @@ def _ExpandExportExternVar(Code: str) -> str: # --------------------------------------------------------------------------- -# 注解位置的 C 风格 `__attribute__(...)` 描述符(与 TPV +# 注解位置的 C 风格 `__attribute__(...)` 描述符(与 VPC # includes/ast/parser.vp 的 _IsMagicAttributeCall / _MakeTAttrMarker 同源书写法, # TPC 侧在本层文本脱糖;与 const/volatile、`[N]T` 数组前缀同属一套语法糖): # V: __attribute__(section(".PRVERSN")) []char = "0.0.3-dev.428" @@ -940,7 +940,7 @@ def _AttributeSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> S f"源码片段 {Snippet.strip()!r}。" f"`__attribute__(...)` 只在**注解位置**生效(AnnAssign 的 ':' 之后 / 形参 / " f"'->' 之后 / 类字段),期望形态:{_ATTRIBUTE_DESCRIPTOR_HINT};" - f"正确示例:`V_TPV_VERSION: __attribute__(section('.PRVERSN')) []char = \"x\"` / " + f"正确示例:`V_VPC_VERSION: __attribute__(section('.PRVERSN')) []char = \"x\"` / " f"`g: __attribute__(aligned(16)) int = 0`。" f"(注解位置的 __attribute__ 无法脱糖时一律终止编译,绝不静默留作普通调用)" ) @@ -964,7 +964,7 @@ def _MatchOpenBracket(Tokens: list, CloseIdx: int) -> int: def _AttributeMarkerText(ArgText: str) -> str: - """`__attribute__` 单个实参文本 → 描述符文本(与 TPV _MakeTAttrMarker 同口径)。 + """`__attribute__` 单个实参文本 → 描述符文本(与 VPC _MakeTAttrMarker 同口径)。 - 裸名 `always_inline` → `t.attr.always_inline` - 调用 `section(".text")` → `t.attr.section(".text")` @@ -983,7 +983,7 @@ def _AttributeMarkerText(ArgText: str) -> str: def _InAttributeAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: """`Index` 处的 `__attribute__` 是否处于注解位置的描述符序列中。 - 与 TPV `_IsMagicAttributeCall` + `_parse_type_ann` 的描述符循环同口径:允许 + 与 VPC `_IsMagicAttributeCall` + `_parse_type_ann` 的描述符循环同口径:允许 `x: __attribute__(...) T`、`def f(p: __attribute__(...) T)`、 `-> __attribute__(...) T`,以及 `const` / `volatile` / 前序 `__attribute__(...)` 之后(顺序自由、可混写)。表达式位置的普通调用 @@ -1206,7 +1206,7 @@ def _ExpandTypeDescriptors(Code: str) -> str: InAnnotationPosition = True elif Prev.string == '=' and _IsTypedefAnnotationRhs(Tokens, PrevIdx): # `LPCSTR: t.CTypedef = const str`:typedef 的右值也是类型位置 - # (与 TPV 的 _parse_type_ann 右值分支同口径)。 + # (与 VPC 的 _parse_type_ann 右值分支同口径)。 InAnnotationPosition = True if InAnnotationPosition: if Run[0].start[0] != Run[-1].end[0]: @@ -1218,7 +1218,7 @@ def _ExpandTypeDescriptors(Code: str) -> str: if Next is not None and Next.type == tokenize.OP and Next.string == '[': # `const [N]T` / `volatile [N]T` / `const volatile [N]T`: TPC vast has # no `[N]T`; desugar the array prefix together with the descriptor into - # `t.CConst | t.CArray[T, N]` (same shape as TPV). + # `t.CConst | t.CArray[T, N]` (same shape as VPC). Desugared = _DesugarArrayPrefix(Tokens, j, n, Code) if Desugared is not None: End, ArrayText = Desugared @@ -1273,7 +1273,7 @@ def _ExpandTypeDescriptors(Code: str) -> str: # --------------------------------------------------------------------------- -# 枚举存储宽度("最宽成员类型")——两侧(TPV ClassGenEnum / TPC HandlesClassDef) +# 枚举存储宽度("最宽成员类型")——两侧(VPC ClassGenEnum / TPC HandlesClassDef) # 的唯一同口径规则源,对照表见 Debugger/enum-semantics.md。 # --------------------------------------------------------------------------- # 规则: @@ -1282,15 +1282,15 @@ def _ExpandTypeDescriptors(Code: str) -> str: # `bool` / `t.CBool` → **8**(枚举侧口径):枚举存储必须是可按字节寻址的 # 整数类型,`i1` 不能作为独立枚举宽度;本口径只作用于"枚举成员注解" # 这一处,**不改** TPC 全局 `bool`→`i8`(LLVMTypeMapper.py:174)与 -# TPV 全局 `bool`→`i1`(TypeGenResolve.vp:2201-2203)的各自的全局语义。 +# VPC 全局 `bool`→`i1`(TypeGenResolve.vp:2201-2203)的各自的全局语义。 # TPC 侧 `bool` 经 BuiltinTypeMap → `t.CBool.Size == 8` 天然成立 # (t.CBool 同 8),故本文件不另加硬编码分支。 # 指针注解 → 在成员注册处明确报错(本文件与 ClassGenEnum 的 ptr 拦截) # 其余(无法解析 / 非整数:float/void/struct…)→ 视为"无整数成员" -# (不引入新的硬失败;TPV 侧更严,对"有注解但解析失败"是 fatal,差异已记录) +# (不引入新的硬失败;VPC 侧更严,对"有注解但解析失败"是 fatal,差异已记录) _ENUM_DEFAULT_INT_WIDTH: int = 32 -# State 标记名(与 TPV ClassGenEnum._is_state_annotation 同集合):该注解只是 +# State 标记名(与 VPC ClassGenEnum._is_state_annotation 同集合):该注解只是 # `t.CEnum` 成员的存在性标记,不携带宽度语义。 _ENUM_STATE_MARKER_NAMES: frozenset[str] = frozenset({'State'}) @@ -1375,7 +1375,7 @@ def EnumMemberAnnotationIntWidth(Annotation: object | None, `t.State` 标记或无注解 → None;整数类型(`int`/`char`/`C*` 家族 / builtins 与库 typedef)→ 其 `Size`;无法解析或非整数(float/void/struct…)→ None (默认 32,与改前行为一致,不引入新的硬失败)。指针形态由调用方在解析前 - 拦截(HandlesClassDef._EnumMemberAnnotationIsPtr / TPV 的 + 拦截(HandlesClassDef._EnumMemberAnnotationIsPtr / VPC 的 _enum_member_annotation_is_ptr),此处不重复判定。 解析顺序(同一规则源,供"有 SymbolTable 的定义端"与"无 SymbolTable 的 @@ -1442,10 +1442,10 @@ def EnumClassIntWidth(Body: list | None, Default: int = _ENUM_DEFAULT_INT_WIDTH) -> int: """枚举存储宽度 = **最宽成员类型**(bit);无"可解析为整数的成员" → Default(32)。 - 与 TPV ClassGenEnum._translate_enum_def 的 base_ty 同口径(`base_ty` 从 i32 + 与 VPC ClassGenEnum._translate_enum_def 的 base_ty 同口径(`base_ty` 从 i32 起步,被任何 `get_llvm_type_bits > 0` 的成员替换 ⇒ 等价于"成员位宽最大值, 无成员时 i32")。**不是** `max(默认 32, 成员位宽)`:该写法会把 `A: uint8` - 单成员的枚举钉在 i32,与 TPV 的 i8 分裂(本轮已统一为前者)。 + 单成员的枚举钉在 i32,与 VPC 的 i8 分裂(本轮已统一为前者)。 调用点(同一规则源,禁止各写一套): - 定义端:HandlesClassDef._RegisterEnumMembers → _EnumClassIntWidth @@ -1468,7 +1468,7 @@ def EnumClassIntWidth(Body: list | None, # --------------------------------------------------------------------------- # 枚举成员"可见名 vs 登记名"兜底判据——唯一规则源 -# (TPC HandlesClassDef._RegisterEnumMembers / TPV +# (TPC HandlesClassDef._RegisterEnumMembers / VPC # ClassGenEnum._translate_enum_def 在**注册处**共用同一口径) # # 背景:`enum` 成员行一旦出现"注册器不认的写法",成员会被**静默丢弃** @@ -1504,10 +1504,10 @@ def _EnumMemberNameNodes(Node: object) -> list[object]: def EnumBodyMemberNames(Body: list | None) -> list[tuple[str, object]]: """枚举类体内"源码可见的成员名"清单 `[(名字, 语句节点), …]`(按出现顺序)。 - 与 TPV `ClassGenEnum._enum_body_visible_names` / `_enum_target_visible_names` + 与 VPC `ClassGenEnum._enum_body_visible_names` / `_enum_target_visible_names` 同口径(同一判据的两侧实现)。调用点(注册处,登记完成后立即比对): - TPC:HandlesClassDef._RegisterEnumMembers - - TPV:ClassGenEnum._translate_enum_def + - VPC:ClassGenEnum._translate_enum_def """ Names: list[tuple[str, object]] = [] for Stmt in (Body or ()): @@ -1566,301 +1566,224 @@ def EnumMemberCountMismatchError(ClassName: str, Visible: list[tuple[str, object ) -# ExpandSoftKeywords 是 Code 的纯函数(全程无全局可变状态,见其"九种展开"实现): -# 同一文件内容在一次编译里会被多处重复展开(Phase1 / Phase2 / 依赖扫描), -# 缓存结果避免重复正则替换与 tokenize(cProfile:_ExpandTypeDescriptors 里的 -# tokenize.generate_tokens 全程 ~590 万次 TokenInfo._make)。纯函数缓存不改输出;有界防膨胀。 -_EXPAND_CACHE: dict[str, str] = {} -_EXPAND_CACHE_LIMIT: int = 256 - - -def ExpandSoftKeywords(Code: str) -> str: - """将 Viper 软关键字/语法糖展开为等价的 Python 形态(行号不变)。 - - 1) fn/fun/func/function → def(函数定义行):仅在"行首缩进 + 关键字 + 空白 + - 标识符"的函数定义形态匹配,变量名(fn = 5)、调用(fn(x))、注解(x: fn) - 与字符串内容不受影响。 - 2) `enum Name:` 块 → `class Name(t.CEnum):` 块(裸成员补 `: t.State`): - 与手写继承式枚举产出**同一 AST**,故语义(默认编号 0..N、自定义成员类型、 - 宽度取最宽成员)完全一致。 - 3) `define NAME[: T] = 值` → `NAME: [T |] t.CDefine = 值`:编译期常量声明, - 与手写 `NAME: t.CDefine = 值` 产出**同一 AST/同一注册路径**(见 - _ExpandDefineConsts)。`define NAME(...)`(宏函数)不受影响。 - 4) `typedef T1 = T2` → `T1: t.CTypedef = T2`:类型别名声明,与手写 - `T1: t.CTypedef = T2` 产出**同一 AST/同一注册路径**(见 _ExpandTypedefs)。 - 行首 `typedef` 的非声明形态**响亮失败**(该词不在任何既有源码中作标识符)。 - 5) 类型描述符 `const T` / `volatile T` → `t.CConst | T` / `t.CVolatile | T`: - 与手写 `t.CConst | T` 产出**同一 AST/同一解析路径**(见 _ExpandTypeDescriptors)。 - 只在注解位置生效的软关键字(`t.attr.const()` 等非类型位置原样保留); - 缺类型 / 后随非类型 / 裸用**响亮失败**(SyntaxError)。`static` 已从注解位置 - 迁出——注解位置出现 `static`(`名字: static 类型`)**响亮失败**。 - 6) `union NAME:` → `class NAME(t.CUnion):`:与手写 `class NAME(t.CUnion):` 产出 - **同一 AST/同一注册路径**(见 _ExpandUnionBlocks)。成员为普通注解字段。 - 行首 `union` 的非 `union 名字:` 形态**响亮失败**(该词不在任何源码中作标识符)。 - 7) 裸数组前缀 `[N]T` / `[]T` → `t.CArray[T, N]` / `t.CArray[T, None]`(见 - _ExpandArrayPrefixAnnotations):只在注解位置生效(AnnAssign 的 ':' 之后 / - 形参 / '->' 之后 / 类字段),右值列表字面量**绝不改动**;无法脱糖的注解位置 - 数组前缀**响亮失败**(SyntaxError,绝不静默产出 List 注解)。 - 8) `renum NAME:` → `class NAME(t.REnum):`:与手写 `class NAME(t.REnum):` 产出 - **同一 AST/同一注册路径**(见 _ExpandRenumBlocks)。成员为普通类体(嵌套 - `class 变体:` + 无载荷赋值)。行首 `renum` 的非 `renum 名字:` 形态**响亮失败** - (该词不在任何源码中作标识符)。 - 9) `export 名字: T [= 初值]` → `名字: T | t.CExport [= 初值]`; - `extern 名字: T` → `名字: T | t.State`;`static 名字: T [= 初值]` → - `名字: t.CStatic | T [= 初值]`(见 _ExpandExportExternVar)。 - 与 TPV 同一标记、同一消费路径;export/extern 在类型右侧追加标记,static 在 - 类型左侧前置标记(与 TPV `_ParseStaticModified` 的 `pending_static` 同源); - 函数形态(`export def f()` / `export f()` / `extern def f()` / `static def f()`, - 后者由 vast.Parser._ParseStaticDef 处理)不受影响; - `extern` 带初值**响亮失败**(外部定义不写初值)。 - 九种展开都同行替换(长度可增,不增删换行),AST 行号映射保持有效。 - 10) `new [描述符...] T(args)` → `T(args)`(见 _ExpandNewExprs):TPV 的生命周期 - 分配软关键字,TPC 不解其语义,展开成等价裸构造(TPC 的常规分配路径)即可; - 只有"new + [描述符] + 类型名 + (" 形态命中,`new = 1` / `new.x` / `new(1)` / - `for new in` 仍是标识符。 - """ - if not Code: - return Code - Cached: str | None = _EXPAND_CACHE.get(Code) - if Cached is not None: - return Cached - Result: str = _ExpandSoftKeywordsImpl(Code) - if len(_EXPAND_CACHE) >= _EXPAND_CACHE_LIMIT: - _EXPAND_CACHE.clear() - _EXPAND_CACHE[Code] = Result - return Result - - -# --------------------------------------------------------------------------- -# `new` 生命周期表达式(TPV 软关键字)的 TPC 容忍层 -# --------------------------------------------------------------------------- -# TPV 用 `new [描述符...] T(args)`(见 includes/ast/parser.vp)表达"分配 + 生命周期"; -# 共享 includes(如 includes/w32/fileio.vp 的 `return new File(...)`)会带上它,但 TPC 不认 -# `new`(当普通名字 → NameError),于是 TPC 引导编译 TransPyV 时中断。 -# 本层把它展开成等价的裸构造 `T(args)`(TPC 的常规分配路径,与展开前 include 源码一致); -# 只认"new + [描述符] + 类型名 + (" 这一形态——`new = 1` / `new.x` / `new(1)` / `for new in` -# 仍是标识符(与 TPV parser 的软关键字判据同口径)。跨行 new 表达式不处理(保持原样, -# 由后续 parse 响亮失败)。 -_NEW_LIFE_DESCRIPTORS: frozenset[str] = frozenset({'stack', 'heap', 'hlife', 'slife'}) - - -def _ExpandNewExprs(Code: str) -> str: - """`new [stack|heap|hlife|slife|memgr(...)] T(args)` → `T(args)`(TPC 容忍展开)。 - - 仅命中"new + [描述符...] + 类型名 + '('"形态;其余 `new`(标识符)原样保留。 - 命中后删除 `[new .. 类型名)` 这一段(同行),保留类型名与其后的调用。 - """ - if 'new' not in Code: - return Code - try: - Tokens: list = list(tokenize.generate_tokens(io.StringIO(Code).readline)) - except Exception: - return Code - n: int = len(Tokens) - Lines0: list[str] = Code.splitlines() - Edits: list[tuple[int, int, int, str, str]] = [] - i: int = 0 - while i < n: - Tok = Tokens[i] - i += 1 - if Tok.type != tokenize.NAME or Tok.string != 'new': - continue - # 跳过描述符(顺序无关;memgr 带一个括号组) - j: int = _NextSignificantTokenIndex(Tokens, i - 1) - while j >= 0: - Tj = Tokens[j] - if Tj.type == tokenize.NAME and Tj.string in _NEW_LIFE_DESCRIPTORS: - j = _NextSignificantTokenIndex(Tokens, j) - continue - if Tj.type == tokenize.NAME and Tj.string == 'memgr': - k: int = _NextSignificantTokenIndex(Tokens, j) - if k >= 0 and Tokens[k].type == tokenize.OP and Tokens[k].string == '(': - depth: int = 0 - m: int = k - while m < n: - if Tokens[m].type == tokenize.OP and Tokens[m].string == '(': - depth += 1 - elif Tokens[m].type == tokenize.OP and Tokens[m].string == ')': - depth -= 1 - if depth == 0: - break - m += 1 - j = _NextSignificantTokenIndex(Tokens, m) if m < n else -1 - continue - break - if j < 0: - continue - TypeTok = Tokens[j] - # 类型名:NAME 且本身不是描述符(`new heap` 这种缺类型的不命中) - if TypeTok.type != tokenize.NAME or TypeTok.string in _NEW_LIFE_DESCRIPTORS: - continue - k2: int = _NextSignificantTokenIndex(Tokens, j) - if k2 < 0 or Tokens[k2].type != tokenize.OP or Tokens[k2].string != '(': - continue - # 命中:删除 `[new .. 类型名)`(须同行) - if Tok.start[0] != TypeTok.start[0]: - continue - StartCol: int = Tok.start[1] - EndCol: int = TypeTok.start[1] - Line0: str = Lines0[Tok.start[0] - 1] - Edits.append((Tok.start[0], StartCol, EndCol, '', Line0[StartCol:EndCol])) - return _ApplySameLineEdits(Code, Edits) - - -def _ExpandSoftKeywordsImpl(Code: str) -> str: - """ExpandSoftKeywords 的实现体(无缓存,供缓存未命中时调用)。""" - Code = _SOFT_KEYWORD_RE.sub(lambda m: f'{m.group(1)}def{m.group(3)}', Code) - # `new` 生命周期软关键字(TPV):尽早展开成裸构造,后续各层不再看到 descriptors。 - if 'new' in Code: - Code = _ExpandNewExprs(Code) - if 'enum' in Code: - Code = _ExpandEnumBlocks(Code) - if 'union' in Code: - Code = _ExpandUnionBlocks(Code) - if 'renum' in Code: - Code = _ExpandRenumBlocks(Code) - if 'define' in Code: - Code = _ExpandDefineConsts(Code) - if 'typedef' in Code: - Code = _ExpandTypedefs(Code) - # 描述符放在最后:先让语句级语法糖(enum/union/define/typedef)落到规范形态, - # 本层再看规范注解(如 `define X: const int = 1` → `X: const int | t.CDefine = 1` - # → `X: t.CConst | int | t.CDefine = 1`,两种顺序结果相同)。 - # `__attribute__(...)` 排在 const/volatile 之前:其注解位置判据需能回溯到**原始** - # 描述符串(`const __attribute__(x) int` 的 `const` 尚未被改写为 `t.CConst |`), - # 且它自身产出的 `t.attr.X |` 不会干扰紧随的 const/volatile 展开。 - if _ATTRIBUTE_CALLEE in Code: - Code = _ExpandAttributeDescriptors(Code) - # const/volatile 是注解位置的存储类描述符;`static` 已从注解位置迁出为**前置** - # 修饰符(`static 名字: 类型 = 值`),本层进入 _ExpandTypeDescriptors 只为让旧位置 - # `名字: static 类型` 能被扫描到并**响亮失败**(见 _StaticPositionSyntaxError)。 - if 'const' in Code or 'volatile' in Code or 'static' in Code: - Code = _ExpandTypeDescriptors(Code) - # 裸 `[N]T` 数组前缀在描述符之后:`const [N]T` 已由 _ExpandTypeDescriptors 消化为 - # `t.CConst | t.CArray[T, N]`(此处不再有注解位置的裸 '['),避免两条路径重复改写。 - if '[' in Code: - Code = _ExpandArrayPrefixAnnotations(Code) - # export / extern / static 变量声明(语句级软关键字):必须排在**最后**——此时注解 - # 已由数组前缀 / 描述符两层脱糖成规范形态(`t.CArray[T, N]` / `t.CConst | ...`), - # 本层只删修饰符 + 插入存储类标记(export/extern 右追加 `| t.CExport` / `| t.State`; - # static 左前置 `t.CStatic | `)。顺序若反过来,`|` 会被数组前缀展开并进元素类型 - # (见 _ExpandExportExternVar)。 - if 'export' in Code or 'extern' in Code or 'static' in Code: - Code = _ExpandExportExternVar(Code) - return Code - - -# --------------------------------------------------------------------------- -# t.CDefine 编译期整数常量的"可存储范围 + 宽度规则"判定——唯一规则源 -# (`define` 与手写 `X: t.CDefine = 值` 共用同一判据;对照表见 -# Debugger/define-constants.md) -# --------------------------------------------------------------------------- -# 规则: -# ① 本语言**没有 64 位以上的整数类型**(t.py 最大为 CInt64T/CUInt64T,不存在 -# CInt128T/CUInt128T)⇒ 常量的实际存储上限即 64 位,按"有符号/无符号两种 -# 解释"合并为 [-2^63, 2^64-1]。这与既有位模式惯用法一致(0xFFFFFFFF 落 i32 -# 的 -1、0xA5A5A5A5A5A5A5A5 落 i64 负数,见 includes/memhub.vp:123 与 -# LLVMCG/TypeConvert.py:181-186、HandlesExprAttr.py:163-173)。 -# ② 显式类型注解(`define NAME: T = 值` ⇒ `NAME: T | t.CDefine = 值`)另行要求 -# 值能落进 T 的**位宽** w:接受 [-(2^(w-1)), 2^w - 1](同样按"两种解释" -# 惯例,`define X: int = 0xFFFFFFFF` 与 `= -1` 均合法);超出位宽必须报错。 -# ③ 判据基于**精确值**(任意精度整数:字面量原值或 ConstEvaluator 的求值结果), -# 绝不做定宽截断/回绕后再比较。 -# ④ 非整数常量(浮点/字符串/bool)不进入本判据,走既有 register_cdefine_double -# /register_cdefine_string 路径。 -# -# 宽度规则("W_min + 使用处宽度",用户 2026-09-21 确认): -# W_min = 8/16/32/64 中**能容纳该值的最小位宽**(DefineMinWidth)。 -# 常量是立即数,故**每个使用点按上下文决定实际物化宽度**: -# 使用处宽度更宽 ⇒ 就地按该宽度物化(升宽,不损失信息); -# 使用处宽度更窄 ⇒ fail-fast(RequireDefineUseWidth,报文点明 W_min 与所需宽度); -# 取不到使用处宽度时(裸语句 / 变参实参 / 当前架构未下传类型的调用实参)⇒ -# 沿用既有 32/64 二值口径(语言默认整型宽度;见 §8 论证),**不**降到 W_min: -# 降到 W_min 会把 i8/i16 立即数喂进 i32 上下文与 C 默认实参提升, -# 属于"静默降级/ABI 破坏",与本轮口径冲突。 -DEFINE_STORE_MIN: int = -(1 << 63) -DEFINE_STORE_MAX: int = (1 << 64) - 1 -# W_min 候选位宽(升序):取"最小可容纳者" -DEFINE_WIDTH_LADDER: tuple[int, ...] = (8, 16, 32, 64) -# 报文用:可用整数类型清单(与 EnumMemberAnnotationIntWidth 的类型解析口径同源) -_DEFINE_INT_TYPE_HINT: str = ( - 't.CInt8T/t.CUInt8T、t.CInt16T/t.CUInt16T、t.CInt32T/t.CUInt32T、t.CInt64T/t.CUInt64T' - '(或 builtins 的 int8/uint8/int16/uint16/int32/uint32/int64/uint64、char、sizet)' -) - - -def DefineWidthLoHi(Width: int) -> tuple[int, int]: - """位宽 w → 可接受值闭区间(两解释口径):`[-(2^(w-1)), 2^w - 1]`。 - - 负值恰为有符号可表示范围,非负值放宽到无符号位模式范围(与既有 - `0xFFFFFFFF` → i32 `-1`、`0xA5A5A5A5A5A5A5A5` → i64 负数的惯用法一致)。 - """ - return (-(1 << (Width - 1)), (1 << Width) - 1) - - -def DefineMinWidth(Value: object) -> int | None: - """值 → W_min(8/16/32/64 中最小可容纳者);None = 超出 64 位(不可存储)。 - - 判定表(与 Debugger/define-constants.md 的"判定表"逐行对应,**闭区间,边界值 - 归属右侧档**:`2^16 = 65536` 属 32 位档): - [0, 2^8-1] → 8 [2^8, 2^16-1] → 16 - [2^16, 2^32-1] → 32 [2^32, 2^64-1] → 64 - [-2^7, -1] → 8 [-2^15, -2^7-1] → 16 - [-2^31, -2^15-1] → 32 [-2^63, -2^31-1] → 64 - > 2^64-1 / < -2^63 → None(需要扩容/更大的类型) - 符号性:负数按**有符号**解释(必须留出符号位);非负数按**无符号位模式** - 解释(同时兼容有符号解释下的可表示范围,取更宽的 2^w-1 上界)。 - bool / 非 int 不进入本判据(返回 None,由调用方按非整数路径处理)。 - - 本实现按位宽**计算**上下界(`1 << (w-1)` / `(1 << w) - 1`),不含十进制大字面量, - Python 侧因此天然精确。**TPV 侧的孪生实现不得照抄十进制阈值**:TPC 编译 - `[0x80000000, 0xFFFFFFFF]` 的无类型字面量会落到 i32 并在提升时 sext 成负数 - (HandlesExpr.py:263-265 + HandlesExprOps.py:401-413),曾把 `<= 4294967295` - 的 32 位档阈值退化成 `<= -1`(现场 `includes/subprocess.vp:141`, - 详见 Debugger/define-constants.md §14)。 - """ - if isinstance(Value, bool) or not isinstance(Value, int): - return None - for Width in DEFINE_WIDTH_LADDER: - Lo, Hi = DefineWidthLoHi(Width) - if Lo <= Value <= Hi: - return Width - return None - - -def IsCDefineMarker(BaseType: object | None) -> bool: - """BaseType 是否为 t.CDefine 标记本身("未声明具体类型"的自动推导形态)。 - - 兼容 HandlesTypeMerge 把**类型类**直接放进 BaseType 的形态 - (`Info.BaseType = TypeClass`,HandlesTypeMerge.py:228-234)。 - """ - if BaseType is None: - return False - if isinstance(BaseType, type): - return issubclass(BaseType, t.CDefine) - return isinstance(BaseType, t.CDefine) - - -def DefineIntWidthOf(BaseType: object | None) -> int | None: - """声明类型 → 整数位宽(bit);非整数/无法判定 → None。 - - 取宽口径与 EnumMemberAnnotationIntWidth 同源(复用 _IntSizeOf), - 并兼容"类形态"(未实例化的 CType 子类)与"实例形态"两种 BaseType。 - """ - if BaseType is None: - return None - Width: int | None = _IntSizeOf(BaseType) - if Width is not None: - return Width - if (isinstance(BaseType, type) and issubclass(BaseType, t.CType) - and BaseType is not t.CType): - try: - return _IntSizeOf(BaseType()) - except Exception: - return None - return None - - +# ExpandSoftKeywords 是 Code 的纯函数(全程无全局可变状态,见其"九种展开"实现): +# 同一文件内容在一次编译里会被多处重复展开(Phase1 / Phase2 / 依赖扫描), +# 缓存结果避免重复正则替换与 tokenize(cProfile:_ExpandTypeDescriptors 里的 +# tokenize.generate_tokens 全程 ~590 万次 TokenInfo._make)。纯函数缓存不改输出;有界防膨胀。 +_EXPAND_CACHE: dict[str, str] = {} +_EXPAND_CACHE_LIMIT: int = 256 + + +def ExpandSoftKeywords(Code: str) -> str: + """将 Viper 软关键字/语法糖展开为等价的 Python 形态(行号不变)。 + + 1) fn/fun/func/function → def(函数定义行):仅在"行首缩进 + 关键字 + 空白 + + 标识符"的函数定义形态匹配,变量名(fn = 5)、调用(fn(x))、注解(x: fn) + 与字符串内容不受影响。 + 2) `enum Name:` 块 → `class Name(t.CEnum):` 块(裸成员补 `: t.State`): + 与手写继承式枚举产出**同一 AST**,故语义(默认编号 0..N、自定义成员类型、 + 宽度取最宽成员)完全一致。 + 3) `define NAME[: T] = 值` → `NAME: [T |] t.CDefine = 值`:编译期常量声明, + 与手写 `NAME: t.CDefine = 值` 产出**同一 AST/同一注册路径**(见 + _ExpandDefineConsts)。`define NAME(...)`(宏函数)不受影响。 + 4) `typedef T1 = T2` → `T1: t.CTypedef = T2`:类型别名声明,与手写 + `T1: t.CTypedef = T2` 产出**同一 AST/同一注册路径**(见 _ExpandTypedefs)。 + 行首 `typedef` 的非声明形态**响亮失败**(该词不在任何既有源码中作标识符)。 + 5) 类型描述符 `const T` / `volatile T` → `t.CConst | T` / `t.CVolatile | T`: + 与手写 `t.CConst | T` 产出**同一 AST/同一解析路径**(见 _ExpandTypeDescriptors)。 + 只在注解位置生效的软关键字(`t.attr.const()` 等非类型位置原样保留); + 缺类型 / 后随非类型 / 裸用**响亮失败**(SyntaxError)。`static` 已从注解位置 + 迁出——注解位置出现 `static`(`名字: static 类型`)**响亮失败**。 + 6) `union NAME:` → `class NAME(t.CUnion):`:与手写 `class NAME(t.CUnion):` 产出 + **同一 AST/同一注册路径**(见 _ExpandUnionBlocks)。成员为普通注解字段。 + 行首 `union` 的非 `union 名字:` 形态**响亮失败**(该词不在任何源码中作标识符)。 + 7) 裸数组前缀 `[N]T` / `[]T` → `t.CArray[T, N]` / `t.CArray[T, None]`(见 + _ExpandArrayPrefixAnnotations):只在注解位置生效(AnnAssign 的 ':' 之后 / + 形参 / '->' 之后 / 类字段),右值列表字面量**绝不改动**;无法脱糖的注解位置 + 数组前缀**响亮失败**(SyntaxError,绝不静默产出 List 注解)。 + 8) `renum NAME:` → `class NAME(t.REnum):`:与手写 `class NAME(t.REnum):` 产出 + **同一 AST/同一注册路径**(见 _ExpandRenumBlocks)。成员为普通类体(嵌套 + `class 变体:` + 无载荷赋值)。行首 `renum` 的非 `renum 名字:` 形态**响亮失败** + (该词不在任何源码中作标识符)。 + 9) `export 名字: T [= 初值]` → `名字: T | t.CExport [= 初值]`; + `extern 名字: T` → `名字: T | t.State`;`static 名字: T [= 初值]` → + `名字: t.CStatic | T [= 初值]`(见 _ExpandExportExternVar)。 + 与 VPC 同一标记、同一消费路径;export/extern 在类型右侧追加标记,static 在 + 类型左侧前置标记(与 VPC `_ParseStaticModified` 的 `pending_static` 同源); + 函数形态(`export def f()` / `export f()` / `extern def f()` / `static def f()`, + 后者由 vast.Parser._ParseStaticDef 处理)不受影响; + `extern` 带初值**响亮失败**(外部定义不写初值)。 + 以上展开都同行替换(长度可增,不增删换行),AST 行号映射保持有效。 + 10) `new [描述符...] T(args)`:**已不在本层展开**——vast 解析器原生解析成 New + 节点(lib/vast/ExprParser._ParseNewExpr),存储/构造语义由 Handles 落地 + (HandlesExprCall._HandleNewExprLlvm);`new = 1` / `new.x` / `new(1)` / + `for new in` 仍是标识符。 + """ + if not Code: + return Code + Cached: str | None = _EXPAND_CACHE.get(Code) + if Cached is not None: + return Cached + Result: str = _ExpandSoftKeywordsImpl(Code) + if len(_EXPAND_CACHE) >= _EXPAND_CACHE_LIMIT: + _EXPAND_CACHE.clear() + _EXPAND_CACHE[Code] = Result + return Result + + +def _ExpandSoftKeywordsImpl(Code: str) -> str: + """ExpandSoftKeywords 的实现体(无缓存,供缓存未命中时调用)。""" + Code = _SOFT_KEYWORD_RE.sub(lambda m: f'{m.group(1)}def{m.group(3)}', Code) + # `new` 生命周期软关键字(VPC):**不再**在此层展开——vast 解析器已原生解析成 + # New 节点(lib/vast/ExprParser._ParseNewExpr),存储语义由 Handles 落地。 + if 'enum' in Code: + Code = _ExpandEnumBlocks(Code) + if 'union' in Code: + Code = _ExpandUnionBlocks(Code) + if 'renum' in Code: + Code = _ExpandRenumBlocks(Code) + if 'define' in Code: + Code = _ExpandDefineConsts(Code) + if 'typedef' in Code: + Code = _ExpandTypedefs(Code) + # 描述符放在最后:先让语句级语法糖(enum/union/define/typedef)落到规范形态, + # 本层再看规范注解(如 `define X: const int = 1` → `X: const int | t.CDefine = 1` + # → `X: t.CConst | int | t.CDefine = 1`,两种顺序结果相同)。 + # `__attribute__(...)` 排在 const/volatile 之前:其注解位置判据需能回溯到**原始** + # 描述符串(`const __attribute__(x) int` 的 `const` 尚未被改写为 `t.CConst |`), + # 且它自身产出的 `t.attr.X |` 不会干扰紧随的 const/volatile 展开。 + if _ATTRIBUTE_CALLEE in Code: + Code = _ExpandAttributeDescriptors(Code) + # const/volatile 是注解位置的存储类描述符;`static` 已从注解位置迁出为**前置** + # 修饰符(`static 名字: 类型 = 值`),本层进入 _ExpandTypeDescriptors 只为让旧位置 + # `名字: static 类型` 能被扫描到并**响亮失败**(见 _StaticPositionSyntaxError)。 + if 'const' in Code or 'volatile' in Code or 'static' in Code: + Code = _ExpandTypeDescriptors(Code) + # 裸 `[N]T` 数组前缀在描述符之后:`const [N]T` 已由 _ExpandTypeDescriptors 消化为 + # `t.CConst | t.CArray[T, N]`(此处不再有注解位置的裸 '['),避免两条路径重复改写。 + if '[' in Code: + Code = _ExpandArrayPrefixAnnotations(Code) + # export / extern / static 变量声明(语句级软关键字):必须排在**最后**——此时注解 + # 已由数组前缀 / 描述符两层脱糖成规范形态(`t.CArray[T, N]` / `t.CConst | ...`), + # 本层只删修饰符 + 插入存储类标记(export/extern 右追加 `| t.CExport` / `| t.State`; + # static 左前置 `t.CStatic | `)。顺序若反过来,`|` 会被数组前缀展开并进元素类型 + # (见 _ExpandExportExternVar)。 + if 'export' in Code or 'extern' in Code or 'static' in Code: + Code = _ExpandExportExternVar(Code) + return Code + + +# --------------------------------------------------------------------------- +# t.CDefine 编译期整数常量的"可存储范围 + 宽度规则"判定——唯一规则源 +# (`define` 与手写 `X: t.CDefine = 值` 共用同一判据;对照表见 +# Debugger/define-constants.md) +# --------------------------------------------------------------------------- +# 规则: +# ① 本语言**没有 64 位以上的整数类型**(t.py 最大为 CInt64T/CUInt64T,不存在 +# CInt128T/CUInt128T)⇒ 常量的实际存储上限即 64 位,按"有符号/无符号两种 +# 解释"合并为 [-2^63, 2^64-1]。这与既有位模式惯用法一致(0xFFFFFFFF 落 i32 +# 的 -1、0xA5A5A5A5A5A5A5A5 落 i64 负数,见 includes/memhub.vp:123 与 +# LLVMCG/TypeConvert.py:181-186、HandlesExprAttr.py:163-173)。 +# ② 显式类型注解(`define NAME: T = 值` ⇒ `NAME: T | t.CDefine = 值`)另行要求 +# 值能落进 T 的**位宽** w:接受 [-(2^(w-1)), 2^w - 1](同样按"两种解释" +# 惯例,`define X: int = 0xFFFFFFFF` 与 `= -1` 均合法);超出位宽必须报错。 +# ③ 判据基于**精确值**(任意精度整数:字面量原值或 ConstEvaluator 的求值结果), +# 绝不做定宽截断/回绕后再比较。 +# ④ 非整数常量(浮点/字符串/bool)不进入本判据,走既有 register_cdefine_double +# /register_cdefine_string 路径。 +# +# 宽度规则("W_min + 使用处宽度",用户 2026-09-21 确认): +# W_min = 8/16/32/64 中**能容纳该值的最小位宽**(DefineMinWidth)。 +# 常量是立即数,故**每个使用点按上下文决定实际物化宽度**: +# 使用处宽度更宽 ⇒ 就地按该宽度物化(升宽,不损失信息); +# 使用处宽度更窄 ⇒ fail-fast(RequireDefineUseWidth,报文点明 W_min 与所需宽度); +# 取不到使用处宽度时(裸语句 / 变参实参 / 当前架构未下传类型的调用实参)⇒ +# 沿用既有 32/64 二值口径(语言默认整型宽度;见 §8 论证),**不**降到 W_min: +# 降到 W_min 会把 i8/i16 立即数喂进 i32 上下文与 C 默认实参提升, +# 属于"静默降级/ABI 破坏",与本轮口径冲突。 +DEFINE_STORE_MIN: int = -(1 << 63) +DEFINE_STORE_MAX: int = (1 << 64) - 1 +# W_min 候选位宽(升序):取"最小可容纳者" +DEFINE_WIDTH_LADDER: tuple[int, ...] = (8, 16, 32, 64) +# 报文用:可用整数类型清单(与 EnumMemberAnnotationIntWidth 的类型解析口径同源) +_DEFINE_INT_TYPE_HINT: str = ( + 't.CInt8T/t.CUInt8T、t.CInt16T/t.CUInt16T、t.CInt32T/t.CUInt32T、t.CInt64T/t.CUInt64T' + '(或 builtins 的 int8/uint8/int16/uint16/int32/uint32/int64/uint64、char、sizet)' +) + + +def DefineWidthLoHi(Width: int) -> tuple[int, int]: + """位宽 w → 可接受值闭区间(两解释口径):`[-(2^(w-1)), 2^w - 1]`。 + + 负值恰为有符号可表示范围,非负值放宽到无符号位模式范围(与既有 + `0xFFFFFFFF` → i32 `-1`、`0xA5A5A5A5A5A5A5A5` → i64 负数的惯用法一致)。 + """ + return (-(1 << (Width - 1)), (1 << Width) - 1) + + +def DefineMinWidth(Value: object) -> int | None: + """值 → W_min(8/16/32/64 中最小可容纳者);None = 超出 64 位(不可存储)。 + + 判定表(与 Debugger/define-constants.md 的"判定表"逐行对应,**闭区间,边界值 + 归属右侧档**:`2^16 = 65536` 属 32 位档): + [0, 2^8-1] → 8 [2^8, 2^16-1] → 16 + [2^16, 2^32-1] → 32 [2^32, 2^64-1] → 64 + [-2^7, -1] → 8 [-2^15, -2^7-1] → 16 + [-2^31, -2^15-1] → 32 [-2^63, -2^31-1] → 64 + > 2^64-1 / < -2^63 → None(需要扩容/更大的类型) + 符号性:负数按**有符号**解释(必须留出符号位);非负数按**无符号位模式** + 解释(同时兼容有符号解释下的可表示范围,取更宽的 2^w-1 上界)。 + bool / 非 int 不进入本判据(返回 None,由调用方按非整数路径处理)。 + + 本实现按位宽**计算**上下界(`1 << (w-1)` / `(1 << w) - 1`),不含十进制大字面量, + Python 侧因此天然精确。**VPC 侧的孪生实现不得照抄十进制阈值**:TPC 编译 + `[0x80000000, 0xFFFFFFFF]` 的无类型字面量会落到 i32 并在提升时 sext 成负数 + (HandlesExpr.py:263-265 + HandlesExprOps.py:401-413),曾把 `<= 4294967295` + 的 32 位档阈值退化成 `<= -1`(现场 `includes/subprocess.vp:141`, + 详见 Debugger/define-constants.md §14)。 + """ + if isinstance(Value, bool) or not isinstance(Value, int): + return None + for Width in DEFINE_WIDTH_LADDER: + Lo, Hi = DefineWidthLoHi(Width) + if Lo <= Value <= Hi: + return Width + return None + + +def IsCDefineMarker(BaseType: object | None) -> bool: + """BaseType 是否为 t.CDefine 标记本身("未声明具体类型"的自动推导形态)。 + + 兼容 HandlesTypeMerge 把**类型类**直接放进 BaseType 的形态 + (`Info.BaseType = TypeClass`,HandlesTypeMerge.py:228-234)。 + """ + if BaseType is None: + return False + if isinstance(BaseType, type): + return issubclass(BaseType, t.CDefine) + return isinstance(BaseType, t.CDefine) + + +def DefineIntWidthOf(BaseType: object | None) -> int | None: + """声明类型 → 整数位宽(bit);非整数/无法判定 → None。 + + 取宽口径与 EnumMemberAnnotationIntWidth 同源(复用 _IntSizeOf), + 并兼容"类形态"(未实例化的 CType 子类)与"实例形态"两种 BaseType。 + """ + if BaseType is None: + return None + Width: int | None = _IntSizeOf(BaseType) + if Width is not None: + return Width + if (isinstance(BaseType, type) and issubclass(BaseType, t.CType) + and BaseType is not t.CType): + try: + return _IntSizeOf(BaseType()) + except Exception: + return None + return None + + def DefineIntSignednessOf(BaseType: object | None) -> bool | None: """声明类型 → 是否有符号;非整数/无法判定 → None(与 DefineIntWidthOf 同口径)。 @@ -2007,173 +1930,215 @@ def GlobalDeclShadowsDefine(Gen: object, Name: str | None) -> bool: return getattr(g, 'initializer', None) is not None -def RequireDefineIntStorable(Value: object, BaseType: object | None, Name: str, - AnnotationText: str, Lineno: int, Col: int, - FilePath: str, CodeLine: str) -> None: - """t.CDefine 整数常量的可存储性校验(fail-fast;唯一判据,两侧同口径)。 - - 通过则原样返回(零副作用);不可存储则抛 TypeError,报文含五要素: - **文件:行[:列] / 源码片段 / 常量名 / 声明类型或自动推导上限 / 实际值 / - 可存储范围与可用类型清单 / 修法建议(点明"需要更大的类型/扩容")**。 - - 调用点(均为 CDefine 常量的**注册处**,即值与已解析注解同时可得之处): - - 模块级 / 类体内:`Translator/LlvmGenerator._register_cdefine_constant` - - 函数内:`Handles/HandlesAnnAssign._HandleAnnAssignLlvmInner` 的 IsDefine 分支 - **不触碰**非 CDefine 声明路径(调用点全部在 IsDefine 分支内)。 - """ - if isinstance(Value, bool) or not isinstance(Value, int): - return - Lo: int = DEFINE_STORE_MIN - Hi: int = DEFINE_STORE_MAX - Width: int | None = DefineIntWidthOf(BaseType) - WMin: int | None = DefineMinWidth(Value) - WMinText: str = f'{WMin} 位' if WMin is not None else '无(超出 64 位)' - if IsCDefineMarker(BaseType) or Width is None: - Deduced: int = 32 if 0 <= Value <= 0xFFFFFFFF else 64 - TypeDesc: str = f'未声明具体类型(自动推导宽度 {Deduced} 位)' - else: - TypeDesc = f'{AnnotationText or "?"}({Width} 位)' - if Width > 0: - Lo = max(Lo, -(1 << (Width - 1))) - Hi = min(Hi, (1 << Width) - 1) - if Lo <= Value <= Hi: - return - Loc: str = f'{FilePath or "unknown"}:{Lineno}' - if Col >= 0: - Loc += f':{Col}' - raise TypeError( - f'[TransPyC] define 常量数值不可存储: 常量 {Name!r} 的初始值 {Value} 不可存储。' - f'最小可容纳宽度 W_min: {WMinText}。' - f'声明类型: {TypeDesc};该声明可存储范围: {Lo}..{Hi}。' - f'本语言最宽的整数类型为 64 位({DEFINE_STORE_MIN}..{DEFINE_STORE_MAX},' - f'覆盖有符号/无符号两种解释),不存在更宽的整数类型 ⇒ 需要扩容。' - f'禁止静默截断/降级,已终止编译。可用整数类型: {_DEFINE_INT_TYPE_HINT}。' - f'修法: 把声明类型改为更宽的类型(如 t.CInt64T / uint64);若仍放不下,' - f'说明该数字超出本语言可表示的整数范围,需要更大的整数类型(扩容);' - f'或修正该字面量。位置 {Loc}' - + (f',源码: {CodeLine}' if CodeLine else '') - ) - - -def RequireDefineUseWidth(Value: object, ContextWidth: int | None, Name: str, - Lineno: int, Col: int, FilePath: str, CodeLine: str, - ContextDesc: str = '') -> int | None: - """**使用处宽度**校验(fail-fast;唯一判据,两侧同口径)。 - - 规则(`define` 常量被物化成立即数的使用点): - 上下文宽度已知且 `>= W_min` ⇒ 返回该宽度(调用方按此宽度就地升宽物化); - 上下文宽度已知且 `< W_min` ⇒ 抛 TypeError(含 W_min、使用处要求的宽度、 - 实际值、文件:行:列、源码片段、修法); - 上下文宽度无法判定(None) ⇒ 返回 None(调用方沿用既有 32/64 二值口径)。 - 非整数常量(float/str/bool)或值超出 64 位(W_min=None)不在此判据内:后者由 - RequireDefineIntStorable 在**注册处**拦住(更早、更准确的站点)。 - """ - if isinstance(Value, bool) or not isinstance(Value, int): - return None - WMin: int | None = DefineMinWidth(Value) - if WMin is None or ContextWidth is None: - return None - if ContextWidth >= WMin: - return ContextWidth - Loc: str = f'{FilePath or "unknown"}:{Lineno}' - if Col >= 0: - Loc += f':{Col}' - raise TypeError( - f'[TransPyC] define 常量使用处宽度不足: 常量 {Name!r} 的实际值 {Value} ' - f'需要至少 {WMin} 位(最小可容纳宽度 W_min),' - f'但使用处{("(" + ContextDesc + ")") if ContextDesc else ""}只提供 {ContextWidth} 位' - f'(可表示范围: {DefineWidthLoHi(ContextWidth)[0]}..{DefineWidthLoHi(ContextWidth)[1]})。' - f'禁止静默截断/降级,已终止编译。' - f'修法: 把使用处(变量声明 / 形参 / 运算对象 / 返回注解)的类型加宽到至少 {WMin} 位' - f'(如 t.CInt{WMin}T / CUInt{WMin}T 及更宽类型),或改用更小的常量值。' - f'位置 {Loc}' - + (f',源码: {CodeLine}' if CodeLine else '') - ) - - -def _DefineSiteInfo(Gen: object, Trans: object, Node: object) -> tuple[int, int, str, str]: - """站点四要素:行号(含 `_line_offset` 还原)/ 列号 / 源文件路径 / 源码片段。 - - 口径与 `HandlesExprCall._RequireArgValue`(rawptr-migration-and-fastfail.md §27.3) - 逐条一致,供 RequireDefineConstStorable / RequireDefineUseWidthAt 共用。 - """ - Lineno: int = int(getattr(Node, 'lineno', 0) or 0) - Offset: int = int(getattr(Trans, '_line_offset', 0) or 0) - if Offset > 0 and Lineno > Offset: - Lineno -= Offset - Col: int = int(getattr(Node, 'col_offset', -1)) - FilePath: str = getattr(Gen, '_current_source_file', '') or '' - CodeLine: str = '' - if Lineno > 0 and hasattr(Gen, '_get_source_line'): - try: - CodeLine = (Gen._get_source_line(Lineno) or '').strip() - except Exception: - CodeLine = '' - return Lineno, Col, FilePath, CodeLine - - -# 整数字面量的可存储范围(两种解释的并集):[-2^63, 2^64-1]。 -# 与 TPV 侧(includes/ast/lexer.vp 的 _lex_int_exceeds_u64 / _lex_int_neg_exceeds_i64、 -# includes/ast/parser.vp 的一元减联合判定)**同一区间、同一口径**。 -INT_LITERAL_MIN: int = -(1 << 63) -INT_LITERAL_MAX: int = (1 << 64) - 1 - - -def RequireIntLiteralStorable(Gen: object, Trans: object, Node: object, Value: int) -> None: - """整数字面量(含"一元减作用于整数字面量"后的**结果值**)的存储范围 fail-fast。 - - `-` 是一元运算符、不属于字面量:字面量本身无符号(0..2^64-1),负数侧由一元减 - 表达,取负后必须 >= -2^63。任何越界值(如 18446744073709551616、-18446744073709551615、 - -9223372036854775809)在此 **fail-fast**(不静默回绕/截断)。 - - 只在 `Value` 为 int(非 bool)时生效;报文含 `文件:行[:列] + 源码片段 + 字面量原文 - (含负号)+ 允许范围 [-2^63, 2^64-1] + 修法`(站点口径与 _DefineSiteInfo 一致)。 - """ - if isinstance(Value, bool) or not isinstance(Value, int): - return - if INT_LITERAL_MIN <= Value <= INT_LITERAL_MAX: - return - Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) - Loc: str = f'{FilePath}:{Lineno}:{Col}' if FilePath else f'line {Lineno}:{Col}' - raise TypeError( - f'[TransPyC] 整数字面量越界: 字面量 {str(Value)!r} 超出 64 位可表示范围' - f'(允许范围 -9223372036854775808..18446744073709551615 = [-2^63, 2^64-1];' - f'负数结果必须 >= -2^63)。本语言不存在更宽的整数类型;' - f'禁止静默回绕/截断,已终止编译。' - f'修法: 把该字面量改到上述范围内(如需更大数值请拆分为多个 64 位字表达)。' - f'位置 {Loc}' - + (f',源码: {CodeLine}' if CodeLine else '') - ) - - -def RequireDefineUseWidthAt(Gen: object, Trans: object, Node: object, - Value: object, ContextWidth: int | None, Name: str, - ContextDesc: str = '') -> int | None: - """使用处宽度校验的站点包装(取"文件:行[:列] + 源码片段"后转调 RequireDefineUseWidth)。""" - Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) - return RequireDefineUseWidth(Value, ContextWidth, Name, Lineno, Col, FilePath, - CodeLine, ContextDesc) - - -def RequireDefineConstStorable(Gen: object, Trans: object, Node: object, - BaseType: object | None, Annotation: object | None, - Value: object, Name: str) -> None: - """编译期常量注册处的站点包装:取"文件:行[:列] + 源码片段"后调 RequireDefineIntStorable。 - - 站点信息口径与 `HandlesExprCall._RequireArgValue`(rawptr-migration-and-fastfail.md §27) - 逐条一致:`Gen._current_source_file` / `Gen._get_source_line(行号)` / - `Trans._line_offset`(`_inject_auto_imports` 注入行导致的行号偏移还原)。 - """ - AnnText: str = '' - if Annotation is not None: - try: - AnnText = ast.unparse(Annotation) - except Exception: - AnnText = '' - Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) - RequireDefineIntStorable(Value, BaseType, Name, AnnText, Lineno, Col, FilePath, CodeLine) - - +def RequireDefineIntStorable(Value: object, BaseType: object | None, Name: str, + AnnotationText: str, Lineno: int, Col: int, + FilePath: str, CodeLine: str) -> None: + """t.CDefine 整数常量的可存储性校验(fail-fast;唯一判据,两侧同口径)。 + + 通过则原样返回(零副作用);不可存储则抛 TypeError,报文含五要素: + **文件:行[:列] / 源码片段 / 常量名 / 声明类型或自动推导上限 / 实际值 / + 可存储范围与可用类型清单 / 修法建议(点明"需要更大的类型/扩容")**。 + + 调用点(均为 CDefine 常量的**注册处**,即值与已解析注解同时可得之处): + - 模块级 / 类体内:`Translator/LlvmGenerator._register_cdefine_constant` + - 函数内:`Handles/HandlesAnnAssign._HandleAnnAssignLlvmInner` 的 IsDefine 分支 + **不触碰**非 CDefine 声明路径(调用点全部在 IsDefine 分支内)。 + """ + if isinstance(Value, bool) or not isinstance(Value, int): + return + Lo: int = DEFINE_STORE_MIN + Hi: int = DEFINE_STORE_MAX + Width: int | None = DefineIntWidthOf(BaseType) + WMin: int | None = DefineMinWidth(Value) + WMinText: str = f'{WMin} 位' if WMin is not None else '无(超出 64 位)' + if IsCDefineMarker(BaseType) or Width is None: + Deduced: int = 32 if 0 <= Value <= 0xFFFFFFFF else 64 + TypeDesc: str = f'未声明具体类型(自动推导宽度 {Deduced} 位)' + else: + TypeDesc = f'{AnnotationText or "?"}({Width} 位)' + if Width > 0: + Lo = max(Lo, -(1 << (Width - 1))) + Hi = min(Hi, (1 << Width) - 1) + if Lo <= Value <= Hi: + return + Loc: str = f'{FilePath or "unknown"}:{Lineno}' + if Col >= 0: + Loc += f':{Col}' + raise TypeError( + f'[TransPyC] define 常量数值不可存储: 常量 {Name!r} 的初始值 {Value} 不可存储。' + f'最小可容纳宽度 W_min: {WMinText}。' + f'声明类型: {TypeDesc};该声明可存储范围: {Lo}..{Hi}。' + f'本语言最宽的整数类型为 64 位({DEFINE_STORE_MIN}..{DEFINE_STORE_MAX},' + f'覆盖有符号/无符号两种解释),不存在更宽的整数类型 ⇒ 需要扩容。' + f'禁止静默截断/降级,已终止编译。可用整数类型: {_DEFINE_INT_TYPE_HINT}。' + f'修法: 把声明类型改为更宽的类型(如 t.CInt64T / uint64);若仍放不下,' + f'说明该数字超出本语言可表示的整数范围,需要更大的整数类型(扩容);' + f'或修正该字面量。位置 {Loc}' + + (f',源码: {CodeLine}' if CodeLine else '') + ) + + +def RequireDefineUseWidth(Value: object, ContextWidth: int | None, Name: str, + Lineno: int, Col: int, FilePath: str, CodeLine: str, + ContextDesc: str = '') -> int | None: + """**使用处宽度**校验(fail-fast;唯一判据,两侧同口径)。 + + 规则(`define` 常量被物化成立即数的使用点): + 上下文宽度已知且 `>= W_min` ⇒ 返回该宽度(调用方按此宽度就地升宽物化); + 上下文宽度已知且 `< W_min` ⇒ 抛 TypeError(含 W_min、使用处要求的宽度、 + 实际值、文件:行:列、源码片段、修法); + 上下文宽度无法判定(None) ⇒ 返回 None(调用方沿用既有 32/64 二值口径)。 + 非整数常量(float/str/bool)或值超出 64 位(W_min=None)不在此判据内:后者由 + RequireDefineIntStorable 在**注册处**拦住(更早、更准确的站点)。 + """ + if isinstance(Value, bool) or not isinstance(Value, int): + return None + WMin: int | None = DefineMinWidth(Value) + if WMin is None or ContextWidth is None: + return None + if ContextWidth >= WMin: + return ContextWidth + Loc: str = f'{FilePath or "unknown"}:{Lineno}' + if Col >= 0: + Loc += f':{Col}' + raise TypeError( + f'[TransPyC] define 常量使用处宽度不足: 常量 {Name!r} 的实际值 {Value} ' + f'需要至少 {WMin} 位(最小可容纳宽度 W_min),' + f'但使用处{("(" + ContextDesc + ")") if ContextDesc else ""}只提供 {ContextWidth} 位' + f'(可表示范围: {DefineWidthLoHi(ContextWidth)[0]}..{DefineWidthLoHi(ContextWidth)[1]})。' + f'禁止静默截断/降级,已终止编译。' + f'修法: 把使用处(变量声明 / 形参 / 运算对象 / 返回注解)的类型加宽到至少 {WMin} 位' + f'(如 t.CInt{WMin}T / CUInt{WMin}T 及更宽类型),或改用更小的常量值。' + f'位置 {Loc}' + + (f',源码: {CodeLine}' if CodeLine else '') + ) + + +def _DefineSiteInfo(Gen: object, Trans: object, Node: object) -> tuple[int, int, str, str]: + """站点四要素:行号(含 `_line_offset` 还原)/ 列号 / 源文件路径 / 源码片段。 + + 口径与 `HandlesExprCall._RequireArgValue`(rawptr-migration-and-fastfail.md §27.3) + 逐条一致,供 RequireDefineConstStorable / RequireDefineUseWidthAt 共用。 + """ + Lineno: int = int(getattr(Node, 'lineno', 0) or 0) + Offset: int = int(getattr(Trans, '_line_offset', 0) or 0) + if Offset > 0 and Lineno > Offset: + Lineno -= Offset + Col: int = int(getattr(Node, 'col_offset', -1)) + FilePath: str = getattr(Gen, '_current_source_file', '') or '' + CodeLine: str = '' + if Lineno > 0 and hasattr(Gen, '_get_source_line'): + try: + CodeLine = (Gen._get_source_line(Lineno) or '').strip() + except Exception: + CodeLine = '' + return Lineno, Col, FilePath, CodeLine + + +# 整数字面量的可存储范围(两种解释的并集):[-2^63, 2^64-1]。 +# 与 VPC 侧(includes/ast/lexer.vp 的 _lex_int_exceeds_u64 / _lex_int_neg_exceeds_i64、 +# includes/ast/parser.vp 的一元减联合判定)**同一区间、同一口径**。 +INT_LITERAL_MIN: int = -(1 << 63) +INT_LITERAL_MAX: int = (1 << 64) - 1 + + +def RequireIntLiteralStorable(Gen: object, Trans: object, Node: object, Value: int) -> None: + """整数字面量(含"一元减作用于整数字面量"后的**结果值**)的存储范围 fail-fast。 + + `-` 是一元运算符、不属于字面量:字面量本身无符号(0..2^64-1),负数侧由一元减 + 表达,取负后必须 >= -2^63。任何越界值(如 18446744073709551616、-18446744073709551615、 + -9223372036854775809)在此 **fail-fast**(不静默回绕/截断)。 + + 只在 `Value` 为 int(非 bool)时生效;报文含 `文件:行[:列] + 源码片段 + 字面量原文 + (含负号)+ 允许范围 [-2^63, 2^64-1] + 修法`(站点口径与 _DefineSiteInfo 一致)。 + """ + if isinstance(Value, bool) or not isinstance(Value, int): + return + if INT_LITERAL_MIN <= Value <= INT_LITERAL_MAX: + return + Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) + Loc: str = f'{FilePath}:{Lineno}:{Col}' if FilePath else f'line {Lineno}:{Col}' + raise TypeError( + f'[TransPyC] 整数字面量越界: 字面量 {str(Value)!r} 超出 64 位可表示范围' + f'(允许范围 -9223372036854775808..18446744073709551615 = [-2^63, 2^64-1];' + f'负数结果必须 >= -2^63)。本语言不存在更宽的整数类型;' + f'禁止静默回绕/截断,已终止编译。' + f'修法: 把该字面量改到上述范围内(如需更大数值请拆分为多个 64 位字表达)。' + f'位置 {Loc}' + + (f',源码: {CodeLine}' if CodeLine else '') + ) + + +def RequireExplicitWidthLiteral(Gen: object, Trans: object, Node: object, + Value: int, Width: int, Unsigned: int) -> None: + """显式宽度字面量(`123_int32` / `0xFFFFFFFF_uint32`)的范围 fail-fast(**不允许隐式截断**)。 + + 有符号 `int`:[-2^(N-1), 2^(N-1)-1];无符号 `uint`:[0, 2^N-1]; + N == 64 时 int64 全域都合法(十六进制高位型在解析时已按 64 位位型回绕)。 + 越界即 raise(报文含 文件:行[:列] + 源码片段 + 字面量 + 允许范围 + 修法)。 + """ + if isinstance(Value, bool) or not isinstance(Value, int): + return + if Width <= 0 or Width > 64: + return + Ok: bool = True + if Unsigned: + if Width < 64 and Value < 0: + Ok = False + elif Width <= 62 and Value > (1 << Width) - 1: + Ok = False + else: + if Width < 64: + Lo: int = -(1 << (Width - 1)) + Hi: int = (1 << (Width - 1)) - 1 + if Value < Lo or Value > Hi: + Ok = False + if Ok: + return + Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) + Loc: str = f'{FilePath}:{Lineno}:{Col}' if FilePath else f'line {Lineno}:{Col}' + Kind: str = 'uint' if Unsigned else 'int' + if Unsigned: + RangeTxt: str = f'[0, 2^{Width}-1 = {((1 << Width) - 1) if Width < 64 else 2 ** 64 - 1}]' + else: + RangeTxt: str = f'[-2^{Width - 1}, 2^{Width - 1}-1]' if Width < 64 else '[-2^63, 2^63-1]' + raise TypeError( + f'[TransPyC] 显式宽度字面量越界: {str(Value)!r} 放不进 {Kind}{Width}' + f'(该后缀宽度的允许范围是 {RangeTxt})。显式宽度后缀不允许截断,已终止编译。' + f'修法: 放宽后缀宽度(如改为 {Kind}{Width * 2 if Width * 2 <= 64 else 64}),或去掉后缀用显式强转。' + f'位置 {Loc}' + + (f',源码: {CodeLine}' if CodeLine else '') + ) + + +def RequireDefineUseWidthAt(Gen: object, Trans: object, Node: object, + Value: object, ContextWidth: int | None, Name: str, + ContextDesc: str = '') -> int | None: + """使用处宽度校验的站点包装(取"文件:行[:列] + 源码片段"后转调 RequireDefineUseWidth)。""" + Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) + return RequireDefineUseWidth(Value, ContextWidth, Name, Lineno, Col, FilePath, + CodeLine, ContextDesc) + + +def RequireDefineConstStorable(Gen: object, Trans: object, Node: object, + BaseType: object | None, Annotation: object | None, + Value: object, Name: str) -> None: + """编译期常量注册处的站点包装:取"文件:行[:列] + 源码片段"后调 RequireDefineIntStorable。 + + 站点信息口径与 `HandlesExprCall._RequireArgValue`(rawptr-migration-and-fastfail.md §27) + 逐条一致:`Gen._current_source_file` / `Gen._get_source_line(行号)` / + `Trans._line_offset`(`_inject_auto_imports` 注入行导致的行号偏移还原)。 + """ + AnnText: str = '' + if Annotation is not None: + try: + AnnText = ast.unparse(Annotation) + except Exception: + AnnText = '' + Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) + RequireDefineIntStorable(Value, BaseType, Name, AnnText, Lineno, Col, FilePath, CodeLine) + + def RequireNameDefined(Gen: object, Trans: object, Node: object, Name: str) -> None: """未定义名称的 fail-fast(NameError)。 @@ -2197,959 +2162,959 @@ def RequireNameDefined(Gen: object, Trans: object, Node: object, Name: str) -> N ) -def IsTModule(module_id: str, symtable: object | None = None) -> bool: - """检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)""" - if module_id in _T_MODULE_NAMES: - return True - if symtable is not None and hasattr(symtable, 'import_aliases'): - resolved: str = symtable.import_aliases.get(module_id, module_id) - if resolved in _T_MODULE_NAMES: - return True - return False - - -# --------------------------------------------------------------------------- -# virtual / novirtual 修饰符(软关键字)的判据 —— TPC 侧**唯一规则源** -# --------------------------------------------------------------------------- -# 源码形态 等价既有写法 语义 -# virtual class C: @t.CVTable class C: 类级:强制有 vtable -# novirtual class C: @t.NoVTable class C: 类级:强制无 vtable -# virtual def m(...) @t.CVTable def m(...) 方法级:占 vtable 槽 -# novirtual def m(...) @t.NoVTable def m(...) 方法级:不占 vtable 槽 -# -# 解析层(lib/vast/Parser.py:_ParseVirtualModified)只合成同名装饰器,**不新造第二套 -# 表示**;语义全部落在既有点位,本节函数是那些点位共用的同一判据(避免多处各写一份 -# 而漂移),逐条对齐 TPV(Viper 自举编译器)的既有实现: -# - ClassHasFuncLevelCVTable ←→ TPV ClassGenBase._has_any_func_cvtable -# - MethodVirtualMarker ←→ TPV ClassGenBase._should_method_be_virtual 第 1/2 条 -# - IsVtableSlotMethod ←→ TPV ClassGenBase._should_method_be_virtual 全 5 条 -# -# 回归性:判据只在"方法带 CVTable/NoVTable 标记"时偏离原行为;全仓既有 .vp 无任何 -# 方法级标记(普查结论见报告),故对既有类/既有代码零影响。 -def _DecoratorListHasMarker(DecoratorList: object, MarkerName: str) -> bool: - """装饰器表中是否存在指定标记名(接受 @t.X / @X / @t.X() / @X() 四种既有写法)。""" - if not DecoratorList: - return False - for Dec in DecoratorList: - Target: object = Dec - if isinstance(Dec, ast.Call): - Target = Dec.func - if isinstance(Target, ast.Attribute): - if Target.attr == MarkerName: - return True - elif isinstance(Target, ast.Name): - if Target.id == MarkerName: - return True - return False - - -def MethodVirtualMarker(MethodNode: ast.AST) -> str | None: - """方法级 virtual/novirtual 标记 → 'CVTable' / 'NoVTable' / None。 - - NoVTable 优先(与 TPV _should_method_be_virtual 第 1 条同序):同一方法同时带 - 两种标记时按"排除"处理。 - """ - DecList: object = getattr(MethodNode, 'decorator_list', None) - if _DecoratorListHasMarker(DecList, 'NoVTable'): - return 'NoVTable' - if _DecoratorListHasMarker(DecList, 'CVTable'): - return 'CVTable' - return None - - -def ClassHasFuncLevelCVTable(ClassNode: ast.AST) -> bool: - """类体内是否有方法带方法级 @t.CVTable(对齐 TPV _has_any_func_cvtable)。 - - 跳过构造函数(__new__/__init__/__before_init__)——它们不进虚表。 - 用于"类的 vtable 是否由方法级标记触发"这一支(类级 NoVTable / 父类 NoVTable / - 无真实父类时,TPV _detect_vtable_status 返回 _has_any_func_cvtable)。 - """ - for Item in getattr(ClassNode, 'body', None) or []: - if not isinstance(Item, ast.FunctionDef): - continue - if Item.name in ('__new__', '__init__', '__before_init__'): - continue - if _DecoratorListHasMarker(getattr(Item, 'decorator_list', None), 'CVTable'): - return True - return False - - -def IsVtableSlotMethod(MethodNode: ast.AST, VtableByClassLevel: bool) -> bool: - """方法是否占 vtable 槽——与 TPV ClassGenBase._should_method_be_virtual 同判据: - - 1) 方法级 NoVTable ⇒ 不占(最高优先级) - 2) 方法级 CVTable ⇒ 占 - 3) vtable 来自类级 CVTable / 继承链(VtableByClassLevel,即**不看**方法级 - 标记算出来的 vtable 状态)⇒ 占 - 4) 其余(含"类的 vtable 仅由方法级标记触发":如普通类里只写了一个 - `virtual def`)⇒ 不占 - - VtableByClassLevel 必须由调用方按"忽略方法级标记"的口径计算(TPC: - HandlesClassDef._resolve_class_flags 返回的 VtableByClassLevel)。 - """ - Marker: str | None = MethodVirtualMarker(MethodNode) - if Marker == 'NoVTable': - return False - if Marker == 'CVTable': - return True - return bool(VtableByClassLevel) - - -def IsTModulePath(module_path: str) -> bool: - """检查模块路径是否指向 t 类型模块""" - if module_path in _T_MODULE_NAMES: - return True - if module_path: - t_name: str - for t_name in _T_MODULE_NAMES: - if module_path == t_name or module_path.startswith(t_name + '.'): - return True - return False - - -def AnnotationContainsTType(node: ast.AST, TypeName: str) -> bool: - """检测 AST 节点中是否包含指定名称的 t 模块类型""" - if isinstance(node, ast.Attribute): - if (hasattr(node.value, 'id') and IsTModule(node.value.id) and - node.attr == TypeName): - return True - elif isinstance(node, ast.BinOp): - return AnnotationContainsTType(node.left, TypeName) or AnnotationContainsTType(node.right, TypeName) - elif isinstance(node, ast.Call): - if AnnotationContainsTType(node.func, TypeName): - return True - for arg in node.args: - if AnnotationContainsTType(arg, TypeName): - return True - elif isinstance(node, ast.Subscript): - if AnnotationContainsTType(node.value, TypeName): - return True - if AnnotationContainsTType(node.slice, TypeName): - return True - return False - - -# 内建定长数组构造子的裸名(与 t.CArray 等价的写法): -# CArray —— from t import * 后的裸名形式 -# rawarray —— CArray 的内建复刻名(hashlib 等裸名化重构使用,免写 t. 前缀) -# array —— 本轮新增的内建语法保留字(软关键字,仅在类型/注解位置生效, -# 等价 t.CArray[E, N];非类型位置仍可作普通标识符——见 -# Debugger/array-builtin-spec.md 与 FixedArrayKeywordAnnotationError) -_FIXED_ARRAY_BARE_NAMES: tuple[str, ...] = ('CArray', 'rawarray', 'array') -_ARRAY_KEYWORD: str = 'array' - - -def IsFixedArrayBareName(Name: str) -> bool: - """裸名是否为内建定长数组构造子(CArray / rawarray / array)""" - return Name in _FIXED_ARRAY_BARE_NAMES - - -def IsListAnnotation(annotation: ast.expr) -> bool: - """检查注解是否为栈上固定数组类型(t.CArray[...] / 内建 array[...]) - - **不含 `list[...]`**:`list` 是 includes/_list.vp 定义的堆上 STL 容器泛型类 - (`list[T]` / `list[T, N]` 都走泛型特化),与定长数组无关。 - """ - if not isinstance(annotation, ast.Subscript): - return False - value = annotation.value - # t.CArray[...] / c.CArray[...] - if (isinstance(value, ast.Attribute) - and isinstance(value.value, ast.Name) - and value.value.id in ('t', 'c') - and value.attr == 'CArray'): - return True - # 裸名 CArray[...](from t import *)/ 内建 rawarray[...] / 内建 array[...]: - # 三者与 t.CArray 等价(array 为本轮新增的内建软关键字,见上方常量注释) - if (isinstance(value, ast.Name) - and value.id in _FIXED_ARRAY_BARE_NAMES): - return True - return False - - -def ExtractListFromBinOp(annotation: ast.expr) -> ast.Subscript | None: - """从 BinOp(BitOr) 注解中提取定长数组部分,如 t.CArray[i32] | t.CPtr""" - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - for side in (annotation.left, annotation.right): - if IsListAnnotation(side): - return side - return None - - -class ListAnnotationParseResult: - """定长数组注解(t.CArray / rawarray / array)解析结果。 - - is_pointer=True 表示单参数 CArray[type](等价 CArray[type, None])→ 指针模式; - is_pointer=False 表示 Tuple(2) 模式 CArray[type, count]。 - """ - __slots__ = ('elem_type_node', 'count_node', 'is_pointer') - - def __init__(self, elem_type_node: ast.expr, count_node: ast.expr | None, is_pointer: bool) -> None: - self.elem_type_node: ast.expr = elem_type_node - self.count_node: ast.expr | None = count_node - self.is_pointer: bool = is_pointer - - -def ParseListAnnotation(annotation: ast.expr) -> ListAnnotationParseResult | None: - """解析定长数组注解(t.CArray / rawarray / array),处理 CArray[type, count] / - CArray[type] / BinOp(BitOr) 形式。 - - 自动处理 BinOp(BitOr) 包裹(如 CArray[i32, N] | t.CPtr)。 - 返回 None 表示不是定长数组注解(`list[...]` 也返回 None —— 它是堆上 STL 容器)。 - """ - list_node: ast.expr = annotation - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - extracted: ast.Subscript | None = ExtractListFromBinOp(annotation) - if extracted is None: - return None - list_node = extracted - if not IsListAnnotation(list_node): - return None - slice_node: ast.AST = list_node.slice - if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: - return ListAnnotationParseResult(slice_node.elts[0], slice_node.elts[1], is_pointer=False) - # 单参数 list[type] → 指针模式 - return ListAnnotationParseResult(slice_node, None, is_pointer=True) - - -# --------------------------------------------------------------------------- -# 内建定长数组构造子 array 的注解校验(唯一规则源) -# -# array 是**软关键字**:仅在"类型/注解位置"作为内建定长数组构造子生效, -# 语义与 t.CArray 逐点等价(元素类型在前、长度在后)。非类型位置仍是普通 -# 标识符(如 includes/json 的 def array(...)、numpy 的 def array(...), -# 因此**不能**做成词法保留字)。 -# -# 与内建 rawptr 的机制对应:都"不可重定义"(在类型位置优先于任何用户声明), -# 但 array 因与既有标识符重名,只在类型位置生效(软关键字),而 rawptr 在 -# 各解析入口直接短路(硬内置名)。 -# -# 新写法要求**恰好两个参数** `array[E, N]`:缺参 / 多参 / 长度非正整型常量 -# 表达式均为 fail-fast(不静默降级)。t.CArray[E] 的单参/长度自动推导写法 -# 仍然可用(t.CArray 不受影响)。 -# --------------------------------------------------------------------------- -def _IsConstExprShape(Node: ast.AST) -> bool: - """节点是否为"常量表达式形态"(字面量 / 名字 / 属性 / 常量算术/位运算/一元)。 - - 用于 array 长度参数的**语法级**拒绝:调用/列表/比较/布尔/推导式等明显不是 - 编译期整数常量,直接判为误用;Name/Attribute(可能是 CDefine 常量或 - 跨模块常量)与算子组合保留,交由既有常量求值路径判定(避免前向引用误报)。 - """ - if isinstance(Node, (ast.Constant, ast.Name, ast.Attribute)): - return True - if isinstance(Node, ast.BinOp): - return _IsConstExprShape(Node.left) and _IsConstExprShape(Node.right) - if isinstance(Node, ast.UnaryOp): - return _IsConstExprShape(Node.operand) - return False - - -def FixedArrayKeywordAnnotationError(Annotation: ast.AST, - SymbolTable: object = None) -> str | None: - """校验内建定长数组构造子 array 的注解写法。 - - 返回 None 表示"合法"或"不是 array 注解";否则返回中文错误消息(不含 - 位置与源码片段——那两项由调用方按 @p Annotation 的 lineno/col_offset - 与源码行补齐)。SymbolTable 非空时对常量表达式长度做一次求值校验; - 求值不到(前向引用/声明期依赖未就绪)则**延迟**给下游常量求值路径, - 避免误报。 - """ - if Annotation is None: - return None - if isinstance(Annotation, ast.Name): - if Annotation.id == _ARRAY_KEYWORD: - return ("裸用内建定长数组构造子 'array'(缺少下标与参数)" - "——'array' 只在类型/注解位置生效,必须写成 array[E, N]" - "(元素类型在前、长度在后)。" - "期望形态:array[E, N];正确示例:state: array[uint32, 4]") - return None - if not isinstance(Annotation, ast.Subscript): - return None - base: ast.AST = Annotation.value - if not (isinstance(base, ast.Name) and base.id == _ARRAY_KEYWORD): - return None - SliceNode: ast.AST = Annotation.slice - if not isinstance(SliceNode, ast.Tuple): - return ("内建定长数组构造子 'array' 缺少长度参数(当前仅 1 个参数)" - "——必须同时给出元素类型与长度。" - "期望形态:array[E, N];正确示例:state: array[uint32, 4]") - if len(SliceNode.elts) != 2: - return (f"内建定长数组构造子 'array' 参数个数错误(当前 {len(SliceNode.elts)} 个,期望 2 个)" - "——只接受 array[E, N](E=元素类型,N=长度)。" - "期望形态:array[E, N];正确示例:state: array[uint32, 4]") - CountNode: ast.AST = SliceNode.elts[1] - if isinstance(CountNode, ast.Constant): - Value = CountNode.value - if not (isinstance(Value, int) and not isinstance(Value, bool) and Value > 0): - return ("内建定长数组构造子 'array' 的长度必须是正整型常量(>0)" - "——None/0/负数/非整数都不允许(长度自动推导请改用 t.CArray[E])。" - "期望形态:array[E, N];正确示例:state: array[uint32, 4]") - return None - if not _IsConstExprShape(CountNode): - return ("内建定长数组构造子 'array' 的长度必须是整型常量表达式" - "(字面量 / CDefine 常量名 / 常量算术或位运算表达式)" - "——函数调用/列表/比较等运行期表达式不允许。" - "期望形态:array[E, N];正确示例:state: array[uint8, MAX_LEN]") - if SymbolTable is not None: - try: - from lib.core.ConstEvaluator import ConstEvaluator - Value = ConstEvaluator.eval_with_symtab(CountNode, SymbolTable) - except Exception: - Value = None - if Value is not None and not (isinstance(Value, int) - and not isinstance(Value, bool) and Value > 0): - return ("内建定长数组构造子 'array' 的长度必须求值为正整型常量(>0)。" - "期望形态:array[E, N];正确示例:state: array[uint32, 4]") - return None - - -def CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool: - """检测注解节点中是否包含 CInline""" - if annotation_node is None: - return False - if isinstance(annotation_node, ast.Attribute): - if hasattr(annotation_node.value, 'id') and IsTModule(annotation_node.value.id) and annotation_node.attr == 'CInline': - return True - if isinstance(annotation_node, ast.Name): - if annotation_node.id == 'CInline': - return True - if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr): - return CheckAnnotationHasCInline(annotation_node.left) or CheckAnnotationHasCInline(annotation_node.right) - return False - - -def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None: - """从 BinOp(BitOr) 注解左侧提取类型名(如 TypeA | TypeB → TypeA)。 - - 返回 Name.id 或 Attribute.attr,非 BinOp 返回 None。 - """ - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - left: ast.expr = annotation.left - if isinstance(left, ast.Name): - return left.id - if isinstance(left, ast.Attribute): - return left.attr - return None - - -def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set, - TCAliasMap: dict[str, tuple[str, str]] | None = None, - StructSha1Map: dict[str, str] | None = None) -> str | None: - """递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。 - - 检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。 - 支持泛型特化类型(如 GSList[Param]):特化名按**同一规则源** - (ResolveTypeArgStr:sha1 前缀 + rawptr/CPtr 的 '*' 逐层)构造后在 - struct_names 中查找——查表键与 _mangle_generic_class_name 产物逐字一致, - 否则同一注解在"注解查找"与"特化注册"两处解析出不同名字(opaque 副本)。 - """ - if isinstance(node, ast.Name) and node.id in struct_names: - return node.id - if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value in struct_names: - return node.value - if isinstance(node, ast.Attribute) and node.attr in struct_names: - return node.attr - if isinstance(node, ast.Subscript): - base_name: str | None = None - if isinstance(node.value, ast.Name): - base_name = node.value.id - elif isinstance(node.value, ast.Attribute): - base_name = node.value.attr - if base_name: - slice_node: ast.AST = node.slice - if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index): - slice_node = slice_node.value - elem_nodes: list[ast.AST] = ( - list(slice_node.elts) if isinstance(slice_node, ast.Tuple) else [slice_node]) - type_args: list[str] = [ - ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in elem_nodes] - spec_name: str = base_name + '[' + ']['.join(type_args) + ']' - if spec_name in struct_names: - return spec_name - if base_name in struct_names: - return base_name - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - result: str | None = FindStructNameInAnnotation( - node.left, struct_names, TCAliasMap, StructSha1Map) - if result: - return result - return FindStructNameInAnnotation( - node.right, struct_names, TCAliasMap, StructSha1Map) - return None - - -def FindStructNameInAnnotationWithContext(Node: ast.AST, Trans: object, Gen: object, - StructNames: dict | set | None = None) -> str | None: - """按 Gen/Trans 上下文注入规则表后查找结构体名。 - - 规则本体唯一(FindStructNameInAnnotation),此处只做与 - HandlesExprCall._resolve_type_arg_str 同口径的上下文注入,避免各调用点 - 各写一套拼名(查表键必须与 _mangle_generic_class_name 产物逐字一致)。 - """ - Names: dict | set = StructNames if StructNames is not None else Gen.structs - return FindStructNameInAnnotation( - Node, Names, - TCAliasMap=getattr(Trans, '_t_c_imported_names', {}), - StructSha1Map=getattr(Gen, '_struct_sha1_map', {})) - - -def AnnotationContainsName(annotation: ast.AST, name: str) -> bool: - """检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。 - - 同时检查 Attribute.attr 和 Name.id,比 ast.dump 字符串匹配更精确。 - """ - if isinstance(annotation, ast.Attribute): - return annotation.attr == name - if isinstance(annotation, ast.Name): - return annotation.id == name - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - return AnnotationContainsName(annotation.left, name) or AnnotationContainsName(annotation.right, name) - return False - - -def IsCPtrNode(node: ast.AST) -> bool: - """检查节点是否为 t.CPtr 属性引用。""" - return (isinstance(node, ast.Attribute) - and isinstance(node.value, ast.Name) - and node.value.id == 't' - and node.attr == 'CPtr') - - -# 值引用参数标记名 → 自动取址模式(仅注解 AST 内按名字识别,非真实类): -# 'CRef' → 'ref' (&T :实参非指针左值取址,右值 fail-fast) -# 'CTempRef' → 'temp' (&temp T :实参非指针时在调用方建同型临时再取址) -# 'CAutoPtr' → 'always' (t.CAutoPtr:无条件取址,保持既有语义) -_AUTO_ADDR_MARKER_MODES: dict[str, str] = { - 'CRef': 'ref', 'CTempRef': 'temp', 'CAutoPtr': 'always', -} - - -def FindAutoAddrMode(node: ast.AST | None) -> str | None: - """注解 AST 内查找自动取址模式标记,返回 'ref' / 'temp' / 'always';无则 None。 - - 只按名字识别(Attribute.attr 或 Name.id),递归联合/嵌套注解;CRef/CTempRef - 非真实类,不出现在 lib/includes/t.py。 - """ - if node is None: - return None - if isinstance(node, ast.Attribute): - mode = _AUTO_ADDR_MARKER_MODES.get(node.attr) - if mode is not None: - return mode - elif isinstance(node, ast.Name): - mode = _AUTO_ADDR_MARKER_MODES.get(node.id) - if mode is not None: - return mode - for child in ast.iter_child_nodes(node): - mode = FindAutoAddrMode(child) - if mode is not None: - return mode - return None - - -def IsCPtrSubscriptNode(node: ast.AST) -> bool: - """检查节点是否为 t.CPtr[T] 下标指针类型(非裸标记,裸标记见 IsCPtrNode)。 - - 与 TPV TypeGenResolve._is_cptr_subscript(TypeGenResolve.vp:1485-1505)同口径: - value 必须是 t.CPtr 属性引用(只认 t 前缀,c.CPtr[...] 不认); - 切片内的指针层级不限(t.CPtr[t.CPtr]、t.CPtr[t.CPtr[t.CInt]] 均命中)。 - 链式写法 t.CPtr[T][T](value 是 Subscript)两套编译器均不认作该标记。 - """ - return (isinstance(node, ast.Subscript) - and IsCPtrNode(node.value)) - - -# 指针下标的 base 名:rawptr 是编译器内置语法(TPC/TPV 双端保留字,见 -# includes/builtins.vp 语义注释与 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME), -# 单独 rawptr 等价 t.CPtr(void*)。 -_RAWPTR_SUBSCRIPT_NAME: str = 'rawptr' - -# 指针标记类型名(t.CPtr):裸名书写 `CPtr[T]` / 导入别名 `X`(from t import CPtr as X) -# 都归一到本名。 -_CPTR_TYPE_NAME: str = 'CPtr' - - -def ResolveTCImportAliasName(NameId: str, symtable: object | None = None, - TCAliasMap: dict[str, tuple[str, str]] | None = None) -> str: - """t/c 导入别名 → 原始类型名('X' → 'CPtr'、'myint' → 'CInt');未命中原样返回。 - - 两张同源别名表(注册点唯一:HandlesImports._EmitImportFromDeclarationsLlvm / - _RegisterFromImportAliases,与 SymbolTable 序列化),调用方按手头有的表注入: - - TCAliasMap:{别名: (模块, 原名)}(Trans._t_c_imported_names / - DeclarationGenerator._t_c_imported_names 同结构) - - symtable._t_type_symbols:{别名: t.CType 子类}(from t import CPtr as X → t.CPtr) - 非 t/c 模块的 from 导入别名(from ast import AST as A)不参与归一。 - """ - if not NameId: - return NameId - if TCAliasMap: - Entry: tuple[str, str] | None = TCAliasMap.get(NameId) - if Entry: - SrcModule, SrcName = Entry - if IsTModule(SrcModule): - return SrcName - if symtable is not None: - Cls: object | None = getattr(symtable, '_t_type_symbols', {}).get(NameId) - if Cls is not None: - return getattr(Cls, '__name__', NameId) - return NameId - - -def IsRawPtrSubscriptNode(node: ast.AST) -> bool: - """检查节点是否为内置裸指针下标 `rawptr[T]`(非裸标记 rawptr)。 - - `rawptr[T]` 语义 = `t.CPtr[T]` = 源码联合写法 `T | t.CPtr`(Ptr(T)),三者 - 必须走同一解析与拼名路径,否则同一类型的不同书写会分裂出不同特化名 - (结构体/方法声明/调用点三处不一致)。 - 注意:本判据**只**认 `rawptr[T]`;`t.CPtr[T]` / 裸名 `CPtr[T]` 由 - IsPtrSubscriptNode 判定(见下),二者共用同一条脱糖规则 - (DesugarPtrSubscriptToUnion),此处保持 rawptr 专用以便仍需区分书写时使用。 - """ - return (isinstance(node, ast.Subscript) - and isinstance(node.value, ast.Name) - and node.value.id == _RAWPTR_SUBSCRIPT_NAME) - - -def IsI8DoublePtrType(T: object) -> bool: - """是否为 **i8 系两层指针**(`i8**`)—— 即 `T | t.CPtr` 在 T 的类型丢失后**退化**成的形状。 - - 与 "T 是有类型的上层指针" 无关:只认 `i8**`(`Ptr(Ptr(i8))`)。用于识别 - 「类引用指针被解析成通用两层指针」这类**类型信息丢失**。 - """ - import llvmlite.ir as ir - return (isinstance(T, ir.PointerType) - and isinstance(T.pointee, ir.PointerType) - and isinstance(T.pointee.pointee, ir.IntType) - and T.pointee.pointee.width == 8) - - -def IsClassRefPtrAnnotation(Ann: ast.AST, symtable: object | None = None) -> bool: - """注解是否为「**指针包装的类引用**」:`rawptr[Mod.Class]` / `Mod.Class | t.CPtr`。 - - 只回答"这个注解**本应**解析出结构体指针",不做任何类型解析,判据保守: - - 必须是指针包装:`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]`,或含 `t.CPtr` - 标记的联合写法 `T | t.CPtr`; - - 内层须是类引用形态:`Attribute` 且**根模块不是 `t` / `c`** - (如 `ExprGenFstr.ExprGenFstr`、`llvmlite.LLVMType`),或首字母大写的裸名 - (类名书写惯例)。 - 模块名、`rawptr` / `CPtr` 标记本身、泛型特化实参(内层是 Subscript)、 - `str` / `int` 这类小写内建名一律**不算**。 - """ - Inner: ast.AST | None = None - if IsRawPtrSubscriptNode(Ann) or IsCPtrSubscriptNode(Ann): - Inner = Ann.slice - elif isinstance(Ann, ast.BinOp) and isinstance(Ann.op, ast.BitOr): - for Side in (Ann.left, Ann.right): - if IsCPtrMarkerNode(Side, symtable): - continue - if Inner is not None: - return False - Inner = Side - else: - return False - if isinstance(Inner, ast.Attribute): - Cur: ast.AST = Inner - while isinstance(Cur, ast.Attribute): - Cur = Cur.value - Root: str = Cur.id if isinstance(Cur, ast.Name) else '' - return Root not in ('t', 'c') - if isinstance(Inner, ast.Name): - return bool(Inner.id) and Inner.id[0].isupper() - return False - - -def IsPtrSubscriptNode(node: ast.AST, symtable: object | None = None, - TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: - """检查节点是否为"指针下标类型":`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` - / 导入别名 `X[T]`(`from t import CPtr as X`)。 - - 三者语义完全相同(= 源码联合写法 `T | t.CPtr` = Ptr(T),见 - builtins.vp 与 t.py 的 CPtr 语义注释):内层 T 按**参数注解 `a: T` 的规则** - 解析(跨模块类 → '.X'),再包一层指针——T 为类引用时该层被结构体 - 自身吸收('.X*'),其余(标量 / 已是指针的 typedef / 泛型容器特化 / - 嵌套指针下标)逐层外包。 - - - 裸名 `CPtr[T]`(`from t import CPtr` / `from t import *`)与声明端 - DeclarationGenerator._get_type_str 的 ValueIsCPtr 判据一致(那里同样接受 - Attribute 'CPtr' 与 Name 'CPtr' 两种书写)。 - - 传入 symtable 时,`import t as tt` 的 `tt.CPtr[T]` 一并命中(与声明端 - "只看 attr 名"的 ValueIsCPtr 判据对齐,避免同一书写两端分裂); - 不传(名称域 ResolveTypeArgStr 的字面名场景)时只认字面 't'。 - - 导入名别名(`from t import CPtr as X` 的 `X[T]`)经别名表解析 X→CPtr - (TCAliasMap 或 symtable._t_type_symbols,见 ResolveTCImportAliasName), - 定义端与声明端各自注入手头的同一批表,判据只有这一处。 - """ - if IsRawPtrSubscriptNode(node): - return True - if not isinstance(node, ast.Subscript): - return False - value: ast.AST = node.value - if isinstance(value, ast.Name) and value.id == _CPTR_TYPE_NAME: - return True - return IsCPtrMarkerNode(value, symtable, TCAliasMap) - - -def DesugarPtrSubscriptToUnion(node: ast.AST, symtable: object | None = None, - TCAliasMap: dict[str, tuple[str, str]] | None = None) -> ast.AST: - """把指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / 别名 `X[T]`) - 折叠成等价的联合写法 `T | t.CPtr`。 - - 内层 T 递归折叠(`rawptr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`、 - `t.CPtr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`), - 与 TPV TypeGenResolve.resolve_annotation_type 的 Subscript 分支同语义 - (T → Ptr(T),层数逐层外包);折叠后交给 HandlesTypeMerge 既有的 - 联合解析(_CollectAllOperandTypes 会把嵌套 BitOr 拉平成 [T, t.CPtr, ...], - struct 基类型吸收一层、其余逐层累加),从而: - - 内层 T 走通用类型解析(含跨模块类经 Gen.structs 反查 SHA1 前缀); - - `rawptr[X]` / `t.CPtr[X]` / `X[T]` / `X | t.CPtr` 得到同一 CTypeInfo - (PtrCount/BaseType 一致)与同一泛型实参串(逐层 '*')。 - - 非指针下标节点原样返回(调用方无需先判类型);symtable/TCAliasMap 透传给 - 判据(识别 `import t as tt` 的 `tt.CPtr[T]` 与 `from t import CPtr as X` 的 - `X[T]` 内层嵌套)。折叠产物统一用字面 `t.CPtr` 标记,与源码联合写法同形。 - """ - if not IsPtrSubscriptNode(node, symtable, TCAliasMap): - return node - inner: ast.AST = DesugarPtrSubscriptToUnion(node.slice, symtable, TCAliasMap) - cptr_marker: ast.AST = ast.Attribute( - value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - union_node: ast.AST = ast.BinOp(left=inner, op=ast.BitOr(), right=cptr_marker) - try: - ast.copy_location(union_node, node) - except Exception: - pass - return union_node - - -# --------------------------------------------------------------------------- -# 泛型实参串(mangling):定义端与声明端唯一规则源 -# --------------------------------------------------------------------------- -# 同一泛型特化(如 list[rawptr[ast.AST]])在 define 端发射的结构体名 -# (HandlesClassDef._mangle_generic_class_name → %".list[ast.ASTptr]")、 -# 各端方法名、以及 declare/stub 端引用的类型名必须逐字一致,否则会出现 -# Undefined method / 签名错位。此前 DeclarationGenerator._get_type_str 的泛型 -# 分支自带第二套(只认 Name / Name-Tuple 实参的)规则,其余形态(Attribute / -# Subscript / BinOp / 混合 Tuple)退回未特化的 %".list"*,与 define 端 -# 不一致。故此处收拢唯一规则,两端皆调用本组函数。 - -# 定义端 _resolve_generic_slice_type 的 ast.Name 规范化表: -# 源码里的 C 定长类型名 → 喂给 _mangle_generic_class_name 的 type_arg。 -_GENERIC_ARG_INT_NAMES: frozenset[str] = frozenset({ - 'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', - 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong', -}) -_GENERIC_ARG_FLOAT_NAMES: frozenset[str] = frozenset({ - 'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T', -}) -# 源码名 → 泛型 T 替换用的原始类型名(_resolve_generic_slice_type 的第二返回值) -_GENERIC_ARG_RAW_NAMES: dict[str, str] = { - 'int': 'CInt', 'double': 'CDouble', 'float': 'CFloat', - 'char': 'CChar', 'bool': 'CBool', -} -# _mangle_generic_class_name 的浮点归一集合(LLVM 浮点串 → 'f' + Size) -_LLVM_FLOAT_STRINGS: frozenset[str] = frozenset({'float', 'double', 'half', 'fp128'}) - - -def NormalizeGenericArgName(NameId: str) -> str: - """ast.Name 泛型实参 → 定义端 type_arg(HandlesExprCall._resolve_generic_slice_type - 的 ast.Name 分支,逐分支同构): - int/double/float 保留源码名('list[int]' 而非 'list[i32]')、 - char/bool → 'i8'、C 整数族 → 'int'、C 浮点族 → 'double'、其余原名。 - """ - if NameId in ('int', 'double', 'float'): - return NameId - if NameId in ('char', 'bool'): - return 'i8' - if NameId in _GENERIC_ARG_INT_NAMES: - return 'int' - if NameId in _GENERIC_ARG_FLOAT_NAMES: - return 'double' - return NameId - - -def GenericArgRawName(NameId: str) -> str: - """ast.Name 泛型实参 → 替换模板 T 用的原始类型名(_resolve_generic_slice_type - 的第二返回值):'int' → 'CInt'(内建类型须用源码名替换 T,见 - HandlesClassDef._specialize_generic_class 的 _BUILTIN_TYPE_NAMES 判定), - 其余原名。仅定义端 T 替换用;mangling 只用 NormalizeGenericArgName。 - """ - return _GENERIC_ARG_RAW_NAMES.get(NameId, NameId) - - -def MangleTypeArgByRegistry(ArgStr: str) -> str: - """实参串 → 最终 mangled 实参(HandlesClassDef._mangle_generic_class_name - 的单项逻辑,逐分支同构):t 类型名经 NameToLLVM 归一(CInt32T → 'i32', - 't.CInt'/'c.CInt' 先去模块前缀再归一),浮点按 Size 归一为 f{n} - (CDouble → 'f8'),未注册名原样保留(含指针层数与已解析的内层名)。 - """ - LookupStr: str = ArgStr - if ArgStr.startswith('t.') or ArgStr.startswith('c.'): - LookupStr = ArgStr[2:] - LlvmStr: str | None = CTypeRegistry.NameToLLVM(LookupStr) - if not LlvmStr: - return ArgStr - if LlvmStr in _LLVM_FLOAT_STRINGS: - Cls: type | None = CTypeRegistry.GetClassByName(LookupStr) - Size: int = Cls().Size if Cls is not None else 0 - return 'f' + str(Size) - return LlvmStr - - -# 指针层数的首层归一化(与 LLVMTypeMapper._add_pointer_level 同口径) -_POINTER_PLACEHOLDER_BASES: frozenset[str] = frozenset({'', 'void', 'void*'}) - - -def AddPointerLayers(TypeStr: str, Layers: int) -> str: - """给已解析的类型串追加 Layers 层指针(空/void/void* 的首层归一化为 i8*)。 - - 与 LLVMTypeMapper._add_pointer_level 同口径;名称域与类型域因此对 - rawptr[t.CVoid] 一致地给出 i8*(唯一允许的 void 兜底)。 - """ - Result: str = TypeStr - for _ in range(Layers): - if Result in _POINTER_PLACEHOLDER_BASES: - Result = 'i8*' - else: - Result = Result + '*' - return Result - - -def IsCPtrMarkerNode(Node: ast.AST, symtable: object | None = None, - TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: - """`t.CPtr` 裸指针标记(含 `import t as tt` 与 `from t import CPtr as X` 别名): - 联合写法里贡献一层 '*'。 - - 与 IsCPtrNode 的区别:仅按 attr 名 + t 模块前缀判定,不看切片 - (`t.CPtr[T]` 下标是**基类型**而非层数标记,见 ResolveTypeArgStr 的 - Subscript 分支)。 - - Attribute:`t.CPtr` / `tt.CPtr`(t 模块别名经 symtable.import_aliases 解析)。 - - Name:经别名表归一为 'CPtr' 的书写名(`from t import CPtr as X` 的 `X`); - 字面裸名 `CPtr` 是否算标记取决于该模块是否 `from t import CPtr` - (别名表命中一致:两端都由同一批表判定,不另设白名单)。 - """ - if isinstance(Node, ast.Attribute): - return (Node.attr == _CPTR_TYPE_NAME - and isinstance(Node.value, ast.Name) - and IsTModule(Node.value.id, symtable)) - if isinstance(Node, ast.Name): - return ResolveTCImportAliasName(Node.id, symtable, TCAliasMap) == _CPTR_TYPE_NAME - return False - - -def FlattenUnionOperands(Node: ast.AST) -> list[ast.AST]: - """拉平嵌套 BitOr,按书写顺序返回操作数(`A | t.CPtr | t.CPtr` → [A, CPtr, CPtr])。""" - if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): - return FlattenUnionOperands(Node.left) + FlattenUnionOperands(Node.right) - return [Node] - - -def ResolveTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, - StructSha1Map: dict[str, str] | None = None) -> str: - """将类型参数 AST 节点解析为规范化的类型字符串(泛型 mangling 用)。 - - 规则(**与参数注解 `a: T` 完全同一套解析**,定义端/声明端唯一规则源): - - ast.Name: t/c 导入别名 → '{module}.{name}';已注册类 → '{sha1}.{名}' - (与参数注解的 %"{sha1}.X" 同前缀);其余按 NormalizeGenericArgName - 归一('int'/'i32'/'str' 原名、'char'/'bool' → 'i8'、C 整数/浮点族 → - 'int'/'double') - - ast.Attribute: t 模块类型按 CTypeRegistry.NameToLLVM 解析(t.CPtr → i8*、 - t.CInt → i32);跨模块类引用 module.X → '{sha1}.X';其余 '{mod}.{attr}' - - ast.Subscript: 指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / - 导入别名 `X[T]`)已在入口脱糖为上面的联合写法(走同一规则),故此处只剩泛型容器: - '<容器名>[<已解析实参>]'(多实参 '][' 分隔),容器名的 - 模块前缀由调用方(_mangle_name / '%"{模sha1}.{特化名}"')统一加 - - ast.BinOp(BitOr)(含指针下标脱糖而来): 取非 CPtr 侧为基类型, - 每个 `t.CPtr` 标记追加一层 '*'(`X | t.CPtr` ≡ `rawptr[X]` ≡ - `t.CPtr[X]` → '.X*';`rawptr[rawptr[X]]` → '.X**'; - 标量 `int | t.CPtr` → 'int*') - - 其余形态: ast.unparse - - 注意:名称域**不做**"裸类引用吸收一层 CPtr"(那是注解→LLVM 类型域的规则, - 见 DeclarationGenerator._is_bare_class_ref_annotation):名称域描述的是类型 - 身份('X' = X 本身、'X*' = 指向 X 的指针),逐层累加,否则 - `rawptr[X]` 与 `X | t.CPtr` 会分裂成两个名字。 - """ - if IsPtrSubscriptNode(Node, TCAliasMap=TCAliasMap): - Node = DesugarPtrSubscriptToUnion(Node, TCAliasMap=TCAliasMap) - if isinstance(Node, ast.Name): - tn: str = Node.id - if TCAliasMap and tn in TCAliasMap: - # t 模块别名(from t import CInt as myint)按内建归一表解析到同一名字 - # ('myint' 与 'CInt' 都 → 'int'),避免同义写法分裂特化名 - SrcModule, SrcName = TCAliasMap[tn] - if IsTModule(SrcModule): - return NormalizeGenericArgName(SrcName) - return f'{SrcModule}.{SrcName}' - if StructSha1Map and tn in StructSha1Map: - return f'{StructSha1Map[tn]}.{tn}' - return NormalizeGenericArgName(tn) - if isinstance(Node, ast.Attribute): - return _ResolveAttributeArgStr(Node, TCAliasMap, StructSha1Map) - if isinstance(Node, ast.Subscript): - return _ResolveSubscriptArgStr(Node, TCAliasMap, StructSha1Map) - if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): - return _ResolveUnionArgStr(Node, TCAliasMap, StructSha1Map) - return ast.unparse(Node) - - -def _ResolveAttributeArgStr(Node: ast.Attribute, TCAliasMap: dict[str, tuple[str, str]] | None, - StructSha1Map: dict[str, str] | None) -> str: - """Attribute 形态(t.CInt / ast.AST / .AST)→ 实参串。""" - if not isinstance(Node.value, ast.Name): - return ast.unparse(Node) - ModName: str = Node.value.id - AttrName: str = Node.attr - if IsTModule(ModName) or (TCAliasMap and ModName in TCAliasMap): - LlvmStr: str | None = CTypeRegistry.NameToLLVM(AttrName) - if LlvmStr is not None: - return LlvmStr - return f'{ModName}.{AttrName}' - # 跨模块类引用(ast.AST / .AST):与参数注解同一条 sha1 解析 - if StructSha1Map and AttrName in StructSha1Map: - return f'{StructSha1Map[AttrName]}.{AttrName}' - return f'{ModName}.{AttrName}' - - -def _ResolveSubscriptArgStr(Node: ast.Subscript, TCAliasMap: dict[str, tuple[str, str]] | None, - StructSha1Map: dict[str, str] | None) -> str: - """下标形态(t.CPtr[T] / 泛型容器 list[X] / dict[K,V])→ 实参串。 - - 容器名('list' / 'dict' ...)**不带**模块前缀:它只是 _mangle_generic_class_name - 的基名,特化名的模块前缀由调用方统一加(定义端 _mangle_name / 声明端 - '%"{模sha1}.{特化名}"'),并保证 '_make_replacement_node' 能把实参串 - 重新解析成可用的注解 AST('.list[...]' 会因数字开头而解析失败)。 - """ - if IsCPtrSubscriptNode(Node): - Inner: str = ResolveTypeArgStr(Node.slice, TCAliasMap, StructSha1Map) - return AddPointerLayers(Inner, 1) - BaseName: str = '' - if isinstance(Node.value, ast.Name): - BaseName = Node.value.id - elif isinstance(Node.value, ast.Attribute): - BaseName = Node.value.attr - if not BaseName: - return ast.unparse(Node) - SliceNode: ast.AST = Node.slice - ElemNodes: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode] - Resolved: list[str] = [ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in ElemNodes] - return f'{BaseName}[' + ']['.join(Resolved) + ']' - - -def _ResolveUnionArgStr(Node: ast.BinOp, TCAliasMap: dict[str, tuple[str, str]] | None, - StructSha1Map: dict[str, str] | None) -> str: - """联合形态(X | t.CPtr | ...)实参串:非 CPtr 侧为基类型,CPtr 标记逐层加 '*'。 - - 标记判据是同一处 IsCPtrMarkerNode(含 `from t import CPtr as X` 的裸标记别名), - 别名经 TCAliasMap 解析;没带别名表的 `import t as tt` 的 `tt.CPtr` 在此域 - 不做模块别名解析(两端口径一致的既有缺口)。 - """ - BaseType: str = '' - Layers: int = 0 - for Operand in FlattenUnionOperands(Node): - if IsCPtrMarkerNode(Operand, TCAliasMap=TCAliasMap): - Layers += 1 - elif not BaseType: - BaseType = ResolveTypeArgStr(Operand, TCAliasMap, StructSha1Map) - if not BaseType: - # 只有 CPtr 标记(如 `t.CPtr | t.CPtr`):等价 void* 逐层——首层 i8* - return AddPointerLayers('void', Layers) - return AddPointerLayers(BaseType, Layers) - - -def GetStructSha1MapFromSymtab(Symtab: object | None) -> dict[str, str]: - """从符号表取「类名 → 定义模块 sha1」表(Phase2 在 Convert 前挂到 translator)。""" - _Tr: Any = getattr(Symtab, 'translator', None) - _Map: Any = getattr(_Tr, '_struct_sha1_map', None) if _Tr is not None else None - return _Map if isinstance(_Map, dict) else {} - - -def GetTCAliasMapFromSymtab(Symtab: object | None) -> dict | None: - """从符号表取 t/c 导入别名表(`from t import CInt as myint`)。缺则 None。""" - _Tr: Any = getattr(Symtab, 'translator', None) - _Map: Any = getattr(_Tr, '_t_c_imported_names', None) if _Tr is not None else None - return _Map if isinstance(_Map, dict) and _Map else None - - -def IsGenericClassSubscriptBase(Node: ast.AST, Symtab: object | None = None, - StructSha1Map: dict[str, str] | None = None) -> bool: - """下标基名是否为「有类定义的泛型类」(内建 list/dict/set… 或用户泛型类)。 - - 判据与 DeclarationGenerator._get_type_str 的泛型分支(L1393-1423)同一条规则源: - **该名有类定义**即按泛型特化处理。内建容器本身在 includes/_list.vp / _dict.vp 等 - 就是类(实测符号表 `list` 条目 IsStruct=True),故本判据等价覆盖声明端的 - `non_struct_types ∪ _generic_class_names`;用户泛型类(Box[T])同样命中。 - - 只认 Name 形态:`t.CPtr[...]` / `t.CArray[...]` / 裸 `array[...]` / - `rawptr[...]` 等由调用方各自分支先行处理,不走这里。 - """ - if not isinstance(Node, ast.Name) or not Node.id: - return False - if StructSha1Map and Node.id in StructSha1Map: - return True - if Symtab is not None: - _Entry: Any = None - try: - if hasattr(Symtab, 'lookup'): - _Entry = Symtab.lookup(Node.id) - elif hasattr(Symtab, 'get'): - _Entry = Symtab.get(Node.id) - except Exception: - _Entry = None - if _Entry is not None and getattr(_Entry, 'IsStruct', False): - return True - for _Ns in (getattr(Symtab, '_namespaces', {}) or {}).values(): - if not isinstance(_Ns, dict): - continue - _Cand: Any = _Ns.get(Node.id) - if _Cand is not None and getattr(_Cand, 'IsStruct', False): - return True - return False - - -def ResolveGenericClassSpecName(Node: ast.Subscript, Symtab: object | None = None, - StructSha1Map: dict[str, str] | None = None, - TCAliasMap: dict | None = None) -> str | None: - """泛型类下标注解 → 声明端同口径的**特化名**('list[str]' / 'dict[str][int]')。 - - 与 DeclarationGenerator._get_type_str 的泛型分支(L1393-1423)逐字同口径: - spec = 基名 + '[' + ']['.join(实参串) + ']' - 实参串走 ResolveGenericTypeArgStr(与定义端 _mangle_generic_class_name 同一规则源)。 - 非泛型类下标(指针下标 / 定长数组 / t 模块类型 / 未知名)返回 None,由调用方各自 - 既有分支处理(调用点必须放在那些分支**之后**)。 - - 必要性:单参泛型下标此前在两个注解解析器里都 miss(TypeAnnotationResolver 只认 - 双参定长 t.CArray[T, N];LLVMTypeMapper 亦然)⇒ 落到"未知类型"兜底 i32 / 空 CTypeInfo, - 形参与成员类型整体错位(现场:ClassGenGeneric._specialize_generic_class 的 - 形参 `type_args: list[str]*` 在符号表里成 i32;字段 - `TypeGenBase.CTypedefNames: list[str] | t.CPtr` 成 i8*)。 - """ - if not isinstance(Node, ast.Subscript): - return None - Base: ast.AST = Node.value - if not IsGenericClassSubscriptBase(Base, Symtab, StructSha1Map): - return None - SliceNode: ast.AST = Node.slice - Args: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode] - if not Args: - return None - Mangled: list[str] = [ - ResolveGenericTypeArgStr(A, TCAliasMap, StructSha1Map) for A in Args] - if not any(Mangled): - return None - return Base.id + '[' + ']['.join(Mangled) + ']' - - -def ResolveGenericTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, - StructSha1Map: dict[str, str] | None = None) -> str: - """泛型实参的最终 mangled 串(喂给 _mangle_generic_class_name 的单参数)。 - - 与定义端(HandlesExprCall._resolve_generic_slice_type + _mangle_generic_class_name) - 对单个实参的实际结果同一套规则: - - ast.Name: NormalizeGenericArgName →(命中注册类则 '{sha1}.{名}')→ MangleTypeArgByRegistry - ('int' → 'int'、'i32' → 'i32'、'str' → 'str'、'CInt32T' → 'int'、 - 'CFloat64T' → 'double'、'CChar' → 'i8') - - 其余形态(Attribute / Subscript(含 rawptr[...])/ BinOp / 混合 Tuple 元素): - ResolveTypeArgStr(rawptr[T] 脱糖 → 逐层 '*'、t.CPtr → 'i8*'、跨模块类 → sha1 前缀) - → MangleTypeArgByRegistry - """ - return MangleTypeArgByRegistry(ResolveTypeArgStr(Node, TCAliasMap, StructSha1Map)) +def IsTModule(module_id: str, symtable: object | None = None) -> bool: + """检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)""" + if module_id in _T_MODULE_NAMES: + return True + if symtable is not None and hasattr(symtable, 'import_aliases'): + resolved: str = symtable.import_aliases.get(module_id, module_id) + if resolved in _T_MODULE_NAMES: + return True + return False + + +# --------------------------------------------------------------------------- +# virtual / novirtual 修饰符(软关键字)的判据 —— TPC 侧**唯一规则源** +# --------------------------------------------------------------------------- +# 源码形态 等价既有写法 语义 +# virtual class C: @t.CVTable class C: 类级:强制有 vtable +# novirtual class C: @t.NoVTable class C: 类级:强制无 vtable +# virtual def m(...) @t.CVTable def m(...) 方法级:占 vtable 槽 +# novirtual def m(...) @t.NoVTable def m(...) 方法级:不占 vtable 槽 +# +# 解析层(lib/vast/Parser.py:_ParseVirtualModified)只合成同名装饰器,**不新造第二套 +# 表示**;语义全部落在既有点位,本节函数是那些点位共用的同一判据(避免多处各写一份 +# 而漂移),逐条对齐 VPC(Viper 自举编译器)的既有实现: +# - ClassHasFuncLevelCVTable ←→ VPC ClassGenBase._has_any_func_cvtable +# - MethodVirtualMarker ←→ VPC ClassGenBase._should_method_be_virtual 第 1/2 条 +# - IsVtableSlotMethod ←→ VPC ClassGenBase._should_method_be_virtual 全 5 条 +# +# 回归性:判据只在"方法带 CVTable/NoVTable 标记"时偏离原行为;全仓既有 .vp 无任何 +# 方法级标记(普查结论见报告),故对既有类/既有代码零影响。 +def _DecoratorListHasMarker(DecoratorList: object, MarkerName: str) -> bool: + """装饰器表中是否存在指定标记名(接受 @t.X / @X / @t.X() / @X() 四种既有写法)。""" + if not DecoratorList: + return False + for Dec in DecoratorList: + Target: object = Dec + if isinstance(Dec, ast.Call): + Target = Dec.func + if isinstance(Target, ast.Attribute): + if Target.attr == MarkerName: + return True + elif isinstance(Target, ast.Name): + if Target.id == MarkerName: + return True + return False + + +def MethodVirtualMarker(MethodNode: ast.AST) -> str | None: + """方法级 virtual/novirtual 标记 → 'CVTable' / 'NoVTable' / None。 + + NoVTable 优先(与 VPC _should_method_be_virtual 第 1 条同序):同一方法同时带 + 两种标记时按"排除"处理。 + """ + DecList: object = getattr(MethodNode, 'decorator_list', None) + if _DecoratorListHasMarker(DecList, 'NoVTable'): + return 'NoVTable' + if _DecoratorListHasMarker(DecList, 'CVTable'): + return 'CVTable' + return None + + +def ClassHasFuncLevelCVTable(ClassNode: ast.AST) -> bool: + """类体内是否有方法带方法级 @t.CVTable(对齐 VPC _has_any_func_cvtable)。 + + 跳过构造函数(__new__/__init__/__before_init__)——它们不进虚表。 + 用于"类的 vtable 是否由方法级标记触发"这一支(类级 NoVTable / 父类 NoVTable / + 无真实父类时,VPC _detect_vtable_status 返回 _has_any_func_cvtable)。 + """ + for Item in getattr(ClassNode, 'body', None) or []: + if not isinstance(Item, ast.FunctionDef): + continue + if Item.name in ('__new__', '__init__', '__before_init__'): + continue + if _DecoratorListHasMarker(getattr(Item, 'decorator_list', None), 'CVTable'): + return True + return False + + +def IsVtableSlotMethod(MethodNode: ast.AST, VtableByClassLevel: bool) -> bool: + """方法是否占 vtable 槽——与 VPC ClassGenBase._should_method_be_virtual 同判据: + + 1) 方法级 NoVTable ⇒ 不占(最高优先级) + 2) 方法级 CVTable ⇒ 占 + 3) vtable 来自类级 CVTable / 继承链(VtableByClassLevel,即**不看**方法级 + 标记算出来的 vtable 状态)⇒ 占 + 4) 其余(含"类的 vtable 仅由方法级标记触发":如普通类里只写了一个 + `virtual def`)⇒ 不占 + + VtableByClassLevel 必须由调用方按"忽略方法级标记"的口径计算(TPC: + HandlesClassDef._resolve_class_flags 返回的 VtableByClassLevel)。 + """ + Marker: str | None = MethodVirtualMarker(MethodNode) + if Marker == 'NoVTable': + return False + if Marker == 'CVTable': + return True + return bool(VtableByClassLevel) + + +def IsTModulePath(module_path: str) -> bool: + """检查模块路径是否指向 t 类型模块""" + if module_path in _T_MODULE_NAMES: + return True + if module_path: + t_name: str + for t_name in _T_MODULE_NAMES: + if module_path == t_name or module_path.startswith(t_name + '.'): + return True + return False + + +def AnnotationContainsTType(node: ast.AST, TypeName: str) -> bool: + """检测 AST 节点中是否包含指定名称的 t 模块类型""" + if isinstance(node, ast.Attribute): + if (hasattr(node.value, 'id') and IsTModule(node.value.id) and + node.attr == TypeName): + return True + elif isinstance(node, ast.BinOp): + return AnnotationContainsTType(node.left, TypeName) or AnnotationContainsTType(node.right, TypeName) + elif isinstance(node, ast.Call): + if AnnotationContainsTType(node.func, TypeName): + return True + for arg in node.args: + if AnnotationContainsTType(arg, TypeName): + return True + elif isinstance(node, ast.Subscript): + if AnnotationContainsTType(node.value, TypeName): + return True + if AnnotationContainsTType(node.slice, TypeName): + return True + return False + + +# 内建定长数组构造子的裸名(与 t.CArray 等价的写法): +# CArray —— from t import * 后的裸名形式 +# rawarray —— CArray 的内建复刻名(hashlib 等裸名化重构使用,免写 t. 前缀) +# array —— 本轮新增的内建语法保留字(软关键字,仅在类型/注解位置生效, +# 等价 t.CArray[E, N];非类型位置仍可作普通标识符——见 +# Debugger/array-builtin-spec.md 与 FixedArrayKeywordAnnotationError) +_FIXED_ARRAY_BARE_NAMES: tuple[str, ...] = ('CArray', 'rawarray', 'array') +_ARRAY_KEYWORD: str = 'array' + + +def IsFixedArrayBareName(Name: str) -> bool: + """裸名是否为内建定长数组构造子(CArray / rawarray / array)""" + return Name in _FIXED_ARRAY_BARE_NAMES + + +def IsListAnnotation(annotation: ast.expr) -> bool: + """检查注解是否为栈上固定数组类型(t.CArray[...] / 内建 array[...]) + + **不含 `list[...]`**:`list` 是 includes/_list.vp 定义的堆上 STL 容器泛型类 + (`list[T]` / `list[T, N]` 都走泛型特化),与定长数组无关。 + """ + if not isinstance(annotation, ast.Subscript): + return False + value = annotation.value + # t.CArray[...] / c.CArray[...] + if (isinstance(value, ast.Attribute) + and isinstance(value.value, ast.Name) + and value.value.id in ('t', 'c') + and value.attr == 'CArray'): + return True + # 裸名 CArray[...](from t import *)/ 内建 rawarray[...] / 内建 array[...]: + # 三者与 t.CArray 等价(array 为本轮新增的内建软关键字,见上方常量注释) + if (isinstance(value, ast.Name) + and value.id in _FIXED_ARRAY_BARE_NAMES): + return True + return False + + +def ExtractListFromBinOp(annotation: ast.expr) -> ast.Subscript | None: + """从 BinOp(BitOr) 注解中提取定长数组部分,如 t.CArray[i32] | t.CPtr""" + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + for side in (annotation.left, annotation.right): + if IsListAnnotation(side): + return side + return None + + +class ListAnnotationParseResult: + """定长数组注解(t.CArray / rawarray / array)解析结果。 + + is_pointer=True 表示单参数 CArray[type](等价 CArray[type, None])→ 指针模式; + is_pointer=False 表示 Tuple(2) 模式 CArray[type, count]。 + """ + __slots__ = ('elem_type_node', 'count_node', 'is_pointer') + + def __init__(self, elem_type_node: ast.expr, count_node: ast.expr | None, is_pointer: bool) -> None: + self.elem_type_node: ast.expr = elem_type_node + self.count_node: ast.expr | None = count_node + self.is_pointer: bool = is_pointer + + +def ParseListAnnotation(annotation: ast.expr) -> ListAnnotationParseResult | None: + """解析定长数组注解(t.CArray / rawarray / array),处理 CArray[type, count] / + CArray[type] / BinOp(BitOr) 形式。 + + 自动处理 BinOp(BitOr) 包裹(如 CArray[i32, N] | t.CPtr)。 + 返回 None 表示不是定长数组注解(`list[...]` 也返回 None —— 它是堆上 STL 容器)。 + """ + list_node: ast.expr = annotation + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + extracted: ast.Subscript | None = ExtractListFromBinOp(annotation) + if extracted is None: + return None + list_node = extracted + if not IsListAnnotation(list_node): + return None + slice_node: ast.AST = list_node.slice + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + return ListAnnotationParseResult(slice_node.elts[0], slice_node.elts[1], is_pointer=False) + # 单参数 list[type] → 指针模式 + return ListAnnotationParseResult(slice_node, None, is_pointer=True) + + +# --------------------------------------------------------------------------- +# 内建定长数组构造子 array 的注解校验(唯一规则源) +# +# array 是**软关键字**:仅在"类型/注解位置"作为内建定长数组构造子生效, +# 语义与 t.CArray 逐点等价(元素类型在前、长度在后)。非类型位置仍是普通 +# 标识符(如 includes/json 的 def array(...)、numpy 的 def array(...), +# 因此**不能**做成词法保留字)。 +# +# 与内建 rawptr 的机制对应:都"不可重定义"(在类型位置优先于任何用户声明), +# 但 array 因与既有标识符重名,只在类型位置生效(软关键字),而 rawptr 在 +# 各解析入口直接短路(硬内置名)。 +# +# 新写法要求**恰好两个参数** `array[E, N]`:缺参 / 多参 / 长度非正整型常量 +# 表达式均为 fail-fast(不静默降级)。t.CArray[E] 的单参/长度自动推导写法 +# 仍然可用(t.CArray 不受影响)。 +# --------------------------------------------------------------------------- +def _IsConstExprShape(Node: ast.AST) -> bool: + """节点是否为"常量表达式形态"(字面量 / 名字 / 属性 / 常量算术/位运算/一元)。 + + 用于 array 长度参数的**语法级**拒绝:调用/列表/比较/布尔/推导式等明显不是 + 编译期整数常量,直接判为误用;Name/Attribute(可能是 CDefine 常量或 + 跨模块常量)与算子组合保留,交由既有常量求值路径判定(避免前向引用误报)。 + """ + if isinstance(Node, (ast.Constant, ast.Name, ast.Attribute)): + return True + if isinstance(Node, ast.BinOp): + return _IsConstExprShape(Node.left) and _IsConstExprShape(Node.right) + if isinstance(Node, ast.UnaryOp): + return _IsConstExprShape(Node.operand) + return False + + +def FixedArrayKeywordAnnotationError(Annotation: ast.AST, + SymbolTable: object = None) -> str | None: + """校验内建定长数组构造子 array 的注解写法。 + + 返回 None 表示"合法"或"不是 array 注解";否则返回中文错误消息(不含 + 位置与源码片段——那两项由调用方按 @p Annotation 的 lineno/col_offset + 与源码行补齐)。SymbolTable 非空时对常量表达式长度做一次求值校验; + 求值不到(前向引用/声明期依赖未就绪)则**延迟**给下游常量求值路径, + 避免误报。 + """ + if Annotation is None: + return None + if isinstance(Annotation, ast.Name): + if Annotation.id == _ARRAY_KEYWORD: + return ("裸用内建定长数组构造子 'array'(缺少下标与参数)" + "——'array' 只在类型/注解位置生效,必须写成 array[E, N]" + "(元素类型在前、长度在后)。" + "期望形态:array[E, N];正确示例:state: array[uint32, 4]") + return None + if not isinstance(Annotation, ast.Subscript): + return None + base: ast.AST = Annotation.value + if not (isinstance(base, ast.Name) and base.id == _ARRAY_KEYWORD): + return None + SliceNode: ast.AST = Annotation.slice + if not isinstance(SliceNode, ast.Tuple): + return ("内建定长数组构造子 'array' 缺少长度参数(当前仅 1 个参数)" + "——必须同时给出元素类型与长度。" + "期望形态:array[E, N];正确示例:state: array[uint32, 4]") + if len(SliceNode.elts) != 2: + return (f"内建定长数组构造子 'array' 参数个数错误(当前 {len(SliceNode.elts)} 个,期望 2 个)" + "——只接受 array[E, N](E=元素类型,N=长度)。" + "期望形态:array[E, N];正确示例:state: array[uint32, 4]") + CountNode: ast.AST = SliceNode.elts[1] + if isinstance(CountNode, ast.Constant): + Value = CountNode.value + if not (isinstance(Value, int) and not isinstance(Value, bool) and Value > 0): + return ("内建定长数组构造子 'array' 的长度必须是正整型常量(>0)" + "——None/0/负数/非整数都不允许(长度自动推导请改用 t.CArray[E])。" + "期望形态:array[E, N];正确示例:state: array[uint32, 4]") + return None + if not _IsConstExprShape(CountNode): + return ("内建定长数组构造子 'array' 的长度必须是整型常量表达式" + "(字面量 / CDefine 常量名 / 常量算术或位运算表达式)" + "——函数调用/列表/比较等运行期表达式不允许。" + "期望形态:array[E, N];正确示例:state: array[uint8, MAX_LEN]") + if SymbolTable is not None: + try: + from lib.core.ConstEvaluator import ConstEvaluator + Value = ConstEvaluator.eval_with_symtab(CountNode, SymbolTable) + except Exception: + Value = None + if Value is not None and not (isinstance(Value, int) + and not isinstance(Value, bool) and Value > 0): + return ("内建定长数组构造子 'array' 的长度必须求值为正整型常量(>0)。" + "期望形态:array[E, N];正确示例:state: array[uint32, 4]") + return None + + +def CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool: + """检测注解节点中是否包含 CInline""" + if annotation_node is None: + return False + if isinstance(annotation_node, ast.Attribute): + if hasattr(annotation_node.value, 'id') and IsTModule(annotation_node.value.id) and annotation_node.attr == 'CInline': + return True + if isinstance(annotation_node, ast.Name): + if annotation_node.id == 'CInline': + return True + if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr): + return CheckAnnotationHasCInline(annotation_node.left) or CheckAnnotationHasCInline(annotation_node.right) + return False + + +def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None: + """从 BinOp(BitOr) 注解左侧提取类型名(如 TypeA | TypeB → TypeA)。 + + 返回 Name.id 或 Attribute.attr,非 BinOp 返回 None。 + """ + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + left: ast.expr = annotation.left + if isinstance(left, ast.Name): + return left.id + if isinstance(left, ast.Attribute): + return left.attr + return None + + +def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set, + TCAliasMap: dict[str, tuple[str, str]] | None = None, + StructSha1Map: dict[str, str] | None = None) -> str | None: + """递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。 + + 检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。 + 支持泛型特化类型(如 GSList[Param]):特化名按**同一规则源** + (ResolveTypeArgStr:sha1 前缀 + rawptr/CPtr 的 '*' 逐层)构造后在 + struct_names 中查找——查表键与 _mangle_generic_class_name 产物逐字一致, + 否则同一注解在"注解查找"与"特化注册"两处解析出不同名字(opaque 副本)。 + """ + if isinstance(node, ast.Name) and node.id in struct_names: + return node.id + if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value in struct_names: + return node.value + if isinstance(node, ast.Attribute) and node.attr in struct_names: + return node.attr + if isinstance(node, ast.Subscript): + base_name: str | None = None + if isinstance(node.value, ast.Name): + base_name = node.value.id + elif isinstance(node.value, ast.Attribute): + base_name = node.value.attr + if base_name: + slice_node: ast.AST = node.slice + if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index): + slice_node = slice_node.value + elem_nodes: list[ast.AST] = ( + list(slice_node.elts) if isinstance(slice_node, ast.Tuple) else [slice_node]) + type_args: list[str] = [ + ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in elem_nodes] + spec_name: str = base_name + '[' + ']['.join(type_args) + ']' + if spec_name in struct_names: + return spec_name + if base_name in struct_names: + return base_name + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + result: str | None = FindStructNameInAnnotation( + node.left, struct_names, TCAliasMap, StructSha1Map) + if result: + return result + return FindStructNameInAnnotation( + node.right, struct_names, TCAliasMap, StructSha1Map) + return None + + +def FindStructNameInAnnotationWithContext(Node: ast.AST, Trans: object, Gen: object, + StructNames: dict | set | None = None) -> str | None: + """按 Gen/Trans 上下文注入规则表后查找结构体名。 + + 规则本体唯一(FindStructNameInAnnotation),此处只做与 + HandlesExprCall._resolve_type_arg_str 同口径的上下文注入,避免各调用点 + 各写一套拼名(查表键必须与 _mangle_generic_class_name 产物逐字一致)。 + """ + Names: dict | set = StructNames if StructNames is not None else Gen.structs + return FindStructNameInAnnotation( + Node, Names, + TCAliasMap=getattr(Trans, '_t_c_imported_names', {}), + StructSha1Map=getattr(Gen, '_struct_sha1_map', {})) + + +def AnnotationContainsName(annotation: ast.AST, name: str) -> bool: + """检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。 + + 同时检查 Attribute.attr 和 Name.id,比 ast.dump 字符串匹配更精确。 + """ + if isinstance(annotation, ast.Attribute): + return annotation.attr == name + if isinstance(annotation, ast.Name): + return annotation.id == name + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + return AnnotationContainsName(annotation.left, name) or AnnotationContainsName(annotation.right, name) + return False + + +def IsCPtrNode(node: ast.AST) -> bool: + """检查节点是否为 t.CPtr 属性引用。""" + return (isinstance(node, ast.Attribute) + and isinstance(node.value, ast.Name) + and node.value.id == 't' + and node.attr == 'CPtr') + + +# 值引用参数标记名 → 自动取址模式(仅注解 AST 内按名字识别,非真实类): +# 'CRef' → 'ref' (&T :实参非指针左值取址,右值 fail-fast) +# 'CTempRef' → 'temp' (&temp T :实参非指针时在调用方建同型临时再取址) +# 'CAutoPtr' → 'always' (t.CAutoPtr:无条件取址,保持既有语义) +_AUTO_ADDR_MARKER_MODES: dict[str, str] = { + 'CRef': 'ref', 'CTempRef': 'temp', 'CAutoPtr': 'always', +} + + +def FindAutoAddrMode(node: ast.AST | None) -> str | None: + """注解 AST 内查找自动取址模式标记,返回 'ref' / 'temp' / 'always';无则 None。 + + 只按名字识别(Attribute.attr 或 Name.id),递归联合/嵌套注解;CRef/CTempRef + 非真实类,不出现在 lib/includes/t.py。 + """ + if node is None: + return None + if isinstance(node, ast.Attribute): + mode = _AUTO_ADDR_MARKER_MODES.get(node.attr) + if mode is not None: + return mode + elif isinstance(node, ast.Name): + mode = _AUTO_ADDR_MARKER_MODES.get(node.id) + if mode is not None: + return mode + for child in ast.iter_child_nodes(node): + mode = FindAutoAddrMode(child) + if mode is not None: + return mode + return None + + +def IsCPtrSubscriptNode(node: ast.AST) -> bool: + """检查节点是否为 t.CPtr[T] 下标指针类型(非裸标记,裸标记见 IsCPtrNode)。 + + 与 VPC TypeGenResolve._is_cptr_subscript(TypeGenResolve.vp:1485-1505)同口径: + value 必须是 t.CPtr 属性引用(只认 t 前缀,c.CPtr[...] 不认); + 切片内的指针层级不限(t.CPtr[t.CPtr]、t.CPtr[t.CPtr[t.CInt]] 均命中)。 + 链式写法 t.CPtr[T][T](value 是 Subscript)两套编译器均不认作该标记。 + """ + return (isinstance(node, ast.Subscript) + and IsCPtrNode(node.value)) + + +# 指针下标的 base 名:rawptr 是编译器内置语法(TPC/VPC 双端保留字,见 +# includes/builtins.vp 语义注释与 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME), +# 单独 rawptr 等价 t.CPtr(void*)。 +_RAWPTR_SUBSCRIPT_NAME: str = 'rawptr' + +# 指针标记类型名(t.CPtr):裸名书写 `CPtr[T]` / 导入别名 `X`(from t import CPtr as X) +# 都归一到本名。 +_CPTR_TYPE_NAME: str = 'CPtr' + + +def ResolveTCImportAliasName(NameId: str, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> str: + """t/c 导入别名 → 原始类型名('X' → 'CPtr'、'myint' → 'CInt');未命中原样返回。 + + 两张同源别名表(注册点唯一:HandlesImports._EmitImportFromDeclarationsLlvm / + _RegisterFromImportAliases,与 SymbolTable 序列化),调用方按手头有的表注入: + - TCAliasMap:{别名: (模块, 原名)}(Trans._t_c_imported_names / + DeclarationGenerator._t_c_imported_names 同结构) + - symtable._t_type_symbols:{别名: t.CType 子类}(from t import CPtr as X → t.CPtr) + 非 t/c 模块的 from 导入别名(from ast import AST as A)不参与归一。 + """ + if not NameId: + return NameId + if TCAliasMap: + Entry: tuple[str, str] | None = TCAliasMap.get(NameId) + if Entry: + SrcModule, SrcName = Entry + if IsTModule(SrcModule): + return SrcName + if symtable is not None: + Cls: object | None = getattr(symtable, '_t_type_symbols', {}).get(NameId) + if Cls is not None: + return getattr(Cls, '__name__', NameId) + return NameId + + +def IsRawPtrSubscriptNode(node: ast.AST) -> bool: + """检查节点是否为内置裸指针下标 `rawptr[T]`(非裸标记 rawptr)。 + + `rawptr[T]` 语义 = `t.CPtr[T]` = 源码联合写法 `T | t.CPtr`(Ptr(T)),三者 + 必须走同一解析与拼名路径,否则同一类型的不同书写会分裂出不同特化名 + (结构体/方法声明/调用点三处不一致)。 + 注意:本判据**只**认 `rawptr[T]`;`t.CPtr[T]` / 裸名 `CPtr[T]` 由 + IsPtrSubscriptNode 判定(见下),二者共用同一条脱糖规则 + (DesugarPtrSubscriptToUnion),此处保持 rawptr 专用以便仍需区分书写时使用。 + """ + return (isinstance(node, ast.Subscript) + and isinstance(node.value, ast.Name) + and node.value.id == _RAWPTR_SUBSCRIPT_NAME) + + +def IsI8DoublePtrType(T: object) -> bool: + """是否为 **i8 系两层指针**(`i8**`)—— 即 `T | t.CPtr` 在 T 的类型丢失后**退化**成的形状。 + + 与 "T 是有类型的上层指针" 无关:只认 `i8**`(`Ptr(Ptr(i8))`)。用于识别 + 「类引用指针被解析成通用两层指针」这类**类型信息丢失**。 + """ + import llvmlite.ir as ir + return (isinstance(T, ir.PointerType) + and isinstance(T.pointee, ir.PointerType) + and isinstance(T.pointee.pointee, ir.IntType) + and T.pointee.pointee.width == 8) + + +def IsClassRefPtrAnnotation(Ann: ast.AST, symtable: object | None = None) -> bool: + """注解是否为「**指针包装的类引用**」:`rawptr[Mod.Class]` / `Mod.Class | t.CPtr`。 + + 只回答"这个注解**本应**解析出结构体指针",不做任何类型解析,判据保守: + - 必须是指针包装:`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]`,或含 `t.CPtr` + 标记的联合写法 `T | t.CPtr`; + - 内层须是类引用形态:`Attribute` 且**根模块不是 `t` / `c`** + (如 `ExprGenFstr.ExprGenFstr`、`llvmlite.LLVMType`),或首字母大写的裸名 + (类名书写惯例)。 + 模块名、`rawptr` / `CPtr` 标记本身、泛型特化实参(内层是 Subscript)、 + `str` / `int` 这类小写内建名一律**不算**。 + """ + Inner: ast.AST | None = None + if IsRawPtrSubscriptNode(Ann) or IsCPtrSubscriptNode(Ann): + Inner = Ann.slice + elif isinstance(Ann, ast.BinOp) and isinstance(Ann.op, ast.BitOr): + for Side in (Ann.left, Ann.right): + if IsCPtrMarkerNode(Side, symtable): + continue + if Inner is not None: + return False + Inner = Side + else: + return False + if isinstance(Inner, ast.Attribute): + Cur: ast.AST = Inner + while isinstance(Cur, ast.Attribute): + Cur = Cur.value + Root: str = Cur.id if isinstance(Cur, ast.Name) else '' + return Root not in ('t', 'c') + if isinstance(Inner, ast.Name): + return bool(Inner.id) and Inner.id[0].isupper() + return False + + +def IsPtrSubscriptNode(node: ast.AST, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: + """检查节点是否为"指针下标类型":`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` + / 导入别名 `X[T]`(`from t import CPtr as X`)。 + + 三者语义完全相同(= 源码联合写法 `T | t.CPtr` = Ptr(T),见 + builtins.vp 与 t.py 的 CPtr 语义注释):内层 T 按**参数注解 `a: T` 的规则** + 解析(跨模块类 → '.X'),再包一层指针——T 为类引用时该层被结构体 + 自身吸收('.X*'),其余(标量 / 已是指针的 typedef / 泛型容器特化 / + 嵌套指针下标)逐层外包。 + + - 裸名 `CPtr[T]`(`from t import CPtr` / `from t import *`)与声明端 + DeclarationGenerator._get_type_str 的 ValueIsCPtr 判据一致(那里同样接受 + Attribute 'CPtr' 与 Name 'CPtr' 两种书写)。 + - 传入 symtable 时,`import t as tt` 的 `tt.CPtr[T]` 一并命中(与声明端 + "只看 attr 名"的 ValueIsCPtr 判据对齐,避免同一书写两端分裂); + 不传(名称域 ResolveTypeArgStr 的字面名场景)时只认字面 't'。 + - 导入名别名(`from t import CPtr as X` 的 `X[T]`)经别名表解析 X→CPtr + (TCAliasMap 或 symtable._t_type_symbols,见 ResolveTCImportAliasName), + 定义端与声明端各自注入手头的同一批表,判据只有这一处。 + """ + if IsRawPtrSubscriptNode(node): + return True + if not isinstance(node, ast.Subscript): + return False + value: ast.AST = node.value + if isinstance(value, ast.Name) and value.id == _CPTR_TYPE_NAME: + return True + return IsCPtrMarkerNode(value, symtable, TCAliasMap) + + +def DesugarPtrSubscriptToUnion(node: ast.AST, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> ast.AST: + """把指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / 别名 `X[T]`) + 折叠成等价的联合写法 `T | t.CPtr`。 + + 内层 T 递归折叠(`rawptr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`、 + `t.CPtr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`), + 与 VPC TypeGenResolve.resolve_annotation_type 的 Subscript 分支同语义 + (T → Ptr(T),层数逐层外包);折叠后交给 HandlesTypeMerge 既有的 + 联合解析(_CollectAllOperandTypes 会把嵌套 BitOr 拉平成 [T, t.CPtr, ...], + struct 基类型吸收一层、其余逐层累加),从而: + - 内层 T 走通用类型解析(含跨模块类经 Gen.structs 反查 SHA1 前缀); + - `rawptr[X]` / `t.CPtr[X]` / `X[T]` / `X | t.CPtr` 得到同一 CTypeInfo + (PtrCount/BaseType 一致)与同一泛型实参串(逐层 '*')。 + + 非指针下标节点原样返回(调用方无需先判类型);symtable/TCAliasMap 透传给 + 判据(识别 `import t as tt` 的 `tt.CPtr[T]` 与 `from t import CPtr as X` 的 + `X[T]` 内层嵌套)。折叠产物统一用字面 `t.CPtr` 标记,与源码联合写法同形。 + """ + if not IsPtrSubscriptNode(node, symtable, TCAliasMap): + return node + inner: ast.AST = DesugarPtrSubscriptToUnion(node.slice, symtable, TCAliasMap) + cptr_marker: ast.AST = ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + union_node: ast.AST = ast.BinOp(left=inner, op=ast.BitOr(), right=cptr_marker) + try: + ast.copy_location(union_node, node) + except Exception: + pass + return union_node + + +# --------------------------------------------------------------------------- +# 泛型实参串(mangling):定义端与声明端唯一规则源 +# --------------------------------------------------------------------------- +# 同一泛型特化(如 list[rawptr[ast.AST]])在 define 端发射的结构体名 +# (HandlesClassDef._mangle_generic_class_name → %".list[ast.ASTptr]")、 +# 各端方法名、以及 declare/stub 端引用的类型名必须逐字一致,否则会出现 +# Undefined method / 签名错位。此前 DeclarationGenerator._get_type_str 的泛型 +# 分支自带第二套(只认 Name / Name-Tuple 实参的)规则,其余形态(Attribute / +# Subscript / BinOp / 混合 Tuple)退回未特化的 %".list"*,与 define 端 +# 不一致。故此处收拢唯一规则,两端皆调用本组函数。 + +# 定义端 _resolve_generic_slice_type 的 ast.Name 规范化表: +# 源码里的 C 定长类型名 → 喂给 _mangle_generic_class_name 的 type_arg。 +_GENERIC_ARG_INT_NAMES: frozenset[str] = frozenset({ + 'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', + 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong', +}) +_GENERIC_ARG_FLOAT_NAMES: frozenset[str] = frozenset({ + 'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T', +}) +# 源码名 → 泛型 T 替换用的原始类型名(_resolve_generic_slice_type 的第二返回值) +_GENERIC_ARG_RAW_NAMES: dict[str, str] = { + 'int': 'CInt', 'double': 'CDouble', 'float': 'CFloat', + 'char': 'CChar', 'bool': 'CBool', +} +# _mangle_generic_class_name 的浮点归一集合(LLVM 浮点串 → 'f' + Size) +_LLVM_FLOAT_STRINGS: frozenset[str] = frozenset({'float', 'double', 'half', 'fp128'}) + + +def NormalizeGenericArgName(NameId: str) -> str: + """ast.Name 泛型实参 → 定义端 type_arg(HandlesExprCall._resolve_generic_slice_type + 的 ast.Name 分支,逐分支同构): + int/double/float 保留源码名('list[int]' 而非 'list[i32]')、 + char/bool → 'i8'、C 整数族 → 'int'、C 浮点族 → 'double'、其余原名。 + """ + if NameId in ('int', 'double', 'float'): + return NameId + if NameId in ('char', 'bool'): + return 'i8' + if NameId in _GENERIC_ARG_INT_NAMES: + return 'int' + if NameId in _GENERIC_ARG_FLOAT_NAMES: + return 'double' + return NameId + + +def GenericArgRawName(NameId: str) -> str: + """ast.Name 泛型实参 → 替换模板 T 用的原始类型名(_resolve_generic_slice_type + 的第二返回值):'int' → 'CInt'(内建类型须用源码名替换 T,见 + HandlesClassDef._specialize_generic_class 的 _BUILTIN_TYPE_NAMES 判定), + 其余原名。仅定义端 T 替换用;mangling 只用 NormalizeGenericArgName。 + """ + return _GENERIC_ARG_RAW_NAMES.get(NameId, NameId) + + +def MangleTypeArgByRegistry(ArgStr: str) -> str: + """实参串 → 最终 mangled 实参(HandlesClassDef._mangle_generic_class_name + 的单项逻辑,逐分支同构):t 类型名经 NameToLLVM 归一(CInt32T → 'i32', + 't.CInt'/'c.CInt' 先去模块前缀再归一),浮点按 Size 归一为 f{n} + (CDouble → 'f8'),未注册名原样保留(含指针层数与已解析的内层名)。 + """ + LookupStr: str = ArgStr + if ArgStr.startswith('t.') or ArgStr.startswith('c.'): + LookupStr = ArgStr[2:] + LlvmStr: str | None = CTypeRegistry.NameToLLVM(LookupStr) + if not LlvmStr: + return ArgStr + if LlvmStr in _LLVM_FLOAT_STRINGS: + Cls: type | None = CTypeRegistry.GetClassByName(LookupStr) + Size: int = Cls().Size if Cls is not None else 0 + return 'f' + str(Size) + return LlvmStr + + +# 指针层数的首层归一化(与 LLVMTypeMapper._add_pointer_level 同口径) +_POINTER_PLACEHOLDER_BASES: frozenset[str] = frozenset({'', 'void', 'void*'}) + + +def AddPointerLayers(TypeStr: str, Layers: int) -> str: + """给已解析的类型串追加 Layers 层指针(空/void/void* 的首层归一化为 i8*)。 + + 与 LLVMTypeMapper._add_pointer_level 同口径;名称域与类型域因此对 + rawptr[t.CVoid] 一致地给出 i8*(唯一允许的 void 兜底)。 + """ + Result: str = TypeStr + for _ in range(Layers): + if Result in _POINTER_PLACEHOLDER_BASES: + Result = 'i8*' + else: + Result = Result + '*' + return Result + + +def IsCPtrMarkerNode(Node: ast.AST, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: + """`t.CPtr` 裸指针标记(含 `import t as tt` 与 `from t import CPtr as X` 别名): + 联合写法里贡献一层 '*'。 + + 与 IsCPtrNode 的区别:仅按 attr 名 + t 模块前缀判定,不看切片 + (`t.CPtr[T]` 下标是**基类型**而非层数标记,见 ResolveTypeArgStr 的 + Subscript 分支)。 + - Attribute:`t.CPtr` / `tt.CPtr`(t 模块别名经 symtable.import_aliases 解析)。 + - Name:经别名表归一为 'CPtr' 的书写名(`from t import CPtr as X` 的 `X`); + 字面裸名 `CPtr` 是否算标记取决于该模块是否 `from t import CPtr` + (别名表命中一致:两端都由同一批表判定,不另设白名单)。 + """ + if isinstance(Node, ast.Attribute): + return (Node.attr == _CPTR_TYPE_NAME + and isinstance(Node.value, ast.Name) + and IsTModule(Node.value.id, symtable)) + if isinstance(Node, ast.Name): + return ResolveTCImportAliasName(Node.id, symtable, TCAliasMap) == _CPTR_TYPE_NAME + return False + + +def FlattenUnionOperands(Node: ast.AST) -> list[ast.AST]: + """拉平嵌套 BitOr,按书写顺序返回操作数(`A | t.CPtr | t.CPtr` → [A, CPtr, CPtr])。""" + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + return FlattenUnionOperands(Node.left) + FlattenUnionOperands(Node.right) + return [Node] + + +def ResolveTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, + StructSha1Map: dict[str, str] | None = None) -> str: + """将类型参数 AST 节点解析为规范化的类型字符串(泛型 mangling 用)。 + + 规则(**与参数注解 `a: T` 完全同一套解析**,定义端/声明端唯一规则源): + - ast.Name: t/c 导入别名 → '{module}.{name}';已注册类 → '{sha1}.{名}' + (与参数注解的 %"{sha1}.X" 同前缀);其余按 NormalizeGenericArgName + 归一('int'/'i32'/'str' 原名、'char'/'bool' → 'i8'、C 整数/浮点族 → + 'int'/'double') + - ast.Attribute: t 模块类型按 CTypeRegistry.NameToLLVM 解析(t.CPtr → i8*、 + t.CInt → i32);跨模块类引用 module.X → '{sha1}.X';其余 '{mod}.{attr}' + - ast.Subscript: 指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / + 导入别名 `X[T]`)已在入口脱糖为上面的联合写法(走同一规则),故此处只剩泛型容器: + '<容器名>[<已解析实参>]'(多实参 '][' 分隔),容器名的 + 模块前缀由调用方(_mangle_name / '%"{模sha1}.{特化名}"')统一加 + - ast.BinOp(BitOr)(含指针下标脱糖而来): 取非 CPtr 侧为基类型, + 每个 `t.CPtr` 标记追加一层 '*'(`X | t.CPtr` ≡ `rawptr[X]` ≡ + `t.CPtr[X]` → '.X*';`rawptr[rawptr[X]]` → '.X**'; + 标量 `int | t.CPtr` → 'int*') + - 其余形态: ast.unparse + + 注意:名称域**不做**"裸类引用吸收一层 CPtr"(那是注解→LLVM 类型域的规则, + 见 DeclarationGenerator._is_bare_class_ref_annotation):名称域描述的是类型 + 身份('X' = X 本身、'X*' = 指向 X 的指针),逐层累加,否则 + `rawptr[X]` 与 `X | t.CPtr` 会分裂成两个名字。 + """ + if IsPtrSubscriptNode(Node, TCAliasMap=TCAliasMap): + Node = DesugarPtrSubscriptToUnion(Node, TCAliasMap=TCAliasMap) + if isinstance(Node, ast.Name): + tn: str = Node.id + if TCAliasMap and tn in TCAliasMap: + # t 模块别名(from t import CInt as myint)按内建归一表解析到同一名字 + # ('myint' 与 'CInt' 都 → 'int'),避免同义写法分裂特化名 + SrcModule, SrcName = TCAliasMap[tn] + if IsTModule(SrcModule): + return NormalizeGenericArgName(SrcName) + return f'{SrcModule}.{SrcName}' + if StructSha1Map and tn in StructSha1Map: + return f'{StructSha1Map[tn]}.{tn}' + return NormalizeGenericArgName(tn) + if isinstance(Node, ast.Attribute): + return _ResolveAttributeArgStr(Node, TCAliasMap, StructSha1Map) + if isinstance(Node, ast.Subscript): + return _ResolveSubscriptArgStr(Node, TCAliasMap, StructSha1Map) + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + return _ResolveUnionArgStr(Node, TCAliasMap, StructSha1Map) + return ast.unparse(Node) + + +def _ResolveAttributeArgStr(Node: ast.Attribute, TCAliasMap: dict[str, tuple[str, str]] | None, + StructSha1Map: dict[str, str] | None) -> str: + """Attribute 形态(t.CInt / ast.AST / .AST)→ 实参串。""" + if not isinstance(Node.value, ast.Name): + return ast.unparse(Node) + ModName: str = Node.value.id + AttrName: str = Node.attr + if IsTModule(ModName) or (TCAliasMap and ModName in TCAliasMap): + LlvmStr: str | None = CTypeRegistry.NameToLLVM(AttrName) + if LlvmStr is not None: + return LlvmStr + return f'{ModName}.{AttrName}' + # 跨模块类引用(ast.AST / .AST):与参数注解同一条 sha1 解析 + if StructSha1Map and AttrName in StructSha1Map: + return f'{StructSha1Map[AttrName]}.{AttrName}' + return f'{ModName}.{AttrName}' + + +def _ResolveSubscriptArgStr(Node: ast.Subscript, TCAliasMap: dict[str, tuple[str, str]] | None, + StructSha1Map: dict[str, str] | None) -> str: + """下标形态(t.CPtr[T] / 泛型容器 list[X] / dict[K,V])→ 实参串。 + + 容器名('list' / 'dict' ...)**不带**模块前缀:它只是 _mangle_generic_class_name + 的基名,特化名的模块前缀由调用方统一加(定义端 _mangle_name / 声明端 + '%"{模sha1}.{特化名}"'),并保证 '_make_replacement_node' 能把实参串 + 重新解析成可用的注解 AST('.list[...]' 会因数字开头而解析失败)。 + """ + if IsCPtrSubscriptNode(Node): + Inner: str = ResolveTypeArgStr(Node.slice, TCAliasMap, StructSha1Map) + return AddPointerLayers(Inner, 1) + BaseName: str = '' + if isinstance(Node.value, ast.Name): + BaseName = Node.value.id + elif isinstance(Node.value, ast.Attribute): + BaseName = Node.value.attr + if not BaseName: + return ast.unparse(Node) + SliceNode: ast.AST = Node.slice + ElemNodes: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode] + Resolved: list[str] = [ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in ElemNodes] + return f'{BaseName}[' + ']['.join(Resolved) + ']' + + +def _ResolveUnionArgStr(Node: ast.BinOp, TCAliasMap: dict[str, tuple[str, str]] | None, + StructSha1Map: dict[str, str] | None) -> str: + """联合形态(X | t.CPtr | ...)实参串:非 CPtr 侧为基类型,CPtr 标记逐层加 '*'。 + + 标记判据是同一处 IsCPtrMarkerNode(含 `from t import CPtr as X` 的裸标记别名), + 别名经 TCAliasMap 解析;没带别名表的 `import t as tt` 的 `tt.CPtr` 在此域 + 不做模块别名解析(两端口径一致的既有缺口)。 + """ + BaseType: str = '' + Layers: int = 0 + for Operand in FlattenUnionOperands(Node): + if IsCPtrMarkerNode(Operand, TCAliasMap=TCAliasMap): + Layers += 1 + elif not BaseType: + BaseType = ResolveTypeArgStr(Operand, TCAliasMap, StructSha1Map) + if not BaseType: + # 只有 CPtr 标记(如 `t.CPtr | t.CPtr`):等价 void* 逐层——首层 i8* + return AddPointerLayers('void', Layers) + return AddPointerLayers(BaseType, Layers) + + +def GetStructSha1MapFromSymtab(Symtab: object | None) -> dict[str, str]: + """从符号表取「类名 → 定义模块 sha1」表(Phase2 在 Convert 前挂到 translator)。""" + _Tr: Any = getattr(Symtab, 'translator', None) + _Map: Any = getattr(_Tr, '_struct_sha1_map', None) if _Tr is not None else None + return _Map if isinstance(_Map, dict) else {} + + +def GetTCAliasMapFromSymtab(Symtab: object | None) -> dict | None: + """从符号表取 t/c 导入别名表(`from t import CInt as myint`)。缺则 None。""" + _Tr: Any = getattr(Symtab, 'translator', None) + _Map: Any = getattr(_Tr, '_t_c_imported_names', None) if _Tr is not None else None + return _Map if isinstance(_Map, dict) and _Map else None + + +def IsGenericClassSubscriptBase(Node: ast.AST, Symtab: object | None = None, + StructSha1Map: dict[str, str] | None = None) -> bool: + """下标基名是否为「有类定义的泛型类」(内建 list/dict/set… 或用户泛型类)。 + + 判据与 DeclarationGenerator._get_type_str 的泛型分支(L1393-1423)同一条规则源: + **该名有类定义**即按泛型特化处理。内建容器本身在 includes/_list.vp / _dict.vp 等 + 就是类(实测符号表 `list` 条目 IsStruct=True),故本判据等价覆盖声明端的 + `non_struct_types ∪ _generic_class_names`;用户泛型类(Box[T])同样命中。 + + 只认 Name 形态:`t.CPtr[...]` / `t.CArray[...]` / 裸 `array[...]` / + `rawptr[...]` 等由调用方各自分支先行处理,不走这里。 + """ + if not isinstance(Node, ast.Name) or not Node.id: + return False + if StructSha1Map and Node.id in StructSha1Map: + return True + if Symtab is not None: + _Entry: Any = None + try: + if hasattr(Symtab, 'lookup'): + _Entry = Symtab.lookup(Node.id) + elif hasattr(Symtab, 'get'): + _Entry = Symtab.get(Node.id) + except Exception: + _Entry = None + if _Entry is not None and getattr(_Entry, 'IsStruct', False): + return True + for _Ns in (getattr(Symtab, '_namespaces', {}) or {}).values(): + if not isinstance(_Ns, dict): + continue + _Cand: Any = _Ns.get(Node.id) + if _Cand is not None and getattr(_Cand, 'IsStruct', False): + return True + return False + + +def ResolveGenericClassSpecName(Node: ast.Subscript, Symtab: object | None = None, + StructSha1Map: dict[str, str] | None = None, + TCAliasMap: dict | None = None) -> str | None: + """泛型类下标注解 → 声明端同口径的**特化名**('list[str]' / 'dict[str][int]')。 + + 与 DeclarationGenerator._get_type_str 的泛型分支(L1393-1423)逐字同口径: + spec = 基名 + '[' + ']['.join(实参串) + ']' + 实参串走 ResolveGenericTypeArgStr(与定义端 _mangle_generic_class_name 同一规则源)。 + 非泛型类下标(指针下标 / 定长数组 / t 模块类型 / 未知名)返回 None,由调用方各自 + 既有分支处理(调用点必须放在那些分支**之后**)。 + + 必要性:单参泛型下标此前在两个注解解析器里都 miss(TypeAnnotationResolver 只认 + 双参定长 t.CArray[T, N];LLVMTypeMapper 亦然)⇒ 落到"未知类型"兜底 i32 / 空 CTypeInfo, + 形参与成员类型整体错位(现场:ClassGenGeneric._specialize_generic_class 的 + 形参 `type_args: list[str]*` 在符号表里成 i32;字段 + `TypeGenBase.CTypedefNames: list[str] | t.CPtr` 成 i8*)。 + """ + if not isinstance(Node, ast.Subscript): + return None + Base: ast.AST = Node.value + if not IsGenericClassSubscriptBase(Base, Symtab, StructSha1Map): + return None + SliceNode: ast.AST = Node.slice + Args: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode] + if not Args: + return None + Mangled: list[str] = [ + ResolveGenericTypeArgStr(A, TCAliasMap, StructSha1Map) for A in Args] + if not any(Mangled): + return None + return Base.id + '[' + ']['.join(Mangled) + ']' + + +def ResolveGenericTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, + StructSha1Map: dict[str, str] | None = None) -> str: + """泛型实参的最终 mangled 串(喂给 _mangle_generic_class_name 的单参数)。 + + 与定义端(HandlesExprCall._resolve_generic_slice_type + _mangle_generic_class_name) + 对单个实参的实际结果同一套规则: + - ast.Name: NormalizeGenericArgName →(命中注册类则 '{sha1}.{名}')→ MangleTypeArgByRegistry + ('int' → 'int'、'i32' → 'i32'、'str' → 'str'、'CInt32T' → 'int'、 + 'CFloat64T' → 'double'、'CChar' → 'i8') + - 其余形态(Attribute / Subscript(含 rawptr[...])/ BinOp / 混合 Tuple 元素): + ResolveTypeArgStr(rawptr[T] 脱糖 → 逐层 '*'、t.CPtr → 'i8*'、跨模块类 → sha1 前缀) + → MangleTypeArgByRegistry + """ + return MangleTypeArgByRegistry(ResolveTypeArgStr(Node, TCAliasMap, StructSha1Map)) diff --git a/lib/vast/ExprParser.py b/lib/vast/ExprParser.py index 35d1680..76fe26c 100644 --- a/lib/vast/ExprParser.py +++ b/lib/vast/ExprParser.py @@ -23,6 +23,30 @@ from lib.vast.Tokenizer import ( ) +# --------------------------------------------------------------------------- +# `new [描述符...] T(args)` 生命周期分配表达式(与 ViperC includes/ast/exprs.vp +# 的 NEW_SRC_*/NEW_LIFE_* 同值)。TPC 侧同样按 VPC 口径解析成 New 节点,语义落地 +# 由 Handles 负责(见 HandlesExprCall._HandleNewExprLlvm)。 +# --------------------------------------------------------------------------- +NEW_SRC_DEFAULT = 0 +NEW_SRC_STACK = 1 +NEW_SRC_HEAP = 2 +NEW_SRC_MEMGR = 3 +NEW_LIFE_DEFAULT = 0 +NEW_LIFE_SLIFE = 1 +NEW_LIFE_HLIFE = 2 + +# `new` 之后的普通名不能是这些保留字(它们在 vast 里也是 NAME token)。 +# 缺此判据时 `for new in x:` 会被误判为 new 表达式(`in` 紧随其后)。 +_NEW_NEXT_RESERVED: frozenset[str] = frozenset({ + 'in', 'is', 'and', 'or', 'not', 'if', 'else', 'elif', 'for', 'while', + 'lambda', 'await', 'yield', 'return', 'pass', 'break', 'continue', + 'import', 'from', 'as', 'with', 'try', 'except', 'finally', 'raise', + 'def', 'class', 'global', 'nonlocal', 'del', 'assert', 'match', 'case', + 'True', 'False', 'None', +}) + + def _JoinConstParts(parts: list[object]) -> str | bytes: """拼接连续字符串字面量值(str 或 bytes,与 CPython 语义一致)。""" if not parts: @@ -705,6 +729,58 @@ class ExprParserMixin(TokenStream): # ================================================================== # 原子表达式 # ================================================================== + def _ParseNewExpr(self) -> N.expr: + """`new [hlife|slife] [stack|heap|memgr(x)] T(args)`(软关键字,与 VPC 同口径)。 + + 描述符顺序无关、各轴最多一次;T 走主表达式(Name/Attribute/Subscript + 调用 + trailer),若解析结果本身是 Call(`T(args)`)则拆成 func/args,否则 args=None。 + 结果恒为 T*(语义在 Handles 侧落地:取存储 + 构造)。 + """ + start: Token = self._Next() # 吃掉 `new` + src: int = NEW_SRC_DEFAULT + life: int = NEW_LIFE_DEFAULT + memgr: N.expr | None = None + while True: + tok: Token = self._Peek() + if tok.Type != NAME: + break + v: str = tok.Value + if v == 'hlife': + self._Next() + life = NEW_LIFE_HLIFE + continue + if v == 'slife': + self._Next() + life = NEW_LIFE_SLIFE + continue + if v == 'stack': + self._Next() + src = NEW_SRC_STACK + continue + if v == 'heap': + self._Next() + src = NEW_SRC_HEAP + continue + if v == 'memgr': + self._Next() + self._ExpectOp('(') + self._SkipNL() + memgr = self.ParseExpr() + self._SkipNL() + self._ExpectOp(')') + src = NEW_SRC_MEMGR + continue + break + callee: N.expr = self._ParsePrimary() + func: N.expr = callee + args: list[N.expr] | None = None + if isinstance(callee, N.Call): + func = callee.func + args = callee.args + node: N.New = N.New(Source=src, Life=life, memgr=memgr, func=func, args=args) + self._FixLoc(node, start) + return node + def _ParseAtom(self) -> N.expr: self._SkipNL() tok: Token = self._Peek() @@ -713,6 +789,10 @@ class ExprParserMixin(TokenStream): if tok.Type in (STRING, FSTRING): return self._ParseStringConcat() if tok.Type == NAME: + if tok.Value == 'new': + nxt: Token = self._Peek(1) + if nxt.Type == NAME and nxt.Value not in _NEW_NEXT_RESERVED: + return self._ParseNewExpr() if tok.Value in ('True', 'False', 'None'): self._Next() node: N.Constant = N.Constant(value={'True': True, 'False': False, 'None': None}[tok.Value], kind=None) @@ -744,6 +824,13 @@ class ExprParserMixin(TokenStream): def _ParseNumber(self) -> N.Constant: tok: Token = self._Next() text: str = tok.Value + # 显式宽度后缀(_int/_uint/_i/_u):先从文本里剥掉再解析数值 + width: int = getattr(tok, 'IntWidth', 0) + unsigned: int = getattr(tok, 'IntUnsigned', 0) + if width > 0: + cut: int = text.rfind('_') + if cut > 0: + text = text[:cut] try: if text.lower().endswith('j'): num: object = complex(text[:-1].replace('_', '')) @@ -757,6 +844,10 @@ class ExprParserMixin(TokenStream): # 无法解析:保留原字符串(理论上不会发生) num = text node: N.Constant = N.Constant(value=num, kind=None) + if width > 0: + # 显式宽度随节点带走,由 codegen 做「不截断」严格校验 + node.int_width = width + node.int_unsigned = unsigned self._FixLoc(node, tok) return node @@ -1605,8 +1696,8 @@ class ExprParserMixin(TokenStream): int** → (int | t.CPtr) | t.CPtr (i32**) int****** → 6 层嵌套 (i32******) - 展开为 BinOp(BitOr) 链后,TPC/TPV 现有类型解析(TypeAnnotationResolver - 的 PtrCount 相加、TPV TypeGenResolve 的递归 BinOp 分支)零改动即可 + 展开为 BinOp(BitOr) 链后,TPC/VPC 现有类型解析(TypeAnnotationResolver + 的 PtrCount 相加、VPC TypeGenResolve 的递归 BinOp 分支)零改动即可 生成多级指针 LLVM 类型。 无法解析的后缀形态**响亮失败**(SyntaxError,含 行:列 + 源码片段 + 形态 + diff --git a/lib/vast/Nodes.py b/lib/vast/Nodes.py index a8666ea..18071e5 100644 --- a/lib/vast/Nodes.py +++ b/lib/vast/Nodes.py @@ -109,9 +109,14 @@ _NODE_DEFS: dict[str, tuple[tuple[str, ...], tuple[str, ...]]] = { 'YieldFrom': (('value',), _POS_ATTRS), 'Compare': (('left', 'ops', 'comparators'), _POS_ATTRS), 'Call': (('func', 'args', 'keywords'), _POS_ATTRS), + # Viper `new` 生命周期分配表达式(软关键字):new [描述符...] T(args)。 + # Source/Life 为描述符整数(见 NEW_SRC_*/NEW_LIFE_*);memgr 为 `memgr(x)` 的 x + # (无 memgr 描述符时为 None);func 是被构造的 callee(Name/Attribute/Subscript), + # args 为构造实参列表(无括号时为 None)。 + 'New': (('Source', 'Life', 'memgr', 'func', 'args'), _POS_ATTRS), 'FormattedValue': (('value', 'conversion', 'format_spec'), _POS_ATTRS), 'JoinedStr': (('values',), _POS_ATTRS), - 'Constant': (('value', 'kind'), _POS_ATTRS), + 'Constant': (('value', 'kind'), _POS_ATTRS + ('int_width', 'int_unsigned')), 'Attribute': (('value', 'attr', 'ctx'), _POS_ATTRS), 'Subscript': (('value', 'slice', 'ctx'), _POS_ATTRS), 'Starred': (('value', 'ctx'), _POS_ATTRS), @@ -245,6 +250,7 @@ _CLASS_DEFAULTS: dict[str, dict[str, object]] = { 'MatchAs': {'pattern': None, 'name': None}, 'MatchMapping': {'rest': None}, 'MatchStar': {'name': None}, + 'New': {'memgr': None, 'args': None}, 'Raise': {'exc': None, 'cause': None}, 'Return': {'value': None}, 'Slice': {'lower': None, 'upper': None, 'step': None}, @@ -276,7 +282,8 @@ _BASE_CLASS: dict[str, str] = { 'Call': 'expr', 'Compare': 'expr', 'Constant': 'expr', 'Dict': 'expr', 'DictComp': 'expr', 'FormattedValue': 'expr', 'GeneratorExp': 'expr', 'IfExp': 'expr', 'JoinedStr': 'expr', 'Lambda': 'expr', 'List': 'expr', - 'ListComp': 'expr', 'Name': 'expr', 'NamedExpr': 'expr', 'Set': 'expr', + 'ListComp': 'expr', 'Name': 'expr', 'NamedExpr': 'expr', + 'New': 'expr', 'Set': 'expr', 'SetComp': 'expr', 'Slice': 'expr', 'Starred': 'expr', 'Subscript': 'expr', 'Tuple': 'expr', 'UnaryOp': 'expr', 'Yield': 'expr', 'YieldFrom': 'expr', # mod diff --git a/lib/vast/Tokenizer.py b/lib/vast/Tokenizer.py index 4f990be..3e6063e 100644 --- a/lib/vast/Tokenizer.py +++ b/lib/vast/Tokenizer.py @@ -76,10 +76,12 @@ class Token: Start: 源码字符偏移(含 UTF-8 字节偏移换算依赖,供 f-string 内部定位) End: 源码字符偏移(exclusive) """ - __slots__ = ('Type', 'Value', 'Line', 'Col', 'EndLine', 'EndCol', 'Start', 'End') + __slots__ = ('Type', 'Value', 'Line', 'Col', 'EndLine', 'EndCol', 'Start', 'End', + 'IntWidth', 'IntUnsigned') def __init__(self, tok_type: str, value: str, line: int, col: int, - end_line: int, end_col: int) -> None: + end_line: int, end_col: int, + int_width: int = 0, int_unsigned: int = 0) -> None: self.Type: str = tok_type self.Value: str = value self.Line: int = line @@ -88,6 +90,9 @@ class Token: self.EndCol: int = end_col self.Start: int = -1 self.End: int = -1 + # 整型字面量显式宽度后缀(_int/_uint/_i/_u):0 = 未指定 + self.IntWidth: int = int_width + self.IntUnsigned: int = int_unsigned def __repr__(self) -> str: return (f'Token({self.Type}, {self.Value!r}, ' @@ -152,12 +157,13 @@ class Tokenizer: col_bytes = len(seg.encode('utf-8')) return line, col_bytes - def _Make(self, tok_type: str, start: int, end: int) -> Token: + def _Make(self, tok_type: str, start: int, end: int, + int_width: int = 0, int_unsigned: int = 0) -> Token: """以 [start, end) 源码区间构造 token。""" start_line, start_col = self._PosInfo(start) end_line, end_col = self._PosInfo(end) tok: Token = Token(tok_type, self.Source[start:end], start_line, start_col, - end_line, end_col) + end_line, end_col, int_width, int_unsigned) tok.Start = start tok.End = end return tok @@ -335,8 +341,45 @@ class Tokenizer: self.Tokens.append(self._Make(NAME, start, self.Pos)) self.AtLineStart = False + def _TryScanWidthSuffix(self) -> tuple[int, int]: + """整型字面量显式宽度后缀 `_int`/`_uint`/`_i`/`_u`。 + + 命中则推进 Pos 并返回 `(N, 无符?)`;否则返回 `(0, 0)` 且不动位置。 + N 取 1..64(字面量值本身是 64 位);后缀之后不得直接跟标识符字符。 + """ + src: str = self.Source + pos: int = self.Pos + if pos >= self.Length or src[pos] != '_': + return 0, 0 + p: int = pos + 1 + unsigned: int = 0 + if src.startswith('uint', p): + p += 4 + unsigned = 1 + elif src.startswith('int', p): + p += 3 + elif src.startswith('u', p): + p += 1 + unsigned = 1 + elif src.startswith('i', p): + p += 1 + else: + return 0, 0 + digits_start: int = p + while p < self.Length and src[p].isdigit(): + p += 1 + if p == digits_start: + return 0, 0 + width: int = int(src[digits_start:p]) + if width <= 0 or width > 64: + return 0, 0 + if p < self.Length and (src[p].isalnum() or src[p] == '_'): + return 0, 0 + self.Pos = p + return width, unsigned + def _ScanNumber(self) -> None: - """扫描数字字面量:进制前缀/十进制/浮点/指数/复数 j/下划线。""" + """扫描数字字面量:进制前缀/十进制/浮点/指数/复数 j/下划线/显式宽度后缀。""" start: int = self.Pos # 进制前缀 0x/0o/0b if self.Source[self.Pos] == '0' and self.Pos + 1 < self.Length: @@ -346,7 +389,8 @@ class Tokenizer: while self.Pos < self.Length and (self.Source[self.Pos].isalnum() or self.Source[self.Pos] == '_'): self.Pos += 1 - self.Tokens.append(self._Make(NUMBER, start, self.Pos)) + hw, hu = self._TryScanWidthSuffix() + self.Tokens.append(self._Make(NUMBER, start, self.Pos, hw, hu)) self.AtLineStart = False return # 整数部分 @@ -375,7 +419,13 @@ class Tokenizer: # 复数 j if self.Pos < self.Length and self.Source[self.Pos] in 'jJ': self.Pos += 1 - self.Tokens.append(self._Make(NUMBER, start, self.Pos)) + # 显式宽度后缀(仅整数字面量;浮点/复数不接受) + iw: int = 0 + iu: int = 0 + body: str = self.Source[start:self.Pos] + if not any(c in body for c in '.eEjJ'): + iw, iu = self._TryScanWidthSuffix() + self.Tokens.append(self._Make(NUMBER, start, self.Pos, iw, iu)) self.AtLineStart = False # ------------------------------------------------------------------ diff --git a/lib/vast/Unparser.py b/lib/vast/Unparser.py index 3857b09..6cd124a 100644 --- a/lib/vast/Unparser.py +++ b/lib/vast/Unparser.py @@ -319,12 +319,33 @@ class Unparser: 'Await': self._Await, 'Yield': self._Yield, 'YieldFrom': self._YieldFrom, + 'New': self._New, } fn = m.get(name) if fn is not None: return fn(node) # type: ignore[operator] raise NotImplementedError(f'unparse expr: {name}') + def _New(self, node: N.New) -> str: + """`new [hlife|slife] [stack|heap|memgr(x)] T(args)`(与 ExprParser 的取值同口径: + NEW_SRC_STACK=1/HEAP=2/MEMGR=3,NEW_LIFE_SLIFE=1/HLIFE=2,DEFAULT=0)。""" + out: str = 'new' + if getattr(node, 'Life', 0) == 1: + out += ' slife' + elif getattr(node, 'Life', 0) == 2: + out += ' hlife' + src: int = getattr(node, 'Source', 0) + if src == 1: + out += ' stack' + elif src == 2: + out += ' heap' + elif src == 3 and node.memgr is not None: + out += f' memgr({self._expr(node.memgr)})' + callee: str = self._expr(node.func) if node.func is not None else '' + if node.args: + callee += '(' + ', '.join(self._expr(a) for a in node.args) + ')' + return f'{out} {callee}' + def _Name(self, node: N.Name) -> str: return node.id diff --git a/lib/vast/__init__.py b/lib/vast/__init__.py index d3572c8..99d148e 100644 --- a/lib/vast/__init__.py +++ b/lib/vast/__init__.py @@ -138,6 +138,16 @@ def _HumanizeSyntaxError(exc: SyntaxError, source: str, filename: str) -> Syntax return new +# Viper `new` 生命周期软关键字表达式:vast_c(stage 0 子集)不认它,会把 `new` 当普通名 +# 产出 Name 节点。命中时强制走纯 Python 解析(语义基准),避免 C 路径产出错误 AST。 +_NEW_EXPR_RE: _re.Pattern[str] = _re.compile(r'(? bool: + """源码里是否出现 `new <普通名>` 形态(用于规避 C 解析器对 new 的误解)。""" + return _NEW_EXPR_RE.search(source) is not None + + def parse(source: str, filename: str = '', mode: str = 'exec', *, type_comments: bool = False, feature_version: tuple[int, int] | int | None = None) -> AST: """解析源码字符串为 AST(与 ast.parse 同签名、同结构)。 @@ -147,7 +157,8 @@ def parse(source: str, filename: str = '', mode: str = 'exec', *, 异常直接抛出。两条路径的 SyntaxError 都收口成带 文件/行/列/源码片段 的统一 报文(不再向用户漏出解析器内部 traceback)。 """ - if _VAST_C_PARSE is not None and _os.environ.get('VAST_USE_C', '1') != '0': + if (_VAST_C_PARSE is not None and _os.environ.get('VAST_USE_C', '1') != '0' + and not _SourceUsesNewExpr(source)): try: return _VAST_C_PARSE(source, mode) except SyntaxError as _exc: