Class ControlFlowChainGenerator
- All Implemented Interfaces:
Function<ControlFlowChainDetails, List<IRInstr>>
Supported Operators:
- Question operator (?) - null/set checking
- Assign-if-unset (:=?) - conditional assignment
- Null coalescing operator (??) - returns LHS if allocated, else RHS
- Elvis coalescing operator (:?) - returns LHS if allocated AND set, else RHS
- Comparison coalescing operators (
<?, >?, <=?, >=?) - safe comparison with null/unset handling
Key architectural benefits:
- Single source of truth for operator control flow logic
- Consistent memory management patterns across all operators
- Unified optimization metadata for backend code generation
- Reduced code duplication and maintenance burden
NOTE: If/else, switch, while, do-while, for, and try/catch statements have their own dedicated IR generators and are NOT generated by this class.
-
Field Summary
Fields inherited from class AbstractGenerator
constantCopyOnAccessLoader, debugInfoCreator, instructionBuilder, MAX_MESSAGE_LENGTH, stackContext, synthesisedCallMetaData, typeNameOrException -
Constructor Summary
ConstructorsConstructorDescriptionControlFlowChainGenerator(IRGenerationContext stackContext, VariableMemoryManagement variableMemoryManagement, EscapingValueMemoryManagement escapingValueMemoryManagement, Function<ExprProcessingDetails, List<IRInstr>> rawExprProcessor, Supplier<StatementOwnedCloseables> ownedCloseables) -
Method Summary
Modifier and TypeMethodDescriptionapply(ControlFlowChainDetails details) generateAssignIfUnset(ISymbol lhsSymbol, List<IRInstr> assignmentEvaluation, String assignmentResult, DebugInfo debugInfo) Generate SWITCH_CHAIN_BLOCK for assign-if-unset (:=?).generateAssignIfUnsetTestForTemp(String variableName, ISymbol variableSymbol, boolean isTraitCall, String resultVariable, DebugInfo debugInfo) The question operator on a temp that holds the current value of a:=?target: a null there is the never-written slot:=?exists to write, so it is NOT a strict-absent observation (StrictAbsent).generateComparisonCoalescing(ExprProcessingDetails lhsDetails, ExprProcessingDetails rhsDetails, String comparisonOperator, String chainType) Generate CONTROL_FLOW_CHAIN for Comparison Coalescing operators (<?, >?, <=?, >=?).generateElvisCoalescing(ExprProcessingDetails lhsDetails, ExprProcessingDetails rhsDetails) Generate CONTROL_FLOW_CHAIN for Elvis Coalescing operator (:?).generateExternalFieldAssignIfUnset(String isSetBoolean, List<IRInstr> assignmentBody, DebugInfo debugInfo) Generate CONTROL_FLOW_CHAIN for assign-if-unset on external field.generateNullCoalescing(ExprProcessingDetails lhsDetails, ExprProcessingDetails rhsDetails) Generate CONTROL_FLOW_CHAIN for Null Coalescing operator (??).generateQuestionOperator(ExprProcessingDetails exprDetails) Generate SWITCH_CHAIN_BLOCK for Question operator (?).generateQuestionOperatorForTemp(String variableName, ISymbol variableSymbol, boolean isTraitCall, String resultVariable, DebugInfo debugInfo) Generate SWITCH_CHAIN_BLOCK for Question operator applied to a temp variable.generateQuestionOperatorForVariable(ISymbol variableSymbol, String resultVariable, DebugInfo debugInfo) Generate SWITCH_CHAIN_BLOCK for Question operator applied to a variable.generateTernary(ExprProcessingDetails details) Generate IR for ternary operator: condition <- thenValue : elseValue.Methods inherited from class AbstractGenerator
addCoverageProbe, createTempVariable, createTempVariableFromContext, extractReturnType, getRecordedSymbolOrException, processBlockStatements, processBlockStatements, truncateMessage, truncateMessage
-
Constructor Details
-
ControlFlowChainGenerator
public ControlFlowChainGenerator(IRGenerationContext stackContext, VariableMemoryManagement variableMemoryManagement, EscapingValueMemoryManagement escapingValueMemoryManagement, Function<ExprProcessingDetails, List<IRInstr>> rawExprProcessor, Supplier<StatementOwnedCloseables> ownedCloseables)
-
-
Method Details
-
apply
- Specified by:
applyin interfaceFunction<ControlFlowChainDetails, List<IRInstr>>
-
generateQuestionOperator
Generate SWITCH_CHAIN_BLOCK for Question operator (?). Converts "operand?" to a two-case chain: null check + _isSet() default. Question operator is self-contained - no RETAIN/SCOPE_REGISTER needed on input. -
generateQuestionOperatorForVariable
-
generateQuestionOperatorForTemp
public List<IRInstr> generateQuestionOperatorForTemp(String variableName, ISymbol variableSymbol, boolean isTraitCall, String resultVariable, DebugInfo debugInfo) Generate SWITCH_CHAIN_BLOCK for Question operator applied to a temp variable. Used when the variable is a temp holding an expression result, not a named symbol.- Parameters:
variableName- The IR variable name (temp variable)variableSymbol- The symbol whose type both names the target and supplies the _isSet metadataisTraitCall- Whether to use INVOKEINTERFACE vs INVOKEVIRTUALresultVariable- The variable to store the Boolean result intodebugInfo- Debug information- Returns:
- IR instructions implementing the question operator
-
generateAssignIfUnsetTestForTemp
public List<IRInstr> generateAssignIfUnsetTestForTemp(String variableName, ISymbol variableSymbol, boolean isTraitCall, String resultVariable, DebugInfo debugInfo) The question operator on a temp that holds the current value of a:=?target: a null there is the never-written slot:=?exists to write, so it is NOT a strict-absent observation (StrictAbsent). -
generateAssignIfUnset
-
generateExternalFieldAssignIfUnset
public List<IRInstr> generateExternalFieldAssignIfUnset(String isSetBoolean, List<IRInstr> assignmentBody, DebugInfo debugInfo) Generate CONTROL_FLOW_CHAIN for assign-if-unset on external field.Inverts the isSetBoolean (assign when NOT set) and wraps the assignment body in a ASSIGN_IF_UNSET control flow chain.
- Parameters:
isSetBoolean- The Boolean variable holding the isSet result of the field's current valueassignmentBody- The IR instructions to execute if field is unset (evaluate RHS + STORE_FIELD)debugInfo- Debug information- Returns:
- IR instructions implementing the conditional field assignment
-
generateNullCoalescing
public List<IRInstr> generateNullCoalescing(ExprProcessingDetails lhsDetails, ExprProcessingDetails rhsDetails) Generate CONTROL_FLOW_CHAIN for Null Coalescing operator (??). Returns LHS if memory allocated (even if unset), else evaluates and returns RHS.IR Structure: 1 case + default
Case 1: NULL_CHECK - if LHS is null → evaluate and return RHS
Default: LHS not null → return LHS (even if unset)
-
generateElvisCoalescing
public List<IRInstr> generateElvisCoalescing(ExprProcessingDetails lhsDetails, ExprProcessingDetails rhsDetails) Generate CONTROL_FLOW_CHAIN for Elvis Coalescing operator (:?). Returns LHS if memory allocated AND set, else evaluates and returns RHS.IR Structure: 1 case + default
Case: EXPRESSION - if LHS is absent OR not set → evaluate and return RHS
Default: LHS is set → return LHS
The case's condition is the null-safe
?of the LHS (the QUESTION_OPERATOR chain: absent is not set, and_isSet()is only called on a value that is present), inverted. So the RHS is needed in ONE case and its code is generated ONCE. It used to be returned by two cases - "absent" and "not set" - and generated for each, so a nested chain of n doubled n times (triage B13 follow-up; the shared-list alternative put the same labels at two bytecode positions and corrupted the stack map). It is still evaluated only when needed. -
generateComparisonCoalescing
public List<IRInstr> generateComparisonCoalescing(ExprProcessingDetails lhsDetails, ExprProcessingDetails rhsDetails, String comparisonOperator, String chainType) Generate CONTROL_FLOW_CHAIN for Comparison Coalescing operators (<?, >?, <=?, >=?). Returns LHS if both operands valid and comparison true, else gracefully handles null/unset.IR Structure: 3 cases + default - LHS then RHS evaluated once, before any case
Case 1: LHS absent or not set (the null-safe?, inverted) → return RHS
Case 2: RHS absent or not set → return LHS
Case 3: COMPARISON — derived from<=>:(LHS <=> RHS) <op> 0→ if true, return LHS
Default: return RHS
The RHS is generated ONCE. It was generated for each case that needed it - three times - so a nested chain grew as 3^n (the shared-list alternative put the same labels at two bytecode positions and corrupted the stack map). Every path evaluates it exactly once, so evaluating it up front changes nothing observable.
- Parameters:
comparisonOperator- The relational method name applied to the<=>Integer result vs 0 (e.g., "_lt", "_gt", "_lteq", "_gteq")chainType- The chain type for IR (e.g., "LESS_THAN_COALESCING_OPERATOR")
-
generateTernary
Generate IR for ternary operator: condition <- thenValue : elseValue.Pattern: CONTROL_FLOW_CHAIN with TERNARY_OPERATOR chain type - Condition evaluation in tight scope - True branch evaluates thenValue - False branch (default) evaluates elseValue
- Parameters:
details- ExprProcessingDetails containing result variable and ternary expression context- Returns:
- List of IR instructions for ternary operator
-