From b4e19bebf879f9f52a1ca523bc4cb76fe4d6582a Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 25 Sep 2026 07:06:17 +0700 Subject: [PATCH] A loop's initialisers are checked in order and each sees the names bound before it, as a let's do --- lib/check.ml | 11 +++++------ test/programs/recur.flan | 6 ++++++ test/test_acceptance.ml | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/check.ml b/lib/check.ml index 1383ce91..0865e717 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -4737,8 +4737,10 @@ and check_dotimes ctx ~want loc label name (b : Ast.bounds) body = and check_loop ctx ?want loc bs body = scoped ctx (fun () -> (* Each initial value is evaluated once, before the loop, exactly as a - [let]'s is and as [dotimes]'s bound is. *) - let inits = + [let]'s is and as [dotimes]'s bound is — and bound before the next is + checked, as a [let]'s is, so a later initialiser sees an earlier + name. *) + let binds = map_lr (fun (n, v) -> let v = check ctx v in @@ -4747,12 +4749,9 @@ and check_loop ctx ?want loc bs body = fail v.Tast.loc "%s would be bound to %s, which is not a value" n (Types.to_string v.Tast.ty) | _ -> ()); - (n, v)) + (bind ctx n v.Tast.ty ~assignable:true, v)) bs in - let binds = - List.map (fun (n, v) -> (bind ctx n v.Tast.ty ~assignable:true, v)) inits - in let names = List.map (fun (slot, v) -> (slot, v.Tast.ty)) binds in (* The singleton is [in_loop]'s doing: it sits in this recursive group and is therefore monomorphic, and every other caller hands it a list. *) diff --git a/test/programs/recur.flan b/test/programs/recur.flan index 5c6f32a0..e03cf332 100644 --- a/test/programs/recur.flan +++ b/test/programs/recur.flan @@ -99,4 +99,10 @@ (Some v) (recur (+ i 1) (+ acc v)) None acc))) (println "") ; 0+1+2+3 = 6 + + ;; The bindings are sequential, as a let's are: the second initialiser reads + ;; the first name. Only the initial values are; recur still rebinds at once. + (print (loop [a 1 b (+ a 10)] + (if (> a 3) b (recur (+ a 1) (+ b a))))) + (println "") ; 11+1+2+3 = 17 0) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index cc32cacc..7a21bb0c 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -501,7 +501,7 @@ let () = rather than running out of stack. The swap line is the other — recur rebinds every name at once, and interleaved writes would print 1. *) outputs "loop and recur" "programs/recur.flan" - "10\n2\n21\n8\n10000000\n64\n012\n0\n4\n012\n6\n"; + "10\n2\n21\n8\n10000000\n64\n012\n0\n4\n012\n6\n17\n"; (* into. The count of pulls is the assertion a unit test cannot make: one pass, one call per element per stage it reaches, and no intermediate collection anywhere. The two show lines either side of it are the same