Hojun-Cho f4970d886c wcc: opaque use-guards — reject every unsized use (incl tuple/tagged, recursive) (#108)
#108 sub-fold (b): close the footgun #108(a) opened. opaque is abstract
and UNSIZED (size = align = SIZE_UNDEFINED = (u64)-1), legal only behind
indirection. Without guards a bare use would fabricate a (u64)-1-byte
slot — a silent miscompile (rule 7). opaque is illegal by-value in FOUR
aggregate positions (array element, struct field, tuple member, tagged-
union variant) + as a bare value, under size/align, and as a []opaque
element-index. LOUD guards, mirroring harec's scattered `size ==
SIZE_UNDEFINED` checks:

  1. bare value/local/param/return-by-value  (check.c clet, build_fn_type,
     top-level let; harec check.c:1524, :3931)
  2. opaque struct field                      (resolve_type N_TSTRUCT)
  3. [N]opaque array element                  (resolve_type N_TARRAY)
  3t. opaque tuple member                     (resolve_type N_TTUPLE;
      harec type_store.c:1147)
  3u. opaque tagged-union variant             (resolve_type N_TTAGGED;
      harec type_store.c:449)
  4. size(opaque) / align(opaque)             (size/align fold;
     harec check.c:2720)
  5. indexing []opaque                        (N_INDEX; harec check.c:384)

Detection is via the SIZE_UNDEFINED sentinel the guard consults, so the
sized forms `*opaque` (8B) and `[]opaque` (24B header) pass untouched.

Rule-10 per-guard stage placement:
  - Guards 1/2/3/3t/3u/5 are CSTAGE-ONLY. The wwstage check.ww is an
    AST-level approximation with no binding-size computation (g1) and no
    type-decl field/element/member validation walk (g2/g3/3t/3u); its
    N_INDEX indexresult returns the element type without consulting its
    size and defers invalid-index rejection to the cstage (g5). Same
    cstage-only neg-case precedent as 712_redecl / 708_param_shadow_mod.
  - Guard 4 is BOTH-STAGES. The wwstage HAS the size()/align() fold
    (astsize/astalign would otherwise fold opaque to a bogus 0 — a silent
    miscompile); twinned via astunsized + deffolderr. Because the wwstage
    has NO per-construction guards, its fold alone must catch every
    opaque-containing type: astunsized is RECURSIVE — a type is unsized
    iff it is opaque OR an aggregate (array/struct/tuple/tagged) with a
    recursively-unsized member. This both reaches the tuple/tagged folds
    AND closes the leaf-only size([4]opaque)/size(struct{x:opaque})→0
    leak. The cstage size/align guard stays leaf — the cstage rejects
    unsized aggregates at construction, so its fold only ever sees a leaf.

opaque is unused by the bootstrap, so every guard is inert on the
selfhost corpus — 990-997 stay byte-identical. Regenerates the w6c/wwdump
combined.ww (check.ww embed). New compile-fail probe 961_opaque_guards
(14 build-fails rows incl tuple/tagged/nested + 2 *opaque/[]opaque
positive controls); 960 positive probe unchanged.
2026-05-26 09:52:13 +09:00
Description
No description provided
12 MiB
Languages
C 89.4%
Python 7.2%
Makefile 2.3%
Shell 0.8%
Assembly 0.3%