(new T ...) drops the explicit element annotation; unobserved element type crashes codegen #51
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
(new T <elem> <size>)drops the author's explicit element-type annotation at desugar, so any buffer whose element type is not later observed by aget/setpassesail checkand then crashesail buildwith an internal codegen error. This breaks two distinct shapes:(new RawBuf (con Int) 3)used only viaRawBuf.size(neverget/set) — the author wrote(con Int), yet build crashes.(new RawBuf (con Unit) 3)—Unitis not in theparam-inset{Int, Float, Bool}, but it is not rejected at check (see issue split note below); it reaches codegen and crashes the same way.Both contradict the language identity (every definition carries its full type; mandatory type annotations) — the explicit
(con Int)/(con Unit)the author wrote is discarded.Reproduction (verified, fresh build at current HEAD)
Legitimate-but-unobserved (the more serious leg):
ail check->ok (33 symbols across 3 modules)ail build --alloc=rc->Error: module raw_buf: def RawBuf_new__Unit: internal: intrinsic fn RawBuf_new__Unit has no registered intercept; an (intrinsic) body must resolve through the intercept registryThe same module with an observed element (
(let b (app RawBuf.set b 0 9) (app print (app RawBuf.get b 0)))) builds and runs (9), because theset/getpins the element type during inference.Forbidden element (
(con Unit)) behaves identically — check passes, build crashes with the sameRawBuf_new__Uniterror.Root cause (verified, code-pinpointed)
(new T ...)desugar drops the leadingNewArg::Type:crates/ailang-core/src/desugar.rs:1053(theTerm::Newarm rewrites to(app T.new <values>), comment: "The leading NewArg::Type is dropped … recovered by ordinary inference from how the result is used"). Decision recorded in the raw-buf milestone spec, § Term::New desugar.RawBuf.new : forall a. (Int) -> own (RawBuf a)hasaonly in the result, never in the parameters, so the call-site args can never pina. It is pinned only if downstreamget/setforces unification of the result'sa.ais unpinned, monomorphisation defaults it toUnit:crates/ailang-codegen/src/subst.rs:56-60("Any forall var not pinned by the args … defaults to Unit"). The comment there assumes "the specialised body must not actually read ana-typed value" — false forRawBuf.new, whose body is the element-specialised intrinsicRawBuf_new__<elem>; onlyInt/Float/Boolare registered intercepts, soRawBuf_new__Unithas none and codegen aborts.Why this is a design decision, not a one-line patch
Honouring the explicit annotation means carrying the element type from
Term::Newthrough to monomorphisation. There is no type-ascription term in the AST today (noAnn/The/ascribevariant), andRawBuf.new's signature cannot pinafrom its value args. Candidate directions (a spec revision to § Term::New desugar):(new T <elem> <vals>)to a form that pins the element type (a type-ascription term, or a typed application carrying the type-arg) so inference and mono see the author's<elem>.ais pinned by the args.NewArg::Typeagainstparam-inat pre-desugar AND make mono error (not default toUnit) on an unpinned RC/intrinsic element — turns the crash into a clean diagnostic but still rejects the legitimateRawBuf<Int>-by-size program.Split note —
param-inconstruction-site enforcementLeg 2 (forbidden element not rejected at check) was investigated as a separate fix. The
param-inrestricted-set check runs only over written-down type signatures (check_type_well_formed,crates/ailang-check/src/lib.rs:2084); a locally-constructed forbidden element with noRawBuf<T>in any signature is never checked. The natural fix-site iscrates/ailang-check/src/pre_desugar_validation.rs(the documentedTerm::New/desugar lockstep: a reject must precede the desugar that eatsNewArg::Type). This is subsumed by, and should be designed together with, the root annotation-preservation decision above.Context
Surfaced by the comprehensive raw-buf field test (
docs/specs/0059-fieldtest-raw-buf-comprehensive.md). The shipped RawBuf fixtures all observe their element viaget/set, so none triggered this; it appears the moment a buffer is allocated for its size/capacity alone, or a forbidden element is constructed locally.Acceptance
(new RawBuf (con Int) 3)used only viaRawBuf.sizechecks, builds, and runs(new RawBuf (con Unit) 3)is rejected atail checkwithparam-not-in-restricted-setnamingUnit, never reaching codegen