diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index 1ae6d97..16dd993 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -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, diff --git a/App/lib/core/Gen/GenTypes.vp b/App/lib/core/Gen/GenTypes.vp index e236906..c27330d 100644 --- a/App/lib/core/Gen/GenTypes.vp +++ b/App/lib/core/Gen/GenTypes.vp @@ -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 # ============================================================ diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index 9017548..7d088a9 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -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 成 .TypeSpec. +# 并隐式补 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 原函数一致) diff --git a/App/lib/core/Gen/Type/TypeGenCType.vp b/App/lib/core/Gen/Type/TypeGenCType.vp index 21b7d77..76240e3 100644 --- a/App/lib/core/Gen/Type/TypeGenCType.vp +++ b/App/lib/core/Gen/Type/TypeGenCType.vp @@ -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 diff --git a/App/lib/core/Gen/Type/TypeSpec.vp b/App/lib/core/Gen/Type/TypeSpec.vp index 8dc4be1..845a939 100644 --- a/App/lib/core/Gen/Type/TypeSpec.vp +++ b/App/lib/core/Gen/Type/TypeSpec.vp @@ -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 自检(不参与编译流程,供手动调用/后续单测) #