Monomorphize Procedures #
The MonomorphizeProcedures program transformation monomorphizes every
polymorphic procedure so that no procedure's types mention type variables.
Where it runs #
This pass runs in corePipelinePhases immediately before typeCheckPhase
(and therefore after CallElim in transformPipelinePhases). Two consequences
of this position matter:
CallElimhas already run, so no procedure body contains acalland each procedure can be monomorphized independently of the others.typeCheckPhasewhich runs afterMonomorphizeProceduresclearsheader.typeArgsand instantiates the signature to fresh internal type variables such as$__ty0, so running before it lets each procedure still carry its declared type parameters inheader.typeArgsunder their original source names (e.g.a).
The subsequent typeCheckPhase then re-checks the now-monomorphic procedure
against its opaque-typed signature, propagating the opaque types through the
body.
How it works #
For each procedure whose header.typeArgs is x₁, …, xₖ:
- A fresh, globally-unique opaque type is minted per type parameter, named
$__opaque_{procName}_{xᵢ}_N(the$__prefix reserves the internal namespace so the name cannot collide with a user-declared type; the trailing_Nis a fresh counter from the sharedCoreGenState, guaranteeing uniqueness). Each becomes a nullary top-level type declarationtype {name};emitted immediately before the procedure. - A type substitution mapping each
xᵢto its opaque nullary type constructor is applied to the whole procedure: input/output signatures, precondition and postcondition expressions, and every expression and local declaration type in the body (LExpr.applySubstfor expression annotations,LMonoTy.substfor signature and declaration types). header.typeArgsis cleared, so the procedure is now monomorphic.
The statement-level substitution reuses the type checker's Core.Statement.subst
(StatementType.lean, which in turn uses Command.subst and Lambda.LTy.subst).
Only structured bodies are monomorphized: a CFG body is reported as an error.
Applying the type substitution #
The statement / command / declared-type substitution is the type checker's
existing Core.Statement.subst (which uses Command.subst and Lambda.LTy.subst,
in StatementType.lean / LTyUnify.lean). Here we only assemble the
whole-procedure substitution on top of it.
Apply S to a whole procedure: its signature, spec clauses, and body, and
clear header.typeArgs. Throws if the body is a CFG (unsupported). The
body substitution reuses Statement.subst.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The transformation #
Monomorphize a single procedure. One fresh opaque type is minted per
declared type parameter (header.typeArgs); the opaque type declarations are
returned first, followed by the substituted procedure. A procedure with no
type parameters is returned unchanged.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Monomorphize every procedure in the program, introducing opaque types for the free type variables of each polymorphic procedure.
Equations
- One or more equations did not get rendered due to their size.
Instances For
CoreTransformM runner for MonomorphizeProcedures, suitable for use with
Core.Transform.run. Reports whether any opaque type declaration was
introduced (which happens iff some procedure was monomorphized).
Equations
- Core.monomorphizeProcedures p = do let p' ← Core.MonomorphizeProcedures.run p pure (List.length p'.decls != List.length p.decls, p')
Instances For
Pipeline phase for MonomorphizeProcedures. Model-preserving: replacing an
implicitly universally-quantified type variable with a fresh, arbitrary
opaque type is universal generalization (∀-introduction), which preserves
the proof obligation and introduces no spurious models.
Equations
- Core.monomorphizeProceduresPipelinePhase = Core.modelPreservingPipelinePhase "monomorphizeProcedures" fun (prog : Core.Program) => Core.monomorphizeProcedures prog