A float element of a dyn view takes an int it holds exactly and traps on one it does not.

This commit is contained in:
Joseph Ferano 2026-09-26 06:22:26 +07:00
parent e79d9c0d07
commit 749b4aeb84
6 changed files with 46 additions and 12 deletions

View File

@ -23,9 +23,10 @@ str. Waits on the dyn-unless-annotated design.
** DONE Any typed container crosses into dyn as a view ** DONE Any typed container crosses into dyn as a view
CLOSED: [2026-09-26] CLOSED: [2026-09-26]
A str element reads as a copy and is never written, an aggregate element is written A str element reads as a copy and is never written, an aggregate element is written
through its own view, and an int is refused by a float element. A view of storage its through its own view, a float element takes an int only when it holds it exactly (a
own dyn global's initialiser built is refused. Rules out copying at the crossing, and class's float slot's rule), and a u64 above the largest i64 traps when read. A view of
any check in a release build. storage its own dyn global's initialiser built is refused. Rules out copying at the
crossing, a dyn big int for u64, and any check in a release build.
** NEXT Dyn unless annotated ** NEXT Dyn unless annotated
Decided 2026-09-26, replacing the plain rule: number, bool and char literals are typed, Decided 2026-09-26, replacing the plain rule: number, bool and char literals are typed,

View File

@ -3306,10 +3306,10 @@ static int int_range(uint8_t c, int64_t *lo, int64_t *hi) {
/* One element, unboxed on the way in. The dyn value's tag must be the one /* One element, unboxed on the way in. The dyn value's tag must be the one
* the element's type wants, or this traps by name and never coerces a * the element's type wants, or this traps by name and never coerces a
* mismatched value into the slot; an int the element's width cannot hold * mismatched value into the slot; an int the element's width cannot hold
* traps too, naming both. An int is not a float here: it is refused, not * traps too, naming both. An int goes into a float element when the float
* converted, as every write through a view has been. A float into an f32 * holds it exactly, the rule a class's typed float slot follows, and traps
* narrows, as (f32 x) does. [v] is the view and [x] the value, for the * when it does not. A float into an f32 narrows, as (f32 x) does. [v] is the
* sentence. */ * view and [x] the value, for the sentence. */
static void view_write(const uint8_t *loc, int64_t loclen, const char *op, static void view_write(const uint8_t *loc, int64_t loclen, const char *op,
flan_dyn v, const uint8_t *d, flan_dyn x, uint8_t *p) { flan_dyn v, const uint8_t *d, flan_dyn x, uint8_t *p) {
char ty[128]; char ty[128];
@ -3341,9 +3341,24 @@ static void view_write(const uint8_t *loc, int64_t loclen, const char *op,
switch (*d) { switch (*d) {
case 'f': case 'd': { case 'f': case 'd': {
double f; double f;
if (flan_dyn_tag(x) != FLAN_DYN_TAG_FLOAT) if (flan_dyn_tag(x) == FLAN_DYN_TAG_INT) {
/* [slot_admit]'s rule for a class's float slot: exact is a round
trip, and the range test keeps the cast back defined. */
int64_t n = dyn_int_value(x);
f = *d == 'f' ? (double)(float)n : (double)n;
if (!(f >= -9223372036854775808.0 && f < 9223372036854775808.0)
|| (int64_t)f != n) {
desc_spell(d, ty, sizeof ty);
flan_say(loc, loclen,
"dyn %s: %lld has no exact %s, so it does not go into this "
"element. Write it as a float, as in %lld.0",
op, (long long)n, ty, (long long)n);
flan_trap((const uint8_t *)"DynRange", 8);
}
} else if (flan_dyn_tag(x) == FLAN_DYN_TAG_FLOAT)
f = dyn_num_value(x);
else
trap2(loc, loclen, TYPE_TRAP, op, "this view's elements are float", v, x); trap2(loc, loclen, TYPE_TRAP, op, "this view's elements are float", v, x);
f = dyn_num_value(x);
if (*d == 'f') { float g = (float)f; memcpy(p, &g, 4); } if (*d == 'f') { float g = (float)f; memcpy(p, &g, 4); }
else memcpy(p, &f, 8); else memcpy(p, &f, 8);
return; return;

View File

@ -539,7 +539,16 @@ static void refuse_view(const char *what) {
} else if (strcmp(what, "wrongfloat") == 0) { } else if (strcmp(what, "wrongfloat") == 0) {
static double ff[1]; static double ff[1];
v = flan_dyn_view_flat(ff, 1, FLAN_VIEW_F64); v = flan_dyn_view_flat(ff, 1, FLAN_VIEW_F64);
FDYN_set_at(v, flan_dyn_from_i64(0), text("nope"));
} else if (strcmp(what, "inexactfloat") == 0) {
/* An int goes into a float element only when the float holds it
exactly; 2^53 + 1 is the first an f64 does not. */
static double ff[1];
v = flan_dyn_view_flat(ff, 1, FLAN_VIEW_F64);
FDYN_set_at(v, flan_dyn_from_i64(0), flan_dyn_from_i64(1)); FDYN_set_at(v, flan_dyn_from_i64(0), flan_dyn_from_i64(1));
if (ff[0] != 1.0) { printf("an exact int did not land: %g\n", ff[0]); exit(1); }
FDYN_set_at(v, flan_dyn_from_i64(0),
flan_dyn_from_i64(((int64_t)1 << 53) + 1));
} else if (strcmp(what, "flatpush") == 0) { } else if (strcmp(what, "flatpush") == 0) {
v = flan_dyn_view_flat(buf, 2, FLAN_VIEW_I64); v = flan_dyn_view_flat(buf, 2, FLAN_VIEW_I64);
FDYN_push(v, flan_dyn_from_i64(9)); FDYN_push(v, flan_dyn_from_i64(9));

View File

@ -63,12 +63,14 @@
(show "i8" a) (show "u8" b) (show "i16" c) (show "u16" d) (show "i8" a) (show "u8" b) (show "i16" c) (show "u16" d)
(show "i32" e) (show "u32" f) (show "i64" g) (show "u64" h) (show "i32" e) (show "u32" f) (show "i64" g) (show "u64" h)
(println (at b 2))) (println (at b 2)))
;; f32: read widens, write narrows. ;; f32: read widens, write narrows, and an int goes in when the f32
;; holds it exactly.
(let [fs [(f32 0.5) 1.25] (let [fs [(f32 0.5) 1.25]
dv (keep fs)] dv (keep fs)]
(set (at dv 0) 2.75) (set (at dv 0) 2.75)
(set (at dv 1) 3.1) (set (at dv 1) 3.1)
(show "f32" dv) (show "f32" dv)
(set (at dv 0) 4)
(println (at fs 0))) (println (at fs 0)))
;; bool, through a slice cut from a local array. ;; bool, through a slice cut from a local array.
(let [bs [true false true]] (let [bs [true false true]]
@ -208,4 +210,9 @@
(let [nm (Named {.name "ada" .id 7})] (let [nm (Named {.name "ada" .id 7})]
(put (keep nm) :name "bob") (put (keep nm) :name "bob")
0) 0)
;; An int an f32 does not hold exactly: 2^24 + 1.
(= n 9)
(let [fs [(f32 0.5)]]
(set (at (keep fs) 0) 16777217)
0)
:else (do (println "?") 1)))) :else (do (println "?") 1))))

View File

@ -5851,7 +5851,7 @@ level "1"
"i8 [0 -1 -2]\nu8 [251 252 253]\ni16 [-299 301]\nu16 [60001 2]\n\ "i8 [0 -1 -2]\nu8 [251 252 253]\ni16 [-299 301]\nu16 [60001 2]\n\
i32 [-69999 70001]\nu32 [4000000001 2]\ni64 [-4 6]\n\ i32 [-69999 70001]\nu32 [4000000001 2]\ni64 [-4 6]\n\
u64 [9000000000000000001 2]\n253\n\ u64 [9000000000000000001 2]\n253\n\
f32 [2.75 3.1]\n2.75\n\ f32 [2.75 3.1]\n4\n\
bool [true true true]\ntrue\n\ bool [true true true]\ntrue\n\
EIINNOORRSSTT\n\ EIINNOORRSSTT\n\
point #Point{:x 0.5 :y 3}\n40\n9.5\n9.5\n2\ntrue\n\ point #Point{:x 0.5 :y 3}\n40\n9.5\n9.5\n2\ntrue\n\
@ -5867,7 +5867,8 @@ level "1"
("2", "this u64 element is 18000000000000000000, above the largest \ ("2", "this u64 element is 18000000000000000000, above the largest \
dyn int"); dyn int");
("3", "a Point has no field :z. Its fields are :x :y"); ("3", "a Point has no field :z. Its fields are :x :y");
("8", "a str element is read-only through a dyn view") ] ("8", "a str element is read-only through a dyn view");
("9", "16777217 has no exact f32") ]
and any_stale = and any_stale =
[ ("4", "this view points into a local of leak-local, and that call \ [ ("4", "this view points into a local of leak-local, and that call \
has returned"); has returned");

View File

@ -258,6 +258,7 @@ let () =
("wrongwrite", "this view's elements are int"); ("wrongwrite", "this view's elements are int");
("wrongbool", "this view's elements are bool"); ("wrongbool", "this view's elements are bool");
("wrongfloat", "this view's elements are float"); ("wrongfloat", "this view's elements are float");
("inexactfloat", "9007199254740993 has no exact f64");
("flatpush", "this view is a slice or an array and cannot grow"); ("flatpush", "this view is a slice or an array and cannot grow");
(* And the operator's own name in that sentence. [flan_dyn_len] hands (* And the operator's own name in that sentence. [flan_dyn_len] hands
a string down twice — once to its type trap and once to the view a string down twice — once to its type trap and once to the view