From d6e6a5dc0c91a95bf46afe4adfa6f59f3b43ed3d Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 25 Sep 2026 17:12:05 +0700 Subject: [PATCH] A program's global named as a prelude function or global shadows it for the file, with the shadowing warning a defn gets --- lib/check.ml | 23 +++++++++++++++-------- test/programs/shadow-prelude-global.flan | 14 ++++++++++++++ test/test_acceptance.ml | 5 +++++ test/test_flan.ml | 14 ++++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 test/programs/shadow-prelude-global.flan diff --git a/lib/check.ml b/lib/check.ml index 616992ec..d2193bb4 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -14543,30 +14543,37 @@ let shown_name n = else n let shadow_prelude (prelude : Ast.decl list) (decls : Ast.decl list) = - let fn_name (d : Ast.decl) = + (* A value name, and whether it is a function's. A program's global takes + a prelude function's name over as a program's function does: both are + names a call or a read reaches, and the prelude's own uses keep the + prelude's. *) + let value_name (d : Ast.decl) = match d.Ast.d with - | Ast.Defn fn | Ast.Declare (fn, _) | Ast.DeclareC (fn, _) -> Some fn.Ast.name + | Ast.Defn fn | Ast.Declare (fn, _) | Ast.DeclareC (fn, _) -> + Some (fn.Ast.name, true) + | Ast.Defvar (n, _, _, _) | Ast.Defconst (n, _, _) -> Some (n, false) | _ -> None in - let theirs = List.filter_map fn_name prelude in + let theirs = List.map fst (List.filter_map value_name prelude) in let taken = List.filter_map (fun (d : Ast.decl) -> - match fn_name d with - | Some n when List.mem n theirs -> Some (n, d.Ast.dloc) + match value_name d with + | Some (n, f) when List.mem n theirs -> Some (n, d.Ast.dloc, f) | _ -> None) decls in let warnings = List.map - (fun (n, at) -> + (fun (n, at, f) -> Loc.diag ~kind:"check/shadows-prelude" at (Printf.sprintf - "%s shadows the prelude's %s — every call in this file now \ + "%s shadows the prelude's %s — every %s in this file now \ reaches your definition" - n n)) + n n (if f then "call" else "use"))) taken in + let taken = List.map (fun (n, at, _) -> (n, at)) taken in let prelude, decls = List.fold_left (fun (prelude, decls) (n, (at : Loc.t)) -> diff --git a/test/programs/shadow-prelude-global.flan b/test/programs/shadow-prelude-global.flan new file mode 100644 index 00000000..9cd578b7 --- /dev/null +++ b/test/programs/shadow-prelude-global.flan @@ -0,0 +1,14 @@ +;;;; A program's global named as a prelude function takes the name over for +;;;; its own file, as a program's function does, and the prelude's own calls +;;;; keep the prelude's: sort still swaps with the prelude's swap. +(defonce swap i32 3) +(defonce clamp i32 4) +(defconst reverse i32 5) + +(defn main [] i32 + (println (+ swap clamp reverse)) ; 12 + (let [xs [3 1 2]] + (sort (slice xs)) + (println (at xs 0)) ; 1 + (println (at xs 2))) ; 3 + 0) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 214f522e..4080d400 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -592,6 +592,11 @@ let () = outputs "a prelude function shadowed" "programs/shadow-prelude.flan" sp_out; outputs ~x86:true "a prelude function shadowed, x86" "programs/shadow-prelude.flan" sp_out; + (* And by a program's global, the same way. *) + outputs "a prelude function shadowed by a global" + "programs/shadow-prelude-global.flan" "12\n1\n3\n"; + outputs ~x86:true "a prelude function shadowed by a global, x86" + "programs/shadow-prelude-global.flan" "12\n1\n3\n"; (* (max-value T) and (min-value T), concrete and inside a generic. *) let maxof_out = "255\n0\n127\n-128\n2147483647\n-9223372036854775808\n\ diff --git a/test/test_flan.ml b/test/test_flan.ml index fa7c57ab..836e7166 100644 --- a/test/test_flan.ml +++ b/test/test_flan.ml @@ -5953,6 +5953,20 @@ let () = | _ -> check "a defn of a prelude function's name warns exactly once" false); accepts "a defn of a prelude function's name is not defined twice" prelude_src; + (* A global takes the name over the same way, with the same warning. *) + (match + snd (Check.shadow_prelude (Parse.program (Prelude.forms ())) + (program "(defonce swap i32 3)")) + with + | [ d ] -> + check "a global of a prelude function's name warns once" + (d.Loc.kind = "check/shadows-prelude" + && d.Loc.dmsg + = "swap shadows the prelude's swap — every use in this file now \ + reaches your definition") + | _ -> check "a global of a prelude function's name warns exactly once" false); + accepts "a global of a prelude function's name is not defined twice" + "(defonce swap i32 3)\n(defn f [] i32 swap)"; rejects_check "a struct of a prelude type's name is still defined twice" "(defstruct Form [x i32])" ~needle:"Form is defined twice"; (* An operator is a builtin like any other and shadows like any other.