Files
ViperC/App/lib/core/Gen/Expr/ExprGenOps.vp
T
TermiNexus aa52cf157d revert(transpyv): 回滚跨模块链表 Next 改访问器(改由编译器沿继承链解析字段名)
回滚 13080f6 的 110 处访问器调用,恢复裸字段访问 `x.Next`。

理由:该批访问器是把访问移到定义模块内执行,**绕开**了"继承字段名解析",
属于规避而非修复;缺陷本体在 TPC 侧(导入侧继承展平把泛型基类的继承字段
记成 __inherited_N 占位名,名字丢失)。TPC 已按语义修正:
  · e6451a07 继承展平沿继承链取真实字段名(泛型特化回退模板名),
    展平表直接落 `[("Next", <槽位类型>), ...]`,属性访问是普通表命中;
  · 3ca46c01 顺带修掉 _get_or_create_struct 里"同名枚举成员遮蔽类定义"的 i32 出口。
因此源码侧恢复为唯一写法(裸字段访问),由编译器负责解析。

注:本 commit 只回滚访问器;13080f6 里夹带的 CallGenProp.vp 续行合并(非访问器改动)
已在工作区保留(未提交)。
2026-09-28 21:48:53 +08:00

1528 lines
89 KiB
Plaintext
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.
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeSpec as TSpec
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# ============================================================
# ExprGenOps - ExprGenMixin 拆分(阶段 D):比较/一元/布尔/binop 运算
# ============================================================
novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
"""比较/一元/布尔/binop 运算"""
# ============================================================
# 翻译三元表达式 IfExp(test, body, orelse)
#
# 生成: cond ? body : orelse
# then_blk: 翻译 body → br merge
# else_blk: 翻译 orelse → br merge
# merge_blk: phi [body_val, then_blk], [orelse_val, else_blk]
# ============================================================
def translate_ifexp(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译三元表达式 (body if test else orelse),返回 phi 结果 Value"""
if node is None or builder is None or trans is None: return None
ie: rawptr[ast.IfExp] = rawptr[ast.IfExp](node)
if ie is None: return None
func: llvmlite.Function* = trans._cur_func
if func is None: return None
# 1. 求值条件
cond_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ie.test, None, 0, trans)
if cond_val is None: return None
# 转换为 i1
cond_bits: int = self.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value* = cond_val
else:
if self.is_ptr_type(cond_val.Ty) != 0:
# 指针类型:与 null 比较
null_cond: llvmlite.Value* = llvmlite.ConstNull(pool, cond_val.Ty, "null")
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
else:
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
# 2. 创建三个基本块
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
name_buf: str = pool.alloc(32)
if name_buf is None:
return None
viperlib.snprintf(name_buf, 32, "ifexp.then.%d", cnt)
then_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "ifexp.else.%d", cnt)
else_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "ifexp.merge.%d", cnt)
merge_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
if then_bb is None or else_bb is None or merge_bb is None: return None
# 3. 条件跳转
llvmlite.build_cond_br(builder, cond_i1, then_bb, else_bb)
# 4. then 分支
llvmlite.position_at_end(builder, then_bb)
body_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ie.body, None, 0, trans)
then_blk_for_phi: rawptr[llvmlite.BasicBlock] = trans._cur_builder.CurBlock
if body_val is None:
# fail-fast:三元表达式 then 分支翻译失败,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(ie.body, "三元表达式分支翻译失败", "TypeError")
# 暂不 br,等类型对齐后再 br
# 5. else 分支
# then 分支为指针时,把它的类型作为 else 分支的目标类型上下文下传:单引号
# 单字符字面量(CONST_CHAR,如 '?')在指针目标下物化为 1 字符字符串
# (对齐 TPC 引号推断,见 ExprGenConst.translate_constant 的 CONST_CHAR 分支),
# 否则物化为 i32 → 下方类型对齐 coerce 整数→指针被 fail-fast 拒绝
# (VpiReader.vp `rel_path_rt if rel_path_rt is not None else '?'` 根因:then 是
# str(i8*)、else '?' 恒 i32 → CastError)。非指针 then 分支不下传,既有
# 常量物化与位宽行为保持不变。
orelse_target_ty: llvmlite.LLVMType* = None
if self.is_ptr_type(body_val.Ty) != 0:
orelse_target_ty = body_val.Ty
llvmlite.position_at_end(builder, else_bb)
orelse_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ie.orelse, None, 0, trans, orelse_target_ty)
else_blk_for_phi: rawptr[llvmlite.BasicBlock] = trans._cur_builder.CurBlock
if orelse_val is None:
# fail-fast:三元表达式 else 分支翻译失败,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(ie.orelse, "三元表达式分支翻译失败", "TypeError")
# 暂不 br,等类型对齐后再 br
# 类型对齐 (两分支类型可能不同)
# 必须在各自分支中执行 coerce_to_type,确保 merge 块中 PHI 是第一条指令
# (否则 coerce_to_type 生成的 sext/zext/ptrtoint/inttoptr 指令在 PHI 之前,违反 LLVM 规则)
body_bits: int = self.get_llvm_type_bits(body_val.Ty)
orelse_bits: int = self.get_llvm_type_bits(orelse_val.Ty)
body_is_ptr: int = self.is_ptr_type(body_val.Ty)
orelse_is_ptr: int = self.is_ptr_type(orelse_val.Ty)
# 情况0: 一侧是指针,另一侧是整数(如 i64 vs i8* null)
# 必须优先处理,因为 Ptr 的 bits=0 会导致下面三个分支全部跳过
# 选择 body_val.Ty 作为 phi_ty(then 分支优先),orelse_val 对齐到它
# coerce_to_type 内部会调用 ptrtoint(指针→整数)或 inttoptr(整数→指针)
if body_is_ptr != orelse_is_ptr:
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
elif body_bits > orelse_bits and orelse_bits > 0:
# 在 else 块中转换 orelse_val
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
elif orelse_bits > body_bits and body_bits > 0:
# 回到 then 块转换 body_val
llvmlite.position_at_end(builder, then_blk_for_phi)
body_val = self.coerce_to_type(builder, body_val, orelse_val.Ty, node)
elif body_bits == orelse_bits and body_bits > 0:
# bits 相同但类型不同(如 i64 vs Ptr):null 不能用于 i64 PHI
# 此分支理论上不会触发(已被情况0覆盖),保留作为防御
if self.is_ptr_type(body_val.Ty) != self.is_ptr_type(orelse_val.Ty):
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
# 现在分别在 then/else 块中 br 到 merge(在类型转换之后)
llvmlite.position_at_end(builder, then_blk_for_phi)
llvmlite.build_br(builder, merge_bb)
llvmlite.position_at_end(builder, else_blk_for_phi)
llvmlite.build_br(builder, merge_bb)
# 6. merge 块: phi(PHI 是 merge 块的第一条指令)
llvmlite.position_at_end(builder, merge_bb)
phi_ty: llvmlite.LLVMType* = body_val.Ty
if phi_ty is None:
# fail-fast:三元表达式分支类型缺失,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(node, "三元表达式分支类型缺失", "TypeError")
inc1: llvmlite.PhiIncoming* = llvmlite.new_phi_incoming(pool, body_val, then_blk_for_phi)
inc2: llvmlite.PhiIncoming* = llvmlite.new_phi_incoming(pool, orelse_val, else_blk_for_phi)
if inc1 is None or inc2 is None:
return None
# 链表顺序: inc1 (head) -> inc2 (tail)
# 之前错误写成 inc2.Next = inc1,导致 build_phi 从 inc1 遍历时
# inc1.Next 为 None,只输出一个 phi 条目,引发 LLVM 验证错误:
# "PHINode should have one entry for each predecessor"
inc1.Next = inc2
return llvmlite.build_phi(builder, phi_ty, inc1, 2)
# ============================================================
# 翻译比较表达式 Compare(left, ops, comparators)
# ============================================================
def should_zext_widen(self, pool: memhub.MemBuddy*,
narrow_val: llvmlite.Value*,
wide_val: llvmlite.Value*,
narrow_node: ast.AST* = None,
trans: HT.Translator* = None) -> int:
"""判断整数拓宽方向:1=zext(无符号语义),0=sext
**拓宽方向的单一真源**:ExprGenBase._zext_or_sext_val(coerce 路径)委托到此。
trans 必须下传:_node_type_unsigned 对 Name/Subscript 形态需经 trans.SymTab 查
变量/数组元素注解才能判定无符号。此前本函数签名缺 trans 形参,而
translate_binop 两处调用**已在传第 5 参**(被静默丢弃),函数体内的 `trans`
退化为隐式全局(恒 None)⇒ 「源无符号」规则对 Name/Subscript 恒不命中,
无符号值被误用 sext 扩展(本注释原记的 ide.vp identify_data[60] /
timer.vp sec 的 uint16/uint32→更宽 根因,补救从未真正接上)。
对齐 TPC 的 _check_node_unsigned / HandlesExprOps 拓宽规则:
1. i1 (bool) → zext(1 保持 1,而非 sext 的 0xFF...)
2. i8 → zext(系统编程中 i8 几乎总是无符号字节);
例外:源注解**明确有符号**(int8/int8/i8/char…)→ sext 保号
(判据 _node_type_signed,见下)
3. 宽侧是常量且其值超出窄侧位宽的有符号范围 → zext
(常量"想要"无符号解释,如 0xAABBCCDD > INT32_MAX 与
uint32 值比较,sext 会把 0xAABBCCDD 变成
0xFFFFFFFFAABBCCDD 导致恒不等)
"""
if narrow_val is None or narrow_val.Ty is None:
return 0
narrow_bits: int = self.get_llvm_type_bits(narrow_val.Ty)
if narrow_bits == 1:
return 1
if narrow_bits == 8:
# i8 默认按「无符号字节」处理(系统编程惯例,保持既有行为);
# 但**源注解明确为有符号类型**时必须 sext 保号:
# TypeCastTest 回归 —— `int32(v)`(v: int8 = -50)期望 -50,
# 此前 b8470c1 把强转路径统一到本函数后撞上无条件 zext 而变成 206。
if self._node_type_signed(pool, narrow_node, trans) != 0:
return 0
return 1
# 宽侧常量启发式
if wide_val is not None and wide_val.IsConst == 1:
wide_bits: int = self.get_llvm_type_bits(wide_val.Ty)
if wide_bits > narrow_bits and narrow_bits < 64:
signed_max: int64 = (int64(1) << (narrow_bits - 1)) - 1
if wide_val.IntVal > signed_max:
return 1
# 窄侧 AST 节点解析为无符号类型(如 t.CUIntXXT 字面量/强转)
# trans 必须下沉:Name/Subscript 形态靠 trans.SymTab 查变量/数组元素
# 注解的无符号性,缺 trans 时恒返回 0 → 无符号值被误用 sext 扩展
# (ide.vp identify_data[60] / timer.vp sec 的 uint16/uint32→更宽 根因)
if narrow_node is not None:
if self._node_type_unsigned(pool, narrow_node, trans) != 0:
return 1
# 窄侧是高位比特置位的 CDefine 常量(0x80000000..0xFFFFFFFF)→ zext。
# CDefine 常量是编译期值而非变量,_node_type_unsigned 查不到类型注解;
# 值落在有符号负数区表示无符号哨兵(如 INVALID_FILE_ATTRIBUTES =
# 0xFFFFFFFF = 4294967295)。与宽侧(如 ULONG=CUnsignedLong→i64 的
# Win32 属性值)比较时须 zext 到 4294967295,否则 sext 成 -1 与运行时
# 零扩展的 32 位 API 返回值恒不等(SysLibTest exists/rmdir/rename/rmtree
# 全 FAIL 根因)。负值 CDefine(如 -1)不在此区间(-1 > 0x7FFFFFFF 为假),
# 保持 sext 语义不变。
if narrow_node is not None and narrow_node.kind() == ast.ASTKind.Name:
cdef_nm_zw: ast.Name* = ast.Name*(narrow_node)
if cdef_nm_zw.id is not None:
cdv_zw: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(cdef_nm_zw.id)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
if cdv_zw > 0x7FFFFFFF:
return 1
return 0
def _node_type_unsigned(self, pool: memhub.MemBuddy*,
node: ast.AST*,
trans: HT.Translator* = None) -> int:
"""轻量判断 AST 节点是否解析为无符号整数类型(1=是,0=否/未知)
覆盖常见形态(对齐 TPC _check_node_unsigned 的 Call/Subscript 分支):
- uint8/CUInt16T/CUInt32T/CUInt64T/CUnsigned* 等显式无符号类型强转调用
- 无符号类型名的 Name 引用(如 UInt32T 别名)
- 变量名(trans 可用时):查询 SymTab 中变量的 AnnotClassName(如 v: uint32)
"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(node)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(cl.func)
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 \
and at.attr is not None:
if self._type_name_unsigned(at.attr) != 0:
return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None:
# 1) 名字本身是无符号类型名(如 UInt32T 别名)
if self._type_name_unsigned(nm.id) != 0:
return 1
# 2) 变量注解类型为无符号类型(trans 可用时查 SymTab)
if trans is not None:
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if ve is not None:
# [S4 切读] Spec 明确无符号(TS_UINT)优先于字符串判定
if self._entry_spec_unsigned(ve.Spec) != 0:
return 1
vcn_u: str = self.Gen.VarGen.var_annot_class_name(ve)
if vcn_u is not None:
if self._type_name_unsigned(vcn_u) != 0:
return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if self._node_type_unsigned(pool, bop.left, trans) != 0:
return 1
if self._node_type_unsigned(pool, bop.right, trans) != 0:
return 1
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp* = ast.UnaryOp*(node)
if self._node_type_unsigned(pool, uop.operand, trans) != 0:
return 1
return 0
if k == ast.ASTKind.Subscript:
# 数组/指针下标: md5_buf[i](t.CArray[uint8, N])或 buf[i]
# (形参 uint8 | t.CPtr)元素是无符号整型 → printf("%02x", ...) zext。
sub_su: ast.Subscript* = ast.Subscript*(node)
if sub_su.value is not None and sub_su.value.kind() == ast.ASTKind.Name:
snm_su: ast.Name* = ast.Name*(sub_su.value)
if snm_su.id is not None and trans is not None:
ve_su: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, snm_su.id)
if ve_su is not None:
# [S4 切读] Spec 明确无符号优先(数组元素为无符号的联合注解场景
# 时 Spec 仍是 Ptr/Array ⇒ 返回 0,自动回退字符串路径)
if self._entry_spec_unsigned(ve_su.Spec) != 0:
return 1
scn_u: str = self.Gen.VarGen.var_annot_class_name(ve_su)
if scn_u is not None:
if self._annot_str_unsigned(scn_u) != 0:
return 1
return 0
if k == ast.ASTKind.Attribute:
# 结构体字段(如 viperio.Buf 的 self.capacity/length: sizet):经接收者变量的 AnnotClassName 取类名,再按类名+字段名查字段注解,判定无符号(CSizeT/uint*)。本函数在 translate_binop 对每个操作数调用,必须只读、失败返回 0——故不走 lookup_field_by_class(其模块限定名分支会 fatal_error),改用 find_struct + 自遍历字段(未命中一律返回 0)
at_u: ast.Attribute* = ast.Attribute*(node)
if at_u.value is not None and at_u.attr is not None and trans is not None:
cls_nm_u: str = None
if at_u.value.kind() == ast.ASTKind.Name:
vn_u: ast.Name* = ast.Name*(at_u.value)
if vn_u.id is not None:
ve_u: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, vn_u.id)
if ve_u is not None:
cls_nm_u = self.Gen.VarGen.var_annot_class_name(ve_u)
if cls_nm_u is not None:
# 取裸类名(点前部分):只做裸名结构体查找,避免模块限定名解析
bare_u: str = cls_nm_u
dot_u: str = self.Gen.StructGen._last_dot_outside_brackets(cls_nm_u)
if dot_u is not None:
bare_u = dot_u + 1
if bare_u is not None:
st_u: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(bare_u)
if st_u is not None:
fi_u: int = 0
while fi_u < st_u.FieldCount:
fe_u: GenTypes.FieldEntry* = \
self.Gen.StructGen._get_field_entry(st_u, fi_u)
if fe_u is not None and fe_u.Name is not None \
and string.strcmp(fe_u.Name, at_u.attr) == 0:
# [S4 切读] 字段 Spec 明确无符号优先
if self._entry_spec_unsigned(fe_u.Spec) != 0:
return 1
fcn_u: str = self.Gen.StructGen.get_field_annot_class_name(fe_u)
if fcn_u is not None:
if self._annot_str_unsigned(fcn_u) != 0:
return 1
break
fi_u += 1
return 0
return 0
def _node_type_signed(self, pool: memhub.MemBuddy*,
node: ast.AST*,
trans: HT.Translator* = None) -> int:
"""源节点是否**明确有符号**整数(独立真源的 SIGNED 分支;未知返回 0)
只服务 should_zext_widen 的 i8 分支:i8 默认按「无符号字节」zext,
但源注解明确有符号(int8 / int8 / i8 / char …)时须 sext 保号。
判据走独立符号性真源(精确名 + CTypedef 链),与 _node_type_unsigned 的
遍历形态对应,但只覆盖**注解直接可见**的节点形态(Call(t.X) / Name /
Subscript);其余返回 0,即保持 i8 的既有默认行为(不扩大改动面)。
"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(node)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(cl.func)
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 \
and at.attr is not None:
if self._spec_sign_from_annot(at.attr) == TSpec.TS_SIGN_SIGNED:
return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None:
if self._spec_sign_from_annot(nm.id) == TSpec.TS_SIGN_SIGNED:
return 1
if trans is not None:
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if ve is not None:
ncn_s: str = self.Gen.VarGen.var_annot_class_name(ve)
if ncn_s is not None:
if self._spec_sign_from_annot(ncn_s) == TSpec.TS_SIGN_SIGNED:
return 1
return 0
if k == ast.ASTKind.Subscript:
sub_s: ast.Subscript* = ast.Subscript*(node)
if sub_s.value is not None and sub_s.value.kind() == ast.ASTKind.Name:
snm: ast.Name* = ast.Name*(sub_s.value)
if snm.id is not None and trans is not None:
ve_s: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, snm.id)
if ve_s is not None:
scn_s: str = self.Gen.VarGen.var_annot_class_name(ve_s)
if scn_s is not None:
if self._spec_sign_from_annot(scn_s) == TSpec.TS_SIGN_SIGNED:
return 1
return 0
return 0
# ============================================================
# S4 去字符串化(无符号判定块)——**真源唯一**,旧字符串路径已删
#
# 真源:TypeGenCType.spec_sign_of_annot(顶层 '|' 分段 → 去空白/外层括号 →
# '*' 后缀 → IDENT[INNER, ...] 取元素 → 去模块限定前缀 → 精确名白名单 →
# CTypedef 链逐层展开,限深 16 防环)。
# 它已完整覆盖旧路径的能力(含复合注解与别名链),故 S4 直接删掉旧字符串兜底:
# `_type_name_unsigned_legacy` / `_annot_str_unsigned_scan` /
# `_annot_str_unsigned_legacy` / `_last_dot_tail` / `_is_simple_ident`
# —— 那套 strstr 子串扫描正是 A4/A5 假阳性的源头(`BYTEPTR` 含 BYTE 被判
# 无符号;任何含 `uint32` 的串(含 sha1 前缀 mangled 名)都会命中)。
#
# 现语义:真源 UNKNOWN = **不表态** ⇒ 两个入口一律返回 0(保持编译器默认的
# zext/sext 取向)。若日后发现某名字确实是无符号却没被判出,正确处理是
# **补进 SpecSignFromTypeNameSpan 白名单 / 补 CTypedef 声明**,
# 而不是恢复子串扫描(那会把假阳性一并放回来)。
# ============================================================
def _spec_sign_from_annot(self, annot: str) -> int:
"""真源入口:注解 → TS_SIGN_*(UNKNOWN 表示不表态)"""
return self.Gen.TypeGenCType.spec_sign_of_annot(annot, 0)
# ============================================================
# _entry_spec_unsigned - 变量/字段条目的 Spec 是否**明确无符号整数**
#
# S4 切读:条目的 Spec 由 SpecFromLLVMType 反推(恒 TS_INT)+ SpecWithSign
# 按注解切 TS_UINT。因此 `Kind == TS_UINT` 是**注解明确表态无符号**的强信号,
# 可优先于注解名字符串判定直接采用。
#
# ⚠️ 反向不成立:`Kind == TS_INT` 既可能是"注解有符号"也可能是"未表态"
# (SpecFromLLVMType 默认),**不可**据此断言 SIGNED —— 故只切无符号方向,
# 有符号仍走注解名(Spec.SourceName)真源(避免把未表态误判成 signed)。
# ============================================================
def _entry_spec_unsigned(self, sp: TSpec.TypeSpec*) -> int:
"""Spec 是否明确无符号整数(TS_UINT);其它一律 0(未表态/不适用)"""
if sp is None:
return 0
if sp.Kind == TSpec.TS_UINT:
return 1
return 0
def _type_name_unsigned(self, type_name: str) -> int:
"""[S4] 类型名无符号判定:**真源唯一**(旧字符串兜底已删)
真源 UNKNOWN = 不表态 ⇒ 返回 0(非无符号),保持编译器默认的 zext/sext 取向。
"""
if type_name is not None:
if self._spec_sign_from_annot(type_name) == TSpec.TS_SIGN_UNSIGNED:
return 1
return 0
def _annot_str_unsigned(self, annot: str) -> int:
"""[S4] 注解串无符号判定:**真源唯一**(旧子串扫描已删)
真源按顶层 '|' 分段、去括号/空白、取 `IDENT[INNER]` 的元素、去模块限定前缀,
再走精确名白名单 + CTypedef 链展开 —— 已覆盖原 `_annot_str_unsigned_scan`
对复合注解(`t.CArray[uint8, 16]` / `uint8 | t.CPtr`)的全部能力,
且无子串假阳性。UNKNOWN = 不表态 ⇒ 返回 0。
"""
if annot is not None:
if self._spec_sign_from_annot(annot) == TSpec.TS_SIGN_UNSIGNED:
return 1
return 0
def translate_compare(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译比较表达式,返回 i1 结果"""
cmp: rawptr[ast.Compare] = rawptr[ast.Compare](node)
if cmp is None:
return None
ops_list: list[int] | t.CPtr = (list[int] | t.CPtr)(cmp.ops)
if ops_list is None or len(ops_list) == 0:
return None
op: int = ops_list.get(0)
comparators: list[ast.AST*]* = cmp.comparators
if comparators is None or len(comparators) == 0:
return None
rhs_node: ast.AST* = comparators.get(0)
rhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rhs_node, None, 0, trans)
if rhs is None:
return None
# === None 比较优化: x == None / x != None 走身份比较(icmp eq/ne null)===
# 避免对结构体指针生成无效的 __eq__/__ne__ 调用(方法未定义但类有 SHA1 时
# try_operator_overload 会误生成 call void @...__eq__(...) 导致链接错误)
# 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__
lhs_node: ast.AST* = cmp.left
cmp_with_none: int = 0
if op == ast.OpKind.Eq or op == ast.OpKind.Ne or op == ast.OpKind.Is or op == ast.OpKind.IsNot:
if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant:
rhs_cn: ast.Constant* = ast.Constant*(rhs_node)
if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE:
cmp_with_none = 1
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant:
lhs_cn: ast.Constant* = ast.Constant*(lhs_node)
if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE:
cmp_with_none = 1
# === 指针判空模式: ptr != 0 / ptr == 0 ===
# 跳过运算符重载走身份比较(icmp null)。跨模块类(如 cpython.PyObject)
# 未定义 __eq__/__ne__ 时,try_operator_overload 无法判定方法不存在
# (类在依赖模块,cls_sha1 != trans.ModuleSha1),会误发幽灵
# `call void @...__ne__` → 链接 undefined reference(CPythonTest main.vp
# `py_num != 0` 根因)。C 语义:指针与字面量 0 比较 = 判空。
cmp_zero: int = 0
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
if rhs_node is not None and self._is_zero_int_literal(rhs_node) != 0:
cmp_zero = 1
if cmp_zero == 0 and lhs_node is not None and self._is_zero_int_literal(lhs_node) != 0:
cmp_zero = 1
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter),用 alloca 指针尝试重载
# None 比较跳过重载(走身份比较)
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name* = ast.Name*(lhs_node)
if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if lhs_alloca is not None:
if cmp_zero == 0 or self.get_ptr_level(lhs_alloca.Ty) == 0:
ovl_self1: llvmlite.Value* = lhs_alloca
# Ptr(Ptr(Struct)) 对象存储槽:load 出对象指针作 self
# (与 translate_binop 路径 1 一致,避免槽地址当对象地址)
if lhs_alloca.Ty is not None and lhs_alloca.Ty.Pointee != 0 and self.Gen.ExprGenBase.is_ptr_type(lhs_alloca.Ty.Pointee) != 0:
load_self1: llvmlite.Value* = llvmlite.build_load(builder, lhs_alloca.Ty.Pointee, lhs_alloca)
if load_self1 is not None:
ovl_self1 = load_self1
ovl_result: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, ovl_self1, rhs, op, trans, 1)
if ovl_result is not None:
return ovl_result
# 正常翻译 lhs
lhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cmp.left, None, 0, trans)
if lhs is None:
return None
# === in / not in:容器右操作数调用 __contains__(对齐 TPC
# HandlesExprOps.py L261-281)。x in y → y.__contains__(x);
# x not in y → !y.__contains__(x)。容器无 __contains__ →
# 返回 i1 0(TPC 同:恒 False)。
if op == ast.OpKind.In or op == ast.OpKind.NotIn:
cn_st: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(rhs)
if cn_st is not None:
cn_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, cn_st)
if cn_cls is not None:
cn_lookup: str = pool.alloc(128)
if cn_lookup is not None:
viperlib.snprintf(cn_lookup, 128, "%s.__contains__", cn_cls)
cn_fn: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, cn_lookup)
if cn_fn is not None:
cn_ret: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(cn_fn)
cn_fn_name: str = llvmlite.function_get_name(cn_fn)
if cn_ret is not None and cn_fn_name is not None:
# self 参数(容器)按 __contains__ 首参类型对齐
cn_self: llvmlite.Value* = rhs
cn_first: llvmlite.Param* = llvmlite.function_get_param_head(cn_fn)
if cn_first is not None:
cn_self_ty: llvmlite.LLVMType* = llvmlite.param_get_ty(cn_first)
if cn_self_ty is not None:
cn_self = llvmlite.build_bitcast(builder, rhs, cn_self_ty)
if cn_self is not None:
# 参数链表: self → key(独立 null 结尾)
cn_self.Next = lhs
lhs.Next = None
cn_res: llvmlite.Value* = llvmlite.build_call(builder, cn_fn_name, cn_self, 2, cn_ret, 0)
if cn_res is not None:
# 归一化到 i1(__contains__ 返回 int/i32 等)
cn_i1: llvmlite.Value* = cn_res
match cn_ret:
case llvmlite.LLVMType.Int(cn_bits):
if cn_bits > 1:
cn_zero: llvmlite.Value* = llvmlite.ConstInt(pool, cn_ret, 0, "0")
if cn_zero is not None:
cn_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cn_res, cn_zero)
case _:
pass
if op == ast.OpKind.NotIn:
i1_ty_ni: llvmlite.LLVMType* = llvmlite.Int1(pool)
if i1_ty_ni is not None and cn_i1 is not None:
one_i1_ni: llvmlite.Value* = llvmlite.ConstInt(pool, i1_ty_ni, 1, "1")
if one_i1_ni is not None:
cn_i1 = llvmlite.build_xor(builder, cn_i1, one_i1_ni)
if cn_i1 is not None:
return cn_i1
# 容器无 __contains__:TPC 语义恒 False(i1 0)
i1_ty_in: llvmlite.LLVMType* = llvmlite.Int1(pool)
if i1_ty_in is not None:
return llvmlite.ConstInt(pool, i1_ty_in, 0, "0")
return None
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
# None 比较跳过重载(走身份比较);指针判空(cmp_zero + 指针)同样跳过
if cmp_with_none == 0 and (cmp_zero == 0 or self.get_ptr_level(lhs.Ty) == 0):
ovl_result2: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, lhs, rhs, op, trans, 1)
if ovl_result2 is not None:
return ovl_result2
# === 无符号比较(A5):操作数为无符号整数类型(CUInt64T/CUInt32T 等)时
# 用 ULT/ULE/UGT/UGE。此前固定有符号 predicate → uint64 地址比较在
# 跨 2^63 边界时语义错误(paging MakeRangeRO/save_pt_range 的
# `while va < vaddr_end` 生成 icmp slt,A5 根因)。复用 _node_type_unsigned
# (Name→变量 AnnotClassName / Call→转换目标 / Attribute→类型名 / 递归)。
unsigned_l: int = 0
unsigned_r: int = 0
if lhs_node is not None:
unsigned_l = self._node_type_unsigned(pool, lhs_node, trans)
if rhs_node is not None:
unsigned_r = self._node_type_unsigned(pool, rhs_node, trans)
cmp_unsigned: int = 1 if (unsigned_l != 0 or unsigned_r != 0) else 0
predicate: int = llvmlite.ICMP_SLT
if op == ast.OpKind.Lt:
predicate = llvmlite.ICMP_ULT if cmp_unsigned != 0 else llvmlite.ICMP_SLT
elif op == ast.OpKind.Le:
predicate = llvmlite.ICMP_ULE if cmp_unsigned != 0 else llvmlite.ICMP_SLE
elif op == ast.OpKind.Gt:
predicate = llvmlite.ICMP_UGT if cmp_unsigned != 0 else llvmlite.ICMP_SGT
elif op == ast.OpKind.Ge:
predicate = llvmlite.ICMP_UGE if cmp_unsigned != 0 else llvmlite.ICMP_SGE
elif op == ast.OpKind.Eq:
predicate = llvmlite.ICMP_EQ
elif op == ast.OpKind.Ne:
predicate = llvmlite.ICMP_NE
elif op == ast.OpKind.Is:
# `x is y` → 指针/值相等比较
predicate = llvmlite.ICMP_EQ
elif op == ast.OpKind.IsNot:
# `x is not y` → 指针/值不等比较
predicate = llvmlite.ICMP_NE
# 类型安全检查:数值 vs 数值 → 允许(按位宽 sext 对齐);一级指针 vs 一级指针 → 允许(地址比较);数值 vs 指针(1+) → 禁止(fast-fail,除 None 比较外);不同级指针(1 vs 2 等)→ 禁止。None 比较跳过此检查(None 译为 null 指针或 0,与任意指针/数值比较合法)
if cmp_with_none == 0:
lhs_ptr_level: int = self.get_ptr_level(lhs.Ty)
rhs_ptr_level: int = self.get_ptr_level(rhs.Ty)
if lhs_ptr_level != rhs_ptr_level:
# 指针 vs 整数字面量 0(判空模式 ptr != 0 / ptr == 0):
# 0 视为 NULL,转成与指针侧同类型的 null 指针(icmp 需要同类型),
# 不报类型不匹配(ClassArrayTest 的 if ptr != 0 等常见模式)。
zero_is_rhs: int = 0
zero_is_lhs: int = 0
if rhs_ptr_level == 0 and self._is_zero_int_literal(rhs_node) != 0:
zero_is_rhs = 1
if lhs_ptr_level == 0 and self._is_zero_int_literal(lhs_node) != 0:
zero_is_lhs = 1
if zero_is_rhs != 0 or zero_is_lhs != 0:
if zero_is_rhs != 0:
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
else:
lhs = llvmlite.ConstNull(pool, rhs.Ty, "null")
else:
# 指针 vs 非零整数(C 哨兵值模式,如 w32.win32base.INVALID_HANDLE_VALUE = -1):
# 对齐 TPC HandlesExprOps.py L388-398 —— 指针 ptrtoint 成整数做地址值比较。
# (指针 vs 整数字面量 0 已在上方转 null 处理;此处处理非零整数哨兵)
if lhs_ptr_level != 0 and rhs_ptr_level == 0:
lhs = llvmlite.build_ptrtoint(builder, lhs, rhs.Ty)
elif rhs_ptr_level != 0 and lhs_ptr_level == 0:
rhs = llvmlite.build_ptrtoint(builder, rhs, lhs.Ty)
else:
# 不同级指针(1 vs 2 等)→ 禁止(fast-fail)
self.Gen.TypeGenErr.fatal_error(cmp, f"比较类型不匹配: 不同级指针比较 (lhs 层级={lhs_ptr_level}, rhs 层级={rhs_ptr_level})", "TypeError")
else:
# None 比较: None 被翻译为 i8* null(一级指针)
# 如果 lhs 是指针,将 rhs (i8* null) bitcast 为 lhs 的指针类型
# 如果 lhs 是数值,将 rhs 转为数值 0(走正常整数比较)
# 如果 lhs 是结构体值类型,结构体永远不等于 None,返回常量 i1
lhs_ptr_level_none: int = self.get_ptr_level(lhs.Ty)
rhs_ptr_level_none: int = self.get_ptr_level(rhs.Ty)
if lhs_ptr_level_none != 0 and rhs_ptr_level_none != 0:
# 两边都是指针,类型可能不同(如 lhs=Struct*, rhs=i8* null)
# 由下方 bitcast 对齐处理,此处不处理
pass
elif lhs_ptr_level_none != 0 and rhs_ptr_level_none == 0:
# lhs 是指针, rhs 是数值 → 将 rhs 转为同层级 null 指针
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
elif rhs_ptr_level_none != 0 and lhs_ptr_level_none == 0:
# 检查 lhs 是否是结构体值类型(非指针的 Struct)
lhs_is_struct_none: int = 0
match lhs.Ty:
case llvmlite.LLVMType.Struct(_, _, _):
lhs_is_struct_none = 1
case _:
lhs_is_struct_none = 0
if lhs_is_struct_none != 0:
# 结构体值永远不等于 None:==/is 返回 false,!=/is not 返回 true
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
return llvmlite.const_int(pool, 1, 0)
else: # Ne, IsNot
return llvmlite.const_int(pool, 1, 1)
# lhs 是数值, rhs 是 i8* null → 将 rhs 转为数值 0(保持 lhs 不变)
rhs = llvmlite.const_int32(pool, 0)
# 类型对齐:icmp 要求两边类型相同
# 仅对数值类型做对齐(指针类型无需对齐,LLVM 允许同层级指针直接 icmp)
# i1 (bool) 侧用 zext 对齐(1 保持 1,而非 sext 的 0xFF...)
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
if lhs_bits < rhs_bits:
if self.should_zext_widen(pool, lhs, rhs, cmp.left, trans) != 0:
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
else:
lhs = llvmlite.build_sext(builder, lhs, rhs.Ty)
else:
if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0:
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
else:
rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)
# 指针类型对齐:同层级但不同 pointee 类型的指针需要 bitcast 统一类型
# 例如 i8* vs Struct* → 将 rhs bitcast 为 lhs 的类型
if lhs_bits == 0 and rhs_bits == 0:
lhs_is_ptr_now: int = self.is_ptr_type(lhs.Ty)
rhs_is_ptr_now: int = self.is_ptr_type(rhs.Ty)
if lhs_is_ptr_now != 0 and rhs_is_ptr_now != 0:
# 两边都是指针,用 TypePrint 比较类型字符串
lhs_ty_buf: str = pool.alloc(128)
rhs_ty_buf: str = pool.alloc(128)
if lhs_ty_buf is not None and rhs_ty_buf is not None:
llvmlite.TypePrint(lhs_ty_buf, 128, lhs.Ty, pool)
llvmlite.TypePrint(rhs_ty_buf, 128, rhs.Ty, pool)
if string.strcmp(lhs_ty_buf, rhs_ty_buf) != 0:
rhs = llvmlite.build_bitcast(builder, rhs, lhs.Ty)
# 浮点比较:任一操作数为浮点类型时使用 fcmp 而非 icmp
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
if lhs_fbits != 0 or rhs_fbits != 0:
# 浮点类型对齐(fpext/fptrunc)
if lhs_fbits != 0 and rhs_fbits != 0 and lhs_fbits != rhs_fbits:
if lhs_fbits < rhs_fbits:
lhs = llvmlite.build_fpext(builder, lhs, rhs.Ty)
else:
rhs = llvmlite.build_fptrunc(builder, rhs, lhs.Ty)
elif lhs_fbits != 0 and rhs_fbits == 0:
# int → float
rhs = llvmlite.build_si2fp(builder, rhs, lhs.Ty)
elif rhs_fbits != 0 and lhs_fbits == 0:
lhs = llvmlite.build_si2fp(builder, lhs, rhs.Ty)
# 映射比较谓词到 fcmp
fpredicate: int = llvmlite.FCMP_OEQ
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
fpredicate = llvmlite.FCMP_OEQ
elif op == ast.OpKind.Ne or op == ast.OpKind.IsNot:
fpredicate = llvmlite.FCMP_ONE
elif op == ast.OpKind.Lt:
fpredicate = llvmlite.FCMP_OLT
elif op == ast.OpKind.Le:
fpredicate = llvmlite.FCMP_OLE
elif op == ast.OpKind.Gt:
fpredicate = llvmlite.FCMP_OGT
elif op == ast.OpKind.Ge:
fpredicate = llvmlite.FCMP_OGE
return llvmlite.build_fcmp(builder, fpredicate, lhs, rhs)
return llvmlite.build_icmp(builder, predicate, lhs, rhs)
# ============================================================
# 翻译一元运算表达式 UnaryOp(op, operand)
# ============================================================
def translate_unaryop(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译一元运算(-x, +x, not x, ~x)"""
uo: ast.UnaryOp* = ast.UnaryOp*(node)
if uo is None: return None
operand: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo.operand, None, 0, trans)
if operand is None: return None
# === 一元运算符重载: -obj/+obj/~obj 调用 __neg__/__pos__/__invert__ ===
# 仅 USub/UAdd/Invert 有重载语义(not 无 dunder 方法)
if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert:
ovl_u: llvmlite.Value* = self.try_unary_operator_overload(pool, builder, mod, operand, uo.op, trans)
if ovl_u is not None: return ovl_u
if uo.op == ast.OpKind.USub:
# -x
# 浮点类型用 fneg(直接生成 fneg 指令,避免 0.0 - x 的类型不匹配)
operand_fbits: int = self.get_llvm_float_bits(operand.Ty)
if operand_fbits > 0:
return llvmlite.build_fneg(builder, operand)
# 整数类型:-x = 0 - x,根据位宽选择零值常量(避免 i32 与 i64 类型不匹配)
operand_bits: int = self.get_llvm_type_bits(operand.Ty)
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
if operand_bits == 64:
zero = llvmlite.const_int64(pool, 0)
elif operand_bits > 0 and operand_bits != 32:
# 其他位宽(i8/i16 等):用 i32 零值,operand 会被 coerce
zero = llvmlite.const_int32(pool, 0)
return llvmlite.build_sub(builder, zero, operand)
elif uo.op == ast.OpKind.UAdd:
# +x = x
return operand
elif uo.op == ast.OpKind.Not:
# not x = (x == 0/null),返回 i1
if self.is_ptr_type(operand.Ty) != 0:
# 指针类型:与 null 比较
null_op: llvmlite.Value* = llvmlite.ConstNull(pool, operand.Ty, "null")
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, null_op)
# 结构体值类型:永远为真(非空),not 返回 false
operand_is_struct_not: int = 0
match operand.Ty:
case llvmlite.LLVMType.Struct(_, _, _):
operand_is_struct_not = 1
case _:
operand_is_struct_not = 0
if operand_is_struct_not != 0:
return llvmlite.const_int(pool, 1, 0)
operand_bits_not: int = self.get_llvm_type_bits(operand.Ty)
zero = llvmlite.const_int32(pool, 0)
if operand_bits_not == 64:
zero = llvmlite.const_int64(pool, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
elif uo.op == ast.OpKind.Invert:
# ~x = x ^ -1
operand_bits_inv: int = self.get_llvm_type_bits(operand.Ty)
neg1: llvmlite.Value* = llvmlite.const_int32(pool, -1)
if operand_bits_inv == 64:
neg1 = llvmlite.const_int64(pool, -1)
return llvmlite.build_xor(builder, operand, neg1)
elif uo.op == ast.OpKind.Deref:
# *p → load 按 pointee 类型读取(对齐 translate_c_deref)
if self.Gen.ExprGenBase.is_ptr_type(operand.Ty) != 0:
d_pointee: llvmlite.LLVMType* = operand.Ty.Pointee
if d_pointee is not None:
return llvmlite.build_load(builder, d_pointee, operand)
# 非指针即类型推断退化:fail-fast,绝不按 i8 兜底读取
self.Gen.TypeGenErr.fatal_error(node, "单目 '*' 解引用的操作数必须是指针类型(类型推断退化)", "TypeError")
return None
elif uo.op == ast.OpKind.AddrOf:
# &x → 左值地址(不 load):Name 变量 alloca / 模块全局 / 模块函数 /
# Attribute 字段 GEP / Subscript 元素地址(对齐 translate_c_addr 语义)
if trans is None:
return None
if uo.operand.kind() == ast.ASTKind.Name:
nm_ao: ast.Name* = ast.Name*(uo.operand)
if nm_ao.id is not None:
# self/cls 对象引用:返回对象指针本身(Ptr(Struct))而非
# 指针变量的地址(Ptr(Ptr(Struct)))
if string.strcmp(nm_ao.id, "self") == 0 or string.strcmp(nm_ao.id, "cls") == 0:
se_ao: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_ao.id)
if se_ao is not None and se_ao.Alloca is not None and se_ao.Alloca.Ty is not None:
match se_ao.Alloca.Ty:
case llvmlite.LLVMType.Ptr(sa1):
match sa1:
case llvmlite.LLVMType.Ptr(sa2):
obj_ty_sa: llvmlite.LLVMType* = llvmlite.Ptr(pool, sa2)
return llvmlite.build_load(builder, obj_ty_sa, se_ao.Alloca)
case _:
pass
case _:
pass
v_ao: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_ao.id)
if v_ao is None and mod is not None:
# 模块全局变量:经 make_global_ref 转成带引号的 Ptr(Ty) 引用
gv_ao: llvmlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(mod, nm_ao.id)
if gv_ao is not None and gv_ao.Name is not None and gv_ao.Ty is not None:
v_ao = self.Gen.ExprGenAttr.make_global_ref(pool, gv_ao.Name, gv_ao.Ty)
if v_ao is not None and v_ao.Ty is not None:
# 对象指针变量(alloca 是 Ptr(Ptr(Struct)),存对象指针):
# 变量语义即对象,返回对象指针(load 一次),对齐 translate_c_addr
match v_ao.Ty:
case llvmlite.LLVMType.Ptr(ao1):
match ao1:
case llvmlite.LLVMType.Ptr(ao2):
match ao2:
case llvmlite.LLVMType.Struct(_, _, _):
obj_ty_ao: llvmlite.LLVMType* = llvmlite.Ptr(pool, ao2)
return llvmlite.build_load(builder, obj_ty_ao, v_ao)
case _:
pass
case _:
pass
case _:
pass
return v_ao
# 模块函数地址:函数符号本身
if v_ao is None and mod is not None:
f_ao: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, nm_ao.id)
if f_ao is not None:
fname_ao: str = llvmlite.function_get_name(f_ao)
if fname_ao is not None:
# 形参链是 Param 链,而 llvmlite.Func 的类型构造链是 ParamNode 链
# (不相关类)→ 经 _FuncTyFromFunction 重建(同 CallGenCBuiltin)。
fn_ty_ao: llvmlite.LLVMType* = self.Gen.CallGenBase._FuncTyFromFunction(pool, f_ao)
# 函数引用名按需加引号(数字开头/含 '.' 的 sha1 前缀名)
fnlen_ao: sizet = string.strlen(fname_ao)
needs_q_ao: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(fname_ao)
fbuf_ao: str = pool.alloc(fnlen_ao + 4)
if fbuf_ao is not None and fn_ty_ao is not None:
if needs_q_ao != 0:
viperlib.snprintf(fbuf_ao, fnlen_ao + 4, "@\"%s\"", fname_ao)
else:
viperlib.snprintf(fbuf_ao, fnlen_ao + 4, "@%s", fname_ao)
return llvmlite.SSAValue(pool, llvmlite.Ptr(pool, fn_ty_ao), fbuf_ao)
elif uo.operand.kind() == ast.ASTKind.Attribute:
# 字段地址(GEP,不 load)
fptr_ao: llvmlite.Value* = self.Gen.ExprGenAttr.get_attribute_ptr(builder, pool, mod, uo.operand, trans)
if fptr_ao is not None:
return fptr_ao
elif uo.operand.kind() == ast.ASTKind.Subscript:
# 元素地址(GEP,不 load)
sptr_ao: llvmlite.Value* = self.Gen.ExprGenName.get_subscript_ptr(builder, pool, mod, uo.operand, trans)
if sptr_ao is not None:
return sptr_ao
elif uo.operand.kind() == ast.ASTKind.Call:
# &<call>() → rvalue 取址。复用上面已求值的 operand(绝不能二次求值:
# 构造调用会再跑一遍 __before_init__/__init__)。判定见 _addrof_of_call。
ac_ao: llvmlite.Value* = self._addrof_of_call(builder, pool, operand)
if ac_ao is not None:
return ac_ao
self.Gen.TypeGenErr.fatal_error(node, "单目 '&' 取地址的操作数必须是左值(Name/Attribute/Subscript)或构造调用/函数调用结果", "TypeError")
return None
return operand
def _addrof_of_call(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
call_val: llvmlite.Value*) -> llvmlite.Value*:
"""`&<call>()` 的取址结果(call_val 是调用结果,已由 translate_unaryop 求值一次)
两种形态:
· 结构体/类构造调用(`&ViperKernel()`):CallGenCtor._translate_struct_ctor
末尾明写"返回指针(而非结构体值)"——结果**就是**入口 alloca 的栈存储
指针 Ptr(Struct),即"栈构造结构体的地址"本身,直接原样返回。
不能在这里再套 alloca+store:那会得到 Ptr(Ptr(Struct))(存放地址的槽
地址),赋值端还得 load 一次才回落,语义也从"对象地址"变成"槽地址"。
· 其它返回值(标量、按值返回的结构体值等):rvalue 的地址只能靠临时槽
承载——入口 alloca 同类型槽 → store → 返回槽地址(与 c.Addr(expr) 的
Call 形式同口径,见 CallGenCBuiltin.translate_c_addr)。
"""
if call_val is None or call_val.Ty is None:
return None
if self.Gen.ExprGenBase.is_ptr_type(call_val.Ty) != 0 and call_val.Ty.Pointee is not None:
pointee_is_struct: int = 0
match call_val.Ty.Pointee:
case llvmlite.LLVMType.Struct(_, _, _):
pointee_is_struct = 1
case _:
pointee_is_struct = 0
if pointee_is_struct != 0:
return call_val
tmp_slot_ao: llvmlite.Value* = llvmlite.build_alloca_at_entry(builder, call_val.Ty)
if tmp_slot_ao is None:
return None
llvmlite.build_store(builder, call_val, tmp_slot_ao)
return tmp_slot_ao
# ============================================================
# 翻译布尔运算 BoolOp(op, values) — 短路求值,返回 i1
#
# and: a and b and c → 若 a 为假短路到 merge(返回 a 的 i1),否则求 b,
# 最后一个值直接求值并跳 merge
# or: a or b or c → 若 a 为真短路到 merge(返回 a 的 i1),否则求 b,
# 最后一个值直接求值并跳 merge
# merge 块用 phi 合并所有入边的 i1
# ============================================================
def translate_boolop(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译布尔运算(and/or)的短路求值,返回 i1"""
bo: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
if bo is None:
return None
op: int = bo.op
values: list[ast.AST*]* = bo.values
if values is None:
return None
count: sizet = len(values)
if count == 0:
return None
if count == 1:
return self.Gen.ExprGen.translate_value(builder, pool, mod, values.get(0),
funcs_ptr, func_count, trans)
func: llvmlite.Function* = builder.Func
if func is None:
return None
# 创建 merge BB(用 builder.Counter 生成唯一标签名,position_at_end 的
# move_to_end 保证 BB 文本顺序与控制流顺序一致,标签名用 counter 安全)
cnt: int = builder.Counter
builder.Counter = cnt + 1
name_buf: str = pool.alloc(32)
if name_buf is None:
return None
viperlib.snprintf(name_buf, 32, "bool.merge.%d", cnt)
merge_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
# phi 入边链表头/尾
phi_head: llvmlite.PhiIncoming* = None
phi_tail: llvmlite.PhiIncoming* = None
phi_count: int = count - 1 # 循环次数
# 对每个值(除最后一个)求值并短路
for i in range(count - 1):
val_node: ast.AST* = values.get(i)
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, val_node, funcs_ptr, func_count, trans)
if val is None:
return None
# 转为 i1(已经是 i1 的直接用,否则与 0/null 比较)
val_bits: int = self.get_llvm_type_bits(val.Ty)
val_i1: llvmlite.Value* = val
if val_bits != 1:
if self.is_ptr_type(val.Ty) != 0:
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
null_val: llvmlite.Value* = llvmlite.ConstNull(pool, val.Ty, "null")
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, null_val)
else:
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)
# 创建 next BB(求值下一个值)
cnt = builder.Counter
builder.Counter = cnt + 1
viperlib.snprintf(name_buf, 32, "bool.next.%d", cnt)
next_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
# and: 短路假→merge,真→next;or: 短路真→merge,假→next
if op == ast.OpKind.And:
llvmlite.build_cond_br(builder, val_i1, next_bb, merge_bb)
else:
llvmlite.build_cond_br(builder, val_i1, merge_bb, next_bb)
# 添加 phi 入边:(val_i1, cond_br 所在 BB)
cur_bb: rawptr[llvmlite.BasicBlock] = builder.CurBlock
inc: llvmlite.PhiIncoming* = llvmlite.new_phi_incoming(pool, val_i1, cur_bb)
if phi_head is None:
phi_head = inc
phi_tail = inc
else:
phi_tail.Next = inc
phi_tail = inc
# 定位到 next BB,求值下一个值
llvmlite.position_at_end(builder, next_bb)
# 最后一个值:求值后直接跳 merge
last_node: ast.AST* = values.get(count - 1)
last_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, last_node, funcs_ptr, func_count, trans)
if last_val is None:
return None
last_bits: int = self.get_llvm_type_bits(last_val.Ty)
last_i1: llvmlite.Value* = last_val
if last_bits != 1:
if self.is_ptr_type(last_val.Ty) != 0:
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
null_val2: llvmlite.Value* = llvmlite.ConstNull(pool, last_val.Ty, "null")
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, null_val2)
else:
zero = llvmlite.const_int32(pool, 0)
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, zero)
llvmlite.build_br(builder, merge_bb)
cur_bb = builder.CurBlock
inc = llvmlite.new_phi_incoming(pool, last_i1, cur_bb)
if phi_head is None:
phi_head = inc
else:
phi_tail.Next = inc
phi_count += 1
# 定位到 merge,创建 phi 合并所有入边
llvmlite.position_at_end(builder, merge_bb)
i1_ty: llvmlite.LLVMType* = llvmlite.Int1(pool)
return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)
# ============================================================
# BinOp 运算符 → dunder 方法名
# ============================================================
def _binop_to_dunder(self, op: int) -> str:
"""将 BinOp 运算符映射到 dunder 方法名,无映射时返回 None
Python 3 语义:/ 映射 __truediv__(Python 2 的 __div__ 已废弃)。
"""
if op == ast.OpKind.Add: return "__add__"
if op == ast.OpKind.Sub: return "__sub__"
if op == ast.OpKind.Mult: return "__mul__"
if op == ast.OpKind.Div: return "__truediv__"
if op == ast.OpKind.Mod: return "__mod__"
if op == ast.OpKind.BitAnd: return "__and__"
if op == ast.OpKind.BitOr: return "__or__"
if op == ast.OpKind.BitXor: return "__xor__"
if op == ast.OpKind.LShift: return "__lshift__"
if op == ast.OpKind.RShift: return "__rshift__"
if op == ast.OpKind.FloorDiv: return "__floordiv__"
return None
# ============================================================
# Compare 运算符 → dunder 方法名
# ============================================================
def _cmpop_to_dunder(self, op: int) -> str:
"""将 Compare 运算符映射到 dunder 方法名,无映射时返回 None"""
if op == ast.OpKind.Eq: return "__eq__"
if op == ast.OpKind.Ne: return "__ne__"
if op == ast.OpKind.Lt: return "__lt__"
if op == ast.OpKind.Le: return "__le__"
if op == ast.OpKind.Gt: return "__gt__"
if op == ast.OpKind.Ge: return "__ge__"
return None
# ============================================================
# UnaryOp 运算符 → dunder 方法名
# ============================================================
def _unaryop_to_dunder(self, op: int) -> str:
"""将 UnaryOp 运算符映射到 dunder 方法名,无映射时返回 None"""
if op == ast.OpKind.USub: return "__neg__"
if op == ast.OpKind.UAdd: return "__pos__"
if op == ast.OpKind.Invert: return "__invert__"
return None
# ============================================================
# try_operator_overload - 尝试运算符重载
#
# 检查 lhs 是否为结构体指针,并在该类(含继承链)中查找对应的
# dunder 方法。找到则生成方法调用 lhs.__dunder__(rhs),返回
# 调用结果;未找到返回 None,调用方回退到原生运算。
# ============================================================
def try_operator_overload(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
lhs: llvmlite.Value*,
rhs: llvmlite.Value*,
op: int,
trans: HT.Translator*,
is_compare: int) -> llvmlite.Value*:
"""尝试运算符重载:lhs 是结构体指针时调用 dunder 方法,否则返回 None"""
if lhs is None or rhs is None:
return None
# 映射运算符到 dunder 方法名
dunder: str = None
if is_compare != 0:
dunder = self._cmpop_to_dunder(op)
else:
dunder = self._binop_to_dunder(op)
if dunder is None:
return None
# 检查 lhs 是否为结构体指针
struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lhs)
if struct_ty is None:
return None
# 获取类名
class_name: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty)
if class_name is None:
return None
# 在继承链中查找 dunder 方法
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
found_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, lookup_name)
search_class: str = class_name
if found_func is None:
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = rawptr(mro_ptr[mi])
if anc is not None:
parent_lookup: str = pool.alloc(128)
if parent_lookup is not None:
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
parent_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, parent_lookup)
if parent_func is not None:
found_func = parent_func
search_class = anc
break
mi += 1
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
# 优先用类型指针定位 entry 获取 SHA1(规避跨模块同名 find_struct 找错)
cls_sha1: str = None
op_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
if op_entry is not None:
cls_sha1 = op_entry.ModuleSha1
if cls_sha1 is None:
cls_sha1 = self.Gen.StructGen.get_struct_sha1(search_class)
if cls_sha1 is None:
mro_p: sizet* = self.Gen.StructGen.get_mro_chain(pool, search_class)
mj: int = 1
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
anc_p: str = rawptr(mro_p[mj])
if anc_p is not None:
cls_sha1 = self.Gen.StructGen.get_struct_sha1(anc_p)
mj += 1
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
if found_func is None and cls_sha1 is None: return None
# 方法在"定义它的模块"中确实不存在(同模块类但模块内无此 dunder):
# 返回 None 回退原生运算,绝不发出幽灵 call——否则 IR 残留
# `call void @...__div__`(副作用)且调用方回退原生 sdiv 双重发射,
# llc 报 invalid operand type for instruction(CPythonTest PyFloatObj 根因)
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0: return None
# 跨模块类:stub 可能未注入,方法存在性以依赖模块 vpi stub 为准。
# vpi 未命中(依赖模块确实无此 dunder)→ 回退原生比较(icmp),
# 绝不发幽灵 call——`call @{cls_sha1}.{Class}.{dunder}` 链接 undefined
# (MemBuddy 无 __eq__ 时 os._mbuddy == sys._mbuddy,SysLibTest 根因)。
vpi_dup: llvmlite.LLVMType* = self.Gen.CallGenBase._lookup_method_ret_ty_via_vpi(pool, cls_sha1, search_class, dunder)
if vpi_dup is None: return None
# 构建 extra_args 数组(仅含 rhs 一个参数)
extra_args: sizet* = pool.alloc(8)
if extra_args is None: return None
extra_args[0] = sizet(rhs)
# 调用方法: lhs.__dunder__(rhs)
return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, search_class, dunder, lhs, extra_args, 1, trans)
# ============================================================
# try_unary_operator_overload - 尝试一元运算符重载
#
# 检查 operand 是否为结构体指针,并在该类(含继承链)中查找对应的
# dunder 方法(__neg__/__pos__/__invert__)。找到则生成方法调用
# operand.__dunder__(),返回调用结果;未找到返回 None,调用方回退到原生运算。
# ============================================================
def try_unary_operator_overload(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
operand: llvmlite.Value*,
op: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""尝试一元运算符重载:operand 是结构体指针时调用 dunder 方法,否则返回 None"""
if operand is None:
return None
dunder: str = self._unaryop_to_dunder(op)
if dunder is None:
return None
# 检查 operand 是否为结构体指针
struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(operand)
if struct_ty is None:
return None
# 获取类名
class_name: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty)
if class_name is None:
return None
# 在继承链中查找 dunder 方法
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
found_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, lookup_name)
search_class: str = class_name
if found_func is None:
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = rawptr(mro_ptr[mi])
if anc is not None:
parent_lookup: str = pool.alloc(128)
if parent_lookup is not None:
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
parent_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, parent_lookup)
if parent_func is not None:
found_func = parent_func
search_class = anc
break
mi += 1
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
cls_sha1: str = None
uop_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
if uop_entry is not None:
cls_sha1 = uop_entry.ModuleSha1
if cls_sha1 is None:
cls_sha1 = self.Gen.StructGen.get_struct_sha1(search_class)
if cls_sha1 is None:
mro_p: sizet* = self.Gen.StructGen.get_mro_chain(pool, search_class)
mj: int = 1
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
anc_p: str = rawptr(mro_p[mj])
if anc_p is not None:
cls_sha1 = self.Gen.StructGen.get_struct_sha1(anc_p)
mj += 1
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
if found_func is None and cls_sha1 is None:
return None
# 同模块类但模块内无此 dunder → 返回 None 回退原生运算,不发幽灵 call
# (与 try_operator_overload 一致,防 IR 残留 call void @...__neg__)
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0:
return None
# 一元方法无额外参数(extra_args 为空)
return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, search_class, dunder, operand, None, 0, trans)
# ============================================================
# fail_if_double_ptr_binop - 双指针算术 fail-fast(**唯一实现**)
#
# BinOp(translate_binop)与 AugAssign(AssignGenMixin._apply_aug_op)共用此
# 判据与报文,避免两处各写一份导致分叉(此前只有 translate_binop 有一份,
# 且只覆盖 Add/Sub,AugAssign 与 Mult/Div/Mod/Shift 均未受保护)。
#
# 判据:运算符属二元算术/位运算/移位集合 **且** 两操作数都是 LLVM 指针。
# 本语言的 str 是 i8*,没有 `+` 拼接语义;指针 ± 指针也不是 GEP 语义
# (GEP 只接受「一指针一整数」,由调用方在此之前用 translate_ptr_arith 分流)。
# 落到 build_add/build_sub/… 会直出非法 IR(`add i8*, i8*` ⇒ opt 报
# invalid operand type for instruction),故在 IR 生成前 fail-fast。
#
# is_aug != 0 表示增量赋值(+=、-=、…):报文把符号补成 "+=" 形态并标注
# 「(增量赋值)」,避免只报裸 "+",便于定位。
# 命中即 fatal_error 终止编译;未命中返回 0。
# ============================================================
def fail_if_double_ptr_binop(self, pool: memhub.MemBuddy*,
op: int,
lhs: llvmlite.Value*,
rhs: llvmlite.Value*,
node: ast.AST* = None,
is_aug: int = 0) -> int:
"""双指针算术 fail-fast;命中即终止编译,未命中返回 0"""
if lhs is None or rhs is None:
return 0
if self.is_ptr_type(lhs.Ty) == 0 or self.is_ptr_type(rhs.Ty) == 0:
return 0
op_ch: str = None
if op == ast.OpKind.Add: op_ch = "+"
elif op == ast.OpKind.Sub: op_ch = "-"
elif op == ast.OpKind.Mult: op_ch = "*"
elif op == ast.OpKind.Div: op_ch = "/"
elif op == ast.OpKind.FloorDiv: op_ch = "//"
elif op == ast.OpKind.Mod: op_ch = "%"
elif op == ast.OpKind.Pow: op_ch = "**"
elif op == ast.OpKind.BitAnd: op_ch = "&"
elif op == ast.OpKind.BitOr: op_ch = "|"
elif op == ast.OpKind.BitXor: op_ch = "^"
elif op == ast.OpKind.LShift: op_ch = "<<"
elif op == ast.OpKind.RShift: op_ch = ">>"
else:
return 0
op_lbl: str = op_ch
aug_note: str = ""
if is_aug != 0:
# 增量赋值:用 f-string 拼 "+=" 形态(本语言 str 无 `+` 拼接语义,
# 禁止 op_ch + "=" 这种裸字符串加法)
op_lbl = f"{op_ch}="
aug_note = "(增量赋值)"
lty_buf: str = pool.alloc(64)
rty_buf: str = pool.alloc(64)
lty_lbl: str = "?"
rty_lbl: str = "?"
if lty_buf is not None:
llvmlite.TypePrint(lty_buf, 64, lhs.Ty, pool)
lty_lbl = lty_buf
if string.strcmp(lty_buf, "i8*") == 0:
lty_lbl = "str(i8*)"
if rty_buf is not None:
llvmlite.TypePrint(rty_buf, 64, rhs.Ty, pool)
rty_lbl = rty_buf
if string.strcmp(rty_buf, "i8*") == 0:
rty_lbl = "str(i8*)"
col_s: int = 0
if node is not None:
col_s = node.col_offset + 1
self.Gen.TypeGenErr.fatal_error(
node,
f"[TPV] 双指针运算类型非法: op = '{op_lbl}'{aug_note},"
f"左操作数类型 = {lty_lbl},右操作数类型 = {rty_lbl}。"
f"本语言的 str 是 i8*,没有 `+` 拼接语义(`str + str` 与指针 ± 指针"
f"都会退化成裸指针 add/sub ⇒ 非法 IR: opt 报 "
f"invalid operand type for instruction)。"
f"禁止静默降级,已终止编译。"
f"修法: 用 f-string 插值、string.strcat 或 viperlib.snprintf 构建字符串。",
"TypeError", col_s, col_s)
return 1
# ============================================================
# 翻译二元运算(自动类型提升)
# ============================================================
def translate_binop(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译二元运算(自动类型提升)"""
binop: ast.BinOp* = ast.BinOp*(node)
if binop is None:
return None
lhs_node: ast.AST* = binop.left
rhs_node: ast.AST* = binop.right
op: int = binop.op
# 先翻译 rhs(只翻译一次,避免副作用重复)
rhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rhs_node, None, 0, trans)
if rhs is None:
return None
# === 运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter),translate_value 会 load 返回 Struct 值,
# 但 dunder 方法需要 Ptr(Struct) 作为 self。直接用 alloca 指针尝试重载。
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name* = ast.Name*(lhs_node)
if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if lhs_alloca is not None:
ovl_self1: llvmlite.Value* = lhs_alloca
# alloca 是 Ptr(Ptr(Struct))(X|t.CPtr 对象存储槽)时,
# load 出对象指针作为 self——若把槽地址当 self,dunder 内
# load 槽内容(对象指针值)当字段读 → 垃圾(MultiInheritTest
# Vec2Both __add__ 输出 (1.0,2.0) 根因;get_struct_type_from_value
# 递归解指针后路径 1 能识别 alloca,需同步修正 self 语义)
if lhs_alloca.Ty is not None and lhs_alloca.Ty.Pointee != 0 and self.Gen.ExprGenBase.is_ptr_type(lhs_alloca.Ty.Pointee) != 0:
load_self1: llvmlite.Value* = llvmlite.build_load(builder, lhs_alloca.Ty.Pointee, lhs_alloca)
if load_self1 is not None:
ovl_self1 = load_self1
ovl_result: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, ovl_self1, rhs, op, trans, 0)
if ovl_result is not None:
return ovl_result
# 正常翻译 lhs
lhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, lhs_node, None, 0, trans)
if lhs is None:
return None
# === 运算符重载路径 2: lhs 是 Ptr(Struct)(如 Counter|t.CPtr 变量 load 后)===
ovl_result2: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, lhs, rhs, op, trans, 0)
if ovl_result2 is not None:
return ovl_result2
# 获取操作数类型信息
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
# 浮点运算: 任一操作数为浮点时,使用浮点指令(必须在指针算术之前检查,
# 因为 float 的 int bits 为 0,会被误认为指针)
if lhs_fbits != 0 or rhs_fbits != 0:
# 确定目标浮点类型(使用较大的位宽)
target_fbits: int = lhs_fbits
if rhs_fbits > target_fbits:
target_fbits = rhs_fbits
target_float_ty: llvmlite.LLVMType* = llvmlite.Double(pool)
if target_fbits == 32:
target_float_ty = llvmlite.Float(pool)
# 将两个操作数转换为目标浮点类型(int→float via si2fp, float→float via fpext/fptrunc)
lhs = self.coerce_to_type(builder, lhs, target_float_ty, node)
rhs = self.coerce_to_type(builder, rhs, target_float_ty, node)
if op == ast.OpKind.Add:
return llvmlite.build_fadd(builder, lhs, rhs)
if op == ast.OpKind.Sub:
return llvmlite.build_fsub(builder, lhs, rhs)
if op == ast.OpKind.Mult:
return llvmlite.build_fmul(builder, lhs, rhs)
if op == ast.OpKind.Div:
return llvmlite.build_fdiv(builder, lhs, rhs)
if op == ast.OpKind.Mod:
return llvmlite.build_frem(builder, lhs, rhs)
return None
# 指针算术: ptr + int / ptr - int / int + ptr → getelementptr(按元素大小缩放)
# 对齐 TPC ExprGen.emit_binary_op L50-67;int - ptr 无 GEP 形式(C 语义非法)
if (lhs_bits == 0 and rhs_bits != 0) or (lhs_bits != 0 and rhs_bits == 0):
if op == ast.OpKind.Add:
if lhs_bits == 0:
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
# int + ptr(交换)
return self.translate_ptr_arith(pool, builder, rhs, lhs, op)
if op == ast.OpKind.Sub and lhs_bits == 0:
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
# 双指针算术(含 AugAssign)fail-fast:判据/报文唯一实现见
# fail_if_double_ptr_binop(本文件;BinOp 与 AssignGenMixin._apply_aug_op 共用)。
# 指针 ± 整数(GEP 语义)已在上方 translate_ptr_arith 分流;此处的双指针
# Add/Sub/Mult/Div/Mod/Shift/位运算在本语言非法(str 是 i8*,无 `+` 拼接语义),
# 落到 build_add/build_sub/… 会直出非法 IR(`add i8*, i8*`)。
self.fail_if_double_ptr_binop(pool, op, lhs, rhs, node, 0)
# 拓宽方向(zext/sext)与后续除/模谓词都需要"操作数是否无符号",
# 统一在此求值一次并下沉 trans(Name/Subscript 靠 SymTab 注解判定)。
binop_unsigned: int = 0
if lhs_node is not None:
if self._node_type_unsigned(pool, lhs_node, trans) != 0:
binop_unsigned = 1
if binop_unsigned == 0 and rhs_node is not None:
if self._node_type_unsigned(pool, rhs_node, trans) != 0:
binop_unsigned = 1
if lhs_bits > rhs_bits:
if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0:
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
else:
rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
elif rhs_bits > lhs_bits:
if self.should_zext_widen(pool, lhs, rhs, lhs_node, trans) != 0:
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
else:
lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
# 位运算类型安全检查:两边都是指针(lhs_bits==0 and rhs_bits==0)时,
# 位运算(BitOr/BitAnd/BitXor/LShift/RShift)非法,LLVM 要求整数操作数。
# 常见触发场景:CDefine 常量查找失败返回指针 null,与另一指针做 `|`。
if lhs_bits == 0 and rhs_bits == 0:
if op == ast.OpKind.BitOr or op == ast.OpKind.BitAnd or op == ast.OpKind.BitXor or op == ast.OpKind.LShift or op == ast.OpKind.RShift:
# fail-fast:位运算操作数类型非法,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(node, "位运算操作数类型非法: lhs/rhs 均为非整数类型(指针或结构体),无法执行位运算", "TypeError")
if op == ast.OpKind.Add: return llvmlite.build_add(builder, lhs, rhs)
elif op == ast.OpKind.Sub: return llvmlite.build_sub(builder, lhs, rhs)
elif op == ast.OpKind.Mult: return llvmlite.build_mul(builder, lhs, rhs)
elif op == ast.OpKind.Div:
# 无符号操作数 → udiv(binop_unsigned 经 _node_type_unsigned 判定,
# 覆盖 sizet/uint64 及变量/数组元素的注解无符号性)。
# 此前一律 sdiv → 无符号值 ≥ 2^63 时结果错误(viperlib %lu/%lx、
# fat32_mkfs 扇区运算等根因)。
if binop_unsigned != 0:
return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
if binop_unsigned != 0:
return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
if binop_unsigned != 0:
return llvmlite.build_urem(builder, lhs, rhs)
return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.BitAnd: return llvmlite.build_and(builder, lhs, rhs)
elif op == ast.OpKind.BitOr: return llvmlite.build_or(builder, lhs, rhs)
elif op == ast.OpKind.BitXor: return llvmlite.build_xor(builder, lhs, rhs)
elif op == ast.OpKind.LShift: return llvmlite.build_shl(builder, lhs, rhs)
elif op == ast.OpKind.RShift:
# 无符号 >> → lshr(逻辑右移),有符号 >> → ashr(算术右移)
# 对齐 TPC ExprGen.emit_binary_op:i8/i16 默认无符号;其余查操作数无符号性
if lhs_bits == 8 or lhs_bits == 16:
return llvmlite.build_lshr(builder, lhs, rhs)
if self._node_type_unsigned(pool, lhs_node, trans) != 0:
return llvmlite.build_lshr(builder, lhs, rhs)
return llvmlite.build_ashr(builder, lhs, rhs)
return None