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
;; (Name A {.f V}) an instance of a generic struct, its type arguments
;; 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
;; <ptr> a pointer, never followed
;; <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
;; 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)))
(test-flan--check "a generic instance is a struct"
(eq (plist-get n :kind) 'struct))
@ -92,13 +92,13 @@
:children))
'("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]")))
(test-flan--check "a sequence is a sequence" (eq (plist-get n :kind) 'seq))
(test-flan--check "indexed from zero"
(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.
(let* ((n (flan-inspect-parse "[[0 0] [1 ...] ...]"))
(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
sees one AST for both, so the location's file is the only thing left that
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
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 "Declare %s as dyn in %s's parameters: %s%s" n f n
(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 ->
( Printf.sprintf "%s is a %s" n ty,
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
in
Hashtbl.replace env.globals n (ty, false);
Hashtbl.replace global_forms n k;
Hashtbl.replace env.global_locs n loc
| Ast.Defconst (n, Some t, _) ->
Hashtbl.replace env.globals n (resolve env t, true);

View File

@ -610,6 +610,28 @@ let () =
checks "view-param-fix.fln"
"fn take(d) -> i32 = 1\n\nfn give(n: i32, v: dyn) -> i32\n take(v)\n\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. *)
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");