Commit Graph
204 Commits
Author SHA1 Message Date
TermiNexus 9d8aedec4a fix(tpc): 函数声明创建收口到幂等入口 _CreateOrReuseFuncDecl;清理 DDEBUG 探针
类别:a 编译器缺陷(根因修复)+ 调试探针移除。

现场:翻译 CallGenFd.vp 时抛 llvmlite.ir._utils.DuplicatedNameError:
'5d84ea579d83ef73.CallGenFd._translate_fakeduck_call'(llvmlite 的
NameScope 按名字去重,GlobalValue.deduplicate_name=False ⇒ 同名 global
建第二次必抛,且报文只给名字、不给两侧签名)。

根因:BaseGen._get_or_declare_function 建完 ir.Function 后**只加入
module.globals**(llvmlite 自动)而**未登记 Gen.functions**;而
HandlesFunctions._EmitFunctionLlvm 的 need_new 判据只看 Gen.functions
(`MangledName not in Gen.functions`)⇒ 同一名字再建一次 → DuplicatedNameError;
该处 except 又把它吞掉(非 strict 模式只 LogWarning)→ 函数始终没进
Gen.functions → 后续其它创建点(FuncGen._get_or_declare_func /
get_or_declare_c_func,同样只查 self.functions)再建 → 异常从别处逃逸到
Phase2Translator。

修复:
1. BaseGen 新增 _CreateOrReuseFuncDecl(name, func_type) —— 函数声明创建的
   唯一幂等入口:module.globals 已有同名函数且**函数类型结构串相同**则复用
   (并补登记 Gen.functions;FunctionType 无 __eq__,只能比 str);
   类型不同则 fail-fast 报出两侧签名(declare/define 分裂的真因),
   绝不静默复用错签名(调用点会按错签名传参)。
2. _get_or_declare_function 改走该入口(顺带补登记 Gen.functions);
   FuncGen._get_or_declare_func / get_or_declare_c_func 改走该入口;
   HandlesFunctions 定义路径改走该入口,且该处 except 对 FailFastError
   直接 re-raise(不再吞)。
3. 清理遗留调试探针(含 sys.exit(1)):BaseGen 三处 [DDEBUG]
   (_get_or_declare_function 双点 / _mangle_func_name 前导点与结果双点)、
   HandlesExprAsm 两处 [DDEBUG_ASM]、HandlesExprCall 一处
   [DDEBUG] _HandleClosureCallLlvm。
2026-09-29 13:00:11 +08:00
TermiNexus 3bc3c48dee fix(tpc): 注解解析补齐「有类定义 ⇒ 是类」——模块别名占裸名导致类引用退化成 i8**
类别:a 编译器缺陷(根因修复)。

现场:TransPyV Generator.vp:200 `self.ExprGenFstr = SubExprGenFstr` 报
「指针隐式转换已禁止(层数不同):%\"a7b68c79f39cb4d3.ExprGenFstr\"* → i8**」,
成员槽来源明细显示:class_members[Generator] 下标 11 声明槽类型正确
(%\"a7b68c79f39cb4d3.ExprGenFstr\"*),但布局 struct
'47382dc8277c9d28.Generator' 的 elements[11]=i8**(set_body 只能设一次 →
错布局被永久冻结)。

已核对 temp/47382dc8277c9d28.stub.ll:declare 侧该槽逐字正确
(%"a7b68c79f39cb4d3.ExprGenFstr"*),Generator.vp 46 个字段全写作
`rawptr[X.X]`;.vpi 里注解文本也是 `rawptr[ExprGenFstr.ExprGenFstr]`。

真根因:`import lib.core.Gen.Expr.ExprGenFstr as ExprGenFstr` 的**模块别名**与
模块内 `class ExprGenFstr` 同名,平坦符号表(无前缀)一个名字只留一个条目 ⇒
TypeAnnotationResolver._from_node_attribute / _from_node_name 命中别名条目时
Copy 出「无基类型」的空 CTypeInfo ⇒ rawptr 下标分支按
`PtrCount = slice + rawptr(1)` 得 void 指针链(i8*/i8**),类身份尽失;
该值经成员提取(SymbolExtractor._collect_members → CTypeInfo.FromNode)
写进跨模块共享符号表,再由 FuncGen.setup_from_symbol_table 建出 struct body
→ 槽被冻结成 i8**。哪一字段中招取决于别名/类条目谁最后写入平坦表,
故三次构建分别命中 TypeGenCDef / ExprGenFstr(时变、非固定字段)。

修复:新增 TypeAnnotationResolver._RecoverTypeFromNamespaces —— 平坦条目
**不是类型**(模块别名/函数/变量/define/枚举成员占位)时,按 _namespaces
找回同名 typedef / 结构体(struct·renum·exception)/ 枚举条目,与
LLVMTypeMapper._resolve_name 既有「同名冲突找回类型」分支逐条同构(同一条
规则源)。挂接三处:_from_node_attribute 的条目分支与无条目分支、
_from_node_name 的条目分支。

口径细节:结构体表示与本文件既有 promotion 逐字一致
(BaseType=CStruct(name) + IsStruct + Name,**不加** PtrCount)——
rawptr[X] 的层数由 rawptr 分支自己 +1,此处再加会得 X**。
2026-09-29 12:44:03 +08:00
TermiNexus fd212b3ff8 fix(tpc): 联合强转 (T|t.CPtr)(x) 及其属性访问失败改为 fail-fast(附判定依据)
类别:b 机制漏豁免(把静默 return None 改成响亮失败并带证据)。

现场:TransPyV lib/core/Gen/Expr/ExprGenBase.vp:360 的
v = (ast.Expr*(node)).value 报“赋值表达式 'value'(Attribute 形态)编译期
求值为空”,而同一函数前三条 (ast.Assign/AnnAssign/AugAssign*(node)).value
同款写法正常 ⇒ 无法从报错看出是哪一步丢了类型。

两处静默返回改为 fail-fast(均只对该强转形态生效,其余路径行为不变):
1. HandlesExprCall._HandleTypeUnionCastLlvm:目标类型串解析失败(原为
   _vlog().warning + return None)。新增 _DescribeUnionCastFailure 取证:
   MergeTypes 返回值/ToString()/异常、_ExtractTypeStrFromUnionBinOp 结果,
   以及联合中每个操作数在符号表与类登记表(Gen.structs / _struct_sha1_map /
   class_sha1_map)的命中情况 —— 直接指认“基座类名没被当成类”还是
   “CPtr 标记侧解析失败”。
2. HandlesExprAttr._handle_attr_call_result:(T|t.CPtr)(x).attr 的 5 个
   return None(基座求值为空 / 非指针 / find_struct_by_pointee 未命中 /
   结构体不透明 / 成员偏移取不到)改为 _FailFastAttrCallBase,报文含联合
   基类名与三家登记表命中、被指类型 elements、class_members 项数与目标成员偏移。

原因:(T|t.CPtr)(x) 只可能来自指针强转(parser 把 (T*)x / (T*)(x) / T*(x)
统一产成该 Call),必须有值产出;静默丢弃会让整条表达式被判为空,把
“类名解析失败 / 结构体不透明 / 成员偏移取不到”三种不同根因混成同一条报文。
2026-09-29 12:23:54 +08:00
TermiNexus 8263f6ec47 fix(tpc): 形参注解解析落到 i32 时 fail-fast(下标/联合形态不可能是 i32)
a 编译器缺陷(无兜底铁律)。根因链:stub 与 .vpi 两侧都写着
`type_args: list[str] | t.CPtr`(等价 `list[str]*`),**都对**;错在消费侧:
`LLVMTypeMapper._resolve_subscript` 对**单参泛型下标**(`list[str]`,非 `list[T, N]`
定长数组)没有分支,落到末尾 `回退到 i32` 的兜底 —— 该串经
SymbolTypeResolver → SymbolTable._InsertFuncSymbol 原样存进 `FuncPtrParams`,
再由 BuildLLVMFuncTypeFromSig 物化成**形参 i32**,于是调用点报
「实参 %"650c1c3366d09e39.list[str]"* 不能隐式转为整数参数 i32」,把真因
(注解没解析出来)掩盖成"调用点类型不匹配"。

改法:`get_func_param_type_str` 末尾加守卫 —— 注解是 Subscript / 联合(BinOp|)而
解析结果恰为 `'i32'` 时 fail-fast,并带上注解原文(由 SymbolTypeResolver 再补函数名/
形参序号与名字)。理由:下标形态的合法结果是容器/泛型/定长数组(指针、数组或结构体
指针),联合形态是 `T | t.CPtr` 的指针叠加 —— 都不会是 i32;只有"解析失败"才会是。

现场:CallGenMixin.vp:149 → `_specialize_generic_class(trans, gen_class_name, gen_type_args)`
      callee=2292045c9694100e.ClassGenGeneric._specialize_generic_class
      逐项 [3] arg=%"650c1c3366d09e39.list[str]"* param=i32
2026-09-29 12:09:12 +08:00
TermiNexus 7167703ca7 fix(tpc): 降级判据改用「注解形态 + 解析结果形状」,放在字段类型唯一漏斗
上一版拦截失效的原因(实测):`class_member_annotations` 全仓**只被声明与读取、从未被
写入**(Step 2 一直是空转),故依赖它的 `_HasDegradedMemberTypes` 恒为 False。改法:

1) SymbolUtils 新增两个**形状判据**(纯判定,不做类型解析):
   - IsI8DoublePtrType(T):是否 i8 系两层指针(`i8**`)= `T | t.CPtr` 在 T 类型丢失后
     退化成的形状;
   - IsClassRefPtrAnnotation(Ann):注解是否「指针包装的类引用」(rawptr[Mod.Class] /
     Mod.Class | t.CPtr;内层为非 t/c 模块的 Attribute 或首字母大写裸名)。
