【a 编译器缺陷】7aa12263 修好 `_get_attr_ptr` 的 self 分支后,同族其余 GEP 点仍会在
"基址 pointee 是另一份 opaque 同名实例"时抛裸 TypeError(被 IR 边界守卫拦下,报文为
"GEP 的基址指向**未具化**的具名结构体")。根因是同一套收口逻辑被各点各写一份、且
**多数点根本没写**:真正需要的判据只有一条。
新增唯一判据源(HandlesExprAttr):
· `_ConcretePointeeFor(Gen, ClassName, Pointee)`:取该类的**已具化实例** ——
① 类键那份先 `_TryLoadStructFromStub` 补 body;② 否则 `Gen._FindConcreteTwin`
(同短名、槽数最多);③ 都取不到 ⇒ None(**不兜底**,交守卫 fail-fast 报同短名
实例清单 + TPC 发射点)。
· `_ConcretizeGepBase(Gen, Ptr, ClassName, Tag)`:pointee 非 opaque ⇒ 原样返回
(对既有正确路径零变化);opaque ⇒ bitcast 到已具化实例并**同时返回该实例**,
供 `_get_member_offset` 作 StructHint(偏移与 GEP 必须同源,与 a43513c9 同口径)。
接入点(全部改为调用上述判据源,逐点行为只在"原本必崩"的情形下变化):
· HandlesExprAttr._get_attr_ptr — self / 变量 / 属性链 / i8* / 下标 / 强转基座 六条分支
(self 分支已由 7aa12263 修,此处并入统一判据;下标分支此前**完全没有**收口);
· HandlesAnnAssign._HandleAttributeStoreLlvm — self / 变量 / 属性链 三条分支;
· HandlesBase._get_int_ptr — 属性分支;
· HandlesExprBuiltin(len() 的两个接收者 GEP:`obj.len` 与 list 特化字段);
· HandlesMatch — REnum 变体 match 的载荷 GEP(变体 struct 可能未 set_body)。
冒烟(临时脚本已删,鸭子类型 Gen + 真实方法):
· `_get_attr_ptr` self 分支 ⇒ 基址 `%"fef783da4d24330d.Keyword"*`(经 cast_self_Keyword)PASS;
· `_get_attr_ptr` i8* 分支 ⇒ 同上 PASS;
· `HandlesAnnAssign` self 分支 ⇒ GEP 基址为已具化实例 PASS;
· 回归:基址本就具化时必须**原样**(不得多出 bitcast)PASS。
另:`python -m compileall TransPyC/lib` 全绿。
495 lines
30 KiB
Python
495 lines
30 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING, Any
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Handles.HandlesBase import BaseHandle
|
||
import vast as ast
|
||
import llvmlite.ir as ir
|
||
|
||
|
||
class MatchHandle(BaseHandle):
|
||
@staticmethod
|
||
def _PatternCaptureNames(Pattern: ast.AST) -> list[str]:
|
||
"""模式中所有**捕获名**(递归 MatchOr/MatchClass/MatchSequence/MatchMapping)。
|
||
|
||
用于判定"该 case 是否必须登记绑定":有捕获却登记不上,case body 里的名字
|
||
就会变成"未定义名称",必须在**生成 body 之前**响亮失败,而不是留到取名时。
|
||
"""
|
||
Names: list[str] = []
|
||
if isinstance(Pattern, ast.MatchAs):
|
||
if Pattern.name:
|
||
Names.append(Pattern.name)
|
||
if Pattern.pattern is not None:
|
||
Names.extend(MatchHandle._PatternCaptureNames(Pattern.pattern))
|
||
elif isinstance(Pattern, ast.MatchStar):
|
||
if Pattern.name:
|
||
Names.append(Pattern.name)
|
||
elif isinstance(Pattern, (ast.MatchOr, ast.MatchSequence, ast.MatchClass, ast.MatchMapping)):
|
||
for p in (getattr(Pattern, 'patterns', None) or []):
|
||
Names.extend(MatchHandle._PatternCaptureNames(p))
|
||
for p in (getattr(Pattern, 'kwd_patterns', None) or []):
|
||
Names.extend(MatchHandle._PatternCaptureNames(p))
|
||
_rest: str | None = getattr(Pattern, 'rest', None)
|
||
if _rest:
|
||
Names.append(_rest)
|
||
return Names
|
||
|
||
def _HandleMatchLlvm(self, Node: ast.Match) -> None:
|
||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||
SubjectVal: ir.Value | None = self.HandleExprLlvm(Node.subject)
|
||
if not SubjectVal:
|
||
return
|
||
|
||
IsRenumMatch: bool = False
|
||
RenumName: str | None = None
|
||
SubjectPtr: ir.Value | None = None
|
||
if isinstance(Node.subject, ast.Name):
|
||
VarName: str = Node.subject.id
|
||
TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
|
||
if TypeInfo and TypeInfo.IsRenum:
|
||
IsRenumMatch = True
|
||
RenumName = TypeInfo.Name
|
||
SubjectPtr = Gen._Load_var(VarName)
|
||
|
||
if not IsRenumMatch:
|
||
for case in Node.cases:
|
||
if isinstance(case.pattern, ast.MatchClass):
|
||
cls_node: ast.expr = case.pattern.cls
|
||
VariantName: str | None = None
|
||
QualifiedName: str | None = None
|
||
if isinstance(cls_node, ast.Name):
|
||
VariantName = cls_node.id
|
||
elif isinstance(cls_node, ast.Attribute):
|
||
VariantName = cls_node.attr
|
||
# Extract enum name for qualified lookup to avoid collision
|
||
# with same-name factory functions (e.g. def Ptr vs LLVMType.Ptr)
|
||
if isinstance(cls_node.value, ast.Attribute):
|
||
QualifiedName = f"{cls_node.value.attr}.{VariantName}"
|
||
elif isinstance(cls_node.value, ast.Name):
|
||
QualifiedName = f"{cls_node.value.id}.{VariantName}"
|
||
if VariantName:
|
||
# Try qualified name first (e.g., "LLVMType.Ptr") to avoid
|
||
# collision with same-name functions (e.g., def Ptr(...))
|
||
SymInfo: "SymbolTable.SymbolInfo" = None
|
||
if QualifiedName:
|
||
SymInfo = self.Trans.SymbolTable.lookup(QualifiedName)
|
||
if not (SymInfo and SymInfo.IsEnumMember):
|
||
SymInfo = self.Trans.SymbolTable.lookup(VariantName)
|
||
if SymInfo and SymInfo.IsEnumMember and SymInfo.EnumName:
|
||
EnumName: str = SymInfo.EnumName
|
||
EnumInfo: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(EnumName)
|
||
if EnumInfo and EnumInfo.IsRenum:
|
||
IsRenumMatch = True
|
||
RenumName = EnumName
|
||
if SubjectPtr is None:
|
||
SubjectPtr = self.HandleExprLlvm(Node.subject)
|
||
break
|
||
|
||
if IsRenumMatch and SubjectPtr:
|
||
self._HandleRenumMatchLlvm(Node, RenumName, SubjectPtr)
|
||
return
|
||
|
||
if not isinstance(SubjectVal.type, ir.IntType):
|
||
try:
|
||
SubjectVal = Gen.builder.ptrtoint(SubjectVal, ir.IntType(64), name="match_subj")
|
||
SubjectVal = Gen.builder.trunc(SubjectVal, ir.IntType(32), name="match_subj_i32")
|
||
except Exception: # 回退:ptrtoint 失败时直接返回
|
||
return
|
||
SwitchIntType: ir.IntType = SubjectVal.type
|
||
DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default")
|
||
AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end")
|
||
CaseBBs: list[ir.Block] = []
|
||
CaseValues: list[ir.Value] = []
|
||
HasDefault: bool = False
|
||
HasNoBreak: list[bool] = []
|
||
for i, case in enumerate(Node.cases):
|
||
pattern: ast.pattern = case.pattern
|
||
if isinstance(pattern, ast.MatchValue):
|
||
Val: ir.Value | None = self.HandleExprLlvm(pattern.value)
|
||
if Val:
|
||
CaseVal: ir.Value
|
||
if isinstance(Val.type, ir.IntType):
|
||
if Val.type != SwitchIntType:
|
||
if Val.type.width > SwitchIntType.width:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
|
||
else:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant)
|
||
else:
|
||
CaseVal = Val
|
||
else:
|
||
try:
|
||
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}")
|
||
except Exception: # 回退:ptrtoint 失败时设默认值 0
|
||
CaseVal = ir.Constant(SwitchIntType, 0)
|
||
CaseValues.append(CaseVal)
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
elif isinstance(pattern, ast.MatchOr):
|
||
for j, SubPattern in enumerate(pattern.patterns):
|
||
if isinstance(SubPattern, ast.MatchValue):
|
||
Val: ir.Value | None = self.HandleExprLlvm(SubPattern.value)
|
||
if Val:
|
||
CaseVal: ir.Value
|
||
if isinstance(Val.type, ir.IntType):
|
||
if Val.type != SwitchIntType:
|
||
if Val.type.width > SwitchIntType.width:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
|
||
else:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant)
|
||
else:
|
||
CaseVal = Val
|
||
else:
|
||
try:
|
||
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}_{j}")
|
||
except Exception: # 回退:ptrtoint 失败时设默认值 0
|
||
CaseVal = ir.Constant(SwitchIntType, 0)
|
||
CaseValues.append(CaseVal)
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}_{j}"))
|
||
elif isinstance(pattern, ast.MatchSingleton):
|
||
if pattern.value is None:
|
||
CaseValues.append(ir.Constant(SwitchIntType, 0))
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
elif isinstance(pattern, ast.MatchClass):
|
||
# 兜底处理:REnum 检测失败但 case 是 MatchClass(REnum 变体模式)
|
||
# 尝试从变体名查找 tag 值,按整数值处理
|
||
cls_node_mc: ast.expr = pattern.cls
|
||
VariantName_mc: str | None = None
|
||
QualifiedName_mc: str | None = None
|
||
if isinstance(cls_node_mc, ast.Name):
|
||
VariantName_mc = cls_node_mc.id
|
||
elif isinstance(cls_node_mc, ast.Attribute):
|
||
VariantName_mc = cls_node_mc.attr
|
||
if isinstance(cls_node_mc.value, ast.Attribute):
|
||
QualifiedName_mc = f"{cls_node_mc.value.attr}.{VariantName_mc}"
|
||
elif isinstance(cls_node_mc.value, ast.Name):
|
||
QualifiedName_mc = f"{cls_node_mc.value.id}.{VariantName_mc}"
|
||
TagValue_mc: int | None = None
|
||
SymInfo_mc: "SymbolTable.SymbolInfo" = None
|
||
if QualifiedName_mc:
|
||
SymInfo_mc = self.Trans.SymbolTable.lookup(QualifiedName_mc)
|
||
if not (SymInfo_mc and SymInfo_mc.IsEnumMember):
|
||
SymInfo_mc = self.Trans.SymbolTable.lookup(VariantName_mc)
|
||
if SymInfo_mc and SymInfo_mc.IsEnumMember:
|
||
TagValue_mc = SymInfo_mc.value
|
||
if TagValue_mc is not None:
|
||
# 兜底分支**不登记捕获绑定**(它按整数 tag 处理,无变体载荷可 GEP)。
|
||
# 因此只要该 case 有捕获,就必须在此 fail-fast —— 否则 case body 里的
|
||
# 绑定名随后变成"未定义名称"(现场 VpiReader.vp:1521 pointee_cs),
|
||
# 真因(外层 REnum 判定未成立)被掩盖。报文直接列出判据供定位。
|
||
_caps_mc: list[str] = self._PatternCaptureNames(pattern)
|
||
if _caps_mc:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
_en_mc: str | None = getattr(SymInfo_mc, 'EnumName', None)
|
||
_sym_en: "SymbolTable.SymbolInfo" = (
|
||
self.Trans.SymbolTable.lookup(_en_mc) if _en_mc else None)
|
||
_sym_v: "SymbolTable.SymbolInfo" = (
|
||
self.Trans.SymbolTable.lookup(VariantName_mc) if VariantName_mc else None)
|
||
raise FailFastError(
|
||
f"REnum 变体模式 '{QualifiedName_mc or VariantName_mc}' 含捕获 {_caps_mc},"
|
||
f"但外层 REnum 匹配判定未成立(IsRenumMatch=False)⇒ 兜底分支不登记捕获绑定,"
|
||
f"case body 里这些名字必然成为'未定义名称'。判据:"
|
||
f"符号表['{QualifiedName_mc}'] => "
|
||
f"IsEnumMember={getattr(SymInfo_mc, 'IsEnumMember', None)} "
|
||
f"value={getattr(SymInfo_mc, 'value', None)} EnumName={_en_mc!r};"
|
||
f"符号表['{_en_mc}'] => IsRenum={getattr(_sym_en, 'IsRenum', None)} "
|
||
f"IsStruct={getattr(_sym_en, 'IsStruct', None)} "
|
||
f"IsEnum={getattr(_sym_en, 'IsEnum', None)};"
|
||
f"符号表['{VariantName_mc}'] => "
|
||
f"IsEnumMember={getattr(_sym_v, 'IsEnumMember', None)} "
|
||
f"EnumName={getattr(_sym_v, 'EnumName', None)!r}"
|
||
)
|
||
CaseValues.append(ir.Constant(SwitchIntType, TagValue_mc))
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{VariantName_mc}"))
|
||
else:
|
||
# 变体名未找到,跳过此 case(fast-fail 已在外层处理)
|
||
pass
|
||
def _HasNoBreak(stmts: list[ast.stmt]) -> bool:
|
||
for stmt in stmts:
|
||
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
|
||
if isinstance(stmt.value.func, ast.Attribute):
|
||
if (isinstance(stmt.value.func.value, ast.Name) and
|
||
stmt.value.func.value.id == 'c' and
|
||
stmt.value.func.attr == 'NoBreak'):
|
||
return True
|
||
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
|
||
if _HasNoBreak(stmt.body):
|
||
return True
|
||
if getattr(stmt, 'orelse', None):
|
||
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
|
||
return True
|
||
return False
|
||
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
|
||
if not HasDefault:
|
||
CaseBBs.append(DefaultBB)
|
||
SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
|
||
switch_instr: ir.SwitchInstr = Gen.builder.switch(SubjectVal, DefaultBB)
|
||
for val, bb in SwitchCases:
|
||
switch_instr.add_case(val, bb)
|
||
CaseIdx: int = 0
|
||
for i, case in enumerate(Node.cases):
|
||
pattern: ast.pattern = case.pattern
|
||
if isinstance(pattern, ast.MatchOr):
|
||
NumSubCases: int = len(pattern.patterns)
|
||
for j in range(NumSubCases):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, (ast.MatchValue, ast.MatchSingleton)):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchClass):
|
||
# 兜底处理:MatchClass 在非 REnum 路径的 case body 生成
|
||
# case 收集阶段已创建 CaseBB,此处生成 body
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
if not HasDefault:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
Gen.builder.position_at_start(AfterBB)
|
||
|
||
def _HandleRenumMatchLlvm(self, Node: ast.Match, RenumName: str, SubjectPtr: ir.Value) -> None:
|
||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||
if isinstance(SubjectPtr.type, ir.PointerType) and isinstance(SubjectPtr.type.pointee, ir.PointerType):
|
||
SubjectPtr = Gen._load(SubjectPtr, name="Load_match_subj")
|
||
# Bug fix: HandleExprLlvm 可能对 REnum 字段执行了 load,返回值而非指针。
|
||
# 情况1: SubjectPtr 不是指针类型(如 IntType)→ alloca REnum 结构体 + store
|
||
# 情况2: SubjectPtr 是指针但 pointee 不是结构体(如 i32* 指向 __tag)→ bitcast
|
||
NeedAlloca: bool = not isinstance(SubjectPtr.type, ir.PointerType)
|
||
NeedBitcast: bool = (isinstance(SubjectPtr.type, ir.PointerType) and
|
||
not isinstance(SubjectPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)))
|
||
if NeedAlloca or NeedBitcast:
|
||
RenumStructType: Any = Gen.structs.get(RenumName)
|
||
if RenumStructType is None:
|
||
# 跨模块编译时 REnum 结构体可能尚未在 Gen.structs 中注册
|
||
# (如 import llvmlite 后 LLVMType 仅在符号表,Gen.structs 无条目)。
|
||
# 使用 _get_or_create_struct 按需创建(可能为 opaque)。
|
||
RenumStructType = Gen._get_or_create_struct(RenumName)
|
||
if NeedAlloca:
|
||
AllocaPtr: ir.Value = Gen._allocaEntry(RenumStructType, name="match_subj_alloca")
|
||
CastedPtr: ir.Value = Gen.builder.bitcast(AllocaPtr, ir.PointerType(SubjectPtr.type), name="match_subj_cast")
|
||
Gen._store(SubjectPtr, CastedPtr)
|
||
SubjectPtr = AllocaPtr
|
||
else:
|
||
SubjectPtr = Gen.builder.bitcast(SubjectPtr, ir.PointerType(RenumStructType), name="match_subj_recast")
|
||
# REnum 布局为 { i32 __tag, <payload> },tag 在偏移 0。
|
||
# 使用 bitcast 到 i32* 替代 gep [0,0],兼容 opaque 结构体
|
||
# (跨模块编译时 REnum 结构体可能未 set_body,gep 会失败)。
|
||
tag_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, ir.PointerType(ir.IntType(32)), name="match_tag_ptr")
|
||
TagVal: ir.Value = Gen._load(tag_ptr, name="match_tag_val")
|
||
DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default")
|
||
AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end")
|
||
CaseBBs: list[ir.Block] = []
|
||
CaseValues: list[ir.Value] = []
|
||
CaseBindings: list[list[tuple[str, str, ir.Type, int]]] = []
|
||
HasDefault: bool = False
|
||
HasNoBreak: list[bool] = []
|
||
for i, case in enumerate(Node.cases):
|
||
pattern: ast.pattern = case.pattern
|
||
bindings: list[tuple[str, str, ir.Type, int]] = []
|
||
if isinstance(pattern, ast.MatchClass):
|
||
cls_node: ast.expr = pattern.cls
|
||
VariantName: str | None = None
|
||
QualifiedName: str | None = None
|
||
if isinstance(cls_node, ast.Name):
|
||
VariantName = cls_node.id
|
||
elif isinstance(cls_node, ast.Attribute):
|
||
VariantName = cls_node.attr
|
||
if isinstance(cls_node.value, ast.Attribute):
|
||
QualifiedName = f"{cls_node.value.attr}.{VariantName}"
|
||
elif isinstance(cls_node.value, ast.Name):
|
||
QualifiedName = f"{cls_node.value.id}.{VariantName}"
|
||
if VariantName:
|
||
TagValue: int | None = None
|
||
SymInfo: "SymbolTable.SymbolInfo" = None
|
||
if QualifiedName:
|
||
SymInfo = self.Trans.SymbolTable.lookup(QualifiedName)
|
||
if not (SymInfo and SymInfo.IsEnumMember):
|
||
SymInfo = self.Trans.SymbolTable.lookup(VariantName)
|
||
if SymInfo and SymInfo.IsEnumMember:
|
||
TagValue = SymInfo.value
|
||
if TagValue is not None:
|
||
CaseValues.append(ir.Constant(ir.IntType(32), TagValue))
|
||
CaseBB: ir.Block = Gen.func.append_basic_block(name=f"match.case_{VariantName}")
|
||
CaseBBs.append(CaseBB)
|
||
# 变体嵌套结构体按需补发:导入模块侧 stub 只声明主 REnum,
|
||
# 不含 `<REnum>_<Variant>` 声明(见 _EnsureRenumVariantStruct)。
|
||
NestedStructName: str = self._EnsureRenumVariantStruct(Gen, RenumName, VariantName)
|
||
members: list[tuple[str, ir.Type]] = Gen.class_members.get(NestedStructName, [])
|
||
payLoad_members: list[tuple[str, ir.Type]] = [(n, t) for n, t in members if n != '__tag']
|
||
# 模式捕获必须能全部绑定:绑不上就在此 fail-fast,绝不让 case body
|
||
# 里的绑定名留到后面变成"未定义名称"(现场 VpiReader.vp:1521 pointee_cs)。
|
||
_captures: list[str] = [p.name for p in pattern.patterns
|
||
if isinstance(p, ast.MatchAs) and p.name]
|
||
if _captures and len(payLoad_members) < len(_captures):
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
raise FailFastError(
|
||
f"REnum 变体模式 '{RenumName}.{VariantName}' 的载荷成员不足,"
|
||
f"无法绑定模式捕获 {_captures}:"
|
||
f"class_members['{NestedStructName}']={len(members)} 项"
|
||
f"(去 __tag 后载荷 {len(payLoad_members)} 项);"
|
||
f"structs 命中={'有' if NestedStructName in Gen.structs else '无'};"
|
||
f"同前缀 structs 键="
|
||
f"{[k for k in Gen.structs if k.startswith(RenumName + '_')][:8]}"
|
||
)
|
||
for j, sub_pat in enumerate(pattern.patterns):
|
||
if isinstance(sub_pat, ast.MatchAs) and sub_pat.name and j < len(payLoad_members):
|
||
bindings.append((sub_pat.name, payLoad_members[j][0], payLoad_members[j][1], j))
|
||
CaseBindings.append(bindings)
|
||
else:
|
||
CaseValues.append(ir.Constant(ir.IntType(32), 0))
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
CaseBindings.append([])
|
||
elif isinstance(pattern, ast.MatchValue):
|
||
Val: ir.Value | None = self.HandleExprLlvm(pattern.value)
|
||
if Val:
|
||
CaseVal: ir.Value
|
||
if isinstance(Val.type, ir.IntType):
|
||
CaseVal = Val
|
||
else:
|
||
try:
|
||
CaseVal = Gen.builder.ptrtoint(Val, ir.IntType(32), name=f"case_val_{i}")
|
||
except Exception: # 回退:ptrtoint 失败时设默认值 0
|
||
CaseVal = ir.Constant(ir.IntType(32), 0)
|
||
CaseValues.append(CaseVal)
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
CaseBindings.append([])
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
CaseBindings.append([])
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
CaseBindings.append([])
|
||
def _HasNoBreak(stmts: list[ast.stmt]) -> bool:
|
||
for stmt in stmts:
|
||
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
|
||
if isinstance(stmt.value.func, ast.Attribute):
|
||
if (isinstance(stmt.value.func.value, ast.Name) and
|
||
stmt.value.func.value.id == 'c' and
|
||
stmt.value.func.attr == 'NoBreak'):
|
||
return True
|
||
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
|
||
if _HasNoBreak(stmt.body):
|
||
return True
|
||
if getattr(stmt, 'orelse', None):
|
||
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
|
||
return True
|
||
return False
|
||
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
|
||
if not HasDefault:
|
||
CaseBBs.append(DefaultBB)
|
||
SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
|
||
switch_instr: ir.SwitchInstr = Gen.builder.switch(TagVal, DefaultBB)
|
||
for val, bb in SwitchCases:
|
||
switch_instr.add_case(val, bb)
|
||
CaseIdx: int = 0
|
||
for i, case in enumerate(Node.cases):
|
||
pattern: ast.pattern = case.pattern
|
||
if isinstance(pattern, ast.MatchClass):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
bindings: list[tuple[str, str, ir.Type, int]] = CaseBindings[CaseIdx] if CaseIdx < len(CaseBindings) else []
|
||
cls_node: ast.expr = pattern.cls
|
||
VariantName: str | None = None
|
||
if isinstance(cls_node, ast.Name):
|
||
VariantName = cls_node.id
|
||
elif isinstance(cls_node, ast.Attribute):
|
||
VariantName = cls_node.attr
|
||
if VariantName:
|
||
NestedStructName: str = f"{RenumName}_{VariantName}"
|
||
if NestedStructName in Gen.structs:
|
||
NestedStructType: ir.Type = Gen.structs[NestedStructName]
|
||
# 变体 struct 可能仍是 opaque(body 未 set_body / 同名两份实例)
|
||
# ⇒ 先取已具化实例,否则 GEP 在编译期算不出元素类型(判据见
|
||
# ExprAttrHandle._ConcretePointeeFor)。
|
||
_TwV: Any = self.Trans.ExprAttrHandle._ConcretePointeeFor(
|
||
Gen, NestedStructName, NestedStructType)
|
||
if isinstance(_TwV, ir.IdentifiedStructType):
|
||
NestedStructType = _TwV
|
||
NestedStructPtrType: ir.PointerType = ir.PointerType(NestedStructType)
|
||
variant_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, NestedStructPtrType, name=f"match_cast_{VariantName}")
|
||
for bind_name, member_name, member_type, member_idx in bindings:
|
||
elem_ptr: ir.Value = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), member_idx + 1)], name=f"match_{bind_name}")
|
||
# REnum 嵌套结构体共享 max_variant_struct 布局,成员类型可能与
|
||
# 结构体字段类型不一致。bitcast 到成员类型以确保后续 load 得到正确类型。
|
||
if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type:
|
||
elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"match_{bind_name}_cast")
|
||
Gen.variables[bind_name] = elem_ptr
|
||
self.HandleBodyLlvm(case.body)
|
||
for bind_name, _, _, _ in bindings:
|
||
if bind_name in Gen.variables:
|
||
del Gen.variables[bind_name]
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchValue):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
if not HasDefault:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
Gen.builder.position_at_start(AfterBB) |