审计并修复 23 个文件:类型注解中的 T1 | T2 一律 fail-fast;typedef/define/export/extern/static/const/volatile/attribute 优先读取 Markers/Attributes/ByteOrder/BitWidth;指针统一 CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr Subscript;stub 输出统一新语法。
817 lines
46 KiB
Python
817 lines
46 KiB
Python
from __future__ import annotations
|
||
from typing import Any
|
||
# 必须用 TPC 自己的解析器模块(vast),**不是**标准库 ast:TPC 全部源码
|
||
# (.vp 与 .py)都由 vast 解析,产出的是 lib.vast.Nodes.* 节点,与标准库
|
||
# ast 的节点类互不兼容(isinstance 恒 False)。此前误用标准库 ast,使本文件
|
||
# 全部节点类型判据对 Viper 源码静默失效(_value_node_of 的子表达式下沉、
|
||
# _CtorCallStructType 的构造物化豁免、指针 cast 判据都不生效)。
|
||
import vast as ast
|
||
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` 关键字;语义与 ViperC 侧 `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:
|
||
|
||
def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None:
|
||
if isinstance(val.type, ir.PointerType):
|
||
pointee: ir.Type = val.type.pointee
|
||
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
self._local_heap_ptrs.append((val, 'struct', ClassName))
|
||
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||
self._local_heap_ptrs.append((val, 'i8', ClassName))
|
||
if VarName:
|
||
self._var_to_heap_ptr[VarName] = val
|
||
|
||
def _unregister_local_heap_ptr(self, val: ir.Value) -> None:
|
||
self._local_heap_ptrs = [(v, t, cn) for v, t, cn in self._local_heap_ptrs if v is not val]
|
||
|
||
def _emit_local_heap_frees(self) -> None:
|
||
if not self._local_heap_ptrs or not self.builder:
|
||
return
|
||
deleted_objects: set[ir.Value] = set()
|
||
for ptr, ptr_type, ClassName in self._local_heap_ptrs:
|
||
if ptr_type == 'i8':
|
||
Loaded_ptr: ir.Value = self.builder.load(ptr, name="load_ptr")
|
||
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
|
||
is_null: ir.Value = self.builder.icmp_unsigned('==', Loaded_ptr, null_ptr, name="is_null")
|
||
not_null_block: ir.Block = self.builder.append_basic_block(name="not_null")
|
||
next_block: ir.Block = self.builder.append_basic_block(name="next")
|
||
self.builder.cbranch(is_null, next_block, not_null_block)
|
||
self.builder.position_at_end(not_null_block)
|
||
if ClassName and self._has_function(f'{ClassName}.__del__'):
|
||
cast_ptr: ir.Value
|
||
if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8:
|
||
cast_ptr = self.builder.bitcast(ptr, ir.PointerType(self.structs[ClassName]), name=f"cast_{ClassName}")
|
||
else:
|
||
cast_ptr = ptr
|
||
if cast_ptr not in deleted_objects:
|
||
self.builder.call(self._get_function(f'{ClassName}.__del__'), [cast_ptr], name=f"call_{ClassName}.__del__")
|
||
deleted_objects.add(cast_ptr)
|
||
raw: ir.Value
|
||
if ptr_type == 'struct':
|
||
raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="local_free_cast")
|
||
elif ptr_type == 'i8':
|
||
raw = ptr
|
||
else:
|
||
continue
|
||
free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
|
||
free_func: ir.Function = self._get_or_declare_func('free', free_type)
|
||
self.builder.call(free_func, [raw])
|
||
if ptr_type == 'i8':
|
||
self.builder.branch(next_block)
|
||
self.builder.position_at_end(next_block)
|
||
self._local_heap_ptrs = []
|
||
|
||
def _RegisterTempPtr(self, val: ir.Value) -> None:
|
||
if isinstance(val.type, ir.PointerType):
|
||
pointee: ir.Type = val.type.pointee
|
||
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
for CN, ST in self.structs.items():
|
||
if pointee == ST:
|
||
self._temp_struct_ptrs.append(val)
|
||
return
|
||
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||
self._temp_struct_ptrs.append(val)
|
||
|
||
def _UnregisterTempPtr(self, val: ir.Value) -> None:
|
||
self._temp_struct_ptrs = [t for t in self._temp_struct_ptrs if t is not val]
|
||
|
||
def _EmitTempFrees(self) -> None:
|
||
if not self._temp_struct_ptrs or not self.builder:
|
||
return
|
||
for ptr in self._temp_struct_ptrs:
|
||
raw: ir.Value
|
||
if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="free_cast")
|
||
elif isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8:
|
||
raw = ptr
|
||
else:
|
||
continue
|
||
free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
|
||
free_func: ir.Function = self._get_or_declare_func('free', free_type)
|
||
self.builder.call(free_func, [raw])
|
||
self._temp_struct_ptrs = []
|
||
|
||
def _ZeroConst(self, typ: ir.Type) -> ir.Constant:
|
||
if isinstance(typ, ir.PointerType):
|
||
return ir.Constant(typ, None)
|
||
if isinstance(typ, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
return ir.Constant(typ, None)
|
||
if isinstance(typ, ir.ArrayType):
|
||
return ir.Constant(typ, None)
|
||
return ir.Constant(typ, 0)
|
||
|
||
# ============================================================
|
||
# 隐式转换公共判据:None(空值常量)/ 指针层级 / 基本类型 / 无损
|
||
#
|
||
# 语义唯一来源与 ViperC 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。
|
||
# 收敛于此,供赋值 / 返回 / 实参 / 数组元素等所有 coerce 入口复用,避免
|
||
# 各处各自演化出相反结论。
|
||
# ============================================================
|
||
|
||
@staticmethod
|
||
def _IsNoneValue(val: Any, node: Any = None) -> bool:
|
||
"""None 判据:True=空值常量(None),False=否。
|
||
|
||
None 无语义类型信息,**允许隐式转换为任意类型**:指针目标→空指针,
|
||
其余目标→目标类型零值(整数 0 / 浮点 0.0 / 聚合 zeroinitializer)。故
|
||
「指针↔整数」「指针↔非指针值」「指针↔指针(非基本/有损/层数不同)」
|
||
这类针对**真实类型值**的隐式转换禁令都要对 None 短路放行。
|
||
|
||
两条判据(任一命中):
|
||
① AST 层:node 是 Constant 且 value is None;
|
||
② 值层(主判据):已生成好的 **空指针常量**(ir.Constant 且 constant is None
|
||
且类型为指针)。整数常量 0 的类型是整数,不会误命中。
|
||
"""
|
||
if node is not None and type(node).__name__ == 'Constant' and getattr(node, 'value', 1) is None:
|
||
return True
|
||
return (isinstance(val, ir.Constant) and val.constant is None
|
||
and isinstance(val.type, ir.PointerType))
|
||
|
||
@staticmethod
|
||
def _PtrLevel(typ: Any) -> int:
|
||
"""指针层级:PointerType 嵌套层数(0=非指针)。"""
|
||
n: int = 0
|
||
while isinstance(typ, ir.PointerType):
|
||
n += 1
|
||
typ = typ.pointee
|
||
return n
|
||
|
||
@staticmethod
|
||
def _IsGenericPtrType(typ: Any) -> bool:
|
||
"""通用指针(CPtr / void*):指针链末端为 i8(i8* / i8** / ... 均算)。"""
|
||
if not isinstance(typ, ir.PointerType):
|
||
return False
|
||
p: Any = typ.pointee
|
||
while isinstance(p, ir.PointerType):
|
||
p = p.pointee
|
||
return isinstance(p, ir.IntType) and p.width == 8
|
||
|
||
@staticmethod
|
||
def _IsBasicScalarType(typ: Any) -> bool:
|
||
"""基本类型 = 整数/浮点(含布尔 i1);结构体/类/联合/数组/指针一律非基本。"""
|
||
return isinstance(typ, (ir.IntType, ir.FloatType, ir.DoubleType))
|
||
|
||
@staticmethod
|
||
def _ScalarConvertLossless(Src: Any, Dst: Any) -> bool:
|
||
"""基本标量「无损」判定(与 ViperC `_scalar_convert_reason` 同口径,逐条照搬):
|
||
- 整数 ↔ 整数:允许 ⇔ **目标位宽 ≥ 源位宽**(低→高无损;高→低是**有损窄化**,禁);
|
||
- 浮点 ↔ 浮点:**所有方向都允许**;
|
||
- 整数 → 浮点:允许 ⇔ **目标浮点位宽 ≥ 源整数位宽**(否则有损 → 禁);
|
||
- 浮点 → 整数:**不允许**(有损 → 禁)。
|
||
"""
|
||
def _FloatBits(x: Any) -> int:
|
||
if isinstance(x, ir.DoubleType):
|
||
return 64
|
||
if isinstance(x, ir.FloatType):
|
||
return 32
|
||
return 0
|
||
s_int: bool = isinstance(Src, ir.IntType)
|
||
d_int: bool = isinstance(Dst, ir.IntType)
|
||
s_flt: int = _FloatBits(Src)
|
||
d_flt: int = _FloatBits(Dst)
|
||
# 整数 ↔ 整数:低→高无损放行;高→低有损窄化,禁
|
||
if s_int and d_int:
|
||
return Dst.width >= Src.width
|
||
# 浮点 ↔ 浮点:允许
|
||
if s_flt and d_flt:
|
||
return True
|
||
# 整数 → 浮点:目标浮点位宽 ≥ 源整数位宽才无损
|
||
if s_int and d_flt:
|
||
return d_flt >= Src.width
|
||
# 浮点 → 整数:有损,禁止
|
||
if s_flt and d_int:
|
||
return False
|
||
return False
|
||
|
||
def _ClassNameOfPointee(self, Pointee: Any) -> str | None:
|
||
"""把 LLVM 被指类型映射到已注册类名(复用本仓既有 find_struct_by_pointee;
|
||
身份比较失败时按短名(去 sha1 前缀)回退匹配 Gen.structs,与 HandlesAssign
|
||
既有回退同口径)。非结构体 / 未注册 → None。不新造表。
|
||
"""
|
||
if Pointee is None:
|
||
return None
|
||
Found: Any = self.find_struct_by_pointee(Pointee)
|
||
if Found:
|
||
return Found[0]
|
||
if isinstance(Pointee, ir.IdentifiedStructType):
|
||
PShort: str = self._extract_short_name(Pointee.name)
|
||
if PShort:
|
||
for CN, ST in self.structs.items():
|
||
if isinstance(ST, ir.IdentifiedStructType) and \
|
||
self._extract_short_name(ST.name) == PShort:
|
||
return CN
|
||
return None
|
||
|
||
def _IsInheritanceUpcastPointee(self, SrcPointee: Any, DstPointee: Any) -> bool:
|
||
"""继承上转判据:SrcPointee → DstPointee 是否为「派生类 → 基类」(含多级)。
|
||
|
||
复用本仓既有继承判据:_ClassNameOfPointee(→ find_struct_by_pointee)取
|
||
类名,Gen._get_mro_chain 取 MRO 线性化(含自身,父类在后)。目标类名落在
|
||
源类名 MRO 链「自身之后」⇒ 允许上转(Derived* → Base*,C++/GCC 同口径);
|
||
下转(Base* → Derived*)与无关类型(Foo* ↔ Bar*)不命中 ⇒ False。
|
||
"""
|
||
SrcName: str | None = self._ClassNameOfPointee(SrcPointee)
|
||
if not SrcName:
|
||
return False
|
||
DstName: str | None = self._ClassNameOfPointee(DstPointee)
|
||
if not DstName:
|
||
return False
|
||
SrcShort: str = self._extract_short_name(SrcName)
|
||
DstShort: str = self._extract_short_name(DstName)
|
||
if SrcShort == DstShort:
|
||
return False
|
||
Chain: list[str] = self._get_mro_chain(SrcName)
|
||
for Anc in Chain[1:]:
|
||
if self._extract_short_name(Anc) == DstShort:
|
||
return True
|
||
return False
|
||
|
||
def _IsExplicitPtrCastNode(self, Node: Any) -> bool:
|
||
"""值节点是否为「显式指针 cast」调用(用户规则:显式转换一律放行)。
|
||
|
||
形式(TPC 解析产物的共同点 = ast.Call 且 func 是**指针类型表达式**):
|
||
- `CPtr(x)`
|
||
- `rawptr[T](x)` / `CPtr[T](x)` / 裸名 `CPtr[T](x)`
|
||
- `(T*)x` / `(T*)(x)` / `T*(x)`(含 `**` 两层;ExprParser 统一产成
|
||
Call(func = `CPtr[T]` Subscript)
|
||
- 裸 `rawptr(x)`(≡ CPtr)
|
||
非 cast 调用(构造函数 / 普通函数,如 `MemBuddy(x)`、`list[X](...)`)不命中
|
||
⇒ 仍按隐式转换规则判定。只判 func 节点形状,不做类型解析(不与 Gen 耦合)。
|
||
|
||
`T1 | T2` 类型联合已禁止,故不再有 `Call(func=BinOp(BitOr))` 形态。
|
||
"""
|
||
if not isinstance(Node, ast.Call):
|
||
return False
|
||
Func: Any = Node.func
|
||
from lib.core.SymbolUtils import IsCPtrNode, IsPtrSubscriptNode
|
||
if IsCPtrNode(Func) or IsPtrSubscriptNode(Func):
|
||
return True
|
||
if isinstance(Func, ast.Name) and Func.id == 'rawptr':
|
||
return True
|
||
return False
|
||
|
||
def _ClassifyPtrImplicitConvert(self, Src: Any, Dst: Any,
|
||
Node: Any = None) -> tuple[bool, str]:
|
||
"""隐式转换判据:返回 (Allowed, Reason)。
|
||
|
||
规则(用户决定,与 ViperC 同口径):
|
||
① 通用指针(CPtr/i8*)为通配:与任何 T* **双向**互转放行(须同级)。
|
||
② T* → U*(皆非通用):允许 ⇔ 被指类型 T → U 无损;非基本类型
|
||
(结构体/类/联合)一律不允许 ⇒ Foo*→Bar* 禁止;**例外**有二:
|
||
继承上转 Derived* → Base*(目标被指类型是源被指类型的基类,含多级)
|
||
——见 _IsInheritanceUpcastPointee;数组退让 [N]T* → T*(目标被指类型
|
||
是源被指数组的元素类型,单向)——见下。下转 Base* → Derived*、反向
|
||
T* → [N]T* 仍禁止。
|
||
③ 非基本类型 T 与 T* 之间的隐式转换(Foo ↔ Foo*)一律禁止(层数不同);
|
||
**唯一例外**是「构造物化」——该豁免需 AST 层判据,故在 coerce 调用点
|
||
由 _CtorCallStructType 单独放行(本判据只给出「禁」的结论)。
|
||
④ 完全相同类型不算转换 ⇒ 放行。
|
||
⑤ **显式 cast 一律放行**(用户规则「显式转换都可以」):值侧节点本身是写明的
|
||
指针转换调用(CPtr(x) / rawptr[T](x) / (T*)x ...,见
|
||
_IsExplicitPtrCastNode)⇒ 目标类型即程序员指定的转换目标,调用方按
|
||
bitcast 重解释,不做层数/被指类型判定。
|
||
Reason 供报文说明「哪一级不允许 + 原因(非基本类型/有损/层数不同)」。
|
||
"""
|
||
# ⑤ 显式 cast:先于一切判据放行(Node 缺省 None ⇒ 不适用,等价既有行为)
|
||
if self._IsExplicitPtrCastNode(Node):
|
||
return (True, '显式 cast(按 bitcast 重解释)')
|
||
if Src == Dst:
|
||
return (True, '同类型(不构成转换)')
|
||
s_ptr: bool = isinstance(Src, ir.PointerType)
|
||
d_ptr: bool = isinstance(Dst, ir.PointerType)
|
||
if s_ptr and d_ptr:
|
||
if MemoryOpsMixin._PtrLevel(Src) != MemoryOpsMixin._PtrLevel(Dst):
|
||
return (False, '层数不同')
|
||
if MemoryOpsMixin._IsGenericPtrType(Src) or MemoryOpsMixin._IsGenericPtrType(Dst):
|
||
return (True, '通用指针通配')
|
||
sp: Any = Src.pointee
|
||
dp: Any = Dst.pointee
|
||
if sp == dp:
|
||
return (True, '被指类型相同')
|
||
if MemoryOpsMixin._IsBasicScalarType(sp) and MemoryOpsMixin._IsBasicScalarType(dp):
|
||
if MemoryOpsMixin._ScalarConvertLossless(sp, dp):
|
||
return (True, '被指类型无损')
|
||
return (False, '被指类型有损')
|
||
# 豁免:Derived* → Base* 继承上转(目标被指类型是源被指类型的基类,
|
||
# 含多级)允许(C++/GCC 同口径);下转/无关类型不命中,仍禁止。
|
||
if self._IsInheritanceUpcastPointee(sp, dp):
|
||
return (True, '继承上转(派生类指针 → 基类指针)')
|
||
# 豁免:数组退让(单向)——源的被指类型是定长数组 [N]T、目标的被指类型
|
||
# 恰为该数组的**元素类型** T ⇒ 允许(数组指针 → 元素指针,C/C++ 同口径)。
|
||
# 多维数组按「最外层退让一层」自然处理:[N][M]T* → [M]T* 命中(元素即
|
||
# [M]T),[N][M]T* → T* 不命中(元素是 [M]T ≠ T)。反向 T* → [N]T* 因
|
||
# 源的被指类型非数组而不命中,仍按下方非基本类型禁止。元素类型同一性
|
||
# 复用本处既有的 `==` 口径(与 sp == dp / 数组元素比较一致)。
|
||
if isinstance(sp, ir.ArrayType) and sp.element == dp:
|
||
return (True, '数组退让(数组指针 → 元素指针)')
|
||
return (False, '非基本类型(结构体/类/联合)')
|
||
if not s_ptr and not d_ptr:
|
||
return (True, '非指针间(本判据不裁决)')
|
||
# 一方指针、一方非指针:基本标量走既有「指针隐式转整数」禁令;非基本
|
||
# 类型即规则③(T ↔ T* 隐式互转)。
|
||
other: Any = Dst if s_ptr else Src
|
||
if MemoryOpsMixin._IsBasicScalarType(other):
|
||
return (False, '指针与标量(须显式 cast)')
|
||
return (False, '非基本类型(结构体/类/联合)')
|
||
|
||
@staticmethod
|
||
def _ValueNodeOf(Node: Any = None) -> Any:
|
||
"""把「外层语句节点」下沉到其「值子表达式节点」(与 ViperC _value_node_of 同口径)。
|
||
|
||
Assign/AnnAssign/AugAssign/Expr/Return 取 .value;非语句节点原样返回。
|
||
规则③的「构造物化」豁免须看到**真正被转换的那个右值表达式**(如 `p: Point = Point()`
|
||
里的 `Point()`),而 coerce 入口常收到外层语句节点,故统一在此下沉。
|
||
"""
|
||
if Node is None:
|
||
return None
|
||
if isinstance(Node, ast.Assign):
|
||
return Node.value
|
||
if isinstance(Node, ast.AnnAssign):
|
||
return Node.value
|
||
if isinstance(Node, ast.AugAssign):
|
||
return Node.value
|
||
if isinstance(Node, ast.Expr):
|
||
return Node.value
|
||
if isinstance(Node, ast.Return):
|
||
return Node.value
|
||
return Node
|
||
|
||
@staticmethod
|
||
def _IsFrameStackAddr(Node: Any, Gen: Any, TargetType: Any = None) -> bool:
|
||
"""AST 判据:Node 是否交出「当前帧栈上存储的地址」。
|
||
|
||
两种形态(与 ViperC fail_if_stack_addr_escape 同口径):
|
||
(a) `&local`:UnaryOp(AddrOf, Name),且该 Name 是本帧局部 alloca(含形参);
|
||
(b) 数组退让:裸本帧**数组**名出现在**指针**目标位置(如 `return arr` with `-> char*`),
|
||
交出的是首元素地址,同属栈地址。
|
||
模块级全局在静态存储、函数在代码段,取址可以逃逸,故排除。裸聚合名 + 同型聚合目标
|
||
是按值拷贝,不是逃逸,故不在 (b) 判据内。
|
||
"""
|
||
Vars: Any = getattr(Gen, 'variables', None)
|
||
if isinstance(Node, ast.UnaryOp) and isinstance(Node.op, ast.AddrOf):
|
||
Inner: Any = Node.operand
|
||
if not isinstance(Inner, ast.Name) or Inner.id is None:
|
||
return False
|
||
Var: Any = Vars.get(Inner.id) if Vars is not None else None
|
||
if isinstance(Var, ir.AllocaInstr):
|
||
return True
|
||
Module: Any = getattr(Gen, 'module', None)
|
||
if Module is not None and Inner.id in getattr(Module, 'globals', {}):
|
||
return False
|
||
# 全局/函数之外的已知名(如未登记的形参)保守判为栈地址
|
||
return True
|
||
if isinstance(Node, ast.Name) and Node.id is not None and isinstance(TargetType, ir.PointerType):
|
||
Var2: Any = Vars.get(Node.id) if Vars is not None else None
|
||
if (isinstance(Var2, ir.AllocaInstr) and isinstance(Var2.type, ir.PointerType)
|
||
and isinstance(Var2.type.pointee, ir.ArrayType)):
|
||
return True
|
||
return False
|
||
|
||
@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。
|
||
|
||
规则③(非基本类型值 ↔ 指针隐式转换一律禁止)的**唯一例外**是「构造物化」:
|
||
右值来自该类型的构造调用时放行(构造语义,非类型转换)。判据(见
|
||
HandlesExprCall._HandleCallLlvm 的分派口径):
|
||
- callee 裸名/属性名命中已注册聚合(`Gen.structs`)→ 类构造(`Point()`);
|
||
- callee 是 `Qual.Variant(...)`(REnum 变体构造,如 `LLVMType.Struct(...)`)
|
||
—— 变体短名不是聚合,构造出的聚合类型挂在**限定名 Qual**(REnum 类)下。
|
||
REnum 变体构造返回 **boxed 指针**(`Gen._allocaEntry(RenumType)`),其
|
||
pointee 恰是该聚合 —— 正是「构造物化」要放行的形态(boxed 指针 → 值槽的
|
||
整对象拷贝,语义即 load + store)。
|
||
- 泛型类模板特化(Box(42))需类型实参推断,静态取不到类型 → 保守返回 None。
|
||
|
||
`Gen.structs` 的键在跨模块命名隔离下可能是 `sha1.ClassName` 全名,且裸名键
|
||
可能另有一份 opaque 占位条目(两者不是同一 IR 类型)——故不按裸名直查,而是
|
||
按「键等于候选名,或以 `.<候选名>` 结尾」扫描,并要求取到的聚合与 TargetType
|
||
同一(对象同一或 IR 名相同)。这样无论构造走的是哪份条目、无论结果走
|
||
`_HandleREnumConstructLlvm` 还是 stub 侧同签名的变体工厂函数,都能正确放行。
|
||
"""
|
||
N: Any = MemoryOpsMixin._ValueNodeOf(Node)
|
||
if not isinstance(N, ast.Call):
|
||
return None
|
||
Structs: Any = getattr(self, 'structs', {})
|
||
for Name in MemoryOpsMixin._CtorCalleeNames(getattr(N, 'func', None)):
|
||
if not Name:
|
||
continue
|
||
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:
|
||
if align is None:
|
||
align = self._get_align(typ)
|
||
instr: ir.AllocaInstr
|
||
if size is not None:
|
||
instr = self.builder.alloca(typ, size=size, name=name)
|
||
else:
|
||
instr = self.builder.alloca(typ, name=name)
|
||
if align:
|
||
instr.align = align
|
||
return instr
|
||
|
||
def _allocaEntry(self, typ: ir.Type, name: str = '', align: int | None = None) -> ir.AllocaInstr:
|
||
if align is None:
|
||
align = self._get_align(typ)
|
||
saved_block: ir.Block = self.builder.block
|
||
entry_block: ir.Block = self.func.entry_basic_block
|
||
instr: ir.AllocaInstr
|
||
if entry_block.instructions:
|
||
first_instr: ir.Instruction = entry_block.instructions[0]
|
||
self.builder.position_before(first_instr)
|
||
instr = self.builder.alloca(typ, name=name)
|
||
if align:
|
||
instr.align = align
|
||
self.builder.position_at_end(saved_block)
|
||
else:
|
||
self.builder.position_at_start(entry_block)
|
||
instr = self.builder.alloca(typ, name=name)
|
||
if align:
|
||
instr.align = align
|
||
self.builder.position_at_end(saved_block)
|
||
return instr
|
||
|
||
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)
|
||
# volatile 分支直接构造 LoadInstr,绕过了 builder.load 的类型守卫,
|
||
# 故此处先行判定(否则 llvmlite 在 LoadInstr.__init__ 里访问
|
||
# ptr.type.is_opaque 抛无位置信息的裸 AttributeError)。
|
||
from lib.core.LLVMCG.BaseGen import RequirePointerOperand
|
||
RequirePointerOperand(ptr, 'load volatile', self, f"load 名='{name}'")
|
||
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)
|
||
# 同 _EmitLoad:绕过 builder.store 的守卫,先判定指针操作数
|
||
# (非指针 ptr 会静默生成非法 IR 文本,而不是报错)。
|
||
from lib.core.LLVMCG.BaseGen import RequirePointerOperand
|
||
RequirePointerOperand(ptr, 'store volatile', self,
|
||
f"store 值类型={getattr(val, 'type', None)}")
|
||
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 isinstance(ptr.type, ir.PointerType):
|
||
align = self._get_align(ptr.type.pointee)
|
||
else:
|
||
align = 0
|
||
# volatile=None ⇒ 按登记名自动判定(变量自身/所指物/字段,见 _IsPtrVolatile);
|
||
# 显式传入 True/False 时以调用方为准。判定必须先于下方 bitcast(bitcast
|
||
# 结果的名字不再是变量名,会丢登记名)。
|
||
if volatile is None:
|
||
volatile = self._IsPtrVolatile(ptr)
|
||
if isinstance(ptr.type, ir.PointerType):
|
||
pointee_type: ir.Type = ptr.type.pointee
|
||
if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths:
|
||
width: int = self.typedef_int_widths[pointee_type.name]
|
||
if width > 0:
|
||
actual_type: ir.IntType = ir.IntType(width)
|
||
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load")
|
||
return self._EmitLoad(bitcast_ptr, name=name, align=align, volatile=volatile)
|
||
return self._EmitLoad(ptr, name=name, align=align, volatile=volatile)
|
||
|
||
def _Load_var(self, name: str) -> ir.Value | None:
|
||
if name in self._direct_values:
|
||
return self._direct_values[name]
|
||
if name in self.variables and self.variables[name] is not None:
|
||
VarPtr: ir.Value = self.variables[name]
|
||
if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
return VarPtr
|
||
if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
return VarPtr
|
||
return self._load(VarPtr, name=name)
|
||
if name in self.global_vars and name in self.module.globals:
|
||
GVar: ir.GlobalVariable = self.module.globals[name]
|
||
self.variables[name] = GVar
|
||
if isinstance(GVar, ir.Function):
|
||
return GVar
|
||
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
return GVar
|
||
return self._load(GVar, name=name)
|
||
if name in self.module.globals:
|
||
GVar = self.module.globals[name]
|
||
self.variables[name] = GVar
|
||
if isinstance(GVar, ir.Function):
|
||
return GVar
|
||
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
return GVar
|
||
return self._load(GVar, name=name)
|
||
return None
|
||
|
||
def _CoerceIntToI1(self, val: ir.Value, name: str = "bool_norm") -> ir.Value:
|
||
"""整数 → **i1** 目标的真值归一化(C 6.3.1.2 `_Bool` 转换语义):`x != 0` → 1/0。
|
||
|
||
判据与三处真值化上下文**同一**:`bool(x)` 内建(HandlesExprBuiltin.py:74/76)、
|
||
条件 `if/while`(HandlesIf.py:196)、`and/or` 操作数(HandlesExprOps.py:122-127)。
|
||
此前「落存储」(本文件 `_coerce_value`、HandlesAnnAssign._StoreWithCoerce)与
|
||
「显式强转」(HandlesExprCall._HandleTypeCastLlvm)对 i1 目标都走 `trunc`
|
||
(取最低位)⇒ 运行时值 2 得 0 → false,与 `if 2:` 的真值结论相反
|
||
(静默语义分裂);值 3 得 true 而同值域下 `!= 0` 亦为 true,故只在偶数非零时暴露。
|
||
非整数 / 已是 i1 原样返回。
|
||
"""
|
||
if not isinstance(val.type, ir.IntType) or val.type.width == 1:
|
||
return val
|
||
return self.builder.icmp_signed('!=', val, self._ZeroConst(val.type), name=name)
|
||
|
||
def _coerce_value(self, val: ir.Value, target_type: ir.Type, Node: Any = None) -> ir.Value | None:
|
||
if val.type == target_type:
|
||
return val
|
||
# None(空值常量)允许隐式转换为任意类型:指针目标→空指针,其余目标→
|
||
# 目标类型零值(整数 0 / 浮点 0.0 / 聚合 zeroinitializer)。收敛到
|
||
# _IsNoneValue 单一判据,任何「指针↔整数 / 指针↔非指针值」禁令都不拦 None。
|
||
if self._IsNoneValue(val, Node):
|
||
if isinstance(target_type, ir.PointerType):
|
||
return ir.Constant(target_type, None)
|
||
return self._ZeroConst(target_type)
|
||
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
|
||
if isinstance(val.type, ir.IntType):
|
||
return val
|
||
# 聚合目标(数组/结构体)不接受标量(整数/浮点)初始化或赋值:标量→聚合无
|
||
# 转换语义,旧行为让本方法返回 None,_store 再抛 TypeError,而该 TypeError 在
|
||
# 非 strict 模式被静默吞掉 → 聚合完全未初始化(历史现场:`l: [10]char = 0`)。
|
||
# 直接 fail-fast(FailFastError 不被 TypeError 捕获,故能穿透调用点的 try)并
|
||
# 提示正确写法。typedef 伪整数已在上方短路,不受影响。
|
||
if (isinstance(target_type, (ir.ArrayType, ir.LiteralStructType, ir.IdentifiedStructType))
|
||
and isinstance(val.type, (ir.IntType, ir.FloatType, ir.DoubleType))):
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
raise FailFastError(
|
||
f"标量(整数/浮点)不能初始化或赋值给聚合类型 {target_type}:"
|
||
f"旧行为只写首元素字节或整体不初始化,聚合其余部分保持未初始化;"
|
||
f"聚合零初始化请写 None,填充内容请用列表字面量 [...] 或逐元素赋值"
|
||
f"{self._DescribeValueNode(Node)}"
|
||
)
|
||
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.IntType):
|
||
if target_type.width < val.type.width:
|
||
# 目标 i1:真值归一化(与 bool(x)/if 条件同一判据),不是 trunc 取最低位
|
||
if target_type.width == 1:
|
||
return self._CoerceIntToI1(val, name="bool_norm_store")
|
||
# 隐式窄化**禁止**(规则 2026-10-10 收紧,取代"整数↔整数 所有方向都允许"):
|
||
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。
|
||
# 唯一豁免:源是**编译期常量且值落进目标位宽**(无信息损失)。
|
||
if isinstance(val, ir.Constant) and isinstance(val.constant, int):
|
||
Lo: int = -(1 << (target_type.width - 1))
|
||
Hi: int = (1 << target_type.width) - 1
|
||
if Lo <= val.constant <= Hi:
|
||
return self.builder.trunc(val, target_type, name="trunc_const_store")
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
raise FailFastError(
|
||
f"隐式窄化禁止:{val.type.width} 位整数不能隐式赋给 {target_type.width} 位类型"
|
||
f"(会丢高位);请显式转换(如 int32(x) / CInt32T(x))或把目标类型加宽"
|
||
f"{self._DescribeValueNode(Node)}"
|
||
)
|
||
elif val.type.width == 1:
|
||
# i1 是布尔值(来自 icmp/fcmp/__contains__ 归一化),无符号语义,
|
||
# 必须用 zext 而非 sext,否则 i1 的 1 会被符号扩展为 -1
|
||
return self.builder.zext(val, target_type, name="zext_bool_store")
|
||
else:
|
||
return self.builder.sext(val, target_type, name="sext_store")
|
||
if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.PointerType):
|
||
# 新增指针隐式转换规则①/②:通用指针通配双向放行;非通用 T*→U* 仅在
|
||
# 被指类型无损时放行,非基本类型/有损/层数不同一律 fail-fast。
|
||
Allowed: bool
|
||
Reason: str
|
||
Allowed, Reason = self._ClassifyPtrImplicitConvert(val.type, target_type, Node)
|
||
if not Allowed:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
raise FailFastError(
|
||
f"指针隐式转换已禁止({Reason}):不能把指针类型 {val.type} 隐式转为 {target_type};"
|
||
f"如需转换请显式 cast(如 CPtr(...))"
|
||
f"{self._DescribeValueNode(Node)}"
|
||
)
|
||
return self.builder.bitcast(val, target_type, name="ptr_bitcast_store")
|
||
if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.IntType):
|
||
if val.type.width < 64:
|
||
val = self.builder.zext(val, ir.IntType(64), name="zext_ptr_store")
|
||
return self.builder.inttoptr(val, target_type, name="int_to_ptr_store")
|
||
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.PointerType):
|
||
# 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。
|
||
# 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
|
||
# 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
|
||
# None(空指针常量)已在上方短路为目标零值,不会落到这里。
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
raise FailFastError(
|
||
f"指针隐式转整数已禁止:不能把指针类型 {val.type} 赋给整数类型 {target_type};"
|
||
f"如需转换请显式 cast(如 CUInt64T(p) / CInt(p))"
|
||
f"{self._DescribeValueNode(Node)}"
|
||
)
|
||
if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType):
|
||
return self.builder.fptrunc(val, target_type, name="fptrunc_store")
|
||
if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType):
|
||
return self.builder.fpext(val, target_type, name="fpext_store")
|
||
if isinstance(target_type, ir.IntType) and isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||
return self.builder.fptosi(val, target_type, name="fptosi_store")
|
||
if isinstance(target_type, (ir.FloatType, ir.DoubleType)) and isinstance(val.type, ir.IntType):
|
||
return self.builder.sitofp(val, target_type, name="sitofp_store")
|
||
if isinstance(target_type, ir.ArrayType) and isinstance(val.type, ir.ArrayType):
|
||
if target_type.count == val.type.count and target_type.element == val.type.element:
|
||
return val
|
||
if (isinstance(target_type.element, ir.IntType) and target_type.element.width == 8
|
||
and isinstance(val.type.element, ir.IntType) and val.type.element.width == 8):
|
||
if target_type.count > val.type.count:
|
||
padded: list[ir.Constant] = list(val.constant) if hasattr(val, 'constant') else []
|
||
if padded and len(padded) == val.type.count:
|
||
padded.extend([ir.Constant(ir.IntType(8), 0)] * (target_type.count - val.type.count))
|
||
return ir.Constant(target_type, padded)
|
||
elif target_type.count < val.type.count:
|
||
trimmed: list[ir.Constant] = list(val.constant) if hasattr(val, 'constant') else []
|
||
if trimmed and len(trimmed) == val.type.count:
|
||
return ir.Constant(target_type, trimmed[:target_type.count])
|
||
if isinstance(val.type, ir.PointerType) and not isinstance(target_type, ir.PointerType):
|
||
# 规则③:非基本类型 T 与 T* 之间的隐式转换(Foo ↔ Foo*)一律禁止——
|
||
# 指针→非指针值的隐式 load(结构体/数组按值回读)属类型信息缺失,
|
||
# 须显式解引用(Deref(p) / *p)或显式强转。基本标量目标已在上方
|
||
# 「指针隐式转整数」分支 fail-fast,此处只兜非基本类型。
|
||
# **唯一例外=「构造物化」**:右值来自该类型的构造调用时放行,如
|
||
# `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
|
||
Allowed3, Reason3 = self._ClassifyPtrImplicitConvert(val.type, target_type)
|
||
if not Allowed3:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
raise FailFastError(
|
||
f"非基本类型的值/指针隐式转换已禁止(构造物化除外,{Reason3}):"
|
||
f"不能把指针类型 {val.type} 隐式转为值类型 {target_type};"
|
||
f"如需解引用请显式 Deref(p) / *p,或显式强转"
|
||
f"{self._DescribeValueNode(Node)}"
|
||
)
|
||
if val.type.pointee == target_type:
|
||
return self.builder.load(val, name="Load_struct_for_store")
|
||
if isinstance(val.type.pointee, ir.IdentifiedStructType) and isinstance(target_type, ir.IdentifiedStructType):
|
||
if val.type.pointee.name == target_type.name:
|
||
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,
|
||
volatile: bool | None = None, Node: Any = None) -> ir.StoreInstr:
|
||
if align is None:
|
||
if isinstance(ptr.type, ir.PointerType):
|
||
align = self._get_align(ptr.type.pointee)
|
||
else:
|
||
align = 0
|
||
# volatile=None ⇒ 按登记名自动判定(见 _IsPtrVolatile)。判定先于下方任何
|
||
# ptr 重建(null_struct_ptr_cast 等),否则会丢登记名。
|
||
if volatile is None:
|
||
volatile = self._IsPtrVolatile(ptr)
|
||
if isinstance(ptr.type, ir.PointerType):
|
||
target_type: ir.Type = ptr.type.pointee
|
||
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
|
||
if isinstance(val.type, ir.IntType):
|
||
width: int = self.typedef_int_widths[target_type.name]
|
||
if width > 0:
|
||
actual_type: ir.IntType = ir.IntType(width)
|
||
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast")
|
||
if val.type.width != actual_type.width:
|
||
if val.type.width > actual_type.width:
|
||
val = self.builder.trunc(val, actual_type, name="trunc_typedef")
|
||
else:
|
||
val = self.builder.zext(val, actual_type, name="zext_typedef")
|
||
return self._EmitStore(val, bitcast_ptr, align=align, volatile=volatile)
|
||
if val.type != target_type:
|
||
if isinstance(val, ir.Constant) and val.constant is None:
|
||
if isinstance(target_type, ir.PointerType):
|
||
val = ir.Constant(target_type, None)
|
||
elif isinstance(target_type, ir.IdentifiedStructType):
|
||
val = ir.Constant(ir.PointerType(target_type), None)
|
||
ptr = self.builder.bitcast(ptr, ir.PointerType(ir.PointerType(target_type)), name="null_struct_ptr_cast")
|
||
elif isinstance(target_type, ir.IntType):
|
||
# null 指针常量赋给 int 字段 → 0(与 _coerce_value 一致,
|
||
# 防止 `store i32* null, i32*` 类型错乱 IR)
|
||
val = ir.Constant(target_type, 0)
|
||
else:
|
||
coerced: ir.Value | None = self._coerce_value(val, target_type, Node)
|
||
if coerced is not None:
|
||
val = coerced
|
||
else:
|
||
src_info: str = self._get_node_info()
|
||
raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}")
|
||
return self._EmitStore(val, ptr, align=align, volatile=volatile)
|
||
|
||
def _get_var_ptr(self, name: str) -> ir.Value | None:
|
||
if name in self._reg_values:
|
||
val: ir.Value = self._reg_values[name]
|
||
var: ir.AllocaInstr = self._alloca(val.type, name=name)
|
||
self._store(val, var)
|
||
self.variables[name] = var
|
||
del self._reg_values[name]
|
||
return var
|
||
if name in self.variables and self.variables[name] is not None:
|
||
return self.variables[name]
|
||
return None |