2) HandlesBase.CollectAnnAssignMember(字段类型的**唯一漏斗**,覆盖本地类 /
   跨模块 .vpi / 嵌套类三条写入链)新增硬判据:注解是类引用指针 **且** 解析结果正是
   `i8**` ⇒ 必属类型信息丢失,当场 fail-fast,并打出 CTypeInfo 全字段(BaseType/
   PtrCount/IsStruct/IsTypedef/IsEnum/Name)与内层名的符号表条目标志。此前的
   `_CheckPtrAnnotationKeepsClass` 只看"void 基座且无类身份",而现场这条链**带 Name/
   IsTypedef**(Attribute 分支的 typedef 出口 → BaseType=CVoid 但 Name=类名),正好漏过。
3) StructGen 删除依赖那张空表的 `_HasDegradedMemberTypes` / `_IsI8DoublePtr` /
   `_IsClassRefPtrAnnotation` 及 set_body 前的拦截(改由漏斗处兜住,不留死代码)。

现场:Generator.vp:200 `self.ExprGenFstr = SubExprGenFstr`
  → 布局槽 elements[11]=i8** 而声明 %"a7b68c79f39cb4d3.ExprGenFstr"*
2026-09-29 12:04:20 +08:00
TermiNexus 66de269ebf fix(tpc): 撤回失效的"事后修复",改为 set_body 前拦截降级布局(不冻结)
a 编译器缺陷。实测证据(07ea8e2f 的探针回执):对本版本 llvmlite,
`st._elements = None` 之后 `st.set_body(*elems)` 仍以
`RuntimeError: <name> is already defined` 拒绝 —— 说明 **`_elements` 不是 set_body 的
判定存储**,即"清 _elements 以便重载/重设 body"这一全仓惯用手段在本版本是**空操作**
(结构体 body 一经设定即不可改)。因此:

  - 撤回 7fb0e498 加的 `_SyncStructBodyTypes`(使用点就地修复)及其在
    `_get_member_offset` 的调用 —— 它在本版本永不可能成功,只会把编译卡死;
  - 改为**事前拦截**:`_generate_structs` 在 `set_body` 前调用
    `_HasDegradedMemberTypes`,若本次布局里存在「注解是类引用(`Mod.Class` /
    首字母大写裸名,排除 t/c 模块)**却**解析成 `i8**`」的成员,说明该 pass 类型尚未
    就绪 ⇒ **不设 body、保持 opaque**,等类型就绪的 pass 再设。这样从根上避免
    "降级布局被永久冻结"(现场:Generator 的槽 i8** 而声明 %"…ExprGenFstr"*,
    赋值时报「层数不同」)。始终不就绪则在使用点响亮失败(opaqueness / 层数不同),
    不再静默产出错布局。
  - 新增 `_IsI8DoublePtr` / `_IsClassRefPtrAnnotation` 两个判据助手(保守:只在
    "指针包装 + 类引用内层 + i8 系两层指针"三条同时成立时判降级)。
2026-09-29 11:56:08 +08:00
TermiNexus 07ea8e2fd9 fix(tpc): 布局校验/修复排除 REnum 镜像布局 + set_body 失败带回滚与上下文
a 编译器缺陷(承接 b8ed33a2 / 7fb0e498):REnum 的 struct body 是 **max-variant
镜像布局** —— class_members 里的名字是*变体*而非字段,变体嵌套结构体也必须逐字
镜像主结构体。此前两处按"声明类型 vs 槽类型"的校验/修复会把这类布局当成错位:
  - Step 4 前置校验可能据此清 _elements 重建;
  - _SyncStructBodyTypes 可能据此改写布局(跨模块 GEP 偏移随即与定义端失配)。
新增 _IsRenumLayoutClass(主 REnum 自身 / `<REnum>_<Variant>`,判据走 SymbolTable 的
IsRenum)并在两处排除。

另外:就地修复的 `_elements = None` + `set_body` 一旦被 llvmlite 拒绝
(`<name> is already defined`,现场 fd4cd0ec583f42b9.Generator),原来会抛出这句
毫无上下文的裸 RuntimeError。现改为:捕获后**直接写回原 _elements 回滚**,再抛
FailFastError 带上类名 / struct / 原异常 / 元素数前后值,既不留中间状态也不再含糊。
2026-09-29 11:34:17 +08:00
TermiNexus dab0823f5c fix(tpc): 收紧「类身份」判据 + REnum 变体载荷解析失败不再被吞
承接 02d89b09:上一版判据"BaseType 是 CVoid ⇒ 类型信息丢失"**误伤 REnum** ——
REnum 类型条目本身 `BaseType=None`,`T | t.CPtr` 合并时 `_MergeAllComponentTypes`
末尾会把空 BaseType 默认填成 CVoid,但 IsRenum/IsStruct/Name 齐备,`_ctype_to_llvm`
仍能解析出 `%<sha1>.T*`。于是合法注解 `Pointee: rawptr[LLVMType]`(REnum 变体载荷)
被打成致命错误,异常又在此处被上层吞掉 ⇒ 变体只剩 __tag。

两处修正:
1) HandlesBase._CheckPtrAnnotationKeepsClass:判据收紧为「BaseType 是 CVoid **且
   没有任何类身份**(IsStruct / IsRenum / IsEnum / Name 全空)」才失败。
2) HandlesImports REnum 变体载荷注册:历史上 `CollectAnnAssignMember` 的异常被上层
   吞掉、`parsed` 为 None 时静默跳过 ⇒ class_members 只剩 __tag、变体循环中断
   (现场同前缀 structs 键只有 LLVMType_Int / LLVMType_Ptr,源顺序其后还有
   Func/Array/Struct/…),进而使变体构造丢载荷、match 捕获绑不上。现改为:
   - 只把 AnnAssign 当载荷声明(pass / docstring 合法跳过,如 `class Void: pass`);
   - 解析抛异常 ⇒ fail-fast 并带上原异常(拒绝留下只有 __tag 的成员表);
   - 解析返回 None ⇒ 同样 fail-fast。
2026-09-29 11:24:57 +08:00
TermiNexus 53bde9157e fix(tpc): REnum 变体嵌套结构体按需补发 + 模式捕获绑不上即 fail-fast
a 编译器缺陷:导入模块侧 stub 只声明主 REnum(`%"…LLVMType" = type {…}`),**不含**
变体嵌套结构体(`LLVMType_Ptr` 等)——它们由 `_EmitExternalClassDeclLlvm` 的 REnum
分支按主结构体布局就地生成。若本模块从未触发该 REnum 的外部声明发射:

  1) match 模式捕获(`case llvmlite.LLVMType.Ptr(pointee_cs):`)绑定静默为空 ⇒ case
     body 里引用的绑定名随后报"未定义名称"(现场 VpiReader.vp:1521 `pointee_cs`);
  2) REnum 变体构造的载荷实参静默丢弃(只写 tag)—— 静默错码。

