Skip to content
Logo
Prismio
Developers
ImplementedPrismio 0.1.0

Generics and monomorphization

How Prismio discovers generic instantiations, substitutes concrete types, validates bounds, and emits specialized code.

Last verified

Implemented. Available in the audited Prismio 0.1.0 compiler. Pre-1.0 syntax may still change.

Prismio specializes generic functions, structs, enums, traits, and implementations for concrete type arguments. There is no single erased generic body that discovers element layout at runtime.

src/sema/generics.psm validates arity, substitutes types, tracks requested instantiations, and prevents an unbounded specialization chain. Specialized bodies then pass through ordinary semantic analysis, ownership checking, AIF, and code generation.

Why ordering matters

Bounds are checked where the concrete arguments are known. Method and operator rewrites must resolve against the specialized receiver and applicable implementations. An eligible List<Flat> specialization can therefore use inline element storage while another instantiation remains boxed.

Instantiation identity must include every type argument that affects semantics or representation. Deduplication prevents duplicate emission; recursion detection distinguishes a valid recursive call to an existing specialization from generation of an ever-growing type sequence.

Tests

The suite covers generic functions and aggregates, inherent generic implementations, generic trait implementations, trait arguments, where clauses, blanket implementations, coherence, associated items, and nonterminating instantiation rejection. A change to substitution or identity should add both a success case and a case that would collide under an incomplete specialization key.

Template collection and identity

monoCollectTemplates walks the merged module and separates generic functions, structs, enums, traits, and implementation blocks from declarations ready for analysis. monoIsTemplate classifies declarations; monoFindTemplate and monoFindTypeTemplate retrieve them. monoAppendDeclaration adds a generated specialization to the same module so later sema, AIF, and IR passes see an ordinary concrete declaration.

Names use monoSeparator(), currently $, plus a complete encoding of applied arguments. monoMangledName produces that identity, while semaMangleType produces linkage fragments for resolved types. monoSeparatorCount and monoArgsTooDeep guard runaway recursive expansion; monoElide shortens diagnostic presentation without shortening the actual identity.

Substitution

monoCopyOne and monoCopyChain deep-copy the AST portions a specialization will mutate. monoParamCount, monoParamAt, monoParamName, and monoParamIndex interpret the encoded parameter list. monoSubstituteChain walks annotations and expressions, and monoSubstituteInto replaces each matching type parameter with a copied concrete annotation. The template AST must remain unchanged so a second specialization starts from original syntax.

monoAnnotationFromType converts an already resolved semantic type back into an annotation used by a generated declaration. This is necessary when inference, rather than explicit syntax, discovers a type argument.

Instantiation functions

monoInstantiateStruct validates arity, builds the specialization key, returns an existing declaration when cached, or copies/substitutes/appends a new concrete struct. monoResolveGenericCall considers function templates at a call site. monoTemplateAcceptsCall and monoSolveTypeParam infer arguments by matching formal annotations against actual argument TypeInfo values. monoMatchParam recursively matches nested applied types rather than only top-level names.

After solving, monoCheckBounds evaluates every required trait reference. monoCheckOneBound reports one stable diagnostic per failed parameter/bound combination. monoImplApplies, monoImplBoundsHold, and monoImplements determine whether a concrete or generic implementation satisfies the applied trait, including trait type arguments.

Traits, defaults, and associated types

monoExpandDefaultMethods copies a trait default into implementation blocks that do not provide the member. monoExpandOneImpl, monoImplDeclaresMethod, and monoAddDefaultMethod preserve the target's generic context and create the same method record shape as a written implementation.

monoResolveProjections resolves associated type expressions after implementation applicability is known. monoFindAssocType, monoImplAssocTypeAnnotation, and monoConstraintsHold select the concrete associated annotation. Resolution cannot happen during parsing because the applicable implementation depends on substituted type arguments and bounds.

What reaches later passes

There is no erased generic body. A concrete specialization has concrete field types, parameter types, symbol name, method records, and ownership behavior. Sema checks it as an ordinary declaration; AIF can select layout per specialization; LLVM emits a distinct function or named struct. An eligible List<Flat> may therefore use inline storage while List<Owned> uses an owning representation.

Tests must cover explicit and inferred arguments, nested types, repeated cache hits, overloads, recursive templates, generic impl applicability, blanket/concrete overlap, multiple bounds, trait arguments, default methods, associated projections, distinct symbols, and a specialization whose layout differs from another instantiation.