A None arm written first in a match meets the other arms at their Option.

This commit is contained in:
Joseph Ferano 2026-09-26 17:21:06 +07:00
parent 09f1e4369f
commit 89d54a47f4
3 changed files with 36 additions and 5 deletions

View File

@ -10367,8 +10367,24 @@ and check_match ctx ?(tail = false) ?(used = false) ?(stmt = false) ?(opt = fals
let idx = List.mapi (fun i r -> (i, r)) resolved in
if !want <> None then idx
else
List.filter (fun (_, r) -> not (literal_arm r)) idx
@ List.filter (fun (_, r) -> literal_arm r) idx
let typed = List.filter (fun (_, r) -> not (literal_arm r)) idx in
(* A bare [None] that would be checked first has nothing to take its
type from and was always refused; it goes after the others, so it
meets their T at T? (decision 138). Only the leading ones move, so
no order that checked before changes. *)
let none_arm (_, ((a : Ast.arm), _, _)) =
match List.rev a.Ast.body with last :: _ -> is_none_lit last | [] -> false
in
let rec lead acc = function
| x :: rest when none_arm x -> lead (x :: acc) rest
| rest -> (List.rev acc, rest)
in
let nones, typed =
match lead [] typed with
| _ :: _ as nones, rest when List.length nones < List.length idx -> nones, rest
| _ -> [], typed
in
typed @ List.filter (fun (_, r) -> literal_arm r) idx @ nones
in
let checked =
map_lr

View File

@ -28,6 +28,16 @@ fn first(o: $u?) -> $u = o!
fn wrap(x: $t) -> $t? = x
;; A generic body's own $t handed to a $u? parameter.
fn through(x: $t) -> $t = first(x)
;; A None arm first takes its type from the arms after it.
fn arms(k: i32, x: i32) -> i32?
match k
5 -> None
4 -> x
_ -> 0
fn chain(a: bool, b: bool, opt: i32?) -> Option(i32?)
if a
1
@ -50,9 +60,13 @@ fn main()
;; a struct field
let p = P{.a 3 .b x}
println(p.a ?? 0, p.b ?? 0)
p.b = 7
println(p.b ?? 0)
;; an array and a Vec element
let xs: [3 i32?] = [1, None, x]
println(xs[0] ?? 0, xs[1] ?? 0, xs[2] ?? 0)
xs[1] = 5
println(xs[1] ?? 0)
let v: Vec(i32?) = vec-new(i32?)
push(v, 6)
push(v, None)
@ -63,6 +77,7 @@ fn main()
4 -> x
_ -> None
println(m ?? 0)
println(arms(5, 3) ?? -1, arms(4, 3) ?? -1, arms(1, 3) ?? -1)
;; nested: a T into a T?? is Some(Some(t)), a T? is Some of it
let nn: Option(i32?) = 5
let none: i32? = None
@ -77,7 +92,7 @@ fn main()
1
println(two(kk))
;; generics
println(first(9), first(Some(8)), wrap(3) ?? 0)
println(first(9), first(Some(8)), wrap(3) ?? 0, through(5))
;; a narrowed name still takes a payload value
let o: i32? = Some(1)
if o?

View File

@ -2276,8 +2276,8 @@ let () =
[ ("optionals.fln", optionals_out); ("optionals-dyn.fln", optionals_dyn_out);
(* A T where a T? is wanted is Some of it (decision 138). *)
("autowrap.fln",
"-1\n5 4 2\n7 8 -1\n3 4\n1 0 4\n2 6 0\n2 -1 -1 2\n4\nsome some some none\n\
some some some none none\nsome some\n9 8 3\n10\n");
"-1\n5 4 2\n7 8 -1\n3 4\n7\n1 0 4\n5\n2 6 0\n2 -1 -1 2\n4\n-1 3 0\n\
some some some none\nsome some some none none\nsome some\n9 8 3 5\n10\n");
(* x? tests and narrows, e? as g names what it found (decision 133). *)
("presence.fln", "true false true\n6\n-1\n3\n101 209 0\n11\n42\n2\nabsent\n6\nfalse true\n3\n6\n15\n"); ("presence-dyn.fln", "true false\n103 209 0\nno pet\nann\n3 2\n") ];
(* x! over nothing traps at its site and names the expression. *)