Class AbstractSyntheticGenerator

java.lang.Object
org.ek9lang.compiler.phase7.synthesis.AbstractSyntheticGenerator
Direct Known Subclasses:
CompareGenerator, CopyGenerator, DerivedComparisonGenerator, EqualsGenerator, FieldSetStatusGenerator, HashCodeGenerator, IsSetGenerator, NotEqualsGenerator, ToJsonGenerator, ToStringGenerator

public abstract class AbstractSyntheticGenerator extends Object
Base class for synthetic operator generators.

Provides common patterns and utilities used by all synthetic generators:

  • IsSet guard generation for this and parameters
  • Unset return block generation
  • Field iteration utilities
  • Memory management (RETAIN/SCOPE_REGISTER) patterns

All generated IR follows the EK9 tri-state semantics where operations return unset if any operand is unset.

  • Field Details

  • Constructor Details

    • AbstractSyntheticGenerator

      protected AbstractSyntheticGenerator(IRGenerationContext stackContext)
      Create a new synthetic generator with the given context.
      Parameters:
      stackContext - The IR generation context
  • Method Details

    • getReturnBlockHelper

      protected ReturnBlockHelper getReturnBlockHelper()
      Get the shared ReturnBlockHelper instance.

      Lazily initializes a single ReturnBlockHelper per generator instance, avoiding the need to create a new instance in each generate() method.

      Returns:
      The shared ReturnBlockHelper instance
    • getTypeName

      protected String getTypeName(ISymbol symbol)
      Get the fully qualified name for a type.
      Parameters:
      symbol - The symbol to get the type name for
      Returns:
      The fully qualified type name
    • getBooleanTypeName

      protected String getBooleanTypeName()
      Get the Boolean type name from the EK9 type system.
    • getIntegerTypeName

      protected String getIntegerTypeName()
      Get the Integer type name from the EK9 type system.
    • getStringTypeName

      protected String getStringTypeName()
      Get the String type name from the EK9 type system.
    • getBitsTypeName

      protected String getBitsTypeName()
      Get the Bits type name from the EK9 type system.
    • getVoidTypeName

      protected String getVoidTypeName()
      Get the Void type name from the EK9 type system.
    • generateTempName

      protected String generateTempName()
      Generate a temporary variable name.
    • generateLabelName

      protected String generateLabelName(String prefix)
      Generate a label name with the given prefix.
    • createDebugInfo

      protected DebugInfo createDebugInfo(ISymbol symbol)
      Create debug info from a symbol's source token.
    • createSynthesisScope

      protected SynthesisScope createSynthesisScope(ISymbol symbol, String scopeLabel)
      Create an SynthesisScope for the given operator symbol.

      This bundles debugInfo and scopeId together for cleaner method signatures. Use this at the start of a generate() method:

        final var ctx = createSynthesisScope(operatorSymbol, "_eq");
        // Use ctx.debugInfo() and ctx.scopeId() or pass ctx to methods
      
      Parameters:
      symbol - The method symbol to get debug info from
      scopeLabel - The label for the scope (e.g., "_eq", "_cmp", "_hashcode")
      Returns:
      SynthesisScope bundling debugInfo and scopeId
    • initializeScope

      protected AbstractSyntheticGenerator.ScopeSetup initializeScope(MethodSymbol operatorSymbol, String scopeLabel, String returnVarName, String returnTypeName)
      Initialize scope with return variable reference.

      This consolidates the common boilerplate at the start of every generate() method:

        final var ctx = createSynthesisScope(operatorSymbol, "_eq");
        final var instructions = new ArrayList<IRInstr>();
        instructions.add(ScopeInstr.enter(ctx.scopeId(), ctx.debugInfo()));
        instructions.add(MemoryInstr.reference(RETURN_VAR, getBooleanTypeName(), ctx.debugInfo()));
      

      Becomes:

        final var setup = initializeScope(operatorSymbol, "_eq", RETURN_VAR, getBooleanTypeName());
        final var ctx = setup.ctx();
        final var instructions = setup.instructions();
      
      Parameters:
      operatorSymbol - The operator method symbol for debug info
      scopeLabel - The scope label (e.g., "_eq", "_cmp")
      returnVarName - The return variable name (typically "rtn")
      returnTypeName - The return type name
      Returns:
      ScopeSetup containing context and initialized instruction list
    • generateBinaryOperatorGuards

      protected List<IRInstr> generateBinaryOperatorGuards(String aggregateTypeName, String paramName, String unsetLabel, SynthesisScope ctx)
      Generate isSet guards for binary operators (this and param).

      Most binary operators (==, <>, <=>) need to check that both this and the parameter are set before comparing. This helper consolidates the common two-guard pattern:

        instructions.addAll(generateThisIsSetGuard(aggregateTypeName, returnUnsetLabel, ctx));
        instructions.addAll(generateIsSetGuard(paramName, aggregateTypeName, returnUnsetLabel, ctx));
      
      Parameters:
      aggregateTypeName - The fully qualified type name of both this and param
      paramName - The parameter variable name (typically "param")
      unsetLabel - Label to branch to if either is unset
      ctx - Synthesis scope context
      Returns:
      List of IR instructions for both guards
    • generateThisIsSetGuard

      protected List<IRInstr> generateThisIsSetGuard(String aggregateTypeName, DebugInfo debugInfo, String unsetLabel, String scopeId)
      Generate isSet guard check for 'this' with branch to unset return.

      Pattern:

        _temp = CALL this._isSet() -> Boolean
        RETAIN _temp
        SCOPE_REGISTER _temp, scope_id
        _temp_val = UNBOX _temp -> boolean
        BRANCH_IF_FALSE _temp_val -> unset_label
      
      Parameters:
      aggregateTypeName - The fully qualified type name of the aggregate
      debugInfo - Debug information for the instructions
      unsetLabel - Label to branch to if unset
      scopeId - Current scope ID for memory management
      Returns:
      List of IR instructions for the guard
    • generateIsSetGuard

      protected List<IRInstr> generateIsSetGuard(String variableName, String typeName, DebugInfo debugInfo, String unsetLabel, String scopeId)
      Generate isSet guard check for a variable with branch to unset return.

      The check generates:

        1. _temp = CALL variable._isSet() -> Boolean
        2. RETAIN/SCOPE_REGISTER _temp
        3. _tempBool = CALL _temp._true() -> boolean (primitive)
        4. RETAIN/SCOPE_REGISTER _tempBool
        5. BRANCH_FALSE _tempBool, unsetLabel
      
      Parameters:
      variableName - The variable to check
      typeName - The fully qualified type name of the variable
      debugInfo - Debug information for the instructions
      unsetLabel - Label to branch to if unset
      scopeId - Current scope ID for memory management
      Returns:
      List of IR instructions for the guard
    • generateAnyFieldSetGuard

      protected List<IRInstr> generateAnyFieldSetGuard(AggregateSymbol aggregateSymbol, String aggregateTypeName, DebugInfo debugInfo, String returnUnsetLabel, String scopeId)
      Generate guard that returns UNSET if no fields are set (ANY field set semantics).

      Uses _fieldSetStatus()._empty() directly, avoiding dependency on ? operator. Also checks super._isSet() if super has ? operator defined.

      Pattern:

        // If super has ? operator, check it first
        superIsSet = super._isSet()
        superBool = superIsSet._true()
        BRANCH_TRUE superBool, continue_label  // Super is set, skip own field check
      
        // Check own fields
        status = this._fieldSetStatus() -> Bits
        isEmpty = status._empty() -> Boolean
        isEmptyBool = isEmpty._true()
        BRANCH_TRUE isEmptyBool, returnUnsetLabel  // No fields set, return UNSET
      
        continue_label:
      
      Parameters:
      aggregateSymbol - The aggregate being checked
      aggregateTypeName - Fully qualified type name
      debugInfo - Debug information
      returnUnsetLabel - Label to branch to if no fields are set
      scopeId - Current scope ID
      Returns:
      List of IR instructions implementing the guard
    • generateUnsetReturnBlock

      protected List<IRInstr> generateUnsetReturnBlock(String returnTypeName, String returnVarName, DebugInfo debugInfo, String scopeId)
      Generate the return_unset basic block that returns an unset value.

      Pattern for Boolean return:

        return_unset:
          _result = CALL Boolean._new() -> Boolean
          RETAIN _result
          STORE rtn = _result
          SCOPE_EXIT scope_id
          RETURN rtn
      
      Parameters:
      returnTypeName - The type of value to return (e.g., Boolean, Integer)
      returnVarName - The name of the return variable (typically "rtn")
      debugInfo - Debug information for the instructions
      scopeId - Current scope ID for cleanup
      Returns:
      List of IR instructions for the unset return block
    • generateBooleanReturnBlock

      protected List<IRInstr> generateBooleanReturnBlock(boolean value, String returnVarName, DebugInfo debugInfo, String scopeId)
      Generate a return block that returns a specific boolean value (true or false).
      Parameters:
      value - The boolean value to return (true or false)
      returnVarName - The name of the return variable
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • getSyntheticFields

      protected List<ISymbol> getSyntheticFields(AggregateSymbol aggregateSymbol)
      Get all fields from an aggregate that should be included in synthetic operations.

      This returns only the direct properties of the aggregate, not inherited ones. Inherited fields are handled by calling super's synthetic operators.

      Parameters:
      aggregateSymbol - The aggregate to get fields from
      Returns:
      List of field symbols
    • superHasOperator

      protected boolean superHasOperator(AggregateSymbol aggregateSymbol, String operatorName)
      Check if the super aggregate has a specific operator defined.
      Parameters:
      aggregateSymbol - The aggregate whose super we're checking
      operatorName - The operator name (e.g., "==", "<=>")
      Returns:
      true if super has the operator, false otherwise
    • isAnyType

      protected boolean isAnyType(IAggregateSymbol type)
      Check if the given type is the Any type.
    • aggregateHasOperator

      protected boolean aggregateHasOperator(AggregateSymbol aggregateSymbol, String operatorName)
      Check if the given aggregate has a specific operator defined (in its own scope).
      Parameters:
      aggregateSymbol - The aggregate to check
      operatorName - The operator name (e.g., "?", "$", "#?")
      Returns:
      true if the aggregate has the operator, false otherwise
    • getSuperTypeName

      protected String getSuperTypeName(AggregateSymbol aggregateSymbol)
      Get the fully qualified name of the super aggregate.
      Parameters:
      aggregateSymbol - The aggregate to get super from
      Returns:
      The super type name, or empty string if no super (other than Any)
    • generateFieldLoad

      protected List<IRInstr> generateFieldLoad(String targetVar, String objectVar, String fieldName, DebugInfo debugInfo, String scopeId)
      Generate field load instruction with memory management.

      Pattern:

        _temp = LOAD this.fieldName -> FieldType
        RETAIN _temp
        SCOPE_REGISTER _temp, scope_id
      
      Parameters:
      targetVar - The variable to store the loaded value
      objectVar - The object to load from (typically "this" or "other")
      fieldName - The field name to load
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateConstructorCall

      protected List<IRInstr> generateConstructorCall(String resultVar, String typeName, DebugInfo debugInfo, String scopeId)
      Generate a constructor call to create a new instance.

      Pattern:

        _result = CALL (Type)Type.<init>() [pure=true, complexity=1, effects=RETURN_MUTATION]
        RETAIN _result
        SCOPE_REGISTER _result, scope_id
      
      Parameters:
      resultVar - Variable to store result
      typeName - Fully qualified type name to construct
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateConstructorCall

      protected List<IRInstr> generateConstructorCall(String resultVar, String typeName, String argument, String parameterType, DebugInfo debugInfo, String scopeId)
      Generate a constructor call with a single argument to create a new instance.

      Convenience overload for constructors taking exactly one argument.

      Parameters:
      resultVar - Variable to store result
      typeName - Fully qualified type name to construct
      argument - Single argument variable name
      parameterType - Single parameter type
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateConstructorCallWithArgs

      protected List<IRInstr> generateConstructorCallWithArgs(String resultVar, String typeName, List<String> arguments, List<String> parameterTypes, DebugInfo debugInfo, String scopeId)
      Generate a constructor call with arguments to create a new instance.

      Pattern:

        _result = CALL (Type)Type.<init>(args) [pure=true, complexity=1, effects=RETURN_MUTATION]
        RETAIN _result
        SCOPE_REGISTER _result, scope_id
      
      Parameters:
      resultVar - Variable to store result
      typeName - Fully qualified type name to construct
      arguments - Argument variable names to pass to constructor
      parameterTypes - Parameter types (must match arguments)
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateStringLiteralLoad

      protected List<IRInstr> generateStringLiteralLoad(String resultVar, String literalValue, DebugInfo debugInfo, String scopeId)
      Generate a string literal load with memory management.

      Pattern:

        _temp = LOAD_LITERAL "value", org.ek9.lang::String
        RETAIN _temp
        SCOPE_REGISTER _temp, scope_id
      
      Parameters:
      resultVar - Variable to store the loaded literal
      literalValue - The string literal value
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateFieldSetStatusCheck

      protected List<IRInstr> generateFieldSetStatusCheck(String otherParamName, String aggregateTypeName, DebugInfo debugInfo, String scopeId, String returnUnsetLabel)
      Generate field set status comparison check.

      This optimization compares the _fieldSetStatus() bitmasks of both objects. If the bitmasks differ, it means different fields are set/unset between the objects, so the comparison result should be unset (tri-state semantics).

      Pattern:

        _thisStatus = CALL this._fieldSetStatus() -> Bits
        _otherStatus = CALL other._fieldSetStatus() -> Bits
        _statusEq = CALL _thisStatus._eq(_otherStatus) -> Boolean
        _statusEqSet = CALL _statusEq._isSet() -> Boolean
        BRANCH_IF_FALSE _statusEqSet -> return_unset  // shouldn't happen but safe
        _statusEqVal = CALL _statusEq._true() -> boolean
        BRANCH_IF_FALSE _statusEqVal -> return_unset  // different field set patterns
      
      Parameters:
      otherParamName - The name of the other parameter to compare with (typically "param")
      aggregateTypeName - The fully qualified type name of the aggregate
      debugInfo - Debug information
      scopeId - Current scope ID
      returnUnsetLabel - Label to branch to if bitmasks differ
      Returns:
      List of IR instructions implementing the check
    • generateUnsetReturnBlockWithLabel

      protected List<IRInstr> generateUnsetReturnBlockWithLabel(String labelName, String returnTypeName, String returnVarName, DebugInfo debugInfo, String scopeId)
      Generate unset return block at a specific label.

      Unlike generateUnsetReturnBlock(String, String, DebugInfo, String), this version uses the provided label name instead of generating a new one. Use this when you need to branch to the unset return from multiple locations.

      Parameters:
      labelName - The label name for this block
      returnTypeName - The type of value to return
      returnVarName - The name of the return variable
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions for the unset return block
    • generateResultReturnBlock

      protected List<IRInstr> generateResultReturnBlock(String labelName, String returnVarName, DebugInfo debugInfo, String scopeId)
      Generate a result return block that returns whatever is in the return variable.

      This is a shared return point when the return variable has already been set by the caller. Only performs scope cleanup and return.

      Pattern:

        label_name:
        SCOPE_EXIT scope_id
        RETURN returnVarName
      
      Parameters:
      labelName - The label name for this block
      returnVarName - The return variable name (already set by caller)
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions for the return block
    • generateResultReturnBlock

      protected List<IRInstr> generateResultReturnBlock(String labelName, String returnVarName, SynthesisScope ctx)
      Generate result return block using SynthesisScope.

      Convenience overload that extracts debugInfo and scopeId from the context.

      Parameters:
      labelName - The label for this return block
      returnVarName - The return variable name (already set by caller)
      ctx - Synthesis scope context
      Returns:
      List of IR instructions for the return block
    • generateMethodCall

      protected List<IRInstr> generateMethodCall(String resultVar, String targetVar, String targetType, String methodName, String returnType, DebugInfo debugInfo, String scopeId)
      Generate a no-argument method call with result storage and memory management.

      Convenience overload for the common case where the method takes no arguments.

      Parameters:
      resultVar - Variable to store result
      targetVar - Variable to call method on (null for static calls)
      targetType - Fully qualified type name of the target
      methodName - Method to call
      returnType - Return type of method
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateMethodCall

      protected List<IRInstr> generateMethodCall(String resultVar, String targetVar, String targetType, String methodName, String argument, String parameterType, String returnType, DebugInfo debugInfo, String scopeId)
      Generate a single-argument method call with result storage and memory management.

      Convenience overload for the common case where the method takes one argument.

      Parameters:
      resultVar - Variable to store result
      targetVar - Variable to call method on (null for static calls)
      targetType - Fully qualified type name of the target
      methodName - Method to call
      argument - Single argument variable name
      parameterType - Single parameter type
      returnType - Return type of method
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateMethodCall

      protected List<IRInstr> generateMethodCall(String resultVar, String targetVar, String targetType, String methodName, List<String> arguments, List<String> parameterTypes, String returnType, DebugInfo debugInfo, String scopeId)
      Generate a method call on a variable with result storage and memory management.
      Parameters:
      resultVar - Variable to store result
      targetVar - Variable to call method on (null for static calls)
      targetType - Fully qualified type name of the target
      methodName - Method to call
      arguments - Method argument variable names
      parameterTypes - Parameter types (must match arguments)
      returnType - Return type of method
      debugInfo - Debug information
      scopeId - Current scope ID
      Returns:
      List of IR instructions
    • generateMethodCall

      protected List<IRInstr> generateMethodCall(String resultVar, String targetVar, String targetType, String methodName, String returnType, SynthesisScope ctx)
      Generate a no-argument method call using SynthesisScope.
      Parameters:
      resultVar - Variable to store result
      targetVar - Variable to call method on
      targetType - Type of target
      methodName - Method to call
      returnType - Return type
      ctx - Synthesis scope context
      Returns:
      List of IR instructions
    • generateMethodCall

      protected List<IRInstr> generateMethodCall(String resultVar, String targetVar, String targetType, String methodName, String argument, String parameterType, String returnType, SynthesisScope ctx)
      Generate a single-argument method call using SynthesisScope.
      Parameters:
      resultVar - Variable to store result
      targetVar - Variable to call method on
      targetType - Type of target
      methodName - Method to call
      argument - Argument variable
      parameterType - Argument type
      returnType - Return type
      ctx - Synthesis scope context
      Returns:
      List of IR instructions
    • generateIsSetGuard

      protected List<IRInstr> generateIsSetGuard(String variableName, String typeName, String unsetLabel, SynthesisScope ctx)
      Generate isSet guard check for a variable using SynthesisScope.
      Parameters:
      variableName - The variable to check
      typeName - The fully qualified type name of the variable
      unsetLabel - Label to branch to if unset
      ctx - IR generation context
      Returns:
      List of IR instructions for the guard
    • generateThisIsSetGuard

      protected List<IRInstr> generateThisIsSetGuard(String aggregateTypeName, String unsetLabel, SynthesisScope ctx)
      Generate isSet guard for 'this' using SynthesisScope.
      Parameters:
      aggregateTypeName - The fully qualified type name of the aggregate
      unsetLabel - Label to branch to if unset
      ctx - IR generation context
      Returns:
      List of IR instructions for the guard
    • generateFieldLoad

      protected List<IRInstr> generateFieldLoad(String targetVar, String objectVar, String fieldName, SynthesisScope ctx)
      Generate field load using SynthesisScope.
      Parameters:
      targetVar - The variable to store the loaded value
      objectVar - The object to load from
      fieldName - The field name to load
      ctx - IR generation context
      Returns:
      List of IR instructions
    • generateConstructorCall

      protected List<IRInstr> generateConstructorCall(String resultVar, String typeName, SynthesisScope ctx)
      Generate constructor call using SynthesisScope.
      Parameters:
      resultVar - Variable to store result
      typeName - Fully qualified type name to construct
      ctx - IR generation context
      Returns:
      List of IR instructions
    • generateUnsetReturnBlockWithLabel

      protected List<IRInstr> generateUnsetReturnBlockWithLabel(String labelName, String returnTypeName, String returnVarName, SynthesisScope ctx)
      Generate unset return block using SynthesisScope.
      Parameters:
      labelName - The label name for this block
      returnTypeName - The type of value to return
      returnVarName - The name of the return variable
      ctx - IR generation context
      Returns:
      List of IR instructions for the unset return block
    • generateBooleanBranch

      protected List<IRInstr> generateBooleanBranch(String booleanVar, String label, boolean branchIfTrue, SynthesisScope ctx)
      Extract primitive boolean from Boolean object and branch.

      This consolidates the common pattern of calling _true() on a Boolean object to get a primitive boolean, then branching based on the result.

      Pattern:

        _temp = CALL booleanVar._true() -> boolean
        BRANCH_IF_TRUE/FALSE _temp, label
      
      Parameters:
      booleanVar - The Boolean variable to extract value from
      label - The label to branch to
      branchIfTrue - If true, branch when Boolean is true; if false, branch when false
      ctx - Synthesis scope context
      Returns:
      List of IR instructions