diff --git a/forc-plugins/forc-lsp/src/main.rs b/forc-plugins/forc-lsp/src/main.rs index 2303f7785b2..b859e136135 100644 --- a/forc-plugins/forc-lsp/src/main.rs +++ b/forc-plugins/forc-lsp/src/main.rs @@ -1,7 +1,7 @@ //! A simple `forc` plugin for starting the sway language server. //! //! Once installed and available via `PATH`, can be executed via `forc lsp`. - +#![recursion_limit = "256"] // Use Jemalloc for main binary #[global_allocator] static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 0046e2c9624..c744255156d 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "256"] + #[macro_use] pub mod error; diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index 9cc2880a73b..7bdc515aa5c 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -6,18 +6,19 @@ use std::{ sync::Arc, }; -use hashbrown::HashSet; use sway_error::{ error::CompileError, handler::{ErrorEmitted, Handler}, }; -use sway_types::{BaseIdent, Ident, Span, Spanned}; +use sway_types::{integer_bits::IntegerBits, BaseIdent, Ident, Span, Spanned}; use crate::{ - decl_engine::{DeclEngineGet, DeclEngineGetParsedDeclId, DeclEngineInsert}, + decl_engine::{ + parsed_id::ParsedDeclId, DeclEngineGet, DeclEngineGetParsedDeclId, DeclEngineInsert, + }, engine_threading::*, language::{ - parsed::ImplItem, + parsed::{EnumDeclaration, ImplItem, StructDeclaration}, ty::{self, TyImplItem, TyTraitItem}, CallPath, }, @@ -125,8 +126,41 @@ struct TraitEntry { value: TraitValue, } -/// Map of trait name and type to [TraitItems]. -type TraitImpls = Vec; +/// Map of string of type entry id and vec of [TraitEntry]. +/// We are using the HashMap as a wrapper to the vec so the TraitMap algorithms +/// don't need to traverse every TraitEntry. +type TraitImpls = im::HashMap>; + +#[derive(Clone, Hash, Eq, PartialEq, Debug)] +enum TypeRootFilter { + Unknown, + Never, + Placeholder, + TypeParam(usize), + StringSlice, + StringArray(usize), + U8, + U16, + U32, + U64, + U256, + Bool, + Custom(String), + B256, + Contract, + ErrorRecovery, + Tuple(usize), + Enum(ParsedDeclId), + Struct(ParsedDeclId), + ContractCaller(String), + Array(usize), + Storage, + RawUntypedPtr, + RawUntypedSlice, + Ptr, + Slice, + TraitType(String), +} /// Map holding trait implementations for types. /// @@ -135,7 +169,7 @@ type TraitImpls = Vec; #[derive(Clone, Debug, Default)] pub(crate) struct TraitMap { trait_impls: TraitImpls, - satisfied_cache: hashbrown::HashSet, + satisfied_cache: im::HashSet, } pub(crate) enum IsImplSelf { @@ -198,6 +232,8 @@ impl TraitMap { } } + let trait_impls = self.get_impls_mut(engines, type_id); + // check to see if adding this trait will produce a conflicting definition for TraitEntry { key: @@ -211,7 +247,7 @@ impl TraitMap { trait_items: map_trait_items, impl_span: existing_impl_span, }, - } in self.trait_impls.iter() + } in trait_impls.iter() { let CallPath { suffix: @@ -412,10 +448,16 @@ impl TraitMap { impl_span, }; let entry = TraitEntry { key, value }; - let trait_impls: TraitImpls = vec![entry]; + let mut trait_impls: TraitImpls = + im::HashMap::>::new(); + let type_root_filter = Self::get_type_root_filter(engines, type_id); + let mut impls_vector = im::Vector::::new(); + impls_vector.push_back(entry); + trait_impls.insert(type_root_filter, impls_vector); + let trait_map = TraitMap { trait_impls, - satisfied_cache: HashSet::default(), + satisfied_cache: im::HashSet::default(), }; self.extend(trait_map, engines); @@ -514,17 +556,27 @@ impl TraitMap { /// Given [TraitMap]s `self` and `other`, extend `self` with `other`, /// extending existing entries when possible. pub(crate) fn extend(&mut self, other: TraitMap, engines: &Engines) { - for oe in other.trait_impls.into_iter() { - let pos = self - .trait_impls - .binary_search_by(|se| se.key.cmp(&oe.key, &OrdWithEnginesContext::new(engines))); + for (impls_key, oe_vec) in other.trait_impls.iter() { + let self_vec = if let Some(self_vec) = self.trait_impls.get_mut(impls_key) { + self_vec + } else { + self.trait_impls + .insert(impls_key.clone(), im::Vector::::new()); + self.trait_impls.get_mut(impls_key).unwrap() + }; - match pos { - Ok(pos) => self.trait_impls[pos] - .value - .trait_items - .extend(oe.value.trait_items), - Err(pos) => self.trait_impls.insert(pos, oe), + for oe in oe_vec.iter() { + let pos = self_vec.binary_search_by(|se| { + se.key.cmp(&oe.key, &OrdWithEnginesContext::new(engines)) + }); + + match pos { + Ok(pos) => self_vec[pos] + .value + .trait_items + .extend(oe.value.trait_items.clone()), + Err(pos) => self_vec.insert(pos, oe.clone()), + } } } } @@ -533,14 +585,26 @@ impl TraitMap { /// the entries from `self` that implement a trait from the declaration with that span. pub(crate) fn filter_by_trait_decl_span(&self, trait_decl_span: Span) -> TraitMap { let mut trait_map = TraitMap::default(); - for entry in self.trait_impls.iter() { - if entry - .key - .trait_decl_span - .as_ref() - .map_or(false, |span| span == &trait_decl_span) - { - trait_map.trait_impls.push(entry.clone()); + for (key, vec) in self.trait_impls.iter() { + for entry in vec { + if entry + .key + .trait_decl_span + .as_ref() + .map_or(false, |span| span == &trait_decl_span) + { + let trait_map_vec = + if let Some(trait_map_vec) = trait_map.trait_impls.get_mut(key) { + trait_map_vec + } else { + trait_map + .trait_impls + .insert(key.clone(), im::Vector::::new()); + trait_map.trait_impls.get_mut(key).unwrap() + }; + + trait_map_vec.push_back(entry.clone()); + } } } trait_map @@ -758,22 +822,23 @@ impl TraitMap { let type_engine = engines.te(); let decl_engine = engines.de(); let mut trait_map = TraitMap::default(); - for TraitEntry { - key: - TraitKey { - name: map_trait_name, - type_id: map_type_id, - trait_decl_span: map_trait_decl_span, - }, - value: - TraitValue { - trait_items: map_trait_items, - impl_span, - }, - } in self.trait_impls.iter() - { - for type_id in all_types.iter_mut() { - let type_info = type_engine.get(*type_id); + for type_id in all_types.iter_mut() { + let type_info = type_engine.get(*type_id); + let impls = self.get_impls(engines, *type_id); + for TraitEntry { + key: + TraitKey { + name: map_trait_name, + type_id: map_type_id, + trait_decl_span: map_trait_decl_span, + }, + value: + TraitValue { + trait_items: map_trait_items, + impl_span, + }, + } in impls.iter() + { if !type_info.can_change(decl_engine) && *type_id == *map_type_id { trait_map.insert_inner( map_trait_name.clone(), @@ -903,7 +968,8 @@ impl TraitMap { if matches!(&*type_engine.get(type_id), TypeInfo::ErrorRecovery(_)) { return items; } - for entry in self.trait_impls.iter() { + let impls = self.get_impls(engines, type_id); + for entry in impls.iter() { if unify_check.check(type_id, entry.key.type_id) { let mut trait_items = entry .value @@ -936,11 +1002,13 @@ impl TraitMap { if matches!(&*type_engine.get(*type_id), TypeInfo::ErrorRecovery(_)) { return spans; } - for entry in self.trait_impls.iter() { + let impls = self.get_impls(engines, *type_id); + for entry in impls.iter() { if unify_check.check(*type_id, entry.key.type_id) { spans.push(entry.value.impl_span.clone()); } } + spans } @@ -949,18 +1017,25 @@ impl TraitMap { pub(crate) fn get_impl_spans_for_trait_name(&self, trait_name: &CallPath) -> Vec { self.trait_impls .iter() - .filter_map(|entry| { - let map_trait_name = CallPath { - prefixes: entry.key.name.prefixes.clone(), - suffix: entry.key.name.suffix.name.clone(), - is_absolute: entry.key.name.is_absolute, - }; - if &map_trait_name == trait_name { - return Some(entry.value.impl_span.clone()); - } - None + .map(|(_, impls)| { + impls + .iter() + .filter_map(|entry| { + let map_trait_name = CallPath { + prefixes: entry.key.name.prefixes.clone(), + suffix: entry.key.name.suffix.name.clone(), + is_absolute: entry.key.name.is_absolute, + }; + if &map_trait_name == trait_name { + Some(entry.value.impl_span.clone()) + } else { + None + } + }) + .collect::>() }) - .collect() + .collect::>>() + .concat() } /// Find the entries in `self` that are equivalent to `type_id` with trait @@ -987,7 +1062,8 @@ impl TraitMap { if matches!(&*type_engine.get(type_id), TypeInfo::ErrorRecovery(_)) { return items; } - for e in self.trait_impls.iter() { + let impls = self.get_impls(engines, type_id); + for e in impls.iter() { let map_trait_name = CallPath { prefixes: e.key.name.prefixes.clone(), suffix: e.key.name.suffix.name.clone(), @@ -1048,7 +1124,8 @@ impl TraitMap { if matches!(&*type_engine.get(type_id), TypeInfo::ErrorRecovery(_)) { return trait_names; } - for entry in self.trait_impls.iter() { + let impls = self.get_impls(engines, type_id); + for entry in impls.iter() { if unify_check.check(type_id, entry.key.type_id) { let trait_call_path = CallPath { prefixes: entry.key.name.prefixes.clone(), @@ -1275,8 +1352,8 @@ impl TraitMap { let _decl_engine = engines.de(); let unify_check = UnifyCheck::non_dynamic_equality(engines); - let all_impld_traits: BTreeSet<(Ident, TypeId)> = self - .trait_impls + let impls = self.get_impls(engines, type_id); + let all_impld_traits: BTreeSet<(Ident, TypeId)> = impls .iter() .filter_map(|e| { let key = &e.key; @@ -1377,4 +1454,83 @@ impl TraitMap { Ok(()) }) } + + fn get_impls_mut(&mut self, engines: &Engines, type_id: TypeId) -> &mut im::Vector { + let type_root_filter = Self::get_type_root_filter(engines, type_id); + if !self.trait_impls.contains_key(&type_root_filter) { + self.trait_impls + .insert(type_root_filter.clone(), im::Vector::new()); + } + + self.trait_impls.get_mut(&type_root_filter).unwrap() + } + + fn get_impls(&self, engines: &Engines, type_id: TypeId) -> im::Vector { + let type_root_filter = Self::get_type_root_filter(engines, type_id); + let mut vec = self + .trait_impls + .get(&type_root_filter) + .cloned() + .unwrap_or_default(); + if type_root_filter != TypeRootFilter::Placeholder { + vec.extend( + self.trait_impls + .get(&TypeRootFilter::Placeholder) + .cloned() + .unwrap_or_default(), + ); + } + vec + } + + // Return a string representing only the base type. + // This is used by the trait map to filter the entries into a HashMap with the return type string as key. + fn get_type_root_filter(engines: &Engines, type_id: TypeId) -> TypeRootFilter { + use TypeInfo::*; + match &*engines.te().get(type_id) { + Unknown => TypeRootFilter::Unknown, + Never => TypeRootFilter::Never, + UnknownGeneric { .. } | Placeholder(_) => TypeRootFilter::Placeholder, + TypeParam(n) => TypeRootFilter::TypeParam(*n), + StringSlice => TypeRootFilter::StringSlice, + StringArray(x) => TypeRootFilter::StringArray(x.val()), + UnsignedInteger(x) => match x { + IntegerBits::Eight => TypeRootFilter::U8, + IntegerBits::Sixteen => TypeRootFilter::U16, + IntegerBits::ThirtyTwo => TypeRootFilter::U32, + IntegerBits::SixtyFour => TypeRootFilter::U64, + IntegerBits::V256 => TypeRootFilter::U256, + }, + Boolean => TypeRootFilter::Bool, + Custom { + qualified_call_path: call_path, + .. + } => TypeRootFilter::Custom(call_path.call_path.suffix.to_string()), + B256 => TypeRootFilter::B256, + Numeric => TypeRootFilter::U64, // u64 is the default + Contract => TypeRootFilter::Contract, + ErrorRecovery(_) => TypeRootFilter::ErrorRecovery, + Tuple(fields) => TypeRootFilter::Tuple(fields.len()), + Enum(decl_id) => { + // TODO Remove unwrap once #6475 is fixed + TypeRootFilter::Enum(engines.de().get_parsed_decl_id(decl_id).unwrap()) + } + Struct(decl_id) => { + // TODO Remove unwrap once #6475 is fixed + TypeRootFilter::Struct(engines.de().get_parsed_decl_id(decl_id).unwrap()) + } + ContractCaller { abi_name, .. } => TypeRootFilter::ContractCaller(abi_name.to_string()), + Array(_, length) => TypeRootFilter::Array(length.val()), + Storage { .. } => TypeRootFilter::Storage, + RawUntypedPtr => TypeRootFilter::RawUntypedPtr, + RawUntypedSlice => TypeRootFilter::RawUntypedSlice, + Ptr(_) => TypeRootFilter::Ptr, + Slice(_) => TypeRootFilter::Slice, + Alias { ty, .. } => Self::get_type_root_filter(engines, ty.type_id), + TraitType { name, .. } => TypeRootFilter::TraitType(name.to_string()), + Ref { + referenced_type, .. + } => Self::get_type_root_filter(engines, referenced_type.type_id), + } + } } diff --git a/sway-lsp/benches/bench_main.rs b/sway-lsp/benches/bench_main.rs index 1a767ac6792..e47afd40875 100644 --- a/sway-lsp/benches/bench_main.rs +++ b/sway-lsp/benches/bench_main.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "256"] + mod lsp_benchmarks; use criterion::criterion_main; diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 5c0d38a7f43..ebdcc8ac19a 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "256"] + pub mod integration; use crate::integration::{code_actions, lsp};