refactor(tpv): 删除 IsPtrElement 字段,指针元素语义承载于 Spec.ElemBits
- TypeSpec 抽 _spec_clone(消除 SpecWithSign/Source/ElemBits 三处重复逐字段拷贝) - 新增 SpecWithElemBits:覆盖元素槽位宽(独立副本不驻留,ElemBits 不在驻留键内) - set_var_ptr_element 改写 Spec.ElemBits=64;Spec 不可承载(非指针/缺失)即 fail-fast - 新增 var_is_ptr_element(PtrLevel>=1 且 Elem.Bits==8 且 ElemBits==64,排除真实 i64* 与纯 str) - ExprGenName 4 处下标读写切读该入口;GenTypes 删 VarEntry.IsPtrElement
This commit is contained in:
1 parent
e55ec5fde0
commit
a4bc5dc0ce
4 files changed
+110
-47
No files matched your search
@@ -920,7 +920,7 @@ class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
if trans is not None:
|
||||
ve_pe_ts: GenTypes.VarEntry | t.CPtr = \
|
||||
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if ve_pe_ts is not None and ve_pe_ts.IsPtrElement == 1:
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe_ts) != 0:
|
||||
# bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值(i8*),
|
||||
# elem[i] 语义 = load [指针值 + i*8](8 字节指针元素)。
|
||||
# 必须先 load alloca 得指针值,再 GEP i8*(步长 8)——
|
||||
@@ -951,7 +951,7 @@ class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
if trans is not None:
|
||||
ve_pe_rd: GenTypes.VarEntry | t.CPtr = \
|
||||
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if ve_pe_rd is not None and ve_pe_rd.IsPtrElement == 1:
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe_rd) != 0:
|
||||
match pointee:
|
||||
case llvmlite.LLVMType.Int(bits_rd):
|
||||
if bits_rd == 8:
|
||||
@@ -1182,7 +1182,7 @@ class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
if trans is not None:
|
||||
ve_pe: GenTypes.VarEntry | t.CPtr = \
|
||||
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if ve_pe is not None and ve_pe.IsPtrElement == 1:
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe) != 0:
|
||||
# bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值,
|
||||
# 下标语义 = [指针值 + i*8](8 字节指针元素)。必须先 load
|
||||
# alloca 得指针值,再 GEP——否则 GEP base 用 alloca(&elem_ptr)
|
||||
@@ -1232,7 +1232,7 @@ class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
if trans is not None:
|
||||
ve_pe_cs: GenTypes.VarEntry | t.CPtr = \
|
||||
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if ve_pe_cs is not None and ve_pe_cs.IsPtrElement == 1:
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe_cs) != 0:
|
||||
# T|t.CPtr(退化为 i8*,如 _list.vp 的 elem_ptr)变量
|
||||
# 存数据区指针,元素为 8 字节指针:必须先 load alloca
|
||||
# 得指针值,再按 i8* 步长 GEP——否则 GEP base 用 alloca
|
||||
|
||||
@@ -67,11 +67,12 @@ class VarEntry:
|
||||
Name: str
|
||||
Alloca: llvmlite.Value | t.CPtr
|
||||
Used: int
|
||||
IsPtrElement: int # 标志: 1=注解为 bytes|t.CPtr 或 str|t.CPtr, 下标按 8 字节步长
|
||||
# TypeSpec(S3 起为**唯一类型承载**):由 add_var 用 SpecFromLLVMType 从
|
||||
# alloca.Ty 的 pointee 填充(PermPool 分配 ⇒ 跨文件安全),注解侧再由
|
||||
# set_var_annot_class_name 经 SpecWithSign / SpecWithSource 补整数符号性与
|
||||
# 注解来源名(Spec.SourceName,原 AnnotClassName 字段已并入)。
|
||||
# 注解来源名(Spec.SourceName,原 AnnotClassName 字段已并入),由
|
||||
# set_var_ptr_element 经 SpecWithElemBits 补「指针元素」语义(原
|
||||
# IsPtrElement 字段已并入 Spec.ElemBits)。
|
||||
Spec: TSpec.TypeSpec | t.CPtr
|
||||
|
||||
|
||||
|
||||
@@ -836,6 +836,40 @@ def SpecPointedTo(sp: TypeSpec | t.CPtr) -> TypeSpec | t.CPtr:
|
||||
return sp.Elem
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _spec_clone - 浅拷贝 TypeSpec(PermPool,**不驻留**)
|
||||
#
|
||||
# Elem/Ret/Params 指针共享子实例(子实例自身由 NewTypeXxx 驻留)。
|
||||
# 供 SpecWithSign / SpecWithSource / SpecWithElemBits 复用,避免多处重复
|
||||
# 逐字段拷贝(漏字段是最容易引入的隐蔽 bug:例如忘了 IsElemPtr)。
|
||||
# ============================================================
|
||||
def _spec_clone(sp: TypeSpec | t.CPtr) -> TypeSpec | t.CPtr:
|
||||
"""浅拷贝 sp(含全部字段),分配失败返回 None"""
|
||||
if sp is None:
|
||||
return None
|
||||
cp: TypeSpec | t.CPtr = _spec_alloc()
|
||||
if cp is None:
|
||||
return None
|
||||
cp.Kind = sp.Kind
|
||||
cp.Bits = sp.Bits
|
||||
cp.IsSigned = sp.IsSigned
|
||||
cp.PtrLevel = sp.PtrLevel
|
||||
cp.Elem = sp.Elem
|
||||
cp.ArrayCount = sp.ArrayCount
|
||||
cp.BaseName = sp.BaseName
|
||||
cp.SourceName = sp.SourceName
|
||||
cp.Ret = sp.Ret
|
||||
cp.Params = sp.Params
|
||||
cp.ParamCount = sp.ParamCount
|
||||
cp.IsVarArg = sp.IsVarArg
|
||||
cp.Storage = sp.Storage
|
||||
cp.ElemBits = sp.ElemBits
|
||||
cp.IsElemPtr = sp.IsElemPtr
|
||||
cp.DerefBase = sp.DerefBase
|
||||
cp.Ty = sp.Ty
|
||||
return cp
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SpecWithSign - 复制 Spec 并按注解覆盖整数符号性(TS_INT ↔ TS_UINT)
|
||||
#
|
||||
@@ -845,8 +879,7 @@ def SpecPointedTo(sp: TypeSpec | t.CPtr) -> TypeSpec | t.CPtr:
|
||||
# 的整数会得到相同驻留键 "i%d" 而撞键(同键不同义,返回错误实例)。
|
||||
# 注解侧知道真实符号性(uint32 等无符号名),须把 TS_INT 切为 TS_UINT。
|
||||
#
|
||||
# 浅拷贝结构(Elem/Ret/Params 指针共享已驻留子实例);仅当符号性实际变化才
|
||||
# 复制并驻留,否则返回原实例。驻留去重 ⇒ 同「结构+符号」共享一份。
|
||||
# 仅当符号性实际变化才复制并驻留,否则返回原实例。驻留去重 ⇒ 同「结构+符号」共享一份。
|
||||
# ============================================================
|
||||
def SpecWithSign(arena: TypeSpecArena | t.CPtr,
|
||||
sp: TypeSpec | t.CPtr,
|
||||
@@ -864,29 +897,16 @@ def SpecWithSign(arena: TypeSpecArena | t.CPtr,
|
||||
new_signed = TS_SIGN_SIGNED
|
||||
if new_kind == sp.Kind:
|
||||
return sp
|
||||
cp: TypeSpec | t.CPtr = _spec_alloc()
|
||||
cp: TypeSpec | t.CPtr = _spec_clone(sp)
|
||||
if cp is None:
|
||||
return None
|
||||
cp.Kind = new_kind
|
||||
cp.Bits = sp.Bits
|
||||
cp.IsSigned = new_signed
|
||||
cp.PtrLevel = sp.PtrLevel
|
||||
cp.Elem = sp.Elem
|
||||
cp.ArrayCount = sp.ArrayCount
|
||||
cp.BaseName = sp.BaseName
|
||||
# **不复制 SourceName**:本函数走 InternSpec(键 = SpecToText,不含 SourceName),
|
||||
# **清空 SourceName**:本函数走 InternSpec(键 = SpecToText,不含 SourceName),
|
||||
# 若把带来源名的副本写进表,后续同键查找会把该来源名**污染**给无关条目
|
||||
# (键相同 ⇒ 语义必须相同 ⇒ 表内实例的 SourceName 恒为 None)。
|
||||
# 需要来源名时由调用方随后用 SpecWithSource(独立副本)叠加。
|
||||
cp.Ret = sp.Ret
|
||||
cp.Params = sp.Params
|
||||
cp.ParamCount = sp.ParamCount
|
||||
cp.IsVarArg = sp.IsVarArg
|
||||
cp.Storage = sp.Storage
|
||||
cp.ElemBits = sp.ElemBits
|
||||
cp.IsElemPtr = sp.IsElemPtr
|
||||
cp.DerefBase = sp.DerefBase
|
||||
cp.Ty = sp.Ty
|
||||
cp.SourceName = None
|
||||
return InternSpec(arena, cp)
|
||||
|
||||
|
||||
@@ -927,30 +947,38 @@ def SpecWithSource(sp: TypeSpec | t.CPtr, name: str) -> TypeSpec | t.CPtr:
|
||||
return sp
|
||||
if sp.SourceName is not None and string.strcmp(sp.SourceName, name) == 0:
|
||||
return sp
|
||||
cp: TypeSpec | t.CPtr = _spec_alloc()
|
||||
cp: TypeSpec | t.CPtr = _spec_clone(sp)
|
||||
if cp is None:
|
||||
return sp
|
||||
cp.Kind = sp.Kind
|
||||
cp.Bits = sp.Bits
|
||||
cp.IsSigned = sp.IsSigned
|
||||
cp.PtrLevel = sp.PtrLevel
|
||||
cp.Elem = sp.Elem
|
||||
cp.ArrayCount = sp.ArrayCount
|
||||
cp.BaseName = sp.BaseName
|
||||
cp.Ret = sp.Ret
|
||||
cp.Params = sp.Params
|
||||
cp.ParamCount = sp.ParamCount
|
||||
cp.IsVarArg = sp.IsVarArg
|
||||
cp.Storage = sp.Storage
|
||||
cp.ElemBits = sp.ElemBits
|
||||
cp.IsElemPtr = sp.IsElemPtr
|
||||
cp.DerefBase = sp.DerefBase
|
||||
cp.Ty = sp.Ty
|
||||
if _spec_set_source_name(cp, name) != 0:
|
||||
return sp
|
||||
return cp
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SpecWithElemBits - 复制 Spec 并覆盖元素槽位宽(独立副本,**不驻留**)
|
||||
#
|
||||
# 用途(S4 的 IsPtrElement 替代):`str | t.CPtr` / `bytes | t.CPtr` /
|
||||
# `T | t.CPtr` / `64 位标量 | t.CPtr` 在 LLVM 层**都是单层 Ptr(i8)**
|
||||
# (PtrLevel=1 同时对应两种相反语义),无法据类型区分"1 字节步长"与
|
||||
# "8 字节槽步长";区分只能靠注解判定 ⇒ 注解侧把元素槽宽覆盖为 8 字节。
|
||||
#
|
||||
# ElemBits 是派生字段(**不在 SpecToText 键内**)⇒ 必须返回独立副本:
|
||||
# 若走 InternSpec 会命中键相同但 ElemBits 未覆盖的实例,覆盖丢失。
|
||||
# ============================================================
|
||||
def SpecWithElemBits(sp: TypeSpec | t.CPtr, elem_bits: int) -> TypeSpec | t.CPtr:
|
||||
"""复制 sp 并设置 ElemBits(独立副本,不驻留);无变化返回原实例"""
|
||||
if sp is None:
|
||||
return sp
|
||||
if sp.ElemBits == elem_bits:
|
||||
return sp
|
||||
cp: TypeSpec | t.CPtr = _spec_clone(sp)
|
||||
if cp is None:
|
||||
return sp
|
||||
cp.ElemBits = elem_bits
|
||||
return cp
|
||||
|
||||
|
||||
def SpecSourceName(sp: TypeSpec | t.CPtr) -> str:
|
||||
"""Spec 的注解来源名(未设置返回 None)"""
|
||||
if sp is None:
|
||||
|
||||
@@ -169,21 +169,55 @@ class VarGenMixin:
|
||||
return TSpec.SpecSourceName(entry.Spec)
|
||||
|
||||
# ============================================================
|
||||
# set_var_ptr_element — 标记变量为"指针到 str/bytes"类型
|
||||
# set_var_ptr_element — 标记变量为「指针元素(ptr-element)」语义
|
||||
#
|
||||
# 注解为 bytes|t.CPtr 或 str|t.CPtr 的变量,其 alloca 类型是 i8*,
|
||||
# 但下标访问应按 8 字节步长(i8** 语义),而非 1 字节步长(i8* 语义)。
|
||||
# 此标志在 HandlesAnnAssign 中根据注解形式设置。
|
||||
# 注解为 bytes|t.CPtr / str|t.CPtr / T|t.CPtr / 64位标量|t.CPtr 的变量,
|
||||
# 其 LLVM 类型退化为 i8*(Ptr(Int8)),但下标访问应按 **8 字节槽步长**
|
||||
# (i8** 语义),而非 1 字节步长(i8* 语义)。
|
||||
#
|
||||
# [S4] 原 VarEntry.IsPtrElement 布尔字段已删除,改由 Spec.ElemBits 承载:
|
||||
# 上述四类在 LLVM 层都是 Ptr(Int8)(ElemBits=8,无法区分),故注解侧把
|
||||
# 元素槽宽覆盖为 8 字节;消费点经 var_is_ptr_element 读取。
|
||||
# 由 AnnAssign 按注解形式(_is_ptr_element_annotation)调用。
|
||||
# ============================================================
|
||||
def set_var_ptr_element(self, symtab: GenTypes.SymbolTable | t.CPtr,
|
||||
name: str) -> int:
|
||||
"""标记变量为 ptr_element 类型,返回 0=成功 / 1=失败"""
|
||||
"""标记变量为 ptr-element(Spec.ElemBits=64),返回 0=成功 / 1=失败"""
|
||||
if symtab is None or name is None:
|
||||
return 1
|
||||
entry: GenTypes.VarEntry | t.CPtr = self.lookup_var_entry(symtab, name)
|
||||
if entry is None:
|
||||
return 1
|
||||
entry.IsPtrElement = 1
|
||||
# ptr-element 变量的 alloca 是 Ptr(Ptr(Int8)),其值类型 Ptr(Int8) ⇒ Spec 必为
|
||||
# PtrLevel>=1。不满足即上游异常(类型缺失/退化),fail-fast 暴露 ——
|
||||
# 若静默放过,读点 var_is_ptr_element 返回 0 → 下标按 1 字节误算(数据损坏)。
|
||||
if entry.Spec is None or entry.Spec.PtrLevel < 1:
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
None, f"变量 '{name}' 标记为 ptr-element,但其 Spec 不可承载指针语义",
|
||||
"InternalCompilerError")
|
||||
return 1
|
||||
entry.Spec = TSpec.SpecWithElemBits(entry.Spec, 64)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# var_is_ptr_element - 变量是否「指针元素」语义(原 VarEntry.IsPtrElement)
|
||||
#
|
||||
# 判据(三元组 ElemBits 承载):PtrLevel>=1 且元素位宽为 8(LLVM 层退化后的
|
||||
# i8*)且 ElemBits==64(注解侧覆盖的 8 字节槽)。
|
||||
# 后两个条件缺一不可:真实 i64* 变量 ElemBits 也为 64,但其 Elem.Bits=64,
|
||||
# 不会被误判;纯 str/bytes/char|t.CPtr 变量 ElemBits=8,同样不命中。
|
||||
# ============================================================
|
||||
def var_is_ptr_element(self, entry: GenTypes.VarEntry | t.CPtr) -> int:
|
||||
"""变量是否为 ptr-element 语义(1=是 / 0=否)"""
|
||||
if entry is None or entry.Spec is None:
|
||||
return 0
|
||||
sp: TSpec.TypeSpec | t.CPtr = entry.Spec
|
||||
if sp.PtrLevel < 1 or sp.Elem is None:
|
||||
return 0
|
||||
if sp.Elem.Bits != 8:
|
||||
return 0
|
||||
if sp.ElemBits == 64:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
|
||||
Reference in new issue
Block a user