feat(tpv): S2 落无符号判定双写闸门,并修 i8 拓宽丢符号与 WCHARPTR 旧判

- TypeSpec: 新增 SpecFromLLVMType(LLVM 类型 -> TypeSpec 结构转换器,当前无调用点);SpecSignFromTypeName 拆出零分配切片版 Span
- TypeGenCType: 新增 lookup_ctypedef_span 与独立符号性真源 spec_sign_of_annot(精确名白名单 + CTypedef 链逐层展开)
- ExprGenOps: 旧判定改名 *_legacy,外层加双写闸门 _spec_sign_check(新真源明确表态且不一致即 fatal;S2 不切读)
- ExprGenOps: 修旧符号性两个洞 -- 子串表补 WORD、裸别名沿 CTypedef 链展开(闸门实测暴露 WCHARPTR 旧判 signed、新真源 unsigned)
- ExprGenOps: should_zext_widen 的 i8 分支补「源注解明确有符号则 sext」例外,修 b8470c1 引入的 TypeCastTest 回归(t.CInt32T(v) 丢符号);新增 _node_type_signed
- TypeGenBase: 补 import lib.core.Gen.Type.TypeSpec(此前遗漏)并接入驻留表 ensure_spec_arena(PermPool 惰性创建)
- 修两条链接错误: TypeSpec.vp 残留 stdlib.atoi 改 string.atoi;模块别名与模块内类名同名会被误解析为该类静态方法(隐式补 self 实参),四处导入别名统一改 TSpec
This commit is contained in:
TermiNexus committed 2026-09-11 20:32:55 +08:00
1 parent b1090abe70
commit b7100e9dc1
5 files changed
+598 -17

No files matched your search

