A refused dyn view of a global names its own defining form as the fix, and every dyn fix is spelled in the syntax the code was written in

This commit is contained in:
Joseph Ferano 2026-09-25 22:27:36 +07:00
parent c1b21edaf4
commit 35aa5a8c0f
4 changed files with 46 additions and 5 deletions

View File

@ -133,7 +133,7 @@ reply without a daemon behind them, and so that this file names
;; its span bound of 8 fields ;; its span bound of 8 fields
;; (Name A {.f V}) an instance of a generic struct, its type arguments ;; (Name A {.f V}) an instance of a generic struct, its type arguments
;; kept on the head (:text) and the name alone as :type ;; kept on the head (:text) and the name alone as :type
;; [ V V V] an array or a slice, ` ...' likewise ;; [V V V] an array or a slice, ` ...' likewise
;; (some V) / none an option ;; (some V) / none an option
;; <ptr> a pointer, never followed ;; <ptr> a pointer, never followed
;; <Name> a named type the walk had no structure for ;; <Name> a named type the walk had no structure for

View File

@ -76,7 +76,7 @@
;; A generic struct's instance, its type arguments between the name and the ;; A generic struct's instance, its type arguments between the name and the
;; brace, compound ones included. Written back as it was read. ;; brace, compound ones included. Written back as it was read.
(let* ((src "(Pair (Option u8) [3 i32] {.a (some 1) .b [ 1 2 3]})") (let* ((src "(Pair (Option u8) [3 i32] {.a (some 1) .b [1 2 3]})")
(n (flan-inspect-parse src))) (n (flan-inspect-parse src)))
(test-flan--check "a generic instance is a struct" (test-flan--check "a generic instance is a struct"
(eq (plist-get n :kind) 'struct)) (eq (plist-get n :kind) 'struct))
@ -92,13 +92,13 @@
:children)) :children))
'("a"))) '("a")))
;; [ 0 42 0] — Types.Slice and Types.Array both write this. ;; [0 42 0] — Types.Slice and Types.Array both write this.
(let ((n (flan-inspect-parse "[0 42 0]"))) (let ((n (flan-inspect-parse "[0 42 0]")))
(test-flan--check "a sequence is a sequence" (eq (plist-get n :kind) 'seq)) (test-flan--check "a sequence is a sequence" (eq (plist-get n :kind) 'seq))
(test-flan--check "indexed from zero" (test-flan--check "indexed from zero"
(equal (mapcar #'car (plist-get n :children)) '(0 1 2)))) (equal (mapcar #'car (plist-get n :children)) '(0 1 2))))
;; [ [ 0 0] [ 1 ...] ...] — span truncation at both levels, which is what ;; [[0 0] [1 ...] ...] — span truncation at both levels, which is what
;; sand's [100 [100 u32]] actually produces. ;; sand's [100 [100 u32]] actually produces.
(let* ((n (flan-inspect-parse "[[0 0] [1 ...] ...]")) (let* ((n (flan-inspect-parse "[[0 0] [1 ...] ...]"))
(kids (plist-get n :children))) (kids (plist-get n :children)))

View File

@ -3454,7 +3454,13 @@ let view_elem_lit loc (k : int64) =
(* A fix is spelled in the syntax of the file the mistake is in: the checker (* A fix is spelled in the syntax of the file the mistake is in: the checker
sees one AST for both, so the location's file is the only thing left that sees one AST for both, so the location's file is the only thing left that
says which one the reader is looking at. *) says which one the reader is looking at. *)
let fln_source (loc : Loc.t) = Filename.check_suffix loc.Loc.file ".fln" let fln_source (loc : Loc.t) = Source.indented_at loc
(* Which form defined each mutable global, [defonce] or [def], so a fix that
rewrites the definition keeps the form the programmer chose. Filled where
globals are collected; a name missing from it (a defconst) is given
[defonce]. *)
let global_forms : (string, Ast.reinit) Hashtbl.t = Hashtbl.create 16
(* The function being checked and its parameters, by slot. A stack because (* The function being checked and its parameters, by slot. A stack because
a generic's copy is checked from inside the body that called it. *) a generic's copy is checked from inside the body that called it. *)
@ -3496,6 +3502,18 @@ let view_refusal kind loc (e : Tast.expr) reason =
( Printf.sprintf "%s is a %s parameter" n ty, ( Printf.sprintf "%s is a %s parameter" n ty,
Printf.sprintf "Declare %s as dyn in %s's parameters: %s%s" n f n Printf.sprintf "Declare %s as dyn in %s's parameters: %s%s" n f n
(if fln then ": dyn" else " dyn") ) (if fln then ": dyn" else " dyn") )
| None, Some n when (match e.Tast.e with Tast.Global _ -> true | _ -> false) ->
let every =
match e.Tast.e with
| Tast.Global g -> Hashtbl.find_opt global_forms g = Some Ast.Every
| _ -> false
in
( Printf.sprintf "%s is a %s" n ty,
Printf.sprintf "Define %s as a dyn value, as in %s" n
(if fln then
Printf.sprintf "%s %s: dyn = [...]" (if every then "def" else "once") n
else
Printf.sprintf "(%s %s dyn [...])" (if every then "def" else "defonce") n) )
| None, Some n -> | None, Some n ->
( Printf.sprintf "%s is a %s" n ty, ( Printf.sprintf "%s is a %s" n ty,
Printf.sprintf "Build %s as a dyn value where it is made, as in %s" n Printf.sprintf "Build %s as a dyn value where it is made, as in %s" n
@ -14601,6 +14619,7 @@ let collect env (decls : Ast.decl list) =
(match k with Ast.Once -> "defonce" | Ast.Every -> "def") n (match k with Ast.Once -> "defonce" | Ast.Every -> "def") n
in in
Hashtbl.replace env.globals n (ty, false); Hashtbl.replace env.globals n (ty, false);
Hashtbl.replace global_forms n k;
Hashtbl.replace env.global_locs n loc Hashtbl.replace env.global_locs n loc
| Ast.Defconst (n, Some t, _) -> | Ast.Defconst (n, Some t, _) ->
Hashtbl.replace env.globals n (resolve env t, true); Hashtbl.replace env.globals n (resolve env t, true);

View File

@ -610,6 +610,28 @@ let () =
checks "view-param-fix.fln" checks "view-param-fix.fln"
"fn take(d) -> i32 = 1\n\nfn give(n: i32, v: dyn) -> i32\n take(v)\n\n\ "fn take(d) -> i32 = 1\n\nfn give(n: i32, v: dyn) -> i32\n take(v)\n\n\
fn main() -> i32 = 0\n"; fn main() -> i32 = 0\n";
(* A global's fix redefines it, in the form it was defined with. *)
let show_flan = "(defn show [d dyn] i32 1)\n" in
refused "view-global.flan"
("(defonce gs [2 i32] [1 2])\n" ^ show_flan ^ "(defn main [] i32 (show gs))\n")
[ "gs is a [2 i32]"; "as in (defonce gs dyn [...])" ];
refused "view-global-def.flan"
("(def gs [2 i32] [1 2])\n" ^ show_flan ^ "(defn main [] i32 (show gs))\n")
[ "as in (def gs dyn [...])" ];
refused "view-global.fln"
"once gs: [2 i32] = [1 2]\n\nfn show(d) -> i32 = 1\n\nfn main() -> i32 = show(gs)\n"
[ "gs is a [2 i32]"; "as in once gs: dyn = [...]" ];
checks "view-global-fix.flan"
("(defonce gs dyn [1 2])\n(def hs dyn [1 2])\n" ^ show_flan
^ "(defn main [] i32 (show gs) (show hs))\n");
checks "view-global-fix.fln"
"once gs: dyn = [1 2]\n\nfn show(d) -> i32 = 1\n\nfn main() -> i32 = show(gs)\n";
(* The fix is spelled in the syntax the code was sent in, not the one the
file's name implies: an editor request from an indented buffer. *)
Source.with_code ~syntax:Source.Indented ~at:None (fun () ->
refused "unit-tail-request.flan"
"(defn f [coll] dyn (let [i 1] (while (< i 3) (++ i))))\n(defn main [] () (f 1))\n"
[ "fn f(...) -> ()" ]);
(* The fix both of them name. *) (* The fix both of them name. *)
checks "view-fix.fln" checks "view-fix.fln"
(poke_fln ^ "fn main() -> ()\n let d: dyn = [6 2 4 9]\n poke(d)\n poke(the(dyn, [1 2]))\n"); (poke_fln ^ "fn main() -> ()\n let d: dyn = [6 2 4 9]\n poke(d)\n poke(the(dyn, [1 2]))\n");