// Copyright (c) Meta Platforms, Inc. and affiliates. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. //! Reactive function types — tree representation of a compiled function. //! //! `ReactiveFunction` is derived from the HIR CFG by `BuildReactiveFunction`. //! Control flow constructs (if/switch/loops/try) and reactive scopes become //! nested blocks rather than block references. //! //! Corresponds to the reactive types in `HIR.ts`. use react_compiler_diagnostics::SourceLocation; use crate::{ AliasingEffect, BlockId, EvaluationOrder, InstructionValue, LogicalOperator, ParamPattern, Place, ScopeId, }; // ============================================================================= // ReactiveFunction // ============================================================================= /// Tree representation of a compiled function, converted from the CFG-based HIR. /// TS: ReactiveFunction in HIR.ts #[derive(Debug, Clone)] pub struct ReactiveFunction { pub loc: Option, pub id: Option, pub name_hint: Option, pub params: Vec, pub generator: bool, pub is_async: bool, pub body: ReactiveBlock, pub directives: Vec, // No env field — passed separately per established Rust convention } // ============================================================================= // ReactiveBlock and ReactiveStatement // ============================================================================= /// TS: ReactiveBlock = Array pub type ReactiveBlock = Vec; /// TS: ReactiveStatement (discriminated union with 'kind' field) #[derive(Debug, Clone)] pub enum ReactiveStatement { Instruction(ReactiveInstruction), Terminal(ReactiveTerminalStatement), Scope(ReactiveScopeBlock), PrunedScope(PrunedReactiveScopeBlock), } // ============================================================================= // ReactiveInstruction and ReactiveValue // ============================================================================= /// TS: ReactiveInstruction #[derive(Debug, Clone)] pub struct ReactiveInstruction { pub id: EvaluationOrder, pub lvalue: Option, pub value: ReactiveValue, pub effects: Option>, pub loc: Option, } /// Extends InstructionValue with compound expression types that were /// separate blocks+terminals in HIR but become nested expressions here. /// TS: ReactiveValue = InstructionValue | ReactiveLogicalValue | ... #[derive(Debug, Clone)] pub enum ReactiveValue { /// All ~35 base instruction value kinds Instruction(InstructionValue), /// TS: ReactiveLogicalValue LogicalExpression { operator: LogicalOperator, left: Box, right: Box, loc: Option, }, /// TS: ReactiveTernaryValue ConditionalExpression { test: Box, consequent: Box, alternate: Box, loc: Option, }, /// TS: ReactiveSequenceValue SequenceExpression { instructions: Vec, id: EvaluationOrder, value: Box, loc: Option, }, /// TS: ReactiveOptionalCallValue OptionalExpression { id: EvaluationOrder, value: Box, optional: bool, loc: Option, }, } // ============================================================================= // Terminals // ============================================================================= #[derive(Debug, Clone)] pub struct ReactiveTerminalStatement { pub terminal: ReactiveTerminal, pub label: Option, } #[derive(Debug, Clone)] pub struct ReactiveLabel { pub id: BlockId, pub implicit: bool, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum ReactiveTerminalTargetKind { Implicit, Labeled, Unlabeled, } impl std::fmt::Display for ReactiveTerminalTargetKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ReactiveTerminalTargetKind::Implicit => write!(f, "implicit"), ReactiveTerminalTargetKind::Labeled => write!(f, "labeled"), ReactiveTerminalTargetKind::Unlabeled => write!(f, "unlabeled"), } } } #[derive(Debug, Clone)] pub enum ReactiveTerminal { Break { target: BlockId, id: EvaluationOrder, target_kind: ReactiveTerminalTargetKind, loc: Option, }, Continue { target: BlockId, id: EvaluationOrder, target_kind: ReactiveTerminalTargetKind, loc: Option, }, Return { value: Place, id: EvaluationOrder, loc: Option, }, Throw { value: Place, id: EvaluationOrder, loc: Option, }, Switch { test: Place, cases: Vec, id: EvaluationOrder, loc: Option, }, DoWhile { loop_block: ReactiveBlock, test: ReactiveValue, id: EvaluationOrder, loc: Option, }, While { test: ReactiveValue, loop_block: ReactiveBlock, id: EvaluationOrder, loc: Option, }, For { init: ReactiveValue, test: ReactiveValue, update: Option, loop_block: ReactiveBlock, id: EvaluationOrder, loc: Option, }, ForOf { init: ReactiveValue, test: ReactiveValue, loop_block: ReactiveBlock, id: EvaluationOrder, loc: Option, }, ForIn { init: ReactiveValue, loop_block: ReactiveBlock, id: EvaluationOrder, loc: Option, }, If { test: Place, consequent: ReactiveBlock, alternate: Option, id: EvaluationOrder, loc: Option, }, Label { block: ReactiveBlock, id: EvaluationOrder, loc: Option, }, Try { block: ReactiveBlock, handler_binding: Option, handler: ReactiveBlock, id: EvaluationOrder, loc: Option, }, } #[derive(Debug, Clone)] pub struct ReactiveSwitchCase { pub test: Option, pub block: Option, } // ============================================================================= // Scope Blocks // ============================================================================= #[derive(Debug, Clone)] pub struct ReactiveScopeBlock { pub scope: ScopeId, pub instructions: ReactiveBlock, } #[derive(Debug, Clone)] pub struct PrunedReactiveScopeBlock { pub scope: ScopeId, pub instructions: ReactiveBlock, }