Class IRGenerationContext

java.lang.Object
org.ek9lang.compiler.phase7.generation.IRGenerationContext

public class IRGenerationContext extends Object
Central state management for IR generation, following the proven SymbolsAndScopes pattern.

This context provides stack-based scope management that eliminates parameter threading throughout the IR generation process. It manages the transient stack of scope contexts, provides debug information management, and handles instruction building coordination.

Like SymbolsAndScopes, this class provides the shared state management that allows focused helpers to work together cleanly without complex parameter passing.

  • Constructor Details

    • IRGenerationContext

      public IRGenerationContext(IRContext irContext, CoverageProbePlacer sharedProbePlacer)
      Create a new IR generation context with optional shared CoverageProbePlacer. This allows multiple constructs in a module to share probe ID numbering.
      Parameters:
      irContext - The IR context for this generation scope
      sharedProbePlacer - Optional shared probe placer; if null, a new one is created
  • Method Details

    • enterScope

      public void enterScope(String scopeId, DebugInfo debugInfo, IRFrameType frameType)
      Enter a new IR scope context.
    • enterMethodScope

      public void enterMethodScope(String scopeId, DebugInfo debugInfo, IRFrameType frameType)
      Enter a method/function scope with fresh IRContext for counter isolation. Creates new IRContext via copy constructor to ensure fresh temp var and label counters.
    • exitScope

      public void exitScope()
      Exit the current IR scope.
    • inMethodFrame

      public void inMethodFrame(String frameName, DebugInfo debugInfo, Runnable work)
      THE method frame (IR tidy W3.4, INFRA-8): a fresh IRContext - its own temp, label and scope counters - for the work, then popped. For work that sets its own operation body; methodBody(String, DebugInfo, Consumer) is the frame with one builder and the entry block. Use these, never a hand-written enterMethodScope/exitScope pair.
      Parameters:
      frameName - the frame's scope id (an operation's name, _call, constructor, ...)
    • methodBody

      public BasicBlockInstr methodBody(String frameName, DebugInfo debugInfo, Consumer<IRInstructionBuilder> body)
      THE method body (IR tidy W3.4, INFRA-8): in a method frame (inMethodFrame(String, DebugInfo, Runnable)), one builder, the body, then the entry block labelled on the frame's own counter - the operation's body.
    • currentScopeId

      public String currentScopeId()
      Get current scope ID from the stack.
    • currentDebugInfo

      public Optional<DebugInfo> currentDebugInfo()
      Get current debug info from the stack.
    • createDebugInfo

      public DebugInfo createDebugInfo(org.antlr.v4.runtime.tree.ParseTree ctx)
      Create debug info from a parse context.
    • createDebugInfo

      public DebugInfo createDebugInfo(Ek9Token token)
      Create debug info from a token.
    • createDebugInfo

      public DebugInfo createDebugInfo(org.antlr.v4.runtime.Token token)
      Create debug info from an ANTLR token.
    • createDebugInfo

      public DebugInfo createDebugInfo(IToken token)
      Create debug info from an EK9 IToken (null-safe: a null token has no location). Ek9Token is the only IToken there is, so the token is used as it is.
    • createDebugInfoAlways

      public DebugInfo createDebugInfoAlways(org.antlr.v4.runtime.Token token)
      Create debug info ALWAYS, regardless of debug mode flag. Used for require/assert statements where source location is essential for error reporting in production, not just debugging.
    • createDebugInfoAlways

      public DebugInfo createDebugInfoAlways(IToken token)
      Create debug info ALWAYS from an EK9 token, regardless of debug mode flag.
    • createDebugInfoAlways

      public DebugInfo createDebugInfoAlways(org.antlr.v4.runtime.tree.ParseTree ctx)
      Create debug info ALWAYS from a parse context, regardless of debug mode flag. Used for require/assert statements where source location is essential for error reporting in production, not just debugging.
    • getCurrentIRContext

      public IRContext getCurrentIRContext()
      Get current IRContext from stack frame (traverses up to find method-level IRContext). Always creates a fresh IRContext for proper counter isolation if none exists.
    • generateTempName

      public String generateTempName()
      Generate a unique temporary variable name.
    • generateSlotName

      public String generateSlotName()
      Generate a unique compiler-made slot name - see IRContext.generateSlotName().
    • generateScopeId

      public String generateScopeId(String prefix)
      Generate a unique scope identifier.
    • generateBlockLabel

      public String generateBlockLabel(String prefix)
      Generate a unique block label.
    • generateLabelName

      public String generateLabelName(String prefix)
      Generate a unique label name.
    • addInstruction

      public void addInstruction(IRInstr instruction)
      Add instruction to current instruction list.
    • addInstructions

      public void addInstructions(List<IRInstr> instructions)
      Add multiple instructions to current instruction list.
    • extractInstructions

      public List<IRInstr> extractInstructions()
      Get current instruction list and clear it.
    • getParsedModule

      public ParsedModule getParsedModule()
      Get the parsed module from IRContext.
    • getCompilerFlags

      public CompilerFlags getCompilerFlags()
      Get the compiler flags from IRContext.
    • getCoverageProbePlacer

      public CoverageProbePlacer getCoverageProbePlacer()
      Get the coverage probe placer for inserting coverage instrumentation.

      The placer is always available, but only inserts probes when coverage is enabled in compiler flags. This allows generator code to unconditionally call probe methods without checking coverage state.

    • isCoverageEnabled

      public boolean isCoverageEnabled()
      Check if coverage instrumentation is enabled.
    • monomorphisation

      public Monomorphisation monomorphisation()
      THE Scenario-3 monomorphisation of this context - its mapping (Monomorphisation.within(PositionalConceptualLookupMapping.Mapping, Supplier)), the dynamic-construct copies and their continuation into a nested context. Emitted types go through resolveType(ISymbol), never around it.
    • resolveTypeName

      public String resolveTypeName(ISymbol typeSymbol)
      THE "what concrete type name does this (possibly conceptual) type have inside the current Scenario-3 monomorphisation?" step - use this, and never re-derive a variant.

      Scenario 3 generates a parameterized type's method BODIES by walking the GENERIC type's AST (there is only one AST, shared by every parameterization), so every symbol recorded against that AST carries TEMPLATE types - a bare T, or a dependent parameterisation such as List of T and the Iterator of T that its iterator() returns. Each must be re-pointed at its concrete counterpart before it is emitted. Signatures and fields need none of this: those come from the parameterized symbol itself, already substituted by phase 6.

      This MUST take the type SYMBOL, never its name. A parameterised type's fully qualified name is a one-way SHA-256 digest (_List_<hash over Box::T>), so a template name cannot be decomposed - a name-to-name lookup silently returns the TEMPLATE name and leaks a class that is never generated into the emitted IR (NoClassDefFoundError, or NoSuchMethodError where it leaks into a call's return type). ConceptualTypeReplacement does the substitution structurally (in Monomorphisation), from symbols.

      Parameters:
      typeSymbol - The (possibly conceptual) type symbol
      Returns:
      The concrete type FQN to emit
    • resolveType

      public ISymbol resolveType(ISymbol typeSymbol)
      The SYMBOL form of resolveTypeName(ISymbol) - the concrete type a (possibly conceptual) type resolves to under the current generic substitution. Needed where a decision depends on the concrete type's PROPERTIES (e.g. its genus, to decide isTraitCall) rather than just its name: a generic type-parameter constrain by a trait resolves here to that trait, whereas the raw parameter's own genus is CLASS. Outside a generic (no mapping) the symbol is returned unchanged.
      Parameters:
      typeSymbol - The (possibly conceptual) type symbol
      Returns:
      The concrete type symbol to reason about
    • resolveScopeTypeName

      public String resolveScopeTypeName(IScope scope)
      resolveTypeName(ISymbol) for an owning SCOPE that is usually - but not always - a symbol (a method's parent scope, giving the type the method is emitted on). Kept here rather than repeated at each call site so the symbol form, and hence the structural substitution, is never skipped.
      Parameters:
      scope - The scope naming the owning type
      Returns:
      The concrete type FQN to emit
    • resolveTypeByIrName

      public Optional<ISymbol> resolveTypeByIrName(String fullyQualifiedName)
      THE recovery of a type symbol from the IR type NAME a phase-7 site already emitted - only for where the name is all that survived (a CallInstr carries its return type as a name). Prefer carrying the symbol.

      A plain type resolves by a type search from this module. A PARAMETERISED type does not: its name is a digest (module::_Pool_<digest>) and a name search cannot find it (the search's placeholder is not assignable to a parameterised type), so it is matched by its simple name over the scopes of the module the name is in; the digest makes it unique.