Documentation

Common.Fresh

class MonadFresh (m : Type β†’ Type) :

A monotonic counter for generating hygienic fresh names, needed wherever a pass introduces a variable that must not collide with anything a user could have written β€” used by expression desugaring's tuple-pattern/multi-binder-collapse transformations, and by Computable2Guarded's π’ž_par and Guarded2Network's inbox/rx naming.

Its own tiny effect class (like MonadModuleCache, not folded into FlagsEnv) since it's a mutable-store effect, not a Reader.

Instances
    def freshName {m : Type β†’ Type} [Monad m] [MonadFresh m] (namePrefix : String := "fresh") :

    A fresh identifier, guaranteed distinct from any name a user could have written: $ cannot appear in a TLA⁺ identifier (the lexer's identifierOrKeyword only ever accepts letters, digits, and _), so no scope-tracking is needed to avoid collisions β€” a syntactic argument, not a runtime check.

    Equations
    Instances For

      The lifts below carry MonadFresh through each transformer a pass stacks on top of the monad that actually owns the counter β€” one per layer that shows up in a pass's concrete runner (ReaderT for @'s context and Ξ“, StateT for the checker's metavariable/pending-bounds contexts, DiagT for every pass's diagnostics). Written on MonadFresh itself rather than obtained by lifting an underlying MonadStateOf Nat: a pass says what it needs (MonadFresh), not how the counter is stored, and the owner is free to keep it as a field of a larger state record β€” which Driver/Modules.lean's DriverState does.

      @[implicit_reducible]
      instance instMonadFreshReaderT {ρ : Type} {m : Type β†’ Type} [MonadFresh m] :

      Lift through ReaderT β€” lets a pass told only [MonadFresh m] add a local ReaderT layer and still call something needing MonadFresh under it (Desugarer/PlusCal.lean's desugarMailboxArg wraps Expression.desugar's @-reader this way).

      Equations
      @[implicit_reducible]
      instance instMonadFreshStateTOfMonad {Οƒ : Type} {m : Type β†’ Type} [Monad m] [MonadFresh m] :

      Lift through StateT β€” Elaborator.lean's runChecker runs two of them (the metavariable context and the pending bounds) between the checker and its base monad.

      Equations
      @[implicit_reducible]
      instance instMonadFreshDiagTOfMonad {Ξ± Ξ² : Type} {m : Type β†’ Type} [Monad m] [MonadFresh m] :
      MonadFresh (DiagT Ξ± Ξ² m)

      Lift through DiagT β€” every pass reports through one, so this is the layer that stands between essentially any pass and whatever owns its counter.

      Equations
      @[implicit_reducible]
      instance instMonadFreshExceptTOfMonad {Ξ΅ : Type} {m : Type β†’ Type} [Monad m] [MonadFresh m] :

      Lift through ExceptT β€” needed the moment a pass's own runner is a bare ExceptT rather than going through DiagT/MonadDiagnostic (Guarded2Network's G2NM, VerifiedCompiler/, is the first).

      Equations
      @[implicit_reducible]

      The base instance for a pass whose own runner owns a bare Nat counter directly, rather than threading it through Driver/Modules.lean's DriverState (that file's own instance, keyed on MonadStateOf DriverState m). Guarded2Network's G2NM := ExceptT G2NError (StateT Nat Id) is the first stack that wants this β€” every earlier pass runs under the driver's counter instead.

      Equations

      The counter itself lives in Driver/Modules.lean's DriverState, one per compile, and every pass β€” the checker, the desugarer, Computable2Guarded, Guarded2Network β€” draws from that same counter for the whole compile, so compiler-introduced names cannot collide across passes. Deliberately not a global IO.Ref: a process-wide counter makes a compile's generated names depend on how many compiles ran before it in the same process, which is invisible in the CLI (one compile per process) and actively wrong for the regression runner, which compiles many fixtures in one process and checks its output for determinism.