+193 -11
View File
@@ -7,6 +7,7 @@ import memhub
import string
import viperlib
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeSpec as TSpec
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
@@ -178,7 +179,9 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
对齐 TPC 的 _check_node_unsigned / HandlesExprOps 拓宽规则:
1. i1 (bool) → zext(1 保持 1,而非 sext 的 0xFF...)
2. i8 → zext(系统编程中 i8 几乎总是无符号字节)
2. i8 → zext(系统编程中 i8 几乎总是无符号字节);
例外:源注解**明确有符号**(t.CInt8T/int8/i8/char…)→ sext 保号
(判据 _node_type_signed,见下)
3. 宽侧是常量且其值超出窄侧位宽的有符号范围 → zext
(常量"想要"无符号解释,如 0xAABBCCDD > INT32_MAX 与
uint32 值比较,sext 会把 0xAABBCCDD 变成
@@ -190,6 +193,12 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if narrow_bits == 1:
return 1
if narrow_bits == 8:
# i8 默认按「无符号字节」处理(系统编程惯例,保持既有行为);
# 但**源注解明确为有符号类型**时必须 sext 保号:
# TypeCastTest 回归 —— `t.CInt32T(v)`(v: t.CInt8T = -50)期望 -50,
# 此前 b8470c1 把强转路径统一到本函数后撞上无条件 zext 而变成 206。
if self._node_type_signed(pool, narrow_node, trans) != 0:
return 0
return 1
# 宽侧常量启发式
if wide_val is not None and wide_val.IsConst == 1:
@@ -325,8 +334,65 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
fi_u += 1
return 0
return 0
def _type_name_unsigned(self, type_name: str) -> int:
"""类型名是否为无符号整数类型(精确匹配,避免子串误伤如 nUIntCount)"""
def _node_type_signed(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> int:
"""源节点是否**明确有符号**整数(独立真源的 SIGNED 分支;未知返回 0)
只服务 should_zext_widen 的 i8 分支:i8 默认按「无符号字节」zext,
但源注解明确有符号(t.CInt8T / int8 / i8 / char …)时须 sext 保号。
判据走独立符号性真源(精确名 + CTypedef 链),与 _node_type_unsigned 的
遍历形态对应,但只覆盖**注解直接可见**的节点形态(Call(t.X) / Name /
Subscript);其余返回 0,即保持 i8 的既有默认行为(不扩大改动面)。
"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 \
and at.attr is not None:
if self._spec_sign_from_annot(at.attr) == TSpec.TS_SIGN_SIGNED:
return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None:
if self._spec_sign_from_annot(nm.id) == TSpec.TS_SIGN_SIGNED:
return 1
if trans is not None:
ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if ve is not None and ve.AnnotClassName is not None:
if self._spec_sign_from_annot(ve.AnnotClassName) == TSpec.TS_SIGN_SIGNED:
return 1
return 0
if k == ast.ASTKind.Subscript:
sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub_s.value is not None and sub_s.value.kind() == ast.ASTKind.Name:
snm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_s.value)
if snm.id is not None and trans is not None:
ve_s: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, snm.id)
if ve_s is not None and ve_s.AnnotClassName is not None:
if self._spec_sign_from_annot(ve_s.AnnotClassName) == TSpec.TS_SIGN_SIGNED:
return 1
return 0
return 0
def _last_dot_tail(self, name: str) -> str:
"""取最后一个 '.' 之后的部分(无 '.' 返回 None);用于模块限定名退查"""
if name is None:
return None
dot: str = string.strrchr(name, 46)
if dot is None:
return None
return dot + 1
def _type_name_unsigned_legacy(self, type_name: str) -> int:
"""[旧路径] 类型名是否为无符号整数类型(精确匹配 + CTypedef 展开一层)"""
if type_name is None:
return 0
# t.CUIntXXT 形式
@@ -338,8 +404,12 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
# CSizeT 此前缺失 → viperio.Buf.write 的 capacity/length 比较生成
# icmp sgt/sle/slt(应 ult/ule/ult),A5 同族残留根因。
if string.strcmp(type_name, "CSizeT") == 0: return 1
if string.strcmp(type_name, "SSizeT") == 0: return 1
if string.strcmp(type_name, "CUIntPtrT") == 0: return 1
# bool / t.CBool:i1 属无符号语义(should_zext_widen 对 narrow_bits==1
# 已强制 zext,此处登记仅为与独立真源口径一致)。
# 原表把 SSizeT 列为无符号(A3 缺陷)——已删:SSizeT 实为有符号。
if string.strcmp(type_name, "CBool") == 0: return 1
if string.strcmp(type_name, "bool") == 0: return 1
# stdint 裸别名(from stdint import *)
if string.strcmp(type_name, "UInt8T") == 0: return 1
if string.strcmp(type_name, "UInt16T") == 0: return 1
@@ -358,17 +428,32 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if string.strcmp(type_name, "UINT") == 0: return 1
if string.strcmp(type_name, "ULONG") == 0: return 1
# [阶段3] CTypedef 动态表兜底:stdint 别名(DWORD/uint32_t 等)走库定义
# 展开,按 _annot_str_unsigned 子串规则判断符号性(单一真源)。
ts_u: str = self.Gen.TypeGenCType.lookup_ctypedef(type_name)
if ts_u is not None and self.Gen.TypeGenCType.CTypedefFound != 0:
if self._annot_str_unsigned(ts_u) != 0:
# 展开,按 _annot_str_unsigned_legacy 子串规则判断符号性。
# S2 修正:原实现**只展开一层** ⇒ 多层别名漏判(如 WCHAR→WORD→
# t.CUInt16T、FSIZE_t→DWORD→t.CUInt32T);改为沿链展开至不动点。
cur_u: str = type_name
depth_u: int = 0
while depth_u < 16:
ts_u: str = self.Gen.TypeGenCType.lookup_ctypedef(cur_u)
if ts_u is None or self.Gen.TypeGenCType.CTypedefFound == 0:
# 模块限定名(如 stdint.u32 / builtins.DWORD)退到最后一段重查
tail_u: str = self._last_dot_tail(cur_u)
if tail_u is not None:
ts_u = self.Gen.TypeGenCType.lookup_ctypedef(tail_u)
if ts_u is None or self.Gen.TypeGenCType.CTypedefFound == 0:
break
if self._annot_str_unsigned_legacy(ts_u) != 0:
return 1
if string.strcmp(ts_u, cur_u) == 0:
break
cur_u = ts_u
depth_u += 1
return 0
def _annot_str_unsigned(self, annot: str) -> int:
"""检查类型注解字符串是否引用无符号整型(子串扫描)
def _annot_str_unsigned_scan(self, annot: str) -> int:
"""[旧路径·子串表] 纯子串扫描(不做别名链展开)
用于复合注解(t.CArray[uint8, 16] / uint8 | t.CPtr),
_type_name_unsigned 的精确匹配对这类串不适用。
_type_name_unsigned_legacy 的精确匹配对这类串不适用。
"""
if annot is None:
return 0
@@ -388,12 +473,109 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if string.strstr(annot, "uint32") is not None: return 1
if string.strstr(annot, "uint64") is not None: return 1
if string.strstr(annot, "CUnsigned") is not None: return 1
# bool / CBool:i1 属无符号语义(与 _type_name_unsigned_legacy 同口径)
if string.strstr(annot, "CBool") is not None: return 1
if string.strstr(annot, "bool") is not None: return 1
if string.strstr(annot, "BYTE") is not None: return 1
if string.strstr(annot, "UCHAR") is not None: return 1
if string.strstr(annot, "USHORT") is not None: return 1
if string.strstr(annot, "UINT") is not None: return 1
if string.strstr(annot, "ULONG") is not None: return 1
# WORD 系:WORD/DWORD/QWORD/WCHAR/FSIZE_t/LBA_t 均为无符号(WORD=t.CUInt16T)。
# 原表漏 WORD ⇒ 复合别名 `WCHARPTR = WORD | t.CPtr` 被旧判为 signed
# (S2 闸门实测暴露:旧判 signed / 新真源 unsigned,旧错)。
if string.strstr(annot, "WORD") is not None: return 1
return 0
def _is_simple_ident(self, s: str) -> int:
"""是否简单标识符(仅标识符字符与 '.',不含空白/运算符/括号/逗号)"""
if s is None:
return 0
n: sizet = string.strlen(s)
if n == 0:
return 0
i: sizet = 0
while i < n:
if s[i] == ' ' or s[i] == '\t' or s[i] == '\n' or s[i] == '\r':
return 0
if s[i] == '|' or s[i] == '[' or s[i] == ']' or s[i] == '(' or s[i] == ')':
return 0
if s[i] == '*' or s[i] == ',' or s[i] == '+':
return 0
i += 1
return 1
def _annot_str_unsigned_legacy(self, annot: str) -> int:
"""[旧路径] 注解串无符号判定 = 子串扫描 + 裸别名沿 CTypedef 链展开后再扫
为何需要链展开:`_node_type_unsigned` 的 Subscript/Attribute 分支会把
**裸别名**(`sizet` / `u64` / `WORD` …)当作注解串传进来,而子串表只认
`CSizeT` / `uint64` 等长名 ⇒ 这些别名此前被漏判(A5 同族)。链展开后
其目标(`t.CSizeT` / `t.CUInt64T`)能被子串表命中。
复合串(含 '|' / '[' 等)不做链展开,交给子串表。
"""
if self._annot_str_unsigned_scan(annot) != 0:
return 1
if annot is None or self._is_simple_ident(annot) == 0:
return 0
cur: str = annot
d: int = 0
while d < 16:
ts: str = self.Gen.TypeGenCType.lookup_ctypedef(cur)
if ts is None or self.Gen.TypeGenCType.CTypedefFound == 0:
tail: str = self._last_dot_tail(cur)
if tail is not None:
ts = self.Gen.TypeGenCType.lookup_ctypedef(tail)
if ts is None or self.Gen.TypeGenCType.CTypedefFound == 0:
break
if self._annot_str_unsigned_scan(ts) != 0:
return 1
if string.strcmp(ts, cur) == 0:
break
cur = ts
d += 1
return 0
# ============================================================
# S2 双写(无符号判定块)——独立真源与旧判定比对,不一致即 fatal
#
# 新真源:TypeGenCType.spec_sign_of_annot(精确名白名单 + CTypedef 链逐层展开),
# 与旧路径(strstr 子串扫描 + 只展开一层)**完全独立**,故能暴露 A2~A5 类错判。
# 读仍走旧路径(S3 才切读);此处只做一致性闸门(D6-3 决策:直接 fatal)。
#
# 只在**新真源明确表态**时比对:别名/复合注解若新真源返回 UNKNOWN
# (尚未覆盖),则不参与比对,避免用"未知"误杀旧路径的正确判定。
# ============================================================
def _spec_sign_from_annot(self, annot: str) -> int:
"""新真源入口:注解 → TS_SIGN_*(UNKNOWN 表示不表态)"""
return self.Gen.TypeGenCType.spec_sign_of_annot(annot, 0)
def _spec_sign_check(self, text: str, old_unsigned: int) -> None:
"""S2 一致性闸门:新真源明确表态且与旧判定不一致 → fatal"""
if text is None:
return
new_sign: int = self._spec_sign_from_annot(text)
if new_sign == TSpec.TS_SIGN_UNKNOWN:
return
new_unsigned: int = 0
if new_sign == TSpec.TS_SIGN_UNSIGNED:
new_unsigned = 1
if new_unsigned == old_unsigned:
return
old_txt: str = "signed"
if old_unsigned != 0:
old_txt = "unsigned"
new_txt: str = "signed"
if new_unsigned != 0:
new_txt = "unsigned"
msg: str = f"[S2 符号性双写] 注解 '{text}':旧判定={old_txt},新真源={new_txt}"
self.Gen.TypeGenErr.fatal_error(None, msg, "TypeError")
def _type_name_unsigned(self, type_name: str) -> int:
"""[S2 双写] 类型名无符号判定 + 独立真源比对"""
old: int = self._type_name_unsigned_legacy(type_name)
self._spec_sign_check(type_name, old)
return old
def _annot_str_unsigned(self, annot: str) -> int:
"""[S2 双写] 注解串无符号判定 + 独立真源比对"""
old: int = self._annot_str_unsigned_legacy(annot)
self._spec_sign_check(annot, old)
return old
def translate_compare(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
+2 -1
View File
@@ -6,7 +6,8 @@ import memhub
# TypeSpec:统一类型描述符(Debugger/_Type.md 第 6 节设计)。
# S1 阶段仅 import 使该模块参与编译(尚未在任何条目上使用其字段);
# S2 起在 VarEntry/FieldEntry/FuncEntry 上加 Spec 字段并双写。
import lib.core.Gen.Type.TypeSpec as TypeSpec
# 别名用 TSpec:模块内类名同为 TypeSpec,别名同名会被解析器误判为类静态方法。
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
+34 -3
View File
@@ -12,7 +12,11 @@ import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.CompilerFactory as CompilerFactory
# TypeSpec 模块导入别名用 TSpec(模块内类名也叫 TypeSpec,别名同名会被解析器
# 当成"类 TypeSpec 的静态方法",导致跨模块调用被 mangled 成 <sha1>.TypeSpec.<fn>
# 并隐式补 self 实参 → undefined symbol)。
import lib.core.Gen.Type.TypeSpec as TSpec
@@ -94,8 +98,13 @@ class TypeGenBase:
# --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) ---
ConstEvalOk: int
# --- 全局当前文件名(原 _g_current_file) ---
CurrentFile: str
# --- 全局当前文件名(原 _g_current_filename) ---
CurrentFile: str
# --- TypeSpec 驻留表(统一类型描述符,Debugger/_Type.md 第 6 节) ---
# 惰性创建(ensure_spec_arena);由类型子系统自身持有,避免模块级全局表。
# 实例与内部数组均 PermPool 分配 ⇒ 跨文件/跨 mark 存活。
Specs: TSpec.TypeSpecArena | t.CPtr
def InitTypeGen(self) -> int:
"""初始化类型 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
if self is None:
@@ -131,7 +140,29 @@ class TypeGenBase:
self.ImportAliasBuf = None
self.ConstEvalOk = 0
self.CurrentFile = None
self.Specs = None
return 0
# ============================================================
# ensure_spec_arena - 惰性创建 TypeSpec 驻留表(幂等)
#
# 用 PermPool 分配 arena 本体;表内部缓冲由 TSpec.SpecArenaInit 分配
# (同样 PermPool)。失败返回 None(调用方按 fail-fast 处理,不静默退化)。
# ============================================================
def ensure_spec_arena(self) -> TSpec.TypeSpecArena | t.CPtr:
"""确保 TypeSpec 驻留表已创建,返回 arena;失败返回 None"""
if self.Specs is not None:
return self.Specs
size: sizet = TSpec.TypeSpecArena.__sizeof__()
arena: TSpec.TypeSpecArena | t.CPtr = (TSpec.TypeSpecArena | t.CPtr)(
t.CVoid(CompilerFactory.factory.PermPool.alloc(size), t.CPtr))
if arena is None:
return None
string.memset(arena, 0, size)
if TSpec.SpecArenaInit(arena, 0) != 0:
return None
self.Specs = arena
return arena
# ============================================================
# P2 迁移方法(函数体来自 HandlesType.vp,全局变量改 self.字段,
# 同模块函数调用改 self.方法;签名与 HandlesType 原函数一致)
+149 -1
View File
@@ -13,7 +13,8 @@ import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeSpec as TSpec
# builtins 诊断串自有缓冲长度(仅服务"变量类型注解解析失败"报错文本)
TYPEDEF_DIAG_BUF: t.CDefine = 256
@@ -122,6 +123,153 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
def is_ctypedef_found(self) -> int:
"""检查上次 lookup_ctypedef 是否找到值"""
return self.CTypedefFound
def lookup_ctypedef_span(self, text: str, start: sizet, end: sizet) -> str:
"""按名字切片查 CTypedef(零分配,不复制子串)
与 lookup_ctypedef 同表;命中置 CTypedefFound=1 并返回类型表达式串,
未命中返回 None(CTypedefFound=0)。用于符号性独立真源的别名链展开。
"""
self.CTypedefFound = 0
if text is None or end <= start:
return None
if self.CTypedefNames is None:
return None
want: sizet = end - start
n: sizet = len(self.CTypedefNames)
i: sizet
for i in range(n):
nm: str = self.CTypedefNames.get(i)
if nm is None:
continue
if string.strlen(nm) != want:
continue
hit: int = 1
k: sizet = 0
while k < want:
if nm[k] != text[start + k]:
hit = 0
break
k += 1
if hit != 0:
ts: str = self.CTypedefTypeStrs.get(i)
self.CTypedefFound = 1
return ts
return None
# ============================================================
# 独立符号性真源(S2 双写比对用;与旧 _annot_str_unsigned 完全独立)
#
# 旧实现:strstr 子串扫描 + CTypedef 只展开一层 ⇒ 两类错判:
# A4 `BYTEPTR` 含 `BYTE` 被判无符号;A5 sha1 前缀名含 `uint32` 命中;
# 多层别名(WCHAR→WORD→t.CUInt16T)因只展开一层而漏判。
# 本实现:精确名白名单(TSpec.SpecSignFromTypeNameSpan)+ 别名链逐层展开。
# ============================================================
def spec_sign_of_annot(self, text: str, depth: int = 0) -> int:
"""按注解文本判定其整数值符号性(独立真源),未覆盖返回 TS_SIGN_UNKNOWN"""
if text is None or depth > 16:
return TSpec.TS_SIGN_UNKNOWN
return self._spec_sign_of_span(text, 0, string.strlen(text), depth)
def _spec_sign_of_span(self, text: str, start: sizet, end: sizet,
depth: int) -> int:
"""顶层 '|' 分段,返回首个能确定的符号性(`X | t.CPtr` 取 X)"""
seg_start: sizet = start
bracket_depth: int = 0
i: sizet = start
while i <= end:
is_split: int = 0
if i == end:
is_split = 1
elif text[i] == '|' and bracket_depth == 0:
is_split = 1
if is_split != 0:
r: int = self._spec_sign_of_segment(text, seg_start, i, depth)
if r != TSpec.TS_SIGN_UNKNOWN:
return r
seg_start = i + 1
elif text[i] == '[' or text[i] == '(':
bracket_depth += 1
elif text[i] == ']' or text[i] == ')':
if bracket_depth > 0:
bracket_depth -= 1
i += 1
return TSpec.TS_SIGN_UNKNOWN
def _spec_sign_of_segment(self, text: str, start: sizet, end: sizet,
depth: int) -> int:
"""单个片段:去空白/括号 → 指针后缀 → 下标取元素 → 末段名 → 别名链展开"""
s: sizet = start
e: sizet = end
while s < e:
if text[s] != ' ' and text[s] != '\t' and text[s] != '\n':
break
s += 1
while e > s:
if text[e - 1] != ' ' and text[e - 1] != '\t' and text[e - 1] != '\n':
break
e -= 1
# 去外层成对括号(如 "(t.CUInt32T)")
while e > s + 1:
if text[s] != '(' or text[e - 1] != ')':
break
s += 1
e -= 1
while s < e:
if text[s] != ' ' and text[s] != '\t':
break
s += 1
while e > s:
if text[e - 1] != ' ' and text[e - 1] != '\t':
break
e -= 1
if e <= s:
return TSpec.TS_SIGN_UNKNOWN
# C 指针后缀 '*' → 指针,不表态(指针语义由 PtrLevel 表达)
if text[e - 1] == '*':
return TSpec.TS_SIGN_UNKNOWN
# 泛型/数组/rowptr 下标:IDENT[INNER, ...] → 取 INNER(元素决定值符号性)
bracket: sizet = 0
has_bracket: int = 0
k: sizet = s
while k < e:
if text[k] == '[':
bracket = k
has_bracket = 1
break
k += 1
if has_bracket != 0:
inner_s: sizet = bracket + 1
inner_e: sizet = inner_s
d: int = 0
while inner_e < e:
if text[inner_e] == '[' or text[inner_e] == '(':
d += 1
elif text[inner_e] == ']':
if d == 0:
break
d -= 1
elif text[inner_e] == ')':
if d > 0:
d -= 1
elif text[inner_e] == ',':
if d == 0:
break
inner_e += 1
return self._spec_sign_of_span(text, inner_s, inner_e, depth)
# 去模块限定前缀:取最后一个 '.' 之后的名字
name_s: sizet = s
w: sizet = s
while w < e:
if text[w] == '.':
name_s = w + 1
w += 1
exact: int = TSpec.SpecSignFromTypeNameSpan(text, name_s, e)
if exact != TSpec.TS_SIGN_UNKNOWN:
return exact
# 精确名未覆盖 → 沿 CTypedef 链展开(depth 限制防环)
if depth >= 16:
return TSpec.TS_SIGN_UNKNOWN
ts: str = self.lookup_ctypedef_span(text, name_s, e)
if ts is None or self.CTypedefFound == 0:
return TSpec.TS_SIGN_UNKNOWN
return self._spec_sign_of_span(ts, 0, string.strlen(ts), depth + 1)
def clear_ctypedefs(self) -> None:
"""清空 CTypedef 表(模块切换时调用)"""
self.CTypedefNames = None
+220 -1
View File
@@ -59,6 +59,122 @@ SPEC_INTERN_MAX: t.CDefine = 8192
SPEC_TEXT_MAX: t.CDefine = 256
# ============================================================
# 符号性精确名表 - 按「名字切片」判定(**不做子串扫描**)
#
# 为什么必须精确匹配:旧实现 `_annot_str_unsigned` 用 strstr 子串扫描,
# 已确认两类误判(Debugger/_Type.md A4/A5):
# - `BYTEPTR`(本质 i8* 指针)含子串 `BYTE` → 被判为无符号整数;
# - 任何含 `uint32` 子串的名字(含 sha1 前缀名)都会命中。
#
# 覆盖范围界定:本模块**只认 t/c 核心原语名与少数内建名**;stdint 的别名
# (DWORD/WORD/QWORD/size_t/uint32_t/u32/…)由调用方沿 CTypedef 链**逐层展开**
# 后再落到本函数(链展开需要 CTypedef 表,不在本模块做,避免反向依赖)。
#
# 规范性修正(相对旧表):
# - `SSizeT` 是**有符号**(旧 `_type_name_unsigned` 把它列为无符号,为 A3 缺陷);
# - `BOOL` 按 stdint.vp 定义是 `t.CInt`(**有符号**),并非布尔无符号;
# - 指针名(`BYTEPTR/UINTPTR/INTPTR/CHARPTR/…`)在此返回 UNKNOWN ——
# 指针的"无符号"语义由 PtrLevel 表达,与整数符号性无关。
# ============================================================
# ============================================================
# _spec_name_eq - 比较 text[start:end] 与字面量 lit 是否逐字符相等(零分配)
# ============================================================
def _spec_name_eq(text: str, start: sizet, end: sizet, lit: str) -> int:
"""切片名比较:长度相同且逐字符相等返回 1,否则 0"""
if text is None or lit is None:
return 0
if end <= start:
return 0
ln: sizet = string.strlen(lit)
if end - start != ln:
return 0
k: sizet = 0
while k < ln:
if text[start + k] != lit[k]:
return 0
k += 1
return 1
# ============================================================
# SpecSignFromTypeNameSpan - 按名字切片精确判定符号性(零分配)
# 返回 TS_SIGN_UNSIGNED / TS_SIGN_SIGNED / TS_SIGN_UNKNOWN(不表态)
# ============================================================
def SpecSignFromTypeNameSpan(text: str, start: sizet, end: sizet) -> int:
"""按 text[start:end] 精确判定符号性,未覆盖返回 TS_SIGN_UNKNOWN"""
if text is None:
return TS_SIGN_UNKNOWN
# --- t/c 核心无符号原语 ---
if _spec_name_eq(text, start, end, "CUInt8T") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUInt16T") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUInt32T") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUInt64T") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedChar") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedShort") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedInt") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedLong") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedLongLong") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CSizeT") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUIntPtrT") != 0:
return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CBool") != 0:
return TS_SIGN_UNSIGNED
# --- 内建小写标量 ---
if _spec_name_eq(text, start, end, "bool") != 0:
return TS_SIGN_UNSIGNED
# --- t/c 核心有符号原语 ---
if _spec_name_eq(text, start, end, "CInt8T") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt16T") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt32T") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt64T") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CSignedChar") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CShort") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CLong") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CLongLong") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CPtrDiffT") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CChar") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "int") != 0:
return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "char") != 0:
return TS_SIGN_SIGNED
return TS_SIGN_UNKNOWN
# ============================================================
# SpecSignFromTypeName - 类型名(NUL 终止串)精确判定符号性
# ============================================================
def SpecSignFromTypeName(name: str) -> int:
"""按类型名精确判定符号性,未覆盖返回 TS_SIGN_UNKNOWN"""
if name is None:
return TS_SIGN_UNKNOWN
return SpecSignFromTypeNameSpan(name, 0, string.strlen(name))
# ============================================================
# TypeSpec - 类型描述符
#
@@ -497,7 +613,7 @@ def _text_to_spec(text: str, depth: int) -> TypeSpec | t.CPtr:
return _spec_make(TS_VOID, 0, TS_SIGN_UNKNOWN)
# i32 / u64 / f64
if text[0] == 'u' or text[0] == 'i' or text[0] == 'f':
bits: int = stdlib.atoi(text + 1)
bits: int = string.atoi(text + 1)
if bits <= 0:
return None
if text[0] == 'u':
@@ -671,6 +787,109 @@ def SpecBitWidth(sp: TypeSpec | t.CPtr) -> int:
return sp.Bits
# ============================================================
# SpecFromLLVMType - 已解析 LLVM 类型 → TypeSpec(结构翻译)
#
# 只做**结构层**翻译:Int / Float / Void / Ptr / Array / Func / Struct。
# - 指针层级:由 Ptr 递归得到(NewTypePtr 内 PtrLevel = Elem.PtrLevel + 1)
# - 数组维度:取 Array.Count(元素语义三元组由 NewTypeArray 填)
# - 函数签名:Ret + 形参数组(沿 ParamNode 链表遍历;llvmlite **无按下标取参**)
# - 结构体:取结构体名;匿名结构体退化为无名 TS_STRUCT
#
# ⚠️ 两个**已知局限**(有意为之,避免臆造):
# 1. 符号性:LLVM 类型层**无法区分 iN 与 uN**(都是 i32)⇒ 本转换器一律给
# `TS_INT + TS_SIGN_SIGNED`(与 SpecBaseText 的 "iN" 编码口径一致,保证驻留键
# 不产生"同键不同义")。**无符号性必须由注解侧(符号性真源)覆盖**;
# 消费方在 S3 切读前**不要直接信任**本 spec 的 IsSigned。
# 2. 变参:`...` 不在 `LLVMType.Func` 上(只存在于 `llvmlite.Function.IsVarArg`)
# ⇒ 本转换器恒置 IsVarArg=0;带变参的签名需由函数对象侧补写。
#
# 本函数当前**无调用点**(S2 只入库;接线在后续步骤)。
# ============================================================
def SpecFromLLVMType(arena: TypeSpecArena | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> TypeSpec | t.CPtr:
"""把已解析的 LLVM 类型翻译成 TypeSpec;不支持或为 None 时返回 None"""
if arena is None or ty is None:
return None
match ty:
case llvmlite.LLVMType.Int(bits):
if bits == 1:
return NewTypeSpec(arena, TS_BOOL, 1, TS_SIGN_UNSIGNED)
return NewTypeSpec(arena, TS_INT, bits, TS_SIGN_SIGNED)
case llvmlite.LLVMType.Float(bits):
return NewTypeSpec(arena, TS_FLOAT, bits, TS_SIGN_SIGNED)
case llvmlite.LLVMType.Void():
return NewTypeSpec(arena, TS_VOID, 0, TS_SIGN_UNKNOWN)
case llvmlite.LLVMType.Ptr(pointee):
return _spec_from_ptr(arena, pointee)
case llvmlite.LLVMType.Array(elem_ty, count):
return _spec_from_array(arena, elem_ty, count)
case llvmlite.LLVMType.Func(ret, params, pcount):
return _spec_from_func(arena, ret, params, pcount)
case llvmlite.LLVMType.Struct(_, _, sname):
return _spec_from_struct(arena, sname)
case _:
return None
def _spec_from_ptr(arena: TypeSpecArena | t.CPtr,
pointee: llvmlite.LLVMType | t.CPtr) -> TypeSpec | t.CPtr:
"""Ptr(pointee) → TypeSpec(PtrLevel/ElemBits/IsElemPtr 由 NewTypePtr 填)"""
inner: TypeSpec | t.CPtr = SpecFromLLVMType(arena, pointee)
if inner is None:
return None
return NewTypePtr(arena, inner)
def _spec_from_array(arena: TypeSpecArena | t.CPtr,
elem_ty: llvmlite.LLVMType | t.CPtr,
count: int) -> TypeSpec | t.CPtr:
"""[count x elem] → TypeSpec(ArrayCount/ElemBits/IsElemPtr 由 NewTypeArray 填)"""
inner: TypeSpec | t.CPtr = SpecFromLLVMType(arena, elem_ty)
if inner is None:
return None
return NewTypeArray(arena, inner, count)
def _spec_from_func(arena: TypeSpecArena | t.CPtr,
ret: llvmlite.LLVMType | t.CPtr,
params: llvmlite.ParamNode | t.CPtr,
pcount: int) -> TypeSpec | t.CPtr:
"""函数类型 → TypeSpec(形参沿 ParamNode 链表遍历;IsVarArg 恒 0,见上)"""
ret_s: TypeSpec | t.CPtr = SpecFromLLVMType(arena, ret)
if ret_s is None:
return None
if pcount <= 0:
no_params: sizet | t.CPtr = None
return NewTypeFunc(arena, ret_s, no_params, 0, 0)
params_arr: sizet | t.CPtr = CompilerFactory.factory.PermPool.alloc(8 * pcount)
if params_arr is None:
return None
node: llvmlite.ParamNode | t.CPtr = params
i: int = 0
while node is not None and i < pcount:
pty: llvmlite.LLVMType | t.CPtr = llvmlite.paramnode_get_ty(node)
ps: TypeSpec | t.CPtr = SpecFromLLVMType(arena, pty)
if ps is None:
return None
slot: sizet | t.CPtr = (sizet | t.CPtr)(
t.CVoid(uint64(params_arr) + uint64(i * 8), t.CPtr))
if slot is None:
return None
slot[0] = sizet(ps)
node = llvmlite.paramnode_get_next(node)
i += 1
return NewTypeFunc(arena, ret_s, params_arr, pcount, 0)
def _spec_from_struct(arena: TypeSpecArena | t.CPtr,
sname: str) -> TypeSpec | t.CPtr:
"""有名结构体 → TS_STRUCT + BaseName;匿名结构体 → 无名 TS_STRUCT"""
if sname is not None:
return NewTypeStruct(arena, TS_STRUCT, sname)
return NewTypeSpec(arena, TS_STRUCT, 0, TS_SIGN_UNKNOWN)
# ============================================================
# SpecSelfCheck - S1 自检(不参与编译流程,供手动调用/后续单测)
#