Files
TransPyC/lib/core/Handles/HandlesExprCall.py

4007 lines
231 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 ast
import os
import re
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.includes import t
from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import IsTModule, ExtractTypeNameFromBinOp
class ExprCallHandle(BaseHandle):
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 _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 _HandleCallLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
FuncAttr: str | None = None
ModulePath: str | None = None
if isinstance(Node.func, ast.Name):
FuncName = Node.func.id
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)):
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 != 32:
if arg_val.type.width < 32:
arg_val = Gen.builder.zext(arg_val, ir.IntType(32), name="zext_enum_arg")
else:
arg_val = Gen.builder.trunc(arg_val, ir.IntType(32), name="trunc_enum_arg")
return arg_val
return ir.Constant(ir.IntType(32), 0)
if SymInfo.IsTypedef:
return self._HandleTypedefCastLlvm(Node, FuncName)
NewFuncName = f'{FuncName}.__before_init__'
InitFuncName = f'{FuncName}.__init__'
if Gen._has_function(NewFuncName):
return self._HandleClassNewLlvm(Node, FuncName)
if Gen._has_function(InitFuncName) and FuncName in Gen.structs:
return self._HandleClassNewLlvm(Node, FuncName)
if FuncName in Gen.structs:
return self._HandleClassNewLlvm(Node, FuncName)
if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and FuncName in self.Trans.ClassHandler._generic_class_templates:
return self._HandleClassNewLlvm(Node, FuncName)
if self.Trans.FunctionDefCache.get(FuncName):
return self._HandleClosureCallLlvm(Node, FuncName)
if FuncName in self._C_LIB_FUNCS:
return self._HandleClosureCallLlvm(Node, FuncName)
ClassName = Gen.var_struct_class.get(FuncName)
if ClassName and Gen._has_function(f'{ClassName}.__call__'):
return self._HandleInstanceCallLlvm(Node, FuncName, ClassName)
t_c_imported = getattr(self.Trans, '_t_c_imported_names', {})
if FuncName in t_c_imported:
src_module, src_name = t_c_imported[FuncName]
virtual_node = self._make_virtual_attr_call(Node, src_module, src_name)
return self._HandleCallLlvm(virtual_node)
# 检查是否是全局变量中的函数指针t.Callable 类型在模块级定义)
if FuncName not in Gen.variables and FuncName in Gen.global_vars:
Gen._Load_var(FuncName)
if FuncName not in Gen.variables and FuncName in Gen.module.globals:
GVar = Gen.module.globals[FuncName]
if not isinstance(GVar, ir.Function):
StoredType = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else GVar.type
if isinstance(StoredType, ir.PointerType) and isinstance(StoredType.pointee, ir.FunctionType):
Gen.variables[FuncName] = GVar
# 检查是否是闭包变量lambda 赋值给变量后调用)
if FuncName in Gen.variables and Gen.variables[FuncName] is not None:
VarPtr = Gen.variables[FuncName]
# 检查是否是函数指针变量t.Callable 类型),直接加载并调用
if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(VarPtr.type.pointee.pointee, ir.FunctionType):
fn_ptr = Gen._load(VarPtr, name=f"Load_fn_{FuncName}")
args = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
args.append(ArgVal)
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)
return Gen.builder.call(fn_ptr, 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
if len(expected_fn_type.args) > 0 and call_args[0].type != expected_fn_type.args[0]:
call_args[0] = Gen.builder.bitcast(call_args[0], expected_fn_type.args[0], name=f"cast_env_{FuncName}")
return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}")
raise Exception(f"Undefined function: '{FuncName}'")
if isinstance(Node.func, ast.Subscript):
# Handle list[int](mb) syntax — generic class constructor with explicit type args
sub: ast.Subscript = Node.func
if isinstance(sub.value, ast.Name):
ClassName: str = sub.value.id
if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and ClassName in self.Trans.ClassHandler._generic_class_templates:
return self._HandleGenericClassNewLlvm(Node, ClassName, explicit_slice=sub.slice)
if isinstance(Node.func, ast.Attribute):
if Node.func.attr in ('update', 'final', 'transform'):
pass
if Node.func.attr == '__sizeof__':
if isinstance(Node.func.value, ast.Subscript):
spec_name = self._ResolveGenericSlice(Node.func.value)
if spec_name:
return self._HandleSizeofLlvm(ast.Name(id=spec_name, ctx=ast.Load()))
# 对于 BinOp/BitOr 联合类型(如 AST | t.CPtr
# sizeof 应返回指针大小 8而非结构体大小
# 必须在 HandleExprLlvm 之前检查,避免对类型名求值产生副作用
if isinstance(Node.func.value, ast.BinOp) and isinstance(Node.func.value.op, ast.BitOr):
if self._AnnotationContainsCPtr(Node.func.value):
return ir.Constant(ir.IntType(64), 8)
# 对于 ast.Attribute如 ast.ClassDef.__sizeof__() / t.CInt.__sizeof__()
# 直接调用 _HandleSizeofLlvm不走 HandleExprLlvm
# 因为 ast.Attribute 在 sizeof 上下文中一定是类型名,不是变量
if isinstance(Node.func.value, ast.Attribute):
type_name = Node.func.value.attr
SizeofNode = ast.Name(id=type_name, ctx=ast.Load())
_r = self._HandleSizeofLlvm(SizeofNode)
return _r
# 对于 ast.Name需要区分变量和类型名
# - 如果是已知变量Gen.variables/_reg_values/_direct_values/self
# 走 HandleExprLlvm 路径(如 myvar.__sizeof__()
# - 否则视为类型名,直接调用 _HandleSizeofLlvm
# 这避免了 HandleExprLlvm 对类型名求值产生副作用
# (如修改 builder 状态、污染 variables 缓存等),
# 这些副作用会导致泛型特化后 then 块为空
if isinstance(Node.func.value, ast.Name):
nm: str = Node.func.value.id
is_variable: bool = (
nm == 'self'
or nm in Gen.variables
or nm in getattr(Gen, '_reg_values', {})
or nm in getattr(Gen, '_direct_values', {})
)
if not is_variable:
SizeofNode = ast.Name(id=nm, ctx=ast.Load())
_r = self._HandleSizeofLlvm(SizeofNode)
return _r
# 变量/实例的 sizeof: 通过 HandleExprLlvm 获取值
ObjVal = self.HandleExprLlvm(Node.func.value)
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
pointee = ObjVal.type.pointee
if isinstance(pointee, ir.ArrayType):
# Calculate element size in bytes
elem = pointee.element
if isinstance(elem, ir.IntType):
elem_size = elem.width // 8
elif isinstance(elem, ir.PointerType):
elem_size = 8 # 64-bit pointers
elif isinstance(elem, (ir.LiteralStructType, ir.IdentifiedStructType)):
# Approximate: sum of field sizes
elem_size = 0
for field in getattr(elem, 'elements', []):
if isinstance(field, ir.IntType):
elem_size += field.width // 8
elif isinstance(field, ir.PointerType):
elem_size += 8
elif isinstance(field, ir.ArrayType):
if isinstance(field.element, ir.IntType):
elem_size += field.count * (field.element.width // 8)
else:
elem_size += field.count * 8
else:
elem_size += 8
elif isinstance(elem, ir.ArrayType):
if isinstance(elem.element, ir.IntType):
elem_size = elem.count * (elem.element.width // 8)
else:
elem_size = elem.count * 8
elif isinstance(elem, ir.FloatType):
elem_size = 4
elif isinstance(elem, ir.DoubleType):
elem_size = 8
else:
elem_size = 8 # fallback
return ir.Constant(ir.IntType(64), pointee.count * elem_size)
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
# 结构体实例: 直接计算大小
self._resolve_opaque_struct_members(pointee, Gen)
size = Gen._get_struct_size(pointee)
if size > 0:
return ir.Constant(ir.IntType(64), size)
# 回退: 尝试作为类型名处理
if isinstance(Node.func.value, ast.Name):
type_name = Node.func.value.id
SizeofNode = ast.Name(id=type_name, ctx=ast.Load())
return self._HandleSizeofLlvm(SizeofNode)
ModulePath = self._get_ModulePath(Node.func.value)
if ModulePath:
first_part = ModulePath.split('.')[0]
resolved_first = self.Trans.SymbolTable.resolve_alias(first_part)
if resolved_first != first_part:
ModulePath = resolved_first + ModulePath[len(first_part):]
# 统一获取模块 SHA1用于跨模块类型引用确保用源模块的 SHA1 而非当前模块)
# 放在 ModulePath 和 alias 解析之后,供所有 _HandleClassNewLlvm/_ensure_struct_declared 调用点使用
_ctor_module_sha1: str | None = None
if ModulePath and ModulePath not in {'t', 'c'}:
_ctor_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {})
_ctor_module_sha1 = _ctor_sha1_map.get(ModulePath)
if not _ctor_module_sha1 and '.' in ModulePath:
_ctor_module_sha1 = _ctor_sha1_map.get(ModulePath.split('.')[-1])
if ModulePath:
is_instance_var = isinstance(Node.func.value, ast.Name) and (
Node.func.value.id in Gen.var_struct_class
or Node.func.value.id in Gen.variables
or Node.func.value.id in Gen.global_vars
) and Node.func.value.id not in Gen.ModuleSha1Map
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
):
_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
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
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
if is_class_name:
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
# c 模块的特殊语法设施Addr/Deref/DerefAs/Set/Load/Asm 等)必须优先处理,
# 即使它们在 Gen.structs 中c.py 的类会被声明为结构体但不应被实例化)。
# c.py 是语法部分,不应被编译,其中的类是编译器语法设施。
if ModulePath == 'c':
if FuncAttr == 'Asm':
return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node)
elif FuncAttr == 'Addr':
return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node)
elif FuncAttr == 'Set':
return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node)
elif FuncAttr == 'Load':
return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node)
elif FuncAttr == 'Deref':
return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node)
elif FuncAttr == 'DerefAs':
return self._HandleCDerefAsLlvm(Node)
elif FuncAttr == 'PtrToInt':
return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node)
elif FuncAttr in ('CIf', 'CElif'):
return self.Trans.CSpecialCallHandle._HandleCIfLlvm(Node)
elif FuncAttr == 'CError':
msg = "compile-time error"
if Node.args:
arg = Node.args[0]
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
msg = arg.value
lineno = getattr(Node, 'lineno', 0)
raise Exception(f"#error: {msg} (line {lineno})")
elif FuncAttr in ('AsmInp', 'AsmOut'):
return ir.Constant(ir.IntType(32), 0)
elif FuncAttr == 'LLVMIR':
return self._HandleLLVMIRLlvm(Node)
elif FuncAttr in ('LInp', 'LOut'):
return ir.Constant(ir.IntType(32), 0)
if FuncAttr in Gen.structs:
result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1)
if result is not None:
return result
if ModulePath == 't':
MethodResult_t = self._HandleMethodCallLlvm(Node)
if MethodResult_t is not None:
return MethodResult_t
raise Exception(f"Undefined method: 't.{FuncAttr}'")
if isinstance(Node.func.value, ast.Attribute):
inner_attr = Node.func.value.attr
if inner_attr in Gen.structs or self.Trans.SymbolTable.is_struct(inner_attr):
static_result = self._HandleStaticMethodCallLlvm(Node, inner_attr, FuncAttr)
if static_result is not None:
return static_result
MethodResult_attr = self._HandleMethodCallLlvm(Node)
is_zero_attr = isinstance(MethodResult_attr, ir.Constant) and isinstance(MethodResult_attr.type, ir.IntType) and MethodResult_attr.constant == 0
if MethodResult_attr is not None and not is_zero_attr:
return MethodResult_attr
result = self._HandleExternalCallLlvm(Node, FuncAttr, ModulePath)
if result is not None:
return result
MethodResult = self._HandleMethodCallLlvm(Node)
IsZeroResult = isinstance(MethodResult, ir.Constant) and isinstance(MethodResult.type, ir.IntType) and MethodResult.constant == 0
if IsZeroResult and FuncAttr is not None:
mangled_name = Gen._mangle_func_name(FuncAttr, ModulePath if ModulePath and ModulePath != 't' else None)
if Gen._has_function(mangled_name):
CallArgs = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
return Gen.builder.call(Gen._get_function(mangled_name), CallArgs, name=f"call_{FuncAttr}")
if MethodResult is None or IsZeroResult:
imported = getattr(self.Trans, '_ImportedModules', set())
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
IsKnown = (ModulePath in imported or ModulePath in self.Trans.SymbolTable.import_aliases or
ModulePath in ModuleSha1Map)
if not IsKnown and ModulePath and '.' in ModulePath:
LastPart = ModulePath.split('.')[-1]
IsKnown = (LastPart in imported or LastPart in self.Trans.SymbolTable.import_aliases or
LastPart in ModuleSha1Map)
# 最后尝试: 用 FuncAttr 直接查找 Gen.functions_find_function 会遍历 SHA1 前缀)
if FuncAttr and ModulePath and ModulePath not in {'t', 'c'}:
# _ctor_module_sha1 已在函数顶部统一获取,此处无需重复
# 先检查 FuncAttr 是否是函数/变量,如果是则跳过 struct 声明
_attr_sym = self.Trans.SymbolTable.lookup(FuncAttr)
_is_func_or_var = _attr_sym and (_attr_sym.IsFunction or _attr_sym.IsVariable)
if not _is_func_or_var and ModulePath:
_full_key = f"{ModulePath}.{FuncAttr}"
_full_sym = self.Trans.SymbolTable.lookup(_full_key)
if _full_sym and (_full_sym.IsFunction or _full_sym.IsVariable):
_is_func_or_var = True
# 优先检查 FuncAttr 是否为 struct/类构造器
if not _is_func_or_var and FuncAttr not in Gen.structs:
self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1)
if not _is_func_or_var and FuncAttr in Gen.structs:
result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1)
if result is not None:
return result
# 检查 FullAttrKey 是否为 struct如 hashlib.md5
if ModulePath:
FullAttrKey = f"{ModulePath}.{FuncAttr}"
if self.Trans.SymbolTable.has(FullAttrKey):
FullSymInfo = self.Trans.SymbolTable[FullAttrKey]
if FullSymInfo.IsStruct or FullSymInfo.IsCpythonObject:
if FuncAttr not in Gen.structs:
self._ensure_struct_declared(FuncAttr, module_sha1=_ctor_module_sha1)
if FuncAttr in Gen.structs:
result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1)
if result is not None:
return result
fallback_func = Gen._find_function(FuncAttr)
if fallback_func is not None:
CallArgs = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
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 arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
self._append_eh_msg_out_arg(CallArgs, func_decl, Gen)
result = Gen.builder.call(func_decl, CallArgs, name=f"call_{FuncAttr}")
return self._check_eh_return(result, FuncAttr, Gen, called_func=func_decl)
if ModulePath and IsKnown:
raise Exception(f"Undefined symbol: '{ModulePath}.{FuncAttr}'")
if ModulePath and ModulePath not in {'t', 'c'} and not IsKnown:
raise Exception(f"Unknown module: '{ModulePath}' (undefined symbol: '{ModulePath}.{FuncAttr}')")
if not ModulePath and FuncAttr:
raise Exception(f"Undefined method: '{FuncAttr}'")
return MethodResult if MethodResult is not None else ir.Constant(ir.IntType(32), 0)
if isinstance(Node.func, ast.BinOp) and isinstance(Node.func.op, ast.BitOr):
return self._HandleTypeUnionCastLlvm(Node)
raise Exception(f"Unsupported call expression")
def _ensure_struct_declared(self, class_name: str, module_sha1: str | None = None) -> None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
# 如果名称是函数或变量,不应创建 struct
SymInfo = self.Trans.SymbolTable.lookup(class_name)
if SymInfo and (SymInfo.IsFunction or SymInfo.IsVariable):
return
# 跨模块类型引用:如果提供了 module_sha1优先检查带 SHA1 前缀的完整名
full_key: str | None = f"{module_sha1}.{class_name}" if module_sha1 else None
if full_key and full_key in Gen.structs:
existing = Gen.structs[full_key]
if isinstance(existing, ir.IdentifiedStructType) and (existing.elements is None or len(existing.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1)
return
if class_name in Gen.structs:
existing = Gen.structs[class_name]
if isinstance(existing, ir.IdentifiedStructType) and (existing.elements is None or len(existing.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1)
existing = Gen.structs.get(class_name, existing)
if isinstance(existing, ir.IdentifiedStructType) and existing.elements is not None and len(existing.elements) > 0:
return
else:
# 短名已存在但 SHA1 可能不匹配:如果 module_sha1 与现有 struct 的 SHA1 不一致,
# 需要用正确的 SHA1 创建独立 struct
if module_sha1:
_existing_sha1: str = Gen._extract_struct_sha1(existing)
if _existing_sha1 and _existing_sha1 != module_sha1:
# SHA1 不匹配,用正确的 SHA1 创建
correct_st = Gen._get_or_create_struct(class_name, source_sha1=module_sha1)
self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1)
return
SymInfo = self.Trans.SymbolTable.lookup(class_name)
if SymInfo and SymInfo.Members:
member_types = []
if isinstance(SymInfo.Members, dict):
for member_name, member_info in SymInfo.Members.items():
if isinstance(member_info, CTypeInfo):
mt = member_info.ToLLVM(Gen)
elif isinstance(member_info, str):
mt = Gen._CType2LLVM(member_info, '*' in member_info)
else:
mt = Gen._CType2LLVM(str(member_info) if member_info else 'int', False)
if isinstance(mt, ir.VoidType):
mt = ir.IntType(8)
member_types.append(mt)
else:
for item in SymInfo.Members:
if isinstance(item, (list, tuple)) and len(item) == 2:
member_name, member_type_str = item
mt = Gen._CType2LLVM(member_type_str, '*' in member_type_str)
if isinstance(mt, ir.VoidType):
mt = ir.IntType(8)
member_types.append(mt)
if member_types:
st = Gen._get_or_create_struct(class_name, source_sha1=module_sha1)
if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0):
st.set_body(*member_types)
else:
Gen._get_or_create_struct(class_name, source_sha1=module_sha1)
else:
if class_name not in Gen.structs and not (full_key and full_key in Gen.structs):
self.Trans.ImportHandler._TryLoadStructFromStub(class_name, Gen, source_sha1=module_sha1)
Gen._get_or_create_struct(class_name, source_sha1=module_sha1)
def _HandleStaticMethodCallLlvm(self, Node: ast.Call, ClassName: str, MethodName: str) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
class_sha1 = struct_sha1_map.get(ClassName)
if class_sha1:
MangledName = f'{class_sha1}.{ClassName}.{MethodName}'
else:
MangledName = f'{ClassName}.{MethodName}'
func = Gen._find_function(MangledName)
if not func:
func = Gen._find_function(f'{MangledName}__')
if not func:
stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(MangledName, Gen)
if stub_func_type:
func = self.GetOrCreateStubFuncDecl(MangledName, stub_func_type, Gen)
if not func and class_sha1:
plain_name = f'{ClassName}.{MethodName}'
stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen)
if stub_func_type:
func = self.GetOrCreateStubFuncDecl(MangledName, stub_func_type, Gen)
if not func:
if ClassName not in Gen.structs:
self._ensure_struct_declared(ClassName)
SymInfo = self.LookupFunctionSymbol(MethodName, module_path=ClassName)
if SymInfo and SymInfo.IsFunction:
sig = self.BuildLLVMFuncTypeFromSig(SymInfo, Gen)
if sig is not None:
ret_type, llvm_param_types, _ = sig
is_static = FuncMeta.STATIC_METHOD in SymInfo.MetaList
if ClassName in Gen.structs and not is_static:
llvm_param_types.insert(0, ir.PointerType(Gen.structs[ClassName]))
func_type = ir.FunctionType(ret_type, llvm_param_types)
func = ir.Function(Gen.module, func_type, name=MangledName)
Gen.functions[MangledName] = func
if not func:
p = Gen.class_parent.get(ClassName)
while p:
parent_mangled = Gen._mangle_name(f'{p}.{MethodName}')
parent_func = Gen.functions.get(parent_mangled)
if parent_func:
func = parent_func
break
parent_func = Gen._find_function(f'{p}.{MethodName}')
if parent_func:
func = parent_func
break
p = Gen.class_parent.get(p)
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)
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
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)
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")
CallArgs[0] = ObjVal
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)
CallArgs = Gen._apply_auto_addr(MangledName, CallArgs)
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]
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
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__'
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 _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
if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute):
if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c':
if expr.func.attr == '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 expr.func.attr == '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
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.match(r'%__OP(\d+)__\s*=\s*(.+)', 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.match(r'icmp\s+(\w+)\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__', template)
if icmp_match:
pred = icmp_match.group(1)
op1 = resolve_op(int(icmp_match.group(3)))
op2 = resolve_op(int(icmp_match.group(4)))
if op1 and op2:
result = Gen.builder.icmp_signed(pred, op1, op2, name="llvmir_result")
return self._StoreLLVMIROutputs(Gen, result, output_targets)
fcmp_match = re.match(r'fcmp\s+(\w+(?:\s+fast)?)\s+(\w+)\s+%__OP(\d+)__,\s*%__OP(\d+)__', template)
if fcmp_match:
pred = fcmp_match.group(1)
op1 = resolve_op(int(fcmp_match.group(3)))
op2 = resolve_op(int(fcmp_match.group(4)))
if op1 and op2:
result = Gen.builder.fcmp_ordered(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.match(r'select\s+i1\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__', 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.match(r'Load\s+(\w+),\s*(\w+)\s+%__OP(\d+)__', 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.match(r'store\s+(\w+)\s+%__OP(\d+)__,\s*(\w+)\s+%__OP(\d+)__', 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.match(r'getelementptr\s+(?:inbounds\s+)?(\w+),\s*(\w+)\s+%__OP(\d+)__,\s*(.+)', 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.split(r',\s*', indices_str):
idx_part = idx_part.strip()
idx_m = re.match(r'%__OP(\d+)__', 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.match(r'sub\s+(\w+)\s+(\d+),\s*%__OP(\d+)__', 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.match(r'xor\s+(\w+)\s+(-1|\d+),\s*%__OP(\d+)__', 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.match(r'call\s+(\w+)\s+@([\w.]+)\((.+)\)', 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.split(r',\s*', args_str):
arg_part = arg_part.strip()
typed_op = re.match(r'(\w+\*?)\s+%__OP(\d+)__', 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.match(r'(\w+)\s+(-?\d+)', 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 = ir.Function(Gen.module, func_type, name=func_name)
else:
existing = Gen.module.globals.get(func_name)
if existing:
func = existing
else:
func = ir.Function(Gen.module, func_type, name=func_name)
result = Gen.builder.call(func, call_args, name="llvmir_result")
if output_targets:
return self._StoreLLVMIROutputs(Gen, result, output_targets)
if isinstance(call_ret_type, ir.VoidType):
return ir.Constant(ir.IntType(32), 0)
return result
if output_targets:
for i, target in enumerate(output_targets):
if isinstance(target, ir.AllocaInstr):
Gen.builder.store(ir.Constant(target.type.pointee, 0), target)
return ir.Constant(ir.IntType(32), 0)
return ir.Constant(ret_type, 0)
def _StoreLLVMIROutputs(self, Gen: LlvmGeneratorMixin, result: ir.Value, output_targets: list[ir.Value]) -> ir.Value:
for target in output_targets:
if isinstance(target, ir.AllocaInstr):
if target.type.pointee == result.type:
Gen.builder.store(result, target)
else:
i64t = ir.IntType(64)
if isinstance(result.type, ir.IntType) and result.type.width == 64 and isinstance(target.type.pointee, ir.PointerType):
ptr_val = Gen.builder.inttoptr(result, target.type.pointee)
Gen.builder.store(ptr_val, target)
elif isinstance(result.type, ir.PointerType) and isinstance(target.type.pointee, ir.IntType) and target.type.pointee.width == 64:
int_val = Gen.builder.ptrtoint(result, i64t)
Gen.builder.store(int_val, target)
else:
try:
cast_val = Gen.builder.bitcast(result, target.type.pointee)
Gen.builder.store(cast_val, target)
except Exception:
Gen.builder.store(result, target)
elif isinstance(target, (ir.GlobalVariable, ir.GEPInstr)):
if isinstance(target.type, ir.PointerType):
if target.type.pointee == result.type:
Gen.builder.store(result, target)
else:
try:
cast_val = Gen.builder.bitcast(result, target.type.pointee)
Gen.builder.store(cast_val, target)
except Exception:
Gen.builder.store(result, target)
return result
def _ParseLLVMType(self, Gen: LlvmGeneratorMixin, type_str: str) -> ir.Type | None:
type_map = {
'i1': ir.IntType(1), 'i8': ir.IntType(8), 'i16': ir.IntType(16),
'i32': ir.IntType(32), 'i64': ir.IntType(64), 'i128': ir.IntType(128),
'float': ir.FloatType(), 'double': ir.DoubleType(),
'void': ir.VoidType(),
}
if type_str in type_map:
return type_map[type_str]
if type_str.endswith('*'):
pointee = self._ParseLLVMType(Gen, type_str[:-1])
if pointee:
return ir.PointerType(pointee)
return None
def _ConvertLLVMIRType(self, Gen: LlvmGeneratorMixin, val: ir.Value, expected_type_str: str) -> ir.Value | None:
expected_type = self._ParseLLVMType(Gen, expected_type_str.rstrip('*'))
if not expected_type:
return val
IsPtr = expected_type_str.endswith('*')
if IsPtr:
expected_ptr_type = ir.PointerType(expected_type)
if isinstance(val.type, ir.PointerType) and val.type != expected_ptr_type:
try:
return Gen.builder.bitcast(val, expected_ptr_type)
except Exception:
return val
elif isinstance(val.type, ir.IntType) and val.type.width == 64:
try:
return Gen.builder.inttoptr(val, expected_ptr_type)
except Exception:
return val
return val
if val.type == expected_type:
return val
if isinstance(val.type, ir.IntType) and isinstance(expected_type, ir.IntType):
if expected_type.width < val.type.width:
return Gen.builder.trunc(val, expected_type)
else:
return Gen.builder.zext(val, expected_type)
if isinstance(val.type, ir.PointerType) and isinstance(expected_type, ir.IntType):
if expected_type.width == 64:
return Gen.builder.ptrtoint(val, expected_type)
if isinstance(val.type, ir.IntType) and isinstance(expected_type, ir.PointerType):
if val.type.width == 64:
return Gen.builder.inttoptr(val, expected_type)
return val
def _HandleExternalCallLlvm(self, Node: ast.Call, func_name: str, ModulePath: str | None) -> ir.Value | None:
Gen = self.Trans.LlvmGen
# 解析 import 别名: 如 window -> vpsdk.window
if ModulePath and ModulePath not in ('c', 't'):
ModulePath = self.Trans.SymbolTable.resolve_alias(ModulePath)
is_user_module = (ModulePath and ModulePath not in ('c', 't') and
(ModulePath in Gen.ModuleSha1Map or
ModulePath in getattr(self.Trans, '_ImportedModules', set()) or
ModulePath in self.Trans.SymbolTable.import_aliases))
if ModulePath and ModulePath not in ('c', 't') and not is_user_module:
is_user_module = (ModulePath in Gen.ModuleSha1Map or
ModulePath.split('.')[-1] in Gen.ModuleSha1Map or
ModulePath.split('.')[-1] in getattr(self.Trans, '_ImportedModules', set()))
if is_user_module and ModulePath not in Gen.ModuleSha1Map and ModulePath not in getattr(self.Trans, '_ImportedModules', set()):
ModulePath = ModulePath.split('.')[-1]
if ModulePath and ModulePath not in ('c', 't') and not is_user_module:
return None
if is_user_module:
mangled_name = Gen._mangle_func_name(func_name, ModulePath)
sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath)
is_inline = sym_info and (sym_info.IsInline or isinstance(sym_info.Storage, t.CInline))
if is_inline and sym_info.InlineBody:
self._HandleInlineExpandLlvm(Node, sym_info)
return ir.Constant(ir.IntType(32), 1)
sym_info_exact = self.Trans.SymbolTable.lookup(f'{ModulePath}.{func_name}')
if sym_info_exact:
exact_params = sym_info_exact.FuncPtrParams
exact_param_names = [pn for pn, _ in exact_params]
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 一致的查找逻辑)
global_defaults: dict = self.Trans._global_function_default_args
func_defaults: list | None = None
if mangled_name in global_defaults:
func_defaults = global_defaults[mangled_name]
elif func_name in global_defaults:
func_defaults = global_defaults[func_name]
else:
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
total_params: int = len(exact_param_names)
num_defaults: int = len(func_defaults) if func_defaults else 0
min_args: int = total_params - num_defaults
# 只检查必需参数(有默认值的参数允许缺失)
for i in range(min_args):
if i >= provided and i not in kw_provided:
raise Exception(f"调用 {ModulePath}.{func_name}() 缺少必需参数 '{exact_param_names[i]}',该参数没有默认值")
elif ModulePath in Gen.ModuleSha1Map and len(Node.args) == 0:
func = Gen.functions.get(mangled_name)
if func and len(func.function_type.args) > 0:
raise Exception(f"调用 {ModulePath}.{func_name}() 缺少必需参数,函数需要 {len(func.function_type.args)} 个参数但未传入任何参数")
if Gen._has_function(mangled_name):
return self._HandleClosureCallLlvm(Node, mangled_name)
try:
stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen)
except Exception:
stub_func_type = None
if not stub_func_type and mangled_name != func_name:
try:
stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(func_name, Gen)
except Exception:
stub_func_type = None
if stub_func_type:
if is_user_module:
decl_name = mangled_name
else:
decl_name = mangled_name if mangled_name in self.Trans.ImportHandler._stub_func_cache else func_name
self.GetOrCreateStubFuncDecl(decl_name, stub_func_type, Gen, alias=func_name)
return self._HandleClosureCallLlvm(Node, decl_name)
sym_info = self.LookupFunctionSymbol(func_name, module_path=ModulePath)
if sym_info:
sig = self.BuildLLVMFuncTypeFromSig(sym_info, Gen, mangled_name=mangled_name, use_infer_fallback=True)
if sig is not None:
ret_type, llvm_param_types, sym_is_variadic = sig
self.GetOrCreateFuncDecl(mangled_name, ret_type, llvm_param_types, Gen,
is_variadic=sym_is_variadic, replace_name=func_name)
return self._HandleClosureCallLlvm(Node, mangled_name)
# 回退: 直接用 func_name 查找_find_function 会遍历 SHA1 前缀)
fallback_func = Gen._find_function(func_name)
if fallback_func is not None:
return self._HandleClosureCallLlvm(Node, func_name)
return None
if ModulePath == 'c':
if func_name == 'Asm':
return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node)
elif func_name == 'Addr':
return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node)
elif func_name == 'Set':
return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node)
elif func_name == 'Load':
return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node)
elif func_name == 'Deref':
return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node)
elif func_name == 'DerefAs':
return self._HandleCDerefAsLlvm(Node)
elif func_name == 'PtrToInt':
return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node)
elif func_name in ('CIf', 'CElif'):
return self.Trans.CSpecialCallHandle._HandleCIfLlvm(Node)
elif func_name == 'CError':
msg = "compile-time error"
if Node.args:
arg = Node.args[0]
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
msg = arg.value
lineno = getattr(Node, 'lineno', 0)
raise Exception(f"#error: {msg} (line {lineno})")
elif func_name in ('AsmInp', 'AsmOut'):
return ir.Constant(ir.IntType(32), 0)
elif func_name == 'LLVMIR':
return self._HandleLLVMIRLlvm(Node)
elif func_name in ('LInp', 'LOut'):
return ir.Constant(ir.IntType(32), 0)
win32_apis = {
'SetConsoleOutputCP': (ir.IntType(32), [ir.IntType(32)]),
'SetConsoleCP': (ir.IntType(32), [ir.IntType(32)]),
'GetConsoleOutputCP': (ir.IntType(32), []),
'GetConsoleCP': (ir.IntType(32), []),
}
if ModulePath and 'win32console' in ModulePath and func_name in win32_apis:
ret_type, param_types = win32_apis[func_name]
if func_name not in Gen.functions:
func_type = ir.FunctionType(ret_type, param_types)
func_decl = ir.Function(Gen.module, func_type, name=func_name)
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
try:
type_info = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.func)
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"解析类型失败: {_e}", "Exception")
if not isinstance(type_info, CTypeInfo):
return None
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:
return None
if type_info.IsPtr and not IsPtr:
IsPtr = True
if '*' not in target_type_str:
target_type_str += ' *'
if isinstance(type_info.Storage, t.CExport) or isinstance(type_info.Storage, t.CExtern):
pass
if type_info.IsState:
Gen._export_funcs.add('_type_union_cast')
return self._HandleTypeCastLlvm(Node.args[0], target_type_str)
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:
result = Gen.builder.trunc(val, target_type, name="trunc_cast")
if is_dst_unsigned:
Gen._unsigned_results[id(result)] = True
return result
if is_dst_unsigned:
Gen._unsigned_results[id(val)] = True
return val
if src_is_float and dst_is_float:
if isinstance(val.type, ir.FloatType) and isinstance(target_type, ir.DoubleType):
return Gen.builder.fpext(val, target_type, name="fpext_cast")
if isinstance(val.type, ir.DoubleType) and isinstance(target_type, ir.FloatType):
return Gen.builder.fptrunc(val, target_type, name="fptrunc_cast")
return val
if src_IsPtr and dst_is_int:
if target_type.width < 64:
ptr_to_i64 = Gen.builder.ptrtoint(val, ir.IntType(64), name="ptr2i64")
return Gen.builder.trunc(ptr_to_i64, target_type, name="ptr2int_cast")
return Gen.builder.ptrtoint(val, target_type, name="ptr2int_cast")
if src_is_int and dst_IsPtr:
return Gen.builder.inttoptr(val, target_type, name="int2ptr_cast")
if src_IsPtr and dst_IsPtr:
if isinstance(val.type.pointee, ir.PointerType) and isinstance(target_type.pointee, ir.IntType):
Loaded = Gen._load(val, name="load_ptr")
return Loaded
return Gen.builder.bitcast(val, target_type, name="ptrcast")
try:
return Gen.builder.bitcast(val, target_type, name="cast")
except Exception: # 回退bitcast 失败时返回原值
return val
def _HandleREnumConstructLlvm(self, Node: ast.Call, RenumName: str, VariantName: str, TagValue: int) -> ir.Value | None:
Gen = self.Trans.LlvmGen
if RenumName not in Gen.structs:
self.Trans.ImportHandler._TryLoadStructFromStub(RenumName, Gen)
if RenumName not in Gen.structs:
return None
RenumType = Gen.structs[RenumName]
result = Gen._allocaEntry(RenumType, name=f"{RenumName}_{VariantName}")
tag_ptr = Gen.builder.gep(result, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="renum_tag_ptr")
Gen._store(ir.Constant(ir.IntType(32), TagValue), tag_ptr)
NestedStructName = f"{RenumName}_{VariantName}"
if NestedStructName in Gen.structs and Node.args:
NestedStructType = Gen.structs[NestedStructName]
NestedStructPtrType = ir.PointerType(NestedStructType)
variant_ptr = Gen.builder.bitcast(result, NestedStructPtrType, name=f"cast_{NestedStructName}")
members = Gen.class_members.get(NestedStructName, [])
payLoad_members = [(n, t) for n, t in members if n != '__tag']
for i, arg in enumerate(Node.args):
if i < len(payLoad_members):
member_name, member_type = payLoad_members[i]
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
Coerced = Gen._coerce_value(ArgVal, member_type)
if Coerced is not None:
ArgVal = Coerced
elem_ptr = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i + 1)], name=f"{VariantName}_{member_name}")
# REnum 嵌套结构体共享 max_variant_struct 布局,成员类型可能与
# 结构体字段类型不一致(如 Bits:i32 与 Ret:LLVMType* 重叠)。
# 需 bitcast elem_ptr 到成员类型,避免 _store 按 struct 字段类型做错误转换。
if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type:
elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"{VariantName}_{member_name}_cast")
Gen._store(ArgVal, elem_ptr)
return result
def _HandleTypedefCastLlvm(self, Node: ast.Call, TypedefName: str) -> ir.Value:
Gen = self.Trans.LlvmGen
SymInfo = self.Trans.SymbolTable.lookup(TypedefName)
if not SymInfo or not SymInfo.IsTypedef:
return ir.Constant(ir.IntType(32), 0)
# 确保 Name 已设置,以便 _resolve_typedef_ctype 能正确解析
if not SymInfo.Name:
SymInfo.Name = TypedefName
TargetType = Gen._ctype_to_llvm(SymInfo)
if not Node.args:
if isinstance(TargetType, ir.IntType):
return ir.Constant(TargetType, 0)
elif isinstance(TargetType, ir.PointerType):
return ir.Constant(TargetType, None)
return ir.Constant(ir.IntType(32), 0)
ArgVal = self.HandleExprLlvm(Node.args[0])
if not ArgVal:
if isinstance(TargetType, ir.IntType):
return ir.Constant(TargetType, 0)
return ir.Constant(ir.IntType(32), 0)
if ArgVal.type == TargetType:
return ArgVal
if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType):
if ArgVal.type.width < TargetType.width:
return Gen.builder.zext(ArgVal, TargetType, name=f"zext_{TypedefName}_cast")
elif ArgVal.type.width > TargetType.width:
return Gen.builder.trunc(ArgVal, TargetType, name=f"trunc_{TypedefName}_cast")
return ArgVal
if isinstance(TargetType, ir.PointerType):
if isinstance(ArgVal.type, ir.PointerType):
return Gen.builder.bitcast(ArgVal, TargetType, name=f"bitcast_{TypedefName}_cast")
if isinstance(ArgVal.type, ir.IntType):
return Gen.builder.inttoptr(ArgVal, TargetType, name=f"inttoptr_{TypedefName}_cast")
# 如果参数是结构体值,需要 alloca 并返回指针
if isinstance(ArgVal.type, ir.BaseStructType):
alloca = Gen.builder.alloca(ArgVal.type, name=f"struct_alloca_{TypedefName}")
Gen.builder.store(ArgVal, alloca, align=8)
return Gen.builder.bitcast(alloca, TargetType, name=f"bitcast_struct_{TypedefName}")
if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.PointerType):
return Gen.builder.ptrtoint(ArgVal, TargetType, name=f"ptrtoint_{TypedefName}_cast")
if isinstance(TargetType, (ir.FloatType, ir.DoubleType)) and isinstance(ArgVal.type, ir.IntType):
return Gen.builder.sitofp(ArgVal, TargetType, name=f"sitofp_{TypedefName}_cast")
if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fptosi(ArgVal, TargetType, name=f"fptosi_{TypedefName}_cast")
return ArgVal
def _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} 个参数")
max_args = 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 []
if FuncDef_args_defaults:
defaults = FuncDef.args.defaults
num_defaults = len(defaults)
if num_defaults > 0:
min_args = regular_param_count - num_defaults
for i in range(regular_param_count):
if resolved[i] is None and i >= min_args:
default_idx = i - min_args
if default_idx < len(defaults):
default_node = defaults[default_idx]
default_val = self.HandleExprLlvm(default_node)
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 if FuncDef_args_defaults else regular_param_count
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}")
adjusted.append(arg)
result = Gen.builder.call(func, adjusted, name=f"call_{FuncName}")
else:
CallArgs = Gen._apply_auto_addr(FuncName, CallArgs)
adjusted = Gen._adjust_args(CallArgs, func)
# 兜底:确保所有指针类型参数与函数签名匹配(如 struct* -> i8*
for i, (arg, param) in enumerate(zip(adjusted, func.args)):
if arg.type != param.type:
if isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType):
adjusted[i] = Gen.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")
result = Gen.builder.call(func, adjusted, name=f"call_{FuncName}")
self._check_eh_return(result, FuncName, Gen, called_func=func)
return result
@staticmethod
def _has_cvtable_decorator(node: ast.ClassDef) -> bool:
"""检查 ClassDef 是否有 @t.CVTable 装饰器(或 @CVTable"""
for decorator in node.decorator_list:
if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'CVTable':
return True
if isinstance(decorator, ast.Call):
inner = decorator.func
if isinstance(inner, ast.Attribute) and getattr(inner.value, 'id', None) == 't' and inner.attr == 'CVTable':
return True
if isinstance(inner, ast.Name) and inner.id == 'CVTable':
return True
if isinstance(decorator, ast.Name) and decorator.id == 'CVTable':
return True
return False
@staticmethod
def _has_novtable_decorator(node: ast.ClassDef) -> bool:
"""检查 ClassDef 是否有 @t.NoVTable 装饰器"""
for decorator in node.decorator_list:
if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'NoVTable':
return True
if isinstance(decorator, ast.Name) and decorator.id == 'NoVTable':
return True
return False
def _try_register_vtable_ondemand(self, ClassName: str, class_sha1: str, Gen: "LlvmGeneratorMixin") -> None:
"""按需 vtable 注册:从 .pyi stub 检查 @t.CVTable 并注册到 vtable 集合。
沿继承链递归收集方法,保证 vtable 布局与 prescan 的 _EmitClassLlvm 一致:
- 基类方法在前(固定索引,保证多态分派正确)
- 子类新增方法在后
- 子类覆盖的方法放在父类的位置(用子类实现)
与 prescan 的 _resolve_class_flags 逻辑一致:
- 类自身有 @t.CVTable → 注册
- 类自身有 @t.NoVTable → 不注册
- 类继承 @t.CVTable 基类(且无 @t.NoVTable→ 注册(自动启用 CVTable
"""
try:
import_handler = self.Trans.ImportHandler
project_root = import_handler._find_project_root()
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
# 缓存已读取的 .pyi 文件sha1 -> class_map
pyi_cache: dict[str, dict[str, ast.ClassDef]] = {}
def load_pyi(sha1: str) -> dict[str, ast.ClassDef] | None:
if sha1 in pyi_cache:
return pyi_cache[sha1]
pyi_path = os.path.join(project_root, 'temp', f'{sha1}.pyi')
if not os.path.isfile(pyi_path):
return None
with open(pyi_path, 'r', encoding='utf-8') as f:
pyi_code = f.read()
tree = ast.parse(pyi_code)
cmap: dict[str, ast.ClassDef] = {}
for node in ast.iter_child_nodes(tree):
if isinstance(node, ast.ClassDef):
cmap[node.name] = node
pyi_cache[sha1] = 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)
"""
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)
# 收集自身方法
self_methods: list[str] = []
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_methods.append(f"{class_name}.__call__")
else:
self_methods.append(f"{class_name}.{method_name}")
# 收集基类方法
parent_methods: list[str] = []
parent_name: str | 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
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)
if base_novtable:
pass # 父类是 NoVTable不继承 vtable
elif base_cvtable:
is_cvtable = True
parent_methods.extend(base_methods)
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 base_novtable:
pass
elif base_cvtable:
is_cvtable = True
parent_methods.extend(base_methods)
else:
# 无法找到基类的 sha1
if base_name in Gen._cross_module_novtable:
pass # 父类是 NoVTable不继承 vtable
# 合并方法列表:基类方法在前,子类方法在后
# 子类覆盖的方法放在父类的位置
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:
# 父类独有方法 — 用子类名(虚分派时函数查找会沿继承链回退)
merged.append(f"{class_name}.{mn}")
for m in self_methods:
mn = m.split('.')[-1] if '.' in m else m
if mn not in covered_names:
merged.append(m)
return (merged, is_cvtable, is_novtable, parent_name)
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
# 设置 class_parent用于虚分派时的函数查找回退
if parent_name and ClassName not in Gen.class_parent:
Gen.class_parent[ClassName] = parent_name
except Exception:
pass
def _try_complete_generic_specialization(self, ClassName: str, MethodName: str, Gen: LlvmGeneratorMixin) -> None:
"""泛型特化类方法按需完整特化。
当 ClassName 是泛型特化名(如 ndarray[double])且缓存中只有
declare_only 版本时,补发方法体。这样方法调用能找到 define
避免链接时 undefined reference。
触发场景numpy 模块内部函数(如 np_sum调用 a.sum()
其中 a 的类型注解 ndarray[t.CDouble] | t.CPtr 在类型解析时
只触发了 declare_only=True 特化(创建 struct + __before_init__
方法体fill/delete/dot/sum/...)未被编译。
"""
# 仅对特化名(含 '[' 且以 ']' 结尾)触发
if '[' not in ClassName or not ClassName.endswith(']'):
return
# 反推模板类名(如 ndarray[double] → ndarray
base_name: str = ClassName.split('[')[0]
# 检查是否是已注册的泛型模板
if not (hasattr(self.Trans.ClassHandler, '_generic_class_templates') and base_name in self.Trans.ClassHandler._generic_class_templates):
return
# 构造 spec_key与 _specialize_generic_class 一致ClassName<arg1,arg2,...>
spec_key: str = base_name + '<' + ','.join(ClassName[len(base_name) + 1:-1].split('][')) + '>'
spec_cache = getattr(Gen, '_generic_class_specializations', {})
cached = spec_cache.get(spec_key)
if not isinstance(cached, tuple) or len(cached) < 2:
return
cached_name: str = cached[0]
was_declare_only: bool = cached[1]
# 仅当缓存是 declare_only 版本时才需要补发
if not was_declare_only:
return
# 检查方法是否已有 defineblocks 非空表示已编译方法体)
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
class_sha1 = struct_sha1_map.get(cached_name)
full_method_name: str = f'{class_sha1}.{cached_name}.{MethodName}' if class_sha1 else f'{cached_name}.{MethodName}'
existing_func = Gen._find_function(full_method_name)
if existing_func is not None:
existing_blocks = getattr(existing_func, 'blocks', [])
if existing_blocks:
return # 方法体已编译
# 从缓存获取 type_args/type_names
type_info = getattr(Gen, '_generic_class_spec_type_info', {}).get(spec_key)
if type_info is None:
return
cached_type_args, cached_type_names = type_info
# 不预先标记_specialize_generic_class 内部缓存逻辑会检测
# cached_declare_only=True 且 declare_only=False自动补发方法体
# 并更新缓存为 (spec_name, False)。预注册机制L117防止递归。
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)
def _HandleMethodCallLlvm(self, Node: ast.Call) -> ir.Value:
Gen = self.Trans.LlvmGen
MethodName = Node.func.attr
# 检查是否为 @staticmethod 或 @classmethod如果是则走特殊路径
if isinstance(Node.func.value, ast.Name):
VarName = Node.func.value.id
ClassName = Gen.var_struct_class.get(VarName)
if ClassName:
SymKey = f'{ClassName}.{MethodName}'
SymInfo = self.Trans.SymbolTable.lookup(SymKey)
if SymInfo and SymInfo.MetaList:
if FuncMeta.STATIC_METHOD in SymInfo.MetaList:
return self._HandleStaticMethodCallLlvm(Node, ClassName, MethodName)
if FuncMeta.CLASS_METHOD in SymInfo.MetaList:
return self._HandleStaticMethodCallLlvm(Node, ClassName, MethodName)
if isinstance(Node.func.value, ast.Name) and IsTModule(Node.func.value.id, self.Trans.SymbolTable):
result = self.Trans.TSpecialCallHandle._HandleTSpecialCallLlvm(Node)
if result is not None:
return result
ctype_cls = CTypeRegistry.GetClassByName(MethodName)
if ctype_cls is not None:
try:
ctype_inst = ctype_cls()
except Exception:
ctype_inst = None
target_bits = getattr(ctype_inst, 'Size', None) if ctype_inst else None
if target_bits is None:
target_bits = 0
is_float = getattr(ctype_inst, 'IsSigned', False) is None and target_bits > 0 if ctype_inst else False
IsPtr_cast = len(Node.args) >= 2 and (
(isinstance(Node.args[1], ast.Attribute) and Node.args[1].attr == 'CPtr' and isinstance(Node.args[1].value, ast.Name) and IsTModule(Node.args[1].value.id, self.Trans.SymbolTable)) or
(isinstance(Node.args[1], ast.Name) and Node.args[1].id == 'CPtr')
)
if Node.args:
ArgNode = Node.args[0]
if isinstance(ArgNode, ast.Subscript):
SubscriptPtr = self._HandleSubscriptPtrLlvm(ArgNode)
if SubscriptPtr:
if MethodName == 'CPtr' or IsPtr_cast:
return SubscriptPtr
if isinstance(SubscriptPtr.type, ir.PointerType):
val = Gen._load(SubscriptPtr, name="Load_subscript_val")
else:
val = SubscriptPtr
if target_bits > 0 and isinstance(val.type, ir.IntType) and not is_float:
if val.type.width < target_bits:
val = Gen.builder.zext(val, ir.IntType(target_bits), name="zext_cast")
elif val.type.width > target_bits:
val = Gen.builder.trunc(val, ir.IntType(target_bits), name="trunc_cast")
return val
val = self.HandleExprLlvm(ArgNode)
if IsPtr_cast:
if target_bits > 0 and not is_float:
target_ptr_type = ir.PointerType(ir.IntType(target_bits))
elif is_float and target_bits == 32:
target_ptr_type = ir.PointerType(ir.FloatType())
elif is_float and target_bits == 64:
target_ptr_type = ir.PointerType(ir.DoubleType())
else:
target_ptr_type = ir.PointerType(ir.IntType(8))
if isinstance(val.type, ir.PointerType):
if val.type != target_ptr_type:
val = Gen.builder.bitcast(val, target_ptr_type, name="ptr_cast")
elif isinstance(val.type, ir.IntType):
if val.type.width < 64:
val = Gen.builder.zext(val, ir.IntType(64), name="zext_ptr_cast")
val = Gen.builder.inttoptr(val, target_ptr_type, name="int_to_ptr_cast")
return val
if val and target_bits > 0 and not is_float:
if isinstance(val.type, ir.IntType):
if val.type.width < target_bits:
val = Gen.builder.zext(val, ir.IntType(target_bits), name="zext_cast")
elif val.type.width > target_bits:
val = Gen.builder.trunc(val, ir.IntType(target_bits), name="trunc_cast")
elif isinstance(val.type, ir.PointerType):
val = Gen.builder.ptrtoint(val, ir.IntType(target_bits), name="ptrtoint_cast")
return val
if is_float:
return ir.Constant(ir.FloatType() if target_bits == 32 else ir.DoubleType(), 0.0)
return ir.Constant(ir.IntType(target_bits) if target_bits > 0 else ir.IntType(32), 0)
if self.Trans.SymbolTable.has(MethodName):
SymInfo = self.Trans.SymbolTable[MethodName]
if SymInfo.IsTypedef:
return self._HandleTypedefCastLlvm(Node, MethodName)
if isinstance(Node.func.value, ast.Name) and Node.func.value.id == 'c':
if MethodName == 'Asm':
return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node)
if MethodName == 'Addr':
return self.Trans.CSpecialCallHandle._HandleCAddrLlvm(Node)
elif MethodName == 'Set':
return self.Trans.CSpecialCallHandle._HandleCSetLlvm(Node)
elif MethodName == 'Load':
return self.Trans.CSpecialCallHandle._HandleCLoadLlvm(Node)
elif MethodName == 'Deref':
return self.Trans.CSpecialCallHandle._HandleCDerefLlvm(Node)
elif MethodName == 'DerefAs':
return self._HandleCDerefAsLlvm(Node)
elif MethodName == 'PtrToInt':
return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node)
elif MethodName in ('CIf', 'CElif'):
if Node.args:
ArgVal = self.HandleExprLlvm(Node.args[0])
if ArgVal:
if isinstance(ArgVal, ir.Constant) and isinstance(ArgVal.type, ir.IntType):
return ir.Constant(ir.IntType(1), 1 if ArgVal.constant != 0 else 0)
if isinstance(ArgVal.type, ir.IntType):
return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond")
if isinstance(ArgVal.type, ir.PointerType):
return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond")
return ir.Constant(ir.IntType(1), 0)
elif MethodName in ('CIfdef', 'CIfndef'):
return ir.Constant(ir.IntType(1), 1)
elif MethodName == 'CError':
msg = "compile-time error"
if Node.args:
arg = Node.args[0]
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
msg = arg.value
else:
try:
val = self.HandleExprLlvm(arg)
if isinstance(val, str):
msg = val
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"处理函数调用失败: {_e}", "Exception")
lineno = getattr(Node, 'lineno', 0)
raise Exception(f"#error: {msg} (line {lineno})")
elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'):
return ir.Constant(ir.IntType(1), 1)
elif MethodName in ('LLVMIR', 'LInp', 'LOut'):
return None
ObjVal = self.HandleExprLlvm(Node.func.value)
if not ObjVal:
return None
if isinstance(ObjVal.type, (ir.IdentifiedStructType, ir.LiteralStructType)):
if isinstance(Node.func.value, ast.Name):
var_name = Node.func.value.id
if var_name in Gen.variables:
var_ptr = Gen.variables[var_name]
if isinstance(var_ptr.type, ir.PointerType) and isinstance(var_ptr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
ObjVal = var_ptr
else:
tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp")
Gen._store(ObjVal, tmp)
ObjVal = tmp
else:
tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp")
Gen._store(ObjVal, tmp)
ObjVal = tmp
else:
tmp = Gen._allocaEntry(ObjVal.type, name="chain_method_tmp")
Gen._store(ObjVal, tmp)
ObjVal = tmp
if isinstance(Node.func.value, ast.Name):
ModuleName = Node.func.value.id
imported = getattr(self.Trans, '_ImportedModules', set())
actual_module = self.Trans.SymbolTable.resolve_alias(ModuleName)
if ModuleName in imported or actual_module in imported:
mangled_name = Gen._mangle_func_name(MethodName, actual_module)
if Gen._has_function(mangled_name):
CallArgs = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
func = Gen._get_function(mangled_name)
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:
CallArgs = Gen._apply_auto_addr(MethodName, CallArgs)
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 = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
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:
CallArgs = Gen._apply_auto_addr(MethodName, CallArgs)
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 = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
self._fill_default_args(CallArgs, func, mangled_name)
self._append_eh_msg_out_arg(CallArgs, func, Gen)
CallArgs = Gen._apply_auto_addr(mangled_name, CallArgs)
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 arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
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)
# 泛型特化 fallback当 ClassName 是 opaque 基础名(如 'ndarray',不含 '['
# 且注解未指定类型参数时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本
# (如 'ndarray[double]'),优先选有对应方法的特化。这样方法调用能正确分派到
# 特化版本的方法(如 ndarray[double].delete而非查找 opaque stub declare。
if '[' not in ClassName:
SpecClass: str | None = None
for CN in Gen.structs.keys():
if not CN.startswith(f'{ClassName}['):
continue
struct_sha1_tmp = getattr(Gen, '_struct_sha1_map', {}).get(CN)
if struct_sha1_tmp:
if Gen._has_function(f'{struct_sha1_tmp}.{CN}.{MethodName}'):
SpecClass = CN
break
if Gen._has_function(f'{CN}.{MethodName}'):
SpecClass = CN
break
if SpecClass:
ClassName = SpecClass
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
class_sha1 = struct_sha1_map.get(ClassName)
if class_sha1:
FullMethodName = f'{class_sha1}.{ClassName}.{MethodName}'
else:
FullMethodName = f'{ClassName}.{MethodName}'
# 泛型特化类方法按需完整特化:当 ClassName 是特化名(如 ndarray[double]
# 且缓存中只有 declare_only 版本时,补发方法体。否则方法调用会查找
# stub declare无 define链接时报 undefined reference。
self._try_complete_generic_specialization(ClassName, MethodName, Gen)
# 提前查找函数以获取参数信息
func_for_kw = Gen._find_function(FullMethodName)
if not func_for_kw:
func_for_kw = Gen._find_function(f'{FullMethodName}__')
if not func_for_kw and class_sha1:
func_for_kw = Gen._find_function(f'{ClassName}.{MethodName}')
# 处理关键字参数: 先用 _fill_default_args 填充默认值,再用 keyword args 覆盖
if Node.keywords and func_for_kw:
# 优先从全局参数名表获取 Python 参数名(支持跨模块 include 函数)
global_param_names = getattr(self.Trans, '_global_function_param_names', {})
param_names = global_param_names.get(FullMethodName)
if not param_names:
param_names = global_param_names.get(f'{ClassName}.{MethodName}')
if not param_names:
# 回退到 LLVM 函数参数名(仅当参数名不是自动生成的数字名时有效)
llvm_names = [arg.name for arg in func_for_kw.args] if func_for_kw.args else []
# 检测是否为 LLVM 自动生成的数字名(如 "0", "1", "2"
if llvm_names and not all(n.isdigit() for n in llvm_names):
param_names = llvm_names
if param_names:
# 构造临时 call_args (含 self) 调用 _fill_default_args 填充默认值
temp_call_args = [ObjVal] + list(CallArgs)
self._fill_default_args(temp_call_args, func_for_kw, FullMethodName)
# 提取回非 self 参数 (现在已填充默认值)
CallArgs.clear()
CallArgs.extend(temp_call_args[1:])
# 用 keyword args 覆盖对应位置
for kw in Node.keywords:
if kw.arg is None:
continue
ArgVal = self.HandleExprLlvm(kw.value)
if not ArgVal:
continue
kw_name = kw.arg
if kw_name in param_names:
idx = param_names.index(kw_name)
# idx 0 是 self, 所以 CallArgs 中的偏移是 idx-1
target_idx = idx - 1
if 0 <= target_idx < len(CallArgs):
CallArgs[target_idx] = ArgVal
# 按需 vtable 注册:当 ClassName 不在 vtable 集合中但有 class_sha1 时,
# 从 .pyi stub 检查 @t.CVTable 装饰器并注册。
# 修复场景list[str].__new__ 中 pool.alloc(48) 编译时,
# MemManager 可能因导入时序问题未注册到当前 Gen 的 vtable 集合,
# 导致生成 call_直接调用而非 vcall_虚分派运行时段错误。
if (ClassName not in Gen.class_vtable
and ClassName not in Gen._cross_module_vtable_classes
and class_sha1):
self._try_register_vtable_ondemand(ClassName, class_sha1, Gen)
if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods:
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)
if not func:
func = Gen._find_function(f'{FullMethodName}__')
if not func:
p = Gen.class_parent.get(ClassName)
while p:
parent_full = f'{p}.{MethodName}'
func = Gen._find_function(parent_full)
if not func:
func = Gen._find_function(f'{parent_full}__')
if func:
break
p = Gen.class_parent.get(p)
# 修复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:
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 = [ObjVal] + 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)
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)
if func:
call_args = [ObjVal] + CallArgs
self._fill_default_args(call_args, func, FullMethodName)
self._append_eh_msg_out_arg(call_args, func, Gen)
call_args = Gen._apply_auto_addr(FullMethodName, call_args)
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):
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
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)
param_types = [self_ptr_type] + [a.type for a in CallArgs]
func_type = ir.FunctionType(ir.IntType(32), param_types)
func = ir.Function(Gen.module, func_type, name=FullMethodName)
Gen.functions[FullMethodName] = func
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}")
adjusted = Gen._adjust_args([call_self] + CallArgs, func)
result = Gen.builder.call(func, adjusted, name=f"call_{FullMethodName}")
return result
raise Exception(f"Undefined method '{MethodName}' in class '{ClassName}' (no function declaration found for '{FullMethodName}')")
_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)
func = ir.Function(Gen.module, func_type, name=func_name)
Gen.functions[func_name] = func
return func
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
if current_class and current_class in Gen.class_members:
for m_name, m_type in Gen.class_members[current_class]:
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, current_class)
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}")
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)):
for CN, ST in Gen.structs.items():
if pointee == ST:
result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__len__', val, Gen)
if result is not None:
return result
break
if isinstance(pointee, ir.ArrayType):
return ir.Constant(ir.IntType(64), pointee.count)
if isinstance(pointee, (ir.FloatType, ir.DoubleType, ir.IntType)) and pointee.width != 8:
return ir.Constant(ir.IntType(64), 0)
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 = ir.Function(Gen.module, correct_strlen_type, name='strlen')
Gen.functions['strlen'] = strlen_func
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
if strlen_func:
result = Gen.builder.call(strlen_func, [val], name="strlen_call")
return result
elif isinstance(val.type, ir.ArrayType):
return ir.Constant(ir.IntType(64), val.type.count)
return ir.Constant(ir.IntType(64), 0)
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
走和普通类型签名同样的规范化路径(不单独创建规则):
- ast.Name: 若为已注册结构体,加 SHA1 前缀(如 'AST''9ada227b160c07a1.AST'
与 DeclarationGenerator._get_type_str 对 ast.Name 的处理一致
- ast.Attribute: t.CPtr → 'ptr'(指针标记,等价于普通签名中 t.CPtr → i8* 的指针语义;
'ptr' 而非 '*' 是因为 _get_or_create_struct 的 clean_name 会移除 '*'
导致 Phase2 直接编译与 .stub.ll 加载路径的 mangled 名不一致);
其他 t 模块类型按 CTypeRegistry.NameToLLVM 解析
- ast.BinOp: 递归处理左右操作数并直接拼接(如 AST | t.CPtr → 'sha1.ASTptr'
避免空格/管道符导致 _get_or_create_struct 的 clean_name 清洗不一致
"""
if isinstance(node, ast.Name):
tn: str = node.id
t_c_imported = getattr(self.Trans, '_t_c_imported_names', {})
if tn in t_c_imported:
src_module, src_name = t_c_imported[tn]
return f'{src_module}.{src_name}'
# 若为已注册结构体,加 SHA1 前缀(和普通签名路径一致)
Gen = self.Trans.LlvmGen
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) if Gen is not None else {}
if tn in struct_sha1_map:
return f'{struct_sha1_map[tn]}.{tn}'
return tn
if isinstance(node, ast.Attribute):
if isinstance(node.value, ast.Name):
full_name: str = f'{node.value.id}.{node.attr}'
# t.CPtr → 'ptr'(指针标记,和普通签名中 t.CPtr 被解析为指针类型 i8* 语义一致)
if full_name == 't.CPtr':
return 'ptr'
# 其他 t 模块类型按 CTypeRegistry 解析(和普通签名路径一致)
llvm_str: str | None = CTypeRegistry.NameToLLVM(node.attr)
if llvm_str is not None:
return llvm_str
return full_name
return ast.unparse(node)
if isinstance(node, ast.BinOp):
left_str: str = self._resolve_type_arg_str(node.left)
right_str: str = self._resolve_type_arg_str(node.right)
# 直接拼接(如 'sha1.AST' + 'ptr' = 'sha1.ASTptr'),避免空格导致清洗不一致
return f'{left_str}{right_str}'
return ast.unparse(node)
def _get_raw_type_name(self, node: ast.AST) -> str:
"""获取类型节点的原始名称(不带模块前缀),用于泛型 T 替换。
ast.Name → sn.id (如 'AST', 'CInt')
ast.Attribute → sn.attr (如 t.CInt → 'CInt')
ast.BinOp → 左侧的原始名称 (如 AST | CPtr → 'AST')
"""
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
return node.attr
if isinstance(node, ast.BinOp):
return self._get_raw_type_name(node.left)
return ast.unparse(node)
def _resolve_generic_slice_type(self, sn: ast.AST) -> tuple[str, str] | None:
"""解析泛型 slice 中的单个类型参数节点,返回 (type_arg, type_name) 或 None。
type_arg: 用于 mangling走和普通签名同样的规范化路径
(如 'AST | t.CPtr''9ada227b160c07a1.ASTptr'AST 加 SHA1 前缀t.CPtr 处理为 ptr
type_name: 用于替换泛型类型参数 T'AST''CInt'_specialize_generic_class 会自动加 | t.CPtr 给类名)
"""
if isinstance(sn, ast.Name):
tn: str = sn.id
ts: str = 'int'
if tn == 'int':
ts, tn = 'int', 'CInt'
elif tn == 'double':
ts, tn = 'double', 'CDouble'
elif tn == 'float':
ts, tn = 'float', 'CFloat'
elif tn == 'char':
ts, tn = 'i8', 'CChar'
elif tn == 'bool':
ts, tn = 'i8', 'CBool'
elif tn in ('CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong'):
ts = 'int'
elif tn in ('CDouble', 'CFloat', 'CFloat64T', 'CFloat32T'):
ts = 'double'
else:
ts = tn
return (ts, tn)
# BinOp (如 AST | CPtr) 或 Attribute (如 t.CPtr / t.CInt)
type_str: str = self._resolve_type_arg_str(sn)
# type_name 保留模块前缀(如 't.CUnsignedChar'),避免 T(0) 替换为
# CUnsignedChar(0) 后因 CUnsignedChar 不在顶层作用域而报
# "Undefined function: 'CUnsignedChar'"。
# t.CPtr 特殊处理:保留 'CPtr' 不带前缀,因为 cptr_params 检测依赖
# _BUILTIN_TYPE_NAMES 中的 'CPtr' 来识别指针类型参数
if isinstance(sn, ast.Attribute) and isinstance(sn.value, ast.Name):
if sn.attr == 'CPtr':
raw_name: str = 'CPtr'
else:
raw_name: str = f'{sn.value.id}.{sn.attr}'
else:
raw_name: str = self._get_raw_type_name(sn)
return (type_str, raw_name)
def _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 / ParsePythonFileLlvmGen 为 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])
while len(type_args) < len(type_param_names):
type_args.append('int')
type_names.append('CInt')
# 类型注解解析(如 Float64Array: t.CTypedef = ndarray[t.CDouble]
# 只需 struct 定义,不需方法体;使用 declare_only=True 避免过早触发
# 完整特化(此时被引用的函数如 printf 可能尚未由 from X import Y 注册,
# 导致 "Undefined function" 错误)。完整特化由后续类实例化
# _HandleClassNewLlvm → _specialize_generic_class(declare_only=False)
# 触发,缓存机制会补发方法体。
return self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names, declare_only=True)
@staticmethod
def _AnnotationContainsCPtr(Node: ast.AST) -> bool:
"""检查类型注解 AST 是否包含 t.CPtr即是否为指针类型"""
if isinstance(Node, ast.Attribute):
if Node.attr == 'CPtr':
return True
if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr):
return (ExprCallHandle._AnnotationContainsCPtr(Node.left)
or ExprCallHandle._AnnotationContainsCPtr(Node.right))
return False
@staticmethod
def _is_sizeof_type_name(name: str, Gen: "LlvmGeneratorMixin") -> bool:
"""检查名称是否是已知的类型名(用于 __sizeof__ 上下文)。
检查顺序: Gen.structs → SHA1 前缀 → 内建类型 → CTypeRegistry
"""
if not name:
return False
# 1. 直接在 Gen.structs 中查找
if name in Gen.structs:
return True
# 2. 带 SHA1 前缀查找(如 ClassDef → 657e182b27c2a022.ClassDef
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
if name in struct_sha1_map:
return True
# 遍历所有 SHA1 前缀
for sha1 in struct_sha1_map.values():
prefixed = f"{sha1}.{name}"
if prefixed in Gen.structs:
return True
# 3. 内建类型名
if name in ('str', 'bytes', 'int', 'double', 'float', 'char', 'bool',
'void', 'i8', 'i16', 'i32', 'i64', 'half', 'fp128'):
return True
# 4. CTypeRegistry 中的类型
if CTypeRegistry.GetClassByName(name) is not None:
return True
if CTypeRegistry.ResolveName(name):
return True
return False
def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Constant:
Gen = self.Trans.LlvmGen
if isinstance(Node, ast.Name):
type_name = Node.id
elif isinstance(Node, ast.Attribute):
type_name = Node.attr
else:
return ir.Constant(ir.IntType(64), 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
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
size = Gen._get_struct_size(struct_type)
if size > 0:
return ir.Constant(ir.IntType(64), size)
return ir.Constant(ir.IntType(64), 0)
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:
type_str = f"<enum '{enum_type_name}' size=4 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 _EmitStringFromValue(self, val: ir.Value) -> ir.Value:
Gen = self.Trans.LlvmGen
if isinstance(val.type, ir.IntType):
buf = Gen._alloca(ir.IntType(8), name="str_buf", size=ir.Constant(ir.IntType(32), 32))
snprintf_func = Gen.get_or_declare_c_func('snprintf', ir.FunctionType(ir.IntType(32), [
ir.PointerType(ir.IntType(8)),
ir.IntType(32),
ir.PointerType(ir.IntType(8))
]))
fmt_ptr = Gen.emit_constant("%d", 'string')
Gen.builder.call(snprintf_func, [buf, ir.Constant(ir.IntType(32), 32), fmt_ptr, val])
return Gen.builder.gep(buf, [ir.Constant(ir.IntType(32), 0)], name="str_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'] = ir.Function(Gen.module, memcpy_type, name='llvm.memcpy')
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 _HandleArgLlvm(self, args: list[ast.AST], CallNode: ast.Call | None = None) -> ir.Value | None:
Gen = self.Trans.LlvmGen
va_list_ptr = None
variadic_info = getattr(Gen, '_va_arg_info', None)
if not variadic_info:
variadic_info = getattr(Gen, '_variadic_info', None)
if variadic_info:
va_list_ptr = variadic_info.get('va_list_ptr')
if not va_list_ptr:
vararg_name = variadic_info.get('vararg_name', 'args') if variadic_info else 'args'
if vararg_name in Gen._direct_values:
va_list_ptr = Gen._direct_values[vararg_name]
elif vararg_name in Gen.variables:
va_list_ptr = Gen.variables[vararg_name]
if not va_list_ptr:
return ir.Constant(ir.IntType(32), 0)
target_type = ir.IntType(32)
if args:
type_arg = args[0]
if isinstance(type_arg, ast.Name):
type_name = type_arg.id
if type_name in ('str', 'bytes'):
target_type = ir.PointerType(ir.IntType(8))
elif type_name == 'CPtr':
target_type = ir.PointerType(ir.IntType(8))
elif type_name in Gen.structs:
target_type = ir.PointerType(Gen.structs[type_name])
else:
ctype_cls = CTypeRegistry.GetClassByName(type_name)
if ctype_cls is not None:
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
else:
resolved = CTypeRegistry.ResolveName(type_name)
if resolved is not None:
ctype_cls, ptr_level = resolved
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
if llvm_str:
base = Gen._type_str_to_llvm(llvm_str)
if base is not None:
for _ in range(ptr_level):
if isinstance(base, ir.VoidType):
base = ir.IntType(8).as_pointer()
else:
base = ir.PointerType(base)
target_type = base
elif isinstance(type_arg, ast.Attribute):
if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable):
attr_name = type_arg.attr
ctype_cls = CTypeRegistry.GetClassByName(attr_name)
if ctype_cls is not None:
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
else:
assign_node = getattr(self.Trans, '_current_assign_node', None)
if assign_node:
ann = getattr(assign_node, 'annotation', None)
if ann:
ann_name = None
if isinstance(ann, ast.Name):
ann_name = ann.id
elif isinstance(ann, ast.Attribute):
ann_name = ann.attr
elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr):
ann_name = ExtractTypeNameFromBinOp(ann)
if ann_name:
ctype_cls = CTypeRegistry.GetClassByName(ann_name)
if ctype_cls is not None:
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
elif ann_name in Gen.structs:
target_type = ir.PointerType(Gen.structs[ann_name])
va_arg_counter = getattr(Gen, '_va_arg_counter', 0)
Gen._va_arg_counter = va_arg_counter + 1
result = Gen.emit_va_arg(va_list_ptr, target_type)
if result is not None and getattr(result, 'name', ''):
result.name = f"va_arg_result_{Gen._va_arg_counter}"
return result
def _HandleVaArgLlvm(self, args: list[ast.AST]) -> ir.Constant:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
return ir.Constant(ir.IntType(32), 0)
def _HandleCDerefAsLlvm(self, Node: ast.Call) -> ir.Constant:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if len(Node.args) < 2:
return ir.Constant(ir.IntType(32), 0)
ptr_val: ir.Value | None = self.HandleExprLlvm(Node.args[0])
val_val: ir.Value | None = self.HandleExprLlvm(Node.args[1])
if ptr_val is None or val_val is None:
return ir.Constant(ir.IntType(32), 0)
if isinstance(ptr_val.type, ir.PointerType):
store_val: ir.Value = val_val
# 两端均为指向同一结构体类型的指针时执行结构体拷贝(*ptr = *val
# 而非存储指针值。这对 REnum 变体构造c.DerefAs(ptr, LLVMType.Int(32))) 至关重要。
if (isinstance(val_val.type, ir.PointerType) and
isinstance(ptr_val.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)) and
ptr_val.type.pointee == val_val.type.pointee):
loaded: ir.Value = Gen._load(val_val, name="deref_as_struct_load")
Gen._store(loaded, ptr_val)
return ir.Constant(ir.IntType(32), 0)
if isinstance(val_val.type, ir.PointerType):
void_pp: ir.PointerType = ir.PointerType(ir.PointerType(ir.IntType(8)))
if ptr_val.type != void_pp:
ptr_val = Gen.builder.bitcast(ptr_val, void_pp, name="deref_as_pp")
if val_val.type.pointee != ir.IntType(8):
store_val = Gen.builder.bitcast(val_val, ir.PointerType(ir.IntType(8)), name="deref_as_vp")
elif isinstance(val_val.type, ir.IntType) and isinstance(ptr_val.type.pointee, ir.IntType):
if val_val.type.width != ptr_val.type.pointee.width:
if ptr_val.type.pointee.width == 8 and val_val.type.width > 8:
target_ptr_type = ir.PointerType(val_val.type)
ptr_val = Gen.builder.bitcast(ptr_val, target_ptr_type, name="deref_as_cast")
elif val_val.type.width < ptr_val.type.pointee.width:
store_val = Gen.builder.zext(val_val, ptr_val.type.pointee, name="deref_as_zext")
else:
store_val = Gen.builder.trunc(val_val, ptr_val.type.pointee, name="deref_as_trunc")
elif isinstance(val_val.type, ir.IntType) and isinstance(ptr_val.type.pointee, ir.PointerType):
if val_val.type.width == 64:
store_val = Gen.builder.inttoptr(val_val, ptr_val.type.pointee, name="deref_as_inttoptr")
else:
ext = Gen.builder.zext(val_val, ir.IntType(64), name="deref_as_zext")
store_val = Gen.builder.inttoptr(ext, ptr_val.type.pointee, name="deref_as_inttoptr")
elif val_val.type != ptr_val.type.pointee:
target_ptr_type = ir.PointerType(val_val.type)
ptr_val = Gen.builder.bitcast(ptr_val, target_ptr_type, name="deref_as_cast")
Gen.builder.store(store_val, ptr_val)
return ir.Constant(ir.IntType(32), 0)
def _InjectRequiresFields(self, result: ir.Value, ClassName: str, Gen: LlvmGeneratorMixin) -> None:
"""在 __new__ 后、__init__ 前自动注入 __requires__/__require_must__ 字段。
- 编译期:对 __require_must__ 做纯静态可达性检查(遍历 _with_provides_stack
- 运行时:对每个 requires 字段调用 _find_provider非 NULL 则 bitcast 写入
"""
requires_all: list[str] = []
requires_all.extend(Gen.class_requires.get(ClassName, []))
requires_all.extend(Gen.class_require_must.get(ClassName, []))
if not requires_all:
return
# 编译期 __require_must__ 静态检查
must_fields: list[str] = Gen.class_require_must.get(ClassName, [])
if must_fields:
# 跳过类自身方法内的检查(类的实现内部不需要 with 上下文)
current_class = getattr(Gen, '_current_class_name', None)
if current_class != ClassName:
available: set[str] = set()
for provides_list in Gen._with_provides_stack:
available.update(provides_list)
for field in must_fields:
if field not in available:
raise SyntaxError(
f"[TransPyC] Class '{ClassName}' __require_must__ field '{field}' "
f"not provided by any enclosing 'with' context"
)
# 运行时注入
find_func = Gen.functions.get('_find_provider')
if not find_func:
return
members = Gen.class_members.get(ClassName, [])
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
field_map: dict[str, tuple[int, ir.Type]] = {}
for i, (mname, mtype) in enumerate(members):
if mname in requires_all:
field_map[mname] = (base + i, mtype)
for field_name in requires_all:
if field_name not in field_map:
continue
field_idx, field_type = field_map[field_name]
field_str = Gen._create_string_global(field_name)
ptr_val = Gen.builder.call(find_func, [field_str], name=f"find_{field_name}")
null_ptr = ir.Constant(ptr_val.type, None)
is_null = Gen.builder.icmp_unsigned('==', ptr_val, null_ptr, name=f"is_null_{field_name}")
skip_bb = Gen.func.append_basic_block(f"skip_inject_{field_name}")
inject_bb = Gen.func.append_basic_block(f"inject_{field_name}")
Gen.builder.cbranch(is_null, skip_bb, inject_bb)
Gen.builder.position_at_end(inject_bb)
if isinstance(field_type, ir.PointerType):
casted = Gen.builder.bitcast(ptr_val, field_type, name=f"cast_{field_name}")
elif isinstance(field_type, ir.IntType):
casted = Gen.builder.ptrtoint(ptr_val, field_type, name=f"cast_{field_name}")
else:
casted = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name=f"cast_{field_name}")
elem_ptr = Gen.builder.gep(result, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), field_idx)], name=f"field_{field_name}_ptr")
Gen._store(casted, elem_ptr)
Gen.builder.branch(skip_bb)
Gen.builder.position_at_end(skip_bb)
def _HandleClassNewLlvm(self, Node: ast.Call, ClassName: str, module_sha1: str | None = None) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
# 跨模块类型引用:优先用 {module_sha1}.{ClassName} 查找 struct
_struct_full_key: str | None = f"{module_sha1}.{ClassName}" if module_sha1 else None
def _get_struct_type(name: str) -> ir.Type | None:
"""优先用 SHA1 前缀完整名查找,回退到短名"""
if _struct_full_key and _struct_full_key in Gen.structs:
return Gen.structs[_struct_full_key]
return Gen.structs.get(name)
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
if Gen._has_function(NewFuncName):
NewFunc = Gen._get_function(NewFuncName)
StructType = _get_struct_type(ClassName)
if StructType:
result = Gen._allocaEntry(StructType, name=f"{ClassName}_alloca")
else:
return ir.Constant(ir.IntType(32), 0)
Gen.builder.call(NewFunc, [result], name=f"before_init_{ClassName}")
if has_new_method:
NewMethodFunc = Gen._find_function(NewMethodFuncName)
if NewMethodFunc:
NewMethodArgs = [result]
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
NewMethodArgs.append(ArgVal)
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
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 arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
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)
InitArgs = Gen._apply_auto_addr(InitFuncName, InitArgs)
adjusted_init = Gen._adjust_args(InitArgs, InitFunc)
Gen.builder.call(InitFunc, adjusted_init, name=f"init_{ClassName}")
return result
if _struct_full_key and _struct_full_key in Gen.structs:
StructType = Gen.structs[_struct_full_key]
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(_struct_full_key, 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
elif ClassName in Gen.structs:
StructType = Gen.structs[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 arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
NewMethodArgs.append(ArgVal)
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
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.HandleExprLlvm(arg, VarType=arg_var_type)
if ArgVal:
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)
InitArgs = Gen._apply_auto_addr(InitFuncName, InitArgs)
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
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
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, [])
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
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
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen)
if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'):
obj_val = self.HandleExprLlvm(Node.value)
idx_val = self.HandleExprLlvm(Node.slice)
if obj_val and idx_val:
# 处理 obj_val 为 i8整数的情况联合类型 | t.CPtr 降级后可能被 load 为 i8
if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8:
obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if self._is_char_pointer(obj_val):
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
if isinstance(idx_val.type, ir.IntType) and idx_val.type.width < 64:
idx_val = Gen.builder.zext(idx_val, ir.IntType(64), name="zext_idx")
result = Gen.builder.call(Gen._get_function(f'{ClassName}.__getitem__'), [obj_val, idx_val], name=f"call_{ClassName}.__getitem__")
return result
ValueVal = self.HandleExprLlvm(Node.value)
if not ValueVal:
return None
IndexVal = self.HandleExprLlvm(Node.slice)
if not IndexVal:
return None
if isinstance(ValueVal.type, ir.IntType):
var_ptr = self._get_int_ptr(Node.value)
if var_ptr:
i8_ptr = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index")
ptr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr")
return ptr
return None
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
LoadedValueVal = Gen._load(ValueVal, name="Load_subscript")
if isinstance(LoadedValueVal.type, ir.PointerType):
ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name="subscript")
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="subscript")
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="subscript")
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="subscript")
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="subscript")
return ElemPtr
return None
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:
type_args.append('int')
while len(type_args) < len(type_param_names):
type_args.append('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]
CallArgs = Gen._apply_auto_addr(spec_name, CallArgs)
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])
# 补齐缺少的类型参数
while len(type_args) < len(type_param_names):
type_args.append('int')
type_names.append('CInt')
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'
else:
float_types = {'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T'}
int_types = {'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong'}
if func_id in float_types:
type_str = 'double'
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', 'CChar', 'CShort', 'CLong'}
if attr_name in float_types:
type_str = 'double'
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'
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:
type_args.append('int')
type_names.append('CInt')
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)