Type Construction
The pass that builds the typed IR from Type Inference's results: typechecker::construction (~8,050 lines across typechecker/construction.rs and typechecker/construction/{declarations,expressions,calls,patterns,narrowing}.rs), typed_ast/ (748 lines, the typed IR itself), and the typechecker's shared support consumed by construction — registry.rs, projections.rs, overload.rs, conversions.rs, object_safety.rs, handoff.rs (~3,500 lines). ~12,300 lines total. Owning crate: metel-frontend. This section consumes Type Inference's output; it does not re-derive it.
Model
Construction turns solved facts into the typed program consumed by move checking, elaboration, and evaluation. It processes modules in dependency order and carries their exported schemes forward (module handoff). Where earlier stages resolved an identity, construction preserves it on typed nodes instead of replacing it with another name lookup (identity stamping).
It is also the last place to enforce rules whose operands must already be concrete: ascriptions disappear after guiding construction (ascriptions), operator and import diagnostics retain their distinct meanings (operators, visibility), and defaults, overload selections, and generic shape recovery become explicit typed facts (defaults, overloads, generic reconstruction).
Verifiable architecture claims
Owner metel-frontend unless a requirement names another.
Requirement №1
check_graph typechecks a normalized module graph in topological order, running inference then construction per module, and accumulates each module's exported schemes in GlobalExports so later modules import them without re-inferring.
Requirement №2
Construction stamps the typed IR with the identities the frontend already produced (FieldId/VariantId member ids, the span-to-BindingId bridge) through a FrozenIdentity bundle in check_graph_with_report; callers that pass None get every id as None, so construction degrades gracefully.
Requirement №3
Opaque return variables are validated at constraint-composition time, not only against the final substitution: when an opaque-return marker resolves directly to a concrete type, inference reports T0018, and construction must not reintroduce the concrete identity.
Requirement №4
The typechecker treats [T] as Copy unconditionally, as a deliberate InferType::Array special case that does not inspect the element type; other array-aspect eligibility stays conditional on the element through ordinary stdlib impl and bound machinery.
Requirement №5
Until destructors run, a std::core::Drop impl may declare only an empty drop body: construction identifies the standard aspect by declaring module (a same-named user aspect is unaffected) and rejects a non-empty body with T0001.
Requirement №6
Type ascriptions constrain inference and construction but are erased from typed IR: construction builds the inner expression using the resolved annotation as context instead of emitting a runtime no-op TypedExpr::Ascribe node.
Requirement №7
Operand legality is checked once operand types are resolved: arithmetic and unary negation accept numeric or Never operands, ordering comparisons also accept Str, and construction reports T0005 rather than leaving invalid operator shapes for evaluation.
Requirement №8
Import visibility is diagnosed while building import schemes, where the full module graph is known: name resolution records import bindings without collapsing a private-item error into a missing-item one, so construction reports T0009 (visibility) apart from T0003 (absence).
Requirement №9
Aspect default methods are materialized as typed methods before evaluation: inference collects an impl's inherited defaults and construction emits a typed body for each, except that negative impls acquire none, so runtime dispatch never evaluates untyped default-method syntax.
Requirement №10
Free-function overload selection is exact-match, and construction stamps the selected callable's stable SymbolId on the typed call; the runtime dispatches that identity through its symbol-value registry, and overload sets stay module-local.
Requirement №11
At a generic struct literal or field access, construction instantiates the declared raw field template with the receiver's resolved type arguments; it does not cache one concrete field type under the generic declaration's surface name.
Requirement №12
Generic runtime reconstruction recovers a struct or enum's type arguments from its typed field values against the declared field templates when the value carries no argument list; this is best-effort for values whose fields reveal no parameter, rather than storing generic metadata on every value.
Known limitations
LIMIT-TYPE-CONSTRUCTION-001 — Call::callee_id falls back to name dispatch for first-class functions. callee_id is empty for methods, nested functions and first-class function calls, which fall back to name dispatch. Open record ↗plannedLIMIT-TYPE-CONSTRUCTION-002 — Overload sets are not exportable across modules. Overload sets are seeded by two different mechanisms; exporting them across modules (METEL-188) is future work. Open record ↗knownLIMIT-TYPE-CONSTRUCTION-003 — == and != reject non-primitive operands instead of dispatching through Eq. == and != reject operands that are not numbers, booleans, strings or chars; call .eq(..) instead. Open record ↗Resolved (1)
LIMIT-TYPE-CONSTRUCTION-004 — Generic closure reconstruction omitted captured types. Runtime construction lacked the captured bindings' creation-time types when rebuilding a polymorphic closure body. Open record ↗