diff --git a/App/lib/core/Gen/MatchGenMixin.vp b/App/lib/core/Gen/MatchGenMixin.vp index af523d2..afe7115 100644 --- a/App/lib/core/Gen/MatchGenMixin.vp +++ b/App/lib/core/Gen/MatchGenMixin.vp @@ -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) #