A class slot's type names its own package's class first, an int widens into a float slot exactly when it round-trips, and a constructor's refusal names the call that was wrong
This commit is contained in:
parent
9aeee9d676
commit
53880eb685
4
TODO.org
4
TODO.org
@ -2041,8 +2041,8 @@ them.
|
|||||||
|
|
||||||
** DONE defclass slots take types, checked on write
|
** DONE defclass slots take types, checked on write
|
||||||
CLOSED: [2026-09-25]
|
CLOSED: [2026-09-25]
|
||||||
The constructor's parameters stay dyn and every store checks at run time; an int
|
Constructor parameters stay dyn and each store checks at run time; an int widens
|
||||||
widens into a float slot only when exact, and nil fits only an (Option T) slot.
|
into a float slot only if it round-trips, and nil fits only an (Option T) slot.
|
||||||
|
|
||||||
** DONE println takes up to a second to appear
|
** DONE println takes up to a second to appear
|
||||||
CLOSED: [2026-09-25]
|
CLOSED: [2026-09-25]
|
||||||
|
|||||||
30
lib/check.ml
30
lib/check.ml
@ -1608,13 +1608,18 @@ let pair_params ?(also = fun _ -> false) env (items : Ast.pitem list)
|
|||||||
(* A class named in [cls]'s slot vector: [n] as written, or [n] in [cls]'s own
|
(* A class named in [cls]'s slot vector: [n] as written, or [n] in [cls]'s own
|
||||||
package, since [Load] leaves a bare name in a slot vector unqualified. *)
|
package, since [Load] leaves a bare name in a slot vector unqualified. *)
|
||||||
let class_named ~classes cls n =
|
let class_named ~classes cls n =
|
||||||
if List.mem n classes then Some n
|
(* The class's own package first: an importer may declare a class of the
|
||||||
else
|
same bare name, and a slot the package wrote means the package's. *)
|
||||||
|
let own =
|
||||||
match String.rindex_opt cls '/' with
|
match String.rindex_opt cls '/' with
|
||||||
| Some i ->
|
| Some i ->
|
||||||
let q = String.sub cls 0 (i + 1) ^ n in
|
let q = String.sub cls 0 (i + 1) ^ n in
|
||||||
if List.mem q classes then Some q else None
|
if List.mem q classes then Some q else None
|
||||||
| None -> None
|
| None -> None
|
||||||
|
in
|
||||||
|
match own with
|
||||||
|
| Some _ -> own
|
||||||
|
| None -> if List.mem n classes then Some n else None
|
||||||
|
|
||||||
let rec slot_of env ~classes cls fname (t : Ast.texpr) : slot_ty =
|
let rec slot_of env ~classes cls fname (t : Ast.texpr) : slot_ty =
|
||||||
let refuse what =
|
let refuse what =
|
||||||
@ -9620,6 +9625,27 @@ and ordinary_call ctx ~want loc name args =
|
|||||||
in
|
in
|
||||||
(match Hashtbl.find_opt ctx.env.tracks name with
|
(match Hashtbl.find_opt ctx.env.tracks name with
|
||||||
| Some tr -> expect ctx loc ~want (tracked_call loc ctx.env name tr ret args)
|
| Some tr -> expect ctx loc ~want (tracked_call loc ctx.env name tr ret args)
|
||||||
|
| None when Hashtbl.mem ctx.env.classes name ->
|
||||||
|
(* A class's constructor, told where it was called from so that a
|
||||||
|
slot it refuses names this call and not only the defclass. The
|
||||||
|
arguments go into temps first: one may itself construct, and
|
||||||
|
the site is set last, immediately before the call, so nothing
|
||||||
|
between the two can replace it. The constructor takes it as its
|
||||||
|
first act; one reached through a function value finds none. *)
|
||||||
|
let temps =
|
||||||
|
List.map (fun (a : Tast.expr) -> (fresh_slot ctx a.Tast.ty, a)) args
|
||||||
|
in
|
||||||
|
let uses =
|
||||||
|
List.map
|
||||||
|
(fun (s, (a : Tast.expr)) -> mk a.Tast.loc a.Tast.ty (Tast.Local s))
|
||||||
|
temps
|
||||||
|
in
|
||||||
|
expect ctx loc ~want
|
||||||
|
(mk loc ret
|
||||||
|
(Tast.Let
|
||||||
|
(temps,
|
||||||
|
[ rt loc Types.Unit "flan_dyn_ctor_site" [ here loc ];
|
||||||
|
mk loc ret (Tast.Call (name, uses)) ])))
|
||||||
| None -> expect ctx loc ~want (mk loc ret (Tast.Call (name, args))))
|
| None -> expect ctx loc ~want (mk loc ret (Tast.Call (name, args))))
|
||||||
| None ->
|
| None ->
|
||||||
if Hashtbl.mem ctx.env.datas name then
|
if Hashtbl.mem ctx.env.datas name then
|
||||||
|
|||||||
@ -53,7 +53,8 @@ let no_method = "NoMethod"
|
|||||||
each of its instances, run once per instance at the first [get], [put] or
|
each of its instances, run once per instance at the first [get], [put] or
|
||||||
[set] that reaches it after the redefinition. By then the instance already
|
[set] that reaches it after the redefinition. By then the instance already
|
||||||
holds the new slots, each kept one with its old value and each gained one
|
holds the new slots, each kept one with its old value and each gained one
|
||||||
nil; [added] is a vec of the gained slots' keywords and [discarded] a map
|
at its type's zero value, or nil for an untyped, Option or class slot;
|
||||||
|
[added] is a vec of the gained slots' keywords and [discarded] a map
|
||||||
from each lost slot's keyword to the value it held. A method is written for
|
from each lost slot's keyword to the value it held. A method is written for
|
||||||
a class, from a live session, and is how a migration does more than match
|
a class, from a live session, and is how a migration does more than match
|
||||||
slots by name:
|
slots by name:
|
||||||
|
|||||||
@ -4780,6 +4780,7 @@ declare i64 @flan_dyn_map_new()
|
|||||||
declare i64 @flan_dyn_map_new_class(i64, ptr, i64)
|
declare i64 @flan_dyn_map_new_class(i64, ptr, i64)
|
||||||
declare void @flan_dyn_slot_set(i64, i64, i64, ptr, i64)
|
declare void @flan_dyn_slot_set(i64, i64, i64, ptr, i64)
|
||||||
declare void @flan_dyn_slot_init(i64, i64, i64, ptr, i64)
|
declare void @flan_dyn_slot_init(i64, i64, i64, ptr, i64)
|
||||||
|
declare void @flan_dyn_ctor_site(ptr, i64)
|
||||||
declare void @flan_dyn_map_put(i64, i64, i64, ptr, i64)
|
declare void @flan_dyn_map_put(i64, i64, i64, ptr, i64)
|
||||||
declare i64 @flan_dyn_class_of(i64)
|
declare i64 @flan_dyn_class_of(i64)
|
||||||
declare void @flan_dyn_class_def(i64, ptr, i64)
|
declare void @flan_dyn_class_def(i64, ptr, i64)
|
||||||
|
|||||||
@ -1655,8 +1655,8 @@ static void slot_type_text(const slot_type *t, char *buf, size_t cap) {
|
|||||||
/* Whether [v] may be stored in a slot of type [t], and what is stored: [v]
|
/* Whether [v] may be stored in a slot of type [t], and what is stored: [v]
|
||||||
* itself, or — for an int into a float slot — the float it widens to. The
|
* itself, or — for an int into a float slot — the float it widens to. The
|
||||||
* widening is the typed side's rule read off the value rather than off a
|
* widening is the typed side's rule read off the value rather than off a
|
||||||
* static type: an integer the float's significand holds exactly is admitted
|
* static type: an integer the float holds exactly is admitted as that float,
|
||||||
* as that float, and one it does not is refused, as (f64 x) would be for the
|
* and one it does not is refused, as (f64 x) would be for the
|
||||||
* type that could hold it. A float into an f32 slot has to be one an f32
|
* type that could hold it. A float into an f32 slot has to be one an f32
|
||||||
* holds, which is the typed side refusing f64 into f32. */
|
* holds, which is the typed side refusing f64 into f32. */
|
||||||
static int slot_admit(const slot_type *t, flan_dyn v, flan_dyn *out) {
|
static int slot_admit(const slot_type *t, flan_dyn v, flan_dyn *out) {
|
||||||
@ -1675,9 +1675,15 @@ static int slot_admit(const slot_type *t, flan_dyn v, flan_dyn *out) {
|
|||||||
return t->fbits == 53 || d != d || (double)(float)d == d;
|
return t->fbits == 53 || d != d || (double)(float)d == d;
|
||||||
}
|
}
|
||||||
if (tag == FLAN_DYN_TAG_INT) {
|
if (tag == FLAN_DYN_TAG_INT) {
|
||||||
int64_t x = dyn_int_value(v), lim = (int64_t)1 << t->fbits;
|
/* Exact is a round trip, not a range: 2^54 is an f64 exactly and
|
||||||
if (x < -lim || x > lim) return 0;
|
2^53+1 is not. The range test before the cast back is what keeps
|
||||||
*out = flan_dyn_from_f64((double)x);
|
that cast defined, since INT64_MAX rounds up to 2^63. */
|
||||||
|
int64_t x = dyn_int_value(v);
|
||||||
|
double d = t->fbits == 53 ? (double)x : (double)(float)x;
|
||||||
|
if (!(d >= -9223372036854775808.0 && d < 9223372036854775808.0)
|
||||||
|
|| (int64_t)d != x)
|
||||||
|
return 0;
|
||||||
|
*out = flan_dyn_from_f64(d);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -2117,8 +2123,25 @@ static class_entry *class_sync(flan_obj *o) {
|
|||||||
* what it has, and has to: a constructor compiled before a redefinition may
|
* what it has, and has to: a constructor compiled before a redefinition may
|
||||||
* still be on some stack, and letting its definition win would put the
|
* still be on some stack, and letting its definition win would put the
|
||||||
* class back the way it was. Redefining is [flan_dyn_class_def]'s alone. */
|
* class back the way it was. Redefining is [flan_dyn_class_def]'s alone. */
|
||||||
|
/* A constructor call's site: [pending] from the caller, moved to [building]
|
||||||
|
* by the constructor's first act, so a call through a function value — which
|
||||||
|
* sets nothing — finds none rather than an earlier call's. Nothing between
|
||||||
|
* the caller setting it and the constructor taking it can construct: the
|
||||||
|
* caller evaluated every argument first. */
|
||||||
|
static const uint8_t *site_pending, *site_building;
|
||||||
|
static int64_t site_pending_len, site_building_len;
|
||||||
|
|
||||||
|
void flan_dyn_ctor_site(const uint8_t *loc, int64_t loclen) {
|
||||||
|
site_pending = loc;
|
||||||
|
site_pending_len = loclen;
|
||||||
|
}
|
||||||
|
|
||||||
flan_dyn flan_dyn_map_new_class(flan_dyn k, const uint8_t *spec, int64_t n) {
|
flan_dyn flan_dyn_map_new_class(flan_dyn k, const uint8_t *spec, int64_t n) {
|
||||||
flan_obj *o;
|
flan_obj *o;
|
||||||
|
site_building = site_pending;
|
||||||
|
site_building_len = site_pending_len;
|
||||||
|
site_pending = NULL;
|
||||||
|
site_pending_len = 0;
|
||||||
if (flan_dyn_tag(k) != FLAN_DYN_TAG_KEYWORD)
|
if (flan_dyn_tag(k) != FLAN_DYN_TAG_KEYWORD)
|
||||||
trap1(NULL, 0, TYPE_TRAP, "class instance", "a class tag is a keyword", k);
|
trap1(NULL, 0, TYPE_TRAP, "class instance", "a class tag is a keyword", k);
|
||||||
if (class_find(dyn_kw(k)) == NULL) {
|
if (class_find(dyn_kw(k)) == NULL) {
|
||||||
@ -3029,7 +3052,10 @@ static _Noreturn void trap_slot_type(const uint8_t *loc, int64_t loclen,
|
|||||||
say(sv, SAY_MAX, v);
|
say(sv, SAY_MAX, v);
|
||||||
slot_type_text(t, st, sizeof st);
|
slot_type_text(t, st, sizeof st);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
trap_where(loc, loclen);
|
/* A constructor's refusal is placed at the call that was wrong, when the
|
||||||
|
call said where it was, and names the slot's declaration after it. */
|
||||||
|
trap_where(by == BY_NEW && site_building != NULL ? site_building : loc,
|
||||||
|
by == BY_NEW && site_building != NULL ? site_building_len : loclen);
|
||||||
fprintf(stderr, "dyn %s: the slot :%.*s of %.*s is declared %s, and ",
|
fprintf(stderr, "dyn %s: the slot :%.*s of %.*s is declared %s, and ",
|
||||||
by == BY_PUT ? "put" : by == BY_SET ? "set" : "construct", sn, ss,
|
by == BY_PUT ? "put" : by == BY_SET ? "set" : "construct", sn, ss,
|
||||||
cn, cs, st);
|
cn, cs, st);
|
||||||
@ -3047,6 +3073,9 @@ static _Noreturn void trap_slot_type(const uint8_t *loc, int64_t loclen,
|
|||||||
fprintf(stderr, "(put %s :%.*s %s)\n", sm, sn, ss, sv);
|
fprintf(stderr, "(put %s :%.*s %s)\n", sm, sn, ss, sv);
|
||||||
else if (by == BY_SET)
|
else if (by == BY_SET)
|
||||||
fprintf(stderr, "(set (get %s :%.*s) %s)\n", sm, sn, ss, sv);
|
fprintf(stderr, "(set (get %s :%.*s) %s)\n", sm, sn, ss, sv);
|
||||||
|
else if (site_building != NULL && loc != NULL)
|
||||||
|
fprintf(stderr, "(%.*s ...) with :%.*s %s; the slot is declared at %.*s\n",
|
||||||
|
cn, cs, sn, ss, sv, (int)loclen, (const char *)loc);
|
||||||
else
|
else
|
||||||
fprintf(stderr, "(%.*s ...) with :%.*s %s\n", cn, cs, sn, ss, sv);
|
fprintf(stderr, "(%.*s ...) with :%.*s %s\n", cn, cs, sn, ss, sv);
|
||||||
flan_trap((const uint8_t *)"DynType", 7);
|
flan_trap((const uint8_t *)"DynType", 7);
|
||||||
|
|||||||
@ -112,6 +112,11 @@ flan_dyn flan_dyn_map_new_class(flan_dyn k, const uint8_t *spec, int64_t n);
|
|||||||
void flan_dyn_slot_init(flan_dyn m, flan_dyn k, flan_dyn v,
|
void flan_dyn_slot_init(flan_dyn m, flan_dyn k, flan_dyn v,
|
||||||
const uint8_t *loc, int64_t loclen);
|
const uint8_t *loc, int64_t loclen);
|
||||||
|
|
||||||
|
/* Where the constructor about to be called was called from. Set by the
|
||||||
|
* caller immediately before the call and taken by the constructor's
|
||||||
|
* [flan_dyn_map_new_class], so a refusal in its stores names the call. */
|
||||||
|
void flan_dyn_ctor_site(const uint8_t *loc, int64_t loclen);
|
||||||
|
|
||||||
/* (set (get inst :slot) v): [m] must be a class instance and [k] a slot its
|
/* (set (get inst :slot) v): [m] must be a class instance and [k] a slot its
|
||||||
* class declares, and [v] must fit the slot's type; each is a trap with its
|
* class declares, and [v] must fit the slot's type; each is a trap with its
|
||||||
* own sentence, at [loc]. A declared slot always exists, so this stores and
|
* own sentence, at [loc]. A declared slot always exists, so this stores and
|
||||||
@ -136,7 +141,8 @@ flan_dyn flan_dyn_class_of(flan_dyn v);
|
|||||||
* migrates nothing. When it does move, every instance built against an
|
* migrates nothing. When it does move, every instance built against an
|
||||||
* earlier definition migrates lazily at its next [get], [put], [has-key?],
|
* earlier definition migrates lazily at its next [get], [put], [has-key?],
|
||||||
* [len] or equality comparison: slots the class still has keep their values
|
* [len] or equality comparison: slots the class still has keep their values
|
||||||
* matched by name, slots it has gained appear as nil, and keys it no longer
|
* matched by name, a slot it has gained holds its type's zero value — nil
|
||||||
|
* for an untyped, an (Option T) or a class-typed slot — and keys it no longer
|
||||||
* declares are dropped. The instance's identity is preserved throughout;
|
* declares are dropped. The instance's identity is preserved throughout;
|
||||||
* this is CLHS 4.3.6, and [flan_dyn_class_hook] is its user hook.
|
* this is CLHS 4.3.6, and [flan_dyn_class_hook] is its user hook.
|
||||||
*
|
*
|
||||||
|
|||||||
2
test/daemon-x86.out
Normal file
2
test/daemon-x86.out
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
flan dev: built dev-hook.flan in 444ms
|
||||||
|
flan dev: /home/joe/Development/flan/.claude/worktrees/agent-a6ffe579d55d1892b/test/programs/dev-hook.flan ready on /tmp/claude-1000/rv-3672919.sock (17ms, one process)
|
||||||
@ -1256,7 +1256,7 @@ extern int (*flan_dyn_migrate_hook)(void *fn, uint64_t instance, uint64_t added,
|
|||||||
void flan_dyn_class_hook(void *fn);
|
void flan_dyn_class_hook(void *fn);
|
||||||
|
|
||||||
static flan_dyn hk_p, hk_q, hk_r;
|
static flan_dyn hk_p, hk_q, hk_r;
|
||||||
static int hk_runs_p, hk_runs_q, hk_runs_r, hk_fresh;
|
static int hk_runs_p, hk_runs_q, hk_runs_r, hk_fresh, hk_per_call = 20;
|
||||||
static int64_t hk_gone_len = -1;
|
static int64_t hk_gone_len = -1;
|
||||||
|
|
||||||
static int hk_call(void *fn, uint64_t instance, uint64_t added,
|
static int hk_call(void *fn, uint64_t instance, uint64_t added,
|
||||||
@ -1272,7 +1272,7 @@ static int hk_call(void *fn, uint64_t instance, uint64_t added,
|
|||||||
if (instance == hk_q) hk_runs_q++;
|
if (instance == hk_q) hk_runs_q++;
|
||||||
if (instance == hk_r) hk_runs_r++;
|
if (instance == hk_r) hk_runs_r++;
|
||||||
(void)slot(hk_p, "x");
|
(void)slot(hk_p, "x");
|
||||||
for (i = 0; i < 20; i++) {
|
for (i = 0; i < hk_per_call; i++) {
|
||||||
snprintf(name, sizeof name, "fresh%d", hk_fresh++);
|
snprintf(name, sizeof name, "fresh%d", hk_fresh++);
|
||||||
(void)flan_dyn_map_new_class(
|
(void)flan_dyn_map_new_class(
|
||||||
flan_dyn_kw((const uint8_t *)name, (int64_t)strlen(name)),
|
flan_dyn_kw((const uint8_t *)name, (int64_t)strlen(name)),
|
||||||
@ -1315,6 +1315,28 @@ static void hook_reentry(void) {
|
|||||||
check(hk_runs_q == 1 && hk_runs_r == 1,
|
check(hk_runs_q == 1 && hk_runs_r == 1,
|
||||||
"each other instance runs its hook once, at its own first touch");
|
"each other instance runs its hook once, at its own first touch");
|
||||||
check(hk_runs_p == 1, "and the first instance's did not run again");
|
check(hk_runs_p == 1, "and the first instance's did not run again");
|
||||||
|
|
||||||
|
/* A migration started by a store rather than a read, with a hook that
|
||||||
|
grows the registry past its capacity each time: the store goes on to
|
||||||
|
check the value against the class's entry, and it has to be the entry
|
||||||
|
the registry holds after the hook, not the one it held before. 200 and
|
||||||
|
then 300 new classes are each enough to move the table whatever its
|
||||||
|
capacity was. :x is an f64 slot now, so the int stored must arrive as a
|
||||||
|
float, which only the entry's types can say. */
|
||||||
|
hk_per_call = 200;
|
||||||
|
define("pt", "x f64\ny\nz\nw");
|
||||||
|
flan_dyn_map_put(hk_p, flan_dyn_kw((const uint8_t *)"x", 1),
|
||||||
|
flan_dyn_from_i64(5), NULL, 0);
|
||||||
|
check(hk_runs_p == 2, "a put migrates and runs the hook");
|
||||||
|
check(flan_dyn_tag(slot(hk_p, "x")) == FLAN_DYN_TAG_FLOAT,
|
||||||
|
"a put after a hook that moved the registry widens by the new entry");
|
||||||
|
hk_per_call = 300;
|
||||||
|
define("pt", "x f64\ny\nz\nw\nv");
|
||||||
|
flan_dyn_slot_set(hk_p, flan_dyn_kw((const uint8_t *)"w", 1),
|
||||||
|
flan_dyn_from_i64(7), NULL, 0);
|
||||||
|
check(hk_runs_p == 3, "a set migrates and runs the hook");
|
||||||
|
check(flan_dyn_need_i64(slot(hk_p, "w")) == 7,
|
||||||
|
"a set after a hook that moved the registry finds its slot");
|
||||||
flan_dyn_migrate_hook = NULL;
|
flan_dyn_migrate_hook = NULL;
|
||||||
flan_dyn_class_hook(NULL);
|
flan_dyn_class_hook(NULL);
|
||||||
flan_dyn_root_pop(3);
|
flan_dyn_root_pop(3);
|
||||||
|
|||||||
8
test/programs/dyn-class-pkg.flan
Normal file
8
test/programs/dyn-class-pkg.flan
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
;;;; A slot type a package wrote names the package's own class, not an
|
||||||
|
;;;; importer's class of the same bare name.
|
||||||
|
(import g "pkgs/geo")
|
||||||
|
(defclass pt [z])
|
||||||
|
(defn main [] i32
|
||||||
|
(println (g/mk))
|
||||||
|
(println (pt 1))
|
||||||
|
0)
|
||||||
@ -42,9 +42,14 @@
|
|||||||
;; parameter, when the float holds it exactly.
|
;; parameter, when the float holds it exactly.
|
||||||
(put s :speed 3)
|
(put s :speed 3)
|
||||||
(println (+ (get s :speed) 0.5))
|
(println (+ (get s :speed) 0.5))
|
||||||
|
;; Exact is a round trip, not a range: 2^54 is an f64 exactly.
|
||||||
|
(put s :speed 18014398509481984)
|
||||||
|
(println (= (get s :speed) 18014398509481984.0))
|
||||||
(let [n (node s nil nil)]
|
(let [n (node s nil nil)]
|
||||||
(set (get n :next) (node s nil 2))
|
(set (get n :next) (node s nil 2))
|
||||||
(println (get (get n :next) :weight))
|
(println (get (get n :next) :weight))
|
||||||
(set (get n :weight) nil)
|
(set (get n :weight) nil)
|
||||||
|
;; 2^30 is an f32 exactly, though it is past f32's 24-bit significand.
|
||||||
|
(set (get n :weight) 1073741824)
|
||||||
(println (class-of (get n :owner)))))
|
(println (class-of (get n :owner)))))
|
||||||
0)
|
0)
|
||||||
|
|||||||
5
test/programs/pkgs/geo/geo.flan
Normal file
5
test/programs/pkgs/geo/geo.flan
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
;;;; A package whose slot types name its own classes, imported by
|
||||||
|
;;;; dyn-class-pkg.flan, which declares a class of the same bare name.
|
||||||
|
(defclass pt [x f64 y f64])
|
||||||
|
(defclass seg [a pt b (Option pt) tag])
|
||||||
|
(defn mk [] dyn (seg (pt 1 2) nil :t))
|
||||||
@ -5088,11 +5088,14 @@ level "1"
|
|||||||
whose arguments the two emit separately. *)
|
whose arguments the two emit separately. *)
|
||||||
let slots_out =
|
let slots_out =
|
||||||
"#state{ :pause false :step 3 :speed 1.5 :name \"sand\" :tag :x}\n\
|
"#state{ :pause false :step 3 :speed 1.5 :name \"sand\" :tag :x}\n\
|
||||||
true\n-7\n[ 1 2]\n2.5\n9\n6\n12\n3.5\n2\n:state\n"
|
true\n-7\n[ 1 2]\n2.5\n9\n6\n12\n3.5\ntrue\n2\n:state\n"
|
||||||
in
|
in
|
||||||
outputs "dyn: typed class slots" "programs/dyn-class-slots.flan" slots_out;
|
outputs "dyn: typed class slots" "programs/dyn-class-slots.flan" slots_out;
|
||||||
outputs ~x86:true "dyn: typed class slots, --x86"
|
outputs ~x86:true "dyn: typed class slots, --x86"
|
||||||
"programs/dyn-class-slots.flan" slots_out;
|
"programs/dyn-class-slots.flan" slots_out;
|
||||||
|
outputs "dyn: a package's slot type names its own class"
|
||||||
|
"programs/dyn-class-pkg.flan"
|
||||||
|
"#g/seg{ :a #g/pt{ :x 1 :y 2} :b nil :tag :t}\n#pt{ :z 1}\n";
|
||||||
let slot_trap ?x86 () =
|
let slot_trap ?x86 () =
|
||||||
let exe = compile ?x86 "programs/dyn-slot-trap.flan" in
|
let exe = compile ?x86 "programs/dyn-slot-trap.flan" in
|
||||||
List.iter
|
List.iter
|
||||||
@ -5106,9 +5109,9 @@ level "1"
|
|||||||
(match x86 with Some true -> ", --x86" | _ -> "")
|
(match x86 with Some true -> ", --x86" | _ -> "")
|
||||||
text code want
|
text code want
|
||||||
end)
|
end)
|
||||||
[ ("0", "dyn-slot-trap.flan:4:18: dyn construct: the slot :pause of \
|
[ ("0", "dyn-slot-trap.flan:14:28: dyn construct: the slot :pause of \
|
||||||
state is declared bool, and this is int — (state ...) with \
|
state is declared bool, and this is int — (state ...) with \
|
||||||
:pause 1");
|
:pause 1; the slot is declared at programs/dyn-slot-trap.flan:4:18");
|
||||||
("1", "dyn-slot-trap.flan:15:19: dyn put: the slot :pause of state \
|
("1", "dyn-slot-trap.flan:15:19: dyn put: the slot :pause of state \
|
||||||
is declared bool, and this is int");
|
is declared bool, and this is int");
|
||||||
("2", "dyn-slot-trap.flan:16:19: dyn set: the slot :step of state \
|
("2", "dyn-slot-trap.flan:16:19: dyn set: the slot :step of state \
|
||||||
@ -5119,9 +5122,9 @@ level "1"
|
|||||||
declare is added with put, not set");
|
declare is added with put, not set");
|
||||||
("4", "dyn-slot-trap.flan:18:19: dyn set: (get m k) is a place only \
|
("4", "dyn-slot-trap.flan:18:19: dyn set: (get m k) is a place only \
|
||||||
on a class instance, and this is a map with no class");
|
on a class instance, and this is a map with no class");
|
||||||
("5", "dyn-slot-trap.flan:5:17: dyn construct: the slot :owner of \
|
("5", "dyn-slot-trap.flan:19:28: dyn construct: the slot :owner of \
|
||||||
node is declared state, and this is not an instance of it");
|
node is declared state, and this is not an instance of it");
|
||||||
("6", "dyn-slot-trap.flan:4:18: dyn construct: the slot :pause of \
|
("6", "dyn-slot-trap.flan:20:22: dyn construct: the slot :pause of \
|
||||||
state is declared bool, and this is nil") ];
|
state is declared bool, and this is nil") ];
|
||||||
(try Sys.remove exe with Sys_error _ -> ())
|
(try Sys.remove exe with Sys_error _ -> ())
|
||||||
in
|
in
|
||||||
|
|||||||
@ -2221,7 +2221,9 @@ reason is the whole difference between the two: an instance carries a header nam
|
|||||||
its class and a flat struct does not. Redefining one re-registers the class and
|
its class and a flat struct does not. Redefining one re-registers the class and
|
||||||
bumps a generation counter, which is O(1) and walks no heap; every live instance
|
bumps a generation counter, which is O(1) and walks no heap; every live instance
|
||||||
migrates at its next touch. Slots matched by name keep their values, a gained slot
|
migrates at its next touch. Slots matched by name keep their values, a gained slot
|
||||||
appears as <code>nil</code>, a dropped one goes, the object is the same object, and
|
starts at its type's zero value — <code>false</code>, <code>0</code>,
|
||||||
|
<code>0.0</code> or <code>""</code>, and <code>nil</code> for a slot with no type,
|
||||||
|
an <code>(Option T)</code> or a class — a dropped one goes, the object is the same object, and
|
||||||
<code>class-of</code> still answers the same tag, so every method still reaches it.
|
<code>class-of</code> still answers the same tag, so every method still reaches it.
|
||||||
That is CLHS 4.3.6's protocol. Its user hook is
|
That is CLHS 4.3.6's protocol. Its user hook is
|
||||||
<code>update-instance-for-redefined-class</code>: a method of it written for a
|
<code>update-instance-for-redefined-class</code>: a method of it written for a
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user