main
rs 91 lines 3 KB
Raw
1 // Copyright (c) Meta Platforms, Inc. and affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the root directory of this source tree.
5
6 //! Flattens labeled terminals where the label is not reachable, and
7 //! nulls out labels for other terminals where the label is unused.
8 //!
9 //! Corresponds to `src/ReactiveScopes/PruneUnusedLabels.ts`.
10
11 use rustc_hash::FxHashSet;
12
13 use react_compiler_hir::{
14 BlockId, ReactiveFunction, ReactiveStatement, ReactiveTerminal, ReactiveTerminalStatement,
15 ReactiveTerminalTargetKind, environment::Environment,
16 };
17
18 use crate::visitors::{ReactiveFunctionTransform, Transformed, transform_reactive_function};
19
20 /// Prune unused labels from a reactive function.
21 pub fn prune_unused_labels(
22 func: &mut ReactiveFunction,
23 env: &Environment,
24 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
25 let mut transform = Transform { env };
26 let mut labels: FxHashSet<BlockId> = FxHashSet::default();
27 transform_reactive_function(func, &mut transform, &mut labels)
28 }
29
30 struct Transform<'a> {
31 env: &'a Environment,
32 }
33
34 impl<'a> ReactiveFunctionTransform for Transform<'a> {
35 type State = FxHashSet<BlockId>;
36
37 fn env(&self) -> &Environment {
38 self.env
39 }
40
41 fn transform_terminal(
42 &mut self,
43 stmt: &mut ReactiveTerminalStatement,
44 state: &mut FxHashSet<BlockId>,
45 ) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
46 // Traverse children first
47 self.traverse_terminal(stmt, state)?;
48
49 // Collect labeled break/continue targets
50 match &stmt.terminal {
51 ReactiveTerminal::Break {
52 target,
53 target_kind: ReactiveTerminalTargetKind::Labeled,
54 ..
55 }
56 | ReactiveTerminal::Continue {
57 target,
58 target_kind: ReactiveTerminalTargetKind::Labeled,
59 ..
60 } => {
61 state.insert(*target);
62 }
63 _ => {}
64 }
65
66 // Is this terminal reachable via a break/continue to its label?
67 let is_reachable_label = stmt
68 .label
69 .as_ref()
70 .map_or(false, |label| state.contains(&label.id));
71
72 if let ReactiveTerminal::Label { block, .. } = &mut stmt.terminal {
73 if !is_reachable_label {
74 // Flatten labeled terminals where the label isn't necessary.
75 // Note: In TS, there's a check for `last.terminal.target === null`
76 // to pop a trailing break, but since target is always a BlockId (number),
77 // that check is always false, so the trailing break is never removed.
78 let flattened = std::mem::take(block);
79 return Ok(Transformed::ReplaceMany(flattened));
80 }
81 }
82
83 if !is_reachable_label {
84 if let Some(label) = &mut stmt.label {
85 label.implicit = true;
86 }
87 }
88
89 Ok(Transformed::Keep)
90 }
91 }