diff --git a/lib/core/LLVMCG/MemoryOps.py b/lib/core/LLVMCG/MemoryOps.py index 201fd03..46d83a0 100644 --- a/lib/core/LLVMCG/MemoryOps.py +++ b/lib/core/LLVMCG/MemoryOps.py @@ -388,53 +388,77 @@ class MemoryOpsMixin: return Node.value return Node - def _CtorCallStructType(self, Node: Any = None) -> Any: + @staticmethod + def _IsSameAggregate(A: Any, B: Any) -> bool: + """两个 LLVM 聚合类型是否指同一类型(对象同一,或 Identified 结构体 IR 名相同)。""" + if A is None or B is None: + return False + if A == B: + return True + return (isinstance(A, ir.IdentifiedStructType) + and isinstance(B, ir.IdentifiedStructType) + and A.name == B.name) + + @staticmethod + def _CtorCalleeNames(Func: Any) -> list[str]: + """构造调用 callee 的候选聚合名(类构造 / REnum 变体构造)。 + + - `Point()` → ['Point'](callee 即类名) + - `Qual.Variant(...)` → ['Variant', 'Qual'] + - `mod.Qual.Variant(...)` → ['Variant', 'Qual'] + 变体短名在前(同名类构造优先,与实际 dispatch 一致),限定名(REnum 类名) + 在后。 + """ + Names: list[str] = [] + if isinstance(Func, ast.Name): + Names.append(Func.id) + elif isinstance(Func, ast.Attribute): + Names.append(Func.attr) + QualNode: Any = getattr(Func, 'value', None) + if isinstance(QualNode, ast.Name): + Names.append(QualNode.id) + elif isinstance(QualNode, ast.Attribute): + Names.append(QualNode.attr) + return Names + + def _CtorCallStructType(self, Node: Any, TargetType: Any) -> Any: """AST 层判定:Node(或其值子节点)是否为「构造调用」右值;是则返回其构造的 聚合类型(IdentifiedStruct/LiteralStruct),否则返回 None。 规则③(非基本类型值 ↔ 指针隐式转换一律禁止)的**唯一例外**是「构造物化」: - 右值来自该类型的构造调用时放行(构造语义,非类型转换)。判据复用本仓既有的 - 「Call 的被调方是构造」口径(见 HandlesExprCall._HandleCallLlvm 的分派顺序): + 右值来自该类型的构造调用时放行(构造语义,非类型转换)。判据(见 + 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 指针 → 值槽的整对象拷贝)。 + - callee 是 `Qual.Variant(...)`(REnum 变体构造,如 `LLVMType.Struct(...)`) + —— 变体短名不是聚合,构造出的聚合类型挂在**限定名 Qual**(REnum 类)下。 + REnum 变体构造返回 **boxed 指针**(`Gen._allocaEntry(RenumType)`),其 + pointee 恰是该聚合 —— 正是「构造物化」要放行的形态(boxed 指针 → 值槽的 + 整对象拷贝,语义即 load + store)。 - 泛型类模板特化(Box(42))需类型实参推断,静态取不到类型 → 保守返回 None。 - 顺序与 `_HandleCallLlvm` 一致:先按 callee 名判类构造,miss 后再按限定名判 - 变体构造(若同名类存在,实际 dispatch 也走类构造路径)。 + + `Gen.structs` 的键在跨模块命名隔离下可能是 `sha1.ClassName` 全名,且裸名键 + 可能另有一份 opaque 占位条目(两者不是同一 IR 类型)——故不按裸名直查,而是 + 按「键等于候选名,或以 `.<候选名>` 结尾」扫描,并要求取到的聚合与 TargetType + 同一(对象同一或 IR 名相同)。这样无论构造走的是哪份条目、无论结果走 + `_HandleREnumConstructLlvm` 还是 stub 侧同签名的变体工厂函数,都能正确放行。 """ 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 - # 变体构造的限定名:`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: + for Name in MemoryOpsMixin._CtorCalleeNames(getattr(N, 'func', None)): + if not Name: 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 + for Key, Val in Structs.items(): + if not isinstance(Key, str): + continue + if Key != Name and not Key.endswith('.' + Name): + continue + ST: Any = Val.pointee if isinstance(Val, ir.PointerType) else Val + if not isinstance(ST, (ir.IdentifiedStructType, ir.LiteralStructType)): + continue + if MemoryOpsMixin._IsSameAggregate(ST, TargetType): + return ST return None def _alloca(self, typ: ir.Type, name: str = '', align: int | None = None, size: ir.Value | None = None) -> ir.AllocaInstr: @@ -623,16 +647,10 @@ class MemoryOpsMixin: # 须显式解引用(c.Deref(p) / *p)或显式强转。基本标量目标已在上方 # 「指针隐式转整数」分支 fail-fast,此处只兜非基本类型。 # **唯一例外=「构造物化」**:右值来自该类型的构造调用时放行,如 - # `p: Point = Point()`(参照 GCC 构造语义:这是构造,不是类型转换)。 - CtorST: Any = self._CtorCallStructType(Node) - CtorExempt: bool = False - if CtorST is not None: - if CtorST == target_type: - CtorExempt = True - elif (isinstance(CtorST, ir.IdentifiedStructType) - and isinstance(target_type, ir.IdentifiedStructType) - and CtorST.name == target_type.name): - CtorExempt = True + # `p: Point = Point()`、`*slot = LLVMType.Struct(...)`(参照 GCC 构造 + # 语义:这是构造,不是类型转换)。判据把 TargetType 传下去,要求 AST 侧 + # 的构造类型与目标类型**同一**(对象同一或 IR 名相同)才算命中。 + CtorExempt: bool = self._CtorCallStructType(Node, target_type) is not None if not CtorExempt: Allowed3: bool Reason3: str @@ -652,6 +670,15 @@ class MemoryOpsMixin: Loaded: ir.Value = self.builder.load(val, name="Load_struct_for_store") if Loaded.type == target_type: return Loaded + if CtorExempt: + # 构造物化命中,但源的被指类型与目标不是同一 LLVM 对象、IR 名也不一致 + # (跨模块命名隔离:`Gen.structs` 里裸名 opaque 占位与 sha1 全名实体 + # 并存,构造走的是另一份条目)⇒ 按整对象拷贝语义落地:先把指针 + # bitcast 到目标被指类型再 load(指针→指针 bitcast 恒合法),确保 + # 不会把指针值写进值槽。 + Src: ir.Value = self.builder.bitcast( + val, ir.PointerType(target_type), name="ctor_ptr_bitcast") + return self.builder.load(Src, name="Load_ctor_for_store") return None def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None,