修法:
  - 新增 BaseHandle._EnsureRenumVariantStruct:成员缺失时按需补发(先试 stub,再按
    类 sha1 找到定义模块 .vpi 触发 `_TryLoadClassMembersFromPyi` → 内部即调
    `_EmitExternalClassDeclLlvm`)。
  - HandlesMatch._HandleRenumMatchLlvm:改用该助手;**模式捕获登记不全即 fail-fast**
    (报出变体名、class_members 项数、structs 命中与同前缀键),绝不让绑定名留到
    取名时才炸。
  - HandlesMatch 非 REnum 兜底分支:MatchClass 含捕获时改为 fail-fast,并列出判据
    (IsEnumMember/EnumName/IsRenum/IsStruct/IsEnum),直接暴露"外层 REnum 判定为何
    未成立"。
  - HandlesExprCall._HandleREnumConstructLlvm:载荷实参存在而嵌套结构体缺失时补齐,
    补不上则 fail-fast(拒绝产出"只有 tag 没有载荷"的值)。
2026-09-29 11:21:19 +08:00
TermiNexus 02d89b0900 fix(tpc): 字段解析「内层是类却成通用指针」当场 fail-fast(降级源头定位)
承接 b8ed33a2 / 7fb0e498:前两者修的是**结果**(错位 body),本次收**源头** ——
class_members 在某个早期 pass 被解析出 i8**(void 基座 + 2 层),随后被冻进
struct body。CollectAnnAssignMember 是字段类型的唯一汇总点,在此加判据式 fail-fast:

判据(只拦"必属类型信息丢失"的组合):注解是指针包装(rawptr[T] / t.CPtr[T] /
裸名 CPtr[T] / `T | t.CPtr` 联合)且其**内层节点(Name/Attribute)的名字在
Gen.structs 或 Gen._struct_sha1_map 中有类定义**,而解析出的 BaseType 却是
t.CVoid ⇒ 必定是解析路径把类丢了,响亮失败并附完整判据:
  解析结果 BaseType/PtrCount/IsStruct/IsTypedef/Name
  符号表[内层名] => IsTypedef/BaseType/PtrCount/IsStruct/IsRenum/IsModuleAlias/IsEnumMember
  Gen.structs 命中 / _struct_sha1_map 命中

放行:rawptr[rawptr] 这类本就 void 基座的多层指针、模块名、泛型特化实参(内层非
Name/Attribute)—— 不误伤合法写法。
2026-09-29 00:21:39 +08:00
TermiNexus 7fb0e49854 fix(tpc): 成员偏移查询点就地修复错位 struct body(修正晚于冻结的时序)
a 编译器缺陷(承接 b8ed33a2):Step 4 的前置校验只在**当次** _generate_structs 生效,
漏掉"class_members 被 .vpi/stub 修正在后、struct body 冻结在前"的时序 —— 于是
一次只修好一部分槽(实测:第 3 槽修好、第 11 槽仍错),错误逐个前移。

证据链(成员槽来源明细 + 全盘 grep):
  class_members[Generator] 项数=47 base=0 下标=11
      声明槽类型=%"a7b68c79f39cb4d3.ExprGenFstr"*
  布局 struct name='fd4cd0ec583f42b9.Generator' 元素数=47 elements[11]=i8**
  且**没有任何** stub 文本里含 i8** ⇒ body 是从"当时已降级的 class_members"冻住的,
  后来重载只修 class_members,改不了已冻结的 body(llvmlite set_body 只能设一次)。

修法:新增 StructGen._SyncStructBodyTypes —— 在**使用点**(_get_member_offset,
即任何 GEP 之前、成员表已在上方补齐/重载之后)按 class_members 逐槽比对,
只把**存储时必被拒绝**的槽(判据同 _MemberSlotAccepts)改回声明类型,其余元素
(含通用指针 i8* 槽)原样保留;_elements 置空后 set_body 重设(命名类型对象不变,
故已发出的 getelementptr %"name"* 不受影响)。同名类 sha1 冲突路径
(_ResolvedClass != ClassName)跳过,避免用别的同名类成员表写坏布局。
2026-09-29 00:19:06 +08:00
TermiNexus b8ed33a200 fix(tpc): struct body 冻结前逐元素校验类型一致性(错位 body 不再被当"完整")
a 编译器缺陷:_generate_structs Step 4 判定既有 struct body 是否"完整"时只比
「元素个数 / 首元素是 vtable 指针 / 是否含 T 占位」—— **类型错位**的 body
(个数命中、元素名也全对,但某槽被冻结成通用多层指针)会被直接 continue;
而 llvmlite 的 set_body 只能设一次 ⇒ 错布局被永久冻结,后续 .vpi / .stub 重载
只修 class_members、改不了 body ⇒ 赋值时报「层数不同」。

现场(成员槽来源明细实锤:声明侧正确、布局侧坏):
  class_members[Generator] 项数=47 base=0 下标=3
      声明槽类型=%"d827f3045bd28467.TypeGenCDef"*
  布局 struct name='fd4cd0ec583f42b9.Generator' 元素数=47 elements[3]=i8**
  → self.TypeGenCDef = SubTypeGenCDef
    指针隐式转换已禁止(层数不同):不能把 %…TypeGenCDef"* 隐式转为 i8**

修法:新增 _MemberSlotAccepts(判据与存储侧同一套 _ClassifyPtrImplicitConvert),
把 class_members 声明类型与 elements[base+i] 逐项比对;只有**存储时必被拒绝**的
组合(层数不同 / 指针与非指针混位)判为不一致 ⇒ 走既有「清 _elements + 清 stub
缓存」路径重建布局(通用指针槽 i8*、继承上转、数组退让照旧放行,不误清)。
2026-09-29 00:06:11 +08:00
TermiNexus c49dc1d915 fix(tpc): 类型静默降级改 fail-fast + 成员槽写入失败附来源明细
a 编译器缺陷:类型信息缺失时静默兜底会掩盖上游根因,兜出来的"看着合理"的类型
(如 i8**)又会让下游报出与真因无关的错。按既定铁律改为响亮失败:

1) HandlesImports._parse_llvm_type:被指类型是具名聚合(%...)却在
   create_structs=True 下取不到结构体时,原静默 `return PointerType(i8)` ——
   结构体指针被悄悄变成通用指针,外层再套一层即成 i8**。改 fail-fast。
   (create_structs=False 属"尚未加载",非异常,保持原样。)

2) HandlesTypeMerge._HandleSubscript:新增判据自洽守卫 —— base 是指针标记
   (rawptr / t.CPtr / 裸名 CPtr / 别名)而 IsPtrSubscriptNode 未命中时,
   历史上会落到 CollectAll 当"数组下标"解析、静默产出「基座 void + 多层指针」
   (i8**)。现 fail-fast(判据分裂即内部不变量被破坏)。

3) HandlesAssign._HandleAttributeStoreLlvm(`self.字段 = 值`):成员槽写入失败
   时追加「成员槽来源」明细 —— class_members 声明侧(项数/base/下标/声明槽类型)
   + 布局侧(struct IR 名/元素数/elements[offset])+ structs 键数与是否同一对象,
   使"声明侧退化"与"布局侧退化"一眼可分,杜绝再靠猜。

现场:TransPyV/App/lib/core/Gen/Generator.vp:193 `self.ExprGenConst = SubExprGenConst`
  → 指针隐式转换已禁止(层数不同):不能把指针类型
    %"cfd1f3d0d7e87fea.ExprGenConst"* 隐式转为 i8**
