Auto-import bare RawBuf in type-constructor positions, not only op positions #50

Closed
opened 2026-05-30 17:18:59 +02:00 by Brummel · 0 comments
Owner

Summary

The kernel-tier auto-import makes a bare RawBuf resolve to the kernel
type raw_buf.RawBuf in value/op positions ((new RawBuf ...),
RawBuf.get, etc.), but NOT in a type-constructor position inside a
user ADT field declaration. A field typed (con RawBuf (con Int))
stays the unqualified RawBuf<Int> and does not unify with
raw_buf.RawBuf<Int>, so a RawBuf cannot be stored in a user ADT field
unless the author spells the qualified raw_buf.RawBuf.

This is the Series substrate shape (the design ledger describes
Series a as a RawBuf wrapped in a user ADT with bookkeeping —
design/models/0007-kernel-extensions.md §Series), so the inconsistency
will be hit by the pending series milestone.

Reproduction (verified, current HEAD)

Bare RawBuf field type — rejected:

(module adt
  (data Box (ctor Box (con RawBuf (con Int))))
  (fn slot1 (type (fn-type (params (borrow (con Box))) (ret (con Int))))
    (params bx) (body (match bx (case (pat-ctor Box buf) (app RawBuf.get buf 1)))))
  (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params)
    (body (let buf (new RawBuf (con Int) 3)
      (let buf (app RawBuf.set buf 1 42)
        (let bx (term-ctor Box Box buf) (app print (app slot1 bx))))))))

ail check -> [type-mismatch] slot1: expected raw_buf.RawBuf<$m0>, got RawBuf<Int>
             [type-mismatch] main:  expected RawBuf<Int>, got raw_buf.RawBuf<Int>

Qualified raw_buf.RawBuf field type — works:

; same module, field type (con raw_buf.RawBuf (con Int)) throughout
ail check  -> ok (35 symbols across 3 modules)
ail run    -> 42
AILANG_RC_STATS=1 -> allocs=3 frees=3 live=0

So storing a RawBuf in a user ADT field, reading it through a borrow,
and the drop cascade (the ADT's drop frees the RawBuf slab) all work
correctly with the qualified name; only the bare-name resolution in the
type-constructor position is missing.

Impact

A downstream author following the auto-import story (bare RawBuf works
without an import) gets a confusing type-mismatch the moment they wrap a
RawBuf in their own type, with no hint that the qualified raw_buf.RawBuf
is required. The op-position and type-position handling of the same
auto-imported name diverge.

Context

Found probing the RawBuf-in-ADT substrate during the raw-buf usability
repair (commits 75109f1..d5cc6e9, fieldtest docs/specs/0058). Not a
regression from that work; the auto-import has always been op-position
only. Adjacent to issue #34 (workspace search path) and #14, but
distinct: this is name-resolution of an auto-imported kernel type in a
type-constructor position.

Acceptance

  • A user ADT field typed with bare (con RawBuf (con Int)) resolves
    to raw_buf.RawBuf<Int> and checks/builds/runs
  • RED test pinning the bare-name field type before the fix
## Summary The kernel-tier auto-import makes a bare `RawBuf` resolve to the kernel type `raw_buf.RawBuf` in value/op positions (`(new RawBuf ...)`, `RawBuf.get`, etc.), but NOT in a type-constructor position inside a user ADT field declaration. A field typed `(con RawBuf (con Int))` stays the unqualified `RawBuf<Int>` and does not unify with `raw_buf.RawBuf<Int>`, so a RawBuf cannot be stored in a user ADT field unless the author spells the qualified `raw_buf.RawBuf`. This is the `Series` substrate shape (the design ledger describes `Series a` as a RawBuf wrapped in a user ADT with bookkeeping — design/models/0007-kernel-extensions.md §Series), so the inconsistency will be hit by the pending series milestone. ## Reproduction (verified, current HEAD) Bare `RawBuf` field type — rejected: (module adt (data Box (ctor Box (con RawBuf (con Int)))) (fn slot1 (type (fn-type (params (borrow (con Box))) (ret (con Int)))) (params bx) (body (match bx (case (pat-ctor Box buf) (app RawBuf.get buf 1))))) (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body (let buf (new RawBuf (con Int) 3) (let buf (app RawBuf.set buf 1 42) (let bx (term-ctor Box Box buf) (app print (app slot1 bx)))))))) ail check -> [type-mismatch] slot1: expected raw_buf.RawBuf<$m0>, got RawBuf<Int> [type-mismatch] main: expected RawBuf<Int>, got raw_buf.RawBuf<Int> Qualified `raw_buf.RawBuf` field type — works: ; same module, field type (con raw_buf.RawBuf (con Int)) throughout ail check -> ok (35 symbols across 3 modules) ail run -> 42 AILANG_RC_STATS=1 -> allocs=3 frees=3 live=0 So storing a RawBuf in a user ADT field, reading it through a borrow, and the drop cascade (the ADT's drop frees the RawBuf slab) all work correctly with the qualified name; only the bare-name resolution in the type-constructor position is missing. ## Impact A downstream author following the auto-import story (bare `RawBuf` works without an import) gets a confusing type-mismatch the moment they wrap a RawBuf in their own type, with no hint that the qualified `raw_buf.RawBuf` is required. The op-position and type-position handling of the same auto-imported name diverge. ## Context Found probing the RawBuf-in-ADT substrate during the raw-buf usability repair (commits 75109f1..d5cc6e9, fieldtest docs/specs/0058). Not a regression from that work; the auto-import has always been op-position only. Adjacent to issue #34 (workspace search path) and #14, but distinct: this is name-resolution of an auto-imported kernel type in a type-constructor position. ## Acceptance - [ ] A user ADT field typed with bare `(con RawBuf (con Int))` resolves to `raw_buf.RawBuf<Int>` and checks/builds/runs - [ ] RED test pinning the bare-name field type before the fix
Brummel added the bug label 2026-05-30 17:18:59 +02:00
Sign in to join this conversation.