From b92e91bcaac45b96f41219e92a97a1bf6b8e3b7b Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 26 Sep 2026 05:27:23 +0700 Subject: [PATCH] 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. --- lib/check.ml | 41 ++++++++++++++++++++++++++++--------- test/syntax/algorithms.flan | 18 ++++++++-------- test/syntax/algorithms.fln | 18 ++++++++-------- test/test_flan.ml | 18 ++++++++++++---- 4 files changed, 63 insertions(+), 32 deletions(-) diff --git a/lib/check.ml b/lib/check.ml index e82242d1..db1fd732 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -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)") diff --git a/test/syntax/algorithms.flan b/test/syntax/algorithms.flan index a5e86892..a511f265 100644 --- a/test/syntax/algorithms.flan +++ b/test/syntax/algorithms.flan @@ -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") :-) diff --git a/test/syntax/algorithms.fln b/test/syntax/algorithms.fln index 037f14fa..ac29948e 100644 --- a/test/syntax/algorithms.fln +++ b/test/syntax/algorithms.fln @@ -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") :- diff --git a/test/test_flan.ml b/test/test_flan.ml index cadce45c..1f3d72cb 100644 --- a/test/test_flan.ml +++ b/test/test_flan.ml @@ -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