fix(match): case A | B 的 OR 模式注册全部备选(switch 仅取首个)

_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(原为静默丢弃)。
This commit is contained in:
TermiNexus committed 2026-10-03 12:11:43 +08:00
1 parent 3b465121cd
commit f2b65f57ec
1 file changed
+110 -35
+110 -35
View File
@@ -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: