A cast to a type variable, and the container builtins over one

(t x) is not a name is_cast knows - t is not a machine type - so it is
its own arm, admitted by numeric? because a cast produces a number.
vec-new, pool-new and map-new all reach the one list of what names a
type, so the spike's line for vec-new had already covered the other two;
zeroed takes its type from the position it is written in. All four are
pinned in programs/generics.flan.
This commit is contained in:
Joseph Ferano 2026-09-13 14:52:17 +07:00
parent dad725afe4
commit de93ffc89e
3 changed files with 56 additions and 2 deletions

View File

@ -4645,6 +4645,26 @@ and named_call ctx ~want loc name args =
float goes through (i32 x) first" name float goes through (i32 x) first" name
(Types.to_string other)); (Types.to_string other));
prim (Tast.Cast target) target [ a ] prim (Tast.Cast target) target [ a ]
(* A cast to a *type variable*: [(t x)] inside a generic body. The name is
not one [is_cast] knows, because [is_cast] asks whether the name is a
machine type and [t] is not so this is its own arm, above the ordinary
one and below the enums, and it reaches the same [Cast] prim.
Inside an instantiation [resolve_name] has already answered with the
concrete target, so the copy casts to a real type and the emitter sees
nothing unusual. During the abstract pass the target is [Var t] and the
[where] clause is what says the cast means anything at all: a cast
produces a number, so [numeric?] is what admits it. *)
| _ when (List.mem name ctx.env.tyvars || List.mem_assoc name ctx.env.subst)
&& List.length args = 1 ->
let target = resolve_name ctx.env ~seen:[] loc name in
unconstrained ctx.env loc ("a cast to " ^ name) ~needs:"numeric?" target;
let a = check ctx (List.hd args) in
(match a.Tast.ty with
| Types.Enum _ -> ()
| t when Types.is_numeric t || generic_ty t -> ()
| t -> fail loc "%s converts a number, found %s" name (Types.to_string t));
prim (Tast.Cast target) target [ a ]
| _ when is_cast name && List.length args = 1 -> | _ when is_cast name && List.length args = 1 ->
let target = resolve_name ctx.env ~seen:[] loc name in let target = resolve_name ctx.env ~seen:[] loc name in
let a = check ctx (List.hd args) in let a = check ctx (List.hd args) in

View File

@ -62,6 +62,30 @@
{:where (copyable? $t)} {:where (copyable? $t)}
(println x)) (println x))
;; A cast to a type variable. [(t x)] is not a name [is_cast] knows — [t] is
;; not a machine type — so it is its own arm, and [numeric?] is what admits
;; it, because a cast produces a number. Inside the copy the target is
;; concrete and the emitter sees an ordinary cast.
(defn widen [x i32 d $t] $t
{:where (numeric? $t)}
(do d (t x)))
;; The builtins that take a *type name* as an argument, over a variable. Each
;; reaches the one list of what names a type, so all three came at once.
;; (pool-new t) and (map-new t i32) are the other two; a Pool of a variable
;; needs it not to be move-only, which [copyable?] is.
(defn one-of [x $t] (Vec $t)
{:where (copyable? $t)}
(let [v (vec-new t)]
(push v x)
v))
;; (zeroed) takes its type from the position it is written in, so a variable
;; in that position is answered by the instantiation like any other type.
(defn zero-of [x $t] $t
{:where (copyable? $t)}
(do x (zeroed)))
(defn main [] () (defn main [] ()
(println (ident 3)) (println (ident 3))
(println (ident 4.5)) (println (ident 4.5))
@ -98,8 +122,15 @@
(match (min-of (slice ns 0 4)) (Some m) (println m) _ (println -1)) (match (min-of (slice ns 0 4)) (Some m) (println m) _ (println -1))
(match (max-of (slice fs 0 3)) (Some m) (println m) _ (println -1.0)) (match (max-of (slice fs 0 3)) (Some m) (println m) _ (println -1.0))
(match (index-of (slice ns 0 4) 18) (Some i) (println i) _ (println -1)) (match (index-of (slice ns 0 4) 18) (Some i) (println i) _ (println -1))
(println (widen 3 0.0))
(println (widen 3 (i64 0)))
(println (zero-of 9))
(let [a (arena-new 4096) (let [a (arena-new 4096)
keep (filter (slice ns 0 4) (fn [x] (> x 5)))] keep (filter (slice ns 0 4) (fn [x] (> x 5)))
one (one-of 4.5)]
(println (len (as-slice keep))) (println (len (as-slice keep)))
(println (at (as-slice one) 0))
(free one)
(free keep) (free keep)
(free-all a)))) (free-all a))))

View File

@ -1323,7 +1323,10 @@ let () =
(* Generics end to end: one written body per family, several emitted, and (* Generics end to end: one written body per family, several emitted, and
the collapsed prelude running underneath it. Every line of the expected the collapsed prelude running underneath it. Every line of the expected
output is an answer a per-type copy used to give. *) output is an answer a per-type copy used to give. *)
let generics_out = "3\n4.5\ntrue\n7\n5\n-1\n5\n42\n3\n1\n10\n1\n8\n3\n4.5\ntext\n1\n2.5\n9\n36\n2\n2.5\n0\n3\n" in let generics_out =
"3\n4.5\ntrue\n7\n5\n-1\n5\n42\n3\n1\n10\n1\n8\n\
3\n4.5\ntext\n1\n2.5\n9\n36\n2\n2.5\n0\n3\n3\n0\n3\n4.5\n"
in
outputs "generics" "programs/generics.flan" generics_out; outputs "generics" "programs/generics.flan" generics_out;
outputs ~opt:"-O0" "generics, -O0" "programs/generics.flan" generics_out; outputs ~opt:"-O0" "generics, -O0" "programs/generics.flan" generics_out;