d2d89fcb60
This block is structurally unlike the previous four. It does not only
adjudicate, it DISPATCHES: Instant + Duration must become el_instant_add_dur,
LocalDate + Duration must become el_local_date_add_dur. The emitted C depends on
the type answer, so it cannot move to a post-hoc query. Selecting which call to
emit is an emitter's actual job.
PREDICTIONS AND RESULTS
P1 the block conflates dispatch with adjudication TRUE
P2 adjudication can move, dispatch cannot TRUE
P3 this pass shrinks codegen far less than the last TRUE, and worse:
4513 -> 4537, it GREW
by 24 lines
P4 the rules are affine algebra, closed by construction TRUE
P5 no type propagation -- name tracking plus a
hardcoded list of which builtins return which type TRUE, 19 names
P3 is the honest result and it is not spun: moving 19 names into a data file
cost more lines than it saved, because a generic loader is larger than the
enumeration it replaces. The win is not line count. It is that adding a 20th
temporal builtin is now a one-line edit to signatures.rel instead of a compiler
change, and that the data is inspectable.
WHY THE HEADER CANNOT SUPPLY THIS, unlike arity: el_runtime.h declares every
builtin as returning el_val_t, because El has ONE type. That single type is why
the whole seam is cheap and it is exactly why the C boundary cannot say that
now() returns an Instant while unix_seconds() returns an Int. The El-level type
is real and the boundary erases it.
INCOMPLETE, and stated rather than hidden: P2 said adjudication could move to a
query. It has NOT. Violations still emit TIME_TYPE_ERROR inline from the
emitter. Only the type DATA moved. Moving the adjudication needs the operand
types recorded as relations, which is a further pass.
98/98 native, 4/4 temporal_signatures.sh, fixpoint ok.
34 lines
1.4 KiB
Plaintext
34 lines
1.4 KiB
Plaintext
# signatures.rel — El-level return types for runtime builtins.
|
|
#
|
|
# el_runtime.h declares every builtin as returning el_val_t, because El has ONE
|
|
# type. That single type is why the whole seam is cheap, and it is also why the
|
|
# header cannot say that now() returns an Instant while unix_seconds() returns
|
|
# an Int. The El-level type is real and the C boundary erases it.
|
|
#
|
|
# So the compiler needs this, and unlike the other checks it needs it at
|
|
# EMISSION time: Instant + Duration must become el_instant_add_dur, and that is
|
|
# dispatch, not adjudication. What moved here is the DATA -- previously 19
|
|
# hardcoded names across two functions in codegen.el. What stays in the emitter
|
|
# is choosing which call to emit, which is an emitter's actual job.
|
|
#
|
|
# <builtin> returns <El type>
|
|
|
|
now returns Instant
|
|
el_now_instant returns Instant
|
|
unix_seconds returns Instant
|
|
unix_millis returns Instant
|
|
instant_from_iso8601 returns Instant
|
|
el_instant_add_dur returns Instant
|
|
el_instant_sub_dur returns Instant
|
|
|
|
el_duration_from_nanos returns Duration
|
|
duration_seconds returns Duration
|
|
duration_millis returns Duration
|
|
duration_nanos returns Duration
|
|
el_instant_diff returns Duration
|
|
el_duration_add returns Duration
|
|
el_duration_sub returns Duration
|
|
el_duration_scale returns Duration
|
|
el_duration_div returns Duration
|
|
ttl_cache_age returns Duration
|