fix(sizeof,parse): 尺寸链去静默兜底,解析报错可定位,补 C 解析器缺口

StructGen: _get_struct_size/_get_align 去 opaque→8/ptr_size、未知→4 兜底并补 VectorType;LiteralStructType.fields→.elements(llvmlite 无 fields,必然 AttributeError)

HandlesClassDef: union 载荷尺寸经不存在的 ir.DataLayout 静默退化成 8 字节;HandlesExprCall/ExprBuiltin: sizeof 各静默 0 分支改 fail-fast,数组元素 sizeof 统一走 _get_struct_size

HandlesFor: nonlocal struct/array 堆分配不再退化成 malloc(8)

vast/vast_c: 补一元 * / & 解析,装饰器后 extern/export 分派修复,C 侧报错带 token 与行号;StubGen.Converter: 宏展开丢尾随空行修 .pyi 缩进非法

其余为 rawptr 迁移 / fail-fast 口径等历史改动一并入库
This commit is contained in:
TermiNexus committed 2026-09-26 19:27:22 +08:00
1 parent 53a345a59a
commit c83fc752c6
53 files changed
+6238 -887

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
# -*- coding: utf-8 -*-
"""只读审计:全仓"把源码交给 Python 解析器"的调用点是否都过了 Viper 软关键字层。
背景(为什么需要这个门禁)
Viper 前端 = TPC 自研 vast(`lib/vast`)+ **唯一展开点**
`lib/core/SymbolUtils.ExpandSoftKeywords`(`fn/fun/func/function` → `def`;
`enum X:` 块 → `class X(t.CEnum):`,两者均同行替换、行号不变)。
凡把 **Viper 源码文本**交给 `ast.parse` / `vast.parse` 的入口都必须先过这一层;
漏过者的失败形态是**静默降级**,不是报错:`includes/ast/base.vp:168` 的
`enum ASTFlag:` 曾让整个 base.vp 的声明(含顶层 `_init_ast`)一个都不注册,
下游只报 `Undefined function: '_init_ast'`(浪费一轮排查,见
`Debugger/enum-semantics.md` §13)。判据 = `Grep "ast\\.parse\\("` 逐个确认
上游是否已有展开层,或该输入**必然无糖**。
分类(逐调用点,见 `Classify`)
PASS 首实参文本含 `ExpandSoftKeywords`(同一表达式内已展开)
EVAL `mode='eval'`:注解 / 表达式的**片段**解析,不是整份源码 ⇒ 无需
EXEMPT 命中下方冻结豁免表:输入必然是 `.vpi`/`.pyi` 无糖产物,或同一函数
上游(紧邻几行)已展开 ⇒ 已人工审计确认
SUSPECT 其余 ⇒ **需人工判定**;`--strict` 下 exit 1(门禁失败)
覆盖范围:仓库内全部 `*.py`(含根目录脚本、`MCP/**`、`TransPyC/Tools/**`),
跳过 `.git`/`__pycache__`/`output`/`temp`/`.tpv_build`/`dlls` 与解析器自身
(`lib/vast`、`lib/vast_c` 是前端**实现**,不是前端**消费者**)。
TPV 侧源码(`TransPyV/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。
用法(**只读,不写任何文件**)
python TransPyC/Tools/AuditViperParseEntries.py # 打印审计表
python TransPyC/Tools/AuditViperParseEntries.py --strict # 有 SUSPECT 则 exit 1
"""
import ast as pyast
import os
import sys
ScriptDir = os.path.dirname(os.path.abspath(__file__))
RepoRoot = os.path.dirname(os.path.dirname(ScriptDir))
SkipDirs = {'__pycache__', '.git', '.tpv_build', 'dlls', 'output', 'temp', 'node_modules'}
# 前端实现(vast 解析器本体;它自己就是"唯一许可入口"的实现层)
SkipRelPrefixes = ('TransPyC/lib/vast/', 'TransPyC/lib/vast_c/')
# ---- 冻结豁免表:(仓库相对路径, 首实参文本归一化) -> 豁免理由 ------------------
# 新增合法调用点时必须在此登记原因;否则 --strict 会把新调用点当作 SUSPECT 拦下。
# 表来源:本轮全仓穷举审计(见最终报告 B 节审计表)。
Exempt = {
('TransPyC/lib/Projectrans/Phase2Translator.py', 'f.read()'):
'.vpi/.pyi 无糖产物(_ScanPyiFiles / _PyiCacheWorkerInitializer 仅扫 .vpi/.pyi)',
('TransPyC/lib/Projectrans/Phase2Translator.py', 'sig_content'):
'sig_files 全部来自 temp/*.vpi|*.pyi(Phase2Translator._LoadSha1Map:759)',
('TransPyC/lib/Projectrans/DeclarationGenerator.py', 'pyi_content'):
'generate(pyi_content, ...) 的入参来自 Phase1Generator 读 temp/{sha1}.vpi',
('TransPyC/lib/StubGen/Converter.py', 'PyContent'):
'convert() 首行 PyContent = ExpandSoftKeywords(PyContent)(Converter.py:68)',
('TransPyC/lib/Shell/TransPyC.py', 'content'):
'同函数 :100 content = ExpandSoftKeywords(content)',
('TransPyC/lib/Shell/TransPyC.py', 'Content'):
'同函数 :445 Content = ExpandSoftKeywords(Content)',
('TransPyC/lib/Shell/TransPyC.py', 'self.code'):
'Convert() :522 self.code = ExpandSoftKeywords(self.code)',
('TransPyC/lib/core/Translator/PythonParser.py', 'content'):
'ParsePythonFile() :76 content = ExpandSoftKeywords(content)',
('TransPyC/lib/core/Translator/AnnotationLoader.py', 'ann_content'):
'三处调用点各自紧邻上一行 ann_content = ExpandSoftKeywords(ann_content)',
('TransPyC/lib/core/SymbolExtractor.py', 'code'):
'extract(FilePath) 的 FilePath 全部来自 LoadModuleSymbols → .vpi/.pyi',
('TransPyC/lib/core/Handles/HandlesImports.py', '_fh0.read()'):
'.vpi(_TryLoadClassMembersFromPyi guard 分支补解析 class_parents)',
('TransPyC/lib/core/Handles/HandlesImports.py', 'f.read()'):
'.vpi(_TryLoadClassMembersFromPyi 主路径 _pyi_cache 填充)',
('TransPyC/lib/core/Handles/HandlesExprCall.py', 'pyi_code'):
'.vpi(_vtable_pyi_cache 填充,路径固定 temp/{sha1}.vpi)',
}
def Classify(Rel, Arg, Mode):
"""→ (类别, 理由)。"""
if 'ExpandSoftKeywords' in Arg:
return 'PASS', '首实参已过 ExpandSoftKeywords'
if Mode.strip('\'"') == 'eval':
return 'EVAL', "mode='eval'(注解/表达式片段,非整份源码)"
Hit = Exempt.get((Rel, Arg))
if Hit:
return 'EXEMPT', Hit
return 'SUSPECT', '未过展开层且不在冻结豁免表中(需人工判定该输入是否必然无糖)'
def IterCallSites():
"""遍历仓库 .py,产出 (rel, line, arg, mode);无法按 CPython 解析的文件记入 Unparsed。"""
for Root, Dirs, Files in os.walk(RepoRoot):
Dirs[:] = sorted(d for d in Dirs if d not in SkipDirs)
for Name in sorted(Files):
if not Name.endswith('.py'):
continue
Path = os.path.join(Root, Name)
Rel = os.path.relpath(Path, RepoRoot).replace(os.sep, '/')
if Rel.startswith(SkipRelPrefixes):
continue
try:
with open(Path, encoding='utf-8') as Fh:
Src = Fh.read()
Tree = pyast.parse(Src, Rel)
except Exception as Err:
yield ('<unparsed>', 0, Rel, type(Err).__name__)
continue
for Node in pyast.walk(Tree):
if not isinstance(Node, pyast.Call):
continue
Func = Node.func
if not isinstance(Func, pyast.Attribute) or Func.attr != 'parse':
continue
if not (isinstance(Func.value, pyast.Name) and Func.value.id in ('ast', 'vast')):
continue
Arg = pyast.get_source_segment(Src, Node.args[0]) if Node.args else ''
Mode = ''
for Kw in Node.keywords:
if Kw.arg == 'mode':
Mode = pyast.get_source_segment(Src, Kw.value) or ''
yield ('site', Node.lineno, Rel, ' '.join((Arg or '').split()), ' '.join(Mode.split()))
def Main(Strict):
Rows = []
Unparsed = []
for Item in IterCallSites():
if Item[0] == '<unparsed>':
Unparsed.append((Item[2], Item[3]))
continue
_, Line, Rel, Arg, Mode = Item
Kind, Why = Classify(Rel, Arg, Mode)
Rows.append((Kind, Rel, Line, Arg, Why))
Order = {'SUSPECT': 0, 'PASS': 1, 'EVAL': 2, 'EXEMPT': 3}
Rows.sort(key=lambda R: (Order[R[0]], R[1], R[2]))
Count = {}
for Kind, _, _, _, _ in Rows:
Count[Kind] = Count.get(Kind, 0) + 1
for Kind, Rel, Line, Arg, Why in Rows:
print('%-8s %s:%d arg=%s — %s' % (Kind, Rel, Line, (Arg or '')[:60], Why))
print('-' * 100)
print('sites=%d PASS=%d EVAL=%d EXEMPT=%d SUSPECT=%d unparsed=%d' % (
len(Rows), Count.get('PASS', 0), Count.get('EVAL', 0),
Count.get('EXEMPT', 0), Count.get('SUSPECT', 0), len(Unparsed)))
if Unparsed:
print('无法按 CPython 解析(未审计): %s' % Unparsed)
if Strict and Count.get('SUSPECT'):
sys.stderr.write('[strict] 门禁失败: 有 %d 个未过展开层且未登记的解析入口\n'
% Count['SUSPECT'])
return 1
return 0
if __name__ == '__main__':
sys.exit(Main('--strict' in sys.argv[1:]))
+840
View File
@@ -0,0 +1,840 @@
# -*- coding: utf-8 -*-
"""作用域感知静态扫描: `rawptr[<族>.<X>]` 变量上访问 <X> 不存在的字段(fast-fail 潜在点)。
族(Family)= 一个"类名限定前缀 + 类定义来源"。默认族表见 BuildFamilies;
`rawptr[ast.X]` / `rawptr[llvmlite.X]` / `rawptr[GenTypes.X]` / `rawptr[HT.X]` /
`rawptr[memhub.X]` 均可解析。另加载 linkedlist.vp 作为**支撑族**(空前缀 ''),
供 `class ParamNode(GSListNode[ParamNode])` 这类**裸基类名**解析(否则 `.Next` 会误报)。
相对旧版的关键修正(旧版按“变量名全文件聚合 + 正则硬匹配”导致大量误报):
1) 作用域建模: 为 模块 / 函数(方法/嵌套函数) / lambda 建立独立作用域,
类体作用域按 Python 语义对方法“穿透”; 属性访问按“该访问点所在作用域链”找
**最近一次、且位置在访问点之前** 的声明(不是“全文件第一条”)。
2) 形参(含 self 与 rawptr[X] 形参)计入声明; 无注解形参视为“遮蔽”外层同名变量。
3) 重新赋值/重声明: AnnAssign / Assign(cast 形态) / 赋值继承(Name RHS) 都参与;
非指针赋值记录为“已解析但非指针”=> 不再继承更早的类。
4) 跳过非代码: 解析源码后, 注释/字符串/docstring 内容天然不产生 Attribute 节点
(如 "%s.__iter__" 之类的字符串与三引号注释块不再命中)。
5) 类成员表按族隔离: 基类先在**本族**找, 再找**支撑族**(''), 最后全局唯一名;
同一基类名跨族重复时报 AMBIG(宁可 UNRESOLVED 也不猜)。
6) `t.REnum`(tagged union)派生类: 变体字段合并进父类成员集
(TPV 允许在父类指针上访问变体字段, 不合并会产生大量误报)。
前端 = **TPC 自研 vast**(`lib/vast`,与 StubGen/Converter.py 用的 `import vast as ast`
是同一源文件; 不可用 CPython `ast`, 否则含 `list[X*]` 等 vast 专有写法的文件会整体
SyntaxError 被跳过)。vast 与 CPython ast 的差异及本脚本的适配:
A) `X*` / `X**` 被 vast 脱糖成 `X | t.CPtr`(`**` 加两层), 其中合成的
`t.CPtr` Attribute 节点**连 lineno 属性都没有**(编译器内部脱糖产物, 不是源码里的
访问; 全树实测 555 个, 全部是 ('t','CPtr'))=> 计入 Uses 时用
`getattr(Node,'lineno',None) is not None` 排除。
B) vast 的 SyntaxError 可能不带 lineno(C 路径)=> 从报文 `at line N` 兜底提取。
C) vast 的装饰器 `@t.NoVTable` 的 col_offset 是 `@` 的位置(CPython 是其后 1 列);
该 Attribute 的接收者 `t` 不是族指针, 判据不受影响。
D) 其余节点类名/属性名/walk/iter_child_nodes 与 CPython ast 同构(已逐节点对照)。
输出: 文件:行号 / 变量 / 推导出的类 / 字段 / 声明来源行 / 判定, 并统计
NOWHERE / OTHER / RESOLVED / UNRESOLVED, 并按族(ast/llvmlite/其它)分桶。
路径: 全部按**脚本自身位置**推导(ScriptDir = <RepoRoot>/TransPyC/Tools),
不依赖 cwd;可用 --app-root / --includes / --out 覆盖。
用法:
python TransPyC/Tools/ScanAstFieldMiss.py [--app-root DIR] [--includes DIR]
[--out FILE] [--verbose] [--strict]
python TransPyC/Tools/ScanAstFieldMiss.py --selftest [--includes DIR] [--out FILE]
默认值:
--app-root <RepoRoot>/TransPyV/App
--includes <RepoRoot>/includes
--out <RepoRoot>/Debugger/ScanAstFieldMiss.txt
退出码:
0 非 --strict 的正常扫描; 或 --strict 下 NOWHERE / OTHER / parsefail 三者全 0
1 --strict 下 有 NOWHERE/OTHER 命中, **或 parsefail > 0**
(parsefail 也算门禁失败: 文件被前端静默跳过 = 假绿, 见下)
2 参数错误
--selftest: 0 = 断言全通过; 1 = 有断言失败
--selftest 为什么必须有(历史教训):
上一版门禁的前端是 CPython `ast`, 对含 `list[X*]` / `rawptr[X]*` 等 vast 专有写法的
文件整体 SyntaxError 跳过 ⇒ 曾出现 "OTHER=0 全绿" 而工作树里实际有 **18 处**
`rawptr[llvmlite.Function].Ty` 真错(整批文件根本没被检查)。
故 --selftest 内置合成用例(生成到临时目录、跑完即删), 断言"**报出集合 == 预期集合**",
覆盖 4 类正例(含 vast 专有 `rawptr[X]*` / `rawptr[X]**`)与 4 类负例
(合法字段 / 跨函数同名各自声明 / docstring 与注释 / 返回值推断生效),
用来证明扫描器不是空转。
"""
import glob
import os
import re
import shutil
import sys
import tempfile
# ============================================================
# 0. 路径推导(全部按脚本自身位置, 不依赖 cwd)
# ScriptDir = <RepoRoot>/TransPyC/Tools
# TpcRoot = <RepoRoot>/TransPyC (TransPyC 仓库根)
# RepoRoot = <RepoRoot> (工作区根)
# ============================================================
ScriptDir = os.path.dirname(os.path.abspath(__file__))
TpcRoot = os.path.dirname(ScriptDir)
RepoRoot = os.path.dirname(TpcRoot)
DefaultAppRoot = os.path.join(RepoRoot, 'TransPyV', 'App')
DefaultIncludesDir = os.path.join(RepoRoot, 'includes')
DefaultOutFile = os.path.join(RepoRoot, 'Debugger', 'ScanAstFieldMiss.txt')
# ---- 前端: TPC 自研 vast(非 CPython ast)。需要 TransPyC 包根在 sys.path,
# lib._bootstrap 会再补齐 lib/ 与 lib/includes(使 `import vast` 可用)。
if TpcRoot not in sys.path:
sys.path.insert(0, TpcRoot)
from lib import vast as ast # noqa: E402
from lib.core.SymbolUtils import ExpandSoftKeywords # noqa: E402
SupportPrefix = ''
# ============================================================
# 1. 类成员表: 字段 / 方法 / 基类(按族隔离;每次扫描重建)
# ============================================================
FamilyTable = []
SupportFiles = []
FamilyNames = set()
ClassFields = {}
ClassMethods = {}
ClassBase = {}
ClassLoc = {} # 裸类名 -> [族, ...](用于跨族基类解析)
EnumClass = {} # (族, 类名) -> 1 表示 t.REnum/t.CEnum 派生(变体字段合并)
REnumClass = {} # (族, 类名) -> 1 表示 t.REnum 派生(额外有编译器内建 .kind())
MemberCache = {}
FamilyFields = {} # 族 -> 该族所有字段并集(OTHER/NOWHERE 判定用)
AmbiguousBases = set()
Uses = [] # [(AttrNode, Scope)];Walk 现场填充, 每个文件扫描前 clear
# ---- 返回注解表(同文件内 def f(...) -> rawptr[族.X]):供 x = f() 形态的类型推导。
# 仅补充覆盖(收紧),不改变任何既有判据:名字在文件内返回注解不一致时报歧义并放弃。
FuncRet = {}
def ResetTables():
"""清空全部类表/缓存(供同一进程内多轮扫描复用, 如 --selftest)。"""
global FamilyTable, SupportFiles, FamilyNames
global ClassFields, ClassMethods, ClassBase, ClassLoc, EnumClass, REnumClass
global MemberCache, FamilyFields, AmbiguousBases
FamilyTable = []
SupportFiles = []
FamilyNames = set()
ClassFields = {}
ClassMethods = {}
ClassBase = {}
ClassLoc = {}
EnumClass = {}
REnumClass = {}
MemberCache = {}
FamilyFields = {}
AmbiguousBases = set()
FuncRet.clear()
def AddClass(Fam, Name, Bases):
ClassFields.setdefault((Fam, Name), set())
ClassMethods.setdefault((Fam, Name), set())
ClassLoc.setdefault(Name, [])
if Fam not in ClassLoc[Name]:
ClassLoc[Name].append(Fam)
if Bases:
ClassBase.setdefault((Fam, Name), set()).update(Bases)
def BaseName(Node):
if isinstance(Node, ast.Name):
return Node.id
if isinstance(Node, ast.Attribute):
return Node.attr
if isinstance(Node, ast.Subscript): # GSListNode[ParamNode] -> GSListNode
return BaseName(Node.value)
return None
def CollectBody(Fam, Name, Body, Enum):
"""收集类体成员;Enum=True 时把嵌套变体类的字段并入本类(REnum 语义)。"""
for Item in Body:
if isinstance(Item, ast.AnnAssign) and isinstance(Item.target, ast.Name):
ClassFields[(Fam, Name)].add(Item.target.id)
elif isinstance(Item, (ast.FunctionDef, ast.AsyncFunctionDef)):
ClassMethods[(Fam, Name)].add(Item.name)
elif Enum and isinstance(Item, ast.ClassDef):
CollectBody(Fam, Name, Item.body, Enum) # 变体字段并入父类
def RegisterClass(Fam, Node):
Bases = set()
for B in Node.bases:
Nm = BaseName(B)
if Nm:
Bases.add(Nm)
AddClass(Fam, Node.name, Bases)
Enum = 1 if (Bases & {'REnum', 'CEnum'}) else 0
if Enum:
EnumClass[(Fam, Node.name)] = 1
if 'REnum' in Bases:
# REnum.kind() 是编译器内建(HandlesExprCall.py:3126 'kind' → __tag 读取)
REnumClass[(Fam, Node.name)] = 1
CollectBody(Fam, Node.name, Node.body, Enum)
def ParseSrc(Src, Path):
"""vast 前端解析入口(全脚本单一前端; 失败抛异常, 由调用方记账)。
`.vp` 源码须先过 Viper 软关键字层(`lib/core/SymbolUtils.ExpandSoftKeywords`:
`fn/fun/func/function` → `def`、`enum X:` → `class X(t.CEnum):`),与
`StubGen/Converter.py` / `Shell/TransPyC.py` 等既有前端入口同口径。
否则含 `enum` 的 `.vp`(如 `includes/ast/base.vp:168`)会在此整体 SyntaxError,
被 `LoadInclude` 的正则兜底或 `RunScan` 的 `parsefail` 记成"解析失败"——两者
都会**静默漏掉该文件的类/字段**(本脚本的假绿来源)。`.py` 无糖,展开为空操作
(`'enum' in Code` 门禁 + 行首锚定正则)。
"""
return ast.parse(ExpandSoftKeywords(Src), Path)
def ErrLine(E):
"""vast 的 SyntaxError 可能无 lineno(C 路径)=> 从报文 `at line N` 兜底提取。"""
L = getattr(E, 'lineno', None)
if L:
return L
M = re.search(r'at line (\d+)', str(E))
return int(M.group(1)) if M else -1
def LoadIncludeRegex(Src, Fam):
"""ast.parse 失败(docstring 含 \\xNN 等)时的回退:缩进感知的类/字段/方法收集。"""
Indent = {}
Enum = {}
for Ln in Src.splitlines():
M = re.match(r'(\s*)class (\w+)(?:\[[^\]]*\])?(?:\(([^)]*)\))?\s*:', Ln)
if M:
Depth = len(M.group(1))
Name = M.group(2)
Bases = set()
if M.group(3):
for B in M.group(3).split(','):
B = B.strip().split('.')[-1].split('[')[0].strip()
if B:
Bases.add(B)
AddClass(Fam, Name, Bases)
Indent[Name] = Depth
Enum[Name] = 1 if (Bases & {'REnum', 'CEnum'}) else 0
if Enum[Name]:
EnumClass[(Fam, Name)] = 1
continue
M2 = re.match(r'(\s+)(\w+)\s*:\s', Ln)
if M2 and Indent:
Depth = len(M2.group(1))
Owner = [N for N, D in Indent.items() if D < Depth]
if Owner:
P = max(Owner, key=lambda N: Indent[N])
ClassFields[(Fam, P)].add(M2.group(2))
continue
M3 = re.match(r'(\s+)def (\w+)\(', Ln)
if M3 and Indent:
Depth = len(M3.group(1))
Owner = [N for N, D in Indent.items() if D < Depth]
if Owner:
P = max(Owner, key=lambda N: Indent[N])
ClassMethods[(Fam, P)].add(M3.group(2))
def LoadInclude(Path, Fam):
try:
Src = open(Path, encoding='utf-8').read()
except Exception:
return
try:
Tree = ParseSrc(Src, Path)
except Exception: # noqa: BLE001 (vast 失败可能是 SyntaxError/ValueError)
LoadIncludeRegex(Src, Fam)
return
for Node in Tree.body:
if isinstance(Node, ast.ClassDef):
RegisterClass(Fam, Node)
def BuildFamilies(AppRoot, IncludesDir):
"""按 AppRoot/IncludesDir 建族表并加载类定义(含支撑族)。"""
global FamilyTable, SupportFiles, FamilyNames
FamilyTable = [
('ast', [os.path.join(IncludesDir, 'ast')]),
('llvmlite', [os.path.join(IncludesDir, 'llvmlite')]),
('GenTypes', [os.path.join(AppRoot, 'lib', 'core', 'Gen', 'GenTypes.vp')]),
('HT', [os.path.join(AppRoot, 'lib', 'core', 'Gen', 'Translator.vp')]),
('memhub', [os.path.join(IncludesDir, 'memhub.vp')]),
]
SupportFiles = [os.path.join(IncludesDir, 'linkedlist.vp')]
FamilyNames = set(P for P, _ in FamilyTable) | {''}
for Prefix, Paths in FamilyTable:
for P in Paths:
if os.path.isdir(P):
for F in sorted(glob.glob(os.path.join(P, '*.vp'))):
LoadInclude(F, Prefix)
else:
LoadInclude(P, Prefix)
for F in SupportFiles:
LoadInclude(F, SupportPrefix)
def ComputeFamilyFields():
global FamilyFields
FamilyFields = {}
for (_F, _N) in ClassFields:
FamilyFields.setdefault(_F, set()).update(ClassFields[(_F, _N)])
def FindClass(Name, Prefer):
"""裸类名 -> (族, 类名)。优先本族, 再支撑族, 最后全局唯一名。"""
if (Prefer, Name) in ClassFields:
return (Prefer, Name)
if (SupportPrefix, Name) in ClassFields:
return (SupportPrefix, Name)
Locs = ClassLoc.get(Name, [])
if len(Locs) == 1:
return (Locs[0], Name)
if len(Locs) > 1:
AmbiguousBases.add(Name)
return None
def Members(Fam, Name, Seen=None):
if Seen is None:
Seen = set()
Key = (Fam, Name)
if Key in Seen:
return set()
Seen.add(Key)
Out = set(ClassFields.get(Key, ())) | set(ClassMethods.get(Key, ()))
if Key in REnumClass:
Out.add('kind')
for B in ClassBase.get(Key, ()):
Loc = FindClass(B, Fam)
if Loc is not None:
if Loc in REnumClass:
Out.add('kind')
Out |= Members(Loc[0], Loc[1], Seen)
return Out
def Member(Fam, Name):
Key = (Fam, Name)
if Key not in MemberCache:
MemberCache[Key] = Members(Fam, Name)
return MemberCache[Key]
# ============================================================
# 2. 类型推导: 从注解 / cast 调用里取 (族, 类名)
# ============================================================
def AnnClass(Ann):
"""注解里若为 rawptr[<族>.<X>](可被 `| t.CPtr` 包裹)返回 (族, X), 否则 None。"""
if Ann is None:
return None
if isinstance(Ann, ast.BinOp) and isinstance(Ann.op, ast.BitOr):
return AnnClass(Ann.left) or AnnClass(Ann.right)
if isinstance(Ann, ast.Subscript):
if isinstance(Ann.value, ast.Name) and Ann.value.id == 'rawptr':
Sl = Ann.slice
if isinstance(Sl, ast.Attribute) and isinstance(Sl.value, ast.Name) \
and Sl.value.id in FamilyNames and Sl.value.id != '':
return (Sl.value.id, Sl.attr)
return None
def CastClass(Val):
"""`rawptr[<族>.<X>](...)` 形态的 cast 调用返回 (族, X), 否则 None。"""
if isinstance(Val, ast.Call) and isinstance(Val.func, ast.Subscript):
return AnnClass(Val.func)
return None
def CollectFuncRets(Tree):
FuncRet.clear()
Ambig = set()
for Node in ast.walk(Tree):
if isinstance(Node, (ast.FunctionDef, ast.AsyncFunctionDef)):
C = AnnClass(Node.returns)
if Node.name in FuncRet and FuncRet[Node.name] != C:
Ambig.add(Node.name)
else:
FuncRet[Node.name] = C
for N in Ambig:
FuncRet.pop(N, None)
# ============================================================
# 3. 作用域模型
# ============================================================
class Scope:
__slots__ = ('Kind', 'Parent', 'Decls')
def __init__(self, Kind, Parent):
self.Kind = Kind # 'module' / 'func' / 'class'
self.Parent = Parent
self.Decls = {} # name -> [(Lineno, Col, Cls, DeclLine)]
def Add(self, Name, Lineno, Col, Cls):
self.Decls.setdefault(Name, []).append((Lineno, Col, Cls, Lineno))
def Resolve(Sc, Name, Lineno, Col):
"""沿作用域链找 Name 在 (Lineno, Col) 之前最近一次的声明。
返回 (Cls, DeclLine); Cls 为 None 表示“已解析但非族指针”或“未找到”。
类体作用域对嵌套函数穿透(Python 语义)。
"""
Cur = Sc
First = True
while Cur is not None:
if Cur.Kind != 'class' or First:
if Name in Cur.Decls:
Best = None
for D in Cur.Decls[Name]:
if (D[0], D[1]) <= (Lineno, Col):
if Best is None or (D[0], D[1]) > (Best[0], Best[1]):
Best = D
if Best is not None:
return Best[2], Best[3]
First = False
Cur = Cur.Parent
return None, None
def Walk(Node, Sc):
if Node is None:
return
if isinstance(Node, (ast.FunctionDef, ast.AsyncFunctionDef)):
for D in Node.decorator_list:
Walk(D, Sc)
A = Node.args
Formals = list(A.posonlyargs) + list(A.args) + list(A.kwonlyargs)
for P in Formals:
Walk(P.annotation, Sc)
for D in A.defaults:
Walk(D, Sc)
for D in A.kw_defaults:
Walk(D, Sc)
if A.vararg is not None:
Walk(A.vararg.annotation, Sc)
if A.kwarg is not None:
Walk(A.kwarg.annotation, Sc)
Walk(Node.returns, Sc)
FS = Scope('func', Sc)
for P in Formals:
FS.Add(P.arg, Node.lineno, 0, AnnClass(P.annotation))
if A.vararg is not None:
FS.Add(A.vararg.arg, Node.lineno, 0, AnnClass(A.vararg.annotation))
if A.kwarg is not None:
FS.Add(A.kwarg.arg, Node.lineno, 0, AnnClass(A.kwarg.annotation))
for S in Node.body:
Walk(S, FS)
return
if isinstance(Node, ast.Lambda):
A = Node.args
Formals = list(A.posonlyargs) + list(A.args) + list(A.kwonlyargs)
for P in Formals:
Walk(P.annotation, Sc)
for D in A.defaults:
Walk(D, Sc)
for D in A.kw_defaults:
Walk(D, Sc)
LS = Scope('func', Sc)
for P in Formals:
LS.Add(P.arg, Node.lineno, 0, AnnClass(P.annotation))
Walk(Node.body, LS)
return
if isinstance(Node, ast.ClassDef):
for D in Node.decorator_list:
Walk(D, Sc)
for B in Node.bases:
Walk(B, Sc)
for K in Node.keywords:
Walk(K.value, Sc)
CS = Scope('class', Sc)
for S in Node.body:
Walk(S, CS)
return
if isinstance(Node, ast.Attribute):
# 只收源码里真实存在的属性访问: vast 脱糖 `X*` 会合成 `t.CPtr` Attribute,
# 该节点**连 lineno 属性都没有**(全树 555 个, 全为 ('t','CPtr')), 不是源码
# 访问、也不能参与位置比较 => 用 getattr 显式排除。
if isinstance(Node.value, ast.Name) and getattr(Node, 'lineno', None) is not None:
Uses.append((Node, Sc))
Walk(Node.value, Sc)
return
if isinstance(Node, ast.AnnAssign):
if isinstance(Node.target, ast.Name):
Sc.Add(Node.target.id, Node.target.lineno, Node.target.col_offset,
AnnClass(Node.annotation))
elif isinstance(Node.target, ast.Attribute):
Walk(Node.target, Sc) # `x.y: T = ...` 的属性访问
Walk(Node.annotation, Sc)
Walk(Node.value, Sc)
return
if isinstance(Node, ast.Assign):
Cls = None
V = Node.value
if isinstance(V, ast.Call):
Cls = CastClass(V)
if Cls is None and isinstance(V.func, ast.Name):
Cls = FuncRet.get(V.func.id)
elif isinstance(V, ast.Name):
Cls, _ = Resolve(Sc, V.id, V.lineno, V.col_offset)
for T in Node.targets:
if isinstance(T, ast.Name):
Sc.Add(T.id, T.lineno, T.col_offset, Cls)
# 赋值**目标**位置的属性访问也要计入(如 `vtable_node.Ty = ...`)
Walk(T, Sc)
Walk(Node.value, Sc)
return
if isinstance(Node, ast.AugAssign):
if isinstance(Node.target, ast.Name):
Sc.Add(Node.target.id, Node.target.lineno, Node.target.col_offset, None)
Walk(Node.target, Sc)
Walk(Node.value, Sc)
return
if isinstance(Node, (ast.For, ast.AsyncFor)):
for T in ast.walk(Node.target):
if isinstance(T, ast.Name):
Sc.Add(T.id, Node.lineno, 0, None)
Walk(Node.iter, Sc)
for S in Node.body:
Walk(S, Sc)
for S in Node.orelse:
Walk(S, Sc)
return
if isinstance(Node, ast.NamedExpr):
if isinstance(Node.target, ast.Name):
Sc.Add(Node.target.id, Node.target.lineno, Node.target.col_offset, None)
Walk(Node.value, Sc)
return
for Ch in ast.iter_child_nodes(Node):
Walk(Ch, Sc)
# ============================================================
# 4. 扫描(一轮 = ResetTables + BuildFamilies + 遍历 AppRoot)
# ============================================================
def BucketOf(Fam):
return Fam if Fam in ('ast', 'llvmlite') else 'OTHER:' + (Fam or 'support')
def Bump(D, Key):
D[Key] = D.get(Key, 0) + 1
def RunScan(AppRoot, IncludesDir, OutFile, Verbose):
"""跑一轮扫描: 写报告文件 + 返回统计/命中集合(不打印)。"""
ResetTables()
BuildFamilies(AppRoot, IncludesDir)
ComputeFamilyFields()
Nowhere = []
Other = []
ResolvedCount = 0
UnresolvedCount = 0
Files = sorted(glob.glob(os.path.join(AppRoot, '**', '*.vp'), recursive=True))
ParseFail = []
for Path in Files:
try:
Src = open(Path, encoding='utf-8').read()
except Exception:
continue
try:
Tree = ParseSrc(Src, Path)
except Exception as E: # noqa: BLE001 (vast 失败可能是 SyntaxError/ValueError)
ParseFail.append((os.path.relpath(Path, AppRoot), ErrLine(E), str(E)[:80]))
continue
RawLines = Src.splitlines()
Uses.clear()
CollectFuncRets(Tree)
MS = Scope('module', None)
for S in Tree.body:
Walk(S, MS)
for AttrNode, Sc in Uses:
Var = AttrNode.value.id
Field = AttrNode.attr
Cls, DeclLine = Resolve(Sc, Var, AttrNode.lineno, AttrNode.col_offset)
if Cls is None:
UnresolvedCount += 1
continue
Fam, CName = Cls
if (Fam, CName) not in ClassFields:
UnresolvedCount += 1
continue
ResolvedCount += 1
if Field in Member(Fam, CName):
continue
Rel = os.path.relpath(Path, AppRoot)
SrcLine = RawLines[AttrNode.lineno - 1].strip()[:96] if AttrNode.lineno <= len(RawLines) else ''
Row = (Rel, AttrNode.lineno, Var, CName, Field, DeclLine, SrcLine, Fam)
if Field in FamilyFields.get(Fam, set()):
Other.append(Row)
else:
Nowhere.append(Row)
# 稳定排序(便于 diff / 门禁)
SortKey = lambda R: (R[0], R[1], R[2], R[4], R[7], R[3], R[5])
Nowhere.sort(key=SortKey)
Other.sort(key=SortKey)
BucketsN = {}
BucketsO = {}
for R in Nowhere:
Bump(BucketsN, BucketOf(R[7]))
for R in Other:
Bump(BucketsO, BucketOf(R[7]))
ParsedN = len(Files) - len(ParseFail)
UsesN = ResolvedCount + UnresolvedCount
ClassLine = ' '.join('%s=%d' % (f, sum(1 for (x, _n) in ClassFields if x == f))
for f in sorted(set(x for x, _n in ClassFields)))
def W(Fh, Rows, Tag):
for R in Rows:
Fh.write('%-46s:%-5d var=%s cls=%s.%s field=%s decl@%d 判定=%s %s\n' %
(R[0], R[1], R[2], R[7], R[3], R[4], R[5], Tag, R[6]))
OutDir = os.path.dirname(os.path.abspath(OutFile))
if OutDir:
os.makedirs(OutDir, exist_ok=True)
with open(OutFile, 'w', encoding='utf-8') as Fh:
Fh.write('files=%d parsed=%d parsefail=%d uses=%d NOWHERE=%d OTHER=%d RESOLVED=%d UNRESOLVED=%d\n' %
(len(Files), ParsedN, len(ParseFail), UsesN,
len(Nowhere), len(Other), ResolvedCount, UnresolvedCount))
Fh.write('classes: %s\n' % ClassLine)
Fh.write('NOWHERE buckets: %s\n' % sorted(BucketsN.items()))
Fh.write('OTHER buckets: %s\n' % sorted(BucketsO.items()))
if AmbiguousBases:
Fh.write('AMBIGUOUS BASES (跨族重名, 已不解析): %s\n' % sorted(AmbiguousBases))
Fh.write('\n=== NOWHERE (本族任何类都没有该字段) ===\n')
W(Fh, Nowhere, 'NOWHERE')
Fh.write('\n=== OTHER (字段只存在于本族别的类) ===\n')
W(Fh, Other, 'OTHER')
if ParseFail:
Fh.write('\n=== PARSE FAIL (vast 无法解析, 已跳过) ===\n')
for P in ParseFail:
Fh.write('%s:%s %s\n' % P)
return {
'Files': len(Files), 'Parsed': ParsedN, 'ParseFail': ParseFail,
'Uses': UsesN, 'Nowhere': Nowhere, 'Other': Other,
'Resolved': ResolvedCount, 'Unresolved': UnresolvedCount,
'BucketsN': BucketsN, 'BucketsO': BucketsO,
'Ambiguous': sorted(AmbiguousBases), 'Classes': ClassLine,
'OutFile': OutFile,
}
def PrintSummary(Stats, Verbose):
print('files=%d parsed=%d parsefail=%d uses=%d NOWHERE=%d OTHER=%d RESOLVED=%d UNRESOLVED=%d -> %s' %
(Stats['Files'], Stats['Parsed'], len(Stats['ParseFail']), Stats['Uses'],
len(Stats['Nowhere']), len(Stats['Other']),
Stats['Resolved'], Stats['Unresolved'], Stats['OutFile']))
print('NOWHERE buckets: %s' % sorted(Stats['BucketsN'].items()))
print('OTHER buckets: %s' % sorted(Stats['BucketsO'].items()))
if Stats['Ambiguous']:
print('AMBIGUOUS BASES: %s' % Stats['Ambiguous'])
if Stats['ParseFail']:
print('PARSE FAIL: %s' % Stats['ParseFail'])
Limit = 25 if not Verbose else 100000
for R in Stats['Nowhere'][:Limit]:
print('N %-40s:%-5d %s.%s.%s [decl@%d]' % (R[0], R[1], R[7], R[3], R[4], R[5]))
for R in Stats['Other'][:Limit]:
print('O %-40s:%-5d %s.%s.%s [decl@%d]' % (R[0], R[1], R[7], R[3], R[4], R[5]))
# ============================================================
# 5. --selftest: 内置合成用例(临时目录生成, 跑完即删)
# 断言"报出集合 == 预期集合";正例 6 条(含 vast 专有写法)、负例 4 个文件。
# ============================================================
SelftestCases = [
('CaseP1AstNameOther.vp', """import ast
def ProbeP1(x_r: rawptr[ast.AST]) -> int:
return x_r.name
"""),
('CaseP2LlvFunctionTyOther.vp', """import llvmlite
def ProbeP2(x_r: rawptr[llvmlite.Function]) -> int:
return x_r.Ty
"""),
('CaseP3AstNowhere.vp', """import ast
def ProbeP3(x_r: rawptr[ast.AST]) -> int:
return x_r.zzz_not_a_field
"""),
('CaseP4AstStarOther.vp', """import ast
def ProbeP4(x_r: rawptr[ast.AST]*) -> int:
return x_r.name
"""),
('CaseP5AstStarStarOther.vp', """import ast
def ProbeP5(x_r: rawptr[ast.AST]**) -> int:
return x_r.name
"""),
('CaseP6ReturnInferOther.vp', """import llvmlite
def MkP6(x_r: rawptr[llvmlite.Function]) -> rawptr[llvmlite.Function]:
return x_r
def ProbeP6(x_r: rawptr[llvmlite.Function]) -> int:
y_r = MkP6(x_r)
return y_r.Ty
"""),
('CaseN1ParamNodeTy.vp', """import llvmlite
def ProbeN1(x_r: rawptr[llvmlite.ParamNode]) -> int:
return x_r.Ty
"""),
('CaseN2SameNameScopes.vp', """import llvmlite
def ProbeN2A(x_r: rawptr[llvmlite.ParamNode]) -> int:
return x_r.Ty
def ProbeN2B(x_r: rawptr[llvmlite.Function]) -> int:
return x_r.Ty
"""),
('CaseN3DocComment.vp', """import ast
def ProbeN3(x_r: rawptr[ast.AST]) -> int:
\"\"\"docstring: x_r.name / x_r.zzz_not_a_field 只是说明文字, 不是代码。\"\"\"
# 注释: x_r.name / x_r.zzz_not_a_field
return 0
"""),
('CaseN4ReturnInferOk.vp', """import llvmlite
def MkN4(x_r: rawptr[llvmlite.ParamNode]) -> rawptr[llvmlite.ParamNode]:
return x_r
def ProbeN4(x_r: rawptr[llvmlite.ParamNode]) -> int:
y_r = MkN4(x_r)
return y_r.Ty
"""),
]
# (文件, 变量, 族.类, 字段) -> 判定
SelftestExpected = {
('CaseP1AstNameOther.vp', 'x_r', 'ast.AST', 'name'): 'OTHER',
('CaseP2LlvFunctionTyOther.vp', 'x_r', 'llvmlite.Function', 'Ty'): 'OTHER',
('CaseP3AstNowhere.vp', 'x_r', 'ast.AST', 'zzz_not_a_field'): 'NOWHERE',
('CaseP4AstStarOther.vp', 'x_r', 'ast.AST', 'name'): 'OTHER',
('CaseP5AstStarStarOther.vp', 'x_r', 'ast.AST', 'name'): 'OTHER',
('CaseP6ReturnInferOther.vp', 'y_r', 'llvmlite.Function', 'Ty'): 'OTHER',
('CaseN2SameNameScopes.vp', 'x_r', 'llvmlite.Function', 'Ty'): 'OTHER',
}
def SelfTest(IncludesDir, OutFile):
AppRoot = tempfile.mkdtemp(prefix='ScanAstFieldMissSelftest.')
try:
for Name, Src in SelftestCases:
with open(os.path.join(AppRoot, Name), 'w', encoding='utf-8', newline='\n') as Fh:
Fh.write(Src)
Report = OutFile or os.path.join(AppRoot, 'Selftest.txt')
Stats = RunScan(AppRoot, IncludesDir, Report, False)
Reported = {}
for R in Stats['Nowhere']:
Reported[(R[0], R[2], '%s.%s' % (R[7], R[3]), R[4])] = 'NOWHERE'
for R in Stats['Other']:
Reported[(R[0], R[2], '%s.%s' % (R[7], R[3]), R[4])] = 'OTHER'
Ok = True
if Stats['ParseFail']:
Ok = False
print('[selftest] 解析失败(预期 parsefail=0):')
for P in Stats['ParseFail']:
print(' %s:%s %s' % P)
Missing = sorted(set(SelftestExpected) - set(Reported))
Extra = sorted(set(Reported) - set(SelftestExpected))
TagDiff = sorted(K for K in set(Reported) & set(SelftestExpected)
if Reported[K] != SelftestExpected[K])
for K in Missing:
Ok = False
print('[selftest] 漏报: %s -> 预期 %s' % (K, SelftestExpected[K]))
for K in Extra:
Ok = False
print('[selftest] 误报: %s -> %s' % (K, Reported[K]))
for K in TagDiff:
Ok = False
print('[selftest] 判定不符: %s -> %s(预期 %s)' % (K, Reported[K], SelftestExpected[K]))
print('[selftest] files=%d parsed=%d parsefail=%d NOWHERE=%d OTHER=%d 报出=%d 预期=%d' %
(Stats['Files'], Stats['Parsed'], len(Stats['ParseFail']),
len(Stats['Nowhere']), len(Stats['Other']), len(Reported), len(SelftestExpected)))
print('[selftest] %s' % ('PASS' if Ok else 'FAIL'))
return 0 if Ok else 1
finally:
shutil.rmtree(AppRoot, ignore_errors=True)
# ============================================================
# 6. CLI
# ============================================================
def ParseArgs(Argv):
Opt = {'AppRoot': DefaultAppRoot, 'Includes': DefaultIncludesDir, 'Out': None,
'Strict': False, 'Verbose': False, 'SelfTest': False}
Need = {'--app-root': 'AppRoot', '--includes': 'Includes', '--out': 'Out'}
Flags = {'--strict': 'Strict', '--verbose': 'Verbose', '--selftest': 'SelfTest'}
I = 0
while I < len(Argv):
A = Argv[I]
if A in Need:
if I + 1 >= len(Argv):
sys.stderr.write('参数缺值: %s\n' % A)
return None
Opt[Need[A]] = Argv[I + 1]
I += 2
continue
if A in Flags:
Opt[Flags[A]] = True
I += 1
continue
sys.stderr.write('未知参数: %s(用法见本脚本 docstring)\n' % A)
return None
return Opt
def main():
Opt = ParseArgs(sys.argv[1:])
if Opt is None:
return 2
if Opt['SelfTest']:
return SelfTest(Opt['Includes'], Opt['Out'])
Stats = RunScan(Opt['AppRoot'], Opt['Includes'], Opt['Out'] or DefaultOutFile, Opt['Verbose'])
PrintSummary(Stats, Opt['Verbose'])
if Opt['Strict'] and (Stats['Nowhere'] or Stats['Other'] or Stats['ParseFail']):
sys.stderr.write('[strict] 门禁失败: NOWHERE=%d OTHER=%d parsefail=%d\n' %
(len(Stats['Nowhere']), len(Stats['Other']), len(Stats['ParseFail'])))
return 1
return 0
if __name__ == '__main__':
sys.exit(main())
+8 -8
View File
@@ -149,7 +149,7 @@ def main() -> None:
parser.add_argument('--clear-cache', action='store_true', help='清除 .transpyc_cache 全局缓存(.vpi/.stub.ll/.doc.json)') parser.add_argument('--clear-cache', action='store_true', help='清除 .transpyc_cache 全局缓存(.vpi/.stub.ll/.doc.json)')
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)') parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)')
parser.add_argument('--dwarf', action='store_true', help='函数级 DWARF 调试元数据:每模块一份 DICompileUnit/DIFile,每个 define 一份 DISubprogram 并挂 !dbg,函数入口第一条指令挂 DILocation。使 ASan/llvm-symbolizer 能把崩溃地址反查为 "<文件>.vp:函数起始行"(默认关闭;--asan 时自动开启)。开启时强制重译 App 模块(不复用不含 !llvm.dbg.cu 的旧 .ll/.obj),.ll/.obj 体积与编译时间略增') parser.add_argument('--dwarf', action='store_true', help='函数级 DWARF 调试元数据:每模块一份 DICompileUnit/DIFile,每个 define 一份 DISubprogram 并挂 !dbg,函数入口第一条指令挂 DILocation。使 ASan/llvm-symbolizer 能把崩溃地址反查为 "<文件>.vp:函数起始行"(默认关闭;--asan 时自动开启)。开启时强制重译 App 模块(不复用不含 !llvm.dbg.cu 的旧 .ll/.obj),.ll/.obj 体积与编译时间略增')
parser.add_argument('--ctrlc-stack', action='store_true', help='Ctrl+C 调用栈打印(仅 Windows/PE):链接完成后用 llvm-dwarfdump 把最终 exe 的 .debug_line/.debug_info 导出为 "<exe>.stackmap" 地址→源行旁文件(小端定长格式),供 exe 内运行时在 Ctrl+C/Ctrl+Break 时把 CRT 到当前的返回地址逐帧翻译为 "<文件>.vp:行/函数"。会自动开启 DWARF(同 --asan,否则行表为空);找不到 llvm-dwarfdump / 调用失败 / 解析不到任何行记录 / 目标不是 Windows PE 时直接报错终止编译') parser.add_argument('--traceback', action='store_true', help='Ctrl+C 调用栈打印(仅 Windows/PE):链接完成后用 llvm-dwarfdump 把最终 exe 的 .debug_line/.debug_info 导出为 "<exe>.stackmap" 地址→源行旁文件(小端定长格式),供 exe 内运行时在 Ctrl+C/Ctrl+Break 时把 CRT 到当前的返回地址逐帧翻译为 "<文件>.vp:行/函数"。会自动开启 DWARF(同 --asan,否则行表为空);找不到 llvm-dwarfdump / 调用失败 / 解析不到任何行记录 / 目标不是 Windows PE 时直接报错终止编译')
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)') parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 <path>(默认为 hotspot_sites.txt);同时为每个函数定义加计时器,按自身耗时(self)降序写入同目录 hotspot_time.txt,行为 `<函数名> <self> <inc> <进入次数>`(单位 tick,x86_64 上为序列化 lfence; rdtsc);self 已扣除被插桩子调用的 inc,但不含未插桩被调方(间接调用、strcmp/memcpy 等无定义外部函数),它们计入调用者的 self') parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 <path>(默认为 hotspot_sites.txt);同时为每个函数定义加计时器,按自身耗时(self)降序写入同目录 hotspot_time.txt,行为 `<函数名> <self> <inc> <进入次数>`(单位 tick,x86_64 上为序列化 lfence; rdtsc);self 已扣除被插桩子调用的 inc,但不含未插桩被调方(间接调用、strcmp/memcpy 等无定义外部函数),它们计入调用者的 self')
parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat') parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat')
@@ -163,20 +163,20 @@ def main() -> None:
if args.asan: if args.asan:
_vlog().info('ASan 模式已启用(opt -passes=asan + clang64 libclang_rt.asan_dynamic*)', 'asan') _vlog().info('ASan 模式已启用(opt -passes=asan + clang64 libclang_rt.asan_dynamic*)', 'asan')
# Ctrl+C 调用栈(--ctrlc-stack):链接后用 llvm-dwarfdump 导出 <exe>.stackmap。 # Ctrl+C 调用栈(--traceback):链接后用 llvm-dwarfdump 导出 <exe>.stackmap。
# 不在此处做工具/平台校验——真正的 fail-fast 落在链接成功后的旁文件导出步骤 # 不在此处做工具/平台校验——真正的 fail-fast 落在链接成功后的旁文件导出步骤
# (那里才有最终 exe 路径可判 PE)。此处仅提示已启用。 # (那里才有最终 exe 路径可判 PE)。此处仅提示已启用。
if args.ctrlc_stack: if args.traceback:
_vlog().info('Ctrl+C 调用栈模式已启用(链接后用 llvm-dwarfdump 导出 <exe>.stackmap)', 'ctrlc-stack') _vlog().info('Ctrl+C 调用栈模式已启用(链接后用 llvm-dwarfdump 导出 <exe>.stackmap)', 'traceback')
# 函数级 DWARF 调试元数据(--dwarf):默认关闭(硬要求——普通构建不承担 # 函数级 DWARF 调试元数据(--dwarf):默认关闭(硬要求——普通构建不承担
# 任何风险,生成的 .ll 与改动前逐字节一致)。--asan 时自动开启:ASan 报告 # 任何风险,生成的 .ll 与改动前逐字节一致)。--asan 时自动开启:ASan 报告
# 本身就需要行号才能定位,且 ASan 构建已是显式选择的非默认路径。 # 本身就需要行号才能定位,且 ASan 构建已是显式选择的非默认路径。
# --ctrlc-stack 同理自动开启:旁文件的"地址→源行"完全来自 DWARF 行表, # --traceback 同理自动开启:旁文件的"地址→源行"完全来自 DWARF 行表,
# 没有 DI 元数据时 .debug_line 为空、旁文件必为空。 # 没有 DI 元数据时 .debug_line 为空、旁文件必为空。
dwarf_enabled: bool = bool(args.dwarf or args.asan or args.ctrlc_stack) dwarf_enabled: bool = bool(args.dwarf or args.asan or args.traceback)
if dwarf_enabled: if dwarf_enabled:
_auto_src: str = '--asan' if args.asan else ('--ctrlc-stack' if args.ctrlc_stack else '') _auto_src: str = '--asan' if args.asan else ('--traceback' if args.traceback else '')
_vlog().info( _vlog().info(
'函数级 DWARF 调试元数据已启用:每个 .vp 模块一份 DICompileUnit + 每函数 DISubprogram/DILocation' '函数级 DWARF 调试元数据已启用:每个 .vp 模块一份 DICompileUnit + 每函数 DISubprogram/DILocation'
+ (f'(由 {_auto_src} 自动开启)' if _auto_src and not args.dwarf else ''), + (f'(由 {_auto_src} 自动开启)' if _auto_src and not args.dwarf else ''),
@@ -349,7 +349,7 @@ def main() -> None:
except: except:
pass pass
_vlog().info(f"includes.binary: 已清空 {precompiled_dir} ({cleared} 个文件)") _vlog().info(f"includes.binary: 已清空 {precompiled_dir} ({cleared} 个文件)")
trans: Phase2Translator = Phase2Translator(src, temp_dir, output_dir, compile_cmd=cc_cmd, compile_flags=cc_flags, linker_cmd=linker_cmd, linker_flags=linker_flags, linker_output=linker_output, static_libs=static_libs, dynamic_libs=dynamic_libs, include_dirs=project_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout, force_recompile_includes=force_recompile_includes, asan_mode=args.asan, dwarf_enabled=dwarf_enabled, ctrlc_stack_enabled=args.ctrlc_stack) trans: Phase2Translator = Phase2Translator(src, temp_dir, output_dir, compile_cmd=cc_cmd, compile_flags=cc_flags, linker_cmd=linker_cmd, linker_flags=linker_flags, linker_output=linker_output, static_libs=static_libs, dynamic_libs=dynamic_libs, include_dirs=project_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout, force_recompile_includes=force_recompile_includes, asan_mode=args.asan, dwarf_enabled=dwarf_enabled, traceback_enabled=args.traceback)
trans.sha1_map = Load_sha1_map(temp_dir) trans.sha1_map = Load_sha1_map(temp_dir)
trans.slice_level = slice_level trans.slice_level = slice_level
# 调用点热点统计与每函数计时(--hotspot-sites):None=未启用(零影响) # 调用点热点统计与每函数计时(--hotspot-sites):None=未启用(零影响)
+47 -11
View File
@@ -10,7 +10,8 @@ from lib.includes import t
from lib.includes.t import CTypeRegistry from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import ( from lib.core.SymbolUtils import (
IsListAnnotation, AnnotationContainsName, IsCPtrSubscriptNode, IsCPtrNode, IsListAnnotation, AnnotationContainsName, IsCPtrSubscriptNode, IsCPtrNode,
IsRawPtrSubscriptNode, IsPtrSubscriptNode, ResolveGenericTypeArgStr) IsRawPtrSubscriptNode, IsPtrSubscriptNode, ResolveGenericTypeArgStr,
EnumClassIntWidth, ClassHasFuncLevelCVTable)
class DeclarationGenerator: class DeclarationGenerator:
@@ -55,7 +56,9 @@ class DeclarationGenerator:
t.configure_platform(self.target_triple) t.configure_platform(self.target_triple)
def generate(self, pyi_content: str, src_path: str) -> str: def generate(self, pyi_content: str, src_path: str) -> str:
tree: ast.Module = ast.parse(pyi_content) # 传 src_path:解析失败时报错/回退诊断才带得上来源(否则 vast 记为 <unknown>,
# 既无法定位文件,也无法知道是哪份 .pyi 出问题)
tree: ast.Module = ast.parse(pyi_content, src_path)
self._DefineConstants: dict[str, int | str] = {} self._DefineConstants: dict[str, int | str] = {}
self._pyi_tree = tree self._pyi_tree = tree
@@ -255,9 +258,17 @@ class DeclarationGenerator:
if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable': if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable':
return None return None
VarType: str = self._get_type_str(node.annotation) VarType: str = self._get_type_str(node.annotation)
if VarType.startswith('[0 x ') and node.value and isinstance(node.value, ast.List): if VarType.startswith('[0 x ') and node.value:
elem_type: str = VarType[5:-1] elem_type: str = VarType[5:-1]
actual_count: int = len(node.value.elts) actual_count: int = 0
if isinstance(node.value, ast.List):
actual_count = len(node.value.elts)
elif isinstance(node.value, ast.Constant) and isinstance(node.value.value, str):
# 字符串字面量:与 HandlesBase.ParseArrayCount(:731-732) / HandlesAssign
# 全局路径同口径(len(str)+1,含结尾 NUL)。此前只修 List 值 → CArray
# 字符串全局的 stub 声明停留在 [0 x i8],与定义端 [N x i8] 长度不一致
# (跨模块 type mismatch / llc 拒绝)。
actual_count = len(node.value.value) + 1
if actual_count > 0: if actual_count > 0:
VarType = f'[{actual_count} x {elem_type}]' VarType = f'[{actual_count} x {elem_type}]'
return f'@{var_name} = external global {VarType}' return f'@{var_name} = external global {VarType}'
@@ -350,6 +361,10 @@ class DeclarationGenerator:
if is_enum: if is_enum:
enum_values: dict[str, int] = {} enum_values: dict[str, int] = {}
next_value: int = 0 next_value: int = 0
# 枚举存储宽度 = 最宽成员类型(与定义端 HandlesClassDef / 跨模块读取端
# 同一规则源 EnumClassIntWidth;此处无 SymbolTable,走 t 类名 / builtins /
# 库 typedef 的表)。声明端的全局类型必须与定义端运行时常量同宽(ABI)。
enum_width: int = EnumClassIntWidth(node.body)
for item in node.body: for item in node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
var_name: str = item.target.id var_name: str = item.target.id
@@ -367,7 +382,7 @@ class DeclarationGenerator:
enum_values[var_name] = next_value enum_values[var_name] = next_value
next_value += 1 next_value += 1
for var_name, value in enum_values.items(): for var_name, value in enum_values.items():
decls.append(f'@__config_{class_name}_{var_name} = external global i32') decls.append(f'@__config_{class_name}_{var_name} = external global i{enum_width}')
return decls return decls
if is_renum: if is_renum:
# REnum 布局: { i32 __tag, <max_variant_payload> } # REnum 布局: { i32 __tag, <max_variant_payload> }
@@ -473,6 +488,14 @@ class DeclarationGenerator:
# (@t.NoVTable 标记的类及其子类不使用 vtable,字段展平嵌入) # (@t.NoVTable 标记的类及其子类不使用 vtable,字段展平嵌入)
if has_parent_class and not is_cvtable and not is_novtable and not parent_is_novtable: if has_parent_class and not is_cvtable and not is_novtable and not parent_is_novtable:
is_cvtable = True is_cvtable = True
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
# 与 HandlesClassDef._resolve_class_flags / TPV _detect_vtable_status 的
# `_has_any_func_cvtable` 分支逐条对齐(类级 NoVTable / 无真实父类)。
# 必须在此同步:定义模块按 IsCVTable 在 struct 首部放 vptr,若 stub 不同步,
# 跨模块使用该类的 struct 布局会比定义模块少一个字段 ⇒ 字段整体错位。
if not is_cvtable and ClassHasFuncLevelCVTable(node):
if is_novtable or not has_parent_class:
is_cvtable = True
base_has_vtable: bool = False base_has_vtable: bool = False
for base in node.bases: for base in node.bases:
base_name: str | None = None base_name: str | None = None
@@ -847,6 +870,7 @@ class DeclarationGenerator:
if has_methods and not is_cpython_object: if has_methods and not is_cpython_object:
is_cpython_object = True is_cpython_object = True
base_has_vtable: bool = False base_has_vtable: bool = False
has_real_parent: bool = False
for base in node.bases: for base in node.bases:
base_name: str | None = None base_name: str | None = None
if isinstance(base, ast.Attribute): if isinstance(base, ast.Attribute):
@@ -859,6 +883,7 @@ class DeclarationGenerator:
elif isinstance(base.value, ast.Name): elif isinstance(base.value, ast.Name):
base_name = base.value.id base_name = base.value.id
if base_name and not self._is_marker_base(base_name): if base_name and not self._is_marker_base(base_name):
has_real_parent = True
_base_node: ast.ClassDef | None = None _base_node: ast.ClassDef | None = None
for n in ast.iter_child_nodes(self._pyi_tree): for n in ast.iter_child_nodes(self._pyi_tree):
if isinstance(n, ast.ClassDef) and n.name == base_name: if isinstance(n, ast.ClassDef) and n.name == base_name:
@@ -874,6 +899,11 @@ class DeclarationGenerator:
base_has_vtable = True base_has_vtable = True
if base_has_vtable: if base_has_vtable:
break break
# 方法级 virtual 触发类级 vtable(与上方 _generate_class_decl 同一判据,
# 保持在同一个类里两处口径一致;详见该处注释)
if not is_cvtable and not base_has_vtable and ClassHasFuncLevelCVTable(node):
if is_novtable or not has_real_parent:
is_cvtable = True
return has_methods and is_cvtable and not base_has_vtable and not is_novtable return has_methods and is_cvtable and not base_has_vtable and not is_novtable
def _FlattenInheritedDecl(self, cls_name: str, seen_cls: set, member_types: list[str], def _FlattenInheritedDecl(self, cls_name: str, seen_cls: set, member_types: list[str],
@@ -1156,7 +1186,7 @@ class DeclarationGenerator:
return _dg_llvm return _dg_llvm
_dg_node: ast.AST | None = None _dg_node: ast.AST | None = None
try: try:
_dg_node = ast.parse(_dg_bv, mode='eval').body _dg_node = ast.parse(_dg_bv, '<DeclarationGenerator:type_expr>', mode='eval').body
except Exception: except Exception:
_dg_node = None _dg_node = None
if _dg_node is not None: if _dg_node is not None:
@@ -1291,11 +1321,13 @@ class DeclarationGenerator:
return f'[{self._get_const_int(annotation.slice)} x i8]' return f'[{self._get_const_int(annotation.slice)} x i8]'
if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int):
return f'[{annotation.slice.value} x {base}]' return f'[{annotation.slice.value} x {base}]'
# 处理 t.CArray[elem_type, count] / rawarray[elem_type, count] 注解 → [count x elem_type] # 处理 t.CArray[elem_type, count] / rawarray[elem_type, count] / array[elem_type, count]
# 支持 t.CArray (ast.Attribute)、CArray/rawarray (ast.Name) 三种写法 # 注解 → [count x elem_type]
# 支持 t.CArray (ast.Attribute)、CArray/rawarray/array (ast.Name) 三种写法
# (array 为本轮新增的内建软关键字,与 t.CArray 逐点等价)
ValueIsCArray: bool = ( ValueIsCArray: bool = (
(isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CArray') (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CArray')
or (isinstance(annotation.value, ast.Name) and annotation.value.id in ('CArray', 'rawarray')) or (isinstance(annotation.value, ast.Name) and annotation.value.id in ('CArray', 'rawarray', 'array'))
) )
if ValueIsCArray: if ValueIsCArray:
SliceNode: ast.AST = annotation.slice SliceNode: ast.AST = annotation.slice
@@ -1307,9 +1339,13 @@ class DeclarationGenerator:
# count 为 None 或 0 → 零长度数组(外部符号) # count 为 None 或 0 → 零长度数组(外部符号)
return f'[0 x {ElemType}]' return f'[0 x {ElemType}]'
elif isinstance(SliceNode, (ast.Attribute, ast.Name, ast.Subscript)): elif isinstance(SliceNode, (ast.Attribute, ast.Name, ast.Subscript)):
# 单参数 t.CArray[elem_type] → 指针模式 # 单参数 t.CArray[elem_type]:与 t.CArray[elem_type, None] 语义统一为
# "长度自动推导数组"。此处无初始化字面量可推导 → 与 ,None 分支同用
# [0 x elem] 占位,真实长度由 _generate_global_decl 按初始化字面量
# (List 长度 / str 长度+1) 修正。
# 参数位置经 _decay_array_to_ptr(:23-28) 退化为 elem*,与旧指针行为一致。
ElemType: str = self._get_type_str(SliceNode, embedded=True) ElemType: str = self._get_type_str(SliceNode, embedded=True)
return f'{ElemType}*' return f'[0 x {ElemType}]'
if isinstance(annotation.value, ast.Name) and annotation.value.id == 'tuple': if isinstance(annotation.value, ast.Name) and annotation.value.id == 'tuple':
slice_node: ast.AST = annotation.slice slice_node: ast.AST = annotation.slice
elem_types: list[str] elem_types: list[str]
+14 -1
View File
@@ -9,6 +9,7 @@ import traceback
from lib.Projectrans.Utils import compute_sha1, get_file_dependencies, find_reachable_files_from_entries, parse_python_file from lib.Projectrans.Utils import compute_sha1, get_file_dependencies, find_reachable_files_from_entries, parse_python_file
from lib.Projectrans.DeclarationGenerator import DeclarationGenerator from lib.Projectrans.DeclarationGenerator import DeclarationGenerator
from lib.core.VLogger import get_logger as _vlog from lib.core.VLogger import get_logger as _vlog
from lib.core.SymbolUtils import ExpandSoftKeywords
from lib.constants.config import mode as _ConfigMode from lib.constants.config import mode as _ConfigMode
from lib.includes import t from lib.includes import t
from StubGen import PythonToStubConverter from StubGen import PythonToStubConverter
@@ -383,6 +384,11 @@ class Phase1Generator:
用于 .stub.ll 缓存依赖校验:当依赖的 include SHA1 变化时, 用于 .stub.ll 缓存依赖校验:当依赖的 include SHA1 变化时,
当前 include 的 .stub.ll 缓存应失效(因为类型引用的 SHA1 前缀会变化)。 当前 include 的 .stub.ll 缓存应失效(因为类型引用的 SHA1 前缀会变化)。
签名同时纳入 includes/builtins.vp:它是注解类型解析的隐式输入
(不在任何模块的 import 闭包,但其 CTypedef 参与签名/字段类型解析),
其内容变化必须让所有 .stub.ll 失效——否则旧声明(如 sizet 曾被解析成
%"{mod}.sizet"* 假结构体)会被缓存继续引用,源码修复不生效。
""" """
if not self._include_dep_graph or not self._include_file_map: if not self._include_dep_graph or not self._include_file_map:
return '' return ''
@@ -400,6 +406,11 @@ class Phase1Generator:
if DepSha1: if DepSha1:
Result.add(DepSha1) Result.add(DepSha1)
Queue.extend(self._include_dep_graph.get(DepMod, set())) Queue.extend(self._include_dep_graph.get(DepMod, set()))
BuiltinsInfo: tuple[str, str, str] | None = self._include_file_map.get('builtins')
if BuiltinsInfo is not None:
BuiltinsSha1: str = self._get_sha1(BuiltinsInfo[0])
if BuiltinsSha1:
Result.add(BuiltinsSha1)
return '|'.join(sorted(Result)) return '|'.join(sorted(Result))
def _ReadStubDep(self, stub_path: str) -> str: def _ReadStubDep(self, stub_path: str) -> str:
@@ -510,7 +521,9 @@ class Phase1Generator:
""" """
result: dict[str, str] = {} result: dict[str, str] = {}
try: try:
tree = ast.parse(content) # `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析,
# 否则 SyntaxError 被吞成"无 docstring",与主翻译路径口径不一致。
tree = ast.parse(ExpandSoftKeywords(content), '<Phase1Generator:_extract_docstrings>')
except SyntaxError: except SyntaxError:
return result return result
for node in tree.body: for node in tree.body:
+94 -94
View File
@@ -31,11 +31,11 @@ from lib.core.LLVMCG.IRMetadataFix import SanitizeIRMetadata
_FILE_STAT_CACHE: dict[str, bool] = {} _FILE_STAT_CACHE: dict[str, bool] = {}
# --ctrlc-stack(Ctrl+C 调用栈)强制纳入编译+链接的 include 模块源文件名。 # --traceback(Ctrl+C 调用栈)强制纳入编译+链接的 include 模块源文件名。
# 该模块对外只有 Install() 入口,且无任何用户代码 import 它 —— 若不强制纳入, # 该模块对外只有 Install() 入口,且无任何用户代码 import 它 —— 若不强制纳入,
# 产物里既没有 Install 符号、也没有调用它的启动期构造器,功能静默失效。 # 产物里既没有 Install 符号、也没有调用它的启动期构造器,功能静默失效。
# .vp 优先,.py 仅为向后兼容的回退名。 # .vp 优先,.py 仅为向后兼容的回退名。
_CTRLC_STACK_MODULE_FILES: tuple[str, ...] = ('dbgstack.vp', 'dbgstack.py') _TRACEBACK_MODULE_FILES: tuple[str, ...] = ('traceback.vp', 'traceback.py')
def _CachedIsFile(path: str) -> bool: def _CachedIsFile(path: str) -> bool:
@@ -80,9 +80,9 @@ def _FindAsanRuntimeDir() -> str | None:
return found[-1] return found[-1]
# ---------- --ctrlc-stack:地址→源行 旁文件(<exe>.stackmap)---------- # ---------- --traceback:地址→源行 旁文件(<exe>.stackmap)----------
# #
# 开启 --ctrlc-stack 时,链接成功后用 llvm-dwarfdump 解析最终 PE 的 # 开启 --traceback 时,链接成功后用 llvm-dwarfdump 解析最终 PE 的
# .debug_line/.debug_info,把已有的"函数级/语句级 DWARF 行标记"导出成 # .debug_line/.debug_info,把已有的"函数级/语句级 DWARF 行标记"导出成
# <exe>.stackmap,供 exe 内运行时处理器在 Ctrl+C/Ctrl+Break 时把 CRT 到当前 # <exe>.stackmap,供 exe 内运行时处理器在 Ctrl+C/Ctrl+Break 时把 CRT 到当前
# 的返回地址逐帧翻译回 ".vp:行/函数"。以下函数只在开关开启时被调用;未开启时 # 的返回地址逐帧翻译回 ".vp:行/函数"。以下函数只在开关开启时被调用;未开启时
@@ -95,7 +95,7 @@ def _ReadPEImageLayout(exe_path: str) -> tuple[int, int, dict[str, tuple[int, in
"""读取 PE 的 (ImageBase, SizeOfImage, 节表) ;非 PE / 解析失败返回 None。 """读取 PE 的 (ImageBase, SizeOfImage, 节表) ;非 PE / 解析失败返回 None。
节表为 {节名: (节 RVA, 节 VirtualSize)}。写端需要这三样才能把 DWARF 地址严格 节表为 {节名: (节 RVA, 节 VirtualSize)}。写端需要这三样才能把 DWARF 地址严格
归一到"运行期 RVA"(运行期 dbgstack.vp 用 `帧地址 − GetModuleHandleA(NULL)`): 归一到"运行期 RVA"(运行期 traceback.vp 用 `帧地址 − GetModuleHandleA(NULL)`):
* 合法行 ⇔ 地址落在映像 VA 区间 [ImageBase, ImageBase + SizeOfImage); * 合法行 ⇔ 地址落在映像 VA 区间 [ImageBase, ImageBase + SizeOfImage);
* "运行期可能出现的 RVA 区间"的粗判据 = [.text 节 RVA, SizeOfImage)。 * "运行期可能出现的 RVA 区间"的粗判据 = [.text 节 RVA, SizeOfImage)。
(原 _ReadPEImageBase 只返回 ImageBase,不足以做上述判定,故扩展为本函数。) (原 _ReadPEImageBase 只返回 ImageBase,不足以做上述判定,故扩展为本函数。)
@@ -413,7 +413,7 @@ def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp
hotspot_sites_file: str | None = None, hotspot_sites_file: str | None = None,
hotspot_sites_all: bool = False, hotspot_sites_all: bool = False,
hotspot_callees: list[str] | None = None, hotspot_callees: list[str] | None = None,
ctrlc_stack_enabled: bool = False, traceback_enabled: bool = False,
dwarf_enabled: bool = False) -> tuple[str, str, str]: dwarf_enabled: bool = False) -> tuple[str, str, str]:
# 注意:不可跨任务复用 Phase2Translator/共享符号表!_translate_file 编译过程中 # 注意:不可跨任务复用 Phase2Translator/共享符号表!_translate_file 编译过程中
# 会向共享 SymbolTable 写入符号(import insert/类注册等),复用导致前文件的 # 会向共享 SymbolTable 写入符号(import insert/类注册等),复用导致前文件的
@@ -437,10 +437,10 @@ def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp
trans.hotspot_sites_file = hotspot_sites_file trans.hotspot_sites_file = hotspot_sites_file
trans.hotspot_sites_all = hotspot_sites_all trans.hotspot_sites_all = hotspot_sites_all
trans.hotspot_callees = hotspot_callees trans.hotspot_callees = hotspot_callees
# Ctrl+C 调用栈(--ctrlc-stack):worker 子进程无父进程属性,须显式透传 # Ctrl+C 调用栈(--traceback):worker 子进程无父进程属性,须显式透传
# (False=未启用,零影响);dbgstack 模块据此注入 Install() 启动期构造器 # (False=未启用,零影响);traceback 模块据此注入 Install() 启动期构造器
trans.ctrlc_stack_enabled = ctrlc_stack_enabled trans.traceback_enabled = traceback_enabled
# 函数级 DWARF(--ctrlc-stack 时 include 模块也必须带行表,否则栈里落到 # 函数级 DWARF(--traceback 时 include 模块也必须带行表,否则栈里落到
# includes/* 的帧取不到源行):worker 子进程无父进程属性,须显式透传 # includes/* 的帧取不到源行):worker 子进程无父进程属性,须显式透传
# (False=未启用,零影响),仅用于 _translate_file 的 dwarf_enabled 形参 # (False=未启用,零影响),仅用于 _translate_file 的 dwarf_enabled 形参
trans.dwarf_enabled = dwarf_enabled trans.dwarf_enabled = dwarf_enabled
@@ -736,7 +736,7 @@ def _BuildPyiCacheFile(temp_dir: str) -> str | None:
continue continue
try: try:
with open(p, 'r', encoding='utf-8') as f: with open(p, 'r', encoding='utf-8') as f:
cache[p] = ast.parse(f.read()) cache[p] = ast.parse(f.read(), p)
except Exception: except Exception:
cache[p] = None cache[p] = None
try: try:
@@ -776,7 +776,7 @@ def _PyiCacheWorkerInitializer(temp_dir: str) -> None:
for p in _ScanPyiFiles(temp_dir): for p in _ScanPyiFiles(temp_dir):
try: try:
with open(p, 'r', encoding='utf-8') as f: with open(p, 'r', encoding='utf-8') as f:
new_cache[p] = ast.parse(f.read()) new_cache[p] = ast.parse(f.read(), p)
except Exception: except Exception:
new_cache[p] = None new_cache[p] = None
if new_cache: if new_cache:
@@ -914,7 +914,7 @@ class Phase2Translator:
_include_sha1_cache: dict[str, str] = {} _include_sha1_cache: dict[str, str] = {}
_include_exist_cache: dict[str, bool] = {} _include_exist_cache: dict[str, bool] = {}
def __init__(self, src_root: str, temp_dir: str, output_dir: str, compile_cmd: str = 'llc', compile_flags: list[str] | None = None, linker_cmd: str | None = None, linker_flags: list[str] | None = None, linker_output: str | None = None, static_libs: list[str] | None = None, dynamic_libs: list[str] | None = None, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, force_recompile_includes: bool = False, build_pyi_cache: bool = True, asan_mode: bool = False, dwarf_enabled: bool = False, ctrlc_stack_enabled: bool = False) -> None: def __init__(self, src_root: str, temp_dir: str, output_dir: str, compile_cmd: str = 'llc', compile_flags: list[str] | None = None, linker_cmd: str | None = None, linker_flags: list[str] | None = None, linker_output: str | None = None, static_libs: list[str] | None = None, dynamic_libs: list[str] | None = None, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, force_recompile_includes: bool = False, build_pyi_cache: bool = True, asan_mode: bool = False, dwarf_enabled: bool = False, traceback_enabled: bool = False) -> None:
self.src_root = os.path.abspath(src_root) self.src_root = os.path.abspath(src_root)
self.temp_dir = os.path.abspath(temp_dir) self.temp_dir = os.path.abspath(temp_dir)
self.output_dir = os.path.abspath(output_dir) self.output_dir = os.path.abspath(output_dir)
@@ -950,12 +950,12 @@ class Phase2Translator:
# !llvm.dbg.cu + 每函数 DISubprogram + 入口 DILocation,供 ASan / # !llvm.dbg.cu + 每函数 DISubprogram + 入口 DILocation,供 ASan /
# llvm-symbolizer 反查 ".vp:函数起始行"。 # llvm-symbolizer 反查 ".vp:函数起始行"。
self.dwarf_enabled: bool = dwarf_enabled self.dwarf_enabled: bool = dwarf_enabled
# Ctrl+C 调用栈(--ctrlc-stack):False=未启用(零影响,不调用任何外部 # Ctrl+C 调用栈(--traceback):False=未启用(零影响,不调用任何外部
# 工具、不写任何旁文件);True=链接成功后额外用 llvm-dwarfdump 把最终 # 工具、不写任何旁文件);True=链接成功后额外用 llvm-dwarfdump 把最终
# exe 的 DWARF 行表导出为 "<exe>.stackmap"(地址→源行旁文件,供 exe 内 # exe 的 DWARF 行表导出为 "<exe>.stackmap"(地址→源行旁文件,供 exe 内
# 运行时在 Ctrl+C/Ctrl+Break 时逐帧翻译返回地址)。要求 --dwarf 已开启 # 运行时在 Ctrl+C/Ctrl+Break 时逐帧翻译返回地址)。要求 --dwarf 已开启
# (Config 侧联动,否则行表为空),且目标为 Windows PE。 # (Config 侧联动,否则行表为空),且目标为 Windows PE。
self.ctrlc_stack_enabled: bool = ctrlc_stack_enabled self.traceback_enabled: bool = traceback_enabled
# 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数 # 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数
# 落盘文件路径(每函数计时落同目录 hotspot_time.txt)。各模块运行时自带 # 落盘文件路径(每函数计时落同目录 hotspot_time.txt)。各模块运行时自带
# Ctrl+C 处理器、统一以追加方式写该路径;含 main 的模块在 main 入口清空一次 # Ctrl+C 处理器、统一以追加方式写该路径;含 main 的模块在 main 入口清空一次
@@ -1173,9 +1173,9 @@ class Phase2Translator:
# 行为与改动前完全一致(零影响)。 # 行为与改动前完全一致(零影响)。
# --dwarf(函数级 DWARF)同理:缓存 .ll/.obj 不含 !llvm.dbg.cu,必须一并 # --dwarf(函数级 DWARF)同理:缓存 .ll/.obj 不含 !llvm.dbg.cu,必须一并
# 强制重译,否则开启开关后仍复用旧产物、看不到任何 .vp 行号。 # 强制重译,否则开启开关后仍复用旧产物、看不到任何 .vp 行号。
# --ctrlc-stack 依赖同一份 .debug_line 行表(Config 已联动开启 --dwarf), # --traceback 依赖同一份 .debug_line 行表(Config 已联动开启 --dwarf),
# 这里显式纳入,避免将来联动改动后静默复用无 DI 的旧产物。 # 这里显式纳入,避免将来联动改动后静默复用无 DI 的旧产物。
hotspot_on: bool = bool(self.hotspot_sites_file) or bool(self.dwarf_enabled) or bool(self.ctrlc_stack_enabled) hotspot_on: bool = bool(self.hotspot_sites_file) or bool(self.dwarf_enabled) or bool(self.traceback_enabled)
for i, src_path in enumerate(py_files, 1): for i, src_path in enumerate(py_files, 1):
rel = os.path.relpath(src_path, self.src_root) rel = os.path.relpath(src_path, self.src_root)
# 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过) # 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过)
@@ -1786,7 +1786,7 @@ class Phase2Translator:
# `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析: # `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析:
# 否则此处 SyntaxError 会静默 return 原 code(自动注入 import _list/_dict # 否则此处 SyntaxError 会静默 return 原 code(自动注入 import _list/_dict
# 失效),与主翻译路径(Shell/TransPyC 已展开)口径不一致。 # 失效),与主翻译路径(Shell/TransPyC 已展开)口径不一致。
tree = ast.parse(ExpandSoftKeywords(code)) tree = ast.parse(ExpandSoftKeywords(code), src_path)
except SyntaxError: except SyntaxError:
return code return code
@@ -1851,10 +1851,10 @@ class Phase2Translator:
"""翻译单个源文件,嵌入相关 .stub.ll 声明 """翻译单个源文件,嵌入相关 .stub.ll 声明
dwarf_enabled: 函数级 DWARF 调试元数据(--dwarf),默认 False(零影响)。 dwarf_enabled: 函数级 DWARF 调试元数据(--dwarf),默认 False(零影响)。
App 源模块在 --dwarf/--asan/--ctrlc-stack 时传 True。include 文件默认 App 源模块在 --dwarf/--asan/--traceback 时传 True。include 文件默认
传 False(其 .obj 会落进跨项目共享的 includes.binary 预编译缓存,注入 DI 传 False(其 .obj 会落进跨项目共享的 includes.binary 预编译缓存,注入 DI
会污染该缓存);仅 --ctrlc-stack 时传 True——此时 include 的 .obj 改落 会污染该缓存);仅 --traceback 时传 True——此时 include 的 .obj 改落
隔离目录(includes.binary_ctrlcstack,见 _get_precompiled_obj_path), 隔离目录(includes.binary_traceback,见 _get_precompiled_obj_path),
不会污染共享缓存。 不会污染共享缓存。
""" """
_t0: float = time.time() _t0: float = time.time()
@@ -1891,15 +1891,15 @@ class Phase2Translator:
# 未启用时不设置 → 与改动前逐字节一致(零影响) # 未启用时不设置 → 与改动前逐字节一致(零影响)
if dwarf_enabled: if dwarf_enabled:
trans.translator.DwarfEnabled = True trans.translator.DwarfEnabled = True
# Ctrl+C 调用栈(--ctrlc-stack):仅当本文件就是 dbgstack 模块时置位—— # Ctrl+C 调用栈(--traceback):仅当本文件就是 traceback 模块时置位——
# 只有该模块需要注入 @llvm.global_ctors 构造器调用自己的 Install() # 只有该模块需要注入 @llvm.global_ctors 构造器调用自己的 Install()
# (见 BaseGen._RegisterDbgStackInstallViaCtor)。其它模块不设置该属性 # (见 BaseGen._RegisterTracebackInstallViaCtor)。其它模块不设置该属性
# → Gen.ctrlc_stack_ctor 保持 False,IR 与改动前逐字节一致(零影响)。 # → Gen.traceback_ctor 保持 False,IR 与改动前逐字节一致(零影响)。
_ctrlc_stack_ctor: bool = bool( _traceback_ctor: bool = bool(
self.ctrlc_stack_enabled self.traceback_enabled
and os.path.basename(src_path) in _CTRLC_STACK_MODULE_FILES) and os.path.basename(src_path) in _TRACEBACK_MODULE_FILES)
if _ctrlc_stack_ctor: if _traceback_ctor:
trans.translator.CtrlcStackCtor = True trans.translator.TracebackCtor = True
trans.SliceLevel = self.slice_level trans.SliceLevel = self.slice_level
trans.translator.SliceLevel = self.slice_level trans.translator.SliceLevel = self.slice_level
trans.translator.SliceCount = 0 trans.translator.SliceCount = 0
@@ -1953,7 +1953,7 @@ class Phase2Translator:
try: try:
with open(sig_path, 'r', encoding='utf-8') as f: with open(sig_path, 'r', encoding='utf-8') as f:
sig_content = f.read() sig_content = f.read()
sig_tree = ast.parse(sig_content) sig_tree = ast.parse(sig_content, sig_path)
for node in ast.iter_child_nodes(sig_tree): for node in ast.iter_child_nodes(sig_tree):
if isinstance(node, ast.FunctionDef): if isinstance(node, ast.FunctionDef):
if _check_annotation_for_export(node.returns): if _check_annotation_for_export(node.returns):
@@ -2097,13 +2097,13 @@ class Phase2Translator:
out_dir = os.path.dirname(out_path) out_dir = os.path.dirname(out_path)
if out_dir: if out_dir:
os.makedirs(out_dir, exist_ok=True) os.makedirs(out_dir, exist_ok=True)
# --ctrlc-stack:核对注入钩子确实生效——本模块要求注入 @llvm.global_ctors # --traceback:核对注入钩子确实生效——本模块要求注入 @llvm.global_ctors
# 构造器(StructGen.finalize 里落成 .ll 文本之前),若产物中没有该全局 # 构造器(StructGen.finalize 里落成 .ll 文本之前),若产物中没有该全局
# 说明钩子不可用(如序列化路径被改动绕过),显式报错终止,不静默产出 # 说明钩子不可用(如序列化路径被改动绕过),显式报错终止,不静默产出
# 缺注册的产物(那会让整个功能静默失效)。 # 缺注册的产物(那会让整个功能静默失效)。
if _ctrlc_stack_ctor and 'llvm.global_ctors' not in result: if _traceback_ctor and 'llvm.global_ctors' not in result:
raise RuntimeError( raise RuntimeError(
'--ctrlc-stack: dbgstack 模块未注入 @llvm.global_ctors ' '--traceback: traceback 模块未注入 @llvm.global_ctors '
'(模块级收尾钩子未生效,不静默降级)') '(模块级收尾钩子未生效,不静默降级)')
# 修复 LLVM 22+: external global 不能有初值,改为 linkonce # 修复 LLVM 22+: external global 不能有初值,改为 linkonce
result = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', result, flags=re.MULTILINE) result = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', result, flags=re.MULTILINE)
@@ -2218,8 +2218,8 @@ class Phase2Translator:
return opt_r return opt_r
compile_input = asan_ll compile_input = asan_ll
cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, compile_input] cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, compile_input]
if self.ctrlc_stack_enabled: if self.traceback_enabled:
# --ctrlc-stack:让 llc 保留帧指针(-frame-pointer=all)。dbghelp 主路径 # --traceback:让 llc 保留帧指针(-frame-pointer=all)。dbghelp 主路径
# 不依赖帧指针,但符号表不可用时要靠 RBP 链兜底——llc 默认 # 不依赖帧指针,但符号表不可用时要靠 RBP 链兜底——llc 默认
# omit-frame-pointer 会让 RBP 链只走出 1~2 帧(实测"共 2 帧")。 # omit-frame-pointer 会让 RBP 链只走出 1~2 帧(实测"共 2 帧")。
# 只在开启该开关时加,关闭时命令与改前逐字一致。 # 只在开启该开关时加,关闭时命令与改前逐字一致。
@@ -2786,14 +2786,14 @@ class Phase2Translator:
def _scan_include_libraries(self) -> None: def _scan_include_libraries(self) -> None:
"""扫描 includes 目录,检测被使用的模块对应的库文件和源文件""" """扫描 includes 目录,检测被使用的模块对应的库文件和源文件"""
# --ctrlc-stack:强制纳入 dbgstack 模块 —— 无任何用户代码 import 它, # --traceback:强制纳入 traceback 模块 —— 无任何用户代码 import 它,
# 不纳入就不会被翻译 ⇒ 产物没有 Install 符号可注入启动期构造器。 # 不纳入就不会被翻译 ⇒ 产物没有 Install 符号可注入启动期构造器。
# 直接借用既有的 used_includes 收集路径(下面按同一套 py_index / # 直接借用既有的 used_includes 收集路径(下面按同一套 py_index /
# 传递依赖逻辑把它加入 extra_py_files,再交给 _compile_include_py_files # 传递依赖逻辑把它加入 extra_py_files,再交给 _compile_include_py_files
# 编译并把 .obj 追加进 extra_link_files)。开关关闭时不触碰 used_includes # 编译并把 .obj 追加进 extra_link_files)。开关关闭时不触碰 used_includes
# → 本函数行为与改动前逐字一致(零影响)。 # → 本函数行为与改动前逐字一致(零影响)。
if self.ctrlc_stack_enabled: if self.traceback_enabled:
self.used_includes.add('dbgstack') self.used_includes.add('traceback')
if not self.used_includes: if not self.used_includes:
return return
@@ -2845,16 +2845,16 @@ class Phase2Translator:
_vlog().info(f"\n[includes] 需要链接的库文件: {len(self.extra_link_files)} 个") _vlog().info(f"\n[includes] 需要链接的库文件: {len(self.extra_link_files)} 个")
if self.extra_compile_files: if self.extra_compile_files:
_vlog().info(f"[includes] 需要编译的源文件: {len(self.extra_compile_files)} 个") _vlog().info(f"[includes] 需要编译的源文件: {len(self.extra_compile_files)} 个")
# --ctrlc-stack:断言 dbgstack 源文件确实被纳入待编译集合。找不到就必然 # --traceback:断言 traceback 源文件确实被纳入待编译集合。找不到就必然
# 得不到 Install 符号、构造器无处注入(功能静默失效)——显式报错终止, # 得不到 Install 符号、构造器无处注入(功能静默失效)——显式报错终止,
# 不静默跳过(多半是 project.json 的 includes 未包含仓库的 includes 目录)。 # 不静默跳过(多半是 project.json 的 includes 未包含仓库的 includes 目录)。
if self.ctrlc_stack_enabled: if self.traceback_enabled:
_dbgstack_found: bool = any( _traceback_found: bool = any(
os.path.basename(_p) in _CTRLC_STACK_MODULE_FILES os.path.basename(_p) in _TRACEBACK_MODULE_FILES
for _p in self.extra_py_files) for _p in self.extra_py_files)
if not _dbgstack_found: if not _traceback_found:
raise RuntimeError( raise RuntimeError(
'--ctrlc-stack: 在 includes 目录中找不到 dbgstack.vp/.py,' '--traceback: 在 includes 目录中找不到 traceback.vp/.py,'
'无法编译并链接 Install()(请确认 project.json 的 includes ' '无法编译并链接 Install()(请确认 project.json 的 includes '
f'包含仓库 includes 目录;已扫描: {self.include_dirs})') f'包含仓库 includes 目录;已扫描: {self.include_dirs})')
@@ -3104,14 +3104,14 @@ class Phase2Translator:
预编译目录位于 include 目录同级,命名为 {basename}.binary/。 预编译目录位于 include 目录同级,命名为 {basename}.binary/。
文件命名为 {sha1}.{triple_safe}.{rel_model}.obj,区分目标平台和重定位模型。 文件命名为 {sha1}.{triple_safe}.{rel_model}.obj,区分目标平台和重定位模型。
--ctrlc-stack:include 模块也带 DWARF 行表(栈帧要能落到 includes/* 的源行), --traceback:include 模块也带 DWARF 行表(栈帧要能落到 includes/* 的源行),
此类 .obj 不得回写进跨项目共享的 includes.binary(会污染其它项目的预编译 此类 .obj 不得回写进跨项目共享的 includes.binary(会污染其它项目的预编译
缓存),改落**兄弟目录** {basename}.binary + 后缀(不新增层级,形态与 缓存),改落**兄弟目录** {basename}.binary + 后缀(不新增层级,形态与
ASan 变体目录 includes.binary_asan 一致)。 ASan 变体目录 includes.binary_asan 一致)。
关闭时后缀为空 → 路径与改动前逐字一致(含 --dwarf/--asan 单独开启的情形)。 关闭时后缀为空 → 路径与改动前逐字一致(含 --dwarf/--asan 单独开启的情形)。
--asan 与 --ctrlc-stack 同开:ASan 插桩也会改变 include obj 内容(见 --asan 与 --traceback 同开:ASan 插桩也会改变 include obj 内容(见
_llc_compile),两者产物不可互相复用,故再叠加 _asan 后缀区分 _llc_compile),两者产物不可互相复用,故再叠加 _asan 后缀区分
(优先级:是否隔离由 --ctrlc-stack 决定,--asan 只决定后缀再叠加 _asan)。 (优先级:是否隔离由 --traceback 决定,--asan 只决定后缀再叠加 _asan)。
""" """
AbsSrcPath: str = os.path.abspath(src_path) AbsSrcPath: str = os.path.abspath(src_path)
RelModel: str = 'static' RelModel: str = 'static'
@@ -3128,8 +3128,8 @@ class Phase2Translator:
inc_parent: str = os.path.dirname(abs_inc) inc_parent: str = os.path.dirname(abs_inc)
inc_base: str = os.path.basename(abs_inc) inc_base: str = os.path.basename(abs_inc)
_iso_suffix: str = '' _iso_suffix: str = ''
if self.ctrlc_stack_enabled: if self.traceback_enabled:
_iso_suffix = '_ctrlcstack_asan' if self.asan_mode else '_ctrlcstack' _iso_suffix = '_traceback_asan' if self.asan_mode else '_traceback'
precompiled_dir: str = os.path.join(inc_parent, inc_base + '.binary' + _iso_suffix) precompiled_dir: str = os.path.join(inc_parent, inc_base + '.binary' + _iso_suffix)
triple_safe: str = (self.triple or 'default').replace(os.sep, '_').replace('/', '_').replace('\\', '_').replace(':', '_') triple_safe: str = (self.triple or 'default').replace(os.sep, '_').replace('/', '_').replace('\\', '_').replace(':', '_')
return os.path.join(precompiled_dir, f"{sha1}.{triple_safe}.{RelModel}.obj") return os.path.join(precompiled_dir, f"{sha1}.{triple_safe}.{RelModel}.obj")
@@ -3155,18 +3155,18 @@ class Phase2Translator:
# 本函数行为与改动前逐字节一致(零影响)。 # 本函数行为与改动前逐字节一致(零影响)。
hotspot_on: bool = bool(self.hotspot_sites_file) hotspot_on: bool = bool(self.hotspot_sites_file)
# --ctrlc-stack:include 模块也必须带 DWARF 行表(否则运行时 Ctrl+C 的栈里 # --traceback:include 模块也必须带 DWARF 行表(否则运行时 Ctrl+C 的栈里
# 落在 includes/*(memhub/string/viperlib/w32…)的帧取不到源行,只能落到 # 落在 includes/*(memhub/string/viperlib/w32…)的帧取不到源行,只能落到
# 邻行或 "+0x…(unknown)")。为此翻译时给 include 透传 dwarf_enabled=True # 邻行或 "+0x…(unknown)")。为此翻译时给 include 透传 dwarf_enabled=True
# (见下方 worker/串行四处的 dwarf_enabled=self.ctrlc_stack_enabled),并把 # (见下方 worker/串行四处的 dwarf_enabled=self.traceback_enabled),并把
# 旧目标文件(output 目录 {sha1}.obj 及隔离缓存 includes.binary_ctrlcstack # 旧目标文件(output 目录 {sha1}.obj 及隔离缓存 includes.binary_traceback
# 内的旧 .obj)一并失效重译——复用上一次无 DI 的产物会让行表静默缺失。 # 内的旧 .obj)一并失效重译——复用上一次无 DI 的产物会让行表静默缺失。
# 与 hotspot_on 处理方式一致;同时覆盖 dbgstack.vp(其 .obj 还须含 # 与 hotspot_on 处理方式一致;同时覆盖 traceback.vp(其 .obj 还须含
# @llvm.global_ctors 启动期构造器)。取舍:开启本开关时 include 目标文件每 # @llvm.global_ctors 启动期构造器)。取舍:开启本开关时 include 目标文件每
# 次构建都重译(放弃隔离缓存的复用,换取"必然带 DWARF"的正确性保证), # 次构建都重译(放弃隔离缓存的复用,换取"必然带 DWARF"的正确性保证),
# 仅调试模式可接受。开关关闭时 include_dwarf=False,本函数行为与改动前 # 仅调试模式可接受。开关关闭时 include_dwarf=False,本函数行为与改动前
# 逐字节一致(零影响,含 --dwarf/--asan 单独开启的情形)。 # 逐字节一致(零影响,含 --dwarf/--asan 单独开启的情形)。
include_dwarf: bool = bool(self.ctrlc_stack_enabled) include_dwarf: bool = bool(self.traceback_enabled)
# 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射) # 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射)
include_ModuleSha1Map = self._build_current_ModuleSha1Map(None) include_ModuleSha1Map = self._build_current_ModuleSha1Map(None)
@@ -3206,7 +3206,7 @@ class Phase2Translator:
self._collect_inline_symbols(src_path) self._collect_inline_symbols(src_path)
continue continue
# --hotspot-sites / --ctrlc-stack:include 的 .py 未变时下面两处会因旧 # --hotspot-sites / --traceback:include 的 .py 未变时下面两处会因旧
# .obj 存在而直接跳过,复用上一次(未插桩 / 不含 DI)的目标文件。 # .obj 存在而直接跳过,复用上一次(未插桩 / 不含 DI)的目标文件。
# 启用时先让这两类旧 .obj 一并失效,使其必然走入 need_translate 重新 # 启用时先让这两类旧 .obj 一并失效,使其必然走入 need_translate 重新
# 翻译并嵌入热点计数/计时 IR 或 DWARF 行表。 # 翻译并嵌入热点计数/计时 IR 或 DWARF 行表。
@@ -3351,8 +3351,8 @@ class Phase2Translator:
hotspot_sites_file=self.hotspot_sites_file, hotspot_sites_file=self.hotspot_sites_file,
hotspot_sites_all=self.hotspot_sites_all, hotspot_sites_all=self.hotspot_sites_all,
hotspot_callees=self.hotspot_callees, hotspot_callees=self.hotspot_callees,
ctrlc_stack_enabled=self.ctrlc_stack_enabled, traceback_enabled=self.traceback_enabled,
dwarf_enabled=self.ctrlc_stack_enabled dwarf_enabled=self.traceback_enabled
) )
in_progress[future] = item in_progress[future] = item
@@ -3372,8 +3372,8 @@ class Phase2Translator:
hotspot_sites_file=self.hotspot_sites_file, hotspot_sites_file=self.hotspot_sites_file,
hotspot_sites_all=self.hotspot_sites_all, hotspot_sites_all=self.hotspot_sites_all,
hotspot_callees=self.hotspot_callees, hotspot_callees=self.hotspot_callees,
ctrlc_stack_enabled=self.ctrlc_stack_enabled, traceback_enabled=self.traceback_enabled,
dwarf_enabled=self.ctrlc_stack_enabled dwarf_enabled=self.traceback_enabled
) )
in_progress[future] = item in_progress[future] = item
remaining.clear() remaining.clear()
@@ -3417,12 +3417,12 @@ class Phase2Translator:
for item in need_translate: for item in need_translate:
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.ctrlc_stack_enabled) self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
else: else:
item = need_translate[0] item = need_translate[0]
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.ctrlc_stack_enabled) self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
# ========== 阶段2:串行后处理(dso_local + stub_decls 注入)========== # ========== 阶段2:串行后处理(dso_local + stub_decls 注入)==========
compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)] compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)]
@@ -3435,16 +3435,16 @@ class Phase2Translator:
with open(ll_path, 'r', encoding='utf-8') as f: with open(ll_path, 'r', encoding='utf-8') as f:
ll_content = f.read() ll_content = f.read()
# --ctrlc-stack 提醒:include 模块若发射了 define 却不带 DI(!llvm.dbg.cu), # --traceback 提醒:include 模块若发射了 define 却不带 DI(!llvm.dbg.cu),
# 其 .debug_line 行为空 ⇒ 运行时 Ctrl+C 栈里该 include 帧取不到源行 # 其 .debug_line 行为空 ⇒ 运行时 Ctrl+C 栈里该 include 帧取不到源行
# (会退化为 +0x… (unknown))。 # (会退化为 +0x… (unknown))。
# **只警告、不报错**:像 includes/stdarg.vp 这类模块的 define 是"合成"的 # **只警告、不报错**:像 includes/stdarg.vp 这类模块的 define 是"合成"的
# (由 extern/sugar 派生、无 .vp 函数体),本就不会有源级 DI —— 按 fail-fast # (由 extern/sugar 派生、无 .vp 函数体),本就不会有源级 DI —— 按 fail-fast
# 报错会把这类模块全部误杀;而"行号缺失"只降低可读性,不是编译正确性问题。 # 报错会把这类模块全部误杀;而"行号缺失"只降低可读性,不是编译正确性问题。
if self.ctrlc_stack_enabled and re.search(r'^define ', ll_content, re.MULTILINE) \ if self.traceback_enabled and re.search(r'^define ', ll_content, re.MULTILINE) \
and '!llvm.dbg.cu' not in ll_content: and '!llvm.dbg.cu' not in ll_content:
_vlog().warning( _vlog().warning(
f"--ctrlc-stack: include 模块 {module_name}.py 发射了 define 但 IR 未携带 " f"--traceback: include 模块 {module_name}.py 发射了 define 但 IR 未携带 "
f"DWARF (!llvm.dbg.cu)——该模块内联帧在 Ctrl+C 栈里将只显示地址: {ll_path}") f"DWARF (!llvm.dbg.cu)——该模块内联帧在 Ctrl+C 栈里将只显示地址: {ll_path}")
# dso_local 正则标注(使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare) # dso_local 正则标注(使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare)
@@ -3602,8 +3602,8 @@ class Phase2Translator:
result = compile_results.get(task) result = compile_results.get(task)
if result and result.returncode == 0: if result and result.returncode == 0:
self.extra_link_files.append(obj_path) self.extra_link_files.append(obj_path)
# 复制到 includes.binary 预编译目录(--ctrlc-stack 时 precompiled_obj # 复制到 includes.binary 预编译目录(--traceback 时 precompiled_obj
# 指向隔离目录 includes.binary_ctrlcstack,不会污染共享缓存) # 指向隔离目录 includes.binary_traceback,不会污染共享缓存)
if precompiled_obj: if precompiled_obj:
precompiled_dir = os.path.dirname(precompiled_obj) precompiled_dir = os.path.dirname(precompiled_obj)
try: try:
@@ -3611,12 +3611,12 @@ class Phase2Translator:
os.makedirs(precompiled_dir, exist_ok=True) os.makedirs(precompiled_dir, exist_ok=True)
shutil.copy2(obj_path, precompiled_obj) shutil.copy2(obj_path, precompiled_obj)
except Exception as _e: except Exception as _e:
# --ctrlc-stack fail-fast:隔离目录创建 / 写回失败必须显式终止, # --traceback fail-fast:隔离目录创建 / 写回失败必须显式终止,
# 不静默降级(否则本次构建的 include obj 拿不到、或落到共享缓存 # 不静默降级(否则本次构建的 include obj 拿不到、或落到共享缓存
# 之外的位置,DWARF 行表与后续复用的前提都被破坏)。 # 之外的位置,DWARF 行表与后续复用的前提都被破坏)。
if self.ctrlc_stack_enabled: if self.traceback_enabled:
raise RuntimeError( raise RuntimeError(
f"--ctrlc-stack: include 预编译缓存写入隔离目录失败: " f"--traceback: include 预编译缓存写入隔离目录失败: "
f"{precompiled_obj}: {_e}") from _e f"{precompiled_obj}: {_e}") from _e
if _config_mode == "strict": if _config_mode == "strict":
_vlog().warning(f"预编译缓存写入失败: {_e}") _vlog().warning(f"预编译缓存写入失败: {_e}")
@@ -4186,7 +4186,7 @@ class Phase2Translator:
break break
def _WriteStackMap(self, exe_path: str) -> None: def _WriteStackMap(self, exe_path: str) -> None:
"""--ctrlc-stack:链接成功后导出"地址→源行"旁文件 <exe>.stackmap。 """--traceback:链接成功后导出"地址→源行"旁文件 <exe>.stackmap。
用 llvm-dwarfdump 解析最终 PE 的 .debug_line(地址→文件/行)与 用 llvm-dwarfdump 解析最终 PE 的 .debug_line(地址→文件/行)与
.debug_info(DISubprogram 的 low/high_pc + DW_AT_name → 函数名), .debug_info(DISubprogram 的 low/high_pc + DW_AT_name → 函数名),
@@ -4198,7 +4198,7 @@ class Phase2Translator:
rva(u64) + file_off(u32) + line(u32) + func_off(u32) rva(u64) + file_off(u32) + line(u32) + func_off(u32)
字符串池 str_bytes 字节(紧接记录区,UTF-8,全部以 '\\0' 结尾) 字符串池 str_bytes 字节(紧接记录区,UTF-8,全部以 '\\0' 结尾)
口径(必须与运行期严格一致):运行期 dbgstack.vp 取 口径(必须与运行期严格一致):运行期 traceback.vp 取
`rva = 帧地址 − GetModuleHandleA(NULL)`,即"链接期 ImageBase 之上的偏移", `rva = 帧地址 − GetModuleHandleA(NULL)`,即"链接期 ImageBase 之上的偏移",
故写端唯一正确口径是 **`RVA = dwarfdump 打印的地址 − PE.ImageBase`**。 故写端唯一正确口径是 **`RVA = dwarfdump 打印的地址 − PE.ImageBase`**。
@@ -4214,27 +4214,27 @@ class Phase2Translator:
的记录保留(RVA = 地址 − ImageBase);其余(未重定位的 0、越界值)一律**丢弃并 的记录保留(RVA = 地址 − ImageBase);其余(未重定位的 0、越界值)一律**丢弃并
统计**(不静默留垃圾条目)。同一 RVA 只保留行号最大的一条。 统计**(不静默留垃圾条目)。同一 RVA 只保留行号最大的一条。
写出前自检(任一不满足即 fail-fast 抛异常,遵循 --ctrlc-stack 既有风格): 写出前自检(任一不满足即 fail-fast 抛异常,遵循 --traceback 既有风格):
记录数 / min~max RVA 与 SizeOfImage 的关系 / 所有 RVA ∈ [0, SizeOfImage) / 记录数 / min~max RVA 与 SizeOfImage 的关系 / 所有 RVA ∈ [0, SizeOfImage) /
RVA 严格升序 / 至少若干条落在运行期可能出现的 RVA 区间 RVA 严格升序 / 至少若干条落在运行期可能出现的 RVA 区间
(粗判据 [.text 节 RVA, SizeOfImage))。 (粗判据 [.text 节 RVA, SizeOfImage))。
任何一步失败都直接抛异常终止编译(--ctrlc-stack 的硬要求,无兜底)。 任何一步失败都直接抛异常终止编译(--traceback 的硬要求,无兜底)。
""" """
import struct import struct
import bisect import bisect
def _Fail(msg: str) -> None: def _Fail(msg: str) -> None:
full: str = msg + '(这是 --ctrlc-stack 的要求)' full: str = msg + '(这是 --traceback 的要求)'
_vlog().error(full) _vlog().error(full)
raise RuntimeError(full) raise RuntimeError(full)
layout = _ReadPEImageLayout(exe_path) layout = _ReadPEImageLayout(exe_path)
if layout is None: if layout is None:
_Fail(f"--ctrlc-stack 只支持 Windows PE 产物,但 {exe_path} 不是有效 PE") _Fail(f"--traceback 只支持 Windows PE 产物,但 {exe_path} 不是有效 PE")
image_base, size_of_image, sections = layout image_base, size_of_image, sections = layout
if size_of_image <= 0: if size_of_image <= 0:
_Fail(f"--ctrlc-stack 读到的 PE.SizeOfImage 非正({size_of_image}),无法界定 RVA 域") _Fail(f"--traceback 读到的 PE.SizeOfImage 非正({size_of_image}),无法界定 RVA 域")
img_lo: int = image_base img_lo: int = image_base
img_hi: int = image_base + size_of_image img_hi: int = image_base + size_of_image
# 运行期可能出现的 RVA 区间的**粗判据**下界:.text 节 RVA(缺失时退化为最小节 RVA) # 运行期可能出现的 RVA 区间的**粗判据**下界:.text 节 RVA(缺失时退化为最小节 RVA)
@@ -4245,22 +4245,22 @@ class Phase2Translator:
dwarfdump: str | None = shutil.which('llvm-dwarfdump') or shutil.which('llvm-dwarfdump.exe') dwarfdump: str | None = shutil.which('llvm-dwarfdump') or shutil.which('llvm-dwarfdump.exe')
if not dwarfdump: if not dwarfdump:
_Fail("--ctrlc-stack 找不到 llvm-dwarfdump(请确认 PATH 内含 LLVM 工具)") _Fail("--traceback 找不到 llvm-dwarfdump(请确认 PATH 内含 LLVM 工具)")
try: try:
proc = subprocess.run( proc = subprocess.run(
[dwarfdump, '--debug-line', '--debug-info', exe_path], [dwarfdump, '--debug-line', '--debug-info', exe_path],
capture_output=True) capture_output=True)
except Exception as e: except Exception as e:
_Fail(f"--ctrlc-stack 调用 llvm-dwarfdump 失败: {e}") _Fail(f"--traceback 调用 llvm-dwarfdump 失败: {e}")
if proc.returncode != 0: if proc.returncode != 0:
_Fail(f"--ctrlc-stack 的 llvm-dwarfdump 返回 {proc.returncode}: " _Fail(f"--traceback 的 llvm-dwarfdump 返回 {proc.returncode}: "
+ proc.stderr.decode('utf-8', errors='replace').strip()) + proc.stderr.decode('utf-8', errors='replace').strip())
dump_text: str = proc.stdout.decode('utf-8', errors='replace') dump_text: str = proc.stdout.decode('utf-8', errors='replace')
rows: list[tuple[int, str, int]] = _ParseDwarfLineRows(dump_text) rows: list[tuple[int, str, int]] = _ParseDwarfLineRows(dump_text)
if not rows: if not rows:
_Fail(f"--ctrlc-stack 未能从 {exe_path} 解析出任何 DWARF 行记录(.debug_line 为空?)") _Fail(f"--traceback 未能从 {exe_path} 解析出任何 DWARF 行记录(.debug_line 为空?)")
# ---- 行记录:逐条归一化为"运行期 RVA";不能归一的一律丢弃并统计 ---- # ---- 行记录:逐条归一化为"运行期 RVA";不能归一的一律丢弃并统计 ----
dropped_rows: int = 0 dropped_rows: int = 0
@@ -4278,12 +4278,12 @@ class Phase2Translator:
best[rva] = (path, line) best[rva] = (path, line)
if dropped_rows: if dropped_rows:
_vlog().warning( _vlog().warning(
f" [--ctrlc-stack] 丢弃 {dropped_rows} 条无法归一化的 DWARF 行记录" f" [--traceback] 丢弃 {dropped_rows} 条无法归一化的 DWARF 行记录"
f"(地址不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;判定原因:多为被连接器" f"(地址不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;判定原因:多为被连接器"
f" COMDAT 去重丢弃的泛型副本,其地址恰为 0)示例地址: " f" COMDAT 去重丢弃的泛型副本,其地址恰为 0)示例地址: "
+ ", ".join(f"0x{a:x}" for a in dropped_row_samples)) + ", ".join(f"0x{a:x}" for a in dropped_row_samples))
if not best: if not best:
_Fail(f"--ctrlc-stack 从 {exe_path} 得到的行记录全部无法归一到运行期 RVA 域") _Fail(f"--traceback 从 {exe_path} 得到的行记录全部无法归一到运行期 RVA 域")
# ---- 子程序(函数名):同样逐条归一化;不能归一的丢弃 ---- # ---- 子程序(函数名):同样逐条归一化;不能归一的丢弃 ----
funcs: list[tuple[int, int, str]] = _ParseDwarfSubprograms(dump_text) funcs: list[tuple[int, int, str]] = _ParseDwarfSubprograms(dump_text)
@@ -4302,7 +4302,7 @@ class Phase2Translator:
func_ranges.append((lo_rva, hi_rva, name)) func_ranges.append((lo_rva, hi_rva, name))
if dropped_funcs: if dropped_funcs:
_vlog().warning( _vlog().warning(
f" [--ctrlc-stack] 丢弃 {dropped_funcs} 个无法归一化的 DWARF 子程序" f" [--traceback] 丢弃 {dropped_funcs} 个无法归一化的 DWARF 子程序"
f"(low_pc 不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;同上为去重副本)") f"(low_pc 不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;同上为去重副本)")
func_ranges.sort(key=lambda f: f[0]) func_ranges.sort(key=lambda f: f[0])
func_lows: list[int] = [f[0] for f in func_ranges] func_lows: list[int] = [f[0] for f in func_ranges]
@@ -4341,19 +4341,19 @@ class Phase2Translator:
max_rva: int = rvas[-1] max_rva: int = rvas[-1]
for i in range(1, len(rvas)): for i in range(1, len(rvas)):
if rvas[i] <= rvas[i - 1]: if rvas[i] <= rvas[i - 1]:
_Fail(f"--ctrlc-stack 自检失败:记录 RVA 非严格升序" _Fail(f"--traceback 自检失败:记录 RVA 非严格升序"
f"(第 {i} 条 0x{rvas[i]:x} <= 前一条 0x{rvas[i - 1]:x},会破坏运行期二分查找)") f"(第 {i} 条 0x{rvas[i]:x} <= 前一条 0x{rvas[i - 1]:x},会破坏运行期二分查找)")
outside: int = sum(1 for r in rvas if not (0 <= r < size_of_image)) outside: int = sum(1 for r in rvas if not (0 <= r < size_of_image))
if outside: if outside:
_Fail(f"--ctrlc-stack 自检失败:有 {outside} 条记录的 RVA 落在 [0, SizeOfImage=0x{size_of_image:x}) 之外") _Fail(f"--traceback 自检失败:有 {outside} 条记录的 RVA 落在 [0, SizeOfImage=0x{size_of_image:x}) 之外")
# 运行期可能出现的 RVA 区间(粗判据):[.text 节 RVA, SizeOfImage)。一条都没有 ⇒ 口径又错了。 # 运行期可能出现的 RVA 区间(粗判据):[.text 节 RVA, SizeOfImage)。一条都没有 ⇒ 口径又错了。
plausible: int = sum(1 for r in rvas if text_rva <= r < size_of_image) plausible: int = sum(1 for r in rvas if text_rva <= r < size_of_image)
if plausible <= 0: if plausible <= 0:
_Fail(f"--ctrlc-stack 自检失败:没有任何记录的 RVA 落在运行期可能出现的区间" _Fail(f"--traceback 自检失败:没有任何记录的 RVA 落在运行期可能出现的区间"
f" [0x{text_rva:x}(.text RVA), 0x{size_of_image:x}(SizeOfImage)) 内" f" [0x{text_rva:x}(.text RVA), 0x{size_of_image:x}(SizeOfImage)) 内"
f"(RVA 口径疑似又错了)") f"(RVA 口径疑似又错了)")
_vlog().info( _vlog().info(
f" [--ctrlc-stack] 自检: 记录={len(records)} RVA范围=[0x{min_rva:x}, 0x{max_rva:x}]" f" [--traceback] 自检: 记录={len(records)} RVA范围=[0x{min_rva:x}, 0x{max_rva:x}]"
f" PE.SizeOfImage=0x{size_of_image:x}({size_of_image})" f" PE.SizeOfImage=0x{size_of_image:x}({size_of_image})"
f" 各节最高结束 RVA=0x{max_sec_end:x}" f" 各节最高结束 RVA=0x{max_sec_end:x}"
f" | 全部∈[0,SizeOfImage)=是 严格升序=是" f" | 全部∈[0,SizeOfImage)=是 严格升序=是"
@@ -4371,9 +4371,9 @@ class Phase2Translator:
with open(map_path, 'wb') as f: with open(map_path, 'wb') as f:
f.write(out) f.write(out)
except Exception as e: except Exception as e:
_Fail(f"--ctrlc-stack 写出旁文件失败 {map_path}: {e}") _Fail(f"--traceback 写出旁文件失败 {map_path}: {e}")
_vlog().success( _vlog().success(
f" [--ctrlc-stack] 已写出地址→源行旁文件: {map_path}" f" [--traceback] 已写出地址→源行旁文件: {map_path}"
f"({len(records)} 条记录,{len(out)} 字节)") f"({len(records)} 条记录,{len(out)} 字节)")
def _link_obj_files(self, active_sha1s: set[str]) -> None: def _link_obj_files(self, active_sha1s: set[str]) -> None:
@@ -4504,13 +4504,13 @@ SECTIONS {
lib_flags.append('-L' + self.asan_rt_dir) lib_flags.append('-L' + self.asan_rt_dir)
lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a') lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a')
lib_flags.append('-l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a') lib_flags.append('-l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a')
# Ctrl+C 栈转储(--ctrlc-stack):includes/dbgstack.vp 的主回溯路径用 # Ctrl+C 栈转储(--traceback):includes/traceback.vp 的主回溯路径用
# StackWalk64 / SymInitialize / SymFunctionTableAccess64 / SymGetModuleBase64 # StackWalk64 / SymInitialize / SymFunctionTableAccess64 / SymGetModuleBase64
# (均在 dbghelp),故开启该开关时必须显式链接 -ldbghelp,否则链接期报未定义符号。 # (均在 dbghelp),故开启该开关时必须显式链接 -ldbghelp,否则链接期报未定义符号。
# 与 ASan 同理:库要排在目标文件之后 ⇒ 追加到 lib_flags。 # 与 ASan 同理:库要排在目标文件之后 ⇒ 追加到 lib_flags。
# (若确实无法链 dbghelp:把 includes/dbgstack.vp 的 DBGSTACK_USE_DBGHELP 置 0, # (若确实无法链 dbghelp:把 includes/traceback.vp 的 TRACEBACK_USE_DBGHELP 置 0,
# 退化为纯 RBP 链回溯,此时不该再带本 flag。) # 退化为纯 RBP 链回溯,此时不该再带本 flag。)
if self.ctrlc_stack_enabled: if self.traceback_enabled:
lib_flags.append('-ldbghelp') lib_flags.append('-ldbghelp')
cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags
_vlog().info(f" 执行: {' '.join(cmd)}") _vlog().info(f" 执行: {' '.join(cmd)}")
@@ -4564,8 +4564,8 @@ SECTIONS {
_vlog().error(f"\n链接异常,立即终止编译。") _vlog().error(f"\n链接异常,立即终止编译。")
sys.exit(1) sys.exit(1)
# --ctrlc-stack:链接成功后导出"地址→源行"旁文件(<exe>.stackmap)。 # --traceback:链接成功后导出"地址→源行"旁文件(<exe>.stackmap)。
# 刻意放在上面的链接 try/except 之外,使导出阶段的 fail-fast RuntimeError # 刻意放在上面的链接 try/except 之外,使导出阶段的 fail-fast RuntimeError
# 直接向上抛出终止编译,而不被"链接异常兜底"吞掉。 # 直接向上抛出终止编译,而不被"链接异常兜底"吞掉。
if self.ctrlc_stack_enabled: if self.traceback_enabled:
self._WriteStackMap(exe_path) self._WriteStackMap(exe_path)
+3 -2
View File
@@ -78,10 +78,11 @@ def parse_python_file(filepath: str) -> tuple[str, ast.Module] | tuple[str, None
except Exception: except Exception:
return ('', None) return ('', None)
try: try:
# 软关键字展开:fn/fun/func/function -> def(行号不变),供 ast.parse 使用。 # 软关键字/语法糖展开:fn/fun/func/function -> def、enum 块 -> class X(t.CEnum)
# (均同行替换,行号不变),供 ast.parse 使用。
# 注意 content 保持原始文本返回(compute_sha1 必须与 Phase1 一致), # 注意 content 保持原始文本返回(compute_sha1 必须与 Phase1 一致),
# 仅 tree 基于展开后的代码解析。 # 仅 tree 基于展开后的代码解析。
tree: ast.Module = ast.parse(ExpandSoftKeywords(content)) tree: ast.Module = ast.parse(ExpandSoftKeywords(content), filepath)
except SyntaxError: except SyntaxError:
return (content, None) return (content, None)
_FILE_AST_CACHE[norm_path] = (mtime, content, tree) _FILE_AST_CACHE[norm_path] = (mtime, content, tree)
+5 -5
View File
@@ -110,7 +110,7 @@ class TransPyC:
f.write('') f.write('')
# 解析AST并提取符号 # 解析AST并提取符号
tree: ast.Module = ast.parse(content) tree: ast.Module = ast.parse(content, SymbolFile.FilePath or '<TransPyC:PreProcessSymbol>')
if debug: if debug:
with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f: with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f:
@@ -449,8 +449,8 @@ class TransPyC:
self.translator.OriginalLines = Content.split('\n') self.translator.OriginalLines = Content.split('\n')
self.translator.Content = Content self.translator.Content = Content
# 解析Python代码为AST # 解析Python代码为AST
Tree: ast.Module = ast.parse(Content) Tree: ast.Module = ast.parse(Content, InputFile)
# 解析文件以提取类型信息(用于 EmbeddedAssignments) # 解析文件以提取类型信息(用于 EmbeddedAssignments)
self.translator.ParsePythonFile(InputFile, encoding) self.translator.ParsePythonFile(InputFile, encoding)
@@ -538,13 +538,13 @@ class TransPyC:
self.translator.OriginalLines = self.code.split('\n') self.translator.OriginalLines = self.code.split('\n')
# 解析Python代码为AST # 解析Python代码为AST
Tree: ast.Module = ast.parse(self.code) Tree: ast.Module = ast.parse(self.code, SourceFilename or '<TransPyC:Convert>')
# 从源代码直接预扫描 import 语句,注册别名 # 从源代码直接预扫描 import 语句,注册别名
# 注意:不能依赖 Tree 中的 import 节点,因为 ParsePythonFile 可能修改了 AST # 注意:不能依赖 Tree 中的 import 节点,因为 ParsePythonFile 可能修改了 AST
if not getattr(self.translator, '_ImportedModules', None): if not getattr(self.translator, '_ImportedModules', None):
self.translator._ImportedModules = set() self.translator._ImportedModules = set()
_prescan_tree: ast.Module = ast.parse(self.code) _prescan_tree: ast.Module = ast.parse(self.code, SourceFilename or '<TransPyC:Convert>')
for _node in ast.iter_child_nodes(_prescan_tree): for _node in ast.iter_child_nodes(_prescan_tree):
if isinstance(_node, ast.Import): if isinstance(_node, ast.Import):
for _alias in _node.names: for _alias in _node.names:
+53 -7
View File
@@ -88,7 +88,8 @@ class PythonToStubConverter:
HasImportT: bool = False HasImportT: bool = False
HasImportC: bool = False HasImportC: bool = False
try: try:
tree: ast.Module = ast.parse(PyContent) # 传来源名:解析失败时 vast 的报错/回退诊断才带得上模块名(否则记为 <unknown>)
tree: ast.Module = ast.parse(PyContent, f'{ModuleName}.vp')
for node in tree.body: for node in tree.body:
if isinstance(node, ast.Import): if isinstance(node, ast.Import):
for alias in node.names: for alias in node.names:
@@ -127,7 +128,7 @@ class PythonToStubConverter:
try: try:
if tree is None: if tree is None:
# 第一遍解析失败(非语法类异常):重试一次,保持原"主循环重新解析"语义 # 第一遍解析失败(非语法类异常):重试一次,保持原"主循环重新解析"语义
tree = ast.parse(PyContent) tree = ast.parse(PyContent, f'{ModuleName}.vp')
# 第一遍扫描:收集 Postdefinition 变量 # 第一遍扫描:收集 Postdefinition 变量
PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]} PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]}
@@ -492,12 +493,23 @@ class PythonToStubConverter:
if SourceLines: if SourceLines:
StartLine: int = node.lineno - 1 StartLine: int = node.lineno - 1
EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1 EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1
for i in range(StartLine, min(EndLine, len(SourceLines))): Copied: list[str] = [SourceLines[i]
lines.append(SourceLines[i]) for i in range(StartLine, min(EndLine, len(SourceLines)))]
# 丢弃尾随空行:解析器的 end_lineno 会被紧随的空行 NL 顶到下一行
# (vast Parser._ParseSuite 单行套件分支返回前 `_SkipNL()` 吞掉空行 NL,
# 调用方 _FixLoc 取 _Prev() 即该 NL)。若不丢弃,`define f(...): return x`
# 这类内联体宏末尾会多出空白行,使下方"末行是否已有内联体"判据落空,
# 凭空补一行 ` pass` → 存根缩进非法(hashlib.__sha512 第 29 行)。
while Copied and not Copied[-1].strip():
Copied.pop()
lines.extend(Copied)
if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'): if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'):
pass pass
else: elif lines:
lines.append(f'{IndentStr} pass') lines.append(f'{IndentStr} pass')
else:
# 源区间为空(理论不可达):产出合法占位,绝不落出 ` pass` 这类非法缩进
lines.append(f'{IndentStr}pass')
return lines return lines
# 处理函数装饰器 # 处理函数装饰器
@@ -661,7 +673,17 @@ class PythonToStubConverter:
@staticmethod @staticmethod
def _GetTypeString(annotation: ast.AST) -> str: def _GetTypeString(annotation: ast.AST) -> str:
"""获取类型字符串""" """获取类型字符串
支持集与 TPV(TransPyV/App/lib/Stub/StubGen/Converter.vp 的 _PyiTypeStr)
等价:Name/Attribute/Subscript/BinOp/UnaryOp/Compare/BoolOp/Constant/
List/Call(TPV 另支持裸 Tuple,本侧仅在 Subscript.slice 位置接受 Tuple)。
未支持的节点种类与运算符在此 fail-fast(不自造 't.CVoid' 回落)。
"""
# 无注解(None):调用方未提供注解,沿用原语义返回 't.CVoid';
# 这不是"未知节点"——未知节点走函数尾 fail-fast。
if annotation is None:
return 't.CVoid'
if isinstance(annotation, ast.Name): if isinstance(annotation, ast.Name):
return annotation.id return annotation.id
elif isinstance(annotation, ast.Attribute): elif isinstance(annotation, ast.Attribute):
@@ -706,6 +728,15 @@ class PythonToStubConverter:
return f'{LeftStr} / {RightStr}' return f'{LeftStr} / {RightStr}'
elif isinstance(annotation.op, ast.Mod): elif isinstance(annotation.op, ast.Mod):
return f'{LeftStr} % {RightStr}' return f'{LeftStr} % {RightStr}'
# 位与/右移:条件宏实参(如 c.CIf(MASK_BYTE & 0x0F)、
# c.CIf((MAX_SIZE >> SHIFT_BITS) > 0))。两运算符必须与 TPV 的
# _PyiBinOpOpText 保持一致,否则同一份源码两侧写出的 vpi 文本不同。
elif isinstance(annotation.op, ast.BitAnd):
return f'{LeftStr} & {RightStr}'
elif isinstance(annotation.op, ast.RShift):
return f'{LeftStr} >> {RightStr}'
# 其余二元运算符(LShift/BitXor/FloorDiv/Pow/MatMult 等)不支持:
# 落到函数尾 fail-fast(与 TPV 同语义)
elif isinstance(annotation, ast.UnaryOp): elif isinstance(annotation, ast.UnaryOp):
if isinstance(annotation.op, ast.Not): if isinstance(annotation.op, ast.Not):
operand: str = PythonToStubConverter._GetTypeString(annotation.operand) operand: str = PythonToStubConverter._GetTypeString(annotation.operand)
@@ -740,6 +771,11 @@ class PythonToStubConverter:
for i, op in enumerate(ops): for i, op in enumerate(ops):
result += f' {op} {comparators[i]}' result += f' {op} {comparators[i]}'
return result return result
elif isinstance(annotation, ast.BoolOp):
# 布尔运算:条件宏实参(如 c.CIf(ENABLED and MAX_SIZE > 0))。
# 文本与 TPV 的 BoolOp 分支一致:and → ' and '、or → ' or '。
Joiner: str = ' or ' if isinstance(annotation.op, ast.Or) else ' and '
return Joiner.join([PythonToStubConverter._GetTypeString(v) for v in annotation.values])
elif isinstance(annotation, ast.Constant): elif isinstance(annotation, ast.Constant):
return repr(annotation.value) return repr(annotation.value)
elif isinstance(annotation, ast.Index): elif isinstance(annotation, ast.Index):
@@ -765,7 +801,17 @@ class PythonToStubConverter:
return f'{FuncStr}({keywords_str})' return f'{FuncStr}({keywords_str})'
else: else:
return f'{FuncStr}({ArgsStr})' return f'{FuncStr}({ArgsStr})'
return 't.CVoid' # 未知/未支持的注解节点 fail-fast(与 TPV StubGen 的 _PyiTypeStr 同语义、
# 同文案、同类型):静默回落 't.CVoid' 会把错误类型写进 stub 签名——实例:
# 源里的 t.Callable[[], int] 曾写成 t.Callable[int, int](见
# ViperApps/ViperOS/VKernel/.tpv_build/temp/0b/0bb176432facf634.vpi:23),
# 下游按错误签名解析且无任何提示。
# 归因信息:节点 kind + 行号;文件归因由调用方在日志里带上
# (Generator.GenerateStub → "Failed to process <文件>",
# Phase1Generator → "处理 include 文件失败 <相对路径>")。
KindName: str = type(annotation).__name__
LineNo: int = getattr(annotation, 'lineno', 0)
raise TypeError(f'vpi 类型注解节点类型未知: {KindName} (line {LineNo})')
@staticmethod @staticmethod
def _ExtractPostdefClass(annotation: ast.AST) -> str | None: def _ExtractPostdefClass(annotation: ast.AST) -> str | None:
+84 -13
View File
@@ -9,7 +9,10 @@ from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
from lib.core.VLogger import get_logger as _vlog from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode from lib.constants.config import mode as _config_mode
from lib.includes import t from lib.includes import t
from lib.core.SymbolUtils import IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext, AnnotationContainsName, IsCPtrNode from lib.core.SymbolUtils import (
IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext,
AnnotationContainsName, IsCPtrNode, RequireDefineConstStorable,
)
def _IsGenericTpName(name: str) -> bool: def _IsGenericTpName(name: str) -> bool:
@@ -52,6 +55,26 @@ class AnnAssignHandle(BaseHandle):
Gen._store(Value, NewVar) Gen._store(Value, NewVar)
Gen.variables[VarName] = NewVar Gen.variables[VarName] = NewVar
@staticmethod
def _RhsEndsWithVoidCall(Gen: LlvmCodeGenerator, BlockBefore: ir.Block | None, InsnStart: int) -> bool:
"""判定刚求值的带注解赋值 RHS 是否"按类型即无值"(以返回 void 的调用收尾)。
判据完全取自**实际发射的指令**(不依赖 RHS 的 AST 形态白名单):仅当求值
发生在同一基本块、且新增指令的最后一条是返回 void 的 call 时,才认为该
表达式语义上本就无值(如返回 void 的内建/库调用),此时"无值可存"合法。
其余情况——无任何指令产出(类型下标被当乘法 ⇒ 整条 RHS 未生成任何东西)
或只产出 load/gep 等非收尾指令后求值失败(属性链解析失败,实测同块新增
1 条 load)——都属编译期求值失败,必须 fail-fast。
"""
BlockAfter: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
if InsnStart < 0 or BlockAfter is None or BlockAfter is not BlockBefore:
return False
NewInstrs: list[ir.Instruction] = BlockAfter.instructions[InsnStart:]
if not NewInstrs:
return False
Last: ir.Instruction = NewInstrs[-1]
return isinstance(Last, ir.CallInstr) and isinstance(Last.type, ir.VoidType)
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None: def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen Gen: LlvmCodeGenerator = self.Trans.LlvmGen
AttrName: str = Target.attr AttrName: str = Target.attr
@@ -210,7 +233,16 @@ class AnnAssignHandle(BaseHandle):
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.IntType): elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.IntType):
Value = Gen.builder.inttoptr(Value, TargetType, name="int_to_ptr") Value = Gen.builder.inttoptr(Value, TargetType, name="int_to_ptr")
elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.PointerType): elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.PointerType):
Value = Gen.builder.ptrtoint(Value, TargetType, name="ptr_to_int") if isinstance(Value, ir.Constant) and Value.constant is None:
# null 指针常量赋给整数字段 → 0(C 空指针常量语义)
Value = ir.Constant(TargetType, 0)
else:
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能把指针类型 {Value.type} 赋给整数类型 {TargetType};"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
Gen._store(Value, Ptr) Gen._store(Value, Ptr)
def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None: def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None:
@@ -356,6 +388,9 @@ class AnnAssignHandle(BaseHandle):
def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None: def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen Gen: LlvmCodeGenerator = self.Trans.LlvmGen
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
# 局部变量/self 字段注解不经过 ResolveAnnotationType,故在此显式校验。
self._CheckFixedArrayKeyword(Node.annotation)
# M1 协程:记录 async 函数调用赋值的变量名(coro.coro_run 内联翻译需要) # M1 协程:记录 async 函数调用赋值的变量名(coro.coro_run 内联翻译需要)
if isinstance(Node.target, ast.Name) and Node.value is not None: if isinstance(Node.target, ast.Name) and Node.value is not None:
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
@@ -373,6 +408,10 @@ class AnnAssignHandle(BaseHandle):
IsPtr: bool = TypeInfo.IsPtr IsPtr: bool = TypeInfo.IsPtr
if TypeInfo.IsDefine: if TypeInfo.IsDefine:
if Node.value and isinstance(Node.value, ast.Constant): if Node.value and isinstance(Node.value, ast.Constant):
# fail-fast:常量值必须能落进声明类型(define 带类型形态)/ 64 位存储
# (自动推导形态)。唯一判据见 SymbolUtils.RequireDefineIntStorable。
RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType,
Node.annotation, Node.value.value, VarName)
if not hasattr(Gen, '_define_constants'): if not hasattr(Gen, '_define_constants'):
Gen._define_constants = {} Gen._define_constants = {}
Gen._define_constants[VarName] = Node.value.value Gen._define_constants[VarName] = Node.value.value
@@ -387,6 +426,8 @@ class AnnAssignHandle(BaseHandle):
ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans) ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans)
eval_val = ConstEvaluator.eval_full(Node.value, ctx) eval_val = ConstEvaluator.eval_full(Node.value, ctx)
if eval_val is not None and isinstance(eval_val, (int, float)): if eval_val is not None and isinstance(eval_val, (int, float)):
RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType,
Node.annotation, eval_val, VarName)
if not hasattr(Gen, '_define_constants'): if not hasattr(Gen, '_define_constants'):
Gen._define_constants = {} Gen._define_constants = {}
Gen._define_constants[VarName] = eval_val Gen._define_constants[VarName] = eval_val
@@ -491,7 +532,13 @@ class AnnAssignHandle(BaseHandle):
and 'list' in self.Trans.ClassHandler._generic_class_templates): and 'list' in self.Trans.ClassHandler._generic_class_templates):
self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub) self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub)
return return
if TypeInfo.IsVoid and isinstance(Node.annotation, ast.BinOp): # Only fall back to the left operand when the union neither resolved a base
# type nor accumulated multi-level pointers. A pointer-marker based union
# (`t.CPtr*` = `t.CPtr | t.CPtr` => CVoid/2) is already a fully resolved
# multi-level pointer; falling back to its left operand (`t.CPtr` => 1 layer)
# used to silently degrade it to i8*. Keep PtrCount > 1 untouched.
if (TypeInfo.IsVoid and TypeInfo.PtrCount <= 1
and isinstance(Node.annotation, ast.BinOp)):
found: str | None = FindStructNameInAnnotationWithContext( found: str | None = FindStructNameInAnnotationWithContext(
Node.annotation, self.Trans, Gen) Node.annotation, self.Trans, Gen)
if found: if found:
@@ -513,6 +560,11 @@ class AnnAssignHandle(BaseHandle):
and Node.annotation.id not in Gen.class_vtable): and Node.annotation.id not in Gen.class_vtable):
TypeInfo.PtrCount = 0 TypeInfo.PtrCount = 0
IsPtr = False IsPtr = False
# const / volatile 局部变量登记(统一登记入口)。放在此处:TypeInfo 的
# PtrCount/IsStruct 已定稿(上方 526-547),且早于本函数所有后续分支与
# 提前 return(数组/list/_reg_values/已存在 alloca 等),保证「局部变量」
# 这一登记点只有一处、无遗漏。
Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=False)
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo) VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
if not IsPtr and TypeInfo.IsStruct and TypeInfo.Name: if not IsPtr and TypeInfo.IsStruct and TypeInfo.Name:
stripped: str = TypeInfo.Name stripped: str = TypeInfo.Name
@@ -653,11 +705,13 @@ class AnnAssignHandle(BaseHandle):
elif isinstance(Node.value, ast.Attribute): elif isinstance(Node.value, ast.Attribute):
# 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp # 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp
# 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。 # 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen) self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName)
Gen._record_var_signedness(VarName, IsUnsigned) Gen._record_var_signedness(VarName, IsUnsigned)
return return
del Gen._reg_values[VarName] del Gen._reg_values[VarName]
InitValue: ir.Value | None = None InitValue: ir.Value | None = None
RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
RhsInsnStart: int = len(RhsBlock.instructions) if RhsBlock is not None else -1
if Node.value: if Node.value:
InitValue = self.HandleExprLlvm(Node.value, VarType=VarType) InitValue = self.HandleExprLlvm(Node.value, VarType=VarType)
# 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型 # 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型
@@ -665,10 +719,14 @@ class AnnAssignHandle(BaseHandle):
CalledName: str = Node.value.func.id CalledName: str = Node.value.func.id
if CalledName in Gen._closure_return_types: if CalledName in Gen._closure_return_types:
Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName] Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName]
if InitValue is None and Node.value is not None and isinstance(Node.value, ast.Attribute): if InitValue is None and Node.value is not None:
# 防静默:带值带注解的属性链求值失败(返回 None)必须报编译错误, # 防静默(通用判据,不再只认 ast.Attribute):带值带注解赋值 RHS
# 否则变量 alloca 已创建却无 store → 后续 load 未初始化栈变量。 # 求值为 None 且非"按类型即无值"(未以返回 void 的调用收尾)时,
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen) # 变量 alloca 已创建却无 store → 后续 load 读未初始化栈变量
# (运行期随机崩溃;如类型下标被当乘法的 list[X*]*(...) 整条 RHS
# 不产任何指令)。判据取自实际发射的指令,见 _RhsEndsWithVoidCall。
if not self._RhsEndsWithVoidCall(Gen, RhsBlock, RhsInsnStart):
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName)
if VarName in Gen._reg_values: if VarName in Gen._reg_values:
del Gen._reg_values[VarName] del Gen._reg_values[VarName]
if VarName in Gen.variables and Gen.variables[VarName] is not None: if VarName in Gen.variables and Gen.variables[VarName] is not None:
@@ -844,11 +902,21 @@ class AnnAssignHandle(BaseHandle):
elif isinstance(VarType, ir.PointerType) and isinstance(InitValue.type, ir.IntType): elif isinstance(VarType, ir.PointerType) and isinstance(InitValue.type, ir.IntType):
InitValue = Gen.builder.inttoptr(InitValue, VarType, name=f"int2ptr_{VarName}") InitValue = Gen.builder.inttoptr(InitValue, VarType, name=f"int2ptr_{VarName}")
elif isinstance(VarType, ir.IntType) and isinstance(InitValue.type, ir.PointerType): elif isinstance(VarType, ir.IntType) and isinstance(InitValue.type, ir.PointerType):
InitValue = Gen.builder.ptrtoint(InitValue, VarType, name=f"ptr2int_{VarName}") if isinstance(InitValue, ir.Constant) and InitValue.constant is None:
# null 指针常量初始化整数变量 → 0(C 空指针常量语义)
InitValue = ir.Constant(VarType, 0)
else:
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能用指针类型 {InitValue.type} 初始化整数变量 "
f"{VarName}: {VarType};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
# 大端局部变量:存储时 bswap # 大端局部变量:存储时 bswap
InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}") InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}")
Gen._store(InitValue, var) Gen._store(InitValue, var)
except Exception as _e: except (TypeError, ValueError) as _e:
# 仅回退 llvmlite 自身的类型/值错误;FailFastError 等一律上抛(不吞)
if _config_mode == "strict": if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}") self.Trans.LogWarning(f"异常被忽略: {_e}")
Gen.variables[VarName] = var Gen.variables[VarName] = var
@@ -955,12 +1023,15 @@ class AnnAssignHandle(BaseHandle):
if _config_mode == "strict": if _config_mode == "strict":
raise raise
_vlog().warning(f"解析类型失败: {_e}", "Exception") _vlog().warning(f"解析类型失败: {_e}", "Exception")
_RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
_RhsInsnStart: int = len(_RhsBlock.instructions) if _RhsBlock is not None else -1
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType) Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType)
if Value: if Value:
self._HandleAttributeStoreLlvm(Node.target, Value) self._HandleAttributeStoreLlvm(Node.target, Value)
elif isinstance(Node.value, ast.Attribute): elif not self._RhsEndsWithVoidCall(Gen, _RhsBlock, _RhsInsnStart):
# 防静默:self 字段带注解赋值右侧属性链求值失败必须报错 # 防静默(通用判据,不再只认 ast.Attribute):self 字段带注解
self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen) # 赋值右侧求值失败(None)必须报错,否则字段写被静默跳过。
self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen, Node.annotation, Node.target.attr)
if Value: if Value:
AttrName: str = Node.target.attr AttrName: str = Node.target.attr
len_name: str = f'{AttrName}__len' len_name: str = f'{AttrName}__len'
+298 -28
View File
@@ -31,8 +31,9 @@ class AssignHandle(BaseHandle):
return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements) return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements)
return False return False
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None) -> ir.Value | None: def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None,
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType) ConstWidthHint: int | None = None) -> ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
def _HandleAssignLlvm(self, Node: ast.Assign) -> None: def _HandleAssignLlvm(self, Node: ast.Assign) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
@@ -41,9 +42,8 @@ class AssignHandle(BaseHandle):
Target: ast.expr = Node.targets[0] Target: ast.expr = Node.targets[0]
if isinstance(Target, ast.Name): if isinstance(Target, ast.Name):
VarName: str = Target.id VarName: str = Target.id
if VarName in Gen.var_const_flags: if Gen._IsVarConst(VarName):
src_info: str = Gen._get_node_info() _vlog().error(Gen._ConstAssignMessage("赋值", VarName))
_vlog().error(f"不能对 const 变量 '{VarName}' 赋值{src_info}")
sys.exit(1) sys.exit(1)
if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and IsTModule(Node.value.value.id, self.Trans.SymbolTable): if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and IsTModule(Node.value.value.id, self.Trans.SymbolTable):
AttrName: str = Node.value.attr AttrName: str = Node.value.attr
@@ -698,8 +698,9 @@ class AssignHandle(BaseHandle):
val: ir.Value | None = self.HandleExprLlvm(kw.value) val: ir.Value | None = self.HandleExprLlvm(kw.value)
if val: if val:
member_values[kw.arg] = val member_values[kw.arg] = val
has_vtable: bool = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源),
base: int = 1 if has_vtable else 0 # 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。
base: int = Gen._member_base(ClassName)
for i, (member_name, member_type) in enumerate(members): for i, (member_name, member_type) in enumerate(members):
if isinstance(member_type, ir.VoidType): if isinstance(member_type, ir.VoidType):
continue continue
@@ -719,6 +720,15 @@ class AssignHandle(BaseHandle):
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None: def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
AttrName: str = Target.attr AttrName: str = Target.attr
# const 类字段:给 `obj.字段 = ...` / `self.字段 = ...` 直接赋值同样拒绝
# (判据 = 字段自身 const,见 BaseGen._IsFieldConst / Debugger/type-descriptors.md)。
# 类名能解析时按 `类名.字段名` 精确判定;解析不到时退化裸名(保守报错)。
_TargetCN: str | None = None
if isinstance(Target.value, ast.Name):
_TargetCN = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
if Gen._IsFieldConst(_TargetCN, AttrName):
_vlog().error(Gen._ConstAssignMessage("字段赋值", AttrName))
sys.exit(1)
if isinstance(Target.value, ast.Name) and Target.value.id == 'self': if isinstance(Target.value, ast.Name) and Target.value.id == 'self':
ClassName: str | None = self.Trans._CurrentCpythonObjectClass ClassName: str | None = self.Trans._CurrentCpythonObjectClass
if ClassName and ClassName in Gen.structs: if ClassName and ClassName in Gen.structs:
@@ -1256,15 +1266,20 @@ class AssignHandle(BaseHandle):
def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value) -> None: def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
# pointee-volatile 基址名后缀(与 HandlesExprAttr._HandleSubscriptLlvm 同款);
# 空后缀 ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。
_PVSuffix: str = Gen._SubscriptSuffix(Target.value)
# 查询数组元素字节序 # 查询数组元素字节序
arr_byte_order: str = '' arr_byte_order: str = ''
if isinstance(Target.value, ast.Name) and Target.value.id in Gen.local_var_byteorders: if isinstance(Target.value, ast.Name) and Target.value.id in Gen.local_var_byteorders:
arr_byte_order = Gen.local_var_byteorders[Target.value.id] arr_byte_order = Gen.local_var_byteorders[Target.value.id]
if isinstance(Target.value, ast.Name): if isinstance(Target.value, ast.Name):
SubVarName: str = Target.value.id SubVarName: str = Target.value.id
if SubVarName in Gen.var_const_flags: if Gen._IsVarConst(SubVarName):
src_info: str = Gen._get_node_info() # 与 Name 赋值/增量赋值同一条 fail-fast(原先此处 LogError 后继续,
self.Trans.LogError(f"不能对 const 变量 '{SubVarName}' 的元素赋值{src_info}") # 属静默继续;现统一 error + exit,判据与报文两侧一致)。
_vlog().error(Gen._ConstAssignMessage("的元素赋值", SubVarName))
sys.exit(1)
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Target.value, Gen) ClassName: str | None = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__setitem__'): if ClassName and Gen._has_function(f'{ClassName}.__setitem__'):
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
@@ -1306,7 +1321,7 @@ class AssignHandle(BaseHandle):
PtrToStruct: ir.Type = Value.type.pointee PtrToStruct: ir.Type = Value.type.pointee
BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal, BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal,
ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp") ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp")
ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name=f"subscript{_PVSuffix}")
Gen.builder.store(Value, ElemPtr) Gen.builder.store(Value, ElemPtr)
return return
if BaseHandle._is_char_pointer(IndexVal): if BaseHandle._is_char_pointer(IndexVal):
@@ -1324,7 +1339,7 @@ class AssignHandle(BaseHandle):
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store")
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return return
if isinstance(ValueVal.type, ir.PointerType): if isinstance(ValueVal.type, ir.PointerType):
@@ -1336,7 +1351,7 @@ class AssignHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
elif (isinstance(pointee, ir.IntType) and pointee.width == 8 elif (isinstance(pointee, ir.IntType) and pointee.width == 8
and isinstance(Target.value, ast.Name) and isinstance(Target.value, ast.Name)
and (Gen.var_ptr_element.get(Target.value.id, False) and (Gen.var_ptr_element.get(Target.value.id, False)
@@ -1346,11 +1361,17 @@ class AssignHandle(BaseHandle):
CastedPtrPtr: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_store") CastedPtrPtr: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_store")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_store") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_store")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript") ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return return
else: else:
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
# fail-fast:rawptr[T](基座是指针存储)的元素为对象 T、而 RHS 是指向 T
# 的指针时,下游 _StoreWithCoerce/_coerce_value 会静默 load 整对象再按
# sizeof(T) 存储(写 sizeof(T) 而非 8 字节指针)。数组槽(t.CArray[T,N])
# 本就按 sizeof(T) 布局,整对象赋值合法 ⇒ 由 IsArrayBase 排除。
self._RejectWholeObjectCopyIntoPtrSlot(
Target, Value, ElemPtr, isinstance(pointee, ir.ArrayType))
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
elif isinstance(ValueVal.type, ir.ArrayType): elif isinstance(ValueVal.type, ir.ArrayType):
zero: ir.Constant = ir.Constant(ir.IntType(32), 0) zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
@@ -1364,7 +1385,7 @@ class AssignHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return return
if isinstance(Target.value, ast.Subscript): if isinstance(Target.value, ast.Subscript):
@@ -1375,7 +1396,7 @@ class AssignHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return return
if isinstance(Target.value, ast.Attribute): if isinstance(Target.value, ast.Attribute):
@@ -1386,12 +1407,12 @@ class AssignHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return return
arr_alloc: ir.AllocaInstr = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") arr_alloc: ir.AllocaInstr = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc) Gen._store(ValueVal, arr_alloc)
ElemPtr: ir.Value = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript") ElemPtr: ir.Value = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order) self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
ModifiedArr: ir.Value = Gen._load(arr_alloc, name="modified_arr") ModifiedArr: ir.Value = Gen._load(arr_alloc, name="modified_arr")
if isinstance(Target.value, ast.Subscript): if isinstance(Target.value, ast.Subscript):
@@ -1403,6 +1424,71 @@ class AssignHandle(BaseHandle):
if OuterPtr: if OuterPtr:
Gen._store(ModifiedArr, OuterPtr) Gen._store(ModifiedArr, OuterPtr)
@staticmethod
def _StructTypeKey(ty: ir.Type) -> str:
"""结构体类型的稳定标识(identified 用 name,literal 用类型文本)"""
if isinstance(ty, ir.IdentifiedStructType):
return ty.name
return str(ty)
# ============================================================
# _RejectWholeObjectCopyIntoPtrSlot - 下标赋值的"整对象拷贝"fail-fast
#
# 目标形态(唯一判据):赋值左值是 rawptr[T] 下标(元素 = 对象/结构体 T,
# 基座为指针存储),而 RHS 的值类型是指向 T 的指针。此时 _StoreWithCoerce /
# _coerce_value 的"指针 → 非指针值: load 解引用"分支会生成
# %v = load %T, %T* %rhs
# store %T %v, %T* %elem (写 sizeof(T) 字节)
# 而该槽只该写 8 字节指针(槽是按 8 字节排布的缓冲/空闲链表)⇒ 每个元素
# 越界 sizeof(T)-8 字节,写穿相邻块(MemBuddy 空闲链表被写穿的静默降级根因)。
#
# 不拦(判据刻意收窄,避免误伤):
# · 数组槽 t.CArray[T,N](IsArrayBase=True):元素槽本就按 sizeof(T) 布局,
# `v[i] = T(...)`(构造函数返回临时对象地址)是合法的整对象复制;
# · 元素是标量(rawptr[int]):ElemPtr 元素不是结构体;
# · 元素本身是指针(rawptr[rawptr[T]] / X|t.CPtr):元素类型是 Ptr;
# · RHS 所指类型与元素类型不一致(非本形态)。
# 与 TPV AssignGenMixin._reject_whole_object_copy_into_ptr_slot 同口径。
# ============================================================
def _RejectWholeObjectCopyIntoPtrSlot(self, Target: ast.expr, Value: ir.Value,
ElemPtr: ir.Value, IsArrayBase: bool) -> None:
"""rawptr[T] 下标赋值 + RHS 为指向 T 的指针 ⇒ fail-fast(整对象拷贝)"""
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if IsArrayBase:
return
if ElemPtr is None or not isinstance(ElemPtr.type, ir.PointerType):
return
ElemTy: ir.Type = ElemPtr.type.pointee
if not isinstance(ElemTy, (ir.IdentifiedStructType, ir.LiteralStructType)):
return
if Value is None or not isinstance(Value.type, ir.PointerType):
return
RhsInner: ir.Type = Value.type.pointee
if not isinstance(RhsInner, (ir.IdentifiedStructType, ir.LiteralStructType)):
return
ElemKey: str = AssignHandle._StructTypeKey(ElemTy)
RhsKey: str = AssignHandle._StructTypeKey(RhsInner)
if ElemKey is None or RhsKey is None:
return
if ElemKey != RhsKey:
return
ElemSize: int = Gen._get_struct_size(ElemTy)
PtrSize: int = 8
Over: int = ElemSize - PtrSize
if Over < 0:
Over = 0
SrcInfo: str = Gen._get_node_info()
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"下标赋值的左值元素是对象 '{ElemKey}'({ElemSize} 字节),"
f"右值却是指向 '{RhsKey}' 的指针:编译器会静默生成整对象拷贝"
f"(load {ElemKey} + store {ElemKey},写 {ElemSize} 字节),"
f"而不是按指针写 {PtrSize} 字节。若该地址实际按 8 字节槽存放指针"
f"(如 rawptr[T] 缓冲/空闲链表),每写一个元素就越界 {Over} 字节,"
f"直接写穿相邻块。修法:把指针槽的基座类型写成指针元素形态"
f"(`rawptr[T] | t.CPtr` 或 `rawptr[rawptr[T]]`)后再赋值;"
f"或把基座显式转为 t.CPtr(无类型指针)后按 8 字节指针语义写入。{SrcInfo}")
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value) -> None: def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Ptr.type, ir.PointerType): if isinstance(Ptr.type, ir.PointerType):
@@ -1456,12 +1542,21 @@ class AssignHandle(BaseHandle):
return None return None
def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None: def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None:
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
# 模块级全局注解不经过 ResolveAnnotationType,故在此显式校验。
self._CheckFixedArrayKeyword(Node.annotation)
if isinstance(Node.target, ast.Name): if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id VarName: str = Node.target.id
Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName) Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
if Info: if Info:
if Info.IsTypedef: if Info.IsTypedef:
return return
# 模块级全局 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`):
# 先解析(空/缺段名、非正整数字面量 aligned → fail-fast 终止),再在各
# 全局创建点用 _ApplyGlobalAttrs 施加。此前 TPC 全局路径完全没有
# section/align/weak 代码(函数侧 section 只来自装饰器,见
# HandlesFunctions.py:1361-1362 → :1767)。
GlobalAttrs: dict[str, Any] = self._ExtractGlobalAttrs(Node, Gen)
parse_result = ParseListAnnotation(Node.annotation) parse_result = ParseListAnnotation(Node.annotation)
if parse_result: if parse_result:
elem_type_node: ast.expr = parse_result.elem_type_node elem_type_node: ast.expr = parse_result.elem_type_node
@@ -1470,15 +1565,30 @@ class AssignHandle(BaseHandle):
check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True, check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True,
) )
if parse_result.is_pointer: if parse_result.is_pointer:
# 单参数 list[type] → 指针 # 单参数 t.CArray[T]:与 t.CArray[T, None] 统一为"长度自动推导数组"。
VarType: ir.Type = ir.PointerType(ElemType) # 有可推导初值(列表字面量 / 字符串字面量)时不再生成裸指针,而是落入
if VarName not in Gen.variables and VarName not in Gen.module.globals: # 下方数组路径:由 ParseArrayCount(count_node=None) 从字面量推导长度、
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) # 字节内联到变量自身(与类成员 DeclarationGenerator.py:775-784、
GlobalVar.initializer = ir.Constant(VarType, None) # 局部 HandlesAnnAssign.py:446-489 同一口径;用户语义:单参数即
Gen.variables[VarName] = GlobalVar # "长度自动推导",不再是隐式指针)。
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: # 无可推导初值(纯声明 / 真正的指针用法)时保持既有指针语义逐字不变。
Gen.global_struct_class[VarName] = elem_type_node.id HasDerivableInit: bool = bool(
return Node.value and (
isinstance(Node.value, ast.List)
or (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str))
)
)
if not HasDerivableInit:
VarType: ir.Type = ir.PointerType(ElemType)
if VarName not in Gen.variables and VarName not in Gen.module.globals:
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
GlobalVar.initializer = ir.Constant(VarType, None)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
Gen.global_struct_class[VarName] = elem_type_node.id
return
# 有初值 → 落入下方数组路径(count_node=None → 从字面量推导长度)
count_node: ast.expr | None = parse_result.count_node count_node: ast.expr | None = parse_result.count_node
ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global') ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global')
VarType = ir.ArrayType(ElemType, ArrayCount) VarType = ir.ArrayType(ElemType, ArrayCount)
@@ -1506,6 +1616,7 @@ class AssignHandle(BaseHandle):
GlobalVar.linkage = 'common' GlobalVar.linkage = 'common'
else: else:
GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount) GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar Gen.variables[VarName] = GlobalVar
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
Gen.global_struct_class[VarName] = elem_type_node.id Gen.global_struct_class[VarName] = elem_type_node.id
@@ -1523,6 +1634,7 @@ class AssignHandle(BaseHandle):
GlobalVar.initializer = ir.Constant(VarType, None) GlobalVar.initializer = ir.Constant(VarType, None)
else: else:
GlobalVar.initializer = ir.Constant(VarType, None) GlobalVar.initializer = ir.Constant(VarType, None)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar Gen.variables[VarName] = GlobalVar
Gen.global_struct_class[VarName] = StructName Gen.global_struct_class[VarName] = StructName
return return
@@ -1557,6 +1669,12 @@ class AssignHandle(BaseHandle):
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo) VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(VarType, ir.VoidType): if isinstance(VarType, ir.VoidType):
VarType = ir.IntType(32) VarType = ir.IntType(32)
# const / volatile 模块级全局登记(统一登记入口,持久层 global_*)。
# 局限(明确记录,见 Debugger/type-descriptors.md 已知差异):本分支只覆盖
# 标量/指针型全局;数组(t.CArray[...])与结构体型全局在更早的分支
# (上方 1568-1627)已 return,不在此登记 —— 全仓普查无这类带 const/
# volatile 注解的全局,故无可观测差异。
Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=True)
# 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考, # 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考,
# 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。 # 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。
if not hasattr(Gen, '_module_ann_types'): if not hasattr(Gen, '_module_ann_types'):
@@ -1593,6 +1711,7 @@ class AssignHandle(BaseHandle):
if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)):
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName) GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
GlobalVar.initializer = ir.Constant(VarType, None) GlobalVar.initializer = ir.Constant(VarType, None)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar Gen.variables[VarName] = GlobalVar
if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name: if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name:
Gen.global_struct_class[VarName] = TypeInfo.Name Gen.global_struct_class[VarName] = TypeInfo.Name
@@ -1666,8 +1785,159 @@ class AssignHandle(BaseHandle):
GlobalVar.linkage = 'common' GlobalVar.linkage = 'common'
else: else:
GlobalVar.initializer = Gen._zero_value_for_type(VarType) GlobalVar.initializer = Gen._zero_value_for_type(VarType)
# 属性最后施加:weak 需覆盖分支内可能设过的 'common'(对齐 TransPyV
# TranslatorGenMixin.vp:926-929 先清 Linkage 再设 weak 的语义)。
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar Gen.variables[VarName] = GlobalVar
# ======================================================================
# 模块级全局的 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`)
#
# 镜像 TransPyV(Viper 自举侧)已有实现:
# TranslatorGenMixin._scan_global_tattr / _extract_global_attr_section /
# _extract_global_attr_aligned / _extract_global_attr_weak(.vp:590-651)
# + ClassGenBase._tattr_name(.vp:341-358)+ _class_has_tattr 的
# `c.Attribute(t.attr.xxx)` 解包(.vp:359-394)。
# TPC(Python 侧)此前完全没有全局 section/align/weak 代码。
# ======================================================================
def _TAttrName(self, node: ast.AST | None) -> str | None:
"""从 t.attr.<name> / t.attr.<name>(args) 节点提取最外层属性名。
对应 TransPyV ClassGenBase._tattr_name:Call → 递归取 func;
Attribute → 取 attr。
"""
if isinstance(node, ast.Call):
return self._TAttrName(node.func)
if isinstance(node, ast.Attribute):
return node.attr
return None
def _ScanGlobalTAttr(self, node: ast.AST | None, want_name: str) -> ast.AST | None:
"""在全局变量注解中查找 t.attr.<want_name> 节点,命中返回该节点。
注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)),
递归左右子节点。命中节点形态:Call(t.attr.aligned(16))或
Attribute(t.attr.weak)。同时兼容包装写法
`T | c.Attribute(t.attr.section("..."))`(解包参考 TransPyV
ClassGenBase._class_has_tattr,.vp:375-389)。
"""
if node is None:
return None
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
hit_left: ast.AST | None = self._ScanGlobalTAttr(node.left, want_name)
if hit_left is not None:
return hit_left
return self._ScanGlobalTAttr(node.right, want_name)
# 包装写法:c.Attribute(t.attr.xxx / t.attr.xxx(...)) → 拆 args 再扫
# (与 HandlesFunctions.py:1354 装饰器侧同一判据:func.attr=='Attribute'
# 且 func.value 为 Name('c'))
if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)
and node.func.attr == 'Attribute'
and isinstance(node.func.value, ast.Name) and node.func.value.id == 'c'):
for arg in node.args:
hit_arg: ast.AST | None = self._ScanGlobalTAttr(arg, want_name)
if hit_arg is not None:
return hit_arg
return None
if self._TAttrName(node) == want_name:
return node
return None
@staticmethod
def _TAttrArgStr(node: ast.AST | None) -> str | None:
"""取 t.attr.<name>("...") 的字符串字面量参数(缺参/非字面量返回 None)。"""
if isinstance(node, ast.Call) and node.args:
arg0: ast.AST = node.args[0]
if isinstance(arg0, ast.Constant) and isinstance(arg0.value, str):
return arg0.value
return None
@staticmethod
def _TAttrAlignInt(node: ast.AST | None) -> int | None:
"""取 t.attr.aligned(N) 的整数字面量 N(缺参/非字面量返回 None)。
对应 TransPyV ClassGenBase._tattr_aligned_int(.vp:395-415)。
"""
if isinstance(node, ast.Call) and node.args:
arg0: ast.AST = node.args[0]
if (isinstance(arg0, ast.Constant) and isinstance(arg0.value, int)
and not isinstance(arg0.value, bool)):
return arg0.value
return None
def _RaiseGlobalAttrError(self, Msg: str, Node: ast.AST, Gen: "Translator.LlvmGen", VarName: str) -> None:
"""fail-fast:全局 t.attr.* 属性非法(缺/空段名、非正整数字面量 aligned)。
定位串(文件:行:列)与报错出口对齐 HandlesBase._RaiseUnresolvedExpr
(HandlesBase.py:552-615)——错误信息含源码位置后 sys.exit(1)。
"""
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
col: int = getattr(Node, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or ''
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的行数
_line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
loc: str = src_file
if lineno > 0:
loc += f":{lineno}"
if col >= 0:
loc += f":{col}"
_vlog().compile_error(
f"模块级变量 '{VarName}' 的 {Msg}(位置 {loc})", src_file, lineno, '')
sys.exit(1)
def _ExtractGlobalAttrs(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> dict[str, Any]:
"""从模块级全局注解提取 t.attr.section/aligned/weak。
返回 {'section': str|None, 'align': int, 'weak': bool}。
非法值(空/缺段名、非正整数 aligned)→ fail-fast 终止(对齐 TransPyV
TranslatorGenMixin._extract_global_attr_section/aligned 的致命错误语义)。
"""
Attrs: dict[str, Any] = {'section': None, 'align': 0, 'weak': False}
Ann: ast.AST = Node.annotation
VarName: str = Node.target.id if isinstance(Node.target, ast.Name) else '<anonymous>'
sec_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'section')
if sec_node is not None:
sec_val: str | None = self._TAttrArgStr(sec_node)
if not sec_val:
self._RaiseGlobalAttrError(
"t.attr.section(...) 段名缺失或为空(空段名会生成非法的 section \"\")",
sec_node, Gen, VarName)
Attrs['section'] = sec_val
align_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'aligned')
if align_node is not None:
align_val: int | None = self._TAttrAlignInt(align_node)
if align_val is None or align_val <= 0:
self._RaiseGlobalAttrError(
"t.attr.aligned(N) 需要正整数字面量 N", align_node, Gen, VarName)
Attrs['align'] = align_val
if self._ScanGlobalTAttr(Ann, 'weak') is not None:
Attrs['weak'] = True
return Attrs
@staticmethod
def _ApplyGlobalAttrs(GlobalVar: ir.GlobalVariable, Attrs: dict[str, Any]) -> None:
"""把提取出的 section/align/weak 施加到已创建的 LLVM 全局变量上。
llvmlite 口径(llvmlite/ir/values.py):
- section 写**裸**段名(序列化时自动加引号,:843-844);
- align 需 int(:846-847);GlobalVariable.align 默认 None(:807);
- weak 用 linkage='weak'(GlobalVariable 无 weak 属性,对照函数侧
HandlesFunctions.py:1772)。
"""
if not Attrs:
return
sec: str | None = Attrs.get('section')
if sec:
GlobalVar.section = sec
align_val: int = int(Attrs.get('align') or 0)
if align_val > 0:
GlobalVar.align = align_val
if Attrs.get('weak'):
GlobalVar.linkage = 'weak'
def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None: def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None:
if isinstance(value_node, ast.Constant): if isinstance(value_node, ast.Constant):
if isinstance(value_node.value, bool): if isinstance(value_node.value, bool):
+60 -10
View File
@@ -303,9 +303,14 @@ class AsyncFunctionHandle(BaseHandle):
if not any(isinstance(n, ast.Await) for n in ast.walk(stmt)): if not any(isinstance(n, ast.Await) for n in ast.walk(stmt)):
continue # 无 await:委托 ForHandler,不需额外槽 continue # 无 await:委托 ForHandler,不需额外槽
if not self._IsRangeFor(stmt): if not self._IsRangeFor(stmt):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(stmt, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)" f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
f"(line {getattr(stmt, 'lineno', 0)})" f"(line {_lineno})"
) )
slots.append(f'__f_idx_{for_counter}') slots.append(f'__f_idx_{for_counter}')
slots.append(f'__f_len_{for_counter}') slots.append(f'__f_len_{for_counter}')
@@ -551,9 +556,14 @@ class AsyncFunctionHandle(BaseHandle):
if isinstance(stmt, ast.Expr): if isinstance(stmt, ast.Expr):
self.Trans.ExprHandler.HandleExprLlvm(stmt.value) self.Trans.ExprHandler.HandleExprLlvm(stmt.value)
return return
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(stmt, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M2] async 函数体暂不支持语句: {type(stmt).__name__} " f"[coro-M2] async 函数体暂不支持语句: {type(stmt).__name__} "
f"(line {getattr(stmt, 'lineno', 0)})" f"(line {_lineno})"
) )
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@@ -640,9 +650,14 @@ class AsyncFunctionHandle(BaseHandle):
return return
async_name: str | None = self._GetAsyncCallName(call) if isinstance(call, ast.Call) else None async_name: str | None = self._GetAsyncCallName(call) if isinstance(call, ast.Call) else None
if not async_name: if not async_name:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(await_node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M3] await 目标必须是 async 函数调用或 coro_sleep(ms) " f"[coro-M3] await 目标必须是 async 函数调用或 coro_sleep(ms) "
f"(line {getattr(await_node, 'lineno', 0)})" f"(line {_lineno})"
) )
self.Trans.ExprHandler.HandleExprLlvm(await_node) # → _HandleAwaitLlvm self.Trans.ExprHandler.HandleExprLlvm(await_node) # → _HandleAwaitLlvm
@@ -1002,8 +1017,13 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroIf(self, Node: ast.If, Gen: LlvmCodeGenerator) -> None: def _HandleCoroIf(self, Node: ast.If, Gen: LlvmCodeGenerator) -> None:
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)): if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M2] if 条件中不支持 await (line {getattr(Node, 'lineno', 0)})" f"[coro-M2] if 条件中不支持 await (line {_lineno})"
) )
Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test) Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test)
if not Cond: if not Cond:
@@ -1034,8 +1054,13 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroWhile(self, Node: ast.While, Gen: LlvmCodeGenerator) -> None: def _HandleCoroWhile(self, Node: ast.While, Gen: LlvmCodeGenerator) -> None:
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)): if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M2] while 条件中不支持 await (line {getattr(Node, 'lineno', 0)})" f"[coro-M2] while 条件中不支持 await (line {_lineno})"
) )
CondBB: ir.Block = Gen.func.append_basic_block(name='coro_wcond') CondBB: ir.Block = Gen.func.append_basic_block(name='coro_wcond')
BodyBB: ir.Block = Gen.func.append_basic_block(name='coro_wbody') BodyBB: ir.Block = Gen.func.append_basic_block(name='coro_wbody')
@@ -1076,12 +1101,22 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroTry(self, Node: ast.Try, Gen: LlvmCodeGenerator) -> None: def _HandleCoroTry(self, Node: ast.Try, Gen: LlvmCodeGenerator) -> None:
"""协程内 try/except:捕获 raise 与 await 传播异常(M6:多 except 类型匹配;Exception/裸 except 捕获所有)。""" """协程内 try/except:捕获 raise 与 await 传播异常(M6:多 except 类型匹配;Exception/裸 except 捕获所有)。"""
if not Node.handlers: if not Node.handlers:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M6] 协程内 try 需要 except 子句 (line {getattr(Node, 'lineno', 0)})" f"[coro-M6] 协程内 try 需要 except 子句 (line {_lineno})"
) )
if Node.finalbody: if Node.finalbody:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno = int(getattr(Node, 'lineno', 0) or 0)
_line_offset = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M6] 协程内暂不支持 try/finally (line {getattr(Node, 'lineno', 0)})" f"[coro-M6] 协程内暂不支持 try/finally (line {_lineno})"
) )
except_bb: ir.Block = Gen.func.append_basic_block('coro_try_except') except_bb: ir.Block = Gen.func.append_basic_block('coro_try_except')
merge_bb: ir.Block = Gen.func.append_basic_block('coro_try_merge') merge_bb: ir.Block = Gen.func.append_basic_block('coro_try_merge')
@@ -1159,8 +1194,13 @@ class AsyncFunctionHandle(BaseHandle):
"""协程内 try/finally(无 except):正常/异常路径都执行 fbody; """协程内 try/finally(无 except):正常/异常路径都执行 fbody;
异常时 finally 后重新传播(done=1 + br exit);finally 内暂不支持 await。""" 异常时 finally 后重新传播(done=1 + br exit);finally 内暂不支持 await。"""
if any(isinstance(n, ast.Await) for s in Node.finalbody for n in ast.walk(s)): if any(isinstance(n, ast.Await) for s in Node.finalbody for n in ast.walk(s)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M7] finally 内暂不支持 await (line {getattr(Node, 'lineno', 0)})" f"[coro-M7] finally 内暂不支持 await (line {_lineno})"
) )
fin_bb: ir.Block = Gen.func.append_basic_block('coro_fin') fin_bb: ir.Block = Gen.func.append_basic_block('coro_fin')
fin_exc_bb: ir.Block = Gen.func.append_basic_block('coro_fin_exc') fin_exc_bb: ir.Block = Gen.func.append_basic_block('coro_fin_exc')
@@ -1201,15 +1241,25 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroFor(self, Node: ast.For, Gen: LlvmCodeGenerator) -> None: def _HandleCoroFor(self, Node: ast.For, Gen: LlvmCodeGenerator) -> None:
if any(isinstance(n, ast.Await) for n in ast.walk(Node)): if any(isinstance(n, ast.Await) for n in ast.walk(Node)):
if not self._IsRangeFor(Node): if not self._IsRangeFor(Node):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)" f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
f"(line {getattr(Node, 'lineno', 0)})" f"(line {_lineno})"
) )
self._HandleCoroForRange(Node, Gen) self._HandleCoroForRange(Node, Gen)
return return
if any(isinstance(n, ast.Return) for st in Node.body for n in ast.walk(st)): if any(isinstance(n, ast.Return) for st in Node.body for n in ast.walk(st)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[coro-M2] For 循环体内暂不支持 return(M3 实现)(line {getattr(Node, 'lineno', 0)})" f"[coro-M2] For 循环体内暂不支持 return(M3 实现)(line {_lineno})"
) )
# 无 await/return:委托现有 ForHandler(迭代索引等 alloca 不跨挂起,安全) # 无 await/return:委托现有 ForHandler(迭代索引等 alloca 不跨挂起,安全)
self.Trans.ForHandler._HandleForLlvm(Node) self.Trans.ForHandler._HandleForLlvm(Node)
+8 -9
View File
@@ -65,9 +65,8 @@ class AugAssignHandle(BaseHandle):
src_info: str = Gen._get_node_info() src_info: str = Gen._get_node_info()
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}") _vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
sys.exit(1) sys.exit(1)
if VarName in Gen.var_const_flags: if Gen._IsVarConst(VarName):
src_info: str = Gen._get_node_info() _vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
_vlog().error(f"不能对 const 变量 '{VarName}' 增量赋值{src_info}")
sys.exit(1) sys.exit(1)
ClassName: str | None = Gen.var_struct_class.get(VarName) ClassName: str | None = Gen.var_struct_class.get(VarName)
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op)) iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
@@ -118,7 +117,7 @@ class AugAssignHandle(BaseHandle):
GVar = Gen.module.globals[VarName] GVar = Gen.module.globals[VarName]
Gen.variables[VarName] = GVar Gen.variables[VarName] = GVar
OldVal: ir.Value = Gen._load(GVar, name=VarName) OldVal: ir.Value = Gen._load(GVar, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned) NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, GVar) Gen._store(NewVal, GVar)
return return
if VarName in Gen._reg_values: if VarName in Gen._reg_values:
@@ -156,7 +155,7 @@ class AugAssignHandle(BaseHandle):
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left") OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width: elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right") Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned) NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType): if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
if NewVal.type.width > orig_type.width: if NewVal.type.width > orig_type.width:
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result") NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
@@ -179,7 +178,7 @@ class AugAssignHandle(BaseHandle):
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug") NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
Gen._store(NewVal, store_ptr) Gen._store(NewVal, store_ptr)
return return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned) NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(store_ptr.type, ir.PointerType): if isinstance(store_ptr.type, ir.PointerType):
TargetType: ir.Type = store_ptr.type.pointee TargetType: ir.Type = store_ptr.type.pointee
if NewVal.type != TargetType: if NewVal.type != TargetType:
@@ -191,7 +190,7 @@ class AugAssignHandle(BaseHandle):
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen.variables[VarName] = var Gen.variables[VarName] = var
OldVal: ir.Value = Gen._load(var, name=VarName) OldVal: ir.Value = Gen._load(var, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned) NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, var) Gen._store(NewVal, var)
elif isinstance(Node.target, ast.Attribute): elif isinstance(Node.target, ast.Attribute):
AttrName: str = Node.target.attr AttrName: str = Node.target.attr
@@ -210,7 +209,7 @@ class AugAssignHandle(BaseHandle):
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr") OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
elif OldVal.type.width > Value.type.width: elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr") Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned) NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal) self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
elif isinstance(Node.target, ast.Subscript): elif isinstance(Node.target, ast.Subscript):
SubTarget: ast.Subscript = Node.target SubTarget: ast.Subscript = Node.target
@@ -236,7 +235,7 @@ class AugAssignHandle(BaseHandle):
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub") NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
Gen._store(NewVal, ElemPtr) Gen._store(NewVal, ElemPtr)
return return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned) NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType): if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
if NewVal.type.width > ElemPtr.type.pointee.width: if NewVal.type.width > ElemPtr.type.pointee.width:
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result") NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
+122 -20
View File
@@ -13,7 +13,8 @@ from lib.constants import config as _config
from lib.includes import t from lib.includes import t
from lib.includes.t import CTypeRegistry from lib.includes.t import CTypeRegistry
from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES
from lib.core.SymbolUtils import IsPtrSubscriptNode, IsListAnnotation from lib.core.SymbolUtils import (
IsPtrSubscriptNode, IsListAnnotation, FixedArrayKeywordAnnotationError)
from lib.core.VLogger import get_logger as _vlog from lib.core.VLogger import get_logger as _vlog
@@ -523,12 +524,13 @@ class BaseHandle:
pointee: _ir.Type = val.type.pointee pointee: _ir.Type = val.type.pointee
return isinstance(pointee, _ir.IntType) and pointee.width == 8 return isinstance(pointee, _ir.IntType) and pointee.width == 8
def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None) -> _ir.Value | None: def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None,
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType) ConstWidthHint: int | None = None) -> _ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
@staticmethod @staticmethod
def _ExprText(Node: ast.AST) -> str: def _ExprText(Node: ast.AST) -> str:
"""把属性链/名字节点渲染成源码文本(如 func.Blocks.Head),供错误信息使用""" """把表达式节点渲染成源码文本(如 func.Blocks.Head、list[X*] * uint64(v)),供错误信息使用"""
if isinstance(Node, ast.Attribute): if isinstance(Node, ast.Attribute):
parts: list[str] = [] parts: list[str] = []
cur: ast.AST = Node cur: ast.AST = Node
@@ -541,32 +543,97 @@ class BaseHandle:
return '.'.join(reversed(parts)) return '.'.join(reversed(parts))
if isinstance(Node, ast.Name): if isinstance(Node, ast.Name):
return Node.id return Node.id
try:
rendered: str = ast.unparse(Node)
if rendered:
return rendered
except Exception:
pass
return getattr(Node, 'attr', '') or '<expr>' return getattr(Node, 'attr', '') or '<expr>'
def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any) -> None: def _FailFastFixedArrayKeyword(self, Node: ast.AST, Msg: str) -> None:
"""fail-fast:属性/成员访问表达式在编译期求值失败(返回 None)时终止。 """内建定长数组构造子 array 误用的 fail-fast 出口。
属性链(含两级自动解引用链,如 blk = func.Blocks.Head)若解析不出 报错含:文件:行:列 + 源码片段 + 期望形态(array[E, N])+ 正确示例
目标类型,_handle_attr_nested 会返回 None;赋值调用方若静默跳过 (消息文本由 SymbolUtils.FixedArrayKeywordAnnotationError 给出)。
store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机崩溃)。
此处用 VLogger 输出 文件名:行:列 + 表达式源码后退出(严格模式语义)。
""" """
import sys import sys
Gen: Any = getattr(self.Trans, 'LlvmGen', None)
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
col: int = getattr(Node, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or ''
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
code_line: str = ''
if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'):
try:
code_line = (Gen._get_source_line(lineno) or '').strip()
except Exception:
code_line = ''
loc: str = src_file
if lineno > 0:
loc += f":{lineno}"
if col >= 0:
loc += f":{col}"
full: str = f"{Msg}。位置 {loc}" + (f",源码: {code_line}" if code_line else '')
_vlog().compile_error(full, src_file, lineno, code_line)
sys.exit(1)
def _CheckFixedArrayKeyword(self, Annotation: ast.AST) -> None:
"""注解入口统一校验内建定长数组构造子 array(误用即 fail-fast)。
只校验带位置的注解入口(本类的 ResolveAnnotationType / CollectAnnAssignMember
+ HandlesAnnAssign 局部 / HandlesAssign 全局显式调用),**不**在通用表达式
解析路径上调用——以保证 array 在非类型位置仍是普通标识符(软关键字)。
"""
if Annotation is None:
return
Msg: str | None = FixedArrayKeywordAnnotationError(Annotation, self.Trans.SymbolTable)
if Msg:
self._FailFastFixedArrayKeyword(Annotation, Msg)
def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any, AnnNode: ast.AST | None = None,
VarName: str | None = None) -> None:
"""fail-fast:带值表达式在编译期求值失败(返回 None 且无值可存)时终止。
属性链(含两级自动解引用链,如 blk = func.Blocks.Head)解析不出目标
类型时 _handle_attr_nested 会返回 None;类型下标被当乘法的畸形写法
(如 list[X*]*(...))会让整条 RHS 不产任何指令而求值 None。赋值调用方
若静默跳过 store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机
崩溃)。此处用 VLogger 输出 目标变量 + 注解文本 + 表达式源码与形态 +
文件名:行:列 后退出(严格模式语义)。
"""
import sys
ann_text: str = ''
if AnnNode is not None:
try:
ann_text = ast.unparse(AnnNode)
except Exception:
ann_text = ''
# RHS 形态 = AST 节点类名(Name/Subscript/BinOp/Call/Attribute/…),
# 便于报错可直接按形态分类,不必再从表达式文本反推。
rhs_kind: str = type(Node).__name__
raw_lineno: int = getattr(Node, 'lineno', 0) or 0 raw_lineno: int = getattr(Node, 'lineno', 0) or 0
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
col: int = getattr(Node, 'col_offset', -1) col: int = getattr(Node, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or '' src_file: str = getattr(Gen, '_current_source_file', '') or ''
expr_text: str = self._ExprText(Node) expr_text: str = self._ExprText(Node)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator)。
_line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
code_line: str = '' code_line: str = ''
if lineno > 0 and src_file: if lineno > 0 and src_file:
# 行号基准修正(vasts AST 行号 vs 磁盘原文件行号偏差): # 行号基准修正(vast AST 行号 vs 磁盘原文件行号偏差):
# Phase2Translator._inject_auto_imports 会在解析前向源码头部注入 # Phase2Translator._inject_auto_imports 会在解析前向源码头部注入
# 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件 # 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件
# 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、 # 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、
# FuncGenMixin.vp 真实 514 报 515)。而 _get_source_line 基于 # FuncGenMixin.vp 真实 514 报 515)。上面已按 _line_offset 精确还原;
# _current_source_lines(原文件),导致报错行号偏大且"代码行" # 此处再以表达式文本为锚(从原始行号向上至多 8 行)回溯校正
# 取错行。修正:以表达式文本为锚,从原始行号向上(至多 8 行) # 属性链(其 expr_text 逐字出现在磁盘行中)可能残留的偏差。
# 回溯取"最近且包含该表达式文本"的源码行作为真实行号。
if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'): if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'):
_src_lines: Any = getattr(Gen, '_current_source_lines', None) _src_lines: Any = getattr(Gen, '_current_source_lines', None)
if isinstance(_src_lines, list) and _src_lines: if isinstance(_src_lines, list) and _src_lines:
@@ -583,8 +650,11 @@ class BaseHandle:
loc += f":{lineno}" loc += f":{lineno}"
if col >= 0: if col >= 0:
loc += f":{col}" loc += f":{col}"
msg: str = (f"{What}表达式 '{expr_text}' 编译期求值为空(属性访问未能解析出" msg: str = (f"{What}表达式 '{expr_text}'({rhs_kind} 形态)编译期求值为空("
f"目标类型/成员),赋值将被跳过、变量保持未初始化。位置 {loc}" f"表达式未解析出目标、无值可存;且其求值未以返回 void 的调用收尾),"
f"赋值将被跳过、变量保持未初始化"
f"{(',目标变量 ' + repr(VarName)) if VarName else ''}"
f"{(',注解 ' + ann_text) if ann_text else ''}。位置 {loc}"
f"{(',源码: ' + code_line) if code_line else ''}") f"{(',源码: ' + code_line) if code_line else ''}")
_vlog().compile_error(msg, src_file, lineno, code_line) _vlog().compile_error(msg, src_file, lineno, code_line)
sys.exit(1) sys.exit(1)
@@ -767,6 +837,9 @@ class BaseHandle:
CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端 CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端
的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。 的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。
""" """
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast):
# 本方法覆盖函数参数/返回注解与类字段(CollectAnnAssignMember 亦经此)。
self._CheckFixedArrayKeyword(annotation)
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) merger: Any = getattr(self.Trans, 'TypeMergeHandler', None)
if merger: if merger:
@@ -943,6 +1016,15 @@ class BaseHandle:
if isinstance(MemberType, _ir.VoidType): if isinstance(MemberType, _ir.VoidType):
MemberType = _ir.PointerType(_ir.IntType(8)) MemberType = _ir.PointerType(_ir.IntType(8))
return VarName, MemberType, TypeInfo return VarName, MemberType, TypeInfo
except RecursionError:
# RecursionError 不是"可忽略异常":它是类型解析/声明发射链**成环**的
# 真缺陷信号。此前被下方 `except Exception` 吞成一行
# `WARN: HandlesBase: 忽略异常 maximum recursion depth exceeded` 并静默
# 退化为 i32 ⇒ 真正的环(如跨模块类声明发射重入
# _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub ↔ _TryLoadClassMembersFromPyi)
# 被掩盖,下游只看到类型解析静默退化。此处 fail-fast 上抛(保留原始栈),
# 让环路根因暴露在错误栈顶,不再被 WARN 掩埋。
raise
except Exception as e: except Exception as e:
_vlog().warning(f"HandlesBase: 忽略异常 {e}", exc_info=e) _vlog().warning(f"HandlesBase: 忽略异常 {e}", exc_info=e)
return VarName, _ir.IntType(32), None return VarName, _ir.IntType(32), None
@@ -995,15 +1077,35 @@ class BaseHandle:
ret_type = inferred ret_type = inferred
else: else:
ret_type = Gen._CType2LLVM('i32', False) ret_type = Gen._CType2LLVM('i32', False)
param_type_infos = [pt for _, pt in (sym_info.FuncPtrParams or [])] param_type_infos = list(sym_info.FuncPtrParams or [])
if isinstance(ret_type, _ir.VoidType): if isinstance(ret_type, _ir.VoidType):
ret_type = _ir.IntType(32) ret_type = _ir.IntType(32)
llvm_param_types: list[Any] = [] llvm_param_types: list[Any] = []
for pt in param_type_infos: for ParamIdx, (ParamName, pt) in enumerate(param_type_infos):
if isinstance(pt, CTypeInfo) and pt.BaseType: if isinstance(pt, CTypeInfo) and pt.BaseType:
lp: Any = pt.ToLLVM(Gen) lp: Any = pt.ToLLVM(Gen)
elif isinstance(pt, str):
lp = Gen._CType2LLVM(pt, '*' in pt)
else: else:
lp = Gen._CType2LLVM(pt if isinstance(pt, str) else 'i32', '*' in pt if isinstance(pt, str) else False) # fast-fail:形参类型未解析出 BaseType 时不再硬编码 'i32' 造形参。
# 旧回退会把"类型解析失败"伪装成 llvmlite 的
# Type of #{N} arg mismatch: {expected} != {actual}
# (形参被静默写成 i32 ⇒ 报错方向与真因相反,且不含符号/位置信息,
# 如 "Type of #2 arg mismatch: i32 != %"<sha1>.SymbolTable"*"),
# 这里直接抛出带上下文的编译错误暴露真实缺失。
if isinstance(pt, CTypeInfo):
_PtDiag: str = (
f"CTypeInfo(BaseType=None, Name={pt.Name!r}, PtrCount={pt.PtrCount},"
f" IsTypedef={pt.IsTypedef}, IsFuncPtr={pt.IsFuncPtr}, IsState={pt.IsState},"
f" OriginalType={pt.OriginalType!r})")
else:
_PtDiag = f"{type(pt).__name__}({pt!r})"
raise TypeError(
f"[fast-fail] 函数 '{sym_info.Name}' 第 {ParamIdx + 1} 个形参 '{ParamName}' 的"
f"类型未解析出 BaseType,无法构建 LLVM 形参类型({_PtDiag});"
f"定义位置={getattr(sym_info, 'file', None)!r}:{sym_info.Lineno},"
f" IsVariadic={is_variadic}, mangled_name={mangled_name!r}"
)
if isinstance(lp, _ir.VoidType): if isinstance(lp, _ir.VoidType):
lp = _ir.IntType(8).as_pointer() lp = _ir.IntType(8).as_pointer()
llvm_param_types.append(lp) llvm_param_types.append(lp)
+9 -2
View File
@@ -62,6 +62,12 @@ class BodyHandle(BaseHandle):
self.Trans.DeleteHandler._HandleDeleteLlvm(Node) self.Trans.DeleteHandler._HandleDeleteLlvm(Node)
except Exception as e: except Exception as e:
lineno: int = getattr(Node, 'lineno', 0) lineno: int = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator);
# 报文行号与源码行必须同用还原后的 lineno,二者一致(否则会显示下一行源码)。
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
source_line: str | None = Gen._get_source_line(lineno) source_line: str | None = Gen._get_source_line(lineno)
src_file: str = Gen._current_source_file or '' src_file: str = Gen._current_source_file or ''
src_path: str = src_file if src_file else 'unknown' src_path: str = src_file if src_file else 'unknown'
@@ -73,5 +79,6 @@ class BodyHandle(BaseHandle):
if Gen.builder and not Gen.builder.block.is_terminated: if Gen.builder and not Gen.builder.block.is_terminated:
Gen._EmitTempFrees() Gen._EmitTempFrees()
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None) -> ir.Value | None: def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None,
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType) ConstWidthHint: int | None = None) -> ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
+182 -20
View File
@@ -9,12 +9,22 @@ import llvmlite.ir as ir
from lib.core.Exportable import EnumMember as ExportEnumMember from lib.core.Exportable import EnumMember as ExportEnumMember
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.SymbolUtils import ( from lib.core.SymbolUtils import (
IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry) IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry,
EnumClassIntWidth,
EnumBodyMemberNames, EnumMemberCountMismatchError,
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
from lib.core.VLogger import get_logger as _vlog from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode from lib.constants.config import mode as _config_mode
from lib.includes import t from lib.includes import t
from lib.includes.t import CTypeRegistry from lib.includes.t import CTypeRegistry
# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CNeedPtr/CAutoPtr,后两者继承
# CPtr)、内建 rawptr(= t.CPtr)、str/bytes(= i8*)。枚举值是整数常量,
# 指针成员语义不成立,命中即明确报错(不静默当 int)。
_ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({
'CPtr', 'CNeedPtr', 'CAutoPtr', 'rawptr', 'str', 'bytes',
})
class ClassHandle(BaseHandle): class ClassHandle(BaseHandle):
def _is_exception_class(self, Node: ast.ClassDef) -> bool: def _is_exception_class(self, Node: ast.ClassDef) -> bool:
@@ -438,11 +448,15 @@ class ClassHandle(BaseHandle):
return 'struct' return 'struct'
return None return None
def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool]: def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool, bool]:
"""解析类的 Object/CVTable 标志和父类信息 """解析类的 Object/CVTable 标志和父类信息
Returns: Returns:
tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable) tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable,
VtableByClassLevel)
VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态(= 类级
CVTable / 继承链判定),供 vtable 槽筛选(SymbolUtils.IsVtableSlotMethod)
使用——与 TPV _should_method_be_virtual 第 3/4 条的判据一致。
""" """
ClassName: str = Node.name ClassName: str = Node.name
IsCpythonObject: bool = False IsCpythonObject: bool = False
@@ -536,6 +550,15 @@ class ClassHandle(BaseHandle):
or ParentClassName in Gen.structs) or ParentClassName in Gen.structs)
if parent_has_vtable or not parent_known: if parent_has_vtable or not parent_known:
IsCVTable = True IsCVTable = True
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
# 与 TPV ClassGenBase._detect_vtable_status 的三条 `_has_any_func_cvtable`
# 分支逐条对齐(类级 NoVTable / 父类 NoVTable / 无真实父类)。
# 注意"父类已知且无 vtable" **不**触发(TPV 同分支返回 0)——
# 父类按无 vptr 布局而子类插 vptr 会让继承方法字段错位。
VtableByClassLevel: bool = IsCVTable
if (not VtableByClassLevel and ClassHasFuncLevelCVTable(Node)
and (IsNoVTable or ParentIsNoVTable or not HasParentClass)):
IsCVTable = True
if IsCVTable: if IsCVTable:
Gen.class_vtable.add(ClassName) Gen.class_vtable.add(ClassName)
# 沿继承链向上传播,将所有祖先类加入 vtable # 沿继承链向上传播,将所有祖先类加入 vtable
@@ -557,7 +580,7 @@ class ClassHandle(BaseHandle):
else: else:
# 类图中没有,尝试从 class_parent 查询 # 类图中没有,尝试从 class_parent 查询
ancestor = Gen.class_parent.get(ancestor) ancestor = Gen.class_parent.get(ancestor)
return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel
def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None: def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None:
"""注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。 """注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。
@@ -651,7 +674,8 @@ class ClassHandle(BaseHandle):
IsCVTable: bool IsCVTable: bool
ParentClassName: str | None ParentClassName: str | None
IsNoVTable: bool IsNoVTable: bool
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable = self._resolve_class_flags(Node, Gen) VtableByClassLevel: bool
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel = self._resolve_class_flags(Node, Gen)
# 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body # 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body
Gen._classes_in_progress.add(ClassName) Gen._classes_in_progress.add(ClassName)
# 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class) # 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class)
@@ -685,7 +709,9 @@ class ClassHandle(BaseHandle):
_ck = (id(Gen), ClassName) _ck = (id(Gen), ClassName)
if _ck in _ih._struct_Load_cache: if _ck in _ih._struct_Load_cache:
del _ih._struct_Load_cache[_ck] del _ih._struct_Load_cache[_ck]
Gen.class_members[ClassName] = [] Gen.class_members[ClassName] = []
# 本地定义是权威重建点:先撤销"已展平"登记,重建完成(且父类齐全)后再登记。
Gen._members_flattened.discard(ClassName)
Gen.class_member_defaults[ClassName] = {} Gen.class_member_defaults[ClassName] = {}
Gen.class_member_signeds[ClassName] = {} Gen.class_member_signeds[ClassName] = {}
Gen.class_member_bitfields[ClassName] = {} Gen.class_member_bitfields[ClassName] = {}
@@ -693,6 +719,9 @@ class ClassHandle(BaseHandle):
Gen.class_member_bitoffsets[ClassName] = {} Gen.class_member_bitoffsets[ClassName] = {}
ParentClass: str | None = None ParentClass: str | None = None
ParentClasses: list[str] = [] ParentClasses: list[str] = []
# 本类展平是否完整(父类若在此刻尚未就绪会被跳过 ⇒ 不登记已展平,
# 留待查询侧 HandlesImports.EnsureClassMembersFlattened 补齐,顺序无关)。
_local_flat_complete: bool = True
if Node.bases: if Node.bases:
class_graph: dict = Gen._class_graph class_graph: dict = Gen._class_graph
for base in Node.bases: for base in Node.bases:
@@ -784,6 +813,9 @@ class ClassHandle(BaseHandle):
_flat: list[tuple] = [] _flat: list[tuple] = []
for ParentClass in ParentClasses: for ParentClass in ParentClasses:
if ParentClass not in class_graph and ParentClass not in Gen.class_members: if ParentClass not in class_graph and ParentClass not in Gen.class_members:
# 父类尚未就绪(跨模块父类在此刻未加载 / 特化表未生成)⇒
# 本类展平不完整,不登记 _members_flattened。
_local_flat_complete = False
continue continue
_FlattenInherited(ParentClass, _seen_cls, _flat, Gen) _FlattenInherited(ParentClass, _seen_cls, _flat, Gen)
if _flat: if _flat:
@@ -871,6 +903,10 @@ class ClassHandle(BaseHandle):
if parsed: if parsed:
VarName, MemberType, TypeInfo = parsed VarName, MemberType, TypeInfo = parsed
Gen.class_members[ClassName].append((VarName, MemberType)) Gen.class_members[ClassName].append((VarName, MemberType))
# const / volatile 类字段登记(统一登记入口,见 BaseGen)。
# 键 `类名.字段名` + 裸字段名;与 TransPyV 侧 ClassGenDef 的
# FieldEntry.IsConst/IsVolatile/PointeeVolatile 同口径。
Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation)
if TypeInfo: if TypeInfo:
if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8: if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8:
if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr):
@@ -1025,14 +1061,30 @@ class ClassHandle(BaseHandle):
# 构造函数(__new__/__init__)不放入 vtable,避免子类与基类 # 构造函数(__new__/__init__)不放入 vtable,避免子类与基类
# vtable 大小不一致导致虚分派索引偏移。__before_init__ 由 TPC # vtable 大小不一致导致虚分派索引偏移。__before_init__ 由 TPC
# 自动生成,不会出现在 FunctionDef 中,此处一并防御。 # 自动生成,不会出现在 FunctionDef 中,此处一并防御。
#
# 方法级修饰符(`novirtual def m(...)` ⇒ @t.NoVTable /
# `virtual def m(...)` ⇒ @t.CVTable):是否占槽按
# SymbolUtils.IsVtableSlotMethod 判定——与 TPV
# ClassGenBase._should_method_be_virtual 同判据(**唯一规则源**),
# 故 TPC/TPV 的虚表槽位顺序与数量逐槽一致(含"类级 NoVTable +
# 方法级 CVTable ⇒ 仍生成虚表且只有该方法进槽"这一组合形态)。
# 仅在本类确有 vtable(IsCVTable)时筛选:无 vtable 的类不进
# Vtables,class_methods 仍保留全部方法供静态解析/继承包装使用。
if MethodName not in ('__new__', '__init__', '__before_init__'): if MethodName not in ('__new__', '__init__', '__before_init__'):
if FullMethodName not in Gen.class_methods[ClassName]: _IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(item, VtableByClassLevel)
if _IsSlot and FullMethodName not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(FullMethodName) Gen.class_methods[ClassName].append(FullMethodName)
Gen.register_method(ClassName, FullMethodName) Gen.register_method(ClassName, FullMethodName, _IsSlot)
else:
Gen.register_method(ClassName, FullMethodName)
if IsCVTable: if IsCVTable:
Gen.class_vtable.add(ClassName) Gen.class_vtable.add(ClassName)
Gen._generate_structs() Gen._generate_structs()
Gen._create_Vtable_globals() Gen._create_Vtable_globals()
if _local_flat_complete:
# 登记"该表已按父类展平":供 _TryLoadClassMembersFromPyi 的 guard 与
# FuncGen._member_base 的查询前补齐使用(与跨模块路径同一口径)。
Gen._members_flattened.add(ClassName)
# _create_Vtable_globals 会跳过正在发射中的类(方法列表可能不完整), # _create_Vtable_globals 会跳过正在发射中的类(方法列表可能不完整),
# 当前类的方法已全部注册(L559-571),此处显式创建其 vtable # 当前类的方法已全部注册(L559-571),此处显式创建其 vtable
if IsCVTable: if IsCVTable:
@@ -1333,11 +1385,10 @@ class ClassHandle(BaseHandle):
if union_member_types: if union_member_types:
max_size: int = 0 max_size: int = 0
for member_type in union_member_types: for member_type in union_member_types:
try: # 统一走 Gen._get_struct_size(与 __sizeof__ / memhub alloc 同源)。
size: int = member_type.get_abi_size(ir.DataLayout(Gen.module.data_layout)) # llvmlite 并无 ir.DataLayout 类,原先那行必然抛 AttributeError,
except Exception as e: # 回退:获取类型大小失败时使用默认值 8 # 再被 except 吞成 size=8 —— 静默少算,union 载荷区被开得过小。
_vlog().warning(f"HandlesClassDef: 忽略异常 {e}", exc_info=e) size: int = Gen._get_struct_size(member_type)
size = 8
max_size = max(max_size, size) max_size = max(max_size, size)
union_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName) union_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName)
union_type.set_body(ir.ArrayType(ir.IntType(8), max_size)) union_type.set_body(ir.ArrayType(ir.IntType(8), max_size))
@@ -1498,19 +1549,94 @@ class ClassHandle(BaseHandle):
# 更新预注册的 REnum 类型信息(填充变体列表) # 更新预注册的 REnum 类型信息(填充变体列表)
PreTypeInfo.RenumVariants = variant_names PreTypeInfo.RenumVariants = variant_names
def _RegisterEnumMembers(self, Node: ast.ClassDef) -> None: def _EnumMemberAnnotationIsPtr(self, annotation: ast.AST | None) -> bool:
ClassName: str = Node.name """枚举成员注解是否含指针形态(逐层下标 + 联合类型逐边),True=含指针
EnumTypeInfo: CTypeInfo = CTypeInfo()
EnumTypeInfo.Name = ClassName 与 TPV 侧 ClassGenEnum._enum_member_annotation_is_ptr 同口径。枚举值是
EnumTypeInfo.BaseType = t.CEnum(ClassName) 整数常量,指针成员语义不成立,必须明确报错(fail-fast)而非静默接受:
EnumTypeInfo.IsEnum = True 此前 TPC 完全忽略成员注解,`Name: t.CPtr` 会被当成普通 i32 成员放行。
指针标记既按名字匹配(含 str/bytes = i8*、rawptr = t.CPtr),也按
from t import CPtr as P 这类别名经 _t_type_symbols 还原后判定。
"""
if annotation is None:
return False
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return (self._EnumMemberAnnotationIsPtr(annotation.left)
or self._EnumMemberAnnotationIsPtr(annotation.right))
if isinstance(annotation, ast.Subscript):
return self._EnumMemberAnnotationIsPtr(annotation.value)
name: str | None = None
if isinstance(annotation, ast.Attribute):
name = annotation.attr
elif isinstance(annotation, ast.Name):
name = annotation.id
if name is None:
return False
if name in _ENUM_PTR_ANNOTATION_NAMES:
return True
symtab: object = self.Trans.SymbolTable
resolved: type | None = None
if hasattr(symtab, '_t_type_symbols'):
resolved = symtab._t_type_symbols.get(name)
if resolved is None:
resolved = getattr(t, name, None)
if isinstance(resolved, type) and issubclass(resolved, t.CPtr):
return True
return False
def _EnumClassIntWidth(self, Node: ast.ClassDef) -> int:
"""按"最宽成员类型"推导枚举存储宽度(bit)。
规则本体唯一:SymbolUtils.EnumClassIntWidth(定义端 / 跨模块 `.vpi` 读取端 /
符号表提取端 / 声明端共用同一函数),此处只注入本端 SymbolTable
(覆盖库内自定义 typedef 与 builtins 表)。无任何可解析为整数的成员
→ 默认 32——既有枚举全部落在此支,与改前的恒 i32 逐字一致(零行为变化)。
"""
return EnumClassIntWidth(Node.body, self.Trans.SymbolTable)
def _EnumSiteLoc(self, Node: ast.AST) -> str:
"""枚举 fail-fast 站点:`文件:行[:列]`(行号经 `_line_offset` 还原)+ 源码片段。
口径与 `_FailFastFixedArrayKeyword` / `SymbolUtils._DefineSiteInfo` 一致。
"""
Gen: object = getattr(self.Trans, 'LlvmGen', None)
lineno: int = int(getattr(Node, 'lineno', 0) or 0)
Offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if Offset > 0 and lineno > Offset:
lineno -= Offset
col: int = int(getattr(Node, 'col_offset', -1))
Loc: str = getattr(Gen, '_current_source_file', '') or ''
Loc += f":{lineno}"
if col >= 0:
Loc += f":{col}"
Source: str = ''
if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'):
try:
Source = (Gen._get_source_line(lineno) or '').strip()
except Exception:
Source = ''
return Loc + (f",源码: {Source}" if Source else '')
def _RegisterEnumMembers(self, Node: ast.ClassDef) -> None:
ClassName: str = Node.name
# 枚举存储宽度 = 最宽成员类型(先推导再登记;成员注册顺序无关)。
# 登记到枚举条目与每个成员条目上,供常量发射点与类型解析点同点读取
# (两侧 ABI:常量宽度必须与枚举类型宽度一致)。
EnumWidth: int = self._EnumClassIntWidth(Node)
EnumTypeInfo: CTypeInfo = CTypeInfo()
EnumTypeInfo.Name = ClassName
EnumTypeInfo.BaseType = t.CEnum(ClassName)
EnumTypeInfo.IsEnum = True
EnumTypeInfo.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(ClassName, EnumTypeInfo) self.Trans.SymbolTable.insert(ClassName, EnumTypeInfo)
enum_export = self.Trans.Exportable.add_enum( enum_export = self.Trans.Exportable.add_enum(
name=ClassName, name=ClassName,
lineno=Node.lineno, lineno=Node.lineno,
is_public=True is_public=True
) )
next_enum_value: int = 0 next_enum_value: int = 0
# 已登记成员名(用于"可见名数 vs 登记数"兜底判据,见 SymbolUtils.EnumBodyMemberNames)
RegisteredNames: list[str] = []
for item in Node.body: for item in Node.body:
if isinstance(item, ast.Assign): if isinstance(item, ast.Assign):
for target in item.targets: for target in item.targets:
@@ -1541,7 +1667,9 @@ class ClassHandle(BaseHandle):
MemberNode.EnumName = ClassName MemberNode.EnumName = ClassName
MemberNode.Lineno = item.lineno MemberNode.Lineno = item.lineno
MemberNode.IsEnumMember = True MemberNode.IsEnumMember = True
MemberNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(VarName, MemberNode) self.Trans.SymbolTable.insert(VarName, MemberNode)
RegisteredNames.append(VarName)
enum_export.members.append(ExportEnumMember( enum_export.members.append(ExportEnumMember(
name=VarName, name=VarName,
value=value, value=value,
@@ -1550,6 +1678,19 @@ class ClassHandle(BaseHandle):
elif isinstance(item, ast.AnnAssign): elif isinstance(item, ast.AnnAssign):
if isinstance(item.target, ast.Name): if isinstance(item.target, ast.Name):
VarName = item.target.id VarName = item.target.id
# [枚举规格 fail-fast] 成员不允许指针类型(t.CPtr 家族 /
# rawptr[...] / str / bytes / 联合里夹带指针)——此前成员注解
# 被整体忽略,指针成员被静默当 i32 放行。
if self._EnumMemberAnnotationIsPtr(item.annotation):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(item, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise TypeError(
f"[fast-fail] 枚举 '{ClassName}' 成员 '{VarName}' 不能是指针类型"
f"(枚举值只支持整数成员类型),line {_lineno}"
)
value = None value = None
if item.value: if item.value:
if isinstance(item.value, ast.Constant): if isinstance(item.value, ast.Constant):
@@ -1575,12 +1716,33 @@ class ClassHandle(BaseHandle):
MemberNode.EnumName = ClassName MemberNode.EnumName = ClassName
MemberNode.Lineno = item.lineno MemberNode.Lineno = item.lineno
MemberNode.IsEnumMember = True MemberNode.IsEnumMember = True
MemberNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(VarName, MemberNode) self.Trans.SymbolTable.insert(VarName, MemberNode)
RegisteredNames.append(VarName)
enum_export.members.append(ExportEnumMember( enum_export.members.append(ExportEnumMember(
name=VarName, name=VarName,
value=value, value=value,
lineno=item.lineno lineno=item.lineno
)) ))
# [兜底判据 fail-fast] "源码里可见的成员名字数" vs "实际登记成员数"。
# 任何注册器不认的成员写法(逗号组带值 `A, B = 1`、逗号组带注解
# `A, B: T`、单名尾随逗号 `A,`,以及未来出现的新形态)都会让可见名数 >
# 登记数——此前是**静默丢成员**(ASTKind 71→3、OpKind 32→2;与
# Debugger/enum-semantics.md §15、rawptr-migration-and-fastfail.md §27
# 同族,这是第三次复发),此处一律 fail-fast,不再静默跳过。
# 判据口径与 TPV ClassGenEnum._translate_enum_def 完全一致(同一规则源)。
Visible: list[tuple[str, object]] = EnumBodyMemberNames(Node.body)
if len(Visible) != len(RegisteredNames):
Rest: list[str] = list(RegisteredNames)
BadNode: object = Node
for Nm, Nd in Visible:
if Nm in Rest:
Rest.remove(Nm)
elif BadNode is Node:
BadNode = Nd
raise TypeError(EnumMemberCountMismatchError(
ClassName, Visible, RegisteredNames, self._EnumSiteLoc(BadNode),
int(getattr(self.Trans, '_line_offset', 0) or 0)))
def _EmitNewFunctionLlvm(self, ClassName: str, Gen: LlvmGeneratorMixin, IsCVTable: bool = False, IsCpythonObject: bool = False) -> None: def _EmitNewFunctionLlvm(self, ClassName: str, Gen: LlvmGeneratorMixin, IsCVTable: bool = False, IsCpythonObject: bool = False) -> None:
NewFuncName: str = f'{ClassName}.__before_init__' NewFuncName: str = f'{ClassName}.__before_init__'
+88 -19
View File
@@ -9,6 +9,19 @@ import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle from lib.core.Handles.HandlesBase import BaseHandle
from lib.constants.config import mode as _config_mode from lib.constants.config import mode as _config_mode
from lib.includes.t import CTypeRegistry as _CR from lib.includes.t import CTypeRegistry as _CR
from lib.core.SymbolUtils import RequireDefineUseWidthAt, RequireIntLiteralStorable, RequireNameDefined
def DefineUseWidthFromLLVMType(Ty: object) -> int | None:
"""LLVM 类型 → CDefine 常量的"使用处位宽"提示;其余 → None。
只认 i8/i16/i32/i64(与 SymbolUtils.DEFINE_WIDTH_LADDER 同集合):
i1(比较/逻辑结果)与指针不构成"整数使用处宽度",必须落回"取不到 ⇒
默认 32/64",否则 `C == 0` 之类会拿到位宽 1 而误报"使用处宽度不足"。
"""
if isinstance(Ty, ir.IntType) and Ty.width in (8, 16, 32, 64):
return int(Ty.width)
return None
class ExprHandle(BaseHandle): class ExprHandle(BaseHandle):
@@ -45,14 +58,18 @@ class ExprHandle(BaseHandle):
def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None:
return self.Trans.ExprAttrHandle._get_llvm_member_offset(field_name, ClassName, Gen) return self.Trans.ExprAttrHandle._get_llvm_member_offset(field_name, ClassName, Gen)
def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None) -> ir.Value | None: def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None,
ConstWidthHint: int | None = None) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if not Gen or not Gen.builder: if not Gen or not Gen.builder:
return None return None
if isinstance(Node, ast.Constant): if isinstance(Node, ast.Constant):
return self._HandleConstantLlvm(Node, VarType) return self._HandleConstantLlvm(Node, VarType)
elif isinstance(Node, ast.Name): elif isinstance(Node, ast.Name):
return self._HandleNameLlvm(Node, VarType) # ConstWidthHint 是**窄通道**:只有 Name(CDefine 常量物化)读取它,
# 其余分支(字面量/列表/运算…)逐字沿用既有 VarType 语义 ⇒ 非常量
# 路径的既有 IR 不变。
return self._HandleNameLlvm(Node, VarType, ConstWidthHint)
elif isinstance(Node, ast.BinOp): elif isinstance(Node, ast.BinOp):
return self.Trans.ExprOpsHandle._HandleBinOpLlvm(Node, VarType) return self.Trans.ExprOpsHandle._HandleBinOpLlvm(Node, VarType)
elif isinstance(Node, ast.BoolOp): elif isinstance(Node, ast.BoolOp):
@@ -161,25 +178,30 @@ class ExprHandle(BaseHandle):
elif elem_type == 'double': elif elem_type == 'double':
byte_offset = i * 8 byte_offset = i * 8
ptr_as_int: Any = Gen.builder.ptrtoint(cast_ptr, ir.IntType(64)) # 元素地址:以 i8* 基址按字节 GEP,再转成元素指针类型
new_ptr_val: Any = Gen.builder.add(ptr_as_int, ir.Constant(ir.IntType(64), byte_offset)) # (不再 ptr→uint64 手动加偏移再 inttoptr,见新规范第 10 条)
elem_addr: Any = Gen.builder.gep(cast_ptr, [ir.Constant(ir.IntType(64), byte_offset)], name=f"elem_addr_{i}")
if elem_type == 'i32': if elem_type == 'i32':
elem_ptr: Any = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(32))) elem_ptr: Any = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(32)))
elif elem_type == 'i16': elif elem_type == 'i16':
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(16))) elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(16)))
elif elem_type == 'float': elif elem_type == 'float':
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.FloatType())) elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.FloatType()))
elif elem_type == 'double': elif elem_type == 'double':
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.DoubleType())) elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.DoubleType()))
else: else:
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(8))) elem_ptr = elem_addr
if elem_type == 'i8': if elem_type == 'i8':
if isinstance(elem_val.type, ir.IntType) and elem_val.type.width > 8: if isinstance(elem_val.type, ir.IntType) and elem_val.type.width > 8:
elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}") elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}")
elif isinstance(elem_val.type, ir.PointerType): elif isinstance(elem_val.type, ir.PointerType):
elem_val = Gen.builder.ptrtoint(elem_val, ir.IntType(64), name=f"ptrtoint_elem_{i}") # 指针 → 整数:隐式转换一律禁止(元素值是指针、元素类型是整数)
elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}") from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type},"
f"但目标元素类型是 i8;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif elem_type == 'i32': elif elem_type == 'i32':
if isinstance(elem_val.type, ir.IntType): if isinstance(elem_val.type, ir.IntType):
if elem_val.type.width < 32: if elem_val.type.width < 32:
@@ -187,7 +209,12 @@ class ExprHandle(BaseHandle):
elif elem_val.type.width > 32: elif elem_val.type.width > 32:
elem_val = Gen.builder.trunc(elem_val, ir.IntType(32), name=f"trunc_elem_{i}") elem_val = Gen.builder.trunc(elem_val, ir.IntType(32), name=f"trunc_elem_{i}")
elif isinstance(elem_val.type, ir.PointerType): elif isinstance(elem_val.type, ir.PointerType):
elem_val = Gen.builder.ptrtoint(elem_val, ir.IntType(32), name=f"ptrtoint_elem_{i}") # 指针 → 整数:隐式转换一律禁止(ptrtoint 到 i32 还会截断地址)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type},"
f"但目标元素类型是 i32;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif elem_type == 'i16': elif elem_type == 'i16':
if isinstance(elem_val.type, ir.IntType): if isinstance(elem_val.type, ir.IntType):
if elem_val.type.width < 16: if elem_val.type.width < 16:
@@ -221,6 +248,10 @@ class ExprHandle(BaseHandle):
elif isinstance(Value, bool): elif isinstance(Value, bool):
return ir.Constant(ir.IntType(32), 1 if Value else 0) return ir.Constant(ir.IntType(32), 1 if Value else 0)
elif isinstance(Value, int): elif isinstance(Value, int):
# 整数字面量越界 fail-fast(字面量本身无符号,0..2^64-1):与 TPV 词法层
# `_lex_int_exceeds_u64` 同口径。负数侧由"一元减 + 字面量"的联合判定负责
# (HandlesExprOps._HandleUnaryOpLlvm),此处只判字面量本身。
RequireIntLiteralStorable(Gen, self.Trans, Node, Value)
if VarType is not None: if VarType is not None:
if isinstance(VarType, ir.IntType): if isinstance(VarType, ir.IntType):
return ir.Constant(VarType, Value & ((1 << VarType.width) - 1)) return ir.Constant(VarType, Value & ((1 << VarType.width) - 1))
@@ -327,7 +358,8 @@ class ExprHandle(BaseHandle):
return ptr return ptr
return ir.Constant(ir.IntType(32), 0) return ir.Constant(ir.IntType(32), 0)
def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None) -> ir.Value: def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None,
ConstWidthHint: int | None = None) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen Gen: LlvmCodeGenerator = self.Trans.LlvmGen
VarName: str = Node.id VarName: str = Node.id
# 治本修复:不把 module.globals 的 initializer 注入 _define_constants。 # 治本修复:不把 module.globals 的 initializer 注入 _define_constants。
@@ -343,6 +375,9 @@ class ExprHandle(BaseHandle):
# 跳过 _define_constants 查询,直接走下方 variables/global_vars/module.globals 的 load。 # 跳过 _define_constants 查询,直接走下方 variables/global_vars/module.globals 的 load。
if VarName in define_constants and not (hasattr(Gen, 'module') and VarName in Gen.module.globals): if VarName in define_constants and not (hasattr(Gen, 'module') and VarName in Gen.module.globals):
val: Any = define_constants[VarName] val: Any = define_constants[VarName]
# CDefine 常量注册值(用于使用处宽度判据;下方 _gvar 覆盖是"同名全局变量"
# 的既有特例,不应把变量值当常量做宽度判据)
_cdefine_val: Any = val
# 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过 # 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过
# 共享 _define_constants 覆盖本模块常量 # 共享 _define_constants 覆盖本模块常量
# (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug) # (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug)
@@ -357,9 +392,28 @@ class ExprHandle(BaseHandle):
if isinstance(_init, ir.Constant) and isinstance(_init.constant, (int, float, str)): if isinstance(_init, ir.Constant) and isinstance(_init.constant, (int, float, str)):
val = _init.constant val = _init.constant
if isinstance(val, int): if isinstance(val, int):
# Use VarType hint if available to avoid unnecessary type mismatch # 使用处宽度(唯一判据 SymbolUtils.RequireDefineUseWidth):
if VarType is not None and isinstance(VarType, ir.IntType): # 上下文(赋值目标 / 形参 / 返回注解 / 运算对象…)给出的位宽更宽 ⇒
return ir.Constant(VarType, val & ((1 << VarType.width) - 1)) # 就地按该宽度物化(立即数升宽无损);更窄 ⇒ fail-fast(替换此前的
# `val & mask` 静默截断)。
# 宽度来源优先级:**窄通道 ConstWidthHint**(仅常量物化读取,由调用
# 装配处按形参/返回类型下传)> 既有尽宽通道 VarType(赋值目标 /
# 二元运算对象)。两者都取不到 ⇒ 落到下方既有 32/64 二值口径。
_use_ty: ir.Type | None = None
# 报文的"使用处"标签按**通道**取真实站点种类(不再写笼统的
# "由调用方下传的类型提示"):窄通道 ConstWidthHint 只由
# 调用装配处(形参)与返回处下传;既有 VarType 通道承载的是
# 赋值目标 / 二元运算对象类型。
_use_ctx: str = '赋值目标 / 运算对象类型'
if ConstWidthHint in (8, 16, 32, 64):
_use_ty = ir.IntType(int(ConstWidthHint))
_use_ctx = '形参 / 返回注解'
elif VarType is not None and isinstance(VarType, ir.IntType):
_use_ty = VarType
if isinstance(_use_ty, ir.IntType):
RequireDefineUseWidthAt(Gen, self.Trans, Node, _cdefine_val,
int(_use_ty.width), VarName, _use_ctx)
return ir.Constant(_use_ty, val & ((1 << _use_ty.width) - 1))
if VarName in Gen.var_signedness and Gen.var_signedness[VarName]: if VarName in Gen.var_signedness and Gen.var_signedness[VarName]:
if val < -(1 << 31) or val > 0xFFFFFFFF: if val < -(1 << 31) or val > 0xFFFFFFFF:
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF) return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
@@ -384,8 +438,20 @@ class ExprHandle(BaseHandle):
if VarName in all_dc: if VarName in all_dc:
val: Any = all_dc[VarName] val: Any = all_dc[VarName]
if isinstance(val, int): if isinstance(val, int):
if VarType is not None and isinstance(VarType, ir.IntType): # 跨模块 CDefine 常量同口径:窄通道 ConstWidthHint(形参/返回
return ir.Constant(VarType, val & ((1 << VarType.width) - 1)) # 类型下传)优先,其次既有 VarType;更宽 ⇒ 按该宽度物化,
# 更窄 ⇒ fail-fast(判据唯一源 SymbolUtils.RequireDefineUseWidth)
_use_ty2: ir.Type | None = None
_use_ctx2: str = '赋值目标 / 运算对象类型'
if ConstWidthHint in (8, 16, 32, 64):
_use_ty2 = ir.IntType(int(ConstWidthHint))
_use_ctx2 = '形参 / 返回注解'
elif VarType is not None and isinstance(VarType, ir.IntType):
_use_ty2 = VarType
if isinstance(_use_ty2, ir.IntType):
RequireDefineUseWidthAt(Gen, self.Trans, Node, val,
int(_use_ty2.width), VarName, _use_ctx2)
return ir.Constant(_use_ty2, val & ((1 << _use_ty2.width) - 1))
if val < -(1 << 31) or val > 0xFFFFFFFF: if val < -(1 << 31) or val > 0xFFFFFFFF:
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF) return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF) return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF)
@@ -460,7 +526,7 @@ class ExprHandle(BaseHandle):
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
if SymInfo: if SymInfo:
if SymInfo.IsEnumMember and isinstance(SymInfo.value, int): if SymInfo.IsEnumMember and isinstance(SymInfo.value, int):
return ir.Constant(ir.IntType(32), SymInfo.value) return ir.Constant(Gen._enum_int_type(SymInfo.EnumName, SymInfo), SymInfo.value)
if SymInfo.IsFunction or SymInfo.IsFuncPtr: if SymInfo.IsFunction or SymInfo.IsFuncPtr:
MangledName: str = Gen._mangle_func_name(VarName) MangledName: str = Gen._mangle_func_name(VarName)
if MangledName in Gen.module.globals: if MangledName in Gen.module.globals:
@@ -487,4 +553,7 @@ class ExprHandle(BaseHandle):
) )
ast.copy_location(virtual_attr, Node) ast.copy_location(virtual_attr, Node)
return self.Trans.ExprAttrHandle._HandleAttributeLlvm(virtual_attr) return self.Trans.ExprAttrHandle._HandleAttributeLlvm(virtual_attr)
# fail-fast: 未定义名称禁止静默物化为 i32 0(会把常量名写错 / define 丢失 /
# 变量漏声明这类上游缺陷藏到运行期,见 RequireNameDefined 的头注释)。
RequireNameDefined(Gen, self.Trans, Node, VarName)
return ir.Constant(ir.IntType(32), 0) return ir.Constant(ir.IntType(32), 0)
+93 -65
View File
@@ -201,6 +201,34 @@ class ExprAsmHandle(BaseHandle):
stripped.append('') stripped.append('')
return '\n'.join([first_line] + stripped) return '\n'.join([first_line] + stripped)
@staticmethod
def _intrinsic_marker(expr: ast.AST) -> str | None:
"""识别内建操作数标记调用,返回规范名或 None
支持两种等价写法(裸名写法省去 c. 前缀):
- 内联汇编:c.AsmInp / c.AsmOut ↔ asminp / asmout
- LLVM IR:c.LInp / c.LOut ↔ irinp / irout
"""
if not isinstance(expr, ast.Call):
return None
func: ast.AST = expr.func
if isinstance(func, ast.Name):
if func.id == 'asminp':
return 'AsmInp'
if func.id == 'asmout':
return 'AsmOut'
if func.id == 'irinp':
return 'LInp'
if func.id == 'irout':
return 'LOut'
return None
if isinstance(func, ast.Attribute):
if isinstance(func.value, ast.Name) and func.value.id == 'c':
if func.attr in ('AsmInp', 'AsmOut', 'LInp', 'LOut'):
return func.attr
return None
return None
def _process_joined_str_template(self, node: ast.AST, out_input_ops: list[Any], out_input_constraints: list[str], def _process_joined_str_template(self, node: ast.AST, out_input_ops: list[Any], out_input_constraints: list[str],
out_output_ops: list[Any], out_output_constraints: list[str]) -> str: out_output_ops: list[Any], out_output_constraints: list[str]) -> str:
if not isinstance(node, ast.JoinedStr): if not isinstance(node, ast.JoinedStr):
@@ -214,39 +242,40 @@ class ExprAsmHandle(BaseHandle):
raw_parts.append(('text', value.value)) raw_parts.append(('text', value.value))
elif isinstance(value, ast.FormattedValue): elif isinstance(value, ast.FormattedValue):
expr: ast.AST = value.value expr: ast.AST = value.value
if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute): _marker: str | None = self._intrinsic_marker(expr)
if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c': if _marker == 'AsmInp':
if expr.func.attr == 'AsmInp': if expr.args:
if expr.args: v: Any = self.HandleExprLlvm(expr.args[0])
v: Any = self.HandleExprLlvm(expr.args[0]) if v:
if v: c: str = 'r'
c: str = 'r' if len(expr.args) >= 2:
if len(expr.args) >= 2: c = self._parse_asm_descr(expr.args[1])
c = self._parse_asm_descr(expr.args[1]) if not c:
if not c: c = 'r'
c = 'r' out_input_ops.append(v)
out_input_ops.append(v) out_input_constraints.append(c)
out_input_constraints.append(c) raw_parts.append(('input', len(out_input_ops) - 1))
raw_parts.append(('input', len(out_input_ops) - 1)) elif _marker == 'AsmOut':
elif expr.func.attr == 'AsmOut': if expr.args:
if expr.args: target_ptr: Any = self._get_var_ptr_for_asm(expr.args[0])
target_ptr: Any = self._get_var_ptr_for_asm(expr.args[0]) if target_ptr:
if target_ptr: v = target_ptr
v = target_ptr else:
else: v = self.HandleExprLlvm(expr.args[0])
v = self.HandleExprLlvm(expr.args[0]) if v:
if v: c: str = '=r'
c: str = '=r' if len(expr.args) >= 2:
if len(expr.args) >= 2: c = self._parse_asm_descr(expr.args[1])
c = self._parse_asm_descr(expr.args[1]) if not c:
if not c: c = '=r'
c = '=r' if not c.startswith('='):
if not c.startswith('='): c = '=' + c
c = '=' + c out_output_ops.append(v)
out_output_ops.append(v) out_output_constraints.append(c)
out_output_constraints.append(c) raw_parts.append(('output', len(out_output_ops) - 1))
raw_parts.append(('output', len(out_output_ops) - 1)) elif not (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute)):
else: # 非 c.* 属性调用的表达式按普通输入操作数处理(保持原行为:
# c.* 属性调用中非 AsmInp/AsmOut 的形态不收集)
fallback: Any = self.HandleExprLlvm(expr) fallback: Any = self.HandleExprLlvm(expr)
if fallback and isinstance(fallback.type, ir.IntType): if fallback and isinstance(fallback.type, ir.IntType):
out_input_ops.append(fallback) out_input_ops.append(fallback)
@@ -316,25 +345,24 @@ class ExprAsmHandle(BaseHandle):
if output_arg and isinstance(output_arg, (ast.List, ast.Tuple)): if output_arg and isinstance(output_arg, (ast.List, ast.Tuple)):
for item in output_arg.elts: for item in output_arg.elts:
if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute): if self._intrinsic_marker(item) == 'AsmOut':
if item.func.value.id == 'c' and item.func.attr == 'AsmOut': if len(item.args) >= 1:
if len(item.args) >= 1: target_ptr: Any = self._get_var_ptr_for_asm(item.args[0])
target_ptr: Any = self._get_var_ptr_for_asm(item.args[0]) if target_ptr:
if target_ptr: output_ops.append(target_ptr)
output_ops.append(target_ptr) else:
else: value: Any = self.HandleExprLlvm(item.args[0])
value: Any = self.HandleExprLlvm(item.args[0]) if value:
if value: output_ops.append(value)
output_ops.append(value) constraint: str = '=r'
constraint: str = '=r' if len(item.args) >= 2:
if len(item.args) >= 2: base_constraint: str = self._parse_asm_descr(item.args[1])
base_constraint: str = self._parse_asm_descr(item.args[1]) if base_constraint:
if base_constraint: if base_constraint.startswith('='):
if base_constraint.startswith('='): constraint = base_constraint
constraint = base_constraint else:
else: constraint = '=' + base_constraint
constraint = '=' + base_constraint output_constraints.append(constraint)
output_constraints.append(constraint)
raw_template: str raw_template: str
if isinstance(asm_template_arg, ast.JoinedStr): if isinstance(asm_template_arg, ast.JoinedStr):
@@ -360,18 +388,17 @@ class ExprAsmHandle(BaseHandle):
if input_arg and isinstance(input_arg, (ast.List, ast.Tuple)): if input_arg and isinstance(input_arg, (ast.List, ast.Tuple)):
for item in input_arg.elts: for item in input_arg.elts:
if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute): if self._intrinsic_marker(item) == 'AsmInp':
if item.func.value.id == 'c' and item.func.attr == 'AsmInp': if len(item.args) >= 1:
if len(item.args) >= 1: value: Any = self.HandleExprLlvm(item.args[0])
value: Any = self.HandleExprLlvm(item.args[0]) if value:
if value: input_ops.append(value)
input_ops.append(value) constraint: str = 'r'
constraint: str = 'r' if len(item.args) >= 2:
if len(item.args) >= 2: constraint = self._parse_asm_descr(item.args[1])
constraint = self._parse_asm_descr(item.args[1]) if not constraint:
if not constraint: constraint = 'r'
constraint = 'r' input_constraints.append(constraint)
input_constraints.append(constraint)
elif isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2: elif isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2:
value_node: ast.AST = item.elts[0] value_node: ast.AST = item.elts[0]
descr_node: ast.AST = item.elts[1] descr_node: ast.AST = item.elts[1]
@@ -565,7 +592,8 @@ class ExprAsmHandle(BaseHandle):
expr.value.attr == 'ASM_DESCR'): expr.value.attr == 'ASM_DESCR'):
AttrName: str = expr.attr AttrName: str = expr.attr
return getattr(t.ASM_DESCR, AttrName, "") return getattr(t.ASM_DESCR, AttrName, "")
elif isinstance(expr.value, ast.Name) and IsTModule(expr.value.id, self.Trans.SymbolTable): elif isinstance(expr.value, ast.Name) and (expr.value.id == 'ASM_DESCR' or IsTModule(expr.value.id, self.Trans.SymbolTable)):
# 裸名 ASM_DESCR.XXX:省去 t. 前缀的等价写法
AttrName: str = expr.attr AttrName: str = expr.attr
return getattr(t.ASM_DESCR, AttrName, "") return getattr(t.ASM_DESCR, AttrName, "")
+63 -29
View File
@@ -199,6 +199,32 @@ class ExprAttrHandle(BaseHandle):
return self._make_define_constant(Gen, define_constants[key]) return self._make_define_constant(Gen, define_constants[key])
return None return None
def _NormalizeModuleGlobalValue(self, Gen: LlvmCodeGenerator, Val: Any, AttrName: str) -> Any:
"""模块限定全局读取归一化:类指针全局的「多一层」元素类型还原为对象指针
`Mod.g`(g 为模块级全局)的取值统一经 _lookup_module_global:注解为
`X | t.CPtr` 的模块级全局(如 GenApi._g_gen: rawptr[Generator.Generator]、
VLogger.logger: Logger | t.CPtr)在本编译器里物化成「比对象多一层」的
元素类型 T**(写入端见 main.vp `GenApi._g_gen = gen`:bitcast T*→T** 后
直接存值,未取地址)⇒ 其位形就是对象指针 T*,静态类型多了一层。
裸名形态(`g.Field`)由 _handle_attr_name 的既有同款补偿 bitcast 处理;
模块限定形态(`Mod.g.Field`)此前无补偿 ⇒ 载入值停在 T**,其 pointee 是
T*(指针而非结构体)⇒ _handle_attr_nested 的 char* 分支与 struct 分支都
不命中而返回 None ⇒ 带注解赋值 RHS 求值为空 fail-fast
(App/lib/core/Repl.vp:763 `GenApi._g_gen.TypeGenCDef.CDefineNames`)、
调用接收者 None 而静默丢调用(VpiReader.vp:4066
`GenApi._g_gen.ClassGenBase._collect_parents(...)`)。此处做同款逆运算:
只改静态类型、不改位形(bitcast 不产指令)。
判据限定「pointee 仍是指向结构体的指针」⇒ `str | t.CPtr` 一类真双层指针
全局(@_argv / @_g_pyi_cdef_names = i8**,实参直传 i8** 形参)不受影响。
"""
if not isinstance(Val, ir.Value) or not isinstance(Val.type, ir.PointerType):
return Val
Inner: Any = Val.type.pointee
if isinstance(Inner, ir.PointerType) and isinstance(Inner.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
return Gen.builder.bitcast(Val, Inner, name=f"cast_{AttrName}_global")
return Val
def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None:
"""在模块全局变量中查找 import 的属性""" """在模块全局变量中查找 import 的属性"""
imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) imported_modules: Any = getattr(self.Trans, '_ImportedModules', None)
@@ -220,7 +246,7 @@ class ExprAttrHandle(BaseHandle):
return GVar return GVar
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar return GVar
return Gen._load(GVar, name=AttrName) return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar, name=AttrName), AttrName)
# SHA1 前缀查找 # SHA1 前缀查找
ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {})
sha1_candidates: list[str] = [VarName, resolved_mod] sha1_candidates: list[str] = [VarName, resolved_mod]
@@ -238,7 +264,7 @@ class ExprAttrHandle(BaseHandle):
return GVar2 return GVar2
if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar2 return GVar2
return Gen._load(GVar2, name=AttrName) return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar2, name=AttrName), AttrName)
if AttrName in Gen.module.globals: if AttrName in Gen.module.globals:
gv: Any = Gen.module.globals[AttrName] gv: Any = Gen.module.globals[AttrName]
if AttrName in Gen.variables and Gen.variables[AttrName] is None: if AttrName in Gen.variables and Gen.variables[AttrName] is None:
@@ -246,7 +272,7 @@ class ExprAttrHandle(BaseHandle):
if str_gv_name in Gen.module.globals: if str_gv_name in Gen.module.globals:
str_gv: Any = Gen.module.globals[str_gv_name] str_gv: Any = Gen.module.globals[str_gv_name]
return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr")
return Gen.builder.load(gv, name=AttrName) return self._NormalizeModuleGlobalValue(Gen, Gen.builder.load(gv, name=AttrName), AttrName)
return None return None
def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None:
@@ -373,6 +399,7 @@ class ExprAttrHandle(BaseHandle):
def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
"""处理枚举成员访问 (VarName 是枚举类型名)""" """处理枚举成员访问 (VarName 是枚举类型名)"""
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
if not SymInfo or not SymInfo.IsEnum: if not SymInfo or not SymInfo.IsEnum:
return None return None
@@ -387,10 +414,10 @@ class ExprAttrHandle(BaseHandle):
for key, info in self.Trans.SymbolTable.items(): for key, info in self.Trans.SymbolTable.items():
if info.IsEnumMember and info.EnumName == VarName and key == Node.attr: if info.IsEnumMember and info.EnumName == VarName and key == Node.attr:
if isinstance(info.value, int): if isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value) return ir.Constant(Gen._enum_int_type(VarName, info), info.value)
return None return None
if isinstance(MemberInfo.value, int): if isinstance(MemberInfo.value, int):
return ir.Constant(ir.IntType(32), MemberInfo.value) return ir.Constant(Gen._enum_int_type(VarName, MemberInfo), MemberInfo.value)
return None return None
def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
@@ -520,12 +547,12 @@ class ExprAttrHandle(BaseHandle):
for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"): for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"):
info: Any = self.Trans.SymbolTable.lookup(qname) info: Any = self.Trans.SymbolTable.lookup(qname)
if info and info.IsEnumMember and isinstance(info.value, int): if info and info.IsEnumMember and isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value) return ir.Constant(Gen._enum_int_type(ClassName, info), info.value)
for key, info in self.Trans.SymbolTable.items(): for key, info in self.Trans.SymbolTable.items():
if key == AttrName: if key == AttrName:
if info.IsEnumMember and info.EnumName == ClassName: if info.IsEnumMember and info.EnumName == ClassName:
if isinstance(info.value, int): if isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value) return ir.Constant(Gen._enum_int_type(ClassName, info), info.value)
break break
# CUnion 处理 # CUnion 处理
@@ -1105,6 +1132,11 @@ class ExprAttrHandle(BaseHandle):
def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None: def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen Gen: LlvmCodeGenerator = self.Trans.LlvmGen
# pointee-volatile(rawptr[t.CVolatile | T])基址:下标 GEP 名带基址名,
# 顶层 _load/_store 经 BaseGen._IsPtrVolatile 回查登记名,使其 load/store
# 带 LLVM volatile。基址名取不到时后缀为空 ⇒ GEP 名仍是 "subscript",
# 既有代码 IR 逐字节不变。
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
# 查询数组元素字节序 # 查询数组元素字节序
arr_byte_order: str = '' arr_byte_order: str = ''
if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders: if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders:
@@ -1178,7 +1210,7 @@ class ExprAttrHandle(BaseHandle):
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name="subscript") ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8: if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8:
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
@@ -1191,7 +1223,7 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
elem_type: Any = pointee.element elem_type: Any = pointee.element
if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr return ElemPtr
@@ -1210,14 +1242,14 @@ class ExprAttrHandle(BaseHandle):
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript") ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
return Gen._load(ElemPtr, name="str_ptr_load") return Gen._load(ElemPtr, name="str_ptr_load")
elem_class: str | None = self._infer_element_struct_class(Node.value, Gen) elem_class: str | None = self._infer_element_struct_class(Node.value, Gen)
if elem_class and elem_class in Gen.structs: if elem_class and elem_class in Gen.structs:
CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr") CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index")
ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name="subscript") ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(pointee, ir.IntType): if isinstance(pointee, ir.IntType):
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
return ElemPtr return ElemPtr
@@ -1230,7 +1262,7 @@ class ExprAttrHandle(BaseHandle):
CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript") ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
return Gen._load(ElemPtr, name="str_ptr_load") return Gen._load(ElemPtr, name="str_ptr_load")
arr_type: Any = self._infer_array_member_type(Node.value, Gen) arr_type: Any = self._infer_array_member_type(Node.value, Gen)
if arr_type and isinstance(arr_type, ir.ArrayType): if arr_type and isinstance(arr_type, ir.ArrayType):
@@ -1241,12 +1273,12 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
arr_elem_type: Any = arr_type.element arr_elem_type: Any = arr_type.element
if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr return ElemPtr
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript") ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr return ElemPtr
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
@@ -1262,7 +1294,7 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
var_arr_elem_type: Any = VarPtr.type.pointee.element var_arr_elem_type: Any = VarPtr.type.pointee.element
if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr return ElemPtr
@@ -1275,7 +1307,7 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
attr_arr_elem_type: Any = AttrPtr.type.pointee.element attr_arr_elem_type: Any = AttrPtr.type.pointee.element
if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr return ElemPtr
@@ -1290,14 +1322,14 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
inner_elem_type: Any = inner_pointee.element inner_elem_type: Any = inner_pointee.element
if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr return ElemPtr
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc) Gen._store(ValueVal, arr_alloc)
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
alloc_arr_elem_type: Any = ValueVal.type.element alloc_arr_elem_type: Any = ValueVal.type.element
if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr return ElemPtr
@@ -1306,6 +1338,8 @@ class ExprAttrHandle(BaseHandle):
def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen Gen: LlvmCodeGenerator = self.Trans.LlvmGen
# 同 _HandleSubscriptLlvm:pointee-volatile 基址名后缀(空后缀 ⇒ 名不变)
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
ValueVal: Any = self.HandleExprLlvm(Node.value) ValueVal: Any = self.HandleExprLlvm(Node.value)
if not ValueVal: if not ValueVal:
return None return None
@@ -1324,12 +1358,12 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript_ptr") ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
elif isinstance(pointee, ir.PointerType): elif isinstance(pointee, ir.PointerType):
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr") ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
else: else:
# 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr) # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr)
@@ -1341,11 +1375,11 @@ class ExprAttrHandle(BaseHandle):
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr") CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript_ptr") ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr") ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
elif isinstance(ValueVal.type, ir.IntType): elif isinstance(ValueVal.type, ir.IntType):
var_ptr: Any = self._get_int_ptr(Node.value) var_ptr: Any = self._get_int_ptr(Node.value)
@@ -1367,7 +1401,7 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript_ptr") ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
if isinstance(Node.value, ast.Subscript): if isinstance(Node.value, ast.Subscript):
InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value)
@@ -1377,7 +1411,7 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript_ptr") ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
if isinstance(Node.value, ast.Attribute): if isinstance(Node.value, ast.Attribute):
AttrPtr: Any = self._get_attr_ptr(Node.value) AttrPtr: Any = self._get_attr_ptr(Node.value)
@@ -1387,11 +1421,11 @@ class ExprAttrHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript_ptr") ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc) Gen._store(ValueVal, arr_alloc)
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript_ptr") ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr return ElemPtr
return None return None
@@ -1705,17 +1739,17 @@ class ExprAttrHandle(BaseHandle):
for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"): for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"):
info: Any = self.Trans.SymbolTable.lookup(qname) info: Any = self.Trans.SymbolTable.lookup(qname)
if info and info.IsEnumMember and isinstance(info.value, int): if info and info.IsEnumMember and isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value) return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
for key, info in self.Trans.SymbolTable.items(): for key, info in self.Trans.SymbolTable.items():
if info.IsEnumMember and info.EnumName == enum_key and key == member_name: if info.IsEnumMember and info.EnumName == enum_key and key == member_name:
if isinstance(info.value, int): if isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value) return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
for key, info in self.Trans.SymbolTable.items(): for key, info in self.Trans.SymbolTable.items():
if info.IsEnumMember and key == member_name: if info.IsEnumMember and key == member_name:
if isinstance(info.value, int): if isinstance(info.value, int):
for enum_key in enum_keys: for enum_key in enum_keys:
if info.EnumName == enum_key: if info.EnumName == enum_key:
return ir.Constant(ir.IntType(32), info.value) return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
return None return None
def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None: def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None:
+13 -6
View File
@@ -727,7 +727,12 @@ class ExprBuiltinHandle(BaseHandle):
elif isinstance(Node, ast.Attribute): elif isinstance(Node, ast.Attribute):
type_name = Node.attr type_name = Node.attr
else: else:
return ir.Constant(ir.IntType(64), 0) # 非 名称/属性 的 sizeof 目标(如 sizeof(a + b)、sizeof(t.CArray[int,4])):
# 曾静默返回 0 → 下游 alloc(0) 返回 NULL。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] sizeof 的目标 {type(Node).__name__}"
f"({ast.unparse(Node)})不是类型名(Name/Attribute),拒绝静默返回 0"
)
if type_name not in Gen.structs: if type_name not in Gen.structs:
# str/bytes 是指针类型 (char*/void*),sizeof = 指针大小 # str/bytes 是指针类型 (char*/void*),sizeof = 指针大小
@@ -758,7 +763,11 @@ class ExprBuiltinHandle(BaseHandle):
if _config_mode == "strict": if _config_mode == "strict":
raise raise
_vlog().warning(f"解析类型失败: {_e}", "Exception") _vlog().warning(f"解析类型失败: {_e}", "Exception")
return ir.Constant(ir.IntType(64), 0) # 所有查找策略均失败:曾静默返回 0 → 下游 elem_size=0 / alloc(0)。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析"
f"(不在 Gen.structs、CTypeRegistry 也找不到),拒绝静默返回 0"
)
struct_type = Gen.structs[type_name] struct_type = Gen.structs[type_name]
if isinstance(struct_type, ir.PointerType): if isinstance(struct_type, ir.PointerType):
@@ -768,10 +777,8 @@ class ExprBuiltinHandle(BaseHandle):
struct_type = Gen.structs.get(type_name, struct_type) struct_type = Gen.structs.get(type_name, struct_type)
# 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确 # 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确
self._resolve_nested_opaque_structs(struct_type, Gen) self._resolve_nested_opaque_structs(struct_type, Gen)
size = Gen._get_struct_size(struct_type) # _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。
if size > 0: return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type))
return ir.Constant(ir.IntType(64), size)
return ir.Constant(ir.IntType(64), 0)
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None: def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
+495 -162
View File
@@ -14,7 +14,9 @@ from lib.includes import t
from lib.includes.t import CTypeRegistry from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import ( from lib.core.SymbolUtils import (
IsTModule, ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, IsTModule, ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion,
ResolveTypeArgStr, GenericArgRawName) ResolveTypeArgStr, GenericArgRawName,
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
def _safe_ptr_type(ty: ir.Type) -> ir.Type: def _safe_ptr_type(ty: ir.Type) -> ir.Type:
@@ -236,6 +238,71 @@ class ExprCallHandle(BaseHandle):
except (AssertionError, TypeError): except (AssertionError, TypeError):
CallArgs.append(ir.Constant(ir.IntType(32), 0)) CallArgs.append(ir.Constant(ir.IntType(32), 0))
def _LookupGlobalDefaults(self, func_name: str | None) -> list | None:
"""按函数名查询全局默认参数表(跨模块调用点用)。
查找顺序:精确键 → 去 mangled 前缀后的裸名 → 以 '.<裸名>' 结尾的键
(如 {sha1}.name)。精确键优先,避免"同名不同模块"的默认值互相乱配。
"""
if not func_name:
return None
table: dict = getattr(self.Trans, '_global_function_default_args', None) or {}
if not table:
return None
hit = table.get(func_name)
if hit:
return hit
base: str = func_name.split('.')[-1]
if base != func_name:
hit = table.get(base)
if hit:
return hit
for key, value in table.items():
if key.endswith('.' + base):
return value
return None
def _LookupGlobalParamNames(self, func_name: str | None) -> list | None:
"""按函数名查询全局参数名表;查找顺序同 _LookupGlobalDefaults。"""
if not func_name:
return None
table: dict = getattr(self.Trans, '_global_function_param_names', None) or {}
if not table:
return None
hit = table.get(func_name)
if hit:
return hit
base: str = func_name.split('.')[-1]
if base != func_name:
hit = table.get(base)
if hit:
return hit
for key, value in table.items():
if key.endswith('.' + base):
return value
return None
@staticmethod
def _ConstantToIrValue(raw: object, param_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Value | None:
"""把全局默认参数表里的 Python 常量转成形参类型的 IR 常量。
只处理整数/布尔(跨模块默认值里最常见,如 cap: t.CSizeT = 4096)。字符串等
常量需要在调用点引用定义模块的全局对象,无法安全重建 → 返回 None,调用点
退回原有零值填充(与修复前行为一致)。
"""
if isinstance(param_type, ir.IntType):
if isinstance(raw, bool):
raw = 1 if raw else 0
if isinstance(raw, int):
max_val: int = (1 << param_type.width) - 1
if raw > max_val or raw < 0:
raw = raw & max_val
return ir.Constant(param_type, raw)
return ir.Constant(param_type, 0)
if isinstance(param_type, ir.PointerType):
return ir.Constant(param_type, None)
return None
def _ApplyKeywordArgs(self, Node: ast.Call, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None, self_offset: int = 1) -> None: def _ApplyKeywordArgs(self, Node: ast.Call, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None, self_offset: int = 1) -> None:
"""在 _fill_default_args 之后调用:用 keyword args 按 param_names 覆盖 CallArgs 对应位置。 """在 _fill_default_args 之后调用:用 keyword args 按 param_names 覆盖 CallArgs 对应位置。
self_offset=1 表示 CallArgs[0] 是 self,参数从 CallArgs[1] 开始(__init__/__new__/__call__/method)。 self_offset=1 表示 CallArgs[0] 是 self,参数从 CallArgs[1] 开始(__init__/__new__/__call__/method)。
@@ -274,6 +341,108 @@ class ExprCallHandle(BaseHandle):
if 0 <= target_idx < len(CallArgs) - self_offset: if 0 <= target_idx < len(CallArgs) - self_offset:
CallArgs[self_offset + target_idx] = ArgVal CallArgs[self_offset + target_idx] = ArgVal
def _CheckFuncPtrArity(self, Node: ast.Call, FnPtr: ir.Value, Args: list[ir.Value],
FuncName: str, Gen: LlvmGeneratorMixin) -> None:
"""函数指针间接调用前的 arity 校验(fail-fast,紧邻 builder.call 之前)。
期望形参个数取自 FnPtr 的静态 LLVM 函数类型的形参表:来源是函数指针槽
(模块级/局部 t.Callable[...] 注解)的 CTypeInfo.FuncPtrParams →
CTypeInfo.ToLLVM/_ctype_to_llvm → ir.FunctionType(ret, params);变参位由
FunctionType.var_arg 承载。不校验时,实参个数少于形参个数会让 llvmlite
CallInstr 按形参个数索引 args[i] 抛裸 `IndexError: list index out of range`
(无函数名、无源位置,栈顶落在被调函数 docstring 行,无法定位真因)。
变参函数(var_arg=True,如 C printf 形态)只要求实参不少于固定形参个数:
多出的实参按变参传递,不报错;仅 len(Args) < 固定形参个数 时报错。
"""
FnType: ir.Type = FnPtr.type.pointee if isinstance(FnPtr.type, ir.PointerType) else FnPtr.type
if not isinstance(FnType, ir.FunctionType):
return
Expected: int = len(FnType.args)
Actual: int = len(Args)
IsVariadic: bool = bool(getattr(FnType, 'var_arg', False))
if IsVariadic:
if Actual >= Expected:
return
elif Actual == Expected:
return
# 源位置:按本文件既有写法取 Node 自身的 lineno/col_offset(不用
# _current_node_info/_current_lineno —— 二者可能停留在上一条语句),
# 源文件取 Gen._current_source_file,源码行取 Gen._get_source_line。
Lineno: int = getattr(Node, 'lineno', 0) or 0
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator);
# 报文行号与源码行必须同用还原后的 Lineno,二者一致(否则会显示下一行源码)。
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and Lineno > _line_offset:
Lineno -= _line_offset
Col: int = getattr(Node, 'col_offset', -1)
SrcFile: str = getattr(Gen, '_current_source_file', '') or 'unknown'
Loc: str = f"{SrcFile}:{Lineno}"
if Col >= 0:
Loc += f":{Col}"
SourceLine: str = ''
if Lineno > 0 and hasattr(Gen, '_get_source_line'):
SourceLine = (Gen._get_source_line(Lineno) or '').strip()
ExpectText: str = (f"至少 {Expected} 个形参(变参函数,仅固定形参)"
if IsVariadic else f" {Expected} 个形参")
raise Exception(
f"[TransPyC] 函数指针调用实参个数不匹配: '{FuncName}' 期望{ExpectText},"
f"实际传入 {Actual} 个实参;请检查该函数指针(t.Callable[...] 注解槽)的"
f"形参表是否与调用处实参个数一致。位置 {Loc}"
f"{(',源码: ' + SourceLine) if SourceLine else ''}"
)
def _RequireArgValue(self, ArgVal: ir.Value | None, Callee: str, ArgIndex: int,
ArgNode: ast.AST, CallNode: ast.Call) -> ir.Value:
"""fail-fast:调用实参编译期求值为空(`HandleExprLlvm` 返回 None)时终止装配。
调用装配处原写作 `ArgVal = self.HandleExprLlvm(arg); if ArgVal: 追加(ArgVal)`:
实参求值为 None 时它被**静默整条丢弃**,导致后续实参整体前移一格,最终由
`FuncGen._adjust_args` 报出误导性的"指针类型不能隐式转换为整数参数"(真因是
"少了一个实参")。实例:`ast.OpKind.BitOr` 因枚举成员未登记而求值 None ⇒
`ast.BinOp(...)` 的 4 个实参退化为 3 个 ⇒ `AST*` 落到 `op: i32`(见
`Debugger/rawptr-migration-and-fastfail.md` §27、`Debugger/enum-semantics.md`
的「逗号分组成员被静默丢弃」一节)。
`HandleExprLlvm` 返回 None 只有一种语义——该表达式编译期**求值失败**
(名称/属性链/枚举成员未登记、跨模块符号查找失败、类构造在声明期结构体不完整等);
"按类型本无值"的形态(以返回 void 的调用收尾)返回的是 CallInstr 对象而非 None,
故不存在需要跳过的合法实参。此处改为当场终止,报文含被调者、实参序号与源表达式、
根因提示与源码位置;**不丢弃该实参**。
"""
if ArgVal is not None:
return ArgVal
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
expr_text: str = self._ExprText(ArgNode)
ArgKind: str = type(ArgNode).__name__
raw_lineno: int = getattr(ArgNode, 'lineno', 0) or getattr(CallNode, 'lineno', 0) or 0
lineno: int = raw_lineno
# 行号还原:`_inject_auto_imports` 会在解析前往文件头注入 import 行,AST 行号
# 比磁盘真实行号大 N;与 HandlesBase._RaiseUnresolvedExpr 同口径。
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
col: int = getattr(ArgNode, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or 'unknown'
code_line: str = ''
if lineno > 0 and hasattr(Gen, '_get_source_line'):
try:
code_line = (Gen._get_source_line(lineno) or '').strip()
except Exception:
code_line = ''
loc: str = f"{src_file}:{lineno}"
if col >= 0:
loc += f":{col}"
raise Exception(
f"[TransPyC] 调用实参编译期求值为空: 被调者 '{Callee}' 的第 {ArgIndex} 个实参 "
f"'{expr_text}'({ArgKind} 形态)求值失败;已终止编译,不丢弃该实参"
f"(丢弃会使后续实参前移一格,报出与真因无关的类型不匹配)。"
f"常见根因:枚举成员/名称未登记、跨模块符号查找失败、类定义在声明期结构体不完整。"
f"位置 {loc}"
f"{(',源码: ' + code_line) if code_line else ''}"
)
def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value: def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
# 协程:coro.coro_run(帧) 内联翻译为 resume 循环 # 协程:coro.coro_run(帧) 内联翻译为 resume 循环
@@ -300,6 +469,15 @@ class ExprCallHandle(BaseHandle):
ModulePath: str | None = None ModulePath: str | None = None
if isinstance(Node.func, ast.Name): if isinstance(Node.func, ast.Name):
FuncName = Node.func.id FuncName = Node.func.id
# __asm__(...) 语法糖:等价 c.Asm(...)(裸名调用,无需 import c)
if FuncName == '__asm__':
return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node)
# __llvmir__(...) 语法糖:等价 c.LLVMIR(...)
if FuncName == '__llvmir__':
return self._HandleLLVMIRLlvm(Node)
# irinp/irout 等价 c.LInp/c.LOut(仅作模板占位符,独立调用返回 0)
if FuncName in ('irinp', 'irout'):
return ir.Constant(ir.IntType(32), 0)
# M1 协程:普通函数调用 async 函数 → create_<name>()(返回帧指针) # M1 协程:普通函数调用 async 函数 → create_<name>()(返回帧指针)
if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(FuncName): if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(FuncName):
return self.Trans.AsyncHandler._HandleAsyncCreateCallLlvm(Node, FuncName) return self.Trans.AsyncHandler._HandleAsyncCreateCallLlvm(Node, FuncName)
@@ -316,16 +494,19 @@ class ExprCallHandle(BaseHandle):
# 若该名字同时是类(在 Gen.structs 中或存在 __before_init__),优先作为类构造函数处理 # 若该名字同时是类(在 Gen.structs 中或存在 __before_init__),优先作为类构造函数处理
IsAlsoClass: bool = (FuncName in Gen.structs) or Gen._has_function(f'{FuncName}.__before_init__') IsAlsoClass: bool = (FuncName in Gen.structs) or Gen._has_function(f'{FuncName}.__before_init__')
if SymInfo.IsEnum and not (IsAlsoClass and getattr(SymInfo, 'IsEnumMember', False)): if SymInfo.IsEnum and not (IsAlsoClass and getattr(SymInfo, 'IsEnumMember', False)):
# 枚举类型转换 `EnumName(v)`:结果宽度 = 该枚举的存储宽度
# (最宽成员类型),与成员常量宽度同点(ABI 一致)。
_enum_ty: ir.IntType = Gen._enum_int_type(FuncName, SymInfo)
if Node.args: if Node.args:
arg_val = self.HandleExprLlvm(Node.args[0]) arg_val = self.HandleExprLlvm(Node.args[0])
if arg_val: if arg_val:
if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != 32: if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != _enum_ty.width:
if arg_val.type.width < 32: if arg_val.type.width < _enum_ty.width:
arg_val = Gen.builder.zext(arg_val, ir.IntType(32), name="zext_enum_arg") arg_val = Gen.builder.zext(arg_val, _enum_ty, name="zext_enum_arg")
else: else:
arg_val = Gen.builder.trunc(arg_val, ir.IntType(32), name="trunc_enum_arg") arg_val = Gen.builder.trunc(arg_val, _enum_ty, name="trunc_enum_arg")
return arg_val return arg_val
return ir.Constant(ir.IntType(32), 0) return ir.Constant(_enum_ty, 0)
if SymInfo.IsTypedef: if SymInfo.IsTypedef:
return self._HandleTypedefCastLlvm(Node, FuncName) return self._HandleTypedefCastLlvm(Node, FuncName)
NewFuncName = f'{FuncName}.__before_init__' NewFuncName = f'{FuncName}.__before_init__'
@@ -370,6 +551,7 @@ class ExprCallHandle(BaseHandle):
ArgVal = self.HandleExprLlvm(arg) ArgVal = self.HandleExprLlvm(arg)
if ArgVal: if ArgVal:
args.append(ArgVal) args.append(ArgVal)
self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen)
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}") return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
closure_ptr = Gen._load(VarPtr, name=f"Load_closure_{FuncName}") closure_ptr = Gen._load(VarPtr, name=f"Load_closure_{FuncName}")
if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.FunctionType): if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.FunctionType):
@@ -379,6 +561,7 @@ class ExprCallHandle(BaseHandle):
ArgVal = self.HandleExprLlvm(arg) ArgVal = self.HandleExprLlvm(arg)
if ArgVal: if ArgVal:
args.append(ArgVal) args.append(ArgVal)
self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen)
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}") return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
# 处理 t.CPtr (i8*) 函数指针调用 # 处理 t.CPtr (i8*) 函数指针调用
# 当函数被赋值给 t.CPtr 变量时,原始函数类型退化为 i8* # 当函数被赋值给 t.CPtr 变量时,原始函数类型退化为 i8*
@@ -449,13 +632,39 @@ class ExprCallHandle(BaseHandle):
safe_env_type: ir.Type = _safe_ptr_type(expected_fn_type.args[0]) safe_env_type: ir.Type = _safe_ptr_type(expected_fn_type.args[0])
call_args[0] = Gen.builder.bitcast(call_args[0], safe_env_type, name=f"cast_env_{FuncName}") call_args[0] = Gen.builder.bitcast(call_args[0], safe_env_type, name=f"cast_env_{FuncName}")
return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}") return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}")
# 编译器内置裸指针强转 rawptr(x):rawptr 是 TPC/TPV 双端**语法保留字**
# (无 SymbolTable typedef 条目、无泛型模板;builtins.vp 已不再声明它,
# 见 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME 与 TPV 同口径内置分支
# TypeGenResolve.vp:921)。落到下方 typedef 表判据必然 miss ⇒ 误报
# "Undefined function: 'rawptr'"(TransPyV/App/lib/core/Gen/
# StructGenMixin.vp `vt_ptr: rawptr = rawptr(*(uint64*)(default_val))` 根因)。
# 单独 rawptr 等价 t.CPtr(void* → i8*):目标类型取内置 rawptr 的权威
# CTypeInfo(_builtin_rawptr_info,与注解侧同源),转换复用 (rawptr[X])(x)
# / (t.CPtr)(x) 的 _emit_rawptr_cast——不新造第二套 rawptr 表示。
# 不照抄 uchar 的做法(往 builtins.vp 加 `uchar: t.CTypedef = ...`):
# rawptr 不是普通 typedef 名而是内置语法,TPV register_ctypedef 显式拒绝
# 重定义(TypeGenCType.vp:157-160)、TPC builtins_typedef_value 亦硬编码
# 返回内置值(TypeAnnotationResolver.py:313-314)⇒ 该声明不生效。
# 本分支置于下方 try 之外:解析/转换异常必须外抛,不得被吞成
# "Undefined function: 'rawptr'" 的误导报文。
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_RP
if FuncName == _TAR_RP._BUILTIN_ROWPTR_NAME:
_rawptr_target: ir.Type = Gen._ctype_to_llvm(_TAR_RP._builtin_rawptr_info())
return self._emit_rawptr_cast(Node, Gen, _rawptr_target)
# builtins 隐式 CTypedef 作为类型转换调用(uint64(x)/int64(x)/size_t(x) 等, # builtins 隐式 CTypedef 作为类型转换调用(uint64(x)/int64(x)/size_t(x) 等,
# builtins.vp 声明、无需 import,不注册进 SymbolTable):常规 miss 后在此 # builtins.vp 声明、无需 import,不注册进 SymbolTable):常规 miss 后在此
# 尝试 typedef cast(uint64(buf) = t.CUInt64T(buf)),否则报 # 尝试 typedef cast(uint64(buf) = t.CUInt64T(buf)),否则报
# "Undefined function: 'uint64'"(includes/ast/base.vp bootstrap 根因)。 # "Undefined function: 'uint64'"(includes/ast/base.vp bootstrap 根因)。
_td_diag: str = ''
try: try:
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_TD from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_TD
if FuncName in _TAR_TD._scan_builtins()['typedefs']: _td_table: dict = _TAR_TD._scan_builtins()['typedefs']
if FuncName not in _td_table:
# 典型成因:includes/builtins.vp 未定位到(PROJECT_INCLUDE_DIRS 尚未
# 装配)、或声明写法未被文本扫描识别 ⇒ 整表为空/缺项。
_td_diag = (f"(builtins typedef 表 {len(_td_table)} 项,无 '{FuncName}';"
f"includes 目录={_TAR_TD._builtins_include_dirs()})")
if FuncName in _td_table:
# from_type_name 对裸 typedef 名(uint64 等)解析不出真实类型类 # from_type_name 对裸 typedef 名(uint64 等)解析不出真实类型类
# (BuiltinTypeMap 无此键,CreateFromTypeName 落空成空 CType), # (BuiltinTypeMap 无此键,CreateFromTypeName 落空成空 CType),
# 因此改走权威 typedef 值:'t.CUInt64T' → CTypeRegistry.NameToLLVM # 因此改走权威 typedef 值:'t.CUInt64T' → CTypeRegistry.NameToLLVM
@@ -487,9 +696,11 @@ class ExprCallHandle(BaseHandle):
_tg_ptr_td: ir.Type = ir.PointerType(_tg_td) _tg_ptr_td: ir.Type = ir.PointerType(_tg_td)
return self._emit_rawptr_cast(Node, Gen, _tg_ptr_td) return self._emit_rawptr_cast(Node, Gen, _tg_ptr_td)
return self._emit_rawptr_cast(Node, Gen, _tg_td) return self._emit_rawptr_cast(Node, Gen, _tg_td)
except Exception: except Exception as _e_td:
pass # 不再静默:兜底链任何一步抛错都要带出来(此前 pass 让真正的根因
raise Exception(f"Undefined function: '{FuncName}'") # 只剩一句误导性的 "Undefined function")。
_td_diag = _td_diag + f"(typedef cast 求值异常: {type(_e_td).__name__}: {_e_td})"
raise Exception(f"Undefined function: '{FuncName}'{_td_diag}")
if isinstance(Node.func, ast.Subscript): if isinstance(Node.func, ast.Subscript):
# Handle list[int](mb) syntax — generic class constructor with explicit type args # Handle list[int](mb) syntax — generic class constructor with explicit type args
# Handle xxx[T](x) syntax — generic function call with explicit type args # Handle xxx[T](x) syntax — generic function call with explicit type args
@@ -558,39 +769,12 @@ class ExprCallHandle(BaseHandle):
if ObjVal and isinstance(ObjVal.type, ir.PointerType): if ObjVal and isinstance(ObjVal.type, ir.PointerType):
pointee = ObjVal.type.pointee pointee = ObjVal.type.pointee
if isinstance(pointee, ir.ArrayType): if isinstance(pointee, ir.ArrayType):
# Calculate element size in bytes # 元素尺寸统一走 Gen._get_struct_size(含字段对齐/填充,与 __sizeof__ /
elem = pointee.element # memhub alloc 同源):_get_struct_size(ArrayType) = 元素尺寸 × 元素数。
if isinstance(elem, ir.IntType): # 原实现手写「字段宽度直接相加 + 未知类型兜底 8」,会漏算 struct 的填充
elem_size = elem.width // 8 # ({i8,i64} 算成 9 而非 16),sizeof 偏小 → 调用方按过小尺寸 alloc →
elif isinstance(elem, ir.PointerType): # 越界写坏邻居块头(与 1 GiB 主池漏满同一根因链)。
elem_size = 8 # 64-bit pointers return ir.Constant(ir.IntType(64), Gen._get_struct_size(pointee))
elif isinstance(elem, (ir.LiteralStructType, ir.IdentifiedStructType)):
# Approximate: sum of field sizes
elem_size = 0
for field in getattr(elem, 'elements', []):
if isinstance(field, ir.IntType):
elem_size += field.width // 8
elif isinstance(field, ir.PointerType):
elem_size += 8
elif isinstance(field, ir.ArrayType):
if isinstance(field.element, ir.IntType):
elem_size += field.count * (field.element.width // 8)
else:
elem_size += field.count * 8
else:
elem_size += 8
elif isinstance(elem, ir.ArrayType):
if isinstance(elem.element, ir.IntType):
elem_size = elem.count * (elem.element.width // 8)
else:
elem_size = elem.count * 8
elif isinstance(elem, ir.FloatType):
elem_size = 4
elif isinstance(elem, ir.DoubleType):
elem_size = 8
else:
elem_size = 8 # fallback
return ir.Constant(ir.IntType(64), pointee.count * elem_size)
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
# 结构体实例: 直接计算大小 # 结构体实例: 直接计算大小
self._resolve_opaque_struct_members(pointee, Gen) self._resolve_opaque_struct_members(pointee, Gen)
@@ -649,6 +833,13 @@ class ExprCallHandle(BaseHandle):
or _base_name in Gen.global_vars) or _base_name in Gen.global_vars)
): ):
_is_chained_instance = True _is_chained_instance = True
elif self._IsModuleQualifiedGlobalChain(_base_name, Node.func.value):
# 模块限定全局变量链:`Module.Global.field.method(...)`
# (如 GenApi._g_gen.ClassGenBase._collect_parents(...))。
# 链根是模块别名、次段是该模块的全局变量 ⇒ 接收者是对象,
# 与 self/局部变量/全局变量同一条"实例链"判据(详见
# _IsModuleQualifiedGlobalChain 的说明)。
_is_chained_instance = True
if (is_instance_var or _is_chained_instance) and not is_class_name: if (is_instance_var or _is_chained_instance) and not is_class_name:
return self._HandleMethodCallLlvm(Node) return self._HandleMethodCallLlvm(Node)
FuncAttr = Node.func.attr FuncAttr = Node.func.attr
@@ -681,6 +872,10 @@ class ExprCallHandle(BaseHandle):
if isinstance(arg, ast.Constant) and isinstance(arg.value, str): if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
msg = arg.value msg = arg.value
lineno = getattr(Node, 'lineno', 0) lineno = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
raise Exception(f"#error: {msg} (line {lineno})") raise Exception(f"#error: {msg} (line {lineno})")
elif FuncAttr in ('AsmInp', 'AsmOut'): elif FuncAttr in ('AsmInp', 'AsmOut'):
return ir.Constant(ir.IntType(32), 0) return ir.Constant(ir.IntType(32), 0)
@@ -1025,8 +1220,17 @@ class ExprCallHandle(BaseHandle):
if is_classmethod_call and ClassName in Gen.structs: if is_classmethod_call and ClassName in Gen.structs:
ClsPtr = Gen._alloca(Gen.structs[ClassName], name=f"cls_{ClassName}") ClsPtr = Gen._alloca(Gen.structs[ClassName], name=f"cls_{ClassName}")
CallArgs.append(ClsPtr) CallArgs.append(ClsPtr)
for arg in Node.args: # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度
ArgVal = self.HandleExprLlvm(arg) # (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量实参走原路径。
_arg_off: int = len(CallArgs)
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = None
if func is not None:
_ft_args: list[ir.Type] = func.function_type.args
if _ai + _arg_off < len(_ft_args):
_pt = _ft_args[_ai + _arg_off]
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal: if ArgVal:
CallArgs.append(ArgVal) CallArgs.append(ArgVal)
if (ClassName not in Gen.class_vtable if (ClassName not in Gen.class_vtable
@@ -1087,15 +1291,22 @@ class ExprCallHandle(BaseHandle):
if ClassName in Gen.structs: if ClassName in Gen.structs:
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
CallArgs = [ObjVal] CallArgs = [ObjVal]
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
CallFuncName = f'{ClassName}.__call__' CallFuncName = f'{ClassName}.__call__'
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
class_sha1 = struct_sha1_map.get(ClassName) class_sha1 = struct_sha1_map.get(ClassName)
if class_sha1: if class_sha1:
CallFuncName = f'{class_sha1}.{ClassName}.__call__' CallFuncName = f'{class_sha1}.{ClassName}.__call__'
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
# CallArgs[0] 为 self ⇒ 第 i 个用户实参对应形参 i+1。
_cfunc: ir.Function | None = (Gen._get_function(CallFuncName)
if Gen._has_function(CallFuncName) else None)
_ptypes: list[ir.Type] = _cfunc.function_type.args if _cfunc is not None else []
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai + 1] if _ai + 1 < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal:
CallArgs.append(ArgVal)
if not Gen._has_function(CallFuncName): if not Gen._has_function(CallFuncName):
raise Exception(f"Class '{ClassName}' has no __call__ method") raise Exception(f"Class '{ClassName}' has no __call__ method")
func = Gen._get_function(CallFuncName) func = Gen._get_function(CallFuncName)
@@ -1131,6 +1342,66 @@ class ExprCallHandle(BaseHandle):
parts.reverse() parts.reverse()
return '.'.join(parts) if parts else None return '.'.join(parts) if parts else None
def _IsModuleQualifiedGlobalChain(self, base_name: str, receiver: ast.AST) -> bool:
"""判定"模块限定全局变量"接收者链(链根=模块别名、次段=该模块全局变量)。
与调用点既有的"链根是已知变量(self / 局部变量 / 全局变量 / struct 变量)"
判据互补,补的是**模块限定全局变量**这一形态:
`import lib.core.Gen.GenApi as GenApi` 后写
`GenApi._g_gen.ClassGenBase._collect_parents(...)`——链根 `GenApi` 是模块
别名(不在 var_struct_class / variables / global_vars 中),次段 `_g_gen`
是该模块的全局变量。缺此判定时该接收者被当作"模块.类"引用,落到下方
`inner_attr in Gen.structs` 分支走 _HandleStaticMethodCallLlvm——该路径按
静态方法口径处理(**不前置接收者 self**),实参整体错位一格
(trans: Translator* 对上 max_parents: i32),触发 fail-fast
"指针类型 ... 不能隐式转换为整数参数 i32";不报错的同形调用(实参全为指针)
则静默生成缺 self 的错误调用。
判据只有两段:链根是模块(SymbolTable.import_aliases / IsModuleAlias 符号 /
ModuleSha1Map / _ImportedModules 任一命中),且**紧邻根名的那一段属性**不是
类型/函数类符号(类 / 枚举 / REnum / 枚举成员 / 函数 / CDefine / 异常类 /
模块别名)——即该段是"对象"(全局变量;跨模块全局在本符号表里登记为
IsTypedef 的空类型条目,故 IsTypedef 不在排除集内)。紧随段是类
(`Module.Class.static_method(...)`)时返回 False,静态方法口径保持不变。
"""
symbol_table: Any = self.Trans.SymbolTable
# 紧邻根名的那一段属性:GenApi._g_gen.ClassGenBase → '_g_gen'
first_attr: str | None = None
cursor: ast.AST = receiver
while isinstance(cursor, ast.Attribute):
first_attr = cursor.attr
cursor = cursor.value
if not first_attr or not isinstance(cursor, ast.Name):
return False
# 链根是不是模块:优先别名表(import ... as X 的 X → 模块全名),
# 其次符号表里的模块别名标记,最后模块表(其键含短名)兜底。
module_path: str = symbol_table.resolve_alias(base_name)
if module_path != base_name:
is_module: bool = True
else:
alias_entry: Any = symbol_table.lookup(base_name)
if alias_entry is not None and getattr(alias_entry, 'IsModuleAlias', False):
module_path = getattr(alias_entry, 'ResolvedModule', '') or ''
is_module = bool(module_path)
else:
is_module = (
base_name in getattr(self.Trans.LlvmGen, 'ModuleSha1Map', {})
or base_name in getattr(self.Trans, '_ImportedModules', set()))
if not is_module:
return False
segment: Any = symbol_table.lookup(f'{module_path}.{first_attr}') if module_path else None
if segment is None:
segment = symbol_table.lookup(first_attr)
if segment is None:
return False
# 该段是"类型/函数类"符号 ⇒ 属于 `Module.Class...` 引用,不按实例链处理
# (同名枚举成员/异常类在符号表里与类名冲突,一并排除,避免误判)。
for _type_flag in ('IsStruct', 'IsEnum', 'IsRenum', 'IsUnion', 'IsEnumMember',
'IsFunction', 'IsDefine', 'IsExceptionClass', 'IsModuleAlias'):
if getattr(segment, _type_flag, False):
return False
return True
def _HandleLLVMIRLlvm(self, Node: ast.Call) -> ir.Value: def _HandleLLVMIRLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if not Node.args: if not Node.args:
@@ -1158,36 +1429,32 @@ class ExprCallHandle(BaseHandle):
parts.append(value.value) parts.append(value.value)
elif isinstance(value, ast.FormattedValue): elif isinstance(value, ast.FormattedValue):
expr = value.value expr = value.value
if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute): # c.LInp/c.LOut 与裸名 irinp/irout 等价(见 _intrinsic_marker)
if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c': _marker: str | None = self.Trans.ExprAsmHandle._intrinsic_marker(expr)
if expr.func.attr == 'LInp': _is_c_qualified: bool = (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute)
if expr.args: and isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c')
val = self.HandleExprLlvm(expr.args[0]) if _marker == 'LInp':
if val: if expr.args:
input_ops.append(val) val = self.HandleExprLlvm(expr.args[0])
all_ops.append(('inp', len(input_ops) - 1)) if val:
parts.append(f'%__OP{operand_seq}__') input_ops.append(val)
operand_seq += 1
elif expr.func.attr == 'LOut':
if expr.args:
target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0])
if target_ptr:
val = target_ptr
else:
val = self.HandleExprLlvm(expr.args[0])
if val:
output_targets.append(val)
all_ops.append(('out', len(output_targets) - 1))
parts.append(f'%__OP{operand_seq}__')
operand_seq += 1
else:
fallback = self.HandleExprLlvm(expr)
if fallback:
input_ops.append(fallback)
all_ops.append(('inp', len(input_ops) - 1)) all_ops.append(('inp', len(input_ops) - 1))
parts.append(f'%__OP{operand_seq}__') parts.append(f'%__OP{operand_seq}__')
operand_seq += 1 operand_seq += 1
else: elif _marker == 'LOut':
if expr.args:
target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0])
if target_ptr:
val = target_ptr
else:
val = self.HandleExprLlvm(expr.args[0])
if val:
output_targets.append(val)
all_ops.append(('out', len(output_targets) - 1))
parts.append(f'%__OP{operand_seq}__')
operand_seq += 1
elif not _is_c_qualified:
# c.* 中非 LInp/LOut 的形态不收集(保持原行为)
fallback = self.HandleExprLlvm(expr) fallback = self.HandleExprLlvm(expr)
if fallback: if fallback:
input_ops.append(fallback) input_ops.append(fallback)
@@ -1551,6 +1818,14 @@ class ExprCallHandle(BaseHandle):
if sym_info_exact: if sym_info_exact:
exact_params = sym_info_exact.FuncPtrParams exact_params = sym_info_exact.FuncPtrParams
exact_param_names = [pn for pn, _ in exact_params] exact_param_names = [pn for pn, _ in exact_params]
# stub/LLVM 侧形参名多为 arg0/arg1 占位(SymbolTable._InsertFuncSymbol),
# 用它们无法把命名实参 `f(cap = 8, x = 1)` 绑到正确位置,会误报"缺少必需
# 参数"。优先替换为源码/vpi 扫描得到的语义参数名(长度一致才替换,
# 避免 shape 不符时错配)。
semantic_names = (self._LookupGlobalParamNames(mangled_name)
or self._LookupGlobalParamNames(func_name))
if semantic_names and len(semantic_names) == len(exact_param_names):
exact_param_names = semantic_names
if exact_param_names: if exact_param_names:
provided = len(Node.args) provided = len(Node.args)
kw_provided = set() kw_provided = set()
@@ -1558,17 +1833,9 @@ class ExprCallHandle(BaseHandle):
if kw.arg and kw.arg in exact_param_names: if kw.arg and kw.arg in exact_param_names:
kw_provided.add(exact_param_names.index(kw.arg)) kw_provided.add(exact_param_names.index(kw.arg))
# 获取默认参数信息,计算必需参数数量(与 _fill_default_args 一致的查找逻辑) # 获取默认参数信息,计算必需参数数量(与 _fill_default_args 一致的查找逻辑)
global_defaults: dict = self.Trans._global_function_default_args func_defaults: list | None = self._LookupGlobalDefaults(mangled_name)
func_defaults: list | None = None if func_defaults is None:
if mangled_name in global_defaults: func_defaults = self._LookupGlobalDefaults(func_name)
func_defaults = global_defaults[mangled_name]
elif func_name in global_defaults:
func_defaults = global_defaults[func_name]
else:
for gk, gv in global_defaults.items():
if func_name in gk or gk.endswith(func_name) or gk.endswith('.' + func_name):
func_defaults = gv
break
total_params: int = len(exact_param_names) total_params: int = len(exact_param_names)
if func_defaults is None: if func_defaults is None:
# 默认参数信息缺失(跨模块 stub 加载:vpi/stub 只含参数 # 默认参数信息缺失(跨模块 stub 加载:vpi/stub 只含参数
@@ -1578,6 +1845,9 @@ class ExprCallHandle(BaseHandle):
# err_type/col_start/col_end 默认值未同步时误报"缺 arg3")。 # err_type/col_start/col_end 默认值未同步时误报"缺 arg3")。
# 同文件函数的参数检查由 FunctionDef 路径(L1983)覆盖。 # 同文件函数的参数检查由 FunctionDef 路径(L1983)覆盖。
pass pass
elif len(func_defaults) > total_params:
# 表里的条目与当前函数长度不符(同名不同模块)→ 不做严格检查
pass
else: else:
num_defaults: int = len(func_defaults) num_defaults: int = len(func_defaults)
min_args: int = total_params - num_defaults min_args: int = total_params - num_defaults
@@ -1645,6 +1915,10 @@ class ExprCallHandle(BaseHandle):
if isinstance(arg, ast.Constant) and isinstance(arg.value, str): if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
msg = arg.value msg = arg.value
lineno = getattr(Node, 'lineno', 0) lineno = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
raise Exception(f"#error: {msg} (line {lineno})") raise Exception(f"#error: {msg} (line {lineno})")
elif func_name in ('AsmInp', 'AsmOut'): elif func_name in ('AsmInp', 'AsmOut'):
return ir.Constant(ir.IntType(32), 0) return ir.Constant(ir.IntType(32), 0)
@@ -2124,7 +2398,10 @@ class ExprCallHandle(BaseHandle):
if not is_variadic and len(Node.args) > regular_param_count: if not is_variadic and len(Node.args) > regular_param_count:
raise Exception(f"调用 {FuncName}() 传入了 {len(Node.args)} 个参数,但函数只需要 {regular_param_count} 个参数") raise Exception(f"调用 {FuncName}() 传入了 {len(Node.args)} 个参数,但函数只需要 {regular_param_count} 个参数")
max_args = len(Node.args) if is_variadic else regular_param_count # 变参函数:固定形参(含带默认值的)之外才是可变部分。被省略的固定形参
# 也必须补进调用实参,否则实参个数少于固定形参 → 被调函数读到寄存器/栈
# 垃圾(VET json.write 缺省 cap 读成指针量级 → pool.alloc 巨量 → OOM 的形态)。
max_args = max(regular_param_count, len(Node.args)) if is_variadic else regular_param_count
resolved = [None] * max_args resolved = [None] * max_args
for i, arg in enumerate(Node.args): for i, arg in enumerate(Node.args):
@@ -2153,31 +2430,44 @@ class ExprCallHandle(BaseHandle):
resolved[idx] = ArgVal resolved[idx] = ArgVal
FuncDef_args_defaults = getattr(getattr(FuncDef, 'args', None), 'defaults', []) if FuncDef else [] FuncDef_args_defaults = getattr(getattr(FuncDef, 'args', None), 'defaults', []) if FuncDef else []
if FuncDef_args_defaults: # 跨模块调用(被调函数不在本模块 FunctionDefCache:其它 App 模块 / includes
defaults = FuncDef.args.defaults # 模块 / .vpi 声明)时 FuncDef 为 None,本模块没有被调方 AST,AST 默认值不可用。
num_defaults = len(defaults) # 此时从全局默认参数表(Phase2 预扫描 App 与 includes 源码得到)取值,按
if num_defaults > 0: # "尾部默认参数"语义补齐被省略的实参;缺失该表时保持原有零值填充。
min_args = regular_param_count - num_defaults Global_defaults_vals: list | None = None
for i in range(regular_param_count): if not FuncDef_args_defaults:
if resolved[i] is None and i >= min_args: Global_defaults_vals = self._LookupGlobalDefaults(FuncName)
default_idx = i - min_args if Global_defaults_vals is None and func is not None:
if default_idx < len(defaults): Global_defaults_vals = self._LookupGlobalDefaults(getattr(func, 'name', None))
default_node = defaults[default_idx] Num_defaults: int = len(FuncDef_args_defaults) if FuncDef_args_defaults else (
default_val = self.HandleExprLlvm(default_node) len(Global_defaults_vals) if Global_defaults_vals else 0)
if default_val: if Num_defaults > 0:
param_type = func.function_type.args[i] Min_args: int = regular_param_count - Num_defaults
if default_val.type != param_type: for i in range(regular_param_count):
if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType): if resolved[i] is not None or i < Min_args:
if default_val.type.width < param_type.width: continue
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}") default_idx = i - Min_args
elif default_val.type.width > param_type.width: if default_idx >= Num_defaults:
default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}") continue
elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType): if FuncDef_args_defaults:
default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}") default_val = self.HandleExprLlvm(FuncDef_args_defaults[default_idx])
resolved[i] = default_val else:
default_val = self._ConstantToIrValue(Global_defaults_vals[default_idx],
func.function_type.args[i], Gen)
if default_val:
param_type = func.function_type.args[i]
if default_val.type != param_type:
if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType):
if default_val.type.width < param_type.width:
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
elif default_val.type.width > param_type.width:
default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}")
elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType):
default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}")
resolved[i] = default_val
if FuncDef and param_names: if FuncDef and param_names:
min_args = regular_param_count - num_defaults if FuncDef_args_defaults else regular_param_count min_args = regular_param_count - Num_defaults
provided = len(Node.args) provided = len(Node.args)
kw_provided = set() kw_provided = set()
for kw in Node.keywords: for kw in Node.keywords:
@@ -2361,7 +2651,7 @@ class ExprCallHandle(BaseHandle):
return cmap return cmap
with open(pyi_path, 'r', encoding='utf-8') as f: with open(pyi_path, 'r', encoding='utf-8') as f:
pyi_code = f.read() pyi_code = f.read()
tree = ast.parse(pyi_code) tree = ast.parse(pyi_code, pyi_path)
cmap: dict[str, ast.ClassDef] = {} cmap: dict[str, ast.ClassDef] = {}
for node in ast.iter_child_nodes(tree): for node in ast.iter_child_nodes(tree):
if isinstance(node, ast.ClassDef): if isinstance(node, ast.ClassDef):
@@ -2397,17 +2687,18 @@ class ExprCallHandle(BaseHandle):
is_cvtable = self._has_cvtable_decorator(target_node) is_cvtable = self._has_cvtable_decorator(target_node)
is_novtable = self._has_novtable_decorator(target_node) is_novtable = self._has_novtable_decorator(target_node)
# 收集自身方法 # 收集自身方法(方法级修饰符的槽位过滤放在父类状态确定之后:
self_methods: list[str] = [] # 判据 SymbolUtils.IsVtableSlotMethod 需要 VtableByClassLevel)
self_method_pairs: list[tuple] = []
for item in target_node.body: for item in target_node.body:
if isinstance(item, ast.FunctionDef): if isinstance(item, ast.FunctionDef):
method_name = item.name method_name = item.name
if method_name in ('__new__', '__init__', '__before_init__'): if method_name in ('__new__', '__init__', '__before_init__'):
continue continue
if method_name == '__call__': if method_name == '__call__':
self_methods.append(f"{class_name}.__call__") self_method_pairs.append((item, f"{class_name}.__call__"))
else: else:
self_methods.append(f"{class_name}.{method_name}") self_method_pairs.append((item, f"{class_name}.{method_name}"))
# 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先) # 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先)
parent_methods: list[str] = [] parent_methods: list[str] = []
@@ -2466,6 +2757,24 @@ class ExprCallHandle(BaseHandle):
if first_parent_vtable: if first_parent_vtable:
is_cvtable = True is_cvtable = True
# VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态
# (= 类级 CVTable / 继承链判定)——供槽位筛选与"方法级触发"共用。
_VtByClassLevel: bool = is_cvtable
# 方法级修饰符筛选:`novirtual def`(⇒ @t.NoVTable)不进槽;
# `virtual def`(⇒ @t.CVTable)进槽。判据唯一源 = IsVtableSlotMethod
# (与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径),
# 故跨模块虚表槽序与定义模块逐槽一致。
self_methods: list[str] = []
for _nd, _full in self_method_pairs:
if (not is_cvtable) or IsVtableSlotMethod(_nd, _VtByClassLevel):
self_methods.append(_full)
# 方法级 virtual 触发类级 vtable(与 HandlesClassDef._resolve_class_flags /
# TPV _detect_vtable_status 的 `_has_any_func_cvtable` 分支对齐:
# 类级 NoVTable / 无真实父类这两种可判定形态;父类 vtable 已在上面处理)
if not is_cvtable and ClassHasFuncLevelCVTable(target_node):
if is_novtable or parent_name is None:
is_cvtable = True
# 合并方法列表:基类方法在前,子类方法在后 # 合并方法列表:基类方法在前,子类方法在后
# 子类覆盖的方法放在父类的位置; # 子类覆盖的方法放在父类的位置;
# 父类独有方法直接用父类方法原名(实际定义类.方法,与 TPV 一致) # 父类独有方法直接用父类方法原名(实际定义类.方法,与 TPV 一致)
@@ -2603,9 +2912,9 @@ class ExprCallHandle(BaseHandle):
if st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None: if st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
return None return None
members: list = Gen.class_members.get(lookup_cls, []) members: list = Gen.class_members.get(lookup_cls, [])
has_vtable: bool = (ClassName in Gen.class_vtable # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源),
or ClassName in Gen._cross_module_vtable_classes) # 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。
base: int = 1 if has_vtable else 0 base: int = Gen._member_base(ClassName)
idx: int | None = None idx: int | None = None
for i, (name, _) in enumerate(members): for i, (name, _) in enumerate(members):
if name == field_name: if name == field_name:
@@ -2628,7 +2937,7 @@ class ExprCallHandle(BaseHandle):
try: try:
with open(r'd:\Users\TermiNexus\Desktop\TransPyC\_diag_offset.log', 'a', encoding='utf-8') as _f: with open(r'd:\Users\TermiNexus\Desktop\TransPyC\_diag_offset.log', 'a', encoding='utf-8') as _f:
_f.write(f"{lookup_cls}|{field_name}|members={len(members)}|struct={len(st.elements)}" _f.write(f"{lookup_cls}|{field_name}|members={len(members)}|struct={len(st.elements)}"
f"|vtable={int(has_vtable)}|idx={idx}|diff={diff_elems}\n") f"|base={base}|idx={idx}|diff={diff_elems}\n")
except Exception: except Exception:
pass pass
offset: int = 0 offset: int = 0
@@ -2870,6 +3179,10 @@ class ExprCallHandle(BaseHandle):
raise raise
_vlog().warning(f"处理函数调用失败: {_e}", "Exception") _vlog().warning(f"处理函数调用失败: {_e}", "Exception")
lineno = getattr(Node, 'lineno', 0) lineno = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
raise Exception(f"#error: {msg} (line {lineno})") raise Exception(f"#error: {msg} (line {lineno})")
elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'): elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'):
return ir.Constant(ir.IntType(1), 1) return ir.Constant(ir.IntType(1), 1)
@@ -2904,12 +3217,16 @@ class ExprCallHandle(BaseHandle):
if (ModuleName in imported or actual_module in imported) and ModuleName not in Gen.var_struct_class: if (ModuleName in imported or actual_module in imported) and ModuleName not in Gen.var_struct_class:
mangled_name = Gen._mangle_func_name(MethodName, actual_module) mangled_name = Gen._mangle_func_name(MethodName, actual_module)
if Gen._has_function(mangled_name): if Gen._has_function(mangled_name):
func = Gen._get_function(mangled_name)
CallArgs = [] CallArgs = []
for arg in Node.args: # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
ArgVal = self.HandleExprLlvm(arg) _ptypes: list[ir.Type] = func.function_type.args if func is not None else []
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal: if ArgVal:
CallArgs.append(ArgVal) CallArgs.append(ArgVal)
func = Gen._get_function(mangled_name)
self._fill_default_args(CallArgs, func, MethodName) self._fill_default_args(CallArgs, func, MethodName)
func_ft = getattr(func, 'function_type', None) func_ft = getattr(func, 'function_type', None)
is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False
@@ -2961,8 +3278,12 @@ class ExprCallHandle(BaseHandle):
_mf = Gen._get_function(MethodName) _mf = Gen._get_function(MethodName)
if '.' not in _mf.name: if '.' not in _mf.name:
CallArgs = [] CallArgs = []
for arg in Node.args: # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
ArgVal = self.HandleExprLlvm(arg) _ptypes: list[ir.Type] = _mf.function_type.args
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal: if ArgVal:
CallArgs.append(ArgVal) CallArgs.append(ArgVal)
func = _mf func = _mf
@@ -3026,8 +3347,12 @@ class ExprCallHandle(BaseHandle):
if stub_func_type: if stub_func_type:
func = self.GetOrCreateStubFuncDecl(mangled_name, stub_func_type, Gen) func = self.GetOrCreateStubFuncDecl(mangled_name, stub_func_type, Gen)
CallArgs = [] CallArgs = []
for arg in Node.args: # 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
ArgVal = self.HandleExprLlvm(arg) _ptypes: list[ir.Type] = func.function_type.args if func is not None else []
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal: if ArgVal:
CallArgs.append(ArgVal) CallArgs.append(ArgVal)
self._fill_default_args(CallArgs, func, mangled_name) self._fill_default_args(CallArgs, func, mangled_name)
@@ -3419,8 +3744,8 @@ class ExprCallHandle(BaseHandle):
member_idx = mi member_idx = mi
break break
if member_idx >= 0 and isinstance(ObjVal.type, ir.PointerType): if member_idx >= 0 and isinstance(ObjVal.type, ir.PointerType):
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = 1 if has_vtable else 0 base = Gen._member_base(ClassName)
member_ptr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + member_idx)], name=f"funcptr_{MethodName}_ptr") member_ptr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + member_idx)], name=f"funcptr_{MethodName}_ptr")
raw_ptr = Gen._load(member_ptr, name=f"funcptr_{MethodName}_raw") raw_ptr = Gen._load(member_ptr, name=f"funcptr_{MethodName}_raw")
func_ptr_type = ir.PointerType(ir.FunctionType(ir.IntType(32), [i8ptr] + [a.type for a in CallArgs])) func_ptr_type = ir.PointerType(ir.FunctionType(ir.IntType(32), [i8ptr] + [a.type for a in CallArgs]))
@@ -3845,9 +4170,19 @@ class ExprCallHandle(BaseHandle):
# 所有元素存取同槽(get(0)==get(1),list[对象] join 误报根因)。 # 所有元素存取同槽(get(0)==get(1),list[对象] join 误报根因)。
if _BinOpContainsCPtr(Node): if _BinOpContainsCPtr(Node):
return ir.Constant(ir.IntType(64), 8) return ir.Constant(ir.IntType(64), 8)
return ir.Constant(ir.IntType(64), 0) # 不带 t.CPtr 的联合(A | B)无法确定尺寸:曾静默返回 0 →
# 下游 elem_size=0 → alloc(0) 返回 NULL / 所有元素存取同槽。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] 联合类型 '{ast.unparse(Node)}' 不含 t.CPtr,"
f"无法确定尺寸,拒绝静默返回 0(会导致 alloc(0)/元素同槽)"
)
else: else:
return ir.Constant(ir.IntType(64), 0) # 非 名称/属性/联合 的 sizeof 目标(如表达式、调用):曾静默返回 0。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] 不支持的 sizeof 目标 {type(Node).__name__}"
f"({ast.unparse(Node)}),"
f"拒绝静默返回 0(会导致 alloc(0))"
)
if type_name not in Gen.structs: if type_name not in Gen.structs:
# SHA1 前缀查找:如 ClassDef → 657e182b27c2a022.ClassDef # SHA1 前缀查找:如 ClassDef → 657e182b27c2a022.ClassDef
@@ -3941,10 +4276,8 @@ class ExprCallHandle(BaseHandle):
Gen.var_signedness = _saved_var_signedness Gen.var_signedness = _saved_var_signedness
Gen.global_vars = _saved_global_vars Gen.global_vars = _saved_global_vars
self.Trans.VarScopes = _saved_var_scopes self.Trans.VarScopes = _saved_var_scopes
size = Gen._get_struct_size(struct_type) # _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。
if size > 0: return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type))
return ir.Constant(ir.IntType(64), size)
return ir.Constant(ir.IntType(64), 0)
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None: def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
Gen = self.Trans.LlvmGen Gen = self.Trans.LlvmGen
@@ -4034,7 +4367,8 @@ class ExprCallHandle(BaseHandle):
if enum_member_found: if enum_member_found:
enum_type_name = enum_class_name enum_type_name = enum_class_name
if enum_type_name: if enum_type_name:
type_str = f"<enum '{enum_type_name}' size=4 signed=true, ptr=false>" _enum_bits: int = self.Trans.LlvmGen._enum_int_type(enum_type_name).width
type_str = f"<enum '{enum_type_name}' size={_enum_bits // 8} signed=true, ptr=false>"
else: else:
llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg) llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg)
if isinstance(llvm_type, ir.PointerType): if isinstance(llvm_type, ir.PointerType):
@@ -4382,8 +4716,8 @@ class ExprCallHandle(BaseHandle):
if not find_func: if not find_func:
return return
members = Gen.class_members.get(ClassName, []) members = Gen.class_members.get(ClassName, [])
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = 1 if has_vtable else 0 base = Gen._member_base(ClassName)
field_map: dict[str, tuple[int, ir.Type]] = {} field_map: dict[str, tuple[int, ir.Type]] = {}
for i, (mname, mtype) in enumerate(members): for i, (mname, mtype) in enumerate(members):
if mname in requires_all: if mname in requires_all:
@@ -4447,10 +4781,9 @@ class ExprCallHandle(BaseHandle):
NewMethodFunc = Gen._find_function(NewMethodFuncName) NewMethodFunc = Gen._find_function(NewMethodFuncName)
if NewMethodFunc: if NewMethodFunc:
NewMethodArgs = [result] NewMethodArgs = [result]
for arg in Node.args: for ArgIdx, arg in enumerate(Node.args, start=1):
ArgVal = self.HandleExprLlvm(arg) ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node)
if ArgVal: NewMethodArgs.append(ArgVal)
NewMethodArgs.append(ArgVal)
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName) self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1) self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs) NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
@@ -4479,10 +4812,9 @@ class ExprCallHandle(BaseHandle):
if Gen._has_function(InitFuncName): if Gen._has_function(InitFuncName):
InitFunc = Gen._get_function(InitFuncName) InitFunc = Gen._get_function(InitFuncName)
InitArgs = [result] InitArgs = [result]
for arg in Node.args: for ArgIdx, arg in enumerate(Node.args, start=1):
ArgVal = self.HandleExprLlvm(arg) ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__init__", ArgIdx, arg, Node)
if ArgVal: InitArgs.append(ArgVal)
InitArgs.append(ArgVal)
self._fill_default_args(InitArgs, InitFunc, InitFuncName) self._fill_default_args(InitArgs, InitFunc, InitFuncName)
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1) self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen) self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
@@ -4522,10 +4854,9 @@ class ExprCallHandle(BaseHandle):
NewMethodFunc = Gen._find_function(NewMethodFuncName) NewMethodFunc = Gen._find_function(NewMethodFuncName)
if NewMethodFunc: if NewMethodFunc:
NewMethodArgs = [result] NewMethodArgs = [result]
for arg in Node.args: for ArgIdx, arg in enumerate(Node.args, start=1):
ArgVal = self.HandleExprLlvm(arg) ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node)
if ArgVal: NewMethodArgs.append(ArgVal)
NewMethodArgs.append(ArgVal)
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName) self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1) self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs) NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
@@ -4563,9 +4894,8 @@ class ExprCallHandle(BaseHandle):
init_param_types = InitFunc.function_type.args if hasattr(InitFunc, 'function_type') else [] init_param_types = InitFunc.function_type.args if hasattr(InitFunc, 'function_type') else []
for i, arg in enumerate(Node.args): for i, arg in enumerate(Node.args):
arg_var_type = init_param_types[i + 1] if i + 1 < len(init_param_types) else None arg_var_type = init_param_types[i + 1] if i + 1 < len(init_param_types) else None
ArgVal = self.HandleExprLlvm(arg, VarType=arg_var_type) ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg, VarType=arg_var_type), f"{ClassName}.__init__", i + 1, arg, Node)
if ArgVal: InitArgs.append(ArgVal)
InitArgs.append(ArgVal)
self._fill_default_args(InitArgs, InitFunc, InitFuncName) self._fill_default_args(InitArgs, InitFunc, InitFuncName)
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1) self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen) self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
@@ -4613,8 +4943,8 @@ class ExprCallHandle(BaseHandle):
return return
if not isinstance(StructType, ir.IdentifiedStructType): if not isinstance(StructType, ir.IdentifiedStructType):
return return
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = 1 if has_vtable else 0 base = Gen._member_base(ClassName)
for i, (member_name, member_type) in enumerate(members): for i, (member_name, member_type) in enumerate(members):
if member_name in defaults: if member_name in defaults:
default_val = defaults[member_name] default_val = defaults[member_name]
@@ -4627,8 +4957,8 @@ class ExprCallHandle(BaseHandle):
if not isinstance(StructType, ir.IdentifiedStructType): if not isinstance(StructType, ir.IdentifiedStructType):
return return
members = Gen.class_members.get(ClassName, []) members = Gen.class_members.get(ClassName, [])
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes # base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = 1 if has_vtable else 0 base = Gen._member_base(ClassName)
for i, arg in enumerate(Node.args): for i, arg in enumerate(Node.args):
if i >= len(members): if i >= len(members):
break break
@@ -4674,6 +5004,9 @@ class ExprCallHandle(BaseHandle):
def _HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: def _HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
# pointee-volatile 基址名后缀(同 HandlesExprAttr._HandleSubscriptLlvm);空后缀
# ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen) ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen)
if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'): if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'):
obj_val = self.HandleExprLlvm(Node.value) obj_val = self.HandleExprLlvm(Node.value)
@@ -4706,7 +5039,7 @@ class ExprCallHandle(BaseHandle):
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
LoadedValueVal = Gen._load(ValueVal, name="Load_subscript") LoadedValueVal = Gen._load(ValueVal, name="Load_subscript")
if isinstance(LoadedValueVal.type, ir.PointerType): if isinstance(LoadedValueVal.type, ir.PointerType):
ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name="subscript") ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
return ElemPtr return ElemPtr
if isinstance(ValueVal.type, ir.PointerType): if isinstance(ValueVal.type, ir.PointerType):
pointee = ValueVal.type.pointee pointee = ValueVal.type.pointee
@@ -4717,12 +5050,12 @@ class ExprCallHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
return Gen._load(ElemPtr, name="arr_subscript_val") return Gen._load(ElemPtr, name="arr_subscript_val")
else: else:
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript") ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(pointee, ir.IntType): if isinstance(pointee, ir.IntType):
return Gen._load(ElemPtr, name="subscript_val") return Gen._load(ElemPtr, name="subscript_val")
return ElemPtr return ElemPtr
@@ -4738,11 +5071,11 @@ class ExprCallHandle(BaseHandle):
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else: else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
return ElemPtr return ElemPtr
arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc) Gen._store(ValueVal, arr_alloc)
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript") ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
return ElemPtr return ElemPtr
return None return None
+20 -4
View File
@@ -194,8 +194,18 @@ class ExprLambdaHandle(BaseHandle):
result_type = ir.IntType(8) result_type = ir.IntType(8)
# 重新处理 Else 分支的类型转换 # 重新处理 Else 分支的类型转换
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType): elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 指针转整数 if isinstance(BodyVal.type.pointee, ir.IntType) and BodyVal.type.pointee.width == 8:
BodyVal = Gen.builder.ptrtoint(BodyVal, result_type, name="ifexp_body_ptr2int") # 1 字符字符串字面量(i8*)与 char(i8)混用(如
# `p if p is not None else '?'`):取首字符。与 else 侧对称
# (旧代码仅 else 侧这样处理,body 侧 ptrtoint 截断地址是错的)
BodyVal = Gen._load(BodyVal, name="ifexp_body_Load_char")
else:
# 其余指针 → 整数:隐式转换一律禁止(两分支类型不一致须显式 cast)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:条件表达式两分支类型不一致({BodyVal.type} / {result_type});"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
Gen.builder.branch(MergeBB) Gen.builder.branch(MergeBB)
# 在 ElseBB 末尾添加类型转换(如果需要)和分支 # 在 ElseBB 末尾添加类型转换(如果需要)和分支
if not ElseEndBB.is_terminated: if not ElseEndBB.is_terminated:
@@ -213,11 +223,17 @@ class ExprLambdaHandle(BaseHandle):
result_type = ir.IntType(8) result_type = ir.IntType(8)
# 重新处理 BodyVal 分支的类型转换(已经在上面处理过了) # 重新处理 BodyVal 分支的类型转换(已经在上面处理过了)
elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType): elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 如果指针指向 i8,加载字符值
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8: if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
# 1 字符字符串字面量(i8*)与 char(i8)混用(原语义,保留):
# 取首字符;只有非 i8* 的指针转整数才 fail-fast
OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char") OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char")
else: else:
OrelseVal = Gen.builder.ptrtoint(OrelseVal, result_type, name="ifexp_orelse_ptr2int") # 其余指针 → 整数:隐式转换一律禁止(两分支类型不一致须显式 cast)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:条件表达式两分支类型不一致({OrelseVal.type} / {result_type});"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
Gen.builder.branch(MergeBB) Gen.builder.branch(MergeBB)
Gen.builder.position_at_start(MergeBB) Gen.builder.position_at_start(MergeBB)
result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result") result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result")
+11
View File
@@ -6,6 +6,7 @@ if TYPE_CHECKING:
from lib.core.Handles.HandlesBase import BaseHandle from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast import vast as ast
import llvmlite.ir as ir import llvmlite.ir as ir
from lib.core.SymbolUtils import RequireIntLiteralStorable
class ExprOpsHandle(BaseHandle): class ExprOpsHandle(BaseHandle):
@@ -108,6 +109,10 @@ class ExprOpsHandle(BaseHandle):
vname: str = Node.left.id vname: str = Node.left.id
if vname in Gen.var_signedness and Gen.var_signedness[vname]: if vname in Gen.var_signedness and Gen.var_signedness[vname]:
is_unsigned = True is_unsigned = True
# 双指针算术 fail-fast 已**下沉到单一收敛点** `Gen.emit_binary_op`
# (LLVMCG/ExprGen.py):BinOp 与 AugAssign(+=、-=、*= …)都经该函数,
# 判据(运算符属算术/位运算/移位集合 且 两操作数都是 LLVM 指针)与报文
# 在那里一次收口。此处不再保留副本,避免两处判据/报文分叉。
result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned) result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned)
return result return result
@@ -244,6 +249,12 @@ class ExprOpsHandle(BaseHandle):
elif OpSymbol == '+': elif OpSymbol == '+':
return OperandVal return OperandVal
elif OpSymbol == '-': elif OpSymbol == '-':
# 一元减 + 整数字面量的**联合越界判定**(与 TPV `_parse_factor` 同口径):
# `-` 不属于字面量,幅值合法(≤ 2^64-1)的字面量取负后可能 < -2^63。
if isinstance(Node.operand, ast.Constant) \
and isinstance(Node.operand.value, int) \
and not isinstance(Node.operand.value, bool):
RequireIntLiteralStorable(Gen, self.Trans, Node, -Node.operand.value)
if isinstance(OperandVal.type, ir.IntType): if isinstance(OperandVal.type, ir.IntType):
return Gen.builder.neg(OperandVal, name="neg_result") return Gen.builder.neg(OperandVal, name="neg_result")
elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)): elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)):
+3 -1
View File
@@ -339,7 +339,9 @@ class ExprUtils:
attr_name: str = node.attr attr_name: str = node.attr
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name) AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None): if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
return ir.IntType(32) # 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型)
return self.Trans.LlvmGen._enum_int_type(
getattr(AttrInfo, 'EnumName', None), AttrInfo)
return ir.IntType(32) return ir.IntType(32)
elif isinstance(node, ast.BinOp): elif isinstance(node, ast.BinOp):
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left) left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
+4 -10
View File
@@ -360,16 +360,10 @@ class ForHandle(BaseHandle):
old_var: ir.Value = Gen.variables[var_name] old_var: ir.Value = Gen.variables[var_name]
if isinstance(old_var.type, ir.PointerType): if isinstance(old_var.type, ir.PointerType):
elem_type: ir.Type = old_var.type.pointee elem_type: ir.Type = old_var.type.pointee
# 计算元素大小(字节) # 尺寸统一走 Gen._get_struct_size(与 __sizeof__ / alloc 同源,含对齐/填充):
elem_size: int = 8 # 默认指针大小 # 原实现只认 Int/Float/Double/Pointer,struct/array 落到「默认 8」,
if isinstance(elem_type, ir.IntType): # 于是 malloc(8) 后整块 store → 堆越界写坏相邻块。
elem_size = max(1, elem_type.width // 8) elem_size: int = Gen._get_struct_size(elem_type)
elif isinstance(elem_type, ir.FloatType):
elem_size = 4
elif isinstance(elem_type, ir.DoubleType):
elem_size = 8
elif isinstance(elem_type, ir.PointerType):
elem_size = 8
# malloc 堆存储 # malloc 堆存储
heap_raw: ir.Value = Gen.builder.call(malloc_func, [ir.Constant(ir.IntType(64), elem_size)], name=f"heap_{var_name}") heap_raw: ir.Value = Gen.builder.call(malloc_func, [ir.Constant(ir.IntType(64), elem_size)], name=f"heap_{var_name}")
heap_ptr: ir.Value = Gen.builder.bitcast(heap_raw, ir.PointerType(elem_type), name=f"heap_{var_name}_ptr") heap_ptr: ir.Value = Gen.builder.bitcast(heap_raw, ir.PointerType(elem_type), name=f"heap_{var_name}_ptr")
+10 -4
View File
@@ -898,7 +898,7 @@ class FunctionHandle(BaseHandle):
if '[' in replacement: if '[' in replacement:
SubNode: ast.AST | None = None SubNode: ast.AST | None = None
try: try:
SubNode = ast.parse(replacement, mode='eval').body SubNode = ast.parse(replacement, '<stub:type_expr>', mode='eval').body
except Exception: except Exception:
SubNode = None SubNode = None
if not isinstance(SubNode, ast.Subscript): if not isinstance(SubNode, ast.Subscript):
@@ -936,7 +936,7 @@ class FunctionHandle(BaseHandle):
slice_node = self._make_replacement_node(inner_name) slice_node = self._make_replacement_node(inner_name)
else: else:
try: try:
slice_node = ast.parse(inner_name, mode='eval').body slice_node = ast.parse(inner_name, '<stub:type_expr>', mode='eval').body
except Exception: except Exception:
slice_node = self._make_name_node(inner_name) slice_node = self._make_name_node(inner_name)
if slice_node is None: if slice_node is None:
@@ -1627,8 +1627,10 @@ class FunctionHandle(BaseHandle):
if Arg.annotation: if Arg.annotation:
try: try:
PInfo = self.ResolveAnnotationType(Arg.annotation) PInfo = self.ResolveAnnotationType(Arg.annotation)
if PInfo and (PInfo.DataConst or PInfo.VarConst): # const/volatile 形参登记(统一登记入口)。const 判据沿用形参历史
Gen.var_const_flags[Arg.arg] = True # 条件(IsParam=True,见 BaseGen.RegisterVarQualifiers 说明);
# volatile / pointee-volatile 与 TransPyV 侧同口径。
Gen.RegisterVarQualifiers(Arg.arg, PInfo, Arg.annotation, IsParam=True)
except Exception as _e: except Exception as _e:
if _config_mode == "strict": if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}") self.Trans.LogWarning(f"异常被忽略: {_e}")
@@ -2140,6 +2142,10 @@ class FunctionHandle(BaseHandle):
Gen.var_struct_class = {} Gen.var_struct_class = {}
Gen.var_ptr_element = {} Gen.var_ptr_element = {}
Gen.var_const_flags = {} Gen.var_const_flags = {}
# const/volatile 的**函数作用域层**随函数出栈清空(模块级层 global_* 与
# 类字段层 field_* 是持久的,不在此清;见 BaseGen 的登记说明)。
Gen.var_volatile_flags = {}
Gen.var_pointee_volatile_flags = {}
# 恢复保存的 var_type_info # 恢复保存的 var_type_info
Gen.var_type_info = saved_var_type_info Gen.var_type_info = saved_var_type_info
Gen.var_type_assignments = saved_var_type_assignments Gen.var_type_assignments = saved_var_type_assignments
+369 -20
View File
@@ -14,7 +14,9 @@ from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode from lib.constants.config import mode as _config_mode
from lib.includes import t from lib.includes import t
from lib.includes.t import CTypeRegistry from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext
from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords
from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod
# 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写, # 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写,
@@ -25,6 +27,107 @@ _MARKER_ANNOT_NAMES: frozenset[str] = frozenset({
'State', 'TLS', 'State', 'TLS',
}) })
# 标记基类名(不是真实父类):与 HandlesClassDef._EmitClassLlvm 的 base 跳过表同口径。
_MARKER_BASE_NAMES: frozenset[str] = frozenset({
'Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum',
'Exception', 'Enum',
})
def _HasRealParentClass(Node: ast.ClassDef) -> bool:
"""类是否声明了**真实父类**(排除 Object/CVTable/NoVTable/CEnum/... 标记基类)。"""
for base in getattr(Node, 'bases', None) or []:
base_name: str | None = None
if hasattr(base, 'id'):
base_name = base.id
elif hasattr(base, 'attr'):
base_name = base.attr
elif isinstance(base, ast.Subscript):
if hasattr(base.value, 'id'):
base_name = base.value.id
elif hasattr(base.value, 'attr'):
base_name = base.value.attr
if base_name and base_name not in _MARKER_BASE_NAMES:
return True
return False
def _VtableByClassLevelForImport(Node: ast.ClassDef, Gen: object) -> bool:
"""跨模块导入类的 VtableByClassLevel(忽略方法级 CVTable 标记的 vtable 状态)。
与 HandlesClassDef._resolve_class_flags 的口径一致:类级 @t.CVTable 或
"有真实父类且父类不是 NoVTable" ⇒ True。供 SymbolUtils.IsVtableSlotMethod
判定方法是否占虚表槽(唯一的判据源),使导入侧的虚表槽序与定义模块逐槽相同。
"""
if getattr(Node, 'decorator_list', None):
for decorator in Node.decorator_list:
if isinstance(decorator, ast.Attribute) and decorator.attr == 'CVTable':
return True
if isinstance(decorator, ast.Name) and decorator.id == 'CVTable':
return True
if not _HasRealParentClass(Node):
return False
_novtable_set: set = getattr(Gen, '_cross_module_novtable', None) or set()
for base in getattr(Node, 'bases', None) or []:
base_name: str | None = None
if hasattr(base, 'id'):
base_name = base.id
elif hasattr(base, 'attr'):
base_name = base.attr
if base_name and base_name not in _MARKER_BASE_NAMES:
if base_name in _novtable_set:
return False
return True
def _ParseFailureSite(Exc: BaseException) -> str:
"""解析异常 → `行[:列]` 文本;定位信息完全取不到时返回 `'?'`。
CPython/vast 的 SyntaxError 若按 (msg, (file, lineno, offset, text)) 构造则带
`.lineno`/`.offset`;而 TPC 自研 vast 的 Parser/ExprParser 只把行号写进报文
(`f"... at line {tok.Line}"`,见 lib/vast/Parser.py:826、ExprParser.py:640),
字段为空 ⇒ 必须正则兜底,否则诊断退化成"无行号",无法直接跳到失败行。
"""
Line: int = int(getattr(Exc, 'lineno', 0) or 0)
Col: int = int(getattr(Exc, 'offset', 0) or 0)
if not Line:
Matched: re.Match | None = re.search(r'at line (\d+)', str(Exc))
if Matched:
Line = int(Matched.group(1))
if not Line:
return '?'
return f"{Line}:{Col}" if Col else str(Line)
def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequence: str) -> None:
"""声明文件"读 → parse"失败的统一诊断口径(strict fail-fast / 非严格告警)。
诊断必带四要素:**文件路径**、**失败行[:列]**、**异常类型与文本**、**后果说明**
(后果由调用方按点位给出,如"该模块声明将全部缺失,后续可能表现为
`Undefined function`")。原实现(`_LoadModuleDeclarationsFromFile:364-367`)只在
strict 模式调 `Trans.LogWarning`——而 `Trans.Warnings` 全仓只被 append、无人消费
(BaseTranslator.py:55/137),**连 strict 模式都是无声的**;非严格模式更是直接
`return`。于是 `includes/ast/base.vp:168` 的 `enum ASTFlag:` 让整个 base.vp 的
声明(含顶层 `_init_ast`)一个都不注册,下游只报 `Undefined function: '_init_ast'`,
浪费了一轮排查。
fail-fast 依据:仓库有严格模式开关 `lib/constants/config.py:59 mode = "strict"`,
且既有多处同口径 `if _config_mode == "strict": raise`(本文件
`_RegisterSubModuleSha1:547`、`_build_stub_index:2443`、`_TryLoadStructFromStub:2539`
等)——本函数与它们一致,未放宽任何既有 fail-fast。
`FileNotFoundError` 例外:属"isfile 与 open 之间文件被删/被 `--clean` 清掉"的
竞态(先例见本文件 `_build_stub_index:2439-2441`),不是"源码解析失败",
故只告警、不触发 fail-fast。
"""
Site: str = _ParseFailureSite(Exc)
Message: str = (
f"声明解析失败 {FilePath}:{Site} — {type(Exc).__name__}: {Exc};{Consequence}"
)
if _config_mode == "strict" and not isinstance(Exc, FileNotFoundError):
raise ImportError(Message) from Exc
_vlog().warning(Message, "Exception")
class ImportHandle(BaseHandle): class ImportHandle(BaseHandle):
_struct_Load_cache = {} _struct_Load_cache = {}
@@ -353,10 +456,21 @@ class ImportHandle(BaseHandle):
try: try:
with open(pyi_path, 'r', encoding='utf-8') as f: with open(pyi_path, 'r', encoding='utf-8') as f:
ModuleCode = f.read() ModuleCode = f.read()
ModuleTree = ast.parse(ModuleCode) # Viper 软关键字层(与 TPC 其余前端入口同一层):`.vp` 源码可含
# `enum X:` / `fn` 等语法糖,直接 vast.parse 会在糖行 SyntaxError →
# 被下面的 except 吞掉并 return → **该模块的全部声明(函数/类/全局)
# 一个都不注册**,调用方随后报 "Undefined function"
# (includes/ast/base.vp 的 `enum ASTFlag:` → 六处 `from .base import …`
# 全部拿不到 `_init_ast`,即本次现场)。`.vpi`/`.pyi` 无糖,展开是空操作。
ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), pyi_path)
except Exception as _e: except Exception as _e:
if _config_mode == "strict": # 统一诊断口径(strict fail-fast / 非严格告警),必带文件+行号+异常+后果。
self.Trans.LogWarning(f"异常被忽略: {_e}") # 原实现"吞异常 + return"(strict 也只写进无人消费的 Trans.Warnings),
# 是本次 `Undefined function: '_init_ast'` 被伪装成下游症状的直接原因。
_ReportDeclarationParseFailure(
pyi_path, _e,
"该模块的全部声明(函数/类/全局/枚举)不会注册,"
"后续引用方可能表现为 Undefined function / Undefined struct")
return return
# 计算源模块名(从 .pyi 文件路径推导)。 # 计算源模块名(从 .pyi 文件路径推导)。
@@ -715,7 +829,9 @@ class ImportHandle(BaseHandle):
try: try:
with open(FullModulePath, 'r', encoding='utf-8') as f: with open(FullModulePath, 'r', encoding='utf-8') as f:
ModuleCode = f.read() ModuleCode = f.read()
ModuleTree = ast.parse(ModuleCode) # 同上:`.vp` 源码须先过 Viper 软关键字层再 vast.parse(否则含
# `enum X:` / `fn` 的模块声明整体丢失,落到 except 的 stub 兜底)。
ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), FullModulePath)
if module_name == 'pyzlib': if module_name == 'pyzlib':
func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)] func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)]
has_functions_or_classes = False has_functions_or_classes = False
@@ -854,6 +970,21 @@ class ImportHandle(BaseHandle):
elif 'includes' in FullModulePath and module_name: elif 'includes' in FullModulePath and module_name:
self._LoadDeclarationsFromStubLlvm(module_name, Gen) self._LoadDeclarationsFromStubLlvm(module_name, Gen)
except Exception as e: except Exception as e:
# 同族口径:读/parse 失败必须显式(文件+行号+异常+后果),不允许静默降级。
# 非严格模式仍保留既有 stub 兜底(StubGen 产物声明仍可用);strict 模式
# 由 _ReportDeclarationParseFailure 直接 fail-fast,与 :412 主点一致。
#
# **限定条件**:只有"确实有源码文件却读/解析失败"才算声明解析失败。本函数
# 的 `FullModulePath` 可为 None——TPV 自举工程 includes 为合并形态,源码模块
# (如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由上方
# `_LoadDeclarationsFromStubLlvm` 从 temp/*.stub.ll 加载,属**正常路径**
# (见本函数 :750 附近的长注释);此时 `open(None)` 的 TypeError 与"文件恰好
# 不在"都不应触发 fail-fast。
if FullModulePath and os.path.isfile(FullModulePath):
_ReportDeclarationParseFailure(
FullModulePath, e,
"该模块的源码声明全部未注册,"
"仅能依赖 stub 兜底(非严格模式);声明可能与源码不一致")
if module_name: if module_name:
self._LoadDeclarationsFromStubLlvm(module_name, Gen) self._LoadDeclarationsFromStubLlvm(module_name, Gen)
@@ -1597,6 +1728,13 @@ class ImportHandle(BaseHandle):
if isinstance(arg.value, ast.Attribute): if isinstance(arg.value, ast.Attribute):
if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed': if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed':
IsPacked = True IsPacked = True
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
# 与 HandlesClassDef._resolve_class_flags / TPV _detect_vtable_status 的
# `_has_any_func_cvtable` 分支对齐(跨模块这里只能判"类级 NoVTable /
# 无真实父类"两种可判定形态;父类 vtable 状态由 .pyi 按需注册路径解析)。
if not IsCVTable and ClassHasFuncLevelCVTable(Node):
if IsNoVTable or not _HasRealParentClass(Node):
IsCVTable = True
return (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, return (IsGenericClass, IsCenum, IsRenum, IsCpythonObject,
IsCVTable, IsNoVTable, IsPacked) IsCVTable, IsNoVTable, IsPacked)
@@ -1642,6 +1780,14 @@ class ImportHandle(BaseHandle):
EnumTypeNode.Name = ClassName EnumTypeNode.Name = ClassName
EnumTypeNode.BaseType = t.CEnum(ClassName) EnumTypeNode.BaseType = t.CEnum(ClassName)
EnumTypeNode.IsEnum = True EnumTypeNode.IsEnum = True
# [跨模块枚举宽度] 读侧重推导:.vpi/.pyi 保留成员注解(`Name: T`),
# 故在本处按"最宽成员类型"重算(与定义端 HandlesClassDef 同一规则源
# EnumClassIntWidth),使跨模块使用同一枚举时宽度一致。本函数会以短名
# 覆盖共享符号表(_shared_sym.json pickle)中的同名条目,若不在此登记
# 宽度,覆盖后即退化为默认 i32(= 上一轮遗留缺口)。旧产物缺注解时
# 自然回落 32(Default),不崩。
EnumWidth: int = EnumClassIntWidth(Node.body, self.Trans.SymbolTable)
EnumTypeNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(ClassName, EnumTypeNode) self.Trans.SymbolTable.insert(ClassName, EnumTypeNode)
next_enum_value: int = 0 next_enum_value: int = 0
for item in Node.body: for item in Node.body:
@@ -1672,6 +1818,7 @@ class ImportHandle(BaseHandle):
MemberNode.EnumName = ClassName MemberNode.EnumName = ClassName
MemberNode.Lineno = item.lineno MemberNode.Lineno = item.lineno
MemberNode.IsEnumMember = True MemberNode.IsEnumMember = True
MemberNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(VarName, MemberNode) self.Trans.SymbolTable.insert(VarName, MemberNode)
self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode) self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode)
self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode) self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode)
@@ -1776,6 +1923,26 @@ class ImportHandle(BaseHandle):
next_tag = value + 1 next_tag = value + 1
RenumTypeNode.RenumVariants = variant_names RenumTypeNode.RenumVariants = variant_names
return return
# 【声明发射重入标记】本函数主体会重入自身:成员收集(CollectAnnAssignMember
# → ResolveAnnotationType → 泛型特化 list[T]/GSListNode[T] → _EmitClassLlvm
# → StructGen._generate_structs → _TryLoadStructFromStub(X)
# → _TryLoadClassMembersFromPyi(X) → 本函数(X))。首字段即指针/容器的类必现
# (llvmlite.Param 的 Attrs、match.MatchOr 的 patterns、stmts.Interactive 的
# body…),且每轮仅 4 帧:本函数 → _TryLoadStructFromStub → 成员加载 → 本函数。
# 两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同,失败时键还会被
# 清除(_TryLoadStructFromStub 的"仍 opaque ⇒ 删键重试"),缓存守卫挡不住
# ⇒ 无限递归(RecursionError 被上层 except 吞掉,表现为类型解析静默退化)。
# 登记 (ClassName, source_sha1);由 `_TryLoadStructFromStub` 里的"顺带加载成员"
# 那一步短路(该边是递归环的唯一闭合边:结构体 body 加载会连带成员加载 → 又回到
# 本函数);本函数自身**不**短路——重入的成员收集是"把表填起来"的唯一途径,
# 消费者(重入编译的其他模块方法体按名查字段)必须能拿到它。
# 放在 CEnum/REnum 早退(上方 return)之后:那两个分支不加载成员/结构体。
_emit_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None)
if _emit_inprog is None:
_emit_inprog = set()
Gen._extern_decl_emit_in_progress = _emit_inprog
_emit_key: tuple = (ClassName, source_sha1)
_emit_inprog.add(_emit_key)
# 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub # 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub
# 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods) # 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods)
# 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32) # 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32)
@@ -1814,6 +1981,11 @@ class ImportHandle(BaseHandle):
# 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。 # 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。
# 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。 # 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。
if not IsGenericClass: if not IsGenericClass:
# 虚表槽筛选:方法级 `novirtual def`(⇒ @t.NoVTable)不进槽、方法级
# `virtual def`(⇒ @t.CVTable)进槽——判据为 SymbolUtils.IsVtableSlotMethod
# (唯一规则源,与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径),
# 保证跨模块虚表槽序与定义模块逐槽一致。
_VtByClassLevel: bool = _VtableByClassLevelForImport(Node, Gen)
for _item in Node.body: for _item in Node.body:
if isinstance(_item, ast.FunctionDef): if isinstance(_item, ast.FunctionDef):
_has_methods_prescan = True _has_methods_prescan = True
@@ -1825,7 +1997,8 @@ class ImportHandle(BaseHandle):
if _MethodName in ('__new__', '__init__', '__before_init__'): if _MethodName in ('__new__', '__init__', '__before_init__'):
continue continue
_FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}" _FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}"
if _FuncFullName not in Gen.class_methods[ClassName]: _IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(_item, _VtByClassLevel)
if _IsSlot and _FuncFullName not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(_FuncFullName) Gen.class_methods[ClassName].append(_FuncFullName)
if _has_methods_prescan and IsCVTable: if _has_methods_prescan and IsCVTable:
Gen._cross_module_vtable_classes.add(ClassName) Gen._cross_module_vtable_classes.add(ClassName)
@@ -2016,10 +2189,14 @@ class ImportHandle(BaseHandle):
# 构造函数(__new__/__init__/__before_init__)不放入 vtable, # 构造函数(__new__/__init__/__before_init__)不放入 vtable,
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。 # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。
# register_method 内部也会过滤,此处仅跳过 class_methods.append。 # register_method 内部也会过滤,此处仅跳过 class_methods.append。
# 方法级修饰符(virtual def/novirtual def)⇒ 是否占槽按
# SymbolUtils.IsVtableSlotMethod(唯一规则源)判定,与定义模块逐槽一致。
_IsSlot2: bool = (not IsCVTable) or IsVtableSlotMethod(
item, _VtableByClassLevelForImport(Node, Gen))
if MethodName not in ('__new__', '__init__', '__before_init__'): if MethodName not in ('__new__', '__init__', '__before_init__'):
if FuncFullName not in Gen.class_methods[ClassName]: if _IsSlot2 and FuncFullName not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(FuncFullName) Gen.class_methods[ClassName].append(FuncFullName)
Gen.register_method(ClassName, FuncFullName) Gen.register_method(ClassName, FuncFullName, _IsSlot2)
is_item_static = any(isinstance(d, ast.Name) and d.id == 'staticmethod' for d in item.decorator_list) is_item_static = any(isinstance(d, ast.Name) and d.id == 'staticmethod' for d in item.decorator_list)
is_item_property = any(isinstance(d, ast.Name) and d.id == 'property' for d in item.decorator_list) is_item_property = any(isinstance(d, ast.Name) and d.id == 'property' for d in item.decorator_list)
is_item_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list) is_item_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list)
@@ -2158,6 +2335,13 @@ class ImportHandle(BaseHandle):
# 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。 # 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。
_any_parent_loaded: bool = False _any_parent_loaded: bool = False
_all_inherited: list[tuple] = [] _all_inherited: list[tuple] = []
# 展平完整性判定(供 `_members_flattened` 登记):仅当"每个声明父类都已在
# class_members 中就绪且其自身已展平",或"占位符路径已按 struct 槽位补齐
# 继承前缀"时才为 True。未完成(典型:声明期 struct 尚 opaque / 父类所在
# 模块 stub 未就绪)时不登记,后续查询会重跑本节把继承前缀补齐
# —— 使展平结果与"父类/struct 就绪的先后顺序"无关。
_flat_complete: bool = not _ParentClasses
_placeholder_applied: bool = False
# 按继承源去重的跨父类共享 seen 集合(必须在 for 循环外,跨所有父类 # 按继承源去重的跨父类共享 seen 集合(必须在 for 循环外,跨所有父类
# 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个 # 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个
# 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段) # 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段)
@@ -2270,6 +2454,14 @@ class ImportHandle(BaseHandle):
_inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)] _inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)]
# 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致 # 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致
Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName] Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName]
_placeholder_applied = True
if _ParentClasses:
# 完整展平的判据:占位符路径已按 struct 槽位补齐继承前缀,或
# 每个声明父类都已在 class_members 中就绪且其自身已展平。
_flat_complete = _placeholder_applied or all(
(_pc in Gen.class_members and _pc in Gen._members_flattened)
for _pc in _ParentClasses if _pc
)
# 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。 # 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。
# 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的 # 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的
# _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque # _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque
@@ -2277,6 +2469,10 @@ class ImportHandle(BaseHandle):
# 本保护要消除的目标。 # 本保护要消除的目标。
Gen._collecting_members_set.discard(ClassName) Gen._collecting_members_set.discard(ClassName)
Gen._generate_structs() Gen._generate_structs()
if _flat_complete:
# 登记"该表已按父类展平",供 _TryLoadClassMembersFromPyi 的 guard 与
# _member_base 的查询前补齐使用。未登记 ⇒ 后续任一查询都可重跑本节补齐。
Gen._members_flattened.add(ClassName)
# ============================================================ # ============================================================
# 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位), # 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位),
# 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只 # 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只
@@ -2301,12 +2497,18 @@ class ImportHandle(BaseHandle):
for _m in Gen.class_methods.get(ClassName, []): for _m in Gen.class_methods.get(ClassName, []):
_mn: str = _m.split('.')[-1] if '.' in _m else _m _mn: str = _m.split('.')[-1] if '.' in _m else _m
_child_method_map[_mn] = _m _child_method_map[_mn] = _m
# 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册) # 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)。
# 方法级 `novirtual def`(⇒ @t.NoVTable)不占槽,**不得**用它覆盖父类槽位
# ——与 TPV _collect_vtable_methods 的 child_nvt 分支同口径(子类标记
# NoVTable 时槽位仍指向实际定义类的实现)。
_VtByClassLevelC: bool = _VtableByClassLevelForImport(Node, Gen)
for _item_m in getattr(Node, 'body', []): for _item_m in getattr(Node, 'body', []):
if isinstance(_item_m, ast.FunctionDef): if isinstance(_item_m, ast.FunctionDef):
_mn2: str = _item_m.name _mn2: str = _item_m.name
if _mn2 in ('__new__', '__init__', '__before_init__'): if _mn2 in ('__new__', '__init__', '__before_init__'):
continue continue
if not IsVtableSlotMethod(_item_m, _VtByClassLevelC):
continue
_child_method_map[_mn2] = f"{ClassName}.{_mn2}" _child_method_map[_mn2] = f"{ClassName}.{_mn2}"
_new_methods: list[str] = [] _new_methods: list[str] = []
_covered_names: set[str] = set() _covered_names: set[str] = set()
@@ -2348,6 +2550,8 @@ class ImportHandle(BaseHandle):
bw = bitfields.get(name, 0) bw = bitfields.get(name, 0)
Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset
current_bit_offset += bw current_bit_offset += bw
# 声明发射结束:解除重入标记(与上方 add 配对)。
_emit_inprog.discard(_emit_key)
def _build_stub_index(self) -> None: def _build_stub_index(self) -> None:
"""扫描所有 .stub.ll 文件构建 class_name → 条目列表索引 """扫描所有 .stub.ll 文件构建 class_name → 条目列表索引
@@ -2408,6 +2612,28 @@ class ImportHandle(BaseHandle):
_vlog().warning(f"构建 stub 索引失败: {_e}", "Exception") _vlog().warning(f"构建 stub 索引失败: {_e}", "Exception")
self._stub_index_scanned.add(stub_path) self._stub_index_scanned.add(stub_path)
@staticmethod
def _ExternDeclEmitting(Gen: LlvmGeneratorMixin, class_name: str, source_sha1: str | None) -> bool:
"""该类(同名同源)的跨模块声明发射是否**正在进行中**(递归重入判定)。
标记由 `_EmitExternalClassDeclLlvm` 主体开头登记、末尾解除(键
`(ClassName, source_sha1)`)。定义模块 sha1 未知(None)时按类名保守判定:
None 与 '<sha1>' 两种形态可能指同一个类,宁可本次漏加载(由最外层收尾)
也不递归——`_EmitExternalClassDeclLlvm` → `_TryLoadStructFromStub(X)`
→ `_TryLoadClassMembersFromPyi(X)` → `_EmitExternalClassDeclLlvm(X)`
每轮仅 4 帧,且两条加载路径的缓存键(带/不带 sha1 前缀)互不相同、
失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住,
会一直递归到 RecursionError(被上层 except 吞掉 ⇒ 类型解析静默退化)。
"""
_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None)
if not _inprog:
return False
if (class_name, source_sha1) in _inprog:
return True
if source_sha1 is None:
return any(_k[0] == class_name for _k in _inprog)
return False
def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None: def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None:
# 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义, # 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义,
# 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致 # 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致
@@ -2503,7 +2729,17 @@ class ImportHandle(BaseHandle):
raise raise
_vlog().warning(f"处理导入失败: {_e}", "Exception") _vlog().warning(f"处理导入失败: {_e}", "Exception")
stub_filename = actual_sha1 + '.stub.ll' stub_filename = actual_sha1 + '.stub.ll'
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen) # 【递归环闭合边 · 重入短路】该类的声明发射尚未结束(`_EmitExternalClassDeclLlvm`
# 正在跑:见其主体开头的 `_extern_decl_emit_in_progress` 登记)⇒ 本调用是环内一环:
# 发射(X) → _TryLoadStructFromStub(X) →(此处)成员加载(X) → 发射(X) …
# 每轮仅 4 帧,且两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同、
# 失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住 ⇒ 无限递归。
# 短路对象**只限"顺带加载成员"这一步**:上面的 struct body 解析照常执行
# (发射主体依赖它提前 set_body —— 否则 llvmlite `__len__` 的
# `assert self.elements is not None` 直接炸,见 _EmitExternalFuncDeclLlvm
# 的 `if _new_struct:`);成员由外层发射收尾统一收集。
if not ImportHandle._ExternDeclEmitting(Gen, class_name, source_sha1):
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
# 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、 # 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、
# 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久 # 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久
# 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。 # 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。
@@ -2515,6 +2751,82 @@ class ImportHandle(BaseHandle):
except KeyError: except KeyError:
pass pass
def EnsureClassMembersFlattened(self, class_name: str, Gen: LlvmGeneratorMixin) -> None:
"""查询前补齐:确保 `class_members[class_name]` 已按父类展平(幂等、顺序无关)。
背景:`_EmitExternalClassDeclLlvm` 的展平只在"父类成员表 + 声明期 struct
body 都已就绪"时才能算出继承前缀;若当时 struct 仍 opaque(定义模块 stub
尚未生成)或父类未就绪,表就停在"自有字段",而长度随之与 struct 元素数
不再自洽(delta = 缺失槽数)。此后 `_TryLoadClassMembersFromPyi` 的原
guard(`len>0` 即短路)会把这份残缺表永久冻结 ⇒ 该类**自有字段**的 GEP
下标缺了继承前缀而整体前移(`ExceptHandler.type` 返回 1 而非 8)。
本方法是该冻结的解除点(与 `FuncGen._member_base` 的查询侧调用配套):
只有"未登记已展平 且 该类有父类 且 struct 与成员表长度不自洽(含 struct
尚未登记)"才重跑展平;对已展平类、无父类类(含 REnum 变体等非继承键)
是纯内存判断直接返回,真正重跑的类另按依赖签名去重(见下)。
以 `(struct 元素数, 成员表长度)` 作为"依赖状态签名":同一签名下补齐无效
时不再重复尝试(避免依赖长期不就绪时在热路径反复重建);依赖一旦变化
(struct 被 `_generate_structs` 修正、或表被别处改动)签名变化即重试。
"""
if Gen is None:
return
_flat: set | None = getattr(Gen, '_members_flattened', None)
if _flat is None or class_name in _flat:
return
_inprog: set | None = getattr(Gen, '_members_flatten_in_progress', None)
if _inprog is None or class_name in _inprog:
return
if class_name in (getattr(Gen, '_collecting_members_set', None) or ()):
# 该类的字段正在收集中(重入路径)⇒ 不在此处重跑展平,
# 交由收集完成后的 `_EmitExternalClassDeclLlvm` 正常收尾。
return
_parents: list = (getattr(Gen, 'class_parents', {}) or {}).get(class_name) or []
if not _parents:
# 无父类 ⇒ 展平平凡成立。表非空时登记(后续查询直接短路);表为空时
# 不登记、也不在此补齐(那是"表未加载",由 _get_member_offset 既有的
# 懒加载路径负责,不在本专项范围内)。
if Gen.class_members.get(class_name):
_flat.add(class_name)
return
_tbl: list = Gen.class_members.get(class_name) or []
_st = Gen.structs.get(class_name)
_num_elements: int = -1
if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None:
_num_elements = len(_st.elements)
if (_num_elements - len(_tbl)) in (0, 1):
# struct 布局与成员表长度自洽(无 vtable 槽 / 恰一个 vtable 槽)
# ⇒ 已展平:登记,后续查询直接短路。
_flat.add(class_name)
return
_sig: tuple[int, int] = (_num_elements, len(_tbl))
_sigs: dict = getattr(Gen, '_members_flatten_repair_sig', None)
if _sigs is None:
_sigs = {}
Gen._members_flatten_repair_sig = _sigs
if _sigs.get(class_name) == _sig:
# 同一依赖状态下补齐无效(父类/struct 仍未就绪)⇒ 不重复尝试。
return
_sigs[class_name] = _sig
if not hasattr(self, 'Trans') or self.Trans is None:
return
_sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(class_name)
_cands: list[str] = [f"{_sha1}.stub.ll"] if _sha1 else []
if not _cands:
_sha1_map: dict = getattr(Gen, 'ModuleSha1Map', {}) or {}
_cands = [f"{_s}.stub.ll" for _s in _sha1_map.values()]
_inprog.add(class_name)
try:
for _stub in _cands:
try:
if self._TryLoadClassMembersFromPyi(class_name, _stub, Gen):
break
except Exception:
continue
finally:
_inprog.discard(class_name)
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin, def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin,
force: bool = False) -> bool: force: bool = False) -> bool:
"""从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。 """从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。
@@ -2526,19 +2838,42 @@ class ImportHandle(BaseHandle):
""" """
ProjectRoot = self._find_project_root() ProjectRoot = self._find_project_root()
temp_dir = os.path.join(ProjectRoot, 'temp') temp_dir = os.path.join(ProjectRoot, 'temp')
if (not force) and class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0: # guard(2026-xx 收紧):只在"表非空 **且已登记为已展平**"时短路。原实现
# guard 命中:class_members 已存在(可能从别处预置、不完整),但 # 只看 `len>0`,于是"停在自有字段"的残缺表(展平未完成)一旦生成就再也不能
# class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names # 补齐(顺序敏感)——这正是自有字段下标缺继承前缀的成因。未展平的表允许
# 重跑 `_EmitExternalClassDeclLlvm`(其自有字段收集按名去重、继承前缀按名
# 去重,重跑幂等),从而与父类/struct 的就绪顺序无关。
# 【重入短路 · 与成员表长度解耦】该类**正在**被 `_EmitExternalClassDeclLlvm`
# 收集字段(`_collecting_members_set` 置位于本文件 :2054、解除于 :2481)时,
# 本次调用必是递归重入,直接返回、交由最外层收尾。
# 为什么判据不能带 `len(class_members)>0`:类内**第一个**字段的注解解析就会
# 重入本方法 —— CollectAnnAssignMember → ResolveAnnotationType → 泛型特化
# (list[T] 等)→ _EmitClassLlvm → StructGen._generate_structs
# → _TryLoadStructFromStub(X) → 本方法(X)。此刻 class_members[X] 仍为空
# (第一个字段尚未收集完),旧判据(要求 len>0)放行 ⇒ 无限递归(每轮 4 帧):
# _TryLoadClassMembersFromPyi(X) → _EmitExternalClassDeclLlvm(X)
# → _TryLoadStructFromStub(X) → _TryLoadClassMembersFromPyi(X) …
# 现场(2026-09-24 深度探针):match.MatchOr 的 patterns、stmts.Interactive 的
# body 等"首字段即指针/容器"的类;RecursionError 被上层 except 吞掉后表现为
# `WARN: … 忽略异常 maximum recursion depth exceeded` + 类型解析静默退化
# (失败的 typedef 报文、注解赋值 RHS 求值为空等次生现象)。
_collecting: set | None = getattr(Gen, '_collecting_members_set', None)
if ((not force) and (
(_collecting is not None and class_name in _collecting)
or (class_name in Gen.class_members
and len(Gen.class_members[class_name]) > 0
and class_name in Gen._members_flattened))):
# guard 命中:class_members 已存在(可能从别处预置、不完整)或该类正在
# 收集中;但 class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names
# 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114) # 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114)
# → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载 # → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载
# class_members,避免覆盖可能更完整的已有数据)。 # class_members,避免覆盖可能更完整的已有数据)。
_path0: str = os.path.join(temp_dir, stub_filename.replace('.stub.ll', '.vpi'))
try: try:
_pyi_b0: str = stub_filename.replace('.stub.ll', '.vpi')
_path0: str = os.path.join(temp_dir, _pyi_b0)
_tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0) _tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0)
if _tree0 is None and os.path.isfile(_path0): if _tree0 is None and os.path.isfile(_path0):
with open(_path0, 'r', encoding='utf-8') as _fh0: with open(_path0, 'r', encoding='utf-8') as _fh0:
_tree0 = ast.parse(_fh0.read()) _tree0 = ast.parse(_fh0.read(), _path0)
getattr(self, '_pyi_cache', {})[_path0] = _tree0 getattr(self, '_pyi_cache', {})[_path0] = _tree0
if _tree0 is not None: if _tree0 is not None:
for _n0 in _tree0.body: for _n0 in _tree0.body:
@@ -2553,8 +2888,14 @@ class ImportHandle(BaseHandle):
Gen.class_parents = {} Gen.class_parents = {}
Gen.class_parents[class_name] = _b0 Gen.class_parents[class_name] = _b0
break break
except Exception: except Exception as _e0:
pass # 同族口径:此处原为 `except Exception: pass`(静默)。class_parents
# 未回填 ⇒ 多继承/菱形展平不完整 ⇒ class_members 长度不确定 ⇒
# this_adjust 错位。改为显式诊断(strict fail-fast / 非严格告警)。
_ReportDeclarationParseFailure(
_path0, _e0,
f"类 '{class_name}' 的 class_parents 未能回填,"
"多继承展平可能不完整(字段下标/this_adjust 可能错位)")
return False return False
pyi_basename = stub_filename.replace('.stub.ll', '.vpi') pyi_basename = stub_filename.replace('.stub.ll', '.vpi')
pyi_path = os.path.join(temp_dir, pyi_basename) pyi_path = os.path.join(temp_dir, pyi_basename)
@@ -2576,9 +2917,15 @@ class ImportHandle(BaseHandle):
return False return False
try: try:
with open(pyi_path, 'r', encoding='utf-8') as f: with open(pyi_path, 'r', encoding='utf-8') as f:
self._pyi_cache[pyi_path] = ast.parse(f.read()) self._pyi_cache[pyi_path] = ast.parse(f.read(), pyi_path)
except Exception as e: except Exception as e:
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) # 同族口径:原告警不带文件名("忽略异常"),改为显式诊断
# (strict fail-fast / 非严格告警)。.vpi 是编译器无糖产物,
# 解析失败属内部不变量被破坏,非严格模式仍缓存 None 并返回 False。
_ReportDeclarationParseFailure(
pyi_path, e,
f"类 '{class_name}' 的成员/父类信息不会加载,"
"class_members 可能残缺(字段下标/this_adjust 可能错位)")
self._pyi_cache[pyi_path] = None self._pyi_cache[pyi_path] = None
return False return False
@@ -2621,6 +2968,8 @@ class ImportHandle(BaseHandle):
# 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。 # 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。
# 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。 # 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。
Gen.class_members[class_name] = [] Gen.class_members[class_name] = []
# 同步撤销"已展平"登记:重建若因依赖未就绪而不完整,须允许后续查询再补齐。
Gen._members_flattened.discard(class_name)
for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields', for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields',
'class_member_byteorders', 'class_member_bitoffsets'): 'class_member_byteorders', 'class_member_bitoffsets'):
_gd: dict | None = getattr(Gen, _gd_attr, None) _gd: dict | None = getattr(Gen, _gd_attr, None)
+11 -3
View File
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from lib.core.translator import Translator from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
import vast as ast import vast as ast
import llvmlite.ir as ir import llvmlite.ir as ir
@@ -96,7 +97,11 @@ class ReturnHandle(BaseHandle):
IsRetPtr = True IsRetPtr = True
ExpectedType: ir.Type = ReturnTypeInfo.ToLLVM(Gen) if ReturnTypeInfo and ReturnTypeInfo.BaseType else Gen._CType2LLVM('int', IsRetPtr) ExpectedType: ir.Type = ReturnTypeInfo.ToLLVM(Gen) if ReturnTypeInfo and ReturnTypeInfo.BaseType else Gen._CType2LLVM('int', IsRetPtr)
expected_types.append(ExpectedType) expected_types.append(ExpectedType)
Val: ir.Value | None = self.HandleExprLlvm(val) # 返回注解即"使用处宽度":CDefine 常量在此按返回注解的位宽物化
# (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量返回值不受影响
# (ConstWidthHint 只被常量物化分支读取)。
Val: ir.Value | None = self.HandleExprLlvm(
val, ConstWidthHint=DefineUseWidthFromLLVMType(ExpectedType))
if Val is None: if Val is None:
if isinstance(ExpectedType, ir.PointerType): if isinstance(ExpectedType, ir.PointerType):
Val = ir.Constant(ExpectedType, None) Val = ir.Constant(ExpectedType, None)
@@ -131,9 +136,12 @@ class ReturnHandle(BaseHandle):
Gen.emit_return(result) Gen.emit_return(result)
return return
if Node.value: if Node.value:
Val: ir.Value | None = self.HandleExprLlvm(Node.value) # 本函数返回类型即"使用处宽度":先取回返回类型再翻译值,供 CDefine 常量
# 按该位宽物化(更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast)。
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
Val: ir.Value | None = self.HandleExprLlvm(
Node.value, ConstWidthHint=DefineUseWidthFromLLVMType(ret_type))
if Val: if Val:
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IdentifiedStructType) and isinstance(Val, ir.Constant) and isinstance(Val.type, ir.PointerType) and Val.constant is None: if isinstance(ret_type, ir.IdentifiedStructType) and isinstance(Val, ir.Constant) and isinstance(Val.type, ir.PointerType) and Val.constant is None:
if ret_type.elements: if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [ zero_val: ir.Constant = ir.Constant(ret_type, [
+7 -90
View File
@@ -5,37 +5,13 @@ import llvmlite.ir as ir
if TYPE_CHECKING: if TYPE_CHECKING:
from lib.core.translator import Translator from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
from lib.core.Handles.HandlesBase import BaseHandle, BuiltinTypeMap from lib.core.Handles.HandlesBase import BaseHandle
from lib.includes.t import CTypeRegistry from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import IsTModule from lib.core.SymbolUtils import IsTModule
from lib.core.VLogger import get_logger as _vlog from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode from lib.constants.config import mode as _config_mode
def _IsTypeExpr(node: ast.AST | None) -> bool:
"""判断 AST 节点是否为类型表达式(类型转换的目标,如 Name/Attribute/Subscript/BinOp|)"""
return (
isinstance(node, ast.Name)
or isinstance(node, ast.Attribute)
or isinstance(node, ast.Subscript)
or (isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr))
)
def _IsCastWrappingDeref(rhs: ast.AST | None, deref_node: ast.Call) -> bool:
"""判断 rhs 是否为"类型转换链包裹 c.Deref"(如 (T | t.CPtr)(t.CPtr(c.Deref(p))))。
允许在 c.Deref 处于类型转换调用链中时仍从赋值注解推断加载类型;
ifexp/compare/binop 包裹(非转换调用)不放行,避免把 i8* 错 bitcast 成
其他位宽加载(见 _infer_deref_target_type 文档)。
"""
if rhs is deref_node:
return True
if isinstance(rhs, ast.Call) and len(rhs.args) == 1 and _IsTypeExpr(rhs.func):
return _IsCastWrappingDeref(rhs.args[0], deref_node)
return False
class CSpecialCallHandle(BaseHandle): class CSpecialCallHandle(BaseHandle):
def _HandleCIfLlvm(self, Node: ast.Call) -> ir.Value: def _HandleCIfLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
@@ -251,7 +227,6 @@ class CSpecialCallHandle(BaseHandle):
if arg_val is None: if arg_val is None:
return ir.Constant(ir.IntType(32), 0) return ir.Constant(ir.IntType(32), 0)
if isinstance(arg_val.type, ir.PointerType): if isinstance(arg_val.type, ir.PointerType):
pointee: ir.Type = arg_val.type.pointee
# 嵌套 Deref 的内层:加载为指针 (i8*),不推断目标类型 # 嵌套 Deref 的内层:加载为指针 (i8*),不推断目标类型
# 因为结果会被外层 Deref 再次解引用 # 因为结果会被外层 Deref 再次解引用
NestedLoadPtr: bool = getattr(Gen, '_nested_deref_load_ptr', False) NestedLoadPtr: bool = getattr(Gen, '_nested_deref_load_ptr', False)
@@ -261,75 +236,17 @@ class CSpecialCallHandle(BaseHandle):
Loaded = Gen._load(casted, name="deref") Loaded = Gen._load(casted, name="deref")
if Loaded is not None: if Loaded is not None:
return Loaded return Loaded
# void* (i8*) 需要根据赋值目标类型推断加载类型 # 读取宽度只由指针的 pointee 类型决定(_load 按 arg_val.type.pointee
elif isinstance(pointee, ir.IntType) and pointee.width == 8: # 加载一个元素):pointee 是 i8(t.CPtr / void* / str / bytes)即恒读
target_type: ir.Type | None = self._infer_deref_target_type(Gen, Node) # 1 字节,不再按赋值目标注解反推宽度——那会让同一处 c.Deref(i8*) 的
if target_type is not None and not isinstance(target_type, ir.IntType): # 读取宽度取决于它是否恰好出现在带注解赋值的 RHS 里。需要 4/8 字节等
# bitcast i8* -> target_type* then load # 更宽读取时,由调用点显式给出元素类型指针后再解引用
casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast") # (c.Deref((int | t.CPtr)(p)) / c.Deref(t.CUInt64T(p, t.CPtr)))。
Loaded = Gen._load(casted, name="deref")
if Loaded is not None:
return Loaded
elif target_type is not None and isinstance(target_type, ir.IntType):
# 目标是整数类型 (如 i32/i64): bitcast 并加载
if target_type.width != 8:
casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast")
Loaded = Gen._load(casted, name="deref")
if Loaded is not None:
return Loaded
Loaded = Gen._load(arg_val, name="deref") Loaded = Gen._load(arg_val, name="deref")
if Loaded is not None: if Loaded is not None:
return Loaded return Loaded
return arg_val return arg_val
def _infer_deref_target_type(self, Gen: LlvmGeneratorMixin, DerefNode: ast.Call | None = None) -> ir.Type | None:
"""从当前赋值上下文推断 c.Deref 的目标加载类型。
当用户写 v: int = c.Deref(ptr) 时,从赋值注解推断目标类型。
注意:仅当 c.Deref 是赋值的直接 RHS,或 RHS 是类型转换链包裹
(如 `(T | t.CPtr)(t.CPtr(c.Deref(p)))`)时才推断;若 c.Deref 嵌套在
ifexp/compare/binop 中(如 `a = 1 if c.Deref(p) == 65 else 0`),
不应推断,否则会把 i8* 错误 bitcast 为 i32* 加载 4 字节。"""
assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None)
if not assign_node:
return None
# 仅当 c.Deref 是赋值的直接 RHS 或处于类型转换链中时才推断类型
if DerefNode is not None:
rhs: ast.AST | None = getattr(assign_node, 'value', None)
if rhs is not DerefNode and not _IsCastWrappingDeref(rhs, DerefNode):
return None
ann: ast.AST | None = getattr(assign_node, 'annotation', None)
if not ann:
return None
# 收集候选类型名:Name / Attribute / 联合 BinOp| 的所有 side。
# 逐个尝试解析,取第一个能解析为 LLVM 类型的(如
# `vthreading.Thread | t.CPtr` → 'Thread' 未注册 → 'CPtr' → i8*)。
candidates: list[str] = []
if isinstance(ann, ast.Name):
candidates.append(ann.id)
elif isinstance(ann, ast.Attribute):
candidates.append(ann.attr)
elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr):
# 处理 int | t.CPtr 形式的联合类型注解
for side in (ann.left, ann.right):
if isinstance(side, ast.Name):
candidates.append(side.id)
elif isinstance(side, ast.Attribute):
candidates.append(side.attr)
for ann_name in candidates:
# 使用 BuiltinTypeMap 统一查询(同时支持 Python 类名和字符串速记)
entry: tuple[type, int] | None = BuiltinTypeMap.Get(ann_name)
if entry is not None:
ctype_cls: type = entry[0]
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
if llvm_str:
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
return resolved
# 检查是否是已注册的结构体类型
if ann_name in Gen.structs:
return Gen.structs[ann_name]
return None
def _HandleCPtrToIntLlvm(self, Node: ast.Call) -> ir.Value: def _HandleCPtrToIntLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if not Node.args: if not Node.args:
+6 -1
View File
@@ -141,8 +141,13 @@ class WithHandle(BaseHandle):
current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack) current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack)
new_fields: int = len(Gen.class_provides[ClassName]) new_fields: int = len(Gen.class_provides[ClassName])
if current_depth + new_fields > 32: if current_depth + new_fields > 32:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError( raise SyntaxError(
f"[TransPyC] with provider stack overflow at line {Node.lineno}: " f"[TransPyC] with provider stack overflow at line {_lineno}: "
f"current depth {current_depth} + new {new_fields} fields > max 32. " f"current depth {current_depth} + new {new_fields} fields > max 32. "
f"Class '{ClassName}' provides too many fields or with nesting too deep." f"Class '{ClassName}' provides too many fields or with nesting too deep."
) )
+12 -12
View File
@@ -257,10 +257,10 @@ class BaseGenMixin:
# True=每个 define 发 DISubprogram + 入口首条指令发 DILocation。 # True=每个 define 发 DISubprogram + 入口首条指令发 DILocation。
self.dwarf_enabled: bool = False self.dwarf_enabled: bool = False
self.DebugInfo: Any = None self.DebugInfo: Any = None
# Ctrl+C 调用栈(--ctrlc-stack):仅在翻译 includes/dbgstack.vp 这一个模块时 # Ctrl+C 调用栈(--traceback):仅在翻译 includes/traceback.vp 这一个模块时
# 置 True —— 落成 .ll 文本前为本模块注入 @llvm.global_ctors 构造器调用其 # 置 True —— 落成 .ll 文本前为本模块注入 @llvm.global_ctors 构造器调用其
# Install()(见 _RegisterDbgStackInstallViaCtor)。默认 False(零影响)。 # Install()(见 _RegisterTracebackInstallViaCtor)。默认 False(零影响)。
self.ctrlc_stack_ctor: bool = False self.traceback_ctor: bool = False
pi: dict[str, bool | int] = self._platform_info pi: dict[str, bool | int] = self._platform_info
# 根据目标平台设置宏(声明式,从三元组推导) # 根据目标平台设置宏(声明式,从三元组推导)
if pi['is_windows']: if pi['is_windows']:
@@ -2100,12 +2100,12 @@ class BaseGenMixin:
gv.linkage = 'appending' gv.linkage = 'appending'
gv.initializer = ir.Constant(arr_ty, [entry_val]) gv.initializer = ir.Constant(arr_ty, [entry_val])
def _RegisterDbgStackInstallViaCtor(self) -> None: def _RegisterTracebackInstallViaCtor(self) -> None:
"""用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--ctrlc-stack)。 """用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--traceback)。
仅在"本翻译单元就是 includes/dbgstack.vp"时由 StructGen.finalize() 调用 仅在"本翻译单元就是 includes/traceback.vp"时由 StructGen.finalize() 调用
(ctrlc_stack_ctor 由 Phase2Translator._translate_file 按源文件名置位)。 (traceback_ctor 由 Phase2Translator._translate_file 按源文件名置位)。
生成 internal void @__dbgstack_install_ctor()(entry 块直接 call 本模块的 生成 internal void @__traceback_install_ctor()(entry 块直接 call 本模块的
Install() 并忽略其 int 返回值),再写入 appending linkage 的 Install() 并忽略其 int 返回值),再写入 appending linkage 的
@llvm.global_ctors —— 每模块一份,链接器按 appending 语义拼进 .init_array, @llvm.global_ctors —— 每模块一份,链接器按 appending 语义拼进 .init_array,
进程启动时执行一次。 进程启动时执行一次。
@@ -2121,22 +2121,22 @@ class BaseGenMixin:
install_fn: ir.Function | None = self._find_function('Install') install_fn: ir.Function | None = self._find_function('Install')
if install_fn is None: if install_fn is None:
raise RuntimeError( raise RuntimeError(
'--ctrlc-stack: 在 dbgstack 模块内找不到 Install() 函数,' '--traceback: 在 traceback 模块内找不到 Install() 函数,'
'无法注入启动期构造器(不静默降级)') '无法注入启动期构造器(不静默降级)')
i32: ir.IntType = ir.IntType(32) i32: ir.IntType = ir.IntType(32)
i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) i8p: ir.PointerType = ir.PointerType(ir.IntType(8))
void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), [])) void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), []))
ctor: ir.Function = ir.Function( ctor: ir.Function = ir.Function(
self.module, ir.FunctionType(ir.VoidType(), []), self.module, ir.FunctionType(ir.VoidType(), []),
name=self._UniqueGlobalName('__dbgstack_install_ctor')) name=self._UniqueGlobalName('__traceback_install_ctor'))
ctor.linkage = 'internal' ctor.linkage = 'internal'
cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry')) cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry'))
cb.call(install_fn, []) # Install() 无参;返回值(int)有意忽略 cb.call(install_fn, []) # Install() 无参;返回值(int)有意忽略
cb.ret_void() cb.ret_void()
if 'llvm.global_ctors' in self.module.globals: if 'llvm.global_ctors' in self.module.globals:
raise RuntimeError( raise RuntimeError(
'--ctrlc-stack: 模块内已存在 @llvm.global_ctors,' '--traceback: 模块内已存在 @llvm.global_ctors,'
'无法注入 dbgstack 启动期构造器(不静默降级)') '无法注入 traceback 启动期构造器(不静默降级)')
elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p]) elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p])
arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1) arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1)
entry_val: ir.Constant = ir.Constant(elem_ty, [ entry_val: ir.Constant = ir.Constant(elem_ty, [
+54 -6
View File
@@ -46,7 +46,7 @@ class ExprGenMixin:
return self.builder.bitcast(global_var, ir.PointerType(ir.IntType(8)), name="str") return self.builder.bitcast(global_var, ir.PointerType(ir.IntType(8)), name="str")
return ir.Constant(ir.IntType(32), int(value)) return ir.Constant(ir.IntType(32), int(value))
def emit_binary_op(self, op: str, left: ir.Value, right: ir.Value, is_unsigned: bool = False) -> ir.Value: def emit_binary_op(self, op: str, left: ir.Value, right: ir.Value, is_unsigned: bool = False, is_aug: bool = False) -> ir.Value:
if isinstance(left.type, ir.PointerType) and isinstance(right.type, ir.IntType): if isinstance(left.type, ir.PointerType) and isinstance(right.type, ir.IntType):
# 数组指针需要先 decay 为元素指针,否则 GEP 会以整个数组为步长 # 数组指针需要先 decay 为元素指针,否则 GEP 会以整个数组为步长
# 例如 [64 x i8]* + 1 会前进 64 字节而非 1 字节 # 例如 [64 x i8]* + 1 会前进 64 字节而非 1 字节
@@ -65,6 +65,46 @@ class ExprGenMixin:
left = self.builder.zext(left, ir.IntType(64), name="int2ptrint2") left = self.builder.zext(left, ir.IntType(64), name="int2ptrint2")
if op == '+' or op == 'Add': if op == '+' or op == 'Add':
return self.builder.gep(right, [left], name="ptr_add_rev") return self.builder.gep(right, [left], name="ptr_add_rev")
# ============================================================
# 双指针算术全域 fail-fast(**单一收敛点**)
#
# BinOp(HandlesExprOps._HandleBinOpLlvm)与 AugAssign
# (HandlesAugAssign._HandleAugAssignLlvm,+=、-=、*= …)都经本函数,
# 故在此一次收口,避免多点各写一份判据/报文。
# 判据:运算符属二元算术/位运算/移位集合,且**两操作数都是 LLVM 指针**。
# 本语言的 str 是 i8*,没有 `+` 拼接语义;指针 ± 指针也不是 GEP 语义
# (GEP 只接受「一指针一整数」,已在上方两个 ptr/int 分支处理并返回)。
# 落到下方 ops/shift_ops/bit_ops 会直出非法 IR(`add i8*, i8*` ⇒ opt 报
# invalid operand type for instruction),故在 IR 生成前 fail-fast。
# 判据与报文逐句对齐 TPV ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。
# ============================================================
if op in ('+', 'Add', '-', 'Sub', '*', 'Mult', '/', 'Div', '//', 'FloorDiv',
'%', 'Mod', '**', 'Pow', '&', 'BitAnd', '|', 'BitOr', '^', 'BitXor',
'<<', 'LShift', '>>', 'RShift') \
and isinstance(left.type, ir.PointerType) \
and isinstance(right.type, ir.PointerType):
LeftTy: str = str(left.type)
RightTy: str = str(right.type)
if LeftTy == 'i8*':
LeftTy = 'str(i8*)'
if RightTy == 'i8*':
RightTy = 'str(i8*)'
OpCh: str = op
AugNote: str = ''
if is_aug:
# 增量赋值(+= 等)要能自报家门,便于定位(不写成裸 '+')
OpCh = f'{op}='
AugNote = '(增量赋值)'
raise TypeError(
f"[TransPyC] 双指针运算类型非法: op = '{OpCh}'{AugNote},"
f'左操作数类型 = {LeftTy},右操作数类型 = {RightTy}。'
f'本语言的 str 是 i8*,没有 `+` 拼接语义(`str + str` 与指针 ± 指针'
f'都会退化成裸指针 add/sub ⇒ 非法 IR: opt 报 '
f'invalid operand type for instruction)。'
f'禁止静默降级,已终止编译。'
f'修法: 用 f-string 插值、string.strcat 或 viperlib.snprintf 构建字符串。'
+ self._get_node_info()
)
is_float: bool = isinstance(left.type, (ir.FloatType, ir.DoubleType)) or \ is_float: bool = isinstance(left.type, (ir.FloatType, ir.DoubleType)) or \
isinstance(right.type, (ir.FloatType, ir.DoubleType)) isinstance(right.type, (ir.FloatType, ir.DoubleType))
if is_float: if is_float:
@@ -291,10 +331,14 @@ class ExprGenMixin:
if isinstance(ret_type, ir.PointerType) and isinstance(value.type, ir.PointerType): if isinstance(ret_type, ir.PointerType) and isinstance(value.type, ir.PointerType):
value = self.builder.bitcast(value, ret_type, name="ret_cast") value = self.builder.bitcast(value, ret_type, name="ret_cast")
elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType): elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
if isinstance(value.type.pointee, ir.IntType) and isinstance(ret_type, ir.IntType) and value.type.pointee.width == ret_type.width: if isinstance(ret_type, ir.IntType):
value = self._load(value, name="ret_Load") # 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
elif isinstance(ret_type, ir.IntType): # 原先"同宽 load 所指物 / 其余 ptrtoint"都是静默转换。
value = self.builder.ptrtoint(value, ret_type, name="ret_ptrtoint") from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:函数 {self.func.name} 声明返回 {ret_type},"
f"但 return 的是指针类型 {value.type};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif isinstance(value.type.pointee, ret_type.__class__) and not isinstance(value.type.pointee, ir.IntType): elif isinstance(value.type.pointee, ret_type.__class__) and not isinstance(value.type.pointee, ir.IntType):
value = self._load(value, name="ret_Load") value = self._load(value, name="ret_Load")
elif isinstance(value.type.pointee, ir.PointerType): elif isinstance(value.type.pointee, ir.PointerType):
@@ -302,7 +346,11 @@ class ExprGenMixin:
if Loaded.type == ret_type: if Loaded.type == ret_type:
value = Loaded value = Loaded
elif isinstance(ret_type, ir.IntType): elif isinstance(ret_type, ir.IntType):
value = self.builder.ptrtoint(Loaded, ret_type, name="ret_ptrtoint") from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:函数 {self.func.name} 声明返回 {ret_type},"
f"但 return 的是指针类型 {Loaded.type};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif isinstance(ret_type, ir.IntType) and isinstance(value.type, ir.IntType): elif isinstance(ret_type, ir.IntType) and isinstance(value.type, ir.IntType):
if value.type.width < ret_type.width: if value.type.width < ret_type.width:
if value.type.width == 1: if value.type.width == 1:
+106 -62
View File
@@ -21,14 +21,14 @@ import re
# `!N = ` 形式的编号元数据定义行 # `!N = ` 形式的编号元数据定义行
_Definition = re.compile(r'^!(\d+) = ') _Definition = re.compile(r'^!(\d+) = ')
# 编号元数据引用
_Reference = re.compile(r'!(\d+)')
# `!name = ...` 形式的命名元数据(首字符为字母,故不会命中 `!N = `) # `!name = ...` 形式的命名元数据(首字符为字母,故不会命中 `!N = `)
_NamedDefinition = re.compile(r'^!([A-Za-z][\w.]*)\s*=\s*(.*)$') _NamedDefinition = re.compile(r'^!([A-Za-z][\w.]*)\s*=\s*(.*)$')
# `!{ ... }` 列表形式 # `!{ ... }` 列表形式
_ListForm = re.compile(r'^!\{(.*)\}$', re.DOTALL) _ListForm = re.compile(r'^!\{(.*)\}$', re.DOTALL)
# 纯 id 引用 `!N` # 纯 id 引用 `!N`
_PureId = re.compile(r'^!(\d+)$') _PureId = re.compile(r'^!(\d+)$')
# Decimal digit set for the no-regex fast rewrite path.
_DIGITS: frozenset[str] = frozenset('0123456789')
def _SplitTopLevel(Content: str) -> list[str]: def _SplitTopLevel(Content: str) -> list[str]:
@@ -78,47 +78,69 @@ def _ListItems(Rhs: str) -> list[str] | None:
return _SplitTopLevel(m.group(1)) return _SplitTopLevel(m.group(1))
def _RewriteReferences(Line: str, Mapping: dict[int, int]) -> str: def _RewritePlain(Line: str, StrMap: dict[str, str]) -> str:
"""改写一行里对编号元数据的引用,跳过字符串字面量与 `;` 注释。""" """Rewrite `!N` references in a line that has no `"` and no `;`.
if '!' not in Line:
Equivalent to a regex substitution of `!` + digits but without the regex
engine callback: split on '!' (C-level), map each digit run by full token,
keep unknown ids verbatim.
"""
Parts: list[str] = Line.split('!')
if len(Parts) == 1:
return Line return Line
if '"' not in Line and ';' not in Line: Out: list[str] = [Parts[0]]
# 元数据行绝大多数不含字符串/注释:整体正则替换,避免逐字符扫描 Ap = Out.append
return _Reference.sub( Get = StrMap.get
lambda m: '!' + str(Mapping.get(int(m.group(1)), int(m.group(1)))), Line) for Part in Parts[1:]:
Out: list[str] = [] d: int = 0
n: int = len(Part)
while d < n and Part[d] in _DIGITS:
d += 1
if not d:
Ap('!')
Ap(Part)
continue
Digits: str = Part[:d]
# Leading zeros normalise the way int()/str() did in the old path.
Tok: str = '!' + Digits if (d == 1 or Digits[0] != '0') else '!' + str(int(Digits))
New: str | None = Get(Tok)
Ap(Tok if New is None else New)
Ap(Part[d:])
return ''.join(Out)
def _RewriteQuoted(Line: str, StrMap: dict[str, str]) -> str:
"""Rewrite `!N` refs in a line that contains `"` and/or `;`.
String literals and the trailing `;` comment are copied verbatim; the
remaining (quote/comment free) chunks go through `_RewritePlain`. Uses
C-level str.find instead of the old per-character Python scan.
"""
Out: list[str] = []; Ap = Out.append
i: int = 0 i: int = 0
n: int = len(Line) n: int = len(Line)
while i < n: while i < n:
Ch: str = Line[i] q: int = Line.find('"', i)
if Ch == '"': s: int = Line.find(';', i, q if q >= 0 else n)
j: int = i + 1 if s >= 0:
while j < n: Ap(_RewritePlain(Line[i:s], StrMap))
if Line[j] == '\\': Ap(Line[s:]) # ';' comment: copy verbatim to end of line
j += 2
continue
if Line[j] == '"':
j += 1
break
j += 1
Out.append(Line[i:j])
i = j
continue
if Ch == ';':
Out.append(Line[i:])
break break
if Ch == '!': if q < 0:
m = _Reference.match(Line, i) Ap(_RewritePlain(Line[i:], StrMap))
if m: break
New: int | None = Mapping.get(int(m.group(1))) Ap(_RewritePlain(Line[i:q], StrMap))
Out.append('!' + str(New) if New is not None else m.group(0)) j: int = q + 1
i = m.end() while j < n:
if Line[j] == '\\':
j += 2
continue continue
Out.append(Ch) if Line[j] == '"':
i += 1 j += 1
continue break
Out.append(Ch) j += 1
i += 1 Ap(Line[q:j]) # string literal: copy verbatim
i = j
return ''.join(Out) return ''.join(Out)
@@ -139,7 +161,7 @@ def SanitizeIRMetadata(text: str) -> str:
i: int = 0 i: int = 0
while i < LineCount: while i < LineCount:
if Segments[i].startswith('!'): if Segments[i].startswith('!'):
j: int = i j: int = i + 1
while j < LineCount and Segments[j].startswith('!'): while j < LineCount and Segments[j].startswith('!'):
j += 1 j += 1
Blocks.append((i, j)) Blocks.append((i, j))
@@ -161,7 +183,13 @@ def SanitizeIRMetadata(text: str) -> str:
# 3) 块内按定义出现顺序重新编号(局部 id -> 全局 id) # 3) 块内按定义出现顺序重新编号(局部 id -> 全局 id)
Lines: list[str] = list(Segments) Lines: list[str] = list(Segments)
BlockMaps: list[dict[int, int]] = [] BlockMaps: list[dict[int, int]] = []
BlockStrMaps: list[dict[str, str]] = []
BlockIsIdentity: list[bool] = [] BlockIsIdentity: list[bool] = []
# Definition line index -> (new global id, length of its rewritten "!N = " prefix).
# Lets step 4/5 skip a per-line regex to re-detect definition lines.
DefInfo: dict[int, tuple[int, int]] = {}
# Named metadata lines are collected here (only block lines can start with '!').
NamedLines: dict[str, list[int]] = {}
Counter: int = 0 Counter: int = 0
for Start, End in Blocks: for Start, End in Blocks:
Mapping: dict[int, int] = {} Mapping: dict[int, int] = {}
@@ -169,14 +197,21 @@ def SanitizeIRMetadata(text: str) -> str:
for k in range(Start, End): for k in range(Start, End):
m = _Definition.match(Lines[k]) m = _Definition.match(Lines[k])
if not m: if not m:
nm = _NamedDefinition.match(Lines[k])
if nm is not None:
NamedLines.setdefault(nm.group(1), []).append(k)
continue continue
Local: int = int(m.group(1)) Local: int = int(m.group(1))
Mapping[Local] = Counter Mapping[Local] = Counter
if Local != Counter: if Local != Counter:
Identity = False Identity = False
Lines[k] = '!' + str(Counter) + ' = ' + Lines[k][m.end():] Prefix: str = '!' + str(Counter) + ' = '
DefInfo[k] = (Counter, len(Prefix))
Lines[k] = Prefix + Lines[k][m.end():]
Counter += 1 Counter += 1
BlockMaps.append(Mapping) BlockMaps.append(Mapping)
BlockStrMaps.append({'!' + str(LocalId): '!' + str(GlobId)
for LocalId, GlobId in Mapping.items()})
BlockIsIdentity.append(Identity) BlockIsIdentity.append(Identity)
# 4) 改写全文引用(定义体右值、define/指令行的 !dbg、命名元数据列表) # 4) 改写全文引用(定义体右值、define/指令行的 !dbg、命名元数据列表)
@@ -185,36 +220,40 @@ def SanitizeIRMetadata(text: str) -> str:
# 旧 id 843 → Mapping[843]=1686),一旦改写左侧就会出现两个定义共用同一 # 旧 id 843 → Mapping[843]=1686),一旦改写左侧就会出现两个定义共用同一
# id,opt/llc 随即报 "Metadata id is already used"(本 bug 实测残留 276 个 # id,opt/llc 随即报 "Metadata id is already used"(本 bug 实测残留 276 个
# 重复 id)。故定义行只改写 '=' 之后的右值。 # 重复 id)。故定义行只改写 '=' 之后的右值。
GetDef = DefInfo.get
for k in range(LineCount): for k in range(LineCount):
Index: int = Owner[k] Index: int = Owner[k]
if Index < 0 or BlockIsIdentity[Index] or not BlockMaps[Index]: if Index < 0 or BlockIsIdentity[Index] or not BlockMaps[Index]:
continue continue
Line: str = Lines[k] Line: str = Lines[k]
Head: re.Match | None = _Definition.match(Line) if '!' not in Line:
if Head: continue
Fixed: str = Line[:Head.end()] + _RewriteReferences(Line[Head.end():], BlockMaps[Index]) StrMap: dict[str, str] = BlockStrMaps[Index]
Def: tuple[int, int] | None = GetDef(k)
if Def is not None:
Split: int = Def[1]
Tail: str = Line[Split:]
Fixed: str = Line[:Split] + (_RewritePlain(Tail, StrMap)
if ('"' not in Tail and ';' not in Tail)
else _RewriteQuoted(Tail, StrMap))
else: else:
Fixed = _RewriteReferences(Line, BlockMaps[Index]) Fixed = (_RewritePlain(Line, StrMap)
if ('"' not in Line and ';' not in Line)
else _RewriteQuoted(Line, StrMap))
if Fixed != Line: if Fixed != Line:
Lines[k] = Fixed Lines[k] = Fixed
# 5) 定义体索引(新 id -> 右值文本),供命名元数据判重 # 5) 定义体索引(新 id -> 右值文本),供命名元数据判重。
# 只遍历定义行(步骤 3 已登记),不再逐行做正则匹配。
BodyOf: dict[int, str] = {} BodyOf: dict[int, str] = {}
for k in range(LineCount): for k, (NewId, Split) in DefInfo.items():
m = _Definition.match(Lines[k]) BodyOf[NewId] = Lines[k][Split:].strip()
if m:
BodyOf[int(m.group(1))] = Lines[k][m.end():].strip()
# 6) 命名元数据:同名多份合并成一份。 # 6) 命名元数据:同名多份合并成一份。
# - !llvm.dbg.cu:全部条目聚合(每个 DICompileUnit 都必须被列出,不能去重) # - !llvm.dbg.cu:全部条目聚合(每个 DICompileUnit 都必须被列出,不能去重)
# - 其它(!llvm.module.flags、!llvm.ident ...):条目内容(id 引用解析成 # - 其它(!llvm.module.flags、!llvm.ident ...):条目内容(id 引用解析成
# 节点正文)完全相同的多份只留一份,不同则聚合。 # 节点正文)完全相同的多份只留一份,不同则聚合。
NamedLines: dict[str, list[int]] = {} # NamedLines 已在步骤 3 随块扫描一并收集(命名行的名字不受引用改写影响)。
for k in range(LineCount):
m = _NamedDefinition.match(Lines[k])
if m:
NamedLines.setdefault(m.group(1), []).append(k)
Dropped: set[int] = set() Dropped: set[int] = set()
for Name, Indices in NamedLines.items(): for Name, Indices in NamedLines.items():
if len(Indices) == 1 and Name != 'llvm.dbg.cu': if len(Indices) == 1 and Name != 'llvm.dbg.cu':
@@ -225,20 +264,25 @@ def SanitizeIRMetadata(text: str) -> str:
SeenItems: set[str] = set() SeenItems: set[str] = set()
SeenBodies: set[tuple[str, ...]] = set() SeenBodies: set[tuple[str, ...]] = set()
Single: bool = True Single: bool = True
IsDbgCu: bool = Name == 'llvm.dbg.cu'
for k in Indices: for k in Indices:
Rhs: str = _NamedDefinition.match(Lines[k]).group(2).strip() NamedMatch = _NamedDefinition.match(Lines[k])
Rhs: str = (NamedMatch.group(2) if NamedMatch is not None else '').strip()
ListItems: list[str] | None = _ListItems(Rhs) ListItems: list[str] | None = _ListItems(Rhs)
if ListItems is None: if ListItems is None:
Single = False Single = False
break break
Bodies: tuple[str, ...] = tuple( if not IsDbgCu:
BodyOf.get(int(_PureId.match(Item).group(1)), Item) if _PureId.match(Item) else Item # !llvm.dbg.cu never de-dupes by body, so skip resolving bodies for it.
for Item in ListItems Bodies: list[str] = []
) for Item in ListItems:
if Name != 'llvm.dbg.cu': PureMatch = _PureId.match(Item)
if Bodies in SeenBodies: Bodies.append(BodyOf.get(int(PureMatch.group(1)), Item)
if PureMatch else Item)
Key: tuple[str, ...] = tuple(Bodies)
if Key in SeenBodies:
continue continue
SeenBodies.add(Bodies) SeenBodies.add(Key)
for Item in ListItems: for Item in ListItems:
if Item not in SeenItems: if Item not in SeenItems:
SeenItems.add(Item) SeenItems.add(Item)
+93 -17
View File
@@ -2,6 +2,50 @@ from __future__ import annotations
import llvmlite.ir as ir import llvmlite.ir as ir
class _VolatileLoadInstr(ir.LoadInstr):
"""带 LLVM `volatile` 限定符的 load 指令。
llvmlite 0.47 的 `ir.LoadInstr` / `ir.StoreInstr` 不支持 volatile(无该属性,
`descr` 也不输出该关键字),故按本仓库既有做法(BaseGen.VaArgInstruction
自定义 `descr`)派生子类,只把 `load` 关键字扩成 `load volatile`。
输出格式与 llvmlite 原始实现逐字段一致(类型、指针、align、元数据),
仅多一个 `volatile` 关键字;语义与 TransPyV 侧 `build_load(..., vol)` 对齐。
"""
def descr(self, buf: list[str]) -> None:
[val] = self.operands
if self.align is not None:
align = ', align %d' % (self.align)
else:
align = ''
buf.append("load volatile {0}, {1} {2}{3}{4}\n".format(
self.type,
val.type,
val.get_reference(),
align,
self._stringify_metadata(leading_comma=True),
))
class _VolatileStoreInstr(ir.StoreInstr):
"""带 LLVM `volatile` 限定符的 store 指令(与 _VolatileLoadInstr 同款理由)。"""
def descr(self, buf: list[str]) -> None:
val, ptr = self.operands
if self.align is not None:
align = ', align %d' % (self.align)
else:
align = ''
buf.append("store volatile {0} {1}, {2} {3}{4}{5}\n".format(
val.type,
val.get_reference(),
ptr.type,
ptr.get_reference(),
align,
self._stringify_metadata(leading_comma=True),
))
class MemoryOpsMixin: class MemoryOpsMixin:
def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None: def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None:
@@ -126,12 +170,42 @@ class MemoryOpsMixin:
self.builder.position_at_end(saved_block) self.builder.position_at_end(saved_block)
return instr return instr
def _load(self, ptr: ir.Value, name: str = '', align: int | None = None) -> ir.LoadInstr: def _EmitLoad(self, ptr: ir.Value, name: str = '', align: int | None = None,
volatile: bool = False) -> ir.Value:
"""发射一条 load;volatile=True 时走 _VolatileLoadInstr(LLVM `load volatile`)。
类型推断完全交给 LoadInstr 自身(与 llvmlite 原始路径同一套逻辑),
不额外兜底:推不出类型时与原生 `builder.load` 一样直接抛错。
"""
if not volatile:
return self.builder.load(ptr, name=name, align=align)
instr: ir.LoadInstr = _VolatileLoadInstr(self.builder.block, ptr, name=name)
instr.align = align
self.builder._insert(instr)
return instr
def _EmitStore(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
volatile: bool = False) -> ir.StoreInstr:
"""发射一条 store;volatile=True 时走 _VolatileStoreInstr(LLVM `store volatile`)。"""
if not volatile:
return self.builder.store(val, ptr, align=align)
instr: ir.StoreInstr = _VolatileStoreInstr(self.builder.block, val, ptr)
instr.align = align
self.builder._insert(instr)
return instr
def _load(self, ptr: ir.Value, name: str = '', align: int | None = None,
volatile: bool | None = None) -> ir.LoadInstr:
if align is None: if align is None:
if isinstance(ptr.type, ir.PointerType): if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee) align = self._get_align(ptr.type.pointee)
else: else:
align = 0 align = 0
# volatile=None ⇒ 按登记名自动判定(变量自身/所指物/字段,见 _IsPtrVolatile);
# 显式传入 True/False 时以调用方为准。判定必须先于下方 bitcast(bitcast
# 结果的名字不再是变量名,会丢登记名)。
if volatile is None:
volatile = self._IsPtrVolatile(ptr)
if isinstance(ptr.type, ir.PointerType): if isinstance(ptr.type, ir.PointerType):
pointee_type: ir.Type = ptr.type.pointee pointee_type: ir.Type = ptr.type.pointee
if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths: if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths:
@@ -139,8 +213,8 @@ class MemoryOpsMixin:
if width > 0: if width > 0:
actual_type: ir.IntType = ir.IntType(width) actual_type: ir.IntType = ir.IntType(width)
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load") bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load")
return self.builder.load(bitcast_ptr, name=name, align=align) return self._EmitLoad(bitcast_ptr, name=name, align=align, volatile=volatile)
return self.builder.load(ptr, name=name, align=align) return self._EmitLoad(ptr, name=name, align=align, volatile=volatile)
def _Load_var(self, name: str) -> ir.Value | None: def _Load_var(self, name: str) -> ir.Value | None:
if name in self._direct_values: if name in self._direct_values:
@@ -199,17 +273,14 @@ class MemoryOpsMixin:
# (此前 pointee.width == target.width 时直接 load null, # (此前 pointee.width == target.width 时直接 load null,
# 生成 `load i32, i32* null` → ASAN access-violation) # 生成 `load i32, i32* null` → ASAN access-violation)
return ir.Constant(target_type, 0) return ir.Constant(target_type, 0)
if isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == target_type.width: # 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
return self.builder.load(val, name="load_ptr_as_int_store") # 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
if target_type.width <= 8 and isinstance(val.type.pointee, ir.IntType): # 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
Loaded: ir.Value = self.builder.load(val, name="load_ptr_byte_store") from lib.core.LLVMCG.FuncGen import FailFastError
if Loaded.type != target_type: raise FailFastError(
if isinstance(Loaded.type, ir.IntType) and isinstance(target_type, ir.IntType): f"指针隐式转整数已禁止:不能把指针类型 {val.type} 赋给整数类型 {target_type};"
if target_type.width < Loaded.type.width: f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
return self.builder.trunc(Loaded, target_type, name="trunc_ptr_byte_store") )
return self.builder.zext(Loaded, target_type, name="zext_ptr_byte_store")
return Loaded
return self.builder.ptrtoint(val, target_type, name="ptr_to_int_store")
if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType): if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType):
return self.builder.fptrunc(val, target_type, name="fptrunc_store") return self.builder.fptrunc(val, target_type, name="fptrunc_store")
if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType): if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType):
@@ -242,12 +313,17 @@ class MemoryOpsMixin:
return Loaded return Loaded
return None return None
def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None) -> ir.StoreInstr: def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
volatile: bool | None = None) -> ir.StoreInstr:
if align is None: if align is None:
if isinstance(ptr.type, ir.PointerType): if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee) align = self._get_align(ptr.type.pointee)
else: else:
align = 0 align = 0
# volatile=None ⇒ 按登记名自动判定(见 _IsPtrVolatile)。判定先于下方任何
# ptr 重建(null_struct_ptr_cast 等),否则会丢登记名。
if volatile is None:
volatile = self._IsPtrVolatile(ptr)
if isinstance(ptr.type, ir.PointerType): if isinstance(ptr.type, ir.PointerType):
target_type: ir.Type = ptr.type.pointee target_type: ir.Type = ptr.type.pointee
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths: if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
@@ -261,7 +337,7 @@ class MemoryOpsMixin:
val = self.builder.trunc(val, actual_type, name="trunc_typedef") val = self.builder.trunc(val, actual_type, name="trunc_typedef")
else: else:
val = self.builder.zext(val, actual_type, name="zext_typedef") val = self.builder.zext(val, actual_type, name="zext_typedef")
return self.builder.store(val, bitcast_ptr, align=align) return self._EmitStore(val, bitcast_ptr, align=align, volatile=volatile)
if val.type != target_type: if val.type != target_type:
if isinstance(val, ir.Constant) and val.constant is None: if isinstance(val, ir.Constant) and val.constant is None:
if isinstance(target_type, ir.PointerType): if isinstance(target_type, ir.PointerType):
@@ -280,7 +356,7 @@ class MemoryOpsMixin:
else: else:
src_info: str = self._get_node_info() src_info: str = self._get_node_info()
raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}") raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}")
return self.builder.store(val, ptr, align=align) return self._EmitStore(val, ptr, align=align, volatile=volatile)
def _get_var_ptr(self, name: str) -> ir.Value | None: def _get_var_ptr(self, name: str) -> ir.Value | None:
if name in self._reg_values: if name in self._reg_values:
+75 -23
View File
@@ -10,6 +10,14 @@ from lib.includes import t as _t
class StructGenMixin: class StructGenMixin:
def _get_align(self, llvm_type: ir.Type) -> int: def _get_align(self, llvm_type: ir.Type) -> int:
"""类型自然对齐(字节)。
与 `_get_struct_size` 同一约定:**不允许静默近似**。对齐猜错会让字段字节偏移
与 LLVM 实际布局不一致(本函数是 `_get_struct_size` 的字段偏移累加与
`_compute_field_byte_offset` 的输入),故拿不到对齐时一律响亮失败:
- opaque/未解析的 IdentifiedStructType → 曾按 ptr_size 猜;
- 未识别类型 → 曾按 4 猜。
"""
if isinstance(llvm_type, ir.IntType): if isinstance(llvm_type, ir.IntType):
return max(1, llvm_type.width // 8) return max(1, llvm_type.width // 8)
if isinstance(llvm_type, ir.FloatType): if isinstance(llvm_type, ir.FloatType):
@@ -20,15 +28,28 @@ class StructGenMixin:
return self.ptr_size return self.ptr_size
if isinstance(llvm_type, ir.ArrayType): if isinstance(llvm_type, ir.ArrayType):
return self._get_align(llvm_type.element) return self._get_align(llvm_type.element)
if isinstance(llvm_type, ir.VectorType):
# 与 LLVM 一致:向量对齐 = 元素对齐(同 _get_struct_size 的向量口径)
return self._get_align(llvm_type.element)
if isinstance(llvm_type, ir.LiteralStructType): if isinstance(llvm_type, ir.LiteralStructType):
return max((self._get_align(f) for f in llvm_type.elements), default=1) return max((self._get_align(f) for f in llvm_type.elements), default=1)
if isinstance(llvm_type, ir.IdentifiedStructType): if isinstance(llvm_type, ir.IdentifiedStructType):
resolved = self._resolve_opaque_struct(llvm_type)
if resolved is not llvm_type:
return self._get_align(resolved)
if llvm_type.elements is None or len(llvm_type.elements) == 0: if llvm_type.elements is None or len(llvm_type.elements) == 0:
return self.ptr_size # opaque struct, assume pointer alignment raise ValueError(
f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' "
f"布局未就绪(opaque / 未解析到定义),拒绝按指针对齐兜底 —— "
f"对齐猜错会让字段字节偏移与 LLVM 实际布局不一致(读写落到错误字段)。"
f"请先确保该结构体已注册/可从 stub 加载后再取对齐。"
)
return max((self._get_align(f) for f in llvm_type.elements), default=1) return max((self._get_align(f) for f in llvm_type.elements), default=1)
if isinstance(llvm_type, ir.VoidType): if isinstance(llvm_type, ir.VoidType):
return 0 return 0
return 4 raise ValueError(
f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})"
f"无法计算对齐,拒绝以常数 4 兜底 —— 对齐猜错会让字段字节偏移与 LLVM 实际布局不一致。")
def _resolve_opaque_struct(self, llvm_type: ir.Type) -> ir.Type: def _resolve_opaque_struct(self, llvm_type: ir.Type) -> ir.Type:
"""尝试从 Gen.structs 中查找已解析的同名 struct,解决跨模块 struct 类型对象不一致的问题""" """尝试从 Gen.structs 中查找已解析的同名 struct,解决跨模块 struct 类型对象不一致的问题"""
@@ -77,6 +98,17 @@ class StructGenMixin:
return llvm_type return llvm_type
def _get_struct_size(self, llvm_type: ir.Type) -> int: def _get_struct_size(self, llvm_type: ir.Type) -> int:
"""类型字节尺寸(含字段对齐/填充)。
**不允许静默近似**:本函数的返回值会被 `X.__sizeof__()` 直接采用
(HandlesExprCall._HandleSizeofLlvm),而 memhub 的 alloc 又直接吃该值。
此前两处静默兜底会**少算尺寸**,进而按过小尺寸分配 → 越界写坏邻居块的
8 字节块头(块头 `(order<<1)|1` 被写成偶数 ⇒ reset_to 视为"已释放"跳过、
free 被 _is_valid_ptr 判无效而静默 no-op)⇒ 该块永久丢失 ⇒ 1 GiB 主池漏满:
- opaque/未解析的 IdentifiedStructType → 曾一律返回 ptr_size;
- 未识别类型(如 SIMD VectorType)→ 曾一律返回 4。
故尺寸拿不到时一律响亮失败(项目约定:类型信息缺失立即终止,不兜底)。
"""
if isinstance(llvm_type, ir.IntType): if isinstance(llvm_type, ir.IntType):
return max(1, llvm_type.width // 8) return max(1, llvm_type.width // 8)
if isinstance(llvm_type, ir.FloatType): if isinstance(llvm_type, ir.FloatType):
@@ -87,10 +119,20 @@ class StructGenMixin:
return self.ptr_size return self.ptr_size
if isinstance(llvm_type, ir.ArrayType): if isinstance(llvm_type, ir.ArrayType):
return self._get_struct_size(llvm_type.element) * llvm_type.count return self._get_struct_size(llvm_type.element) * llvm_type.count
if isinstance(llvm_type, ir.VectorType):
# 与 LLVM 一致:元素尺寸 × 元素数,再向上取到 2 的幂(<3 x i32> = 16 字节)
elem_size: int = self._get_struct_size(llvm_type.element)
lanes: int = llvm_type.count
padded: int = 1
while padded < lanes:
padded = padded << 1
return elem_size * padded
if isinstance(llvm_type, ir.LiteralStructType): if isinstance(llvm_type, ir.LiteralStructType):
total: int = 0 total: int = 0
max_align: int = 1 max_align: int = 1
for f in llvm_type.fields: # llvmlite 的 LiteralStructType 只有 `.elements`(**没有** `.fields`):
# 旧代码写 `.fields` 必然 AttributeError,尺寸/对齐链在此静默断掉。
for f in llvm_type.elements:
fa: int = self._get_align(f) fa: int = self._get_align(f)
fs: int = self._get_struct_size(f) fs: int = self._get_struct_size(f)
max_align = max(max_align, fa) max_align = max(max_align, fa)
@@ -102,22 +144,32 @@ class StructGenMixin:
return total return total
if isinstance(llvm_type, ir.IdentifiedStructType): if isinstance(llvm_type, ir.IdentifiedStructType):
# 尝试解析 opaque struct 为已注册的版本 # 尝试解析 opaque struct 为已注册的版本
llvm_type = self._resolve_opaque_struct(llvm_type) resolved = self._resolve_opaque_struct(llvm_type)
if resolved is not llvm_type:
return self._get_struct_size(resolved)
if llvm_type.elements is None or len(llvm_type.elements) == 0: if llvm_type.elements is None or len(llvm_type.elements) == 0:
return self.ptr_size # opaque struct, assume pointer size raise ValueError(
total: int = 0 f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' "
max_align: int = 1 f"布局未就绪(opaque / 未解析到定义),拒绝按指针大小兜底 —— "
f"近似会少算尺寸,调用方(memhub alloc)按过小尺寸分配并越界写坏邻居块头。"
f"请先确保该结构体已注册/可从 stub 加载后再取尺寸。"
)
total2: int = 0
max_align2: int = 1
for f in llvm_type.elements: for f in llvm_type.elements:
fa: int = self._get_align(f) fa2: int = self._get_align(f)
fs: int = self._get_struct_size(f) fs2: int = self._get_struct_size(f)
max_align = max(max_align, fa) max_align2 = max(max_align2, fa2)
if fa > 1: if fa2 > 1:
total = (total + fa - 1) // fa * fa total2 = (total2 + fa2 - 1) // fa2 * fa2
total += fs total2 += fs2
if max_align > 1: if max_align2 > 1:
total = (total + max_align - 1) // max_align * max_align total2 = (total2 + max_align2 - 1) // max_align2 * max_align2
return total return total2
return 4 raise ValueError(
f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})"
f"无法计算尺寸,拒绝以常数兜底 —— 近似会少算尺寸并越界写坏内存池块头。"
)
@staticmethod @staticmethod
def _strip_unnecessary_quotes(ir_str: str) -> str: def _strip_unnecessary_quotes(ir_str: str) -> str:
@@ -140,12 +192,12 @@ class StructGenMixin:
_DbgInfo = self._GetDebugInfo() _DbgInfo = self._GetDebugInfo()
if _DbgInfo is not None: if _DbgInfo is not None:
_DbgInfo.Finalize() _DbgInfo.Finalize()
# Ctrl+C 调用栈(--ctrlc-stack):同样是"落成 .ll 文本之前"的模块级收尾—— # Ctrl+C 调用栈(--traceback):同样是"落成 .ll 文本之前"的模块级收尾——
# 当本模块就是 includes/dbgstack.vp 时,注入 @llvm.global_ctors 构造器调用 # 当本模块就是 includes/traceback.vp 时,注入 @llvm.global_ctors 构造器调用
# 其 Install()(见 BaseGen._RegisterDbgStackInstallViaCtor)。未启用/非该模块 # 其 Install()(见 BaseGen._RegisterTracebackInstallViaCtor)。未启用/非该模块
# 时 ctrlc_stack_ctor=False,不做任何事(零影响)。 # 时 traceback_ctor=False,不做任何事(零影响)。
if self.ctrlc_stack_ctor: if self.traceback_ctor:
self._RegisterDbgStackInstallViaCtor() self._RegisterTracebackInstallViaCtor()
ir_text: str ir_text: str
try: try:
ir_text = str(self.module) ir_text = str(self.module)
+21 -4
View File
@@ -56,6 +56,16 @@ class TypeConvertMixin:
if resolved is not None: if resolved is not None:
return resolved return resolved
# 枚举(CEnum)类型注解 `x: Tri`:宽度 = 该枚举的存储宽度(最宽成员类型),
# 与成员常量发射点同点(ABI:两侧必须同宽)。默认宽度 32 时与改前逐字一致。
# 走 IsTypedef 之前的分支不受影响(typedef 形式枚举先被上方解析)。
if type_info.IsEnum and type_info.Name and not type_info.IsRenum:
_enum_ty: ir.Type = self._enum_int_type(type_info.Name)
_enum_result: ir.Type = _enum_ty
for _ in range(type_info.PtrCount):
_enum_result = ir.PointerType(_enum_result)
return _enum_result
# REnum 类型:通过 Name 查找或创建结构体(BaseType 可能为 None) # REnum 类型:通过 Name 查找或创建结构体(BaseType 可能为 None)
# 这对 BinOp `LLVMType | t.CPtr` 合并后的 CTypeInfo 至关重要 # 这对 BinOp `LLVMType | t.CPtr` 合并后的 CTypeInfo 至关重要
if type_info.IsRenum and type_info.Name: if type_info.IsRenum and type_info.Name:
@@ -146,6 +156,9 @@ class TypeConvertMixin:
enum_name: str = type_info.Name if type_info else '' enum_name: str = type_info.Name if type_info else ''
if enum_name and enum_name in self.structs: if enum_name and enum_name in self.structs:
return self.structs[enum_name] return self.structs[enum_name]
if isinstance(BaseType, _t.CEnum):
# CEnum 存储宽度 = 最宽成员类型(REnum 仍走上方结构体路径)
return self._enum_int_type(enum_name)
return ir.IntType(32) return ir.IntType(32)
if isinstance(BaseType, _t.CUnion): if isinstance(BaseType, _t.CUnion):
@@ -510,14 +523,16 @@ class TypeConvertMixin:
return ir.PointerType(_cls_struct) return ir.PointerType(_cls_struct)
return _cls_struct return _cls_struct
if _Entry and _Entry.IsEnum: if _Entry and _Entry.IsEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) _enum_ty: ir.Type = self._enum_int_type(type_str, _Entry)
return ir.PointerType(_enum_ty) if IsPtr else _enum_ty
if _Entry and _Entry.IsExceptionClass: if _Entry and _Entry.IsExceptionClass:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
if _Entry and _Entry.IsFunction: if _Entry and _Entry.IsFunction:
return ir.PointerType(ir.IntType(8)) return ir.PointerType(ir.IntType(8))
if _Entry and _Entry.BaseType: if _Entry and _Entry.BaseType:
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum: if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) _enum_ty2: ir.Type = self._enum_int_type(type_str, _Entry)
return ir.PointerType(_enum_ty2) if IsPtr else _enum_ty2
for ClassName in self.structs: for ClassName in self.structs:
if type_str == ClassName: if type_str == ClassName:
if IsPtr: if IsPtr:
@@ -564,14 +579,16 @@ class TypeConvertMixin:
return ir.PointerType(opaque_lp) return ir.PointerType(opaque_lp)
return opaque_lp return opaque_lp
if _Entry and _Entry.IsEnum: if _Entry and _Entry.IsEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) _enum_ty3: ir.Type = self._enum_int_type(LastPart, _Entry)
return ir.PointerType(_enum_ty3) if IsPtr else _enum_ty3
if _Entry and _Entry.IsExceptionClass: if _Entry and _Entry.IsExceptionClass:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
if _Entry and _Entry.IsFunction: if _Entry and _Entry.IsFunction:
return ir.PointerType(ir.IntType(8)) return ir.PointerType(ir.IntType(8))
if _Entry and _Entry.BaseType: if _Entry and _Entry.BaseType:
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum: if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) _enum_ty4: ir.Type = self._enum_int_type(LastPart, _Entry)
return ir.PointerType(_enum_ty4) if IsPtr else _enum_ty4
if LastPart in self.structs: if LastPart in self.structs:
if IsPtr: if IsPtr:
return ir.PointerType(self.structs[LastPart]) return ir.PointerType(self.structs[LastPart])
+6
View File
@@ -4,6 +4,8 @@ from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
import vast as ast import vast as ast
from lib.core.SymbolUtils import _ENUM_DEFAULT_INT_WIDTH
if TYPE_CHECKING: if TYPE_CHECKING:
from lib.core.Handles.HandlesBase import CTypeInfo from lib.core.Handles.HandlesBase import CTypeInfo
@@ -18,6 +20,10 @@ class ClassSymbolData:
is_cpython_object: bool = False is_cpython_object: bool = False
is_packed: bool = False is_packed: bool = False
enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno) enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno)
# 枚举存储宽度(bit):按"最宽成员类型"推导(SymbolUtils.EnumClassIntWidth)。
# 仅 type_kind == 'enum' 有意义;非枚举 / 无可解析整数成员 → 默认 32。
# 该属性随符号表条目序列化(_shared_sym.json pickle 的 _sm._extra)⇒ 跨模块同宽。
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
@dataclass @dataclass
+12 -3
View File
@@ -10,7 +10,8 @@ from lib.core.SymbolData import (
FuncSymbolData, DefineSymbolData, AnonymousTypeData FuncSymbolData, DefineSymbolData, AnonymousTypeData
) )
from lib.core.Handles.HandlesBase import CTypeInfo from lib.core.Handles.HandlesBase import CTypeInfo
from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName
from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH
from lib.includes import t from lib.includes import t
@@ -24,7 +25,7 @@ class ASTSymbolExtractor:
"""提取文件中的所有符号信息""" """提取文件中的所有符号信息"""
with open(file_path, 'r', encoding='utf-8') as f: with open(file_path, 'r', encoding='utf-8') as f:
code: str = f.read() code: str = f.read()
tree: ast.Module = ast.parse(code) tree: ast.Module = ast.parse(code, file_path)
result: ModuleSymbols = ModuleSymbols(file_path=file_path) result: ModuleSymbols = ModuleSymbols(file_path=file_path)
@@ -192,9 +193,16 @@ class ASTSymbolExtractor:
type_kind = 'exception' type_kind = 'exception'
break break
# 提取枚举成员 # 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端
# HandlesClassDef._RegisterEnumMembers / TPV ClassGenEnum 同一规则源)。
# 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源
# (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle),
# 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读
# 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。
enum_members: list[tuple[str, int, int]] = [] enum_members: list[tuple[str, int, int]] = []
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
if type_kind == 'enum': if type_kind == 'enum':
enum_width = EnumClassIntWidth(node.body, self._symtab)
next_enum_value: int = 0 next_enum_value: int = 0
for item in node.body: for item in node.body:
if isinstance(item, ast.Assign): if isinstance(item, ast.Assign):
@@ -257,6 +265,7 @@ class ASTSymbolExtractor:
is_cpython_object=is_cpython_object, is_cpython_object=is_cpython_object,
is_packed=is_packed, is_packed=is_packed,
enum_members=enum_members, enum_members=enum_members,
enum_width=enum_width,
) )
# ------------------------------------------------------------------ # ------------------------------------------------------------------
+7 -3
View File
@@ -27,9 +27,13 @@ class SymbolInserter:
# 插入类符号 # 插入类符号
for cls in module_symbols.classes: for cls in module_symbols.classes:
# 枚举存储宽度("最宽成员类型")随条目登记:本条通路是跨模块符号表的
# 来源(.vpi/.pyi → LoadModuleSymbols),宽度必须一路带到序列化产物。
_enum_width: int | None = cls.enum_width if cls.type_kind == 'enum' else None
self._symtab._InsertClassSymbol( self._symtab._InsertClassSymbol(
cls.name, cls.type_kind, cls.lineno, file_path, cls.name, cls.type_kind, cls.lineno, file_path,
cls.members, cls.is_cpython_object, cls.is_packed cls.members, cls.is_cpython_object, cls.is_packed,
EnumWidth=_enum_width
) )
loaded.append(cls.name) loaded.append(cls.name)
if self._namespace: if self._namespace:
@@ -38,10 +42,10 @@ class SymbolInserter:
# 插入枚举成员(短名 + 类名.成员名)—— CEnum 和 REnum 共用 # 插入枚举成员(短名 + 类名.成员名)—— CEnum 和 REnum 共用
if cls.type_kind in ('enum', 'renum'): if cls.type_kind in ('enum', 'renum'):
for member_name, member_value, member_lineno in cls.enum_members: for member_name, member_value, member_lineno in cls.enum_members:
self._symtab._InsertEnumMemberSymbol(member_name, cls.name, member_lineno, file_path, value=member_value) self._symtab._InsertEnumMemberSymbol(member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(member_name) loaded.append(member_name)
class_member_name: str = f"{cls.name}.{member_name}" class_member_name: str = f"{cls.name}.{member_name}"
self._symtab._InsertEnumMemberSymbol(class_member_name, cls.name, member_lineno, file_path, value=member_value) self._symtab._InsertEnumMemberSymbol(class_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(class_member_name) loaded.append(class_member_name)
# REnum: 记录变体名列表到类符号(供 HandlesExprCall 跨模块变体构造识别) # REnum: 记录变体名列表到类符号(供 HandlesExprCall 跨模块变体构造识别)
if cls.type_kind == 'renum': if cls.type_kind == 'renum':
+7 -3
View File
@@ -28,9 +28,13 @@ class PackageReexporter:
# 插入类符号(前缀.类名) # 插入类符号(前缀.类名)
for cls in module_symbols.classes: for cls in module_symbols.classes:
full_name: str = f"{prefix}.{cls.name}" full_name: str = f"{prefix}.{cls.name}"
# 枚举宽度同样带到"模块限定名"条目上(跨模块类型注解按
# <sha1>.Enum / module.Enum 形态查表,与短名条目同宽)。
_enum_width: int | None = cls.enum_width if cls.type_kind == 'enum' else None
self._symtab._InsertClassSymbol( self._symtab._InsertClassSymbol(
full_name, cls.type_kind, cls.lineno, file_path, full_name, cls.type_kind, cls.lineno, file_path,
cls.members, cls.is_cpython_object, cls.is_packed cls.members, cls.is_cpython_object, cls.is_packed,
EnumWidth=_enum_width
) )
loaded.append(full_name) loaded.append(full_name)
if self._namespace: if self._namespace:
@@ -40,11 +44,11 @@ class PackageReexporter:
if cls.type_kind in ('enum', 'renum'): if cls.type_kind in ('enum', 'renum'):
for member_name, member_value, member_lineno in cls.enum_members: for member_name, member_value, member_lineno in cls.enum_members:
full_member_name: str = f"{prefix}.{member_name}" full_member_name: str = f"{prefix}.{member_name}"
self._symtab._InsertEnumMemberSymbol(full_member_name, cls.name, member_lineno, file_path, value=member_value) self._symtab._InsertEnumMemberSymbol(full_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(full_member_name) loaded.append(full_member_name)
# 同时注册 prefix.ClassName.member_name,以支持 module.Class.MEMBER 三层访问 # 同时注册 prefix.ClassName.member_name,以支持 module.Class.MEMBER 三层访问
qualified_member_name: str = f"{prefix}.{cls.name}.{member_name}" qualified_member_name: str = f"{prefix}.{cls.name}.{member_name}"
self._symtab._InsertEnumMemberSymbol(qualified_member_name, cls.name, member_lineno, file_path, value=member_value) self._symtab._InsertEnumMemberSymbol(qualified_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(qualified_member_name) loaded.append(qualified_member_name)
# REnum: 记录变体名列表到前缀类符号 # REnum: 记录变体名列表到前缀类符号
if cls.type_kind == 'renum': if cls.type_kind == 'renum':
+10 -2
View File
@@ -417,7 +417,7 @@ class SymbolTable:
# 符号插入方法(供 SymbolInserter / PackageReexporter 调用) # 符号插入方法(供 SymbolInserter / PackageReexporter 调用)
# ================================================================== # ==================================================================
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict[str, CTypeInfo], IsCpythonObject: bool, IsPacked: bool = False) -> None: def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict[str, CTypeInfo], IsCpythonObject: bool, IsPacked: bool = False, EnumWidth: int | None = None) -> None:
info: CTypeInfo = CTypeInfo() info: CTypeInfo = CTypeInfo()
info.Lineno = lineno info.Lineno = lineno
info.file = FilePath info.file = FilePath
@@ -429,6 +429,10 @@ class SymbolTable:
info.IsUnion = True info.IsUnion = True
elif TypeKind == 'enum': elif TypeKind == 'enum':
info.IsEnum = True info.IsEnum = True
# 枚举存储宽度("最宽成员类型"):登记到条目上 → 随符号表序列化
# (ToDict/pickle 的 _sm._extra)→ 跨模块读回同宽;未登记 → 默认 32。
if EnumWidth:
info.set('enum_width', int(EnumWidth))
elif TypeKind == 'renum': elif TypeKind == 'renum':
info.IsStruct = True info.IsStruct = True
info.IsRenum = True info.IsRenum = True
@@ -444,7 +448,7 @@ class SymbolTable:
self._symbols[FullName] = info self._symbols[FullName] = info
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str, value: int | None = None) -> None: def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str, value: int | None = None, EnumWidth: int | None = None) -> None:
info: CTypeInfo = CTypeInfo() info: CTypeInfo = CTypeInfo()
info.IsEnumMember = True info.IsEnumMember = True
info.EnumName = EnumName info.EnumName = EnumName
@@ -452,6 +456,10 @@ class SymbolTable:
info.file = FilePath info.file = FilePath
if value is not None: if value is not None:
info.value = value info.value = value
# 成员条目同样携带枚举宽度:读取端(_enum_int_type)优先用调用点手头的
# 成员条目取宽,跨模块下成员 key 可能是模块限定名,不依赖枚举条目名可达。
if EnumWidth:
info.set('enum_width', int(EnumWidth))
self._symbols[FullName] = info self._symbols[FullName] = info
def _InsertTypedefSymbol(self, FullName: str, OriginalType_kind: str | None, OriginalClass: CTypeInfo | str | None, lineno: int, FilePath: str, members: dict[str, CTypeInfo] | None = None) -> None: def _InsertTypedefSymbol(self, FullName: str, OriginalType_kind: str | None, OriginalClass: CTypeInfo | str | None, lineno: int, FilePath: str, members: dict[str, CTypeInfo] | None = None) -> None:
+1543 -9
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -198,7 +198,7 @@ class AnnotationLoaderMixin:
with open(ModuleName, 'r', encoding='utf-8') as f: with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read() ann_content: str = f.read()
ann_content = ExpandSoftKeywords(ann_content) ann_content = ExpandSoftKeywords(ann_content)
ann_tree: ast.Module = ast.parse(ann_content) ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
for node in ann_tree.body: for node in ann_tree.body:
if isinstance(node, ast.ClassDef): if isinstance(node, ast.ClassDef):
ClassName: str = node.name ClassName: str = node.name
@@ -239,7 +239,7 @@ class AnnotationLoaderMixin:
with open(ModuleName, 'r', encoding='utf-8') as f: with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read() ann_content: str = f.read()
ann_content = ExpandSoftKeywords(ann_content) ann_content = ExpandSoftKeywords(ann_content)
ann_tree: ast.Module = ast.parse(ann_content) ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
for node in ann_tree.body: for node in ann_tree.body:
# 检测 xxx: t.CEnum 形式的枚举成员变量 # 检测 xxx: t.CEnum 形式的枚举成员变量
if isinstance(node, ast.AnnAssign): if isinstance(node, ast.AnnAssign):
@@ -286,7 +286,7 @@ class AnnotationLoaderMixin:
with open(ModuleName, 'r', encoding='utf-8') as f: with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read() ann_content: str = f.read()
ann_content = ExpandSoftKeywords(ann_content) ann_content = ExpandSoftKeywords(ann_content)
ann_tree: ast.Module = ast.parse(ann_content) ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
for node in ann_tree.body: for node in ann_tree.body:
# 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句 # 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句
if isinstance(node, ast.AnnAssign): if isinstance(node, ast.AnnAssign):
+13
View File
@@ -32,6 +32,19 @@ class ConstEvalMixin:
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined) _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
if not isinstance(value_node, (ast.List, ast.Set)): if not isinstance(value_node, (ast.List, ast.Set)):
return None return None
# 显式空元素判据(与 TPV AssignGenMixin._init_global_array_from_list 的裸空指针
# 判据两侧一致):Python 侧的空 AST 元素就是 None(TPV 侧是裸 0)。列表元素计数
# 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值;
# 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。
for _gi, _elt in enumerate(value_node.elts):
if _elt is None:
raise TypeError(
f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。'
f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);'
f'拒绝静默补零生成残缺初始化,已终止编译。'
f'位置 line {getattr(value_node, "lineno", 0)}'
f':{getattr(value_node, "col_offset", 0)}'
)
if isinstance(ElemType, ir.ArrayType): if isinstance(ElemType, ir.ArrayType):
constants: list[ir.Constant] = [] constants: list[ir.Constant] = []
for elt in value_node.elts: for elt in value_node.elts:
+4 -4
View File
@@ -125,7 +125,7 @@ class LlvmGeneratorMixin:
TypeInfo: CTypeInfo | None = None TypeInfo: CTypeInfo | None = None
if Node.type_comment: if Node.type_comment:
try: try:
tc_ast: ast.Expression = ast.parse(Node.type_comment, mode='eval') tc_ast: ast.Expression = ast.parse(Node.type_comment, '<LlvmGenerator:type_comment>', mode='eval')
TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body) TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body)
except Exception as _e: except Exception as _e:
if _config_mode == "strict": if _config_mode == "strict":
@@ -701,10 +701,10 @@ class LlvmGeneratorMixin:
# LlvmCodeGenerator;未启用时 dwarf_enabled=False → 不构造 DI、不挂 !dbg, # LlvmCodeGenerator;未启用时 dwarf_enabled=False → 不构造 DI、不挂 !dbg,
# 生成的 .ll 与改动前逐字节一致(零影响) # 生成的 .ll 与改动前逐字节一致(零影响)
self.LlvmGen.dwarf_enabled = bool(getattr(self, 'DwarfEnabled', False)) self.LlvmGen.dwarf_enabled = bool(getattr(self, 'DwarfEnabled', False))
# Ctrl+C 调用栈(--ctrlc-stack):透传"本模块需注入 Install() 启动期构造器" # Ctrl+C 调用栈(--traceback):透传"本模块需注入 Install() 启动期构造器"
# 标记(仅 includes/dbgstack.vp 的翻译上下文为 True);未启用时为 False # 标记(仅 includes/traceback.vp 的翻译上下文为 True);未启用时为 False
# → finalize() 不做任何事,生成的 .ll 与改动前逐字节一致(零影响) # → finalize() 不做任何事,生成的 .ll 与改动前逐字节一致(零影响)
self.LlvmGen.ctrlc_stack_ctor = bool(getattr(self, 'CtrlcStackCtor', False)) self.LlvmGen.traceback_ctor = bool(getattr(self, 'TracebackCtor', False))
# --- 修复:共享 vtable 状态跨模块 --- # --- 修复:共享 vtable 状态跨模块 ---
# 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性, # 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性,
+1 -1
View File
@@ -80,7 +80,7 @@ class PythonParserMixin:
self.CurrentFile = FilePath self.CurrentFile = FilePath
# 解析Python代码为AST # 解析Python代码为AST
tree: ast.Module = ast.parse(content) tree: ast.Module = ast.parse(content, FilePath)
# 保存 AST 树供后续使用 # 保存 AST 树供后续使用
self.tree = tree self.tree = tree
+189 -41
View File
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os import os
import re import re
import threading
import vast as ast import vast as ast
import warnings import warnings
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@@ -96,13 +97,68 @@ class TypeAnnotationResolver:
# ============================================================ # ============================================================
# builtins 最后匹配(对齐 TPV TypeGenResolve._lookup_builtins_typedef): # builtins 最后匹配(对齐 TPV TypeGenResolve._lookup_builtins_typedef):
# 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的 # 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的 typedef
# "NAME: t.CTypedef = TYPE" 声明作为最终兜底(rawptr/size_t 等)。 # 声明作为最终兜底(rawptr/sizet/uint64 等)。
# builtins 始终纳入编译链但不在 App 文件 import 闭包,其 typedef 不随 # builtins 始终纳入编译链但不在 App 文件 import 闭包,其 typedef 不随
# from_imports 注入,故需按源文件扫描兜底。 # from_imports 注入,故需按源文件扫描兜底。
# 必须同时兼容两种等价写法:
# - 语法糖 `typedef 名字 = 类型`(builtins.vp 现用形态)
# - 注解形态 `名字: t.CTypedef = 类型`(stdint.vp 等库模块)
# 只认注解形态时语法糖声明全被跳过 → 兜底表为空 → 裸名退化为
# `%"{mod}.名字"*` 假结构体(sizet 曾被当成结构体,BinOp 报
# "Operands must be the same type, got (%"…sizet"*, i64)")。
# ============================================================ # ============================================================
_builtins_scan_cache: dict | None = None _builtins_scan_cache: dict | None = None
# builtins 解析链的**全链**重入深度上限与线程局部计数。
# 既有 `_resolve_builtins_type_str` 的 `depth`/`visited` 只在自身递归内传递;
# 而复合值(含 `|` / `[`)分支会经 `from_node` 重新进入
# `_from_node_name → _lookup_builtins_typedef`,把 depth/visited 归零 ⇒ 该方向
# 可绕过环检测无限递归(A = B | t.CPtr、B = A 一类声明)。此计数器覆盖
# `_lookup_builtins_typedef → _resolve_builtins_type_str → from_node → …` 全程,
# 超上限即判为成环并 fail-fast(不再沿链继续)。合法链通常 ≤ 4 层。
_BUILTINS_CHAIN_MAX_DEPTH: int = 32
_chain_state: threading.local = threading.local()
# 解析失败(成环/悬空)的 builtins typedef 名 → 错误报文缓存:
# 同一失败不再被反复重试(每次重试都会重新扫描/解析整条链)。
# builtins.vp 在一次编译进程内不变,故无需失效。
_builtins_failure_cache: dict[str, str] = {}
@classmethod
def _chain_depth_enter(cls) -> int:
depth: int = getattr(cls._chain_state, 'depth', 0)
cls._chain_state.depth = depth + 1
return depth
@classmethod
def _chain_depth_exit(cls, prev: int) -> None:
cls._chain_state.depth = prev
# `typedef 名字 = 类型` 语法糖(对应 SymbolUtils._ExpandTypedefs 的展开形态)
_TYPEDEF_SUGAR_RE: re.Pattern = re.compile(
r'^typedef\s+([A-Za-z_]\w*)\s*=(?!=)\s*(.+?)\s*$')
@staticmethod
def _parse_typedef_line(stripped: str) -> tuple[str, str] | None:
"""单行源码文本 → typedef 声明 (名字, 类型值);非 typedef 行返回 None
兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。
"""
m: re.Match | None = TypeAnnotationResolver._TYPEDEF_SUGAR_RE.match(stripped)
if m is not None:
return m.group(1), m.group(2)
if ':' not in stripped or 't.CTypedef' not in stripped:
return None
name, _, rest = stripped.partition(':')
if 't.CTypedef' not in rest:
return None
value: str = rest.partition('=')[2].strip()
name = name.strip()
if not name or not value:
return None
return name, value
@staticmethod @staticmethod
def _builtins_include_dirs() -> list[str]: def _builtins_include_dirs() -> list[str]:
"""includes 目录列表(builtins.vp 定位用)——只认项目配置 includes, """includes 目录列表(builtins.vp 定位用)——只认项目配置 includes,
@@ -150,23 +206,23 @@ class TypeAnnotationResolver:
if m: if m:
aliases[m.group(2)] = m.group(1) aliases[m.group(2)] = m.group(1)
continue continue
if ':' not in stripped: td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped)
continue if td is not None:
name: str typedefs[td[0]] = td[1]
rest: str # 只缓存"扫到了东西"的结果:includes 目录尚未装配(PROJECT_INCLUDE_DIRS 为空)
name, _, rest = stripped.partition(':') # 或 builtins.vp 未定位时,缓存空表会让本进程内后续所有 builtins typedef 解析
name = name.strip() # (sizet(x)/uint64(x) 这类 cast 调用)永久 miss,只在真正扫到内容后落缓存。
if 't.CTypedef' not in rest: if not typedefs and not aliases:
continue return {'typedefs': typedefs, 'aliases': aliases}
value: str = rest.partition('=')[2].strip()
if value:
typedefs[name] = value
cls._builtins_scan_cache = {'typedefs': typedefs, 'aliases': aliases} cls._builtins_scan_cache = {'typedefs': typedefs, 'aliases': aliases}
return cls._builtins_scan_cache return cls._builtins_scan_cache
@classmethod @classmethod
def _scan_library_typedef(cls, mod: str, attr: str) -> str | None: def _scan_library_typedef(cls, mod: str, attr: str) -> str | None:
"""查库模块源中 "attr: t.CTypedef = TYPE",返回 TYPE(权威定义)或 None""" """查库模块源中 typedef 声明,返回类型值(权威定义)或 None
兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。
"""
path: str | None = TypeAnnotationResolver._find_include_file(mod) path: str | None = TypeAnnotationResolver._find_include_file(mod)
if path is None: if path is None:
return None return None
@@ -180,29 +236,24 @@ class TypeAnnotationResolver:
stripped: str = line.strip() stripped: str = line.strip()
if not stripped or stripped.startswith('#'): if not stripped or stripped.startswith('#'):
continue continue
if ':' not in stripped: td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped)
continue if td is not None and td[0] == attr:
name: str return td[1]
rest: str
name, _, rest = stripped.partition(':')
if name.strip() != attr:
continue
if 't.CTypedef' not in rest:
continue
value: str = rest.partition('=')[2].strip()
if value:
return value
return None return None
@classmethod @classmethod
def _resolve_builtins_type_str(cls, type_str: str, aliases: dict[str, str], def _resolve_builtins_type_str(cls, type_str: str, aliases: dict[str, str],
SymbolTable, depth: int = 0) -> CTypeInfo | None: SymbolTable, depth: int = 0,
visited: frozenset[str] | None = None) -> CTypeInfo | None:
"""解析 builtins typedef 值(支持 t.XXX / mod.attr / 裸名;mod 支持 import 别名) """解析 builtins typedef 值(支持 t.XXX / mod.attr / 裸名;mod 支持 import 别名)
Args: Args:
type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT) type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT / sizet)
aliases: builtins.vp 的 import 别名表(__stdint → stdint) aliases: builtins.vp 的 import 别名表(__stdint → stdint)
depth: 递归深度守卫(防 typedef 值自引用成环) depth: 递归深度守卫(防 typedef 值自引用成环)
visited: 本次展开链上已展开的 typedef 名(环检测)。命中环/自引用返回
None(而不是退化成 %"{mod}.{名}"* 假结构体),由调用方
_lookup_builtins_typedef fail-fast 报出根因。
""" """
if not type_str or depth > 8: if not type_str or depth > 8:
return None return None
@@ -225,8 +276,13 @@ class TypeAnnotationResolver:
mod = mod.strip() mod = mod.strip()
attr = attr.strip() attr = attr.strip()
real_mod: str = aliases.get(mod, mod) real_mod: str = aliases.get(mod, mod)
# t/c 内建命名空间:走常规 Attribute 解析(含 CPtr/State 特殊语义) # t/c 内建命名空间:**优先按内建类名直接解析**,不依赖调用方 SymbolTable
# 的装配进度(import 注册阶段 t 尚未入表 ⇒ 原先返回 None,被上层误归因为
# "自引用/循环引用";实测 builtins typedef 'sizet' → 't.CSizeT' 即卡在此处)。
if real_mod in ('t', 'c'): if real_mod in ('t', 'c'):
_bi: CTypeInfo | None = TypeAnnotationResolver.from_type_name(attr)
if _bi is not None:
return _bi
node = TypeAnnotationResolver._parse_type_expr(type_str) node = TypeAnnotationResolver._parse_type_expr(type_str)
if node is not None: if node is not None:
return TypeAnnotationResolver.from_node(node, SymbolTable) return TypeAnnotationResolver.from_node(node, SymbolTable)
@@ -234,20 +290,58 @@ class TypeAnnotationResolver:
# 库模块权威 typedef(stdint.size_t → 读 stdint.vp 扫描) # 库模块权威 typedef(stdint.size_t → 读 stdint.vp 扫描)
lib_val: str | None = TypeAnnotationResolver._scan_library_typedef(real_mod, attr) lib_val: str | None = TypeAnnotationResolver._scan_library_typedef(real_mod, attr)
if lib_val is not None: if lib_val is not None:
return TypeAnnotationResolver._resolve_builtins_type_str(lib_val, aliases, SymbolTable, depth + 1) return TypeAnnotationResolver._resolve_builtins_type_str(
lib_val, aliases, SymbolTable, depth + 1, visited)
# 库源未命中:回退常规解析(模块可能已由 import 闭包注入 SymbolTable) # 库源未命中:回退常规解析(模块可能已由 import 闭包注入 SymbolTable)
node = TypeAnnotationResolver._parse_type_expr(type_str) node = TypeAnnotationResolver._parse_type_expr(type_str)
if node is not None: if node is not None:
return TypeAnnotationResolver.from_node(node, SymbolTable) return TypeAnnotationResolver.from_node(node, SymbolTable)
return None return None
# 裸名:按 BuiltinTypeMap/t 类名解析 # 裸名:名字本身即类型(t/C 类名、BuiltinTypeMap 的 C 速记名、裸 LLVM 标量名)
return TypeAnnotationResolver.from_type_name(type_str) # → 直接解析;否则它可能是**另一个 typedef 的引用**,沿 builtins typedef 表
# 继续展开到基础类型。
# 依据:stdint.vp 的 `size_t/SIZE_T: t.CTypedef = sizet` 与 builtins.vp 的
# `size_t/uzt: t.CTypedef = sizet` 都是"别名 → 权威名 → t 类型"的链式写法,
# 与 TPV resolve_typedef_or_map 的 CTypedef 表递归(TypeGenResolve.vp:723-734)
# 同口径。此前裸名一律走 from_type_name:链中名字命中不到基础类型时退化成
# %"{mod}.{名}"* 假结构体(sizet/size_t → "Type of #N arg mismatch:
# %…sizet"* != i32" 的根因)。
if TypeAnnotationResolver._is_direct_type_name(type_str):
return TypeAnnotationResolver.from_type_name(type_str)
chain: str | None = TypeAnnotationResolver._scan_builtins()['typedefs'].get(type_str)
if chain is None:
# 非 builtins typedef 名的裸名(str/bytes/None 等):保留既有兜底
return TypeAnnotationResolver.from_type_name(type_str)
seen: frozenset[str] = visited or frozenset()
if type_str in seen or chain == type_str:
# 自引用/环(如 `sizet: t.CTypedef = sizet`):无信息可展开,返回 None
return None
return TypeAnnotationResolver._resolve_builtins_type_str(
chain, aliases, SymbolTable, depth + 1, seen | {type_str})
@staticmethod
def _is_direct_type_name(name: str) -> bool:
"""名字本身即类型(非"指向另一个 typedef 的别名")
覆盖 t 的 CType 子类名(CInt/CChar/CSizeT/CInt64T 等)、BuiltinTypeMap 的
C 速记名(int/char/bool)与裸 LLVM 标量名(i64/double…,泛型推导产物)。
"""
if not name:
return False
if BuiltinTypeMap.Get(name) is not None:
return True
if name in CTypeInfo._get_llvm_primitive_map():
return True
cls: type | None = CTypeHelper.GetTModuleCType(name)
if cls is None:
cls = getattr(t, name, None)
return isinstance(cls, type) and issubclass(cls, t.CType) and cls is not t.CType
@staticmethod @staticmethod
def _parse_type_expr(text: str) -> ast.AST | None: def _parse_type_expr(text: str) -> ast.AST | None:
"""vast mode='eval' 解析单个表达式,返回表达式节点或 None""" """vast mode='eval' 解析单个表达式,返回表达式节点或 None"""
try: try:
expr_tree: ast.AST = ast.parse(text, mode='eval') expr_tree: ast.AST = ast.parse(text, '<TypeAnnotationResolver:type_expr>', mode='eval')
body = getattr(expr_tree, 'body', None) body = getattr(expr_tree, 'body', None)
if body is not None and not isinstance(body, list): if body is not None and not isinstance(body, list):
return body return body
@@ -278,18 +372,52 @@ class TypeAnnotationResolver:
"""builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明 """builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明
命中(该名字确为 builtins typedef)返回解析出的 CTypeInfo, 命中(该名字确为 builtins typedef)返回解析出的 CTypeInfo,
miss 返回 None(交由调用方走原有兜底)。 miss(该名字不是 builtins typedef)返回 None(交由调用方走原有兜底)。
rawptr 为编译器内置语法:优先于一切外部声明(不可重定义)。 rawptr 为编译器内置语法:优先于一切外部声明(不可重定义)。
名字在表内却解析不出基础类型 = 声明本身无信息/成环(自引用)⇒ fail-fast,
不再静默退化为 %"{mod}.{名}"* 假结构体(下游仅报 "Type of #N arg
mismatch: %…* != …",无法归因到 typedef 声明)。
""" """
if not TypeName: if not TypeName:
return None return None
if TypeName == cls._BUILTIN_ROWPTR_NAME: if TypeName == cls._BUILTIN_ROWPTR_NAME:
return cls._builtin_rawptr_info() return cls._builtin_rawptr_info()
data: dict = cls._scan_builtins() # 同一失败不再反复解析:命中失败缓存则直接按既有报文 fail-fast,
type_str: str | None = data['typedefs'].get(TypeName) # 不再重扫 builtins.vp / 重走整条解析链。
if type_str is None: _cached_err: str | None = cls._builtins_failure_cache.get(TypeName)
return None if _cached_err is not None:
return cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable) raise ValueError(_cached_err)
# 全链重入深度守卫:覆盖 `_lookup_builtins_typedef → _resolve_builtins_type_str
# → from_node → _from_node_name → _lookup_builtins_typedef` 的所有回路
# (含 `|`/`[` 复合值分支导致的 depth/visited 归零绕过)。超上限即成环。
_prev_depth: int = cls._chain_depth_enter()
try:
if _prev_depth >= cls._BUILTINS_CHAIN_MAX_DEPTH:
_err: str = (
f"builtins typedef 解析链成环或过深(>{cls._BUILTINS_CHAIN_MAX_DEPTH} 层),"
f"解析 '{TypeName}' 时终止:includes/builtins.vp 中相关 typedef 声明"
f"(含 `|` / `[...]` 复合值)互相引用成环。请改为指向真实基础类型"
f"(如 t.CSizeT),或断开环上引用。")
cls._builtins_failure_cache[TypeName] = _err
raise ValueError(_err)
data: dict = cls._scan_builtins()
type_str: str | None = data['typedefs'].get(TypeName)
if type_str is None:
return None
info: CTypeInfo | None = cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable)
if info is None:
# 不再一律归因"自引用/循环引用"(那正是把 t.CSizeT 解析失败误报成环、
# 把人引向错误方向的根因):把声明值与该值解析失败的形态如实报出。
_err = (
f"builtins typedef '{TypeName}' 无法解析到基础类型:includes/builtins.vp "
f"声明 '{TypeName}: t.CTypedef = {type_str}',其值 '{type_str}' 解析失败"
f"(既不是内建类型名,也不是链上可展开的 typedef)。"
f"请把该声明指向真实类型(如 t.CSizeT),或确认该类型名存在。")
cls._builtins_failure_cache[TypeName] = _err
raise ValueError(_err)
return info
finally:
cls._chain_depth_exit(_prev_depth)
@staticmethod @staticmethod
def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo: def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo:
@@ -742,6 +870,17 @@ class TypeAnnotationResolver:
Result.ArrayDims.insert(0, str(count_val)) Result.ArrayDims.insert(0, str(count_val))
else: else:
Result.PtrCount += 1 Result.PtrCount += 1
else:
# 单参数 t.CArray[T](= t.CArray[T, None]):长度自动推导数组。
# 指针位置语义为"元素指针" Ptr(T),需在元素类型上再加一层指针。
# 与裸名 CArray[T](HandlesTypeMerge.CollectAll 的
# `ElementType.PtrCount + 1`)、stub 侧(LLVMTypeMapper
# IsListAnnotation 的 `elem_type + '*'`)、声明端
# (DeclarationGenerator `[0 x T]` → _decay_array_to_ptr = T*)
# 以及 TPV(TypeGenResolve 单参 → Ptr(elem_ty))同口径。
# 此前缺这一层:t.CArray[str] 得 i8*(即 str 本身),
# result[k] 退化成 i8 → 写字符串指针触发 ptr→int fail-fast。
Result.PtrCount += 1
if ElemInfo.IsPtr and not ElemInfo.ArrayDims: if ElemInfo.IsPtr and not ElemInfo.ArrayDims:
Result.PtrCount = max(Result.PtrCount, 1) Result.PtrCount = max(Result.PtrCount, 1)
return Result return Result
@@ -789,12 +928,21 @@ class TypeAnnotationResolver:
params_list = slice_node.elts[0] params_list = slice_node.elts[0]
return_node = slice_node.elts[1] return_node = slice_node.elts[1]
ParamTypes = [] ParamTypes = []
if isinstance(params_list, ast.List): _ParamsIsList: bool = isinstance(params_list, ast.List)
if _ParamsIsList:
for elt in params_list.elts: for elt in params_list.elts:
ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable) ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable)
if ParamTypeInfo: if ParamTypeInfo:
ParamTypes.append(('', ParamTypeInfo)) ParamTypes.append(('', ParamTypeInfo))
if not ParamTypes: # 空参数表 `t.Callable[[], R]` 语义 = 零形参:LLVM 端即 `R ()*`,
# 调用点按零实参间接 call。此前在此注入 void 占位形参 → 得
# `R (void)*`(void 作形参本身不是合法 LLVM 函数类型),调用点
# 实参个数(0) 与形参个数(1) 不符 → llvmlite CallInstr
# `arg = args[i]` IndexError(Repl.vp 模块级 t.Callable 槽
# `ReplFragFn()` 翻译崩溃根因);与本文件另三处 Callable 形态
# (:378 / :422 / :471)的 `FuncPtrParams = []` 口径统一。
# 非 List 形态(遗留写法 t.Callable[A, R])保留原 void 占位行为。
if not ParamTypes and not _ParamsIsList:
ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void'))) ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void')))
Result.FuncPtrParams = ParamTypes Result.FuncPtrParams = ParamTypes
ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable) ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable)
+248 -19
View File
@@ -38,6 +38,32 @@ def _JoinConstParts(parts: list[object]) -> str | bytes:
return ''.join(str(p) for p in parts) return ''.join(str(p) for p in parts)
_HEX_ALPHABET: str = '0123456789abcdefABCDEF'
def _HexValOf(ch: str) -> int:
"""单个字符的十六进制位值;非十六进制字符返回 0(与 C 解析器 vast_c.HexVal 同口径)。"""
idx: int = _HEX_ALPHABET.find(ch)
if idx < 0:
return 0
return idx if idx < 16 else idx - 6
def _HexEscapeVal(body: str, start: int, count: int) -> int:
"""取转义序列后 count 位十六进制的值(越界位置与非十六进制字符均按 0 计)。
两侧解析器对非法 `\\x` / `\\u` / `\\U` 转义必须产出同一结果且都不许抛异常:
C 解析器走 HexVal(非 hex → 0),此处原来直接 `int(..., 16)`,遇 `\\xNN`
这类字面量会抛 ValueError(includes/ast/lexer.vp 的文档字符串即触发)。
"""
val: int = 0
n: int = len(body)
for k in range(count):
idx: int = start + k
val = val * 16 + _HexValOf(body[idx] if idx < n else '0')
return val
class TokenStream: class TokenStream:
"""token 流管理基类:统一提供游标操作、位置标注工具。""" """token 流管理基类:统一提供游标操作、位置标注工具。"""
@@ -54,6 +80,13 @@ class TokenStream:
# 类型注解解析模式:为 True 时 '*' / '**' 按 C 风格指针后缀处理 # 类型注解解析模式:为 True 时 '*' / '**' 按 C 风格指针后缀处理
# (int* → int | t.CPtr、int** → 嵌套两层),不按乘法/幂运算消费。 # (int* → int | t.CPtr、int** → 嵌套两层),不按乘法/幂运算消费。
self._InTypeAnnotation: bool = False self._InTypeAnnotation: bool = False
# 下标实参嵌套层数:'[' 实参内是表达式位置(t.CArray[T, A * B] 的尺寸),
# 此处的 '*' 在有右操作数时按乘法解析(见 _ParseTerm / _AtOperandAfterStarRun)。
self._SubscriptArgs: int = 0
# Tail token of the last consumed C-style pointer suffix ('*' / '**').
# _ParseParen compares it against _Prev() to recognize the paren-less cast
# `(T*)x`, which must not be mistaken for a plain group `(a | b)`.
self._PtrSuffixTail: Token | None = None
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# 游标操作 # 游标操作
@@ -397,8 +430,17 @@ class ExprParserMixin(TokenStream):
tok: Token = self._Peek() tok: Token = self._Peek()
op_type: type | None = None op_type: type | None = None
if tok.Type == OP: if tok.Type == OP:
if tok.Value == '*' and self._AtPtrSuffix(left):
# C 风格指针后缀('*' 一层);展开为 base | t.CPtr 后继续。
left = self._ConsumePtrSuffix(left)
continue
if self._InTypeAnnotation and tok.Value == '*': if self._InTypeAnnotation and tok.Value == '*':
break # 注解模式:'*' 是 C 风格指针后缀标记,不按乘法处理 # 注解模式:'*' 后已无操作数(']' / ',' / ')' / ':' / '=' / 行尾 / ';')
# 时它是 C 风格指针后缀标记,交上方判据与 _ParseTypeAnnotation 统一消费;
# 但下标实参内的尺寸表达式(t.CArray[T, A * B])'*' 后仍有操作数,
# 必须按乘法继续——否则下标收尾报 `expected ']'`。
if not (self._SubscriptArgs > 0 and self._AtOperandAfterStarRun()):
break
op_type = {'*': N.Mult, '/': N.Div, '//': N.FloorDiv, op_type = {'*': N.Mult, '/': N.Div, '//': N.FloorDiv,
'%': N.Mod, '@': N.MatMult}.get(tok.Value) '%': N.Mod, '@': N.MatMult}.get(tok.Value)
if op_type is None: if op_type is None:
@@ -429,6 +471,9 @@ class ExprParserMixin(TokenStream):
start: Token = self._Peek() start: Token = self._Peek()
base: N.expr = self._ParseAwaitPrimary() base: N.expr = self._ParseAwaitPrimary()
if self._AtOp('**'): if self._AtOp('**'):
if self._AtPtrSuffix(base):
# C 风格指针后缀('**' 计 2 层)
return self._ConsumePtrSuffix(base)
if self._InTypeAnnotation: if self._InTypeAnnotation:
return base # 注解模式:'**' 是 C 风格指针后缀标记(2 层),不按幂处理 return base # 注解模式:'**' 是 C 风格指针后缀标记(2 层),不按幂处理
self._Next() self._Next()
@@ -456,7 +501,13 @@ class ExprParserMixin(TokenStream):
while True: while True:
self._SkipNL() self._SkipNL()
tok: Token = self._Peek() tok: Token = self._Peek()
if tok.Type == OP and tok.Value == '.': if tok.Type == OP and tok.Value in ('*', '**') and self._AtPtrSuffixCall(node):
# `T*(x)` / `T**(x)` 指针后缀强转:解析为 cast 调用(与
# `(T | t.CPtr)(x)` 同形)。在 trailer 循环里消费,故其后的
# `.attr` / `[i]` / `(…)` 仍由本循环继续挂接(如
# `ast.Attribute*(expr_root).value`)。
node = self._ConsumePtrSuffixCall(node, start)
elif tok.Type == OP and tok.Value == '.':
self._Next() self._Next()
attr_tok: Token = self._ExpectName() attr_tok: Token = self._ExpectName()
attr_node: N.Attribute = N.Attribute(value=node, attr=attr_tok.Value, ctx=N.Load()) attr_node: N.Attribute = N.Attribute(value=node, attr=attr_tok.Value, ctx=N.Load())
@@ -543,13 +594,22 @@ class ExprParserMixin(TokenStream):
return node return node
def _ParseSliceList(self) -> N.AST: def _ParseSliceList(self) -> N.AST:
"""解析下标列表:a[1] / a[1:2] / a[1:2:3] / a[1,2] / a[...] / a[::]""" """解析下标列表:a[1] / a[1:2] / a[1:2:3] / a[1,2] / a[...] / a[::]
start: Token = self._Peek()
elts: list[N.AST] = [self._ParseSliceItem()] 下标实参内是**表达式**位置(如 `t.CArray[T, A * B]` 的尺寸),此处的 '*'
while self._AtOp(','): 应按乘法解析;同时 `t.CArray[str*, 4]` 的指针后缀仍要成立——两者由
self._Next() `_AtOperandAfterStarRun`('*' 后是否还有操作数)区分,故此处登记已进入实参。
self._SkipNL() """
elts.append(self._ParseSliceItem()) self._SubscriptArgs += 1
try:
start: Token = self._Peek()
elts: list[N.AST] = [self._ParseSliceItem()]
while self._AtOp(','):
self._Next()
self._SkipNL()
elts.append(self._ParseSliceItem())
finally:
self._SubscriptArgs -= 1
if len(elts) == 1: if len(elts) == 1:
return elts[0] return elts[0]
tup: N.Tuple = N.Tuple(elts=elts, ctx=N.Load()) tup: N.Tuple = N.Tuple(elts=elts, ctx=N.Load())
@@ -770,16 +830,13 @@ class ExprParserMixin(TokenStream):
out.append(simple[esc]) out.append(simple[esc])
i += 1 i += 1
elif esc == 'x': elif esc == 'x':
hex_digits: str = body[i + 1:i + 3] out.append(chr(_HexEscapeVal(body, i + 1, 2)))
out.append(chr(int(hex_digits, 16)))
i += 3 i += 3
elif esc == 'u': elif esc == 'u':
hex_digits = body[i + 1:i + 5] out.append(chr(_HexEscapeVal(body, i + 1, 4)))
out.append(chr(int(hex_digits, 16)))
i += 5 i += 5
elif esc == 'U': elif esc == 'U':
hex_digits = body[i + 1:i + 9] out.append(chr(_HexEscapeVal(body, i + 1, 8)))
out.append(chr(int(hex_digits, 16)))
i += 9 i += 9
elif esc.isdigit() and '0' <= esc <= '7': elif esc.isdigit() and '0' <= esc <= '7':
oct_digits: str = body[i:i + 3] oct_digits: str = body[i:i + 3]
@@ -823,13 +880,13 @@ class ExprParserMixin(TokenStream):
out.append(simple[esc]) out.append(simple[esc])
i += 1 i += 1
elif esc == 'x': elif esc == 'x':
out.append(int(body[i + 1:i + 3], 16)) out.append(_HexEscapeVal(body, i + 1, 2))
i += 3 i += 3
elif esc == 'u': elif esc == 'u':
out.append(int(body[i + 1:i + 5], 16)) out.append(_HexEscapeVal(body, i + 1, 4))
i += 5 i += 5
elif esc == 'U': elif esc == 'U':
out.append(int(body[i + 1:i + 9], 16)) out.append(_HexEscapeVal(body, i + 1, 8))
i += 9 i += 9
elif esc.isdigit() and '0' <= esc <= '7': elif esc.isdigit() and '0' <= esc <= '7':
oct_digits: str = body[i:i + 3] oct_digits: str = body[i:i + 3]
@@ -1105,6 +1162,10 @@ class ExprParserMixin(TokenStream):
self._FixLoc(walrus, start) self._FixLoc(walrus, start)
return walrus return walrus
first: N.expr = self.ParseExpr() first: N.expr = self.ParseExpr()
# Paren-less C cast `(T*)x`: the inner expression ends with a pointer suffix
# (_Prev() is that suffix's tail token) and the operand follows ')'. Same
# product as `(T*)(x)` / `T*(x)`: a Call whose func is `T | t.CPtr`.
is_ptr_cast: bool = self._PtrSuffixTail is not None and self._AtOp(')') and self._Prev() is self._PtrSuffixTail
self._SkipNL() self._SkipNL()
# 生成器表达式 (x for ...) # 生成器表达式 (x for ...)
if self._AtName('for'): if self._AtName('for'):
@@ -1130,6 +1191,11 @@ class ExprParserMixin(TokenStream):
return tup return tup
# 普通括号表达式 (x) # 普通括号表达式 (x)
self._ExpectOp(')') self._ExpectOp(')')
if is_ptr_cast and self._AtCastOperand():
operand: N.expr = self._ParseFactor()
cast_call: N.Call = N.Call(func=first, args=[operand], keywords=[])
self._FixLoc(cast_call, start)
return cast_call
return first return first
def _ParseList(self) -> N.expr: def _ParseList(self) -> N.expr:
@@ -1496,6 +1562,11 @@ class ExprParserMixin(TokenStream):
展开为 BinOp(BitOr) 链后,TPC/TPV 现有类型解析(TypeAnnotationResolver 展开为 BinOp(BitOr) 链后,TPC/TPV 现有类型解析(TypeAnnotationResolver
的 PtrCount 相加、TPV TypeGenResolve 的递归 BinOp 分支)零改动即可 的 PtrCount 相加、TPV TypeGenResolve 的递归 BinOp 分支)零改动即可
生成多级指针 LLVM 类型。 生成多级指针 LLVM 类型。
无法解析的后缀形态**响亮失败**(SyntaxError,含 行:列 + 源码片段 + 形态 +
正确写法示例),绝不静默丢弃:注解末尾若仍遗留未被 `_AtPtrSuffix` 接受的
'*'/'**'(如 `a: int * 2` / `a: int* | t.CPtr`),判据拒绝消费即终止编译
(历史事故:静默退化会产出错误的指针层数)。
""" """
saved_flag: bool = self._InTypeAnnotation saved_flag: bool = self._InTypeAnnotation
self._InTypeAnnotation = True self._InTypeAnnotation = True
@@ -1505,20 +1576,178 @@ class ExprParserMixin(TokenStream):
self._InTypeAnnotation = saved_flag self._InTypeAnnotation = saved_flag
if expr is None: if expr is None:
return None return None
# 循环消费 '*' / '**' 后缀('**' 是词法单 token,计 2 层) if self._AtPtrSuffix(expr):
return self._ConsumePtrSuffix(expr)
tok: Token = self._Peek()
if tok.Type == OP and tok.Value in ('*', '**'):
snippet: str = ''
try:
snippet = (self.Source.splitlines()[tok.Line - 1] or '').strip()
except Exception:
snippet = ''
raise SyntaxError(
f"[vast] 注解位置的指针后缀无法解析:第 {tok.Line} 行第 {tok.Col} 列的 "
f"{tok.Value!r} 前面不是可加后缀的类型(Name / Attribute / Subscript),"
f"或其后不是后缀的合法结束位置(注解结尾,或 ']' / ',' / ')' / ':' / '=')。"
f"源码片段 {snippet!r}。"
f"期望形态:`T*`(1 层)/ `T**`(2 层),T 可为裸名 / t.X / mod.Type / "
f"rawptr[T] / t.CPtr[T]。"
f"正确示例:`a: t.CPtr*` / `a: rawptr*` / `a: t.X**` / "
f"`a: rawptr[ast.AST*]` / `def f(p: int*) -> int**`。"
)
return expr
# 类型结束后缀:'_AtPtrSuffix' 的判据表
# - 全局(任意表达式上下文):'*'/'**' 串后紧跟这些 operator 时,乘法/幂本就缺右操作数
# (即原语法错误),故按指针后缀处理不会改变任何合法表达式的语义;
# - 注解模式额外允许语句结束位置(NEWLINE/NL/ENDMARKER/';')。
_PTR_SUFFIX_END_OPS: tuple[str, ...] = (']', ',', ')', ':', '=')
_PTR_SUFFIX_STMT_END_OPS: tuple[str, ...] = (';',)
# 指针后缀「强转调用」后缀:'*'/'**' 串后**紧贴**(无空白)'(' 时,写成 C 风格
# 强转调用 `T*(x)`。'(' **不**放进 `_PTR_SUFFIX_END_OPS`:`a * (b)` 是合法乘法
# ('(' 正是乘法的右操作数起点),无条件把 '(' 计入会误伤乘法;故另立一条
# 「紧贴」判据(见 `_AtPtrSuffixCall`),只认 `T*(` 这种**无空白**的强转写法。
_PTR_SUFFIX_CALL_OP: str = '('
def _AtPtrSuffix(self, base: N.expr) -> bool:
"""判定当前 '*'/'**' 串是否为 C 风格指针后缀(作用于 base)。
判据(两条同时成立):
1. base 是"类型原子"形态(Name / Attribute / Subscript)—— 即 ``ast.AST``、
``t.CArray[X, N]``、``rawptr[X]`` 之类;
2. 该 '*'/'**' 串(可跨括号内换行 NL)之后紧跟"无右操作数"位置:
``]`` / ``,`` / ``)`` / ``:`` / ``=``(全局),或注解模式下的
NEWLINE / NL / ENDMARKER / ``;``。
因此合法表达式 ``x[a * b]`` / ``f(x * y)`` / ``x[1 * 2]`` / ``a ** b``
('*' 之后是操作数)永不被判为指针后缀。
"""
if not isinstance(base, (N.Name, N.Attribute, N.Subscript)):
return False
j: int = 0
stars: int = 0
while True:
tok: Token = self._Peek(j)
if tok.Type == OP and tok.Value in ('*', '**'):
stars += 1 if tok.Value == '*' else 2
j += 1
elif tok.Type == NL:
j += 1
else:
break
if stars == 0:
return False
nxt: Token = self._Peek(j)
if nxt.Type == OP and nxt.Value in self._PTR_SUFFIX_END_OPS:
return True
if self._InTypeAnnotation:
if nxt.Type in (NEWLINE, NL, ENDMARKER):
return True
if nxt.Type == OP and nxt.Value in self._PTR_SUFFIX_STMT_END_OPS:
return True
return False
def _ConsumePtrSuffix(self, expr: N.expr) -> N.expr:
"""消费连续的 C 风格指针后缀('*' 一层 / '**' 两层),展开为 base | t.CPtr 链。"""
while True: while True:
tok: Token = self._Peek() tok: Token = self._Peek()
if tok.Type == OP and tok.Value == '*': if tok.Type == OP and tok.Value == '*':
self._Next() self._Next()
self._PtrSuffixTail = tok
expr = self._MakePtrType(expr, tok) expr = self._MakePtrType(expr, tok)
elif tok.Type == OP and tok.Value == '**': elif tok.Type == OP and tok.Value == '**':
self._Next() self._Next()
self._PtrSuffixTail = tok
expr = self._MakePtrType(expr, tok) expr = self._MakePtrType(expr, tok)
expr = self._MakePtrType(expr, tok) expr = self._MakePtrType(expr, tok)
else: else:
break break
return expr return expr
def _AtCastOperand(self) -> bool:
"""Whether the current token may start the operand of a paren-less cast `(T*)x`.
'(' is deliberately absent: `(T*)(x)` is already handled by the call trailer,
so that path's AST stays untouched. NL is not skipped either: a line break
after ')' starts a new statement, not a cast operand.
"""
tok: Token = self._Peek()
if tok.Type in (NAME, NUMBER, STRING):
return True
return tok.Type == OP and tok.Value in ('[', '{', '*', '&', '-', '+', '~')
def _AtOperandAfterStarRun(self) -> bool:
"""当前 '*'/'**' 串之后是否还有操作数起点(可跨括号内换行 NL)。
与 `_AtPtrSuffix` 互补:后者判「'*' 后是无右操作数位置」(']' / ',' / ')' /
':' / '=' / 行尾 / ';' ⇒ 指针后缀),本方法判其反面('*' 后是 NAME / NUMBER /
STRING / FSTRING 或 '(' / '[' / '{' / 一元前缀 ⇒ 乘法)。用于下标实参内区分
`t.CArray[T, A * B]`(乘法)与 `t.CArray[T*, 4]`(指针后缀)。
"""
j: int = 0
while True:
tok: Token = self._Peek(j)
if tok.Type == OP and tok.Value in ('*', '**'):
j += 1
elif tok.Type == NL:
j += 1
else:
break
nxt: Token = self._Peek(j)
if nxt.Type in (NAME, NUMBER, STRING, FSTRING):
return True
return nxt.Type == OP and nxt.Value in ('(', '[', '{', '*', '&', '-', '+', '~', '...')
def _AtPtrSuffixCall(self, base: N.expr) -> bool:
"""判定当前 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**。
判据(三条同时成立):
1. base 是"类型原子"形态(Name / Attribute / Subscript)—— 与
`_AtPtrSuffix` 的判据 1 同源(`ast.Call` / `list[ast.AST*]` 之类);
2. base 与 '*'/'**' 串**紧贴**(源码字符相邻,无空白/换行);
3. 该 '*'/'**' 串(串内**逐个紧贴**)之后**紧贴**一个 '('。
即整段写作 **无空白**的 `T*(x)`(C 风格强转写法)。为什么 '(' 不在
`_PTR_SUFFIX_END_OPS` 里:`a * (b)` 是合法乘法('(' 可作乘法右操作数的
起点),把 '(' 无条件计入表会误伤乘法。故此处叠加"前驱是类型原子 + 两侧
紧贴"两条判据:`a * (b + c)` / `f(x) * (y)` / `n * (m - 1)` /
`(i + 1) * (j)`('*' 前后有空白,且前驱多为非类型原子)仍按乘法解析。
"""
if not isinstance(base, (N.Name, N.Attribute, N.Subscript)):
return False
first: Token = self._Peek(0)
prev: Token = self._Prev()
if prev.Type in (NEWLINE, NL) or prev.End != first.Start:
return False # base 与 '*'/'**' 之间有空白/换行:非 C 风格强转写法
j: int = 0
stars: int = 0
last_end: int = -1
while True:
tok: Token = self._Peek(j)
if tok.Type == OP and tok.Value in ('*', '**'):
if last_end != -1 and tok.Start != last_end:
return False # 星串内部出现空白:非紧邻,不判为强转
last_end = tok.End
stars += 1
j += 1
else:
break
if stars == 0:
return False
nxt: Token = self._Peek(j)
return nxt.Type == OP and nxt.Value == self._PTR_SUFFIX_CALL_OP \
and nxt.Start == last_end
def _ConsumePtrSuffixCall(self, base: N.expr, start: Token) -> N.expr:
"""把已认定为 `T*(...)` 的指针后缀强转解析为**调用**。
产物与既有强转形态 `(T | t.CPtr)(...)` 逐字同形:同为 `ast.Call`
(func = `T` 展开的 `base | t.CPtr` 链,args/keywords = 括号实参),
不新造第二套 cast 表示。
"""
func: N.expr = self._ConsumePtrSuffix(base)
return self._ParseCallTail(func, start)
def _MakePtrType(self, base: N.expr, tok: Token) -> N.expr: def _MakePtrType(self, base: N.expr, tok: Token) -> N.expr:
"""构造一层指针包装:base | t.CPtr。""" """构造一层指针包装:base | t.CPtr。"""
cptr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()), cptr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
+156 -6
View File
@@ -22,9 +22,56 @@ from lib.vast.Tokenizer import (
) )
# ======================================================================
# virtual / novirtual 修饰符(软关键字)
# ======================================================================
# 语法:`virtual class` / `novirtual class` / `virtual def` / `novirtual def`
# (修饰符写在 class/def **之前**,允许与修饰对象同行,也允许各占一行)。
#
# 语义:与既有装饰器**同一套表示**——virtual → `@t.CVTable`,
# novirtual → `@t.NoVTable`。本层只"合成装饰器节点",不新增第二套表示:
# - 类级:CVTable 强制有 vtable / NoVTable 强制无 vtable
# (TPC:HandlesClassDef._resolve_class_flags 读 ClassDef.decorator_list
# 的 CVTable/NoVTable;TPV:ClassGenBase._detect_vtable_status)
# - 方法级:CVTable 进虚表槽 / NoVTable 不进虚表槽
# (TPV:ClassGenBase._should_method_be_virtual;TPC:同判据见
# SymbolUtils.ClassVtableSlotMethods / 方法收集处)
#
# 行号守恒:合成节点的位置取"修饰符 token"的位置,ClassDef/FunctionDef 自身的
# lineno/col_offset 仍是源码里 class/def 的真实位置 ⇒ 与手写装饰器形态在
# 行号上也不产生任何偏移(vs 文本插入装饰器行会整篇 +1 行)。
#
# 软关键字边界(非保留字):
# - 只在**语句起始**且后随 class/def 时生效;作标识符的其它位置
# (`x.virtual` / `f(virtual)` / `d = {"virtual": 1}`)逐字不变;
# - 语句起始但后随非 class/def ⇒ **响亮失败**(绝不静默丢弃修饰符);
# - `virtual def` / `novirtual def` 出现在类体之外(模块级 / 函数体 / 分支体)
# ⇒ **响亮失败**。
_VIRTUAL_MARKERS: dict[str, str] = {'virtual': 'CVTable', 'novirtual': 'NoVTable'}
# def 的别名(与 Tokenizer.SOFT_KEYWORDS 的 def 系列同集合,**不含** extern——
# extern 由 _ParseExternDef 单独解析,不能走 _ParseFunctionDef)
_DEF_ALIASES: tuple[str, ...] = ('fn', 'fun', 'func', 'function')
_VIRTUAL_POSITION_HINT: str = (
'可写位置:类级 `virtual class C:` / `novirtual class C:`;'
'方法级 `virtual def m(self): ...` / `novirtual def m(self): ...`'
'(**只能写在类体内**:virtual class / class / novirtual class 的类体)'
)
_VIRTUAL_EXAMPLE: str = (
'virtual class Shape: / novirtual class Node: / '
'class A: virtual def run(self) -> int: ... / '
'novirtual class B: novirtual def helper(self) -> int: ...'
)
class Parser(ExprParserMixin): class Parser(ExprParserMixin):
"""Viper/Python 完整语法分析器。""" """Viper/Python 完整语法分析器。"""
# virtual / novirtual 修饰符:语句起始 + 紧跟 class/def 时才生效(软关键字)。
# _InClassBody:当前正在解析的套件的**直接拥有者**是否为 ClassDef
# (由 _ParseSuite 按 owner_is_class 维护),用于 fail-fast
# "virtual def / novirtual def 只能直接写在类体内"。
_InClassBody: bool = False
# ================================================================== # ==================================================================
# 模块级入口 # 模块级入口
# ================================================================== # ==================================================================
@@ -67,6 +114,14 @@ class Parser(ExprParserMixin):
# def 分派之前 # def 分派之前
if v == 'define' and self._Peek(1).Type == NAME: if v == 'define' and self._Peek(1).Type == NAME:
return self._ParseDefineDef() return self._ParseDefineDef()
# virtual / novirtual 修饰符(软关键字):只在"语句起始 + 紧跟 class/def"
# 时生效,合成既有装饰器 @t.CVTable / @t.NoVTable(见 _ParseVirtualModified)。
# 放在 def/class 分派之前:修饰符必须先被消费,class/def 才能照常分派。
# 与软关键字 fn→def 的先后关系:`.vp` 源码在进入本解析器之前已过
# SymbolUtils.ExpandSoftKeywords(fn/fun/func/function → def),
# 故 `virtual fn m()` 在此已是 `virtual def m()`;本层只认 class/def。
if v in _VIRTUAL_MARKERS:
return self._ParseVirtualModified()
# 软关键字函数定义:fn/fun/func/function + 标识符 # 软关键字函数定义:fn/fun/func/function + 标识符
if v in SOFT_KEYWORDS and self._Peek(1).Type == NAME: if v in SOFT_KEYWORDS and self._Peek(1).Type == NAME:
return self._ParseFunctionDef() return self._ParseFunctionDef()
@@ -520,8 +575,83 @@ class Parser(ExprParserMixin):
return self._ParseFunctionDef(is_async=True, decorators=decorator_list) return self._ParseFunctionDef(is_async=True, decorators=decorator_list)
if tok.Type == NAME and tok.Value == 'class': if tok.Type == NAME and tok.Value == 'class':
return self._ParseClassDef(decorators=decorator_list) return self._ParseClassDef(decorators=decorator_list)
if tok.Type == NAME and tok.Value in _VIRTUAL_MARKERS:
# `@deco` 与 `virtual class`/`virtual def` 组合:合成标记追加在既有装饰器之后
return self._ParseVirtualModified(decorators=decorator_list)
raise SyntaxError(f"decorator must be followed by def/class, got {tok.Value!r}") raise SyntaxError(f"decorator must be followed by def/class, got {tok.Value!r}")
# ==================================================================
# virtual / novirtual 修饰符 + class/def
# ==================================================================
def _SourceLine(self, lineno: int) -> str:
"""取 lineno 行的源码文本(去掉行终止符),供 fail-fast 报文附源码片段。"""
starts: list[int] = self.LineStarts
if lineno < 1 or lineno > len(starts):
return ''
begin: int = starts[lineno - 1]
end: int = starts[lineno] if lineno < len(starts) else len(self.Source)
return self.Source[begin:end].rstrip('\n').rstrip('\r')
def _VirtualSyntaxError(self, detail: str, tok: Token, extra: str = '') -> SyntaxError:
"""构造 virtual/novirtual 修饰符非法形态的 fail-fast 报文。
报文含 行:列 + 源码片段 + 该修饰符允许出现的位置 + 正确示例;行尾另附
`at line N`(TPC 既有诊断路径按该正则兜底提取行号,见
HandlesImports._ParseFailureSite),文件名由调用方补全。
"""
return SyntaxError(
f"[TPC] virtual/novirtual 修饰符非法({detail}):行 {tok.Line} 列 {tok.Col},"
f"源码片段 {self._SourceLine(tok.Line).strip()!r}。"
f"{_VIRTUAL_POSITION_HINT}。正确示例:{_VIRTUAL_EXAMPLE}。"
+ (f" {extra}" if extra else '')
+ " (修饰符不会被静默忽略:非法形态一律终止编译)"
+ f" at line {tok.Line}"
)
def _ParseVirtualModified(self, decorators: list[N.expr] | None = None) -> N.stmt:
"""`virtual class` / `novirtual class` / `virtual def` / `novirtual def`。
合成 `@t.CVTable` / `@t.NoVTable` 装饰器后交给 _ParseClassDef /
_ParseFunctionDef,产出与**手写同名装饰器**逐字段同形的 AST
(decorator_list 多一个 Attribute(Name('t', Load), 'CVTable'|'NoVTable', Load)),
故下游(_resolve_class_flags / _should_method_be_virtual 同族判据)零新增分支。
非法形态**响亮失败**(不静默丢修饰符):
- 后随非 class/def;
- `virtual def` / `novirtual def` 不在类体内(模块级 / 函数体 / 分支体)。
"""
mod: Token = self._Peek() # virtual / novirtual
nxt: Token = self._Peek(1)
marker: str = _VIRTUAL_MARKERS[mod.Value]
# 后随必须是 class / def;def 的别名 fn/fun/func/function 同样接受
# (与 TPV 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的
# fn→def 展开只命中**行首**的 fn,`virtual fn m()` 不会被先展开,
# 故此处必须显式接受别名(否则两端对同一源码一端报错一端通过)。
if nxt.Type != NAME or (nxt.Value != 'class'
and nxt.Value != 'def'
and nxt.Value not in _DEF_ALIASES):
nxt_text: str = ('(行尾)' if nxt.Type in (NEWLINE, NL, ENDMARKER)
else repr(nxt.Value))
raise self._VirtualSyntaxError(
f"{mod.Value!r} 后随 {nxt_text}(非 class/def)", mod,
f"`{mod.Value}` 只在 class/def(def 的别名 fn/fun/func/function)"
f"之前生效;若此处本意是普通标识符名,"
f"请改名(该词在语句起始会被判为修饰符)。")
if nxt.Value == 'def' and not self._InClassBody:
raise self._VirtualSyntaxError(
f'{mod.Value} def 出现在类体之外', mod,
f'{mod.Value} def 描述的是「类体内方法的虚/非虚」,'
f'模块级 / 函数体 / 分支体内没有可归属的类。')
self._Next() # 消费修饰符
deco: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
attr=marker, ctx=N.Load())
self._FixLoc(deco, mod)
deco_list: list[N.expr] = list(decorators or [])
deco_list.append(deco)
if nxt.Value == 'class':
return self._ParseClassDef(decorators=deco_list)
return self._ParseFunctionDef(decorators=deco_list)
# ================================================================== # ==================================================================
# 函数定义(含软关键字 fn/fun/func/function) # 函数定义(含软关键字 fn/fun/func/function)
# ================================================================== # ==================================================================
@@ -766,7 +896,7 @@ class Parser(ExprParserMixin):
break break
self._ExpectOp(')') self._ExpectOp(')')
self._ExpectOp(':') self._ExpectOp(':')
body: list[N.stmt] = self._ParseSuite() body: list[N.stmt] = self._ParseSuite(owner_is_class=True)
node: N.ClassDef = N.ClassDef( node: N.ClassDef = N.ClassDef(
name=name_tok.Value, bases=bases, keywords=keywords, body=body, name=name_tok.Value, bases=bases, keywords=keywords, body=body,
decorator_list=decorators or [], type_params=type_params, decorator_list=decorators or [], type_params=type_params,
@@ -777,11 +907,18 @@ class Parser(ExprParserMixin):
# ================================================================== # ==================================================================
# 语句块(套件) # 语句块(套件)
# ================================================================== # ==================================================================
def _ParseSuite(self) -> list[N.stmt]: def _ParseSuite(self, owner_is_class: bool = False) -> list[N.stmt]:
"""解析语句块:':' NEWLINE INDENT ... DEDENT 或单行简单语句。 """解析语句块:':' NEWLINE INDENT ... DEDENT 或单行简单语句。
块首允许出现空行/注释行(NL),跳过后再检测 INDENT。 块首允许出现空行/注释行(NL),跳过后再检测 INDENT。
owner_is_class:本套件的**直接拥有者**是否 ClassDef。套件解析期间置
`_InClassBody`,供 `_ParseVirtualModified` 判 `virtual def`/`novirtual def`
是否"直接写在类体内"(函数体 / 分支体 / 循环体都不算)。默认 False ⇒
除类体外的所有套件拥有者天然正确,无需逐个改动。
""" """
prev_in_class: bool = self._InClassBody
self._InClassBody = owner_is_class
# 已消费 ':' 后的情况:检查是否换行 # 已消费 ':' 后的情况:检查是否换行
if self._At(NEWLINE): if self._At(NEWLINE):
self._Next() self._Next()
@@ -804,8 +941,10 @@ class Parser(ExprParserMixin):
body.append(extra) body.append(extra)
self._SkipNEWLINE() self._SkipNEWLINE()
self._Expect(DEDENT) self._Expect(DEDENT)
self._InClassBody = prev_in_class
return body return body
# 空套件(语法错误,宽松返回空) # 空套件(语法错误,宽松返回空)
self._InClassBody = prev_in_class
return [] return []
# 单行套件:statement (; statement)* NEWLINE # 单行套件:statement (; statement)* NEWLINE
body = [self.ParseStatement()] body = [self.ParseStatement()]
@@ -815,6 +954,11 @@ class Parser(ExprParserMixin):
break break
body.append(self.ParseStatement()) body.append(self.ParseStatement())
self._ExpectNEWLINE() self._ExpectNEWLINE()
# 吞掉紧跟在单行套件之后的注释行/空行 NL:多行套件分支在 DEDENT 之前
# 已由循环内 _SkipNEWLINE 吞掉这些 NL,单行套件没有 DEDENT,若不在此吞掉,
# 调用方紧随其后的 elif/else/except/finally 续接子句会被 NL 挡住 → 分支链断裂。
self._SkipNL()
self._InClassBody = prev_in_class
return body return body
def _Expect(self, tok_type: str, value: str | None = None) -> Token: def _Expect(self, tok_type: str, value: str | None = None) -> Token:
@@ -1227,9 +1371,15 @@ def Parse(source: str) -> N.Module:
def _ComputeLineStarts(source: str) -> list[int]: def _ComputeLineStarts(source: str) -> list[int]:
"""计算源码所有行起始字符索引(含首行 0)。""" """计算源码所有行起始字符索引(含首行 0)。
Uses C-level str.find instead of a per-character Python loop (large .vp
sources: millions of characters scanned once per parse).
"""
starts: list[int] = [0] starts: list[int] = [0]
for i, ch in enumerate(source): Find = source.find
if ch == '\n': i: int = Find('\n')
starts.append(i + 1) while i >= 0:
starts.append(i + 1)
i = Find('\n', i + 1)
return starts return starts
+4 -2
View File
@@ -179,7 +179,7 @@ class Tokenizer:
if self.Pos >= self.Length: if self.Pos >= self.Length:
break break
continue continue
if ch in ' \t\f': if ch in ' \t\f\v':
self.Pos += 1 self.Pos += 1
continue continue
if ch == '\r': if ch == '\r':
@@ -258,7 +258,9 @@ class Tokenizer:
elif ch == '\t': elif ch == '\t':
col = (col // 8 + 1) * 8 col = (col // 8 + 1) * 8
self.Pos += 1 self.Pos += 1
elif ch == '\f': elif ch == '\f' or ch == '\v':
# \f(换页)与 \v(垂直制表)不计入缩进列,直接跳过;\v 与 Viper
# 词法层 includes/ast/lexer.vp `_is_space` 一致(对齐语言定义)。
self.Pos += 1 self.Pos += 1
else: else:
break break
+90 -4
View File
@@ -19,6 +19,7 @@ ExpandSoftKeywords 预处理。
from __future__ import annotations from __future__ import annotations
import os as _os import os as _os
import re as _re
import sys as _sys import sys as _sys
from lib.vast.Nodes import AST from lib.vast.Nodes import AST
@@ -56,27 +57,112 @@ def _ParsePure(source: str, mode: str) -> AST:
# C 加速解析器(vast_c 扩展):可用则优先,输出与 vast.parse 结构完全一致。 # C 加速解析器(vast_c 扩展):可用则优先,输出与 vast.parse 结构完全一致。
# VAST_USE_C=0 禁用 C 路径;=strict 时 C 路径异常直接抛出(fail-fast)。 # VAST_USE_C=0 禁用 C 路径;=strict 时 C 路径异常直接抛出(fail-fast)。
#
# 回退策略(响亮回退):C 路径抛异常时回退纯 Python,并向 stderr 打印一行可诊断
# 信息(文件名 + 异常)。该回退**不会**改变产出——纯 Python 是本包语义基准,
# 故绝不静默产出与纯 Python 不同的 AST。连续相同的(文件, 异常)只打印一次。
_VAST_C_PARSE = None _VAST_C_PARSE = None
try: try:
from lib.vast_c import parse as _VAST_C_PARSE from lib.vast_c import parse as _VAST_C_PARSE
except Exception: except Exception:
_VAST_C_PARSE = None _VAST_C_PARSE = None
_CFallbackSeen: set = set()
def _ReportCFallback(filename: str, exc: BaseException) -> None:
"""C 解析失败回退时的可诊断信息(去重,写 stderr)。"""
detail: str = f'{type(exc).__name__}: {exc}'
key = (filename, detail)
if key in _CFallbackSeen:
return
_CFallbackSeen.add(key)
_sys.stderr.write(
f'[vast] C 解析器失败,回退纯 Python:{filename}: {detail}\n')
# vast 语法错误统一报文:解析器原生消息末段带 "at line N"(ExprParser/Parser 各站点同款),
# 取出行号后补齐源码行与列号,用户看到的是单行 [TPC] 报文而不是解析器内部调用链。
_SYNTAX_LINE_RE: _re.Pattern[str] = _re.compile(r'at line (\d+)')
# 原生消息里的 token 原文(如 NAME('foo') / OP('*=') / INDENT(' ')),用于定位列号
_SYNTAX_TOKEN_RE: _re.Pattern[str] = _re.compile(r"\((?:'([^']*)'|\"([^\"]*)\")\)")
def _SourceLine(source: str, lineno: int) -> str:
"""取 1-based 第 lineno 行源码(行号缺失/越界返回空串)。"""
if lineno <= 0:
return ''
lines: list[str] = source.splitlines()
if lineno > len(lines):
return ''
return lines[lineno - 1]
def _LocateToken(msg: str, line_text: str) -> int:
"""定位原生消息里 token 原文在该行的 1-based 列;找不到退回首个非空白列。"""
if not line_text:
return 0
pos: int = -1
matched = _SYNTAX_TOKEN_RE.search(msg)
if matched is not None:
tok_text: str = matched.group(1) if matched.group(1) is not None else matched.group(2)
if tok_text.strip():
pos = line_text.find(tok_text)
if pos < 0:
pos = len(line_text) - len(line_text.lstrip())
return pos + 1
def _HumanizeSyntaxError(exc: SyntaxError, source: str, filename: str) -> SyntaxError:
"""把解析器的 SyntaxError 收口成「行:列 + 源码片段 + 原始消息」的单行报文。
重新抛出的语法错误带 filename/lineno/offset/text 四要素,故上层即使用
traceback.format_exc 也只打印该文件对应行与 caret,不再漏出解析器内部调用链。
"""
msg: str = str(exc)
lineno: int = getattr(exc, 'lineno', 0) or 0
if lineno <= 0:
found = _SYNTAX_LINE_RE.search(msg)
if found is not None:
lineno = int(found.group(1))
line_text: str = getattr(exc, 'text', '') or _SourceLine(source, lineno)
col: int = getattr(exc, 'offset', 0) or 0
if col <= 0:
col = _LocateToken(msg, line_text)
new: SyntaxError = SyntaxError(
f"[TPC] 语法错误({msg}):行 {lineno} 列 {col},源码片段 {line_text.strip()!r}。")
new.filename = filename
new.lineno = lineno
new.offset = col
new.text = line_text
return new
def parse(source: str, filename: str = '<unknown>', mode: str = 'exec', *, def parse(source: str, filename: str = '<unknown>', mode: str = 'exec', *,
type_comments: bool = False, feature_version: tuple[int, int] | int | None = None) -> AST: type_comments: bool = False, feature_version: tuple[int, int] | int | None = None) -> AST:
"""解析源码字符串为 AST(与 ast.parse 同签名、同结构)。 """解析源码字符串为 AST(与 ast.parse 同签名、同结构)。
优先使用 C 加速解析器 vast_c(输出结构完全一致),异常时回退纯 Python vast; 优先使用 C 加速解析器 vast_c(输出结构完全一致),异常时回退纯 Python vast
环境变量 VAST_USE_C:'0' 禁用 C 路径,'strict' 时 C 路径异常直接抛出。 并打印可诊断信息;环境变量 VAST_USE_C:'0' 禁用 C 路径,'strict' 时 C 路径
异常直接抛出。两条路径的 SyntaxError 都收口成带 文件/行/列/源码片段 的统一
报文(不再向用户漏出解析器内部 traceback)。
""" """
if _VAST_C_PARSE is not None and _os.environ.get('VAST_USE_C', '1') != '0': if _VAST_C_PARSE is not None and _os.environ.get('VAST_USE_C', '1') != '0':
try: try:
return _VAST_C_PARSE(source, mode) return _VAST_C_PARSE(source, mode)
except Exception: except SyntaxError as _exc:
if _os.environ.get('VAST_USE_C') == 'strict':
raise _HumanizeSyntaxError(_exc, source, filename) from None
_ReportCFallback(filename, _exc)
except Exception as _exc:
if _os.environ.get('VAST_USE_C') == 'strict': if _os.environ.get('VAST_USE_C') == 'strict':
raise raise
return _ParsePure(source, mode) _ReportCFallback(filename, _exc)
try:
return _ParsePure(source, mode)
except SyntaxError as _exc:
raise _HumanizeSyntaxError(_exc, source, filename) from None
__all__: list[str] = [ __all__: list[str] = [
Binary file not shown.
+412 -19
View File
@@ -46,6 +46,22 @@ typedef struct {
int EndOff; int EndOff;
} Token; } Token;
/* Token 类型名(与 Python vast.Tokenizer 的常量同名,使两侧报文措辞一致)。 */
static const char *TokKindName(TokenKind K) {
switch (K) {
case TK_NAME: return "NAME";
case TK_NUMBER: return "NUMBER";
case TK_STRING: return "STRING";
case TK_FSTRING: return "FSTRING";
case TK_OP: return "OP";
case TK_NEWLINE: return "NEWLINE";
case TK_NL: return "NL";
case TK_INDENT: return "INDENT";
case TK_DEDENT: return "DEDENT";
default: return "ENDMARKER";
}
}
/* ================= 词法分析器(Lexer) ================= */ /* ================= 词法分析器(Lexer) ================= */
typedef struct { typedef struct {
@@ -81,7 +97,11 @@ typedef struct {
int Error; /* 语法错误标志(可回退) */ int Error; /* 语法错误标志(可回退) */
const char *ErrMsg; const char *ErrMsg;
int ErrLine; int ErrLine;
Token *ErrTok; /* 出错处的 token(供报错带上类型与原文) */
int FStringExprEnd; /* f-string 表达式段结束位置(body 内偏移) */ int FStringExprEnd; /* f-string 表达式段结束位置(body 内偏移) */
int InTypeAnnotation; /* 类型注解模式:'*'/'**' 允许按 C 风格指针后缀判定 */
int InClassBody; /* 当前套件的直接拥有者是否为 ClassDef(virtual def 判据) */
Token *PtrSuffixTail; /* 最近消费的 C 指针后缀尾 token(识别 (T*)x 无括号强转) */
} Parser; } Parser;
/* vast.nodes 模块与节点类缓存(进程级惰性初始化) */ /* vast.nodes 模块与节点类缓存(进程级惰性初始化) */
@@ -481,7 +501,9 @@ static void HandleIndent(Lexer *L) {
} else if (Ch == '\t') { } else if (Ch == '\t') {
Col = (Col / 8 + 1) * 8; Col = (Col / 8 + 1) * 8;
L->Pos++; L->Pos++;
} else if (Ch == '\f') { } else if (Ch == '\f' || Ch == '\v') {
/* \f(换页)与 \v(垂直制表)不计入缩进列;\v 对齐 Viper 词法层
* includes/ast/lexer.vp 的 _is_space(与 Python Tokenizer 同口径)。 */
L->Pos++; L->Pos++;
} else { } else {
break; break;
@@ -770,7 +792,7 @@ static void TokenizeMainLoop(Lexer *L) {
} }
continue; continue;
} }
if (Ch == ' ' || Ch == '\t' || Ch == '\f') { if (Ch == ' ' || Ch == '\t' || Ch == '\f' || Ch == '\v') {
L->Pos++; L->Pos++;
continue; continue;
} }
@@ -927,6 +949,7 @@ static void SyntaxErrorAt(Parser *P, Token *T, const char *Msg) {
P->Error = 1; P->Error = 1;
P->ErrMsg = Msg; P->ErrMsg = Msg;
P->ErrLine = T ? T->Line : 0; P->ErrLine = T ? T->Line : 0;
P->ErrTok = T;
} }
static Token *ExpectOp(Parser *P, const char *S) { static Token *ExpectOp(Parser *P, const char *S) {
@@ -1466,6 +1489,7 @@ static PyObject *JoinConstParts(PyObject *Parts) {
/* ================= 表达式解析 ================= */ /* ================= 表达式解析 ================= */
static PyObject *MakeTAttrNode(Parser *P, const char *AttrName);
static PyObject *ParseExpr(Parser *P); static PyObject *ParseExpr(Parser *P);
static PyObject *ParseConditional(Parser *P); static PyObject *ParseConditional(Parser *P);
static PyObject *ParseDisjunction(Parser *P); static PyObject *ParseDisjunction(Parser *P);
@@ -1507,7 +1531,7 @@ static PyObject *ParseSubExpression(Parser *P, const char *Text, Py_ssize_t Text
int AbsBase); int AbsBase);
static int SkipInnerLiteral(const char *Raw, int BodyStart, int Start, int BodyEnd); static int SkipInnerLiteral(const char *Raw, int BodyStart, int Start, int BodyEnd);
/* f-string 内部字面量常量(仅 lineno/col_offset,对齐 vast) */ /* f-string 相邻字面量合并常量(仅 lineno/col_offset,对齐 vast) */
static PyObject *MakeFStrConstant(Parser *P, PyObject *Value, Token *OuterTok) { static PyObject *MakeFStrConstant(Parser *P, PyObject *Value, Token *OuterTok) {
PyObject *N = MakeConstant(P, Value); PyObject *N = MakeConstant(P, Value);
if (N != NULL) { if (N != NULL) {
@@ -1516,6 +1540,26 @@ static PyObject *MakeFStrConstant(Parser *P, PyObject *Value, Token *OuterTok) {
return N; return N;
} }
/* 合并常量:位置取被合并的前一个 Constant 节点(对齐 Python 的 parts[-1].lineno/col_offset)。 */
static PyObject *MakeFStrConstantLike(Parser *P, PyObject *Value, PyObject *SrcNode) {
PyObject *N = MakeConstant(P, Value);
if (N == NULL) {
return NULL;
}
PyObject *Line = PyObject_GetAttrString(SrcNode, "lineno");
PyObject *Col = PyObject_GetAttrString(SrcNode, "col_offset");
if (Line != NULL) {
PyObject_SetAttrString(N, "lineno", Line);
}
if (Col != NULL) {
PyObject_SetAttrString(N, "col_offset", Col);
}
Py_XDECREF(Line);
Py_XDECREF(Col);
PyErr_Clear();
return N;
}
static PyObject *ParseStringConcat(Parser *P) { static PyObject *ParseStringConcat(Parser *P) {
Token *Start = PeekTok(P, 0); Token *Start = PeekTok(P, 0);
PyObject *Parts = NewList(); /* JoinedStr 的 values */ PyObject *Parts = NewList(); /* JoinedStr 的 values */
@@ -1607,7 +1651,7 @@ static PyObject *ParseStringConcat(Parser *P) {
Py_DECREF(ConstParts); Py_DECREF(ConstParts);
return NULL; return NULL;
} }
PyObject *MergedConst = MakeFStrConstant(P, Merged, Start); PyObject *MergedConst = MakeFStrConstantLike(P, Merged, Last);
Py_DECREF(Merged); Py_DECREF(Merged);
if (MergedConst == NULL) { if (MergedConst == NULL) {
Py_DECREF(JVals); Py_DECREF(JVals);
@@ -1675,7 +1719,7 @@ static PyObject *ParseStringConcat(Parser *P) {
Py_DECREF(ConstParts); Py_DECREF(ConstParts);
return NULL; return NULL;
} }
PyObject *MergedConst = MakeFStrConstant(P, Merged, Start); PyObject *MergedConst = MakeFStrConstantLike(P, Merged, Last);
Py_DECREF(Merged); Py_DECREF(Merged);
if (MergedConst == NULL) { if (MergedConst == NULL) {
Py_DECREF(Parts); Py_DECREF(Parts);
@@ -2510,6 +2554,170 @@ static PyObject *ParseArith(Parser *P) {
return ParseBinLoop(P, ParseTerm, "+", "-", "Add", "Sub"); return ParseBinLoop(P, ParseTerm, "+", "-", "Add", "Sub");
} }
/* ============ C 风格指针后缀(* / **,对齐 vast ExprParser) ============ */
/* 构造一层指针包装 base | t.CPtr(对齐 Python _MakePtrType)。 */
static PyObject *MakePtrType(Parser *P, PyObject *Base, Token *Tok) {
PyObject *CPtr = MakeTAttrNode(P, "CPtr");
if (CPtr == NULL) {
return NULL;
}
PyObject *Op = MakeSingleton(P, "BitOr");
if (Op == NULL) {
Py_DECREF(CPtr);
return NULL;
}
PyObject *Argv[3] = { Base, Op, CPtr };
PyObject *Node = NewNode(P, "BinOp", Argv, 3);
Py_DECREF(Op);
Py_DECREF(CPtr);
if (Node != NULL) {
FixLoc(P, Node, Tok);
}
return Node;
}
/* 判定当前位置的 '*'/'**' 串是否为 C 风格指针后缀(作用于"类型原子"节点)。
*
* 判据(两条同时成立,对齐 Python ExprParser._AtPtrSuffix):
* 1. 节点是 Name / Attribute / Subscript(ast.AST / t.CArray[X, N] / rawptr[X] …);
* 2. '*'/'**' 串(可跨括号内换行 NL)之后紧跟"无右操作数"位置:
* ']'/','/')'/':'/'='(任意上下文)—— 乘法/幂在这些位置本就缺右操作数
* (即原语法错误),故不会改变任何合法表达式(x[a * b] / f(x * y) /
* x[1 * 2] / a ** b)的语义;注解模式额外允许 NEWLINE/NL/ENDMARKER/';'。
*/
static int AtPtrSuffixEnd(Parser *P, PyObject *Base) {
static const char *EndOps[] = {"]", ",", ")", ":", "="};
if (!IsNameOrAttrOrSub(Base)) {
return 0;
}
int J = 0;
int Stars = 0;
for (;;) {
Token *T = PeekTok(P, J);
if (TokIsOp(T, "*")) {
Stars += 1;
J++;
} else if (TokIsOp(T, "**")) {
Stars += 2;
J++;
} else if (T->Kind == TK_NL) {
J++;
} else {
break;
}
}
if (Stars == 0) {
return 0;
}
Token *Nxt = PeekTok(P, J);
for (size_t I = 0; I < sizeof(EndOps) / sizeof(EndOps[0]); I++) {
if (TokIsOp(Nxt, EndOps[I])) {
return 1;
}
}
if (P->InTypeAnnotation) {
if (Nxt->Kind == TK_NEWLINE || Nxt->Kind == TK_NL
|| Nxt->Kind == TK_ENDMARKER || TokIsOp(Nxt, ";")) {
return 1;
}
}
return 0;
}
/* 消费连续的 '*'/'**' 指针后缀('**' 计 2 层),展开为 base | t.CPtr 链。 */
static PyObject *ConsumePtrSuffix(Parser *P, PyObject *Node) {
for (;;) {
Token *T = PeekTok(P, 0);
int Layers;
if (TokIsOp(T, "*")) {
Layers = 1;
} else if (TokIsOp(T, "**")) {
Layers = 2;
} else {
break;
}
NextTok(P);
P->PtrSuffixTail = T; /* 记录尾 token,供 ParseParen 识别 (T*)x */
for (int I = 0; I < Layers; I++) {
PyObject *Wrapped = MakePtrType(P, Node, T);
Py_DECREF(Node);
if (Wrapped == NULL) {
return NULL;
}
Node = Wrapped;
}
}
return Node;
}
static PyObject *ParseCallTail(Parser *P, PyObject *Func, Token *Start);
/* 判定当前位置的 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**
* (对齐 Python ExprParser._AtPtrSuffixCall):
* 1. 节点是类型原子 Name/Attribute/Subscript;
* 2. 节点与 '*'/'**' 串**紧贴**(源码字节相邻,无空白/换行);
* 3. '*'/'**' 串(串内 token 逐个紧贴)之后**紧贴**一个 '('。
* 即整段写作**无空白**的 `T*(x)`(C 风格强转写法)。'(' 不入 EndOps 表
* (`a * (b)` 是合法乘法,'(' 可作乘法右操作数起点);故叠加"前驱是类型原子 +
* 两侧紧贴",`a * (b + c)` / `f(x) * (y)` / `n * (m - 1)` 等仍按乘法解析。
*/
static int AtPtrSuffixCall(Parser *P, PyObject *Base) {
if (!IsNameOrAttrOrSub(Base)) {
return 0;
}
Token *First = PeekTok(P, 0);
Token *Prev = PrevTok(P);
if (Prev == NULL || Prev->Kind == TK_NL || Prev->Kind == TK_NEWLINE
|| Prev->End != First->Start) {
return 0; /* base 与 '*'/'**' 之间有空白/换行 */
}
int J = 0;
int Stars = 0;
Token *RunPrev = NULL;
Token *Last = NULL;
for (;;) {
Token *T = PeekTok(P, J);
if (TokIsOp(T, "*") || TokIsOp(T, "**")) {
if (RunPrev != NULL && T->Start != RunPrev->End) {
return 0; /* 星串内部出现空白:非紧邻 */
}
Stars++;
RunPrev = T;
Last = T;
J++;
} else {
break;
}
}
if (Stars == 0 || Last == NULL) {
return 0;
}
Token *Nxt = PeekTok(P, J);
return TokIsOp(Nxt, "(") && Nxt->Start == Last->End;
}
/* 消费已认定的 `T*(...)` 强转:func = base 展开的指针类型,随后解析调用实参。
* 接管 Base 引用(与 ConsumePtrSuffix 一致),返回 Call 的新引用。 */
static PyObject *ConsumePtrSuffixCall(Parser *P, PyObject *Base, Token *Start) {
PyObject *Func = ConsumePtrSuffix(P, Base);
if (Func == NULL) {
return NULL;
}
PyObject *Call = ParseCallTail(P, Func, Start);
Py_DECREF(Func);
return Call;
}
/* 类型注解入口:置注解模式标志(对齐 Python ExprParser._ParseTypeAnnotation)。 */
static PyObject *ParseTypeAnnotation(Parser *P) {
int Saved = P->InTypeAnnotation;
P->InTypeAnnotation = 1;
PyObject *Node = ParseExpr(P);
P->InTypeAnnotation = Saved;
return Node;
}
/* term:factor (*|/|//|%|@ factor)* */ /* term:factor (*|/|//|%|@ factor)* */
static PyObject *ParseTerm(Parser *P) { static PyObject *ParseTerm(Parser *P) {
Token *Start = PeekTok(P, 0); Token *Start = PeekTok(P, 0);
@@ -2522,6 +2730,14 @@ static PyObject *ParseTerm(Parser *P) {
Token *T = PeekTok(P, 0); Token *T = PeekTok(P, 0);
const char *OpName = NULL; const char *OpName = NULL;
if (T->Kind == TK_OP) { if (T->Kind == TK_OP) {
if (TokIsOp(T, "*") && AtPtrSuffixEnd(P, Left)) {
/* C 风格指针后缀('*' 一层) */
Left = ConsumePtrSuffix(P, Left);
if (Left == NULL) {
return NULL;
}
continue;
}
if (TokIsOp(T, "*")) OpName = "Mult"; if (TokIsOp(T, "*")) OpName = "Mult";
else if (TokIsOp(T, "/")) OpName = "Div"; else if (TokIsOp(T, "/")) OpName = "Div";
else if (TokIsOp(T, "//")) OpName = "FloorDiv"; else if (TokIsOp(T, "//")) OpName = "FloorDiv";
@@ -2557,17 +2773,23 @@ static PyObject *ParseTerm(Parser *P) {
return Left; return Left;
} }
/* factor:+|-|~ factor | power */ /* factor:+|-|~|*|& factor | power */
static PyObject *ParseFactor(Parser *P) { static PyObject *ParseFactor(Parser *P) {
Token *T = PeekTok(P, 0); Token *T = PeekTok(P, 0);
if (T->Kind == TK_OP && (TokIsOp(T, "+") || TokIsOp(T, "-") || TokIsOp(T, "~"))) { /* 一元前缀与 Python ExprParser._ParseFactor 逐字对齐:'+' USub/'-'/'~' 之外还含
* Viper 新规范的 '*'(Deref) 与 '&'(AddrOf)(`*p = v` / `&x`)。缺这两项时
* C 解析器对含取址/解引用的文件整体失败并回退纯 Python(C 缺口清单根因)。 */
if (T->Kind == TK_OP && (TokIsOp(T, "+") || TokIsOp(T, "-") || TokIsOp(T, "~")
|| TokIsOp(T, "*") || TokIsOp(T, "&"))) {
Token *Start = NextTok(P); Token *Start = NextTok(P);
PyObject *Operand = ParseFactor(P); PyObject *Operand = ParseFactor(P);
if (Operand == NULL) { if (Operand == NULL) {
return NULL; return NULL;
} }
const char *OpName = TokIsOp(Start, "+") ? "UAdd" const char *OpName = TokIsOp(Start, "+") ? "UAdd"
: (TokIsOp(Start, "-") ? "USub" : "Invert"); : (TokIsOp(Start, "-") ? "USub"
: (TokIsOp(Start, "~") ? "Invert"
: (TokIsOp(Start, "*") ? "Deref" : "AddrOf")));
PyObject *Op = MakeSingleton(P, OpName); PyObject *Op = MakeSingleton(P, OpName);
if (Op == NULL) { if (Op == NULL) {
Py_DECREF(Operand); Py_DECREF(Operand);
@@ -2595,6 +2817,10 @@ static PyObject *ParsePower(Parser *P) {
if (!AtOp(P, "**")) { if (!AtOp(P, "**")) {
return Base; return Base;
} }
if (AtPtrSuffixEnd(P, Base)) {
/* C 风格指针后缀('**' 计 2 层) */
return ConsumePtrSuffix(P, Base);
}
NextTok(P); NextTok(P);
PyObject *Exp = ParseFactor(P); PyObject *Exp = ParseFactor(P);
if (Exp == NULL) { if (Exp == NULL) {
@@ -2946,7 +3172,15 @@ static PyObject *ParsePrimary(Parser *P) {
for (;;) { for (;;) {
SkipNL(P); SkipNL(P);
Token *T = PeekTok(P, 0); Token *T = PeekTok(P, 0);
if (T->Kind == TK_OP && TokIsOp(T, ".")) { if (T->Kind == TK_OP && (TokIsOp(T, "*") || TokIsOp(T, "**"))
&& AtPtrSuffixCall(P, Node)) {
/* `T*(x)` / `T**(x)` 指针后缀强转:解析为 cast 调用(与 (T | t.CPtr)(x)
* 同形)。在 trailer 循环里消费,故其后的 .attr / [i] / (…) 继续挂接。 */
Node = ConsumePtrSuffixCall(P, Node, Start);
if (Node == NULL) {
return NULL;
}
} else if (T->Kind == TK_OP && TokIsOp(T, ".")) {
NextTok(P); NextTok(P);
Token *AttrTok = ExpectName(P, NULL); Token *AttrTok = ExpectName(P, NULL);
if (AttrTok == NULL) { if (AttrTok == NULL) {
@@ -3032,6 +3266,26 @@ static PyObject *ParseAtom(Parser *P) {
return NULL; return NULL;
} }
/* 无括号强转 `(T*)x` 的操作数起点判据(对齐 Python ExprParser._AtCastOperand):
* NAME/NUMBER/STRING,或 OP 中的 '['/'{'/'*'/'&'/'-'/'+'/'~'。'(' 不在内
* (`(T*)(x)` 已由调用 trailer 处理);NL 不跳过(换行后是新语句,不是操作数)。 */
static int AtCastOperand(Parser *P) {
Token *T = PeekTok(P, 0);
if (T->Kind == TK_NAME || T->Kind == TK_NUMBER || T->Kind == TK_STRING) {
return 1;
}
if (T->Kind != TK_OP) {
return 0;
}
static const char *Ops[] = {"[", "{", "*", "&", "-", "+", "~"};
for (size_t I = 0; I < sizeof(Ops) / sizeof(Ops[0]); I++) {
if (TokIsOp(T, Ops[I])) {
return 1;
}
}
return 0;
}
/* 括号:空元组 / yield / walrus / 生成器表达式 / 元组 / 普通表达式 */ /* 括号:空元组 / yield / walrus / 生成器表达式 / 元组 / 普通表达式 */
static PyObject *ParseParen(Parser *P) { static PyObject *ParseParen(Parser *P) {
Token *Start = NextTok(P); /* '(' */ Token *Start = NextTok(P); /* '(' */
@@ -3101,6 +3355,10 @@ static PyObject *ParseParen(Parser *P) {
if (First == NULL) { if (First == NULL) {
return NULL; return NULL;
} }
/* 无括号强转 `(T*)x`:内层表达式以指针后缀结束(_Prev() 即该后缀尾 token),
* ')' 紧随其后 → 与 `(T*)(x)` / `T*(x)` 同形,产物为 Call(func = T | t.CPtr)。 */
int IsPtrCast = (P->PtrSuffixTail != NULL && AtOp(P, ")")
&& PrevTok(P) == P->PtrSuffixTail);
SkipNL(P); SkipNL(P);
if (AtName(P, "for")) { if (AtName(P, "for")) {
PyObject *Generators = ParseComprehensionGenerators(P); PyObject *Generators = ParseComprehensionGenerators(P);
@@ -3170,6 +3428,33 @@ static PyObject *ParseParen(Parser *P) {
Py_DECREF(First); Py_DECREF(First);
return NULL; return NULL;
} }
if (IsPtrCast && AtCastOperand(P)) {
PyObject *Operand = ParseFactor(P);
if (Operand == NULL) {
Py_DECREF(First);
return NULL;
}
PyObject *Args = NewList();
PyObject *Kw = NewList();
if (Args == NULL || Kw == NULL) {
Py_XDECREF(Args);
Py_XDECREF(Kw);
Py_DECREF(Operand);
Py_DECREF(First);
return NULL;
}
PyList_Append(Args, Operand);
Py_DECREF(Operand);
PyObject *Argv[3] = { First, Args, Kw };
PyObject *Call = NewNode(P, "Call", Argv, 3);
Py_DECREF(Args);
Py_DECREF(Kw);
Py_DECREF(First);
if (Call != NULL) {
FixLoc(P, Call, Start);
}
return Call;
}
return First; return First;
} }
@@ -4012,7 +4297,7 @@ static PyObject *ParseArg(Parser *P, int AllowAnnotation) {
PyObject *Annotation = NULL; PyObject *Annotation = NULL;
if (AllowAnnotation && AtOp(P, ":")) { if (AllowAnnotation && AtOp(P, ":")) {
NextTok(P); NextTok(P);
Annotation = ParseExpr(P); Annotation = ParseTypeAnnotation(P);
if (Annotation == NULL) { if (Annotation == NULL) {
return NULL; return NULL;
} }
@@ -4037,6 +4322,7 @@ static PyObject *ParseArg(Parser *P, int AllowAnnotation) {
static PyObject *ParseStatement(Parser *P); static PyObject *ParseStatement(Parser *P);
static PyObject *ParseSimpleStmt(Parser *P); static PyObject *ParseSimpleStmt(Parser *P);
static PyObject *ParseSuite(Parser *P); static PyObject *ParseSuite(Parser *P);
static PyObject *ParseSuiteOwned(Parser *P, int OwnerIsClass);
static PyObject *ParseIf(Parser *P); static PyObject *ParseIf(Parser *P);
static PyObject *ParseWhile(Parser *P); static PyObject *ParseWhile(Parser *P);
static PyObject *ParseFor(Parser *P, int IsAsync); static PyObject *ParseFor(Parser *P, int IsAsync);
@@ -4056,6 +4342,7 @@ static PyObject *ParseGlobal(Parser *P, const char *NodeName);
static PyObject *ParseDelete(Parser *P); static PyObject *ParseDelete(Parser *P);
static PyObject *ParseAssert(Parser *P); static PyObject *ParseAssert(Parser *P);
static PyObject *ParseDecorated(Parser *P); static PyObject *ParseDecorated(Parser *P);
static PyObject *ParseVirtualModified(Parser *P, PyObject *Decorators);
static PyObject *ParseTypeParams(Parser *P); static PyObject *ParseTypeParams(Parser *P);
static PyObject *ParseExceptHandler(Parser *P); static PyObject *ParseExceptHandler(Parser *P);
static PyObject *ParseTargetList(Parser *P); static PyObject *ParseTargetList(Parser *P);
@@ -4116,6 +4403,9 @@ static PyObject *ParseStatement(Parser *P) {
if (TokIsName(T, "define") && PeekTok(P, 1)->Kind == TK_NAME) { if (TokIsName(T, "define") && PeekTok(P, 1)->Kind == TK_NAME) {
return ParseFunctionDef(P, 0, NULL); return ParseFunctionDef(P, 0, NULL);
} }
if (TokIsName(T, "virtual") || TokIsName(T, "novirtual")) {
return ParseVirtualModified(P, NULL);
}
if ((TokIsName(T, "fn") || TokIsName(T, "fun") || TokIsName(T, "func") if ((TokIsName(T, "fn") || TokIsName(T, "fun") || TokIsName(T, "func")
|| TokIsName(T, "function")) || TokIsName(T, "function"))
&& PeekTok(P, 1)->Kind == TK_NAME) { && PeekTok(P, 1)->Kind == TK_NAME) {
@@ -4361,7 +4651,7 @@ static PyObject *TryParseAnnAssign(Parser *P) {
if (T0->Kind == TK_NAME && T1->Kind == TK_OP && TokIsOp(T1, ":")) { if (T0->Kind == TK_NAME && T1->Kind == TK_OP && TokIsOp(T1, ":")) {
Token *NameTok = NextTok(P); Token *NameTok = NextTok(P);
NextTok(P); /* ':' */ NextTok(P); /* ':' */
PyObject *Annotation = ParseExpr(P); PyObject *Annotation = ParseTypeAnnotation(P);
if (Annotation == NULL) { if (Annotation == NULL) {
return NULL; return NULL;
} }
@@ -4406,7 +4696,7 @@ static PyObject *TryParseAnnAssign(Parser *P) {
} }
if (AtOp(P, ":") && IsNameOrAttrOrSub(Expr)) { if (AtOp(P, ":") && IsNameOrAttrOrSub(Expr)) {
NextTok(P); NextTok(P);
PyObject *Annotation = ParseExpr(P); PyObject *Annotation = ParseTypeAnnotation(P);
if (Annotation == NULL) { if (Annotation == NULL) {
Py_DECREF(Expr); Py_DECREF(Expr);
return NULL; return NULL;
@@ -4848,11 +5138,15 @@ static PyObject *ParseDecorated(Parser *P) {
SkipNEWLINE(P); SkipNEWLINE(P);
} }
Token *T = PeekTok(P, 0); Token *T = PeekTok(P, 0);
/* extern / export 必须走 ParseExternDef(与 Python _ParseDecorated 的
* _ParseExternDef/_ParseExportDef 分派一致)。此前误走 ParseFunctionDef:
* 它无条件把当前 token 当 `def` 吃掉、再 ExpectName 拿到 `def`,
* 于是 `@deco` + `export def f(...)` 在 `(` 处报 expected operator NAME('f')。 */
if (T->Kind == TK_NAME && TokIsName(T, "extern")) { if (T->Kind == TK_NAME && TokIsName(T, "extern")) {
return ParseFunctionDef(P, 0, DecoratorList); return ParseExternDef(P, 0, DecoratorList);
} }
if (T->Kind == TK_NAME && TokIsName(T, "export")) { if (T->Kind == TK_NAME && TokIsName(T, "export")) {
return ParseFunctionDef(P, 0, DecoratorList); return ParseExternDef(P, 1, DecoratorList);
} }
if (T->Kind == TK_NAME && TokIsName(T, "define")) { if (T->Kind == TK_NAME && TokIsName(T, "define")) {
return ParseFunctionDef(P, 0, DecoratorList); return ParseFunctionDef(P, 0, DecoratorList);
@@ -4867,11 +5161,67 @@ static PyObject *ParseDecorated(Parser *P) {
if (T->Kind == TK_NAME && TokIsName(T, "class")) { if (T->Kind == TK_NAME && TokIsName(T, "class")) {
return ParseClassDef(P, DecoratorList); return ParseClassDef(P, DecoratorList);
} }
if (T->Kind == TK_NAME && (TokIsName(T, "virtual") || TokIsName(T, "novirtual"))) {
return ParseVirtualModified(P, DecoratorList);
}
SyntaxErrorAt(P, T, "decorator must be followed by def/class"); SyntaxErrorAt(P, T, "decorator must be followed by def/class");
Py_DECREF(DecoratorList); Py_DECREF(DecoratorList);
return NULL; return NULL;
} }
/* virtual / novirtual 修饰符(对齐 vast Parser._ParseVirtualModified):
* 语句起始的 `virtual`/`novirtual` 后随 class/def(def 别名 fn/fun/func/function 同样接受)
* 时,合成装饰器 t.CVTable / t.NoVTable 追加到既有装饰器之后,再交由
* ParseClassDef / ParseFunctionDef 解析。非法形态(后随非 class/def;virtual def
* 不在类体内)一律报错,绝不静默丢弃修饰符。 */
static PyObject *ParseVirtualModified(Parser *P, PyObject *Decorators) {
Token *Mod = PeekTok(P, 0);
Token *Nxt = PeekTok(P, 1);
const char *Marker = TokIsName(Mod, "virtual") ? "CVTable" : "NoVTable";
int IsClass = 0;
int IsDef = 0;
if (Nxt->Kind == TK_NAME) {
if (TokIsName(Nxt, "class")) {
IsClass = 1;
} else if (TokIsName(Nxt, "def") || TokIsName(Nxt, "fn")
|| TokIsName(Nxt, "fun") || TokIsName(Nxt, "func")
|| TokIsName(Nxt, "function")) {
IsDef = 1;
}
}
if (!IsClass && !IsDef) {
SyntaxErrorAt(P, Mod, "virtual/novirtual must precede class/def");
return NULL;
}
if (IsDef && !P->InClassBody) {
SyntaxErrorAt(P, Mod, "virtual def must appear inside a class body");
return NULL;
}
NextTok(P); /* 消费修饰符 */
PyObject *Deco = MakeTAttrNode(P, Marker);
if (Deco == NULL) {
return NULL;
}
FixLoc(P, Deco, Mod);
PyObject *DecoList = NewList();
if (DecoList == NULL) {
Py_DECREF(Deco);
return NULL;
}
if (Decorators != NULL) {
Py_ssize_t N = PyList_GET_SIZE(Decorators);
for (Py_ssize_t I = 0; I < N; I++) {
PyList_Append(DecoList, PyList_GET_ITEM(Decorators, I));
}
}
PyList_Append(DecoList, Deco);
Py_DECREF(Deco);
PyObject *Node = IsClass ? ParseClassDef(P, DecoList)
: ParseFunctionDef(P, 0, DecoList);
Py_DECREF(DecoList);
return Node;
}
/* def / fn / fun / func / function(含 async def) */ /* def / fn / fun / func / function(含 async def) */
static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators) { static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators) {
Token *Start = NextTok(P); Token *Start = NextTok(P);
@@ -4903,7 +5253,7 @@ static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators)
PyObject *Returns = NULL; PyObject *Returns = NULL;
if (AtOp(P, "->")) { if (AtOp(P, "->")) {
NextTok(P); NextTok(P);
Returns = ParseExpr(P); Returns = ParseTypeAnnotation(P);
if (Returns == NULL) { if (Returns == NULL) {
Py_DECREF(Args); Py_DECREF(Args);
Py_DECREF(TypeParams); Py_DECREF(TypeParams);
@@ -5054,7 +5404,7 @@ static PyObject *ParseExternDef(Parser *P, int IsExport, PyObject *Decorators) {
PyObject *Returns = NULL; PyObject *Returns = NULL;
if (AtOp(P, "->")) { if (AtOp(P, "->")) {
NextTok(P); NextTok(P);
Returns = ParseExpr(P); Returns = ParseTypeAnnotation(P);
if (Returns == NULL) { if (Returns == NULL) {
Py_DECREF(Args); Py_DECREF(Args);
Py_DECREF(TypeParams); Py_DECREF(TypeParams);
@@ -5335,7 +5685,7 @@ static PyObject *ParseClassDef(Parser *P, PyObject *Decorators) {
Py_DECREF(TypeParams); Py_DECREF(TypeParams);
return NULL; return NULL;
} }
PyObject *Body = ParseSuite(P); PyObject *Body = ParseSuiteOwned(P, 1);
if (Body == NULL) { if (Body == NULL) {
Py_DECREF(Bases); Py_DECREF(Bases);
Py_DECREF(Keywords); Py_DECREF(Keywords);
@@ -5381,7 +5731,7 @@ static PyObject *ParseClassDef(Parser *P, PyObject *Decorators) {
} }
/* 语句块(套件) */ /* 语句块(套件) */
static PyObject *ParseSuite(Parser *P) { static PyObject *ParseSuiteImpl(Parser *P) {
if (PeekTok(P, 0)->Kind == TK_NEWLINE) { if (PeekTok(P, 0)->Kind == TK_NEWLINE) {
NextTok(P); NextTok(P);
while (PeekTok(P, 0)->Kind == TK_NL) { while (PeekTok(P, 0)->Kind == TK_NL) {
@@ -5458,9 +5808,25 @@ static PyObject *ParseSuite(Parser *P) {
Py_DECREF(Body); Py_DECREF(Body);
return NULL; return NULL;
} }
/* 吞掉单行套件后的空行/注释行 NL:与纯 Python _ParseSuite 的单行分支一致,
* 使外层语句的 end_lineno 落在最后一个被吞掉的 NL 上(而非本行 NEWLINE)。 */
SkipNL(P);
return Body; return Body;
} }
/* 套件入口:OwnerIsClass 表示该套件的直接拥有者是否 ClassDef(供 virtual def 判据)。 */
static PyObject *ParseSuiteOwned(Parser *P, int OwnerIsClass) {
int Prev = P->InClassBody;
P->InClassBody = OwnerIsClass;
PyObject *Body = ParseSuiteImpl(P);
P->InClassBody = Prev;
return Body;
}
static PyObject *ParseSuite(Parser *P) {
return ParseSuiteOwned(P, 0);
}
/* if / elif / else */ /* if / elif / else */
static PyObject *ParseIf(Parser *P) { static PyObject *ParseIf(Parser *P) {
Token *Start = NextTok(P); /* if */ Token *Start = NextTok(P); /* if */
@@ -6492,13 +6858,40 @@ static PyObject *VastC_Parse(PyObject *Self, PyObject *Args, PyObject *Kwargs) {
P.Error = 0; P.Error = 0;
P.ErrMsg = NULL; P.ErrMsg = NULL;
P.ErrLine = 0; P.ErrLine = 0;
P.ErrTok = NULL;
P.FStringExprEnd = 0; P.FStringExprEnd = 0;
P.InTypeAnnotation = 0;
P.InClassBody = 0;
P.PtrSuffixTail = NULL;
PyObject *Tree = ParseModule(&P); PyObject *Tree = ParseModule(&P);
PyMem_Free(Tokens); PyMem_Free(Tokens);
PyMem_Free(LineStarts); PyMem_Free(LineStarts);
if (Tree == NULL) { if (Tree == NULL) {
if (P.Error) { if (P.Error) {
PyErr_SetString(PyExc_SyntaxError, P.ErrMsg ? P.ErrMsg : "vast_c: syntax error"); /* 报错必须自足可定位:带上 类型名 + token 原文 + 行号,措辞与
* Python ExprParser 的 `unexpected token OP('*') at line N` 一致,
* 这样 `[vast] C 解析器失败,回退纯 Python:<文件>: …` 一行即可定位。 */
char TokBuf[64];
const char *TokName = "ENDMARKER";
TokBuf[0] = '\0';
if (P.ErrTok != NULL) {
TokName = TokKindName(P.ErrTok->Kind);
Py_ssize_t TL = (Py_ssize_t)(P.ErrTok->End - P.ErrTok->Start);
if (TL < 0) {
TL = 0;
}
if (TL > (Py_ssize_t)sizeof(TokBuf) - 1) {
TL = (Py_ssize_t)sizeof(TokBuf) - 1;
}
for (Py_ssize_t I = 0; I < TL; I++) {
char C = P.ErrTok->Start[I];
TokBuf[I] = (C == '\n' || C == '\r') ? ' ' : C;
}
TokBuf[TL] = '\0';
}
PyErr_Format(PyExc_SyntaxError, "%s %s('%s') at line %d",
P.ErrMsg ? P.ErrMsg : "vast_c: syntax error",
TokName, TokBuf, P.ErrLine);
} }
return NULL; return NULL;
} }