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
|
||||
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
|
||||
CLOSED: [2026-09-20]
|
||||
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"
|
||||
n
|
||||
|
||||
let pair_params ?(also = fun _ -> false) env (items : Ast.pitem list)
|
||||
: Ast.field list =
|
||||
(* The pairings a parameter vector owes to a type the program declares under
|
||||
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 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 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.fname = n; fty = t; floc = loc } :: go rest
|
||||
| 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
|
||||
(* 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
|
||||
@ -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)
|
||||
decls
|
||||
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) =
|
||||
match f.Ast.praw with
|
||||
| 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
|
||||
List.map
|
||||
(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
|
||||
resync point that needs no resynchronising. *)
|
||||
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_union_members env;
|
||||
let s = Loc.sink ~on:keep_going in
|
||||
|
||||
@ -5814,6 +5814,29 @@ let () =
|
||||
(match checked shadow_src with
|
||||
| _ -> true
|
||||
| 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.shadowed_builtins (program "(defn f [] i32 1)") = []);
|
||||
(* A prelude function's name is taken over the same way, for the calls in
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user