A refused dyn view of a parameter names its declaration as the fix

This commit is contained in:
Joseph Ferano 2026-09-25 22:09:44 +07:00
parent 4f060bb648
commit d16fe79b79
2 changed files with 38 additions and 9 deletions

View File

@ -3456,6 +3456,10 @@ let view_elem_lit loc (k : int64) =
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) = Filename.check_suffix loc.Loc.file ".fln"
(* 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. *)
let grow_params : (ctx * (int * Ast.field) list) list ref = ref []
(* The two refusals below share their subject and their fix. The subject is (* The two refusals below share their subject and their fix. The subject is
the name as written when the refused value is a bare name, so the message the name as written when the refused value is a bare name, so the message
can say [a is a [4 i64]]; anything longer is "this". The fix is the one can say [a is a [4 i64]]; anything longer is "this". The fix is the one
@ -3467,7 +3471,7 @@ let view_subject (e : Tast.expr) =
| Some s | Some s
when s <> "" when s <> ""
&& String.for_all && String.for_all
(fun c -> not (List.mem c [ ' '; '('; ')'; '['; ']'; '{'; '}'; '"' ])) (fun c -> not (List.mem c [ ' '; '('; ')'; '['; ']'; '{'; '}'; '"'; '.'; ',' ]))
s -> s ->
Some s Some s
| _ -> None | _ -> None
@ -3475,14 +3479,29 @@ let view_subject (e : Tast.expr) =
let view_refusal kind loc (e : Tast.expr) reason = let view_refusal kind loc (e : Tast.expr) reason =
let ty = Types.to_string e.Tast.ty in let ty = Types.to_string e.Tast.ty in
let fln = fln_source loc in let fln = fln_source loc in
(* A parameter is made by the caller, so its fix is its declaration. The
name is compared as well as the slot: a closure numbers its slots from
zero too, and is checked while its enclosing function is on the stack. *)
let param =
match e.Tast.e, view_subject e, !grow_params with
| Tast.Local s, Some n, (ctx, ps) :: _ ->
(match List.assoc_opt s ps with
| Some (p : Ast.field) when p.Ast.fname = n -> Some (ctx.owner, n)
| _ -> None)
| _ -> None
in
let subject, fix = let subject, fix =
match view_subject e with match param, view_subject e with
| Some n -> | Some (f, n), _ ->
( 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 ->
( 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
(if fln then Printf.sprintf "let %s: dyn = [...]" n (if fln then Printf.sprintf "let %s: dyn = [...]" n
else Printf.sprintf "(let [%s (the dyn [...])] ...)" n) ) else Printf.sprintf "(let [%s (the dyn [...])] ...)" n) )
| None -> | None, None ->
( Printf.sprintf "This is a %s" ty, ( Printf.sprintf "This is a %s" ty,
Printf.sprintf "Build it as a dyn value where it is made, as in %s" Printf.sprintf "Build it as a dyn value where it is made, as in %s"
(if fln then "the(dyn, [...])" else "(the dyn [...])") ) (if fln then "the(dyn, [...])" else "(the dyn [...])") )
@ -5153,11 +5172,9 @@ let if_depth = ref 0
(* A Vec or a Map parameter is a copy of the caller's header — Odin's rule — (* A Vec or a Map parameter is a copy of the caller's header — Odin's rule —
so growing it reallocates a block only this function's copy points at, and so growing it reallocates a block only this function's copy points at, and
the caller's container never sees the elements. The function being checked the caller's container never sees the elements. The warnings found so far,
and its container parameters, by slot, and the warnings found so far, one one per parameter, printed by [build_program]; the parameters themselves
per parameter, printed by [build_program]. A stack because a generic's copy are [grow_params], above [view_refusal], which reads them too. *)
is checked from inside the body that called it. *)
let grow_params : (ctx * (int * Ast.field) list) list ref = ref []
let grow_warnings : Loc.diag list ref = ref [] let grow_warnings : Loc.diag list ref = ref []
let note_grown ctx op loc (target : Tast.expr) = let note_grown ctx op loc (target : Tast.expr) =

View File

@ -589,6 +589,18 @@ let () =
(poke_fln ^ "fn main() -> ()\n let d = [6 2 4 9]\n poke(d)\n") (poke_fln ^ "fn main() -> ()\n let d = [6 2 4 9]\n poke(d)\n")
[ "d is a [4 i32]"; "only when its elements are i64, f64 or bool, and these are i32"; [ "d is a [4 i32]"; "only when its elements are i64, f64 or bool, and these are i32";
"as in let d: dyn = [...]" ]; "as in let d: dyn = [...]" ];
(* A parameter is made by the caller, so its fix is its declaration. *)
refused "view-param.fln"
"fn take(d) -> i32 = 1\n\nfn give(n: i32, v: [4 i64]) -> i32\n take(v)\n\n\
fn main() -> i32 = 0\n"
[ "v is a [4 i64] parameter"; "Declare v as dyn in give's parameters: v: dyn" ];
refused "view-param.flan"
"(defn take [d dyn] i32 1)\n(defn give [n i32 v (Vec i64)] i32 (take v))\n\
(defn main [] i32 0)\n"
[ "v is a (Vec i64) parameter"; "Declare v as dyn in give's parameters: v dyn" ];
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";
(* 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");