_ctor_call_agg_ty 从被调方沿属性链一路走到链根裸名,三段式 llvmlite.LLVMType.Struct(...) 于是被判成限定名 'llvmlite'(非 REnum 类)⇒ 返回 None ⇒ ClassGenDef.vp:128 的 *existing.Ty = LLVMType.Struct(...) 被 「指针 → 非基本类型值」禁令拦下。改为取「变体名左边那一段」:两段式取裸名 (EnumType.Variant)、三段式取属性名(llvmlite.LLVMType.Struct → LLVMType)。
1002 lines
59 KiB
Plaintext
1002 lines
59 KiB
Plaintext
import lib.core.Gen.Generator as Generator
|
||
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import llvmlite
|
||
import memhub
|
||
import string
|
||
import stdlib
|
||
import viperlib
|
||
import lib.core.Gen.Translator as HT
|
||
import lib.Projectrans.Utils as Utils
|
||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||
import lib.core.Gen.GenApi as GenApi
|
||
import lib.core.Gen.GenTypes as GenTypes
|
||
|
||
|
||
# ============================================================
|
||
# 指针间隐式转换判定码(coerce_to_type 的「指针 → 指针」分支使用)
|
||
#
|
||
# 由 _ptr_cast_reason 统一给出,供报错说明「哪一级不允许 + 为什么」:
|
||
# PC_OK 允许(含规则 1 通用指针通配、规则 4 完全相同不视为转换)
|
||
# PC_DEPTH 指针层数不同(禁)
|
||
# PC_NONBASIC 被指类型是非基本类型(结构体/类/联合/数组等):非基本类型与其
|
||
# 指针之间、以及不同非基本类型的指针之间一律不允许(规则 2/3)
|
||
# PC_LOSSY 被指类型之间转换有损(整数→浮点位宽不足 / 浮点→整数)(规则 2)
|
||
# ============================================================
|
||
define PC_OK = 0
|
||
define PC_DEPTH = 1
|
||
define PC_NONBASIC = 2
|
||
define PC_LOSSY = 3
|
||
|
||
|
||
# ============================================================
|
||
# ExprGenBase - ExprGenMixin 拆分(阶段 D):表达式基础工具(类型判断/指针层级/coerce)
|
||
# ============================================================
|
||
novirtual class ExprGenBase:
|
||
"""表达式基础工具(类型判断/指针层级/coerce)"""
|
||
|
||
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
|
||
Gen: Generator.Generator*
|
||
DocKeys: bytes
|
||
DocValues: bytes
|
||
DocCount: int
|
||
|
||
# --- OOM 诊断:跨模块 CDefine 查找调用计数(CONST_INT 参数翻译疑似循环查找) ---
|
||
DiagCDefineCount: int
|
||
# --- OOM 诊断:translate_name_value 调用计数(顶层 CDefine 查找 14593 次来源) ---
|
||
DiagNameValueCount: int
|
||
# --- OOM 诊断:translate_value 调用计数(CONST_INT 循环调用源定位) ---
|
||
DiagTranslateValueCount: int
|
||
# ============================================================
|
||
# P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数,
|
||
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# 从 AST 节点提取函数名
|
||
# ============================================================
|
||
def get_func_name(self, func_node: ast.AST*) -> str:
|
||
"""从函数节点提取函数名"""
|
||
if func_node is None: return None
|
||
k: int = func_node.kind()
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(func_node)
|
||
return nm.id
|
||
elif k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(func_node)
|
||
return at.attr
|
||
return None
|
||
# ============================================================
|
||
# 转义 LLVM IR 字符串字面量
|
||
# ============================================================
|
||
def escape_llvm_string(self, pool: memhub.MemBuddy*,
|
||
src: str) -> str:
|
||
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
|
||
if src is None: return None
|
||
slen: sizet = string.strlen(src)
|
||
alloc_size: sizet = 4 * slen + 8
|
||
buf: str = pool.alloc(alloc_size)
|
||
if buf is None: return None
|
||
pos: sizet = 0
|
||
buf[pos] = 'c'
|
||
pos += 1
|
||
buf[pos] = '"'
|
||
pos += 1
|
||
for i in range(slen):
|
||
ch: char = src[i]
|
||
if ch == '\0':
|
||
break
|
||
if ch == '\"' or ch == '\\' or ch < 0x20:
|
||
buf[pos] = '\\'
|
||
pos += 1
|
||
hi: int = (ch >> 4) & 0xF
|
||
lo: int = ch & 0xF
|
||
buf[pos] = Utils.nibble_to_hex_char(hi, 1)
|
||
pos += 1
|
||
buf[pos] = Utils.nibble_to_hex_char(lo, 1)
|
||
pos += 1
|
||
else:
|
||
buf[pos] = ch
|
||
pos += 1
|
||
buf[pos] = '\\'
|
||
pos += 1
|
||
buf[pos] = '0'
|
||
pos += 1
|
||
buf[pos] = '0'
|
||
pos += 1
|
||
buf[pos] = '"'
|
||
pos += 1
|
||
buf[pos] = '\0'
|
||
return buf
|
||
# ============================================================
|
||
# 获取 LLVM 整数类型的位宽
|
||
#
|
||
# [S4 切读·位宽块] 实现已收拢到 `TSpec.SpecTypeBits`(该块在 TypeSpec 模块中的
|
||
# **唯一实现**),此处只做委托,不再保留同码副本(TypeGenErr.get_llvm_type_bits
|
||
# 同样委托到它)。两份副本一旦分叉,会出现「同一类型在两处算出不同位宽」的
|
||
# 隐蔽不一致。私有调用点(self.get_llvm_type_bits)保持原样不受影响。
|
||
# ============================================================
|
||
def get_llvm_type_bits(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
|
||
return TSpec.SpecTypeBits(ty)
|
||
# ============================================================
|
||
# 获取 LLVM 浮点类型的位宽
|
||
# ============================================================
|
||
def get_llvm_float_bits(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
|
||
if ty is None: return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Float(bits):
|
||
return bits
|
||
case _:
|
||
return 0
|
||
# ============================================================
|
||
# is_ptr_type - 检查类型是否为 Ptr
|
||
#
|
||
# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
|
||
# ============================================================
|
||
def is_ptr_type(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
|
||
if ty is None: return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
return 1
|
||
case _:
|
||
return 0
|
||
# ============================================================
|
||
# get_ptr_level - 计算指针层级
|
||
#
|
||
# 返回值:
|
||
# 0 = 非指针(数值/结构体等)
|
||
# 1 = 一级指针(如 i8*, Struct*)
|
||
# 2 = 二级指针(如 i8**, Struct**)
|
||
# 3+ = 更高级指针
|
||
#
|
||
# 用于比较运算的类型安全检查:
|
||
# - 数值(0) vs 数值(0) → 允许
|
||
# - 一级指针(1) vs 一级指针(1) → 允许
|
||
# - 数值(0) vs 指针(1+) → 禁止(fast-fail)
|
||
# - 不同级指针(1 vs 2) → 禁止(fast-fail)
|
||
# ============================================================
|
||
def get_ptr_level(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针...
|
||
|
||
[S3 切读·指针层级块] 实现已收拢到 `TSpec.SpecTypePtrLevel`
|
||
(该块在 TypeSpec 模块中的**唯一实现**),此处只做委托,不再保留同码副本。
|
||
原实现里记录的「match codegen 缺陷」规避说明随实现一并移入 TypeSpec。
|
||
"""
|
||
return TSpec.SpecTypePtrLevel(ty)
|
||
# ============================================================
|
||
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
|
||
#
|
||
# 用于指针判空比较(ptr != 0 / ptr == 0):指针与字面量 0 比较时
|
||
# 0 视为 NULL,允许并转成 null 指针(比较类型检查的例外)。
|
||
# ============================================================
|
||
def _is_zero_int_literal(self, node: ast.AST*) -> int:
|
||
"""判断节点是否为整数字面量 0,返回 1=是 / 0=否"""
|
||
if node is None: return 0
|
||
if node.kind() != ast.ASTKind.Constant: return 0
|
||
cn: ast.Constant* = ast.Constant*(node)
|
||
if cn is None or cn.const_kind != ast.CONST_INT: return 0
|
||
if cn.int_val == 0: return 1
|
||
return 0
|
||
# ============================================================
|
||
# is_array_type - 检查类型是否为 Array
|
||
#
|
||
# 返回 1=是, 0=否。类型检查工具函数。
|
||
# ============================================================
|
||
def is_array_type(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
|
||
if ty is None: return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Array(_, _):
|
||
return 1
|
||
case _:
|
||
return 0
|
||
# ============================================================
|
||
# _zext_index_to_i32 - 数组下标索引零扩展
|
||
#
|
||
# LLVM GEP 对窄整数索引默认符号扩展:i8 0xE2(226) 会被当成 -30,
|
||
# 导致数组下标越界读(字节 >= 128 的乱码根因)。
|
||
# 数组下标语义为非负,<32 位整数索引一律 zext 到 i32。
|
||
# 仅用于数组 GEP;指针负偏移(p[-1])不经过此路径,不受影响。
|
||
# ============================================================
|
||
def _zext_index_to_i32(self, builder: llvmlite.IRBuilder*,
|
||
idx_val: llvmlite.Value*) -> llvmlite.Value*:
|
||
"""将 <32 位整数下标零扩展到 i32(数组下标非负语义)"""
|
||
if idx_val is None or idx_val.Ty is None or builder is None:
|
||
return idx_val
|
||
idx_bits: int = self.get_llvm_type_bits(idx_val.Ty)
|
||
if idx_bits != 0 and idx_bits < 32:
|
||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(builder.Pool)
|
||
return llvmlite.build_zext(builder, idx_val, i32_ty)
|
||
return idx_val
|
||
# ============================================================
|
||
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
|
||
#
|
||
# 处理两种情况:
|
||
# - ty 直接是 Struct → 检查是否注册
|
||
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
|
||
#
|
||
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
|
||
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
|
||
# ============================================================
|
||
def _get_custom_struct_cls_nm(self, pool: memhub.MemBuddy*,
|
||
ty: llvmlite.LLVMType*) -> str:
|
||
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
|
||
if ty is None:
|
||
return None
|
||
# 直接检查 ty 是否是注册的结构体
|
||
cls_nm_direct: str = self.Gen.StructGen.get_class_name_by_type(pool, ty)
|
||
if cls_nm_direct is not None:
|
||
return cls_nm_direct
|
||
# 如果 ty 是 Ptr,检查 pointee
|
||
if self.is_ptr_type(ty) != 0:
|
||
pointee_cs: llvmlite.LLVMType* = ty.Pointee
|
||
if pointee_cs is not None:
|
||
return self.Gen.StructGen.get_class_name_by_type(pool, pointee_cs)
|
||
return None
|
||
# ============================================================
|
||
# _is_list_subscript_base - 判断下标基座是否为 list[T] 变量
|
||
#
|
||
# 属性访问的 Subscript 基座(如 lst[i].x)需要区分:
|
||
# - list[T] 变量:translate_subscript 走 __getitem__ 内联返回元素地址
|
||
# - 普通结构体指针数组(如 pts: Point | t.CPtr):get_subscript_ptr 返回元素地址
|
||
# 路由错误会让 list[T] 被当作结构体数组 GEP,访问错位崩溃。
|
||
# ============================================================
|
||
def _is_list_subscript_base(self, pool: memhub.MemBuddy*,
|
||
trans: HT.Translator*,
|
||
sub_node: ast.AST*) -> int:
|
||
"""判断下标基座是否为 list[T] 变量(1=是)"""
|
||
if sub_node is None or trans is None: return 0
|
||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||
nm: ast.Name* = ast.Name*(sub.value)
|
||
if nm.id is None: return 0
|
||
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is None or alloca.Ty is None: return 0
|
||
if self.is_ptr_type(alloca.Ty) == 0: return 0
|
||
pointee: llvmlite.LLVMType* = alloca.Ty.Pointee
|
||
if pointee is None: return 0
|
||
match pointee:
|
||
case llvmlite.LLVMType.Ptr(inner_ty):
|
||
lsn_ls: str = None
|
||
match inner_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, lsn3):
|
||
lsn_ls = lsn3
|
||
case _:
|
||
pass
|
||
if lsn_ls is not None and string.strstr(lsn_ls, "list[") is not None:
|
||
return 1
|
||
case _:
|
||
pass
|
||
return 0
|
||
# ============================================================
|
||
# _sub_base_class_name - 解析下标基座变量的结构体类名
|
||
# ============================================================
|
||
def _sub_base_class_name(self, pool: memhub.MemBuddy*,
|
||
trans: HT.Translator*,
|
||
sub_node: ast.AST*) -> str:
|
||
"""解析下标基座变量的结构体类名(无则 None)"""
|
||
if sub_node is None or trans is None: return None
|
||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return None
|
||
nm: ast.Name* = ast.Name*(sub.value)
|
||
if nm.id is None: return None
|
||
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is None or alloca.Ty is None: return None
|
||
if self.is_ptr_type(alloca.Ty) == 0: return None
|
||
pointee: llvmlite.LLVMType* = alloca.Ty.Pointee
|
||
if pointee is None: return None
|
||
if self.is_ptr_type(pointee) != 0:
|
||
inner: llvmlite.LLVMType* = pointee.Pointee
|
||
if inner is not None:
|
||
return self.Gen.StructGen.get_class_name_by_type(pool, inner)
|
||
return self.Gen.StructGen.get_class_name_by_type(pool, pointee)
|
||
# ============================================================
|
||
# _sub_base_uses_getitem - 下标基座是否应走 __getitem__ 语义
|
||
#
|
||
# 属性访问的 Subscript 基座路由依据:
|
||
# - list[T] / OOP 定义 __getitem__ 的类(如 dict[key])→ translate_value
|
||
# (translate_subscript 走 __getitem__ 内联/方法调用返回元素值或地址)
|
||
# - 普通结构体指针数组(如 pts: Point | t.CPtr)→ get_subscript_ptr
|
||
# (GEP 元素地址,避免结构体值 base=非指针 报错)
|
||
# ============================================================
|
||
def _sub_base_uses_getitem(self, pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
trans: HT.Translator*,
|
||
sub_node: ast.AST*) -> int:
|
||
"""下标基座应走 __getitem__ 语义时返回 1,否则 0(应走 GEP 地址语义)"""
|
||
if self._is_list_subscript_base(pool, trans, sub_node) != 0: return 1
|
||
cls_ugi: str = self._sub_base_class_name(pool, trans, sub_node)
|
||
if cls_ugi is None: return 0
|
||
lookup_ugi: str = pool.alloc(128)
|
||
if lookup_ugi is None: return 0
|
||
viperlib.snprintf(lookup_ugi, 128, "%s.__getitem__", cls_ugi)
|
||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_ugi) is not None: return 1
|
||
return 0
|
||
# ============================================================
|
||
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||
#
|
||
# 用于支持 X|t.CPtr 类型变量的属性访问:
|
||
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
|
||
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
|
||
# ============================================================
|
||
def _deref_if_ptr_ptr(self, builder: llvmlite.IRBuilder*,
|
||
obj_ptr: llvmlite.Value*) -> llvmlite.Value*:
|
||
"""如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用获取内层指针"""
|
||
if obj_ptr is None or obj_ptr.Ty is None: return obj_ptr
|
||
if self.is_ptr_type(obj_ptr.Ty) == 0: return obj_ptr
|
||
pointee: llvmlite.LLVMType* = obj_ptr.Ty.Pointee
|
||
if pointee is None: return obj_ptr
|
||
if self.is_ptr_type(pointee) == 0: return obj_ptr
|
||
# obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||
loaded: llvmlite.Value* = llvmlite.build_load(builder, pointee, obj_ptr)
|
||
if loaded is None: return obj_ptr
|
||
return loaded
|
||
# ============================================================
|
||
# _value_node_of - 把「外层语句节点」下沉到其「值子表达式节点」
|
||
#
|
||
# 诊断错位修复(2026-09-28):赋值/返回等路径传给 coerce_to_type 的 node 常是
|
||
# 外层语句节点(实测 Assign,kind=3,col_offset=-1),导致报错打成
|
||
# `出错表达式: 表达式 kind=3(列 -1)`,指不到真正出错的子表达式。
|
||
# 此处把语句节点(Assign/AnnAssign/AugAssign/Expr/Return)换成其 value 子节点,
|
||
# 使报错定位到真正被转换的那个值表达式。非语句节点原样返回。
|
||
# ============================================================
|
||
def _value_node_of(self, node: ast.AST*) -> ast.AST*:
|
||
"""语句节点 → 其值子表达式节点;非语句/无值则原样返回"""
|
||
if node is None:
|
||
return node
|
||
k: int = node.kind()
|
||
v: ast.AST* = None
|
||
if k == ast.ASTKind.Assign:
|
||
v = (ast.Assign*(node)).value
|
||
elif k == ast.ASTKind.AnnAssign:
|
||
v = (ast.AnnAssign*(node)).value
|
||
elif k == ast.ASTKind.AugAssign:
|
||
v = (ast.AugAssign*(node)).value
|
||
elif k == ast.ASTKind.Expr:
|
||
v = (ast.Expr*(node)).value
|
||
elif k == ast.ASTKind.Return:
|
||
v = (ast.Return*(node)).value
|
||
if v is None:
|
||
return node
|
||
return v
|
||
# ============================================================
|
||
# IsNoneValue - 判据:源值是否为 None(空值常量),返回 1=是 / 0=否
|
||
#
|
||
# None 是特殊的空值常量:语义上「无值」,按语言规则**允许隐式转换为任意
|
||
# 类型**——目标类型取其空值/零值(整数/浮点→0,指针→空指针,聚合→零初始化)。
|
||
# 故「指针↔整数」「指针↔非指针值」「float↔ptr」这类针对**真实类型值**的
|
||
# 隐式转换禁令不应拦 None:那些禁令防的是「有类型信息的真实值被静默当成
|
||
# 另一种类型」,而 None 本就没有具体类型信息,赋任何类型都只是「置空/置零」。
|
||
#
|
||
# 真实用例(用户实际踩到):
|
||
# VKernel/Kernel/intr/syscall.vp:87 `p.fd_used[fd] = None`
|
||
# (fd_used 是 [N]uint64 之类的整数数组/字段)此前被
|
||
# 「指针隐式转整数已禁止」拦成 CastError,本修正后应输出整数 0。
|
||
#
|
||
# 两条判据(任一命中即视为 None):
|
||
# ① AST 层面:node 是 Constant 且 const_kind == ast.CONST_NONE
|
||
# ② 值层面(主判据):已是生成好的 null 常量——None 经 translate_constant
|
||
# 物化为 ConstNull(i8*)(指针类型 + 编译期零 + IsConst=1)。指针类型的
|
||
# 编译期零常量即空指针(ConstNull / 指针型 ConstZero 皆如此);整数
|
||
# 常量 0 的 Ty 是整数类型,不会误命中。
|
||
# ② 为主判据的原因:赋值路径传给 coerce_to_type 的 node 常是外层语句节点
|
||
# (实测上述用例 node 是 Assign,kind=3,col_offset=-1),拿不到 Constant
|
||
# 节点,只能靠已经生成好的值来判定。
|
||
# ============================================================
|
||
def IsNoneValue(self, val: llvmlite.Value*,
|
||
node: ast.AST* = None) -> int:
|
||
"""判据:val/node 是否表示 None(空值常量),1=是 / 0=否"""
|
||
# 判据①:AST 是 None 字面量
|
||
if node is not None and node.kind() == ast.ASTKind.Constant:
|
||
cn_nv: ast.Constant* = ast.Constant*(node)
|
||
if cn_nv is not None and cn_nv.const_kind == ast.CONST_NONE:
|
||
return 1
|
||
# 判据②:已生成好的 null 常量(指针类型 + 编译期零)
|
||
if val is not None and val.IsConst == 1 and val.IntVal == 0 \
|
||
and self.is_ptr_type(val.Ty) != 0:
|
||
return 1
|
||
return 0
|
||
# ============================================================
|
||
# _NoneValueAsTarget - 把 None(空值)物化为目标类型的空值/零值
|
||
#
|
||
# 仅在 coerce_to_type 判定源值是 None 时调用。构造手法沿用本仓既有写法:
|
||
# 整数目标 → ConstInt(ty, 0)(零值)
|
||
# 浮点目标 → ConstFloat(ty, 0.0)(零值)
|
||
# 聚合目标(Struct/Array)→ ConstZero(ty)(zeroinitializer)
|
||
# 其余(Void 等)→ ConstZero(ty) 兜底
|
||
# 指针目标不在此处处理:coerce_to_type 的「指针→指针 bitcast」路径已能
|
||
# 接受 None(i8* null → T*),保留其原有 IR 形态。
|
||
# ============================================================
|
||
def _NoneValueAsTarget(self, builder: llvmlite.IRBuilder*,
|
||
target_ty: llvmlite.LLVMType*) -> llvmlite.Value*:
|
||
"""把 None 转换为目标类型的空值/零值"""
|
||
if target_ty is None:
|
||
return None
|
||
pool: memhub.MemBuddy* = builder.Pool
|
||
t_bits: int = self.get_llvm_type_bits(target_ty)
|
||
if t_bits != 0:
|
||
return llvmlite.ConstInt(pool, target_ty, 0, "0")
|
||
t_fbits: int = self.get_llvm_float_bits(target_ty)
|
||
if t_fbits != 0:
|
||
return llvmlite.ConstFloat(pool, target_ty, 0.0)
|
||
return llvmlite.ConstZero(pool, target_ty)
|
||
# ============================================================
|
||
# _is_universal_ptr_ty - 判据:类型是否为「通用指针」(t.CPtr / i8* = void*)
|
||
#
|
||
# 规则 1(通用指针是通配):t.CPtr 与 i8*(void*,裸 str/bytes 那一族的指针形态)
|
||
# 视为「任意指针」,与任何 T* 双向隐式互转都放行。
|
||
# 判据:单层 Ptr 且 pointee 为 i8(t.CPtr = Ptr(Int8);Ptr(Void) 工厂已回退为
|
||
# Ptr(Int8),故 void* 在 LLVM 层同样表现为 Ptr(i8))。char* 同属 i8*,一并视为通用指针。
|
||
# 多级指针(如 i8**)不是通用指针(需逐级比较 pointee)。
|
||
# 返回 1=是 / 0=否。
|
||
# ============================================================
|
||
def _is_universal_ptr_ty(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""ty 是否为通用指针(i8*/t.CPtr),1=是 / 0=否"""
|
||
if ty is None or self.is_ptr_type(ty) == 0:
|
||
return 0
|
||
if self.get_ptr_level(ty) != 1:
|
||
return 0
|
||
pointee: llvmlite.LLVMType* = ty.Pointee
|
||
if pointee is None:
|
||
return 1
|
||
if self.get_llvm_type_bits(pointee) == 8:
|
||
return 1
|
||
return 0
|
||
# ============================================================
|
||
# _is_basic_scalar_ty - 判据:类型是否为基本类型(整数/浮点),1=是 / 0=否
|
||
#
|
||
# 非基本类型 = 结构体/类/联合/数组/函数等(LLVM 层 Struct/Array/Func)。
|
||
# 规则 2/3:非基本类型一律不允许参与指针间隐式转换(除完全相同类型,见规则 4)。
|
||
# ============================================================
|
||
def _is_basic_scalar_ty(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""ty 是否为基本类型(整数/浮点),1=是 / 0=否"""
|
||
if ty is None:
|
||
return 0
|
||
if self.get_llvm_type_bits(ty) != 0:
|
||
return 1
|
||
if self.get_llvm_float_bits(ty) != 0:
|
||
return 1
|
||
return 0
|
||
# ============================================================
|
||
# _scalar_convert_reason - 基本标量 T → U 是否无损(沿用 coerce_to_type 既有规则)
|
||
#
|
||
# 整数/浮点:沿用既有规则(高→低允许安全降级、低→高无损;整数→浮点需浮点位宽
|
||
# ≥ 整数位宽;浮点→整数不允许)。
|
||
# 返回 PC_OK(无损/允许)或 PC_LOSSY(有损/禁止)。
|
||
# ============================================================
|
||
def _scalar_convert_reason(self, src_pt: llvmlite.LLVMType*,
|
||
dst_pt: llvmlite.LLVMType*) -> int:
|
||
"""基本标量 src_pt → dst_pt 的隐式转换判定码(PC_OK / PC_LOSSY)"""
|
||
s_int: int = self.get_llvm_type_bits(src_pt)
|
||
d_int: int = self.get_llvm_type_bits(dst_pt)
|
||
s_flt: int = self.get_llvm_float_bits(src_pt)
|
||
d_flt: int = self.get_llvm_float_bits(dst_pt)
|
||
# 整数 ↔ 整数:高→低安全降级、低→高无损,均允许
|
||
if s_int != 0 and d_int != 0:
|
||
return PC_OK
|
||
# 浮点 ↔ 浮点:允许
|
||
if s_flt != 0 and d_flt != 0:
|
||
return PC_OK
|
||
# 整数 → 浮点:浮点位宽 ≥ 整数位宽才无损
|
||
if s_int != 0 and d_flt != 0:
|
||
if d_flt >= s_int:
|
||
return PC_OK
|
||
return PC_LOSSY
|
||
# 浮点 → 整数:有损,禁止
|
||
if s_flt != 0 and d_int != 0:
|
||
return PC_LOSSY
|
||
return PC_OK
|
||
# ============================================================
|
||
# _class_name_of_ptr_pointee - 指针被指类型 → 已注册类名
|
||
#
|
||
# 复用 StructGen.get_class_name_by_type(内部即 find_struct_by_type 首注册
|
||
# 优先匹配 + 按名/后缀回退),把 LLVM 被指类型映射到类名;非结构体/未注册
|
||
# 返回 None。不新造表。
|
||
# ============================================================
|
||
def _class_name_of_ptr_pointee(self, pool: memhub.MemBuddy*,
|
||
pt: llvmlite.LLVMType*) -> str:
|
||
"""指针被指类型映射到已注册类名;失败返回 None"""
|
||
if pt is None:
|
||
return None
|
||
return self.Gen.StructGen.get_class_name_by_type(pool, pt)
|
||
# ============================================================
|
||
# _is_inheritance_upcast - 继承上转判据(Derived* → Base*,含多级)
|
||
#
|
||
# 规则 2 的豁免:两侧被指类型都解析到已注册类时,若**目标类名出现在源类名
|
||
# 的 MRO 链「自身之后」**(即目标为源的基类/祖先)⇒ 允许隐式上转。
|
||
# 复用既有继承判据(StructGen.get_class_name_by_type + get_mro_chain),
|
||
# 不引入新表。下转(Base* → Derived*:派生类不在基类 MRO 链里)与无关
|
||
# 类型(Foo* ↔ Bar*)都不命中 ⇒ 返回 0,仍按规则 2/3 禁止。
|
||
# ============================================================
|
||
def _is_inheritance_upcast(self, pool: memhub.MemBuddy*,
|
||
src_pt: llvmlite.LLVMType*,
|
||
dst_pt: llvmlite.LLVMType*) -> int:
|
||
"""派生类指针 → 基类指针(含多级):1=允许上转 / 0=否"""
|
||
src_name: str = self._class_name_of_ptr_pointee(pool, src_pt)
|
||
if src_name is None:
|
||
return 0
|
||
dst_name: str = self._class_name_of_ptr_pointee(pool, dst_pt)
|
||
if dst_name is None:
|
||
return 0
|
||
if string.strcmp(src_name, dst_name) == 0:
|
||
return 0
|
||
chain: sizet* = self.Gen.StructGen.get_mro_chain(pool, src_name)
|
||
if chain is None:
|
||
return 0
|
||
# chain[0] = 自身;父类/祖先在后,遇 0 终止。只查「自身之后」的祖先。
|
||
i: int = 1
|
||
while chain[i] != 0:
|
||
anc: str = rawptr(chain[i])
|
||
if anc is not None and string.strcmp(anc, dst_name) == 0:
|
||
return 1
|
||
i += 1
|
||
return 0
|
||
# ============================================================
|
||
# _is_array_decay - 数组退让判据([N]T* → T*,单向)
|
||
#
|
||
# 规则 2 的豁免:源被指类型是**定长数组** Array(T, N),且目标被指类型恰为该
|
||
# 数组的**元素类型** T ⇒ 允许隐式转换(数组指针 → 元素指针,C/C++ 同口径)。
|
||
# 多维数组按「最外层退让一层」自然处理:Array(Array(T, M), N) 的元素是
|
||
# Array(T, M) ⇒ Array(Array(T,M),N)* → Array(T,M)* 命中;→ T* 不命中
|
||
# (元素是 Array(T,M) ≠ T)。反向 T* → Array(T,N)* 因源被指类型非数组而不命中。
|
||
# 元素类型同一性复用本仓既有 AssignGen._stg_ty_equal(内容比较),不新造表。
|
||
# 返回 1=允许退让 / 0=否。
|
||
# ============================================================
|
||
def _is_array_decay(self, pool: memhub.MemBuddy*,
|
||
src_pt: llvmlite.LLVMType*,
|
||
dst_pt: llvmlite.LLVMType*) -> int:
|
||
"""源被指类型是定长数组且目标被指类型是其元素类型:1=是 / 0=否"""
|
||
if src_pt is None or dst_pt is None:
|
||
return 0
|
||
if self.is_array_type(src_pt) == 0:
|
||
return 0
|
||
elem: llvmlite.LLVMType* = None
|
||
match src_pt:
|
||
case llvmlite.LLVMType.Array(et, _):
|
||
elem = et
|
||
case _:
|
||
return 0
|
||
if elem is None:
|
||
return 0
|
||
return self.Gen.AssignGen._stg_ty_equal(pool, elem, dst_pt)
|
||
# ============================================================
|
||
# _pointee_convert_reason - 被指类型 src_pt → dst_pt(T*→U* 的 T→U 关系)判定码
|
||
#
|
||
# 规则 2:T* → U*(T、U 都非通用指针)允许 ⇔ 被指类型 T → U 无损。
|
||
# 豁免一:T、U 都是已注册类且 U 是 T 的基类(含多级)时,Derived* → Base*
|
||
# 继承上转允许(见 _is_inheritance_upcast)。
|
||
# 豁免二:T 是定长数组、U 恰为其元素类型时,数组退让 [N]T* → T* 允许(单向,
|
||
# 见 _is_array_decay)。反向 T* → [N]T* 不命中,仍禁止。
|
||
# 规则 3:非基本类型 T 与其指针 T* 之间(值/指针互换)一律禁止——此处体现为
|
||
# 被指类型一侧是「非基本值类型」、另一侧是「其指针(或其它的基本/非基本类型)」
|
||
# 时一律不匹配 ⇒ PC_NONBASIC。
|
||
# 被指类型仍是指针(T** → U**):递归按指针规则判。
|
||
# ============================================================
|
||
def _pointee_convert_reason(self, pool: memhub.MemBuddy*,
|
||
src_pt: llvmlite.LLVMType*,
|
||
dst_pt: llvmlite.LLVMType*) -> int:
|
||
"""被指类型 src_pt → dst_pt 的判定码(PC_OK/PC_NONBASIC/PC_LOSSY/PC_DEPTH)"""
|
||
if src_pt is None or dst_pt is None:
|
||
return PC_OK
|
||
s_is_ptr: int = self.is_ptr_type(src_pt)
|
||
d_is_ptr: int = self.is_ptr_type(dst_pt)
|
||
if s_is_ptr != 0 or d_is_ptr != 0:
|
||
# 一侧是指针、另一侧不是(如被指类型 Foo vs Foo*)→ 非基本类型值/指针互换,禁止
|
||
if s_is_ptr == 0 or d_is_ptr == 0:
|
||
return PC_NONBASIC
|
||
# 两侧都是指针:递归
|
||
return self._ptr_cast_reason(pool, src_pt, dst_pt)
|
||
# 一侧是非基本类型(结构体/类/联合/数组/函数)→ 一般禁止
|
||
if self._is_basic_scalar_ty(src_pt) == 0 or self._is_basic_scalar_ty(dst_pt) == 0:
|
||
# 豁免一:Derived* → Base* 继承上转(含多级)允许(C++/GCC 同口径)
|
||
if self._is_inheritance_upcast(pool, src_pt, dst_pt) != 0:
|
||
return PC_OK
|
||
# 豁免二:数组退让(单向)——源被指类型是定长数组 Array(T, N)、目标被指
|
||
# 类型恰为其元素类型 T ⇒ 允许(数组指针 → 元素指针)。多维按最外层退让
|
||
# 一层;反向 T → Array(T, N) 与其它非基本类型规则不变,仍禁止。
|
||
if self._is_array_decay(pool, src_pt, dst_pt) != 0:
|
||
return PC_OK
|
||
return PC_NONBASIC
|
||
return self._scalar_convert_reason(src_pt, dst_pt)
|
||
# ============================================================
|
||
# _ptr_cast_reason - 指针 → 指针隐式转换的判定码(新规则单一真源)
|
||
#
|
||
# 规则 1:通用指针(t.CPtr / i8*)与任何 T* 双向互转都放行 ⇒ PC_OK。
|
||
# 规则 2:T* → U*(T、U 都非通用指针)允许 ⇔ 被指类型 T → U 无损;非基本类型
|
||
# 一律不允许。
|
||
# 规则 3:非基本类型 T 与其指针 T* 之间一律禁止。
|
||
# 规则 4:完全相同类型(指针深度与被指类型都相同,含 Foo* → Foo*)不算转换 ⇒ PC_OK。
|
||
# 返回 PC_OK(允许)/ PC_DEPTH / PC_NONBASIC / PC_LOSSY。
|
||
# ============================================================
|
||
def _ptr_cast_reason(self, pool: memhub.MemBuddy*,
|
||
src_ty: llvmlite.LLVMType*,
|
||
dst_ty: llvmlite.LLVMType*) -> int:
|
||
"""指针 → 指针对隐式转换的判定码(PC_OK 表示允许)"""
|
||
if src_ty is None or dst_ty is None:
|
||
return PC_OK
|
||
slev: int = self.get_ptr_level(src_ty)
|
||
dlev: int = self.get_ptr_level(dst_ty)
|
||
if slev == 0 or dlev == 0:
|
||
return PC_OK
|
||
# 层数不同 → 禁止(无论被指类型)
|
||
if slev != dlev:
|
||
return PC_DEPTH
|
||
# 规则 4:完全相同类型(层数 + 逐级被指类型都相同)不算转换
|
||
if self.Gen.AssignGen._stg_ty_equal(pool, src_ty, dst_ty) != 0:
|
||
return PC_OK
|
||
# 规则 1:通用指针是通配(i8* ↔ 任何 T*)
|
||
if self._is_universal_ptr_ty(src_ty) != 0 or self._is_universal_ptr_ty(dst_ty) != 0:
|
||
return PC_OK
|
||
# 规则 2/3:逐级比较被指类型
|
||
return self._pointee_convert_reason(pool, src_ty.Pointee, dst_ty.Pointee)
|
||
# ============================================================
|
||
# _type_text - LLVM 类型的可读文本(TypePrint),供报文使用(失败返回 "?")
|
||
# ============================================================
|
||
def _type_text(self, pool: memhub.MemBuddy*,
|
||
ty: llvmlite.LLVMType*) -> str:
|
||
"""LLVM 类型文本(TypePrint);失败返回 "?" """
|
||
if ty is None:
|
||
return "?"
|
||
buf: str = pool.alloc(256)
|
||
if buf is None:
|
||
return "?"
|
||
llvmlite.TypePrint(buf, 256, ty, pool)
|
||
if buf[0] == 0:
|
||
return "?"
|
||
return buf
|
||
# ============================================================
|
||
# _ptr_cast_reason_text - 把指针间隐式转换的禁止原因写成可读文本
|
||
#
|
||
# 报文写清「哪一级不允许 + 为什么(非基本类型 / 有损 / 层数不同)」,并给出
|
||
# 源/目标 LLVM 类型文本。返回值会逃逸 ⇒ 经 escape_fstring_copy 拷到 stdlib 堆。
|
||
# ============================================================
|
||
def _ptr_cast_reason_text(self, code: int, pool: memhub.MemBuddy*,
|
||
src_ty: llvmlite.LLVMType*,
|
||
dst_ty: llvmlite.LLVMType*) -> str:
|
||
"""指针间隐式转换禁止原因的可读文本(含源/目标类型)"""
|
||
why: str = "原因未知"
|
||
if code == PC_DEPTH:
|
||
why = f"指针层数不同(源 {self.get_ptr_level(src_ty)} 级 → 目标 {self.get_ptr_level(dst_ty)} 级)"
|
||
elif code == PC_NONBASIC:
|
||
why = "被指类型是非基本类型(结构体/类/联合/数组等):非基本类型与其指针之间、以及不同非基本类型的指针之间一律不允许隐式转换"
|
||
elif code == PC_LOSSY:
|
||
why = "被指类型之间的转换有损(整数→浮点位宽不足 / 浮点→整数)"
|
||
s_txt: str = self._type_text(pool, src_ty)
|
||
d_txt: str = self._type_text(pool, dst_ty)
|
||
return GenApi.escape_fstring_copy(f"指针隐式转指针已禁止:{why};源类型 {s_txt} → 目标类型 {d_txt}")
|
||
# ============================================================
|
||
# _ctor_call_agg_ty - 判据:node 是否为「类/结构体构造调用」;是则返回其聚合类型
|
||
#
|
||
# 规则 3(非基本类型值 ↔ 指针的隐式转换一律禁止)的**唯一例外**=「构造物化」:
|
||
# `p: Point = Point()` —— 右值来自该类型的构造调用(构造语义,不是类型转换)。
|
||
# 本判据在 **AST 层**复用本仓既有「Call 的被调方是类/构造」识别:
|
||
# - 先经 _value_node_of 把外层语句节点(AnnAssign/Assign 等)下沉到真正的右值;
|
||
# - callee 裸名/属性名 → StructGen.find_struct(已注册结构体/类)→ 其聚合类型;
|
||
# - 泛型类模板(Box(42))特化名需类型实参推断,静态取不到类型 → 保守返回 None。
|
||
# 非构造调用(普通函数/方法/未知符号)一律返回 None(→ 不进豁免,照常报错)。
|
||
# ============================================================
|
||
def _ctor_call_agg_ty(self, node: ast.AST*) -> llvmlite.LLVMType*:
|
||
"""node(或其值子节点)是构造调用 → 返回其构造的聚合类型;否则 None"""
|
||
n: ast.AST* = self._value_node_of(node)
|
||
if n is None or n.kind() != ast.ASTKind.Call:
|
||
return None
|
||
cl: ast.Call* = ast.Call*(n)
|
||
if cl.func is None:
|
||
return None
|
||
callee: str = None
|
||
if cl.func.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(cl.func)
|
||
callee = nm.id
|
||
elif cl.func.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
callee = at.attr
|
||
if callee is None:
|
||
return None
|
||
ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(callee)
|
||
if ent is not None and ent.Ty is not None:
|
||
return ent.Ty
|
||
# REnum 变体构造 `EnumType.Variant(...)`:被调方是「基础枚举.变体」,变体名不是
|
||
# 结构体名(find_struct 必 miss);但构造结果物化的就是**基础枚举**的整对象
|
||
# (boxed 指针指向该枚举结构体)⇒ 返回该 REnum 的聚合类型,使「构造物化」豁免
|
||
# 覆盖 `*槽 = <boxed 变体>`。否则该写法会被「指针 → 非基本类型值」禁令拦下
|
||
# (现场 ClassGenDef.vp:128 `*existing.Ty = llvmlite.LLVMType.Struct(...)`)。
|
||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||
# 取「变体名左边那一段」作为枚举类名:被调方形如 `<限定链>.<变体>`,
|
||
# EnumType.Variant → 限定段是裸名 EnumType
|
||
# llvmlite.LLVMType.Struct → 限定段是属性 LLVMType(**不是**链根
|
||
# llvmlite,早期实现走到链根导致漏判)
|
||
qual: ast.AST* = (ast.Attribute*(cl.func)).value
|
||
base_nm: str = None
|
||
if qual is not None:
|
||
if qual.kind() == ast.ASTKind.Attribute:
|
||
base_nm = (ast.Attribute*(qual)).attr
|
||
elif qual.kind() == ast.ASTKind.Name:
|
||
base_nm = (ast.Name*(qual)).id
|
||
if base_nm is not None and self.Gen.ClassGenEnum._is_renum_class_name(base_nm) != 0:
|
||
rn_ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(base_nm)
|
||
if rn_ent is not None and rn_ent.Ty is not None:
|
||
return rn_ent.Ty
|
||
return None
|
||
# ============================================================
|
||
# _same_agg_ty - 两个聚合类型是否为「同一非基本类型」(构造物化豁免的类型匹配)
|
||
# ============================================================
|
||
def _same_agg_ty(self, pool: memhub.MemBuddy*,
|
||
a: llvmlite.LLVMType*, b: llvmlite.LLVMType*) -> int:
|
||
"""a、b 是否为同一聚合类型,1=是 / 0=否"""
|
||
if a is None or b is None:
|
||
return 0
|
||
return self.Gen.AssignGen._stg_ty_equal(pool, a, b)
|
||
# ============================================================
|
||
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
|
||
# ============================================================
|
||
def coerce_to_type(self, builder: llvmlite.IRBuilder*,
|
||
val: llvmlite.Value*,
|
||
target_ty: llvmlite.LLVMType*,
|
||
node: ast.AST* = None,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""将 val 强制转换为 target_ty
|
||
|
||
整数: sext(扩展)/ trunc(截断)
|
||
浮点: fpext(扩展)/ fptrunc(截断)
|
||
int→float: sitofp
|
||
float→int: fptosi
|
||
|
||
node: AST 节点(用于错误定位),可为 None
|
||
"""
|
||
if val is None or target_ty is None: return val
|
||
# [FIX 2026-09-28] None(空值常量)允许隐式转换为任意类型:None 无语义
|
||
# 类型信息,赋给任意类型只是「置空/置零」,故下方所有针对**真实类型值**
|
||
# 的隐式转换禁令(指针→整数 / 整数→指针 / 指针→非指针值 / float↔ptr)
|
||
# 对 None 一律短路放行。统一收敛到 IsNoneValue 单一判据,避免遗漏。
|
||
# 指针目标仍走原有「指针→指针 bitcast」路径(保留既有 IR 形态);
|
||
# 非指针目标直接物化为目标类型的零值/空值。
|
||
# 真实用例:`p.fd_used[fd] = None`(fd_used 为整数数组字段)曾被
|
||
# 「指针隐式转整数已禁止」拦成 CastError。
|
||
if self.IsNoneValue(val, node) != 0:
|
||
if self.is_ptr_type(target_ty) != 0:
|
||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||
return self._NoneValueAsTarget(builder, target_ty)
|
||
# void 类型的值**不允许被当作值使用**(用户 2026-10-01:void 只能用于 `void*`
|
||
# 与 `typedef 不透明结构体 = void`)。任何把 void 值 coerce 到非 void 目标
|
||
# (赋值 / 传参 / 返回 / 二元运算对齐)都是无意义操作 ⇒ fail-fast,而不是
|
||
# 按 LLVM void 硬拼出非法 IR("load void"/"bitcast void")。void → void
|
||
# 属「无值传递」,放行。
|
||
if self.Gen.StmtGenDecl._is_void_type(val.Ty) != 0:
|
||
if self.Gen.StmtGenDecl._is_void_type(target_ty) == 0:
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
"void 类型的值不能参与任何操作(void 只能用于 void* 与 typedef 别名)", "TypeError")
|
||
return val
|
||
# 函数 → 指针: bitcast(如 CreateThread(..., _thread_alloc_free, ...) 传函数指针)
|
||
# 函数 Value 的 Ty 是 Func,既不是整数也不是指针,需在整数/浮点转换之前处理
|
||
match val.Ty:
|
||
case llvmlite.LLVMType.Func(_, _, _):
|
||
if self.is_ptr_type(target_ty) != 0:
|
||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||
return val
|
||
case _:
|
||
pass
|
||
# 整数转换
|
||
val_bits: int = self.get_llvm_type_bits(val.Ty)
|
||
target_bits: int = self.get_llvm_type_bits(target_ty)
|
||
# 整数 → 指针: 禁止隐式转换(FIX 2026-08-30),唯一豁免是 C 空指针常量 0
|
||
# (NULL: t.CDefine = 0 / 字面量 0)——C 语义中 0 赋指针即 null pointer
|
||
# constant,inttoptr 0 生成合法空指针,不属于"整数当指针"的类型信息缺失。
|
||
# 原为 inttoptr 兜底(跨模块方法返回 i64/i32 默认推断时静默转指针),
|
||
# 掩盖类型信息缺失——如 `BootInfo = saved_rdi`(整数 → bootinfo*)。
|
||
# 按"按需过滤/不兜底/fail-fast"原则:非常量整数必须显式强转
|
||
# (t.CPtr(x) 无类型指针右值可隐式转为左侧类型化指针,走指针→指针
|
||
# bitcast 路径)。
|
||
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
|
||
# [FIX 2026-08-31] C 空指针常量豁免:编译期常量 0 赋给指针 → 空指针。
|
||
# NULL 是 t.CDefine = 0(如 self.handle = NULL / CreateThread(NULL,...)),
|
||
# 属标准 C 空指针用法,须放行而非报 CastError。先拓宽到 i64
|
||
# (inttoptr 要求同宽,对齐 MatchGenMixin 模式),再 inttoptr 成空指针。
|
||
if val.IsConst == 1 and val.IntVal == 0:
|
||
return llvmlite.build_inttoptr(builder, llvmlite.const_int64(builder.Pool, 0), target_ty)
|
||
m_i2p: str = "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型"
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
m_i2p, "CastError")
|
||
return val
|
||
# Pointer -> integer: rejected (symmetric with the integer -> pointer rule above).
|
||
# No implicit pointer <-> integer conversion is allowed; an explicit cast is
|
||
# required. (The old code silently did ptrtoint, and for 8-bit targets fell
|
||
# through to the build_load branch below, copying the pointee's first byte.)
|
||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and self.is_ptr_type(target_ty) == 0:
|
||
m_p2i: str = "指针隐式转整数已禁止:必须显式强转(如 uint64(p) / t.CInt64T(p) / int(p))才能把指针赋给整数类型"
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
m_p2i, "CastError")
|
||
return val
|
||
if val_bits != 0 and target_bits != 0:
|
||
if val_bits == target_bits:
|
||
return val
|
||
# 目标 i1(bool / t.CBool):**真值归一化** `x != 0 → 1/0`(C 6.3.1.2 的
|
||
# _Bool 转换语义),不是 trunc(取最低位)。判据与 `bool(x)`
|
||
# (CallGenDispatch.vp:255-281)、条件归一化(AsyncGenMixin.vp:1611
|
||
# _normalize_cond)、and/or 操作数三处**同一**:同一个运行时值 2 在
|
||
# `if 2:` 里为真,赋给 bool 变量/字段/形参必须同样为真。此处此前走 trunc
|
||
# ⇒ 值 2 取最低位得 0 → false,与条件上下文结论相反(静默语义分裂);
|
||
# 值 3 得 true 而同值域下 `!= 0` 亦为 true,故只在偶数非零时暴露。
|
||
# 归一化不丢位 ⇒ 下方「窄化截断溢出」常量诊断对 i1 目标不适用(先返回)。
|
||
if target_bits == 1:
|
||
zero_i1: llvmlite.Value* = llvmlite.const_int(builder.Pool, val_bits, 0)
|
||
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero_i1)
|
||
if val_bits < target_bits:
|
||
# 统一 zext/sext(问题2修正):i1/i8 默认 zext;AST 节点解析为
|
||
# 无符号类型(t.CUIntXXT 强转/无符号类型名)走 zext;否则 sext。
|
||
# 此前一律 sext(i1 除外)→ uint32 等无符号值赋值/传参被符号
|
||
# 扩展(高位置 1,如 0xFFFFFFFF 变 0xFFFFFFFFFFFFFFFF),与比较路径
|
||
# should_zext_widen(i8 默认 zext)规则相反 → 隐蔽数据损坏。
|
||
# trans 必须下传:_node_type_unsigned 对 Name/Subscript 形态需经
|
||
# trans.SymTab 查变量注解(uint16/sec:uint32 等)才能判定无符号,
|
||
# 否则返回未知 → 退回 sext(无符号扩展缺陷根因)。
|
||
if self._zext_or_sext_val(builder.Pool, val, node, trans) != 0:
|
||
return llvmlite.build_zext(builder, val, target_ty)
|
||
return llvmlite.build_sext(builder, val, target_ty)
|
||
# 窄化截断诊断(_Type.md 问题 8):宽→窄 trunc 静默丢位。
|
||
# 编译期已知常量且正溢出目标位宽无符号范围 → fail-fast(确定性数据
|
||
# 损坏,如 buf[idx] = 0x1FF 写入 i8 槽位被静默截断)。负常量按模
|
||
# 2^bits 语义放行(-1 → 0xFF 合法);非常量运行时窄化保留 trunc
|
||
# (值域可能安全,无法静态判定)。
|
||
if val.IsConst == 1 and val.IntVal > 0:
|
||
target_max_u: uint64 = (uint64(1) << target_bits) - 1
|
||
if uint64(val.IntVal) > target_max_u:
|
||
max_print: int64 = int64(target_max_u)
|
||
self.Gen.TypeGenErr.fatal_error(node, f"窄化截断溢出:常量 {val.IntVal} 超出目标类型 {target_bits} 位范围 [0, {max_print}],赋值将被静默截断", "OverflowError")
|
||
return llvmlite.build_trunc(builder, val, target_ty)
|
||
# 浮点转换
|
||
val_fbits: int = self.get_llvm_float_bits(val.Ty)
|
||
target_fbits: int = self.get_llvm_float_bits(target_ty)
|
||
# float ↔ ptr: 禁止(无意义的转换,fast-fail 防止误走入 build_load 路径)
|
||
if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or \
|
||
(self.is_ptr_type(val.Ty) != 0 and target_fbits != 0):
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
"unsupported type conversion: float <-> ptr", "CastError")
|
||
return val
|
||
if val_fbits != 0 and target_fbits != 0:
|
||
if val_fbits == target_fbits:
|
||
return val
|
||
if val_fbits < target_fbits:
|
||
return llvmlite.build_fpext(builder, val, target_ty)
|
||
return llvmlite.build_fptrunc(builder, val, target_ty)
|
||
# int → float
|
||
if val_bits != 0 and target_fbits != 0:
|
||
return llvmlite.build_si2fp(builder, val, target_ty)
|
||
# float → int
|
||
if val_fbits != 0 and target_bits != 0:
|
||
return llvmlite.build_fp2si(builder, val, target_ty)
|
||
# 聚合值 → 指针:落栈 materialize 临时对象后传其地址。FuncGenDef 已确认「裸类/数组形参一律 lowering 为 Ptr(struct/array)」——TPV 内部函数/方法不存在"按值传结构体"的形参(唯一例外:C ABI 外部声明的裸结构体形参按值,见 VpiReader._AggregateParamTy「三处必须同步」)。故调用点的 rvalue 聚合(函数调用返回值、下标元素 load 等)必须落栈取址,否则实参类型与指针形参失配。alloca 走入口块(支配性安全),生命周期覆盖整个调用,pointee 与聚合类型一致时直接用 alloca 地址
|
||
if self.is_ptr_type(val.Ty) == 0 and self.is_ptr_type(target_ty) != 0:
|
||
vk_agg: int = val.Ty.kind()
|
||
if vk_agg == llvmlite.LLVMType.Struct or vk_agg == llvmlite.LLVMType.Array:
|
||
# 规则 3:非基本类型的「值 → 指针」隐式转换(Foo → Foo*)一律禁止,
|
||
# 唯一例外=「构造物化」:右值来自该类型的构造调用时放行(见 _ctor_call_agg_ty)。
|
||
ctor_v2p: llvmlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||
if self._same_agg_ty(builder.Pool, ctor_v2p, val.Ty) == 0:
|
||
m_v2p: str = "非基本类型的值/指针隐式转换已禁止(构造物化除外):不能把非基本类型值隐式转为指针(Foo → Foo*),如需取址请显式 c.addr(...) / &v,或显式强转"
|
||
v2p_txt: str = GenApi.escape_fstring_copy(
|
||
f";源值类型 {self._type_text(builder.Pool, val.Ty)} → 目标类型 {self._type_text(builder.Pool, target_ty)}")
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
f"{m_v2p}{v2p_txt}", "CastError")
|
||
return val
|
||
tmp_agg: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, val.Ty)
|
||
if tmp_agg is not None:
|
||
llvmlite.build_store(builder, val, tmp_agg)
|
||
match target_ty:
|
||
case llvmlite.LLVMType.Ptr(pointee_agg):
|
||
if self.Gen.AssignGen._stg_ty_equal(builder.Pool, pointee_agg, val.Ty) != 0:
|
||
return tmp_agg
|
||
case _:
|
||
pass
|
||
bc_agg: llvmlite.Value* = llvmlite.build_bitcast(builder, tmp_agg, target_ty)
|
||
if bc_agg is not None:
|
||
return bc_agg
|
||
return tmp_agg
|
||
# Pointer -> non-pointer value: aggregate targets keep the implicit load **only**
|
||
# for 「构造物化」(a constructor returning Ptr(Struct) held in a Struct-typed
|
||
# variable is reinterpreted in place); a real Foo* value must be explicitly
|
||
# dereferenced. Scalar integer targets were already rejected above.
|
||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
|
||
target_kind: int = target_ty.kind()
|
||
if target_kind == llvmlite.LLVMType.Struct or target_kind == llvmlite.LLVMType.Array:
|
||
# 规则 3:非基本类型的「指针 → 值」隐式转换(Foo* → Foo)一律禁止,
|
||
# 唯一例外=「构造物化」:右值来自该类型的构造调用时放行(见 _ctor_call_agg_ty)。
|
||
ctor_p2v: llvmlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||
if self._same_agg_ty(builder.Pool, ctor_p2v, target_ty) == 0:
|
||
m_p2v: str = "非基本类型的值/指针隐式转换已禁止(构造物化除外):不能把指针类型隐式转为非基本类型值(Foo* → Foo),如需解引用请显式 c.deref(p) / *p,或显式强转"
|
||
p2v_txt: str = GenApi.escape_fstring_copy(
|
||
f";源类型 {self._type_text(builder.Pool, val.Ty)} → 目标值类型 {self._type_text(builder.Pool, target_ty)}")
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
f"{m_p2v}{p2v_txt}", "CastError")
|
||
return val
|
||
return llvmlite.build_load(builder, target_ty, val)
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
f"指针隐式转非指针值已禁止:必须显式解引用(c.Deref(p) / *p)或显式强转(uint64(p))", "CastError")
|
||
return val
|
||
# 指针 → 指针: 隐式转换须满足两条新规则(见 _ptr_cast_reason 单一真源):
|
||
# 规则 1 通用指针(t.CPtr / i8*)与任何 T* 双向互转都放行;
|
||
# 规则 2 T* → U* 允许 ⇔ 被指类型 T → U 无损,非基本类型一律禁止;
|
||
# 规则 3 非基本类型 T 与其指针 T* 之间一律禁止;
|
||
# 规则 4 完全相同类型(含 Foo* → Foo*)不算转换,照常通过。
|
||
# 违反者沿用既有 fail-fast 风格报错(CastError + 「…已禁止:」前缀)。
|
||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
|
||
pc: int = self._ptr_cast_reason(builder.Pool, val.Ty, target_ty)
|
||
if pc != PC_OK:
|
||
reason: str = self._ptr_cast_reason_text(pc, builder.Pool, val.Ty, target_ty)
|
||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
|
||
reason, "CastError")
|
||
return val
|
||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||
return val
|
||
|
||
def _zext_or_sext_val(self, pool: memhub.MemBuddy*,
|
||
val: llvmlite.Value*,
|
||
node: ast.AST* = None,
|
||
trans: HT.Translator* = None) -> int:
|
||
"""统一整数拓宽方向:1=zext(无符号语义),0=sext
|
||
|
||
委托到 ExprGenOps.should_zext_widen(拓宽方向的**单一真源**),避免两套
|
||
规则各自演化导致同一「窄→宽」在 coerce 路径与比较/二元路径结论相反。
|
||
coerce 路径无「宽侧值」上下文,wide_val 传 None 使「宽侧常量超窄侧有符号
|
||
范围」那条启发式自然失效;其余三条(i1/i8、源无符号、CDefine 高位常量)
|
||
与旧实现一致或更强。
|
||
|
||
trans 供 _node_type_unsigned 经 SymTab 解析变量注解的符号性(Name/
|
||
Subscript 形态必需);省略则仅能识别显式 t.CUIntXXT 强转。
|
||
"""
|
||
if val is None or val.Ty is None: return 0
|
||
return self.Gen.ExprGenOps.should_zext_widen(pool, val, None, node, trans)
|
||
|
||
# translate_ptr_arith:指针算术 ptr±int,用 getelementptr(按 pointee 元素大小缩放索引,对齐 TPC ExprGen.emit_binary_op / HandlesAugAssign.py);数组 pointee([N x ty]*)先 decay 为元素指针(GEP 0,0),避免以整个数组为步长
|
||
def translate_ptr_arith(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
ptr_val: llvmlite.Value*,
|
||
int_val: llvmlite.Value*,
|
||
op: int) -> llvmlite.Value*:
|
||
"""指针算术: ptr + int / ptr - int → GEP(按元素大小缩放索引)"""
|
||
if ptr_val is None or int_val is None or ptr_val.Ty is None: return None
|
||
# 提取 pointee 类型(非指针 → 不处理返回 None)
|
||
elem_ty: llvmlite.LLVMType* = None
|
||
match ptr_val.Ty:
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
elem_ty = pointee
|
||
case _:
|
||
return None
|
||
if elem_ty is None: return None
|
||
# 索引统一到 i64(对齐 TPC zext 到 i64)
|
||
i64_ty: llvmlite.LLVMType* = llvmlite.Int64(pool)
|
||
idx: llvmlite.Value* = self.coerce_to_type(builder, int_val, i64_ty, None)
|
||
if idx is None: return None
|
||
# ptr - int → 负索引(LLVM GEP 索引为加法语义)
|
||
if op == ast.OpKind.Sub:
|
||
neg_idx: llvmlite.Value* = llvmlite.build_sub(builder, llvmlite.const_int64(pool, 0), idx)
|
||
if neg_idx is None: return None
|
||
idx = neg_idx
|
||
# 数组 pointee → 先 decay 为元素指针(getelementptr [N x ty], [N x ty]* %p, i32 0, i32 0)
|
||
if self.is_array_type(elem_ty) != 0:
|
||
inner_ty: llvmlite.LLVMType* = None
|
||
match elem_ty:
|
||
case llvmlite.LLVMType.Array(inner, _):
|
||
inner_ty = inner
|
||
case _:
|
||
return None
|
||
if inner_ty is None:
|
||
return None
|
||
decayed: llvmlite.Value* = llvmlite.build_gep_array(builder, elem_ty, inner_ty, ptr_val, llvmlite.const_int64(pool, 0))
|
||
if decayed is None: return None
|
||
return llvmlite.build_gep(builder, inner_ty, decayed, idx)
|
||
return llvmlite.build_gep(builder, elem_ty, ptr_val, idx)
|
||
def InitExprGen(self) -> int:
|
||
"""初始化 ExprGen 族字段(构造时由 Generator 调用)"""
|
||
if self is None: return 1
|
||
self.DocKeys = None
|
||
self.DocValues = None
|
||
self.DocCount = 0
|
||
self.DiagCDefineCount = 0
|
||
self.DiagNameValueCount = 0
|
||
self.DiagTranslateValueCount = 0
|
||
return 0
|