from __future__ import annotations from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from lib.core.translator import Translator from lib.core.LlvmCodeGenerator import LlvmCodeGenerator import vast as ast import llvmlite.ir as ir from lib.core.SymbolUtils import IsTModule class ExprUtils: def __init__(self, translator: "Translator") -> None: self.Trans: Translator = translator def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None: OpMap: dict[type, str] = { ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/', ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&', ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>', ast.MatMult: '@', } return OpMap.get(type(Op), None) def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None: OpMap: dict[type, str] = { ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-', ast.Deref: '*', ast.AddrOf: '&', } return OpMap.get(type(Op), None) def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None: OpMap: dict[type, str] = { ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=', ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not', ast.In: 'in', ast.NotIn: 'not in', } return OpMap.get(type(Op), None) def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: if isinstance(node, ast.Name): VarName: str = node.id return Gen.var_struct_class.get(VarName) if isinstance(node, ast.Attribute): AttrName: str = node.attr # 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable) # 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派 # (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST') BaseClass: str | None = self._get_var_class(node.value, Gen) if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self': BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None) if not BaseClass: # 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory): # var_struct_class 只登记**当前模块**变量,跨模块量不在其中 → 递归到此 # 返回 None → 整条链 `Mod.Var.Field` 解析为空 → 赋值/调用 fail-fast # ("属性访问未能解析出目标类型/成员",StructGenMixin.vp:139 根因)。 BaseClass = self._resolve_module_var_class(Gen, node.value, AttrName) if BaseClass: # 对齐 _HandleLenLlvm L3445-3454:class_member_element_class / # class_members 的键是裸类名,而 var_struct_class 可能带 sha1 前缀 # (如 'd81b22c853de40a2.Call')。直查会 miss → 容器字段 # (list[...] / rawptr[list[...]],如 ast.Call.args)方法分派失败 # → "Undefined method 'get' in class 'rawptr'"。剥前缀 + MRO 链查 # element_class(list 特化名,如 list[...ASTptr])优先。 _bare: str = BaseClass if len(_bare) > 17 and _bare[16] == '.': _bare = _bare[17:] element_class_map = getattr(Gen, 'class_member_element_class', {}) try: _mro_chain: list = Gen._get_mro_chain(_bare) except Exception: _mro_chain = [_bare] for _mro_cls in _mro_chain: _mk: str = _mro_cls if len(_mk) > 17 and _mk[16] == '.': _mk = _mk[17:] _sub = element_class_map.get(_mk) if _sub and AttrName in _sub: return _sub[AttrName] if _bare in Gen.class_members: for m_name, m_type in Gen.class_members[_bare]: if m_name == AttrName: if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): found = Gen.find_struct_by_pointee(m_type.pointee) if found: return found[0] break # Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后, # 避免字段名和变量名冲突导致错误分派) cls: str | None = Gen.var_struct_class.get(AttrName) if cls: return cls return None return None def _resolve_module_var_class(self, Gen: LlvmCodeGenerator, ModNode: ast.AST, AttrName: str) -> str | None: """`模块别名.模块级变量` 的类名解析(如 CompilerFactory.factory) 背景:`CompilerFactory.factory.PermPool` 这类链的第一跳既不在 `var_struct_class`(只含当前模块变量)也不在 `global_struct_class` (只含按变量名登记的项),于是 `_get_var_class` 递归到底返回 None → 整条链解析为空 → 赋值/调用 fail-fast("属性访问未能解析出目标类型/成员")。 这里按模块别名把 `Mod.Var` 落到其 LLVM 全局量,再从全局量类型反推结构体 类名;候选键形态对齐 `_lookup_module_global`(裸名 / {mod}.{var} / {sha1}.{var})。仅当 ModNode 确为 import 模块(含别名)时生效。 """ if not isinstance(ModNode, ast.Name): return None Mod: str = ModNode.id imported: Any = getattr(self.Trans, '_ImportedModules', None) or [] sha1_map: Any = getattr(Gen, 'ModuleSha1Map', {}) or {} try: resolved_mod: str = self.Trans.SymbolTable.resolve_alias(Mod) except Exception: resolved_mod = Mod aliases: Any = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {} _is_mod: bool = (Mod in imported or resolved_mod in imported or Mod in sha1_map or resolved_mod in sha1_map or Mod in aliases or resolved_mod in aliases) if not _is_mod: # 兜底:ModuleSha1Map 的键多为全路径(lib.core.CompilerFactory), # 用后缀扫描判定「Mod 是否确实是某个已导入模块(含别名)」。 for _k in sha1_map: if _k == Mod or _k.endswith(f'.{Mod}'): _is_mod = True break if not _is_mod: return None # 1) 变量名侧已有登记(stub 解析时跨模块量常以**裸变量名**登记) gsc: Any = getattr(Gen, 'global_struct_class', {}) or {} vsc: Any = getattr(Gen, 'var_struct_class', {}) or {} for key in (f"{Mod}.{AttrName}", f"{resolved_mod}.{AttrName}", AttrName): if key in gsc: return gsc[key] if key in vsc: return vsc[key] # 2) 借属性侧既有解析器(覆盖 module.globals/variables 与前缀形态) try: _via_attr: str | None = self.Trans.ExprAttrHandle._resolve_var_classname(Gen, AttrName) except Exception: _via_attr = None if _via_attr: return _via_attr # 3) 从 LLVM 全局量类型反推结构体类名 cands: list[str] = [f"{Mod}.{AttrName}", AttrName] for m in imported: if m == Mod or m.endswith(f".{Mod}"): k: str = f"{m}.{AttrName}" if k not in cands: cands.append(k) for cand in (Mod, resolved_mod): sha1: str | None = sha1_map.get(cand) if sha1: k2: str = f"{sha1}.{AttrName}" if k2 not in cands: cands.append(k2) globals_map: Any = getattr(getattr(Gen, 'module', None), 'globals', {}) or {} # 3b) 后缀扫描:全局量键常为 "{sha1}.{var}",sha1 不一定能从 Mod 反查到 for _gk in globals_map: if _gk.endswith(f".{AttrName}") and _gk not in cands: cands.append(_gk) for key in cands: gv: Any = globals_map.get(key) if gv is None: continue ty: Any = getattr(gv, 'type', None) if not isinstance(ty, ir.PointerType): continue pointee: Any = ty.pointee # 全局量本身是指针变量(值类型 X*,LLVM 全局类型 X**)→ 再剥一层 if isinstance(pointee, ir.PointerType): pointee = pointee.pointee if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): found: Any = Gen.find_struct_by_pointee(pointee) if found: return found[0] return None def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None: FullMethodName: str = f'{ClassName}.{op_name}' if not Gen._has_function(FullMethodName): FullMethodName = f'{ClassName}.{op_name}__' if not Gen._has_function(FullMethodName): # 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 VPC 一致) for anc in Gen._get_mro_chain(ClassName)[1:]: anc_method: str = f'{anc}.{op_name}' if Gen._has_function(anc_method): FullMethodName = anc_method break anc_method_ss = f'{anc}.{op_name}__' if Gen._has_function(anc_method_ss): FullMethodName = anc_method_ss break if not Gen._has_function(FullMethodName): # 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且 # 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本 # (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时, # var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。 # 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。 if '[' not in ClassName: SpecClass: str | None = None # 先收集所有候选特化(有方法 declare 或 define 的) best_candidates: list[str] = [] for CN in Gen.structs.keys(): if CN.startswith(f'{ClassName}[') and (Gen._has_function(f'{CN}.{op_name}') or Gen._has_function(f'{CN}.{op_name}__')): best_candidates.append(CN) # 优先选已编译方法体(define)的特化:dict 插入序选第一个会命中 # 只有 declare 无 define 的错误特化(如 list[AST]),生成 undefined # symbol(ld.lld undefined symbol: list[AST].__len__ 根因)。 for CN in best_candidates: _fn: str = f'{CN}.{op_name}' if Gen._has_function(f'{CN}.{op_name}') else f'{CN}.{op_name}__' _f: Any = Gen._get_function(_fn) if _f is not None and getattr(_f, 'blocks', []): SpecClass = CN break if SpecClass is None and best_candidates: # 全部只有 declare:按需补发方法体(幂等,_specialize_generic_class # 缓存机制会从 declare_only 升级为完整特化),补发后重新检查 try: self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen) except Exception: pass _fn2: str = f'{best_candidates[0]}.{op_name}' if Gen._has_function(f'{best_candidates[0]}.{op_name}') else f'{best_candidates[0]}.{op_name}__' _f2: Any = Gen._get_function(_fn2) if _f2 is not None and getattr(_f2, 'blocks', []): SpecClass = best_candidates[0] if SpecClass: FullMethodName = f'{SpecClass}.{op_name}' if not Gen._has_function(FullMethodName): FullMethodName = f'{SpecClass}.{op_name}__' if Gen._has_function(FullMethodName): func: Any = Gen._get_function(FullMethodName) # 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时, # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 method_anc: str | None = None _fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName if '.' in FullMethodName: _fm_cls: str = FullMethodName.split('.')[0] if _fm_cls != ClassName: method_anc = _fm_cls _call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc) call_args: list[Any] = [_call_self] if other_val is not None: call_args.append(other_val) if len(func.ftype.args) == len(call_args): for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)): if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType): if expected_type != actual_val.type: if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}") result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}") return result return None def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]: info: dict[str, Any] = { 'name': "unknown", 'size': 0, 'signed': False, 'ptr': False } if isinstance(llvm_type, ir.IntType): info['size'] = llvm_type.width // 8 info['signed'] = True if llvm_type.width == 8: info['name'] = "char" elif llvm_type.width == 16: info['name'] = "short" elif llvm_type.width == 32: info['name'] = "int" elif llvm_type.width == 64: info['name'] = "long long" else: info['name'] = f"int{llvm_type.width}" elif isinstance(llvm_type, ir.DoubleType): info['size'] = 8 info['signed'] = True info['name'] = "double" elif isinstance(llvm_type, ir.FloatType): info['size'] = 4 info['signed'] = True info['name'] = "float" elif isinstance(llvm_type, ir.PointerType): info['size'] = 8 info['signed'] = False info['ptr'] = True pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee) info['name'] = f"{pointee_info['name']}*" elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)): size: int = 0 for elem in llvm_type.elements: elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem) size += elem_info['size'] info['size'] = size info['signed'] = False if isinstance(llvm_type, ir.IdentifiedStructType): info['name'] = llvm_type.name else: info['name'] = "struct" elif isinstance(llvm_type, ir.ArrayType): elem_info = self._llvm_type_to_detailed_string(llvm_type.element) info['size'] = elem_info['size'] * llvm_type.count info['signed'] = elem_info['signed'] info['name'] = f"{elem_info['name']}[]" elif isinstance(llvm_type, ir.VoidType): info['size'] = 0 info['signed'] = False info['name'] = "void" else: info['size'] = 0 info['signed'] = False info['name'] = str(llvm_type) return info def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type: Gen: LlvmCodeGenerator = self.Trans.LlvmGen if isinstance(node, ast.Constant): if isinstance(node.value, bool): return ir.IntType(32) elif isinstance(node.value, int): return ir.IntType(32) elif isinstance(node.value, float): return ir.DoubleType() elif isinstance(node.value, str): if getattr(node, 'kind', None) == 'u': return ir.PointerType(ir.IntType(16)) return ir.PointerType(ir.IntType(8)) return ir.IntType(32) elif isinstance(node, ast.Name): if node.id in Gen.variables: var_ptr: Any = Gen.variables[node.id] if isinstance(var_ptr.type, ir.PointerType): return var_ptr.type.pointee return var_ptr.type if node.id in Gen._reg_values: return Gen._reg_values[node.id].type return ir.IntType(32) elif isinstance(node, ast.Attribute): attr_name: str = node.attr AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name) if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None): # 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型) return self.Trans.LlvmGen._enum_int_type( getattr(AttrInfo, 'EnumName', None), AttrInfo) return ir.IntType(32) elif isinstance(node, ast.BinOp): left_type: ir.Type = self._infer_expr_llvm_type_full(node.left) right_type: ir.Type = self._infer_expr_llvm_type_full(node.right) if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)): return ir.DoubleType() if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType): return ir.PointerType(ir.IntType(8)) return ir.IntType(32) elif isinstance(node, ast.UnaryOp): op_inner: ir.Type = self._infer_expr_llvm_type_full(node.operand) if isinstance(node.op, ast.Deref): # *p → pointee 类型 if isinstance(op_inner, ir.PointerType): return op_inner.pointee return op_inner if isinstance(node.op, ast.AddrOf): # &x → T*(取地址即指针) return ir.PointerType(op_inner) return op_inner elif isinstance(node, ast.Subscript): val_type: ir.Type = self._infer_expr_llvm_type_full(node.value) if isinstance(val_type, ir.PointerType): if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8: return ir.IntType(8) if isinstance(val_type.pointee, ir.ArrayType): return ir.PointerType(val_type.pointee.element) return val_type.pointee if isinstance(val_type, ir.ArrayType): return val_type.element if isinstance(val_type, ir.IntType): return ir.IntType(8) return ir.IntType(32) elif isinstance(node, ast.Call): if isinstance(node.func, ast.Name): func_name: str = node.func.id if Gen._has_function(func_name): fn: Any = Gen._get_function(func_name) return fn.function_type.return_type if func_name == 'len': return ir.IntType(32) if func_name == 'sizeof': return ir.IntType(32) if func_name == 'ord': return ir.IntType(32) if func_name == 'chr': return ir.IntType(8) if func_name == 'int': return ir.IntType(32) if func_name == 'float' or func_name == 'double': return ir.DoubleType() elif isinstance(node.func, ast.Attribute): if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c': if node.func.attr == 'Deref': return ir.IntType(64) if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable): if node.func.attr == 'CChar': return ir.PointerType(ir.IntType(8)) return ir.IntType(32) elif isinstance(node, ast.Compare): return ir.IntType(1) elif isinstance(node, ast.BoolOp): return ir.IntType(1) elif isinstance(node, ast.IfExp): return self._infer_expr_llvm_type_full(node.body) elif isinstance(node, ast.Attribute): return ir.PointerType(ir.IntType(8)) return ir.IntType(32) def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None: """收集 AST 节点中的绑定变量和自由变量 Args: node: AST 节点 bound: 集合,收集绑定变量(ast.Store 上下文) free: 集合,收集自由变量(ast.Load 上下文),可为 None """ if free is None: free = set() if isinstance(node, ast.Name): if isinstance(node.ctx, ast.Store): bound.add(node.id) elif isinstance(node.ctx, ast.Load): free.add(node.id) elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)): # 这些节点有自己的作用域,不递归进入 # 但需要收集参数名作为 bound if hasattr(node, 'args') and node.args: for arg in node.args.args: bound.add(arg.arg) elif getattr(node, '_fields', None): for field in node._fields: value: Any = getattr(node, field, None) if isinstance(value, list): for item in value: if isinstance(item, (ast.stmt, ast.expr)): self._collect_names(item, bound, free) elif isinstance(value, (ast.stmt, ast.expr)): self._collect_names(value, bound, free)