use indexmap::IndexMap;
use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
use react_compiler_hir::environment::Environment;
use std::collections::HashSet;
use react_compiler_hir::{
AliasingEffect, BlockId, Effect, EvaluationOrder, FunctionId, HirFunction, IdentifierId,
InstructionValue, Place, ReactFunctionType, HIR,
};
pub fn analyse_functions<F>(func: &mut HirFunction, env: &mut Environment, debug_logger: &mut F) -> Result<(), CompilerDiagnostic>
where
F: FnMut(&HirFunction, &Environment),
{
let mut inner_func_ids: Vec<FunctionId> = Vec::new();
for (_block_id, block) in &func.body.blocks {
for instr_id in &block.instructions {
let instr = &func.instructions[instr_id.0 as usize];
match &instr.value {
InstructionValue::FunctionExpression { lowered_func, .. }
| InstructionValue::ObjectMethod { lowered_func, .. } => {
inner_func_ids.push(lowered_func.func);
}
_ => {}
}
}
}
for func_id in inner_func_ids {
let mut inner_func = std::mem::replace(
&mut env.functions[func_id.0 as usize],
placeholder_function(),
);
lower_with_mutation_aliasing(&mut inner_func, env, debug_logger)?;
if env.has_invariant_errors() {
env.functions[func_id.0 as usize] = inner_func;
return Ok(());
}
for operand in &inner_func.context {
let new_range = env.new_mutable_range(EvaluationOrder(0), EvaluationOrder(0));
let ident = &mut env.identifiers[operand.identifier.0 as usize];
ident.mutable_range = new_range;
ident.scope = None;
}
env.functions[func_id.0 as usize] = inner_func;
}
Ok(())
}
fn lower_with_mutation_aliasing<F>(func: &mut HirFunction, env: &mut Environment, debug_logger: &mut F) -> Result<(), CompilerDiagnostic>
where
F: FnMut(&HirFunction, &Environment),
{
analyse_functions(func, env, debug_logger)?;
crate::infer_mutation_aliasing_effects::infer_mutation_aliasing_effects(
func, env, true,
)?;
if env.has_invariant_errors() {
return Ok(());
}
react_compiler_optimization::dead_code_elimination(func, env);
let function_effects = crate::infer_mutation_aliasing_ranges::infer_mutation_aliasing_ranges(
func, env, true,
)?;
if let Err(err) = react_compiler_ssa::rewrite_instruction_kinds_based_on_reassignment(func, env) {
env.errors.merge(err);
return Ok(());
}
crate::infer_reactive_scope_variables::infer_reactive_scope_variables(func, env)?;
func.aliasing_effects = Some(function_effects.clone());
let mut captured_or_mutated: HashSet<IdentifierId> = HashSet::new();
for effect in &function_effects {
match effect {
AliasingEffect::Assign { from, .. }
| AliasingEffect::Alias { from, .. }
| AliasingEffect::Capture { from, .. }
| AliasingEffect::CreateFrom { from, .. }
| AliasingEffect::MaybeAlias { from, .. } => {
captured_or_mutated.insert(from.identifier);
}
AliasingEffect::Mutate { value, .. }
| AliasingEffect::MutateConditionally { value }
| AliasingEffect::MutateTransitive { value }
| AliasingEffect::MutateTransitiveConditionally { value } => {
captured_or_mutated.insert(value.identifier);
}
AliasingEffect::Impure { .. }
| AliasingEffect::Render { .. }
| AliasingEffect::MutateFrozen { .. }
| AliasingEffect::MutateGlobal { .. }
| AliasingEffect::CreateFunction { .. }
| AliasingEffect::Create { .. }
| AliasingEffect::Freeze { .. }
| AliasingEffect::ImmutableCapture { .. } => {
}
AliasingEffect::Apply { .. } => {
return Err(CompilerDiagnostic::new(
ErrorCategory::Invariant,
"[AnalyzeFunctions] Expected Apply effects to be replaced with more precise effects",
None,
));
}
}
}
for operand in &mut func.context {
if captured_or_mutated.contains(&operand.identifier)
|| operand.effect == Effect::Capture
{
operand.effect = Effect::Capture;
} else {
operand.effect = Effect::Read;
}
}
debug_logger(func, env);
Ok(())
}
fn placeholder_function() -> HirFunction {
HirFunction {
loc: None,
id: None,
name_hint: None,
fn_type: ReactFunctionType::Other,
params: Vec::new(),
return_type_annotation: None,
returns: Place {
identifier: IdentifierId(0),
effect: Effect::Unknown,
reactive: false,
loc: None,
},
context: Vec::new(),
body: HIR {
entry: BlockId(0),
blocks: IndexMap::new(),
},
instructions: Vec::new(),
generator: false,
is_async: false,
directives: Vec::new(),
aliasing_effects: None,
}
}