Files
ViperC/App/lib/core/Gen/Expr/ExprGenBase.vp
T
TermiNexus be14fa5f94 fix(ctor): REnum 变体构造识别取错限定名,导致构造物化豁免失效
_ctor_call_agg_ty 从被调方沿属性链一路走到链根裸名,三段式
llvmlite.LLVMType.Struct(...) 于是被判成限定名 'llvmlite'(非 REnum 类)⇒
返回 None ⇒ ClassGenDef.vp:128 的 *existing.Ty = LLVMType.Struct(...) 被
「指针 → 非基本类型值」禁令拦下。改为取「变体名左边那一段」:两段式取裸名
(EnumType.Variant)、三段式取属性名(llvmlite.LLVMType.Struct → LLVMType)。
2026-10-01 21:44:55 +08:00

1002 lines
59 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.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