(max-of T) and (min-of T) are a numeric type's limits, at a concrete type or a type variable the bound admits, and given a slice they are still the prelude's reductions
This commit is contained in:
parent
2ea47f91eb
commit
5c0698d749
54
lib/check.ml
54
lib/check.ml
@ -7371,6 +7371,60 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
||||
expect ctx loc ~want
|
||||
(List.fold_left (fun acc arg -> pick acc (check ctx ~want:ty arg))
|
||||
(pick a b) rest)
|
||||
(* (max-of T) and (min-of T): the type-limit constants, by type, so a
|
||||
generic body can name its own type's. Odin's max(T) and min(T), and the
|
||||
same answer for a float: the largest finite value and its negation, not
|
||||
the smallest positive one. Given a value rather than a type, the name is
|
||||
the prelude's reduction of a slice, and the call is an ordinary one —
|
||||
the same split [vec-new] makes between a type and an allocator. *)
|
||||
| ("max-of" | "min-of")
|
||||
when (match args with
|
||||
| [ a ] ->
|
||||
type_of_expr a <> None
|
||||
|| (match a.Ast.e with
|
||||
| Ast.Var n ->
|
||||
lookup ctx n = None
|
||||
&& (not (Hashtbl.mem ctx.env.globals n))
|
||||
&& type_named ctx n
|
||||
| _ -> false)
|
||||
| _ -> false) ->
|
||||
let a = List.hd args in
|
||||
let ty =
|
||||
match type_of_expr a, a.Ast.e with
|
||||
| Some t, _ -> resolve ctx.env t
|
||||
| _, Ast.Var n -> resolve_name ctx.env ~seen:[] a.Ast.loc n
|
||||
| _ -> fail a.Ast.loc "internal: max-of's type argument is not a type"
|
||||
in
|
||||
let max = String.equal name "max-of" in
|
||||
let v =
|
||||
match ty with
|
||||
| Types.Int k ->
|
||||
let b = Types.bits k in
|
||||
let n =
|
||||
if Types.signed k then
|
||||
let top = Int64.shift_left 1L (b - 1) in
|
||||
if max then Int64.sub top 1L else Int64.neg top
|
||||
else if not max then 0L
|
||||
else if b = 64 then -1L
|
||||
else Int64.sub (Int64.shift_left 1L b) 1L
|
||||
in
|
||||
mk loc ty (Tast.Int (n, k))
|
||||
| Types.Float k ->
|
||||
let m =
|
||||
match k with
|
||||
| Types.F32 -> Int32.float_of_bits 0x7f7fffffl
|
||||
| Types.F64 -> Float.max_float
|
||||
in
|
||||
mk loc ty (Tast.Float ((if max then m else -.m), k))
|
||||
| Types.Var _ ->
|
||||
unconstrained ctx.env loc name ~needs:"numeric?" ty;
|
||||
int_literal loc ~want:(Some ty) ~preds:ctx.env.tvpreds 0L
|
||||
| _ ->
|
||||
fail a.Ast.loc
|
||||
"%s takes a numeric? type, and %s is not one — as in (%s i32)" name
|
||||
(Types.to_string ty) name
|
||||
in
|
||||
expect ctx loc ~want v
|
||||
(* (zeroed) is the all-bytes-zero value of whatever it is being stored into,
|
||||
so it only means anything where a type is expected of it. *)
|
||||
| "zeroed" ->
|
||||
|
||||
@ -406,6 +406,8 @@ let source = {flan|
|
||||
;; or more numbers, and a defn cannot shadow a builtin: nothing shadows [+]
|
||||
;; either. These reduce a slice, which is a different operation with a
|
||||
;; different arity, so the different name is honest rather than a workaround.
|
||||
;; Given a type instead of a slice, (min-of i8) and (max-of $t) are the type's
|
||||
;; limits, and the checker answers those itself.
|
||||
(defn min-of [s [$t]] (Option $t)
|
||||
{:where (ordered? $t)}
|
||||
(if (= (length s) 0)
|
||||
|
||||
54
test/programs/max-of.flan
Normal file
54
test/programs/max-of.flan
Normal file
@ -0,0 +1,54 @@
|
||||
;;;; (max-of T) and (min-of T): a numeric type's limits, named by the type, at
|
||||
;;;; a concrete type and inside a generic whose bound admits numbers.
|
||||
|
||||
;; A selection sort, descending, whose running best starts at the least value
|
||||
;; of the element type, so any element beats it.
|
||||
(defn sort-desc [s [$t]] ()
|
||||
{:where (numeric? $t)}
|
||||
(dotimes [i (length s)]
|
||||
(let [best (min-of $t)
|
||||
at-best i]
|
||||
(dotimes [j (- (length s) i)]
|
||||
(let [k (+ i j)]
|
||||
(when (> (at s k) best)
|
||||
(set best (at s k))
|
||||
(set at-best k))))
|
||||
(swap s i at-best))))
|
||||
|
||||
(defn largest [s [$t]] $t
|
||||
{:where (numeric? $t)}
|
||||
(let [best (min-of t)]
|
||||
(dotimes [i (length s)]
|
||||
(when (> (at s i) best) (set best (at s i))))
|
||||
best))
|
||||
|
||||
(defn show-i32 [s [i32]] ()
|
||||
(dotimes [i (length s)] (print (at s i)) (print " "))
|
||||
(println ""))
|
||||
|
||||
(defn show-f64 [s [f64]] ()
|
||||
(dotimes [i (length s)] (print (at s i)) (print " "))
|
||||
(println ""))
|
||||
|
||||
(defn main [] i32
|
||||
(println (max-of u8))
|
||||
(println (min-of u8))
|
||||
(println (max-of i8))
|
||||
(println (min-of i8))
|
||||
(println (max-of i32))
|
||||
(println (min-of i64))
|
||||
(println (max-of u64))
|
||||
(println (= (max-of f32) f32-max))
|
||||
(println (= (min-of f64) (- f64-max)))
|
||||
(println (= (max-of i16) i16-max))
|
||||
(let [a [(i32 3) -7 12 0 -2147483648 5]
|
||||
b [2.5 -1.0 1e300 -1e308]
|
||||
c [(u8 4) 0 200 9]]
|
||||
(sort-desc (slice a))
|
||||
(show-i32 (slice a))
|
||||
(sort-desc (slice b))
|
||||
(show-f64 (slice b))
|
||||
(println (largest (slice c)))
|
||||
(println (let [d [(i64 -5) -9]] (largest (slice d))))
|
||||
(println (let [e [(f32 -1.0) -3.0]] (= (largest (slice e)) (f32 -1.0)))))
|
||||
0)
|
||||
@ -573,6 +573,14 @@ 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;
|
||||
(* (max-of T) and (min-of T), concrete and inside a generic. *)
|
||||
let maxof_out =
|
||||
"255\n0\n127\n-128\n2147483647\n-9223372036854775808\n\
|
||||
18446744073709551615\ntrue\ntrue\ntrue\n\
|
||||
12 5 3 0 -7 -2147483648 \n1e+300 2.5 -1 -1e+308 \n200\n-5\ntrue\n" in
|
||||
outputs "max-of and min-of" "programs/max-of.flan" maxof_out;
|
||||
outputs ~opt:"-O0" "max-of and min-of, -O0" "programs/max-of.flan" maxof_out;
|
||||
outputs ~x86:true "max-of and min-of, x86" "programs/max-of.flan" maxof_out;
|
||||
(* (- x) negates, on every numeric type, a type variable and a dyn. *)
|
||||
let neg_out =
|
||||
"-3\n7\n-2.5\n-inf\n-1.5\n255\n-4\n-2.5\n-inf\n-9000000000\n\
|
||||
|
||||
@ -6399,6 +6399,19 @@ let () =
|
||||
"(defn main [] i32 (let [a [None None]] 0))"
|
||||
~needle:"what None is an Option of";
|
||||
|
||||
(* ── (max-of T) and (min-of T) ──────────────────────────────────── *)
|
||||
infers "max-of carries its type" "(max-of u16)" "u16";
|
||||
infers "min-of at a float" "(min-of f32)" "f32";
|
||||
rejects_check "max-of at an unbounded type variable names the bound"
|
||||
"(defn f [x $t] $t (max-of $t))" ~needle:"{:where (numeric? $t)}";
|
||||
accepts "max-of at a type variable the bound admits"
|
||||
"(defn f [x $t] $t {:where (integer? $t)} (max-of t))";
|
||||
rejects_check "max-of at a type that is not a number names the bound"
|
||||
"(defn f [] string (max-of string))"
|
||||
~needle:"max-of takes a numeric? type, and string is not one";
|
||||
accepts "max-of of a slice is still the prelude's reduction"
|
||||
"(defn f [xs [i32]] (Option i32) (max-of xs))";
|
||||
|
||||
(* ── (the T e) ─────────────────────────────────────────────────── *)
|
||||
infers "the gives a literal its type" "(the u8 200)" "u8";
|
||||
infers "the widens as an annotation does" "(the i64 (the i32 1))" "i64";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user