From d0e33331b5e907bd718b6834bdc6f15991135c01 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sun, 20 Sep 2026 18:19:39 +0700 Subject: [PATCH] An expectation outranks the join, because an expectation is information --- lib/check.ml | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/check.ml b/lib/check.ml index 3cc77d2..9ee4a51 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -7580,15 +7580,26 @@ and binary ctx ?(dyn_ok = false) ?(join = true) name loc ~want args = if a.Tast.ty = Types.Dyn || b.Tast.ty = Types.Dyn || Types.equal a.Tast.ty b.Tast.ty then a, b - (* Both operands are already in hand here, so the join is read off - directly rather than through [join_pair]'s retry. Whichever one the - other widens into is the pair's type; with no join, the re-check - produces the refusal, which names the cast at y's own line. *) + (* Asking y for [a]'s type stays the first thing tried, and not only for + continuity: y was checked above with no expectation at all, and an + expectation is information. A sum of two products handed to an f32 + function is the case — test/programs/math.flan does it — because every + literal inside those products defaults to f64 on its own terms, so + reading the join off the two unexpected halves would answer f64 for a + form the site asked to be f32. The re-check builds them at f32 as it + always did. + + [b] is only used when that re-check refuses, which is the direction + [expect] cannot serve: a is the narrower operand and it is the one + that has to move. Nothing is checked a third time — the own-terms [b] + already in hand is the answer. *) else - (match Types.join a.Tast.ty b.Tast.ty with - | Some t -> - widen a.Tast.loc t a, widen b.Tast.loc t b - | None -> a, check ctx ~want:a.Tast.ty y) + (match check ctx ~want:a.Tast.ty y with + | b' -> a, b' + | exception e -> + if join && Types.widens_to ~from:a.Tast.ty ~into:b.Tast.ty then + widen a.Tast.loc b.Tast.ty a, b + else raise e) end else begin let a = check ctx ?want x in