[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.
This commit is contained in:
Boshen
2026-06-22 14:30:43 -07:00
committed by GitHub
parent bb3548eff8
commit 8e28314c42
89 changed files with 1036 additions and 961 deletions
+12
View File
@@ -287,6 +287,7 @@ dependencies = [
"react_compiler_ssa",
"react_compiler_typeinference",
"react_compiler_validation",
"rustc-hash",
"serde",
"serde_json",
]
@@ -297,6 +298,7 @@ version = "0.1.0"
dependencies = [
"indexmap",
"react_compiler_diagnostics",
"rustc-hash",
"serde",
"serde-transcode",
"serde_json",
@@ -308,6 +310,7 @@ dependencies = [
name = "react_compiler_diagnostics"
version = "0.1.0"
dependencies = [
"rustc-hash",
"serde",
]
@@ -317,6 +320,7 @@ version = "0.1.0"
dependencies = [
"indexmap",
"react_compiler_diagnostics",
"rustc-hash",
"serde",
"serde_json",
]
@@ -332,6 +336,7 @@ dependencies = [
"react_compiler_optimization",
"react_compiler_ssa",
"react_compiler_utils",
"rustc-hash",
]
[[package]]
@@ -342,6 +347,7 @@ dependencies = [
"react_compiler_ast",
"react_compiler_diagnostics",
"react_compiler_hir",
"rustc-hash",
"serde_json",
]
@@ -367,6 +373,7 @@ dependencies = [
"react_compiler_hir",
"react_compiler_lowering",
"react_compiler_ssa",
"rustc-hash",
]
[[package]]
@@ -378,6 +385,7 @@ dependencies = [
"react_compiler_ast",
"react_compiler_diagnostics",
"react_compiler_hir",
"rustc-hash",
"serde_json",
]
@@ -388,6 +396,7 @@ dependencies = [
"indexmap",
"react_compiler_diagnostics",
"react_compiler_hir",
"rustc-hash",
]
[[package]]
@@ -397,6 +406,7 @@ dependencies = [
"react_compiler_diagnostics",
"react_compiler_hir",
"react_compiler_ssa",
"rustc-hash",
]
[[package]]
@@ -404,6 +414,7 @@ name = "react_compiler_utils"
version = "0.1.0"
dependencies = [
"indexmap",
"rustc-hash",
]
[[package]]
@@ -413,6 +424,7 @@ dependencies = [
"indexmap",
"react_compiler_diagnostics",
"react_compiler_hir",
"rustc-hash",
]
[[package]]
@@ -15,5 +15,6 @@ react_compiler_ssa = { path = "../react_compiler_ssa" }
react_compiler_typeinference = { path = "../react_compiler_typeinference" }
react_compiler_validation = { path = "../react_compiler_validation" }
indexmap = "2"
rustc-hash = "2"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_ast::common::BaseNode;
use react_compiler_ast::declarations::{
@@ -72,9 +72,9 @@ pub struct ProgramContext {
pub debug_enabled: bool,
// Internal state
already_compiled: HashSet<u32>,
known_referenced_names: HashSet<String>,
imports: HashMap<String, HashMap<String, NonLocalImportSpecifier>>,
already_compiled: FxHashSet<u32>,
known_referenced_names: FxHashSet<String>,
imports: FxHashMap<String, FxHashMap<String, NonLocalImportSpecifier>>,
}
impl ProgramContext {
@@ -104,9 +104,9 @@ impl ProgramContext {
renames: Vec::new(),
timing: TimingData::new(profiling),
debug_enabled,
already_compiled: HashSet::new(),
known_referenced_names: HashSet::new(),
imports: HashMap::new(),
already_compiled: FxHashSet::default(),
known_referenced_names: FxHashSet::default(),
imports: FxHashMap::default(),
}
}
@@ -230,13 +230,13 @@ impl ProgramContext {
}
/// Get the set of known referenced names for seeding per-function Environment UID generation.
pub fn known_referenced_names(&self) -> &HashSet<String> {
pub fn known_referenced_names(&self) -> &FxHashSet<String> {
&self.known_referenced_names
}
/// Merge UID names generated during a function compilation back into the program context,
/// so subsequent function compilations avoid collisions.
pub fn merge_uid_known_names(&mut self, names: &HashSet<String>) {
pub fn merge_uid_known_names(&mut self, names: &FxHashSet<String>) {
self.known_referenced_names.extend(names.iter().cloned());
}
@@ -259,7 +259,7 @@ impl ProgramContext {
}
/// Get an immutable view of the generated imports.
pub fn imports(&self) -> &HashMap<String, HashMap<String, NonLocalImportSpecifier>> {
pub fn imports(&self) -> &FxHashMap<String, FxHashMap<String, NonLocalImportSpecifier>> {
&self.imports
}
}
@@ -274,7 +274,7 @@ pub fn validate_restricted_imports(
Some(b) if !b.is_empty() => b,
_ => return None,
};
let restricted: HashSet<&str> = blocklisted.iter().map(|s| s.as_str()).collect();
let restricted: FxHashSet<&str> = blocklisted.iter().map(|s| s.as_str()).collect();
let mut error = CompilerError::new();
for stmt in &program.body {
@@ -326,7 +326,7 @@ pub fn add_imports_to_program(program: &mut Program, context: &ProgramContext) {
}
// Collect existing non-namespaced imports by module name
let existing_import_indices: HashMap<String, usize> = program
let existing_import_indices: FxHashMap<String, usize> = program
.body
.iter()
.enumerate()
@@ -8,6 +8,7 @@
//! Analogous to TS `Pipeline.ts` (`compileFn` → `run` → `runWithEnvironment`).
//! Currently runs BuildHIR (lowering) and PruneMaybeThrows.
use indexmap::IndexMap;
use react_compiler_ast::scope::ScopeInfo;
use react_compiler_diagnostics::CompilerError;
use react_compiler_hir::ReactFunctionType;
@@ -15,6 +16,7 @@ use react_compiler_hir::environment::Environment;
use react_compiler_hir::environment::OutputMode;
use react_compiler_hir::environment_config::EnvironmentConfig;
use react_compiler_lowering::FunctionNode;
use rustc_hash::{FxBuildHasher, FxHashMap};
use super::compile_result::CodegenFunction;
use super::compile_result::CompilerErrorDetailInfo;
@@ -1229,25 +1231,23 @@ pub fn compile_outlined_fn(
fn build_outlined_scope_info(
func: &mut react_compiler_ast::statements::FunctionDeclaration,
) -> react_compiler_ast::scope::ScopeInfo {
use std::collections::HashMap;
use react_compiler_ast::scope::*;
let mut pos: u32 = 1; // reserve 0 for the function itself
func.base.start = Some(0);
let mut fn_bindings: HashMap<String, BindingId> = HashMap::new();
let mut fn_bindings: FxHashMap<String, BindingId> = FxHashMap::default();
let mut bindings_list: Vec<BindingData> = Vec::new();
let mut ref_to_binding: indexmap::IndexMap<u32, BindingId> = indexmap::IndexMap::new();
let mut ref_to_binding: IndexMap<u32, BindingId, FxBuildHasher> = IndexMap::default();
// Helper to add a binding
let _add_binding =
|name: &str,
kind: BindingKind,
p: u32,
fn_bindings: &mut HashMap<String, BindingId>,
fn_bindings: &mut FxHashMap<String, BindingId>,
bindings_list: &mut Vec<BindingData>,
ref_to_binding: &mut indexmap::IndexMap<u32, BindingId>| {
ref_to_binding: &mut IndexMap<u32, BindingId, FxBuildHasher>| {
if fn_bindings.contains_key(name) {
// Already exists, just add reference
let bid = fn_bindings[name];
@@ -1296,7 +1296,7 @@ fn build_outlined_scope_info(
id: ScopeId(0),
parent: None,
kind: ScopeKind::Program,
bindings: HashMap::new(),
bindings: FxHashMap::default(),
};
let fn_scope = ScopeData {
id: ScopeId(1),
@@ -1305,21 +1305,21 @@ fn build_outlined_scope_info(
bindings: fn_bindings,
};
let mut node_to_scope: HashMap<u32, ScopeId> = HashMap::new();
let mut node_to_scope: FxHashMap<u32, ScopeId> = FxHashMap::default();
node_to_scope.insert(0, ScopeId(1));
// Mirror position maps into node-ID maps for outlined functions
let mut node_id_to_scope: HashMap<u32, ScopeId> = HashMap::new();
let mut node_id_to_scope: FxHashMap<u32, ScopeId> = FxHashMap::default();
node_id_to_scope.insert(0, ScopeId(1));
let ref_node_id_to_binding: indexmap::IndexMap<u32, BindingId> =
let ref_node_id_to_binding: IndexMap<u32, BindingId, FxBuildHasher> =
ref_to_binding.iter().map(|(&k, &v)| (k, v)).collect();
ScopeInfo {
scopes: vec![program_scope, fn_scope],
bindings: bindings_list,
node_to_scope,
node_to_scope_end: HashMap::new(),
reference_to_binding: indexmap::IndexMap::new(),
node_to_scope_end: FxHashMap::default(),
reference_to_binding: IndexMap::default(),
ref_node_id_to_binding,
node_id_to_scope,
program_scope: ScopeId(0),
@@ -1331,9 +1331,9 @@ fn outlined_assign_pattern_positions(
pattern: &mut react_compiler_ast::patterns::PatternLike,
pos: &mut u32,
kind: react_compiler_ast::scope::BindingKind,
fn_bindings: &mut std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
fn_bindings: &mut rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
bindings_list: &mut Vec<react_compiler_ast::scope::BindingData>,
ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
) {
use react_compiler_ast::patterns::PatternLike;
use react_compiler_ast::scope::*;
@@ -1432,9 +1432,9 @@ fn outlined_assign_pattern_positions(
fn outlined_assign_stmt_positions(
stmt: &mut react_compiler_ast::statements::Statement,
pos: &mut u32,
fn_bindings: &mut std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
fn_bindings: &mut rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
bindings_list: &mut Vec<react_compiler_ast::scope::BindingData>,
ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
) {
use react_compiler_ast::statements::Statement;
@@ -1477,8 +1477,8 @@ fn outlined_assign_stmt_positions(
fn outlined_assign_expr_positions(
expr: &mut react_compiler_ast::expressions::Expression,
pos: &mut u32,
fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
) {
use react_compiler_ast::expressions::*;
@@ -1538,8 +1538,8 @@ fn outlined_assign_expr_positions(
fn outlined_assign_jsx_name_positions(
name: &mut react_compiler_ast::jsx::JSXElementName,
pos: &mut u32,
fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
) {
match name {
react_compiler_ast::jsx::JSXElementName::JSXIdentifier(id) => {
@@ -1561,8 +1561,8 @@ fn outlined_assign_jsx_name_positions(
fn outlined_assign_jsx_member_positions(
member: &mut react_compiler_ast::jsx::JSXMemberExpression,
pos: &mut u32,
fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
) {
match &mut *member.object {
react_compiler_ast::jsx::JSXMemberExprObject::JSXIdentifier(id) => {
@@ -1583,8 +1583,8 @@ fn outlined_assign_jsx_member_positions(
fn outlined_assign_jsx_val_positions(
val: &mut react_compiler_ast::jsx::JSXAttributeValue,
pos: &mut u32,
fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
) {
match val {
react_compiler_ast::jsx::JSXAttributeValue::JSXExpressionContainer(c) => {
@@ -1608,8 +1608,8 @@ fn outlined_assign_jsx_val_positions(
fn outlined_assign_jsx_child_positions(
child: &mut react_compiler_ast::jsx::JSXChild,
pos: &mut u32,
fn_bindings: &std::collections::HashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut indexmap::IndexMap<u32, react_compiler_ast::scope::BindingId>,
fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,
ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,
) {
match child {
react_compiler_ast::jsx::JSXChild::JSXExpressionContainer(c) => {
@@ -14,8 +14,7 @@
//! 5. Processing each function through the compilation pipeline
//! 6. Applying compiled functions back to the AST
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_ast::File;
use react_compiler_ast::Program;
@@ -2084,9 +2083,9 @@ struct CompiledFnForReplacement {
fn get_functions_referenced_before_declaration(
program: &Program,
compiled_fns: &[CompiledFnForReplacement],
) -> HashSet<u32> {
) -> FxHashSet<u32> {
// Collect function names and their node_ids for compiled FunctionDeclarations
let mut fn_names: HashMap<String, u32> = HashMap::new();
let mut fn_names: FxHashMap<String, u32> = FxHashMap::default();
for compiled in compiled_fns {
if compiled.original_kind == OriginalFnKind::FunctionDeclaration {
if let Some(ref name) = compiled.fn_name {
@@ -2098,10 +2097,10 @@ fn get_functions_referenced_before_declaration(
}
if fn_names.is_empty() {
return HashSet::new();
return FxHashSet::default();
}
let mut referenced_before_decl: HashSet<u32> = HashSet::new();
let mut referenced_before_decl: FxHashSet<u32> = FxHashSet::default();
// Walk through program body in order. For each statement, check if it references
// any of the function names before the function's declaration.
@@ -2597,7 +2596,7 @@ fn apply_compiled_functions(
let referenced_before_decl = if has_gating {
get_functions_referenced_before_declaration(program, compiled_fns)
} else {
HashSet::new()
FxHashSet::default()
};
// For gated functions, we need to clone the original function expressions
@@ -11,7 +11,7 @@
//!
//! Analogous to TS `ValidateSourceLocations.ts`.
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_ast::common::SourceLocation as AstSourceLocation;
use react_compiler_ast::expressions::{
@@ -38,14 +38,14 @@ pub fn validate_source_locations(
let important_original = collect_important_original_locations(func);
// Step 2: Collect all locations from the generated AST
let mut generated = HashMap::<String, HashSet<String>>::new();
let mut generated = FxHashMap::<String, FxHashSet<String>>::default();
collect_generated_from_block(&codegen.body.body, &mut generated);
for outlined in &codegen.outlined {
collect_generated_from_block(&outlined.func.body.body, &mut generated);
}
// Step 3: Validate that all important locations are preserved
let strict_node_types: HashSet<&str> =
let strict_node_types: FxHashSet<&str> =
["VariableDeclaration", "VariableDeclarator", "Identifier"]
.into_iter()
.collect();
@@ -101,7 +101,7 @@ pub fn validate_source_locations(
struct ImportantLocation {
key: String,
loc: AstSourceLocation,
node_types: HashSet<&'static str>,
node_types: FxHashSet<&'static str>,
}
// ---- Location key ----
@@ -157,7 +157,7 @@ fn report_wrong_node_type(
env: &mut Environment,
loc: &AstSourceLocation,
expected_type: &str,
actual_types: &HashSet<String>,
actual_types: &FxHashSet<String>,
) {
let diag_loc = ast_to_diag_loc(loc);
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 {
fn collect_important_original_locations(
func: &FunctionNode<'_>,
) -> HashMap<String, ImportantLocation> {
let mut locations = HashMap::new();
) -> FxHashMap<String, ImportantLocation> {
let mut locations = FxHashMap::default();
// Note: TS uses func.traverse() which visits DESCENDANTS only, not the root
// function node itself. So we don't record the root function as important.
@@ -294,14 +294,14 @@ fn collect_important_original_locations(
fn record_important(
node_type: &'static str,
loc: &Option<AstSourceLocation>,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
if let Some(loc) = loc {
let key = location_key(loc);
if let Some(existing) = locations.get_mut(&key) {
existing.node_types.insert(node_type);
} else {
let mut node_types = HashSet::new();
let mut node_types = FxHashSet::default();
node_types.insert(node_type);
locations.insert(
key.clone(),
@@ -318,7 +318,7 @@ fn record_important(
fn collect_original_block(
stmts: &[Statement],
in_single_return_arrow: bool,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
for stmt in stmts {
collect_original_statement(stmt, in_single_return_arrow, locations);
@@ -328,7 +328,7 @@ fn collect_original_block(
fn collect_original_statement(
stmt: &Statement,
in_single_return_arrow: bool,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
// Record this statement if it's an important type
if let Some(type_name) = important_statement_type(stmt) {
@@ -476,7 +476,7 @@ fn collect_original_statement(
fn collect_original_var_declaration(
decl: &VariableDeclaration,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
for declarator in &decl.declarations {
// VariableDeclarator is an important type
@@ -490,7 +490,7 @@ fn collect_original_var_declaration(
fn collect_original_expression(
expr: &Expression,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
// Record this expression if it's an important type
if let Some(type_name) = important_expression_type(expr) {
@@ -667,7 +667,7 @@ fn collect_original_expression(
fn collect_original_arrow_children(
arrow: &ArrowFunctionExpression,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
for param in &arrow.params {
collect_original_pattern(param, locations);
@@ -685,7 +685,7 @@ fn collect_original_arrow_children(
fn collect_original_fn_expr_children(
func: &FunctionExpression,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
if let Some(id) = &func.id {
record_important("Identifier", &id.base.loc, locations);
@@ -698,7 +698,7 @@ fn collect_original_fn_expr_children(
fn collect_original_pattern(
pattern: &PatternLike,
locations: &mut HashMap<String, ImportantLocation>,
locations: &mut FxHashMap<String, ImportantLocation>,
) {
match pattern {
PatternLike::Identifier(id) => {
@@ -852,7 +852,7 @@ fn expression_loc(expr: &Expression) -> &Option<AstSourceLocation> {
fn collect_generated_from_block(
stmts: &[Statement],
locations: &mut HashMap<String, HashSet<String>>,
locations: &mut FxHashMap<String, FxHashSet<String>>,
) {
for stmt in stmts {
collect_generated_statement(stmt, locations);
@@ -862,7 +862,7 @@ fn collect_generated_from_block(
fn record_generated(
type_name: &str,
loc: &Option<AstSourceLocation>,
locations: &mut HashMap<String, HashSet<String>>,
locations: &mut FxHashMap<String, FxHashSet<String>>,
) {
if let Some(loc) = loc {
let key = location_key(loc);
@@ -873,7 +873,10 @@ fn record_generated(
}
}
fn collect_generated_statement(stmt: &Statement, locations: &mut HashMap<String, HashSet<String>>) {
fn collect_generated_statement(
stmt: &Statement,
locations: &mut FxHashMap<String, FxHashSet<String>>,
) {
// Record this statement's location
let type_name = statement_type_name(stmt);
record_generated(type_name, statement_loc(stmt), locations);
@@ -1008,7 +1011,7 @@ fn collect_generated_statement(stmt: &Statement, locations: &mut HashMap<String,
fn collect_generated_var_declaration(
decl: &VariableDeclaration,
locations: &mut HashMap<String, HashSet<String>>,
locations: &mut FxHashMap<String, FxHashSet<String>>,
) {
for declarator in &decl.declarations {
record_generated("VariableDeclarator", &declarator.base.loc, locations);
@@ -1021,7 +1024,7 @@ fn collect_generated_var_declaration(
fn collect_generated_expression(
expr: &Expression,
locations: &mut HashMap<String, HashSet<String>>,
locations: &mut FxHashMap<String, FxHashSet<String>>,
) {
let type_name = expression_type_name(expr);
record_generated(type_name, expression_loc(expr), locations);
@@ -1188,7 +1191,7 @@ fn collect_generated_expression(
fn collect_generated_pattern(
pattern: &PatternLike,
locations: &mut HashMap<String, HashSet<String>>,
locations: &mut FxHashMap<String, FxHashSet<String>>,
) {
match pattern {
PatternLike::Identifier(id) => {
@@ -9,6 +9,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] }
serde-transcode = "1"
indexmap = { version = "2", features = ["serde"] }
rustc-hash = "2"
[dev-dependencies]
walkdir = "2"
+12 -12
View File
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use rustc_hash::{FxBuildHasher, FxHashMap};
use indexmap::IndexMap;
use serde::Deserialize;
@@ -20,7 +20,7 @@ pub struct ScopeData {
pub kind: ScopeKind,
/// Bindings declared directly in this scope, keyed by name.
/// Maps to BindingId for lookup in the binding table.
pub bindings: HashMap<String, BindingId>,
pub bindings: FxHashMap<String, BindingId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -113,18 +113,18 @@ pub struct ScopeInfo {
/// **NOT for identity lookups** — use `node_id_to_scope` (via `resolve_scope_for_node`)
/// instead. Retained only for position-range containment queries
/// (e.g., "is reference R inside function scope S?").
pub node_to_scope: HashMap<u32, ScopeId>,
pub node_to_scope: FxHashMap<u32, ScopeId>,
/// Maps an AST node's start offset to the node's end offset.
/// Parallel to `node_to_scope` — used for position-range containment checks.
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub node_to_scope_end: HashMap<u32, u32>,
#[serde(default, skip_serializing_if = "FxHashMap::is_empty")]
pub node_to_scope_end: FxHashMap<u32, u32>,
/// **DEPRECATED** — retained only for Babel bridge JSON deserialization.
/// All backends pass empty maps; only the Babel bridge populates this.
/// Use `ref_node_id_to_binding` for all lookups and iteration.
#[serde(default)]
pub reference_to_binding: IndexMap<u32, BindingId>,
pub reference_to_binding: IndexMap<u32, BindingId, FxBuildHasher>,
/// Maps an identifier reference's node-ID to the binding it resolves to.
/// Only present for identifiers that resolve to a binding (not globals).
@@ -134,15 +134,15 @@ pub struct ScopeInfo {
skip_serializing_if = "IndexMap::is_empty",
rename = "refNodeIdToBinding"
)]
pub ref_node_id_to_binding: IndexMap<u32, BindingId>,
pub ref_node_id_to_binding: IndexMap<u32, BindingId, FxBuildHasher>,
/// Maps a scope-creating AST node's node-ID to the scope it creates.
#[serde(
default,
skip_serializing_if = "HashMap::is_empty",
skip_serializing_if = "FxHashMap::is_empty",
rename = "nodeIdToScope"
)]
pub node_id_to_scope: HashMap<u32, ScopeId>,
pub node_id_to_scope: FxHashMap<u32, ScopeId>,
/// The program-level (module) scope. Always scopes[0].
pub program_scope: ScopeId,
@@ -207,7 +207,7 @@ impl ScopeInfo {
name: &str,
ancestor: ScopeId,
) -> Option<&BindingData> {
let mut descendants = std::collections::HashSet::new();
let mut descendants = rustc_hash::FxHashSet::default();
descendants.insert(ancestor);
let mut changed = true;
while changed {
@@ -238,7 +238,7 @@ impl ScopeInfo {
name: &str,
ancestor: ScopeId,
) -> Option<(BindingId, &BindingData)> {
let mut descendants = std::collections::HashSet::new();
let mut descendants = rustc_hash::FxHashSet::default();
descendants.insert(ancestor);
let mut changed = true;
while changed {
@@ -306,7 +306,7 @@ impl ScopeInfo {
ancestor: ScopeId,
is_claimed: impl Fn(ScopeId) -> bool,
) -> Option<ScopeId> {
let mut descendants = std::collections::HashSet::new();
let mut descendants = rustc_hash::FxHashSet::default();
descendants.insert(ancestor);
let mut changed = true;
while changed {
@@ -4,4 +4,5 @@ version = "0.1.0"
edition = "2024"
[dependencies]
rustc-hash = "2"
serde = { version = "1", features = ["derive"] }
@@ -158,8 +158,8 @@ pub fn code_frame_columns(
let number_max_width = format!("{}", end).len();
// Build a lookup map for marker lines
let mut marker_map: std::collections::HashMap<usize, MarkerEntry> =
std::collections::HashMap::new();
let mut marker_map: rustc_hash::FxHashMap<usize, MarkerEntry> =
rustc_hash::FxHashMap::default();
let line_diff = end_line as usize - start_line as usize;
for (line_number, entry) in marker_lines_raw {
// Resolve placeholder lengths using actual source lines
@@ -6,5 +6,6 @@ edition = "2024"
[dependencies]
react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
indexmap = { version = "2", features = ["serde"] }
rustc-hash = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@@ -20,11 +20,11 @@ use crate::type_config::{
pub fn default_module_type_provider(module_name: &str) -> Option<TypeConfig> {
match module_name {
"react-hook-form" => Some(TypeConfig::Object(ObjectTypeConfig {
properties: Some(IndexMap::from([(
properties: Some(IndexMap::from_iter([(
"useForm".to_string(),
TypeConfig::Hook(HookTypeConfig {
return_type: Box::new(TypeConfig::Object(ObjectTypeConfig {
properties: Some(IndexMap::from([(
properties: Some(IndexMap::from_iter([(
"watch".to_string(),
TypeConfig::Function(FunctionTypeConfig {
positional_params: Vec::new(),
@@ -58,7 +58,7 @@ pub fn default_module_type_provider(module_name: &str) -> Option<TypeConfig> {
})),
"@tanstack/react-table" => Some(TypeConfig::Object(ObjectTypeConfig {
properties: Some(IndexMap::from([(
properties: Some(IndexMap::from_iter([(
"useReactTable".to_string(),
TypeConfig::Hook(HookTypeConfig {
positional_params: Some(Vec::new()),
@@ -77,7 +77,7 @@ pub fn default_module_type_provider(module_name: &str) -> Option<TypeConfig> {
})),
"@tanstack/react-virtual" => Some(TypeConfig::Object(ObjectTypeConfig {
properties: Some(IndexMap::from([(
properties: Some(IndexMap::from_iter([(
"useVirtualizer".to_string(),
TypeConfig::Hook(HookTypeConfig {
positional_params: Some(Vec::new()),
@@ -9,7 +9,7 @@
//! Uses the Cooper/Harvey/Kennedy algorithm from
//! https://www.cs.rice.edu/~keith/Embed/dom.pdf
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
@@ -24,7 +24,7 @@ use crate::{BlockId, HirFunction, Terminal};
pub struct PostDominator {
/// The exit node (synthetic node representing function exit).
pub exit: BlockId,
nodes: HashMap<BlockId, BlockId>,
nodes: FxHashMap<BlockId, BlockId>,
}
impl PostDominator {
@@ -50,8 +50,8 @@ impl PostDominator {
struct Node {
id: BlockId,
index: usize,
preds: HashSet<BlockId>,
succs: HashSet<BlockId>,
preds: FxHashSet<BlockId>,
succs: FxHashSet<BlockId>,
}
struct Graph {
@@ -59,7 +59,7 @@ struct Graph {
/// Nodes stored in iteration order (RPO for reverse graph).
nodes: Vec<Node>,
/// Map from BlockId to index in the nodes vec.
node_index: HashMap<BlockId, usize>,
node_index: FxHashMap<BlockId, usize>,
}
impl Graph {
@@ -112,7 +112,7 @@ fn build_reverse_graph(
let exit_id = BlockId(next_block_id_counter);
// Build initial nodes with reversed edges
let mut raw_nodes: HashMap<BlockId, Node> = HashMap::new();
let mut raw_nodes: FxHashMap<BlockId, Node> = FxHashMap::default();
// Create exit node
raw_nodes.insert(
@@ -120,15 +120,15 @@ fn build_reverse_graph(
Node {
id: exit_id,
index: 0,
preds: HashSet::new(),
succs: HashSet::new(),
preds: FxHashSet::default(),
succs: FxHashSet::default(),
},
);
for (id, block) in &func.body.blocks {
let successors = each_terminal_successor(&block.terminal);
let mut preds_set: HashSet<BlockId> = successors.into_iter().collect();
let succs_set: HashSet<BlockId> = block.preds.iter().copied().collect();
let mut preds_set: FxHashSet<BlockId> = successors.into_iter().collect();
let succs_set: FxHashSet<BlockId> = block.preds.iter().copied().collect();
let is_return = matches!(&block.terminal, Terminal::Return { .. });
let is_throw = matches!(&block.terminal, Terminal::Throw { .. });
@@ -150,7 +150,7 @@ fn build_reverse_graph(
}
// DFS from exit to compute RPO
let mut visited = HashSet::new();
let mut visited = FxHashSet::default();
let mut postorder = Vec::new();
dfs_postorder(exit_id, &raw_nodes, &mut visited, &mut postorder);
@@ -158,7 +158,7 @@ fn build_reverse_graph(
postorder.reverse();
let mut nodes = Vec::with_capacity(postorder.len());
let mut node_index = HashMap::new();
let mut node_index = FxHashMap::default();
for (idx, id) in postorder.into_iter().enumerate() {
let mut node = raw_nodes.remove(&id).unwrap();
node.index = idx;
@@ -175,8 +175,8 @@ fn build_reverse_graph(
fn dfs_postorder(
id: BlockId,
nodes: &HashMap<BlockId, Node>,
visited: &mut HashSet<BlockId>,
nodes: &FxHashMap<BlockId, Node>,
visited: &mut FxHashSet<BlockId>,
postorder: &mut Vec<BlockId>,
) {
if !visited.insert(id) {
@@ -196,8 +196,8 @@ fn dfs_postorder(
fn compute_immediate_dominators(
graph: &Graph,
) -> Result<HashMap<BlockId, BlockId>, CompilerDiagnostic> {
let mut doms: HashMap<BlockId, BlockId> = HashMap::new();
) -> Result<FxHashMap<BlockId, BlockId>, CompilerDiagnostic> {
let mut doms: FxHashMap<BlockId, BlockId> = FxHashMap::default();
doms.insert(graph.entry, graph.entry);
let mut changed = true;
@@ -249,7 +249,7 @@ fn compute_immediate_dominators(
Ok(doms)
}
fn intersect(a: BlockId, b: BlockId, graph: &Graph, doms: &HashMap<BlockId, BlockId>) -> BlockId {
fn intersect(a: BlockId, b: BlockId, graph: &Graph, doms: &FxHashMap<BlockId, BlockId>) -> BlockId {
let mut block1 = graph.get_node(a);
let mut block2 = graph.get_node(b);
while block1.id != block2.id {
@@ -277,10 +277,10 @@ pub fn post_dominator_frontier(
func: &HirFunction,
post_dominators: &PostDominator,
target_id: BlockId,
) -> HashSet<BlockId> {
) -> FxHashSet<BlockId> {
let target_post_dominators = post_dominators_of(func, post_dominators, target_id);
let mut visited = HashSet::new();
let mut frontier = HashSet::new();
let mut visited = FxHashSet::default();
let mut frontier = FxHashSet::default();
let mut to_visit: Vec<BlockId> = target_post_dominators.iter().copied().collect();
to_visit.push(target_id);
@@ -305,9 +305,9 @@ pub fn post_dominators_of(
func: &HirFunction,
post_dominators: &PostDominator,
target_id: BlockId,
) -> HashSet<BlockId> {
let mut result = HashSet::new();
let mut visited = HashSet::new();
) -> FxHashSet<BlockId> {
let mut result = FxHashSet::default();
let mut visited = FxHashSet::default();
let mut queue = vec![target_id];
while let Some(current_id) = queue.pop() {
@@ -339,8 +339,8 @@ pub fn post_dominators_of(
pub fn compute_unconditional_blocks(
func: &HirFunction,
next_block_id_counter: u32,
) -> Result<HashSet<BlockId>, CompilerDiagnostic> {
let mut unconditional = HashSet::new();
) -> Result<FxHashSet<BlockId>, CompilerDiagnostic> {
let mut unconditional = FxHashSet::default();
let dominators = compute_post_dominator_tree(func, next_block_id_counter, false)?;
let exit = dominators.exit;
let mut current: Option<BlockId> = Some(func.body.entry);
@@ -1,5 +1,4 @@
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::CompilerDiagnostic;
use react_compiler_diagnostics::CompilerError;
@@ -80,12 +79,12 @@ pub struct Environment {
// Used by codegen to filter type annotation renames — only rename identifiers
// whose node_id is in this set (type labels like ObjectTypeIndexer params
// are NOT in this set and should keep their original names).
pub reference_node_ids: HashSet<u32>,
pub reference_node_ids: FxHashSet<u32>,
// Hoisted identifiers: tracks which bindings have already been hoisted
// via DeclareContext to avoid duplicate hoisting.
// Uses u32 to avoid depending on react_compiler_ast types.
hoisted_identifiers: HashSet<u32>,
hoisted_identifiers: FxHashSet<u32>,
// Config flags for validation passes (kept for backwards compat with existing pipeline code)
pub validate_preserve_existing_memoization_guarantees: bool,
@@ -95,8 +94,8 @@ pub struct Environment {
// Type system registries
globals: GlobalRegistry,
pub shapes: ShapeRegistry,
module_types: HashMap<String, Option<Global>>,
module_type_errors: HashMap<String, Vec<String>>,
module_types: FxHashMap<String, Option<Global>>,
module_type_errors: FxHashMap<String, Vec<String>>,
// Environment configuration (feature flags, custom hooks, etc.)
pub config: EnvironmentConfig,
@@ -111,7 +110,7 @@ pub struct Environment {
// Known names for collision-aware UID generation. Lazily populated from
// identifiers on first use, then updated with each generated name.
// Matches Babel's generateUid behavior of checking hasBinding/hasReference.
uid_known_names: Option<HashSet<String>>,
uid_known_names: Option<FxHashSet<String>>,
}
/// An outlined function entry, stored on Environment during compilation.
@@ -164,7 +163,7 @@ impl Environment {
}
// Register reanimated module type when enabled
let mut module_types: HashMap<String, Option<Global>> = HashMap::new();
let mut module_types: FxHashMap<String, Option<Global>> = FxHashMap::default();
if config.enable_custom_type_definition_for_reanimated {
let reanimated_module_type = globals::get_reanimated_module_type(&mut shapes);
module_types.insert(
@@ -190,8 +189,8 @@ impl Environment {
instrument_gating_name: None,
hook_guard_name: None,
renames: Vec::new(),
reference_node_ids: HashSet::new(),
hoisted_identifiers: HashSet::new(),
reference_node_ids: FxHashSet::default(),
hoisted_identifiers: FxHashSet::default(),
validate_preserve_existing_memoization_guarantees: config
.validate_preserve_existing_memoization_guarantees,
validate_no_set_state_in_render: config.validate_no_set_state_in_render,
@@ -200,7 +199,7 @@ impl Environment {
globals: global_registry,
shapes,
module_types,
module_type_errors: HashMap::new(),
module_type_errors: FxHashMap::default(),
default_nonmutating_hook: None,
default_mutating_hook: None,
outlined_functions: Vec::new(),
@@ -237,8 +236,8 @@ impl Environment {
instrument_gating_name: self.instrument_gating_name.clone(),
hook_guard_name: self.hook_guard_name.clone(),
renames: Vec::new(),
reference_node_ids: HashSet::new(),
hoisted_identifiers: HashSet::new(),
reference_node_ids: FxHashSet::default(),
hoisted_identifiers: FxHashSet::default(),
validate_preserve_existing_memoization_guarantees: self
.validate_preserve_existing_memoization_guarantees,
validate_no_set_state_in_render: self.validate_no_set_state_in_render,
@@ -897,7 +896,7 @@ impl Environment {
// Lazily build the set of known names from existing identifiers.
// This approximates Babel's hasBinding/hasGlobal/hasReference checks.
if self.uid_known_names.is_none() {
let mut known = HashSet::new();
let mut known = FxHashSet::default();
for id in &self.identifiers {
if let Some(name) = &id.name {
known.insert(name.value().to_string());
@@ -929,7 +928,7 @@ impl Environment {
/// Seed the UID known names set with external names (e.g. from ProgramContext).
/// This ensures UID generation avoids names generated by previous function compilations,
/// matching Babel's behavior where the program scope accumulates all generated UIDs.
pub fn seed_uid_known_names(&mut self, names: &HashSet<String>) {
pub fn seed_uid_known_names(&mut self, names: &FxHashSet<String>) {
match &mut self.uid_known_names {
Some(existing) => existing.extend(names.iter().cloned()),
None => self.uid_known_names = Some(names.clone()),
@@ -937,7 +936,7 @@ impl Environment {
}
/// Return the UID known names accumulated during this compilation.
pub fn take_uid_known_names(&mut self) -> Option<HashSet<String>> {
pub fn take_uid_known_names(&mut self) -> Option<FxHashSet<String>> {
self.uid_known_names.take()
}
@@ -7,7 +7,8 @@
//!
//! Contains feature flags and custom hook definitions that control compiler behavior.
use std::collections::HashMap;
use indexmap::IndexMap;
use rustc_hash::{FxBuildHasher, FxHashMap};
use serde::{Deserialize, Serialize};
@@ -80,12 +81,12 @@ fn default_true() -> bool {
pub struct EnvironmentConfig {
/// Custom hook type definitions, keyed by hook name.
#[serde(default)]
pub custom_hooks: HashMap<String, HookConfig>,
pub custom_hooks: FxHashMap<String, HookConfig>,
/// Pre-resolved module type provider results.
/// Map from module name to TypeConfig, computed by the JS shim.
#[serde(default)]
pub module_type_provider: Option<indexmap::IndexMap<String, TypeConfig>>,
pub module_type_provider: Option<IndexMap<String, TypeConfig, FxBuildHasher>>,
/// Custom macro-like function names that should have their operands
/// memoized in the same scope (similar to fbt).
@@ -185,7 +186,7 @@ pub struct EnvironmentConfig {
impl Default for EnvironmentConfig {
fn default() -> Self {
Self {
custom_hooks: HashMap::new(),
custom_hooks: FxHashMap::default(),
enable_reset_cache_on_source_file_changes: None,
module_type_provider: None,
enable_preserve_existing_memoization_guarantees: true,
@@ -8,7 +8,7 @@
//! Provides `DEFAULT_SHAPES` (built-in object shapes) and `DEFAULT_GLOBALS`
//! (global variable types including React hooks and JS built-ins).
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use std::sync::LazyLock;
use crate::Effect;
@@ -31,14 +31,14 @@ pub type Global = Type;
/// Registry mapping global names to their types.
///
/// Supports two modes:
/// - **Builder mode** (`base=None`): wraps a single HashMap, used during
/// - **Builder mode** (`base=None`): wraps a single FxHashMap, used during
/// `build_default_globals` to construct the static base.
/// - **Overlay mode** (`base=Some`): holds a `&'static HashMap` base plus a small
/// extras HashMap. Lookups check extras first, then base. Inserts go into extras.
/// - **Overlay mode** (`base=Some`): holds a `&'static FxHashMap` base plus a small
/// extras FxHashMap. Lookups check extras first, then base. Inserts go into extras.
/// Cloning only copies the extras map (the base pointer is shared).
pub struct GlobalRegistry {
base: Option<&'static HashMap<String, Global>>,
entries: HashMap<String, Global>,
base: Option<&'static FxHashMap<String, Global>>,
entries: FxHashMap<String, Global>,
}
impl GlobalRegistry {
@@ -46,15 +46,15 @@ impl GlobalRegistry {
pub fn new() -> Self {
Self {
base: None,
entries: HashMap::new(),
entries: FxHashMap::default(),
}
}
/// Create an overlay-mode registry backed by a static base.
pub fn with_base(base: &'static HashMap<String, Global>) -> Self {
pub fn with_base(base: &'static FxHashMap<String, Global>) -> Self {
Self {
base: Some(base),
entries: HashMap::new(),
entries: FxHashMap::default(),
}
}
@@ -83,9 +83,9 @@ impl GlobalRegistry {
self.entries.keys().chain(base_keys)
}
/// Consume the registry and return the inner HashMap.
/// Consume the registry and return the inner FxHashMap.
/// Only valid in builder mode (no base).
pub fn into_inner(self) -> HashMap<String, Global> {
pub fn into_inner(self) -> FxHashMap<String, Global> {
debug_assert!(
self.base.is_none(),
"into_inner() called on overlay-mode GlobalRegistry"
@@ -108,8 +108,8 @@ impl Clone for GlobalRegistry {
// =============================================================================
struct BaseRegistries {
shapes: HashMap<String, ObjectShape>,
globals: HashMap<String, Global>,
shapes: FxHashMap<String, ObjectShape>,
globals: FxHashMap<String, Global>,
}
static BASE: LazyLock<BaseRegistries> = LazyLock::new(|| {
@@ -122,12 +122,12 @@ static BASE: LazyLock<BaseRegistries> = LazyLock::new(|| {
});
/// Get a reference to the static base shapes registry.
pub fn base_shapes() -> &'static HashMap<String, ObjectShape> {
pub fn base_shapes() -> &'static FxHashMap<String, ObjectShape> {
&BASE.shapes
}
/// Get a reference to the static base globals registry.
pub fn base_globals() -> &'static HashMap<String, Global> {
pub fn base_globals() -> &'static FxHashMap<String, Global> {
&BASE.globals
}
@@ -1332,7 +1332,7 @@ fn build_object_shape(shapes: &mut ShapeRegistry) {
None,
false,
);
let mut mixed_props = HashMap::new();
let mut mixed_props = FxHashMap::default();
mixed_props.insert("toString".to_string(), mixed_to_string);
mixed_props.insert("indexOf".to_string(), mixed_index_of);
mixed_props.insert("includes".to_string(), mixed_includes);
@@ -9,14 +9,14 @@ pub mod reactive;
pub mod type_config;
pub mod visitors;
use indexmap::IndexMap;
use indexmap::IndexSet;
use indexmap::{IndexMap, IndexSet};
pub use react_compiler_diagnostics::CompilerDiagnostic;
pub use react_compiler_diagnostics::ErrorCategory;
pub use react_compiler_diagnostics::GENERATED_SOURCE;
pub use react_compiler_diagnostics::Position;
pub use react_compiler_diagnostics::SourceLocation;
pub use reactive::*;
use rustc_hash::FxBuildHasher;
// =============================================================================
// ID newtypes
@@ -57,7 +57,7 @@ pub struct MutableRangeId(pub u32);
// =============================================================================
/// Wrapper around f64 that stores raw bytes for deterministic equality and hashing.
/// This allows use in HashMap keys and ensures NaN == NaN (bitwise comparison).
/// This allows use in FxHashMap keys and ensures NaN == NaN (bitwise comparison).
#[derive(Debug, Clone, Copy)]
pub struct FloatValue(u64);
@@ -190,7 +190,7 @@ pub enum ParamPattern {
#[derive(Debug, Clone)]
pub struct HIR {
pub entry: BlockId,
pub blocks: IndexMap<BlockId, BasicBlock>,
pub blocks: IndexMap<BlockId, BasicBlock, FxBuildHasher>,
}
/// Block kinds
@@ -222,7 +222,7 @@ pub struct BasicBlock {
pub id: BlockId,
pub instructions: Vec<InstructionId>,
pub terminal: Terminal,
pub preds: IndexSet<BlockId>,
pub preds: IndexSet<BlockId, FxBuildHasher>,
pub phis: Vec<Phi>,
}
@@ -230,7 +230,7 @@ pub struct BasicBlock {
#[derive(Debug, Clone)]
pub struct Phi {
pub place: Place,
pub operands: IndexMap<BlockId, Place>,
pub operands: IndexMap<BlockId, Place, FxBuildHasher>,
}
// =============================================================================
@@ -8,7 +8,7 @@
//! Defines the shape registry used by Environment to resolve property types
//! and function call signatures for built-in objects, hooks, and user-defined types.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use crate::Effect;
use crate::Type;
@@ -119,21 +119,21 @@ pub struct FunctionSignature {
/// Ported from TS `ObjectShape`.
#[derive(Debug, Clone)]
pub struct ObjectShape {
pub properties: HashMap<String, Type>,
pub properties: FxHashMap<String, Type>,
pub function_type: Option<FunctionSignature>,
}
/// Registry mapping shape IDs to their ObjectShape definitions.
///
/// Supports two modes:
/// - **Builder mode** (`base=None`): wraps a single HashMap, used during
/// - **Builder mode** (`base=None`): wraps a single FxHashMap, used during
/// `build_builtin_shapes` / `build_default_globals` to construct the static base.
/// - **Overlay mode** (`base=Some`): holds a `&'static HashMap` base plus a small
/// extras HashMap. Lookups check extras first, then base. Inserts go into extras.
/// - **Overlay mode** (`base=Some`): holds a `&'static FxHashMap` base plus a small
/// extras FxHashMap. Lookups check extras first, then base. Inserts go into extras.
/// Cloning only copies the extras map (the base pointer is shared).
pub struct ShapeRegistry {
base: Option<&'static HashMap<String, ObjectShape>>,
entries: HashMap<String, ObjectShape>,
base: Option<&'static FxHashMap<String, ObjectShape>>,
entries: FxHashMap<String, ObjectShape>,
}
impl ShapeRegistry {
@@ -141,15 +141,15 @@ impl ShapeRegistry {
pub fn new() -> Self {
Self {
base: None,
entries: HashMap::new(),
entries: FxHashMap::default(),
}
}
/// Create an overlay-mode registry backed by a static base.
pub fn with_base(base: &'static HashMap<String, ObjectShape>) -> Self {
pub fn with_base(base: &'static FxHashMap<String, ObjectShape>) -> Self {
Self {
base: Some(base),
entries: HashMap::new(),
entries: FxHashMap::default(),
}
}
@@ -163,9 +163,9 @@ impl ShapeRegistry {
self.entries.insert(key, value);
}
/// Consume the registry and return the inner HashMap.
/// Consume the registry and return the inner FxHashMap.
/// Only valid in builder mode (no base).
pub fn into_inner(self) -> HashMap<String, ObjectShape> {
pub fn into_inner(self) -> FxHashMap<String, ObjectShape> {
debug_assert!(
self.base.is_none(),
"into_inner() called on overlay-mode ShapeRegistry"
@@ -8,7 +8,7 @@
//! It also exports standalone formatting functions (format_loc, format_primitive, etc.)
//! that require no state.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_diagnostics::CompilerError;
use react_compiler_diagnostics::CompilerErrorOrDiagnostic;
@@ -223,8 +223,8 @@ pub fn format_value_reason(reason: ValueReason) -> &'static str {
/// like Places, Identifiers, Scopes, Types, InstructionValues, etc.
pub struct PrintFormatter<'a> {
pub env: &'a Environment,
pub seen_identifiers: HashSet<IdentifierId>,
pub seen_scopes: HashSet<ScopeId>,
pub seen_identifiers: FxHashSet<IdentifierId>,
pub seen_scopes: FxHashSet<ScopeId>,
pub output: Vec<String>,
pub indent_level: usize,
}
@@ -233,8 +233,8 @@ impl<'a> PrintFormatter<'a> {
pub fn new(env: &'a Environment) -> Self {
Self {
env,
seen_identifiers: HashSet::new(),
seen_scopes: HashSet::new(),
seen_identifiers: FxHashSet::default(),
seen_scopes: FxHashSet::default(),
output: Vec::new(),
indent_level: 0,
}
@@ -9,6 +9,7 @@
//! and `installTypeConfig` to describe module/function/hook types.
use indexmap::IndexMap;
use rustc_hash::FxBuildHasher;
use crate::Effect;
@@ -166,7 +167,7 @@ pub enum TypeConfig {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ObjectTypeConfig {
pub properties: Option<IndexMap<String, TypeConfig>>,
pub properties: Option<IndexMap<String, TypeConfig, FxBuildHasher>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use crate::environment::Environment;
use crate::{
@@ -1289,14 +1289,14 @@ pub struct ScopeBlockTraversal {
/// Live stack of active scopes
active_scopes: Vec<ScopeId>,
/// Map from block ID to scope block info
pub block_infos: HashMap<BlockId, ScopeBlockInfo>,
pub block_infos: FxHashMap<BlockId, ScopeBlockInfo>,
}
impl ScopeBlockTraversal {
pub fn new() -> Self {
ScopeBlockTraversal {
active_scopes: Vec::new(),
block_infos: HashMap::new(),
block_infos: FxHashMap::default(),
}
}
@@ -11,3 +11,4 @@ react_compiler_optimization = { path = "../react_compiler_optimization" }
react_compiler_ssa = { path = "../react_compiler_ssa" }
react_compiler_utils = { path = "../react_compiler_utils" }
indexmap = "2"
rustc-hash = "2"
@@ -9,7 +9,7 @@
//!
//! Ported from TypeScript `src/ReactiveScopes/AlignMethodCallScopes.ts`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::{EvaluationOrder, HirFunction, IdentifierId, InstructionValue, ScopeId};
@@ -25,7 +25,7 @@ use react_compiler_utils::DisjointSet;
/// Corresponds to TS `alignMethodCallScopes(fn: HIRFunction): void`.
pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) {
// Maps an identifier to the scope it should be assigned to (or None to remove scope)
let mut scope_mapping: HashMap<IdentifierId, Option<ScopeId>> = HashMap::new();
let mut scope_mapping: FxHashMap<IdentifierId, Option<ScopeId>> = FxHashMap::default();
let mut merged_scopes = DisjointSet::<ScopeId>::new();
// Phase 1: Walk instructions and collect scope relationships
@@ -74,9 +74,10 @@ pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) {
}
// Phase 2: Merge scope ranges for unioned scopes.
// Use a HashMap to accumulate min/max across all scopes mapping to the same root,
// Use a FxHashMap to accumulate min/max across all scopes mapping to the same root,
// matching TS behavior where root.range is updated in-place during iteration.
let mut range_updates: HashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> = HashMap::new();
let mut range_updates: FxHashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> =
FxHashMap::default();
merged_scopes.for_each(|scope_id, root_id| {
if scope_id == root_id {
@@ -93,7 +94,7 @@ pub fn align_method_call_scopes(func: &mut HirFunction, env: &mut Environment) {
});
// Save original scope range IDs before updating
let original_range_ids: HashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
let original_range_ids: FxHashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
.keys()
.map(|&root_id| {
let range_id = env.scopes[root_id.0 as usize].range.id;
@@ -9,8 +9,8 @@
//!
//! Ported from TypeScript `src/ReactiveScopes/AlignObjectMethodScopes.ts`.
use rustc_hash::{FxHashMap, FxHashSet};
use std::cmp;
use std::collections::{HashMap, HashSet};
use react_compiler_hir::environment::Environment;
use react_compiler_hir::{
@@ -26,7 +26,7 @@ use react_compiler_utils::DisjointSet;
/// instructions whose operands reference those methods. Returns a disjoint set
/// of scopes that must be merged.
fn find_scopes_to_merge(func: &HirFunction, env: &Environment) -> DisjointSet<ScopeId> {
let mut object_method_decls: HashSet<IdentifierId> = HashSet::new();
let mut object_method_decls: FxHashSet<IdentifierId> = FxHashSet::default();
let mut merged_scopes = DisjointSet::<ScopeId>::new();
for (_block_id, block) in &func.body.blocks {
@@ -99,9 +99,10 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment)
let mut merged_scopes = find_scopes_to_merge(func, env);
// Step 1: Merge affected scopes to their canonical root.
// Use a HashMap to accumulate min/max across all scopes mapping to the same root,
// Use a FxHashMap to accumulate min/max across all scopes mapping to the same root,
// matching TS behavior where root.range is updated in-place during iteration.
let mut range_updates: HashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> = HashMap::new();
let mut range_updates: FxHashMap<ScopeId, (EvaluationOrder, EvaluationOrder)> =
FxHashMap::default();
merged_scopes.for_each(|scope_id, root_id| {
if scope_id == root_id {
@@ -118,7 +119,7 @@ pub fn align_object_method_scopes(func: &mut HirFunction, env: &mut Environment)
});
// Save original scope range IDs before updating
let original_range_ids: HashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
let original_range_ids: FxHashMap<ScopeId, react_compiler_hir::MutableRangeId> = range_updates
.keys()
.map(|&root_id| {
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)
// Step 2: Repoint identifiers whose scopes were merged
// Build a map from old scope -> root scope for quick lookup
let mut scope_remap: HashMap<ScopeId, ScopeId> = HashMap::new();
let mut scope_remap: FxHashMap<ScopeId, ScopeId> = FxHashMap::default();
merged_scopes.for_each(|scope_id, root_id| {
if scope_id != root_id {
scope_remap.insert(scope_id, root_id);
@@ -22,8 +22,7 @@
//! instructions in each scope, the scopes must be aligned to block-scope
//! boundaries — we can't memoize half of a loop!
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_hir::BlockId;
use react_compiler_hir::BlockKind;
@@ -99,9 +98,9 @@ pub fn align_reactive_scopes_to_block_scopes_hir(func: &mut HirFunction, env: &m
env.scopes.iter().map(|s| s.range.clone()).collect();
let mut active_block_fallthrough_ranges: Vec<BlockFallthroughRange> = Vec::new();
let mut active_scopes: HashSet<ScopeId> = HashSet::new();
let mut seen: HashSet<ScopeId> = HashSet::new();
let mut value_block_nodes: HashMap<BlockId, ValueBlockNode> = HashMap::new();
let mut active_scopes: FxHashSet<ScopeId> = FxHashSet::default();
let mut seen: FxHashSet<ScopeId> = FxHashSet::default();
let mut value_block_nodes: FxHashMap<BlockId, ValueBlockNode> = FxHashMap::default();
let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();
@@ -301,8 +300,8 @@ fn record_place_id(
identifier_id: IdentifierId,
node: &Option<ValueBlockNode>,
env: &mut Environment,
active_scopes: &mut HashSet<ScopeId>,
seen: &mut HashSet<ScopeId>,
active_scopes: &mut FxHashSet<ScopeId>,
seen: &mut FxHashSet<ScopeId>,
) {
// Get the scope for this identifier, if active at this instruction
let scope_id = match env.identifiers[identifier_id.0 as usize].scope {
@@ -15,7 +15,7 @@
use indexmap::IndexMap;
use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
use react_compiler_hir::environment::Environment;
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::{
AliasingEffect, BlockId, Effect, EvaluationOrder, FunctionId, HIR, HirFunction, IdentifierId,
@@ -138,7 +138,7 @@ where
// Phase 2: Populate the Effect of each context variable to use in inferring
// the outer function. Corresponds to TS Phase 2 in lowerWithMutationAliasing.
let mut captured_or_mutated: HashSet<IdentifierId> = HashSet::new();
let mut captured_or_mutated: FxHashSet<IdentifierId> = FxHashSet::default();
for effect in &function_effects {
match effect {
AliasingEffect::Assign { from, .. }
@@ -208,7 +208,7 @@ fn placeholder_function() -> HirFunction {
context: Vec::new(),
body: HIR {
entry: BlockId(0),
blocks: IndexMap::new(),
blocks: IndexMap::default(),
},
instructions: Vec::new(),
generator: false,
@@ -11,10 +11,9 @@
//!
//! Ported from TypeScript `src/HIR/BuildReactiveScopeTerminalsHIR.ts`.
use std::collections::HashMap;
use std::collections::HashSet;
use indexmap::{IndexMap, IndexSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use indexmap::IndexMap;
use react_compiler_hir::BasicBlock;
use react_compiler_hir::BlockId;
use react_compiler_hir::EvaluationOrder;
@@ -38,7 +37,7 @@ use react_compiler_lowering::mark_predecessors;
/// Collect all unique scopes from places in the function that have non-empty ranges.
/// Corresponds to TS `getScopes(fn)`.
fn get_scopes(func: &HirFunction, env: &Environment) -> Vec<ScopeId> {
let mut scope_ids: HashSet<ScopeId> = HashSet::new();
let mut scope_ids: FxHashSet<ScopeId> = FxHashSet::default();
let mut visit_place = |identifier_id: IdentifierId| {
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
});
let mut rewrites: Vec<TerminalRewriteInfo> = Vec::new();
let mut fallthroughs: HashMap<ScopeId, BlockId> = HashMap::new();
let mut fallthroughs: FxHashMap<ScopeId, BlockId> = FxHashMap::default();
let mut active_items: Vec<ScopeId> = Vec::new();
for i in 0..items.len() {
@@ -222,7 +221,7 @@ fn handle_rewrite(
};
let curr_block_id = context.next_block_id;
let mut preds = indexmap::IndexSet::new();
let mut preds = IndexSet::default();
for &p in &context.next_preds {
preds.insert(p);
}
@@ -264,8 +263,8 @@ pub fn build_reactive_scope_terminals_hir(func: &mut HirFunction, env: &mut Envi
let mut queued_rewrites = collect_scope_rewrites(func, env);
// Step 2: Apply rewrites by splitting blocks
let mut rewritten_final_blocks: HashMap<BlockId, BlockId> = HashMap::new();
let mut next_blocks: IndexMap<BlockId, BasicBlock> = IndexMap::new();
let mut rewritten_final_blocks: FxHashMap<BlockId, BlockId> = FxHashMap::default();
let mut next_blocks: IndexMap<BlockId, BasicBlock, FxBuildHasher> = IndexMap::default();
// Reverse so we can pop from the end while traversing in ascending order
queued_rewrites.reverse();
@@ -300,7 +299,7 @@ pub fn build_reactive_scope_terminals_hir(func: &mut HirFunction, env: &mut Envi
}
if !context.rewrites.is_empty() {
let mut final_preds = indexmap::IndexSet::new();
let mut final_preds = IndexSet::default();
for &p in &context.next_preds {
final_preds.insert(p);
}
@@ -11,10 +11,9 @@
//! creation, aliasing, mutation, freezing, and error conditions for each
//! instruction and terminal in the HIR.
use std::collections::HashMap;
use std::collections::HashSet;
use indexmap::{IndexMap, IndexSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use indexmap::IndexSet;
use react_compiler_diagnostics::CompilerDiagnostic;
use react_compiler_diagnostics::CompilerDiagnosticDetail;
use react_compiler_diagnostics::ErrorCategory;
@@ -61,7 +60,7 @@ pub fn infer_mutation_aliasing_effects(
let mut initial_state = InferenceState::empty(env, is_function_expression);
// Map of blocks to the last (merged) incoming state that was processed
let mut states_by_block: HashMap<BlockId, InferenceState> = HashMap::new();
let mut states_by_block: FxHashMap<BlockId, InferenceState> = FxHashMap::default();
// Initialize context variables
for ctx_place in &func.context {
@@ -115,12 +114,12 @@ pub fn infer_mutation_aliasing_effects(
}
}
let mut queued_states: indexmap::IndexMap<BlockId, InferenceState> = indexmap::IndexMap::new();
let mut queued_states: IndexMap<BlockId, InferenceState, FxBuildHasher> = IndexMap::default();
// Queue helper
fn queue(
queued_states: &mut indexmap::IndexMap<BlockId, InferenceState>,
states_by_block: &HashMap<BlockId, InferenceState>,
queued_states: &mut IndexMap<BlockId, InferenceState, FxBuildHasher>,
states_by_block: &FxHashMap<BlockId, InferenceState>,
block_id: BlockId,
state: InferenceState,
) {
@@ -152,16 +151,16 @@ pub fn infer_mutation_aliasing_effects(
let non_mutating_spreads = find_non_mutated_destructure_spreads(func, env);
let mut context = Context {
interned_effects: HashMap::new(),
instruction_signature_cache: HashMap::new(),
catch_handlers: HashMap::new(),
interned_effects: FxHashMap::default(),
instruction_signature_cache: FxHashMap::default(),
catch_handlers: FxHashMap::default(),
is_function_expression,
hoisted_context_declarations,
non_mutating_spreads,
effect_value_id_cache: HashMap::new(),
function_values: HashMap::new(),
function_signature_cache: HashMap::new(),
aliasing_config_temp_cache: HashMap::new(),
effect_value_id_cache: FxHashMap::default(),
function_values: FxHashMap::default(),
function_signature_cache: FxHashMap::default(),
aliasing_config_temp_cache: FxHashMap::default(),
};
let mut iteration_count = 0;
@@ -262,11 +261,11 @@ impl ValueId {
#[derive(Debug, Clone)]
struct AbstractValue {
kind: ValueKind,
reason: IndexSet<ValueReason>,
reason: IndexSet<ValueReason, FxBuildHasher>,
}
fn hashset_of(r: ValueReason) -> IndexSet<ValueReason> {
let mut s = IndexSet::new();
fn hashset_of(r: ValueReason) -> IndexSet<ValueReason, FxBuildHasher> {
let mut s = IndexSet::default();
s.insert(r);
s
}
@@ -282,9 +281,9 @@ fn hashset_of(r: ValueReason) -> IndexSet<ValueReason> {
struct InferenceState {
is_function_expression: bool,
/// The kind of each value, based on its allocation site
values: HashMap<ValueId, AbstractValue>,
values: FxHashMap<ValueId, AbstractValue>,
/// The set of values pointed to by each identifier
variables: HashMap<IdentifierId, HashSet<ValueId>>,
variables: FxHashMap<IdentifierId, FxHashSet<ValueId>>,
/// Tracks uninitialized identifier access errors (matches TS invariant).
/// Uses Cell so it can be set from `&self` methods like `kind()`.
/// Stores (IdentifierId, usage_loc) where usage_loc is the source location
@@ -296,8 +295,8 @@ impl InferenceState {
fn empty(_env: &Environment, is_function_expression: bool) -> Self {
InferenceState {
is_function_expression,
values: HashMap::new(),
variables: HashMap::new(),
values: FxHashMap::default(),
variables: FxHashMap::default(),
uninitialized_access: std::cell::Cell::new(None),
}
}
@@ -342,7 +341,7 @@ impl InferenceState {
}
fn define(&mut self, place_id: IdentifierId, value_id: ValueId) {
let mut set = HashSet::new();
let mut set = FxHashSet::default();
set.insert(value_id);
self.variables.insert(place_id, set);
}
@@ -354,7 +353,7 @@ impl InferenceState {
// Create a stable value for uninitialized identifiers
// Use a deterministic ID based on the from identifier
let vid = ValueId(from.0 | 0x80000000);
let mut set = HashSet::new();
let mut set = FxHashSet::default();
set.insert(vid);
if !self.values.contains_key(&vid) {
self.values.insert(
@@ -380,7 +379,7 @@ impl InferenceState {
Some(v) => v.clone(),
None => return,
};
let merged: HashSet<ValueId> = prev_values.union(&new_values).copied().collect();
let merged: FxHashSet<ValueId> = prev_values.union(&new_values).copied().collect();
self.variables.insert(place, merged);
}
@@ -486,8 +485,8 @@ impl InferenceState {
}
fn merge(&self, other: &InferenceState) -> Option<InferenceState> {
let mut next_values: Option<HashMap<ValueId, AbstractValue>> = None;
let mut next_variables: Option<HashMap<IdentifierId, HashSet<ValueId>>> = None;
let mut next_values: Option<FxHashMap<ValueId, AbstractValue>> = None;
let mut next_variables: Option<FxHashMap<IdentifierId, FxHashSet<ValueId>>> = None;
// Merge values present in both
for (id, this_value) in &self.values {
@@ -521,7 +520,7 @@ impl InferenceState {
}
if has_new {
let nvars = next_variables.get_or_insert_with(|| self.variables.clone());
let merged: HashSet<ValueId> =
let merged: FxHashSet<ValueId> =
this_values.union(other_values).copied().collect();
nvars.insert(*id, merged);
}
@@ -550,9 +549,9 @@ impl InferenceState {
fn infer_phi(
&mut self,
phi_place_id: IdentifierId,
phi_operands: &indexmap::IndexMap<BlockId, Place>,
phi_operands: &IndexMap<BlockId, Place, FxBuildHasher>,
) {
let mut values: HashSet<ValueId> = HashSet::new();
let mut values: FxHashSet<ValueId> = FxHashSet::default();
for (_, operand) in phi_operands {
if let Some(operand_values) = self.variables.get(&operand.identifier) {
for v in operand_values {
@@ -567,7 +566,10 @@ impl InferenceState {
}
}
fn is_superset(a: &IndexSet<ValueReason>, b: &IndexSet<ValueReason>) -> bool {
fn is_superset(
a: &IndexSet<ValueReason, FxBuildHasher>,
b: &IndexSet<ValueReason, FxBuildHasher>,
) -> bool {
b.iter().all(|x| a.contains(x))
}
@@ -593,24 +595,24 @@ enum MutationResult {
// =============================================================================
struct Context {
interned_effects: HashMap<String, AliasingEffect>,
instruction_signature_cache: HashMap<u32, InstructionSignature>,
catch_handlers: HashMap<BlockId, Place>,
interned_effects: FxHashMap<String, AliasingEffect>,
instruction_signature_cache: FxHashMap<u32, InstructionSignature>,
catch_handlers: FxHashMap<BlockId, Place>,
is_function_expression: bool,
hoisted_context_declarations: HashMap<DeclarationId, Option<Place>>,
non_mutating_spreads: HashSet<IdentifierId>,
hoisted_context_declarations: FxHashMap<DeclarationId, Option<Place>>,
non_mutating_spreads: FxHashSet<IdentifierId>,
/// Cache of ValueIds keyed by effect hash, ensuring stable allocation-site identity
/// across fixpoint iterations. Mirrors TS `effectInstructionValueCache`.
effect_value_id_cache: HashMap<String, ValueId>,
effect_value_id_cache: FxHashMap<String, ValueId>,
/// Maps ValueId to FunctionId for function expressions, so we can look up
/// locally-declared functions when processing Apply effects.
function_values: HashMap<ValueId, FunctionId>,
function_values: FxHashMap<ValueId, FunctionId>,
/// Cache of function expression signatures, keyed by FunctionId
function_signature_cache: HashMap<FunctionId, AliasingSignature>,
function_signature_cache: FxHashMap<FunctionId, AliasingSignature>,
/// Cache of temporary places created for aliasing signature config temporaries.
/// Keyed by (lvalue_identifier_id, temp_name) to ensure stable allocation
/// across fixpoint iterations.
aliasing_config_temp_cache: HashMap<(IdentifierId, String), Place>,
aliasing_config_temp_cache: FxHashMap<(IdentifierId, String), Place>,
}
impl Context {
@@ -785,11 +787,11 @@ fn merge_value_kinds(a: ValueKind, b: ValueKind) -> ValueKind {
fn find_hoisted_context_declarations(
func: &HirFunction,
env: &Environment,
) -> HashMap<DeclarationId, Option<Place>> {
let mut hoisted: HashMap<DeclarationId, Option<Place>> = HashMap::new();
) -> FxHashMap<DeclarationId, Option<Place>> {
let mut hoisted: FxHashMap<DeclarationId, Option<Place>> = FxHashMap::default();
fn visit(
hoisted: &mut HashMap<DeclarationId, Option<Place>>,
hoisted: &mut FxHashMap<DeclarationId, Option<Place>>,
place: &Place,
env: &Environment,
) {
@@ -831,8 +833,8 @@ fn find_hoisted_context_declarations(
fn find_non_mutated_destructure_spreads(
func: &HirFunction,
env: &Environment,
) -> HashSet<IdentifierId> {
let mut known_frozen: HashSet<IdentifierId> = HashSet::new();
) -> FxHashSet<IdentifierId> {
let mut known_frozen: FxHashSet<IdentifierId> = FxHashSet::default();
if func.fn_type == ReactFunctionType::Component {
if let Some(param) = func.params.first() {
if let ParamPattern::Place(p) = param {
@@ -847,7 +849,8 @@ fn find_non_mutated_destructure_spreads(
}
}
let mut candidate_non_mutating_spreads: HashMap<IdentifierId, IdentifierId> = HashMap::new();
let mut candidate_non_mutating_spreads: FxHashMap<IdentifierId, IdentifierId> =
FxHashMap::default();
for (_block_id, block) in &func.body.blocks {
if !candidate_non_mutating_spreads.is_empty() {
for phi in &block.phis {
@@ -954,7 +957,7 @@ fn find_non_mutated_destructure_spreads(
}
}
let mut non_mutating: HashSet<IdentifierId> = HashSet::new();
let mut non_mutating: FxHashSet<IdentifierId> = FxHashSet::default();
for (key, value) in &candidate_non_mutating_spreads {
if key == value {
non_mutating.insert(*key);
@@ -991,7 +994,7 @@ fn infer_block(
let block = &func.body.blocks[&block_id];
// Process phis
let phis: Vec<(IdentifierId, indexmap::IndexMap<BlockId, Place>)> = block
let phis: Vec<(IdentifierId, IndexMap<BlockId, Place, FxBuildHasher>)> = block
.phis
.iter()
.map(|phi| (phi.place.identifier, phi.operands.clone()))
@@ -1144,7 +1147,7 @@ fn apply_signature(
| InstructionValue::ObjectMethod { lowered_func, .. } => {
let inner_func = &env.functions[lowered_func.func.0 as usize];
if let Some(ref aliasing_effects) = inner_func.aliasing_effects {
let context_ids: HashSet<IdentifierId> =
let context_ids: FxHashSet<IdentifierId> =
inner_func.context.iter().map(|p| p.identifier).collect();
for effect in aliasing_effects {
let (mutate_value, is_mutate) = match effect {
@@ -1203,7 +1206,7 @@ fn apply_signature(
}
// Track which values we've already initialized
let mut initialized: HashSet<IdentifierId> = HashSet::new();
let mut initialized: FxHashSet<IdentifierId> = FxHashSet::default();
// Get the cached signature effects
let sig = context.instruction_signature_cache.get(&instr_idx).unwrap();
@@ -1296,7 +1299,7 @@ fn apply_effect(
context: &mut Context,
state: &mut InferenceState,
effect: AliasingEffect,
initialized: &mut HashSet<IdentifierId>,
initialized: &mut FxHashSet<IdentifierId>,
effects: &mut Vec<AliasingEffect>,
env: &mut Environment,
func: &HirFunction,
@@ -1484,7 +1487,7 @@ fn apply_effect(
} else {
ValueKind::Frozen
},
reason: IndexSet::new(),
reason: IndexSet::default(),
},
);
state.define(into.identifier, value_id);
@@ -2582,7 +2585,7 @@ fn compute_effects_for_legacy_signature(
args: &[PlaceOrSpreadOrHole],
_loc: Option<&SourceLocation>,
env: &Environment,
function_values: &HashMap<ValueId, FunctionId>,
function_values: &FxHashMap<ValueId, FunctionId>,
todo_errors: &mut Vec<react_compiler_diagnostics::CompilerErrorDetail>,
) -> Vec<AliasingEffect> {
let return_value_reason = signature.return_value_reason.unwrap_or(ValueReason::Other);
@@ -2786,7 +2789,7 @@ fn are_arguments_immutable_and_non_mutating(
state: &InferenceState,
args: &[PlaceOrSpreadOrHole],
env: &Environment,
function_values: &HashMap<ValueId, FunctionId>,
function_values: &FxHashMap<ValueId, FunctionId>,
) -> bool {
for arg in args {
match arg {
@@ -2870,14 +2873,14 @@ fn compute_effects_for_aliasing_signature_config(
args: &[PlaceOrSpreadOrHole],
context: &[Place],
_loc: Option<&SourceLocation>,
temp_cache: &mut HashMap<(IdentifierId, String), Place>,
temp_cache: &mut FxHashMap<(IdentifierId, String), Place>,
) -> Result<Option<Vec<AliasingEffect>>, CompilerDiagnostic> {
// Build substitutions from config strings to places
let mut substitutions: HashMap<String, Vec<Place>> = HashMap::new();
let mut substitutions: FxHashMap<String, Vec<Place>> = FxHashMap::default();
substitutions.insert(config.receiver.clone(), vec![receiver.clone()]);
substitutions.insert(config.returns.clone(), vec![lvalue.clone()]);
let mut mutable_spreads: HashSet<IdentifierId> = HashSet::new();
let mut mutable_spreads: FxHashSet<IdentifierId> = FxHashSet::default();
for (i, arg) in args.iter().enumerate() {
match arg {
@@ -3113,8 +3116,8 @@ fn compute_effects_for_aliasing_signature(
return Ok(None);
}
let mut mutable_spreads: HashSet<IdentifierId> = HashSet::new();
let mut substitutions: HashMap<IdentifierId, Vec<Place>> = HashMap::new();
let mut mutable_spreads: FxHashSet<IdentifierId> = FxHashSet::default();
let mut substitutions: FxHashMap<IdentifierId, Vec<Place>> = FxHashMap::default();
substitutions.insert(signature.receiver, vec![receiver.clone()]);
substitutions.insert(signature.returns, vec![lvalue.clone()]);
@@ -3407,7 +3410,7 @@ fn compute_effects_for_aliasing_signature(
/// since the primary reason is always inserted first, this effectively
/// picks the most specific non-Other reason. We replicate this by
/// preferring any non-Other reason over Other.
fn primary_reason(reasons: &IndexSet<ValueReason>) -> ValueReason {
fn primary_reason(reasons: &IndexSet<ValueReason, FxBuildHasher>) -> ValueReason {
for &r in reasons {
if r != ValueReason::Other {
return r;
@@ -14,7 +14,7 @@
//! vars, aliasing between params/context-vars/return-value)
//! - The legacy `Effect` to store on each Place
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use indexmap::IndexMap;
@@ -76,10 +76,10 @@ enum NodeValue {
#[derive(Debug, Clone)]
struct Node {
id: IdentifierId,
created_from: IndexMap<IdentifierId, usize>,
captures: IndexMap<IdentifierId, usize>,
aliases: IndexMap<IdentifierId, usize>,
maybe_aliases: IndexMap<IdentifierId, usize>,
created_from: IndexMap<IdentifierId, usize, FxBuildHasher>,
captures: IndexMap<IdentifierId, usize, FxBuildHasher>,
aliases: IndexMap<IdentifierId, usize, FxBuildHasher>,
maybe_aliases: IndexMap<IdentifierId, usize, FxBuildHasher>,
edges: Vec<Edge>,
transitive: Option<MutationInfo>,
local: Option<MutationInfo>,
@@ -92,10 +92,10 @@ impl Node {
fn new(id: IdentifierId, value: NodeValue) -> Self {
Node {
id,
created_from: IndexMap::new(),
captures: IndexMap::new(),
aliases: IndexMap::new(),
maybe_aliases: IndexMap::new(),
created_from: IndexMap::default(),
captures: IndexMap::default(),
aliases: IndexMap::default(),
maybe_aliases: IndexMap::default(),
edges: Vec::new(),
transitive: None,
local: None,
@@ -107,13 +107,13 @@ impl Node {
}
struct AliasingState {
nodes: IndexMap<IdentifierId, Node>,
nodes: IndexMap<IdentifierId, Node, FxBuildHasher>,
}
impl AliasingState {
fn new() -> Self {
AliasingState {
nodes: IndexMap::new(),
nodes: IndexMap::default(),
}
}
@@ -198,7 +198,7 @@ impl AliasingState {
}
fn render(&self, index: usize, start: IdentifierId, env: &mut Environment) {
let mut seen = HashSet::new();
let mut seen = FxHashSet::default();
let mut queue: Vec<IdentifierId> = vec![start];
while let Some(current) = queue.pop() {
if !seen.insert(current) {
@@ -260,7 +260,7 @@ impl AliasingState {
Forwards,
}
let mut seen: HashMap<IdentifierId, MutationKind> = HashMap::new();
let mut seen: FxHashMap<IdentifierId, MutationKind> = FxHashMap::default();
let mut queue: Vec<QueueEntry> = vec![QueueEntry {
place: start,
transitive,
@@ -475,7 +475,7 @@ pub fn infer_mutation_aliasing_ranges(
into: Place,
index: usize,
}
let mut pending_phis: HashMap<BlockId, Vec<PendingPhiOperand>> = HashMap::new();
let mut pending_phis: FxHashMap<BlockId, Vec<PendingPhiOperand>> = FxHashMap::default();
struct PendingMutation {
index: usize,
@@ -510,7 +510,7 @@ pub fn infer_mutation_aliasing_ranges(
}
state.create(&func.returns, NodeValue::Object);
let mut seen_blocks: HashSet<BlockId> = HashSet::new();
let mut seen_blocks: FxHashSet<BlockId> = FxHashSet::default();
// Collect block iteration data to avoid borrow conflicts
let block_order: Vec<BlockId> = func.body.blocks.keys().cloned().collect();
@@ -734,7 +734,7 @@ pub fn infer_mutation_aliasing_ranges(
// Set effect on mutated params/context vars
// We need to do this in a separate pass because we need to know which params
// were mutated before setting effects
let mut captured_params: HashSet<IdentifierId> = HashSet::new();
let mut captured_params: FxHashSet<IdentifierId> = FxHashSet::default();
for param in &func.params {
let place = match param {
react_compiler_hir::ParamPattern::Place(p) => p,
@@ -892,7 +892,7 @@ pub fn infer_mutation_aliasing_ranges(
// Compute operand effects from instruction effects
let effects = instr.effects.as_ref().unwrap().clone();
let mut operand_effects: HashMap<IdentifierId, Effect> = HashMap::new();
let mut operand_effects: FxHashMap<IdentifierId, Effect> = FxHashMap::default();
for effect in &effects {
match effect {
@@ -14,7 +14,7 @@
//! 4. Mutation with reactive operands
//! 5. Conditional assignment based on reactive control flow
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
use react_compiler_hir::dominator::post_dominator_frontier;
@@ -69,7 +69,7 @@ pub fn infer_reactive_places(
// is already reactive, the TS `continue`s and skips operand processing.
// We track which phi operand Places should be marked reactive.
// Key: (block_id, phi_idx, operand_idx), Value: should be reactive
let mut phi_operand_reactive: HashMap<(BlockId, usize, usize), bool> = HashMap::new();
let mut phi_operand_reactive: FxHashMap<(BlockId, usize, usize), bool> = FxHashMap::default();
// Fixpoint iteration — compute reactive set
loop {
@@ -235,7 +235,7 @@ pub fn infer_reactive_places(
struct ReactivityMap<'a> {
has_changes: bool,
reactive: HashSet<IdentifierId>,
reactive: FxHashSet<IdentifierId>,
aliased_identifiers: &'a mut DisjointSet<IdentifierId>,
}
@@ -243,7 +243,7 @@ impl<'a> ReactivityMap<'a> {
fn new(aliased_identifiers: &'a mut DisjointSet<IdentifierId>) -> Self {
ReactivityMap {
has_changes: false,
reactive: HashSet::new(),
reactive: FxHashSet::default(),
aliased_identifiers,
}
}
@@ -273,13 +273,13 @@ impl<'a> ReactivityMap<'a> {
// =============================================================================
struct StableSidemap {
map: HashMap<IdentifierId, bool>,
map: FxHashMap<IdentifierId, bool>,
}
impl StableSidemap {
fn new() -> Self {
StableSidemap {
map: HashMap::new(),
map: FxHashMap::default(),
}
}
@@ -538,7 +538,7 @@ fn apply_reactive_flags_replay(
env: &mut Environment,
reactive_map: &mut ReactivityMap,
stable_sidemap: &mut StableSidemap,
phi_operand_reactive: &HashMap<(BlockId, usize, usize), bool>,
phi_operand_reactive: &FxHashMap<(BlockId, usize, usize), bool>,
) {
let reactive_ids = build_reactive_id_set(reactive_map);
@@ -697,8 +697,8 @@ fn apply_reactive_flags_replay(
apply_reactive_flags_to_inner_functions(func, env, &reactive_ids);
}
fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> HashSet<IdentifierId> {
let mut result = HashSet::new();
fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> FxHashSet<IdentifierId> {
let mut result = FxHashSet::default();
for &id in &reactive_map.reactive {
result.insert(id);
}
@@ -714,7 +714,7 @@ fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> HashSet<Identifier
fn apply_reactive_flags_to_inner_functions(
func: &HirFunction,
env: &mut Environment,
reactive_ids: &HashSet<IdentifierId>,
reactive_ids: &FxHashSet<IdentifierId>,
) {
for (_block_id, block) in &func.body.blocks {
for instr_id in &block.instructions {
@@ -733,7 +733,7 @@ fn apply_reactive_flags_to_inner_functions(
fn apply_reactive_flags_to_inner_func(
func_id: FunctionId,
env: &mut Environment,
reactive_ids: &HashSet<IdentifierId>,
reactive_ids: &FxHashSet<IdentifierId>,
) {
// Collect nested function IDs first to avoid borrow issues
let nested_func_ids: Vec<FunctionId> = {
@@ -15,7 +15,7 @@
//! 3. MergeOverlappingReactiveScopes ensures scopes do not overlap.
//! 4. BuildReactiveBlocks groups the statements for each scope.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
use react_compiler_hir::environment::Environment;
@@ -45,7 +45,7 @@ pub fn infer_reactive_scope_variables(
// Phase 2: assign scopes
// Maps each group root identifier to the ScopeId assigned to that group.
let mut scopes: HashMap<IdentifierId, ScopeState> = HashMap::new();
let mut scopes: FxHashMap<IdentifierId, ScopeState> = FxHashMap::default();
scope_identifiers.for_each(|identifier_id, group_id| {
let ident_range = env.identifiers[identifier_id.0 as usize]
@@ -267,7 +267,7 @@ pub(crate) fn find_disjoint_mutable_values(
env: &Environment,
) -> DisjointSet<IdentifierId> {
let mut scope_identifiers = DisjointSet::<IdentifierId>::new();
let mut declarations: HashMap<DeclarationId, IdentifierId> = HashMap::new();
let mut declarations: FxHashMap<DeclarationId, IdentifierId> = FxHashMap::default();
let enable_forest = env.config.enable_forest;
@@ -284,8 +284,8 @@ pub(crate) fn find_disjoint_mutable_values(
.map(|iid| func.instructions[iid.0 as usize].id)
.unwrap_or(block.terminal.evaluation_order());
let is_phi_mutated_after_creation = phi_range.start.0 + 1 != phi_range.end.0
&& phi_range.end > first_instr_id;
let is_phi_mutated_after_creation =
phi_range.start.0 + 1 != phi_range.end.0 && phi_range.end > first_instr_id;
// A phi operand defined at or after the phi's block is a loop
// back-edge: the variable is reassigned within the loop (eg a
// counter `a++` or `a = a + 1`). The reassignment must count as
@@ -13,7 +13,7 @@
//! 1. Forward data-flow: identify all macro tags (including property loads like `fbt.param`)
//! 2. Reverse data-flow: merge arguments of macro invocations into the same scope
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_hir::environment::Environment;
use react_compiler_hir::visitors;
@@ -34,7 +34,7 @@ enum InlineLevel {
struct MacroDefinition {
level: InlineLevel,
/// Maps property names to their own MacroDefinition. `"*"` is a wildcard.
properties: Option<HashMap<String, MacroDefinition>>,
properties: Option<FxHashMap<String, MacroDefinition>>,
}
fn shallow_macro() -> MacroDefinition {
@@ -52,7 +52,7 @@ fn transitive_macro() -> MacroDefinition {
}
fn fbt_macro() -> MacroDefinition {
let mut props = HashMap::new();
let mut props = FxHashMap::default();
props.insert("*".to_string(), shallow_macro());
// fbt.enum gets FBT_MACRO (recursive/transitive)
// We'll fill this in after construction since it's self-referential.
@@ -66,7 +66,7 @@ fn fbt_macro() -> MacroDefinition {
let enum_macro = MacroDefinition {
level: InlineLevel::Transitive,
properties: Some({
let mut p = HashMap::new();
let mut p = FxHashMap::default();
p.insert("*".to_string(), shallow_macro());
// enum's enum is also recursive, but in practice the depth is bounded
p.insert("enum".to_string(), transitive_macro());
@@ -81,8 +81,8 @@ fn fbt_macro() -> MacroDefinition {
}
/// Built-in FBT tags and their macro definitions.
fn fbt_tags() -> HashMap<String, MacroDefinition> {
let mut tags = HashMap::new();
fn fbt_tags() -> FxHashMap<String, MacroDefinition> {
let mut tags = FxHashMap::default();
tags.insert("fbt".to_string(), fbt_macro());
tags.insert("fbt:param".to_string(), shallow_macro());
tags.insert("fbt:enum".to_string(), fbt_macro());
@@ -98,9 +98,9 @@ fn fbt_tags() -> HashMap<String, MacroDefinition> {
pub fn memoize_fbt_and_macro_operands_in_same_scope(
func: &HirFunction,
env: &mut Environment,
) -> HashSet<IdentifierId> {
) -> FxHashSet<IdentifierId> {
// Phase 1: Build macro kinds map from built-in FBT tags + custom macros
let mut macro_kinds: HashMap<String, MacroDefinition> = fbt_tags();
let mut macro_kinds: FxHashMap<String, MacroDefinition> = fbt_tags();
if let Some(ref custom_macros) = env.config.custom_macros {
for name in custom_macros {
macro_kinds.insert(name.clone(), transitive_macro());
@@ -120,9 +120,9 @@ pub fn memoize_fbt_and_macro_operands_in_same_scope(
/// things like `fbt.foo.bar(...)`.
fn populate_macro_tags(
func: &HirFunction,
macro_kinds: &HashMap<String, MacroDefinition>,
) -> HashMap<IdentifierId, MacroDefinition> {
let mut macro_tags: HashMap<IdentifierId, MacroDefinition> = HashMap::new();
macro_kinds: &FxHashMap<String, MacroDefinition>,
) -> FxHashMap<IdentifierId, MacroDefinition> {
let mut macro_tags: FxHashMap<IdentifierId, MacroDefinition> = FxHashMap::default();
for block in func.body.blocks.values() {
for &instr_id in &block.instructions {
@@ -134,9 +134,7 @@ fn populate_macro_tags(
value: PrimitiveValue::String(s),
..
} => {
if let Some(macro_def) =
s.as_str().and_then(|utf8| macro_kinds.get(utf8))
{
if let Some(macro_def) = s.as_str().and_then(|utf8| macro_kinds.get(utf8)) {
// We don't distinguish between tag names and strings, so record
// all `fbt` string literals in case they are used as a jsx tag.
macro_tags.insert(lvalue_id, macro_def.clone());
@@ -180,10 +178,10 @@ fn populate_macro_tags(
fn merge_macro_arguments(
func: &HirFunction,
env: &mut Environment,
macro_tags: &mut HashMap<IdentifierId, MacroDefinition>,
macro_kinds: &HashMap<String, MacroDefinition>,
) -> HashSet<IdentifierId> {
let mut macro_values: HashSet<IdentifierId> = macro_tags.keys().copied().collect();
macro_tags: &mut FxHashMap<IdentifierId, MacroDefinition>,
macro_kinds: &FxHashMap<String, MacroDefinition>,
) -> FxHashSet<IdentifierId> {
let mut macro_values: FxHashSet<IdentifierId> = macro_tags.keys().copied().collect();
// Iterate blocks in reverse order
let block_ids: Vec<_> = func.body.blocks.keys().copied().collect();
@@ -356,8 +354,8 @@ fn visit_operands(
lvalue_id: IdentifierId,
value: &InstructionValue,
env: &mut Environment,
macro_values: &mut HashSet<IdentifierId>,
macro_tags: &mut HashMap<IdentifierId, MacroDefinition>,
macro_values: &mut FxHashSet<IdentifierId>,
macro_tags: &mut FxHashMap<IdentifierId, MacroDefinition>,
) {
macro_values.insert(lvalue_id);
@@ -15,8 +15,8 @@
//!
//! Ported from TypeScript `src/HIR/MergeOverlappingReactiveScopesHIR.ts`.
use rustc_hash::FxHashMap;
use std::cmp;
use std::collections::HashMap;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::visitors;
@@ -46,7 +46,7 @@ struct ScopeInfo {
/// Sorted descending by id (so we can pop from the end for smallest)
scope_ends: Vec<ScopeEndEntry>,
/// Maps IdentifierId -> ScopeId for all places that have a scope
place_scopes: HashMap<IdentifierId, ScopeId>,
place_scopes: FxHashMap<IdentifierId, ScopeId>,
}
// =============================================================================
@@ -95,9 +95,9 @@ fn is_mutable(env: &Environment, id: EvaluationOrder, identifier_id: IdentifierI
// =============================================================================
fn collect_scope_info(func: &HirFunction, env: &Environment) -> ScopeInfo {
let mut scope_starts_map: HashMap<EvaluationOrder, Vec<ScopeId>> = HashMap::new();
let mut scope_ends_map: HashMap<EvaluationOrder, Vec<ScopeId>> = HashMap::new();
let mut place_scopes: HashMap<IdentifierId, ScopeId> = HashMap::new();
let mut scope_starts_map: FxHashMap<EvaluationOrder, Vec<ScopeId>> = FxHashMap::default();
let mut scope_ends_map: FxHashMap<EvaluationOrder, Vec<ScopeId>> = FxHashMap::default();
let mut place_scopes: FxHashMap<IdentifierId, ScopeId> = FxHashMap::default();
let mut collect_place_scope = |identifier_id: IdentifierId, env: &Environment| {
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 {
// We must NOT sort by ScopeId here — the insertion order determines which scope
// becomes the root in the disjoint set union.
fn dedup_preserve_order(scopes: &mut Vec<ScopeId>) {
let mut seen = std::collections::HashSet::new();
let mut seen = rustc_hash::FxHashSet::default();
scopes.retain(|s| seen.insert(*s));
}
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
// When scope.range is updated, ALL identifiers referencing that range object
// automatically see the new values. We use MutableRangeId to identify which
// identifiers share the same logical range as a root scope.
let mut original_root_range_ids: HashMap<ScopeId, react_compiler_hir::MutableRangeId> =
HashMap::new();
let mut original_root_range_ids: FxHashMap<ScopeId, react_compiler_hir::MutableRangeId> =
FxHashMap::default();
for (_, root_id) in &scope_groups {
if !original_root_range_ids.contains_key(root_id) {
let range_id = env.scopes[root_id.0 as usize].range.id;
@@ -13,7 +13,8 @@
//! - `src/HIR/DeriveMinimalDependenciesHIR.ts`
use indexmap::IndexMap;
use std::collections::{BTreeSet, HashMap, HashSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use std::collections::BTreeSet;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::visitors::{ScopeBlockInfo, ScopeBlockTraversal};
@@ -44,7 +45,7 @@ pub fn propagate_scope_dependencies_hir(func: &mut HirFunction, env: &mut Enviro
let (working, registry) =
collect_hoistable_and_propagate(func, env, &temporaries, &hoistable_objects);
// Convert to scope-keyed map with full dependency paths
let mut keyed: HashMap<ScopeId, Vec<ReactiveScopeDependency>> = HashMap::new();
let mut keyed: FxHashMap<ScopeId, Vec<ReactiveScopeDependency>> = FxHashMap::default();
for (_block_id, block) in &func.body.blocks {
if let Terminal::Scope {
scope,
@@ -128,17 +129,17 @@ fn are_equal_paths(a: &[DependencyPathEntry], b: &[DependencyPathEntry]) -> bool
fn find_temporaries_used_outside_declaring_scope(
func: &HirFunction,
env: &Environment,
) -> HashSet<DeclarationId> {
let mut declarations: HashMap<DeclarationId, ScopeId> = HashMap::new();
let mut pruned_scopes: HashSet<ScopeId> = HashSet::new();
) -> FxHashSet<DeclarationId> {
let mut declarations: FxHashMap<DeclarationId, ScopeId> = FxHashMap::default();
let mut pruned_scopes: FxHashSet<ScopeId> = FxHashSet::default();
let mut traversal = ScopeBlockTraversal::new();
let mut used_outside_declaring_scope: HashSet<DeclarationId> = HashSet::new();
let mut used_outside_declaring_scope: FxHashSet<DeclarationId> = FxHashSet::default();
let handle_place = |place_id: IdentifierId,
declarations: &HashMap<DeclarationId, ScopeId>,
declarations: &FxHashMap<DeclarationId, ScopeId>,
traversal: &ScopeBlockTraversal,
pruned_scopes: &HashSet<ScopeId>,
used_outside: &mut HashSet<DeclarationId>,
pruned_scopes: &FxHashSet<ScopeId>,
used_outside: &mut FxHashSet<DeclarationId>,
env: &Environment| {
let decl_id = env.identifiers[place_id.0 as usize].declaration_id;
if let Some(&declaring_scope) = declarations.get(&decl_id) {
@@ -227,9 +228,9 @@ fn find_temporaries_used_outside_declaring_scope(
fn collect_temporaries_sidemap(
func: &HirFunction,
env: &Environment,
used_outside_declaring_scope: &HashSet<DeclarationId>,
) -> HashMap<IdentifierId, ReactiveScopeDependency> {
let mut temporaries = HashMap::new();
used_outside_declaring_scope: &FxHashSet<DeclarationId>,
) -> FxHashMap<IdentifierId, ReactiveScopeDependency> {
let mut temporaries = FxHashMap::default();
collect_temporaries_sidemap_impl(
func,
env,
@@ -269,8 +270,8 @@ fn convert_hoisted_lvalue_kind(kind: InstructionKind) -> Option<InstructionKind>
fn collect_temporaries_sidemap_impl(
func: &HirFunction,
env: &Environment,
used_outside_declaring_scope: &HashSet<DeclarationId>,
temporaries: &mut HashMap<IdentifierId, ReactiveScopeDependency>,
used_outside_declaring_scope: &FxHashSet<DeclarationId>,
temporaries: &mut FxHashMap<IdentifierId, ReactiveScopeDependency>,
inner_fn_context: Option<EvaluationOrder>,
) {
for (_block_id, block) in &func.body.blocks {
@@ -369,7 +370,7 @@ fn get_property(
property_name: &PropertyLiteral,
optional: bool,
loc: Option<react_compiler_hir::SourceLocation>,
temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
_env: &Environment,
) -> ReactiveScopeDependency {
let resolved = temporaries.get(&object.identifier);
@@ -405,9 +406,9 @@ fn get_property(
// =============================================================================
struct OptionalChainSidemap {
temporaries_read_in_optional: HashMap<IdentifierId, ReactiveScopeDependency>,
processed_instrs_in_optional: HashSet<ProcessedInstr>,
hoistable_objects: HashMap<BlockId, ReactiveScopeDependency>,
temporaries_read_in_optional: FxHashMap<IdentifierId, ReactiveScopeDependency>,
processed_instrs_in_optional: FxHashSet<ProcessedInstr>,
hoistable_objects: FxHashMap<BlockId, ReactiveScopeDependency>,
}
/// We track processed instructions/terminals by their lvalue IdentifierId + block id.
@@ -423,10 +424,10 @@ enum ProcessedInstr {
fn collect_optional_chain_sidemap(func: &HirFunction, env: &Environment) -> OptionalChainSidemap {
let mut ctx = OptionalTraversalContext {
seen_optionals: HashSet::new(),
processed_instrs_in_optional: HashSet::new(),
temporaries_read_in_optional: HashMap::new(),
hoistable_objects: HashMap::new(),
seen_optionals: FxHashSet::default(),
processed_instrs_in_optional: FxHashSet::default(),
temporaries_read_in_optional: FxHashMap::default(),
hoistable_objects: FxHashMap::default(),
};
traverse_function_optional(func, env, &mut ctx);
@@ -439,10 +440,10 @@ fn collect_optional_chain_sidemap(func: &HirFunction, env: &Environment) -> Opti
}
struct OptionalTraversalContext {
seen_optionals: HashSet<BlockId>,
processed_instrs_in_optional: HashSet<ProcessedInstr>,
temporaries_read_in_optional: HashMap<IdentifierId, ReactiveScopeDependency>,
hoistable_objects: HashMap<BlockId, ReactiveScopeDependency>,
seen_optionals: FxHashSet<BlockId>,
processed_instrs_in_optional: FxHashSet<ProcessedInstr>,
temporaries_read_in_optional: FxHashMap<IdentifierId, ReactiveScopeDependency>,
hoistable_objects: FxHashMap<BlockId, ReactiveScopeDependency>,
}
fn traverse_function_optional(
@@ -785,8 +786,8 @@ fn traverse_optional_block(
#[derive(Debug, Clone)]
struct PropertyPathNode {
properties: HashMap<PropertyLiteral, usize>, // index into registry
optional_properties: HashMap<PropertyLiteral, usize>, // index into registry
properties: FxHashMap<PropertyLiteral, usize>, // index into registry
optional_properties: FxHashMap<PropertyLiteral, usize>, // index into registry
#[allow(dead_code)]
parent: Option<usize>,
full_path: ReactiveScopeDependency,
@@ -797,14 +798,14 @@ struct PropertyPathNode {
struct PropertyPathRegistry {
nodes: Vec<PropertyPathNode>,
roots: HashMap<IdentifierId, usize>,
roots: FxHashMap<IdentifierId, usize>,
}
impl PropertyPathRegistry {
fn new() -> Self {
Self {
nodes: Vec::new(),
roots: HashMap::new(),
roots: FxHashMap::default(),
}
}
@@ -819,8 +820,8 @@ impl PropertyPathRegistry {
}
let idx = self.nodes.len();
self.nodes.push(PropertyPathNode {
properties: HashMap::new(),
optional_properties: HashMap::new(),
properties: FxHashMap::default(),
optional_properties: FxHashMap::default(),
parent: None,
full_path: ReactiveScopeDependency {
identifier: identifier_id,
@@ -858,8 +859,8 @@ impl PropertyPathRegistry {
let mut new_path = parent_full_path.path.clone();
new_path.push(entry.clone());
self.nodes.push(PropertyPathNode {
properties: HashMap::new(),
optional_properties: HashMap::new(),
properties: FxHashMap::default(),
optional_properties: FxHashMap::default(),
parent: Some(parent_idx),
full_path: ReactiveScopeDependency {
identifier: parent_full_path.identifier,
@@ -962,11 +963,11 @@ struct BlockInfo {
fn collect_hoistable_property_loads(
func: &HirFunction,
env: &Environment,
temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
hoistable_from_optionals: &HashMap<BlockId, ReactiveScopeDependency>,
) -> HashMap<BlockId, BlockInfo> {
temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
hoistable_from_optionals: &FxHashMap<BlockId, ReactiveScopeDependency>,
) -> FxHashMap<BlockId, BlockInfo> {
let mut registry = PropertyPathRegistry::new();
let known_immutable_identifiers: HashSet<IdentifierId> = if func.fn_type
let known_immutable_identifiers: FxHashSet<IdentifierId> = if func.fn_type
== ReactFunctionType::Component
|| func.fn_type == ReactFunctionType::Hook
{
@@ -978,7 +979,7 @@ fn collect_hoistable_property_loads(
})
.collect()
} else {
HashSet::new()
FxHashSet::default()
};
let assumed_invoked_fns = get_assumed_invoked_functions(func, env);
@@ -994,11 +995,11 @@ fn collect_hoistable_property_loads(
}
struct CollectHoistableContext<'a> {
temporaries: &'a HashMap<IdentifierId, ReactiveScopeDependency>,
known_immutable_identifiers: &'a HashSet<IdentifierId>,
hoistable_from_optionals: &'a HashMap<BlockId, ReactiveScopeDependency>,
nested_fn_immutable_context: Option<&'a HashSet<IdentifierId>>,
assumed_invoked_fns: &'a HashSet<FunctionId>,
temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,
known_immutable_identifiers: &'a FxHashSet<IdentifierId>,
hoistable_from_optionals: &'a FxHashMap<BlockId, ReactiveScopeDependency>,
nested_fn_immutable_context: Option<&'a FxHashSet<IdentifierId>>,
assumed_invoked_fns: &'a FxHashSet<FunctionId>,
}
fn is_immutable_at_instr(
@@ -1027,7 +1028,7 @@ fn in_range(id: EvaluationOrder, range: &MutableRange) -> bool {
fn get_maybe_non_null_in_instruction(
value: &InstructionValue,
temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
) -> Option<ReactiveScopeDependency> {
match value {
InstructionValue::PropertyLoad { object, .. } => Some(
@@ -1057,10 +1058,10 @@ fn collect_hoistable_property_loads_impl(
env: &Environment,
ctx: &CollectHoistableContext,
registry: &mut PropertyPathRegistry,
) -> HashMap<BlockId, BlockInfo> {
) -> FxHashMap<BlockId, BlockInfo> {
let nodes = collect_non_nulls_in_blocks(func, env, ctx, registry);
let working = propagate_non_null(func, &nodes, registry);
// Return the propagated results, converting HashSet<usize> back to BlockInfo
// Return the propagated results, converting FxHashSet<usize> back to BlockInfo
working
.into_iter()
.map(|(k, v)| {
@@ -1078,17 +1079,18 @@ fn collect_hoistable_property_loads_impl(
/// Returns the set of LoweredFunction FunctionIds that are assumed to be invoked.
/// The `temporaries` map is shared across recursive calls (matching TS behavior where
/// the same Map is passed to recursive invocations for inner functions).
fn get_assumed_invoked_functions(func: &HirFunction, env: &Environment) -> HashSet<FunctionId> {
let mut temporaries: HashMap<IdentifierId, (FunctionId, HashSet<FunctionId>)> = HashMap::new();
fn get_assumed_invoked_functions(func: &HirFunction, env: &Environment) -> FxHashSet<FunctionId> {
let mut temporaries: FxHashMap<IdentifierId, (FunctionId, FxHashSet<FunctionId>)> =
FxHashMap::default();
get_assumed_invoked_functions_impl(func, env, &mut temporaries)
}
fn get_assumed_invoked_functions_impl(
func: &HirFunction,
env: &Environment,
temporaries: &mut HashMap<IdentifierId, (FunctionId, HashSet<FunctionId>)>,
) -> HashSet<FunctionId> {
let mut hoistable: HashSet<FunctionId> = HashSet::new();
temporaries: &mut FxHashMap<IdentifierId, (FunctionId, FxHashSet<FunctionId>)>,
) -> FxHashSet<FunctionId> {
let mut hoistable: FxHashSet<FunctionId> = FxHashSet::default();
// Step 1: Collect identifier to function expression mappings
for (_block_id, block) in &func.body.blocks {
@@ -1096,8 +1098,10 @@ fn get_assumed_invoked_functions_impl(
let instr = &func.instructions[instr_id.0 as usize];
match &instr.value {
InstructionValue::FunctionExpression { lowered_func, .. } => {
temporaries
.insert(instr.lvalue.identifier, (lowered_func.func, HashSet::new()));
temporaries.insert(
instr.lvalue.identifier,
(lowered_func.func, FxHashSet::default()),
);
}
InstructionValue::StoreLocal {
value: val, lvalue, ..
@@ -1221,7 +1225,7 @@ fn collect_non_nulls_in_blocks(
env: &Environment,
ctx: &CollectHoistableContext,
registry: &mut PropertyPathRegistry,
) -> HashMap<BlockId, BlockInfo> {
) -> FxHashMap<BlockId, BlockInfo> {
// Known non-null identifiers (e.g. component props)
let mut known_non_null: BTreeSet<usize> = BTreeSet::new();
if func.fn_type == ReactFunctionType::Component && !func.params.is_empty() {
@@ -1231,7 +1235,7 @@ fn collect_non_nulls_in_blocks(
}
}
let mut nodes: HashMap<BlockId, BlockInfo> = HashMap::new();
let mut nodes: FxHashMap<BlockId, BlockInfo> = FxHashMap::default();
for (block_id, block) in &func.body.blocks {
let mut assumed = known_non_null.clone();
@@ -1290,7 +1294,7 @@ fn collect_non_nulls_in_blocks(
if ctx.assumed_invoked_fns.contains(&lowered_func.func) {
let inner_func = &env.functions[lowered_func.func.0 as usize];
// Build nested fn immutable context
let nested_fn_immutable_context: HashSet<IdentifierId> =
let nested_fn_immutable_context: FxHashSet<IdentifierId> =
if ctx.nested_fn_immutable_context.is_some() {
// Already in a nested fn context, use existing
ctx.nested_fn_immutable_context.unwrap().clone()
@@ -1307,7 +1311,7 @@ fn collect_non_nulls_in_blocks(
let inner_assumed = get_assumed_invoked_functions(inner_func, env);
let inner_ctx = CollectHoistableContext {
temporaries: ctx.temporaries,
known_immutable_identifiers: &HashSet::new(),
known_immutable_identifiers: &FxHashSet::default(),
hoistable_from_optionals: ctx.hoistable_from_optionals,
nested_fn_immutable_context: Some(&nested_fn_immutable_context),
assumed_invoked_fns: &inner_assumed,
@@ -1347,13 +1351,13 @@ fn collect_non_nulls_in_blocks(
/// and should be filtered out, allowing non-null info to propagate through non-cyclic paths.
fn propagate_non_null(
func: &HirFunction,
nodes: &HashMap<BlockId, BlockInfo>,
nodes: &FxHashMap<BlockId, BlockInfo>,
registry: &mut PropertyPathRegistry,
) -> HashMap<BlockId, BTreeSet<usize>> {
) -> FxHashMap<BlockId, BTreeSet<usize>> {
// Build successor map. Use BTreeSet to iterate successors in sorted BlockId
// order, matching the TS Set<BlockId> insertion order (blocks are created in
// ascending BlockId order).
let mut block_successors: HashMap<BlockId, BTreeSet<BlockId>> = HashMap::new();
let mut block_successors: FxHashMap<BlockId, BTreeSet<BlockId>> = FxHashMap::default();
for (block_id, block) in &func.body.blocks {
for pred in &block.preds {
block_successors.entry(*pred).or_default().insert(*block_id);
@@ -1361,7 +1365,7 @@ fn propagate_non_null(
}
// Clone nodes into mutable working set
let mut working: HashMap<BlockId, BTreeSet<usize>> = nodes
let mut working: FxHashMap<BlockId, BTreeSet<usize>> = nodes
.iter()
.map(|(k, v)| (*k, v.assumed_non_null_objects.clone()))
.collect();
@@ -1374,7 +1378,7 @@ fn propagate_non_null(
let mut changed = false;
// Forward pass (using predecessors)
let mut traversal_state: HashMap<BlockId, TraversalState> = HashMap::new();
let mut traversal_state: FxHashMap<BlockId, TraversalState> = FxHashMap::default();
for &block_id in &block_ids {
let block_changed = recursively_propagate_non_null(
block_id,
@@ -1426,10 +1430,10 @@ enum PropagationDirection {
fn recursively_propagate_non_null(
node_id: BlockId,
direction: PropagationDirection,
traversal_state: &mut HashMap<BlockId, TraversalState>,
working: &mut HashMap<BlockId, BTreeSet<usize>>,
traversal_state: &mut FxHashMap<BlockId, TraversalState>,
working: &mut FxHashMap<BlockId, BTreeSet<usize>>,
func: &HirFunction,
block_successors: &HashMap<BlockId, BTreeSet<BlockId>>,
block_successors: &FxHashMap<BlockId, BTreeSet<BlockId>>,
registry: &mut PropertyPathRegistry,
) -> bool {
// Avoid re-visiting computed or currently active nodes
@@ -1500,12 +1504,12 @@ fn recursively_propagate_non_null(
fn collect_hoistable_and_propagate(
func: &HirFunction,
env: &Environment,
temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
hoistable_from_optionals: &HashMap<BlockId, ReactiveScopeDependency>,
) -> (HashMap<BlockId, BTreeSet<usize>>, PropertyPathRegistry) {
temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
hoistable_from_optionals: &FxHashMap<BlockId, ReactiveScopeDependency>,
) -> (FxHashMap<BlockId, BTreeSet<usize>>, PropertyPathRegistry) {
let mut registry = PropertyPathRegistry::new();
let assumed_invoked_fns = get_assumed_invoked_functions(func, env);
let known_immutable_identifiers: HashSet<IdentifierId> = if func.fn_type
let known_immutable_identifiers: FxHashSet<IdentifierId> = if func.fn_type
== ReactFunctionType::Component
|| func.fn_type == ReactFunctionType::Hook
{
@@ -1517,7 +1521,7 @@ fn collect_hoistable_and_propagate(
})
.collect()
} else {
HashSet::new()
FxHashSet::default()
};
let ctx = CollectHoistableContext {
@@ -1538,9 +1542,9 @@ fn collect_hoistable_and_propagate(
#[allow(dead_code)]
fn key_by_scope_id(
func: &HirFunction,
block_keyed: &HashMap<BlockId, BlockInfo>,
) -> HashMap<ScopeId, BlockInfo> {
let mut keyed: HashMap<ScopeId, BlockInfo> = HashMap::new();
block_keyed: &FxHashMap<BlockId, BlockInfo>,
) -> FxHashMap<ScopeId, BlockInfo> {
let mut keyed: FxHashMap<ScopeId, BlockInfo> = FxHashMap::default();
for (_block_id, block) in &func.body.blocks {
if let Terminal::Scope {
scope,
@@ -1600,7 +1604,7 @@ enum HoistableAccessType {
}
struct HoistableNode {
properties: HashMap<PropertyLiteral, Box<HoistableNodeEntry>>,
properties: FxHashMap<PropertyLiteral, Box<HoistableNodeEntry>>,
access_type: HoistableAccessType,
}
@@ -1609,7 +1613,7 @@ struct HoistableNodeEntry {
}
struct DependencyNode {
properties: IndexMap<PropertyLiteral, Box<DependencyNodeEntry>>,
properties: IndexMap<PropertyLiteral, Box<DependencyNodeEntry>, FxBuildHasher>,
access_type: PropertyAccessType,
loc: Option<react_compiler_hir::SourceLocation>,
}
@@ -1619,8 +1623,8 @@ struct DependencyNodeEntry {
}
struct ReactiveScopeDependencyTreeHIR {
hoistable_roots: HashMap<IdentifierId, (HoistableNode, bool)>, // node + reactive
dep_roots: IndexMap<IdentifierId, (DependencyNode, bool)>, // node + reactive (preserves insertion order like JS Map)
hoistable_roots: FxHashMap<IdentifierId, (HoistableNode, bool)>, // node + reactive
dep_roots: IndexMap<IdentifierId, (DependencyNode, bool), FxBuildHasher>, // node + reactive (preserves insertion order like JS Map)
}
impl ReactiveScopeDependencyTreeHIR {
@@ -1628,7 +1632,8 @@ impl ReactiveScopeDependencyTreeHIR {
hoistable_objects: impl Iterator<Item = &'a ReactiveScopeDependency>,
_env: &Environment,
) -> Self {
let mut hoistable_roots: HashMap<IdentifierId, (HoistableNode, bool)> = HashMap::new();
let mut hoistable_roots: FxHashMap<IdentifierId, (HoistableNode, bool)> =
FxHashMap::default();
// Sort hoistable objects so that entries with optional first path come
// before non-optional ones. This matches the TS behavior where
@@ -1651,7 +1656,7 @@ impl ReactiveScopeDependencyTreeHIR {
};
(
HoistableNode {
properties: HashMap::new(),
properties: FxHashMap::default(),
access_type,
},
dep.reactive,
@@ -1671,7 +1676,7 @@ impl ReactiveScopeDependencyTreeHIR {
.or_insert_with(|| {
Box::new(HoistableNodeEntry {
node: HoistableNode {
properties: HashMap::new(),
properties: FxHashMap::default(),
access_type,
},
})
@@ -1682,7 +1687,7 @@ impl ReactiveScopeDependencyTreeHIR {
Self {
hoistable_roots,
dep_roots: IndexMap::new(),
dep_roots: IndexMap::default(),
}
}
@@ -1690,7 +1695,7 @@ impl ReactiveScopeDependencyTreeHIR {
let root = self.dep_roots.entry(dep.identifier).or_insert_with(|| {
(
DependencyNode {
properties: IndexMap::new(),
properties: IndexMap::default(),
access_type: PropertyAccessType::UnconditionalAccess,
loc: dep.loc,
},
@@ -1735,7 +1740,7 @@ impl ReactiveScopeDependencyTreeHIR {
.or_insert_with(|| {
Box::new(DependencyNodeEntry {
node: DependencyNode {
properties: IndexMap::new(),
properties: IndexMap::default(),
access_type,
loc: entry.loc,
},
@@ -1809,30 +1814,30 @@ struct Decl {
/// Context for dependency collection.
struct DependencyCollectionContext<'a> {
declarations: HashMap<DeclarationId, Decl>,
reassignments: HashMap<IdentifierId, Decl>,
declarations: FxHashMap<DeclarationId, Decl>,
reassignments: FxHashMap<IdentifierId, Decl>,
scope_stack: Vec<ScopeId>,
dep_stack: Vec<Vec<ReactiveScopeDependency>>,
deps: IndexMap<ScopeId, Vec<ReactiveScopeDependency>>,
temporaries: &'a HashMap<IdentifierId, ReactiveScopeDependency>,
deps: IndexMap<ScopeId, Vec<ReactiveScopeDependency>, FxBuildHasher>,
temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,
#[allow(dead_code)]
temporaries_used_outside_scope: &'a HashSet<DeclarationId>,
processed_instrs_in_optional: &'a HashSet<ProcessedInstr>,
temporaries_used_outside_scope: &'a FxHashSet<DeclarationId>,
processed_instrs_in_optional: &'a FxHashSet<ProcessedInstr>,
inner_fn_context: Option<EvaluationOrder>,
}
impl<'a> DependencyCollectionContext<'a> {
fn new(
temporaries_used_outside_scope: &'a HashSet<DeclarationId>,
temporaries: &'a HashMap<IdentifierId, ReactiveScopeDependency>,
processed_instrs_in_optional: &'a HashSet<ProcessedInstr>,
temporaries_used_outside_scope: &'a FxHashSet<DeclarationId>,
temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,
processed_instrs_in_optional: &'a FxHashSet<ProcessedInstr>,
) -> Self {
Self {
declarations: HashMap::new(),
reassignments: HashMap::new(),
declarations: FxHashMap::default(),
reassignments: FxHashMap::default(),
scope_stack: Vec::new(),
dep_stack: Vec::new(),
deps: IndexMap::new(),
deps: IndexMap::default(),
temporaries,
temporaries_used_outside_scope,
processed_instrs_in_optional,
@@ -2222,10 +2227,10 @@ fn handle_instruction(
fn collect_dependencies(
func: &HirFunction,
env: &mut Environment,
used_outside_declaring_scope: &HashSet<DeclarationId>,
temporaries: &HashMap<IdentifierId, ReactiveScopeDependency>,
processed_instrs_in_optional: &HashSet<ProcessedInstr>,
) -> IndexMap<ScopeId, Vec<ReactiveScopeDependency>> {
used_outside_declaring_scope: &FxHashSet<DeclarationId>,
temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,
processed_instrs_in_optional: &FxHashSet<ProcessedInstr>,
) -> IndexMap<ScopeId, Vec<ReactiveScopeDependency>, FxBuildHasher> {
let mut ctx = DependencyCollectionContext::new(
used_outside_declaring_scope,
temporaries,
@@ -8,4 +8,5 @@ react_compiler_ast = { path = "../react_compiler_ast" }
react_compiler_hir = { path = "../react_compiler_hir" }
react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
indexmap = "2"
rustc-hash = "2"
serde_json = "1"
@@ -1,7 +1,6 @@
use std::collections::HashSet;
use rustc_hash::{FxBuildHasher, FxHashSet};
use indexmap::IndexMap;
use indexmap::IndexSet;
use indexmap::{IndexMap, IndexSet};
use react_compiler_ast::scope::BindingId;
use react_compiler_ast::scope::BindingKind as AstBindingKind;
use react_compiler_ast::scope::ScopeId;
@@ -2531,7 +2530,7 @@ fn collect_binding_names_from_pattern(
pattern: &react_compiler_ast::patterns::PatternLike,
scope_id: react_compiler_ast::scope::ScopeId,
scope_info: &ScopeInfo,
out: &mut HashSet<BindingId>,
out: &mut FxHashSet<BindingId>,
) {
use react_compiler_ast::patterns::PatternLike;
match pattern {
@@ -2712,7 +2711,7 @@ fn lower_block_statement_inner(
}
// Track which bindings have been "declared" (their declaration statement has been seen)
let mut declared: HashSet<BindingId> = HashSet::new();
let mut declared: FxHashSet<BindingId> = FxHashSet::default();
for body_stmt in &block.body {
let stmt_start = statement_start(body_stmt).unwrap_or(0);
@@ -4320,8 +4319,11 @@ pub fn lower(
let context_identifiers = find_context_identifiers(func, scope_info, env, &identifier_locs)?;
// For top-level functions, context is empty (no captured refs)
let context_map: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> =
IndexMap::new();
let context_map: IndexMap<
react_compiler_ast::scope::BindingId,
Option<SourceLocation>,
FxBuildHasher,
> = IndexMap::default();
let (hir_func, _used_names, _child_bindings) = lower_inner(
params,
@@ -5592,7 +5594,7 @@ fn lower_function(
} else {
let parent = builder.function_scope();
let scope_info = builder.scope_info();
let mapped: std::collections::HashSet<react_compiler_ast::scope::ScopeId> =
let mapped: rustc_hash::FxHashSet<react_compiler_ast::scope::ScopeId> =
scope_info.node_id_to_scope.values().copied().collect();
let param_names: Vec<String> = params
.iter()
@@ -5604,7 +5606,7 @@ fn lower_function(
}
})
.collect();
let mut descendants = std::collections::HashSet::new();
let mut descendants = rustc_hash::FxHashSet::default();
descendants.insert(parent);
let mut changed = true;
while changed {
@@ -5671,7 +5673,11 @@ fn lower_function(
ident_locs,
ref_override.as_ref(),
);
let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
let merged_context: IndexMap<
react_compiler_ast::scope::BindingId,
Option<SourceLocation>,
FxBuildHasher,
> = {
let parent_context = builder.context().clone();
let mut merged = parent_context;
for (k, v) in captured_context {
@@ -5743,7 +5749,11 @@ fn lower_function_declaration(
ident_locs,
None,
);
let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
let merged_context: IndexMap<
react_compiler_ast::scope::BindingId,
Option<SourceLocation>,
FxBuildHasher,
> = {
let parent_context = builder.context().clone();
let mut merged = parent_context;
for (k, v) in captured_context {
@@ -5944,7 +5954,11 @@ fn lower_function_for_object_method(
ident_locs,
None,
);
let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
let merged_context: IndexMap<
react_compiler_ast::scope::BindingId,
Option<SourceLocation>,
FxBuildHasher,
> = {
let parent_context = builder.context().clone();
let mut merged = parent_context;
for (k, v) in captured_context {
@@ -5991,19 +6005,27 @@ fn lower_inner(
loc: Option<SourceLocation>,
scope_info: &ScopeInfo,
env: &mut Environment,
parent_bindings: Option<IndexMap<react_compiler_ast::scope::BindingId, IdentifierId>>,
parent_used_names: Option<IndexMap<String, react_compiler_ast::scope::BindingId>>,
context_map: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>>,
parent_bindings: Option<
IndexMap<react_compiler_ast::scope::BindingId, IdentifierId, FxBuildHasher>,
>,
parent_used_names: Option<
IndexMap<String, react_compiler_ast::scope::BindingId, FxBuildHasher>,
>,
context_map: IndexMap<
react_compiler_ast::scope::BindingId,
Option<SourceLocation>,
FxBuildHasher,
>,
function_scope: react_compiler_ast::scope::ScopeId,
component_scope: react_compiler_ast::scope::ScopeId,
context_identifiers: &HashSet<react_compiler_ast::scope::BindingId>,
context_identifiers: &FxHashSet<react_compiler_ast::scope::BindingId>,
is_top_level: bool,
identifier_locs: &IdentifierLocIndex,
) -> Result<
(
HirFunction,
IndexMap<String, react_compiler_ast::scope::BindingId>,
IndexMap<react_compiler_ast::scope::BindingId, IdentifierId>,
IndexMap<String, react_compiler_ast::scope::BindingId, FxBuildHasher>,
IndexMap<react_compiler_ast::scope::BindingId, IdentifierId, FxBuildHasher>,
),
CompilerError,
> {
@@ -6776,22 +6798,22 @@ fn gather_captured_context(
func_start: u32,
func_end: u32,
identifier_locs: &IdentifierLocIndex,
ref_node_ids_override: Option<&IndexSet<u32>>,
) -> IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> {
ref_node_ids_override: Option<&IndexSet<u32, FxBuildHasher>>,
) -> IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>, FxBuildHasher> {
let parent_scope = scope_info.scopes[function_scope.0 as usize].parent;
let pure_scopes = match parent_scope {
Some(parent) => capture_scopes(scope_info, parent, component_scope),
None => IndexSet::new(),
None => IndexSet::default(),
};
// Collect the earliest (lowest source position) reference location for each
// captured binding. Using the minimum position makes the result independent of
// ref_node_id_to_binding iteration order, matching the behavior the TS compiler
// gets from Babel's position-ordered traversal.
let mut captured: std::collections::HashMap<
let mut captured: rustc_hash::FxHashMap<
react_compiler_ast::scope::BindingId,
(u32, Option<SourceLocation>), // (min_position, loc)
> = std::collections::HashMap::new();
> = rustc_hash::FxHashMap::default();
for (&ref_nid, &binding_id) in &scope_info.ref_node_id_to_binding {
if let Some(allowed) = ref_node_ids_override {
@@ -6878,8 +6900,8 @@ fn capture_scopes(
scope_info: &ScopeInfo,
from: react_compiler_ast::scope::ScopeId,
to: react_compiler_ast::scope::ScopeId,
) -> IndexSet<react_compiler_ast::scope::ScopeId> {
let mut result = IndexSet::new();
) -> IndexSet<react_compiler_ast::scope::ScopeId, FxBuildHasher> {
let mut result = IndexSet::default();
let mut current = Some(from);
while let Some(scope_id) = current {
result.insert(scope_id);
@@ -7118,8 +7140,8 @@ fn collect_fbt_sub_tags_from_stmts(
}
}
fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet<u32> {
let mut positions = IndexSet::new();
fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet<u32, FxBuildHasher> {
let mut positions = IndexSet::default();
match body {
FunctionBody::Block(block) => {
for stmt in &block.body {
@@ -7135,7 +7157,7 @@ fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet<u32> {
fn collect_identifier_node_ids_from_stmt(
stmt: &react_compiler_ast::statements::Statement,
positions: &mut IndexSet<u32>,
positions: &mut IndexSet<u32, FxBuildHasher>,
) {
use react_compiler_ast::statements::Statement;
match stmt {
@@ -7175,7 +7197,7 @@ fn collect_identifier_node_ids_from_stmt(
fn collect_identifier_node_ids_from_expr(
expr: &react_compiler_ast::expressions::Expression,
positions: &mut IndexSet<u32>,
positions: &mut IndexSet<u32, FxBuildHasher>,
) {
use react_compiler_ast::expressions::Expression;
match expr {
@@ -4,8 +4,7 @@
//! walking the AST with scope tracking to find variables that cross
//! function boundaries.
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_ast::expressions::*;
use react_compiler_ast::patterns::*;
@@ -35,7 +34,7 @@ struct ContextIdentifierVisitor<'a> {
/// Stack of inner function scopes encountered during traversal.
/// Empty when at the top level of the function being compiled.
function_stack: Vec<ScopeId>,
binding_info: HashMap<BindingId, BindingInfo>,
binding_info: FxHashMap<BindingId, BindingInfo>,
error: Option<CompilerError>,
}
@@ -313,8 +312,8 @@ fn is_captured_by_function(
/// ref_node_id_to_binding. These are entries where the reference's node_id
/// matches the binding's declaration_node_id — i.e., the "reference" is
/// actually the declaration itself.
fn build_declaration_node_ids(scope_info: &ScopeInfo) -> HashSet<(BindingId, u32)> {
let mut result = HashSet::new();
fn build_declaration_node_ids(scope_info: &ScopeInfo) -> FxHashSet<(BindingId, u32)> {
let mut result = FxHashSet::default();
for (&ref_nid, &binding_id) in &scope_info.ref_node_id_to_binding {
let binding = &scope_info.bindings[binding_id.0 as usize];
if binding.declaration_node_id == Some(ref_nid) {
@@ -338,7 +337,7 @@ pub fn find_context_identifiers(
scope_info: &ScopeInfo,
env: &mut Environment,
identifier_locs: &crate::identifier_loc_index::IdentifierLocIndex,
) -> Result<HashSet<BindingId>, CompilerError> {
) -> Result<FxHashSet<BindingId>, CompilerError> {
let func_scope = scope_info
.resolve_scope_for_node(func.node_id())
.unwrap_or(scope_info.program_scope);
@@ -347,7 +346,7 @@ pub fn find_context_identifiers(
scope_info,
env,
function_stack: Vec::new(),
binding_info: HashMap::new(),
binding_info: FxHashMap::default(),
error: None,
};
let mut walker = AstWalker::with_initial_scope(scope_info, func_scope);
@@ -1,5 +1,4 @@
use indexmap::IndexMap;
use indexmap::IndexSet;
use indexmap::{IndexMap, IndexSet};
use react_compiler_ast::scope::BindingId;
use react_compiler_ast::scope::ImportBindingKind;
use react_compiler_ast::scope::ScopeId;
@@ -13,6 +12,7 @@ use react_compiler_hir::environment::Environment;
use react_compiler_hir::visitors::each_terminal_successor;
use react_compiler_hir::visitors::terminal_fallthrough;
use react_compiler_hir::*;
use rustc_hash::FxBuildHasher;
use crate::identifier_loc_index::IdentifierLocIndex;
@@ -139,18 +139,18 @@ fn new_block(id: BlockId, kind: BlockKind) -> WipBlock {
// ---------------------------------------------------------------------------
pub struct HirBuilder<'a> {
completed: IndexMap<BlockId, BasicBlock>,
completed: IndexMap<BlockId, BasicBlock, FxBuildHasher>,
current: WipBlock,
entry: BlockId,
scopes: Vec<Scope>,
/// Context identifiers: variables captured from an outer scope.
/// Maps the outer scope's BindingId to the source location where it was referenced.
context: IndexMap<BindingId, Option<SourceLocation>>,
context: IndexMap<BindingId, Option<SourceLocation>, FxBuildHasher>,
/// Resolved bindings: maps a BindingId to the HIR IdentifierId created for it.
bindings: IndexMap<BindingId, IdentifierId>,
bindings: IndexMap<BindingId, IdentifierId, FxBuildHasher>,
/// Names already used by bindings, for collision avoidance.
/// Maps name string -> how many times it has been used (for appending _0, _1, ...).
used_names: IndexMap<String, BindingId>,
used_names: IndexMap<String, BindingId, FxBuildHasher>,
env: &'a mut Environment,
scope_info: &'a ScopeInfo,
exception_handler_stack: Vec<BlockId>,
@@ -166,10 +166,10 @@ pub struct HirBuilder<'a> {
/// Set of BindingIds for variables declared in scopes between component_scope
/// and any inner function scope, that are referenced from an inner function scope.
/// These need StoreContext/LoadContext instead of StoreLocal/LoadLocal.
context_identifiers: std::collections::HashSet<BindingId>,
context_identifiers: rustc_hash::FxHashSet<BindingId>,
/// Set of ScopeIds that have been matched to synthetic blocks/functions.
/// Prevents the same scope from being reused for different synthetic nodes.
claimed_synthetic_scopes: std::collections::HashSet<ScopeId>,
claimed_synthetic_scopes: rustc_hash::FxHashSet<ScopeId>,
/// Index mapping identifier byte offsets to source locations and JSX status.
identifier_locs: &'a IdentifierLocIndex,
}
@@ -192,17 +192,17 @@ impl<'a> HirBuilder<'a> {
scope_info: &'a ScopeInfo,
function_scope: ScopeId,
component_scope: ScopeId,
context_identifiers: std::collections::HashSet<BindingId>,
bindings: Option<IndexMap<BindingId, IdentifierId>>,
context: Option<IndexMap<BindingId, Option<SourceLocation>>>,
context_identifiers: rustc_hash::FxHashSet<BindingId>,
bindings: Option<IndexMap<BindingId, IdentifierId, FxBuildHasher>>,
context: Option<IndexMap<BindingId, Option<SourceLocation>, FxBuildHasher>>,
entry_block_kind: Option<BlockKind>,
used_names: Option<IndexMap<String, BindingId>>,
used_names: Option<IndexMap<String, BindingId, FxBuildHasher>>,
identifier_locs: &'a IdentifierLocIndex,
) -> Self {
let entry = env.next_block_id();
let kind = entry_block_kind.unwrap_or(BlockKind::Block);
HirBuilder {
completed: IndexMap::new(),
completed: IndexMap::default(),
current: new_block(entry, kind),
entry,
scopes: Vec::new(),
@@ -217,7 +217,7 @@ impl<'a> HirBuilder<'a> {
function_scope,
component_scope,
context_identifiers,
claimed_synthetic_scopes: std::collections::HashSet::new(),
claimed_synthetic_scopes: rustc_hash::FxHashSet::default(),
identifier_locs,
}
}
@@ -290,12 +290,12 @@ impl<'a> HirBuilder<'a> {
}
/// Access the context map.
pub fn context(&self) -> &IndexMap<BindingId, Option<SourceLocation>> {
pub fn context(&self) -> &IndexMap<BindingId, Option<SourceLocation>, FxBuildHasher> {
&self.context
}
/// Access the pre-computed context identifiers set.
pub fn context_identifiers(&self) -> &std::collections::HashSet<BindingId> {
pub fn context_identifiers(&self) -> &rustc_hash::FxHashSet<BindingId> {
&self.context_identifiers
}
@@ -326,18 +326,21 @@ impl<'a> HirBuilder<'a> {
}
/// Access the bindings map.
pub fn bindings(&self) -> &IndexMap<BindingId, IdentifierId> {
pub fn bindings(&self) -> &IndexMap<BindingId, IdentifierId, FxBuildHasher> {
&self.bindings
}
/// Access the used names map.
pub fn used_names(&self) -> &IndexMap<String, BindingId> {
pub fn used_names(&self) -> &IndexMap<String, BindingId, FxBuildHasher> {
&self.used_names
}
/// Merge used names from a child builder back into this builder.
/// This ensures name deduplication works across function scopes.
pub fn merge_used_names(&mut self, child_used_names: IndexMap<String, BindingId>) {
pub fn merge_used_names(
&mut self,
child_used_names: IndexMap<String, BindingId, FxBuildHasher>,
) {
for (name, binding_id) in child_used_names {
self.used_names.entry(name).or_insert(binding_id);
}
@@ -346,7 +349,10 @@ impl<'a> HirBuilder<'a> {
/// Merge bindings (binding_id -> IdentifierId) from a child builder back into this builder.
/// This matches TS behavior where parent and child share the same #bindings map by reference,
/// so bindings resolved by the child are automatically visible to the parent.
pub fn merge_bindings(&mut self, child_bindings: IndexMap<BindingId, IdentifierId>) {
pub fn merge_bindings(
&mut self,
child_bindings: IndexMap<BindingId, IdentifierId, FxBuildHasher>,
) {
for (binding_id, identifier_id) in child_bindings {
self.bindings.entry(binding_id).or_insert(identifier_id);
}
@@ -403,7 +409,7 @@ impl<'a> HirBuilder<'a> {
id: block_id,
instructions: wip.instructions,
terminal,
preds: IndexSet::new(),
preds: IndexSet::default(),
phis: Vec::new(),
},
);
@@ -427,7 +433,7 @@ impl<'a> HirBuilder<'a> {
id: block_id,
instructions: wip.instructions,
terminal,
preds: IndexSet::new(),
preds: IndexSet::default(),
phis: Vec::new(),
},
);
@@ -451,7 +457,7 @@ impl<'a> HirBuilder<'a> {
id: block_id,
instructions: block.instructions,
terminal,
preds: IndexSet::new(),
preds: IndexSet::default(),
phis: Vec::new(),
},
);
@@ -471,7 +477,7 @@ impl<'a> HirBuilder<'a> {
id: completed_wip.id,
instructions: completed_wip.instructions,
terminal,
preds: IndexSet::new(),
preds: IndexSet::default(),
phis: Vec::new(),
},
);
@@ -493,7 +499,7 @@ impl<'a> HirBuilder<'a> {
id: completed_wip.id,
instructions: completed_wip.instructions,
terminal,
preds: IndexSet::new(),
preds: IndexSet::default(),
phis: Vec::new(),
},
);
@@ -769,8 +775,8 @@ impl<'a> HirBuilder<'a> {
(
HIR,
Vec<Instruction>,
IndexMap<String, BindingId>,
IndexMap<BindingId, IdentifierId>,
IndexMap<String, BindingId, FxBuildHasher>,
IndexMap<BindingId, IdentifierId, FxBuildHasher>,
),
CompilerError,
> {
@@ -1150,19 +1156,19 @@ impl<'a> HirBuilder<'a> {
pub fn get_reverse_postordered_blocks(
hir: &HIR,
_instructions: &[Instruction],
) -> IndexMap<BlockId, BasicBlock> {
let mut visited: IndexSet<BlockId> = IndexSet::new();
let mut used: IndexSet<BlockId> = IndexSet::new();
let mut used_fallthroughs: IndexSet<BlockId> = IndexSet::new();
) -> IndexMap<BlockId, BasicBlock, FxBuildHasher> {
let mut visited: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
let mut used: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
let mut used_fallthroughs: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
let mut postorder: Vec<BlockId> = Vec::new();
fn visit(
hir: &HIR,
block_id: BlockId,
is_used: bool,
visited: &mut IndexSet<BlockId>,
used: &mut IndexSet<BlockId>,
used_fallthroughs: &mut IndexSet<BlockId>,
visited: &mut IndexSet<BlockId, FxBuildHasher>,
used: &mut IndexSet<BlockId, FxBuildHasher>,
used_fallthroughs: &mut IndexSet<BlockId, FxBuildHasher>,
postorder: &mut Vec<BlockId>,
) {
let was_used = used.contains(&block_id);
@@ -1222,7 +1228,7 @@ pub fn get_reverse_postordered_blocks(
&mut postorder,
);
let mut blocks = IndexMap::new();
let mut blocks = IndexMap::default();
for block_id in postorder.into_iter().rev() {
let block = hir.blocks.get(&block_id).unwrap();
if used.contains(&block_id) {
@@ -1252,7 +1258,7 @@ pub fn get_reverse_postordered_blocks(
/// For each block with a `For` terminal whose update block is not in the
/// blocks map, set update to None.
pub fn remove_unreachable_for_updates(hir: &mut HIR) {
let block_ids: IndexSet<BlockId> = hir.blocks.keys().copied().collect();
let block_ids: IndexSet<BlockId, FxBuildHasher> = hir.blocks.keys().copied().collect();
for block in hir.blocks.values_mut() {
if let Terminal::For { update, .. } = &mut block.terminal {
if let Some(update_id) = *update {
@@ -1267,7 +1273,7 @@ pub fn remove_unreachable_for_updates(hir: &mut HIR) {
/// For each block with a `DoWhile` terminal whose test block is not in
/// the blocks map, replace the terminal with a Goto to the loop block.
pub fn remove_dead_do_while_statements(hir: &mut HIR) {
let block_ids: IndexSet<BlockId> = hir.blocks.keys().copied().collect();
let block_ids: IndexSet<BlockId, FxBuildHasher> = hir.blocks.keys().copied().collect();
for block in hir.blocks.values_mut() {
let should_replace = if let Terminal::DoWhile { test, .. } = &block.terminal {
!block_ids.contains(test)
@@ -1304,7 +1310,7 @@ pub fn remove_dead_do_while_statements(hir: &mut HIR) {
/// Also cleans up the fallthrough block's predecessors if the handler
/// was the only path to it.
pub fn remove_unnecessary_try_catch(hir: &mut HIR) {
let block_ids: IndexSet<BlockId> = hir.blocks.keys().copied().collect();
let block_ids: IndexSet<BlockId, FxBuildHasher> = hir.blocks.keys().copied().collect();
// Collect the blocks that need replacement and their associated data
let replacements: Vec<(BlockId, BlockId, BlockId, BlockId, Option<SourceLocation>)> = hir
@@ -1376,13 +1382,13 @@ pub fn mark_predecessors(hir: &mut HIR) {
block.preds.clear();
}
let mut visited: IndexSet<BlockId> = IndexSet::new();
let mut visited: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
fn visit(
hir: &mut HIR,
block_id: BlockId,
prev_block_id: Option<BlockId>,
visited: &mut IndexSet<BlockId>,
visited: &mut IndexSet<BlockId, FxBuildHasher>,
) {
// Add predecessor
if let Some(prev_id) = prev_block_id {
@@ -5,7 +5,7 @@
//! lookups; each entry also stores `start` (byte offset) for range-containment
//! checks in `gather_captured_context`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_ast::expressions::*;
use react_compiler_ast::jsx::JSXIdentifier;
@@ -45,7 +45,7 @@ pub struct IdentifierLocEntry {
/// Index mapping node_id → IdentifierLocEntry for all Identifier
/// and JSXIdentifier nodes in a function's AST.
pub type IdentifierLocIndex = HashMap<u32, IdentifierLocEntry>;
pub type IdentifierLocIndex = FxHashMap<u32, IdentifierLocEntry>;
struct IdentifierLocVisitor {
index: IdentifierLocIndex,
@@ -268,7 +268,7 @@ pub fn build_identifier_loc_index(
.unwrap_or(scope_info.program_scope);
let mut visitor = IdentifierLocVisitor {
index: HashMap::new(),
index: FxHashMap::default(),
current_opening_element_loc: None,
};
let mut walker = AstWalker::with_initial_scope(scope_info, func_scope);
@@ -9,3 +9,4 @@ react_compiler_hir = { path = "../react_compiler_hir" }
react_compiler_lowering = { path = "../react_compiler_lowering" }
react_compiler_ssa = { path = "../react_compiler_ssa" }
indexmap = "2"
rustc-hash = "2"
@@ -24,7 +24,7 @@
//!
//! Analogous to TS `Optimization/ConstantPropagation.ts`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::JsString;
use react_compiler_hir::environment::Environment;
@@ -68,16 +68,16 @@ impl Constant {
}
}
/// Map of known constant values. Uses HashMap (not IndexMap) since iteration
/// Map of known constant values. Uses FxHashMap (not IndexMap) since iteration
/// order does not affect correctness — this map is only used for lookups.
type Constants = HashMap<IdentifierId, Constant>;
type Constants = FxHashMap<IdentifierId, Constant>;
// =============================================================================
// Public entry point
// =============================================================================
pub fn constant_propagation(func: &mut HirFunction, env: &mut Environment) {
let mut constants: Constants = HashMap::new();
let mut constants: Constants = FxHashMap::default();
constant_propagation_impl(func, env, &mut constants);
}
@@ -11,7 +11,7 @@
//!
//! Ported from TypeScript `src/Optimization/DeadCodeElimination.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::environment::{Environment, OutputMode};
use react_compiler_hir::object_shape::HookKind;
@@ -69,16 +69,16 @@ pub fn dead_code_elimination(func: &mut HirFunction, env: &Environment) {
/// State for tracking referenced identifiers during mark phase.
struct State {
/// SSA-specific usages (by IdentifierId)
identifiers: HashSet<IdentifierId>,
identifiers: FxHashSet<IdentifierId>,
/// Named variable usages (any version)
named: HashSet<String>,
named: FxHashSet<String>,
}
impl State {
fn new() -> Self {
State {
identifiers: HashSet::new(),
named: HashSet::new(),
identifiers: FxHashSet::default(),
named: FxHashSet::default(),
}
}
@@ -409,7 +409,7 @@ fn pruneable_value(value: &InstructionValue, state: &State, env: &Environment) -
/// Check if the CFG has any back edges (indicating loops).
fn has_back_edge(func: &HirFunction) -> bool {
let mut visited: HashSet<BlockId> = HashSet::new();
let mut visited: FxHashSet<BlockId> = FxHashSet::default();
for (block_id, block) in &func.body.blocks {
for pred_id in &block.preds {
if !visited.contains(pred_id) {
@@ -12,8 +12,7 @@
//!
//! Analogous to TS `Inference/DropManualMemoization.ts`.
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::CompilerDiagnostic;
use react_compiler_diagnostics::CompilerDiagnosticDetail;
@@ -58,17 +57,17 @@ struct ManualMemoCallee {
struct IdentifierSidemap {
/// Maps identifier id -> InstructionId of FunctionExpression instructions
functions: HashSet<IdentifierId>,
functions: FxHashSet<IdentifierId>,
/// Maps identifier id -> ManualMemoCallee for useMemo/useCallback callees
manual_memos: HashMap<IdentifierId, ManualMemoCallee>,
manual_memos: FxHashMap<IdentifierId, ManualMemoCallee>,
/// Set of identifier ids that loaded 'React' global
react: HashSet<IdentifierId>,
react: FxHashSet<IdentifierId>,
/// Maps identifier id -> deps list info for array expressions
maybe_deps_lists: HashMap<IdentifierId, MaybeDepsListInfo>,
maybe_deps_lists: FxHashMap<IdentifierId, MaybeDepsListInfo>,
/// Maps identifier id -> ManualMemoDependency for dependency tracking
maybe_deps: HashMap<IdentifierId, ManualMemoDependency>,
maybe_deps: FxHashMap<IdentifierId, ManualMemoDependency>,
/// Set of identifier ids that are results of optional chains
optionals: HashSet<IdentifierId>,
optionals: FxHashSet<IdentifierId>,
}
#[derive(Debug, Clone)]
@@ -99,11 +98,11 @@ pub fn drop_manual_memoization(
let optionals = find_optional_places(func)?;
let mut sidemap = IdentifierSidemap {
functions: HashSet::new(),
manual_memos: HashMap::new(),
react: HashSet::new(),
maybe_deps: HashMap::new(),
maybe_deps_lists: HashMap::new(),
functions: FxHashSet::default(),
manual_memos: FxHashMap::default(),
react: FxHashSet::default(),
maybe_deps: FxHashMap::default(),
maybe_deps_lists: FxHashMap::default(),
optionals,
};
let mut next_manual_memo_id: u32 = 0;
@@ -113,7 +112,7 @@ pub fn drop_manual_memoization(
// - (if validation is enabled) collect manual memoization markers
//
// queued_inserts maps InstructionId -> new Instruction to insert after that instruction
let mut queued_inserts: HashMap<InstructionId, Instruction> = HashMap::new();
let mut queued_inserts: FxHashMap<InstructionId, Instruction> = FxHashMap::default();
// Collect all block instruction lists up front to avoid borrowing func immutably
// while needing to mutate it
@@ -202,7 +201,7 @@ fn process_manual_memo_call(
sidemap: &mut IdentifierSidemap,
is_validation_enabled: bool,
next_manual_memo_id: &mut u32,
queued_inserts: &mut HashMap<InstructionId, Instruction>,
queued_inserts: &mut FxHashMap<InstructionId, Instruction>,
) {
let instr = &func.instructions[instr_id.0 as usize];
@@ -386,7 +385,7 @@ fn collect_temporaries(
/// Returns the variable + property reads represented by the instruction value.
pub fn collect_maybe_memo_dependencies(
value: &InstructionValue,
maybe_deps: &HashMap<IdentifierId, ManualMemoDependency>,
maybe_deps: &FxHashMap<IdentifierId, ManualMemoDependency>,
optional: bool,
env: &Environment,
) -> Option<ManualMemoDependency> {
@@ -649,10 +648,10 @@ fn extract_manual_memoization_args(
// findOptionalPlaces
// =============================================================================
fn find_optional_places(func: &HirFunction) -> Result<HashSet<IdentifierId>, CompilerDiagnostic> {
fn find_optional_places(func: &HirFunction) -> Result<FxHashSet<IdentifierId>, CompilerDiagnostic> {
use react_compiler_hir::Terminal;
let mut optionals = HashSet::new();
let mut optionals = FxHashSet::default();
for block in func.body.blocks.values() {
if let Terminal::Optional {
optional: true,
@@ -40,7 +40,8 @@
//!
//! Analogous to TS `Inference/InlineImmediatelyInvokedFunctionExpressions.ts`.
use std::collections::{HashMap, HashSet};
use indexmap::IndexSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_hir::environment::Environment;
use react_compiler_hir::visitors;
@@ -62,9 +63,9 @@ pub fn inline_immediately_invoked_function_expressions(
env: &mut Environment,
) {
// Track all function expressions that are assigned to a temporary
let mut functions: HashMap<IdentifierId, FunctionId> = HashMap::new();
let mut functions: FxHashMap<IdentifierId, FunctionId> = FxHashMap::default();
// Functions that are inlined (by identifier id of the callee)
let mut inlined_functions: HashSet<IdentifierId> = HashSet::new();
let mut inlined_functions: FxHashSet<IdentifierId> = FxHashSet::default();
// Iterate the *existing* blocks from the outer component to find IIFEs
// and inline them. During iteration we will modify `func` (by inlining the CFG
@@ -140,7 +141,7 @@ pub fn inline_immediately_invoked_function_expressions(
instructions: continuation_instructions,
kind: block_kind,
phis: Vec::new(),
preds: indexmap::IndexSet::new(),
preds: IndexSet::default(),
terminal: continuation_terminal,
};
func.body
@@ -13,7 +13,7 @@
//!
//! Analogous to TS `HIR/MergeConsecutiveBlocks.ts`.
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_hir::visitors;
use react_compiler_hir::{
@@ -53,7 +53,7 @@ pub fn merge_consecutive_blocks(func: &mut HirFunction, functions: &mut [HirFunc
}
// Build fallthrough set
let mut fallthrough_blocks: HashSet<BlockId> = HashSet::new();
let mut fallthrough_blocks: FxHashSet<BlockId> = FxHashSet::default();
for block in func.body.blocks.values() {
if let Some(ft) = visitors::terminal_fallthrough(&block.terminal) {
fallthrough_blocks.insert(ft);
@@ -186,13 +186,13 @@ pub fn merge_consecutive_blocks(func: &mut HirFunction, functions: &mut [HirFunc
/// Tracks which blocks have been merged and into which target.
struct MergedBlocks {
map: HashMap<BlockId, BlockId>,
map: FxHashMap<BlockId, BlockId>,
}
impl MergedBlocks {
fn new() -> Self {
Self {
map: HashMap::new(),
map: FxHashMap::default(),
}
}
@@ -11,7 +11,7 @@
//!
//! Conditional on `env.config.enable_name_anonymous_functions`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::object_shape::HookKind;
@@ -63,7 +63,7 @@ pub fn name_anonymous_functions(func: &mut HirFunction, env: &mut Environment) {
if updates.is_empty() {
return;
}
let update_map: HashMap<FunctionId, &String> =
let update_map: FxHashMap<FunctionId, &String> =
updates.iter().map(|(fid, name)| (*fid, name)).collect();
// 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) {
/// Apply name hints to FunctionExpression instruction values.
fn apply_name_hints_to_instructions(
instructions: &mut [Instruction],
update_map: &HashMap<FunctionId, &String>,
update_map: &FxHashMap<FunctionId, &String>,
) {
for instr in instructions.iter_mut() {
if let InstructionValue::FunctionExpression {
@@ -117,9 +117,9 @@ struct Node {
fn name_anonymous_functions_impl(func: &HirFunction, env: &Environment) -> Vec<Node> {
// Functions that we track to generate names for
let mut functions: HashMap<IdentifierId, usize> = HashMap::new();
let mut functions: FxHashMap<IdentifierId, usize> = FxHashMap::default();
// Tracks temporaries that read from variables/globals/properties
let mut names: HashMap<IdentifierId, String> = HashMap::new();
let mut names: FxHashMap<IdentifierId, String> = FxHashMap::default();
// Tracks all function nodes
let mut nodes: Vec<Node> = Vec::new();
@@ -256,8 +256,8 @@ fn handle_call(
_func: &HirFunction,
callee_id: IdentifierId,
args: &[PlaceOrSpread],
functions: &mut HashMap<IdentifierId, usize>,
names: &HashMap<IdentifierId, String>,
functions: &mut FxHashMap<IdentifierId, usize>,
names: &FxHashMap<IdentifierId, String>,
nodes: &mut Vec<Node>,
) {
let callee_ident = &env.identifiers[callee_id.0 as usize];
@@ -17,7 +17,7 @@
//!
//! Ported from TypeScript `src/Optimization/OptimizeForSSR.ts`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::object_shape::HookKind;
@@ -43,7 +43,7 @@ pub fn optimize_for_ssr(func: &mut HirFunction, env: &Environment) {
// Any use of the hook return other than the expected destructuring pattern
// prevents inlining (we delete from inlined_state if we see the identifier used
// as an operand elsewhere).
let mut inlined_state: HashMap<IdentifierId, InlinedStateReplacement> = HashMap::new();
let mut inlined_state: FxHashMap<IdentifierId, InlinedStateReplacement> = FxHashMap::default();
for (_block_id, block) in &func.body.blocks {
for &instr_id in &block.instructions {
@@ -11,7 +11,7 @@
//!
//! Conditional on `env.config.enable_function_outlining`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::{
@@ -25,7 +25,7 @@ use react_compiler_ssa::enter_ssa::placeholder_function;
pub fn outline_functions(
func: &mut HirFunction,
env: &mut Environment,
fbt_operands: &HashSet<IdentifierId>,
fbt_operands: &FxHashSet<IdentifierId>,
) {
// Collect per-instruction actions to maintain depth-first name allocation order.
// Each entry: (instr index, function_id to recurse into, should_outline)
@@ -8,9 +8,9 @@
//! Outlines JSX expressions in callbacks into separate component functions.
//! This pass is conditional on `env.config.enable_jsx_outlining` (defaults to false).
use std::collections::{HashMap, HashSet};
use indexmap::{IndexMap, IndexSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use indexmap::IndexMap;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::{
BasicBlock, BlockId, BlockKind, EvaluationOrder, FunctionId, HIR, HirFunction, IdentifierId,
@@ -58,7 +58,7 @@ fn outline_jsx_impl(
outlined_fns: &mut Vec<HirFunction>,
) {
// Collect LoadGlobal instructions (tag -> instr)
let mut globals: HashMap<IdentifierId, usize> = HashMap::new(); // id -> instr_idx
let mut globals: FxHashMap<IdentifierId, usize> = FxHashMap::default(); // id -> instr_idx
// Process each block
let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();
@@ -66,9 +66,9 @@ fn outline_jsx_impl(
let block = &func.body.blocks[block_id];
let instr_ids = block.instructions.clone();
let mut rewrite_instr: HashMap<EvaluationOrder, Vec<Instruction>> = HashMap::new();
let mut rewrite_instr: FxHashMap<EvaluationOrder, Vec<Instruction>> = FxHashMap::default();
let mut jsx_group: Vec<JsxInstrInfo> = Vec::new();
let mut children_ids: HashSet<IdentifierId> = HashSet::new();
let mut children_ids: FxHashSet<IdentifierId> = FxHashSet::default();
// First pass: collect all instruction info without borrowing func mutably
enum InstrAction {
@@ -211,8 +211,8 @@ fn process_and_outline_jsx(
func: &mut HirFunction,
env: &mut Environment,
jsx_group: &mut Vec<JsxInstrInfo>,
globals: &HashMap<IdentifierId, usize>,
rewrite_instr: &mut HashMap<EvaluationOrder, Vec<Instruction>>,
globals: &FxHashMap<IdentifierId, usize>,
rewrite_instr: &mut FxHashMap<EvaluationOrder, Vec<Instruction>>,
outlined_fns: &mut Vec<HirFunction>,
) {
if jsx_group.len() <= 1 {
@@ -237,7 +237,7 @@ fn process_jsx_group(
func: &HirFunction,
env: &mut Environment,
jsx_group: &[JsxInstrInfo],
globals: &HashMap<IdentifierId, usize>,
globals: &FxHashMap<IdentifierId, usize>,
) -> Option<OutlinedResult> {
// Only outline in callbacks, not top-level components
if func.fn_type == ReactFunctionType::Component {
@@ -266,9 +266,9 @@ fn collect_props(
jsx_group: &[JsxInstrInfo],
) -> Option<Vec<OutlinedJsxAttribute>> {
let mut id_counter = 1u32;
let mut seen: HashSet<String> = HashSet::new();
let mut seen: FxHashSet<String> = FxHashSet::default();
let mut attributes = Vec::new();
let jsx_ids: HashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
let jsx_ids: FxHashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
let mut generate_name = |old_name: &str, _env: &mut Environment| -> String {
let mut new_name = old_name.to_string();
@@ -402,7 +402,7 @@ fn emit_outlined_fn(
env: &mut Environment,
jsx_group: &[JsxInstrInfo],
old_props: &[OutlinedJsxAttribute],
globals: &HashMap<IdentifierId, usize>,
globals: &FxHashMap<IdentifierId, usize>,
) -> Option<HirFunction> {
let old_to_new_props = create_old_to_new_props_mapping(env, old_props);
@@ -458,7 +458,7 @@ fn emit_outlined_fn(
kind: BlockKind::Block,
id: BlockId(0),
instructions: instr_ids,
preds: indexmap::IndexSet::new(),
preds: IndexSet::default(),
terminal: Terminal::Return {
value: last_lvalue,
return_variant: ReturnVariant::Explicit,
@@ -469,7 +469,7 @@ fn emit_outlined_fn(
phis: Vec::new(),
};
let mut blocks = IndexMap::new();
let mut blocks = IndexMap::default();
blocks.insert(BlockId(0), block);
let outlined_fn = HirFunction {
@@ -498,7 +498,7 @@ fn emit_outlined_fn(
fn emit_load_globals(
func: &HirFunction,
jsx_group: &[JsxInstrInfo],
globals: &HashMap<IdentifierId, usize>,
globals: &FxHashMap<IdentifierId, usize>,
) -> Option<Vec<Instruction>> {
let mut instructions = Vec::new();
for info in jsx_group {
@@ -516,9 +516,9 @@ fn emit_load_globals(
fn emit_updated_jsx(
func: &HirFunction,
jsx_group: &[JsxInstrInfo],
old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute>,
old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute, FxBuildHasher>,
) -> Vec<Instruction> {
let jsx_ids: HashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
let jsx_ids: FxHashSet<IdentifierId> = jsx_group.iter().map(|j| j.lvalue_id).collect();
let mut new_instrs = Vec::new();
for info in jsx_group {
@@ -594,8 +594,8 @@ fn emit_updated_jsx(
fn create_old_to_new_props_mapping(
env: &mut Environment,
old_props: &[OutlinedJsxAttribute],
) -> IndexMap<IdentifierId, OutlinedJsxAttribute> {
let mut old_to_new = IndexMap::new();
) -> IndexMap<IdentifierId, OutlinedJsxAttribute, FxBuildHasher> {
let mut old_to_new = IndexMap::default();
for old_prop in old_props {
if old_prop.original_name == "key" {
@@ -629,7 +629,7 @@ fn create_old_to_new_props_mapping(
fn emit_destructure_props(
env: &mut Environment,
props_obj: &Place,
old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute>,
old_to_new_props: &IndexMap<IdentifierId, OutlinedJsxAttribute, FxBuildHasher>,
) -> Instruction {
let mut properties = Vec::new();
for prop in old_to_new_props.values() {
@@ -10,7 +10,7 @@
//!
//! Analogous to TS `Optimization/PruneMaybeThrows.ts`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, GENERATED_SOURCE,
@@ -86,8 +86,8 @@ pub fn prune_maybe_throws(
Ok(())
}
fn prune_maybe_throws_impl(func: &mut HirFunction) -> Option<HashMap<BlockId, BlockId>> {
let mut terminal_mapping: HashMap<BlockId, BlockId> = HashMap::new();
fn prune_maybe_throws_impl(func: &mut HirFunction) -> Option<FxHashMap<BlockId, BlockId>> {
let mut terminal_mapping: FxHashMap<BlockId, BlockId> = FxHashMap::default();
let instructions = &func.instructions;
for block in func.body.blocks.values_mut() {
@@ -12,7 +12,7 @@
//! Analogous to TS `PruneUnusedLabelsHIR.ts`.
use react_compiler_hir::{BlockId, BlockKind, GotoVariant, HirFunction, Terminal};
use std::collections::HashMap;
use rustc_hash::FxHashMap;
pub fn prune_unused_labels_hir(func: &mut HirFunction) {
// Phase 1: Identify label terminals whose body block immediately breaks
@@ -45,7 +45,7 @@ pub fn prune_unused_labels_hir(func: &mut HirFunction) {
}
// Phase 2: Apply merges
let mut rewrites: HashMap<BlockId, BlockId> = HashMap::new();
let mut rewrites: FxHashMap<BlockId, BlockId> = FxHashMap::default();
for (original_label_id, next_id, fallthrough_id) in &merged {
let label_id = rewrites
@@ -8,5 +8,6 @@ react_compiler_ast = { path = "../react_compiler_ast" }
react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
react_compiler_hir = { path = "../react_compiler_hir" }
indexmap = "2"
rustc-hash = "2"
serde_json = "1"
hmac-sha256 = "1"
@@ -8,7 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/AssertScopeInstructionsWithinScope.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
use react_compiler_hir::environment::Environment;
@@ -25,7 +25,7 @@ pub fn assert_scope_instructions_within_scopes(
env: &Environment,
) -> Result<(), CompilerDiagnostic> {
// Pass 1: Collect all scope IDs
let mut existing_scopes: HashSet<ScopeId> = HashSet::new();
let mut existing_scopes: FxHashSet<ScopeId> = FxHashSet::default();
let find_visitor = FindAllScopesVisitor { env };
visit_reactive_function(func, &find_visitor, &mut existing_scopes);
@@ -33,7 +33,7 @@ pub fn assert_scope_instructions_within_scopes(
let check_visitor = CheckInstructionsAgainstScopesVisitor { env };
let mut check_state = CheckState {
existing_scopes,
active_scopes: HashSet::new(),
active_scopes: FxHashSet::default(),
error: None,
};
visit_reactive_function(func, &check_visitor, &mut check_state);
@@ -52,13 +52,13 @@ struct FindAllScopesVisitor<'a> {
}
impl<'a> ReactiveFunctionVisitor for FindAllScopesVisitor<'a> {
type State = HashSet<ScopeId>;
type State = FxHashSet<ScopeId>;
fn env(&self) -> &Environment {
self.env
}
fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut HashSet<ScopeId>) {
fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut FxHashSet<ScopeId>) {
self.traverse_scope(scope, state);
state.insert(scope.scope);
}
@@ -69,8 +69,8 @@ impl<'a> ReactiveFunctionVisitor for FindAllScopesVisitor<'a> {
// =============================================================================
struct CheckState {
existing_scopes: HashSet<ScopeId>,
active_scopes: HashSet<ScopeId>,
existing_scopes: FxHashSet<ScopeId>,
active_scopes: FxHashSet<ScopeId>,
error: Option<CompilerDiagnostic>,
}
@@ -7,7 +7,7 @@
//!
//! Corresponds to `src/ReactiveScopes/AssertWellFormedBreakTargets.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::{
BlockId, ReactiveFunction, ReactiveTerminal, ReactiveTerminalStatement,
@@ -19,7 +19,7 @@ use crate::visitors::{ReactiveFunctionVisitor, visit_reactive_function};
/// Assert that all break/continue targets reference existent labels.
pub fn assert_well_formed_break_targets(func: &ReactiveFunction, env: &Environment) {
let visitor = Visitor { env };
let mut state: HashSet<BlockId> = HashSet::new();
let mut state: FxHashSet<BlockId> = FxHashSet::default();
visit_reactive_function(func, &visitor, &mut state);
}
@@ -28,13 +28,17 @@ struct Visitor<'a> {
}
impl<'a> ReactiveFunctionVisitor for Visitor<'a> {
type State = HashSet<BlockId>;
type State = FxHashSet<BlockId>;
fn env(&self) -> &Environment {
self.env
}
fn visit_terminal(&self, stmt: &ReactiveTerminalStatement, seen_labels: &mut HashSet<BlockId>) {
fn visit_terminal(
&self,
stmt: &ReactiveTerminalStatement,
seen_labels: &mut FxHashSet<BlockId>,
) {
if let Some(label) = &stmt.label {
seen_labels.insert(label.id);
}
@@ -7,7 +7,7 @@
//!
//! Corresponds to `src/ReactiveScopes/BuildReactiveFunction.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -108,10 +108,10 @@ impl ControlFlowTarget {
struct Context<'a> {
ir: &'a HirFunction,
next_schedule_id: u32,
emitted: HashSet<BlockId>,
scope_fallthroughs: HashSet<BlockId>,
scheduled: HashSet<BlockId>,
catch_handlers: HashSet<BlockId>,
emitted: FxHashSet<BlockId>,
scope_fallthroughs: FxHashSet<BlockId>,
scheduled: FxHashSet<BlockId>,
catch_handlers: FxHashSet<BlockId>,
control_flow_stack: Vec<ControlFlowTarget>,
}
@@ -120,10 +120,10 @@ impl<'a> Context<'a> {
Self {
ir,
next_schedule_id: 0,
emitted: HashSet::new(),
scope_fallthroughs: HashSet::new(),
scheduled: HashSet::new(),
catch_handlers: HashSet::new(),
emitted: FxHashSet::default(),
scope_fallthroughs: FxHashSet::default(),
scheduled: FxHashSet::default(),
catch_handlers: FxHashSet::default(),
control_flow_stack: Vec::new(),
}
}
@@ -10,8 +10,7 @@
//!
//! Corresponds to `src/ReactiveScopes/CodegenReactiveFunction.ts` in the TS compiler.
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_ast::common::BaseNode;
use react_compiler_ast::common::Position as AstPosition;
@@ -195,8 +194,8 @@ fn source_file_hash(code: &str) -> String {
pub fn codegen_function(
func: &ReactiveFunction,
env: &mut Environment,
unique_identifiers: HashSet<String>,
fbt_operands: HashSet<IdentifierId>,
unique_identifiers: FxHashSet<String>,
fbt_operands: FxHashSet<IdentifierId>,
) -> Result<CodegenFunction, CompilerError> {
let fn_name = func.id.as_deref().unwrap_or("[[ anonymous ]]");
let mut cx = Context::new(env, fn_name.to_string(), unique_identifiers, fbt_operands);
@@ -556,7 +555,7 @@ pub fn codegen_function(
// Context
// =============================================================================
type Temporaries = HashMap<DeclarationId, Option<ExpressionOrJsxText>>;
type Temporaries = FxHashMap<DeclarationId, Option<ExpressionOrJsxText>>;
#[derive(Clone)]
enum ExpressionOrJsxText {
@@ -569,37 +568,37 @@ struct Context<'env> {
#[allow(dead_code)]
fn_name: String,
next_cache_index: u32,
declarations: HashSet<DeclarationId>,
declarations: FxHashSet<DeclarationId>,
temp: Temporaries,
object_methods: HashMap<
object_methods: FxHashMap<
IdentifierId,
(
InstructionValue,
Option<react_compiler_diagnostics::SourceLocation>,
),
>,
unique_identifiers: HashSet<String>,
fbt_operands: HashSet<IdentifierId>,
synthesized_names: HashMap<String, String>,
unique_identifiers: FxHashSet<String>,
fbt_operands: FxHashSet<IdentifierId>,
synthesized_names: FxHashMap<String, String>,
}
impl<'env> Context<'env> {
fn new(
env: &'env mut Environment,
fn_name: String,
unique_identifiers: HashSet<String>,
fbt_operands: HashSet<IdentifierId>,
unique_identifiers: FxHashSet<String>,
fbt_operands: FxHashSet<IdentifierId>,
) -> Self {
Context {
env,
fn_name,
next_cache_index: 0,
declarations: HashSet::new(),
temp: HashMap::new(),
object_methods: HashMap::new(),
declarations: FxHashSet::default(),
temp: FxHashMap::default(),
object_methods: FxHashMap::default(),
unique_identifiers,
fbt_operands,
synthesized_names: HashMap::new(),
synthesized_names: FxHashMap::default(),
}
}
@@ -4163,7 +4162,7 @@ fn create_function_body_hook_guard(
fn apply_renames_to_json(
value: &mut serde_json::Value,
renames: &[react_compiler_hir::environment::BindingRename],
reference_node_ids: &std::collections::HashSet<u32>,
reference_node_ids: &rustc_hash::FxHashSet<u32>,
) {
apply_renames_to_json_inner(value, renames, reference_node_ids, false);
}
@@ -4171,7 +4170,7 @@ fn apply_renames_to_json(
fn apply_renames_to_json_inner(
value: &mut serde_json::Value,
renames: &[react_compiler_hir::environment::BindingRename],
reference_node_ids: &std::collections::HashSet<u32>,
reference_node_ids: &rustc_hash::FxHashSet<u32>,
is_property_key: bool,
) {
if renames.is_empty() {
@@ -8,7 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/ExtractScopeDeclarationsFromDestructuring.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::{
DeclarationId, IdentifierId, IdentifierName, InstructionKind, InstructionValue, LValue,
@@ -29,7 +29,7 @@ pub fn extract_scope_declarations_from_destructuring(
func: &mut ReactiveFunction,
env: &mut Environment,
) -> Result<(), react_compiler_diagnostics::CompilerError> {
let mut declared: HashSet<DeclarationId> = HashSet::new();
let mut declared: FxHashSet<DeclarationId> = FxHashSet::default();
for param in &func.params {
let place = match param {
ParamPattern::Place(p) => p,
@@ -44,7 +44,7 @@ pub fn extract_scope_declarations_from_destructuring(
}
struct ExtractState {
declared: HashSet<DeclarationId>,
declared: FxHashSet<DeclarationId>,
}
struct Transform<'a> {
@@ -94,7 +94,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> {
}) = &mut instruction.value
{
// Check if this is a mixed destructuring (some declared, some not)
let mut reassigned: HashSet<IdentifierId> = HashSet::new();
let mut reassigned: FxHashSet<IdentifierId> = FxHashSet::default();
let mut has_declaration = false;
for place in visitors::each_pattern_operand(&lvalue.pattern) {
@@ -8,7 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts`.
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::CompilerError;
use react_compiler_hir::{
@@ -36,14 +36,14 @@ pub fn merge_reactive_scopes_that_invalidate_together(
) -> Result<(), CompilerError> {
// Pass 1: find last usage of each declaration
let visitor = FindLastUsageVisitor { env: &*env };
let mut last_usage: HashMap<DeclarationId, EvaluationOrder> = HashMap::new();
let mut last_usage: FxHashMap<DeclarationId, EvaluationOrder> = FxHashMap::default();
visit_reactive_function(func, &visitor, &mut last_usage);
// Pass 2+3: merge scopes
let mut transform = MergeTransform {
env,
last_usage,
temporaries: HashMap::new(),
temporaries: FxHashMap::default(),
};
let mut state: Option<Vec<ReactiveScopeDependency>> = None;
transform_reactive_function(func, &mut transform, &mut state)
@@ -59,7 +59,7 @@ struct FindLastUsageVisitor<'a> {
}
impl<'a> ReactiveFunctionVisitor for FindLastUsageVisitor<'a> {
type State = HashMap<DeclarationId, EvaluationOrder>;
type State = FxHashMap<DeclarationId, EvaluationOrder>;
fn env(&self) -> &Environment {
self.env
@@ -81,8 +81,8 @@ impl<'a> ReactiveFunctionVisitor for FindLastUsageVisitor<'a> {
/// TS: `class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | null>`
struct MergeTransform<'a> {
env: &'a mut Environment,
last_usage: HashMap<DeclarationId, EvaluationOrder>,
temporaries: HashMap<DeclarationId, DeclarationId>,
last_usage: FxHashMap<DeclarationId, EvaluationOrder>,
temporaries: FxHashMap<DeclarationId, DeclarationId>,
}
impl<'a> ReactiveFunctionTransform for MergeTransform<'a> {
@@ -138,7 +138,7 @@ impl<'a> MergeTransform<'a> {
scope_id: ScopeId,
from: usize,
to: usize,
lvalues: HashSet<DeclarationId>,
lvalues: FxHashSet<DeclarationId>,
}
let mut current: Option<MergedScope> = None;
@@ -308,7 +308,7 @@ impl<'a> MergeTransform<'a> {
scope_id: next_scope_id,
from: i,
to: i + 1,
lvalues: HashSet::new(),
lvalues: FxHashSet::default(),
});
}
}
@@ -319,7 +319,7 @@ impl<'a> MergeTransform<'a> {
scope_id: next_scope_id,
from: i,
to: i + 1,
lvalues: HashSet::new(),
lvalues: FxHashSet::default(),
});
}
}
@@ -398,7 +398,7 @@ impl<'a> MergeTransform<'a> {
/// Updates scope declarations to remove any that are not used after the scope.
fn update_scope_declarations(
scope_id: ScopeId,
last_usage: &HashMap<DeclarationId, EvaluationOrder>,
last_usage: &FxHashMap<DeclarationId, EvaluationOrder>,
env: &mut Environment,
) {
let range_end = env.scopes[scope_id.0 as usize].range.end;
@@ -417,8 +417,8 @@ fn update_scope_declarations(
/// Returns whether all lvalues are last used at or before the given scope.
fn are_lvalues_last_used_by_scope(
scope_id: ScopeId,
lvalues: &HashSet<DeclarationId>,
last_usage: &HashMap<DeclarationId, EvaluationOrder>,
lvalues: &FxHashSet<DeclarationId>,
last_usage: &FxHashMap<DeclarationId, EvaluationOrder>,
env: &Environment,
) -> bool {
let range_end = env.scopes[scope_id.0 as usize].range.end;
@@ -437,7 +437,7 @@ fn can_merge_scopes(
current_id: ScopeId,
next_id: ScopeId,
env: &Environment,
temporaries: &HashMap<DeclarationId, DeclarationId>,
temporaries: &FxHashMap<DeclarationId, DeclarationId>,
) -> bool {
let current = &env.scopes[current_id.0 as usize];
let next = &env.scopes[next_id.0 as usize];
@@ -8,8 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/PromoteUsedTemporaries.ts`.
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_hir::DeclarationId;
use react_compiler_hir::FunctionId;
@@ -35,9 +34,9 @@ use react_compiler_hir::environment::Environment;
// =============================================================================
struct State {
tags: HashSet<DeclarationId>,
promoted: HashSet<DeclarationId>,
pruned: HashMap<DeclarationId, PrunedInfo>,
tags: FxHashSet<DeclarationId>,
promoted: FxHashSet<DeclarationId>,
pruned: FxHashMap<DeclarationId, PrunedInfo>,
}
struct PrunedInfo {
@@ -53,9 +52,9 @@ struct PrunedInfo {
/// TS: `promoteUsedTemporaries`
pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environment) {
let mut state = State {
tags: HashSet::new(),
promoted: HashSet::new(),
pruned: HashMap::new(),
tags: FxHashSet::default(),
promoted: FxHashSet::default(),
pruned: FxHashMap::default(),
};
// 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
promote_temporaries_block(&func.body, &mut state, env);
// Phase 3: promote interposed temporaries
let mut consts: HashSet<IdentifierId> = HashSet::new();
let mut globals: HashSet<IdentifierId> = HashSet::new();
let mut consts: FxHashSet<IdentifierId> = FxHashSet::default();
let mut globals: FxHashSet<IdentifierId> = FxHashSet::default();
for param in &func.params {
match param {
ParamPattern::Place(p) => {
@@ -90,7 +89,7 @@ pub fn promote_used_temporaries(func: &mut ReactiveFunction, env: &mut Environme
}
}
}
let mut inter_state: HashMap<IdentifierId, (IdentifierId, bool)> = HashMap::new();
let mut inter_state: FxHashMap<IdentifierId, (IdentifierId, bool)> = FxHashMap::default();
promote_interposed_block(
&func.body,
&mut state,
@@ -555,9 +554,9 @@ fn visit_hir_function_for_promotion(func_id: FunctionId, state: &mut State, env:
fn promote_interposed_block(
block: &ReactiveBlock,
state: &mut State,
inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut HashSet<IdentifierId>,
globals: &mut HashSet<IdentifierId>,
inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut FxHashSet<IdentifierId>,
globals: &mut FxHashSet<IdentifierId>,
env: &mut Environment,
) {
for stmt in block {
@@ -595,8 +594,8 @@ fn promote_interposed_block(
fn promote_interposed_place(
place: &Place,
state: &mut State,
inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
consts: &HashSet<IdentifierId>,
inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
consts: &FxHashSet<IdentifierId>,
env: &mut Environment,
) {
if let Some(&(id, needs_promotion)) = inter_state.get(&place.identifier) {
@@ -610,9 +609,9 @@ fn promote_interposed_place(
fn promote_interposed_instruction(
instr: &ReactiveInstruction,
state: &mut State,
inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut HashSet<IdentifierId>,
globals: &mut HashSet<IdentifierId>,
inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut FxHashSet<IdentifierId>,
globals: &mut FxHashSet<IdentifierId>,
env: &mut Environment,
) {
// Check instruction value lvalues (assignment targets)
@@ -803,9 +802,9 @@ fn promote_interposed_instruction(
fn promote_interposed_value(
value: &ReactiveValue,
state: &mut State,
inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut HashSet<IdentifierId>,
globals: &mut HashSet<IdentifierId>,
inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut FxHashSet<IdentifierId>,
globals: &mut FxHashSet<IdentifierId>,
env: &mut Environment,
) {
match value {
@@ -847,9 +846,9 @@ fn promote_interposed_value(
fn promote_interposed_terminal(
stmt: &ReactiveTerminalStatement,
state: &mut State,
inter_state: &mut HashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut HashSet<IdentifierId>,
globals: &mut HashSet<IdentifierId>,
inter_state: &mut FxHashMap<IdentifierId, (IdentifierId, bool)>,
consts: &mut FxHashSet<IdentifierId>,
globals: &mut FxHashSet<IdentifierId>,
env: &mut Environment,
) {
match &stmt.terminal {
@@ -11,7 +11,7 @@
//!
//! Corresponds to `src/ReactiveScopes/PruneAlwaysInvalidatingScopes.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::{
IdentifierId, InstructionValue, PrunedReactiveScopeBlock, ReactiveFunction,
@@ -30,8 +30,8 @@ pub fn prune_always_invalidating_scopes(
) -> Result<(), react_compiler_diagnostics::CompilerError> {
let mut transform = Transform {
env,
always_invalidating_values: HashSet::new(),
unmemoized_values: HashSet::new(),
always_invalidating_values: FxHashSet::default(),
unmemoized_values: FxHashSet::default(),
};
let mut state = false; // withinScope
transform_reactive_function(func, &mut transform, &mut state)
@@ -39,8 +39,8 @@ pub fn prune_always_invalidating_scopes(
struct Transform<'a> {
env: &'a Environment,
always_invalidating_values: HashSet<IdentifierId>,
unmemoized_values: HashSet<IdentifierId>,
always_invalidating_values: FxHashSet<IdentifierId>,
unmemoized_values: FxHashSet<IdentifierId>,
}
impl<'a> ReactiveFunctionTransform for Transform<'a> {
@@ -8,7 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/PruneHoistedContexts.ts`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::{CompilerError, CompilerErrorDetail, ErrorCategory};
use react_compiler_hir::{
@@ -33,7 +33,7 @@ pub fn prune_hoisted_contexts(
let mut transform = Transform { env };
let mut state = VisitorState {
active_scopes: Vec::new(),
uninitialized: HashMap::new(),
uninitialized: FxHashMap::default(),
};
transform_reactive_function(func, &mut transform, &mut state)
}
@@ -49,8 +49,8 @@ enum UninitializedKind {
}
struct VisitorState {
active_scopes: Vec<std::collections::HashSet<IdentifierId>>,
uninitialized: HashMap<IdentifierId, UninitializedKind>,
active_scopes: Vec<rustc_hash::FxHashSet<IdentifierId>>,
uninitialized: FxHashMap<IdentifierId, UninitializedKind>,
}
impl VisitorState {
@@ -81,7 +81,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> {
state: &mut VisitorState,
) -> Result<(), CompilerError> {
let scope_data = &self.env.scopes[scope.scope.0 as usize];
let decl_ids: std::collections::HashSet<IdentifierId> =
let decl_ids: rustc_hash::FxHashSet<IdentifierId> =
scope_data.declarations.iter().map(|(id, _)| *id).collect();
// Add declared but not initialized variables
@@ -8,8 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/PruneNonEscapingScopes.ts`.
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use indexmap::IndexSet;
use react_compiler_hir::ArrayPatternElement;
@@ -74,8 +73,8 @@ pub fn prune_non_escaping_scopes(
// Prune scopes that do not declare/reassign any escaping values
let mut transform = PruneScopesTransform {
env,
pruned_scopes: HashSet::new(),
reassignments: HashMap::new(),
pruned_scopes: FxHashSet::default(),
reassignments: FxHashMap::default(),
};
let mut memoized_state = memoized;
transform_reactive_function(func, &mut transform, &mut memoized_state)
@@ -120,8 +119,8 @@ fn join_aliases(kind1: MemoizationLevel, kind2: MemoizationLevel) -> Memoization
struct IdentifierNode {
level: MemoizationLevel,
memoized: bool,
dependencies: IndexSet<DeclarationId>,
scopes: IndexSet<ScopeId>,
dependencies: IndexSet<DeclarationId, FxBuildHasher>,
scopes: IndexSet<ScopeId, FxBuildHasher>,
seen: bool,
}
@@ -137,19 +136,19 @@ struct ScopeNode {
struct CollectState {
/// Maps lvalues for LoadLocal to the identifier being loaded, to resolve indirections.
definitions: HashMap<DeclarationId, DeclarationId>,
identifiers: HashMap<DeclarationId, IdentifierNode>,
scopes: HashMap<ScopeId, ScopeNode>,
escaping_values: IndexSet<DeclarationId>,
definitions: FxHashMap<DeclarationId, DeclarationId>,
identifiers: FxHashMap<DeclarationId, IdentifierNode>,
scopes: FxHashMap<ScopeId, ScopeNode>,
escaping_values: IndexSet<DeclarationId, FxBuildHasher>,
}
impl CollectState {
fn new() -> Self {
CollectState {
definitions: HashMap::new(),
identifiers: HashMap::new(),
scopes: HashMap::new(),
escaping_values: IndexSet::new(),
definitions: FxHashMap::default(),
identifiers: FxHashMap::default(),
scopes: FxHashMap::default(),
escaping_values: IndexSet::default(),
}
}
@@ -160,8 +159,8 @@ impl CollectState {
IdentifierNode {
level: MemoizationLevel::Never,
memoized: false,
dependencies: IndexSet::new(),
scopes: IndexSet::new(),
dependencies: IndexSet::default(),
scopes: IndexSet::default(),
seen: false,
},
);
@@ -900,8 +899,8 @@ impl<'a> CollectDependenciesVisitor<'a> {
.or_insert_with(|| IdentifierNode {
level: MemoizationLevel::Never,
memoized: false,
dependencies: IndexSet::new(),
scopes: IndexSet::new(),
dependencies: IndexSet::default(),
scopes: IndexSet::default(),
seen: false,
});
node.level = join_aliases(node.level, lv.level);
@@ -1049,17 +1048,17 @@ impl<'a> ReactiveFunctionVisitor for CollectDependenciesVisitor<'a> {
// computeMemoizedIdentifiers
// =============================================================================
fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId> {
let mut memoized = HashSet::new();
fn compute_memoized_identifiers(state: &CollectState) -> FxHashSet<DeclarationId> {
let mut memoized = FxHashSet::default();
// We need mutable access to the nodes, so we clone the state into mutable structures
let mut identifier_nodes: HashMap<
let mut identifier_nodes: FxHashMap<
DeclarationId,
(
MemoizationLevel,
bool,
IndexSet<DeclarationId>,
IndexSet<ScopeId>,
IndexSet<DeclarationId, FxBuildHasher>,
IndexSet<ScopeId, FxBuildHasher>,
bool,
),
> = state
@@ -1079,7 +1078,7 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
})
.collect();
let mut scope_nodes: HashMap<ScopeId, (Vec<DeclarationId>, bool)> = state
let mut scope_nodes: FxHashMap<ScopeId, (Vec<DeclarationId>, bool)> = state
.scopes
.iter()
.map(|(id, node)| (*id, (node.dependencies.clone(), node.seen)))
@@ -1088,18 +1087,18 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
fn visit(
id: DeclarationId,
force_memoize: bool,
identifier_nodes: &mut HashMap<
identifier_nodes: &mut FxHashMap<
DeclarationId,
(
MemoizationLevel,
bool,
IndexSet<DeclarationId>,
IndexSet<ScopeId>,
IndexSet<DeclarationId, FxBuildHasher>,
IndexSet<ScopeId, FxBuildHasher>,
bool,
),
>,
scope_nodes: &mut HashMap<ScopeId, (Vec<DeclarationId>, bool)>,
memoized: &mut HashSet<DeclarationId>,
scope_nodes: &mut FxHashMap<ScopeId, (Vec<DeclarationId>, bool)>,
memoized: &mut FxHashSet<DeclarationId>,
) -> bool {
let Some(&(level, _, _, _, seen)) = identifier_nodes.get(&id) else {
return false;
@@ -1149,18 +1148,18 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
fn force_memoize_scope_dependencies(
id: ScopeId,
identifier_nodes: &mut HashMap<
identifier_nodes: &mut FxHashMap<
DeclarationId,
(
MemoizationLevel,
bool,
IndexSet<DeclarationId>,
IndexSet<ScopeId>,
IndexSet<DeclarationId, FxBuildHasher>,
IndexSet<ScopeId, FxBuildHasher>,
bool,
),
>,
scope_nodes: &mut HashMap<ScopeId, (Vec<DeclarationId>, bool)>,
memoized: &mut HashSet<DeclarationId>,
scope_nodes: &mut FxHashMap<ScopeId, (Vec<DeclarationId>, bool)>,
memoized: &mut FxHashSet<DeclarationId>,
) {
let seen = scope_nodes
.get(&id)
@@ -1198,12 +1197,12 @@ fn compute_memoized_identifiers(state: &CollectState) -> HashSet<DeclarationId>
struct PruneScopesTransform<'a> {
env: &'a Environment,
pruned_scopes: HashSet<ScopeId>,
reassignments: HashMap<DeclarationId, HashSet<IdentifierId>>,
pruned_scopes: FxHashSet<ScopeId>,
reassignments: FxHashMap<DeclarationId, FxHashSet<IdentifierId>>,
}
impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
type State = HashSet<DeclarationId>;
type State = FxHashSet<DeclarationId>;
fn env(&self) -> &Environment {
self.env
@@ -1212,7 +1211,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
fn transform_scope(
&mut self,
scope: &mut ReactiveScopeBlock,
state: &mut HashSet<DeclarationId>,
state: &mut FxHashSet<DeclarationId>,
) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
self.visit_scope(scope, state)?;
@@ -1248,7 +1247,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
fn transform_instruction(
&mut self,
instruction: &mut ReactiveInstruction,
state: &mut HashSet<DeclarationId>,
state: &mut FxHashSet<DeclarationId>,
) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
self.traverse_instruction(instruction, state)?;
@@ -1263,7 +1262,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
let ids = self
.reassignments
.entry(decl_id)
.or_insert_with(HashSet::new);
.or_insert_with(FxHashSet::default);
ids.insert(store_value.identifier);
}
ReactiveValue::Instruction(InstructionValue::LoadLocal { place, .. }) => {
@@ -1285,7 +1284,7 @@ impl<'a> ReactiveFunctionTransform for PruneScopesTransform<'a> {
let ids = self
.reassignments
.entry(decl_id)
.or_insert_with(HashSet::new);
.or_insert_with(FxHashSet::default);
ids.insert(place.identifier);
}
}
@@ -8,7 +8,7 @@
//! Corresponds to `src/ReactiveScopes/PruneNonReactiveDependencies.ts`
//! and `src/ReactiveScopes/CollectReactiveIdentifiers.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::{
EvaluationOrder, IdentifierId, InstructionValue, Place, PrunedReactiveScopeBlock,
@@ -28,9 +28,9 @@ use crate::visitors::{self, ReactiveFunctionTransform, ReactiveFunctionVisitor};
pub fn collect_reactive_identifiers(
func: &ReactiveFunction,
env: &Environment,
) -> HashSet<IdentifierId> {
) -> FxHashSet<IdentifierId> {
let visitor = CollectVisitor { env };
let mut state = HashSet::new();
let mut state = FxHashSet::default();
crate::visitors::visit_reactive_function(func, &visitor, &mut state);
state
}
@@ -40,7 +40,7 @@ struct CollectVisitor<'a> {
}
impl<'a> ReactiveFunctionVisitor for CollectVisitor<'a> {
type State = HashSet<IdentifierId>;
type State = FxHashSet<IdentifierId>;
fn env(&self) -> &Environment {
self.env
@@ -74,7 +74,7 @@ impl<'a> ReactiveFunctionVisitor for CollectVisitor<'a> {
/// TS: `isStableRefType`
fn is_stable_ref_type(
ty: &react_compiler_hir::Type,
reactive_identifiers: &HashSet<IdentifierId>,
reactive_identifiers: &FxHashSet<IdentifierId>,
id: IdentifierId,
) -> bool {
is_use_ref_type(ty) && !reactive_identifiers.contains(&id)
@@ -133,7 +133,7 @@ struct PruneVisitor<'a> {
}
impl<'a> ReactiveFunctionTransform for PruneVisitor<'a> {
type State = HashSet<IdentifierId>;
type State = FxHashSet<IdentifierId>;
fn env(&self) -> &Environment {
self.env
@@ -8,7 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/PruneUnusedLabels.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::{
BlockId, ReactiveFunction, ReactiveStatement, ReactiveTerminal, ReactiveTerminalStatement,
@@ -23,7 +23,7 @@ pub fn prune_unused_labels(
env: &Environment,
) -> Result<(), react_compiler_diagnostics::CompilerError> {
let mut transform = Transform { env };
let mut labels: HashSet<BlockId> = HashSet::new();
let mut labels: FxHashSet<BlockId> = FxHashSet::default();
transform_reactive_function(func, &mut transform, &mut labels)
}
@@ -32,7 +32,7 @@ struct Transform<'a> {
}
impl<'a> ReactiveFunctionTransform for Transform<'a> {
type State = HashSet<BlockId>;
type State = FxHashSet<BlockId>;
fn env(&self) -> &Environment {
self.env
@@ -41,7 +41,7 @@ impl<'a> ReactiveFunctionTransform for Transform<'a> {
fn transform_terminal(
&mut self,
stmt: &mut ReactiveTerminalStatement,
state: &mut HashSet<BlockId>,
state: &mut FxHashSet<BlockId>,
) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
// Traverse children first
self.traverse_terminal(stmt, state)?;
@@ -9,7 +9,7 @@
//!
//! Corresponds to `src/ReactiveScopes/PruneTemporaryLValues.ts`.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_hir::{
DeclarationId, EvaluationOrder, Place, ReactiveFunction, ReactiveInstruction,
@@ -34,7 +34,7 @@ pub fn prune_unused_lvalues(func: &mut ReactiveFunction, env: &Environment) {
// When we see an unnamed lvalue on an instruction, we add its DeclarationId.
// When we see a place reference (operand), we remove its DeclarationId.
let visitor = Visitor { env };
let mut lvalues: HashSet<DeclarationId> = HashSet::new();
let mut lvalues: FxHashSet<DeclarationId> = FxHashSet::default();
visitors::visit_reactive_function(func, &visitor, &mut lvalues);
// 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) {
/// TS: `type LValues = Map<DeclarationId, ReactiveInstruction>`
/// In Rust, we only need the set of DeclarationIds (not the instruction refs)
/// because we apply changes in a separate pass.
type LValues = HashSet<DeclarationId>;
type LValues = FxHashSet<DeclarationId>;
/// TS: `class Visitor extends ReactiveFunctionVisitor<LValues>`
struct Visitor<'a> {
@@ -87,7 +87,7 @@ impl ReactiveFunctionVisitor for Visitor<'_> {
fn null_unused_lvalues(
block: &mut Vec<ReactiveStatement>,
env: &Environment,
unused: &HashSet<DeclarationId>,
unused: &FxHashSet<DeclarationId>,
) {
for stmt in block.iter_mut() {
match stmt {
@@ -110,7 +110,7 @@ fn null_unused_lvalues(
fn null_unused_in_instruction(
instr: &mut ReactiveInstruction,
env: &Environment,
unused: &HashSet<DeclarationId>,
unused: &FxHashSet<DeclarationId>,
) {
if let Some(lv) = &instr.lvalue {
let ident = &env.identifiers[lv.identifier.0 as usize];
@@ -124,7 +124,7 @@ fn null_unused_in_instruction(
fn null_unused_in_value(
value: &mut ReactiveValue,
env: &Environment,
unused: &HashSet<DeclarationId>,
unused: &FxHashSet<DeclarationId>,
) {
match value {
ReactiveValue::SequenceExpression {
@@ -161,7 +161,7 @@ fn null_unused_in_value(
fn null_unused_in_terminal(
terminal: &mut react_compiler_hir::ReactiveTerminal,
env: &Environment,
unused: &HashSet<DeclarationId>,
unused: &FxHashSet<DeclarationId>,
) {
use react_compiler_hir::ReactiveTerminal;
match terminal {
@@ -8,8 +8,7 @@
//!
//! Corresponds to `src/ReactiveScopes/RenameVariables.ts`.
use std::collections::HashMap;
use std::collections::HashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_hir::DeclarationId;
use react_compiler_hir::EvaluationOrder;
@@ -33,19 +32,19 @@ use crate::visitors::{self};
// =============================================================================
struct Scopes {
seen: HashMap<DeclarationId, IdentifierName>,
stack: Vec<HashMap<String, DeclarationId>>,
globals: HashSet<String>,
names: HashSet<String>,
seen: FxHashMap<DeclarationId, IdentifierName>,
stack: Vec<FxHashMap<String, DeclarationId>>,
globals: FxHashSet<String>,
names: FxHashSet<String>,
}
impl Scopes {
fn new(globals: HashSet<String>) -> Self {
fn new(globals: FxHashSet<String>) -> Self {
Self {
seen: HashMap::new(),
stack: vec![HashMap::new()],
seen: FxHashMap::default(),
stack: vec![FxHashMap::default()],
globals,
names: HashSet::new(),
names: FxHashSet::default(),
}
}
@@ -114,7 +113,7 @@ impl Scopes {
}
fn enter(&mut self) {
self.stack.push(HashMap::new());
self.stack.push(FxHashMap::default());
}
fn leave(&mut self) {
@@ -202,15 +201,15 @@ impl ReactiveFunctionVisitor for Visitor<'_> {
/// Renames variables for output — assigns unique names, handles SSA renames.
/// Returns a Set of all unique variable names used.
/// TS: `renameVariables`
pub fn rename_variables(func: &mut ReactiveFunction, env: &mut Environment) -> HashSet<String> {
pub fn rename_variables(func: &mut ReactiveFunction, env: &mut Environment) -> FxHashSet<String> {
rename_variables_with_parent(func, env, None)
}
fn rename_variables_with_parent(
func: &mut ReactiveFunction,
env: &mut Environment,
parent_names: Option<&HashSet<String>>,
) -> HashSet<String> {
parent_names: Option<&FxHashSet<String>>,
) -> FxHashSet<String> {
let globals = collect_referenced_globals(&func.body, env);
// Phase 1: Use ReactiveFunctionVisitor to compute the rename mapping.
@@ -242,7 +241,7 @@ fn rename_variables_with_parent(
}
}
let mut result: HashSet<String> = scopes.names;
let mut result: FxHashSet<String> = scopes.names;
result.extend(globals);
result
}
@@ -267,13 +266,17 @@ fn rename_variables_impl(func: &ReactiveFunction, visitor: &Visitor, scopes: &mu
/// Collects all globally referenced names from the reactive function.
/// TS: `collectReferencedGlobals`
fn collect_referenced_globals(block: &ReactiveBlock, env: &Environment) -> HashSet<String> {
let mut globals = HashSet::new();
fn collect_referenced_globals(block: &ReactiveBlock, env: &Environment) -> FxHashSet<String> {
let mut globals = FxHashSet::default();
collect_globals_block(block, &mut globals, env);
globals
}
fn collect_globals_block(block: &ReactiveBlock, globals: &mut HashSet<String>, env: &Environment) {
fn collect_globals_block(
block: &ReactiveBlock,
globals: &mut FxHashSet<String>,
env: &Environment,
) {
for stmt in block {
match stmt {
react_compiler_hir::ReactiveStatement::Instruction(instr) => {
@@ -292,7 +295,11 @@ fn collect_globals_block(block: &ReactiveBlock, globals: &mut HashSet<String>, e
}
}
fn collect_globals_value(value: &ReactiveValue, globals: &mut HashSet<String>, env: &Environment) {
fn collect_globals_value(
value: &ReactiveValue,
globals: &mut FxHashSet<String>,
env: &Environment,
) {
match value {
ReactiveValue::Instruction(iv) => {
if let InstructionValue::LoadGlobal { binding, .. } = iv {
@@ -340,7 +347,7 @@ fn collect_globals_value(value: &ReactiveValue, globals: &mut HashSet<String>, e
/// Recursively collects LoadGlobal names from an inner HIR function.
fn collect_globals_hir_function(
func_id: FunctionId,
globals: &mut HashSet<String>,
globals: &mut FxHashSet<String>,
env: &Environment,
) {
let inner_func = &env.functions[func_id.0 as usize];
@@ -367,7 +374,7 @@ fn collect_globals_hir_function(
fn collect_globals_terminal(
stmt: &react_compiler_hir::ReactiveTerminalStatement,
globals: &mut HashSet<String>,
globals: &mut FxHashSet<String>,
env: &Environment,
) {
match &stmt.terminal {
@@ -10,7 +10,7 @@
//!
//! Corresponds to `src/ReactiveScopes/StabilizeBlockIds.ts`.
use std::collections::HashMap;
use rustc_hash::{FxBuildHasher, FxHashMap};
use indexmap::IndexSet;
use react_compiler_hir::{
@@ -27,12 +27,12 @@ use crate::visitors::{
/// TS: `stabilizeBlockIds`
pub fn stabilize_block_ids(func: &mut ReactiveFunction, env: &mut Environment) {
// Pass 1: Collect referenced labels (preserving insertion order to match TS Set behavior)
let mut referenced: IndexSet<BlockId> = IndexSet::new();
let mut referenced: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
let collector = CollectReferencedLabels { env: &*env };
visit_reactive_function(func, &collector, &mut referenced);
// Build mappings: referenced block IDs -> sequential IDs (insertion-order deterministic)
let mut mappings: HashMap<BlockId, BlockId> = HashMap::new();
let mut mappings: FxHashMap<BlockId, BlockId> = FxHashMap::default();
for block_id in &referenced {
let len = mappings.len() as u32;
mappings.entry(*block_id).or_insert(BlockId(len));
@@ -52,7 +52,7 @@ struct CollectReferencedLabels<'a> {
}
impl<'a> ReactiveFunctionVisitor for CollectReferencedLabels<'a> {
type State = IndexSet<BlockId>;
type State = IndexSet<BlockId, FxBuildHasher>;
fn env(&self) -> &Environment {
self.env
@@ -80,7 +80,7 @@ impl<'a> ReactiveFunctionVisitor for CollectReferencedLabels<'a> {
// Pass 2: RewriteBlockIds
// =============================================================================
fn get_or_insert_mapping(mappings: &mut HashMap<BlockId, BlockId>, id: BlockId) -> BlockId {
fn get_or_insert_mapping(mappings: &mut FxHashMap<BlockId, BlockId>, id: BlockId) -> BlockId {
let len = mappings.len() as u32;
*mappings.entry(id).or_insert(BlockId(len))
}
@@ -91,7 +91,7 @@ struct RewriteBlockIds<'a> {
}
impl<'a> ReactiveFunctionTransform for RewriteBlockIds<'a> {
type State = HashMap<BlockId, BlockId>;
type State = FxHashMap<BlockId, BlockId>;
fn env(&self) -> &Environment {
self.env
@@ -7,3 +7,4 @@ edition = "2024"
react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
react_compiler_hir = { path = "../react_compiler_hir" }
indexmap = "2"
rustc-hash = "2"
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_hir::environment::Environment;
use react_compiler_hir::visitors;
@@ -10,7 +10,7 @@ use crate::enter_ssa::placeholder_function;
// Helper: rewrite_place
// =============================================================================
fn rewrite_place(place: &mut Place, rewrites: &HashMap<IdentifierId, IdentifierId>) {
fn rewrite_place(place: &mut Place, rewrites: &FxHashMap<IdentifierId, IdentifierId>) {
if let Some(&rewrite) = rewrites.get(&place.identifier) {
place.identifier = rewrite;
}
@@ -21,7 +21,7 @@ fn rewrite_place(place: &mut Place, rewrites: &HashMap<IdentifierId, IdentifierI
// =============================================================================
pub fn eliminate_redundant_phi(func: &mut HirFunction, env: &mut Environment) {
let mut rewrites: HashMap<IdentifierId, IdentifierId> = HashMap::new();
let mut rewrites: FxHashMap<IdentifierId, IdentifierId> = FxHashMap::default();
eliminate_redundant_phi_impl(func, env, &mut rewrites);
}
@@ -32,12 +32,12 @@ pub fn eliminate_redundant_phi(func: &mut HirFunction, env: &mut Environment) {
fn eliminate_redundant_phi_impl(
func: &mut HirFunction,
env: &mut Environment,
rewrites: &mut HashMap<IdentifierId, IdentifierId>,
rewrites: &mut FxHashMap<IdentifierId, IdentifierId>,
) {
let ir = &mut func.body;
let mut has_back_edge = false;
let mut visited: HashSet<BlockId> = HashSet::new();
let mut visited: FxHashSet<BlockId> = FxHashSet::default();
let mut size;
loop {
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use indexmap::IndexMap;
use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory};
@@ -16,35 +16,35 @@ struct IncompletePhi {
}
struct State {
defs: HashMap<IdentifierId, IdentifierId>,
defs: FxHashMap<IdentifierId, IdentifierId>,
incomplete_phis: Vec<IncompletePhi>,
}
struct SSABuilder {
states: HashMap<BlockId, State>,
states: FxHashMap<BlockId, State>,
current: Option<BlockId>,
unsealed_preds: HashMap<BlockId, u32>,
block_preds: HashMap<BlockId, Vec<BlockId>>,
unknown: HashSet<IdentifierId>,
context: HashSet<IdentifierId>,
pending_phis: HashMap<BlockId, Vec<Phi>>,
unsealed_preds: FxHashMap<BlockId, u32>,
block_preds: FxHashMap<BlockId, Vec<BlockId>>,
unknown: FxHashSet<IdentifierId>,
context: FxHashSet<IdentifierId>,
pending_phis: FxHashMap<BlockId, Vec<Phi>>,
processed_functions: Vec<FunctionId>,
}
impl SSABuilder {
fn new(blocks: &IndexMap<BlockId, BasicBlock>) -> Self {
let mut block_preds = HashMap::new();
fn new(blocks: &IndexMap<BlockId, BasicBlock, FxBuildHasher>) -> Self {
let mut block_preds = FxHashMap::default();
for (id, block) in blocks {
block_preds.insert(*id, block.preds.iter().copied().collect());
}
SSABuilder {
states: HashMap::new(),
states: FxHashMap::default(),
current: None,
unsealed_preds: HashMap::new(),
unsealed_preds: FxHashMap::default(),
block_preds,
unknown: HashSet::new(),
context: HashSet::new(),
pending_phis: HashMap::new(),
unknown: FxHashSet::default(),
context: FxHashSet::default(),
pending_phis: FxHashMap::default(),
processed_functions: Vec::new(),
}
}
@@ -226,7 +226,7 @@ impl SSABuilder {
) {
let preds = self.block_preds.get(&block_id).cloned().unwrap_or_default();
let mut pred_defs: IndexMap<BlockId, Place> = IndexMap::new();
let mut pred_defs: IndexMap<BlockId, Place, FxBuildHasher> = IndexMap::default();
for pred_block_id in &preds {
let pred_id = self.get_id_at(old_place, *pred_block_id, env);
pred_defs.insert(
@@ -266,7 +266,7 @@ impl SSABuilder {
self.states.insert(
block_id,
State {
defs: HashMap::new(),
defs: FxHashMap::default(),
incomplete_phis: Vec::new(),
},
);
@@ -310,7 +310,7 @@ fn enter_ssa_impl(
env: &mut Environment,
root_entry: BlockId,
) -> Result<(), CompilerDiagnostic> {
let mut visited_blocks: HashSet<BlockId> = HashSet::new();
let mut visited_blocks: FxHashSet<BlockId> = FxHashSet::default();
let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();
for block_id in &block_ids {
@@ -520,7 +520,7 @@ pub fn placeholder_function() -> HirFunction {
context: Vec::new(),
body: HIR {
entry: BlockId(0),
blocks: IndexMap::new(),
blocks: IndexMap::default(),
},
instructions: Vec::new(),
generator: false,
@@ -15,7 +15,7 @@
//! may be converted to a `const` if the reassignment is not used and was removed
//! by dead code elimination.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation,
@@ -115,7 +115,7 @@ pub fn rewrite_instruction_kinds_based_on_reassignment(
//
// Track: for each DeclarationId, the location of its first declaration,
// and whether it needs to be changed to Let (because of reassignment).
let mut declarations: HashMap<DeclarationId, DeclarationLoc> = HashMap::new();
let mut declarations: FxHashMap<DeclarationId, DeclarationLoc> = FxHashMap::default();
// Track which (block_index, instr_local_index) should have their lvalue.kind set to Reassign
let mut reassign_locs: Vec<(usize, usize)> = Vec::new();
// Track which declaration locations need to be set to Let
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
rustc-hash = "2"
react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
react_compiler_hir = { path = "../react_compiler_hir" }
react_compiler_ssa = { path = "../react_compiler_ssa" }
@@ -8,7 +8,7 @@
//! Generates type equations from the HIR, unifies them, and applies the
//! resolved types back to identifiers. Analogous to TS `InferTypes.ts`.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};
use react_compiler_hir::environment::{Environment, is_hook_name};
@@ -79,7 +79,7 @@ fn pre_resolve_globals(
func: &HirFunction,
function_key: u32,
env: &mut Environment,
global_types: &mut HashMap<(u32, InstructionId), Type>,
global_types: &mut FxHashMap<(u32, InstructionId), Type>,
) {
for &instr_id in func.body.blocks.values().flat_map(|b| &b.instructions) {
let instr = &func.instructions[instr_id.0 as usize];
@@ -95,7 +95,7 @@ fn pre_resolve_globals(
fn pre_resolve_globals_recursive(
func_id: FunctionId,
env: &mut Environment,
global_types: &mut HashMap<(u32, InstructionId), Type>,
global_types: &mut FxHashMap<(u32, InstructionId), Type>,
) {
// Collect LoadGlobal bindings and child function IDs in one pass to avoid
// borrow conflicts (we need &env.functions to read, then &mut env for
@@ -277,13 +277,13 @@ fn type_equals(a: &Type, b: &Type) -> bool {
}
}
fn set_name(names: &mut HashMap<IdentifierId, String>, id: IdentifierId, source: &Identifier) {
fn set_name(names: &mut FxHashMap<IdentifierId, String>, id: IdentifierId, source: &Identifier) {
if let Some(IdentifierName::Named(ref name)) = source.name {
names.insert(id, name.clone());
}
}
fn get_name(names: &HashMap<IdentifierId, String>, id: IdentifierId) -> String {
fn get_name(names: &FxHashMap<IdentifierId, String>, id: IdentifierId) -> String {
names.get(&id).cloned().unwrap_or_default()
}
@@ -335,7 +335,7 @@ fn generate(
// &mut env, but generate_instruction_types takes split borrows on env fields.
// The key is (function_key, InstructionId) where function_key is u32::MAX
// for the outer function and FunctionId.0 for inner functions.
let mut global_types: HashMap<(u32, InstructionId), Type> = HashMap::new();
let mut global_types: FxHashMap<(u32, InstructionId), Type> = FxHashMap::default();
pre_resolve_globals(func, u32::MAX, env, &mut global_types);
// Also pre-resolve inner functions recursively
for &instr_id in func.body.blocks.values().flat_map(|b| &b.instructions) {
@@ -355,7 +355,7 @@ fn generate(
}
}
let mut names: HashMap<IdentifierId, String> = HashMap::new();
let mut names: FxHashMap<IdentifierId, String> = FxHashMap::default();
let mut return_types: Vec<Type> = Vec::new();
for (_block_id, block) in &func.body.blocks {
@@ -420,7 +420,7 @@ fn generate_for_function_id(
identifiers: &[Identifier],
types: &mut Vec<Type>,
functions: &mut Vec<HirFunction>,
global_types: &HashMap<(u32, InstructionId), Type>,
global_types: &FxHashMap<(u32, InstructionId), Type>,
shapes: &ShapeRegistry,
unifier: &mut Unifier,
) -> Result<(), CompilerDiagnostic> {
@@ -457,7 +457,7 @@ fn generate_for_function_id(
// TS creates a fresh `names` Map per recursive `generate` call, so inner
// functions don't inherit or pollute the outer function's name mappings.
let mut inner_names: HashMap<IdentifierId, String> = HashMap::new();
let mut inner_names: FxHashMap<IdentifierId, String> = FxHashMap::default();
let mut inner_return_types: Vec<Type> = Vec::new();
for (_block_id, block) in &inner.body.blocks {
@@ -521,8 +521,8 @@ fn generate_instruction_types(
identifiers: &[Identifier],
types: &mut Vec<Type>,
functions: &mut Vec<HirFunction>,
names: &mut HashMap<IdentifierId, String>,
global_types: &HashMap<(u32, InstructionId), Type>,
names: &mut FxHashMap<IdentifierId, String>,
global_types: &FxHashMap<(u32, InstructionId), Type>,
shapes: &ShapeRegistry,
unifier: &mut Unifier,
) -> Result<(), CompilerDiagnostic> {
@@ -1304,7 +1304,7 @@ fn apply_instruction_operands(
// =============================================================================
struct Unifier {
substitutions: HashMap<TypeId, Type>,
substitutions: FxHashMap<TypeId, Type>,
enable_treat_ref_like_identifiers_as_refs: bool,
enable_treat_set_identifiers_as_state_setters: bool,
custom_hook_type: Option<Type>,
@@ -1317,7 +1317,7 @@ impl Unifier {
enable_treat_set_identifiers_as_state_setters: bool,
) -> Self {
Unifier {
substitutions: HashMap::new(),
substitutions: FxHashMap::default(),
enable_treat_ref_like_identifiers_as_refs,
enable_treat_set_identifiers_as_state_setters,
custom_hook_type,
@@ -5,3 +5,4 @@ edition = "2024"
[dependencies]
indexmap = "2"
rustc-hash = "2"
@@ -7,7 +7,7 @@
//!
//! Ported from TypeScript `src/Utils/DisjointSet.ts`.
use std::collections::HashSet;
use rustc_hash::{FxBuildHasher, FxHashSet};
use std::hash::Hash;
use indexmap::IndexMap;
@@ -17,13 +17,13 @@ use indexmap::IndexMap;
/// Corresponds to TS `DisjointSet<T>` in `src/Utils/DisjointSet.ts`.
/// Uses `IndexMap` to preserve insertion order (matching TS `Map` behavior).
pub struct DisjointSet<K: Copy + Eq + Hash> {
entries: IndexMap<K, K>,
entries: IndexMap<K, K, FxBuildHasher>,
}
impl<K: Copy + Eq + Hash> DisjointSet<K> {
pub fn new() -> Self {
DisjointSet {
entries: IndexMap::new(),
entries: IndexMap::default(),
}
}
@@ -87,8 +87,8 @@ impl<K: Copy + Eq + Hash> DisjointSet<K> {
/// root) and returns a map of items to their roots.
///
/// Corresponds to TS `canonicalize(): Map<T, T>`.
pub fn canonicalize(&mut self) -> IndexMap<K, K> {
let mut result = IndexMap::new();
pub fn canonicalize(&mut self) -> IndexMap<K, K, FxBuildHasher> {
let mut result = IndexMap::default();
let keys: Vec<K> = self.entries.keys().copied().collect();
for item in keys {
let root = self.find(item);
@@ -115,9 +115,9 @@ impl<K: Copy + Eq + Hash> DisjointSet<K> {
/// Groups all items by their root and returns the groups as a list of sets.
///
/// Corresponds to TS `buildSets(): Array<Set<T>>`.
pub fn build_sets(&mut self) -> Vec<HashSet<K>> {
let mut group_to_index: IndexMap<K, usize> = IndexMap::new();
let mut sets: Vec<HashSet<K>> = Vec::new();
pub fn build_sets(&mut self) -> Vec<FxHashSet<K>> {
let mut group_to_index: IndexMap<K, usize, FxBuildHasher> = IndexMap::default();
let mut sets: Vec<FxHashSet<K>> = Vec::new();
let keys: Vec<K> = self.entries.keys().copied().collect();
for item in keys {
let group = self.find(item);
@@ -126,7 +126,7 @@ impl<K: Copy + Eq + Hash> DisjointSet<K> {
None => {
let idx = sets.len();
group_to_index.insert(group, idx);
sets.push(HashSet::new());
sets.push(FxHashSet::default());
idx
}
};
@@ -5,5 +5,6 @@ edition = "2024"
[dependencies]
indexmap = "2"
rustc-hash = "2"
react_compiler_diagnostics = { path = "../react_compiler_diagnostics" }
react_compiler_hir = { path = "../react_compiler_hir" }
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory,
@@ -27,7 +27,7 @@ impl std::fmt::Display for VarRefKind {
}
}
type IdentifierKinds = HashMap<IdentifierId, (Place, VarRefKind)>;
type IdentifierKinds = FxHashMap<IdentifierId, (Place, VarRefKind)>;
/// Validates that context variable lvalues are used consistently.
///
@@ -53,7 +53,7 @@ pub fn validate_context_variable_lvalues_with_errors(
identifiers: &[Identifier],
errors: &mut CompilerError,
) -> Result<(), CompilerDiagnostic> {
let mut identifier_kinds: IdentifierKinds = HashMap::new();
let mut identifier_kinds: IdentifierKinds = FxHashMap::default();
validate_context_variable_lvalues_impl(
func,
&mut identifier_kinds,
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, CompilerSuggestion, CompilerSuggestionOperation,
@@ -33,7 +33,7 @@ pub fn validate_exhaustive_dependencies(
let validate_memo = env.config.validate_exhaustive_memoization_dependencies;
let validate_effect = env.config.validate_exhaustive_effect_dependencies.clone();
let mut temporaries: HashMap<IdentifierId, Temporary> = HashMap::new();
let mut temporaries: FxHashMap<IdentifierId, Temporary> = FxHashMap::default();
for param in &func.params {
let place = match param {
ParamPattern::Place(p) => p,
@@ -51,7 +51,7 @@ pub fn validate_exhaustive_dependencies(
}
let mut start_memo: Option<StartMemoInfo> = None;
let mut memo_locals: HashSet<IdentifierId> = HashSet::new();
let mut memo_locals: FxHashSet<IdentifierId> = FxHashSet::default();
// Callbacks struct holding the mutable state
let mut callbacks = Callbacks {
@@ -61,7 +61,7 @@ pub fn validate_exhaustive_dependencies(
validate_effect: validate_effect.clone(),
reactive: &reactive,
diagnostics: Vec::new(),
invalid_memo_ids: HashSet::new(),
invalid_memo_ids: FxHashSet::default(),
};
collect_dependencies(
@@ -180,13 +180,13 @@ fn path_to_string(path: &[DependencyPathEntry]) -> String {
struct Callbacks<'a> {
start_memo: &'a mut Option<StartMemoInfo>,
#[allow(dead_code)]
memo_locals: &'a mut HashSet<IdentifierId>,
memo_locals: &'a mut FxHashSet<IdentifierId>,
validate_memo: bool,
validate_effect: ExhaustiveEffectDepsMode,
reactive: &'a HashSet<IdentifierId>,
reactive: &'a FxHashSet<IdentifierId>,
diagnostics: Vec<CompilerDiagnostic>,
/// manual_memo_ids that had validation errors (to set has_invalid_deps)
invalid_memo_ids: HashSet<u32>,
invalid_memo_ids: FxHashSet<u32>,
}
// =============================================================================
@@ -283,8 +283,8 @@ fn is_sub_path_ignoring_optionals(
fn collect_reactive_identifiers(
func: &HirFunction,
functions: &[HirFunction],
) -> HashSet<IdentifierId> {
let mut reactive = HashSet::new();
) -> FxHashSet<IdentifierId> {
let mut reactive = FxHashSet::default();
for (_block_id, block) in &func.body.blocks {
for &instr_id in &block.instructions {
let instr = &func.instructions[instr_id.0 as usize];
@@ -319,9 +319,9 @@ fn collect_reactive_identifiers(
// findOptionalPlaces
// =============================================================================
fn find_optional_places(func: &HirFunction) -> HashMap<IdentifierId, bool> {
let mut optionals: HashMap<IdentifierId, bool> = HashMap::new();
let mut visited: HashSet<BlockId> = HashSet::new();
fn find_optional_places(func: &HirFunction) -> FxHashMap<IdentifierId, bool> {
let mut optionals: FxHashMap<IdentifierId, bool> = FxHashMap::default();
let mut visited: FxHashSet<BlockId> = FxHashSet::default();
for (_block_id, block) in &func.body.blocks {
if visited.contains(&block.id) {
@@ -418,8 +418,8 @@ fn find_optional_places(func: &HirFunction) -> HashMap<IdentifierId, bool> {
fn add_dependency(
dep: &Temporary,
dependencies: &mut Vec<InferredDependency>,
dep_keys: &mut HashSet<InferredDependencyKey>,
locals: &HashSet<IdentifierId>,
dep_keys: &mut FxHashSet<InferredDependencyKey>,
locals: &FxHashSet<IdentifierId>,
) {
match dep {
Temporary::Aggregate {
@@ -464,8 +464,8 @@ fn add_dependency(
fn add_dependency_inferred(
dep: &InferredDependency,
dependencies: &mut Vec<InferredDependency>,
dep_keys: &mut HashSet<InferredDependencyKey>,
locals: &HashSet<IdentifierId>,
dep_keys: &mut FxHashSet<InferredDependencyKey>,
locals: &FxHashSet<IdentifierId>,
) {
match dep {
InferredDependency::Global { .. } => {
@@ -487,10 +487,10 @@ fn add_dependency_inferred(
fn visit_candidate_dependency(
place: &Place,
temporaries: &HashMap<IdentifierId, Temporary>,
temporaries: &FxHashMap<IdentifierId, Temporary>,
dependencies: &mut Vec<InferredDependency>,
dep_keys: &mut HashSet<InferredDependencyKey>,
locals: &HashSet<IdentifierId>,
dep_keys: &mut FxHashSet<InferredDependencyKey>,
locals: &FxHashSet<IdentifierId>,
) {
if let Some(dep) = temporaries.get(&place.identifier) {
add_dependency(dep, dependencies, dep_keys, locals);
@@ -502,12 +502,12 @@ fn collect_dependencies(
identifiers: &[Identifier],
types: &[Type],
functions: &[HirFunction],
temporaries: &mut HashMap<IdentifierId, Temporary>,
temporaries: &mut FxHashMap<IdentifierId, Temporary>,
callbacks: &mut Option<&mut Callbacks<'_>>,
is_function_expression: bool,
) -> Result<Temporary, CompilerDiagnostic> {
let optionals = find_optional_places(func);
let mut locals: HashSet<IdentifierId> = HashSet::new();
let mut locals: FxHashSet<IdentifierId> = FxHashSet::default();
if is_function_expression {
for param in &func.params {
@@ -520,15 +520,15 @@ fn collect_dependencies(
}
let mut dependencies: Vec<InferredDependency> = Vec::new();
let mut dep_keys: HashSet<InferredDependencyKey> = HashSet::new();
let mut dep_keys: FxHashSet<InferredDependencyKey> = FxHashSet::default();
// Saved state for when we're inside a memo block (StartMemoize..FinishMemoize).
// In TS, `dependencies` and `locals` are shared by reference between the main
// collection loop and the callbacks — StartMemoize clears them, FinishMemoize
// reads and clears them. We simulate this by saving/restoring.
let mut saved_dependencies: Option<Vec<InferredDependency>> = None;
let mut saved_dep_keys: Option<HashSet<InferredDependencyKey>> = None;
let mut saved_locals: Option<HashSet<IdentifierId>> = None;
let mut saved_dep_keys: Option<FxHashSet<InferredDependencyKey>> = None;
let mut saved_locals: Option<FxHashSet<IdentifierId>> = None;
for (_block_id, block) in &func.body.blocks {
// Process phis
@@ -906,8 +906,8 @@ fn collect_dependencies(
}
InstructionValue::ArrayExpression { elements, loc, .. } => {
let mut array_deps: Vec<InferredDependency> = Vec::new();
let mut array_keys: HashSet<InferredDependencyKey> = HashSet::new();
let empty_locals = HashSet::new();
let mut array_keys: FxHashSet<InferredDependencyKey> = FxHashSet::default();
let empty_locals = FxHashSet::default();
for elem in elements {
let place = match elem {
ArrayElement::Place(p) => Some(p),
@@ -1224,7 +1224,7 @@ fn collect_dependencies(
fn validate_dependencies(
mut inferred: Vec<InferredDependency>,
manual_dependencies: &[ManualMemoDependency],
reactive: &HashSet<IdentifierId>,
reactive: &FxHashSet<IdentifierId>,
manual_memo_loc: Option<SourceLocation>,
category: ErrorCategory,
exhaustive_deps_report_mode: &str,
@@ -1360,7 +1360,7 @@ fn validate_dependencies(
}
// Validate manual deps
let mut matched: HashSet<usize> = HashSet::new(); // indices into manual_dependencies
let mut matched: FxHashSet<usize> = FxHashSet::default(); // indices into manual_dependencies
let mut missing: Vec<&InferredDependency> = Vec::new();
let mut extra: Vec<&ManualMemoDependency> = Vec::new();
@@ -1646,7 +1646,7 @@ fn print_manual_memo_dependency(dep: &ManualMemoDependency, identifiers: &[Ident
fn is_optional_dependency(
identifier: IdentifierId,
reactive: &HashSet<IdentifierId>,
reactive: &FxHashSet<IdentifierId>,
identifiers: &[Identifier],
types: &[Type],
) -> bool {
@@ -1659,7 +1659,7 @@ fn is_optional_dependency(
fn is_optional_dependency_inferred(
dep: &InferredDependency,
reactive: &HashSet<IdentifierId>,
reactive: &FxHashSet<IdentifierId>,
identifiers: &[Identifier],
types: &[Type],
) -> bool {
@@ -10,7 +10,7 @@
//! and not called dynamically. Also validates that hooks are not
//! called inside function expressions.
use std::collections::HashMap;
use rustc_hash::{FxBuildHasher, FxHashMap};
use indexmap::IndexMap;
use react_compiler_diagnostics::{
@@ -51,7 +51,7 @@ fn join_kinds(a: Kind, b: Kind) -> Kind {
fn get_kind_for_place(
place: &Place,
value_kinds: &HashMap<IdentifierId, Kind>,
value_kinds: &FxHashMap<IdentifierId, Kind>,
identifiers: &[Identifier],
) -> Kind {
let known_kind = value_kinds.get(&place.identifier).copied();
@@ -86,8 +86,8 @@ fn get_hook_kind_for_id<'a>(
fn visit_place(
place: &Place,
value_kinds: &HashMap<IdentifierId, Kind>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
value_kinds: &FxHashMap<IdentifierId, Kind>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
env: &mut Environment,
) -> Result<(), CompilerError> {
let kind = value_kinds.get(&place.identifier).copied();
@@ -99,8 +99,8 @@ fn visit_place(
fn record_conditional_hook_error(
place: &Place,
value_kinds: &mut HashMap<IdentifierId, Kind>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
value_kinds: &mut FxHashMap<IdentifierId, Kind>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
env: &mut Environment,
) -> Result<(), CompilerError> {
value_kinds.insert(place.identifier, Kind::Error);
@@ -133,7 +133,7 @@ fn record_conditional_hook_error(
fn record_invalid_hook_usage_error(
place: &Place,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
env: &mut Environment,
) -> Result<(), CompilerError> {
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(
fn record_dynamic_hook_usage_error(
place: &Place,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
env: &mut Environment,
) -> Result<(), CompilerError> {
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(
env: &mut Environment,
) -> Result<(), react_compiler_diagnostics::CompilerDiagnostic> {
let unconditional_blocks = compute_unconditional_blocks(func, env.next_block_id().0)?;
let mut errors_by_loc: IndexMap<SourceLocation, CompilerErrorDetail> = IndexMap::new();
let mut value_kinds: HashMap<IdentifierId, Kind> = HashMap::new();
let mut errors_by_loc: IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher> =
IndexMap::default();
let mut value_kinds: FxHashMap<IdentifierId, Kind> = FxHashMap::default();
// Process params
for param in &func.params {
@@ -512,8 +513,8 @@ fn hook_kind_display(kind: &HookKind) -> &'static str {
/// Uses the canonical `each_instruction_value_operand` from visitors.
fn visit_all_operands(
value: &InstructionValue,
value_kinds: &HashMap<IdentifierId, Kind>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail>,
value_kinds: &FxHashMap<IdentifierId, Kind>,
errors_by_loc: &mut IndexMap<SourceLocation, CompilerErrorDetail, FxBuildHasher>,
env: &mut Environment,
) -> Result<(), CompilerError> {
let operands = visitors::each_instruction_value_operand(value, &*env);
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory};
use react_compiler_hir::environment::Environment;
@@ -20,7 +20,7 @@ use react_compiler_hir::{
/// This prevents a category of bugs in which a closure captures a
/// binding from one render but does not update.
pub fn validate_locals_not_reassigned_after_render(func: &HirFunction, env: &mut Environment) {
let mut context_variables: HashSet<IdentifierId> = HashSet::new();
let mut context_variables: FxHashSet<IdentifierId> = FxHashSet::default();
let mut diagnostics: Vec<CompilerDiagnostic> = Vec::new();
let reassignment = get_context_reassignment(
@@ -85,13 +85,13 @@ fn get_context_reassignment(
types: &[Type],
functions: &[HirFunction],
env: &Environment,
context_variables: &mut HashSet<IdentifierId>,
context_variables: &mut FxHashSet<IdentifierId>,
is_function_expression: bool,
is_async: bool,
diagnostics: &mut Vec<CompilerDiagnostic>,
) -> Option<Place> {
// Maps identifiers to the place that they reassign
let mut reassigning_functions: HashMap<IdentifierId, Place> = HashMap::new();
let mut reassigning_functions: FxHashMap<IdentifierId, Place> = FxHashMap::default();
for (_block_id, block) in &func.body.blocks {
for &instruction_id in &block.instructions {
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{CompilerError, CompilerErrorDetail, ErrorCategory};
use react_compiler_hir::environment::Environment;
@@ -12,15 +12,15 @@ pub fn validate_no_capitalized_calls(
env: &mut Environment,
) -> Result<(), CompilerError> {
// Build the allow list from global registry keys + config entries
let mut allow_list: HashSet<String> = env.globals().keys().cloned().collect();
let mut allow_list: FxHashSet<String> = env.globals().keys().cloned().collect();
if let Some(config_entries) = &env.config.validate_no_capitalized_calls {
for entry in config_entries {
allow_list.insert(entry.clone());
}
}
let mut capital_load_globals: HashMap<IdentifierId, String> = HashMap::new();
let mut capitalized_properties: HashMap<IdentifierId, String> = HashMap::new();
let mut capital_load_globals: FxHashMap<IdentifierId, String> = FxHashMap::default();
let mut capitalized_properties: FxHashMap<IdentifierId, String> = FxHashMap::default();
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";
@@ -10,7 +10,8 @@
//!
//! Port of ValidateNoDerivedComputationsInEffects_exp.ts.
use std::collections::{HashMap, HashSet};
use indexmap::{IndexMap, IndexSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, CompilerErrorDetail, ErrorCategory,
@@ -111,7 +112,7 @@ struct DerivationMetadata {
type_of_value: TypeOfValue,
place_identifier: IdentifierId,
place_name: Option<IdentifierName>,
source_ids: indexmap::IndexSet<IdentifierId>,
source_ids: IndexSet<IdentifierId, FxBuildHasher>,
is_state_source: bool,
}
@@ -129,16 +130,16 @@ struct DepElement {
struct ValidationContext {
/// Map from lvalue identifier to the FunctionId of function expressions
functions: HashMap<IdentifierId, FunctionId>,
functions: FxHashMap<IdentifierId, FunctionId>,
/// Map from lvalue identifier to ArrayExpression elements (candidate deps)
candidate_dependencies: HashMap<IdentifierId, Vec<DepElement>>,
candidate_dependencies: FxHashMap<IdentifierId, Vec<DepElement>>,
derivation_cache: DerivationCache,
effects_cache: HashMap<IdentifierId, EffectMetadata>,
set_state_loads: HashMap<IdentifierId, Option<IdentifierId>>,
set_state_usages: HashMap<IdentifierId, HashSet<LocKey>>,
effects_cache: FxHashMap<IdentifierId, EffectMetadata>,
set_state_loads: FxHashMap<IdentifierId, Option<IdentifierId>>,
set_state_usages: FxHashMap<IdentifierId, FxHashSet<LocKey>>,
}
/// A hashable key for SourceLocation to use in HashSet
/// A hashable key for SourceLocation to use in FxHashSet
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct LocKey {
start_line: u32,
@@ -169,21 +170,21 @@ impl LocKey {
#[derive(Debug, Clone)]
struct DerivationCache {
has_changes: bool,
cache: HashMap<IdentifierId, DerivationMetadata>,
previous_cache: Option<HashMap<IdentifierId, DerivationMetadata>>,
cache: FxHashMap<IdentifierId, DerivationMetadata>,
previous_cache: Option<FxHashMap<IdentifierId, DerivationMetadata>>,
}
impl DerivationCache {
fn new() -> Self {
DerivationCache {
has_changes: false,
cache: HashMap::new(),
cache: FxHashMap::default(),
previous_cache: None,
}
}
fn take_snapshot(&mut self) {
let mut prev = HashMap::new();
let mut prev = FxHashMap::default();
for (key, value) in &self.cache {
prev.insert(
*key,
@@ -241,7 +242,7 @@ impl DerivationCache {
&mut self,
derived_id: IdentifierId,
derived_name: Option<IdentifierName>,
source_ids: indexmap::IndexSet<IdentifierId>,
source_ids: IndexSet<IdentifierId, FxBuildHasher>,
type_of_value: TypeOfValue,
is_state_source: bool,
) {
@@ -302,8 +303,8 @@ fn join_value(lvalue_type: TypeOfValue, value_type: TypeOfValue) -> TypeOfValue
fn get_root_set_state(
key: IdentifierId,
loads: &HashMap<IdentifierId, Option<IdentifierId>>,
visited: &mut HashSet<IdentifierId>,
loads: &FxHashMap<IdentifierId, Option<IdentifierId>>,
visited: &mut FxHashSet<IdentifierId>,
) -> Option<IdentifierId> {
if visited.contains(&key) {
return None;
@@ -320,8 +321,8 @@ fn get_root_set_state(
fn maybe_record_set_state_for_instr(
instr: &react_compiler_hir::Instruction,
env: &Environment,
set_state_loads: &mut HashMap<IdentifierId, Option<IdentifierId>>,
set_state_usages: &mut HashMap<IdentifierId, HashSet<LocKey>>,
set_state_loads: &mut FxHashMap<IdentifierId, Option<IdentifierId>>,
set_state_usages: &mut FxHashMap<IdentifierId, FxHashSet<LocKey>>,
) {
let identifiers = &env.identifiers;
let types = &env.types;
@@ -349,10 +350,10 @@ fn maybe_record_set_state_for_instr(
}
}
let root = get_root_set_state(lvalue_id, set_state_loads, &mut HashSet::new());
let root = get_root_set_state(lvalue_id, set_state_loads, &mut FxHashSet::default());
if let Some(root_id) = root {
set_state_usages.entry(root_id).or_insert_with(|| {
let mut set = HashSet::new();
let mut set = FxHashSet::default();
set.insert(LocKey::from_loc(&instr.lvalue.loc));
set
});
@@ -377,12 +378,12 @@ pub fn validate_no_derived_computations_in_effects_exp(
let identifiers = &env.identifiers;
let mut context = ValidationContext {
functions: HashMap::new(),
candidate_dependencies: HashMap::new(),
functions: FxHashMap::default(),
candidate_dependencies: FxHashMap::default(),
derivation_cache: DerivationCache::new(),
effects_cache: HashMap::new(),
set_state_loads: HashMap::new(),
set_state_usages: HashMap::new(),
effects_cache: FxHashMap::default(),
set_state_loads: FxHashMap::default(),
set_state_usages: FxHashMap::default(),
};
// Initialize derivation cache based on function type
@@ -395,7 +396,7 @@ pub fn validate_no_derived_computations_in_effects_exp(
DerivationMetadata {
place_identifier: place.identifier,
place_name: name,
source_ids: indexmap::IndexSet::new(),
source_ids: IndexSet::default(),
type_of_value: TypeOfValue::FromProps,
is_state_source: true,
},
@@ -411,7 +412,7 @@ pub fn validate_no_derived_computations_in_effects_exp(
DerivationMetadata {
place_identifier: place.identifier,
place_name: name,
source_ids: indexmap::IndexSet::new(),
source_ids: IndexSet::default(),
type_of_value: TypeOfValue::FromProps,
is_state_source: true,
},
@@ -477,7 +478,7 @@ fn record_phi_derivations(
let identifiers = &env.identifiers;
for phi in &block.phis {
let mut type_of_value = TypeOfValue::Ignored;
let mut source_ids: indexmap::IndexSet<IdentifierId> = indexmap::IndexSet::new();
let mut source_ids: IndexSet<IdentifierId, FxBuildHasher> = IndexSet::default();
for (_block_id, operand) in &phi.operands {
if let Some(operand_metadata) = context.derivation_cache.cache.get(&operand.identifier)
@@ -522,7 +523,7 @@ fn record_instruction_derivations(
let mut type_of_value = TypeOfValue::Ignored;
let is_source = false;
let mut sources: indexmap::IndexSet<IdentifierId> = indexmap::IndexSet::new();
let mut sources: IndexSet<IdentifierId, FxBuildHasher> = IndexSet::default();
match &instr.value {
InstructionValue::FunctionExpression { lowered_func, .. } => {
@@ -575,7 +576,7 @@ fn record_instruction_derivations(
context.derivation_cache.add_derivation_entry(
lvalue_id,
name,
indexmap::IndexSet::new(),
IndexSet::default(),
TypeOfValue::FromState,
true,
);
@@ -614,7 +615,7 @@ fn record_instruction_derivations(
context.derivation_cache.add_derivation_entry(
lvalue_id,
name,
indexmap::IndexSet::new(),
IndexSet::default(),
TypeOfValue::FromState,
true,
);
@@ -643,8 +644,11 @@ fn record_instruction_derivations(
for (operand_id, operand_loc) in each_instruction_operand(instr, env) {
// Track setState usages
if context.set_state_loads.contains_key(&operand_id) {
let root =
get_root_set_state(operand_id, &context.set_state_loads, &mut HashSet::new());
let root = get_root_set_state(
operand_id,
&context.set_state_loads,
&mut FxHashSet::default(),
);
if let Some(root_id) = root {
if let Some(usages) = context.set_state_usages.get_mut(&root_id) {
usages.insert(LocKey::from_loc(&operand_loc));
@@ -754,7 +758,7 @@ struct TreeNode {
fn build_tree_node(
source_id: IdentifierId,
context: &ValidationContext,
visited: &HashSet<String>,
visited: &FxHashSet<String>,
) -> Vec<TreeNode> {
let source_metadata = match context.derivation_cache.cache.get(&source_id) {
Some(m) => m,
@@ -773,7 +777,7 @@ fn build_tree_node(
}
let mut children: Vec<TreeNode> = Vec::new();
let mut named_siblings: indexmap::IndexSet<String> = indexmap::IndexSet::new();
let mut named_siblings: IndexSet<String, FxBuildHasher> = IndexSet::default();
for child_id in &source_metadata.source_ids {
assert_ne!(
@@ -813,8 +817,8 @@ fn render_tree(
node: &TreeNode,
indent: &str,
is_last: bool,
props_set: &mut indexmap::IndexSet<String>,
state_set: &mut indexmap::IndexSet<String>,
props_set: &mut IndexSet<String, FxBuildHasher>,
state_set: &mut IndexSet<String, FxBuildHasher>,
) -> String {
let prefix = format!(
"{}{}",
@@ -865,10 +869,10 @@ fn render_tree(
fn get_fn_local_deps(
func_id: Option<FunctionId>,
env: &Environment,
) -> Option<HashSet<IdentifierId>> {
) -> Option<FxHashSet<IdentifierId>> {
let func_id = func_id?;
let inner = &env.functions[func_id.0 as usize];
let mut deps: HashSet<IdentifierId> = HashSet::new();
let mut deps: FxHashSet<IdentifierId> = FxHashSet::default();
for (_block_id, block) in &inner.body.blocks {
for &instr_id in &block.instructions {
@@ -894,34 +898,35 @@ fn validate_effect(
let types = &env.types;
let functions = &env.functions;
let effect_function = &functions[effect_func_id.0 as usize];
let mut seen_blocks: HashSet<BlockId> = HashSet::new();
let mut seen_blocks: FxHashSet<BlockId> = FxHashSet::default();
struct DerivedSetStateCall {
callee_loc: Option<SourceLocation>,
callee_id: IdentifierId,
callee_identifier_name: Option<String>,
source_ids: indexmap::IndexSet<IdentifierId>,
source_ids: IndexSet<IdentifierId, FxBuildHasher>,
}
let mut effect_derived_set_state_calls: Vec<DerivedSetStateCall> = Vec::new();
let mut effect_set_state_usages: HashMap<IdentifierId, HashSet<LocKey>> = HashMap::new();
let mut effect_set_state_usages: FxHashMap<IdentifierId, FxHashSet<LocKey>> =
FxHashMap::default();
// Consider setStates in the effect's dependency array as being part of effectSetStateUsages
for dep in dependencies {
let root = get_root_set_state(
dep.identifier,
&context.set_state_loads,
&mut HashSet::new(),
&mut FxHashSet::default(),
);
if let Some(root_id) = root {
let mut set = HashSet::new();
let mut set = FxHashSet::default();
set.insert(LocKey::from_loc(&dep.loc));
effect_set_state_usages.insert(root_id, set);
}
}
let mut cleanup_function_deps: Option<HashSet<IdentifierId>> = None;
let mut globals: HashSet<IdentifierId> = HashSet::new();
let mut cleanup_function_deps: Option<FxHashSet<IdentifierId>> = None;
let mut globals: FxHashSet<IdentifierId> = FxHashSet::default();
for (_block_id, block) in &effect_function.body.blocks {
// Check for return -> cleanup function
@@ -965,7 +970,7 @@ fn validate_effect(
let root = get_root_set_state(
operand_id,
&context.set_state_loads,
&mut HashSet::new(),
&mut FxHashSet::default(),
);
if let Some(root_id) = root {
if let Some(usages) = effect_set_state_usages.get_mut(&root_id) {
@@ -1045,7 +1050,7 @@ fn validate_effect(
let root_set_state_call = get_root_set_state(
derived.callee_id,
&context.set_state_loads,
&mut HashSet::new(),
&mut FxHashSet::default(),
);
if let Some(root_id) = root_set_state_call {
let effect_usage_count = effect_set_state_usages
@@ -1061,13 +1066,13 @@ fn validate_effect(
&& context.set_state_usages.contains_key(&root_id)
&& effect_usage_count == total_usage_count - 1
{
let mut props_set: indexmap::IndexSet<String> = indexmap::IndexSet::new();
let mut state_set: indexmap::IndexSet<String> = indexmap::IndexSet::new();
let mut props_set: IndexSet<String, FxBuildHasher> = IndexSet::default();
let mut state_set: IndexSet<String, FxBuildHasher> = IndexSet::default();
let mut root_nodes_map: indexmap::IndexMap<String, TreeNode> =
indexmap::IndexMap::new();
let mut root_nodes_map: IndexMap<String, TreeNode, FxBuildHasher> =
IndexMap::default();
for id in &derived.source_ids {
let nodes = build_tree_node(*id, context, &HashSet::new());
let nodes = build_tree_node(*id, context, &FxHashSet::default());
for node in nodes {
if !root_nodes_map.contains_key(&node.name) {
root_nodes_map.insert(node.name.clone(), node);
@@ -1162,9 +1167,9 @@ pub fn validate_no_derived_computations_in_effects(
let effects_to_validate: Vec<(FunctionId, Vec<IdentifierId>)> = {
let ids = &env.identifiers;
let tys = &env.types;
let mut candidate_deps: HashMap<IdentifierId, Vec<IdentifierId>> = HashMap::new();
let mut functions_map: HashMap<IdentifierId, FunctionId> = HashMap::new();
let mut locals_map: HashMap<IdentifierId, IdentifierId> = HashMap::new();
let mut candidate_deps: FxHashMap<IdentifierId, Vec<IdentifierId>> = FxHashMap::default();
let mut functions_map: FxHashMap<IdentifierId, FunctionId> = FxHashMap::default();
let mut locals_map: FxHashMap<IdentifierId, IdentifierId> = FxHashMap::default();
let mut result = Vec::new();
for (_, block) in &func.body.blocks {
@@ -1280,8 +1285,8 @@ fn validate_effect_non_exp(
}
}
let mut seen_blocks: HashSet<BlockId> = HashSet::new();
let mut dep_values: HashMap<IdentifierId, Vec<IdentifierId>> = HashMap::new();
let mut seen_blocks: FxHashSet<BlockId> = FxHashSet::default();
let mut dep_values: FxHashMap<IdentifierId, Vec<IdentifierId>> = FxHashMap::default();
for dep in effect_deps {
dep_values.insert(*dep, vec![*dep]);
}
@@ -1296,7 +1301,7 @@ fn validate_effect_non_exp(
}
for phi in &block.phis {
let mut aggregate: HashSet<IdentifierId> = HashSet::new();
let mut aggregate: FxHashSet<IdentifierId> = FxHashSet::default();
for operand in phi.operands.values() {
if let Some(deps) = dep_values.get(&operand.identifier) {
for d in deps {
@@ -1326,7 +1331,7 @@ fn validate_effect_non_exp(
| InstructionValue::TemplateLiteral { .. }
| InstructionValue::CallExpression { .. }
| InstructionValue::MethodCall { .. } => {
let mut aggregate: HashSet<IdentifierId> = HashSet::new();
let mut aggregate: FxHashSet<IdentifierId> = FxHashSet::default();
for operand in non_exp_value_operands(&instr.value) {
if let Some(deps) = dep_values.get(&operand) {
for d in deps {
@@ -1343,7 +1348,7 @@ fn validate_effect_non_exp(
if is_set_state_type(callee_ty) && args.len() == 1 {
if let PlaceOrSpread::Place(arg) = &args[0] {
if let Some(deps) = dep_values.get(&arg.identifier) {
let dep_set: HashSet<_> = deps.iter().collect();
let dep_set: FxHashSet<_> = deps.iter().collect();
if dep_set.len() == effect_deps.len() {
if let Some(loc) = callee.loc {
set_state_locs.push(loc);
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -53,7 +53,7 @@ fn check_no_freezing_known_mutable_functions(
env: &Environment,
) -> Vec<CompilerDiagnostic> {
// Maps an identifier to the mutation effect that makes it "known mutable"
let mut context_mutation_effects: HashMap<IdentifierId, MutationInfo> = HashMap::new();
let mut context_mutation_effects: FxHashMap<IdentifierId, MutationInfo> = FxHashMap::default();
let mut diagnostics: Vec<CompilerDiagnostic> = Vec::new();
for (_block_id, block) in &func.body.blocks {
@@ -83,7 +83,7 @@ fn check_no_freezing_known_mutable_functions(
InstructionValue::FunctionExpression { lowered_func, .. } => {
let inner_function = &functions[lowered_func.func.0 as usize];
if let Some(ref aliasing_effects) = inner_function.aliasing_effects {
let context_ids: HashSet<IdentifierId> = inner_function
let context_ids: FxHashSet<IdentifierId> = inner_function
.context
.iter()
.map(|place| place.identifier)
@@ -170,7 +170,7 @@ fn check_no_freezing_known_mutable_functions(
/// If an operand with Effect::Freeze is a known-mutable function, emit a diagnostic.
fn check_operand_for_freeze_violation(
operand: &Place,
context_mutation_effects: &HashMap<IdentifierId, MutationInfo>,
context_mutation_effects: &FxHashMap<IdentifierId, MutationInfo>,
identifiers: &[Identifier],
diagnostics: &mut Vec<CompilerDiagnostic>,
) {
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -270,16 +270,16 @@ fn join_ref_access_types_many(types: &[RefAccessType]) -> RefAccessType {
struct Env {
changed: bool,
data: HashMap<IdentifierId, RefAccessType>,
temporaries: HashMap<IdentifierId, Place>,
data: FxHashMap<IdentifierId, RefAccessType>,
temporaries: FxHashMap<IdentifierId, Place>,
}
impl Env {
fn new() -> Self {
Self {
changed: false,
data: HashMap::new(),
temporaries: HashMap::new(),
data: FxHashMap::default(),
temporaries: FxHashMap::default(),
}
}
@@ -626,7 +626,7 @@ fn validate_no_ref_access_in_render_impl(
}
// Collect identifiers that are interpolated as JSX children
let mut interpolated_as_jsx: HashSet<IdentifierId> = HashSet::new();
let mut interpolated_as_jsx: FxHashSet<IdentifierId> = FxHashSet::default();
for (_, block) in &func.body.blocks {
for &instr_id in &block.instructions {
let instr = &func.instructions[instr_id.0 as usize];
@@ -890,7 +890,8 @@ fn validate_no_ref_access_in_render_impl(
* use the effects to determine what validation to apply.
* Track visited id:kind pairs to avoid duplicate errors.
*/
let mut visited_effects: HashSet<String> = HashSet::new();
let mut visited_effects: FxHashSet<String> =
FxHashSet::default();
for effect in effects {
let (place, validation) = match effect {
AliasingEffect::Freeze { value, .. } => {
@@ -12,7 +12,7 @@
//!
//! Port of ValidateNoSetStateInEffects.ts.
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory,
@@ -37,7 +37,7 @@ pub fn validate_no_set_state_in_effects(
let enable_allow_set_state_from_refs = env.config.enable_allow_set_state_from_refs_in_effects;
// Map from IdentifierId to the Place where the setState originated
let mut set_state_functions: HashMap<IdentifierId, SetStateInfo> = HashMap::new();
let mut set_state_functions: FxHashMap<IdentifierId, SetStateInfo> = FxHashMap::default();
let mut errors = CompilerError::new();
for (_block_id, block) in &func.body.blocks {
@@ -246,7 +246,7 @@ fn push_error(errors: &mut CompilerError, info: &SetStateInfo, enable_verbose: b
/// Recursively collect all Place identifiers from a destructure pattern.
fn collect_destructure_places(
pattern: &react_compiler_hir::Pattern,
ref_derived_values: &mut HashSet<IdentifierId>,
ref_derived_values: &mut FxHashSet<IdentifierId>,
) {
match pattern {
react_compiler_hir::Pattern::Array(arr) => {
@@ -279,7 +279,7 @@ fn collect_destructure_places(
fn is_derived_from_ref(
id: IdentifierId,
ref_derived_values: &HashSet<IdentifierId>,
ref_derived_values: &FxHashSet<IdentifierId>,
identifiers: &[Identifier],
types: &[Type],
) -> bool {
@@ -306,12 +306,12 @@ fn collect_operands(value: &InstructionValue, functions: &[HirFunction]) -> Vec<
fn create_ref_controlled_block_checker(
func: &HirFunction,
next_block_id_counter: u32,
ref_derived_values: &HashSet<IdentifierId>,
ref_derived_values: &FxHashSet<IdentifierId>,
identifiers: &[Identifier],
types: &[Type],
) -> Result<HashMap<BlockId, bool>, CompilerDiagnostic> {
) -> Result<FxHashMap<BlockId, bool>, CompilerDiagnostic> {
let post_dominators = compute_post_dominator_tree(func, next_block_id_counter, false)?;
let mut cache: HashMap<BlockId, bool> = HashMap::new();
let mut cache: FxHashMap<BlockId, bool> = FxHashMap::default();
for (block_id, _block) in &func.body.blocks {
let frontier = post_dominator_frontier(func, &post_dominators, *block_id);
@@ -365,7 +365,7 @@ fn create_ref_controlled_block_checker(
/// Tracks ref-derived values to allow setState when the value being set comes from a ref.
fn get_set_state_call(
func: &HirFunction,
set_state_functions: &mut HashMap<IdentifierId, SetStateInfo>,
set_state_functions: &mut FxHashMap<IdentifierId, SetStateInfo>,
identifiers: &[Identifier],
types: &[Type],
functions: &[HirFunction],
@@ -373,7 +373,7 @@ fn get_set_state_call(
next_block_id_counter: u32,
source_code: Option<&str>,
) -> Result<Option<SetStateInfo>, CompilerDiagnostic> {
let mut ref_derived_values: HashSet<IdentifierId> = HashSet::new();
let mut ref_derived_values: FxHashSet<IdentifierId> = FxHashSet::default();
// First pass: collect ref-derived values (needed before building control dominator checker)
// 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(
types,
)?
} else {
HashMap::new()
FxHashMap::default()
};
let is_ref_controlled_block = |block_id: BlockId| -> bool {
@@ -7,7 +7,7 @@
//!
//! Port of ValidateNoSetStateInRender.ts.
use std::collections::HashSet;
use rustc_hash::FxHashSet;
use react_compiler_diagnostics::{CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory};
use react_compiler_hir::dominator::compute_unconditional_blocks;
@@ -18,7 +18,7 @@ pub fn validate_no_set_state_in_render(
func: &HirFunction,
env: &mut Environment,
) -> Result<(), CompilerDiagnostic> {
let mut unconditional_set_state_functions: HashSet<IdentifierId> = HashSet::new();
let mut unconditional_set_state_functions: FxHashSet<IdentifierId> = FxHashSet::default();
let next_block_id = env.next_block_id().0;
let diagnostics = validate_impl(
func,
@@ -52,9 +52,9 @@ fn validate_impl(
functions: &[HirFunction],
next_block_id_counter: u32,
enable_use_keyed_state: bool,
unconditional_set_state_functions: &mut HashSet<IdentifierId>,
unconditional_set_state_functions: &mut FxHashSet<IdentifierId>,
) -> Result<Vec<CompilerDiagnostic>, CompilerDiagnostic> {
let unconditional_blocks: HashSet<BlockId> =
let unconditional_blocks: FxHashSet<BlockId> =
compute_unconditional_blocks(func, next_block_id_counter)?;
let mut active_manual_memo_id: Option<u32> = None;
let mut errors: Vec<CompilerDiagnostic> = Vec::new();
@@ -9,7 +9,7 @@
//! accurately preserved, and that no originally memoized values became
//! unmemoized in the output.
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, SourceLocation,
@@ -25,11 +25,11 @@ use react_compiler_hir::{
/// State tracked during manual memo validation within a StartMemoize..FinishMemoize range.
struct ManualMemoBlockState {
/// Reassigned temporaries (declaration_id -> set of identifier ids that were reassigned to it).
reassignments: HashMap<DeclarationId, HashSet<IdentifierId>>,
reassignments: FxHashMap<DeclarationId, FxHashSet<IdentifierId>>,
/// Source location of the StartMemoize instruction.
loc: Option<SourceLocation>,
/// Declarations produced within this manual memo block.
decls: HashSet<DeclarationId>,
decls: FxHashSet<DeclarationId>,
/// Normalized deps from source (useMemo/useCallback dep array).
deps_from_source: Option<Vec<ManualMemoDependency>>,
/// Manual memo id from StartMemoize.
@@ -41,11 +41,11 @@ struct VisitorState<'a> {
env: &'a mut Environment,
manual_memo_state: Option<ManualMemoBlockState>,
/// Completed (non-pruned) scope IDs.
scopes: HashSet<ScopeId>,
scopes: FxHashSet<ScopeId>,
/// Completed pruned scope IDs.
pruned_scopes: HashSet<ScopeId>,
pruned_scopes: FxHashSet<ScopeId>,
/// Map from identifier ID to its normalized manual memo dependency.
temporaries: HashMap<IdentifierId, ManualMemoDependency>,
temporaries: FxHashMap<IdentifierId, ManualMemoDependency>,
}
/// Validate that manual memoization (useMemo/useCallback) is preserved.
@@ -59,9 +59,9 @@ pub fn validate_preserved_manual_memoization(func: &ReactiveFunction, env: &mut
let mut state = VisitorState {
env,
manual_memo_state: None,
scopes: HashSet::new(),
pruned_scopes: HashSet::new(),
temporaries: HashMap::new(),
scopes: FxHashSet::default(),
pruned_scopes: FxHashSet::default(),
temporaries: FxHashMap::default(),
};
visit_block(&func.body, &mut state);
}
@@ -203,10 +203,10 @@ fn visit_instruction(instr: &ReactiveInstruction, state: &mut VisitorState) {
state.manual_memo_state = Some(ManualMemoBlockState {
loc: instr.loc,
decls: HashSet::new(),
decls: FxHashSet::default(),
deps_from_source,
manual_memo_id: *manual_memo_id,
reassignments: HashMap::new(),
reassignments: FxHashMap::default(),
});
// 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
/// Check if an identifier is unmemoized (has a scope that hasn't completed).
fn is_unmemoized(
id: IdentifierId,
completed_scopes: &HashSet<ScopeId>,
completed_scopes: &FxHashSet<ScopeId>,
identifiers: &[Identifier],
) -> bool {
let ident = &identifiers[id.0 as usize];
@@ -675,8 +675,8 @@ fn get_compare_dependency_result_description(result: CompareDependencyResult) ->
fn validate_inferred_dep(
dep_id: IdentifierId,
dep_path: &[DependencyPathEntry],
temporaries: &HashMap<IdentifierId, ManualMemoDependency>,
decls_within_memo_block: &HashSet<DeclarationId>,
temporaries: &FxHashMap<IdentifierId, ManualMemoDependency>,
decls_within_memo_block: &FxHashSet<DeclarationId>,
valid_deps_in_memo_block: &[ManualMemoDependency],
env: &mut Environment,
memo_location: Option<SourceLocation>,
@@ -9,7 +9,7 @@
//!
//! Port of ValidateStaticComponents.ts.
use std::collections::HashMap;
use rustc_hash::FxHashMap;
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation,
@@ -22,8 +22,8 @@ use react_compiler_hir::{HirFunction, IdentifierId, InstructionValue, JsxTag};
/// Called via `env.logErrors()` pattern in Pipeline.ts.
pub fn validate_static_components(func: &HirFunction) -> CompilerError {
let mut error = CompilerError::new();
let mut known_dynamic_components: HashMap<IdentifierId, Option<SourceLocation>> =
HashMap::new();
let mut known_dynamic_components: FxHashMap<IdentifierId, Option<SourceLocation>> =
FxHashMap::default();
for (_block_id, block) in &func.body.blocks {
// Process phis: propagate dynamic component knowledge through phi nodes
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use react_compiler_diagnostics::{
CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation,
@@ -38,11 +38,11 @@ fn validate_use_memo_impl(
validate_no_void_use_memo: bool,
) -> CompilerError {
let mut void_memo_errors = CompilerError::new();
let mut use_memos: HashSet<IdentifierId> = HashSet::new();
let mut react: HashSet<IdentifierId> = HashSet::new();
let mut func_exprs: HashMap<IdentifierId, FuncExprInfo> = HashMap::new();
let mut unused_use_memos: HashMap<IdentifierId, (SourceLocation, Option<String>)> =
HashMap::new();
let mut use_memos: FxHashSet<IdentifierId> = FxHashSet::default();
let mut react: FxHashSet<IdentifierId> = FxHashSet::default();
let mut func_exprs: FxHashMap<IdentifierId, FuncExprInfo> = FxHashMap::default();
let mut unused_use_memos: FxHashMap<IdentifierId, (SourceLocation, Option<String>)> =
FxHashMap::default();
for (_block_id, block) in &func.body.blocks {
for &instr_id in &block.instructions {
@@ -157,9 +157,9 @@ fn handle_possible_use_memo_call(
functions: &[HirFunction],
errors: &mut CompilerError,
void_memo_errors: &mut CompilerError,
use_memos: &HashSet<IdentifierId>,
func_exprs: &HashMap<IdentifierId, FuncExprInfo>,
unused_use_memos: &mut HashMap<IdentifierId, (SourceLocation, Option<String>)>,
use_memos: &FxHashSet<IdentifierId>,
func_exprs: &FxHashMap<IdentifierId, FuncExprInfo>,
unused_use_memos: &mut FxHashMap<IdentifierId, (SourceLocation, Option<String>)>,
callee: &Place,
args: &[PlaceOrSpread],
lvalue: &Place,
@@ -254,7 +254,7 @@ fn handle_possible_use_memo_call(
}
fn validate_no_context_variable_assignment(func: &HirFunction, errors: &mut CompilerError) {
let context: HashSet<IdentifierId> =
let context: FxHashSet<IdentifierId> =
func.context.iter().map(|place| place.identifier).collect();
for (_block_id, block) in &func.body.blocks {