2026-09-28 23:59:29 +08:00
TermiNexus 49f0386c33 fix(tpc): 构造物化豁免按「与目标同一」匹配,覆盖 sha1 前缀命名
a 编译器缺陷(承接 fa6b3c2e,仍拦 `*槽 = <boxed 变体构造>`):上一版按 callee
候选名直查 `Gen.structs` 取聚合 —— 但跨模块命名隔离下,同一类型的键是
`sha1.ClassName` 全名(stub 里就是 `%"50741ad4b4b0934d.LLVMType"`),裸名键
往往另有一份 opaque 占位条目且**不是同一 IR 类型**。于是
`LLVMType.Struct(...)`(候选名 Struct / LLVMType)取到的是占位/取不到,
与 target_type(`%"50741ad4b4b0934d.LLVMType"`)比对失败 ⇒ 豁免不生效。

修法:不按裸名直查,改为「键等于候选名,或以 `.<候选名>` 结尾」扫描 structs,
并要求取到的聚合与 TargetType **同一**(对象同一或 IR 名相同)才命中。命中后
若源的被指类型与目标仍非同一对象/名字(构造走的是另一份条目),按 DerefAs 的
整对象拷贝语义落地:指针 bitcast 到目标被指类型再 load(指针→指针 bitcast 恒
合法),绝不把指针值写进值槽。

现场(TransPyV/App/lib/core/Gen/Class/ClassGenDef.vp:122):
  *existing.Ty = llvmlite.LLVMType.Struct(...)
  → 非基本类型的值/指针隐式转换已禁止(构造物化除外):不能把指针类型
    %"50741ad4b4b0934d.LLVMType"* 隐式转为值类型 %"50741ad4b4b0934d.LLVMType"
2026-09-28 23:32:23 +08:00
TermiNexus 78c5155de9 revert(tpc): 回滚 51de3b6d 的 [DIAG-decl] 临时诊断
「跨模块函数形参退化成 i32*」(constant_get_str_val / function_get_name 第 0 形参
i32*)一族已由 1c1f78e7 定位修复(同名枚举成员遮蔽类定义,判据按 IsEnumMember
区分),探针使命结束,整体移除:
  - FuncGen._RecordDeclFrom / _DescribeI32ParamDiag 及两处调用点
  - _adjust_args fail-fast 报文中的 _diag_extra 拼接
  - BaseGen._get_or_declare_function 入口记录点
  - HandlesBase.GetOrCreateFuncDecl 入口记录点
2026-09-28 23:23:02 +08:00
TermiNexus fa6b3c2e64 fix(tpc): 「构造物化」豁免覆盖 REnum 变体构造(*槽 = <boxed 变体> 被误拦)
a 编译器缺陷:规则③(非基本类型值 ↔ 指针隐式转换一律禁止——含 T* → T)
的唯一例外是「构造物化」,但 _CtorCallStructType 只按 callee 名在 Gen.structs
里取聚合类型:对 REnum 变体构造 Qual.Variant(...)(如
llvmlite.LLVMType.Struct(fields, fcount, name))取到的是**变体短名 Struct**,
structs 中不存在 ⇒ 恒 miss,于是 `*existing.Ty = <boxed 变体>` 被当作指针→值
隐式转换拦下。报文自己写着「构造物化除外」,实现却没覆盖这一形态。

修法:按 HandlesExprCall._HandleCallLlvm 的分派顺序补齐判据 —— 先按 callee 名
判类构造(Point()),miss 后再按**限定名 Qual**(REnum 类,如 LLVMType)取一次
聚合类型。REnum 变体构造返回 boxed 指针(_allocaEntry(RenumType)),其 pointee
恰为 structs[Qual],与 target_type 比对通过即放行,随后走既有的
「load + 整对象 store」物化路径(与 c.DerefAs 的整对象拷贝分支等价)。

现场:非基本类型的值/指针隐式转换已禁止(构造物化除外):不能把指针类型
%"50741ad4b4b0934d.LLVMType"* 隐式转为值类型 %"50741ad4b4b0934d.LLVMType"
(ClassGenDef.vp:117)。
2026-09-28 23:21:27 +08:00
TermiNexus 833e8c7c33 fix(tpc): 识别点号写法的 REnum 变体构造 mod.REnumClass.Variant(...)
a 编译器缺陷(同一语义两种书写的判据不一致):HandlesExprCall 的跨模块 REnum 变体
构造分支只判 `isinstance(Node.func.value, ast.Name)`,即只认裸名写法
`LLVMType.Ptr(x)`;**点号写法** `llvmlite.LLVMType.Struct(fields, fcount, name)`
(Node.func.value 是 Attribute)漏掉,落到下方"模块函数 Struct"路径 ⇒
  · 返回类型被当成普通函数返回值(实为 boxed 变体构造指针);
  · 实参少一个 pool(arity 也不对)。
现场:ClassGenDef.vp:116 的 `*existing.Ty = llvmlite.LLVMType.Struct(...)`
报"不能把指针类型 %LLVMType* 隐式转为值类型 %LLVMType"。

补齐:新增同口径分支——Node.func.value 为 Attribute 时,按其 attr(REnum 类名)
在符号表(先试 f"{ModulePath}.{attr}" 再试裸名)取 IsRenum 条目,命中
RenumVariants 即走 _HandleREnumConstructLlvm(与裸名分支同一实现)。与
includes/llvmlite/__types.vp:195、__builder.vp:173 的既有用法一致。
2026-09-28 23:14:08 +08:00
TermiNexus 1c1f78e7b3 fix(tpc): 修正枚举判据——按 IsEnumMember 区分成员/类型(bb05a157 判反了)
a 编译器缺陷:bb05a157 用 `not Entry.IsEnum` 排除"枚举类型",但现场实测枚举
**成员**条目的标志组合是
    IsEnum=True + IsEnumMember=True + BaseType=CEnum
(成员的 IsEnum 也被置真)⇒ 成员被误判成枚举类型,`rawptr[Constant]` 落 i32*,
`constant_get_str_val` 形参再次退化成 i32*。

正确判据(两处同源):
  枚举**类型** = IsEnum and not IsEnumMember ⇒ 走枚举分支(宽度 i32 等)
  枚举**成员**(IsEnumMember)⇒ 不是类型 ⇒ 让位给类定义("有类定义 ⇒ 是类")

现场证据(51de3b6d 的 [DIAG-decl] 报文,随 Traceback 输出):
  实参类=5df907d3a02a0bac.Constant | structs=有 |
  _struct_sha1_map=5df907d3a02a0bac | class_sha1_map=5df907d3a02a0bac |
  symtab[IsStruct=False, IsEnum=True, IsEnumMember=True, Base=CEnum] |
  声明函数类型=i8* (i32*) | 声明来源=..._EmitExternalFuncDeclLlvm <- _get_or_declare_function

修后 `ResolveAnnotationType(rawptr[Constant])` 得 CStruct/1 ⇒ _ctype_to_llvm 取
structs['Constant'] ⇒ 声明变为 i8* (%"5df907d3a02a0bac.Constant"*),与实参同型。
2026-09-28 22:44:20 +08:00
TermiNexus 51de3b6dcf chore(tpc): [临时诊断] 声明来源 + i32* 形参判定依据并入 fail-fast 报文
只为定位"跨模块函数形参退化成 i32*"(现场 function_get_name 第 0 形参 i32*,
实参 %"46c7dc5230ea5a52.Function"*)。静态排查已穷尽:_ctype_to_llvm /
_type_str_to_llvm 产生 i32* 的出口只有「BaseType 退化成 i32 + PtrCount=1」
(含 CTypeInfo.ToString() 的 'CInt *' → i32*)与「IsEnum + Name + PtrCount=1」,
离线复现不出,需要运行时证据。

做法:把判定依据直接拼进 FailFastError 报文,使其随 Traceback 一起被看到:
  [DIAG-decl] 实参类 / structs / _struct_sha1_map / class_sha1_map /
              symtab 标志 / 声明函数类型 / 声明来源(创建者调用栈)
并在四个声明创建点记录首个创建者:
  FuncGen._get_or_declare_func / get_or_declare_c_func、
  BaseGen._get_or_declare_function、HandlesBase.GetOrCreateFuncDecl(会替换声明)。

