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.core import TypeSystem from lib.core.TypeSystem import CTypeRegistry from lib.core.SymbolUtils import ( ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, ResolveTypeArgStr, GenericArgRawName, ClassHasFuncLevelCVTable, IsVtableSlotMethod) 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() 只支持定长数组/数组指针(CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;" "非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度," "或改用 CPtr[元素] 并显式维护元素计数" ) # 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) 联合类型中是否含 CPtr(指针标记)""" if isinstance(node, ast.Name): return node.id == '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: 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 函数类型的形参表:来源是函数指针槽 (模块级/局部 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} 个实参;请检查该函数指针(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__(...) 语法糖:等价 Asm(...)(裸名调用,无需 import c) if FuncName == '__asm__': return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) # __llvmir__(...) 语法糖:等价 LLVMIR(...) if FuncName == '__llvmir__': return self._HandleLLVMIRLlvm(Node) # irinp/irout 等价 LInp/LOut(仅作模板占位符,独立调用返回 0) if FuncName in ('irinp', 'irout'): return ir.Constant(ir.IntType(32), 0) # 内建语法糖调用(裸名):Asm / Addr / Deref / DerefAs / Set / Load / # PtrToInt / CError / AsmInp / AsmOut / LLVMIR / LInp / LOut / # CPragma / CUndef 不是普通函数,优先于符号查找。 if FuncName == 'Asm': return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node) if FuncName == 'Addr': return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node) if FuncName == 'Set': return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node) if FuncName == 'Load': return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node) if FuncName == 'Deref': return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node) if FuncName == 'DerefAs': return self._HandleCDerefAsLlvm(Node) if FuncName == 'PtrToInt': return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) if FuncName in ('AsmInp', 'AsmOut', 'LInp', 'LOut'): return ir.Constant(ir.IntType(32), 0) if FuncName == 'LLVMIR': return self._HandleLLVMIRLlvm(Node) if FuncName in ('CPragma', 'CUndef'): return ir.Constant(ir.IntType(1), 1) if FuncName == '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})") # 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, '_from_import_aliases', {}) 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) # 检查是否是全局变量中的函数指针(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] # 检查是否是函数指针变量(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}") # 处理 CPtr (i8*) 函数指针调用 # 当函数被赋值给 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 等价 CPtr(void* → i8*):目标类型取内置 rawptr 的权威 # CTypeInfo(_builtin_rawptr_info,与注解侧同源),转换复用 (rawptr[X])(x) # / (CPtr)(x) 的 _emit_rawptr_cast——不新造第二套 rawptr 表示。 # 不照抄 uchar 的做法(往 builtins.vp 加 `uchar: 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) = 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 值:'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 from lib.core.TypeSystem 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 声明 → 逐级展开到 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.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, CPtr) / int64(v, CPtr):第二参是 # CPtr 标记 → 指针包装(等价旧写法 CUInt8T(s, CPtr), # 对齐 CXXX(v, 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.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) / (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 if _sub_nm in ('rawptr', '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 | 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__() / 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: _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 # 语法糖调用一律使用裸内建名;t./c. 前缀在调用点直接报错。 if ModulePath in ('t', 'c'): raise TypeError( f"[TPC] 类型注解/调用不再接受 '{ModulePath}.' 前缀;" f"请直接写裸内建名,如 {FuncAttr}(...)") # stdarg 模块的变参糖:stdarg.arg / stdarg.va_arg / stdarg.va_start / # stdarg.va_end 命中与裸名同名内建**完全相同**的处理(stdarg 是编译器 # 拦截的糖模块,不是真符号,与内建语法糖同一口径)。仅当 # 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 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 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: # _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 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 # LInp/LOut 与裸名 irinp/irout 等价(见 _intrinsic_marker) _marker: str | None = self.Trans.ExprAsmHandle._intrinsic_marker(expr) 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 else: # 非标记表达式按普通输入操作数处理 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: # 整型加宽必须**保符号**(i32/i64 按有符号语义):此前一律 zext,会把负值 # 当无符号处理。现场:`int64(-1)` 里 `-1` 的 i32 位型被 zext 成 4294967295 # ⇒ `x ^ int64(-1)` 变号 ⇒ IntWidthTest Test 5 第三条(循环不收敛)FAIL。 return Gen.builder.sext(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: ModulePath = self.Trans.SymbolTable.resolve_alias(ModulePath) is_user_module = (ModulePath and (ModulePath in Gen.ModuleSha1Map or ModulePath in getattr(self.Trans, '_ImportedModules', set()) or ModulePath in self.Trans.SymbolTable.import_aliases)) if ModulePath 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 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, TypeSystem.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 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, TypeSystem.CExport) or isinstance(type_info.Storage, TypeSystem.CExtern): pass if type_info.IsState: Gen._export_funcs.add('_type_union_cast') else: # Fallback 路径:MergeTypes 失败(返回 None 或抛异常)时,手动从 BinOp 提取类型信息 # 处理 (BaseType | 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 | CPtr)(x)` 只可能来自指针强转——parser 把 `(T*)x` / `(T*)(x)` / # `T*(x)`(含 `**`)统一产成 func = `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 | CPtr) 这种模式,手动解析为 "{BaseType} *"。 支持嵌套的 | 操作(如 (CInt | CPtr) | CStatic)。 策略: - 递归展平所有 | 操作数 - 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 # 是否包含 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 # CPtr 是指针修饰符,单独处理(增加一层 *) if AttrName == 'CPtr': HasCPtr = True continue # 跳过存储类/限定符/修饰符类型,它们不是基础类型 if AttrName in StorageQualifierNames: continue # 通过 TypeSystem 获取类型类,验证是否为 CType 子类 TypeClass = getattr(TypeSystem, AttrName, None) if not (isinstance(TypeClass, type) and issubclass(TypeClass, TypeSystem.CType)): # 非 CType 类,跳过 continue # 记录第一个非 CPtr 基础类型 if BaseTypeName is None: BaseTypeName = AttrName if not BaseTypeName: return None # 包含 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: # **显式**强转的加宽必须保符号:此前手写 zext,负值/负数常量会被零扩展 # (现场:`int64(-1)` 变 4294967295;IntWidthTest Test 5)。 return Gen.builder.sext(ArgVal, TargetType, name=f"sext_{TypedefName}_cast") elif ArgVal.type.width > TargetType.width: # 显式窄化 = 截断(“隐式窄化禁止”只针对**隐式**路径,不拦显式转换) return Gen.builder.trunc(ArgVal, TargetType, name=f"trunc_{TypedefName}_cast") 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) / (CPtr[X])(expr) 内建指针转换:expr → X* rawptr 是编译器内建裸指针语法(无 SymbolTable typedef 条目、无泛型模板), 此前 Subscript 调用仅查泛型模板 → 落空后报 "Unsupported call expression" (StubMergerCollect.vp `(rawptr[FileEntry])(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) 等):加宽保符号(sext), # 窄化=截断(显式转换不受“隐式窄化禁止”约束)。 # 此前加宽用 zext,`int64(-1)` 会变成 4294967295(IntWidthTest Test 5)。 if ArgVal.type.width < TargetType.width: return Gen.builder.sext(ArgVal, TargetType, name="sext_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 是否有 @CVTable 装饰器(或 @CVTable)""" for decorator in node.decorator_list: if isinstance(decorator, ast.Call): inner = decorator.func 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 是否有 @NoVTable 装饰器""" for decorator in node.decorator_list: 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 检查 @CVTable 并注册到 vtable 集合。 沿继承链递归收集方法,保证 vtable 布局与 prescan 的 _EmitClassLlvm 一致: - 基类方法在前(固定索引,保证多态分派正确) - 子类新增方法在后 - 子类覆盖的方法放在父类的位置(用子类实现) 与 prescan 的 _resolve_class_flags 逻辑一致: - 类自身有 @CVTable → 注册 - 类自身有 @NoVTable → 不注册 - 类继承 @CVTable 基类(且无 @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 一致): # 类自身无 @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`(⇒ @NoVTable)不进槽; # `virtual def`(⇒ @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[CDouble] | 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) : 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.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) 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 检查 @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|CPtr].append 中 self.__pool__.alloc(...), # __pool__ 类型 memhub.MemManager|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 与 ViperC 同口径)。 规则(对齐 ViperC App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支): 定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen; 带 __len__ 的结构体 → 调用 __len__;其余一律编译期报错。 非 i8 裸指针(如 `[]T` 形参实为 `T*`)与标量指针**没有长度语义**,旧实现会 静默返回常量 0(或回落 strlen)从而掩盖错误,这里改为响亮失败。 报文含 行:列 + 源码片段 + 期望形态 + 正确做法提示。 """ raw_lineno: int = getattr(arg_node, 'lineno', 0) or 0 lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 col: int = getattr(arg_node, 'col_offset', -1) src_file: str = getattr(Gen, '_current_source_file', '') or '' _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) if _line_offset > 0 and lineno > _line_offset: lineno -= _line_offset 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 = src_file if lineno > 0: loc += f":{lineno}" if col >= 0: loc += f":{col}" actual: str = str(val.type) if val is not None else '<未知>' msg: str = (f"[fast-fail] {reason}。{_LEN_SUPPORTED_HINT}。" f"实际类型 '{actual}'。位置 {loc}" + (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] | 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 → '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, '_from_import_aliases', {}), StructSha1Map=StructSha1Map) def _get_raw_type_name(self, node: ast.AST) -> str: """获取类型节点的原始名称(不带模块前缀),用于泛型 T 替换。 ast.Name → sn.id (如 'AST', 'CInt') ast.Attribute → sn.attr (如 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 | CPtr' / 'rawptr[AST]' → '{sha1}.AST*'、裸类名 'AST' → '{sha1}.AST'、标量按 NormalizeGenericArgName 归一) type_name: 用于替换泛型类型参数 T(原始源码名,如 'AST' / 'CInt', _specialize_generic_class 会自动给类名加 | CPtr) 指针下标 rawptr[T] 先折叠成等价的 `T | CPtr` 再解析, 与源码联合写法得到同一 (type_arg, type_name):否则 list[rawptr[ast.AST]] 会拼出 'rawptr[ast.AST]' 实参串(内层未混淆)→ 与 list[ast.AST | 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 (如 mod.CInt) type_str: str = self._resolve_type_arg_str(sn) # type_name 保留源码名(如 'CUnsignedChar'),避免 T(0) 替换为 # CUnsignedChar(0) 后因 CUnsignedChar 不在顶层作用域而报 # "Undefined function: 'CUnsignedChar'"。 # CPtr 同样保留 'CPtr':作为 type_map['T'] 替换进模板(item: T → # item: CPtr)时需要可解析;指针参数识别由 _BUILTIN_TYPE_NAMES 的 # tn_bare 检测('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: """判断节点是否为 `CPtr` 标记(裸名)。""" if isinstance(Node, ast.Name): return Node.id == 'CPtr' return False def _CallReturnClass(self, Node: ast.Call, Gen: LlvmGeneratorMixin) -> str | None: """调用结果的**被指类名**:从被调者的返回注解恢复。 背景:`list[str]*` ≡ `list[str] | CPtr`(只读探针已确认二者 AST 逐字同形, 都是 `BinOp(BitOr, Subscript(...), Attribute(CPtr))`)这类返回注解在 LLVM 层 被映射成通用指针 `i8*`,类名随之丢失 ⇒ 调用点只能按通用指针处理 (`parts = s1.split(',')` 之后 `parts[0]` 退化成按字节索引、得到整数,赋给 `str` 时报"整数隐式转指针已禁止",真因在 4 行之前)。 本方法把类名从**注解**取回;判据与 HandlesAnnAssign 的同类恢复 (:644-684 `rawptr[X]`、:1012-1034 联合注解构特化名)同源。 取不到返回 None —— 调用方据此不改动既有行为。 """ _fn: ast.AST = Node.func _cands: list[str] = [] if isinstance(_fn, ast.Name): _cands.append(_fn.id) elif isinstance(_fn, ast.Attribute): _m: str = _fn.attr _recv_cls: str | None = None if isinstance(_fn.value, ast.Name): _recv_cls = (Gen.var_struct_class.get(_fn.value.id) or (getattr(Gen, 'global_struct_class', {}) or {}).get(_fn.value.id)) # FakeDuck 影子类(str→_str / bytes→_bytes)与接收者真实类名两条线都试; # FuncDefCache 的方法键在仓库里既有 'cls.method' 也有裸 'method' 两种形态。 for _c in ([_recv_cls] if _recv_cls else []) + ['_str', '_bytes']: _cands.append(f'{_c}.{_m}') _cands.append(_m) for _nm in _cands: _fd: ast.FunctionDef | None = self.Trans.FunctionDefCache.get(_nm) if _fd is not None and _fd.returns is not None: _cls: str | None = self._ClassFromAnnotation(_fd.returns, Gen) if _cls: return _cls return None def _ClassFromAnnotation(self, Ann: ast.AST, Gen: LlvmGeneratorMixin) -> str | None: """从注解恢复"类名(含泛型特化名)":剔除 `| CPtr` 侧与指针层后取核类型。 - `list[str] | CPtr` / `list[str]*` → `list[str]`(泛型特化名,与 HandlesAnnAssign:1012-1034 的构名方式一致); - `Foo | CPtr` / `Foo*` → `Foo`; - 核类型名必须能在 `Gen.structs` / `Gen.class_members` 命中(含 sha1 前缀 全名的短名归一),否则返回 None(类型信息不完整时不硬塞名字)。 """ _core: ast.AST = Ann if isinstance(_core, ast.BinOp) and isinstance(_core.op, ast.BitOr): _l: ast.AST = _core.left _r: ast.AST = _core.right if self._IsCPtrMark(_r): _core = _l elif self._IsCPtrMark(_l): _core = _r else: return None if isinstance(_core, ast.Subscript): _base: ast.expr = _core.value _bn: str | None = None if isinstance(_base, ast.Name): _bn = _base.id elif isinstance(_base, ast.Attribute): _bn = _base.attr if not _bn: return None _sl: ast.AST = _core.slice if hasattr(ast, 'Index') and isinstance(_sl, ast.Index): _sl = _sl.value _r2: tuple[str, str] | None = self._resolve_generic_slice_type(_sl) if not _r2: return None _spec: str = f'{_bn}[{_r2[0]}]' if _spec in Gen.structs or _spec in Gen.class_members: return _spec # 端键归一:特化键可能是 sha1 前缀全名 for _k in Gen.structs: if _k.split('.')[-1] == _spec: return _k return _spec if isinstance(_core, ast.Name): if _core.id in Gen.structs or _core.id in Gen.class_members: return _core.id for _k in Gen.structs: if _k.split('.')[-1] == _core.id: return _k return None if isinstance(_core, ast.Attribute): if _core.attr in Gen.structs or _core.attr in Gen.class_members: return _core.attr for _k in Gen.structs: if _k.split('.')[-1] == _core.attr: return _k 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: CTypedef = ndarray[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 是否包含 CPtr(即是否为指针类型)""" if isinstance(Node, ast.Name): if Node.id == '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 | CPtr):VPC 注解中 | = and,带 CPtr 的联合 # 是指针类型(X* 或 i8*),sizeof = 8(指针大小)。 # list[T].__init__ 的 T.__sizeof__() 特化后 T 被还原为 'X | 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) # 不带 CPtr 的联合(A | B)无法确定尺寸:曾静默返回 0 → # 下游 elem_size=0 → alloc(0) 返回 NULL / 所有元素存取同槽。fail-fast。 raise ValueError( f"[FATAL][__sizeof__] 联合类型 '{ast.unparse(Node)}' 不含 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。 # 根因:CPtr.__sizeof__() 返回 0 → list[CPtr] # elem_size=0 → append 全写同槽/alloc(0) → 崩溃。 if getattr(ctype_inst, 'HasPosition', None) \ and ctype_inst.HasPosition(TypeSystem.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: """DerefAs(p, v):bitcast **指针** p 到 typeof(v)* 后按值存 v(`*(T*(p)) = v`)。 与 ViperC CallGenCBuiltin.translate_c_derefas 逐字同口径: - 单参形式(len<2)是**解引用读取**(等同 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(整数)的情况:联合类型 | 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)