From f2b65f57ec8400a33ae47267f6f8e5fe60606cc0 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 3 Oct 2026 12:11:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(match):=20case=20A=20|=20B=20=E7=9A=84=20OR?= =?UTF-8?q?=20=E6=A8=A1=E5=BC=8F=E6=B3=A8=E5=86=8C=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E5=A4=87=E9=80=89=EF=BC=88switch=20=E4=BB=85=E5=8F=96=E9=A6=96?= =?UTF-8?q?=E4=B8=AA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _handle_simple_match 的 MatchOr 分支此前只把 pats[0] 译成 case 值 → switch value: case 1 | 2 中 2 落 default(LogicFlowTest SwitchMultiValueTest(2) != 10)。改为按臂记录 MatchOr 节点,建 sw 链时逐备选 new_switch_case 挂同一 body(等价 C 的 case 1: case 2:);_handle_guarded_simple_match 同款:cond 改为各备选 subj==cv 的 OR 再与 guard 取与。非 MatchOr 路径逐字不变;非值备选改为 fail-fast(原为静默丢弃)。 --- App/lib/core/Gen/MatchGenMixin.vp | 145 ++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 35 deletions(-) diff --git a/App/lib/core/Gen/MatchGenMixin.vp b/App/lib/core/Gen/MatchGenMixin.vp index 00c202a..f104449 100644 --- a/App/lib/core/Gen/MatchGenMixin.vp +++ b/App/lib/core/Gen/MatchGenMixin.vp @@ -1352,7 +1352,11 @@ novirtual class MatchGenMixin: cv_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) - if bb_arr is None or cv_arr is None or def_arr is None or nb_arr is None: + # 每个已收集臂的 MatchOr 节点指针(0 = 非 MatchOr):`case 1 | 2:` 需为 + # 每个备选各产出一个 switch case 值(同一 body BB),故此处记录节点, + # 建 sw 链时逐备选展开。非 MatchOr 恒为 0,走原单 cv 路径逐字不变。 + mo_arr: sizet* = pool.alloc(8 * MAX_MATCH_CASES) + if bb_arr is None or cv_arr is None or def_arr is None or nb_arr is None or mo_arr is None: # fail-fast: match 分派数组分配失败 self.Gen.TypeGenErr.fatal_error(node, "match 分派数组分配失败", "TypeError") @@ -1382,6 +1386,9 @@ novirtual class MatchGenMixin: is_def: int = 0 cv: llvmlite.Value* = None + # MatchOr 臂节点(非 MatchOr 保持 None):pass1 只记录,真正的 + # 逐备选 case 值在建 sw 链时产出,保证 `case 1 | 2:` 两个值都命中同一 body。 + or_node: ast.AST* = None if pat is not None: pk: int = pat.kind() @@ -1400,14 +1407,8 @@ novirtual class MatchGenMixin: has_default = 1 default_case_node = case_node elif pk == ast.ASTKind.MatchOr: - mo: rawptr[ast.MatchOr] = rawptr[ast.MatchOr](pat) - pats: list[ast.AST*]* = mo.patterns - if pats is not None and len(pats) > 0: - first_pat: ast.AST* = pats.get(0) - if first_pat is not None \ - and first_pat.kind() == ast.ASTKind.MatchValue: - mv2: rawptr[ast.MatchValue] = (rawptr[ast.MatchValue])(first_pat) - cv = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, mv2.value, trans._funcs, trans._func_count, trans) + # 记录整节点:备选在建 sw 链处逐个数展开,不再只取第 0 个。 + or_node = pat elif pk == ast.ASTKind.MatchClass: # 本路径(非 REnum 分派)不为 class-pattern 臂产出 case 值 → 该臂被 # 静默丢弃、臂体不可达。若该 class-pattern 确实指向已注册 REnum @@ -1430,23 +1431,62 @@ novirtual class MatchGenMixin: body_list: list[ast.AST*]* = mc.children nb: int = self._has_no_break(body_list) + # 本臂占用的收集槽位:collected 随后自增;is_def==0 时上方已保证未越界 + cur_slot: int = collected if collected < MAX_MATCH_CASES: - bb_arr[collected] = sizet(bb) - cv_arr[collected] = sizet(cv) - def_arr[collected] = is_def - nb_arr[collected] = nb + bb_arr[cur_slot] = sizet(bb) + cv_arr[cur_slot] = sizet(cv) + def_arr[cur_slot] = is_def + nb_arr[cur_slot] = nb + mo_arr[cur_slot] = sizet(or_node) collected = collected + 1 - if is_def == 0 and cv is not None: - sc: rawptr[llvmlite.SwitchCase] = llvmlite.new_switch_case(pool, cv, bb) - if sc is not None: - if sw_head is None: - sw_head = sc - sw_tail = sc - else: - sw_tail.Next = sc - sw_tail = sc - sw_count = sw_count + 1 + if is_def == 0: + mo_addr: sizet = mo_arr[cur_slot] + if mo_addr != 0: + # MatchOr 臂:为每个备选各产出一个 case 值,全部指向同一 bb + # (等价 C 的 `case 1: case 2:`,共享 body)。非 MatchValue/ + # MatchSingleton 备选无法产出值,fail-fast 不得静默丢弃。 + mo2: rawptr[ast.MatchOr] = (rawptr[ast.MatchOr])(mo_addr) + pats2: list[ast.AST*]* = mo2.patterns + if pats2 is None or len(pats2) == 0: + self.Gen.TypeGenErr.fatal_error(pat, "switch OR 模式无备选子模式", "TypeError") + pats2_n: sizet = len(pats2) + pats2_i: int = 0 + while pats2_i < pats2_n: + alt_pat: ast.AST* = pats2.get(pats2_i) + if alt_pat is not None: + alt_cv: llvmlite.Value* = None + if alt_pat.kind() == ast.ASTKind.MatchValue: + alt_mv: rawptr[ast.MatchValue] = (rawptr[ast.MatchValue])(alt_pat) + alt_cv = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, alt_mv.value, + trans._funcs, trans._func_count, trans) + elif alt_pat.kind() == ast.ASTKind.MatchSingleton: + # None -> 0(与单值/守卫路径同口径) + alt_cv = llvmlite.const_int32(pool, 0) + else: + self.Gen.TypeGenErr.fatal_error(alt_pat, "switch OR 模式备选不是值模式", "TypeError") + if alt_cv is not None: + sc2: rawptr[llvmlite.SwitchCase] = llvmlite.new_switch_case(pool, alt_cv, bb) + if sc2 is not None: + if sw_head is None: + sw_head = sc2 + sw_tail = sc2 + else: + sw_tail.Next = sc2 + sw_tail = sc2 + sw_count = sw_count + 1 + pats2_i = pats2_i + 1 + elif cv is not None: + sc: rawptr[llvmlite.SwitchCase] = llvmlite.new_switch_case(pool, cv, bb) + if sc is not None: + if sw_head is None: + sw_head = sc + sw_tail = sc + else: + sw_tail.Next = sc + sw_tail = sc + sw_count = sw_count + 1 i = i + 1 @@ -1612,8 +1652,12 @@ novirtual class MatchGenMixin: 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) + # 每个已收集臂的 MatchOr 节点指针(0 = 非 MatchOr):check 链需把 + # `case 1 | 2` 变成各备选 subj_val == cv_i 的 OR,故此处记录节点, + # 发射 check 时逐备选展开取 OR。非 MatchOr 恒为 0,走原单 cv 路径不变。 + or_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: + or nb_arr is None or mc_arr is None or cv_arr is None or or_arr is None: self.Gen.TypeGenErr.fatal_error(node, "match 分派数组分配失败", "TypeError") collected: int = 0 has_default: int = 0 @@ -1636,6 +1680,9 @@ novirtual class MatchGenMixin: gnode0: ast.AST* = mc.guard is_def: int = 0 cv: llvmlite.Value* = None + # MatchOr 臂节点(非 MatchOr 保持 None):pass1 只记录,check 发射时 + # 逐备选展开成 subj_val == cv_i 的 OR,不再只看第 0 个备选。 + or_node: ast.AST* = None if pat is not None: pk: int = pat.kind() if pk == ast.ASTKind.MatchValue: @@ -1653,16 +1700,8 @@ novirtual class MatchGenMixin: # 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) + # 记录整节点:check 发射时逐备选展开为 OR(见 step 4) + or_node = pat else: # 禁止静默兜底:guard 分支只支持 值/None/通配/或 模式 self.Gen.TypeGenErr.fatal_error(pat, "guard 分支不支持的 case 模式", "TypeError") @@ -1686,6 +1725,7 @@ novirtual class MatchGenMixin: nb_arr[collected] = nb mc_arr[collected] = sizet(mc) cv_arr[collected] = sizet(cv) + or_arr[collected] = sizet(or_node) collected = collected + 1 i = i + 1 @@ -1710,8 +1750,43 @@ novirtual class MatchGenMixin: mc2: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](mc_arr[ci]) # 值条件:subj_val == case值(通配臂无 case 值 → cond 保持 None=恒真) cond: llvmlite.Value* = None + or_addr: sizet = or_arr[ci] cv_addr: sizet = cv_arr[ci] - if cv_addr != 0: + if or_addr != 0: + # MatchOr 臂:cond = (subj_val == cv_0) OR (subj_val == cv_1) OR ... + # 各备选逐一比较后取 OR(非 MatchValue/MatchSingleton 备选无法产出 + # 值 → fail-fast,不静默丢弃)。 + mo4: rawptr[ast.MatchOr] = (rawptr[ast.MatchOr])(or_addr) + pats4: list[ast.AST*]* = mo4.patterns + if pats4 is None or len(pats4) == 0: + self.Gen.TypeGenErr.fatal_error(node, "guard OR 模式无备选子模式", "TypeError") + pats4_n: sizet = len(pats4) + pats4_i: int = 0 + while pats4_i < pats4_n: + alt_pat2: ast.AST* = pats4.get(pats4_i) + if alt_pat2 is not None: + alt_cv2: llvmlite.Value* = None + if alt_pat2.kind() == ast.ASTKind.MatchValue: + alt_mv2: rawptr[ast.MatchValue] = (rawptr[ast.MatchValue])(alt_pat2) + alt_cv2 = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, alt_mv2.value, + trans._funcs, trans._func_count, trans) + elif alt_pat2.kind() == ast.ASTKind.MatchSingleton: + # None -> 0(与单值路径同口径) + alt_cv2 = llvmlite.const_int32(pool, 0) + else: + self.Gen.TypeGenErr.fatal_error(alt_pat2, "guard OR 模式备选不是值模式", "TypeError") + if alt_cv2 is not None: + cc: llvmlite.Value* = self._seq_cmp_eq(trans, builder, subj_val, alt_cv2) + if cc is None: + self.Gen.TypeGenErr.fatal_error(node, "match OR 备选值比较失败", "TypeError") + if cond is None: + cond = cc + else: + cond = llvmlite.build_or(builder, cond, cc) + pats4_i = pats4_i + 1 + if cond is None: + self.Gen.TypeGenErr.fatal_error(node, "guard OR 模式未能产出任何比较条件", "TypeError") + elif 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: