A parameter, local or field may be named str or any other type name when a type follows it, and a type-named parameter with no type after it is refused with both fixes.

This commit is contained in:
Joseph Ferano 2026-09-26 05:27:23 +07:00
parent 267db2cd91
commit b92e91bcaa
4 changed files with 63 additions and 32 deletions

View File

@ -2077,12 +2077,10 @@ and array_len env loc = function
another name that is not a type, or by nothing, takes one and is [dyn]. That
is the whole rule, and it reads the way the vector reads.
A name that *is* a type name is refused rather than paired. [(defn f [i64 x]
...)] has no good reading: taken as written it is a parameter called [i64],
which shadows nothing but confuses everything, and the likelier intent is a
pair written backwards. Refusing here costs a rename in the one program that
meant it and closes the one place where this rule could still hand somebody
a signature they did not write. *)
A name that *is* a type name pairs only when a type follows it: [[str str]]
has one reading. [(defn f [i64 x] ...)] has two — a dyn parameter called
[i64], or a pair written backwards — so it is refused rather than handed
back as a signature nobody wrote. *)
let is_type_name env n =
Types.ikind_of_name n <> None
|| Types.fkind_of_name n <> None
@ -2255,11 +2253,20 @@ let pair_params ?(also = fun _ -> false) ?(declared = fun _ -> None)
Loc.failk "check/parameter-name-expected" t.Ast.tloc
"a parameter's name was expected here, and this is a type. Parameters \
are [name Type ...]"
| Ast.Pname (n, loc) :: rest when is_type_name env n ->
ignore rest;
(* A parameter may share a type's name — [str] is a common name for text —
when a type follows it: [[str str]] and [str: str] can only be a name
then a type. Without a type after it, [[i64 x]] is a pair written
backwards as likely as a dyn parameter called [i64], so that one is
refused with both fixes. *)
| Ast.Pname (n, loc) :: Ast.Ptype t :: rest when is_type_name env n ->
{ Ast.fname = n; fty = t; floc = loc } :: go rest
| Ast.Pname (n, loc) :: Ast.Pname (t, tloc) :: rest
when is_type_name env n && is_type_name env t ->
{ Ast.fname = n; fty = { Ast.t = Ast.Tname t; tloc }; floc = loc } :: go rest
| Ast.Pname (n, loc) :: _ when is_type_name env n ->
Loc.failk "check/parameter-named-type" loc
"%s names a type, so it cannot also be this parameter's name. Write \
[name %s], or rename the parameter" n n
"%s names a type, and no type follows this parameter called %s. Give \
it one, as [%s %s], or if the pair is backwards write [name %s]" n n n n n
(* [_] reads a type off a body, and a parameter has none to read. *)
| Ast.Pname (n, _) :: Ast.Pname ("_", tloc) :: _ ->
Loc.failk "check/infer-misplaced" tloc
@ -10416,6 +10423,18 @@ and type_named ctx n =
|| Hashtbl.mem ctx.env.enums n
|| Hashtbl.mem ctx.env.aliases n
(* A local or global that shares a type's name — [str] is a common name for
text — is the binding in (vec-new str), and then nothing names the type.
Said here so the refusal names the binding rather than a missing type. *)
and shadowed_type_arg ctx loc what args =
match args with
| { Ast.e = Ast.Var n; _ } :: _
when type_named ctx n && (lookup ctx n <> None || global_value ctx n) ->
fail loc
"%s here is the value named %s and not the type, so nothing says what \
(%s) makes — rename that binding to write the type" n n what
| _ -> ()
(* The element type for [vec-new]: a leading bare symbol naming a type, a
leading type expression — [(vec-new [u8])], [(vec-new (Ptr Cell))], which
Parse has already read as one — or the expectation at the site. A bare
@ -10440,6 +10459,7 @@ and vec_new_elem ctx ~want loc args =
(match want with
| Some (Types.Vec t) -> t, args
| _ ->
shadowed_type_arg ctx loc "vec-new" args;
fail loc
"nothing here says what (vec-new) is a Vec of — write the element \
type, as (vec-new i32)")
@ -10530,6 +10550,7 @@ and map_new_types ctx ~want loc args =
(match want with
| Some (Types.Map (k, v)) -> k, v, args
| _ ->
shadowed_type_arg ctx loc "map-new" args;
fail loc
"nothing here says what (map-new) maps — write the key and value \
types, as (map-new str i32)")

View File

@ -1,10 +1,10 @@
(import agent "vendor:agent")
(defn find-match [text str pattern str] i32
(dotimes [i (length text)]
(defn find-match [str str pattern str] i32
(dotimes [i (length str)]
(let [matched true]
(dotimes [j (length pattern)]
(when (!= (at text (+ i j)) (at pattern j))
(when (!= (at str (+ i j)) (at pattern j))
(set matched false)))
(when matched
(return i))))
@ -41,11 +41,11 @@
(comment
(insertion-sort [\I \N \S \E \R \T \I \O \N \S \O \R \T])
(insertion-sort (slice [6 2 4 9 1 9 4 5] 0 8))
(let [text (bytes "INSERTIONSORT")]
(insertion-sort text)
(println text))
(let [text (bytes "SELECTIONSORT")]
(selection-sort text)
(println text))
(let [str (bytes "INSERTIONSORT")]
(insertion-sort str)
(println str))
(let [str (bytes "SELECTIONSORT")]
(selection-sort str)
(println str))
(find-match "aababba" "abba")
:-)

View File

@ -3,11 +3,11 @@
import agent "vendor:agent"
fn find-match(text: str, pattern: str) -> i32
for i in range(length(text))
fn find-match(str: str, pattern: str) -> i32
for i in range(length(str))
let matched = true
for j in range(length(pattern))
if text[i + j] != pattern[j]
if str[i + j] != pattern[j]
matched = false
if matched
return i
@ -42,11 +42,11 @@ fn main() -> i32 = 0
comment():
insertion-sort([\I \N \S \E \R \T \I \O \N \S \O \R \T])
insertion-sort(slice([6 2 4 9 1 9 4 5], 0, 8))
let text = bytes("INSERTIONSORT")
insertion-sort(text)
println(text)
let text = bytes("SELECTIONSORT")
selection-sort(text)
println(text)
let str = bytes("INSERTIONSORT")
insertion-sort(str)
println(str)
let str = bytes("SELECTIONSORT")
selection-sort(str)
println(str)
find-match("aababba", "abba")
:-

View File

@ -1479,11 +1479,21 @@ let () =
(* ── dyn, and what it does not do yet ──────────────────────────── *)
(* The pairing rule's own refusal. A name that is also a type's has no good
reading — taken as written it is a parameter called [i64] — and the
likelier intent is a pair the wrong way round, which the message names. *)
(* The pairing rule's own refusal. A type-named parameter with no type after
it is a dyn parameter or a pair the wrong way round; the message names
both fixes. With a type after it there is one reading. *)
rejects_check "a parameter named after a type" "(defn f [i64 x] ())"
~needle:"cannot also be this parameter's name";
~needle:"no type follows this parameter called i64";
rejects_check "a last parameter named after a type" "(defn f [a i32 str] ())"
~needle:"Give it one, as [str str]";
accepts "a parameter named str with a type" "(defn f [str str] i32 (length str))";
accepts "a parameter named str of another type"
"(defn f [str [u8]] i32 (length str))";
accepts "a local named str" "(defn f [b [u8]] str (let [str (str b)] str))";
accepts "a field named str" "(defstruct P [str str]) (defn f [p P] str (.str p))";
rejects_check "a local named str is not the type at vec-new"
"(defn f [] i32 (let [str 1 v (vec-new str)] 0))"
~needle:"str here is the value named str and not the type";
(* M2 item 3 lifted the container-into-dyn refusal: a [(Vec T)], a slice or
a fixed array with an i64/f64/bool element now crosses as a VIEW rather