Files
TransPyC/lib/core/Handles/HandlesMatch.py
T
TermiNexus cc0615e519 feat(match): fallthrough 软关键字 + case 内 break 跳出 match
- vast/Parser:语句位置识别裸 `fallthrough`(仅整条语句命中),脱糖为 c.NoBreak()
  标记调用;作标识符的写法不受影响
- HandlesBody:`c.NoBreak()` 标记语句不产出 IR(语义由 HandlesMatch 消费)
- HandlesMatch:新增 _HandleMatchCaseBody(case 内 break 目标 = match 的 AfterBB,
  经 loop_break_targets 压栈/出栈,不影响外层循环)与 _FallThroughTarget;
  9 处 fallthrough 由"只留无终结符块"改为显式 br 下一个 case body
2026-10-02 22:33:26 +08:00

539 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, 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._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB))
CaseIdx += 1
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
Gen.builder.position_at_start(DefaultBB)
self._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB))
CaseIdx += 1
elif isinstance(pattern, ast.MatchSequence):
Gen.builder.position_at_start(DefaultBB)
self._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, 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._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, 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 _HandleMatchCaseBody(self, Gen: "Translator.LlvmGen",
Body: list[ast.stmt], AfterBB: ir.Block) -> None:
"""翻译 match case 体:case 体内的 break 目标是 match 的 AfterBB
C switch 语义——case 内 break 跳出 **match**(而非外层循环)。故翻译前把
AfterBB 压入 loop_break_targets,翻译完弹出(外层循环目标不受影响)。
"""
Targets: list = Gen.loop_break_targets
Targets.append(AfterBB)
try:
self.HandleBodyLlvm(Body)
finally:
Targets.pop()
@staticmethod
def _FallThroughTarget(CaseBBs: list[ir.Block], CaseIdx: int, AfterBB: ir.Block) -> ir.Block:
"""fallthrough 的落点:下一个 case 的 body(无后继槽位时落到 AfterBB)
`fallthrough`(vast 软关键字,脱糖为 c.NoBreak())表示"本 case 不自动
break",直接落到下一个 case 的 body(对齐 C 的 [[fallthrough]])。
"""
NextIdx: int = CaseIdx + 1
if 0 <= NextIdx < len(CaseBBs):
return CaseBBs[NextIdx]
return 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._HandleMatchCaseBody(Gen, case.body, AfterBB)
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)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB))
CaseIdx += 1
elif isinstance(pattern, ast.MatchValue):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB))
CaseIdx += 1
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
Gen.builder.position_at_start(DefaultBB)
self._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB))
CaseIdx += 1
elif isinstance(pattern, ast.MatchSequence):
Gen.builder.position_at_start(DefaultBB)
self._HandleMatchCaseBody(Gen, case.body, AfterBB)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
else:
Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, 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)