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)。
This commit is contained in:
TermiNexus committed 2026-09-28 23:21:27 +08:00
1 parent 833e8c7c33
commit fa6b3c2e64
1 file changed
+32 -13
+32 -13
View File
@@ -389,34 +389,53 @@ class MemoryOpsMixin:
return Node
def _CtorCallStructType(self, Node: Any = None) -> Any:
"""AST 层判定:Node(或其值子节点)是否为「类/构造调用」右值;是则返回其构造的
"""AST 层判定:Node(或其值子节点)是否为「构造调用」右值;是则返回其构造的
聚合类型(IdentifiedStruct/LiteralStruct),否则返回 None。
规则③(非基本类型值 ↔ 指针隐式转换一律禁止)的**唯一例外**是「构造物化」:
右值来自该类型的构造调用时放行(构造语义,非类型转换)。判据复用本仓既有的
「Call 的被调方是类/构造」口径(见 HandlesExprCall._HandleCallLlvm 的分派):
- callee 裸名/属性名命中已注册类(`Gen.structs`)→ 构造;
「Call 的被调方是构造」口径(见 HandlesExprCall._HandleCallLlvm 的分派顺序):
- callee 裸名/属性名命中已注册聚合(`Gen.structs`)→ 类构造(`Point()`);
- callee 是 `Qual.Variant(...)` / `mod.Qual.Variant(...)` 的**变体**写法 ——
变体短名(如 `Struct`)不在 `Gen.structs` 中,构造出的聚合类型挂在
**限定名 Qual**(REnum 类,如 `LLVMType`)下,故再按 Qual 取一次。
REnum 变体构造返回 **boxed 指针**(`Gen._allocaEntry(RenumType)`,
见 HandlesExprCall._HandleREnumConstructLlvm),其 pointee 恰是
`structs[Qual]` —— 正是「构造物化」要放行的形态
(`*existing.Ty = llvmtype.Struct(...)`,boxed 指针 → 值槽的整对象拷贝)。
- 泛型类模板特化(Box(42))需类型实参推断,静态取不到类型 → 保守返回 None。
顺序与 `_HandleCallLlvm` 一致:先按 callee 名判类构造,miss 后再按限定名判
变体构造(若同名类存在,实际 dispatch 也走类构造路径)。
"""
N: Any = MemoryOpsMixin._ValueNodeOf(Node)
if not isinstance(N, ast.Call):
return None
Func: Any = getattr(N, 'func', None)
ClassName: str | None = None
Qualifier: str | None = None
if isinstance(Func, ast.Name):
ClassName = Func.id
elif isinstance(Func, ast.Attribute):
ClassName = Func.attr
if not ClassName:
return None
ST: Any = getattr(self, 'structs', {}).get(ClassName)
if ST is None:
return None
if isinstance(ST, ir.PointerType):
ST = ST.pointee
if not isinstance(ST, (ir.IdentifiedStructType, ir.LiteralStructType)):
return None
return ST
# 变体构造的限定名:`Qual.Variant` 的 Qual(Name)或 `mod.Qual.Variant`
# 的 Qual(Attribute.attr)。
_QualNode: Any = getattr(Func, 'value', None)
if isinstance(_QualNode, ast.Name):
Qualifier = _QualNode.id
elif isinstance(_QualNode, ast.Attribute):
Qualifier = _QualNode.attr
Structs: Any = getattr(self, 'structs', {})
for _Cand in (ClassName, Qualifier):
if not _Cand:
continue
ST: Any = Structs.get(_Cand)
if ST is None:
continue
if isinstance(ST, ir.PointerType):
ST = ST.pointee
if isinstance(ST, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ST
return None
def _alloca(self, typ: ir.Type, name: str = '', align: int | None = None, size: ir.Value | None = None) -> ir.AllocaInstr:
if align is None: