diff --git a/lib/check.ml b/lib/check.ml index 55f8206f..38a04523 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -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 diff --git a/test/programs/autowrap.fln b/test/programs/autowrap.fln index 636f94ec..7b839efb 100644 --- a/test/programs/autowrap.fln +++ b/test/programs/autowrap.fln @@ -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? diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 7fb8e826..0f64c275 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -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. *)