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:
1 parent
d633a5786f
commit
38860a6313
1 file changed
+113
@@ -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)
|
||||
#
|
||||
|
||||
Reference in new issue
Block a user