定位完成后整体回滚(连同记录点)。
2026-09-28 22:20:09 +08:00
TermiNexus bb05a157a6 fix(tpc): 收紧「有类定义 ⇒ 是类」判据:排除枚举类型本身
a 编译器缺陷(我上一轮 61f074ff / 3ca46c01 引入的过宽判据):判据用的
_struct_sha1_map 是 _build_struct_sha1_map 按 .vpi/.pyi 里**全部 ClassDef** 建的
⇒ **枚举类**(class ASTKind(t.CEnum)、class LogLevel 等)也在其中。于是
`x: ASTKind` 这类枚举注解被当成结构体建成 opaque struct,与「枚举宽度 = i32」
错位,现场表现为 i32 ↔ 结构体互换:
  AttributeError: IntType object has no attribute is_opaque
(llvmlite builder.store / LoadInstr 把 i32 值当指针用)。

收紧为判据分工(两处同口径):
· HandlesTypeMerge._GetCTypeInfoInternal 的 Name 分支:Entry.IsEnum ⇒ 枚举类型,
  不进「类定义」分支(走下方 IsEnum 分支,宽度 i32 等);
· BaseGen._get_or_create_struct:_known_class_name 追加 and not Entry.IsEnum。

「裸名被枚举**成员**占住」(Entry.IsEnumMember,如 ast/base.vp 的
Constant: t.State vs ast/exprs.vp 的 class Constant)仍让位给类定义 —— 这正是
原修复要解决的那一类,不受影响。
2026-09-28 21:57:21 +08:00
TermiNexus e6451a077e fix(tpc): 继承展平沿继承链补真实字段名(消除 __inherited_N 占位名)
a 编译器缺陷:泛型基类的继承字段在导入侧展平时名字丢失。父类是泛型特化
`GSListNode[ParamNode]`,其特化体不在任何 .vpi 中 ⇒ 按特化名加载必落空 ⇒ 占位符
路径按 struct 槽位数补 `__inherited_N`:偏移对、**名字丢**。于是继承字段的属性访问
找不到字段(现场 `[fast-fail] 属性 Next 在类 ParamNode 中找不到
(class_members=[__inherited_0, Ty])`)。

语义修正:新增 _ResolveInheritedFieldNames —— 按**声明顺序**沿继承链取父类声明里的
字段名;父类是泛型特化 `X[Arg]` 时回退用**模板名** X 加载其成员(模板声明了字段名,
如 linkedlist.vp:265 `GSListNode[T]: Next: T*` 的 Next),这正是编译期唯一可信的
名字来源;与 DeclarationGenerator._get_inherited_members(stub 端取模板成员)、
BaseGen._get_or_create_struct 的 `_struct_sha1_map` 拼名规则同一条"类名 → 模板/模块"
规则源。名字取不到才退回占位名(仅保证偏移)。

同时修正占位符配对的槽位起点:有 vtable 指针时首元素是 vtable,上面已把它从
_num_elements 扣掉,配对结构体元素必须同步右移一位(原实现从 elements[0] 起取,
会把 vtable 指针的类型当成第一个继承字段的类型)。

结果:展平表直接是 `[("Next", <槽位类型>), ("Ty", ...)]`,属性访问是普通表命中,
不再依赖任何查找期回退。
2026-09-28 21:46:30 +08:00
TermiNexus 3ca46c01cf fix(tpc): 同名枚举成员不得遮蔽类定义(_get_or_create_struct 的 i32 出口)
a 编译器缺陷:类型注解语境下枚举**成员**从不是类型,判据只能是「该名是否有类
定义」。但 _get_or_create_struct 的两个枚举出口(Entry.IsEnum 分支、Entry.BaseType
为 CEnum 分支)没有这条判据:当平坦符号表条目被**同名枚举成员**占住时(ast/base.vp
的 `Constant: t.State` 与 ast/exprs.vp 的 `class Constant` 同名,谁后插入谁占住裸名),
返回 i32;上游 PtrCount=1 叠加后即 i32*,与调用点实参 %"<sha1>.Constant"* 分裂 ——
现场 `constant_get_str_val` 形参 i32*(上一轮 61f074ff 只修了上游 TypeMerge 的 Name
分支,这里是真正吐出 i32 的那一步)。

判据与既有规则源统一(_struct_sha1_map / class_sha1_map 已登记该名为类 ⇒ 是类):
与 TypeConvert._type_str_to_llvm_base 的 `type_str in self.structs or type_str in
self._struct_sha1_map`、HandlesTypeMerge 的 Name/Attribute 分支、
DeclarationGenerator 的 `attr_name in struct_names` 同一条。

修后落到下方正常结构体创建路径,按 _struct_sha1_map 拼出 `"<sha1>.Constant"`,
与实参/声明端同一结构体对象。
2026-09-28 21:46:15 +08:00
TermiNexus 61f074ffa4 fix(tpc): Name 注解路径的类/枚举同名判据补齐 _struct_sha1_map
a 编译器缺陷:HandlesTypeMerge._GetCTypeInfoInternal 的 ast.Name 分支在「类名与
枚举成员同名」时只查 Gen.structs,漏了 Gen._struct_sha1_map(跨模块类只登记了
类名→模块 sha1、本模块尚未建出结构体)。同文件的 Attribute 分支(L241-269)与
HandlesClassDef._HasClassDefinition、TypeConvert._type_str_to_llvm_base 的
同一判据都是「structs 或 _struct_sha1_map」,Name 路径独缺,属判据分裂。

现场:rawptr[Constant](includes/ast/exprs.vp:59 accessor,class Constant 声明在
同文件更后面)在导入侧解析时,先命中平坦符号表里的枚举成员 Constant: t.State
(ast/base.vp)→ 返回枚举 → 与 t.CPtr 合并后经 _ctype_to_llvm 的 IsEnum 分支
(枚举名未登记 ⇒ 宽度退化为 i32)得 i32*,与调用点实参 %"<exprs_sha1>.Constant"*
分裂 → FuncGen._adjust_args 报「指针隐式转换已禁止」。

补齐后 BaseType=CStruct(Constant),_get_or_create_struct 按 _struct_sha1_map 拼出
"<exprs_sha1>.Constant"(BaseGen.py:527-528),与声明端/实参同一结构体对象。
2026-09-28 19:40:06 +08:00
TermiNexus 74ccd31c19 fix(tpc): 裸名解析处理同名冲突 + 声明阶段多轮,消除跨模块类型解析残余失败
承接上一条(两阶段加载)后仍有 11 个模块签名解析失败,两类根因:

① 同名冲突:平坦符号表(无前缀)一个名字只能留一个条目,不同模块可用不同类型

   种类声明同名符号,且谁后插入谁占住裸名 —— stdint.vp:9 `INT: t.CTypedef = t.CInt`

   与 argparse.vp:33 `define INT = 1`(sig: `INT: t.CDefine = 1`)撞名 ⇒ posix 的

   `oflag: INT` 解析失败;llvmlite/__function.vp:77 `class Function` 与

   ast/tokens.vp:95 枚举成员 `Function: t.State` 撞名 ⇒ 5 个模块的 `func: rawptr[Function]`

   失败;ast/exprs.vp:18 `class Name` 同型(ast/parser.vp)。

   修法:_resolve_name 在平坦条目不是 typedef/类/枚举时,跨 _namespaces 找回同名

   typedef / 类 / 枚举(注解位置只可能是这三种)。

