0014 — Dyn: one NaN-boxed value, checked conversions, and zinc infer
Context. Section 9: in the gradual profile any and unknown are Dyn (DYN-02); in strict, any is refused
and unknown must be narrowed (DYN-01). Conversions out of Dyn are checked (DYN-07, memory safety depends on
it), JSON.parse without a type returns a Dyn tree (DYN-09), Dyn sites are reported (DYN-10), zinc infer types
JavaScript (DYN-11) and zinc build app.js works directly (DYN-14).
Choice.
- Representation (
runtime/zrt_dyn.h, header-only): the 8-bytenanboxform of DYN-03 on every target — doubles as themselves,undefined/null/booleans/strings/arrays/objects tagged in the negative quiet-NaN space. Strings,any[](Array<Dyn>) and objects keep their own reference counts; the tag tellsDynwhich one to retain. The 4-bytewordform is not written: the targets it is meant for (ps1, esp32) default tostrict, where Dyn only carries narrowedunknownvalues. - Objects. JSON objects and literals typed
anyareDynObj: an insertion-orderedMap<String, Dyn>(DYN-05; keys hashed, not interned). Typed objects stored in a Dyn stay themselves; when the program uses Dyn, every class gets generatedzrt_get/zrt_setoverrides for its fields (checked on write). No per-site inline cache (DYN-04). - Conversions (DYN-07) use one plan computed by Sema (
dynShape) and shared by both emitters: primitives check the tag; classes checkisa(sim: prototype chain by class name); plain interfaces andT[]build a checked copy, identical in C++ and in the sim, so the byte-exact oracle holds. Typed arrays other thanany[]cannot become Dyn (a copy would break aliasing: Z9040); functions, maps and sets cannot either (Z9041). Calls through a Dyn are refused (Z9042): narrow first. - Errors. A failed conversion or reading a property of
nullthrough Dyn panics with the text JS prints for an uncaughtTypeError, as array bounds already do (0006).JSON.parseof invalid text throws a catchableError(it is a@throwslibrary call, RT-05). - Operators follow ECMAScript (DYN-06):
+concatenates or adds after ToPrimitive, other arithmetic uses ToNumber,==the loose rules, comparisons order strings or numbers. - Narrowing (DYN-08): after
typeof/instanceof/Array.isArray, Sema takes the checker’s narrowed type and the read becomes a checked conversion into native code. DynFunction(lib/zinc.d.ts): the one way a function crosses a Dyn boundary, for embedders such aszinc:script. At run time it is(args: Dyn[]) => Dyn. A typed function passed where one is expected gets a compiler-built adapter (dynFnAdapterin C++,$z.dynfnin the sim) that converts arguments JavaScript-style: ToNumber then the machine type,String(x), truthiness,unknownas is. It never panics, because the caller may be an untrusted script. Other parameter types are Z9043.- Strict profile:
any(written or produced byJSON.parse) is Z1006; anunknownused without narrowing inconsole, templates, truthiness or==is Z1016. zinc infer(compiler/src/infer.ts) types parameters from their call sites and the fields the body reads (an interface per shape, which allocation sites passed to it extend, so fields share positions: DYN-13), variables from their assignments, and object literals per allocation site (p.k = vright after the literal moves into it). What stays untyped becomesany, i.e. Dyn. A.jsentry is inferred in memory and compiled (DYN-14).
Not done. word representation, per-site caches, interned keys, JS integer-key ordering in objects, function
specialisation (DYN-12), profiling under sim as an inference source, class fields assigned in JS constructors.