A parameter vector paired by a lowercase type the program declares is warned at, naming the type and where it is declared
This commit is contained in:
parent
1716b146cd
commit
e616a29ceb
7
TODO.org
7
TODO.org
@ -854,13 +854,6 @@ Already fixed by 3672da2, which blames the arm that is not a compiler temp; the
|
|||||||
caret is on the last operand and =test/test_flan.ml= asserts its column. Rules
|
caret is on the last operand and =test/test_flan.ml= asserts its column. Rules
|
||||||
out relabelling the else arm, a bool sentinel, and inverting the condition.
|
out relabelling the else arm, a bool sentinel, and inverting the condition.
|
||||||
|
|
||||||
** NEXT Signature pairing's cold-rebuild edge
|
|
||||||
Decided 2026-09-25: the type takes precedence, as today. The warning is at the parameter site: where a name in a parameter vector is read as a program-declared type but could also have been read as a parameter name, the parameter vector gets a warning naming the type and where it is declared.
|
|
||||||
Whether a parameter vector reads as one annotated parameter or two dyn ones
|
|
||||||
depends on what type names exist, so adding a type can silently re-pair an
|
|
||||||
existing signature between compiles. A changed-pairing warning was proposed and
|
|
||||||
not queued.
|
|
||||||
|
|
||||||
** DONE A typed container crosses into dyn as a view, and only from permanent storage
|
** DONE A typed container crosses into dyn as a view, and only from permanent storage
|
||||||
CLOSED: [2026-09-20]
|
CLOSED: [2026-09-20]
|
||||||
The descriptor is pointer, length and element type — a slice plus the piece a
|
The descriptor is pointer, length and element type — a slice plus the piece a
|
||||||
|
|||||||
63
lib/check.ml
63
lib/check.ml
@ -1631,9 +1631,37 @@ let dyn_param_or_typo env n loc =
|
|||||||
parameters are lowercase"
|
parameters are lowercase"
|
||||||
n
|
n
|
||||||
|
|
||||||
let pair_params ?(also = fun _ -> false) env (items : Ast.pitem list)
|
(* The pairings a parameter vector owes to a type the program declares under
|
||||||
: Ast.field list =
|
a name that is also a legal parameter name — a lowercase one, since a
|
||||||
|
capitalised name is refused as a parameter. [(defn f [p point] ...)] is one
|
||||||
|
parameter while [point] is a type and two dyn ones the moment it is not,
|
||||||
|
so adding or removing the type re-pairs the signature with no edit to it.
|
||||||
|
The type still wins; this is the warning at the parameter, filled by
|
||||||
|
[pair_decls] and printed by [build_program] with the other warnings. *)
|
||||||
|
let pairing_warnings : Loc.diag list ref = ref []
|
||||||
|
|
||||||
|
let pair_params ?(also = fun _ -> false) ?(declared = fun _ -> None) env
|
||||||
|
(items : Ast.pitem list) : Ast.field list =
|
||||||
let is_type_name env n = is_type_name env n || also n in
|
let is_type_name env n = is_type_name env n || also n in
|
||||||
|
let warn_pairing n t tloc =
|
||||||
|
let bare =
|
||||||
|
match String.rindex_opt t '/' with
|
||||||
|
| Some i -> String.sub t (i + 1) (String.length t - i - 1)
|
||||||
|
| None -> t
|
||||||
|
in
|
||||||
|
match declared t with
|
||||||
|
| Some (what, (at : Loc.t))
|
||||||
|
when bare <> "" && bare.[0] >= 'a' && bare.[0] <= 'z' ->
|
||||||
|
pairing_warnings :=
|
||||||
|
Loc.diag ~kind:"check/parameter-reads-a-type" tloc
|
||||||
|
(Printf.sprintf
|
||||||
|
"[%s %s] is one parameter %s of type %s, the %s declared at %s, \
|
||||||
|
and not two dyn parameters. If two were meant, give the second \
|
||||||
|
a name no type has"
|
||||||
|
n t n t what (Loc.to_string at))
|
||||||
|
:: !pairing_warnings
|
||||||
|
| _ -> ()
|
||||||
|
in
|
||||||
let dyn loc = { Ast.t = Ast.Tname "dyn"; tloc = loc } in
|
let dyn loc = { Ast.t = Ast.Tname "dyn"; tloc = loc } in
|
||||||
let rec go = function
|
let rec go = function
|
||||||
| [] -> []
|
| [] -> []
|
||||||
@ -1649,6 +1677,7 @@ let pair_params ?(also = fun _ -> false) env (items : Ast.pitem list)
|
|||||||
| Ast.Pname (n, loc) :: Ast.Ptype t :: rest ->
|
| Ast.Pname (n, loc) :: Ast.Ptype t :: rest ->
|
||||||
{ Ast.fname = n; fty = t; floc = loc } :: go rest
|
{ Ast.fname = n; fty = t; floc = loc } :: go rest
|
||||||
| Ast.Pname (n, loc) :: Ast.Pname (t, tloc) :: rest when is_type_name env t ->
|
| Ast.Pname (n, loc) :: Ast.Pname (t, tloc) :: rest when is_type_name env t ->
|
||||||
|
warn_pairing n t tloc;
|
||||||
{ Ast.fname = n; fty = { Ast.t = Ast.Tname t; tloc }; floc = loc } :: go rest
|
{ Ast.fname = n; fty = { Ast.t = Ast.Tname t; tloc }; floc = loc } :: go rest
|
||||||
(* The slot after this one is not a type, so this one is a parameter with
|
(* The slot after this one is not a type, so this one is a parameter with
|
||||||
no type written — unless the slot after it only *looks* unlike a type
|
no type written — unless the slot after it only *looks* unlike a type
|
||||||
@ -1778,10 +1807,32 @@ let pair_decls env (decls : Ast.decl list) : Ast.decl list =
|
|||||||
match d.Ast.d with Ast.Defclass (n, _) -> Some n | _ -> None)
|
match d.Ast.d with Ast.Defclass (n, _) -> Some n | _ -> None)
|
||||||
decls
|
decls
|
||||||
in
|
in
|
||||||
|
(* The types the program declares, with what kind and where, for
|
||||||
|
[pair_params]'s warning. The prelude's are left out: its names are the
|
||||||
|
language's, not a declaration the reader made. *)
|
||||||
|
let types = Hashtbl.create 16 in
|
||||||
|
List.iter
|
||||||
|
(fun (d : Ast.decl) ->
|
||||||
|
let add n what =
|
||||||
|
if d.Ast.dloc.Loc.file <> Prelude.file then
|
||||||
|
Hashtbl.replace types n (what, d.Ast.dloc)
|
||||||
|
in
|
||||||
|
match d.Ast.d with
|
||||||
|
| Ast.Defstruct (n, _, _) -> add n "struct"
|
||||||
|
| Ast.Defenum (n, _) -> add n "enum"
|
||||||
|
| Ast.Defalias (n, _) -> add n "alias"
|
||||||
|
| Ast.Defdata (n, _) -> add n "data type"
|
||||||
|
| Ast.Defunion (n, _) -> add n "union"
|
||||||
|
| _ -> ())
|
||||||
|
decls;
|
||||||
|
pairing_warnings := [];
|
||||||
let fn (f : Ast.fn) =
|
let fn (f : Ast.fn) =
|
||||||
match f.Ast.praw with
|
match f.Ast.praw with
|
||||||
| None -> f
|
| None -> f
|
||||||
| Some items -> { f with Ast.params = pair_params env items; praw = None }
|
| Some items ->
|
||||||
|
{ f with
|
||||||
|
Ast.params = pair_params ~declared:(Hashtbl.find_opt types) env items;
|
||||||
|
praw = None }
|
||||||
in
|
in
|
||||||
List.map
|
List.map
|
||||||
(fun (d : Ast.decl) ->
|
(fun (d : Ast.decl) ->
|
||||||
@ -14108,6 +14159,12 @@ let build_program ~keep_going ?tolerate (decls : Ast.decl list) :
|
|||||||
cannot make the next body fail — which is what makes a declaration a
|
cannot make the next body fail — which is what makes a declaration a
|
||||||
resync point that needs no resynchronising. *)
|
resync point that needs no resynchronising. *)
|
||||||
let decls = collect env decls in
|
let decls = collect env decls in
|
||||||
|
if !print_warnings then
|
||||||
|
List.iter
|
||||||
|
(fun (d : Loc.diag) ->
|
||||||
|
prerr_endline
|
||||||
|
(Loc.entry ~mark:'~' ~label:"warning: " d.Loc.dloc d.Loc.dmsg))
|
||||||
|
(List.rev !pairing_warnings);
|
||||||
check_finite env;
|
check_finite env;
|
||||||
check_union_members env;
|
check_union_members env;
|
||||||
let s = Loc.sink ~on:keep_going in
|
let s = Loc.sink ~on:keep_going in
|
||||||
|
|||||||
@ -5814,6 +5814,29 @@ let () =
|
|||||||
(match checked shadow_src with
|
(match checked shadow_src with
|
||||||
| _ -> true
|
| _ -> true
|
||||||
| exception Loc.Error _ -> false);
|
| exception Loc.Error _ -> false);
|
||||||
|
(* A parameter vector paired by a lowercase type the program declares reads
|
||||||
|
as two dyn parameters the day the type goes, so the pairing is warned at,
|
||||||
|
naming the type and where it is declared. A capitalised type cannot be a
|
||||||
|
parameter name, so it has nothing to warn about. *)
|
||||||
|
(match
|
||||||
|
checked "(defstruct point [x i32])\n(defstruct Vec2 [x i32])\n\
|
||||||
|
(defn px [p point] i32 (.x p))\n(defn vx [v Vec2] i32 (.x v))"
|
||||||
|
with
|
||||||
|
| _ ->
|
||||||
|
(match !Check.pairing_warnings with
|
||||||
|
| [ d ] ->
|
||||||
|
check "a lowercase declared type in a parameter vector is warned at"
|
||||||
|
(d.Loc.kind = "check/parameter-reads-a-type"
|
||||||
|
&& d.Loc.dloc.Loc.line = 3 && d.Loc.dloc.Loc.col = 13
|
||||||
|
&& d.Loc.dmsg
|
||||||
|
= "[p point] is one parameter p of type point, the struct \
|
||||||
|
declared at <test>:1:1, and not two dyn parameters. If two \
|
||||||
|
were meant, give the second a name no type has")
|
||||||
|
| ds ->
|
||||||
|
check
|
||||||
|
(Printf.sprintf "one pairing warning, not %d" (List.length ds))
|
||||||
|
false)
|
||||||
|
| exception Loc.Error _ -> check "the paired program checks" false);
|
||||||
check "a program that shadows nothing is warned at not at all"
|
check "a program that shadows nothing is warned at not at all"
|
||||||
(Check.shadowed_builtins (program "(defn f [] i32 1)") = []);
|
(Check.shadowed_builtins (program "(defn f [] i32 1)") = []);
|
||||||
(* A prelude function's name is taken over the same way, for the calls in
|
(* A prelude function's name is taken over the same way, for the calls in
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user