diff --git a/lib/parse.ml b/lib/parse.ml index 3d9128b..6e04863 100644 --- a/lib/parse.ml +++ b/lib/parse.ml @@ -14,15 +14,21 @@ let sym (f : Form.t) = | Sym s -> s | _ -> fail f "expected a name, found %s" (Form.to_string f) -(* Names for the temporaries a destructuring binding needs — the value is bound - once and every name in the pattern reads *that*, so a pattern over a call - calls it once. [~] is a delimiter in the reader, so no symbol anyone can - write contains one: these cannot collide with a source name and a source - name cannot shadow one. Reset per program so the names, and therefore the - slot numbering downstream, are the same every run. *) +(* Names for the temporaries this file mints — the value is bound once and + everything that needs it reads *that*, so a destructuring pattern over a + call calls it once and a short-circuit operand is evaluated once. [~] is a + delimiter in the reader, so no symbol anyone can write contains one: these + cannot collide with a source name and a source name cannot shadow one. + Reset per program so the names, and therefore the slot numbering + downstream, are the same every run. + + The purpose is part of the name because these names are shown: the + inspector lists a frame's locals by name, and a short-circuit temp called + [destructure~3] is a plain lie about where it came from. One counter across + all purposes, so a name is still unique whatever minted it. *) let temps = ref 0 -let fresh_temp () = incr temps; Printf.sprintf "destructure~%d" !temps +let fresh_temp what = incr temps; Printf.sprintf "%s~%d" what !temps (* Destructuring binds in [let] and nowhere else. Every other binding position — a [defn] parameter, a [defstruct] field, an [fn] parameter, a [dotimes] @@ -673,7 +679,7 @@ and bindings f (items : Form.t list) : Ast.binding list = the reference as well as the binding is what makes the two impossible to separate by accident. *) and temp (p : Form.t) (v : Ast.expr) : Ast.expr * Ast.binding = - let t = fresh_temp () in + let t = fresh_temp "destructure" in ({ Ast.e = Ast.Var t; loc = p.loc }, { Ast.bname = t; bty = None; bval = v; bloc = p.loc }) @@ -915,7 +921,24 @@ and cond f (args : Form.t list) : Ast.expr = because the sentinel it answered was a bool literal that boxed to fit whatever the real branch was; neither is now, and they are at least symmetric about it. Making bool and dyn arms join as dyn is a check_if - question, noted in FIX.org under item 7 and not decided here. *) + question, noted in FIX.org under item 7 and not decided here. + + One known wart, measured rather than guessed, and left alone deliberately. + In a want-free position — [(println (and true true (vec-new i32)))] — the + caret lands on the *second* [true] and not on the vec: the last operand is + the then arm, check_if types the then arm first, and the mismatch is + therefore reported against the else arm, which is the previous operand's + temp. An operand anywhere but last is a condition instead, so check_truthy + blames it at its own loc and the caret is right; [or] is right everywhere, + because there the chain and not the sentinel sits in the else arm. Giving + the else arm's [Var] node the *last* operand's loc moves the caret onto the + vec and makes the sentence read backwards — "expected (Vec i32), found + bool" under a caret on the thing that is the (Vec i32) — so it is not an + improvement; answering a bool literal again would revert the paragraph + above; and inverting the condition to move the last operand into the else + arm buys a [not] per operand and worse locs than it fixes. What would + actually fix it is check_if preferring the arm that is not a compiler temp + when it reports, which is check.ml's call. Written up in FIX.org. *) and shortcircuit f (args : Form.t list) ~is_and : Ast.expr = let mk e = { Ast.e; loc = f.loc } in let rec go = function @@ -923,7 +946,7 @@ and shortcircuit f (args : Form.t list) ~is_and : Ast.expr = | [ last ] -> expr last | x :: rest -> let ex = expr x in - let t = fresh_temp () in + let t = fresh_temp (if is_and then "and" else "or") in let tvar = { Ast.e = Ast.Var t; loc = ex.Ast.loc } in let bind = { Ast.bname = t; bty = None; bval = ex; bloc = ex.Ast.loc } in let rest = go rest in