@samitouri / QOS-React-1 / commits / 06b2a50f32

[rust-compiler] Use rustc-hash FxHasher for all maps and sets (#36811)

Swaps the default hashers across the Rust compiler crates for rustc-hash's faster `FxHasher`. ## Why `FxHasher` is a simple, fast, non-cryptographic hasher. std's default (SipHash) is DoS-resistant but heavy — the compiler keys maps/sets almost entirely on small integer IDs (`IdentifierId`, `BlockId`, `ScopeId`, …) where that protection buys nothing. Switching gives: - **Performance** — faster hashing on the hot ID-keyed maps/sets used throughout the passes. - **Slight binary size reduction** — drops the heavy SipHash hasher pulled in by std's `HashMap`/`HashSet`. ## Changes - `std::collections::HashMap`/`HashSet` → `rustc_hash::FxHashMap`/`FxHashSet` - `indexmap` `IndexMap`/`IndexSet` now use `FxBuildHasher`, inlined as `IndexMap<K, V, FxBuildHasher>` (no `FxIndexMap` type aliases) - `rustc-hash = "2"` added to all 12 crate manifests - `rustc_hash`/`indexmap` imports consolidated into single brace imports at the top of each file The change is serde-neutral: `FxHashMap` and `IndexMap<_, _, FxBuildHasher>` serialize to byte-identical JSON, and `IndexMap` keeps insertion order. `cargo check`, `cargo fmt --check`, and `cargo test` all pass with no warnings.

Boshen committed Jun 23, 2026 at 05:30 UTC 06b2a50f321502514443f675eaa44be8ae826218
89 files changed +1036 -961
compiler/Cargo.lock
+12
@@ -287,6 +287,7 @@ dependencies = [
287 "react_compiler_ssa",
288 "react_compiler_typeinference",
289 "react_compiler_validation",
290 + "rustc-hash",
291 "serde",
292 "serde_json",
293 ]
@@ -297,6 +298,7 @@ version = "0.1.0"
298 dependencies = [
299 "indexmap",
300 "react_compiler_diagnostics",
301 + "rustc-hash",
302 "serde",
303 "serde-transcode",
304 "serde_json",
@@ -308,6 +310,7 @@ dependencies = [
310 name = "react_compiler_diagnostics"
311 version = "0.1.0"
312 dependencies = [
313 + "rustc-hash",
314 "serde",
315 ]
316
@@ -317,6 +320,7 @@ version = "0.1.0"
320 dependencies = [
321 "indexmap",
322 "react_compiler_diagnostics",
323 + "rustc-hash",
324 "serde",
325 "serde_json",
326 ]
@@ -332,6 +336,7 @@ dependencies = [
336 "react_compiler_optimization",
337 "react_compiler_ssa",
338 "react_compiler_utils",
339 + "rustc-hash",
340 ]
341
342 [[package]]
@@ -342,6 +347,7 @@ dependencies = [
347 "react_compiler_ast",
348 "react_compiler_diagnostics",
349 "react_compiler_hir",
350 + "rustc-hash",
351 "serde_json",
352 ]
353
@@ -367,6 +373,7 @@ dependencies = [
373 "react_compiler_hir",
374 "react_compiler_lowering",
375 "react_compiler_ssa",
376 + "rustc-hash",
377 ]
378
379 [[package]]
@@ -378,6 +385,7 @@ dependencies = [
385 "react_compiler_ast",
386 "react_compiler_diagnostics",
387 "react_compiler_hir",
388 + "rustc-hash",
389 "serde_json",
390 ]
391
@@ -388,6 +396,7 @@ dependencies = [
396 "indexmap",
397 "react_compiler_diagnostics",
398 "react_compiler_hir",
399 + "rustc-hash",
400 ]
401
402 [[package]]
@@ -397,6 +406,7 @@ dependencies = [
406 "react_compiler_diagnostics",
407 "react_compiler_hir",
408 "react_compiler_ssa",
409 + "rustc-hash",
410 ]
411
412 [[package]]
@@ -404,6 +414,7 @@ name = "react_compiler_utils"
414 version = "0.1.0"
415 dependencies = [
416 "indexmap",
417 + "rustc-hash",
418 ]
419
420 [[package]]
@@ -413,6 +424,7 @@ dependencies = [
424 "indexmap",
425 "react_compiler_diagnostics",
426 "react_compiler_hir",
427 + "rustc-hash",
428 ]
429
430 [[package]]
compiler/crates/react_compiler/Cargo.toml
+1
@@ -15,5 +15,6 @@ react_compiler_ssa = { path = "../react_compiler_ssa" }
15 react_compiler_typeinference = { path = "../react_compiler_typeinference" }
16 react_compiler_validation = { path = "../react_compiler_validation" }
17 indexmap = "2"
18 +rustc-hash = "2"
19 serde = { version = "1", features = ["derive"] }
20 serde_json = { version = "1", features = ["raw_value"] }
compiler/crates/react_compiler/src/entrypoint/imports.rs
+12 -12
@@ -4,7 +4,7 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7 -use std::collections::{HashMap, HashSet};
7 +use rustc_hash::{FxHashMap, FxHashSet};
8
9 use react_compiler_ast::common::BaseNode;
10 use react_compiler_ast::declarations::{
@@ -72,9 +72,9 @@ pub struct ProgramContext {
72 pub debug_enabled: bool,
73
74 // Internal state
75 - already_compiled: HashSet<u32>,
76 - known_referenced_names: HashSet<String>,
77 - imports: HashMap<String, HashMap<String, NonLocalImportSpecifier>>,
75 + already_compiled: FxHashSet<u32>,
76 + known_referenced_names: FxHashSet<String>,
77 + imports: FxHashMap<String, FxHashMap<String, NonLocalImportSpecifier>>,
78 }
79
80 impl ProgramContext {
@@ -104,9 +104,9 @@ impl ProgramContext {
104 renames: Vec::new(),
105 timing: TimingData::new(profiling),
106 debug_enabled,
107 - already_compiled: HashSet::new(),
108 - known_referenced_names: HashSet::new(),
109 - imports: HashMap::new(),
107 + already_compiled: FxHashSet::default(),
108 + known_referenced_names: FxHashSet::default(),
109 + imports: FxHashMap::default(),
110 }
111 }
112
@@ -230,13 +230,13 @@ impl ProgramContext {
230 }
231
232 /// Get the set of known referenced names for seeding per-function Environment UID generation.
233 - pub fn known_referenced_names(&self) -> &HashSet<String> {
233 + pub fn known_referenced_names(&self) -> &FxHashSet<String> {
234 &self.known_referenced_names
235 }
236
237 /// Merge UID names generated during a function compilation back into the program context,
238 /// so subsequent function compilations avoid collisions.
239 - pub fn merge_uid_known_names(&mut self, names: &HashSet<String>) {
239 + pub fn merge_uid_known_names(&mut self, names: &FxHashSet<String>) {
240 self.known_referenced_names.extend(names.iter().cloned());
241 }
242
@@ -259,7 +259,7 @@ impl ProgramContext {
259 }
260
261 /// Get an immutable view of the generated imports.
262 - pub fn imports(&self) -> &HashMap<String, HashMap<String, NonLocalImportSpecifier>> {
262 + pub fn imports(&self) -> &FxHashMap<String, FxHashMap<String, NonLocalImportSpecifier>> {
263 &self.imports
264 }
265 }
@@ -274,7 +274,7 @@ pub fn validate_restricted_imports(
274 Some(b) if !b.is_empty() => b,
275 _ => return None,
276 };
277 - let restricted: HashSet<&str> = blocklisted.iter().map(|s| s.as_str()).collect();
277 + let restricted: FxHashSet<&str> = blocklisted.iter().map(|s| s.as_str()).collect();
278 let mut error = CompilerError::new();
279
280 for stmt in &program.body {
@@ -326,7 +326,7 @@ pub fn add_imports_to_program(program: &mut Program, context: &ProgramContext) {
326 }
327
328 // Collect existing non-namespaced imports by module name
329 - let existing_import_indices: HashMap<String, usize> = program
329 + let existing_import_indices: FxHashMap<String, usize> = program
330 .body
331 .iter()
332 .enumerate()
compiler/crates/react_compiler/src/entrypoint/pipeline.rs
+26 -26
@@ -8,6 +8,7 @@
8 //! Analogous to TS `Pipeline.ts` (`compileFn` → `run` → `runWithEnvironment`).
9 //! Currently runs BuildHIR (lowering) and PruneMaybeThrows.
10
11 +use indexmap::IndexMap;
12 use react_compiler_ast::scope::ScopeInfo;
13 use react_compiler_diagnostics::CompilerError;
14 use react_compiler_hir::ReactFunctionType;
@@ -15,6 +16,7 @@ use react_compiler_hir::environment::Environment;
16 use react_compiler_hir::environment::OutputMode;
17 use react_compiler_hir::environment_config::EnvironmentConfig;
18 use react_compiler_lowering::FunctionNode;
19 +use rustc_hash::{FxBuildHasher, FxHashMap};
20
21 use super::compile_result::CodegenFunction;
22 use super::compile_result::CompilerErrorDetailInfo;
@@ -1229,25 +1231,23 @@ pub fn compile_outlined_fn(
1231 fn build_outlined_scope_info(
1232 func: &mut react_compiler_ast::statements::FunctionDeclaration,
1233 ) -> react_compiler_ast::scope::ScopeInfo {
1232 - use std::collections::HashMap;
1233 -
1234 use react_compiler_ast::scope::*;
1235
1236 let mut pos: u32 = 1; // reserve 0 for the function itself
1237 func.base.start = Some(0);
1238
1239 - let mut fn_bindings: HashMap<String, BindingId> = HashMap::new();
1239 + let mut fn_bindings: FxHashMap<String, BindingId> = FxHashMap::default();
1240 let mut bindings_list: Vec<BindingData> = Vec::new();
1241 - let mut ref_to_binding: indexmap::IndexMap<u32, BindingId> = indexmap::IndexMap::new();
1241 + let mut ref_to_binding: IndexMap<u32, BindingId, FxBuildHasher> = IndexMap::default();
1242
1243 // Helper to add a binding
1244 let _add_binding =
1245 |name: &str,
1246 kind: BindingKind,
1247 p: u32,
1248 - fn_bindings: &mut HashMap<String, BindingId>,
1248 + fn_bindings: &mut FxHashMap<String, BindingId>,
1249 bindings_list: &mut Vec<BindingData>,
1250 - ref_to_binding: &mut indexmap::IndexMap<u32, BindingId>| {
1250 + ref_to_binding: &mut IndexMap<u32, BindingId, FxBuildHasher>| {
1251 if fn_bindings.contains_key(name) {
1252 // Already exists, just add reference
1253 let bid = fn_bindings[name];
@@ -1296,7 +1296,7 @@ fn build_outlined_scope_info(
1296 id: ScopeId(0),
1297 parent: None,
1298 kind: ScopeKind::Program,
1299 - bindings: HashMap::new(),
1299 + bindings: FxHashMap::default(),
1300 };
1301 let fn_scope = ScopeData {
1302 id: ScopeId(1),
@@ -1305,21 +1305,21 @@ fn build_outlined_scope_info(
1305 bindings: fn_bindings,
1306 };
1307
1308 - let mut node_to_scope: HashMap<u32, ScopeId> = HashMap::new();
1308 + let mut node_to_scope: FxHashMap<u32, ScopeId> = FxHashMap::default();
1309 node_to_scope.insert(0, ScopeId(1));
1310
1311 // Mirror position maps into node-ID maps for outlined functions
1312 - let mut node_id_to_scope: HashMap<u32, ScopeId> = HashMap::new();
1312 + let mut node_id_to_scope: FxHashMap<u32, ScopeId> = FxHashMap::default();
1313 node_id_to_scope.insert(0, ScopeId(1));
1314 - let ref_node_id_to_binding: indexmap::IndexMap<u32, BindingId> =
1314 + let ref_node_id_to_binding: IndexMap<u32, BindingId, FxBuildHasher> =
1315 ref_to_binding.iter().map(|(&k, &v)| (k, v)).collect();
1316
1317 ScopeInfo {
1318 scopes: vec![program_scope, fn_scope],
1319 bindings: bindings_list,
1320 node_to_scope,
1321 - node_to_scope_end: HashMap::new(),
1322 - reference_to_binding: indexmap::IndexMap::new(),
1321 + node_to_scope_end: FxHashMap::default(),
1322 + reference_to_binding: IndexMap::default(),
1323 ref_node_id_to_binding,
1324 node_id_to_scope,
1325 program_scope: ScopeId(0),
@@ -1331,9 +1331,9 @@ fn outlined_assign_pattern_positions(
1331 pattern: &mut react_compiler_ast::patterns::PatternLike,
1332 pos: &mut u32,
1333 kind: react_compiler_ast::scope::BindingKind,
1334 - fn_bindings: &mut std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
1334 + fn_bindings: &mut rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
1335 bindings_list: &mut Vec<react_compiler_ast::scope::BindingData>,
1336 - ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
1336 + ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
1337 ) {
1338 use react_compiler_ast::patterns::PatternLike;
1339 use react_compiler_ast::scope::*;
@@ -1432,9 +1432,9 @@ fn outlined_assign_pattern_positions(
1432 fn outlined_assign_stmt_positions(
1433 stmt: &mut react_compiler_ast::statements::Statement,
1434 pos: &mut u32,
1435 - fn_bindings: &mut std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
1435 + fn_bindings: &mut rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
1436 bindings_list: &mut Vec<react_compiler_ast::scope::BindingData>,
1437 - ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
1437 + ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
1438 ) {
1439 use react_compiler_ast::statements::Statement;
1440
@@ -1477,8 +1477,8 @@ fn outlined_assign_stmt_positions(
1477 fn outlined_assign_expr_positions(
1478 expr: &mut react_compiler_ast::expressions::Expression,
1479 pos: &mut u32,
1480 - fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
1481 - ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
1480 + fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
1481 + ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
1482 ) {
1483 use react_compiler_ast::expressions::*;
1484
@@ -1538,8 +1538,8 @@ fn outlined_assign_expr_positions(
1538 fn outlined_assign_jsx_name_positions(
1539 name: &mut react_compiler_ast::jsx::JSXElementName,
1540 pos: &mut u32,
1541 - fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
1542 - ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
1541 + fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
1542 + ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
1543 ) {
1544 match name {
1545 react_compiler_ast::jsx::JSXElementName::JSXIdentifier(id) => {
@@ -1561,8 +1561,8 @@ fn outlined_assign_jsx_name_positions(
1561 fn outlined_assign_jsx_member_positions(
1562 member: &mut react_compiler_ast::jsx::JSXMemberExpression,
1563 pos: &mut u32,
1564 - fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
1565 - ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
1564 + fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
1565 + ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
1566 ) {
1567 match &mut *member.object {
1568 react_compiler_ast::jsx::JSXMemberExprObject::JSXIdentifier(id) => {
@@ -1583,8 +1583,8 @@ fn outlined_assign_jsx_member_positions(
1583 fn outlined_assign_jsx_val_positions(
1584 val: &mut react_compiler_ast::jsx::JSXAttributeValue,
1585 pos: &mut u32,
1586 - fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
1587 - ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
1586 + fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
1587 + ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
1588 ) {
1589 match val {
1590 react_compiler_ast::jsx::JSXAttributeValue::JSXExpressionContainer(c) => {
@@ -1608,8 +1608,8 @@ fn outlined_assign_jsx_val_positions(
1608 fn outlined_assign_jsx_child_positions(
1609 child: &mut react_compiler_ast::jsx::JSXChild,
1610 pos: &mut u32,
1611 - fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
1612 - ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
1611 + fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
1612 + ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
1613 ) {
1614 match child {
1615 react_compiler_ast::jsx::JSXChild::JSXExpressionContainer(c) => {
compiler/crates/react_compiler/src/entrypoint/program.rs
+6 -7
@@ -14,8 +14,7 @@
14 //! 5. Processing each function through the compilation pipeline
15 //! 6. Applying compiled functions back to the AST
16
17 -use std::collections::HashMap;
18 -use std::collections::HashSet;
17 +use rustc_hash::{FxHashMap, FxHashSet};
18
19 use react_compiler_ast::File;
20 use react_compiler_ast::Program;
@@ -2084,9 +2083,9 @@ struct CompiledFnForReplacement {
2083 fn get_functions_referenced_before_declaration(
2084 program: &Program,
2085 compiled_fns: &[CompiledFnForReplacement],
2087 -) -> HashSet<u32> {
2086 +) -> FxHashSet<u32> {
2087 // Collect function names and their node_ids for compiled FunctionDeclarations
2089 - let mut fn_names: HashMap<String, u32> = HashMap::new();
2088 + let mut fn_names: FxHashMap<String, u32> = FxHashMap::default();
2089 for compiled in compiled_fns {
2090 if compiled.original_kind == OriginalFnKind::FunctionDeclaration {
2091 if let Some(ref name) = compiled.fn_name {
@@ -2098,10 +2097,10 @@ fn get_functions_referenced_before_declaration(
2097 }
2098
2099 if fn_names.is_empty() {
2101 - return HashSet::new();
2100 + return FxHashSet::default();
2101 }
2102
2104 - let mut referenced_before_decl: HashSet<u32> = HashSet::new();
2103 + let mut referenced_before_decl: FxHashSet<u32> = FxHashSet::default();
2104
2105 // Walk through program body in order. For each statement, check if it references
2106 // any of the function names before the function's declaration.
@@ -2597,7 +2596,7 @@ fn apply_compiled_functions(
2596 let referenced_before_decl = if has_gating {
2597 get_functions_referenced_before_declaration(program, compiled_fns)
2598 } else {
2600 - HashSet::new()
2599 + FxHashSet::default()
2600 };
2601
2602 // For gated functions, we need to clone the original function expressions
compiler/crates/react_compiler/src/entrypoint/validate_source_locations.rs
+25 -22
@@ -11,7 +11,7 @@
11 //!
12 //! Analogous to TS `ValidateSourceLocations.ts`.
13
14 -use std::collections::{HashMap, HashSet};
14 +use rustc_hash::{FxHashMap, FxHashSet};
15
16 use react_compiler_ast::common::SourceLocation as AstSourceLocation;
17 use react_compiler_ast::expressions::{
@@ -38,14 +38,14 @@ pub fn validate_source_locations(
38 let important_original = collect_important_original_locations(func);
39
40 // Step 2: Collect all locations from the generated AST
41 - let mut generated = HashMap::<String, HashSet<String>>::new();
41 + let mut generated = FxHashMap::<String, FxHashSet<String>>::default();
42 collect_generated_from_block(&codegen.body.body, &mut generated);
43 for outlined in &codegen.outlined {
44 collect_generated_from_block(&outlined.func.body.body, &mut generated);
45 }
46
47 // Step 3: Validate that all important locations are preserved
48 - let strict_node_types: HashSet<&str> =
48 + let strict_node_types: FxHashSet<&str> =
49 ["VariableDeclaration", "VariableDeclarator", "Identifier"]
50 .into_iter()
51 .collect();
@@ -101,7 +101,7 @@ pub fn validate_source_locations(
101 struct ImportantLocation {
102 key: String,
103 loc: AstSourceLocation,
104 - node_types: HashSet<&'static str>,
104 + node_types: FxHashSet<&'static str>,
105 }
106
107 // ---- Location key ----
@@ -157,7 +157,7 @@ fn report_wrong_node_type(
157 env: &mut Environment,
158 loc: &AstSourceLocation,
159 expected_type: &str,
160 - actual_types: &HashSet<String>,
160 + actual_types: &FxHashSet<String>,
161 ) {
162 let diag_loc = ast_to_diag_loc(loc);
163 let mut actual: Vec<&str> = actual_types.iter().map(|s| s.as_str()).collect();
@@ -249,8 +249,8 @@ fn is_manual_memoization(expr: &Expression) -> bool {
249
250 fn collect_important_original_locations(
251 func: &FunctionNode<'_>,
252 -) -> HashMap<String, ImportantLocation> {
253 - let mut locations = HashMap::new();
252 +) -> FxHashMap<String, ImportantLocation> {
253 + let mut locations = FxHashMap::default();
254
255 // Note: TS uses func.traverse() which visits DESCENDANTS only, not the root
256 // function node itself. So we don't record the root function as important.
@@ -294,14 +294,14 @@ fn collect_important_original_locations(
294 fn record_important(
295 node_type: &'static str,
296 loc: &Option<AstSourceLocation>,
297 - locations: &mut HashMap<String, ImportantLocation>,
297 + locations: &mut FxHashMap<String, ImportantLocation>,
298 ) {
299 if let Some(loc) = loc {
300 let key = location_key(loc);
301 if let Some(existing) = locations.get_mut(&key) {
302 existing.node_types.insert(node_type);
303 } else {
304 - let mut node_types = HashSet::new();
304 + let mut node_types = FxHashSet::default();
305 node_types.insert(node_type);
306 locations.insert(
307 key.clone(),
@@ -318,7 +318,7 @@ fn record_important(
318 fn collect_original_block(
319 stmts: &[Statement],
320 in_single_return_arrow: bool,
321 - locations: &mut HashMap<String, ImportantLocation>,
321 + locations: &mut FxHashMap<String, ImportantLocation>,
322 ) {
323 for stmt in stmts {
324 collect_original_statement(stmt, in_single_return_arrow, locations);
@@ -328,7 +328,7 @@ fn collect_original_block(
328 fn collect_original_statement(
329 stmt: &Statement,
330 in_single_return_arrow: bool,
331 - locations: &mut HashMap<String, ImportantLocation>,
331 + locations: &mut FxHashMap<String, ImportantLocation>,
332 ) {
333 // Record this statement if it's an important type
334 if let Some(type_name) = important_statement_type(stmt) {
@@ -476,7 +476,7 @@ fn collect_original_statement(
476
477 fn collect_original_var_declaration(
478 decl: &VariableDeclaration,
479 - locations: &mut HashMap<String, ImportantLocation>,
479 + locations: &mut FxHashMap<String, ImportantLocation>,
480 ) {
481 for declarator in &decl.declarations {
482 // VariableDeclarator is an important type
@@ -490,7 +490,7 @@ fn collect_original_var_declaration(
490
491 fn collect_original_expression(
492 expr: &Expression,
493 - locations: &mut HashMap<String, ImportantLocation>,
493 + locations: &mut FxHashMap<String, ImportantLocation>,
494 ) {
495 // Record this expression if it's an important type
496 if let Some(type_name) = important_expression_type(expr) {
@@ -667,7 +667,7 @@ fn collect_original_expression(
667
668 fn collect_original_arrow_children(
669 arrow: &ArrowFunctionExpression,
670 - locations: &mut HashMap<String, ImportantLocation>,
670 + locations: &mut FxHashMap<String, ImportantLocation>,
671 ) {
672 for param in &arrow.params {
673 collect_original_pattern(param, locations);
@@ -685,7 +685,7 @@ fn collect_original_arrow_children(
685
686 fn collect_original_fn_expr_children(
687 func: &FunctionExpression,
688 - locations: &mut HashMap<String, ImportantLocation>,
688 + locations: &mut FxHashMap<String, ImportantLocation>,
689 ) {
690 if let Some(id) = &func.id {
691 record_important("Identifier", &id.base.loc, locations);
@@ -698,7 +698,7 @@ fn collect_original_fn_expr_children(
698
699 fn collect_original_pattern(
700 pattern: &PatternLike,
701 - locations: &mut HashMap<String, ImportantLocation>,
701 + locations: &mut FxHashMap<String, ImportantLocation>,
702 ) {
703 match pattern {
704 PatternLike::Identifier(id) => {
@@ -852,7 +852,7 @@ fn expression_loc(expr: &Expression) -> &Option<AstSourceLocation> {
852
853 fn collect_generated_from_block(
854 stmts: &[Statement],
855 - locations: &mut HashMap<String, HashSet<String>>,
855 + locations: &mut FxHashMap<String, FxHashSet<String>>,
856 ) {
857 for stmt in stmts {
858 collect_generated_statement(stmt, locations);
@@ -862,7 +862,7 @@ fn collect_generated_from_block(
862 fn record_generated(
863 type_name: &str,
864 loc: &Option<AstSourceLocation>,
865 - locations: &mut HashMap<String, HashSet<String>>,
865 + locations: &mut FxHashMap<String, FxHashSet<String>>,
866 ) {
867 if let Some(loc) = loc {
868 let key = location_key(loc);
@@ -873,7 +873,10 @@ fn record_generated(
873 }
874 }
875
876 -fn collect_generated_statement(stmt: &Statement, locations: &mut HashMap<String, HashSet<String>>) {
876 +fn collect_generated_statement(
877 + stmt: &Statement,
878 + locations: &mut FxHashMap<String, FxHashSet<String>>,
879 +) {
880 // Record this statement's location
881 let type_name = statement_type_name(stmt);
882 record_generated(type_name, statement_loc(stmt), locations);
@@ -1008,7 +1011,7 @@ fn collect_generated_statement(stmt: &Statement, locations: &mut HashMap<String,
1011
1012 fn collect_generated_var_declaration(
1013 decl: &VariableDeclaration,
1011 - locations: &mut HashMap<String, HashSet<String>>,
1014 + locations: &mut FxHashMap<String, FxHashSet<String>>,
1015 ) {
1016 for declarator in &decl.declarations {
1017 record_generated("VariableDeclarator", &declarator.base.loc, locations);
@@ -1021,7 +1024,7 @@ fn collect_generated_var_declaration(
1024
1025 fn collect_generated_expression(
1026 expr: &Expression,
1024 - locations: &mut HashMap<String, HashSet<String>>,
1027 + locations: &mut FxHashMap<String, FxHashSet<String>>,
1028 ) {
1029 let type_name = expression_type_name(expr);
1030 record_generated(type_name, expression_loc(expr), locations);
@@ -1188,7 +1191,7 @@ fn collect_generated_expression(
1191
1192 fn collect_generated_pattern(
1193 pattern: &PatternLike,
1191 - locations: &mut HashMap<String, HashSet<String>>,
1194 + locations: &mut FxHashMap<String, FxHashSet<String>>,
1195 ) {
1196 match pattern {
1197 PatternLike::Identifier(id) => {
compiler/crates/react_compiler_ast/Cargo.toml
+1
@@ -9,6 +9,7 @@ serde = { version = "1", features = ["derive"] }
9 serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] }
10 serde-transcode = "1"
11 indexmap = { version = "2", features = ["serde"] }
12 +rustc-hash = "2"
13
14 [dev-dependencies]
15 walkdir = "2"
compiler/crates/react_compiler_ast/src/scope.rs
+12 -12
@@ -1,4 +1,4 @@
1 -use std::collections::HashMap;
1 +use rustc_hash::{FxBuildHasher, FxHashMap};
2
3 use indexmap::IndexMap;
4 use serde::Deserialize;
@@ -20,7 +20,7 @@ pub struct ScopeData {
20 pub kind: ScopeKind,
21 /// Bindings declared directly in this scope, keyed by name.
22 /// Maps to BindingId for lookup in the binding table.
23 - pub bindings: HashMap<String, BindingId>,
23 + pub bindings: FxHashMap<String, BindingId>,
24 }
25
26 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -113,18 +113,18 @@ pub struct ScopeInfo {
113 /// **NOT for identity lookups** — use `node_id_to_scope` (via `resolve_scope_for_node`)
114 /// instead. Retained only for position-range containment queries
115 /// (e.g., "is reference R inside function scope S?").
116 - pub node_to_scope: HashMap<u32, ScopeId>,
116 + pub node_to_scope: FxHashMap<u32, ScopeId>,
117
118 /// Maps an AST node's start offset to the node's end offset.
119 /// Parallel to `node_to_scope` — used for position-range containment checks.
120 - #[serde(default, skip_serializing_if = "HashMap::is_empty")]
121 - pub node_to_scope_end: HashMap<u32, u32>,
120 + #[serde(default, skip_serializing_if = "FxHashMap::is_empty")]
121 + pub node_to_scope_end: FxHashMap<u32, u32>,
122
123 /// **DEPRECATED** — retained only for Babel bridge JSON deserialization.
124 /// All backends pass empty maps; only the Babel bridge populates this.
125 /// Use `ref_node_id_to_binding` for all lookups and iteration.
126 #[serde(default)]
127 - pub reference_to_binding: IndexMap<u32, BindingId>,
127 + pub reference_to_binding: IndexMap<u32, BindingId, FxBuildHasher>,
128
129 /// Maps an identifier reference's node-ID to the binding it resolves to.
130 /// Only present for identifiers that resolve to a binding (not globals).
@@ -134,15 +134,15 @@ pub struct ScopeInfo {
134 skip_serializing_if = "IndexMap::is_empty",
135 rename = "refNodeIdToBinding"
136 )]
137 - pub ref_node_id_to_binding: IndexMap<u32, BindingId>,
137 + pub ref_node_id_to_binding: IndexMap<u32, BindingId, FxBuildHasher>,
138
139 /// Maps a scope-creating AST node's node-ID to the scope it creates.
140 #[serde(
141 default,
142 - skip_serializing_if = "HashMap::is_empty",
142 + skip_serializing_if = "FxHashMap::is_empty",
143 rename = "nodeIdToScope"
144 )]
145 - pub node_id_to_scope: HashMap<u32, ScopeId>,
145 + pub node_id_to_scope: FxHashMap<u32, ScopeId>,
146
147 /// The program-level (module) scope. Always scopes[0].
148 pub program_scope: ScopeId,
@@ -207,7 +207,7 @@ impl ScopeInfo {
207 name: &str,
208 ancestor: ScopeId,
209 ) -> Option<&BindingData> {
210 - let mut descendants = std::collections::HashSet::new();
210 + let mut descendants = rustc_hash::FxHashSet::default();
211 descendants.insert(ancestor);
212 let mut changed = true;
213 while changed {
@@ -238,7 +238,7 @@ impl ScopeInfo {
238 name: &str,
239 ancestor: ScopeId,
240 ) -> Option<(BindingId, &BindingData)> {
241 - let mut descendants = std::collections::HashSet::new();
241 + let mut descendants = rustc_hash::FxHashSet::default();
242 descendants.insert(ancestor);
243 let mut changed = true;
244 while changed {
@@ -306,7 +306,7 @@ impl ScopeInfo {
306 ancestor: ScopeId,
307 is_claimed: impl Fn(ScopeId) -> bool,
308 ) -> Option<ScopeId> {
309 - let mut descendants = std::collections::HashSet::new();
309 + let mut descendants = rustc_hash::FxHashSet::default();
310 descendants.insert(ancestor);
311 let mut changed = true;
312 while changed {
compiler/crates/react_compiler_diagnostics/Cargo.toml
+1
@@ -4,4 +4,5 @@ version = "0.1.0"
4 edition = "2024"
5
6 [dependencies]
7 +rustc-hash = "2"
8 serde = { version = "1", features = ["derive"] }
compiler/crates/react_compiler_diagnostics/src/code_frame.rs
+2 -2
@@ -158,8 +158,8 @@ pub fn code_frame_columns(
158 let number_max_width = format!("{}", end).len();
159
160 // Build a lookup map for marker lines
161 - let mut marker_map: std::collections::HashMap<usize, MarkerEntry> =
162 - std::collections::HashMap::new();
161 + let mut marker_map: rustc_hash::FxHashMap<usize, MarkerEntry> =
162 + rustc_hash::FxHashMap::default();
163 let line_diff = end_line as usize - start_line as usize;
164 for (line_number, entry) in marker_lines_raw {
165 // Resolve placeholder lengths using actual source lines
compiler/crates/react_compiler_hir/Cargo.toml
+1
@@ -6,5 +6,6 @@ edition = "2024"
6 [dependencies]
7 react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
8 indexmap = { version = "2", features = ["serde"] }
9 +rustc-hash = "2"
10 serde = { version = "1", features = ["derive"] }
11 serde_json = "1"
compiler/crates/react_compiler_hir/src/default_module_type_provider.rs
+4 -4
@@ -20,11 +20,11 @@ use crate::type_config::{
20 pub fn default_module_type_provider(module_name: &str) -> Option<TypeConfig> {
21 match module_name {
22 "react-hook-form" => Some(TypeConfig::Object(ObjectTypeConfig {
23 - properties: Some(IndexMap::from([(
23 + properties: Some(IndexMap::from_iter([(
24 "useForm".to_string(),
25 TypeConfig::Hook(HookTypeConfig {
26 return_type: Box::new(TypeConfig::Object(ObjectTypeConfig {
27 - properties: Some(IndexMap::from([(
27 + properties: Some(IndexMap::from_iter([(
28 "watch".to_string(),
29 TypeConfig::Function(FunctionTypeConfig {
30 positional_params: Vec::new(),
@@ -58,7 +58,7 @@ pub fn default_module_type_provider(module_name: &str) -> Option<TypeConfig> {
58 })),
59
60 "@tanstack/react-table" => Some(TypeConfig::Object(ObjectTypeConfig {
61 - properties: Some(IndexMap::from([(
61 + properties: Some(IndexMap::from_iter([(
62 "useReactTable".to_string(),
63 TypeConfig::Hook(HookTypeConfig {
64 positional_params: Some(Vec::new()),
@@ -77,7 +77,7 @@ pub fn default_module_type_provider(module_name: &str) -> Option<TypeConfig> {
77 })),
78
79 "@tanstack/react-virtual" => Some(TypeConfig::Object(ObjectTypeConfig {
80 - properties: Some(IndexMap::from([(
80 + properties: Some(IndexMap::from_iter([(
81 "useVirtualizer".to_string(),
82 TypeConfig::Hook(HookTypeConfig {
83 positional_params: Some(Vec::new()),
compiler/crates/react_compiler_hir/src/dominator.rs
+25 -25
@@ -9,7 +9,7 @@
9 //! Uses the Cooper/Harvey/Kennedy algorithm from
10 //! https://www.cs.rice.edu/~keith/Embed/dom.pdf
11
12 -use std::collections::{HashMap, HashSet};
12 +use rustc_hash::{FxHashMap, FxHashSet};
13
14 use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
15
@@ -24,7 +24,7 @@ use crate::{BlockId, HirFunction, Terminal};
24 pub struct PostDominator {
25 /// The exit node (synthetic node representing function exit).
26 pub exit: BlockId,
27 - nodes: HashMap<BlockId, BlockId>,
27 + nodes: FxHashMap<BlockId, BlockId>,
28 }
29
30 impl PostDominator {
@@ -50,8 +50,8 @@ impl PostDominator {
50 struct Node {
51 id: BlockId,
52 index: usize,
53 - preds: HashSet<BlockId>,
54 - succs: HashSet<BlockId>,
53 + preds: FxHashSet<BlockId>,
54 + succs: FxHashSet<BlockId>,
55 }
56
57 struct Graph {
@@ -59,7 +59,7 @@ struct Graph {
59 /// Nodes stored in iteration order (RPO for reverse graph).
60 nodes: Vec<Node>,
61 /// Map from BlockId to index in the nodes vec.
62 - node_index: HashMap<BlockId, usize>,
62 + node_index: FxHashMap<BlockId, usize>,
63 }
64
65 impl Graph {
@@ -112,7 +112,7 @@ fn build_reverse_graph(
112 let exit_id = BlockId(next_block_id_counter);
113
114 // Build initial nodes with reversed edges
115 - let mut raw_nodes: HashMap<BlockId, Node> = HashMap::new();
115 + let mut raw_nodes: FxHashMap<BlockId, Node> = FxHashMap::default();
116
117 // Create exit node
118 raw_nodes.insert(
@@ -120,15 +120,15 @@ fn build_reverse_graph(
120 Node {
121 id: exit_id,
122 index: 0,
123 - preds: HashSet::new(),
124 - succs: HashSet::new(),
123 + preds: FxHashSet::default(),
124 + succs: FxHashSet::default(),
125 },
126 );
127
128 for (id, block) in &func.body.blocks {
129 let successors = each_terminal_successor(&block.terminal);
130 - let mut preds_set: HashSet<BlockId> = successors.into_iter().collect();
131 - let succs_set: HashSet<BlockId> = block.preds.iter().copied().collect();
130 + let mut preds_set: FxHashSet<BlockId> = successors.into_iter().collect();
131 + let succs_set: FxHashSet<BlockId> = block.preds.iter().copied().collect();
132
133 let is_return = matches!(&block.terminal, Terminal::Return { .. });
134 let is_throw = matches!(&block.terminal, Terminal::Throw { .. });
@@ -150,7 +150,7 @@ fn build_reverse_graph(
150 }
151
152 // DFS from exit to compute RPO
153 - let mut visited = HashSet::new();
153 + let mut visited = FxHashSet::default();
154 let mut postorder = Vec::new();
155 dfs_postorder(exit_id, &raw_nodes, &mut visited, &mut postorder);
156
@@ -158,7 +158,7 @@ fn build_reverse_graph(
158 postorder.reverse();
159
160 let mut nodes = Vec::with_capacity(postorder.len());
161 - let mut node_index = HashMap::new();
161 + let mut node_index = FxHashMap::default();
162 for (idx, id) in postorder.into_iter().enumerate() {
163 let mut node = raw_nodes.remove(&id).unwrap();
164 node.index = idx;
@@ -175,8 +175,8 @@ fn build_reverse_graph(
175
176 fn dfs_postorder(
177 id: BlockId,
178 - nodes: &HashMap<BlockId, Node>,
179 - visited: &mut HashSet<BlockId>,
178 + nodes: &FxHashMap<BlockId, Node>,
179 + visited: &mut FxHashSet<BlockId>,
180 postorder: &mut Vec<BlockId>,
181 ) {
182 if !visited.insert(id) {
@@ -196,8 +196,8 @@ fn dfs_postorder(
196
197 fn compute_immediate_dominators(
198 graph: &Graph,
199 -) -> Result<HashMap<BlockId, BlockId>, CompilerDiagnostic> {
200 - let mut doms: HashMap<BlockId, BlockId> = HashMap::new();
199 +) -> Result<FxHashMap<BlockId, BlockId>, CompilerDiagnostic> {
200 + let mut doms: FxHashMap<BlockId, BlockId> = FxHashMap::default();
201 doms.insert(graph.entry, graph.entry);
202
203 let mut changed = true;
@@ -249,7 +249,7 @@ fn compute_immediate_dominators(
249 Ok(doms)
250 }
251
252 -fn intersect(a: BlockId, b: BlockId, graph: &Graph, doms: &HashMap<BlockId, BlockId>) -> BlockId {
252 +fn intersect(a: BlockId, b: BlockId, graph: &Graph, doms: &FxHashMap<BlockId, BlockId>) -> BlockId {
253 let mut block1 = graph.get_node(a);
254 let mut block2 = graph.get_node(b);
255 while block1.id != block2.id {
@@ -277,10 +277,10 @@ pub fn post_dominator_frontier(
277 func: &HirFunction,
278 post_dominators: &PostDominator,
279 target_id: BlockId,
280 -) -> HashSet<BlockId> {
280 +) -> FxHashSet<BlockId> {
281 let target_post_dominators = post_dominators_of(func, post_dominators, target_id);
282 - let mut visited = HashSet::new();
283 - let mut frontier = HashSet::new();
282 + let mut visited = FxHashSet::default();
283 + let mut frontier = FxHashSet::default();
284
285 let mut to_visit: Vec<BlockId> = target_post_dominators.iter().copied().collect();
286 to_visit.push(target_id);
@@ -305,9 +305,9 @@ pub fn post_dominators_of(
305 func: &HirFunction,
306 post_dominators: &PostDominator,
307 target_id: BlockId,
308 -) -> HashSet<BlockId> {
309 - let mut result = HashSet::new();
310 - let mut visited = HashSet::new();
308 +) -> FxHashSet<BlockId> {
309 + let mut result = FxHashSet::default();
310 + let mut visited = FxHashSet::default();
311 let mut queue = vec![target_id];
312
313 while let Some(current_id) = queue.pop() {
@@ -339,8 +339,8 @@ pub fn post_dominators_of(
339 pub fn compute_unconditional_blocks(
340 func: &HirFunction,
341 next_block_id_counter: u32,
342 -) -> Result<HashSet<BlockId>, CompilerDiagnostic> {
343 - let mut unconditional = HashSet::new();
342 +) -> Result<FxHashSet<BlockId>, CompilerDiagnostic> {
343 + let mut unconditional = FxHashSet::default();
344 let dominators = compute_post_dominator_tree(func, next_block_id_counter, false)?;
345 let exit = dominators.exit;
346 let mut current: Option<BlockId> = Some(func.body.entry);
compiler/crates/react_compiler_hir/src/environment.rs
+15 -16
@@ -1,5 +1,4 @@
1 -use std::collections::HashMap;
2 -use std::collections::HashSet;
1 +use rustc_hash::{FxHashMap, FxHashSet};
2
3 use react_compiler_diagnostics::CompilerDiagnostic;
4 use react_compiler_diagnostics::CompilerError;
@@ -80,12 +79,12 @@ pub struct Environment {
79 // Used by codegen to filter type annotation renames — only rename identifiers
80 // whose node_id is in this set (type labels like ObjectTypeIndexer params
81 // are NOT in this set and should keep their original names).
83 - pub reference_node_ids: HashSet<u32>,
82 + pub reference_node_ids: FxHashSet<u32>,
83
84 // Hoisted identifiers: tracks which bindings have already been hoisted
85 // via DeclareContext to avoid duplicate hoisting.
86 // Uses u32 to avoid depending on react_compiler_ast types.
88 - hoisted_identifiers: HashSet<u32>,
87 + hoisted_identifiers: FxHashSet<u32>,
88
89 // Config flags for validation passes (kept for backwards compat with existing pipeline code)
90 pub validate_preserve_existing_memoization_guarantees: bool,
@@ -95,8 +94,8 @@ pub struct Environment {
94 // Type system registries
95 globals: GlobalRegistry,
96 pub shapes: ShapeRegistry,
98 - module_types: HashMap<String, Option<Global>>,
99 - module_type_errors: HashMap<String, Vec<String>>,
97 + module_types: FxHashMap<String, Option<Global>>,
98 + module_type_errors: FxHashMap<String, Vec<String>>,
99
100 // Environment configuration (feature flags, custom hooks, etc.)
101 pub config: EnvironmentConfig,
@@ -111,7 +110,7 @@ pub struct Environment {
110 // Known names for collision-aware UID generation. Lazily populated from
111 // identifiers on first use, then updated with each generated name.
112 // Matches Babel's generateUid behavior of checking hasBinding/hasReference.
114 - uid_known_names: Option<HashSet<String>>,
113 + uid_known_names: Option<FxHashSet<String>>,
114 }
115
116 /// An outlined function entry, stored on Environment during compilation.
@@ -164,7 +163,7 @@ impl Environment {
163 }
164
165 // Register reanimated module type when enabled
167 - let mut module_types: HashMap<String, Option<Global>> = HashMap::new();
166 + let mut module_types: FxHashMap<String, Option<Global>> = FxHashMap::default();
167 if config.enable_custom_type_definition_for_reanimated {
168 let reanimated_module_type = globals::get_reanimated_module_type(&mut shapes);
169 module_types.insert(
@@ -190,8 +189,8 @@ impl Environment {
189 instrument_gating_name: None,
190 hook_guard_name: None,
191 renames: Vec::new(),
193 - reference_node_ids: HashSet::new(),
194 - hoisted_identifiers: HashSet::new(),
192 + reference_node_ids: FxHashSet::default(),
193 + hoisted_identifiers: FxHashSet::default(),
194 validate_preserve_existing_memoization_guarantees: config
195 .validate_preserve_existing_memoization_guarantees,
196 validate_no_set_state_in_render: config.validate_no_set_state_in_render,
@@ -200,7 +199,7 @@ impl Environment {
199 globals: global_registry,
200 shapes,
201 module_types,
203 - module_type_errors: HashMap::new(),
202 + module_type_errors: FxHashMap::default(),
203 default_nonmutating_hook: None,
204 default_mutating_hook: None,
205 outlined_functions: Vec::new(),
@@ -237,8 +236,8 @@ impl Environment {
236 instrument_gating_name: self.instrument_gating_name.clone(),
237 hook_guard_name: self.hook_guard_name.clone(),
238 renames: Vec::new(),
240 - reference_node_ids: HashSet::new(),
241 - hoisted_identifiers: HashSet::new(),
239 + reference_node_ids: FxHashSet::default(),
240 + hoisted_identifiers: FxHashSet::default(),
241 validate_preserve_existing_memoization_guarantees: self
242 .validate_preserve_existing_memoization_guarantees,
243 validate_no_set_state_in_render: self.validate_no_set_state_in_render,
@@ -897,7 +896,7 @@ impl Environment {
896 // Lazily build the set of known names from existing identifiers.
897 // This approximates Babel's hasBinding/hasGlobal/hasReference checks.
898 if self.uid_known_names.is_none() {
900 - let mut known = HashSet::new();
899 + let mut known = FxHashSet::default();
900 for id in &self.identifiers {
901 if let Some(name) = &id.name {
902 known.insert(name.value().to_string());
@@ -929,7 +928,7 @@ impl Environment {
928 /// Seed the UID known names set with external names (e.g. from ProgramContext).
929 /// This ensures UID generation avoids names generated by previous function compilations,
930 /// matching Babel's behavior where the program scope accumulates all generated UIDs.
932 - pub fn seed_uid_known_names(&mut self, names: &HashSet<String>) {
931 + pub fn seed_uid_known_names(&mut self, names: &FxHashSet<String>) {
932 match &mut self.uid_known_names {
933 Some(existing) => existing.extend(names.iter().cloned()),
934 None => self.uid_known_names = Some(names.clone()),
@@ -937,7 +936,7 @@ impl Environment {
936 }
937
938 /// Return the UID known names accumulated during this compilation.
940 - pub fn take_uid_known_names(&mut self) -> Option<HashSet<String>> {
939 + pub fn take_uid_known_names(&mut self) -> Option<FxHashSet<String>> {
940 self.uid_known_names.take()
941 }
942
compiler/crates/react_compiler_hir/src/environment_config.rs
+5 -4
@@ -7,7 +7,8 @@
7 //!
8 //! Contains feature flags and custom hook definitions that control compiler behavior.
9
10 -use std::collections::HashMap;
10 +use indexmap::IndexMap;
11 +use rustc_hash::{FxBuildHasher, FxHashMap};
12
13 use serde::{Deserialize, Serialize};
14
@@ -80,12 +81,12 @@ fn default_true() -> bool {
81 pub struct EnvironmentConfig {
82 /// Custom hook type definitions, keyed by hook name.
83 #[serde(default)]
83 - pub custom_hooks: HashMap<String, HookConfig>,
84 + pub custom_hooks: FxHashMap<String, HookConfig>,
85
86 /// Pre-resolved module type provider results.
87 /// Map from module name to TypeConfig, computed by the JS shim.
88 #[serde(default)]
88 - pub module_type_provider: Option<indexmap::IndexMap<String, TypeConfig>>,
89 + pub module_type_provider: Option<IndexMap<String, TypeConfig, FxBuildHasher>>,
90
91 /// Custom macro-like function names that should have their operands
92 /// memoized in the same scope (similar to fbt).
@@ -185,7 +186,7 @@ pub struct EnvironmentConfig {
186 impl Default for EnvironmentConfig {
187 fn default() -> Self {
188 Self {
188 - custom_hooks: HashMap::new(),
189 + custom_hooks: FxHashMap::default(),
190 enable_reset_cache_on_source_file_changes: None,
191 module_type_provider: None,
192 enable_preserve_existing_memoization_guarantees: true,
compiler/crates/react_compiler_hir/src/globals.rs
+16 -16
@@ -8,7 +8,7 @@
8 //! Provides `DEFAULT_SHAPES` (built-in object shapes) and `DEFAULT_GLOBALS`
9 //! (global variable types including React hooks and JS built-ins).
10
11 -use std::collections::HashMap;
11 +use rustc_hash::FxHashMap;
12 use std::sync::LazyLock;
13
14 use crate::Effect;
@@ -31,14 +31,14 @@ pub type Global = Type;
31 /// Registry mapping global names to their types.
32 ///
33 /// Supports two modes:
34 -/// - **Builder mode** (`base=None`): wraps a single HashMap, used during
34 +/// - **Builder mode** (`base=None`): wraps a single FxHashMap, used during
35 /// `build_default_globals` to construct the static base.
36 -/// - **Overlay mode** (`base=Some`): holds a `&'static HashMap` base plus a small
37 -/// extras HashMap. Lookups check extras first, then base. Inserts go into extras.
36 +/// - **Overlay mode** (`base=Some`): holds a `&'static FxHashMap` base plus a small
37 +/// extras FxHashMap. Lookups check extras first, then base. Inserts go into extras.
38 /// Cloning only copies the extras map (the base pointer is shared).
39 pub struct GlobalRegistry {
40 - base: Option<&'static HashMap<String, Global>>,
41 - entries: HashMap<String, Global>,
40 + base: Option<&'static FxHashMap<String, Global>>,
41 + entries: FxHashMap<String, Global>,
42 }
43
44 impl GlobalRegistry {
@@ -46,15 +46,15 @@ impl GlobalRegistry {
46 pub fn new() -> Self {
47 Self {
48 base: None,
49 - entries: HashMap::new(),
49 + entries: FxHashMap::default(),
50 }
51 }
52
53 /// Create an overlay-mode registry backed by a static base.
54 - pub fn with_base(base: &'static HashMap<String, Global>) -> Self {
54 + pub fn with_base(base: &'static FxHashMap<String, Global>) -> Self {
55 Self {
56 base: Some(base),
57 - entries: HashMap::new(),
57 + entries: FxHashMap::default(),
58 }
59 }
60
@@ -83,9 +83,9 @@ impl GlobalRegistry {
83 self.entries.keys().chain(base_keys)
84 }
85
86 - /// Consume the registry and return the inner HashMap.
86 + /// Consume the registry and return the inner FxHashMap.
87 /// Only valid in builder mode (no base).
88 - pub fn into_inner(self) -> HashMap<String, Global> {
88 + pub fn into_inner(self) -> FxHashMap<String, Global> {
89 debug_assert!(
90 self.base.is_none(),
91 "into_inner() called on overlay-mode GlobalRegistry"
@@ -108,8 +108,8 @@ impl Clone for GlobalRegistry {
108 // =============================================================================
109
110 struct BaseRegistries {
111 - shapes: HashMap<String, ObjectShape>,
112 - globals: HashMap<String, Global>,
111 + shapes: FxHashMap<String, ObjectShape>,
112 + globals: FxHashMap<String, Global>,
113 }
114
115 static BASE: LazyLock<BaseRegistries> = LazyLock::new(|| {
@@ -122,12 +122,12 @@ static BASE: LazyLock<BaseRegistries> = LazyLock::new(|| {
122 });
123
124 /// Get a reference to the static base shapes registry.
125 -pub fn base_shapes() -> &'static HashMap<String, ObjectShape> {
125 +pub fn base_shapes() -> &'static FxHashMap<String, ObjectShape> {
126 &BASE.shapes
127 }
128
129 /// Get a reference to the static base globals registry.
130 -pub fn base_globals() -> &'static HashMap<String, Global> {
130 +pub fn base_globals() -> &'static FxHashMap<String, Global> {
131 &BASE.globals
132 }
133
@@ -1332,7 +1332,7 @@ fn build_object_shape(shapes: &mut ShapeRegistry) {
1332 None,
1333 false,
1334 );
1335 - let mut mixed_props = HashMap::new();
1335 + let mut mixed_props = FxHashMap::default();
1336 mixed_props.insert("toString".to_string(), mixed_to_string);
1337 mixed_props.insert("indexOf".to_string(), mixed_index_of);
1338 mixed_props.insert("includes".to_string(), mixed_includes);
compiler/crates/react_compiler_hir/src/lib.rs
+6 -6
@@ -9,14 +9,14 @@ pub mod reactive;
9 pub mod type_config;
10 pub mod visitors;
11
12 -use indexmap::IndexMap;
13 -use indexmap::IndexSet;
12 +use indexmap::{IndexMap, IndexSet};
13 pub use react_compiler_diagnostics::CompilerDiagnostic;
14 pub use react_compiler_diagnostics::ErrorCategory;
15 pub use react_compiler_diagnostics::GENERATED_SOURCE;
16 pub use react_compiler_diagnostics::Position;
17 pub use react_compiler_diagnostics::SourceLocation;
18 pub use reactive::*;
19 +use rustc_hash::FxBuildHasher;
20
21 // =============================================================================
22 // ID newtypes
@@ -57,7 +57,7 @@ pub struct MutableRangeId(pub u32);
57 // =============================================================================
58
59 /// Wrapper around f64 that stores raw bytes for deterministic equality and hashing.
60 -/// This allows use in HashMap keys and ensures NaN == NaN (bitwise comparison).
60 +/// This allows use in FxHashMap keys and ensures NaN == NaN (bitwise comparison).
61 #[derive(Debug, Clone, Copy)]
62 pub struct FloatValue(u64);
63
@@ -190,7 +190,7 @@ pub enum ParamPattern {
190 #[derive(Debug, Clone)]
191 pub struct HIR {
192 pub entry: BlockId,
193 - pub blocks: IndexMap<BlockId, BasicBlock>,
193 + pub blocks: IndexMap<BlockId, BasicBlock, FxBuildHasher>,
194 }
195
196 /// Block kinds
@@ -222,7 +222,7 @@ pub struct BasicBlock {
222 pub id: BlockId,
223 pub instructions: Vec<InstructionId>,
224 pub terminal: Terminal,
225 - pub preds: IndexSet<BlockId>,
225 + pub preds: IndexSet<BlockId, FxBuildHasher>,
226 pub phis: Vec<Phi>,
227 }
228
@@ -230,7 +230,7 @@ pub struct BasicBlock {
230 #[derive(Debug, Clone)]
231 pub struct Phi {
232 pub place: Place,
233 - pub operands: IndexMap<BlockId, Place>,
233 + pub operands: IndexMap<BlockId, Place, FxBuildHasher>,
234 }
235
236 // =============================================================================
compiler/crates/react_compiler_hir/src/object_shape.rs
+12 -12
@@ -8,7 +8,7 @@
8 //! Defines the shape registry used by Environment to resolve property types
9 //! and function call signatures for built-in objects, hooks, and user-defined types.
10
11 -use std::collections::HashMap;
11 +use rustc_hash::FxHashMap;
12
13 use crate::Effect;
14 use crate::Type;
@@ -119,21 +119,21 @@ pub struct FunctionSignature {
119 /// Ported from TS `ObjectShape`.
120 #[derive(Debug, Clone)]
121 pub struct ObjectShape {
122 - pub properties: HashMap<String, Type>,
122 + pub properties: FxHashMap<String, Type>,
123 pub function_type: Option<FunctionSignature>,
124 }
125
126 /// Registry mapping shape IDs to their ObjectShape definitions.
127 ///
128 /// Supports two modes:
129 -/// - **Builder mode** (`base=None`): wraps a single HashMap, used during
129 +/// - **Builder mode** (`base=None`): wraps a single FxHashMap, used during
130 /// `build_builtin_shapes` / `build_default_globals` to construct the static base.
131 -/// - **Overlay mode** (`base=Some`): holds a `&'static HashMap` base plus a small
132 -/// extras HashMap. Lookups check extras first, then base. Inserts go into extras.
131 +/// - **Overlay mode** (`base=Some`): holds a `&'static FxHashMap` base plus a small
132 +/// extras FxHashMap. Lookups check extras first, then base. Inserts go into extras.
133 /// Cloning only copies the extras map (the base pointer is shared).
134 pub struct ShapeRegistry {
135 - base: Option<&'static HashMap<String, ObjectShape>>,
136 - entries: HashMap<String, ObjectShape>,
135 + base: Option<&'static FxHashMap<String, ObjectShape>>,
136 + entries: FxHashMap<String, ObjectShape>,
137 }
138
139 impl ShapeRegistry {
@@ -141,15 +141,15 @@ impl ShapeRegistry {
141 pub fn new() -> Self {
142 Self {
143 base: None,
144 - entries: HashMap::new(),
144 + entries: FxHashMap::default(),
145 }
146 }
147
148 /// Create an overlay-mode registry backed by a static base.
149 - pub fn with_base(base: &'static HashMap<String, ObjectShape>) -> Self {
149 + pub fn with_base(base: &'static FxHashMap<String, ObjectShape>) -> Self {
150 Self {
151 base: Some(base),
152 - entries: HashMap::new(),
152 + entries: FxHashMap::default(),
153 }
154 }
155
@@ -163,9 +163,9 @@ impl ShapeRegistry {
163 self.entries.insert(key, value);
164 }
165
166 - /// Consume the registry and return the inner HashMap.
166 + /// Consume the registry and return the inner FxHashMap.
167 /// Only valid in builder mode (no base).
168 - pub fn into_inner(self) -> HashMap<String, ObjectShape> {
168 + pub fn into_inner(self) -> FxHashMap<String, ObjectShape> {
169 debug_assert!(
170 self.base.is_none(),
171 "into_inner() called on overlay-mode ShapeRegistry"
compiler/crates/react_compiler_hir/src/print.rs
+5 -5
@@ -8,7 +8,7 @@
8 //! It also exports standalone formatting functions (format_loc, format_primitive, etc.)
9 //! that require no state.
10
11 -use std::collections::HashSet;
11 +use rustc_hash::FxHashSet;
12
13 use react_compiler_diagnostics::CompilerError;
14 use react_compiler_diagnostics::CompilerErrorOrDiagnostic;
@@ -223,8 +223,8 @@ pub fn format_value_reason(reason: ValueReason) -> &'static str {
223 /// like Places, Identifiers, Scopes, Types, InstructionValues, etc.
224 pub struct PrintFormatter<'a> {
225 pub env: &'a Environment,
226 - pub seen_identifiers: HashSet<IdentifierId>,
227 - pub seen_scopes: HashSet<ScopeId>,
226 + pub seen_identifiers: FxHashSet<IdentifierId>,
227 + pub seen_scopes: FxHashSet<ScopeId>,
228 pub output: Vec<String>,
229 pub indent_level: usize,
230 }
@@ -233,8 +233,8 @@ impl<'a> PrintFormatter<'a> {
233 pub fn new(env: &'a Environment) -> Self {
234 Self {
235 env,
236 - seen_identifiers: HashSet::new(),
237 - seen_scopes: HashSet::new(),
236 + seen_identifiers: FxHashSet::default(),
237 + seen_scopes: FxHashSet::default(),
238 output: Vec::new(),
239 indent_level: 0,
240 }
compiler/crates/react_compiler_hir/src/type_config.rs
+2 -1
@@ -9,6 +9,7 @@
9 //! and `installTypeConfig` to describe module/function/hook types.
10
11 use indexmap::IndexMap;
12 +use rustc_hash::FxBuildHasher;
13
14 use crate::Effect;
15
@@ -166,7 +167,7 @@ pub enum TypeConfig {
167
168 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
169 pub struct ObjectTypeConfig {
169 - pub properties: Option<IndexMap<String, TypeConfig>>,
170 + pub properties: Option<IndexMap<String, TypeConfig, FxBuildHasher>>,
171 }
172
173 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
compiler/crates/react_compiler_hir/src/visitors.rs
+3 -3
@@ -4,7 +4,7 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7 -use std::collections::HashMap;
7 +use rustc_hash::FxHashMap;
8
9 use crate::environment::Environment;
10 use crate::{
@@ -1289,14 +1289,14 @@ pub struct ScopeBlockTraversal {
1289 /// Live stack of active scopes
1290 active_scopes: Vec<ScopeId>,
1291 /// Map from block ID to scope block info
1292 - pub block_infos: HashMap<BlockId, ScopeBlockInfo>,
1292 + pub block_infos: FxHashMap<BlockId, ScopeBlockInfo>,
1293 }
1294
1295 impl ScopeBlockTraversal {
1296 pub fn new() -> Self {
1297 ScopeBlockTraversal {
1298 active_scopes: Vec::new(),
1299 - block_infos: HashMap::new(),
1299 + block_infos: FxHashMap::default(),
1300 }
1301 }
1302
compiler/crates/react_compiler_inference/Cargo.toml
+1
@@ -11,3 +11,4 @@ react_compiler_optimization = { path = "../react_compiler_optimization" }
11 react_compiler_ssa = { path = "../react_compiler_ssa" }
12 react_compiler_utils = { path = "../react_compiler_utils" }
13 indexmap = "2"
14 +rustc-hash = "2"
compiler/crates/react_compiler_inference/src/align_method_call_scopes.rs
+6 -5
@@ -9,7 +9,7 @@
9 //!
10 //! Ported from TypeScript `src/ReactiveScopes/AlignMethodCallScopes.ts`.
11
12 -use std::collections::HashMap;
12 +use rustc_hash::FxHashMap;
13
14 use react_compiler_hir::environment::Environment;
15 use react_compiler_hir::{EvaluationOrder, HirFunction, IdentifierId, InstructionValue, ScopeId};
@@ -25,7 +25,7 @@ use react_compiler_utils::DisjointSet;
25 /// Corresponds to TS `alignMethodCallScopes(fn: HIRFunction): void`.
26 pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) {
27 // Maps an identifier to the scope it should be assigned to (or None to remove scope)
28 - let mut scope_mapping: HashMap<IdentifierId, Option<ScopeId>> = HashMap::new();
28 + let mut scope_mapping: FxHashMap<IdentifierId, Option<ScopeId>> = FxHashMap::default();
29 let mut merged_scopes = DisjointSet::<ScopeId>::new();
30
31 // Phase 1: Walk instructions and collect scope relationships
@@ -74,9 +74,10 @@ pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) {
74 }
75
76 // Phase 2: Merge scope ranges for unioned scopes.
77 - // Use a HashMap to accumulate min/max across all scopes mapping to the same root,
77 + // Use a FxHashMap to accumulate min/max across all scopes mapping to the same root,
78 // matching TS behavior where root.range is updated in-place during iteration.
79 - let mut range_updates: HashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> = HashMap::new();
79 + let mut range_updates: FxHashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> =
80 + FxHashMap::default();
81
82 merged_scopes.for_each(|scope_id, root_id| {
83 if scope_id == root_id {
@@ -93,7 +94,7 @@ pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) {
94 });
95
96 // Save original scope range IDs before updating
96 - let original_range_ids: HashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
97 + let original_range_ids: FxHashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
98 .keys()
99 .map(|&root_id| {
100 let range_id = env.scopes[root_id.0 as usize].range.id;
compiler/crates/react_compiler_inference/src/align_object_method_scopes.rs
+7 -6
@@ -9,8 +9,8 @@
9 //!
10 //! Ported from TypeScript `src/ReactiveScopes/AlignObjectMethodScopes.ts`.
11
12 +use rustc_hash::{FxHashMap, FxHashSet};
13 use std::cmp;
13 -use std::collections::{HashMap, HashSet};
14
15 use react_compiler_hir::environment::Environment;
16 use react_compiler_hir::{
@@ -26,7 +26,7 @@ use react_compiler_utils::DisjointSet;
26 /// instructions whose operands reference those methods. Returns a disjoint set
27 /// of scopes that must be merged.
28 fn find_scopes_to_merge(func: &HirFunction, env: &Environment) -> DisjointSet<ScopeId> {
29 - let mut object_method_decls: HashSet<IdentifierId> = HashSet::new();
29 + let mut object_method_decls: FxHashSet<IdentifierId> = FxHashSet::default();
30 let mut merged_scopes = DisjointSet::<ScopeId>::new();
31
32 for (_block_id, block) in &func.body.blocks {
@@ -99,9 +99,10 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment)
99 let mut merged_scopes = find_scopes_to_merge(func, env);
100
101 // Step 1: Merge affected scopes to their canonical root.
102 - // Use a HashMap to accumulate min/max across all scopes mapping to the same root,
102 + // Use a FxHashMap to accumulate min/max across all scopes mapping to the same root,
103 // matching TS behavior where root.range is updated in-place during iteration.
104 - let mut range_updates: HashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> = HashMap::new();
104 + let mut range_updates: FxHashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> =
105 + FxHashMap::default();
106
107 merged_scopes.for_each(|scope_id, root_id| {
108 if scope_id == root_id {
@@ -118,7 +119,7 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment)
119 });
120
121 // Save original scope range IDs before updating
121 - let original_range_ids: HashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
122 + let original_range_ids: FxHashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
123 .keys()
124 .map(|&root_id| {
125 let range_id = env.scopes[root_id.0 as usize].range.id;
@@ -147,7 +148,7 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment)
148
149 // Step 2: Repoint identifiers whose scopes were merged
150 // Build a map from old scope -> root scope for quick lookup
150 - let mut scope_remap: HashMap<ScopeId, ScopeId> = HashMap::new();
151 + let mut scope_remap: FxHashMap<ScopeId, ScopeId> = FxHashMap::default();
152 merged_scopes.for_each(|scope_id, root_id| {
153 if scope_id != root_id {
154 scope_remap.insert(scope_id, root_id);
compiler/crates/react_compiler_inference/src/align_reactive_scopes_to_block_scopes_hir.rs
+6 -7
@@ -22,8 +22,7 @@
22 //! instructions in each scope, the scopes must be aligned to block-scope
23 //! boundaries — we can't memoize half of a loop!
24
25 -use std::collections::HashMap;
26 -use std::collections::HashSet;
25 +use rustc_hash::{FxHashMap, FxHashSet};
26
27 use react_compiler_hir::BlockId;
28 use react_compiler_hir::BlockKind;
@@ -99,9 +98,9 @@ pub fn align_reactive_scopes_to_block_scopes_hir(func: &mut HirFunction, env: &m
98 env.scopes.iter().map(|s| s.range.clone()).collect();
99
100 let mut active_block_fallthrough_ranges: Vec<BlockFallthroughRange> = Vec::new();
102 - let mut active_scopes: HashSet<ScopeId> = HashSet::new();
103 - let mut seen: HashSet<ScopeId> = HashSet::new();
104 - let mut value_block_nodes: HashMap<BlockId, ValueBlockNode> = HashMap::new();
101 + let mut active_scopes: FxHashSet<ScopeId> = FxHashSet::default();
102 + let mut seen: FxHashSet<ScopeId> = FxHashSet::default();
103 + let mut value_block_nodes: FxHashMap<BlockId, ValueBlockNode> = FxHashMap::default();
104
105 let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();
106
@@ -301,8 +300,8 @@ fn record_place_id(
300 identifier_id: IdentifierId,
301 node: &Option<ValueBlockNode>,
302 env: &mut Environment,
304 - active_scopes: &mut HashSet<ScopeId>,
305 - seen: &mut HashSet<ScopeId>,
303 + active_scopes: &mut FxHashSet<ScopeId>,
304 + seen: &mut FxHashSet<ScopeId>,
305 ) {
306 // Get the scope for this identifier, if active at this instruction
307 let scope_id = match env.identifiers[identifier_id.0 as usize].scope {
compiler/crates/react_compiler_inference/src/analyse_functions.rs
+3 -3
@@ -15,7 +15,7 @@
15 use indexmap::IndexMap;
16 use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
17 use react_compiler_hir::environment::Environment;
18 -use std::collections::HashSet;
18 +use rustc_hash::FxHashSet;
19
20 use react_compiler_hir::{
21 AliasingEffect, BlockId, Effect, EvaluationOrder, FunctionId, HIR, HirFunction, IdentifierId,
@@ -138,7 +138,7 @@ where
138
139 // Phase 2: Populate the Effect of each context variable to use in inferring
140 // the outer function. Corresponds to TS Phase 2 in lowerWithMutationAliasing.
141 - let mut captured_or_mutated: HashSet<IdentifierId> = HashSet::new();
141 + let mut captured_or_mutated: FxHashSet<IdentifierId> = FxHashSet::default();
142 for effect in &function_effects {
143 match effect {
144 AliasingEffect::Assign { from, .. }
@@ -208,7 +208,7 @@ fn placeholder_function() -> HirFunction {
208 context: Vec::new(),
209 body: HIR {
210 entry: BlockId(0),
211 - blocks: IndexMap::new(),
211 + blocks: IndexMap::default(),
212 },
213 instructions: Vec::new(),
214 generator: false,
compiler/crates/react_compiler_inference/src/build_reactive_scope_terminals_hir.rs
+8 -9
@@ -11,10 +11,9 @@
11 //!
12 //! Ported from TypeScript `src/HIR/BuildReactiveScopeTerminalsHIR.ts`.
13
14 -use std::collections::HashMap;
15 -use std::collections::HashSet;
14 +use indexmap::{IndexMap, IndexSet};
15 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
16
17 -use indexmap::IndexMap;
17 use react_compiler_hir::BasicBlock;
18 use react_compiler_hir::BlockId;
19 use react_compiler_hir::EvaluationOrder;
@@ -38,7 +37,7 @@ use react_compiler_lowering::mark_predecessors;
37 /// Collect all unique scopes from places in the function that have non-empty ranges.
38 /// Corresponds to TS `getScopes(fn)`.
39 fn get_scopes(func: &HirFunction, env: &Environment) -> Vec<ScopeId> {
41 - let mut scope_ids: HashSet<ScopeId> = HashSet::new();
40 + let mut scope_ids: FxHashSet<ScopeId> = FxHashSet::default();
41
42 let mut visit_place = |identifier_id: IdentifierId| {
43 if let Some(scope_id) = env.identifiers[identifier_id.0 as usize].scope {
@@ -117,7 +116,7 @@ fn collect_scope_rewrites(func: &HirFunction, env: &mut Environment) -> Vec<Term
116 });
117
118 let mut rewrites: Vec<TerminalRewriteInfo> = Vec::new();
120 - let mut fallthroughs: HashMap<ScopeId, BlockId> = HashMap::new();
119 + let mut fallthroughs: FxHashMap<ScopeId, BlockId> = FxHashMap::default();
120 let mut active_items: Vec<ScopeId> = Vec::new();
121
122 for i in 0..items.len() {
@@ -222,7 +221,7 @@ fn handle_rewrite(
221 };
222
223 let curr_block_id = context.next_block_id;
225 - let mut preds = indexmap::IndexSet::new();
224 + let mut preds = IndexSet::default();
225 for &p in &context.next_preds {
226 preds.insert(p);
227 }
@@ -264,8 +263,8 @@ pub fn build_reactive_scope_terminals_hir(func: &mut HirFunction, env: &mut Envi
263 let mut queued_rewrites = collect_scope_rewrites(func, env);
264
265 // Step 2: Apply rewrites by splitting blocks
267 - let mut rewritten_final_blocks: HashMap<BlockId, BlockId> = HashMap::new();
268 - let mut next_blocks: IndexMap<BlockId, BasicBlock> = IndexMap::new();
266 + let mut rewritten_final_blocks: FxHashMap<BlockId, BlockId> = FxHashMap::default();
267 + let mut next_blocks: IndexMap<BlockId, BasicBlock, FxBuildHasher> = IndexMap::default();
268
269 // Reverse so we can pop from the end while traversing in ascending order
270 queued_rewrites.reverse();
@@ -300,7 +299,7 @@ pub fn build_reactive_scope_terminals_hir(func: &mut HirFunction, env: &mut Envi
299 }
300
301 if !context.rewrites.is_empty() {
303 - let mut final_preds = indexmap::IndexSet::new();
302 + let mut final_preds = IndexSet::default();
303 for &p in &context.next_preds {
304 final_preds.insert(p);
305 }
compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_effects.rs
+62 -59
@@ -11,10 +11,9 @@
11 //! creation, aliasing, mutation, freezing, and error conditions for each
12 //! instruction and terminal in the HIR.
13
14 -use std::collections::HashMap;
15 -use std::collections::HashSet;
14 +use indexmap::{IndexMap, IndexSet};
15 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
16
17 -use indexmap::IndexSet;
17 use react_compiler_diagnostics::CompilerDiagnostic;
18 use react_compiler_diagnostics::CompilerDiagnosticDetail;
19 use react_compiler_diagnostics::ErrorCategory;
@@ -61,7 +60,7 @@ pub fn infer_mutation_aliasing_effects(
60 let mut initial_state = InferenceState::empty(env, is_function_expression);
61
62 // Map of blocks to the last (merged) incoming state that was processed
64 - let mut states_by_block: HashMap<BlockId, InferenceState> = HashMap::new();
63 + let mut states_by_block: FxHashMap<BlockId, InferenceState> = FxHashMap::default();
64
65 // Initialize context variables
66 for ctx_place in &func.context {
@@ -115,12 +114,12 @@ pub fn infer_mutation_aliasing_effects(
114 }
115 }
116
118 - let mut queued_states: indexmap::IndexMap<BlockId, InferenceState> = indexmap::IndexMap::new();
117 + let mut queued_states: IndexMap<BlockId, InferenceState, FxBuildHasher> = IndexMap::default();
118
119 // Queue helper
120 fn queue(
122 - queued_states: &mut indexmap::IndexMap<BlockId, InferenceState>,
123 - states_by_block: &HashMap<BlockId, InferenceState>,
121 + queued_states: &mut IndexMap<BlockId, InferenceState, FxBuildHasher>,
122 + states_by_block: &FxHashMap<BlockId, InferenceState>,
123 block_id: BlockId,
124 state: InferenceState,
125 ) {
@@ -152,16 +151,16 @@ pub fn infer_mutation_aliasing_effects(
151 let non_mutating_spreads = find_non_mutated_destructure_spreads(func, env);
152
153 let mut context = Context {
155 - interned_effects: HashMap::new(),
156 - instruction_signature_cache: HashMap::new(),
157 - catch_handlers: HashMap::new(),
154 + interned_effects: FxHashMap::default(),
155 + instruction_signature_cache: FxHashMap::default(),
156 + catch_handlers: FxHashMap::default(),
157 is_function_expression,
158 hoisted_context_declarations,
159 non_mutating_spreads,
161 - effect_value_id_cache: HashMap::new(),
162 - function_values: HashMap::new(),
163 - function_signature_cache: HashMap::new(),
164 - aliasing_config_temp_cache: HashMap::new(),
160 + effect_value_id_cache: FxHashMap::default(),
161 + function_values: FxHashMap::default(),
162 + function_signature_cache: FxHashMap::default(),
163 + aliasing_config_temp_cache: FxHashMap::default(),
164 };
165
166 let mut iteration_count = 0;
@@ -262,11 +261,11 @@ impl ValueId {
261 #[derive(Debug, Clone)]
262 struct AbstractValue {
263 kind: ValueKind,
265 - reason: IndexSet<ValueReason>,
264 + reason: IndexSet<ValueReason, FxBuildHasher>,
265 }
266
268 -fn hashset_of(r: ValueReason) -> IndexSet<ValueReason> {
269 - let mut s = IndexSet::new();
267 +fn hashset_of(r: ValueReason) -> IndexSet<ValueReason, FxBuildHasher> {
268 + let mut s = IndexSet::default();
269 s.insert(r);
270 s
271 }
@@ -282,9 +281,9 @@ fn hashset_of(r: ValueReason) -> IndexSet<ValueReason> {
281 struct InferenceState {
282 is_function_expression: bool,
283 /// The kind of each value, based on its allocation site
285 - values: HashMap<ValueId, AbstractValue>,
284 + values: FxHashMap<ValueId, AbstractValue>,
285 /// The set of values pointed to by each identifier
287 - variables: HashMap<IdentifierId, HashSet<ValueId>>,
286 + variables: FxHashMap<IdentifierId, FxHashSet<ValueId>>,
287 /// Tracks uninitialized identifier access errors (matches TS invariant).
288 /// Uses Cell so it can be set from `&self` methods like `kind()`.
289 /// Stores (IdentifierId, usage_loc) where usage_loc is the source location
@@ -296,8 +295,8 @@ impl InferenceState {
295 fn empty(_env: &Environment, is_function_expression: bool) -> Self {
296 InferenceState {
297 is_function_expression,
299 - values: HashMap::new(),
300 - variables: HashMap::new(),
298 + values: FxHashMap::default(),
299 + variables: FxHashMap::default(),
300 uninitialized_access: std::cell::Cell::new(None),
301 }
302 }
@@ -342,7 +341,7 @@ impl InferenceState {
341 }
342
343 fn define(&mut self, place_id: IdentifierId, value_id: ValueId) {
345 - let mut set = HashSet::new();
344 + let mut set = FxHashSet::default();
345 set.insert(value_id);
346 self.variables.insert(place_id, set);
347 }
@@ -354,7 +353,7 @@ impl InferenceState {
353 // Create a stable value for uninitialized identifiers
354 // Use a deterministic ID based on the from identifier
355 let vid = ValueId(from.0 | 0x80000000);
357 - let mut set = HashSet::new();
356 + let mut set = FxHashSet::default();
357 set.insert(vid);
358 if !self.values.contains_key(&vid) {
359 self.values.insert(
@@ -380,7 +379,7 @@ impl InferenceState {
379 Some(v) => v.clone(),
380 None => return,
381 };
383 - let merged: HashSet<ValueId> = prev_values.union(&new_values).copied().collect();
382 + let merged: FxHashSet<ValueId> = prev_values.union(&new_values).copied().collect();
383 self.variables.insert(place, merged);
384 }
385
@@ -486,8 +485,8 @@ impl InferenceState {
485 }
486
487 fn merge(&self, other: &InferenceState) -> Option<InferenceState> {
489 - let mut next_values: Option<HashMap<ValueId, AbstractValue>> = None;
490 - let mut next_variables: Option<HashMap<IdentifierId, HashSet<ValueId>>> = None;
488 + let mut next_values: Option<FxHashMap<ValueId, AbstractValue>> = None;
489 + let mut next_variables: Option<FxHashMap<IdentifierId, FxHashSet<ValueId>>> = None;
490
491 // Merge values present in both
492 for (id, this_value) in &self.values {
@@ -521,7 +520,7 @@ impl InferenceState {
520 }
521 if has_new {
522 let nvars = next_variables.get_or_insert_with(|| self.variables.clone());
524 - let merged: HashSet<ValueId> =
523 + let merged: FxHashSet<ValueId> =
524 this_values.union(other_values).copied().collect();
525 nvars.insert(*id, merged);
526 }
@@ -550,9 +549,9 @@ impl InferenceState {
549 fn infer_phi(
550 &mut self,
551 phi_place_id: IdentifierId,
553 - phi_operands: &indexmap::IndexMap<BlockId, Place>,
552 + phi_operands: &IndexMap<BlockId, Place, FxBuildHasher>,
553 ) {
555 - let mut values: HashSet<ValueId> = HashSet::new();
554 + let mut values: FxHashSet<ValueId> = FxHashSet::default();
555 for (_, operand) in phi_operands {
556 if let Some(operand_values) = self.variables.get(&operand.identifier) {
557 for v in operand_values {
@@ -567,7 +566,10 @@ impl InferenceState {
566 }
567 }
568
570 -fn is_superset(a: &IndexSet<ValueReason>, b: &IndexSet<ValueReason>) -> bool {
569 +fn is_superset(
570 + a: &IndexSet<ValueReason, FxBuildHasher>,
571 + b: &IndexSet<ValueReason, FxBuildHasher>,
572 +) -> bool {
573 b.iter().all(|x| a.contains(x))
574 }
575
@@ -593,24 +595,24 @@ enum MutationResult {
595 // =============================================================================
596
597 struct Context {
596 - interned_effects: HashMap<String, AliasingEffect>,
597 - instruction_signature_cache: HashMap<u32, InstructionSignature>,
598 - catch_handlers: HashMap<BlockId, Place>,
598 + interned_effects: FxHashMap<String, AliasingEffect>,
599 + instruction_signature_cache: FxHashMap<u32, InstructionSignature>,
600 + catch_handlers: FxHashMap<BlockId, Place>,
601 is_function_expression: bool,
600 - hoisted_context_declarations: HashMap<DeclarationId, Option<Place>>,
601 - non_mutating_spreads: HashSet<IdentifierId>,
602 + hoisted_context_declarations: FxHashMap<DeclarationId, Option<Place>>,
603 + non_mutating_spreads: FxHashSet<IdentifierId>,
604 /// Cache of ValueIds keyed by effect hash, ensuring stable allocation-site identity
605 /// across fixpoint iterations. Mirrors TS `effectInstructionValueCache`.
604 - effect_value_id_cache: HashMap<String, ValueId>,
606 + effect_value_id_cache: FxHashMap<String, ValueId>,
607 /// Maps ValueId to FunctionId for function expressions, so we can look up
608 /// locally-declared functions when processing Apply effects.
607 - function_values: HashMap<ValueId, FunctionId>,
609 + function_values: FxHashMap<ValueId, FunctionId>,
610 /// Cache of function expression signatures, keyed by FunctionId
609 - function_signature_cache: HashMap<FunctionId, AliasingSignature>,
611 + function_signature_cache: FxHashMap<FunctionId, AliasingSignature>,
612 /// Cache of temporary places created for aliasing signature config temporaries.
613 /// Keyed by (lvalue_identifier_id, temp_name) to ensure stable allocation
614 /// across fixpoint iterations.
613 - aliasing_config_temp_cache: HashMap<(IdentifierId, String), Place>,
615 + aliasing_config_temp_cache: FxHashMap<(IdentifierId, String), Place>,
616 }
617
618 impl Context {
@@ -785,11 +787,11 @@ fn merge_value_kinds(a: ValueKind, b: ValueKind) -> ValueKind {
787 fn find_hoisted_context_declarations(
788 func: &HirFunction,
789 env: &Environment,
788 -) -> HashMap<DeclarationId, Option<Place>> {
789 - let mut hoisted: HashMap<DeclarationId, Option<Place>> = HashMap::new();
790 +) -> FxHashMap<DeclarationId, Option<Place>> {
791 + let mut hoisted: FxHashMap<DeclarationId, Option<Place>> = FxHashMap::default();
792
793 fn visit(
792 - hoisted: &mut HashMap<DeclarationId, Option<Place>>,
794 + hoisted: &mut FxHashMap<DeclarationId, Option<Place>>,
795 place: &Place,
796 env: &Environment,
797 ) {
@@ -831,8 +833,8 @@ fn find_hoisted_context_declarations(
833 fn find_non_mutated_destructure_spreads(
834 func: &HirFunction,
835 env: &Environment,
834 -) -> HashSet<IdentifierId> {
835 - let mut known_frozen: HashSet<IdentifierId> = HashSet::new();
836 +) -> FxHashSet<IdentifierId> {
837 + let mut known_frozen: FxHashSet<IdentifierId> = FxHashSet::default();
838 if func.fn_type == ReactFunctionType::Component {
839 if let Some(param) = func.params.first() {
840 if let ParamPattern::Place(p) = param {
@@ -847,7 +849,8 @@ fn find_non_mutated_destructure_spreads(
849 }
850 }
851
850 - let mut candidate_non_mutating_spreads: HashMap<IdentifierId, IdentifierId> = HashMap::new();
852 + let mut candidate_non_mutating_spreads: FxHashMap<IdentifierId, IdentifierId> =
853 + FxHashMap::default();
854 for (_block_id, block) in &func.body.blocks {
855 if !candidate_non_mutating_spreads.is_empty() {
856 for phi in &block.phis {
@@ -954,7 +957,7 @@ fn find_non_mutated_destructure_spreads(
957 }
958 }
959
957 - let mut non_mutating: HashSet<IdentifierId> = HashSet::new();
960 + let mut non_mutating: FxHashSet<IdentifierId> = FxHashSet::default();
961 for (key, value) in &candidate_non_mutating_spreads {
962 if key == value {
963 non_mutating.insert(*key);
@@ -991,7 +994,7 @@ fn infer_block(
994 let block = &func.body.blocks[&block_id];
995
996 // Process phis
994 - let phis: Vec<(IdentifierId, indexmap::IndexMap<BlockId, Place>)> = block
997 + let phis: Vec<(IdentifierId, IndexMap<BlockId, Place, FxBuildHasher>)> = block
998 .phis
999 .iter()
1000 .map(|phi| (phi.place.identifier, phi.operands.clone()))
@@ -1144,7 +1147,7 @@ fn apply_signature(
1147 | InstructionValue::ObjectMethod { lowered_func, .. } => {
1148 let inner_func = &env.functions[lowered_func.func.0 as usize];
1149 if let Some(ref aliasing_effects) = inner_func.aliasing_effects {
1147 - let context_ids: HashSet<IdentifierId> =
1150 + let context_ids: FxHashSet<IdentifierId> =
1151 inner_func.context.iter().map(|p| p.identifier).collect();
1152 for effect in aliasing_effects {
1153 let (mutate_value, is_mutate) = match effect {
@@ -1203,7 +1206,7 @@ fn apply_signature(
1206 }
1207
1208 // Track which values we've already initialized
1206 - let mut initialized: HashSet<IdentifierId> = HashSet::new();
1209 + let mut initialized: FxHashSet<IdentifierId> = FxHashSet::default();
1210
1211 // Get the cached signature effects
1212 let sig = context.instruction_signature_cache.get(&instr_idx).unwrap();
@@ -1296,7 +1299,7 @@ fn apply_effect(
1299 context: &mut Context,
1300 state: &mut InferenceState,
1301 effect: AliasingEffect,
1299 - initialized: &mut HashSet<IdentifierId>,
1302 + initialized: &mut FxHashSet<IdentifierId>,
1303 effects: &mut Vec<AliasingEffect>,
1304 env: &mut Environment,
1305 func: &HirFunction,
@@ -1484,7 +1487,7 @@ fn apply_effect(
1487 } else {
1488 ValueKind::Frozen
1489 },
1487 - reason: IndexSet::new(),
1490 + reason: IndexSet::default(),
1491 },
1492 );
1493 state.define(into.identifier, value_id);
@@ -2582,7 +2585,7 @@ fn compute_effects_for_legacy_signature(
2585 args: &[PlaceOrSpreadOrHole],
2586 _loc: Option<&SourceLocation>,
2587 env: &Environment,
2585 - function_values: &HashMap<ValueId, FunctionId>,
2588 + function_values: &FxHashMap<ValueId, FunctionId>,
2589 todo_errors: &mut Vec<react_compiler_diagnostics::CompilerErrorDetail>,
2590 ) -> Vec<AliasingEffect> {
2591 let return_value_reason = signature.return_value_reason.unwrap_or(ValueReason::Other);
@@ -2786,7 +2789,7 @@ fn are_arguments_immutable_and_non_mutating(
2789 state: &InferenceState,
2790 args: &[PlaceOrSpreadOrHole],
2791 env: &Environment,
2789 - function_values: &HashMap<ValueId, FunctionId>,
2792 + function_values: &FxHashMap<ValueId, FunctionId>,
2793 ) -> bool {
2794 for arg in args {
2795 match arg {
@@ -2870,14 +2873,14 @@ fn compute_effects_for_aliasing_signature_config(
2873 args: &[PlaceOrSpreadOrHole],
2874 context: &[Place],
2875 _loc: Option<&SourceLocation>,
2873 - temp_cache: &mut HashMap<(IdentifierId, String), Place>,
2876 + temp_cache: &mut FxHashMap<(IdentifierId, String), Place>,
2877 ) -> Result<Option<Vec<AliasingEffect>>, CompilerDiagnostic> {
2878 // Build substitutions from config strings to places
2876 - let mut substitutions: HashMap<String, Vec<Place>> = HashMap::new();
2879 + let mut substitutions: FxHashMap<String, Vec<Place>> = FxHashMap::default();
2880 substitutions.insert(config.receiver.clone(), vec![receiver.clone()]);
2881 substitutions.insert(config.returns.clone(), vec![lvalue.clone()]);
2882
2880 - let mut mutable_spreads: HashSet<IdentifierId> = HashSet::new();
2883 + let mut mutable_spreads: FxHashSet<IdentifierId> = FxHashSet::default();
2884
2885 for (i, arg) in args.iter().enumerate() {
2886 match arg {
@@ -3113,8 +3116,8 @@ fn compute_effects_for_aliasing_signature(
3116 return Ok(None);
3117 }
3118
3116 - let mut mutable_spreads: HashSet<IdentifierId> = HashSet::new();
3117 - let mut substitutions: HashMap<IdentifierId, Vec<Place>> = HashMap::new();
3119 + let mut mutable_spreads: FxHashSet<IdentifierId> = FxHashSet::default();
3120 + let mut substitutions: FxHashMap<IdentifierId, Vec<Place>> = FxHashMap::default();
3121 substitutions.insert(signature.receiver, vec![receiver.clone()]);
3122 substitutions.insert(signature.returns, vec![lvalue.clone()]);
3123
@@ -3407,7 +3410,7 @@ fn compute_effects_for_aliasing_signature(
3410 /// since the primary reason is always inserted first, this effectively
3411 /// picks the most specific non-Other reason. We replicate this by
3412 /// preferring any non-Other reason over Other.
3410 -fn primary_reason(reasons: &IndexSet<ValueReason>) -> ValueReason {
3413 +fn primary_reason(reasons: &IndexSet<ValueReason, FxBuildHasher>) -> ValueReason {
3414 for &r in reasons {
3415 if r != ValueReason::Other {
3416 return r;
compiler/crates/react_compiler_inference/src/infer_mutation_aliasing_ranges.rs
+17 -17
@@ -14,7 +14,7 @@
14 //! vars, aliasing between params/context-vars/return-value)
15 //! - The legacy `Effect` to store on each Place
16
17 -use std::collections::{HashMap, HashSet};
17 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
18
19 use indexmap::IndexMap;
20
@@ -76,10 +76,10 @@ enum NodeValue {
76 #[derive(Debug, Clone)]
77 struct Node {
78 id: IdentifierId,
79 - created_from: IndexMap<IdentifierId, usize>,
80 - captures: IndexMap<IdentifierId, usize>,
81 - aliases: IndexMap<IdentifierId, usize>,
82 - maybe_aliases: IndexMap<IdentifierId, usize>,
79 + created_from: IndexMap<IdentifierId, usize, FxBuildHasher>,
80 + captures: IndexMap<IdentifierId, usize, FxBuildHasher>,
81 + aliases: IndexMap<IdentifierId, usize, FxBuildHasher>,
82 + maybe_aliases: IndexMap<IdentifierId, usize, FxBuildHasher>,
83 edges: Vec<Edge>,
84 transitive: Option<MutationInfo>,
85 local: Option<MutationInfo>,
@@ -92,10 +92,10 @@ impl Node {
92 fn new(id: IdentifierId, value: NodeValue) -> Self {
93 Node {
94 id,
95 - created_from: IndexMap::new(),
96 - captures: IndexMap::new(),
97 - aliases: IndexMap::new(),
98 - maybe_aliases: IndexMap::new(),
95 + created_from: IndexMap::default(),
96 + captures: IndexMap::default(),
97 + aliases: IndexMap::default(),
98 + maybe_aliases: IndexMap::default(),
99 edges: Vec::new(),
100 transitive: None,
101 local: None,
@@ -107,13 +107,13 @@ impl Node {
107 }
108
109 struct AliasingState {
110 - nodes: IndexMap<IdentifierId, Node>,
110 + nodes: IndexMap<IdentifierId, Node, FxBuildHasher>,
111 }
112
113 impl AliasingState {
114 fn new() -> Self {
115 AliasingState {
116 - nodes: IndexMap::new(),
116 + nodes: IndexMap::default(),
117 }
118 }
119
@@ -198,7 +198,7 @@ impl AliasingState {
198 }
199
200 fn render(&self, index: usize, start: IdentifierId, env: &mut Environment) {
201 - let mut seen = HashSet::new();
201 + let mut seen = FxHashSet::default();
202 let mut queue: Vec<IdentifierId> = vec![start];
203 while let Some(current) = queue.pop() {
204 if !seen.insert(current) {
@@ -260,7 +260,7 @@ impl AliasingState {
260 Forwards,
261 }
262
263 - let mut seen: HashMap<IdentifierId, MutationKind> = HashMap::new();
263 + let mut seen: FxHashMap<IdentifierId, MutationKind> = FxHashMap::default();
264 let mut queue: Vec<QueueEntry> = vec![QueueEntry {
265 place: start,
266 transitive,
@@ -475,7 +475,7 @@ pub fn infer_mutation_aliasing_ranges(
475 into: Place,
476 index: usize,
477 }
478 - let mut pending_phis: HashMap<BlockId, Vec<PendingPhiOperand>> = HashMap::new();
478 + let mut pending_phis: FxHashMap<BlockId, Vec<PendingPhiOperand>> = FxHashMap::default();
479
480 struct PendingMutation {
481 index: usize,
@@ -510,7 +510,7 @@ pub fn infer_mutation_aliasing_ranges(
510 }
511 state.create(&func.returns, NodeValue::Object);
512
513 - let mut seen_blocks: HashSet<BlockId> = HashSet::new();
513 + let mut seen_blocks: FxHashSet<BlockId> = FxHashSet::default();
514
515 // Collect block iteration data to avoid borrow conflicts
516 let block_order: Vec<BlockId> = func.body.blocks.keys().cloned().collect();
@@ -734,7 +734,7 @@ pub fn infer_mutation_aliasing_ranges(
734 // Set effect on mutated params/context vars
735 // We need to do this in a separate pass because we need to know which params
736 // were mutated before setting effects
737 - let mut captured_params: HashSet<IdentifierId> = HashSet::new();
737 + let mut captured_params: FxHashSet<IdentifierId> = FxHashSet::default();
738 for param in &func.params {
739 let place = match param {
740 react_compiler_hir::ParamPattern::Place(p) => p,
@@ -892,7 +892,7 @@ pub fn infer_mutation_aliasing_ranges(
892
893 // Compute operand effects from instruction effects
894 let effects = instr.effects.as_ref().unwrap().clone();
895 - let mut operand_effects: HashMap<IdentifierId, Effect> = HashMap::new();
895 + let mut operand_effects: FxHashMap<IdentifierId, Effect> = FxHashMap::default();
896
897 for effect in &effects {
898 match effect {
compiler/crates/react_compiler_inference/src/infer_reactive_places.rs
+11 -11
@@ -14,7 +14,7 @@
14 //! 4. Mutation with reactive operands
15 //! 5. Conditional assignment based on reactive control flow
16
17 -use std::collections::{HashMap, HashSet};
17 +use rustc_hash::{FxHashMap, FxHashSet};
18
19 use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
20 use react_compiler_hir::dominator::post_dominator_frontier;
@@ -69,7 +69,7 @@ pub fn infer_reactive_places(
69 // is already reactive, the TS `continue`s and skips operand processing.
70 // We track which phi operand Places should be marked reactive.
71 // Key: (block_id, phi_idx, operand_idx), Value: should be reactive
72 - let mut phi_operand_reactive: HashMap<(BlockId, usize, usize), bool> = HashMap::new();
72 + let mut phi_operand_reactive: FxHashMap<(BlockId, usize, usize), bool> = FxHashMap::default();
73
74 // Fixpoint iteration — compute reactive set
75 loop {
@@ -235,7 +235,7 @@ pub fn infer_reactive_places(
235
236 struct ReactivityMap<'a> {
237 has_changes: bool,
238 - reactive: HashSet<IdentifierId>,
238 + reactive: FxHashSet<IdentifierId>,
239 aliased_identifiers: &'a mut DisjointSet<IdentifierId>,
240 }
241
@@ -243,7 +243,7 @@ impl<'a> ReactivityMap<'a> {
243 fn new(aliased_identifiers: &'a mut DisjointSet<IdentifierId>) -> Self {
244 ReactivityMap {
245 has_changes: false,
246 - reactive: HashSet::new(),
246 + reactive: FxHashSet::default(),
247 aliased_identifiers,
248 }
249 }
@@ -273,13 +273,13 @@ impl<'a> ReactivityMap<'a> {
273 // =============================================================================
274
275 struct StableSidemap {
276 - map: HashMap<IdentifierId, bool>,
276 + map: FxHashMap<IdentifierId, bool>,
277 }
278
279 impl StableSidemap {
280 fn new() -> Self {
281 StableSidemap {
282 - map: HashMap::new(),
282 + map: FxHashMap::default(),
283 }
284 }
285
@@ -538,7 +538,7 @@ fn apply_reactive_flags_replay(
538 env: &mut Environment,
539 reactive_map: &mut ReactivityMap,
540 stable_sidemap: &mut StableSidemap,
541 - phi_operand_reactive: &HashMap<(BlockId, usize, usize), bool>,
541 + phi_operand_reactive: &FxHashMap<(BlockId, usize, usize), bool>,
542 ) {
543 let reactive_ids = build_reactive_id_set(reactive_map);
544
@@ -697,8 +697,8 @@ fn apply_reactive_flags_replay(
697 apply_reactive_flags_to_inner_functions(func, env, &reactive_ids);
698 }
699
700 -fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> HashSet<IdentifierId> {
701 - let mut result = HashSet::new();
700 +fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> FxHashSet<IdentifierId> {
701 + let mut result = FxHashSet::default();
702 for &id in &reactive_map.reactive {
703 result.insert(id);
704 }
@@ -714,7 +714,7 @@ fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> HashSet<Identifier
714 fn apply_reactive_flags_to_inner_functions(
715 func: &HirFunction,
716 env: &mut Environment,
717 - reactive_ids: &HashSet<IdentifierId>,
717 + reactive_ids: &FxHashSet<IdentifierId>,
718 ) {
719 for (_block_id, block) in &func.body.blocks {
720 for instr_id in &block.instructions {
@@ -733,7 +733,7 @@ fn apply_reactive_flags_to_inner_functions(
733 fn apply_reactive_flags_to_inner_func(
734 func_id: FunctionId,
735 env: &mut Environment,
736 - reactive_ids: &HashSet<IdentifierId>,
736 + reactive_ids: &FxHashSet<IdentifierId>,
737 ) {
738 // Collect nested function IDs first to avoid borrow issues
739 let nested_func_ids: Vec<FunctionId> = {
compiler/crates/react_compiler_inference/src/infer_reactive_scope_variables.rs
+5 -5
@@ -15,7 +15,7 @@
15 //! 3. MergeOverlappingReactiveScopes ensures scopes do not overlap.
16 //! 4. BuildReactiveBlocks groups the statements for each scope.
17
18 -use std::collections::HashMap;
18 +use rustc_hash::FxHashMap;
19
20 use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
21 use react_compiler_hir::environment::Environment;
@@ -45,7 +45,7 @@ pub fn infer_reactive_scope_variables(
45
46 // Phase 2: assign scopes
47 // Maps each group root identifier to the ScopeId assigned to that group.
48 - let mut scopes: HashMap<IdentifierId, ScopeState> = HashMap::new();
48 + let mut scopes: FxHashMap<IdentifierId, ScopeState> = FxHashMap::default();
49
50 scope_identifiers.for_each(|identifier_id, group_id| {
51 let ident_range = env.identifiers[identifier_id.0 as usize]
@@ -267,7 +267,7 @@ pub(crate) fn find_disjoint_mutable_values(
267 env: &Environment,
268 ) -> DisjointSet<IdentifierId> {
269 let mut scope_identifiers = DisjointSet::<IdentifierId>::new();
270 - let mut declarations: HashMap<DeclarationId, IdentifierId> = HashMap::new();
270 + let mut declarations: FxHashMap<DeclarationId, IdentifierId> = FxHashMap::default();
271
272 let enable_forest = env.config.enable_forest;
273
@@ -284,8 +284,8 @@ pub(crate) fn find_disjoint_mutable_values(
284 .map(|iid| func.instructions[iid.0 as usize].id)
285 .unwrap_or(block.terminal.evaluation_order());
286
287 - let is_phi_mutated_after_creation = phi_range.start.0 + 1 != phi_range.end.0
288 - && phi_range.end > first_instr_id;
287 + let is_phi_mutated_after_creation =
288 + phi_range.start.0 + 1 != phi_range.end.0 && phi_range.end > first_instr_id;
289 // A phi operand defined at or after the phi's block is a loop
290 // back-edge: the variable is reassigned within the loop (eg a
291 // counter `a++` or `a = a + 1`). The reassignment must count as
compiler/crates/react_compiler_inference/src/memoize_fbt_and_macro_operands_in_same_scope.rs
+18 -20
@@ -13,7 +13,7 @@
13 //! 1. Forward data-flow: identify all macro tags (including property loads like `fbt.param`)
14 //! 2. Reverse data-flow: merge arguments of macro invocations into the same scope
15
16 -use std::collections::{HashMap, HashSet};
16 +use rustc_hash::{FxHashMap, FxHashSet};
17
18 use react_compiler_hir::environment::Environment;
19 use react_compiler_hir::visitors;
@@ -34,7 +34,7 @@ enum InlineLevel {
34 struct MacroDefinition {
35 level: InlineLevel,
36 /// Maps property names to their own MacroDefinition. `"*"` is a wildcard.
37 - properties: Option<HashMap<String, MacroDefinition>>,
37 + properties: Option<FxHashMap<String, MacroDefinition>>,
38 }
39
40 fn shallow_macro() -> MacroDefinition {
@@ -52,7 +52,7 @@ fn transitive_macro() -> MacroDefinition {
52 }
53
54 fn fbt_macro() -> MacroDefinition {
55 - let mut props = HashMap::new();
55 + let mut props = FxHashMap::default();
56 props.insert("*".to_string(), shallow_macro());
57 // fbt.enum gets FBT_MACRO (recursive/transitive)
58 // We'll fill this in after construction since it's self-referential.
@@ -66,7 +66,7 @@ fn fbt_macro() -> MacroDefinition {
66 let enum_macro = MacroDefinition {
67 level: InlineLevel::Transitive,
68 properties: Some({
69 - let mut p = HashMap::new();
69 + let mut p = FxHashMap::default();
70 p.insert("*".to_string(), shallow_macro());
71 // enum's enum is also recursive, but in practice the depth is bounded
72 p.insert("enum".to_string(), transitive_macro());
@@ -81,8 +81,8 @@ fn fbt_macro() -> MacroDefinition {
81 }
82
83 /// Built-in FBT tags and their macro definitions.
84 -fn fbt_tags() -> HashMap<String, MacroDefinition> {
85 - let mut tags = HashMap::new();
84 +fn fbt_tags() -> FxHashMap<String, MacroDefinition> {
85 + let mut tags = FxHashMap::default();
86 tags.insert("fbt".to_string(), fbt_macro());
87 tags.insert("fbt:param".to_string(), shallow_macro());
88 tags.insert("fbt:enum".to_string(), fbt_macro());
@@ -98,9 +98,9 @@ fn fbt_tags() -> HashMap<String, MacroDefinition> {
98 pub fn memoize_fbt_and_macro_operands_in_same_scope(
99 func: &HirFunction,
100 env: &mut Environment,
101 -) -> HashSet<IdentifierId> {
101 +) -> FxHashSet<IdentifierId> {
102 // Phase 1: Build macro kinds map from built-in FBT tags + custom macros
103 - let mut macro_kinds: HashMap<String, MacroDefinition> = fbt_tags();
103 + let mut macro_kinds: FxHashMap<String, MacroDefinition> = fbt_tags();
104 if let Some(ref custom_macros) = env.config.custom_macros {
105 for name in custom_macros {
106 macro_kinds.insert(name.clone(), transitive_macro());
@@ -120,9 +120,9 @@ pub fn memoize_fbt_and_macro_operands_in_same_scope(
120 /// things like `fbt.foo.bar(...)`.
121 fn populate_macro_tags(
122 func: &HirFunction,
123 - macro_kinds: &HashMap<String, MacroDefinition>,
124 -) -> HashMap<IdentifierId, MacroDefinition> {
125 - let mut macro_tags: HashMap<IdentifierId, MacroDefinition> = HashMap::new();
123 + macro_kinds: &FxHashMap<String, MacroDefinition>,
124 +) -> FxHashMap<IdentifierId, MacroDefinition> {
125 + let mut macro_tags: FxHashMap<IdentifierId, MacroDefinition> = FxHashMap::default();
126
127 for block in func.body.blocks.values() {
128 for &instr_id in &block.instructions {
@@ -134,9 +134,7 @@ fn populate_macro_tags(
134 value: PrimitiveValue::String(s),
135 ..
136 } => {
137 - if let Some(macro_def) =
138 - s.as_str().and_then(|utf8| macro_kinds.get(utf8))
139 - {
137 + if let Some(macro_def) = s.as_str().and_then(|utf8| macro_kinds.get(utf8)) {
138 // We don't distinguish between tag names and strings, so record
139 // all `fbt` string literals in case they are used as a jsx tag.
140 macro_tags.insert(lvalue_id, macro_def.clone());
@@ -180,10 +178,10 @@ fn populate_macro_tags(
178 fn merge_macro_arguments(
179 func: &HirFunction,
180 env: &mut Environment,
183 - macro_tags: &mut HashMap<IdentifierId, MacroDefinition>,
184 - macro_kinds: &HashMap<String, MacroDefinition>,
185 -) -> HashSet<IdentifierId> {
186 - let mut macro_values: HashSet<IdentifierId> = macro_tags.keys().copied().collect();
181 + macro_tags: &mut FxHashMap<IdentifierId, MacroDefinition>,
182 + macro_kinds: &FxHashMap<String, MacroDefinition>,
183 +) -> FxHashSet<IdentifierId> {
184 + let mut macro_values: FxHashSet<IdentifierId> = macro_tags.keys().copied().collect();
185
186 // Iterate blocks in reverse order
187 let block_ids: Vec<_> = func.body.blocks.keys().copied().collect();
@@ -356,8 +354,8 @@ fn visit_operands(
354 lvalue_id: IdentifierId,
355 value: &InstructionValue,
356 env: &mut Environment,
359 - macro_values: &mut HashSet<IdentifierId>,
360 - macro_tags: &mut HashMap<IdentifierId, MacroDefinition>,
357 + macro_values: &mut FxHashSet<IdentifierId>,
358 + macro_tags: &mut FxHashMap<IdentifierId, MacroDefinition>,
359 ) {
360 macro_values.insert(lvalue_id);
361
compiler/crates/react_compiler_inference/src/merge_overlapping_reactive_scopes_hir.rs
+8 -8
@@ -15,8 +15,8 @@
15 //!
16 //! Ported from TypeScript `src/HIR/MergeOverlappingReactiveScopesHIR.ts`.
17
18 +use rustc_hash::FxHashMap;
19 use std::cmp;
19 -use std::collections::HashMap;
20
21 use react_compiler_hir::environment::Environment;
22 use react_compiler_hir::visitors;
@@ -46,7 +46,7 @@ struct ScopeInfo {
46 /// Sorted descending by id (so we can pop from the end for smallest)
47 scope_ends: Vec<ScopeEndEntry>,
48 /// Maps IdentifierId -> ScopeId for all places that have a scope
49 - place_scopes: HashMap<IdentifierId, ScopeId>,
49 + place_scopes: FxHashMap<IdentifierId, ScopeId>,
50 }
51
52 // =============================================================================
@@ -95,9 +95,9 @@ fn is_mutable(env: &Environment, id: EvaluationOrder, identifier_id: IdentifierI
95 // =============================================================================
96
97 fn collect_scope_info(func: &HirFunction, env: &Environment) -> ScopeInfo {
98 - let mut scope_starts_map: HashMap<EvaluationOrder, Vec<ScopeId>> = HashMap::new();
99 - let mut scope_ends_map: HashMap<EvaluationOrder, Vec<ScopeId>> = HashMap::new();
100 - let mut place_scopes: HashMap<IdentifierId, ScopeId> = HashMap::new();
98 + let mut scope_starts_map: FxHashMap<EvaluationOrder, Vec<ScopeId>> = FxHashMap::default();
99 + let mut scope_ends_map: FxHashMap<EvaluationOrder, Vec<ScopeId>> = FxHashMap::default();
100 + let mut place_scopes: FxHashMap<IdentifierId, ScopeId> = FxHashMap::default();
101
102 let mut collect_place_scope = |identifier_id: IdentifierId, env: &Environment| {
103 let scope_id = match env.identifiers[identifier_id.0 as usize].scope {
@@ -144,7 +144,7 @@ fn collect_scope_info(func: &HirFunction, env: &Environment) -> ScopeInfo {
144 // We must NOT sort by ScopeId here — the insertion order determines which scope
145 // becomes the root in the disjoint set union.
146 fn dedup_preserve_order(scopes: &mut Vec<ScopeId>) {
147 - let mut seen = std::collections::HashSet::new();
147 + let mut seen = rustc_hash::FxHashSet::default();
148 scopes.retain(|s| seen.insert(*s));
149 }
150 for scopes in scope_starts_map.values_mut() {
@@ -348,8 +348,8 @@ pub fn merge_overlapping_reactive_scopes_hir(func: &mut HirFunction, env: &mut E
348 // When scope.range is updated, ALL identifiers referencing that range object
349 // automatically see the new values. We use MutableRangeId to identify which
350 // identifiers share the same logical range as a root scope.
351 - let mut original_root_range_ids: HashMap<ScopeId, react_compiler_hir::MutableRangeId> =
352 - HashMap::new();
351 + let mut original_root_range_ids: FxHashMap<ScopeId, react_compiler_hir::MutableRangeId> =
352 + FxHashMap::default();
353 for (_, root_id) in &scope_groups {
354 if !original_root_range_ids.contains_key(root_id) {
355 let range_id = env.scopes[root_id.0 as usize].range.id;
compiler/crates/react_compiler_inference/src/propagate_scope_dependencies_hir.rs
+105 -100
@@ -13,7 +13,8 @@
13 //! - `src/HIR/DeriveMinimalDependenciesHIR.ts`
14
15 use indexmap::IndexMap;
16 -use std::collections::{BTreeSet, HashMap, HashSet};
16 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
17 +use std::collections::BTreeSet;
18
19 use react_compiler_hir::environment::Environment;
20 use react_compiler_hir::visitors::{ScopeBlockInfo, ScopeBlockTraversal};
@@ -44,7 +45,7 @@ pub fn propagate_scope_dependencies_hir(func: &mut HirFunction, env: &mut Enviro
45 let (working, registry) =
46 collect_hoistable_and_propagate(func, env, &temporaries, &hoistable_objects);
47 // Convert to scope-keyed map with full dependency paths
47 - let mut keyed: HashMap<ScopeId, Vec<ReactiveScopeDependency>> = HashMap::new();
48 + let mut keyed: FxHashMap<ScopeId, Vec<ReactiveScopeDependency>> = FxHashMap::default();
49 for (_block_id, block) in &func.body.blocks {
50 if let Terminal::Scope {
51 scope,
@@ -128,17 +129,17 @@ fn are_equal_paths(a: &[DependencyPathEntry], b: &[DependencyPathEntry]) -> bool
129 fn find_temporaries_used_outside_declaring_scope(
130 func: &HirFunction,
131 env: &Environment,
131 -) -> HashSet<DeclarationId> {
132 - let mut declarations: HashMap<DeclarationId, ScopeId> = HashMap::new();
133 - let mut pruned_scopes: HashSet<ScopeId> = HashSet::new();
132 +) -> FxHashSet<DeclarationId> {
133 + let mut declarations: FxHashMap<DeclarationId, ScopeId> = FxHashMap::default();
134 + let mut pruned_scopes: FxHashSet<ScopeId> = FxHashSet::default();
135 let mut traversal = ScopeBlockTraversal::new();
135 - let mut used_outside_declaring_scope: HashSet<DeclarationId> = HashSet::new();
136 + let mut used_outside_declaring_scope: FxHashSet<DeclarationId> = FxHashSet::default();
137
138 let handle_place = |place_id: IdentifierId,
138 - declarations: &HashMap<DeclarationId, ScopeId>,
139 + declarations: &FxHashMap<DeclarationId, ScopeId>,
140 traversal: &ScopeBlockTraversal,
140 - pruned_scopes: &HashSet<ScopeId>,
141 - used_outside: &mut HashSet<DeclarationId>,
141 + pruned_scopes: &FxHashSet<ScopeId>,
142 + used_outside: &mut FxHashSet<DeclarationId>,
143 env: &Environment| {
144 let decl_id = env.identifiers[place_id.0 as usize].declaration_id;
145 if let Some(&declaring_scope) = declarations.get(&decl_id) {
@@ -227,9 +228,9 @@ fn find_temporaries_used_outside_declaring_scope(
228 fn collect_temporaries_sidemap(
229 func: &HirFunction,
230 env: &Environment,
230 - used_outside_declaring_scope: &HashSet<DeclarationId>,
231 -) -> HashMap<IdentifierId, ReactiveScopeDependency> {
232 - let mut temporaries = HashMap::new();
231 + used_outside_declaring_scope: &FxHashSet<DeclarationId>,
232 +) -> FxHashMap<IdentifierId, ReactiveScopeDependency> {
233 + let mut temporaries = FxHashMap::default();
234 collect_temporaries_sidemap_impl(
235 func,
236 env,
@@ -269,8 +270,8 @@ fn convert_hoisted_lvalue_kind(kind: InstructionKind) -> Option<InstructionKind>
270 fn collect_temporaries_sidemap_impl(
271 func: &HirFunction,
272 env: &Environment,
272 - used_outside_declaring_scope: &HashSet<DeclarationId>,
273 - temporaries: &mut HashMap<IdentifierId, ReactiveScopeDependency>,
273 + used_outside_declaring_scope: &FxHashSet<DeclarationId>,
274 + temporaries: &mut FxHashMap<IdentifierId, ReactiveScopeDependency>,
275 inner_fn_context: Option<EvaluationOrder>,
276 ) {
277 for (_block_id, block) in &func.body.blocks {
@@ -369,7 +370,7 @@ fn get_property(
370 property_name: &PropertyLiteral,
371 optional: bool,
372 loc: Option<react_compiler_hir::SourceLocation>,
372 - temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
373 + temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
374 _env: &Environment,
375 ) -> ReactiveScopeDependency {
376 let resolved = temporaries.get(&object.identifier);
@@ -405,9 +406,9 @@ fn get_property(
406 // =============================================================================
407
408 struct OptionalChainSidemap {
408 - temporaries_read_in_optional: HashMap<IdentifierId, ReactiveScopeDependency>,
409 - processed_instrs_in_optional: HashSet<ProcessedInstr>,
410 - hoistable_objects: HashMap<BlockId, ReactiveScopeDependency>,
409 + temporaries_read_in_optional: FxHashMap<IdentifierId, ReactiveScopeDependency>,
410 + processed_instrs_in_optional: FxHashSet<ProcessedInstr>,
411 + hoistable_objects: FxHashMap<BlockId, ReactiveScopeDependency>,
412 }
413
414 /// We track processed instructions/terminals by their lvalue IdentifierId + block id.
@@ -423,10 +424,10 @@ enum ProcessedInstr {
424
425 fn collect_optional_chain_sidemap(func: &HirFunction, env: &Environment) -> OptionalChainSidemap {
426 let mut ctx = OptionalTraversalContext {
426 - seen_optionals: HashSet::new(),
427 - processed_instrs_in_optional: HashSet::new(),
428 - temporaries_read_in_optional: HashMap::new(),
429 - hoistable_objects: HashMap::new(),
427 + seen_optionals: FxHashSet::default(),
428 + processed_instrs_in_optional: FxHashSet::default(),
429 + temporaries_read_in_optional: FxHashMap::default(),
430 + hoistable_objects: FxHashMap::default(),
431 };
432
433 traverse_function_optional(func, env, &mut ctx);
@@ -439,10 +440,10 @@ fn collect_optional_chain_sidemap(func: &HirFunction, env: &Environment) -> Opti
440 }
441
442 struct OptionalTraversalContext {
442 - seen_optionals: HashSet<BlockId>,
443 - processed_instrs_in_optional: HashSet<ProcessedInstr>,
444 - temporaries_read_in_optional: HashMap<IdentifierId, ReactiveScopeDependency>,
445 - hoistable_objects: HashMap<BlockId, ReactiveScopeDependency>,
443 + seen_optionals: FxHashSet<BlockId>,
444 + processed_instrs_in_optional: FxHashSet<ProcessedInstr>,
445 + temporaries_read_in_optional: FxHashMap<IdentifierId, ReactiveScopeDependency>,
446 + hoistable_objects: FxHashMap<BlockId, ReactiveScopeDependency>,
447 }
448
449 fn traverse_function_optional(
@@ -785,8 +786,8 @@ fn traverse_optional_block(
786
787 #[derive(Debug, Clone)]
788 struct PropertyPathNode {
788 - properties: HashMap<PropertyLiteral, usize>, // index into registry
789 - optional_properties: HashMap<PropertyLiteral, usize>, // index into registry
789 + properties: FxHashMap<PropertyLiteral, usize>, // index into registry
790 + optional_properties: FxHashMap<PropertyLiteral, usize>, // index into registry
791 #[allow(dead_code)]
792 parent: Option<usize>,
793 full_path: ReactiveScopeDependency,
@@ -797,14 +798,14 @@ struct PropertyPathNode {
798
799 struct PropertyPathRegistry {
800 nodes: Vec<PropertyPathNode>,
800 - roots: HashMap<IdentifierId, usize>,
801 + roots: FxHashMap<IdentifierId, usize>,
802 }
803
804 impl PropertyPathRegistry {
805 fn new() -> Self {
806 Self {
807 nodes: Vec::new(),
807 - roots: HashMap::new(),
808 + roots: FxHashMap::default(),
809 }
810 }
811
@@ -819,8 +820,8 @@ impl PropertyPathRegistry {
820 }
821 let idx = self.nodes.len();
822 self.nodes.push(PropertyPathNode {
822 - properties: HashMap::new(),
823 - optional_properties: HashMap::new(),
823 + properties: FxHashMap::default(),
824 + optional_properties: FxHashMap::default(),
825 parent: None,
826 full_path: ReactiveScopeDependency {
827 identifier: identifier_id,
@@ -858,8 +859,8 @@ impl PropertyPathRegistry {
859 let mut new_path = parent_full_path.path.clone();
860 new_path.push(entry.clone());
861 self.nodes.push(PropertyPathNode {
861 - properties: HashMap::new(),
862 - optional_properties: HashMap::new(),
862 + properties: FxHashMap::default(),
863 + optional_properties: FxHashMap::default(),
864 parent: Some(parent_idx),
865 full_path: ReactiveScopeDependency {
866 identifier: parent_full_path.identifier,
@@ -962,11 +963,11 @@ struct BlockInfo {
963 fn collect_hoistable_property_loads(
964 func: &HirFunction,
965 env: &Environment,
965 - temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
966 - hoistable_from_optionals: &HashMap<BlockId, ReactiveScopeDependency>,
967 -) -> HashMap<BlockId, BlockInfo> {
966 + temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
967 + hoistable_from_optionals: &FxHashMap<BlockId, ReactiveScopeDependency>,
968 +) -> FxHashMap<BlockId, BlockInfo> {
969 let mut registry = PropertyPathRegistry::new();
969 - let known_immutable_identifiers: HashSet<IdentifierId> = if func.fn_type
970 + let known_immutable_identifiers: FxHashSet<IdentifierId> = if func.fn_type
971 == ReactFunctionType::Component
972 || func.fn_type == ReactFunctionType::Hook
973 {
@@ -978,7 +979,7 @@ fn collect_hoistable_property_loads(
979 })
980 .collect()
981 } else {
981 - HashSet::new()
982 + FxHashSet::default()
983 };
984
985 let assumed_invoked_fns = get_assumed_invoked_functions(func, env);
@@ -994,11 +995,11 @@ fn collect_hoistable_property_loads(
995 }
996
997 struct CollectHoistableContext<'a> {
997 - temporaries: &'a HashMap<IdentifierId, ReactiveScopeDependency>,
998 - known_immutable_identifiers: &'a HashSet<IdentifierId>,
999 - hoistable_from_optionals: &'a HashMap<BlockId, ReactiveScopeDependency>,
1000 - nested_fn_immutable_context: Option<&'a HashSet<IdentifierId>>,
1001 - assumed_invoked_fns: &'a HashSet<FunctionId>,
998 + temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,
999 + known_immutable_identifiers: &'a FxHashSet<IdentifierId>,
1000 + hoistable_from_optionals: &'a FxHashMap<BlockId, ReactiveScopeDependency>,
1001 + nested_fn_immutable_context: Option<&'a FxHashSet<IdentifierId>>,
1002 + assumed_invoked_fns: &'a FxHashSet<FunctionId>,
1003 }
1004
1005 fn is_immutable_at_instr(
@@ -1027,7 +1028,7 @@ fn in_range(id: EvaluationOrder, range: &MutableRange) -> bool {
1028
1029 fn get_maybe_non_null_in_instruction(
1030 value: &InstructionValue,
1030 - temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
1031 + temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
1032 ) -> Option<ReactiveScopeDependency> {
1033 match value {
1034 InstructionValue::PropertyLoad { object, .. } => Some(
@@ -1057,10 +1058,10 @@ fn collect_hoistable_property_loads_impl(
1058 env: &Environment,
1059 ctx: &CollectHoistableContext,
1060 registry: &mut PropertyPathRegistry,
1060 -) -> HashMap<BlockId, BlockInfo> {
1061 +) -> FxHashMap<BlockId, BlockInfo> {
1062 let nodes = collect_non_nulls_in_blocks(func, env, ctx, registry);
1063 let working = propagate_non_null(func, &nodes, registry);
1063 - // Return the propagated results, converting HashSet<usize> back to BlockInfo
1064 + // Return the propagated results, converting FxHashSet<usize> back to BlockInfo
1065 working
1066 .into_iter()
1067 .map(|(k, v)| {
@@ -1078,17 +1079,18 @@ fn collect_hoistable_property_loads_impl(
1079 /// Returns the set of LoweredFunction FunctionIds that are assumed to be invoked.
1080 /// The `temporaries` map is shared across recursive calls (matching TS behavior where
1081 /// the same Map is passed to recursive invocations for inner functions).
1081 -fn get_assumed_invoked_functions(func: &HirFunction, env: &Environment) -> HashSet<FunctionId> {
1082 - let mut temporaries: HashMap<IdentifierId, (FunctionId, HashSet<FunctionId>)> = HashMap::new();
1082 +fn get_assumed_invoked_functions(func: &HirFunction, env: &Environment) -> FxHashSet<FunctionId> {
1083 + let mut temporaries: FxHashMap<IdentifierId, (FunctionId, FxHashSet<FunctionId>)> =
1084 + FxHashMap::default();
1085 get_assumed_invoked_functions_impl(func, env, &mut temporaries)
1086 }
1087
1088 fn get_assumed_invoked_functions_impl(
1089 func: &HirFunction,
1090 env: &Environment,
1089 - temporaries: &mut HashMap<IdentifierId, (FunctionId, HashSet<FunctionId>)>,
1090 -) -> HashSet<FunctionId> {
1091 - let mut hoistable: HashSet<FunctionId> = HashSet::new();
1091 + temporaries: &mut FxHashMap<IdentifierId, (FunctionId, FxHashSet<FunctionId>)>,
1092 +) -> FxHashSet<FunctionId> {
1093 + let mut hoistable: FxHashSet<FunctionId> = FxHashSet::default();
1094
1095 // Step 1: Collect identifier to function expression mappings
1096 for (_block_id, block) in &func.body.blocks {
@@ -1096,8 +1098,10 @@ fn get_assumed_invoked_functions_impl(
1098 let instr = &func.instructions[instr_id.0 as usize];
1099 match &instr.value {
1100 InstructionValue::FunctionExpression { lowered_func, .. } => {
1099 - temporaries
1100 - .insert(instr.lvalue.identifier, (lowered_func.func, HashSet::new()));
1101 + temporaries.insert(
1102 + instr.lvalue.identifier,
1103 + (lowered_func.func, FxHashSet::default()),
1104 + );
1105 }
1106 InstructionValue::StoreLocal {
1107 value: val, lvalue, ..
@@ -1221,7 +1225,7 @@ fn collect_non_nulls_in_blocks(
1225 env: &Environment,
1226 ctx: &CollectHoistableContext,
1227 registry: &mut PropertyPathRegistry,
1224 -) -> HashMap<BlockId, BlockInfo> {
1228 +) -> FxHashMap<BlockId, BlockInfo> {
1229 // Known non-null identifiers (e.g. component props)
1230 let mut known_non_null: BTreeSet<usize> = BTreeSet::new();
1231 if func.fn_type == ReactFunctionType::Component && !func.params.is_empty() {
@@ -1231,7 +1235,7 @@ fn collect_non_nulls_in_blocks(
1235 }
1236 }
1237
1234 - let mut nodes: HashMap<BlockId, BlockInfo> = HashMap::new();
1238 + let mut nodes: FxHashMap<BlockId, BlockInfo> = FxHashMap::default();
1239
1240 for (block_id, block) in &func.body.blocks {
1241 let mut assumed = known_non_null.clone();
@@ -1290,7 +1294,7 @@ fn collect_non_nulls_in_blocks(
1294 if ctx.assumed_invoked_fns.contains(&lowered_func.func) {
1295 let inner_func = &env.functions[lowered_func.func.0 as usize];
1296 // Build nested fn immutable context
1293 - let nested_fn_immutable_context: HashSet<IdentifierId> =
1297 + let nested_fn_immutable_context: FxHashSet<IdentifierId> =
1298 if ctx.nested_fn_immutable_context.is_some() {
1299 // Already in a nested fn context, use existing
1300 ctx.nested_fn_immutable_context.unwrap().clone()
@@ -1307,7 +1311,7 @@ fn collect_non_nulls_in_blocks(
1311 let inner_assumed = get_assumed_invoked_functions(inner_func, env);
1312 let inner_ctx = CollectHoistableContext {
1313 temporaries: ctx.temporaries,
1310 - known_immutable_identifiers: &HashSet::new(),
1314 + known_immutable_identifiers: &FxHashSet::default(),
1315 hoistable_from_optionals: ctx.hoistable_from_optionals,
1316 nested_fn_immutable_context: Some(&nested_fn_immutable_context),
1317 assumed_invoked_fns: &inner_assumed,
@@ -1347,13 +1351,13 @@ fn collect_non_nulls_in_blocks(
1351 /// and should be filtered out, allowing non-null info to propagate through non-cyclic paths.
1352 fn propagate_non_null(
1353 func: &HirFunction,
1350 - nodes: &HashMap<BlockId, BlockInfo>,
1354 + nodes: &FxHashMap<BlockId, BlockInfo>,
1355 registry: &mut PropertyPathRegistry,
1352 -) -> HashMap<BlockId, BTreeSet<usize>> {
1356 +) -> FxHashMap<BlockId, BTreeSet<usize>> {
1357 // Build successor map. Use BTreeSet to iterate successors in sorted BlockId
1358 // order, matching the TS Set<BlockId> insertion order (blocks are created in
1359 // ascending BlockId order).
1356 - let mut block_successors: HashMap<BlockId, BTreeSet<BlockId>> = HashMap::new();
1360 + let mut block_successors: FxHashMap<BlockId, BTreeSet<BlockId>> = FxHashMap::default();
1361 for (block_id, block) in &func.body.blocks {
1362 for pred in &block.preds {
1363 block_successors.entry(*pred).or_default().insert(*block_id);
@@ -1361,7 +1365,7 @@ fn propagate_non_null(
1365 }
1366
1367 // Clone nodes into mutable working set
1364 - let mut working: HashMap<BlockId, BTreeSet<usize>> = nodes
1368 + let mut working: FxHashMap<BlockId, BTreeSet<usize>> = nodes
1369 .iter()
1370 .map(|(k, v)| (*k, v.assumed_non_null_objects.clone()))
1371 .collect();
@@ -1374,7 +1378,7 @@ fn propagate_non_null(
1378 let mut changed = false;
1379
1380 // Forward pass (using predecessors)
1377 - let mut traversal_state: HashMap<BlockId, TraversalState> = HashMap::new();
1381 + let mut traversal_state: FxHashMap<BlockId, TraversalState> = FxHashMap::default();
1382 for &block_id in &block_ids {
1383 let block_changed = recursively_propagate_non_null(
1384 block_id,
@@ -1426,10 +1430,10 @@ enum PropagationDirection {
1430 fn recursively_propagate_non_null(
1431 node_id: BlockId,
1432 direction: PropagationDirection,
1429 - traversal_state: &mut HashMap<BlockId, TraversalState>,
1430 - working: &mut HashMap<BlockId, BTreeSet<usize>>,
1433 + traversal_state: &mut FxHashMap<BlockId, TraversalState>,
1434 + working: &mut FxHashMap<BlockId, BTreeSet<usize>>,
1435 func: &HirFunction,
1432 - block_successors: &HashMap<BlockId, BTreeSet<BlockId>>,
1436 + block_successors: &FxHashMap<BlockId, BTreeSet<BlockId>>,
1437 registry: &mut PropertyPathRegistry,
1438 ) -> bool {
1439 // Avoid re-visiting computed or currently active nodes
@@ -1500,12 +1504,12 @@ fn recursively_propagate_non_null(
1504 fn collect_hoistable_and_propagate(
1505 func: &HirFunction,
1506 env: &Environment,
1503 - temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
1504 - hoistable_from_optionals: &HashMap<BlockId, ReactiveScopeDependency>,
1505 -) -> (HashMap<BlockId, BTreeSet<usize>>, PropertyPathRegistry) {
1507 + temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
1508 + hoistable_from_optionals: &FxHashMap<BlockId, ReactiveScopeDependency>,
1509 +) -> (FxHashMap<BlockId, BTreeSet<usize>>, PropertyPathRegistry) {
1510 let mut registry = PropertyPathRegistry::new();
1511 let assumed_invoked_fns = get_assumed_invoked_functions(func, env);
1508 - let known_immutable_identifiers: HashSet<IdentifierId> = if func.fn_type
1512 + let known_immutable_identifiers: FxHashSet<IdentifierId> = if func.fn_type
1513 == ReactFunctionType::Component
1514 || func.fn_type == ReactFunctionType::Hook
1515 {
@@ -1517,7 +1521,7 @@ fn collect_hoistable_and_propagate(
1521 })
1522 .collect()
1523 } else {
1520 - HashSet::new()
1524 + FxHashSet::default()
1525 };
1526
1527 let ctx = CollectHoistableContext {
@@ -1538,9 +1542,9 @@ fn collect_hoistable_and_propagate(
1542 #[allow(dead_code)]
1543 fn key_by_scope_id(
1544 func: &HirFunction,
1541 - block_keyed: &HashMap<BlockId, BlockInfo>,
1542 -) -> HashMap<ScopeId, BlockInfo> {
1543 - let mut keyed: HashMap<ScopeId, BlockInfo> = HashMap::new();
1545 + block_keyed: &FxHashMap<BlockId, BlockInfo>,
1546 +) -> FxHashMap<ScopeId, BlockInfo> {
1547 + let mut keyed: FxHashMap<ScopeId, BlockInfo> = FxHashMap::default();
1548 for (_block_id, block) in &func.body.blocks {
1549 if let Terminal::Scope {
1550 scope,
@@ -1600,7 +1604,7 @@ enum HoistableAccessType {
1604 }
1605
1606 struct HoistableNode {
1603 - properties: HashMap<PropertyLiteral, Box<HoistableNodeEntry>>,
1607 + properties: FxHashMap<PropertyLiteral, Box<HoistableNodeEntry>>,
1608 access_type: HoistableAccessType,
1609 }
1610
@@ -1609,7 +1613,7 @@ struct HoistableNodeEntry {
1613 }
1614
1615 struct DependencyNode {
1612 - properties: IndexMap<PropertyLiteral, Box<DependencyNodeEntry>>,
1616 + properties: IndexMap<PropertyLiteral, Box<DependencyNodeEntry>, FxBuildHasher>,
1617 access_type: PropertyAccessType,
1618 loc: Option<react_compiler_hir::SourceLocation>,
1619 }
@@ -1619,8 +1623,8 @@ struct DependencyNodeEntry {
1623 }
1624
1625 struct ReactiveScopeDependencyTreeHIR {
1622 - hoistable_roots: HashMap<IdentifierId, (HoistableNode, bool)>, // node + reactive
1623 - dep_roots: IndexMap<IdentifierId, (DependencyNode, bool)>, // node + reactive (preserves insertion order like JS Map)
1626 + hoistable_roots: FxHashMap<IdentifierId, (HoistableNode, bool)>, // node + reactive
1627 + dep_roots: IndexMap<IdentifierId, (DependencyNode, bool), FxBuildHasher>, // node + reactive (preserves insertion order like JS Map)
1628 }
1629
1630 impl ReactiveScopeDependencyTreeHIR {
@@ -1628,7 +1632,8 @@ impl ReactiveScopeDependencyTreeHIR {
1632 hoistable_objects: impl Iterator<Item = &'a ReactiveScopeDependency>,
1633 _env: &Environment,
1634 ) -> Self {
1631 - let mut hoistable_roots: HashMap<IdentifierId, (HoistableNode, bool)> = HashMap::new();
1635 + let mut hoistable_roots: FxHashMap<IdentifierId, (HoistableNode, bool)> =
1636 + FxHashMap::default();
1637
1638 // Sort hoistable objects so that entries with optional first path come
1639 // before non-optional ones. This matches the TS behavior where
@@ -1651,7 +1656,7 @@ impl ReactiveScopeDependencyTreeHIR {
1656 };
1657 (
1658 HoistableNode {
1654 - properties: HashMap::new(),
1659 + properties: FxHashMap::default(),
1660 access_type,
1661 },
1662 dep.reactive,
@@ -1671,7 +1676,7 @@ impl ReactiveScopeDependencyTreeHIR {
1676 .or_insert_with(|| {
1677 Box::new(HoistableNodeEntry {
1678 node: HoistableNode {
1674 - properties: HashMap::new(),
1679 + properties: FxHashMap::default(),
1680 access_type,
1681 },
1682 })
@@ -1682,7 +1687,7 @@ impl ReactiveScopeDependencyTreeHIR {
1687
1688 Self {
1689 hoistable_roots,
1685 - dep_roots: IndexMap::new(),
1690 + dep_roots: IndexMap::default(),
1691 }
1692 }
1693
@@ -1690,7 +1695,7 @@ impl ReactiveScopeDependencyTreeHIR {
1695 let root = self.dep_roots.entry(dep.identifier).or_insert_with(|| {
1696 (
1697 DependencyNode {
1693 - properties: IndexMap::new(),
1698 + properties: IndexMap::default(),
1699 access_type: PropertyAccessType::UnconditionalAccess,
1700 loc: dep.loc,
1701 },
@@ -1735,7 +1740,7 @@ impl ReactiveScopeDependencyTreeHIR {
1740 .or_insert_with(|| {
1741 Box::new(DependencyNodeEntry {
1742 node: DependencyNode {
1738 - properties: IndexMap::new(),
1743 + properties: IndexMap::default(),
1744 access_type,
1745 loc: entry.loc,
1746 },
@@ -1809,30 +1814,30 @@ struct Decl {
1814
1815 /// Context for dependency collection.
1816 struct DependencyCollectionContext<'a> {
1812 - declarations: HashMap<DeclarationId, Decl>,
1813 - reassignments: HashMap<IdentifierId, Decl>,
1817 + declarations: FxHashMap<DeclarationId, Decl>,
1818 + reassignments: FxHashMap<IdentifierId, Decl>,
1819 scope_stack: Vec<ScopeId>,
1820 dep_stack: Vec<Vec<ReactiveScopeDependency>>,
1816 - deps: IndexMap<ScopeId, Vec<ReactiveScopeDependency>>,
1817 - temporaries: &'a HashMap<IdentifierId, ReactiveScopeDependency>,
1821 + deps: IndexMap<ScopeId, Vec<ReactiveScopeDependency>, FxBuildHasher>,
1822 + temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,
1823 #[allow(dead_code)]
1819 - temporaries_used_outside_scope: &'a HashSet<DeclarationId>,
1820 - processed_instrs_in_optional: &'a HashSet<ProcessedInstr>,
1824 + temporaries_used_outside_scope: &'a FxHashSet<DeclarationId>,
1825 + processed_instrs_in_optional: &'a FxHashSet<ProcessedInstr>,
1826 inner_fn_context: Option<EvaluationOrder>,
1827 }
1828
1829 impl<'a> DependencyCollectionContext<'a> {
1830 fn new(
1826 - temporaries_used_outside_scope: &'a HashSet<DeclarationId>,
1827 - temporaries: &'a HashMap<IdentifierId, ReactiveScopeDependency>,
1828 - processed_instrs_in_optional: &'a HashSet<ProcessedInstr>,
1831 + temporaries_used_outside_scope: &'a FxHashSet<DeclarationId>,
1832 + temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,
1833 + processed_instrs_in_optional: &'a FxHashSet<ProcessedInstr>,
1834 ) -> Self {
1835 Self {
1831 - declarations: HashMap::new(),
1832 - reassignments: HashMap::new(),
1836 + declarations: FxHashMap::default(),
1837 + reassignments: FxHashMap::default(),
1838 scope_stack: Vec::new(),
1839 dep_stack: Vec::new(),
1835 - deps: IndexMap::new(),
1840 + deps: IndexMap::default(),
1841 temporaries,
1842 temporaries_used_outside_scope,
1843 processed_instrs_in_optional,
@@ -2222,10 +2227,10 @@ fn handle_instruction(
2227 fn collect_dependencies(
2228 func: &HirFunction,
2229 env: &mut Environment,
2225 - used_outside_declaring_scope: &HashSet<DeclarationId>,
2226 - temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
2227 - processed_instrs_in_optional: &HashSet<ProcessedInstr>,
2228 -) -> IndexMap<ScopeId, Vec<ReactiveScopeDependency>> {
2230 + used_outside_declaring_scope: &FxHashSet<DeclarationId>,
2231 + temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
2232 + processed_instrs_in_optional: &FxHashSet<ProcessedInstr>,
2233 +) -> IndexMap<ScopeId, Vec<ReactiveScopeDependency>, FxBuildHasher> {
2234 let mut ctx = DependencyCollectionContext::new(
2235 used_outside_declaring_scope,
2236 temporaries,
compiler/crates/react_compiler_lowering/Cargo.toml
+1
@@ -8,4 +8,5 @@ react_compiler_ast = { path = "../react_compiler_ast" }
8 react_compiler_hir = { path = "../react_compiler_hir" }
9 react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
10 indexmap = "2"
11 +rustc-hash = "2"
12 serde_json = "1"
compiler/crates/react_compiler_lowering/src/build_hir.rs
+51 -29
@@ -1,7 +1,6 @@
1 -use std::collections::HashSet;
1 +use rustc_hash::{FxBuildHasher, FxHashSet};
2
3 -use indexmap::IndexMap;
4 -use indexmap::IndexSet;
3 +use indexmap::{IndexMap, IndexSet};
4 use react_compiler_ast::scope::BindingId;
5 use react_compiler_ast::scope::BindingKind as AstBindingKind;
6 use react_compiler_ast::scope::ScopeId;
@@ -2531,7 +2530,7 @@ fn collect_binding_names_from_pattern(
2530 pattern: &react_compiler_ast::patterns::PatternLike,
2531 scope_id: react_compiler_ast::scope::ScopeId,
2532 scope_info: &ScopeInfo,
2534 - out: &mut HashSet<BindingId>,
2533 + out: &mut FxHashSet<BindingId>,
2534 ) {
2535 use react_compiler_ast::patterns::PatternLike;
2536 match pattern {
@@ -2712,7 +2711,7 @@ fn lower_block_statement_inner(
2711 }
2712
2713 // Track which bindings have been "declared" (their declaration statement has been seen)
2715 - let mut declared: HashSet<BindingId> = HashSet::new();
2714 + let mut declared: FxHashSet<BindingId> = FxHashSet::default();
2715
2716 for body_stmt in &block.body {
2717 let stmt_start = statement_start(body_stmt).unwrap_or(0);
@@ -4320,8 +4319,11 @@ pub fn lower(
4319 let context_identifiers = find_context_identifiers(func, scope_info, env, &identifier_locs)?;
4320
4321 // For top-level functions, context is empty (no captured refs)
4323 - let context_map: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> =
4324 - IndexMap::new();
4322 + let context_map: IndexMap<
4323 + react_compiler_ast::scope::BindingId,
4324 + Option<SourceLocation>,
4325 + FxBuildHasher,
4326 + > = IndexMap::default();
4327
4328 let (hir_func, _used_names, _child_bindings) = lower_inner(
4329 params,
@@ -5592,7 +5594,7 @@ fn lower_function(
5594 } else {
5595 let parent = builder.function_scope();
5596 let scope_info = builder.scope_info();
5595 - let mapped: std::collections::HashSet<react_compiler_ast::scope::ScopeId> =
5597 + let mapped: rustc_hash::FxHashSet<react_compiler_ast::scope::ScopeId> =
5598 scope_info.node_id_to_scope.values().copied().collect();
5599 let param_names: Vec<String> = params
5600 .iter()
@@ -5604,7 +5606,7 @@ fn lower_function(
5606 }
5607 })
5608 .collect();
5607 - let mut descendants = std::collections::HashSet::new();
5609 + let mut descendants = rustc_hash::FxHashSet::default();
5610 descendants.insert(parent);
5611 let mut changed = true;
5612 while changed {
@@ -5671,7 +5673,11 @@ fn lower_function(
5673 ident_locs,
5674 ref_override.as_ref(),
5675 );
5674 - let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
5676 + let merged_context: IndexMap<
5677 + react_compiler_ast::scope::BindingId,
5678 + Option<SourceLocation>,
5679 + FxBuildHasher,
5680 + > = {
5681 let parent_context = builder.context().clone();
5682 let mut merged = parent_context;
5683 for (k, v) in captured_context {
@@ -5743,7 +5749,11 @@ fn lower_function_declaration(
5749 ident_locs,
5750 None,
5751 );
5746 - let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
5752 + let merged_context: IndexMap<
5753 + react_compiler_ast::scope::BindingId,
5754 + Option<SourceLocation>,
5755 + FxBuildHasher,
5756 + > = {
5757 let parent_context = builder.context().clone();
5758 let mut merged = parent_context;
5759 for (k, v) in captured_context {
@@ -5944,7 +5954,11 @@ fn lower_function_for_object_method(
5954 ident_locs,
5955 None,
5956 );
5947 - let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
5957 + let merged_context: IndexMap<
5958 + react_compiler_ast::scope::BindingId,
5959 + Option<SourceLocation>,
5960 + FxBuildHasher,
5961 + > = {
5962 let parent_context = builder.context().clone();
5963 let mut merged = parent_context;
5964 for (k, v) in captured_context {
@@ -5991,19 +6005,27 @@ fn lower_inner(
6005 loc: Option<SourceLocation>,
6006 scope_info: &ScopeInfo,
6007 env: &mut Environment,
5994 - parent_bindings: Option<IndexMap<react_compiler_ast::scope::BindingId, IdentifierId>>,
5995 - parent_used_names: Option<IndexMap<String, react_compiler_ast::scope::BindingId>>,
5996 - context_map: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>>,
6008 + parent_bindings: Option<
6009 + IndexMap<react_compiler_ast::scope::BindingId, IdentifierId, FxBuildHasher>,
6010 + >,
6011 + parent_used_names: Option<
6012 + IndexMap<String, react_compiler_ast::scope::BindingId, FxBuildHasher>,
6013 + >,
6014 + context_map: IndexMap<
6015 + react_compiler_ast::scope::BindingId,
6016 + Option<SourceLocation>,
6017 + FxBuildHasher,
6018 + >,
6019 function_scope: react_compiler_ast::scope::ScopeId,
6020 component_scope: react_compiler_ast::scope::ScopeId,
5999 - context_identifiers: &HashSet<react_compiler_ast::scope::BindingId>,
6021 + context_identifiers: &FxHashSet<react_compiler_ast::scope::BindingId>,
6022 is_top_level: bool,
6023 identifier_locs: &IdentifierLocIndex,
6024 ) -> Result<
6025 (
6026 HirFunction,
6005 - IndexMap<String, react_compiler_ast::scope::BindingId>,
6006 - IndexMap<react_compiler_ast::scope::BindingId, IdentifierId>,
6027 + IndexMap<String, react_compiler_ast::scope::BindingId, FxBuildHasher>,
6028 + IndexMap<react_compiler_ast::scope::BindingId, IdentifierId, FxBuildHasher>,
6029 ),
6030 CompilerError,
6031 > {
@@ -6776,22 +6798,22 @@ fn gather_captured_context(
6798 func_start: u32,
6799 func_end: u32,
6800 identifier_locs: &IdentifierLocIndex,
6779 - ref_node_ids_override: Option<&IndexSet<u32>>,
6780 -) -> IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> {
6801 + ref_node_ids_override: Option<&IndexSet<u32, FxBuildHasher>>,
6802 +) -> IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>, FxBuildHasher> {
6803 let parent_scope = scope_info.scopes[function_scope.0 as usize].parent;
6804 let pure_scopes = match parent_scope {
6805 Some(parent) => capture_scopes(scope_info, parent, component_scope),
6784 - None => IndexSet::new(),
6806 + None => IndexSet::default(),
6807 };
6808
6809 // Collect the earliest (lowest source position) reference location for each
6810 // captured binding. Using the minimum position makes the result independent of
6811 // ref_node_id_to_binding iteration order, matching the behavior the TS compiler
6812 // gets from Babel's position-ordered traversal.
6791 - let mut captured: std::collections::HashMap<
6813 + let mut captured: rustc_hash::FxHashMap<
6814 react_compiler_ast::scope::BindingId,
6815 (u32, Option<SourceLocation>), // (min_position, loc)
6794 - > = std::collections::HashMap::new();
6816 + > = rustc_hash::FxHashMap::default();
6817
6818 for (&ref_nid, &binding_id) in &scope_info.ref_node_id_to_binding {
6819 if let Some(allowed) = ref_node_ids_override {
@@ -6878,8 +6900,8 @@ fn capture_scopes(
6900 scope_info: &ScopeInfo,
6901 from: react_compiler_ast::scope::ScopeId,
6902 to: react_compiler_ast::scope::ScopeId,
6881 -) -> IndexSet<react_compiler_ast::scope::ScopeId> {
6882 - let mut result = IndexSet::new();
6903 +) -> IndexSet<react_compiler_ast::scope::ScopeId, FxBuildHasher> {
6904 + let mut result = IndexSet::default();
6905 let mut current = Some(from);
6906 while let Some(scope_id) = current {
6907 result.insert(scope_id);
@@ -7118,8 +7140,8 @@ fn collect_fbt_sub_tags_from_stmts(
7140 }
7141 }
7142
7121 -fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet<u32> {
7122 - let mut positions = IndexSet::new();
7143 +fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet<u32, FxBuildHasher> {
7144 + let mut positions = IndexSet::default();
7145 match body {
7146 FunctionBody::Block(block) => {
7147 for stmt in &block.body {
@@ -7135,7 +7157,7 @@ fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet<u32> {
7157
7158 fn collect_identifier_node_ids_from_stmt(
7159 stmt: &react_compiler_ast::statements::Statement,
7138 - positions: &mut IndexSet<u32>,
7160 + positions: &mut IndexSet<u32, FxBuildHasher>,
7161 ) {
7162 use react_compiler_ast::statements::Statement;
7163 match stmt {
@@ -7175,7 +7197,7 @@ fn collect_identifier_node_ids_from_stmt(
7197
7198 fn collect_identifier_node_ids_from_expr(
7199 expr: &react_compiler_ast::expressions::Expression,
7178 - positions: &mut IndexSet<u32>,
7200 + positions: &mut IndexSet<u32, FxBuildHasher>,
7201 ) {
7202 use react_compiler_ast::expressions::Expression;
7203 match expr {
compiler/crates/react_compiler_lowering/src/find_context_identifiers.rs
+6 -7
@@ -4,8 +4,7 @@
4 //! walking the AST with scope tracking to find variables that cross
5 //! function boundaries.
6
7 -use std::collections::HashMap;
8 -use std::collections::HashSet;
7 +use rustc_hash::{FxHashMap, FxHashSet};
8
9 use react_compiler_ast::expressions::*;
10 use react_compiler_ast::patterns::*;
@@ -35,7 +34,7 @@ struct ContextIdentifierVisitor<'a> {
34 /// Stack of inner function scopes encountered during traversal.
35 /// Empty when at the top level of the function being compiled.
36 function_stack: Vec<ScopeId>,
38 - binding_info: HashMap<BindingId, BindingInfo>,
37 + binding_info: FxHashMap<BindingId, BindingInfo>,
38 error: Option<CompilerError>,
39 }
40
@@ -313,8 +312,8 @@ fn is_captured_by_function(
312 /// ref_node_id_to_binding. These are entries where the reference's node_id
313 /// matches the binding's declaration_node_id — i.e., the "reference" is
314 /// actually the declaration itself.
316 -fn build_declaration_node_ids(scope_info: &ScopeInfo) -> HashSet<(BindingId, u32)> {
317 - let mut result = HashSet::new();
315 +fn build_declaration_node_ids(scope_info: &ScopeInfo) -> FxHashSet<(BindingId, u32)> {
316 + let mut result = FxHashSet::default();
317 for (&ref_nid, &binding_id) in &scope_info.ref_node_id_to_binding {
318 let binding = &scope_info.bindings[binding_id.0 as usize];
319 if binding.declaration_node_id == Some(ref_nid) {
@@ -338,7 +337,7 @@ pub fn find_context_identifiers(
337 scope_info: &ScopeInfo,
338 env: &mut Environment,
339 identifier_locs: &crate::identifier_loc_index::IdentifierLocIndex,
341 -) -> Result<HashSet<BindingId>, CompilerError> {
340 +) -> Result<FxHashSet<BindingId>, CompilerError> {
341 let func_scope = scope_info
342 .resolve_scope_for_node(func.node_id())
343 .unwrap_or(scope_info.program_scope);
@@ -347,7 +346,7 @@ pub fn find_context_identifiers(
346 scope_info,
347 env,
348 function_stack: Vec::new(),
350 - binding_info: HashMap::new(),
349 + binding_info: FxHashMap::default(),
350 error: None,
351 };
352 let mut walker = AstWalker::with_initial_scope(scope_info, func_scope);
compiler/crates/react_compiler_lowering/src/hir_builder.rs
+46 -40
@@ -1,5 +1,4 @@
1 -use indexmap::IndexMap;
2 -use indexmap::IndexSet;
1 +use indexmap::{IndexMap, IndexSet};
2 use react_compiler_ast::scope::BindingId;
3 use react_compiler_ast::scope::ImportBindingKind;
4 use react_compiler_ast::scope::ScopeId;
@@ -13,6 +12,7 @@ use react_compiler_hir::environment::Environment;
12 use react_compiler_hir::visitors::each_terminal_successor;
13 use react_compiler_hir::visitors::terminal_fallthrough;
14 use react_compiler_hir::*;
15 +use rustc_hash::FxBuildHasher;
16
17 use crate::identifier_loc_index::IdentifierLocIndex;
18
@@ -139,18 +139,18 @@ fn new_block(id: BlockId, kind: BlockKind) -> WipBlock {
139 // ---------------------------------------------------------------------------
140
141 pub struct HirBuilder<'a> {
142 - completed: IndexMap<BlockId, BasicBlock>,
142 + completed: IndexMap<BlockId, BasicBlock, FxBuildHasher>,
143 current: WipBlock,
144 entry: BlockId,
145 scopes: Vec<Scope>,
146 /// Context identifiers: variables captured from an outer scope.
147 /// Maps the outer scope's BindingId to the source location where it was referenced.
148 - context: IndexMap<BindingId, Option<SourceLocation>>,
148 + context: IndexMap<BindingId, Option<SourceLocation>, FxBuildHasher>,
149 /// Resolved bindings: maps a BindingId to the HIR IdentifierId created for it.
150 - bindings: IndexMap<BindingId, IdentifierId>,
150 + bindings: IndexMap<BindingId, IdentifierId, FxBuildHasher>,
151 /// Names already used by bindings, for collision avoidance.
152 /// Maps name string -> how many times it has been used (for appending _0, _1, ...).
153 - used_names: IndexMap<String, BindingId>,
153 + used_names: IndexMap<String, BindingId, FxBuildHasher>,
154 env: &'a mut Environment,
155 scope_info: &'a ScopeInfo,
156 exception_handler_stack: Vec<BlockId>,
@@ -166,10 +166,10 @@ pub struct HirBuilder<'a> {
166 /// Set of BindingIds for variables declared in scopes between component_scope
167 /// and any inner function scope, that are referenced from an inner function scope.
168 /// These need StoreContext/LoadContext instead of StoreLocal/LoadLocal.
169 - context_identifiers: std::collections::HashSet<BindingId>,
169 + context_identifiers: rustc_hash::FxHashSet<BindingId>,
170 /// Set of ScopeIds that have been matched to synthetic blocks/functions.
171 /// Prevents the same scope from being reused for different synthetic nodes.
172 - claimed_synthetic_scopes: std::collections::HashSet<ScopeId>,
172 + claimed_synthetic_scopes: rustc_hash::FxHashSet<ScopeId>,
173 /// Index mapping identifier byte offsets to source locations and JSX status.
174 identifier_locs: &'a IdentifierLocIndex,
175 }
@@ -192,17 +192,17 @@ impl<'a> HirBuilder<'a> {
192 scope_info: &'a ScopeInfo,
193 function_scope: ScopeId,
194 component_scope: ScopeId,
195 - context_identifiers: std::collections::HashSet<BindingId>,
196 - bindings: Option<IndexMap<BindingId, IdentifierId>>,
197 - context: Option<IndexMap<BindingId, Option<SourceLocation>>>,
195 + context_identifiers: rustc_hash::FxHashSet<BindingId>,
196 + bindings: Option<IndexMap<BindingId, IdentifierId, FxBuildHasher>>,
197 + context: Option<IndexMap<BindingId, Option<SourceLocation>, FxBuildHasher>>,
198 entry_block_kind: Option<BlockKind>,
199 - used_names: Option<IndexMap<String, BindingId>>,
199 + used_names: Option<IndexMap<String, BindingId, FxBuildHasher>>,
200 identifier_locs: &'a IdentifierLocIndex,
201 ) -> Self {
202 let entry = env.next_block_id();
203 let kind = entry_block_kind.unwrap_or(BlockKind::Block);
204 HirBuilder {
205 - completed: IndexMap::new(),
205 + completed: IndexMap::default(),
206 current: new_block(entry, kind),
207 entry,
208 scopes: Vec::new(),
@@ -217,7 +217,7 @@ impl<'a> HirBuilder<'a> {
217 function_scope,
218 component_scope,
219 context_identifiers,
220 - claimed_synthetic_scopes: std::collections::HashSet::new(),
220 + claimed_synthetic_scopes: rustc_hash::FxHashSet::default(),
221 identifier_locs,
222 }
223 }
@@ -290,12 +290,12 @@ impl<'a> HirBuilder<'a> {
290 }
291
292 /// Access the context map.
293 - pub fn context(&self) -> &IndexMap<BindingId, Option<SourceLocation>> {
293 + pub fn context(&self) -> &IndexMap<BindingId, Option<SourceLocation>, FxBuildHasher> {
294 &self.context
295 }
296
297 /// Access the pre-computed context identifiers set.
298 - pub fn context_identifiers(&self) -> &std::collections::HashSet<BindingId> {
298 + pub fn context_identifiers(&self) -> &rustc_hash::FxHashSet<BindingId> {
299 &self.context_identifiers
300 }
301
@@ -326,18 +326,21 @@ impl<'a> HirBuilder<'a> {
326 }
327
328 /// Access the bindings map.
329 - pub fn bindings(&self) -> &IndexMap<BindingId, IdentifierId> {
329 + pub fn bindings(&self) -> &IndexMap<BindingId, IdentifierId, FxBuildHasher> {
330 &self.bindings
331 }
332
333 /// Access the used names map.
334 - pub fn used_names(&self) -> &IndexMap<String, BindingId> {
334 + pub fn used_names(&self) -> &IndexMap<String, BindingId, FxBuildHasher> {
335 &self.used_names
336 }
337
338 /// Merge used names from a child builder back into this builder.
339 /// This ensures name deduplication works across function scopes.
340 - pub fn merge_used_names(&mut self, child_used_names: IndexMap<String, BindingId>) {
340 + pub fn merge_used_names(
341 + &mut self,
342 + child_used_names: IndexMap<String, BindingId, FxBuildHasher>,
343 + ) {
344 for (name, binding_id) in child_used_names {
345 self.used_names.entry(name).or_insert(binding_id);
346 }
@@ -346,7 +349,10 @@ impl<'a> HirBuilder<'a> {
349 /// Merge bindings (binding_id -> IdentifierId) from a child builder back into this builder.
350 /// This matches TS behavior where parent and child share the same #bindings map by reference,
351 /// so bindings resolved by the child are automatically visible to the parent.
349 - pub fn merge_bindings(&mut self, child_bindings: IndexMap<BindingId, IdentifierId>) {
352 + pub fn merge_bindings(
353 + &mut self,
354 + child_bindings: IndexMap<BindingId, IdentifierId, FxBuildHasher>,
355 + ) {
356 for (binding_id, identifier_id) in child_bindings {
357 self.bindings.entry(binding_id).or_insert(identifier_id);
358 }
@@ -403,7 +409,7 @@ impl<'a> HirBuilder<'a> {
409 id: block_id,
410 instructions: wip.instructions,
411 terminal,
406 - preds: IndexSet::new(),
412 + preds: IndexSet::default(),
413 phis: Vec::new(),
414 },
415 );
@@ -427,7 +433,7 @@ impl<'a> HirBuilder<'a> {
433 id: block_id,
434 instructions: wip.instructions,
435 terminal,
430 - preds: IndexSet::new(),
436 + preds: IndexSet::default(),
437 phis: Vec::new(),
438 },
439 );
@@ -451,7 +457,7 @@ impl<'a> HirBuilder<'a> {
457 id: block_id,
458 instructions: block.instructions,
459 terminal,
454 - preds: IndexSet::new(),
460 + preds: IndexSet::default(),
461 phis: Vec::new(),
462 },
463 );
@@ -471,7 +477,7 @@ impl<'a> HirBuilder<'a> {
477 id: completed_wip.id,
478 instructions: completed_wip.instructions,
479 terminal,
474 - preds: IndexSet::new(),
480 + preds: IndexSet::default(),
481 phis: Vec::new(),
482 },
483 );
@@ -493,7 +499,7 @@ impl<'a> HirBuilder<'a> {
499 id: completed_wip.id,
500 instructions: completed_wip.instructions,
501 terminal,
496 - preds: IndexSet::new(),
502 + preds: IndexSet::default(),
503 phis: Vec::new(),
504 },
505 );
@@ -769,8 +775,8 @@ impl<'a> HirBuilder<'a> {
775 (
776 HIR,
777 Vec<Instruction>,
772 - IndexMap<String, BindingId>,
773 - IndexMap<BindingId, IdentifierId>,
778 + IndexMap<String, BindingId, FxBuildHasher>,
779 + IndexMap<BindingId, IdentifierId, FxBuildHasher>,
780 ),
781 CompilerError,
782 > {
@@ -1150,19 +1156,19 @@ impl<'a> HirBuilder<'a> {
1156 pub fn get_reverse_postordered_blocks(
1157 hir: &HIR,
1158 _instructions: &[Instruction],
1153 -) -> IndexMap<BlockId, BasicBlock> {
1154 - let mut visited: IndexSet<BlockId> = IndexSet::new();
1155 - let mut used: IndexSet<BlockId> = IndexSet::new();
1156 - let mut used_fallthroughs: IndexSet<BlockId> = IndexSet::new();
1159 +) -> IndexMap<BlockId, BasicBlock, FxBuildHasher> {
1160 + let mut visited: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
1161 + let mut used: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
1162 + let mut used_fallthroughs: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
1163 let mut postorder: Vec<BlockId> = Vec::new();
1164
1165 fn visit(
1166 hir: &HIR,
1167 block_id: BlockId,
1168 is_used: bool,
1163 - visited: &mut IndexSet<BlockId>,
1164 - used: &mut IndexSet<BlockId>,
1165 - used_fallthroughs: &mut IndexSet<BlockId>,
1169 + visited: &mut IndexSet<BlockId, FxBuildHasher>,
1170 + used: &mut IndexSet<BlockId, FxBuildHasher>,
1171 + used_fallthroughs: &mut IndexSet<BlockId, FxBuildHasher>,
1172 postorder: &mut Vec<BlockId>,
1173 ) {
1174 let was_used = used.contains(&block_id);
@@ -1222,7 +1228,7 @@ pub fn get_reverse_postordered_blocks(
1228 &mut postorder,
1229 );
1230
1225 - let mut blocks = IndexMap::new();
1231 + let mut blocks = IndexMap::default();
1232 for block_id in postorder.into_iter().rev() {
1233 let block = hir.blocks.get(&block_id).unwrap();
1234 if used.contains(&block_id) {
@@ -1252,7 +1258,7 @@ pub fn get_reverse_postordered_blocks(
1258 /// For each block with a `For` terminal whose update block is not in the
1259 /// blocks map, set update to None.
1260 pub fn remove_unreachable_for_updates(hir: &mut HIR) {
1255 - let block_ids: IndexSet<BlockId> = hir.blocks.keys().copied().collect();
1261 + let block_ids: IndexSet<BlockId, FxBuildHasher> = hir.blocks.keys().copied().collect();
1262 for block in hir.blocks.values_mut() {
1263 if let Terminal::For { update, .. } = &mut block.terminal {
1264 if let Some(update_id) = *update {
@@ -1267,7 +1273,7 @@ pub fn remove_unreachable_for_updates(hir: &mut HIR) {
1273 /// For each block with a `DoWhile` terminal whose test block is not in
1274 /// the blocks map, replace the terminal with a Goto to the loop block.
1275 pub fn remove_dead_do_while_statements(hir: &mut HIR) {
1270 - let block_ids: IndexSet<BlockId> = hir.blocks.keys().copied().collect();
1276 + let block_ids: IndexSet<BlockId, FxBuildHasher> = hir.blocks.keys().copied().collect();
1277 for block in hir.blocks.values_mut() {
1278 let should_replace = if let Terminal::DoWhile { test, .. } = &block.terminal {
1279 !block_ids.contains(test)
@@ -1304,7 +1310,7 @@ pub fn remove_dead_do_while_statements(hir: &mut HIR) {
1310 /// Also cleans up the fallthrough block's predecessors if the handler
1311 /// was the only path to it.
1312 pub fn remove_unnecessary_try_catch(hir: &mut HIR) {
1307 - let block_ids: IndexSet<BlockId> = hir.blocks.keys().copied().collect();
1313 + let block_ids: IndexSet<BlockId, FxBuildHasher> = hir.blocks.keys().copied().collect();
1314
1315 // Collect the blocks that need replacement and their associated data
1316 let replacements: Vec<(BlockId, BlockId, BlockId, BlockId, Option<SourceLocation>)> = hir
@@ -1376,13 +1382,13 @@ pub fn mark_predecessors(hir: &mut HIR) {
1382 block.preds.clear();
1383 }
1384
1379 - let mut visited: IndexSet<BlockId> = IndexSet::new();
1385 + let mut visited: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
1386
1387 fn visit(
1388 hir: &mut HIR,
1389 block_id: BlockId,
1390 prev_block_id: Option<BlockId>,
1385 - visited: &mut IndexSet<BlockId>,
1391 + visited: &mut IndexSet<BlockId, FxBuildHasher>,
1392 ) {
1393 // Add predecessor
1394 if let Some(prev_id) = prev_block_id {
compiler/crates/react_compiler_lowering/src/identifier_loc_index.rs
+3 -3
@@ -5,7 +5,7 @@
5 //! lookups; each entry also stores `start` (byte offset) for range-containment
6 //! checks in `gather_captured_context`.
7
8 -use std::collections::HashMap;
8 +use rustc_hash::FxHashMap;
9
10 use react_compiler_ast::expressions::*;
11 use react_compiler_ast::jsx::JSXIdentifier;
@@ -45,7 +45,7 @@ pub struct IdentifierLocEntry {
45
46 /// Index mapping node_id → IdentifierLocEntry for all Identifier
47 /// and JSXIdentifier nodes in a function's AST.
48 -pub type IdentifierLocIndex = HashMap<u32, IdentifierLocEntry>;
48 +pub type IdentifierLocIndex = FxHashMap<u32, IdentifierLocEntry>;
49
50 struct IdentifierLocVisitor {
51 index: IdentifierLocIndex,
@@ -268,7 +268,7 @@ pub fn build_identifier_loc_index(
268 .unwrap_or(scope_info.program_scope);
269
270 let mut visitor = IdentifierLocVisitor {
271 - index: HashMap::new(),
271 + index: FxHashMap::default(),
272 current_opening_element_loc: None,
273 };
274 let mut walker = AstWalker::with_initial_scope(scope_info, func_scope);
compiler/crates/react_compiler_optimization/Cargo.toml
+1
@@ -9,3 +9,4 @@ react_compiler_hir = { path = "../react_compiler_hir" }
9 react_compiler_lowering = { path = "../react_compiler_lowering" }
10 react_compiler_ssa = { path = "../react_compiler_ssa" }
11 indexmap = "2"
12 +rustc-hash = "2"
compiler/crates/react_compiler_optimization/src/constant_propagation.rs
+4 -4
@@ -24,7 +24,7 @@
24 //!
25 //! Analogous to TS `Optimization/ConstantPropagation.ts`.
26
27 -use std::collections::HashMap;
27 +use rustc_hash::FxHashMap;
28
29 use react_compiler_diagnostics::JsString;
30 use react_compiler_hir::environment::Environment;
@@ -68,16 +68,16 @@ impl Constant {
68 }
69 }
70
71 -/// Map of known constant values. Uses HashMap (not IndexMap) since iteration
71 +/// Map of known constant values. Uses FxHashMap (not IndexMap) since iteration
72 /// order does not affect correctness — this map is only used for lookups.
73 -type Constants = HashMap<IdentifierId, Constant>;
73 +type Constants = FxHashMap<IdentifierId, Constant>;
74
75 // =============================================================================
76 // Public entry point
77 // =============================================================================
78
79 pub fn constant_propagation(func: &mut HirFunction, env: &mut Environment) {
80 - let mut constants: Constants = HashMap::new();
80 + let mut constants: Constants = FxHashMap::default();
81 constant_propagation_impl(func, env, &mut constants);
82 }
83
compiler/crates/react_compiler_optimization/src/dead_code_elimination.rs
+6 -6
@@ -11,7 +11,7 @@
11 //!
12 //! Ported from TypeScript `src/Optimization/DeadCodeElimination.ts`.
13
14 -use std::collections::HashSet;
14 +use rustc_hash::FxHashSet;
15
16 use react_compiler_hir::environment::{Environment, OutputMode};
17 use react_compiler_hir::object_shape::HookKind;
@@ -69,16 +69,16 @@ pub fn dead_code_elimination(func: &mut HirFunction, env: &Environment) {
69 /// State for tracking referenced identifiers during mark phase.
70 struct State {
71 /// SSA-specific usages (by IdentifierId)
72 - identifiers: HashSet<IdentifierId>,
72 + identifiers: FxHashSet<IdentifierId>,
73 /// Named variable usages (any version)
74 - named: HashSet<String>,
74 + named: FxHashSet<String>,
75 }
76
77 impl State {
78 fn new() -> Self {
79 State {
80 - identifiers: HashSet::new(),
81 - named: HashSet::new(),
80 + identifiers: FxHashSet::default(),
81 + named: FxHashSet::default(),
82 }
83 }
84
@@ -409,7 +409,7 @@ fn pruneable_value(value: &InstructionValue, state: &State, env: &Environment) -
409
410 /// Check if the CFG has any back edges (indicating loops).
411 fn has_back_edge(func: &HirFunction) -> bool {
412 - let mut visited: HashSet<BlockId> = HashSet::new();
412 + let mut visited: FxHashSet<BlockId> = FxHashSet::default();
413 for (block_id, block) in &func.body.blocks {
414 for pred_id in &block.preds {
415 if !visited.contains(pred_id) {
compiler/crates/react_compiler_optimization/src/drop_manual_memoization.rs
+17 -18
@@ -12,8 +12,7 @@
12 //!
13 //! Analogous to TS `Inference/DropManualMemoization.ts`.
14
15 -use std::collections::HashMap;
16 -use std::collections::HashSet;
15 +use rustc_hash::{FxHashMap, FxHashSet};
16
17 use react_compiler_diagnostics::CompilerDiagnostic;
18 use react_compiler_diagnostics::CompilerDiagnosticDetail;
@@ -58,17 +57,17 @@ struct ManualMemoCallee {
57
58 struct IdentifierSidemap {
59 /// Maps identifier id -> InstructionId of FunctionExpression instructions
61 - functions: HashSet<IdentifierId>,
60 + functions: FxHashSet<IdentifierId>,
61 /// Maps identifier id -> ManualMemoCallee for useMemo/useCallback callees
63 - manual_memos: HashMap<IdentifierId, ManualMemoCallee>,
62 + manual_memos: FxHashMap<IdentifierId, ManualMemoCallee>,
63 /// Set of identifier ids that loaded 'React' global
65 - react: HashSet<IdentifierId>,
64 + react: FxHashSet<IdentifierId>,
65 /// Maps identifier id -> deps list info for array expressions
67 - maybe_deps_lists: HashMap<IdentifierId, MaybeDepsListInfo>,
66 + maybe_deps_lists: FxHashMap<IdentifierId, MaybeDepsListInfo>,
67 /// Maps identifier id -> ManualMemoDependency for dependency tracking
69 - maybe_deps: HashMap<IdentifierId, ManualMemoDependency>,
68 + maybe_deps: FxHashMap<IdentifierId, ManualMemoDependency>,
69 /// Set of identifier ids that are results of optional chains
71 - optionals: HashSet<IdentifierId>,
70 + optionals: FxHashSet<IdentifierId>,
71 }
72
73 #[derive(Debug, Clone)]
@@ -99,11 +98,11 @@ pub fn drop_manual_memoization(
98
99 let optionals = find_optional_places(func)?;
100 let mut sidemap = IdentifierSidemap {
102 - functions: HashSet::new(),
103 - manual_memos: HashMap::new(),
104 - react: HashSet::new(),
105 - maybe_deps: HashMap::new(),
106 - maybe_deps_lists: HashMap::new(),
101 + functions: FxHashSet::default(),
102 + manual_memos: FxHashMap::default(),
103 + react: FxHashSet::default(),
104 + maybe_deps: FxHashMap::default(),
105 + maybe_deps_lists: FxHashMap::default(),
106 optionals,
107 };
108 let mut next_manual_memo_id: u32 = 0;
@@ -113,7 +112,7 @@ pub fn drop_manual_memoization(
112 // - (if validation is enabled) collect manual memoization markers
113 //
114 // queued_inserts maps InstructionId -> new Instruction to insert after that instruction
116 - let mut queued_inserts: HashMap<InstructionId, Instruction> = HashMap::new();
115 + let mut queued_inserts: FxHashMap<InstructionId, Instruction> = FxHashMap::default();
116
117 // Collect all block instruction lists up front to avoid borrowing func immutably
118 // while needing to mutate it
@@ -202,7 +201,7 @@ fn process_manual_memo_call(
201 sidemap: &mut IdentifierSidemap,
202 is_validation_enabled: bool,
203 next_manual_memo_id: &mut u32,
205 - queued_inserts: &mut HashMap<InstructionId, Instruction>,
204 + queued_inserts: &mut FxHashMap<InstructionId, Instruction>,
205 ) {
206 let instr = &func.instructions[instr_id.0 as usize];
207
@@ -386,7 +385,7 @@ fn collect_temporaries(
385 /// Returns the variable + property reads represented by the instruction value.
386 pub fn collect_maybe_memo_dependencies(
387 value: &InstructionValue,
389 - maybe_deps: &HashMap<IdentifierId, ManualMemoDependency>,
388 + maybe_deps: &FxHashMap<IdentifierId, ManualMemoDependency>,
389 optional: bool,
390 env: &Environment,
391 ) -> Option<ManualMemoDependency> {
@@ -649,10 +648,10 @@ fn extract_manual_memoization_args(
648 // findOptionalPlaces
649 // =============================================================================
650
652 -fn find_optional_places(func: &HirFunction) -> Result<HashSet<IdentifierId>, CompilerDiagnostic> {
651 +fn find_optional_places(func: &HirFunction) -> Result<FxHashSet<IdentifierId>, CompilerDiagnostic> {
652 use react_compiler_hir::Terminal;
653
655 - let mut optionals = HashSet::new();
654 + let mut optionals = FxHashSet::default();
655 for block in func.body.blocks.values() {
656 if let Terminal::Optional {
657 optional: true,
compiler/crates/react_compiler_optimization/src/inline_iifes.rs
+5 -4
@@ -40,7 +40,8 @@
40 //!
41 //! Analogous to TS `Inference/InlineImmediatelyInvokedFunctionExpressions.ts`.
42
43 -use std::collections::{HashMap, HashSet};
43 +use indexmap::IndexSet;
44 +use rustc_hash::{FxHashMap, FxHashSet};
45
46 use react_compiler_hir::environment::Environment;
47 use react_compiler_hir::visitors;
@@ -62,9 +63,9 @@ pub fn inline_immediately_invoked_function_expressions(
63 env: &mut Environment,
64 ) {
65 // Track all function expressions that are assigned to a temporary
65 - let mut functions: HashMap<IdentifierId, FunctionId> = HashMap::new();
66 + let mut functions: FxHashMap<IdentifierId, FunctionId> = FxHashMap::default();
67 // Functions that are inlined (by identifier id of the callee)
67 - let mut inlined_functions: HashSet<IdentifierId> = HashSet::new();
68 + let mut inlined_functions: FxHashSet<IdentifierId> = FxHashSet::default();
69
70 // Iterate the *existing* blocks from the outer component to find IIFEs
71 // and inline them. During iteration we will modify `func` (by inlining the CFG
@@ -140,7 +141,7 @@ pub fn inline_immediately_invoked_function_expressions(
141 instructions: continuation_instructions,
142 kind: block_kind,
143 phis: Vec::new(),
143 - preds: indexmap::IndexSet::new(),
144 + preds: IndexSet::default(),
145 terminal: continuation_terminal,
146 };
147 func.body
compiler/crates/react_compiler_optimization/src/merge_consecutive_blocks.rs
+4 -4
@@ -13,7 +13,7 @@
13 //!
14 //! Analogous to TS `HIR/MergeConsecutiveBlocks.ts`.
15
16 -use std::collections::{HashMap, HashSet};
16 +use rustc_hash::{FxHashMap, FxHashSet};
17
18 use react_compiler_hir::visitors;
19 use react_compiler_hir::{
@@ -53,7 +53,7 @@ pub fn merge_consecutive_blocks(func: &mut HirFunction, functions: &mut [HirFunc
53 }
54
55 // Build fallthrough set
56 - let mut fallthrough_blocks: HashSet<BlockId> = HashSet::new();
56 + let mut fallthrough_blocks: FxHashSet<BlockId> = FxHashSet::default();
57 for block in func.body.blocks.values() {
58 if let Some(ft) = visitors::terminal_fallthrough(&block.terminal) {
59 fallthrough_blocks.insert(ft);
@@ -186,13 +186,13 @@ pub fn merge_consecutive_blocks(func: &mut HirFunction, functions: &mut [HirFunc
186
187 /// Tracks which blocks have been merged and into which target.
188 struct MergedBlocks {
189 - map: HashMap<BlockId, BlockId>,
189 + map: FxHashMap<BlockId, BlockId>,
190 }
191
192 impl MergedBlocks {
193 fn new() -> Self {
194 Self {
195 - map: HashMap::new(),
195 + map: FxHashMap::default(),
196 }
197 }
198
compiler/crates/react_compiler_optimization/src/name_anonymous_functions.rs
+7 -7
@@ -11,7 +11,7 @@
11 //!
12 //! Conditional on `env.config.enable_name_anonymous_functions`.
13
14 -use std::collections::HashMap;
14 +use rustc_hash::FxHashMap;
15
16 use react_compiler_hir::environment::Environment;
17 use react_compiler_hir::object_shape::HookKind;
@@ -63,7 +63,7 @@ pub fn name_anonymous_functions(func: &mut HirFunction, env: &mut Environment) {
63 if updates.is_empty() {
64 return;
65 }
66 - let update_map: HashMap<FunctionId, &String> =
66 + let update_map: FxHashMap<FunctionId, &String> =
67 updates.iter().map(|(fid, name)| (*fid, name)).collect();
68
69 // Apply name updates to the inner HirFunction in the arena
@@ -86,7 +86,7 @@ pub fn name_anonymous_functions(func: &mut HirFunction, env: &mut Environment) {
86 /// Apply name hints to FunctionExpression instruction values.
87 fn apply_name_hints_to_instructions(
88 instructions: &mut [Instruction],
89 - update_map: &HashMap<FunctionId, &String>,
89 + update_map: &FxHashMap<FunctionId, &String>,
90 ) {
91 for instr in instructions.iter_mut() {
92 if let InstructionValue::FunctionExpression {
@@ -117,9 +117,9 @@ struct Node {
117
118 fn name_anonymous_functions_impl(func: &HirFunction, env: &Environment) -> Vec<Node> {
119 // Functions that we track to generate names for
120 - let mut functions: HashMap<IdentifierId, usize> = HashMap::new();
120 + let mut functions: FxHashMap<IdentifierId, usize> = FxHashMap::default();
121 // Tracks temporaries that read from variables/globals/properties
122 - let mut names: HashMap<IdentifierId, String> = HashMap::new();
122 + let mut names: FxHashMap<IdentifierId, String> = FxHashMap::default();
123 // Tracks all function nodes
124 let mut nodes: Vec<Node> = Vec::new();
125
@@ -256,8 +256,8 @@ fn handle_call(
256 _func: &HirFunction,
257 callee_id: IdentifierId,
258 args: &[PlaceOrSpread],
259 - functions: &mut HashMap<IdentifierId, usize>,
260 - names: &HashMap<IdentifierId, String>,
259 + functions: &mut FxHashMap<IdentifierId, usize>,
260 + names: &FxHashMap<IdentifierId, String>,
261 nodes: &mut Vec<Node>,
262 ) {
263 let callee_ident = &env.identifiers[callee_id.0 as usize];
compiler/crates/react_compiler_optimization/src/optimize_for_ssr.rs
+2 -2
@@ -17,7 +17,7 @@
17 //!
18 //! Ported from TypeScript `src/Optimization/OptimizeForSSR.ts`.
19
20 -use std::collections::HashMap;
20 +use rustc_hash::FxHashMap;
21
22 use react_compiler_hir::environment::Environment;
23 use react_compiler_hir::object_shape::HookKind;
@@ -43,7 +43,7 @@ pub fn optimize_for_ssr(func: &mut HirFunction, env: &Environment) {
43 // Any use of the hook return other than the expected destructuring pattern
44 // prevents inlining (we delete from inlined_state if we see the identifier used
45 // as an operand elsewhere).
46 - let mut inlined_state: HashMap<IdentifierId, InlinedStateReplacement> = HashMap::new();
46 + let mut inlined_state: FxHashMap<IdentifierId, InlinedStateReplacement> = FxHashMap::default();
47
48 for (_block_id, block) in &func.body.blocks {
49 for &instr_id in &block.instructions {
compiler/crates/react_compiler_optimization/src/outline_functions.rs
+2 -2
@@ -11,7 +11,7 @@
11 //!
12 //! Conditional on `env.config.enable_function_outlining`.
13
14 -use std::collections::HashSet;
14 +use rustc_hash::FxHashSet;
15
16 use react_compiler_hir::environment::Environment;
17 use react_compiler_hir::{
@@ -25,7 +25,7 @@ use react_compiler_ssa::enter_ssa::placeholder_function;
25 pub fn outline_functions(
26 func: &mut HirFunction,
27 env: &mut Environment,
28 - fbt_operands: &HashSet<IdentifierId>,
28 + fbt_operands: &FxHashSet<IdentifierId>,
29 ) {
30 // Collect per-instruction actions to maintain depth-first name allocation order.
31 // Each entry: (instr index, function_id to recurse into, should_outline)
compiler/crates/react_compiler_optimization/src/outline_jsx.rs
+19 -19
@@ -8,9 +8,9 @@
8 //! Outlines JSX expressions in callbacks into separate component functions.
9 //! This pass is conditional on `env.config.enable_jsx_outlining` (defaults to false).
10
11 -use std::collections::{HashMap, HashSet};
11 +use indexmap::{IndexMap, IndexSet};
12 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
13
13 -use indexmap::IndexMap;
14 use react_compiler_hir::environment::Environment;
15 use react_compiler_hir::{
16 BasicBlock, BlockId, BlockKind, EvaluationOrder, FunctionId, HIR, HirFunction, IdentifierId,
@@ -58,7 +58,7 @@ fn outline_jsx_impl(
58 outlined_fns: &mut Vec<HirFunction>,
59 ) {
60 // Collect LoadGlobal instructions (tag -> instr)
61 - let mut globals: HashMap<IdentifierId, usize> = HashMap::new(); // id -> instr_idx
61 + let mut globals: FxHashMap<IdentifierId, usize> = FxHashMap::default(); // id -> instr_idx
62
63 // Process each block
64 let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();
@@ -66,9 +66,9 @@ fn outline_jsx_impl(
66 let block = &func.body.blocks[block_id];
67 let instr_ids = block.instructions.clone();
68
69 - let mut rewrite_instr: HashMap<EvaluationOrder, Vec<Instruction>> = HashMap::new();
69 + let mut rewrite_instr: FxHashMap<EvaluationOrder, Vec<Instruction>> = FxHashMap::default();
70 let mut jsx_group: Vec<JsxInstrInfo> = Vec::new();
71 - let mut children_ids: HashSet<IdentifierId> = HashSet::new();
71 + let mut children_ids: FxHashSet<IdentifierId> = FxHashSet::default();
72
73 // First pass: collect all instruction info without borrowing func mutably
74 enum InstrAction {
@@ -211,8 +211,8 @@ fn process_and_outline_jsx(
211 func: &mut HirFunction,
212 env: &mut Environment,
213 jsx_group: &mut Vec<JsxInstrInfo>,
214 - globals: &HashMap<IdentifierId, usize>,
215 - rewrite_instr: &mut HashMap<EvaluationOrder, Vec<Instruction>>,
214 + globals: &FxHashMap<IdentifierId, usize>,
215 + rewrite_instr: &mut FxHashMap<EvaluationOrder, Vec<Instruction>>,
216 outlined_fns: &mut Vec<HirFunction>,
217 ) {
218 if jsx_group.len() <= 1 {
@@ -237,7 +237,7 @@ fn process_jsx_group(
237 func: &HirFunction,
238 env: &mut Environment,
239 jsx_group: &[JsxInstrInfo],
240 - globals: &HashMap<IdentifierId, usize>,
240 + globals: &FxHashMap<IdentifierId, usize>,
241 ) -> Option<OutlinedResult> {
242 // Only outline in callbacks, not top-level components
243 if func.fn_type == ReactFunctionType::Component {
@@ -266,9 +266,9 @@ fn collect_props(
266 jsx_group: &[JsxInstrInfo],
267 ) -> Option<Vec<OutlinedJsxAttribute>> {
268 let mut id_counter = 1u32;
269 - let mut seen: HashSet<String> = HashSet::new();
269 + let mut seen: FxHashSet<String> = FxHashSet::default();
270 let mut attributes = Vec::new();
271 - let jsx_ids: HashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
271 + let jsx_ids: FxHashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
272
273 let mut generate_name = |old_name: &str, _env: &mut Environment| -> String {
274 let mut new_name = old_name.to_string();
@@ -402,7 +402,7 @@ fn emit_outlined_fn(
402 env: &mut Environment,
403 jsx_group: &[JsxInstrInfo],
404 old_props: &[OutlinedJsxAttribute],
405 - globals: &HashMap<IdentifierId, usize>,
405 + globals: &FxHashMap<IdentifierId, usize>,
406 ) -> Option<HirFunction> {
407 let old_to_new_props = create_old_to_new_props_mapping(env, old_props);
408
@@ -458,7 +458,7 @@ fn emit_outlined_fn(
458 kind: BlockKind::Block,
459 id: BlockId(0),
460 instructions: instr_ids,
461 - preds: indexmap::IndexSet::new(),
461 + preds: IndexSet::default(),
462 terminal: Terminal::Return {
463 value: last_lvalue,
464 return_variant: ReturnVariant::Explicit,
@@ -469,7 +469,7 @@ fn emit_outlined_fn(
469 phis: Vec::new(),
470 };
471
472 - let mut blocks = IndexMap::new();
472 + let mut blocks = IndexMap::default();
473 blocks.insert(BlockId(0), block);
474
475 let outlined_fn = HirFunction {
@@ -498,7 +498,7 @@ fn emit_outlined_fn(
498 fn emit_load_globals(
499 func: &HirFunction,
500 jsx_group: &[JsxInstrInfo],
501 - globals: &HashMap<IdentifierId, usize>,
501 + globals: &FxHashMap<IdentifierId, usize>,
502 ) -> Option<Vec<Instruction>> {
503 let mut instructions = Vec::new();
504 for info in jsx_group {
@@ -516,9 +516,9 @@ fn emit_load_globals(
516 fn emit_updated_jsx(
517 func: &HirFunction,
518 jsx_group: &[JsxInstrInfo],
519 - old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute>,
519 + old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute, FxBuildHasher>,
520 ) -> Vec<Instruction> {
521 - let jsx_ids: HashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
521 + let jsx_ids: FxHashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
522 let mut new_instrs = Vec::new();
523
524 for info in jsx_group {
@@ -594,8 +594,8 @@ fn emit_updated_jsx(
594 fn create_old_to_new_props_mapping(
595 env: &mut Environment,
596 old_props: &[OutlinedJsxAttribute],
597 -) -> IndexMap<IdentifierId, OutlinedJsxAttribute> {
598 - let mut old_to_new = IndexMap::new();
597 +) -> IndexMap<IdentifierId, OutlinedJsxAttribute, FxBuildHasher> {
598 + let mut old_to_new = IndexMap::default();
599
600 for old_prop in old_props {
601 if old_prop.original_name == "key" {
@@ -629,7 +629,7 @@ fn create_old_to_new_props_mapping(
629 fn emit_destructure_props(
630 env: &mut Environment,
631 props_obj: &Place,
632 - old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute>,
632 + old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute, FxBuildHasher>,
633 ) -> Instruction {
634 let mut properties = Vec::new();
635 for prop in old_to_new_props.values() {
compiler/crates/react_compiler_optimization/src/prune_maybe_throws.rs
+3 -3
@@ -10,7 +10,7 @@
10 //!
11 //! Analogous to TS `Optimization/PruneMaybeThrows.ts`.
12
13 -use std::collections::HashMap;
13 +use rustc_hash::FxHashMap;
14
15 use react_compiler_diagnostics::{
16 CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, GENERATED_SOURCE,
@@ -86,8 +86,8 @@ pub fn prune_maybe_throws(
86 Ok(())
87 }
88
89 -fn prune_maybe_throws_impl(func: &mut HirFunction) -> Option<HashMap<BlockId, BlockId>> {
90 - let mut terminal_mapping: HashMap<BlockId, BlockId> = HashMap::new();
89 +fn prune_maybe_throws_impl(func: &mut HirFunction) -> Option<FxHashMap<BlockId, BlockId>> {
90 + let mut terminal_mapping: FxHashMap<BlockId, BlockId> = FxHashMap::default();
91 let instructions = &func.instructions;
92
93 for block in func.body.blocks.values_mut() {
compiler/crates/react_compiler_optimization/src/prune_unused_labels_hir.rs
+2 -2
@@ -12,7 +12,7 @@
12 //! Analogous to TS `PruneUnusedLabelsHIR.ts`.
13
14 use react_compiler_hir::{BlockId, BlockKind, GotoVariant, HirFunction, Terminal};
15 -use std::collections::HashMap;
15 +use rustc_hash::FxHashMap;
16
17 pub fn prune_unused_labels_hir(func: &mut HirFunction) {
18 // Phase 1: Identify label terminals whose body block immediately breaks
@@ -45,7 +45,7 @@ pub fn prune_unused_labels_hir(func: &mut HirFunction) {
45 }
46
47 // Phase 2: Apply merges
48 - let mut rewrites: HashMap<BlockId, BlockId> = HashMap::new();
48 + let mut rewrites: FxHashMap<BlockId, BlockId> = FxHashMap::default();
49
50 for (original_label_id, next_id, fallthrough_id) in &merged {
51 let label_id = rewrites
compiler/crates/react_compiler_reactive_scopes/Cargo.toml
+1
@@ -8,5 +8,6 @@ react_compiler_ast = { path = "../react_compiler_ast" }
8 react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
9 react_compiler_hir = { path = "../react_compiler_hir" }
10 indexmap = "2"
11 +rustc-hash = "2"
12 serde_json = "1"
13 hmac-sha256 = "1"
compiler/crates/react_compiler_reactive_scopes/src/assert_scope_instructions_within_scopes.rs
+7 -7
@@ -8,7 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/AssertScopeInstructionsWithinScope.ts`.
10
11 -use std::collections::HashSet;
11 +use rustc_hash::FxHashSet;
12
13 use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
14 use react_compiler_hir::environment::Environment;
@@ -25,7 +25,7 @@ pub fn assert_scope_instructions_within_scopes(
25 env: &Environment,
26 ) -> Result<(), CompilerDiagnostic> {
27 // Pass 1: Collect all scope IDs
28 - let mut existing_scopes: HashSet<ScopeId> = HashSet::new();
28 + let mut existing_scopes: FxHashSet<ScopeId> = FxHashSet::default();
29 let find_visitor = FindAllScopesVisitor { env };
30 visit_reactive_function(func, &find_visitor, &mut existing_scopes);
31
@@ -33,7 +33,7 @@ pub fn assert_scope_instructions_within_scopes(
33 let check_visitor = CheckInstructionsAgainstScopesVisitor { env };
34 let mut check_state = CheckState {
35 existing_scopes,
36 - active_scopes: HashSet::new(),
36 + active_scopes: FxHashSet::default(),
37 error: None,
38 };
39 visit_reactive_function(func, &check_visitor, &mut check_state);
@@ -52,13 +52,13 @@ struct FindAllScopesVisitor<'a> {
52 }
53
54 impl<'a> ReactiveFunctionVisitor for FindAllScopesVisitor<'a> {
55 - type State = HashSet<ScopeId>;
55 + type State = FxHashSet<ScopeId>;
56
57 fn env(&self) -> &Environment {
58 self.env
59 }
60
61 - fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut HashSet<ScopeId>) {
61 + fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut FxHashSet<ScopeId>) {
62 self.traverse_scope(scope, state);
63 state.insert(scope.scope);
64 }
@@ -69,8 +69,8 @@ impl<'a> ReactiveFunctionVisitor for FindAllScopesVisitor<'a> {
69 // =============================================================================
70
71 struct CheckState {
72 - existing_scopes: HashSet<ScopeId>,
73 - active_scopes: HashSet<ScopeId>,
72 + existing_scopes: FxHashSet<ScopeId>,
73 + active_scopes: FxHashSet<ScopeId>,
74 error: Option<CompilerDiagnostic>,
75 }
76
compiler/crates/react_compiler_reactive_scopes/src/assert_well_formed_break_targets.rs
+8 -4
@@ -7,7 +7,7 @@
7 //!
8 //! Corresponds to `src/ReactiveScopes/AssertWellFormedBreakTargets.ts`.
9
10 -use std::collections::HashSet;
10 +use rustc_hash::FxHashSet;
11
12 use react_compiler_hir::{
13 BlockId, ReactiveFunction, ReactiveTerminal, ReactiveTerminalStatement,
@@ -19,7 +19,7 @@ use crate::visitors::{ReactiveFunctionVisitor, visit_reactive_function};
19 /// Assert that all break/continue targets reference existent labels.
20 pub fn assert_well_formed_break_targets(func: &ReactiveFunction, env: &Environment) {
21 let visitor = Visitor { env };
22 - let mut state: HashSet<BlockId> = HashSet::new();
22 + let mut state: FxHashSet<BlockId> = FxHashSet::default();
23 visit_reactive_function(func, &visitor, &mut state);
24 }
25
@@ -28,13 +28,17 @@ struct Visitor<'a> {
28 }
29
30 impl<'a> ReactiveFunctionVisitor for Visitor<'a> {
31 - type State = HashSet<BlockId>;
31 + type State = FxHashSet<BlockId>;
32
33 fn env(&self) -> &Environment {
34 self.env
35 }
36
37 - fn visit_terminal(&self, stmt: &ReactiveTerminalStatement, seen_labels: &mut HashSet<BlockId>) {
37 + fn visit_terminal(
38 + &self,
39 + stmt: &ReactiveTerminalStatement,
40 + seen_labels: &mut FxHashSet<BlockId>,
41 + ) {
42 if let Some(label) = &stmt.label {
43 seen_labels.insert(label.id);
44 }
compiler/crates/react_compiler_reactive_scopes/src/build_reactive_function.rs
+9 -9
@@ -7,7 +7,7 @@
7 //!
8 //! Corresponds to `src/ReactiveScopes/BuildReactiveFunction.ts`.
9
10 -use std::collections::HashSet;
10 +use rustc_hash::FxHashSet;
11
12 use react_compiler_diagnostics::{
13 CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -108,10 +108,10 @@ impl ControlFlowTarget {
108 struct Context<'a> {
109 ir: &'a HirFunction,
110 next_schedule_id: u32,
111 - emitted: HashSet<BlockId>,
112 - scope_fallthroughs: HashSet<BlockId>,
113 - scheduled: HashSet<BlockId>,
114 - catch_handlers: HashSet<BlockId>,
111 + emitted: FxHashSet<BlockId>,
112 + scope_fallthroughs: FxHashSet<BlockId>,
113 + scheduled: FxHashSet<BlockId>,
114 + catch_handlers: FxHashSet<BlockId>,
115 control_flow_stack: Vec<ControlFlowTarget>,
116 }
117
@@ -120,10 +120,10 @@ impl<'a> Context<'a> {
120 Self {
121 ir,
122 next_schedule_id: 0,
123 - emitted: HashSet::new(),
124 - scope_fallthroughs: HashSet::new(),
125 - scheduled: HashSet::new(),
126 - catch_handlers: HashSet::new(),
123 + emitted: FxHashSet::default(),
124 + scope_fallthroughs: FxHashSet::default(),
125 + scheduled: FxHashSet::default(),
126 + catch_handlers: FxHashSet::default(),
127 control_flow_stack: Vec::new(),
128 }
129 }
compiler/crates/react_compiler_reactive_scopes/src/codegen_reactive_function.rs
+17 -18
@@ -10,8 +10,7 @@
10 //!
11 //! Corresponds to `src/ReactiveScopes/CodegenReactiveFunction.ts` in the TS compiler.
12
13 -use std::collections::HashMap;
14 -use std::collections::HashSet;
13 +use rustc_hash::{FxHashMap, FxHashSet};
14
15 use react_compiler_ast::common::BaseNode;
16 use react_compiler_ast::common::Position as AstPosition;
@@ -195,8 +194,8 @@ fn source_file_hash(code: &str) -> String {
194 pub fn codegen_function(
195 func: &ReactiveFunction,
196 env: &mut Environment,
198 - unique_identifiers: HashSet<String>,
199 - fbt_operands: HashSet<IdentifierId>,
197 + unique_identifiers: FxHashSet<String>,
198 + fbt_operands: FxHashSet<IdentifierId>,
199 ) -> Result<CodegenFunction, CompilerError> {
200 let fn_name = func.id.as_deref().unwrap_or("[[ anonymous ]]");
201 let mut cx = Context::new(env, fn_name.to_string(), unique_identifiers, fbt_operands);
@@ -556,7 +555,7 @@ pub fn codegen_function(
555 // Context
556 // =============================================================================
557
559 -type Temporaries = HashMap<DeclarationId, Option<ExpressionOrJsxText>>;
558 +type Temporaries = FxHashMap<DeclarationId, Option<ExpressionOrJsxText>>;
559
560 #[derive(Clone)]
561 enum ExpressionOrJsxText {
@@ -569,37 +568,37 @@ struct Context<'env> {
568 #[allow(dead_code)]
569 fn_name: String,
570 next_cache_index: u32,
572 - declarations: HashSet<DeclarationId>,
571 + declarations: FxHashSet<DeclarationId>,
572 temp: Temporaries,
574 - object_methods: HashMap<
573 + object_methods: FxHashMap<
574 IdentifierId,
575 (
576 InstructionValue,
577 Option<react_compiler_diagnostics::SourceLocation>,
578 ),
579 >,
581 - unique_identifiers: HashSet<String>,
582 - fbt_operands: HashSet<IdentifierId>,
583 - synthesized_names: HashMap<String, String>,
580 + unique_identifiers: FxHashSet<String>,
581 + fbt_operands: FxHashSet<IdentifierId>,
582 + synthesized_names: FxHashMap<String, String>,
583 }
584
585 impl<'env> Context<'env> {
586 fn new(
587 env: &'env mut Environment,
588 fn_name: String,
590 - unique_identifiers: HashSet<String>,
591 - fbt_operands: HashSet<IdentifierId>,
589 + unique_identifiers: FxHashSet<String>,
590 + fbt_operands: FxHashSet<IdentifierId>,
591 ) -> Self {
592 Context {
593 env,
594 fn_name,
595 next_cache_index: 0,
597 - declarations: HashSet::new(),
598 - temp: HashMap::new(),
599 - object_methods: HashMap::new(),
596 + declarations: FxHashSet::default(),
597 + temp: FxHashMap::default(),
598 + object_methods: FxHashMap::default(),
599 unique_identifiers,
600 fbt_operands,
602 - synthesized_names: HashMap::new(),
601 + synthesized_names: FxHashMap::default(),
602 }
603 }
604
@@ -4163,7 +4162,7 @@ fn create_function_body_hook_guard(
4162 fn apply_renames_to_json(
4163 value: &mut serde_json::Value,
4164 renames: &[react_compiler_hir::environment::BindingRename],
4166 - reference_node_ids: &std::collections::HashSet<u32>,
4165 + reference_node_ids: &rustc_hash::FxHashSet<u32>,
4166 ) {
4167 apply_renames_to_json_inner(value, renames, reference_node_ids, false);
4168 }
@@ -4171,7 +4170,7 @@ fn apply_renames_to_json(
4170 fn apply_renames_to_json_inner(
4171 value: &mut serde_json::Value,
4172 renames: &[react_compiler_hir::environment::BindingRename],
4174 - reference_node_ids: &std::collections::HashSet<u32>,
4173 + reference_node_ids: &rustc_hash::FxHashSet<u32>,
4174 is_property_key: bool,
4175 ) {
4176 if renames.is_empty() {
compiler/crates/react_compiler_reactive_scopes/src/extract_scope_declarations_from_destructuring.rs
+4 -4
@@ -8,7 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/ExtractScopeDeclarationsFromDestructuring.ts`.
10
11 -use std::collections::HashSet;
11 +use rustc_hash::FxHashSet;
12
13 use react_compiler_hir::{
14 DeclarationId, IdentifierId, IdentifierName, InstructionKind, InstructionValue, LValue,
@@ -29,7 +29,7 @@ pub fn extract_scope_declarations_from_destructuring(
29 func: &mut ReactiveFunction,
30 env: &mut Environment,
31 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
32 - let mut declared: HashSet<DeclarationId> = HashSet::new();
32 + let mut declared: FxHashSet<DeclarationId> = FxHashSet::default();
33 for param in &func.params {
34 let place = match param {
35 ParamPattern::Place(p) => p,
@@ -44,7 +44,7 @@ pub fn extract_scope_declarations_from_destructuring(
44 }
45
46 struct ExtractState {
47 - declared: HashSet<DeclarationId>,
47 + declared: FxHashSet<DeclarationId>,
48 }
49
50 struct Transform<'a> {
@@ -94,7 +94,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> {
94 }) = &mut instruction.value
95 {
96 // Check if this is a mixed destructuring (some declared, some not)
97 - let mut reassigned: HashSet<IdentifierId> = HashSet::new();
97 + let mut reassigned: FxHashSet<IdentifierId> = FxHashSet::default();
98 let mut has_declaration = false;
99
100 for place in visitors::each_pattern_operand(&lvalue.pattern) {
compiler/crates/react_compiler_reactive_scopes/src/merge_reactive_scopes_that_invalidate_together.rs
+13 -13
@@ -8,7 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts`.
10
11 -use std::collections::{HashMap, HashSet};
11 +use rustc_hash::{FxHashMap, FxHashSet};
12
13 use react_compiler_diagnostics::CompilerError;
14 use react_compiler_hir::{
@@ -36,14 +36,14 @@ pub fn merge_reactive_scopes_that_invalidate_together(
36 ) -> Result<(), CompilerError> {
37 // Pass 1: find last usage of each declaration
38 let visitor = FindLastUsageVisitor { env: &*env };
39 - let mut last_usage: HashMap<DeclarationId, EvaluationOrder> = HashMap::new();
39 + let mut last_usage: FxHashMap<DeclarationId, EvaluationOrder> = FxHashMap::default();
40 visit_reactive_function(func, &visitor, &mut last_usage);
41
42 // Pass 2+3: merge scopes
43 let mut transform = MergeTransform {
44 env,
45 last_usage,
46 - temporaries: HashMap::new(),
46 + temporaries: FxHashMap::default(),
47 };
48 let mut state: Option<Vec<ReactiveScopeDependency>> = None;
49 transform_reactive_function(func, &mut transform, &mut state)
@@ -59,7 +59,7 @@ struct FindLastUsageVisitor<'a> {
59 }
60
61 impl<'a> ReactiveFunctionVisitor for FindLastUsageVisitor<'a> {
62 - type State = HashMap<DeclarationId, EvaluationOrder>;
62 + type State = FxHashMap<DeclarationId, EvaluationOrder>;
63
64 fn env(&self) -> &Environment {
65 self.env
@@ -81,8 +81,8 @@ impl<'a> ReactiveFunctionVisitor for FindLastUsageVisitor<'a> {
81 /// TS: `class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | null>`
82 struct MergeTransform<'a> {
83 env: &'a mut Environment,
84 - last_usage: HashMap<DeclarationId, EvaluationOrder>,
85 - temporaries: HashMap<DeclarationId, DeclarationId>,
84 + last_usage: FxHashMap<DeclarationId, EvaluationOrder>,
85 + temporaries: FxHashMap<DeclarationId, DeclarationId>,
86 }
87
88 impl<'a> ReactiveFunctionTransform for MergeTransform<'a> {
@@ -138,7 +138,7 @@ impl<'a> MergeTransform<'a> {
138 scope_id: ScopeId,
139 from: usize,
140 to: usize,
141 - lvalues: HashSet<DeclarationId>,
141 + lvalues: FxHashSet<DeclarationId>,
142 }
143
144 let mut current: Option<MergedScope> = None;
@@ -308,7 +308,7 @@ impl<'a> MergeTransform<'a> {
308 scope_id: next_scope_id,
309 from: i,
310 to: i + 1,
311 - lvalues: HashSet::new(),
311 + lvalues: FxHashSet::default(),
312 });
313 }
314 }
@@ -319,7 +319,7 @@ impl<'a> MergeTransform<'a> {
319 scope_id: next_scope_id,
320 from: i,
321 to: i + 1,
322 - lvalues: HashSet::new(),
322 + lvalues: FxHashSet::default(),
323 });
324 }
325 }
@@ -398,7 +398,7 @@ impl<'a> MergeTransform<'a> {
398 /// Updates scope declarations to remove any that are not used after the scope.
399 fn update_scope_declarations(
400 scope_id: ScopeId,
401 - last_usage: &HashMap<DeclarationId, EvaluationOrder>,
401 + last_usage: &FxHashMap<DeclarationId, EvaluationOrder>,
402 env: &mut Environment,
403 ) {
404 let range_end = env.scopes[scope_id.0 as usize].range.end;
@@ -417,8 +417,8 @@ fn update_scope_declarations(
417 /// Returns whether all lvalues are last used at or before the given scope.
418 fn are_lvalues_last_used_by_scope(
419 scope_id: ScopeId,
420 - lvalues: &HashSet<DeclarationId>,
421 - last_usage: &HashMap<DeclarationId, EvaluationOrder>,
420 + lvalues: &FxHashSet<DeclarationId>,
421 + last_usage: &FxHashMap<DeclarationId, EvaluationOrder>,
422 env: &Environment,
423 ) -> bool {
424 let range_end = env.scopes[scope_id.0 as usize].range.end;
@@ -437,7 +437,7 @@ fn can_merge_scopes(
437 current_id: ScopeId,
438 next_id: ScopeId,
439 env: &Environment,
440 - temporaries: &HashMap<DeclarationId, DeclarationId>,
440 + temporaries: &FxHashMap<DeclarationId, DeclarationId>,
441 ) -> bool {
442 let current = &env.scopes[current_id.0 as usize];
443 let next = &env.scopes[next_id.0 as usize];
compiler/crates/react_compiler_reactive_scopes/src/promote_used_temporaries.rs
+24 -25
@@ -8,8 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/PromoteUsedTemporaries.ts`.
10
11 -use std::collections::HashMap;
12 -use std::collections::HashSet;
11 +use rustc_hash::{FxHashMap, FxHashSet};
12
13 use react_compiler_hir::DeclarationId;
14 use react_compiler_hir::FunctionId;
@@ -35,9 +34,9 @@ use react_compiler_hir::environment::Environment;
34 // =============================================================================
35
36 struct State {
38 - tags: HashSet<DeclarationId>,
39 - promoted: HashSet<DeclarationId>,
40 - pruned: HashMap<DeclarationId, PrunedInfo>,
37 + tags: FxHashSet<DeclarationId>,
38 + promoted: FxHashSet<DeclarationId>,
39 + pruned: FxHashMap<DeclarationId, PrunedInfo>,
40 }
41
42 struct PrunedInfo {
@@ -53,9 +52,9 @@ struct PrunedInfo {
52 /// TS: `promoteUsedTemporaries`
53 pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environment) {
54 let mut state = State {
56 - tags: HashSet::new(),
57 - promoted: HashSet::new(),
58 - pruned: HashMap::new(),
55 + tags: FxHashSet::default(),
56 + promoted: FxHashSet::default(),
57 + pruned: FxHashMap::default(),
58 };
59
60 // Phase 1: collect promotable temporaries (jsx tags, pruned scope usage)
@@ -78,8 +77,8 @@ pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environme
77 promote_temporaries_block(&func.body, &mut state, env);
78
79 // Phase 3: promote interposed temporaries
81 - let mut consts: HashSet<IdentifierId> = HashSet::new();
82 - let mut globals: HashSet<IdentifierId> = HashSet::new();
80 + let mut consts: FxHashSet<IdentifierId> = FxHashSet::default();
81 + let mut globals: FxHashSet<IdentifierId> = FxHashSet::default();
82 for param in &func.params {
83 match param {
84 ParamPattern::Place(p) => {
@@ -90,7 +89,7 @@ pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environme
89 }
90 }
91 }
93 - let mut inter_state: HashMap<IdentifierId, (IdentifierId, bool)> = HashMap::new();
92 + let mut inter_state: FxHashMap<IdentifierId, (IdentifierId, bool)> = FxHashMap::default();
93 promote_interposed_block(
94 &func.body,
95 &mut state,
@@ -555,9 +554,9 @@ fn visit_hir_function_for_promotion(func_id: FunctionId, state: &mut State, env:
554 fn promote_interposed_block(
555 block: &ReactiveBlock,
556 state: &mut State,
558 - inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
559 - consts: &mut HashSet<IdentifierId>,
560 - globals: &mut HashSet<IdentifierId>,
557 + inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
558 + consts: &mut FxHashSet<IdentifierId>,
559 + globals: &mut FxHashSet<IdentifierId>,
560 env: &mut Environment,
561 ) {
562 for stmt in block {
@@ -595,8 +594,8 @@ fn promote_interposed_block(
594 fn promote_interposed_place(
595 place: &Place,
596 state: &mut State,
598 - inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
599 - consts: &HashSet<IdentifierId>,
597 + inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
598 + consts: &FxHashSet<IdentifierId>,
599 env: &mut Environment,
600 ) {
601 if let Some(&(id, needs_promotion)) = inter_state.get(&place.identifier) {
@@ -610,9 +609,9 @@ fn promote_interposed_place(
609 fn promote_interposed_instruction(
610 instr: &ReactiveInstruction,
611 state: &mut State,
613 - inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
614 - consts: &mut HashSet<IdentifierId>,
615 - globals: &mut HashSet<IdentifierId>,
612 + inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
613 + consts: &mut FxHashSet<IdentifierId>,
614 + globals: &mut FxHashSet<IdentifierId>,
615 env: &mut Environment,
616 ) {
617 // Check instruction value lvalues (assignment targets)
@@ -803,9 +802,9 @@ fn promote_interposed_instruction(
802 fn promote_interposed_value(
803 value: &ReactiveValue,
804 state: &mut State,
806 - inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
807 - consts: &mut HashSet<IdentifierId>,
808 - globals: &mut HashSet<IdentifierId>,
805 + inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
806 + consts: &mut FxHashSet<IdentifierId>,
807 + globals: &mut FxHashSet<IdentifierId>,
808 env: &mut Environment,
809 ) {
810 match value {
@@ -847,9 +846,9 @@ fn promote_interposed_value(
846 fn promote_interposed_terminal(
847 stmt: &ReactiveTerminalStatement,
848 state: &mut State,
850 - inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
851 - consts: &mut HashSet<IdentifierId>,
852 - globals: &mut HashSet<IdentifierId>,
849 + inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
850 + consts: &mut FxHashSet<IdentifierId>,
851 + globals: &mut FxHashSet<IdentifierId>,
852 env: &mut Environment,
853 ) {
854 match &stmt.terminal {
compiler/crates/react_compiler_reactive_scopes/src/prune_always_invalidating_scopes.rs
+5 -5
@@ -11,7 +11,7 @@
11 //!
12 //! Corresponds to `src/ReactiveScopes/PruneAlwaysInvalidatingScopes.ts`.
13
14 -use std::collections::HashSet;
14 +use rustc_hash::FxHashSet;
15
16 use react_compiler_hir::{
17 IdentifierId, InstructionValue, PrunedReactiveScopeBlock, ReactiveFunction,
@@ -30,8 +30,8 @@ pub fn prune_always_invalidating_scopes(
30 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
31 let mut transform = Transform {
32 env,
33 - always_invalidating_values: HashSet::new(),
34 - unmemoized_values: HashSet::new(),
33 + always_invalidating_values: FxHashSet::default(),
34 + unmemoized_values: FxHashSet::default(),
35 };
36 let mut state = false; // withinScope
37 transform_reactive_function(func, &mut transform, &mut state)
@@ -39,8 +39,8 @@ pub fn prune_always_invalidating_scopes(
39
40 struct Transform<'a> {
41 env: &'a Environment,
42 - always_invalidating_values: HashSet<IdentifierId>,
43 - unmemoized_values: HashSet<IdentifierId>,
42 + always_invalidating_values: FxHashSet<IdentifierId>,
43 + unmemoized_values: FxHashSet<IdentifierId>,
44 }
45
46 impl<'a> ReactiveFunctionTransform for Transform<'a> {
compiler/crates/react_compiler_reactive_scopes/src/prune_hoisted_contexts.rs
+5 -5
@@ -8,7 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/PruneHoistedContexts.ts`.
10
11 -use std::collections::HashMap;
11 +use rustc_hash::FxHashMap;
12
13 use react_compiler_diagnostics::{CompilerError, CompilerErrorDetail, ErrorCategory};
14 use react_compiler_hir::{
@@ -33,7 +33,7 @@ pub fn prune_hoisted_contexts(
33 let mut transform = Transform { env };
34 let mut state = VisitorState {
35 active_scopes: Vec::new(),
36 - uninitialized: HashMap::new(),
36 + uninitialized: FxHashMap::default(),
37 };
38 transform_reactive_function(func, &mut transform, &mut state)
39 }
@@ -49,8 +49,8 @@ enum UninitializedKind {
49 }
50
51 struct VisitorState {
52 - active_scopes: Vec<std::collections::HashSet<IdentifierId>>,
53 - uninitialized: HashMap<IdentifierId, UninitializedKind>,
52 + active_scopes: Vec<rustc_hash::FxHashSet<IdentifierId>>,
53 + uninitialized: FxHashMap<IdentifierId, UninitializedKind>,
54 }
55
56 impl VisitorState {
@@ -81,7 +81,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> {
81 state: &mut VisitorState,
82 ) -> Result<(), CompilerError> {
83 let scope_data = &self.env.scopes[scope.scope.0 as usize];
84 - let decl_ids: std::collections::HashSet<IdentifierId> =
84 + let decl_ids: rustc_hash::FxHashSet<IdentifierId> =
85 scope_data.declarations.iter().map(|(id, _)| *id).collect();
86
87 // Add declared but not initialized variables
compiler/crates/react_compiler_reactive_scopes/src/prune_non_escaping_scopes.rs
+40 -41
@@ -8,8 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/PruneNonEscapingScopes.ts`.
10
11 -use std::collections::HashMap;
12 -use std::collections::HashSet;
11 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
12
13 use indexmap::IndexSet;
14 use react_compiler_hir::ArrayPatternElement;
@@ -74,8 +73,8 @@ pub fn prune_non_escaping_scopes(
73 // Prune scopes that do not declare/reassign any escaping values
74 let mut transform = PruneScopesTransform {
75 env,
77 - pruned_scopes: HashSet::new(),
78 - reassignments: HashMap::new(),
76 + pruned_scopes: FxHashSet::default(),
77 + reassignments: FxHashMap::default(),
78 };
79 let mut memoized_state = memoized;
80 transform_reactive_function(func, &mut transform, &mut memoized_state)
@@ -120,8 +119,8 @@ fn join_aliases(kind1: MemoizationLevel, kind2: MemoizationLevel) -> Memoization
119 struct IdentifierNode {
120 level: MemoizationLevel,
121 memoized: bool,
123 - dependencies: IndexSet<DeclarationId>,
124 - scopes: IndexSet<ScopeId>,
122 + dependencies: IndexSet<DeclarationId, FxBuildHasher>,
123 + scopes: IndexSet<ScopeId, FxBuildHasher>,
124 seen: bool,
125 }
126
@@ -137,19 +136,19 @@ struct ScopeNode {
136
137 struct CollectState {
138 /// Maps lvalues for LoadLocal to the identifier being loaded, to resolve indirections.
140 - definitions: HashMap<DeclarationId, DeclarationId>,
141 - identifiers: HashMap<DeclarationId, IdentifierNode>,
142 - scopes: HashMap<ScopeId, ScopeNode>,
143 - escaping_values: IndexSet<DeclarationId>,
139 + definitions: FxHashMap<DeclarationId, DeclarationId>,
140 + identifiers: FxHashMap<DeclarationId, IdentifierNode>,
141 + scopes: FxHashMap<ScopeId, ScopeNode>,
142 + escaping_values: IndexSet<DeclarationId, FxBuildHasher>,
143 }
144
145 impl CollectState {
146 fn new() -> Self {
147 CollectState {
149 - definitions: HashMap::new(),
150 - identifiers: HashMap::new(),
151 - scopes: HashMap::new(),
152 - escaping_values: IndexSet::new(),
148 + definitions: FxHashMap::default(),
149 + identifiers: FxHashMap::default(),
150 + scopes: FxHashMap::default(),
151 + escaping_values: IndexSet::default(),
152 }
153 }
154
@@ -160,8 +159,8 @@ impl CollectState {
159 IdentifierNode {
160 level: MemoizationLevel::Never,
161 memoized: false,
163 - dependencies: IndexSet::new(),
164 - scopes: IndexSet::new(),
162 + dependencies: IndexSet::default(),
163 + scopes: IndexSet::default(),
164 seen: false,
165 },
166 );
@@ -900,8 +899,8 @@ impl<'a> CollectDependenciesVisitor<'a> {
899 .or_insert_with(|| IdentifierNode {
900 level: MemoizationLevel::Never,
901 memoized: false,
903 - dependencies: IndexSet::new(),
904 - scopes: IndexSet::new(),
902 + dependencies: IndexSet::default(),
903 + scopes: IndexSet::default(),
904 seen: false,
905 });
906 node.level = join_aliases(node.level, lv.level);
@@ -1049,17 +1048,17 @@ impl<'a> ReactiveFunctionVisitor for CollectDependenciesVisitor<'a> {
1048 // computeMemoizedIdentifiers
1049 // =============================================================================
1050
1052 -fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId> {
1053 - let mut memoized = HashSet::new();
1051 +fn compute_memoized_identifiers(state: &CollectState) -> FxHashSet<DeclarationId> {
1052 + let mut memoized = FxHashSet::default();
1053
1054 // We need mutable access to the nodes, so we clone the state into mutable structures
1056 - let mut identifier_nodes: HashMap<
1055 + let mut identifier_nodes: FxHashMap<
1056 DeclarationId,
1057 (
1058 MemoizationLevel,
1059 bool,
1061 - IndexSet<DeclarationId>,
1062 - IndexSet<ScopeId>,
1060 + IndexSet<DeclarationId, FxBuildHasher>,
1061 + IndexSet<ScopeId, FxBuildHasher>,
1062 bool,
1063 ),
1064 > = state
@@ -1079,7 +1078,7 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
1078 })
1079 .collect();
1080
1082 - let mut scope_nodes: HashMap<ScopeId, (Vec<DeclarationId>, bool)> = state
1081 + let mut scope_nodes: FxHashMap<ScopeId, (Vec<DeclarationId>, bool)> = state
1082 .scopes
1083 .iter()
1084 .map(|(id, node)| (*id, (node.dependencies.clone(), node.seen)))
@@ -1088,18 +1087,18 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
1087 fn visit(
1088 id: DeclarationId,
1089 force_memoize: bool,
1091 - identifier_nodes: &mut HashMap<
1090 + identifier_nodes: &mut FxHashMap<
1091 DeclarationId,
1092 (
1093 MemoizationLevel,
1094 bool,
1096 - IndexSet<DeclarationId>,
1097 - IndexSet<ScopeId>,
1095 + IndexSet<DeclarationId, FxBuildHasher>,
1096 + IndexSet<ScopeId, FxBuildHasher>,
1097 bool,
1098 ),
1099 >,
1101 - scope_nodes: &mut HashMap<ScopeId, (Vec<DeclarationId>, bool)>,
1102 - memoized: &mut HashSet<DeclarationId>,
1100 + scope_nodes: &mut FxHashMap<ScopeId, (Vec<DeclarationId>, bool)>,
1101 + memoized: &mut FxHashSet<DeclarationId>,
1102 ) -> bool {
1103 let Some(&(level, _, _, _, seen)) = identifier_nodes.get(&id) else {
1104 return false;
@@ -1149,18 +1148,18 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
1148
1149 fn force_memoize_scope_dependencies(
1150 id: ScopeId,
1152 - identifier_nodes: &mut HashMap<
1151 + identifier_nodes: &mut FxHashMap<
1152 DeclarationId,
1153 (
1154 MemoizationLevel,
1155 bool,
1157 - IndexSet<DeclarationId>,
1158 - IndexSet<ScopeId>,
1156 + IndexSet<DeclarationId, FxBuildHasher>,
1157 + IndexSet<ScopeId, FxBuildHasher>,
1158 bool,
1159 ),
1160 >,
1162 - scope_nodes: &mut HashMap<ScopeId, (Vec<DeclarationId>, bool)>,
1163 - memoized: &mut HashSet<DeclarationId>,
1161 + scope_nodes: &mut FxHashMap<ScopeId, (Vec<DeclarationId>, bool)>,
1162 + memoized: &mut FxHashSet<DeclarationId>,
1163 ) {
1164 let seen = scope_nodes
1165 .get(&id)
@@ -1198,12 +1197,12 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
1197
1198 struct PruneScopesTransform<'a> {
1199 env: &'a Environment,
1201 - pruned_scopes: HashSet<ScopeId>,
1202 - reassignments: HashMap<DeclarationId, HashSet<IdentifierId>>,
1200 + pruned_scopes: FxHashSet<ScopeId>,
1201 + reassignments: FxHashMap<DeclarationId, FxHashSet<IdentifierId>>,
1202 }
1203
1204 impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
1206 - type State = HashSet<DeclarationId>;
1205 + type State = FxHashSet<DeclarationId>;
1206
1207 fn env(&self) -> &Environment {
1208 self.env
@@ -1212,7 +1211,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
1211 fn transform_scope(
1212 &mut self,
1213 scope: &mut ReactiveScopeBlock,
1215 - state: &mut HashSet<DeclarationId>,
1214 + state: &mut FxHashSet<DeclarationId>,
1215 ) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
1216 self.visit_scope(scope, state)?;
1217
@@ -1248,7 +1247,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
1247 fn transform_instruction(
1248 &mut self,
1249 instruction: &mut ReactiveInstruction,
1251 - state: &mut HashSet<DeclarationId>,
1250 + state: &mut FxHashSet<DeclarationId>,
1251 ) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
1252 self.traverse_instruction(instruction, state)?;
1253
@@ -1263,7 +1262,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
1262 let ids = self
1263 .reassignments
1264 .entry(decl_id)
1266 - .or_insert_with(HashSet::new);
1265 + .or_insert_with(FxHashSet::default);
1266 ids.insert(store_value.identifier);
1267 }
1268 ReactiveValue::Instruction(InstructionValue::LoadLocal { place, .. }) => {
@@ -1285,7 +1284,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
1284 let ids = self
1285 .reassignments
1286 .entry(decl_id)
1288 - .or_insert_with(HashSet::new);
1287 + .or_insert_with(FxHashSet::default);
1288 ids.insert(place.identifier);
1289 }
1290 }
compiler/crates/react_compiler_reactive_scopes/src/prune_non_reactive_dependencies.rs
+6 -6
@@ -8,7 +8,7 @@
8 //! Corresponds to `src/ReactiveScopes/PruneNonReactiveDependencies.ts`
9 //! and `src/ReactiveScopes/CollectReactiveIdentifiers.ts`.
10
11 -use std::collections::HashSet;
11 +use rustc_hash::FxHashSet;
12
13 use react_compiler_hir::{
14 EvaluationOrder, IdentifierId, InstructionValue, Place, PrunedReactiveScopeBlock,
@@ -28,9 +28,9 @@ use crate::visitors::{self, ReactiveFunctionTransform, ReactiveFunctionVisitor};
28 pub fn collect_reactive_identifiers(
29 func: &ReactiveFunction,
30 env: &Environment,
31 -) -> HashSet<IdentifierId> {
31 +) -> FxHashSet<IdentifierId> {
32 let visitor = CollectVisitor { env };
33 - let mut state = HashSet::new();
33 + let mut state = FxHashSet::default();
34 crate::visitors::visit_reactive_function(func, &visitor, &mut state);
35 state
36 }
@@ -40,7 +40,7 @@ struct CollectVisitor<'a> {
40 }
41
42 impl<'a> ReactiveFunctionVisitor for CollectVisitor<'a> {
43 - type State = HashSet<IdentifierId>;
43 + type State = FxHashSet<IdentifierId>;
44
45 fn env(&self) -> &Environment {
46 self.env
@@ -74,7 +74,7 @@ impl<'a> ReactiveFunctionVisitor for CollectVisitor<'a> {
74 /// TS: `isStableRefType`
75 fn is_stable_ref_type(
76 ty: &react_compiler_hir::Type,
77 - reactive_identifiers: &HashSet<IdentifierId>,
77 + reactive_identifiers: &FxHashSet<IdentifierId>,
78 id: IdentifierId,
79 ) -> bool {
80 is_use_ref_type(ty) && !reactive_identifiers.contains(&id)
@@ -133,7 +133,7 @@ struct PruneVisitor<'a> {
133 }
134
135 impl<'a> ReactiveFunctionTransform for PruneVisitor<'a> {
136 - type State = HashSet<IdentifierId>;
136 + type State = FxHashSet<IdentifierId>;
137
138 fn env(&self) -> &Environment {
139 self.env
compiler/crates/react_compiler_reactive_scopes/src/prune_unused_labels.rs
+4 -4
@@ -8,7 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/PruneUnusedLabels.ts`.
10
11 -use std::collections::HashSet;
11 +use rustc_hash::FxHashSet;
12
13 use react_compiler_hir::{
14 BlockId, ReactiveFunction, ReactiveStatement, ReactiveTerminal, ReactiveTerminalStatement,
@@ -23,7 +23,7 @@ pub fn prune_unused_labels(
23 env: &Environment,
24 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
25 let mut transform = Transform { env };
26 - let mut labels: HashSet<BlockId> = HashSet::new();
26 + let mut labels: FxHashSet<BlockId> = FxHashSet::default();
27 transform_reactive_function(func, &mut transform, &mut labels)
28 }
29
@@ -32,7 +32,7 @@ struct Transform<'a> {
32 }
33
34 impl<'a> ReactiveFunctionTransform for Transform<'a> {
35 - type State = HashSet<BlockId>;
35 + type State = FxHashSet<BlockId>;
36
37 fn env(&self) -> &Environment {
38 self.env
@@ -41,7 +41,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> {
41 fn transform_terminal(
42 &mut self,
43 stmt: &mut ReactiveTerminalStatement,
44 - state: &mut HashSet<BlockId>,
44 + state: &mut FxHashSet<BlockId>,
45 ) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
46 // Traverse children first
47 self.traverse_terminal(stmt, state)?;
compiler/crates/react_compiler_reactive_scopes/src/prune_unused_lvalues.rs
+7 -7
@@ -9,7 +9,7 @@
9 //!
10 //! Corresponds to `src/ReactiveScopes/PruneTemporaryLValues.ts`.
11
12 -use std::collections::HashSet;
12 +use rustc_hash::FxHashSet;
13
14 use react_compiler_hir::{
15 DeclarationId, EvaluationOrder, Place, ReactiveFunction, ReactiveInstruction,
@@ -34,7 +34,7 @@ pub fn prune_unused_lvalues(func: &mut ReactiveFunction, env: &Environment) {
34 // When we see an unnamed lvalue on an instruction, we add its DeclarationId.
35 // When we see a place reference (operand), we remove its DeclarationId.
36 let visitor = Visitor { env };
37 - let mut lvalues: HashSet<DeclarationId> = HashSet::new();
37 + let mut lvalues: FxHashSet<DeclarationId> = FxHashSet::default();
38 visitors::visit_reactive_function(func, &visitor, &mut lvalues);
39
40 // Phase 2: Null out lvalues whose DeclarationId remains in the map.
@@ -48,7 +48,7 @@ pub fn prune_unused_lvalues(func: &mut ReactiveFunction, env: &Environment) {
48 /// TS: `type LValues = Map<DeclarationId, ReactiveInstruction>`
49 /// In Rust, we only need the set of DeclarationIds (not the instruction refs)
50 /// because we apply changes in a separate pass.
51 -type LValues = HashSet<DeclarationId>;
51 +type LValues = FxHashSet<DeclarationId>;
52
53 /// TS: `class Visitor extends ReactiveFunctionVisitor<LValues>`
54 struct Visitor<'a> {
@@ -87,7 +87,7 @@ impl ReactiveFunctionVisitor for Visitor<'_> {
87 fn null_unused_lvalues(
88 block: &mut Vec<ReactiveStatement>,
89 env: &Environment,
90 - unused: &HashSet<DeclarationId>,
90 + unused: &FxHashSet<DeclarationId>,
91 ) {
92 for stmt in block.iter_mut() {
93 match stmt {
@@ -110,7 +110,7 @@ fn null_unused_lvalues(
110 fn null_unused_in_instruction(
111 instr: &mut ReactiveInstruction,
112 env: &Environment,
113 - unused: &HashSet<DeclarationId>,
113 + unused: &FxHashSet<DeclarationId>,
114 ) {
115 if let Some(lv) = &instr.lvalue {
116 let ident = &env.identifiers[lv.identifier.0 as usize];
@@ -124,7 +124,7 @@ fn null_unused_in_instruction(
124 fn null_unused_in_value(
125 value: &mut ReactiveValue,
126 env: &Environment,
127 - unused: &HashSet<DeclarationId>,
127 + unused: &FxHashSet<DeclarationId>,
128 ) {
129 match value {
130 ReactiveValue::SequenceExpression {
@@ -161,7 +161,7 @@ fn null_unused_in_value(
161 fn null_unused_in_terminal(
162 terminal: &mut react_compiler_hir::ReactiveTerminal,
163 env: &Environment,
164 - unused: &HashSet<DeclarationId>,
164 + unused: &FxHashSet<DeclarationId>,
165 ) {
166 use react_compiler_hir::ReactiveTerminal;
167 match terminal {
compiler/crates/react_compiler_reactive_scopes/src/rename_variables.rs
+28 -21
@@ -8,8 +8,7 @@
8 //!
9 //! Corresponds to `src/ReactiveScopes/RenameVariables.ts`.
10
11 -use std::collections::HashMap;
12 -use std::collections::HashSet;
11 +use rustc_hash::{FxHashMap, FxHashSet};
12
13 use react_compiler_hir::DeclarationId;
14 use react_compiler_hir::EvaluationOrder;
@@ -33,19 +32,19 @@ use crate::visitors::{self};
32 // =============================================================================
33
34 struct Scopes {
36 - seen: HashMap<DeclarationId, IdentifierName>,
37 - stack: Vec<HashMap<String, DeclarationId>>,
38 - globals: HashSet<String>,
39 - names: HashSet<String>,
35 + seen: FxHashMap<DeclarationId, IdentifierName>,
36 + stack: Vec<FxHashMap<String, DeclarationId>>,
37 + globals: FxHashSet<String>,
38 + names: FxHashSet<String>,
39 }
40
41 impl Scopes {
43 - fn new(globals: HashSet<String>) -> Self {
42 + fn new(globals: FxHashSet<String>) -> Self {
43 Self {
45 - seen: HashMap::new(),
46 - stack: vec![HashMap::new()],
44 + seen: FxHashMap::default(),
45 + stack: vec![FxHashMap::default()],
46 globals,
48 - names: HashSet::new(),
47 + names: FxHashSet::default(),
48 }
49 }
50
@@ -114,7 +113,7 @@ impl Scopes {
113 }
114
115 fn enter(&mut self) {
117 - self.stack.push(HashMap::new());
116 + self.stack.push(FxHashMap::default());
117 }
118
119 fn leave(&mut self) {
@@ -202,15 +201,15 @@ impl ReactiveFunctionVisitor for Visitor<'_> {
201 /// Renames variables for output — assigns unique names, handles SSA renames.
202 /// Returns a Set of all unique variable names used.
203 /// TS: `renameVariables`
205 -pub fn rename_variables(func: &mut ReactiveFunction, env: &mut Environment) -> HashSet<String> {
204 +pub fn rename_variables(func: &mut ReactiveFunction, env: &mut Environment) -> FxHashSet<String> {
205 rename_variables_with_parent(func, env, None)
206 }
207
208 fn rename_variables_with_parent(
209 func: &mut ReactiveFunction,
210 env: &mut Environment,
212 - parent_names: Option<&HashSet<String>>,
213 -) -> HashSet<String> {
211 + parent_names: Option<&FxHashSet<String>>,
212 +) -> FxHashSet<String> {
213 let globals = collect_referenced_globals(&func.body, env);
214
215 // Phase 1: Use ReactiveFunctionVisitor to compute the rename mapping.
@@ -242,7 +241,7 @@ fn rename_variables_with_parent(
241 }
242 }
243
245 - let mut result: HashSet<String> = scopes.names;
244 + let mut result: FxHashSet<String> = scopes.names;
245 result.extend(globals);
246 result
247 }
@@ -267,13 +266,17 @@ fn rename_variables_impl(func: &ReactiveFunction, visitor: &Visitor, scopes: &mu
266
267 /// Collects all globally referenced names from the reactive function.
268 /// TS: `collectReferencedGlobals`
270 -fn collect_referenced_globals(block: &ReactiveBlock, env: &Environment) -> HashSet<String> {
271 - let mut globals = HashSet::new();
269 +fn collect_referenced_globals(block: &ReactiveBlock, env: &Environment) -> FxHashSet<String> {
270 + let mut globals = FxHashSet::default();
271 collect_globals_block(block, &mut globals, env);
272 globals
273 }
274
276 -fn collect_globals_block(block: &ReactiveBlock, globals: &mut HashSet<String>, env: &Environment) {
275 +fn collect_globals_block(
276 + block: &ReactiveBlock,
277 + globals: &mut FxHashSet<String>,
278 + env: &Environment,
279 +) {
280 for stmt in block {
281 match stmt {
282 react_compiler_hir::ReactiveStatement::Instruction(instr) => {
@@ -292,7 +295,11 @@ fn collect_globals_block(block: &ReactiveBlock, globals: &mut HashSet<String>, e
295 }
296 }
297
295 -fn collect_globals_value(value: &ReactiveValue, globals: &mut HashSet<String>, env: &Environment) {
298 +fn collect_globals_value(
299 + value: &ReactiveValue,
300 + globals: &mut FxHashSet<String>,
301 + env: &Environment,
302 +) {
303 match value {
304 ReactiveValue::Instruction(iv) => {
305 if let InstructionValue::LoadGlobal { binding, .. } = iv {
@@ -340,7 +347,7 @@ fn collect_globals_value(value: &ReactiveValue, globals: &mut HashSet<String>, e
347 /// Recursively collects LoadGlobal names from an inner HIR function.
348 fn collect_globals_hir_function(
349 func_id: FunctionId,
343 - globals: &mut HashSet<String>,
350 + globals: &mut FxHashSet<String>,
351 env: &Environment,
352 ) {
353 let inner_func = &env.functions[func_id.0 as usize];
@@ -367,7 +374,7 @@ fn collect_globals_hir_function(
374
375 fn collect_globals_terminal(
376 stmt: &react_compiler_hir::ReactiveTerminalStatement,
370 - globals: &mut HashSet<String>,
377 + globals: &mut FxHashSet<String>,
378 env: &Environment,
379 ) {
380 match &stmt.terminal {
compiler/crates/react_compiler_reactive_scopes/src/stabilize_block_ids.rs
+6 -6
@@ -10,7 +10,7 @@
10 //!
11 //! Corresponds to `src/ReactiveScopes/StabilizeBlockIds.ts`.
12
13 -use std::collections::HashMap;
13 +use rustc_hash::{FxBuildHasher, FxHashMap};
14
15 use indexmap::IndexSet;
16 use react_compiler_hir::{
@@ -27,12 +27,12 @@ use crate::visitors::{
27 /// TS: `stabilizeBlockIds`
28 pub fn stabilize_block_ids(func: &mut ReactiveFunction, env: &mut Environment) {
29 // Pass 1: Collect referenced labels (preserving insertion order to match TS Set behavior)
30 - let mut referenced: IndexSet<BlockId> = IndexSet::new();
30 + let mut referenced: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
31 let collector = CollectReferencedLabels { env: &*env };
32 visit_reactive_function(func, &collector, &mut referenced);
33
34 // Build mappings: referenced block IDs -> sequential IDs (insertion-order deterministic)
35 - let mut mappings: HashMap<BlockId, BlockId> = HashMap::new();
35 + let mut mappings: FxHashMap<BlockId, BlockId> = FxHashMap::default();
36 for block_id in &referenced {
37 let len = mappings.len() as u32;
38 mappings.entry(*block_id).or_insert(BlockId(len));
@@ -52,7 +52,7 @@ struct CollectReferencedLabels<'a> {
52 }
53
54 impl<'a> ReactiveFunctionVisitor for CollectReferencedLabels<'a> {
55 - type State = IndexSet<BlockId>;
55 + type State = IndexSet<BlockId, FxBuildHasher>;
56
57 fn env(&self) -> &Environment {
58 self.env
@@ -80,7 +80,7 @@ impl<'a> ReactiveFunctionVisitor for CollectReferencedLabels<'a> {
80 // Pass 2: RewriteBlockIds
81 // =============================================================================
82
83 -fn get_or_insert_mapping(mappings: &mut HashMap<BlockId, BlockId>, id: BlockId) -> BlockId {
83 +fn get_or_insert_mapping(mappings: &mut FxHashMap<BlockId, BlockId>, id: BlockId) -> BlockId {
84 let len = mappings.len() as u32;
85 *mappings.entry(id).or_insert(BlockId(len))
86 }
@@ -91,7 +91,7 @@ struct RewriteBlockIds<'a> {
91 }
92
93 impl<'a> ReactiveFunctionTransform for RewriteBlockIds<'a> {
94 - type State = HashMap<BlockId, BlockId>;
94 + type State = FxHashMap<BlockId, BlockId>;
95
96 fn env(&self) -> &Environment {
97 self.env
compiler/crates/react_compiler_ssa/Cargo.toml
+1
@@ -7,3 +7,4 @@ edition = "2024"
7 react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
8 react_compiler_hir = { path = "../react_compiler_hir" }
9 indexmap = "2"
10 +rustc-hash = "2"
compiler/crates/react_compiler_ssa/src/eliminate_redundant_phi.rs
+5 -5
@@ -1,4 +1,4 @@
1 -use std::collections::{HashMap, HashSet};
1 +use rustc_hash::{FxHashMap, FxHashSet};
2
3 use react_compiler_hir::environment::Environment;
4 use react_compiler_hir::visitors;
@@ -10,7 +10,7 @@ use crate::enter_ssa::placeholder_function;
10 // Helper: rewrite_place
11 // =============================================================================
12
13 -fn rewrite_place(place: &mut Place, rewrites: &HashMap<IdentifierId, IdentifierId>) {
13 +fn rewrite_place(place: &mut Place, rewrites: &FxHashMap<IdentifierId, IdentifierId>) {
14 if let Some(&rewrite) = rewrites.get(&place.identifier) {
15 place.identifier = rewrite;
16 }
@@ -21,7 +21,7 @@ fn rewrite_place(place: &mut Place, rewrites: &HashMap<IdentifierId, IdentifierI
21 // =============================================================================
22
23 pub fn eliminate_redundant_phi(func: &mut HirFunction, env: &mut Environment) {
24 - let mut rewrites: HashMap<IdentifierId, IdentifierId> = HashMap::new();
24 + let mut rewrites: FxHashMap<IdentifierId, IdentifierId> = FxHashMap::default();
25 eliminate_redundant_phi_impl(func, env, &mut rewrites);
26 }
27
@@ -32,12 +32,12 @@ pub fn eliminate_redundant_phi(func: &mut HirFunction, env: &mut Environment) {
32 fn eliminate_redundant_phi_impl(
33 func: &mut HirFunction,
34 env: &mut Environment,
35 - rewrites: &mut HashMap<IdentifierId, IdentifierId>,
35 + rewrites: &mut FxHashMap<IdentifierId, IdentifierId>,
36 ) {
37 let ir = &mut func.body;
38
39 let mut has_back_edge = false;
40 - let mut visited: HashSet<BlockId> = HashSet::new();
40 + let mut visited: FxHashSet<BlockId> = FxHashSet::default();
41
42 let mut size;
43 loop {
compiler/crates/react_compiler_ssa/src/enter_ssa.rs
+19 -19
@@ -1,4 +1,4 @@
1 -use std::collections::{HashMap, HashSet};
1 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
2
3 use indexmap::IndexMap;
4 use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory};
@@ -16,35 +16,35 @@ struct IncompletePhi {
16 }
17
18 struct State {
19 - defs: HashMap<IdentifierId, IdentifierId>,
19 + defs: FxHashMap<IdentifierId, IdentifierId>,
20 incomplete_phis: Vec<IncompletePhi>,
21 }
22
23 struct SSABuilder {
24 - states: HashMap<BlockId, State>,
24 + states: FxHashMap<BlockId, State>,
25 current: Option<BlockId>,
26 - unsealed_preds: HashMap<BlockId, u32>,
27 - block_preds: HashMap<BlockId, Vec<BlockId>>,
28 - unknown: HashSet<IdentifierId>,
29 - context: HashSet<IdentifierId>,
30 - pending_phis: HashMap<BlockId, Vec<Phi>>,
26 + unsealed_preds: FxHashMap<BlockId, u32>,
27 + block_preds: FxHashMap<BlockId, Vec<BlockId>>,
28 + unknown: FxHashSet<IdentifierId>,
29 + context: FxHashSet<IdentifierId>,
30 + pending_phis: FxHashMap<BlockId, Vec<Phi>>,
31 processed_functions: Vec<FunctionId>,
32 }
33
34 impl SSABuilder {
35 - fn new(blocks: &IndexMap<BlockId, BasicBlock>) -> Self {
36 - let mut block_preds = HashMap::new();
35 + fn new(blocks: &IndexMap<BlockId, BasicBlock, FxBuildHasher>) -> Self {
36 + let mut block_preds = FxHashMap::default();
37 for (id, block) in blocks {
38 block_preds.insert(*id, block.preds.iter().copied().collect());
39 }
40 SSABuilder {
41 - states: HashMap::new(),
41 + states: FxHashMap::default(),
42 current: None,
43 - unsealed_preds: HashMap::new(),
43 + unsealed_preds: FxHashMap::default(),
44 block_preds,
45 - unknown: HashSet::new(),
46 - context: HashSet::new(),
47 - pending_phis: HashMap::new(),
45 + unknown: FxHashSet::default(),
46 + context: FxHashSet::default(),
47 + pending_phis: FxHashMap::default(),
48 processed_functions: Vec::new(),
49 }
50 }
@@ -226,7 +226,7 @@ impl SSABuilder {
226 ) {
227 let preds = self.block_preds.get(&block_id).cloned().unwrap_or_default();
228
229 - let mut pred_defs: IndexMap<BlockId, Place> = IndexMap::new();
229 + let mut pred_defs: IndexMap<BlockId, Place, FxBuildHasher> = IndexMap::default();
230 for pred_block_id in &preds {
231 let pred_id = self.get_id_at(old_place, *pred_block_id, env);
232 pred_defs.insert(
@@ -266,7 +266,7 @@ impl SSABuilder {
266 self.states.insert(
267 block_id,
268 State {
269 - defs: HashMap::new(),
269 + defs: FxHashMap::default(),
270 incomplete_phis: Vec::new(),
271 },
272 );
@@ -310,7 +310,7 @@ fn enter_ssa_impl(
310 env: &mut Environment,
311 root_entry: BlockId,
312 ) -> Result<(), CompilerDiagnostic> {
313 - let mut visited_blocks: HashSet<BlockId> = HashSet::new();
313 + let mut visited_blocks: FxHashSet<BlockId> = FxHashSet::default();
314 let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();
315
316 for block_id in &block_ids {
@@ -520,7 +520,7 @@ pub fn placeholder_function() -> HirFunction {
520 context: Vec::new(),
521 body: HIR {
522 entry: BlockId(0),
523 - blocks: IndexMap::new(),
523 + blocks: IndexMap::default(),
524 },
525 instructions: Vec::new(),
526 generator: false,
compiler/crates/react_compiler_ssa/src/rewrite_instruction_kinds_based_on_reassignment.rs
+2 -2
@@ -15,7 +15,7 @@
15 //! may be converted to a `const` if the reassignment is not used and was removed
16 //! by dead code elimination.
17
18 -use std::collections::HashMap;
18 +use rustc_hash::FxHashMap;
19
20 use react_compiler_diagnostics::{
21 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation,
@@ -115,7 +115,7 @@ pub fn rewrite_instruction_kinds_based_on_reassignment(
115 //
116 // Track: for each DeclarationId, the location of its first declaration,
117 // and whether it needs to be changed to Let (because of reassignment).
118 - let mut declarations: HashMap<DeclarationId, DeclarationLoc> = HashMap::new();
118 + let mut declarations: FxHashMap<DeclarationId, DeclarationLoc> = FxHashMap::default();
119 // Track which (block_index, instr_local_index) should have their lvalue.kind set to Reassign
120 let mut reassign_locs: Vec<(usize, usize)> = Vec::new();
121 // Track which declaration locations need to be set to Let
compiler/crates/react_compiler_typeinference/Cargo.toml
+1
@@ -4,6 +4,7 @@ version = "0.1.0"
4 edition = "2024"
5
6 [dependencies]
7 +rustc-hash = "2"
8 react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
9 react_compiler_hir = { path = "../react_compiler_hir" }
10 react_compiler_ssa = { path = "../react_compiler_ssa" }
compiler/crates/react_compiler_typeinference/src/infer_types.rs
+13 -13
@@ -8,7 +8,7 @@
8 //! Generates type equations from the HIR, unifies them, and applies the
9 //! resolved types back to identifiers. Analogous to TS `InferTypes.ts`.
10
11 -use std::collections::HashMap;
11 +use rustc_hash::FxHashMap;
12
13 use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
14 use react_compiler_hir::environment::{Environment, is_hook_name};
@@ -79,7 +79,7 @@ fn pre_resolve_globals(
79 func: &HirFunction,
80 function_key: u32,
81 env: &mut Environment,
82 - global_types: &mut HashMap<(u32, InstructionId), Type>,
82 + global_types: &mut FxHashMap<(u32, InstructionId), Type>,
83 ) {
84 for &instr_id in func.body.blocks.values().flat_map(|b| &b.instructions) {
85 let instr = &func.instructions[instr_id.0 as usize];
@@ -95,7 +95,7 @@ fn pre_resolve_globals(
95 fn pre_resolve_globals_recursive(
96 func_id: FunctionId,
97 env: &mut Environment,
98 - global_types: &mut HashMap<(u32, InstructionId), Type>,
98 + global_types: &mut FxHashMap<(u32, InstructionId), Type>,
99 ) {
100 // Collect LoadGlobal bindings and child function IDs in one pass to avoid
101 // borrow conflicts (we need &env.functions to read, then &mut env for
@@ -277,13 +277,13 @@ fn type_equals(a: &Type, b: &Type) -> bool {
277 }
278 }
279
280 -fn set_name(names: &mut HashMap<IdentifierId, String>, id: IdentifierId, source: &Identifier) {
280 +fn set_name(names: &mut FxHashMap<IdentifierId, String>, id: IdentifierId, source: &Identifier) {
281 if let Some(IdentifierName::Named(ref name)) = source.name {
282 names.insert(id, name.clone());
283 }
284 }
285
286 -fn get_name(names: &HashMap<IdentifierId, String>, id: IdentifierId) -> String {
286 +fn get_name(names: &FxHashMap<IdentifierId, String>, id: IdentifierId) -> String {
287 names.get(&id).cloned().unwrap_or_default()
288 }
289
@@ -335,7 +335,7 @@ fn generate(
335 // &mut env, but generate_instruction_types takes split borrows on env fields.
336 // The key is (function_key, InstructionId) where function_key is u32::MAX
337 // for the outer function and FunctionId.0 for inner functions.
338 - let mut global_types: HashMap<(u32, InstructionId), Type> = HashMap::new();
338 + let mut global_types: FxHashMap<(u32, InstructionId), Type> = FxHashMap::default();
339 pre_resolve_globals(func, u32::MAX, env, &mut global_types);
340 // Also pre-resolve inner functions recursively
341 for &instr_id in func.body.blocks.values().flat_map(|b| &b.instructions) {
@@ -355,7 +355,7 @@ fn generate(
355 }
356 }
357
358 - let mut names: HashMap<IdentifierId, String> = HashMap::new();
358 + let mut names: FxHashMap<IdentifierId, String> = FxHashMap::default();
359 let mut return_types: Vec<Type> = Vec::new();
360
361 for (_block_id, block) in &func.body.blocks {
@@ -420,7 +420,7 @@ fn generate_for_function_id(
420 identifiers: &[Identifier],
421 types: &mut Vec<Type>,
422 functions: &mut Vec<HirFunction>,
423 - global_types: &HashMap<(u32, InstructionId), Type>,
423 + global_types: &FxHashMap<(u32, InstructionId), Type>,
424 shapes: &ShapeRegistry,
425 unifier: &mut Unifier,
426 ) -> Result<(), CompilerDiagnostic> {
@@ -457,7 +457,7 @@ fn generate_for_function_id(
457
458 // TS creates a fresh `names` Map per recursive `generate` call, so inner
459 // functions don't inherit or pollute the outer function's name mappings.
460 - let mut inner_names: HashMap<IdentifierId, String> = HashMap::new();
460 + let mut inner_names: FxHashMap<IdentifierId, String> = FxHashMap::default();
461 let mut inner_return_types: Vec<Type> = Vec::new();
462
463 for (_block_id, block) in &inner.body.blocks {
@@ -521,8 +521,8 @@ fn generate_instruction_types(
521 identifiers: &[Identifier],
522 types: &mut Vec<Type>,
523 functions: &mut Vec<HirFunction>,
524 - names: &mut HashMap<IdentifierId, String>,
525 - global_types: &HashMap<(u32, InstructionId), Type>,
524 + names: &mut FxHashMap<IdentifierId, String>,
525 + global_types: &FxHashMap<(u32, InstructionId), Type>,
526 shapes: &ShapeRegistry,
527 unifier: &mut Unifier,
528 ) -> Result<(), CompilerDiagnostic> {
@@ -1304,7 +1304,7 @@ fn apply_instruction_operands(
1304 // =============================================================================
1305
1306 struct Unifier {
1307 - substitutions: HashMap<TypeId, Type>,
1307 + substitutions: FxHashMap<TypeId, Type>,
1308 enable_treat_ref_like_identifiers_as_refs: bool,
1309 enable_treat_set_identifiers_as_state_setters: bool,
1310 custom_hook_type: Option<Type>,
@@ -1317,7 +1317,7 @@ impl Unifier {
1317 enable_treat_set_identifiers_as_state_setters: bool,
1318 ) -> Self {
1319 Unifier {
1320 - substitutions: HashMap::new(),
1320 + substitutions: FxHashMap::default(),
1321 enable_treat_ref_like_identifiers_as_refs,
1322 enable_treat_set_identifiers_as_state_setters,
1323 custom_hook_type,
compiler/crates/react_compiler_utils/Cargo.toml
+1
@@ -5,3 +5,4 @@ edition = "2024"
5
6 [dependencies]
7 indexmap = "2"
8 +rustc-hash = "2"
compiler/crates/react_compiler_utils/src/disjoint_set.rs
+9 -9
@@ -7,7 +7,7 @@
7 //!
8 //! Ported from TypeScript `src/Utils/DisjointSet.ts`.
9
10 -use std::collections::HashSet;
10 +use rustc_hash::{FxBuildHasher, FxHashSet};
11 use std::hash::Hash;
12
13 use indexmap::IndexMap;
@@ -17,13 +17,13 @@ use indexmap::IndexMap;
17 /// Corresponds to TS `DisjointSet<T>` in `src/Utils/DisjointSet.ts`.
18 /// Uses `IndexMap` to preserve insertion order (matching TS `Map` behavior).
19 pub struct DisjointSet<K: Copy + Eq + Hash> {
20 - entries: IndexMap<K, K>,
20 + entries: IndexMap<K, K, FxBuildHasher>,
21 }
22
23 impl<K: Copy + Eq + Hash> DisjointSet<K> {
24 pub fn new() -> Self {
25 DisjointSet {
26 - entries: IndexMap::new(),
26 + entries: IndexMap::default(),
27 }
28 }
29
@@ -87,8 +87,8 @@ impl<K: Copy + Eq + Hash> DisjointSet<K> {
87 /// root) and returns a map of items to their roots.
88 ///
89 /// Corresponds to TS `canonicalize(): Map<T, T>`.
90 - pub fn canonicalize(&mut self) -> IndexMap<K, K> {
91 - let mut result = IndexMap::new();
90 + pub fn canonicalize(&mut self) -> IndexMap<K, K, FxBuildHasher> {
91 + let mut result = IndexMap::default();
92 let keys: Vec<K> = self.entries.keys().copied().collect();
93 for item in keys {
94 let root = self.find(item);
@@ -115,9 +115,9 @@ impl<K: Copy + Eq + Hash> DisjointSet<K> {
115 /// Groups all items by their root and returns the groups as a list of sets.
116 ///
117 /// Corresponds to TS `buildSets(): Array<Set<T>>`.
118 - pub fn build_sets(&mut self) -> Vec<HashSet<K>> {
119 - let mut group_to_index: IndexMap<K, usize> = IndexMap::new();
120 - let mut sets: Vec<HashSet<K>> = Vec::new();
118 + pub fn build_sets(&mut self) -> Vec<FxHashSet<K>> {
119 + let mut group_to_index: IndexMap<K, usize, FxBuildHasher> = IndexMap::default();
120 + let mut sets: Vec<FxHashSet<K>> = Vec::new();
121 let keys: Vec<K> = self.entries.keys().copied().collect();
122 for item in keys {
123 let group = self.find(item);
@@ -126,7 +126,7 @@ impl<K: Copy + Eq + Hash> DisjointSet<K> {
126 None => {
127 let idx = sets.len();
128 group_to_index.insert(group, idx);
129 - sets.push(HashSet::new());
129 + sets.push(FxHashSet::default());
130 idx
131 }
132 };
compiler/crates/react_compiler_validation/Cargo.toml
+1
@@ -5,5 +5,6 @@ edition = "2024"
5
6 [dependencies]
7 indexmap = "2"
8 +rustc-hash = "2"
9 react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
10 react_compiler_hir = { path = "../react_compiler_hir" }
compiler/crates/react_compiler_validation/src/validate_context_variable_lvalues.rs
+3 -3
@@ -1,4 +1,4 @@
1 -use std::collections::HashMap;
1 +use rustc_hash::FxHashMap;
2
3 use react_compiler_diagnostics::{
4 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory,
@@ -27,7 +27,7 @@ impl std::fmt::Display for VarRefKind {
27 }
28 }
29
30 -type IdentifierKinds = HashMap<IdentifierId, (Place, VarRefKind)>;
30 +type IdentifierKinds = FxHashMap<IdentifierId, (Place, VarRefKind)>;
31
32 /// Validates that context variable lvalues are used consistently.
33 ///
@@ -53,7 +53,7 @@ pub fn validate_context_variable_lvalues_with_errors(
53 identifiers: &[Identifier],
54 errors: &mut CompilerError,
55 ) -> Result<(), CompilerDiagnostic> {
56 - let mut identifier_kinds: IdentifierKinds = HashMap::new();
56 + let mut identifier_kinds: IdentifierKinds = FxHashMap::default();
57 validate_context_variable_lvalues_impl(
58 func,
59 &mut identifier_kinds,
compiler/crates/react_compiler_validation/src/validate_exhaustive_dependencies.rs
+30 -30
@@ -1,4 +1,4 @@
1 -use std::collections::{HashMap, HashSet};
1 +use rustc_hash::{FxHashMap, FxHashSet};
2
3 use react_compiler_diagnostics::{
4 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerSuggestion, CompilerSuggestionOperation,
@@ -33,7 +33,7 @@ pub fn validate_exhaustive_dependencies(
33 let validate_memo = env.config.validate_exhaustive_memoization_dependencies;
34 let validate_effect = env.config.validate_exhaustive_effect_dependencies.clone();
35
36 - let mut temporaries: HashMap<IdentifierId, Temporary> = HashMap::new();
36 + let mut temporaries: FxHashMap<IdentifierId, Temporary> = FxHashMap::default();
37 for param in &func.params {
38 let place = match param {
39 ParamPattern::Place(p) => p,
@@ -51,7 +51,7 @@ pub fn validate_exhaustive_dependencies(
51 }
52
53 let mut start_memo: Option<StartMemoInfo> = None;
54 - let mut memo_locals: HashSet<IdentifierId> = HashSet::new();
54 + let mut memo_locals: FxHashSet<IdentifierId> = FxHashSet::default();
55
56 // Callbacks struct holding the mutable state
57 let mut callbacks = Callbacks {
@@ -61,7 +61,7 @@ pub fn validate_exhaustive_dependencies(
61 validate_effect: validate_effect.clone(),
62 reactive: &reactive,
63 diagnostics: Vec::new(),
64 - invalid_memo_ids: HashSet::new(),
64 + invalid_memo_ids: FxHashSet::default(),
65 };
66
67 collect_dependencies(
@@ -180,13 +180,13 @@ fn path_to_string(path: &[DependencyPathEntry]) -> String {
180 struct Callbacks<'a> {
181 start_memo: &'a mut Option<StartMemoInfo>,
182 #[allow(dead_code)]
183 - memo_locals: &'a mut HashSet<IdentifierId>,
183 + memo_locals: &'a mut FxHashSet<IdentifierId>,
184 validate_memo: bool,
185 validate_effect: ExhaustiveEffectDepsMode,
186 - reactive: &'a HashSet<IdentifierId>,
186 + reactive: &'a FxHashSet<IdentifierId>,
187 diagnostics: Vec<CompilerDiagnostic>,
188 /// manual_memo_ids that had validation errors (to set has_invalid_deps)
189 - invalid_memo_ids: HashSet<u32>,
189 + invalid_memo_ids: FxHashSet<u32>,
190 }
191
192 // =============================================================================
@@ -283,8 +283,8 @@ fn is_sub_path_ignoring_optionals(
283 fn collect_reactive_identifiers(
284 func: &HirFunction,
285 functions: &[HirFunction],
286 -) -> HashSet<IdentifierId> {
287 - let mut reactive = HashSet::new();
286 +) -> FxHashSet<IdentifierId> {
287 + let mut reactive = FxHashSet::default();
288 for (_block_id, block) in &func.body.blocks {
289 for &instr_id in &block.instructions {
290 let instr = &func.instructions[instr_id.0 as usize];
@@ -319,9 +319,9 @@ fn collect_reactive_identifiers(
319 // findOptionalPlaces
320 // =============================================================================
321
322 -fn find_optional_places(func: &HirFunction) -> HashMap<IdentifierId, bool> {
323 - let mut optionals: HashMap<IdentifierId, bool> = HashMap::new();
324 - let mut visited: HashSet<BlockId> = HashSet::new();
322 +fn find_optional_places(func: &HirFunction) -> FxHashMap<IdentifierId, bool> {
323 + let mut optionals: FxHashMap<IdentifierId, bool> = FxHashMap::default();
324 + let mut visited: FxHashSet<BlockId> = FxHashSet::default();
325
326 for (_block_id, block) in &func.body.blocks {
327 if visited.contains(&block.id) {
@@ -418,8 +418,8 @@ fn find_optional_places(func: &HirFunction) -> HashMap<IdentifierId, bool> {
418 fn add_dependency(
419 dep: &Temporary,
420 dependencies: &mut Vec<InferredDependency>,
421 - dep_keys: &mut HashSet<InferredDependencyKey>,
422 - locals: &HashSet<IdentifierId>,
421 + dep_keys: &mut FxHashSet<InferredDependencyKey>,
422 + locals: &FxHashSet<IdentifierId>,
423 ) {
424 match dep {
425 Temporary::Aggregate {
@@ -464,8 +464,8 @@ fn add_dependency(
464 fn add_dependency_inferred(
465 dep: &InferredDependency,
466 dependencies: &mut Vec<InferredDependency>,
467 - dep_keys: &mut HashSet<InferredDependencyKey>,
468 - locals: &HashSet<IdentifierId>,
467 + dep_keys: &mut FxHashSet<InferredDependencyKey>,
468 + locals: &FxHashSet<IdentifierId>,
469 ) {
470 match dep {
471 InferredDependency::Global { .. } => {
@@ -487,10 +487,10 @@ fn add_dependency_inferred(
487
488 fn visit_candidate_dependency(
489 place: &Place,
490 - temporaries: &HashMap<IdentifierId, Temporary>,
490 + temporaries: &FxHashMap<IdentifierId, Temporary>,
491 dependencies: &mut Vec<InferredDependency>,
492 - dep_keys: &mut HashSet<InferredDependencyKey>,
493 - locals: &HashSet<IdentifierId>,
492 + dep_keys: &mut FxHashSet<InferredDependencyKey>,
493 + locals: &FxHashSet<IdentifierId>,
494 ) {
495 if let Some(dep) = temporaries.get(&place.identifier) {
496 add_dependency(dep, dependencies, dep_keys, locals);
@@ -502,12 +502,12 @@ fn collect_dependencies(
502 identifiers: &[Identifier],
503 types: &[Type],
504 functions: &[HirFunction],
505 - temporaries: &mut HashMap<IdentifierId, Temporary>,
505 + temporaries: &mut FxHashMap<IdentifierId, Temporary>,
506 callbacks: &mut Option<&mut Callbacks<'_>>,
507 is_function_expression: bool,
508 ) -> Result<Temporary, CompilerDiagnostic> {
509 let optionals = find_optional_places(func);
510 - let mut locals: HashSet<IdentifierId> = HashSet::new();
510 + let mut locals: FxHashSet<IdentifierId> = FxHashSet::default();
511
512 if is_function_expression {
513 for param in &func.params {
@@ -520,15 +520,15 @@ fn collect_dependencies(
520 }
521
522 let mut dependencies: Vec<InferredDependency> = Vec::new();
523 - let mut dep_keys: HashSet<InferredDependencyKey> = HashSet::new();
523 + let mut dep_keys: FxHashSet<InferredDependencyKey> = FxHashSet::default();
524
525 // Saved state for when we're inside a memo block (StartMemoize..FinishMemoize).
526 // In TS, `dependencies` and `locals` are shared by reference between the main
527 // collection loop and the callbacks — StartMemoize clears them, FinishMemoize
528 // reads and clears them. We simulate this by saving/restoring.
529 let mut saved_dependencies: Option<Vec<InferredDependency>> = None;
530 - let mut saved_dep_keys: Option<HashSet<InferredDependencyKey>> = None;
531 - let mut saved_locals: Option<HashSet<IdentifierId>> = None;
530 + let mut saved_dep_keys: Option<FxHashSet<InferredDependencyKey>> = None;
531 + let mut saved_locals: Option<FxHashSet<IdentifierId>> = None;
532
533 for (_block_id, block) in &func.body.blocks {
534 // Process phis
@@ -906,8 +906,8 @@ fn collect_dependencies(
906 }
907 InstructionValue::ArrayExpression { elements, loc, .. } => {
908 let mut array_deps: Vec<InferredDependency> = Vec::new();
909 - let mut array_keys: HashSet<InferredDependencyKey> = HashSet::new();
910 - let empty_locals = HashSet::new();
909 + let mut array_keys: FxHashSet<InferredDependencyKey> = FxHashSet::default();
910 + let empty_locals = FxHashSet::default();
911 for elem in elements {
912 let place = match elem {
913 ArrayElement::Place(p) => Some(p),
@@ -1224,7 +1224,7 @@ fn collect_dependencies(
1224 fn validate_dependencies(
1225 mut inferred: Vec<InferredDependency>,
1226 manual_dependencies: &[ManualMemoDependency],
1227 - reactive: &HashSet<IdentifierId>,
1227 + reactive: &FxHashSet<IdentifierId>,
1228 manual_memo_loc: Option<SourceLocation>,
1229 category: ErrorCategory,
1230 exhaustive_deps_report_mode: &str,
@@ -1360,7 +1360,7 @@ fn validate_dependencies(
1360 }
1361
1362 // Validate manual deps
1363 - let mut matched: HashSet<usize> = HashSet::new(); // indices into manual_dependencies
1363 + let mut matched: FxHashSet<usize> = FxHashSet::default(); // indices into manual_dependencies
1364 let mut missing: Vec<&InferredDependency> = Vec::new();
1365 let mut extra: Vec<&ManualMemoDependency> = Vec::new();
1366
@@ -1646,7 +1646,7 @@ fn print_manual_memo_dependency(dep: &ManualMemoDependency, identifiers: &[Ident
1646
1647 fn is_optional_dependency(
1648 identifier: IdentifierId,
1649 - reactive: &HashSet<IdentifierId>,
1649 + reactive: &FxHashSet<IdentifierId>,
1650 identifiers: &[Identifier],
1651 types: &[Type],
1652 ) -> bool {
@@ -1659,7 +1659,7 @@ fn is_optional_dependency(
1659
1660 fn is_optional_dependency_inferred(
1661 dep: &InferredDependency,
1662 - reactive: &HashSet<IdentifierId>,
1662 + reactive: &FxHashSet<IdentifierId>,
1663 identifiers: &[Identifier],
1664 types: &[Type],
1665 ) -> bool {
compiler/crates/react_compiler_validation/src/validate_hooks_usage.rs
+13 -12
@@ -10,7 +10,7 @@
10 //! and not called dynamically. Also validates that hooks are not
11 //! called inside function expressions.
12
13 -use std::collections::HashMap;
13 +use rustc_hash::{FxBuildHasher, FxHashMap};
14
15 use indexmap::IndexMap;
16 use react_compiler_diagnostics::{
@@ -51,7 +51,7 @@ fn join_kinds(a: Kind, b: Kind) -> Kind {
51
52 fn get_kind_for_place(
53 place: &Place,
54 - value_kinds: &HashMap<IdentifierId, Kind>,
54 + value_kinds: &FxHashMap<IdentifierId, Kind>,
55 identifiers: &[Identifier],
56 ) -> Kind {
57 let known_kind = value_kinds.get(&place.identifier).copied();
@@ -86,8 +86,8 @@ fn get_hook_kind_for_id<'a>(
86
87 fn visit_place(
88 place: &Place,
89 - value_kinds: &HashMap<IdentifierId, Kind>,
90 - errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
89 + value_kinds: &FxHashMap<IdentifierId, Kind>,
90 + errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
91 env: &mut Environment,
92 ) -> Result<(), CompilerError> {
93 let kind = value_kinds.get(&place.identifier).copied();
@@ -99,8 +99,8 @@ fn visit_place(
99
100 fn record_conditional_hook_error(
101 place: &Place,
102 - value_kinds: &mut HashMap<IdentifierId, Kind>,
103 - errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
102 + value_kinds: &mut FxHashMap<IdentifierId, Kind>,
103 + errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
104 env: &mut Environment,
105 ) -> Result<(), CompilerError> {
106 value_kinds.insert(place.identifier, Kind::Error);
@@ -133,7 +133,7 @@ fn record_conditional_hook_error(
133
134 fn record_invalid_hook_usage_error(
135 place: &Place,
136 - errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
136 + errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
137 env: &mut Environment,
138 ) -> Result<(), CompilerError> {
139 let reason = "Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values".to_string();
@@ -164,7 +164,7 @@ fn record_invalid_hook_usage_error(
164
165 fn record_dynamic_hook_usage_error(
166 place: &Place,
167 - errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
167 + errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
168 env: &mut Environment,
169 ) -> Result<(), CompilerError> {
170 let reason = "Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks".to_string();
@@ -199,8 +199,9 @@ pub fn validate_hooks_usage(
199 env: &mut Environment,
200 ) -> Result<(), react_compiler_diagnostics::CompilerDiagnostic> {
201 let unconditional_blocks = compute_unconditional_blocks(func, env.next_block_id().0)?;
202 - let mut errors_by_loc: IndexMap<SourceLocation, CompilerErrorDetail> = IndexMap::new();
203 - let mut value_kinds: HashMap<IdentifierId, Kind> = HashMap::new();
202 + let mut errors_by_loc: IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher> =
203 + IndexMap::default();
204 + let mut value_kinds: FxHashMap<IdentifierId, Kind> = FxHashMap::default();
205
206 // Process params
207 for param in &func.params {
@@ -512,8 +513,8 @@ fn hook_kind_display(kind: &HookKind) -> &'static str {
513 /// Uses the canonical `each_instruction_value_operand` from visitors.
514 fn visit_all_operands(
515 value: &InstructionValue,
515 - value_kinds: &HashMap<IdentifierId, Kind>,
516 - errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
516 + value_kinds: &FxHashMap<IdentifierId, Kind>,
517 + errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
518 env: &mut Environment,
519 ) -> Result<(), CompilerError> {
520 let operands = visitors::each_instruction_value_operand(value, &*env);
compiler/crates/react_compiler_validation/src/validate_locals_not_reassigned_after_render.rs
+4 -4
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -use std::collections::{HashMap, HashSet};
8 +use rustc_hash::{FxHashMap, FxHashSet};
9
10 use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory};
11 use react_compiler_hir::environment::Environment;
@@ -20,7 +20,7 @@ use react_compiler_hir::{
20 /// This prevents a category of bugs in which a closure captures a
21 /// binding from one render but does not update.
22 pub fn validate_locals_not_reassigned_after_render(func: &HirFunction, env: &mut Environment) {
23 - let mut context_variables: HashSet<IdentifierId> = HashSet::new();
23 + let mut context_variables: FxHashSet<IdentifierId> = FxHashSet::default();
24 let mut diagnostics: Vec<CompilerDiagnostic> = Vec::new();
25
26 let reassignment = get_context_reassignment(
@@ -85,13 +85,13 @@ fn get_context_reassignment(
85 types: &[Type],
86 functions: &[HirFunction],
87 env: &Environment,
88 - context_variables: &mut HashSet<IdentifierId>,
88 + context_variables: &mut FxHashSet<IdentifierId>,
89 is_function_expression: bool,
90 is_async: bool,
91 diagnostics: &mut Vec<CompilerDiagnostic>,
92 ) -> Option<Place> {
93 // Maps identifiers to the place that they reassign
94 - let mut reassigning_functions: HashMap<IdentifierId, Place> = HashMap::new();
94 + let mut reassigning_functions: FxHashMap<IdentifierId, Place> = FxHashMap::default();
95
96 for (_block_id, block) in &func.body.blocks {
97 for &instruction_id in &block.instructions {
compiler/crates/react_compiler_validation/src/validate_no_capitalized_calls.rs
+4 -4
@@ -1,4 +1,4 @@
1 -use std::collections::{HashMap, HashSet};
1 +use rustc_hash::{FxHashMap, FxHashSet};
2
3 use react_compiler_diagnostics::{CompilerError, CompilerErrorDetail, ErrorCategory};
4 use react_compiler_hir::environment::Environment;
@@ -12,15 +12,15 @@ pub fn validate_no_capitalized_calls(
12 env: &mut Environment,
13 ) -> Result<(), CompilerError> {
14 // Build the allow list from global registry keys + config entries
15 - let mut allow_list: HashSet<String> = env.globals().keys().cloned().collect();
15 + let mut allow_list: FxHashSet<String> = env.globals().keys().cloned().collect();
16 if let Some(config_entries) = &env.config.validate_no_capitalized_calls {
17 for entry in config_entries {
18 allow_list.insert(entry.clone());
19 }
20 }
21
22 - let mut capital_load_globals: HashMap<IdentifierId, String> = HashMap::new();
23 - let mut capitalized_properties: HashMap<IdentifierId, String> = HashMap::new();
22 + let mut capital_load_globals: FxHashMap<IdentifierId, String> = FxHashMap::default();
23 + let mut capitalized_properties: FxHashMap<IdentifierId, String> = FxHashMap::default();
24
25 let reason = "Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config";
26
compiler/crates/react_compiler_validation/src/validate_no_derived_computations_in_effects.rs
+65 -60
@@ -10,7 +10,8 @@
10 //!
11 //! Port of ValidateNoDerivedComputationsInEffects_exp.ts.
12
13 -use std::collections::{HashMap, HashSet};
13 +use indexmap::{IndexMap, IndexSet};
14 +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
15
16 use react_compiler_diagnostics::{
17 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, CompilerErrorDetail, ErrorCategory,
@@ -111,7 +112,7 @@ struct DerivationMetadata {
112 type_of_value: TypeOfValue,
113 place_identifier: IdentifierId,
114 place_name: Option<IdentifierName>,
114 - source_ids: indexmap::IndexSet<IdentifierId>,
115 + source_ids: IndexSet<IdentifierId, FxBuildHasher>,
116 is_state_source: bool,
117 }
118
@@ -129,16 +130,16 @@ struct DepElement {
130
131 struct ValidationContext {
132 /// Map from lvalue identifier to the FunctionId of function expressions
132 - functions: HashMap<IdentifierId, FunctionId>,
133 + functions: FxHashMap<IdentifierId, FunctionId>,
134 /// Map from lvalue identifier to ArrayExpression elements (candidate deps)
134 - candidate_dependencies: HashMap<IdentifierId, Vec<DepElement>>,
135 + candidate_dependencies: FxHashMap<IdentifierId, Vec<DepElement>>,
136 derivation_cache: DerivationCache,
136 - effects_cache: HashMap<IdentifierId, EffectMetadata>,
137 - set_state_loads: HashMap<IdentifierId, Option<IdentifierId>>,
138 - set_state_usages: HashMap<IdentifierId, HashSet<LocKey>>,
137 + effects_cache: FxHashMap<IdentifierId, EffectMetadata>,
138 + set_state_loads: FxHashMap<IdentifierId, Option<IdentifierId>>,
139 + set_state_usages: FxHashMap<IdentifierId, FxHashSet<LocKey>>,
140 }
141
141 -/// A hashable key for SourceLocation to use in HashSet
142 +/// A hashable key for SourceLocation to use in FxHashSet
143 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
144 struct LocKey {
145 start_line: u32,
@@ -169,21 +170,21 @@ impl LocKey {
170 #[derive(Debug, Clone)]
171 struct DerivationCache {
172 has_changes: bool,
172 - cache: HashMap<IdentifierId, DerivationMetadata>,
173 - previous_cache: Option<HashMap<IdentifierId, DerivationMetadata>>,
173 + cache: FxHashMap<IdentifierId, DerivationMetadata>,
174 + previous_cache: Option<FxHashMap<IdentifierId, DerivationMetadata>>,
175 }
176
177 impl DerivationCache {
178 fn new() -> Self {
179 DerivationCache {
180 has_changes: false,
180 - cache: HashMap::new(),
181 + cache: FxHashMap::default(),
182 previous_cache: None,
183 }
184 }
185
186 fn take_snapshot(&mut self) {
186 - let mut prev = HashMap::new();
187 + let mut prev = FxHashMap::default();
188 for (key, value) in &self.cache {
189 prev.insert(
190 *key,
@@ -241,7 +242,7 @@ impl DerivationCache {
242 &mut self,
243 derived_id: IdentifierId,
244 derived_name: Option<IdentifierName>,
244 - source_ids: indexmap::IndexSet<IdentifierId>,
245 + source_ids: IndexSet<IdentifierId, FxBuildHasher>,
246 type_of_value: TypeOfValue,
247 is_state_source: bool,
248 ) {
@@ -302,8 +303,8 @@ fn join_value(lvalue_type: TypeOfValue, value_type: TypeOfValue) -> TypeOfValue
303
304 fn get_root_set_state(
305 key: IdentifierId,
305 - loads: &HashMap<IdentifierId, Option<IdentifierId>>,
306 - visited: &mut HashSet<IdentifierId>,
306 + loads: &FxHashMap<IdentifierId, Option<IdentifierId>>,
307 + visited: &mut FxHashSet<IdentifierId>,
308 ) -> Option<IdentifierId> {
309 if visited.contains(&key) {
310 return None;
@@ -320,8 +321,8 @@ fn get_root_set_state(
321 fn maybe_record_set_state_for_instr(
322 instr: &react_compiler_hir::Instruction,
323 env: &Environment,
323 - set_state_loads: &mut HashMap<IdentifierId, Option<IdentifierId>>,
324 - set_state_usages: &mut HashMap<IdentifierId, HashSet<LocKey>>,
324 + set_state_loads: &mut FxHashMap<IdentifierId, Option<IdentifierId>>,
325 + set_state_usages: &mut FxHashMap<IdentifierId, FxHashSet<LocKey>>,
326 ) {
327 let identifiers = &env.identifiers;
328 let types = &env.types;
@@ -349,10 +350,10 @@ fn maybe_record_set_state_for_instr(
350 }
351 }
352
352 - let root = get_root_set_state(lvalue_id, set_state_loads, &mut HashSet::new());
353 + let root = get_root_set_state(lvalue_id, set_state_loads, &mut FxHashSet::default());
354 if let Some(root_id) = root {
355 set_state_usages.entry(root_id).or_insert_with(|| {
355 - let mut set = HashSet::new();
356 + let mut set = FxHashSet::default();
357 set.insert(LocKey::from_loc(&instr.lvalue.loc));
358 set
359 });
@@ -377,12 +378,12 @@ pub fn validate_no_derived_computations_in_effects_exp(
378 let identifiers = &env.identifiers;
379
380 let mut context = ValidationContext {
380 - functions: HashMap::new(),
381 - candidate_dependencies: HashMap::new(),
381 + functions: FxHashMap::default(),
382 + candidate_dependencies: FxHashMap::default(),
383 derivation_cache: DerivationCache::new(),
383 - effects_cache: HashMap::new(),
384 - set_state_loads: HashMap::new(),
385 - set_state_usages: HashMap::new(),
384 + effects_cache: FxHashMap::default(),
385 + set_state_loads: FxHashMap::default(),
386 + set_state_usages: FxHashMap::default(),
387 };
388
389 // Initialize derivation cache based on function type
@@ -395,7 +396,7 @@ pub fn validate_no_derived_computations_in_effects_exp(
396 DerivationMetadata {
397 place_identifier: place.identifier,
398 place_name: name,
398 - source_ids: indexmap::IndexSet::new(),
399 + source_ids: IndexSet::default(),
400 type_of_value: TypeOfValue::FromProps,
401 is_state_source: true,
402 },
@@ -411,7 +412,7 @@ pub fn validate_no_derived_computations_in_effects_exp(
412 DerivationMetadata {
413 place_identifier: place.identifier,
414 place_name: name,
414 - source_ids: indexmap::IndexSet::new(),
415 + source_ids: IndexSet::default(),
416 type_of_value: TypeOfValue::FromProps,
417 is_state_source: true,
418 },
@@ -477,7 +478,7 @@ fn record_phi_derivations(
478 let identifiers = &env.identifiers;
479 for phi in &block.phis {
480 let mut type_of_value = TypeOfValue::Ignored;
480 - let mut source_ids: indexmap::IndexSet<IdentifierId> = indexmap::IndexSet::new();
481 + let mut source_ids: IndexSet<IdentifierId, FxBuildHasher> = IndexSet::default();
482
483 for (_block_id, operand) in &phi.operands {
484 if let Some(operand_metadata) = context.derivation_cache.cache.get(&operand.identifier)
@@ -522,7 +523,7 @@ fn record_instruction_derivations(
523
524 let mut type_of_value = TypeOfValue::Ignored;
525 let is_source = false;
525 - let mut sources: indexmap::IndexSet<IdentifierId> = indexmap::IndexSet::new();
526 + let mut sources: IndexSet<IdentifierId, FxBuildHasher> = IndexSet::default();
527
528 match &instr.value {
529 InstructionValue::FunctionExpression { lowered_func, .. } => {
@@ -575,7 +576,7 @@ fn record_instruction_derivations(
576 context.derivation_cache.add_derivation_entry(
577 lvalue_id,
578 name,
578 - indexmap::IndexSet::new(),
579 + IndexSet::default(),
580 TypeOfValue::FromState,
581 true,
582 );
@@ -614,7 +615,7 @@ fn record_instruction_derivations(
615 context.derivation_cache.add_derivation_entry(
616 lvalue_id,
617 name,
617 - indexmap::IndexSet::new(),
618 + IndexSet::default(),
619 TypeOfValue::FromState,
620 true,
621 );
@@ -643,8 +644,11 @@ fn record_instruction_derivations(
644 for (operand_id, operand_loc) in each_instruction_operand(instr, env) {
645 // Track setState usages
646 if context.set_state_loads.contains_key(&operand_id) {
646 - let root =
647 - get_root_set_state(operand_id, &context.set_state_loads, &mut HashSet::new());
647 + let root = get_root_set_state(
648 + operand_id,
649 + &context.set_state_loads,
650 + &mut FxHashSet::default(),
651 + );
652 if let Some(root_id) = root {
653 if let Some(usages) = context.set_state_usages.get_mut(&root_id) {
654 usages.insert(LocKey::from_loc(&operand_loc));
@@ -754,7 +758,7 @@ struct TreeNode {
758 fn build_tree_node(
759 source_id: IdentifierId,
760 context: &ValidationContext,
757 - visited: &HashSet<String>,
761 + visited: &FxHashSet<String>,
762 ) -> Vec<TreeNode> {
763 let source_metadata = match context.derivation_cache.cache.get(&source_id) {
764 Some(m) => m,
@@ -773,7 +777,7 @@ fn build_tree_node(
777 }
778
779 let mut children: Vec<TreeNode> = Vec::new();
776 - let mut named_siblings: indexmap::IndexSet<String> = indexmap::IndexSet::new();
780 + let mut named_siblings: IndexSet<String, FxBuildHasher> = IndexSet::default();
781
782 for child_id in &source_metadata.source_ids {
783 assert_ne!(
@@ -813,8 +817,8 @@ fn render_tree(
817 node: &TreeNode,
818 indent: &str,
819 is_last: bool,
816 - props_set: &mut indexmap::IndexSet<String>,
817 - state_set: &mut indexmap::IndexSet<String>,
820 + props_set: &mut IndexSet<String, FxBuildHasher>,
821 + state_set: &mut IndexSet<String, FxBuildHasher>,
822 ) -> String {
823 let prefix = format!(
824 "{}{}",
@@ -865,10 +869,10 @@ fn render_tree(
869 fn get_fn_local_deps(
870 func_id: Option<FunctionId>,
871 env: &Environment,
868 -) -> Option<HashSet<IdentifierId>> {
872 +) -> Option<FxHashSet<IdentifierId>> {
873 let func_id = func_id?;
874 let inner = &env.functions[func_id.0 as usize];
871 - let mut deps: HashSet<IdentifierId> = HashSet::new();
875 + let mut deps: FxHashSet<IdentifierId> = FxHashSet::default();
876
877 for (_block_id, block) in &inner.body.blocks {
878 for &instr_id in &block.instructions {
@@ -894,34 +898,35 @@ fn validate_effect(
898 let types = &env.types;
899 let functions = &env.functions;
900 let effect_function = &functions[effect_func_id.0 as usize];
897 - let mut seen_blocks: HashSet<BlockId> = HashSet::new();
901 + let mut seen_blocks: FxHashSet<BlockId> = FxHashSet::default();
902
903 struct DerivedSetStateCall {
904 callee_loc: Option<SourceLocation>,
905 callee_id: IdentifierId,
906 callee_identifier_name: Option<String>,
903 - source_ids: indexmap::IndexSet<IdentifierId>,
907 + source_ids: IndexSet<IdentifierId, FxBuildHasher>,
908 }
909
910 let mut effect_derived_set_state_calls: Vec<DerivedSetStateCall> = Vec::new();
907 - let mut effect_set_state_usages: HashMap<IdentifierId, HashSet<LocKey>> = HashMap::new();
911 + let mut effect_set_state_usages: FxHashMap<IdentifierId, FxHashSet<LocKey>> =
912 + FxHashMap::default();
913
914 // Consider setStates in the effect's dependency array as being part of effectSetStateUsages
915 for dep in dependencies {
916 let root = get_root_set_state(
917 dep.identifier,
918 &context.set_state_loads,
914 - &mut HashSet::new(),
919 + &mut FxHashSet::default(),
920 );
921 if let Some(root_id) = root {
917 - let mut set = HashSet::new();
922 + let mut set = FxHashSet::default();
923 set.insert(LocKey::from_loc(&dep.loc));
924 effect_set_state_usages.insert(root_id, set);
925 }
926 }
927
923 - let mut cleanup_function_deps: Option<HashSet<IdentifierId>> = None;
924 - let mut globals: HashSet<IdentifierId> = HashSet::new();
928 + let mut cleanup_function_deps: Option<FxHashSet<IdentifierId>> = None;
929 + let mut globals: FxHashSet<IdentifierId> = FxHashSet::default();
930
931 for (_block_id, block) in &effect_function.body.blocks {
932 // Check for return -> cleanup function
@@ -965,7 +970,7 @@ fn validate_effect(
970 let root = get_root_set_state(
971 operand_id,
972 &context.set_state_loads,
968 - &mut HashSet::new(),
973 + &mut FxHashSet::default(),
974 );
975 if let Some(root_id) = root {
976 if let Some(usages) = effect_set_state_usages.get_mut(&root_id) {
@@ -1045,7 +1050,7 @@ fn validate_effect(
1050 let root_set_state_call = get_root_set_state(
1051 derived.callee_id,
1052 &context.set_state_loads,
1048 - &mut HashSet::new(),
1053 + &mut FxHashSet::default(),
1054 );
1055 if let Some(root_id) = root_set_state_call {
1056 let effect_usage_count = effect_set_state_usages
@@ -1061,13 +1066,13 @@ fn validate_effect(
1066 && context.set_state_usages.contains_key(&root_id)
1067 && effect_usage_count == total_usage_count - 1
1068 {
1064 - let mut props_set: indexmap::IndexSet<String> = indexmap::IndexSet::new();
1065 - let mut state_set: indexmap::IndexSet<String> = indexmap::IndexSet::new();
1069 + let mut props_set: IndexSet<String, FxBuildHasher> = IndexSet::default();
1070 + let mut state_set: IndexSet<String, FxBuildHasher> = IndexSet::default();
1071
1067 - let mut root_nodes_map: indexmap::IndexMap<String, TreeNode> =
1068 - indexmap::IndexMap::new();
1072 + let mut root_nodes_map: IndexMap<String, TreeNode, FxBuildHasher> =
1073 + IndexMap::default();
1074 for id in &derived.source_ids {
1070 - let nodes = build_tree_node(*id, context, &HashSet::new());
1075 + let nodes = build_tree_node(*id, context, &FxHashSet::default());
1076 for node in nodes {
1077 if !root_nodes_map.contains_key(&node.name) {
1078 root_nodes_map.insert(node.name.clone(), node);
@@ -1162,9 +1167,9 @@ pub fn validate_no_derived_computations_in_effects(
1167 let effects_to_validate: Vec<(FunctionId, Vec<IdentifierId>)> = {
1168 let ids = &env.identifiers;
1169 let tys = &env.types;
1165 - let mut candidate_deps: HashMap<IdentifierId, Vec<IdentifierId>> = HashMap::new();
1166 - let mut functions_map: HashMap<IdentifierId, FunctionId> = HashMap::new();
1167 - let mut locals_map: HashMap<IdentifierId, IdentifierId> = HashMap::new();
1170 + let mut candidate_deps: FxHashMap<IdentifierId, Vec<IdentifierId>> = FxHashMap::default();
1171 + let mut functions_map: FxHashMap<IdentifierId, FunctionId> = FxHashMap::default();
1172 + let mut locals_map: FxHashMap<IdentifierId, IdentifierId> = FxHashMap::default();
1173 let mut result = Vec::new();
1174
1175 for (_, block) in &func.body.blocks {
@@ -1280,8 +1285,8 @@ fn validate_effect_non_exp(
1285 }
1286 }
1287
1283 - let mut seen_blocks: HashSet<BlockId> = HashSet::new();
1284 - let mut dep_values: HashMap<IdentifierId, Vec<IdentifierId>> = HashMap::new();
1288 + let mut seen_blocks: FxHashSet<BlockId> = FxHashSet::default();
1289 + let mut dep_values: FxHashMap<IdentifierId, Vec<IdentifierId>> = FxHashMap::default();
1290 for dep in effect_deps {
1291 dep_values.insert(*dep, vec![*dep]);
1292 }
@@ -1296,7 +1301,7 @@ fn validate_effect_non_exp(
1301 }
1302
1303 for phi in &block.phis {
1299 - let mut aggregate: HashSet<IdentifierId> = HashSet::new();
1304 + let mut aggregate: FxHashSet<IdentifierId> = FxHashSet::default();
1305 for operand in phi.operands.values() {
1306 if let Some(deps) = dep_values.get(&operand.identifier) {
1307 for d in deps {
@@ -1326,7 +1331,7 @@ fn validate_effect_non_exp(
1331 | InstructionValue::TemplateLiteral { .. }
1332 | InstructionValue::CallExpression { .. }
1333 | InstructionValue::MethodCall { .. } => {
1329 - let mut aggregate: HashSet<IdentifierId> = HashSet::new();
1334 + let mut aggregate: FxHashSet<IdentifierId> = FxHashSet::default();
1335 for operand in non_exp_value_operands(&instr.value) {
1336 if let Some(deps) = dep_values.get(&operand) {
1337 for d in deps {
@@ -1343,7 +1348,7 @@ fn validate_effect_non_exp(
1348 if is_set_state_type(callee_ty) && args.len() == 1 {
1349 if let PlaceOrSpread::Place(arg) = &args[0] {
1350 if let Some(deps) = dep_values.get(&arg.identifier) {
1346 - let dep_set: HashSet<_> = deps.iter().collect();
1351 + let dep_set: FxHashSet<_> = deps.iter().collect();
1352 if dep_set.len() == effect_deps.len() {
1353 if let Some(loc) = callee.loc {
1354 set_state_locs.push(loc);
compiler/crates/react_compiler_validation/src/validate_no_freezing_known_mutable_functions.rs
+4 -4
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -use std::collections::{HashMap, HashSet};
8 +use rustc_hash::{FxHashMap, FxHashSet};
9
10 use react_compiler_diagnostics::{
11 CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -53,7 +53,7 @@ fn check_no_freezing_known_mutable_functions(
53 env: &Environment,
54 ) -> Vec<CompilerDiagnostic> {
55 // Maps an identifier to the mutation effect that makes it "known mutable"
56 - let mut context_mutation_effects: HashMap<IdentifierId, MutationInfo> = HashMap::new();
56 + let mut context_mutation_effects: FxHashMap<IdentifierId, MutationInfo> = FxHashMap::default();
57 let mut diagnostics: Vec<CompilerDiagnostic> = Vec::new();
58
59 for (_block_id, block) in &func.body.blocks {
@@ -83,7 +83,7 @@ fn check_no_freezing_known_mutable_functions(
83 InstructionValue::FunctionExpression { lowered_func, .. } => {
84 let inner_function = &functions[lowered_func.func.0 as usize];
85 if let Some(ref aliasing_effects) = inner_function.aliasing_effects {
86 - let context_ids: HashSet<IdentifierId> = inner_function
86 + let context_ids: FxHashSet<IdentifierId> = inner_function
87 .context
88 .iter()
89 .map(|place| place.identifier)
@@ -170,7 +170,7 @@ fn check_no_freezing_known_mutable_functions(
170 /// If an operand with Effect::Freeze is a known-mutable function, emit a diagnostic.
171 fn check_operand_for_freeze_violation(
172 operand: &Place,
173 - context_mutation_effects: &HashMap<IdentifierId, MutationInfo>,
173 + context_mutation_effects: &FxHashMap<IdentifierId, MutationInfo>,
174 identifiers: &[Identifier],
175 diagnostics: &mut Vec<CompilerDiagnostic>,
176 ) {
compiler/crates/react_compiler_validation/src/validate_no_ref_access_in_render.rs
+8 -7
@@ -1,4 +1,4 @@
1 -use std::collections::{HashMap, HashSet};
1 +use rustc_hash::{FxHashMap, FxHashSet};
2
3 use react_compiler_diagnostics::{
4 CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -270,16 +270,16 @@ fn join_ref_access_types_many(types: &[RefAccessType]) -> RefAccessType {
270
271 struct Env {
272 changed: bool,
273 - data: HashMap<IdentifierId, RefAccessType>,
274 - temporaries: HashMap<IdentifierId, Place>,
273 + data: FxHashMap<IdentifierId, RefAccessType>,
274 + temporaries: FxHashMap<IdentifierId, Place>,
275 }
276
277 impl Env {
278 fn new() -> Self {
279 Self {
280 changed: false,
281 - data: HashMap::new(),
282 - temporaries: HashMap::new(),
281 + data: FxHashMap::default(),
282 + temporaries: FxHashMap::default(),
283 }
284 }
285
@@ -626,7 +626,7 @@ fn validate_no_ref_access_in_render_impl(
626 }
627
628 // Collect identifiers that are interpolated as JSX children
629 - let mut interpolated_as_jsx: HashSet<IdentifierId> = HashSet::new();
629 + let mut interpolated_as_jsx: FxHashSet<IdentifierId> = FxHashSet::default();
630 for (_, block) in &func.body.blocks {
631 for &instr_id in &block.instructions {
632 let instr = &func.instructions[instr_id.0 as usize];
@@ -890,7 +890,8 @@ fn validate_no_ref_access_in_render_impl(
890 * use the effects to determine what validation to apply.
891 * Track visited id:kind pairs to avoid duplicate errors.
892 */
893 - let mut visited_effects: HashSet<String> = HashSet::new();
893 + let mut visited_effects: FxHashSet<String> =
894 + FxHashSet::default();
895 for effect in effects {
896 let (place, validation) = match effect {
897 AliasingEffect::Freeze { value, .. } => {
compiler/crates/react_compiler_validation/src/validate_no_set_state_in_effects.rs
+10 -10
@@ -12,7 +12,7 @@
12 //!
13 //! Port of ValidateNoSetStateInEffects.ts.
14
15 -use std::collections::{HashMap, HashSet};
15 +use rustc_hash::{FxHashMap, FxHashSet};
16
17 use react_compiler_diagnostics::{
18 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory,
@@ -37,7 +37,7 @@ pub fn validate_no_set_state_in_effects(
37 let enable_allow_set_state_from_refs = env.config.enable_allow_set_state_from_refs_in_effects;
38
39 // Map from IdentifierId to the Place where the setState originated
40 - let mut set_state_functions: HashMap<IdentifierId, SetStateInfo> = HashMap::new();
40 + let mut set_state_functions: FxHashMap<IdentifierId, SetStateInfo> = FxHashMap::default();
41 let mut errors = CompilerError::new();
42
43 for (_block_id, block) in &func.body.blocks {
@@ -246,7 +246,7 @@ fn push_error(errors: &mut CompilerError, info: &SetStateInfo, enable_verbose: b
246 /// Recursively collect all Place identifiers from a destructure pattern.
247 fn collect_destructure_places(
248 pattern: &react_compiler_hir::Pattern,
249 - ref_derived_values: &mut HashSet<IdentifierId>,
249 + ref_derived_values: &mut FxHashSet<IdentifierId>,
250 ) {
251 match pattern {
252 react_compiler_hir::Pattern::Array(arr) => {
@@ -279,7 +279,7 @@ fn collect_destructure_places(
279
280 fn is_derived_from_ref(
281 id: IdentifierId,
282 - ref_derived_values: &HashSet<IdentifierId>,
282 + ref_derived_values: &FxHashSet<IdentifierId>,
283 identifiers: &[Identifier],
284 types: &[Type],
285 ) -> bool {
@@ -306,12 +306,12 @@ fn collect_operands(value: &InstructionValue, functions: &[HirFunction]) -> Vec<
306 fn create_ref_controlled_block_checker(
307 func: &HirFunction,
308 next_block_id_counter: u32,
309 - ref_derived_values: &HashSet<IdentifierId>,
309 + ref_derived_values: &FxHashSet<IdentifierId>,
310 identifiers: &[Identifier],
311 types: &[Type],
312 -) -> Result<HashMap<BlockId, bool>, CompilerDiagnostic> {
312 +) -> Result<FxHashMap<BlockId, bool>, CompilerDiagnostic> {
313 let post_dominators = compute_post_dominator_tree(func, next_block_id_counter, false)?;
314 - let mut cache: HashMap<BlockId, bool> = HashMap::new();
314 + let mut cache: FxHashMap<BlockId, bool> = FxHashMap::default();
315
316 for (block_id, _block) in &func.body.blocks {
317 let frontier = post_dominator_frontier(func, &post_dominators, *block_id);
@@ -365,7 +365,7 @@ fn create_ref_controlled_block_checker(
365 /// Tracks ref-derived values to allow setState when the value being set comes from a ref.
366 fn get_set_state_call(
367 func: &HirFunction,
368 - set_state_functions: &mut HashMap<IdentifierId, SetStateInfo>,
368 + set_state_functions: &mut FxHashMap<IdentifierId, SetStateInfo>,
369 identifiers: &[Identifier],
370 types: &[Type],
371 functions: &[HirFunction],
@@ -373,7 +373,7 @@ fn get_set_state_call(
373 next_block_id_counter: u32,
374 source_code: Option<&str>,
375 ) -> Result<Option<SetStateInfo>, CompilerDiagnostic> {
376 - let mut ref_derived_values: HashSet<IdentifierId> = HashSet::new();
376 + let mut ref_derived_values: FxHashSet<IdentifierId> = FxHashSet::default();
377
378 // First pass: collect ref-derived values (needed before building control dominator checker)
379 // We do a pre-pass to seed ref_derived_values so the control dominator checker has them.
@@ -432,7 +432,7 @@ fn get_set_state_call(
432 types,
433 )?
434 } else {
435 - HashMap::new()
435 + FxHashMap::default()
436 };
437
438 let is_ref_controlled_block = |block_id: BlockId| -> bool {
compiler/crates/react_compiler_validation/src/validate_no_set_state_in_render.rs
+4 -4
@@ -7,7 +7,7 @@
7 //!
8 //! Port of ValidateNoSetStateInRender.ts.
9
10 -use std::collections::HashSet;
10 +use rustc_hash::FxHashSet;
11
12 use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory};
13 use react_compiler_hir::dominator::compute_unconditional_blocks;
@@ -18,7 +18,7 @@ pub fn validate_no_set_state_in_render(
18 func: &HirFunction,
19 env: &mut Environment,
20 ) -> Result<(), CompilerDiagnostic> {
21 - let mut unconditional_set_state_functions: HashSet<IdentifierId> = HashSet::new();
21 + let mut unconditional_set_state_functions: FxHashSet<IdentifierId> = FxHashSet::default();
22 let next_block_id = env.next_block_id().0;
23 let diagnostics = validate_impl(
24 func,
@@ -52,9 +52,9 @@ fn validate_impl(
52 functions: &[HirFunction],
53 next_block_id_counter: u32,
54 enable_use_keyed_state: bool,
55 - unconditional_set_state_functions: &mut HashSet<IdentifierId>,
55 + unconditional_set_state_functions: &mut FxHashSet<IdentifierId>,
56 ) -> Result<Vec<CompilerDiagnostic>, CompilerDiagnostic> {
57 - let unconditional_blocks: HashSet<BlockId> =
57 + let unconditional_blocks: FxHashSet<BlockId> =
58 compute_unconditional_blocks(func, next_block_id_counter)?;
59 let mut active_manual_memo_id: Option<u32> = None;
60 let mut errors: Vec<CompilerDiagnostic> = Vec::new();
compiler/crates/react_compiler_validation/src/validate_preserved_manual_memoization.rs
+14 -14
@@ -9,7 +9,7 @@
9 //! accurately preserved, and that no originally memoized values became
10 //! unmemoized in the output.
11
12 -use std::collections::{HashMap, HashSet};
12 +use rustc_hash::{FxHashMap, FxHashSet};
13
14 use react_compiler_diagnostics::{
15 CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -25,11 +25,11 @@ use react_compiler_hir::{
25 /// State tracked during manual memo validation within a StartMemoize..FinishMemoize range.
26 struct ManualMemoBlockState {
27 /// Reassigned temporaries (declaration_id -> set of identifier ids that were reassigned to it).
28 - reassignments: HashMap<DeclarationId, HashSet<IdentifierId>>,
28 + reassignments: FxHashMap<DeclarationId, FxHashSet<IdentifierId>>,
29 /// Source location of the StartMemoize instruction.
30 loc: Option<SourceLocation>,
31 /// Declarations produced within this manual memo block.
32 - decls: HashSet<DeclarationId>,
32 + decls: FxHashSet<DeclarationId>,
33 /// Normalized deps from source (useMemo/useCallback dep array).
34 deps_from_source: Option<Vec<ManualMemoDependency>>,
35 /// Manual memo id from StartMemoize.
@@ -41,11 +41,11 @@ struct VisitorState<'a> {
41 env: &'a mut Environment,
42 manual_memo_state: Option<ManualMemoBlockState>,
43 /// Completed (non-pruned) scope IDs.
44 - scopes: HashSet<ScopeId>,
44 + scopes: FxHashSet<ScopeId>,
45 /// Completed pruned scope IDs.
46 - pruned_scopes: HashSet<ScopeId>,
46 + pruned_scopes: FxHashSet<ScopeId>,
47 /// Map from identifier ID to its normalized manual memo dependency.
48 - temporaries: HashMap<IdentifierId, ManualMemoDependency>,
48 + temporaries: FxHashMap<IdentifierId, ManualMemoDependency>,
49 }
50
51 /// Validate that manual memoization (useMemo/useCallback) is preserved.
@@ -59,9 +59,9 @@ pub fn validate_preserved_manual_memoization(func: &ReactiveFunction, env: &mut
59 let mut state = VisitorState {
60 env,
61 manual_memo_state: None,
62 - scopes: HashSet::new(),
63 - pruned_scopes: HashSet::new(),
64 - temporaries: HashMap::new(),
62 + scopes: FxHashSet::default(),
63 + pruned_scopes: FxHashSet::default(),
64 + temporaries: FxHashMap::default(),
65 };
66 visit_block(&func.body, &mut state);
67 }
@@ -203,10 +203,10 @@ fn visit_instruction(instr: &ReactiveInstruction, state: &mut VisitorState) {
203
204 state.manual_memo_state = Some(ManualMemoBlockState {
205 loc: instr.loc,
206 - decls: HashSet::new(),
206 + decls: FxHashSet::default(),
207 deps_from_source,
208 manual_memo_id: *manual_memo_id,
209 - reassignments: HashMap::new(),
209 + reassignments: FxHashMap::default(),
210 });
211
212 // Check that each dependency's scope has completed before the memo
@@ -518,7 +518,7 @@ fn destructure_lvalue_places(pattern: &react_compiler_hir::Pattern) -> Vec<&Plac
518 /// Check if an identifier is unmemoized (has a scope that hasn't completed).
519 fn is_unmemoized(
520 id: IdentifierId,
521 - completed_scopes: &HashSet<ScopeId>,
521 + completed_scopes: &FxHashSet<ScopeId>,
522 identifiers: &[Identifier],
523 ) -> bool {
524 let ident = &identifiers[id.0 as usize];
@@ -675,8 +675,8 @@ fn get_compare_dependency_result_description(result: CompareDependencyResult) ->
675 fn validate_inferred_dep(
676 dep_id: IdentifierId,
677 dep_path: &[DependencyPathEntry],
678 - temporaries: &HashMap<IdentifierId, ManualMemoDependency>,
679 - decls_within_memo_block: &HashSet<DeclarationId>,
678 + temporaries: &FxHashMap<IdentifierId, ManualMemoDependency>,
679 + decls_within_memo_block: &FxHashSet<DeclarationId>,
680 valid_deps_in_memo_block: &[ManualMemoDependency],
681 env: &mut Environment,
682 memo_location: Option<SourceLocation>,
compiler/crates/react_compiler_validation/src/validate_static_components.rs
+3 -3
@@ -9,7 +9,7 @@
9 //!
10 //! Port of ValidateStaticComponents.ts.
11
12 -use std::collections::HashMap;
12 +use rustc_hash::FxHashMap;
13
14 use react_compiler_diagnostics::{
15 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation,
@@ -22,8 +22,8 @@ use react_compiler_hir::{HirFunction, IdentifierId, InstructionValue, JsxTag};
22 /// Called via `env.logErrors()` pattern in Pipeline.ts.
23 pub fn validate_static_components(func: &HirFunction) -> CompilerError {
24 let mut error = CompilerError::new();
25 - let mut known_dynamic_components: HashMap<IdentifierId, Option<SourceLocation>> =
26 - HashMap::new();
25 + let mut known_dynamic_components: FxHashMap<IdentifierId, Option<SourceLocation>> =
26 + FxHashMap::default();
27
28 for (_block_id, block) in &func.body.blocks {
29 // Process phis: propagate dynamic component knowledge through phi nodes
compiler/crates/react_compiler_validation/src/validate_use_memo.rs
+10 -10
@@ -1,4 +1,4 @@
1 -use std::collections::{HashMap, HashSet};
1 +use rustc_hash::{FxHashMap, FxHashSet};
2
3 use react_compiler_diagnostics::{
4 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation,
@@ -38,11 +38,11 @@ fn validate_use_memo_impl(
38 validate_no_void_use_memo: bool,
39 ) -> CompilerError {
40 let mut void_memo_errors = CompilerError::new();
41 - let mut use_memos: HashSet<IdentifierId> = HashSet::new();
42 - let mut react: HashSet<IdentifierId> = HashSet::new();
43 - let mut func_exprs: HashMap<IdentifierId, FuncExprInfo> = HashMap::new();
44 - let mut unused_use_memos: HashMap<IdentifierId, (SourceLocation, Option<String>)> =
45 - HashMap::new();
41 + let mut use_memos: FxHashSet<IdentifierId> = FxHashSet::default();
42 + let mut react: FxHashSet<IdentifierId> = FxHashSet::default();
43 + let mut func_exprs: FxHashMap<IdentifierId, FuncExprInfo> = FxHashMap::default();
44 + let mut unused_use_memos: FxHashMap<IdentifierId, (SourceLocation, Option<String>)> =
45 + FxHashMap::default();
46
47 for (_block_id, block) in &func.body.blocks {
48 for &instr_id in &block.instructions {
@@ -157,9 +157,9 @@ fn handle_possible_use_memo_call(
157 functions: &[HirFunction],
158 errors: &mut CompilerError,
159 void_memo_errors: &mut CompilerError,
160 - use_memos: &HashSet<IdentifierId>,
161 - func_exprs: &HashMap<IdentifierId, FuncExprInfo>,
162 - unused_use_memos: &mut HashMap<IdentifierId, (SourceLocation, Option<String>)>,
160 + use_memos: &FxHashSet<IdentifierId>,
161 + func_exprs: &FxHashMap<IdentifierId, FuncExprInfo>,
162 + unused_use_memos: &mut FxHashMap<IdentifierId, (SourceLocation, Option<String>)>,
163 callee: &Place,
164 args: &[PlaceOrSpread],
165 lvalue: &Place,
@@ -254,7 +254,7 @@ fn handle_possible_use_memo_call(
254 }
255
256 fn validate_no_context_variable_assignment(func: &HirFunction, errors: &mut CompilerError) {
257 - let context: HashSet<IdentifierId> =
257 + let context: FxHashSet<IdentifierId> =
258 func.context.iter().map(|place| place.identifier).collect();
259
260 for (_block_id, block) in &func.body.blocks {