feat(match): 重名 case 标签 fail-fast

MatchGenMixin._translate_match_impl 新增前置检查(任何 IR 产出之前):新增
_case_label_same / _value_label_same 比较 switch 标签(常量 / 名字 / 属性链 /
MatchSingleton / MatchClass 变体名 / MatchOr 子模式),标签重复或出现多个
`case _` 时 fatal_error 终止(C switch 不允许重复 case 标签)。
This commit is contained in:
TermiNexus committed 2026-10-02 22:44:34 +08:00
1 parent d633a5786f
commit 38860a6313
1 file changed
+113
+113
View File
@@ -90,6 +90,38 @@ novirtual class MatchGenMixin:
m_node: ast.Match* = ast.Match*(node)
# 重名 case 检测(fail-fast):C 的 switch 不允许重复 case 标签(重复标签让
# 某个臂永远不可达,且常意味着漏改的分支);`case _`(default)只允许一个。
# 在任何 IR 产出之前检查,保证报错即终止。
cases_chk: list[ast.AST*]* = m_node.cases
if cases_chk is not None:
ccn: sizet = len(cases_chk)
default_cnt: int = 0
dci: int = 0
while dci < ccn:
dcase: ast.AST* = cases_chk.get(dci)
if dcase is not None and dcase.kind() == ast.ASTKind.MatchCase:
dpat: ast.AST* = rawptr[ast.MatchCase](dcase).pattern
if dpat is not None:
if dpat.kind() == ast.ASTKind.MatchAs \
and rawptr[ast.MatchAs](dpat).pattern is None:
default_cnt = default_cnt + 1
else:
dcj: int = dci + 1
while dcj < ccn:
dcase2: ast.AST* = cases_chk.get(dcj)
if dcase2 is not None and dcase2.kind() == ast.ASTKind.MatchCase:
dpat2: ast.AST* = rawptr[ast.MatchCase](dcase2).pattern
if self._case_label_same(dpat, dpat2) != 0:
self.Gen.TypeGenErr.fatal_error(dcase2,
f"match 出现重名 case:第 {dci} 与第 {dcj} 个 case 的标签相同(C switch 不允许重复 case 标签)",
"SyntaxError")
dcj = dcj + 1
dci = dci + 1
if default_cnt > 1:
self.Gen.TypeGenErr.fatal_error(node,
f"match 出现 {default_cnt} 个 `case _`(default 只允许一个)", "SyntaxError")
# 1. 求值 subject
subject_node: ast.AST* = m_node.subject
if subject_node is None:
@@ -214,6 +246,87 @@ novirtual class MatchGenMixin:
return None
return self.Gen.ClassGenEnum._lookup_renum_variant(variant_name)
# ============================================================
# _value_label_same / _case_label_same - 重名 case 判定(switch 标签比较)
#
# C 的 switch 不允许重复 case 标签:重复标签会让某个臂永远不可达,且通常
# 意味着漏改的分支。只比较 switch 标签本身:
# MatchValue(常量 / 名字 / 属性链)、MatchSingleton(None)、MatchClass(变体名);
# MatchOr 逐个子模式两两比较;MatchAs / MatchSequence 无 switch 标签,恒不等。
# ============================================================
def _value_label_same(self, a: ast.AST*, b: ast.AST*) -> int:
"""两个 `case` 值节点是否等价(常量值 / 名字 / 属性链)"""
if a is None or b is None:
return 0
ka: int = a.kind()
if ka != b.kind():
return 0
if ka == ast.ASTKind.Constant:
ca: ast.Constant* = ast.Constant*(a)
cb: ast.Constant* = ast.Constant*(b)
if ca.const_kind != cb.const_kind:
return 0
if ca.const_kind == ast.CONST_STR:
if ca.str_val is not None and cb.str_val is not None \
and string.strcmp(ca.str_val, cb.str_val) == 0:
return 1
return 0
if ca.const_kind == ast.CONST_FLOAT:
if ca.float_val == cb.float_val:
return 1
return 0
# 整数 / 布尔 / 字符码
if ca.int_val == cb.int_val:
return 1
return 0
if ka == ast.ASTKind.Name:
na: ast.Name* = ast.Name*(a)
nb: ast.Name* = ast.Name*(b)
if na.id is not None and nb.id is not None and string.strcmp(na.id, nb.id) == 0:
return 1
return 0
if ka == ast.ASTKind.Attribute:
aa: ast.Attribute* = ast.Attribute*(a)
ab: ast.Attribute* = ast.Attribute*(b)
if aa.attr is None or ab.attr is None or string.strcmp(aa.attr, ab.attr) != 0:
return 0
return self._value_label_same(aa.value, ab.value)
return 0
def _case_label_same(self, a: ast.AST*, b: ast.AST*) -> int:
"""两个 case 模式是否有相同的 switch 标签"""
if a is None or b is None:
return 0
ka: int = a.kind()
if ka != b.kind():
return 0
if ka == ast.ASTKind.MatchValue:
av: ast.AST* = rawptr[ast.MatchValue](a).value
bv: ast.AST* = rawptr[ast.MatchValue](b).value
return self._value_label_same(av, bv)
if ka == ast.ASTKind.MatchSingleton:
return 1 # MatchSingleton 只可能是 None
if ka == ast.ASTKind.MatchClass:
an: str = self._extract_variant_name_from_cls(a)
bn: str = self._extract_variant_name_from_cls(b)
if an is None or bn is None:
return 0
if string.strcmp(an, bn) == 0:
return 1
return 0
if ka == ast.ASTKind.MatchOr:
ap: list[ast.AST*]* = rawptr[ast.MatchOr](a).patterns
bp: list[ast.AST*]* = rawptr[ast.MatchOr](b).patterns
if ap is None or bp is None or len(ap) != len(bp):
return 0
oi: int = 0
while oi < len(ap):
if self._case_label_same(ap.get(oi), bp.get(oi)) == 0:
return 0
oi = oi + 1
return 1
return 0
# ============================================================
# _has_no_break - 检测 case body 是否要求 fallthrough(落到下一个 case)
#