Files
TransPyC/lib/core/Handles/HandlesAugAssign.py
T
TermiNexus f19d25782f refactor(tpc): 退役 t/c 模块并将定宽类型升级为内建
删除 lib/includes/t.py、c.py,类型模型迁入 lib/core/TypeSystem.py;移除 IsTModule/_T_MODULE_NAMES 等名称判据,语法糖与内部标记统一为裸名;builtins 的 sizet/intN/uintN/fast/least/uchar/short/long 等改为编译器内建短名。
2026-10-18 13:00:58 +08:00

300 lines
19 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
import vast as ast
import sys
import llvmlite.ir as ir
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.VLogger import get_logger as _vlog
from lib.core.Handles.HandlesBase import BaseHandle
class AugAssignHandle(BaseHandle):
_AUGOP_MAP: dict[type, str] = {
ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__',
ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__',
ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__',
ast.LShift: '__ilshift__', ast.RShift: '__irshift__',
}
_AUGOP_FALLBACK: dict[type, str] = {
ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__',
ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__',
ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__',
ast.LShift: '__lshift__', ast.RShift: '__rshift__',
}
def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type:
"""解析 global 变量的 LLVM 类型:优先用模块级 AnnAssign 的注解类型。
问题场景:函数内 `_g_alloc_count += 1`(global 声明),模块级注解为
`_g_alloc_count: CSizeT`(i64)。若不查模块级注解类型,此处会用 RHS 字面量
类型(i32)自动建 i32 全局,与模块内其他读取/写入(i64)类型不匹配,
产生 "Operands must be the same type, got (i64, i32)" 类错误。
模块级 AnnAssign 已通过 _EmitGlobalAnnAssignLlvm 创建 LLVM 全局,
但注解无初始值(如 `_g_alloc_max: CSizeT = 0`)时 initializer 为空、
Gen.module.globals 中无该符号,故需在此兜底解析注解类型。
"""
module_ann: dict = getattr(Gen, '_module_ann_types', None)
if module_ann and VarName in module_ann:
ann_type: ir.Type = module_ann[VarName]
if ann_type is not None:
return ann_type
if isinstance(fallback, ir.IntType):
return fallback
return ir.IntType(64)
def _is_aug_unsigned(self, Node: ast.AugAssign, OldVal: ir.Value | None, Value: ir.Value) -> bool:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Node.target, ast.Name):
if Gen._check_node_unsigned(Node.target):
return True
elif isinstance(Node.target, ast.Attribute):
if Gen._check_node_unsigned(Node.target):
return True
elif isinstance(Node.target, ast.Subscript):
if Gen._check_node_unsigned(Node.target):
return True
if Node.value is not None and Gen._check_node_unsigned(Node.value):
return True
return False
def _HandleAugAssignLlvm(self, Node: ast.AugAssign) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
if (VarName not in Gen.variables
and VarName not in Gen.global_vars
and VarName not in Gen._reg_values
and VarName not in Gen._direct_values):
src_info: str = Gen._get_node_info()
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
sys.exit(1)
if Gen._IsVarConst(VarName):
_vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
sys.exit(1)
ClassName: str | None = Gen.var_struct_class.get(VarName)
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
if ClassName and iop_name:
iFuncName: str = f'{ClassName}.{iop_name}'
FuncName: str = f'{ClassName}.{self._AUGOP_FALLBACK.get(type(Node.op), "")}'
if iFuncName in Gen.functions or FuncName in Gen.functions:
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load()))
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
if OldVal and Value:
result: ir.Value | None
if iFuncName in Gen.functions:
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, iop_name, OldVal, Gen, Value)
else:
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, self._AUGOP_FALLBACK[type(Node.op)], OldVal, Gen, Value)
if result is not None:
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarPtr: ir.Value = Gen.variables[VarName]
if VarPtr.type.pointee == result.type:
Gen._store(result, VarPtr)
elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(result.type, ir.PointerType):
CastedVal: ir.Value = Gen.builder.bitcast(result, VarPtr.type.pointee, name=f"cast_{VarName}")
Gen._store(CastedVal, VarPtr)
else:
NewVar: ir.AllocaInstr = Gen._allocaEntry(result.type, name=VarName)
Gen._store(result, NewVar)
Gen.variables[VarName] = NewVar
else:
Gen._reg_values[VarName] = result
Gen.variables[VarName] = None
return
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
if not Value:
return
is_unsigned: bool = self._is_aug_unsigned(Node, None, Value)
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if VarName in Gen.global_vars:
# 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局
# (注解无初始值的全局变量),此处自动创建零初始化全局变量
GVar: ir.GlobalVariable
if VarName not in Gen.module.globals:
_gv_type: ir.Type = self._resolve_global_var_type(Gen, VarName, Value.type)
GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName)
GVar.initializer = ir.Constant(_gv_type, 0)
else:
GVar = Gen.module.globals[VarName]
Gen.variables[VarName] = GVar
OldVal: ir.Value = Gen._load(GVar, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, GVar)
return
if VarName in Gen._reg_values:
OldVal: ir.Value = Gen._reg_values[VarName]
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
del Gen._reg_values[VarName]
if VarName in Gen._direct_values:
OldVal: ir.Value = Gen._direct_values.pop(VarName)
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
if Gen.builder.block.is_terminated:
return
if VarName in Gen.variables and Gen.variables[VarName] is not None:
OldVal: ir.Value = Gen._load(Gen.variables[VarName], name=VarName)
store_ptr: ir.Value = Gen.variables[VarName]
if isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Value.type.width != 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset")
NewVal: ir.Value
if Op == '-' or Op == '+':
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add")
else:
ptr_val: ir.Value = OldVal
OldVal = Gen._load(OldVal, name="deref_ptr_for_op")
orig_type: ir.Type = OldVal.type
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
if NewVal.type.width > orig_type.width:
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
store_ptr = ptr_val
else:
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Op in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_aug")
NewVal: ir.Value
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_aug")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_aug")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
Gen._store(NewVal, store_ptr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(store_ptr.type, ir.PointerType):
TargetType: ir.Type = store_ptr.type.pointee
if NewVal.type != TargetType:
Coerced: ir.Value | None = Gen._coerce_value(NewVal, TargetType)
if Coerced is not None:
NewVal = Coerced
Gen._store(NewVal, store_ptr)
elif VarName == 'pos':
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen.variables[VarName] = var
OldVal: ir.Value = Gen._load(var, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, var)
elif isinstance(Node.target, ast.Attribute):
AttrName: str = Node.target.attr
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.target)
if OldVal:
def _deref_if_ptr(val: ir.Value) -> ir.Value:
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, (ir.IntType, ir.FloatType, ir.DoubleType)):
return Gen._load(val, name="deref_aug")
return val
OldVal = _deref_if_ptr(OldVal)
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
elif isinstance(Node.target, ast.Subscript):
SubTarget: ast.Subscript = Node.target
ElemPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(SubTarget)
if ElemPtr:
if isinstance(ElemPtr.type, ir.PointerType):
OldVal: ir.Value = Gen._load(ElemPtr, name="old_subscript_val")
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_sub")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_sub")
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Op in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_sub")
NewVal: ir.Value
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_sub")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_sub")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
Gen._store(NewVal, ElemPtr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
if NewVal.type.width > ElemPtr.type.pointee.width:
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
Gen._store(NewVal, ElemPtr)
elif isinstance(Node.target, ast.UnaryOp):
# 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref)
# (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。
# 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
if not isinstance(Node.target.op, ast.Deref):
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
f"{Gen._get_node_info()}"
)
DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand)
if DerefPtr is None:
raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}")
if not isinstance(DerefPtr.type, ir.PointerType):
raise Exception(
f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})"
f"{Gen._get_node_info()}"
)
DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val")
if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if DerefOld.type.width < Value.type.width:
DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref")
elif DerefOld.type.width > Value.type.width:
Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref")
elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if DerefOp in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref")
OffVal: ir.Value
if DerefOp == '-':
OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref")
else:
OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref")
Gen._store(OffVal, DerefPtr)
return
DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType):
if DerefNew.type.width > DerefPtr.type.pointee.width:
DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result")
Gen._store(DerefNew, DerefPtr)
else:
# fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 ViperC
# translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、
# 变量静默不变"。
TargetOp: str | None = None
if isinstance(Node.target, ast.UnaryOp):
TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用"
f"(当前 {type(Node.target).__name__}"
+ (f" op='{TargetOp}'" if TargetOp else "")
+ f"){Gen._get_node_info()}"
)