A refusal that suggests a fix suggests one that compiles, for vec-new, map-new and a near miss that is a value
This commit is contained in:
parent
6c9635cd53
commit
83b244475a
8
TODO.org
8
TODO.org
@ -1034,6 +1034,14 @@ should not pay for identity and metadata. Not implemented.
|
||||
function nosuch" twice at the same place and counts 2 errors — once from the
|
||||
abstract pass and once from the instantiation.
|
||||
|
||||
** DONE Two refusals suggested something that does not compile
|
||||
CLOSED: [2026-09-25]
|
||||
=vec-new= and =map-new= with no type no longer say "or give the binding a type";
|
||||
they name the type arguments alone, and =(the T expr)= joins them when it lands.
|
||||
An unknown call whose near miss is a value — =(context-allocator)= against
|
||||
=context/allocator=, or a global — says the name is a value written without
|
||||
parentheses, and names no call at all when the call had arguments.
|
||||
|
||||
* Backends
|
||||
|
||||
** DONE The x86 backend tracks LLVM at -O0
|
||||
|
||||
34
lib/check.ml
34
lib/check.ml
@ -6751,7 +6751,7 @@ and vec_new_elem ctx ~want loc args =
|
||||
| _ ->
|
||||
fail loc
|
||||
"nothing here says what (vec-new) is a Vec of — write the element \
|
||||
type, as (vec-new i32), or give the binding a type")
|
||||
type, as (vec-new i32)")
|
||||
|
||||
(* A type written as an argument to vec-new or map-new, read back out of the
|
||||
expression Parse made of it. Only the shapes that cannot be a value there:
|
||||
@ -6817,18 +6817,18 @@ and map_new_types ctx ~want loc args =
|
||||
| a :: _ when type_of_expr a <> None ->
|
||||
fail loc
|
||||
"(map-new) names a key and no value — write both, as (map-new string \
|
||||
i32), or give the binding a type"
|
||||
i32)"
|
||||
| { Ast.e = Ast.Var k; _ } :: rest when is_type k && rest = [] ->
|
||||
fail loc
|
||||
"(map-new %s) names a key and no value — write both, as (map-new %s \
|
||||
i32), or give the binding a type" k k
|
||||
i32)" k k
|
||||
| _ ->
|
||||
(match want with
|
||||
| Some (Types.Map (k, v)) -> k, v, args
|
||||
| _ ->
|
||||
fail loc
|
||||
"nothing here says what (map-new) maps — write the key and value \
|
||||
types, as (map-new string i32), or give the binding a type")
|
||||
types, as (map-new string i32)")
|
||||
|
||||
(* The element type, or the reason this is not a Vec. *)
|
||||
and vec_elem loc what (t : Types.t) =
|
||||
@ -9219,7 +9219,33 @@ and ordinary_call ctx ~want loc name args =
|
||||
| Some _ as m -> m
|
||||
| None -> if capitalised then near_miss ctx.env name else None
|
||||
in
|
||||
(* A near miss that names a value rather than a function is still the
|
||||
near miss, but [(m)] would be refused in its turn, so the sentence
|
||||
says how that name is written instead. *)
|
||||
let callable m =
|
||||
let fn_ty = function
|
||||
| Types.Fn _ | Types.CFn _ | Types.Dyn -> true
|
||||
| _ -> false
|
||||
in
|
||||
match lookup ctx m with
|
||||
| Some b -> fn_ty b.bty
|
||||
| None ->
|
||||
match Hashtbl.find_opt ctx.env.globals m with
|
||||
| Some (ty, _) -> fn_ty ty
|
||||
| None ->
|
||||
not (List.mem m [ "true"; "false"; "nil"; "None";
|
||||
"context/allocator"; "context/temp" ])
|
||||
in
|
||||
match guess with
|
||||
| Some m when not (callable m) ->
|
||||
if args = [] then
|
||||
Loc.failk "check/unknown-function" loc
|
||||
"unknown function %s — did you mean %s? It is a value and not a \
|
||||
function, so it is written without parentheses" name m
|
||||
else
|
||||
Loc.failk "check/unknown-function" loc
|
||||
"unknown function %s. The nearest name, %s, is a value and not a \
|
||||
function" name m
|
||||
| Some m ->
|
||||
Loc.failk "check/unknown-function" loc
|
||||
"unknown function %s — did you mean %s?" name m
|
||||
|
||||
@ -6375,6 +6375,28 @@ let () =
|
||||
"(defmacro idm [x] x) \
|
||||
(defn f [] u64 (idm 18446744073709551615))";
|
||||
|
||||
(* ── Suggestions that compile ─────────────────────────────────── *)
|
||||
(* A let binding has no type slot, so the refusal names only the spelling
|
||||
that works. *)
|
||||
rejects_check "vec-new with no element type names only the type argument"
|
||||
"(defn f [] i32 (let [v (vec-new)] 0))"
|
||||
~needle:"as (vec-new i32)";
|
||||
(match checked "(defn f [] i32 (let [m (map-new)] 0))" with
|
||||
| _ -> check "map-new with no types is refused" false
|
||||
| exception Loc.Error d ->
|
||||
check "map-new's refusal does not suggest a binding type"
|
||||
(not (contains d.Loc.dmsg "binding")));
|
||||
(* The near miss is a value, and is suggested without the parentheses that
|
||||
would make it a refused call. *)
|
||||
rejects_check "a near miss that is a value says it is written bare"
|
||||
"(defn f [] i32 (let [a (context-allocator)] 0))"
|
||||
~needle:"did you mean context/allocator? It is a value and not a function";
|
||||
accepts "the bare spelling that refusal names compiles"
|
||||
"(defn f [] i32 (let [a context/allocator] 0))";
|
||||
rejects_check "a near miss that is a function keeps the plain suggestion"
|
||||
"(defn foo [] i32 1) (defn f [] i32 (fooo))"
|
||||
~needle:"did you mean foo?";
|
||||
|
||||
(* ── The acceptance program checks end to end ──────────────────── *)
|
||||
accepts "calc-me.flan type checks"
|
||||
(In_channel.with_open_bin "../calc-me.flan" In_channel.input_all);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user