diff --git a/App/lib/core/Gen/MatchGenMixin.vp b/App/lib/core/Gen/MatchGenMixin.vp index 70561cc..157ecb1 100644 --- a/App/lib/core/Gen/MatchGenMixin.vp +++ b/App/lib/core/Gen/MatchGenMixin.vp @@ -93,7 +93,9 @@ novirtual class MatchGenMixin: # 重名 case 检测(fail-fast):**match 不允许重复 case 标签**(重复标签让 # 某个臂永远不可达,且常意味着漏改的分支);**switch 允许重复标签**(按书写 # 顺序,先出现者生效),故仅对 match 做值标签去重。 - # 通配臂(match 的 `case _` / switch 的 `default`)两者都只允许一个。 + # 通配臂(match 的 `case _` / switch 的 `default`):**无 guard** 的通配臂 + # 两者都只允许一个(真正的 default);**带 guard** 的通配臂(`case _ if ...`) + # 允许多个(唯一性由各自的 guard 区分,见 _handle_guarded_simple_match)。 # 在任何 IR 产出之前检查,保证报错即终止。报文前缀按 is_switch 区分。 kind_label: str = "match" default_label: str = "`case _`" @@ -112,7 +114,10 @@ novirtual class MatchGenMixin: if dpat is not None: if dpat.kind() == ast.ASTKind.MatchAs \ and rawptr[ast.MatchAs](dpat).pattern is None: - default_cnt = default_cnt + 1 + # 只统计**无 guard** 的通配臂:带 guard 的 `case _ if ...` + # 允许多个(各自 guard 区分),不当 default 计 + if rawptr[ast.MatchCase](dcase).guard is None: + default_cnt = default_cnt + 1 elif m_node.is_switch == 0: dcj: int = dci + 1 while dcj < ccn: @@ -127,7 +132,7 @@ novirtual class MatchGenMixin: dci = dci + 1 if default_cnt > 1: self.Gen.TypeGenErr.fatal_error(node, - f"{kind_label} 出现 {default_cnt} 个 {default_label}(通配/default 只允许一个)", "SyntaxError") + f"{kind_label} 出现 {default_cnt} 个{default_label}(无 guard 的通配/default 只允许一个;带 guard 的 `case _ if ...` / `default if ...` 可有多个)", "SyntaxError") # 1. 求值 subject subject_node: ast.AST* = m_node.subject @@ -375,6 +380,22 @@ novirtual class MatchGenMixin: # 函数里会使本函数引用退化为隐式全局,跨 match 污染)。 rm_merge_reach: int = 0 + # guard 守卫(match/switch 分支条件守卫):本路径用 switch(tag) 一次性分发, + # **不评估 guard**——带 guard 的臂会被静默忽略(可能选错分支)。禁止静默兜底: + # 一旦存在带 guard 的臂,在任何 IR 产出之前 fail-fast 报错。 + rn_cases: list[ast.AST*]* = node.cases + if rn_cases is not None: + rn_n: sizet = len(rn_cases) + rn_i: int = 0 + while rn_i < rn_n: + rn_case: ast.AST* = rn_cases.get(rn_i) + if rn_case is not None and rn_case.kind() == ast.ASTKind.MatchCase: + if rawptr[ast.MatchCase](rn_case).guard is not None: + self.Gen.TypeGenErr.fatal_error(rn_case, + "REnum match 暂不支持 guard 守卫(该臂会被 switch(tag) 静默忽略);请改为普通 match 或去掉 guard", + "NotImplementedError") + rn_i = rn_i + 1 + # 0. subject 归一化:确保 subject_ptr 是指向 REnum 结构体的指针 # (对齐 TPC HandlesMatch.py:238-259 的 _HandleRenumMatchLlvm) # 情况1: subject_ptr 是 T**(变量 alloca)→ load 一次得 T* @@ -1021,6 +1042,13 @@ novirtual class MatchGenMixin: if pk == ast.ASTKind.MatchAs: ma0: rawptr[ast.MatchAs] = rawptr[ast.MatchAs](pat) if ma0.pattern is None: + # 顶层 _ 通配臂带 guard(`case _ if `):本序列路径把 + # `_` 一律当 default(恒真)处理,会**静默忽略 guard**。禁止 + # 静默兜底 → 在任何 IR 产出之前 fail-fast。 + if mc.guard is not None: + self.Gen.TypeGenErr.fatal_error(pat, + "序列匹配的顶层 `case _ if ` 暂不支持 guard 守卫", + "NotImplementedError") is_def = 1 elif pk != ast.ASTKind.MatchSequence: # fail-fast:序列匹配只接受 MatchSequence / 顶层 _ 通配 @@ -1272,6 +1300,24 @@ novirtual class MatchGenMixin: # fail-fast: subject 转 i32 失败 self.Gen.TypeGenErr.fatal_error(node, "match subject 转换为 i32 失败", "TypeError") + # 1b. guard 预扫描:任一臂带 guard → 改走按书写顺序的 if-else 比较链 + # (Python match 语义:按序取首个"模式匹配且 guard 为真"的臂,见 + # _handle_guarded_simple_match)。**无 guard 时**保持下方既有 switch 分发 + # 路径逐字不变(不得有任何行为变化)。 + cases_g: list[ast.AST*]* = node.cases + has_guard: int = 0 + if cases_g is not None: + cg_n: sizet = len(cases_g) + cg_i: int = 0 + while cg_i < cg_n: + cg_case: ast.AST* = cases_g.get(cg_i) + if cg_case is not None and cg_case.kind() == ast.ASTKind.MatchCase: + if rawptr[ast.MatchCase](cg_case).guard is not None: + has_guard = 1 + cg_i = cg_i + 1 + if has_guard != 0: + return self._handle_guarded_simple_match(trans, node, subj_val) + # 2. 创建 default/end BB cnt: int = trans._label_counter trans._label_counter = cnt + 1 @@ -1507,3 +1553,279 @@ novirtual class MatchGenMixin: # 覆盖嵌套控制流污染(同主路径) trans._ctrl_merge_reachable = sm_merge_reach return 0 + + # ============================================================ + # _handle_guarded_simple_match - _handle_simple_match 的 guard 分支 + # + # 任一臂带 guard 时改走**按书写顺序**的 if-else 比较链分发(对齐 Python + # match 语义:按序取首个"模式匹配且 guard 为真"的臂): + # entry → check0 + # check_i: cond_i → body_i(不满足 → check_{i+1}) + # body_i : → end(fallthrough 时落到下一个非通配 body) + # 全部 check 不中 → default_bb(唯一的**无 guard** 通配臂 body,若有)→ end + # cond_i = 值臂(subj_val == case值) / 通配臂(恒真),再与 guard(归一为 i1)取与。 + # guard 归一化、i1 判定、const_int 参数顺序(pool, bits, val)均与 + # _handle_sequence_match 同款(L1118-1135)。 + # 注意:**无 guard** 的 match/switch 不会进入本函数(见 _handle_simple_match + # 的 has_guard 预扫描),既有 switch 分派路径逐字不变。 + # ============================================================ + def _handle_guarded_simple_match(self, trans: HT.Translator*, + node: ast.Match*, + subj_val: llvmlite.Value*) -> int: + pool: memhub.MemBuddy* = trans.Pool + builder: llvmlite.IRBuilder* = trans._cur_builder + func: llvmlite.Function* = trans._cur_func + # 同主路径:局部记录本 guarded match 的 br end_bb,收尾覆盖 + # _ctrl_merge_reachable,消除 case 体内嵌套控制流污染 + gm_merge_reach: int = 0 + + # 1. 创建 default/end BB + cnt: int = trans._label_counter + trans._label_counter = cnt + 1 + name_buf: str = pool.alloc(32) + if name_buf is None: + self.Gen.TypeGenErr.fatal_error(node, "标签缓冲区分配失败", "TypeError") + viperlib.snprintf(name_buf, 32, "match.guard.default.%d", cnt) + default_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "match.guard.end.%d", cnt) + end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf) + + # 2. 收集 case 信息(check/body BB、default 标记、nobreak、mc、cv) + cases_list: list[ast.AST*]* = node.cases + if cases_list is None: + self.Gen.TypeGenErr.fatal_error(node, "match cases 缺失", "TypeError") + case_count: sizet = len(cases_list) + check_arr: sizet* = pool.alloc(8 * MAX_MATCH_CASES) + body_arr: sizet* = pool.alloc(8 * MAX_MATCH_CASES) + def_arr: int | t.CPtr = pool.alloc(4 * MAX_MATCH_CASES) + nb_arr: int | t.CPtr = pool.alloc(4 * MAX_MATCH_CASES) + mc_arr: sizet* = pool.alloc(8 * MAX_MATCH_CASES) + cv_arr: sizet* = pool.alloc(8 * MAX_MATCH_CASES) + if check_arr is None or body_arr is None or def_arr is None \ + or nb_arr is None or mc_arr is None or cv_arr is None: + self.Gen.TypeGenErr.fatal_error(node, "match 分派数组分配失败", "TypeError") + collected: int = 0 + has_default: int = 0 + # 唯一的**无 guard** 通配臂(default):其 body = default_bb,且 default_bb + # 是所有 check 不中时的落点。带 guard 的通配臂不算 default(各有独立 + # check/body BB),避免其 guard 被静默忽略。 + default_case_node: ast.AST* = None + first_check: int = -1 + i: int = 0 + while i < case_count: + if collected >= MAX_MATCH_CASES: + self.Gen.TypeGenErr.fatal_error(node, f"match case 数量超过上限 MAX_MATCH_CASES={MAX_MATCH_CASES}", "OverflowError") + break + case_node: ast.AST* = cases_list.get(i) + if case_node is None or case_node.kind() != ast.ASTKind.MatchCase: + i = i + 1 + continue + mc: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](case_node) + pat: ast.AST* = mc.pattern + gnode0: ast.AST* = mc.guard + is_def: int = 0 + cv: llvmlite.Value* = None + if pat is not None: + pk: int = pat.kind() + if pk == ast.ASTKind.MatchValue: + mv: rawptr[ast.MatchValue] = rawptr[ast.MatchValue](pat) + cv = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, mv.value, + trans._funcs, trans._func_count, trans) + elif pk == ast.ASTKind.MatchSingleton: + # None -> 0 + cv = llvmlite.const_int32(pool, 0) + elif pk == ast.ASTKind.MatchAs: + ma: rawptr[ast.MatchAs] = rawptr[ast.MatchAs](pat) + if ma.pattern is None and gnode0 is None: + # 唯一的**无 guard** 通配臂 → default + is_def = 1 + # ma.pattern is None 且 guard 非空 → 带 guard 的通配臂:条件恒真 + # 再与 guard 取与(见发射阶段),不当作 default + elif pk == ast.ASTKind.MatchOr: + # MatchOr:取第一个 MatchValue 作比较(与 _handle_simple_match 的 + # switch 路径同口径,其余子模式在 guard 链中不展开) + mo: rawptr[ast.MatchOr] = rawptr[ast.MatchOr](pat) + pats_o: list[ast.AST*]* = mo.patterns + if pats_o is not None and len(pats_o) > 0: + fp: ast.AST* = pats_o.get(0) + if fp is not None and fp.kind() == ast.ASTKind.MatchValue: + mv2: rawptr[ast.MatchValue] = rawptr[ast.MatchValue](fp) + cv = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, mv2.value, + trans._funcs, trans._func_count, trans) + else: + # 禁止静默兜底:guard 分支只支持 值/None/通配/或 模式 + self.Gen.TypeGenErr.fatal_error(pat, "guard 分支不支持的 case 模式", "TypeError") + if is_def == 1: + has_default = 1 + default_case_node = case_node + check_arr[collected] = 0 + body_arr[collected] = sizet(default_bb) + else: + viperlib.snprintf(name_buf, 32, "match.guard.check.%d.%d", cnt, i) + cbb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "match.guard.body.%d.%d", cnt, i) + bbb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf) + check_arr[collected] = sizet(cbb) + body_arr[collected] = sizet(bbb) + if first_check < 0: + first_check = collected + body_list: list[ast.AST*]* = mc.children + nb: int = self._has_no_break(body_list) + def_arr[collected] = is_def + nb_arr[collected] = nb + mc_arr[collected] = sizet(mc) + cv_arr[collected] = sizet(cv) + collected = collected + 1 + i = i + 1 + + # 3. 入口 → 第一个 check(或 default/end) + if first_check >= 0: + head_bb: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](check_arr[first_check]) + llvmlite.build_br(builder, head_bb) + elif has_default == 1: + llvmlite.build_br(builder, default_bb) + else: + llvmlite.build_br(builder, end_bb) + + # 4. 发射 check 链(按书写顺序) + ci: int = 0 + while ci < collected: + if def_arr[ci] == 1: + ci = ci + 1 + continue + cbb2: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](check_arr[ci]) + bbb2: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](body_arr[ci]) + llvmlite.position_at_end(builder, cbb2) + mc2: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](mc_arr[ci]) + # 值条件:subj_val == case值(通配臂无 case 值 → cond 保持 None=恒真) + cond: llvmlite.Value* = None + cv_addr: sizet = cv_arr[ci] + if cv_addr != 0: + cv2: llvmlite.Value* = (llvmlite.Value*)(cv_addr) + c1: llvmlite.Value* = self._seq_cmp_eq(trans, builder, subj_val, cv2) + if c1 is None: + self.Gen.TypeGenErr.fatal_error(node, "match case 值比较失败", "TypeError") + cond = c1 + # guard(若有):匹配成立且 guard 非零(归一为 i1,写法同序列路径) + g_addr: sizet = sizet(mc2.guard) + if g_addr != 0: + gnode: ast.AST* = (ast.AST*)(g_addr) + gv: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, gnode, + trans._funcs, trans._func_count, trans) + if gv is None: + self.Gen.TypeGenErr.fatal_error(gnode, "match guard 翻译失败", "TypeError") + gbits: int = self.Gen.ExprGenBase.get_llvm_type_bits(gv.Ty) + if gbits == 1: + g1: llvmlite.Value* = gv + else: + gzero: llvmlite.Value* = llvmlite.const_int(pool, gbits if gbits > 0 else 32, 0) + g1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, gv, gzero) + if cond is None: + cond = g1 + else: + cond = llvmlite.build_and(builder, cond, g1) + if cond is None: + cond = llvmlite.const_int(pool, 1, 1) + # 下一个 check(跳过 default)→ 否则 default/end + nxt: rawptr[llvmlite.BasicBlock] = None + nj: int = ci + 1 + while nj < collected and def_arr[nj] == 1: + nj = nj + 1 + if nj < collected: + nxt = rawptr[llvmlite.BasicBlock](check_arr[nj]) + elif has_default == 1: + nxt = default_bb + else: + nxt = end_bb + llvmlite.build_cond_br(builder, cond, bbb2, nxt) + ci = ci + 1 + + # 5. 翻译 case body + ci = 0 + while ci < collected: + if def_arr[ci] == 1: + ci = ci + 1 + continue + bbb3: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](body_arr[ci]) + llvmlite.position_at_end(builder, bbb3) + mc3: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](mc_arr[ci]) + # case 体内 break = 跳出 match(C switch 语义):暂存/覆盖 _break_bb + gmb_old_break: rawptr[llvmlite.BasicBlock] = trans._break_bb + trans._break_bb = end_bb + gmb_old_inmc: int = trans._in_switch_case + trans._in_switch_case = 1 + self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH) + body_list2: list[ast.AST*]* = mc3.children + if body_list2 is not None: + bc: sizet = len(body_list2) + bi: int = 0 + while bi < bc: + stmt: ast.AST* = body_list2.get(bi) + if stmt is not None: + self.Gen.StmtGen.translate_stmt(trans, stmt) + bi = bi + 1 + self.Gen.VarGen.exit_scope(trans.SymTab) + trans._break_bb = gmb_old_break + trans._in_switch_case = gmb_old_inmc + # 若未终止:默认跳 end;含 fallthrough 则落到下一个非通配 body。 + # 末句控制流全 return → 补 unreachable(不 br end_bb) + if llvmlite.builder_cur_block_is_terminated(builder) == 0 \ + and self._case_tail_unreachable(trans, builder, body_list2) == 0: + if nb_arr[ci] == 0: + llvmlite.build_br(builder, end_bb) + else: + nj2: int = ci + 1 + while nj2 < collected and def_arr[nj2] == 1: + nj2 = nj2 + 1 + if nj2 < collected: + nbb: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](body_arr[nj2]) + llvmlite.build_br(builder, nbb) + elif has_default == 1: + llvmlite.build_br(builder, default_bb) + else: + llvmlite.build_br(builder, end_bb) + trans._ctrl_merge_reachable = 1 + gm_merge_reach = 1 + ci = ci + 1 + + # 6. default 块(唯一的**无 guard** 通配臂;全部 check 不中时落此) + llvmlite.position_at_end(builder, default_bb) + if has_default == 0 or default_case_node is None: + # 无无 guard 通配臂:match 非穷尽 ⇒ merge 可达,default 直接跳 end + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + llvmlite.build_br(builder, end_bb) + trans._ctrl_merge_reachable = 1 + gm_merge_reach = 1 + else: + dmc_d: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](default_case_node) + # default 臂同样按 match-case 语义:break = 跳出 match + gmd_old_break: rawptr[llvmlite.BasicBlock] = trans._break_bb + trans._break_bb = end_bb + gmd_old_inmc: int = trans._in_switch_case + trans._in_switch_case = node.is_switch + self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH) + # dbody3 内联声明接收者遍历(规避 .get 误分派成 list[int].get 的截断) + dbody3: list[ast.AST*]* = dmc_d.children + if dbody3 is not None: + dn3: sizet = len(dbody3) + dj3: int = 0 + while dj3 < dn3: + stmt3: ast.AST* = dbody3.get(dj3) + if stmt3 is not None: + self.Gen.StmtGen.translate_stmt(trans, stmt3) + dj3 = dj3 + 1 + self.Gen.VarGen.exit_scope(trans.SymTab) + trans._break_bb = gmd_old_break + trans._in_switch_case = gmd_old_inmc + # 末句控制流全 return → 补 unreachable(不 br end_bb) + if llvmlite.builder_cur_block_is_terminated(builder) == 0 \ + and self._case_tail_unreachable(trans, builder, dbody3) == 0: + llvmlite.build_br(builder, end_bb) + trans._ctrl_merge_reachable = 1 + gm_merge_reach = 1 + + # 7. 定位到 end + llvmlite.position_at_end(builder, end_bb) + # 覆盖嵌套控制流污染(同主路径) + trans._ctrl_merge_reachable = gm_merge_reach + return 0