Files
TransPyC/lib/core/Handles/HandlesExprCall.py
T
TermiNexus f8d84a1941 fix(generic): 特化缓存声称完整但方法缺失时强制补发
将缓存临时标记为 declare_only 后重新调用 _specialize_generic_class,触发方法体生成。
2026-10-18 19:13:29 +08:00

5654 lines
335 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
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_<name>()(返回帧指针)
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 = dict(_TAR_TD._scan_builtins()['typedefs'])
for _alias_name, _alias_cls in TypeSystem.BUILTIN_TYPE_ALIASES.items():
if _alias_name not in _td_table:
_td_table[_alias_name] = _alias_cls.__name__
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.GetCTypeInfo(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"<ToString 异常 {type(_E).__name__}: {_E}>"
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,不含
# `<REnum>_<Variant>` 声明(见 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(']'):
Gen._generic_spec_error = f'非特化名: {ClassName}'
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):
Gen._generic_spec_error = f'模板未注册: {base_name}'
return
# 构造 spec_key(与 _specialize_generic_class 一致:ClassName<arg1,arg2,...>)
_TypeArgs: list[str] = ClassName[len(base_name) + 1:-1].split('][')
_TypeNames: list[str] = [a.rsplit('.', 1)[-1] for a in _TypeArgs]
spec_key: str = base_name + '<' + ','.join(_TypeArgs) + '>'
spec_cache = getattr(Gen, '_generic_class_specializations', {})
cached = spec_cache.get(spec_key)
if not isinstance(cached, tuple) or len(cached) < 2:
# 缓存中还没有该特化:直接从头生成完整特化(方法体随类特化一起产出)
try:
self.Trans.ClassHandler._specialize_generic_class(
base_name, _TypeArgs, Gen,
type_names=_TypeNames, declare_only=False)
except Exception as _spec_err:
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
return
cached_name: str = cached[0]
was_declare_only: bool = cached[1]
if not was_declare_only:
# 缓存声称已完整,但方法确实不存在:强制补发一次
spec_cache[spec_key] = (cached_name, True)
try:
self.Trans.ClassHandler._specialize_generic_class(
base_name, _TypeArgs, Gen,
type_names=_TypeNames, declare_only=False)
except Exception as _spec_err:
Gen._generic_spec_error = f'强制补发失败: {type(_spec_err).__name__}: {_spec_err}'
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:
Gen._generic_spec_error = f'方法已存在但查找名不一致: {full_method_name}'
return # 方法体已编译
# 从缓存获取 type_args/type_names;缓存缺失时直接从特化名派生
type_info = getattr(Gen, '_generic_class_spec_type_info', {}).get(spec_key)
if type_info is None:
_DerivedArgs: list[str] = ClassName[len(base_name) + 1:-1].split('][')
_DerivedNames: list[str] = [a.rsplit('.', 1)[-1] for a in _DerivedArgs]
type_info = (_DerivedArgs, _DerivedNames)
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
if ClassName.startswith('list[') and ClassName in Gen.structs:
# 泛型特化方法未生成时,回退到裸模板 list 的同名方法
struct_type = Gen.structs[ClassName]
self_ptr_type = ir.PointerType(struct_type)
BareFuncName: str = f'list.{MethodName}'
func = Gen._find_function(BareFuncName)
if func is not None:
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, BareFuncName)
self._append_eh_msg_out_arg(call_args_fb, func, Gen)
adjusted = Gen._adjust_args(call_args_fb, func)
return Gen.builder.call(func, adjusted, name=f"call_{BareFuncName}")
_spec_diag: str = getattr(Gen, '_generic_spec_error', '')
raise Exception(
f"Undefined method '{MethodName}' in class '{ClassName}' "
f"(no function declaration found for '{FullMethodName}')"
+ (f";泛型特化失败: {_spec_diag}" if _spec_diag else ""))
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
if _matched_cn.startswith('list['):
try:
_count_off: int | None = Gen._get_member_offset('__count__', _matched_cn, pointee)
except Exception:
# 特化结构体尚未展开布局(opaque):_list.vp 的 list 依次为
# __data__(ptr) / __count__ / __capacity__ / __pool__ / ...
_count_off = 8
if _count_off is not None:
_i8p = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name='list_i8p')
_count_addr = Gen.builder.gep(
_i8p, [ir.Constant(ir.IntType(64), _count_off)], name='list_count_addr')
_count_ptr = Gen.builder.bitcast(
_count_addr, ir.PointerType(ir.IntType(64)), name='list_count_ptr')
return Gen._load(_count_ptr, name='list_count')
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]
的 '<sha1>.AST*' 分裂成两个特化名(结构体/方法声明/调用点三端不一致)。
"""
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 = "<class 'unknown' size=0 signed=false, ptr=false>"
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"<enum '{enum_type_name}' size={_enum_bits // 8} signed=true, ptr=false>"
else:
llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg)
if isinstance(llvm_type, ir.PointerType):
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"<class '{type_info['name']}' size={type_info['size']} signed={str(type_info['signed']).lower()}, ptr={str(type_info['ptr']).lower()}>"
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 = <memgr>.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)