Two decisions in this lane went against the brief and both are written down
where the next reader will meet them: the return slot stayed mandatory, so the
third state ret = None was to grow does not exist and neither does the fallout
listed for load.ml, shim.ml and cimport.ml; and the parameter rule is resolved in
Check rather than in parse.ml, because cimport passes C type names through
verbatim and POSIX's lowercase stat and timespec are writable in parameter
position, which is what makes a syntactic rule unsound rather than merely
awkward.
The open ABI point is in flan_dyn.h beside the root functions rather than only
in the handoff, because the header is what the two sides diff. A rooted slot
holding 0 is not a value: the compiler zeroes every root at entry because the
push happens before the code that fills it and possibly for a branch that never
runs, and 0 is the only pattern it can write without knowing the encoding. If
the real runtime NaN-boxes and integer zero is the zero word then this is wrong
and both sides change together.
Session.compatible needed nothing: it compares with Types.equal over the
parameters and the return, and dyn is equal to itself and to nothing else. Both
directions are pinned anyway, because this is the one place "changes signature"
covers a change the source does not spell out -- a parameter can become dyn, or
stop being dyn, by a type being declared elsewhere in the program.
@x86 128 match 0 differ 0 refused, @sanitize clean, dune test green.
The type itself, the ABI its operations call into, and the one decision the
feature could not avoid: (defn f [x y]) is one parameter or two, and which one
depends on whether y names a type.
Parse does not decide it. That lookup is the one its defn comment records being
removed for being wrong twice in one day -- the set of type names is incomplete
at parse time by construction, and macros generating definitions is what
widened the failure. So the vector is carried undecided, as Ast.pitems, and
paired in Check, after every file is loaded, every macro expanded and every
header imported. The set is complete there. It is not complete across time, and
the comment says so: a defstruct written later changes a signature with no edit
to the function.
The return slot stays mandatory and dyn is written out in it. The ambiguity
there has no syntactic resolution at all -- a capitalised head in a list is both
a type application and a struct literal -- so the third state the parameters
needed does not exist for the return type, and ret = None goes on meaning Unit.
What the feature costs, and what is taken back: a slot with no type used to be a
syntax error, so a mistyped type now reads as an extra parameter with no
diagnostic. A name within one edit of a type's gets the resolver's own
did-you-mean, and an unknown capitalised name is reported as the unknown type it
is -- not one parameter in the corpus is capitalised. A lowercase name
resembling no type is the feature working, and is the residual.
The x86 backend refuses dyn by name; both callers already name --llvm, and the
daemon takes that backend by default, so this is the first thing a user of dyn
sees. The JS dialect refuses it too, for the opposite reason -- every value
there is already dynamic and what is missing is only the lowering.
runtime/flan_dyn.h is the fixed ABI. flan_dyn_stub.c stands in until the real
collector lands and says in its header that it verifies nothing about roots.