② 声明阶段的顺序依赖:typedef 右侧可引用其它模块的 typedef(win32base 的

   `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint)⇒ 单轮仍与顺序有关。

   修法:Phase2Translator 声明阶段多轮(未解析 typedef 的模块下一轮重 stage,

   无进展即停,_CountUnresolvedTypedefs 判定),签名阶段仍是全部声明入表后统一跑。

③ 顺带修:两阶段是两个 TypeResolver 实例,签名阶段也须注入本模块类型名

   (TypeResolver.inject_known_type_names 供两阶段共用)。

验证(TransPyV 全量 127 个 sig 模拟 ①→②):commit 失败数 11 → 0;

posix.open ret=i32 params=[i8*,i32]、read ret=i64 params=[i32,i8*,i64] 解析正确。
2026-09-28 18:25:57 +08:00
TermiNexus 05f4adb233 fix(tpc): 符号表加载改两阶段,消除模块加载顺序依赖
a 类(编译器缺陷/机制漏豁免):签名解析要通过符号表查其它模块的类型名

(from X import * 的裸名如 posix 的 INT;跨模块类如 rawptr[Constant]),而加载器

按 os.listdir 字典序逐个「提取→解析→插入」⇒ posix 排在 stdint 之前时 INT 查不到。

旧口径静默降级 i32 掩盖了它,fail-fast 后暴露。

修法:加载拆两趟 —— ① StageModuleSymbols:提取 + 只解析/插入声明

(类/枚举/typedef/匿名类型)② CommitModuleSymbols:解析签名 + 插入函数/define +

命名空间重导出。批量加载器(Phase2Translator._build_shared_symbol_data)先收集全部

模块、跑完所有①再统一跑② ⇒ 任一模块的签名都能看到全部声明,与顺序无关。

单模块 LoadModuleSymbols 语义不变(①紧接②)。

验证(探针,posix 先 stage、stdint 后 stage 的“坏顺序”):

  posix.open ret=i32 params=[i8*,i32](INT=stdint 的 typedef);read ret=i64 params=[i32,i8*,i64];

  同样顺序走旧的单模块路径则 RAISE(对照组)。
2026-09-28 18:14:45 +08:00
TermiNexus 8b657b9ac2 feat(tpc): 类型解析 fail-fast 报文补充完整定位信息
报错从单行"类型注解 X 无法解析"改为分层多行:

① SymbolTable._LoadSymbolsFromFile 加 文件路径 + 模块命名空间 + 发起行;

② SymbolTypeResolver._resolve_function 加 函数名/行号/形参序号与形参名 + 注解源码原文

   (新增 _AnnotationText 渲染 Name/Attribute/Subscript/BinOp/Tuple/Constant);

③ LLVMTypeMapper._resolve_name 列出全部已试来源(内置速记/BuiltinTypeMap/

   builtins CTypedef/符号表 typedef/本模块类名数/枚举名数/符号表已加载条目数)与注解行列,

   并给出"是否声明模块未先加载"的排查提示。

样例(VET/temp/24da8b5f154f7cc0.vpi):

  函数 'open'(第 107 行)的返回类型无法解析 / 注解写法:INT | t.State / 裸名 'INT' ...
2026-09-28 18:11:14 +08:00
TermiNexus 14af03577c fix(tpc): 跨模块符号表保留类名,形参注解不再退化成 i32*
a 类(编译器缺陷):.vpi/.pyi 派生的函数签名经 LLVMTypeMapper._resolve_name 解析,

该方法只认内置名 / BuiltinTypeMap / typedef,不认类名 ⇒ rawptr[Constant] 被

_rawptr_subscript_type_str 变成 i32*,写进跨模块符号表(temp/_shared_sym.json 的

FuncPtrParams arg0=i32*),调用点据此声明出 i32 形参,与实参 %"sha1.Constant"* 冲突,

在指针隐式转换规则下报成"源码问题"(掩盖根因)。

修法:TypeResolver.resolve 注入本模块类名/枚举宽度;_resolve_name 按 本模块类名 → %struct.X*、

枚举 → i{width}、已加载模块类/枚举 → 同上、builtins CTypedef → 按定义展开;其余一律

raise TypeNameUnresolvedError(不再 warn + i32)。SymbolTable._LoadSymbolsFromFile 对该异常

re-raise(不再吞成"加载模块失败"返回空符号表)。

验证(vpi 探针):name_get_id/constant_get_str_val 形参 i32* → %struct.Name* / %struct.Constant*;

binop_get_left/binop_get_right/attribute_get_value 返回 %struct.AST*(跨模块类经符号表解析);

int64/size_t/sizet → i64;未知名 → 报错终止。
2026-09-28 18:06:07 +08:00
TermiNexus b3f72d675b fix(ctx): 方法体编译失败路径也复位「当前方法」上下文,修 self 被按内层特化类解析
现象:编译 ExprGenFstr.translate_joined_str 时 self.Gen 被按 list[cfc4ca8da95a30e1.AST*] 解析 → 属性在类中找不到(连带错误)。

链路:parts.get(pi) 触发泛型特化按需补全(HandlesExprCall._try_complete_generic_specialization)→ 内层 list[...] 方法发射时 _EmitFunctionLlvm 先置 _CurrentCpythonObjectClass = 内层类(HandlesFunctions:1978)→ 方法体编译失败只 re-raise,末尾的类名/var_struct_class 复位被跳过 → 上层调用点**吞掉**该异常继续编译外层方法,于是外层 self.X 全按内层类解析、后续指令也会误插进内层函数的块。

两处修复:① _try_complete_generic_specialization 的 except 里同时复位 Gen.builder/func 与 _CurrentCpythonObjectClass/Gen._current_class_name(该点会吞异常,必须自己收尾);② _EmitFunctionLlvm 的失败路径在 re-raise 前复位 Gen._current_class_name / _CurrentCpythonObjectClass / var_struct_class / global_struct_class(对所有吞异常的调用点都成立)。
2026-09-28 16:52:03 +08:00
TermiNexus 96d8e13c5a fix(err): 成员查找 fail-fast 改用可读异常 + 报错语句栈不再重复长报文
① 新增 MemberLookupFailFast(KeyError):_get_member_offset 的 fail-fast 原用 raise KeyError(消息),而 KeyError.__str__ = repr(消息) ⇒ 报文被加上引号、换行被转义成字面 \n(含"源代码 (文件, 行号): 行号 | 源码"的多行报文彻底不可读)。新类继承 KeyError(保住 HandlesExprAttr 两处 except KeyError 兜底点)但 __str__ 返回裸消息。

② 顺带删除该处的 traceback.print_stack 调试转储(遗留调试语句,输出噪音)。

③ Phase2Translator 语句栈渲染:原格式把同一条异常消息贴在每一层帧后面 ⇒ 一句话被打印 N 次;改为外层→内层的调用路径逐帧印位置、异常消息只在最内层印一次。
2026-09-28 16:48:58 +08:00
TermiNexus 49ff1b995f fix(cast): 显式指针 cast 一律放行(按 bitcast 重解释)+ MemoryOps 改用 vast 解析器模块
① 新增 _IsExplicitPtrCastNode:值节点是写明的指针转换调用(t.CPtr(x)/CPtr(x)/c.CPtr(x)/rawptr[T](x)/t.CPtr[T](x)/(T*)x/T*(x))时,_ClassifyPtrImplicitConvert 直接放行(用户规则「显式转换都可以」),调用方按既有 bitcast 重解释;非 cast 调用(MemBuddy(x)/list[X](x)/foo(x))不受影响。

根因:includes/_list.vp:60 的 elem_ptr: T | t.CPtr = t.CPtr(地址) 在 list[X*] 特化下 T|t.CPtr = X**(T 本身是指针),而 t.CPtr(...) 的值是 i8* ⇒ 被判「层数不同」,与报错自己给的「如需转换请显式 cast(如 t.CPtr(...))」建议自相矛盾(ExprGenFstr.vp 编译失败的 2 条 elem_ptr 报错)。

② MemoryOps.py 的 import ast 是标准库 —— 但 TPC 全部源码由 vast 解析,节点类为 lib.vast.Nodes.*,与标准库 ast 互不兼容 ⇒ 该文件所有节点类型判据对 Viper 源码静默失效(_value_node_of 子表达式下沉、_CtorCallStructType 构造物化豁免、以及新增的 cast 判据都不生效)。改为 import vast as ast,一并修复。
2026-09-28 16:48:52 +08:00
TermiNexus 5d1620505e fix(decl): 声明端看穿「装饰器标记 + 裸类引用」联合,修 t.CExtern|X|t.CPtr 多外包一层指针
根因:vpi 记录的模块级全局是 `_mbuddy: t.CExtern | memhub.MemBuddy | t.CPtr` 形态,_get_type_str 把该联合展开为 %"sha1.MemBuddy"*(标记 t.CExtern 展开为空串,已被 BinOp 分支丢弃),但外层 | t.CPtr 走 _is_bare_class_ref_annotation 时对 ast.BinOp 一律 return False ⇒ 未吸收"裸类引用自身即指针"那一层 ⇒ 声明端 %X** 与定义端 %X* 差一层。

后果:Phase1 生成的 .stub.ll 声明 @_mbuddy = external global %X**,消费模块按下声明取值得到 %X**,赋给 %X* 字段(VLogger.vp:105 self.__mbuddy__ = _mbuddy)即报"指针隐式转换已禁止(层数不同)"。

修法:BinOp 分支内若恰一侧展开为空/void(纯装饰器标记 CExtern/CExport/State/CStatic...)则按另一侧递归判定,对齐 TPV TypeGenResolve.vp:2596 第 3 条语义「ActualType | DecoratorMarker → ActualType」(TPV 无此问题,故只改 TPC)。

探针验证:t.CExtern|X|t.CPtr / X|None|t.CPtr / t.State|X|t.CPtr → %X*(修正);X|t.CPtr、rawptr[X]|t.CPtr → %X**、bytes|t.CPtr → i8**、int|t.CPtr → i32*(均不变)。注意 .stub.ll 有全局缓存,需 --clear-cache 重编生效。
2026-09-28 16:35:53 +08:00
TermiNexus 809f9d08fa feat(len): len() 对「非 i8 裸指针 / 无 __len__ 的 struct」改为 fail-fast(对齐 TransPyV)
旧实现:非 i8 裸指针(如 []T 形参实为 T*)静默返回常量 0;struct 无 __len__ 时回落 strlen —— 两者都用假长度掩盖错误。

新口径(两侧逐字一致):定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen;带 __len__ 的结构体 → 调 __len__;其余一律编译期 TypeError。

新增 _LEN_SUPPORTED_HINT 共享提示语 + _FailFastLenUnsupported 出口(含 行:列 + 源码片段 + 正确做法)。HandlesExprCall.py 为同源死代码副本,一并同步语义未删。
2026-09-28 15:53:02 +08:00
TermiNexus 97c196bfc7 feat(cast): 放行数组退让([N]T* → T* 单向),与继承上转同处判据
在 _ClassifyPtrImplicitConvert 的“被指类型非基本”分支里、继承上转之后加数组退让分支:源的被指类型是 ir.ArrayType 且其 element 与目标被指类型相同 ⇒ 放行(C/C++ 同口径)

判据顺序(两侧一致):层数不同即禁 → 完全相同放行 → 通用指针通配 → 标量无损/有损 → 继承上转 → 数组退让 → 否则 DENY;反向 T*→[N]T*、多维只退一层([N][M]T*→[M]T* 放行、→T* 不放行)
2026-09-28 15:38:41 +08:00
TermiNexus 171b0e3eeb fix(cast): 补继承上转豁免(Derived*→Base* 允许隐式转换),修 MemBuddy*→MemManager* 被规则③误拦
_ClassifyPtrImplicitConvert 由 staticmethod 改实例方法(全仓 5 处调用均为 self./Gen.,无 unbound 调用);新增 _ClassNameOfPointee(复用 Gen.find_struct_by_pointee + 短名回退)与 _IsInheritanceUpcastPointee(复用 Gen._get_mro_chain);两指针皆非基本标量时先判上转再判禁

保持:Base*→Derived* 仍禁(下转须显式强转)、无关结构体互转仍禁、通用指针通配/层数/完全相同/None/构造物化/标量无损口径不变
2026-09-28 15:29:41 +08:00
TermiNexus 9f8190d58f fix(cast): 无损口径对齐 TPV + 规则③豁免构造物化(p: Point = Point() 放行,真实 Foo*→Foo 仍禁)
_ScalarConvertLossless 改为与 TPV 一致:int↔int 全向允许、float↔float 全向允许、int→float 需目标浮点位宽≥源整数位宽、float→int 不允许(此前把整数↔浮点一律判有损,两侧不一致)

新增 _ValueNodeOf / _CtorCallStructType:指针→非基本值分支先豁免构造物化(Call 被调方命中 Gen.structs 且类型同一),其余保持禁止;报文前缀与 TPV 逐字一致;诊断沿用 _DescribeValueNode

已知未覆盖:TPC 无对称的“值→指针”检查;泛型构造不豁免;别名导入的类名可能命不中(保守不豁免,只误报不漏放)
2026-09-28 15:17:48 +08:00
TermiNexus d7158bd336 feat(cast): 指针隐式转换新规则 + None 可隐式转任意类型 + 诊断错位修复(9 文件)
MemoryOps._ClassifyPtrImplicitConvert:同类型放行;层数不同禁;任一侧通用指针(i8*)放行;皆非通用按被指类型(非基本类型禁、基本标量按位宽无损);指针与非基本值互换禁

_IsNoneValue 收敛:AST None 常量 或 指针型 null 常量 → 放行任意目标(指针目标给 null,非指针目标给零值);整数 0 不误命中;短路口覆盖 9 处禁令

诊断:新增 BaseGen._DescribeValueNode(子表达式下沉 + 有符号列号 + 源码片段),9 处禁令统一使用,不再打印外层 Assign 与 4294967295 列号

注:TPC 侧规则③比 TPV 更严(指针→非基本值恒禁),可能与既有按值回读冲突;待实机编译确认后再统一两侧口径
2026-09-28 15:10:39 +08:00
TermiNexus 35de6321f6 refactor(return): 移除 @c.CReturn 多返回值处理器,只保留 -> tuple[ty1, ty2, ...],遇到 CReturn 一律报错
DeclarationGenerator:tuple[...] 分支逐字保留(仅 CReturnTypes 机械改名 MultiRetTypes),装饰器分支改为 SyntaxError,报文含函数名与“请改用 -> tuple[ty1, ty2, ...]”提示

HandlesAssign(a, b = f() 解包路径):删除装饰器收集分支并改为报错;tuple[...] 恢复与逐个解包逻辑逐字不变

HandlesFunctions:新增 _CRETURN_REMOVED_HINT 与 _RejectCReturnDecorator,在函数定义入口(_GetFunctionSignatureLlvm / _EmitFunctionLlvm)fail-fast——即使只有装饰器、没有 -> 注解,也不会让返回类型静默退化成 i32

HandlesImports:外部函数声明处同样报错,并删除下游已失效的 CReturnTypes 参数拼接死代码

HandlesReturn / HandlesFor / BaseTranslator:CurrentCReturnTypes 等机械改名(读写端一致,无行为变化)

includes/c.py:保留 CReturn 名字作墓碑(删掉会让 @c.CReturn 变成未知属性、可能被静默忽略),docstring 写明已移除并指路 -> tuple[...];README 措辞同步
2026-09-28 13:57:04 +08:00
TermiNexus e5a6616f8b feat(static): static 修饰符改为语句起始前置 + 新增 static def(旧的注解位置响亮失败)
vast/Parser.py:ParseStatement 与 _ParseDecorated 新增 static def 分派(判据要求第 2 个 token 是 def,故 static = 5 / static x = 5 不会被误吃成函数定义);新增 _ParseStaticDef,镜像 _ParseExportDef 但**不改写返回注解**、而是在既有装饰器之后合成 @t.CStatic

SymbolUtils.py:_TYPE_DESCRIPTOR_MARKERS 删掉 static;新增 _StaticPositionSyntaxError 与 _InStaticOldAnnotationPosition,旧注解位置(名字: static 类型)一律 SyntaxError 并提示前置形式,绝不静默改写;_EXPORT_EXTERN_MODIFIERS 加入 static,_ExpandExportExternVar 新增前置分支——export/extern 追加在类型右侧(或 t.CExport / 或 t.State),static **前置在类型最左**(t.CStatic 加竖线),允许初值;数组前缀展开仍在修饰符展开之前

与 includes/ast/parser.vp 的 pending_static 通道同源:static 名字: 类型 = 值 产出与手写 名字: t.CStatic | 类型、装饰器 @t.CStatic 逐字段同形
2026-09-28 13:49:28 +08:00
TermiNexus f9957787a0 feat(codegen): TPC 模块级 t.State 全局只声明不定义(external linkage)
extern 名字: 类型的展开产物(名字: 类型 | t.State):本模块生成 @名字 = external global T,不写定义(否则 duplicate symbol)

位置早于「已存在则升级 common 定义」分支,避免 extern 被反向升级成定义;与 import 侧 _EmitExternalGlobalDeclLlvm 同名分支同口径

export(t.CExport)侧无需改动:TPC 模块级全局本就用裸名(_mangle_name 不作用于全局变量),裸名导出语义天然满足
2026-09-27 18:01:49 +08:00
TermiNexus 6fce79d531 feat(symbols): TPC 侧同步 export/extern 变量声明语法糖
新增 _ExpandExportExternVar:export 名字: T [= 初值] → 名字: T | t.CExport [= 初值];extern 名字: T → 名字: T | t.State;extern 带初值响亮失败 SyntaxError

排在 _ExpandArrayPrefixAnnotations 之后(先让注解规范化,再插标记);_AtAnnotationColon 把 export/extern 认作语句边界,使数组前缀仍能脱糖

新增 _NextSignificantTokenIndex;ExpandSoftKeywords 文档改列九种展开
2026-09-27 18:00:54 +08:00
TermiNexus 4346552c72 feat(symbols): TPC 侧同步 renum 语法糖,renum X: 展开为 class X(t.REnum):
新增 _ExpandRenumBlocks(与 _ExpandUnionBlocks 同构,只改写头行)并接入 _ExpandSoftKeywordsImpl

行首 renum 的非 renum 名字: 形态响亮失败 SyntaxError(renum 不作标识符)

同步 TransPyV/includes 46cd4b4 的解析侧支持;ExpandSoftKeywords 文档改列八种展开
2026-09-27 17:53:23 +08:00
TermiNexus e1a2821bff fix(tpc): 函数声明创建幂等化,消除 DuplicatedNameError
同名声明只在 module.globals、不在 Gen.functions 时,旧代码会再走 ir.Function,而 llvmlite 的 NameScope 按名字去重(GlobalValue.deduplicate_name=False)必抛 DuplicatedNameError。GetOrCreateStubFuncDecl(含 check_existing=False 路径)、GetOrCreateFuncDecl、win32console 内建声明、output-stub 按需载入四处统一先复用 module.globals。真实事故:VarGenMixin.vp 同一模块两处 self.Gen.ExprGen.translate_value(...) 都走 check_existing=False,第二处崩在 llvmlite。
2026-09-27 16:18:37 +08:00
TermiNexus e33ac795ed fix(codegen): 指针强转基座 T*(x) 的字段写入补联合类名通道
与读取侧 _handle_attr_call_result 的联合分支同口径:_get_attr_ptr 新增 Call 基座分支,用 _extract_union_base_class 取裸类名后 bitcast/inttoptr 回 Ptr(类) 再 GEP 成员。此前写入侧只认 Name/Attribute/Subscript,强转基座的字段写入静默返回 None。
2026-09-27 16:02:40 +08:00
TermiNexus 5825e7e000 feat(codegen): 单目 & 支持对构造调用/函数调用结果取址
与 TransPyV 侧 ExprGenOps._addrof_of_call 同口径:复用 _HandleUnaryOpLlvm 已求值的 OperandVal;构造调用 _HandleClassNewLlvm 返回临时对象的 Ptr(Struct),原样返回;其它类型落到 allocaEntry 临时槽。避免二次求值导致 __before_init__/__init__ 跑两遍。
2026-09-27 15:16:08 +08:00
TermiNexus ae02431dd2 fix(descriptor): static 作为注解位置存储类描述符(名字: static T)
_TYPE_DESCRIPTOR_MARKERS 增加 static -> CStatic,与 const/volatile 共用同一展开器与注解位置判据;static 仅在注解位置生效(冒号后 / 形参 / 返回箭头后),非注解位置的 static 标识符与 static_libs 之类子串不受影响。缺类型(a: static)现在响亮失败而非静默退化。

修用户报告:a: static int(TransPyV/App/main.vp:56,ViperOS 驱动同款 pack_head: static uint32 = 0 广泛使用)此前注解位置未被消费,static 退化成一个类名并被建成 opaque 结构体,产出 a 的类型为 模块sha1 点 static 的 zeroinitializer 全局,llvm-link 报 invalid type for null constant。现展开为 a: t.CStatic | int。

回归:306 个 .vp 中 105 行被改且全部含 const/volatile/static,0 处意外改动;main.vp / mouse.vp / keyboard.vp / devfs.vp / fat32_types.vp 展开后 vast.parse 全部通过。
2026-09-27 14:18:45 +08:00
TermiNexus 64538e08f8 feat(descriptor): 注解位置 __attribute__(...) 脱糖为 t.attr.*,并支持 []T 自动推导数组
SymbolUtils 新增 _ExpandAttributeDescriptors:注解位置(冒号后 / 形参 / 返回箭头后,可与 const/volatile 混写)的 __attribute__(a, b(x)) 文本脱糖为 t.attr.a | t.attr.b(x),与 TPV includes/ast/parser.vp 的 _IsMagicAttributeCall/_MakeTAttrMarker 同源;紧跟的 C 风格前置数组前缀 [N]T / []T 在同一次编辑里一并脱糖为 t.CArray[T, N] / t.CArray[T, None](None = TPC 既有长度自动推导数组,见 HandlesAssign.py:1606)。

修 TransPyV/App/main.vp:44 —— V_TPV_VERSION: __attribute__(section('.PRVERSN')) []char = '...' 此前 []char 被 vast 当成 __attribute__ 调用的下标,报 unexpected token OP(']')(与根因脱节);现展开为 t.attr.section('.PRVERSN') | t.CArray[char, None]。

把 _AtAnnotationColon 从 _InArrayAnnotationPosition 抽出,数组前缀与属性描述符共用同一注解位置判据,避免两份副本分叉。回归:306 个 .vp 仅 main.vp:44 一行被改,无意外改动;11 条合成用例与 main.vp 全文件 vast.parse 均通过。
2026-09-27 14:06:04 +08:00
TermiNexus 3b5d85ce9d fix(handles): 补指针操作符目标写入与实参解引用,消除静默丢弃
- HandlesAssign: 新增 ast.UnaryOp 赋值目标分支(*p = v 复用 _StoreWithCoerce);Tuple/List 目标合并;删除 Tuple 分支里未绑定的死代码(UnboundLocalError)

- HandlesAugAssign: 新增 ast.UnaryOp 目标分支实现真 *p += v,其余形态 fail-fast

- HandlesExpr: 新增 ast.Starred 分支,值位置 *p 归一为 UnaryOp(Deref)(修 t.CPtr(*((uint64*)(blk))) 恒为 0)
2026-09-27 00:19:44 +08:00
TermiNexus 58050a1ab2 fix(member_base): base 改由声明类型与 elements 的一致度校准,修整类字段偏移整体差 1
delta 长度启发式只在成员表与布局恰好差 vtable 一项时成立;成员表多/少一个非 vtable 项时 base 判错,整个类字段偏移整体平移:ast.Constant 的 lineno 落到 pool(MemManager*),调用点报形参 lineno: i32 收到指针,把真因掩盖成指针/整数错配

改法:对 b 属于 {0,1,2} 逐项比较成员声明类型与 elements[b+i],取一致度最高者(完全相同 +2),仅在明显更优(至少多对齐 1 个成员)时覆盖启发式;指针槽区分度最高

同时移除上一轮临时加的运行时字段布局探针(_diag_member_layout 及其两处调用)
2026-09-26 21:29:28 +08:00
TermiNexus 50cea6c7b3 fix(classdef,base,attr): 成员注解解析静默兜底改 fail-fast;新增按需输出的字段布局探针
HandlesBase.CollectAnnAssignMember: 注解解析返回 None / _ctype_to_llvm 得 void / 抛异常三处静默 i32、i8* 兜底改 fail-fast,报错带成员名+注解原文+BaseType/PtrCount 现场

HandlesClassDef: 方法内 self.x: T = ... 的同类三处兜底同样改 fail-fast(带类名/成员名/注解原文)

HandlesExprAttr: 新增 _diag_member_layout(默认静默,仅当 offset 为 None / members[offset] 名不符 / 类型与 elements[offset] 不一致时打一行,上限 15 行)
2026-09-26 21:25:52 +08:00
TermiNexus c83fc752c6 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 口径等历史改动一并入库
2026-09-26 19:27:22 +08:00