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
This commit is contained in:
TermiNexus committed 2026-10-02 22:33:26 +08:00
1 parent 9ff301d747
commit cc0615e519
3 files changed
+107 -10

No files matched your search

+16 -1
View File
@@ -19,7 +19,13 @@ class BodyHandle(BaseHandle):
Gen._set_node_info(Node)
try:
if isinstance(Node, ast.Expr):
self.HandleExprLlvm(Node.value)
# `fallthrough`(vast 软关键字已脱糖为 c.NoBreak())是 match 的
# 标记语句,本身不产出 IR,语义由 HandlesMatch 消费;后续步骤
# (含 _EmitTempFrees)保持原样执行。
if self._IsNoBreakMarkerStmt(Node):
pass
else:
self.HandleExprLlvm(Node.value)
elif isinstance(Node, ast.Return):
self.Trans.ReturnHandler._HandleReturnLlvm(Node)
elif isinstance(Node, ast.Assign):
@@ -79,6 +85,15 @@ class BodyHandle(BaseHandle):
if Gen.builder and not Gen.builder.block.is_terminated:
Gen._EmitTempFrees()
@staticmethod
def _IsNoBreakMarkerStmt(Node: ast.stmt) -> bool:
"""是否 `c.NoBreak()` 标记语句(`fallthrough` 软关键字的脱糖形态)"""
if not isinstance(Node, ast.Expr) or not isinstance(Node.value, ast.Call):
return False
Fn: ast.expr = Node.value.func
return (isinstance(Fn, ast.Attribute) and isinstance(Fn.value, ast.Name)
and Fn.value.id == 'c' and Fn.attr == 'NoBreak')
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None,
ConstWidthHint: int | None = None) -> ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
+53 -9
View File
@@ -239,43 +239,53 @@ class MatchHandle(BaseHandle):
for j in range(NumSubCases):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
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.HandleBodyLlvm(case.body)
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.HandleBodyLlvm(case.body)
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.HandleBodyLlvm(case.body)
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.HandleBodyLlvm(case.body)
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)
@@ -285,6 +295,32 @@ class MatchHandle(BaseHandle):
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):
@@ -455,36 +491,44 @@ class MatchHandle(BaseHandle):
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)
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.HandleBodyLlvm(case.body)
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.HandleBodyLlvm(case.body)
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.HandleBodyLlvm(case.body)
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)
+38
View File
@@ -82,6 +82,11 @@ _STRUCT_EXAMPLE: str = (
'struct Node(Base): value: int'
)
# fallthrough 软关键字(语句级):仅当整条语句就是 `fallthrough` 时命中,
# 脱糖为 `c.NoBreak()` 标记调用(match 生成侧据此让 case 体不自动 break,
# 落到下一个 case)。作标识符/属性的写法逐字不变。
_FALLTHROUGH_KEYWORD: str = 'fallthrough'
class Parser(ExprParserMixin):
"""Viper/Python 完整语法分析器。"""
@@ -186,6 +191,13 @@ class Parser(ExprParserMixin):
return self._ParseMatch()
except SyntaxError:
self.Idx = save
# fallthrough 软关键字(语句级):仅当整条语句就是 `fallthrough`
# (后随 NEWLINE / ';' / ENDMARKER)时命中,脱糖为 `c.NoBreak()`
# 标记调用,由 match 生成侧消费(case 体因此不自动 break)。
# 作标识符/属性的写法(fallthrough = 1 / fallthrough(x) /
# d.fallthrough)不命中,逐字不变。
if v == _FALLTHROUGH_KEYWORD and self._IsFallThroughStmt():
return self._ParseFallThrough()
if v == 'return':
return self._ParseReturn()
if v == 'raise':
@@ -224,6 +236,32 @@ class Parser(ExprParserMixin):
return self._ParseDecorated()
return self._ParseSimpleStmt()
# ==================================================================
# fallthrough 软关键字(语句级)
# ==================================================================
def _IsFallThroughStmt(self) -> bool:
"""当前语句是否为软关键字 `fallthrough`(后随 NEWLINE / ';' / ENDMARKER)
只在"整条语句就是 fallthrough"时命中;`fallthrough = 1`(赋值)、
`fallthrough(x)`(调用)、`d.fallthrough`(属性)都不命中。
"""
nxt: Token = self._Peek(1)
if nxt.Type == NEWLINE or nxt.Type == ENDMARKER:
return True
return nxt.Type == OP and nxt.Value == ';'
def _ParseFallThrough(self) -> N.stmt:
"""`fallthrough` 脱糖为 `c.NoBreak()`(match 生成侧识别的 fallthrough 标记)"""
tok: Token = self._Peek()
self._Next()
marker: N.Call = N.Call(
func=N.Attribute(value=N.Name(id='c', ctx=N.Load()),
attr='NoBreak', ctx=N.Load()),
args=[], keywords=[])
node: N.Expr = N.Expr(value=marker)
self._FixLoc(node, tok)
return node
# ==================================================================
# 简单语句
# ==================================================================