From 4b3257a365b7e61f5a868fd039165f0365934b5c Mon Sep 17 00:00:00 2001 From: shogo-nakano-desu <61229807+shogo-nakano-desu@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:54:06 +0900 Subject: [PATCH 1/6] refactor: port anymap --- Cargo.lock | 28 +- Cargo.toml | 16 +- crates/anymap/Cargo.toml | 20 ++ crates/anymap/src/any.rs | 134 +++++++++ crates/anymap/src/lib.rs | 275 ++++++++++++++++++ crates/hir-def/Cargo.toml | 3 +- crates/rust-analyzer/tests/slow-tests/tidy.rs | 1 - 7 files changed, 468 insertions(+), 9 deletions(-) create mode 100644 crates/anymap/Cargo.toml create mode 100644 crates/anymap/src/any.rs create mode 100644 crates/anymap/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e506aa834eb..6b0f7efc6ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,23 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "always-assert" version = "0.1.3" @@ -34,9 +51,10 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "anymap" -version = "1.0.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72" +version = "0.0.0" +dependencies = [ + "hashbrown 0.14.0", +] [[package]] name = "arbitrary" @@ -464,6 +482,10 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" diff --git a/Cargo.toml b/Cargo.toml index ffac946b18f..0b3ec8ed000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ debug = 0 [workspace.dependencies] # local crates +anymap = { path = "./crates/anymap", version = "0.0.0" } base-db = { path = "./crates/base-db", version = "0.0.0" } cfg = { path = "./crates/cfg", version = "0.0.0" } flycheck = { path = "./crates/flycheck", version = "0.0.0" } @@ -91,9 +92,9 @@ lsp-server = { version = "0.7.4" } # non-local crates smallvec = { version = "1.10.0", features = [ - "const_new", - "union", - "const_generics", + "const_new", + "union", + "const_generics", ] } smol_str = "0.2.0" nohash-hasher = "0.2.0" @@ -103,5 +104,12 @@ serde_json = "1.0.96" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently hashbrown = { version = "0.12.3", features = [ - "inline-more", + "inline-more", ], default-features = false } + +rustc_lexer = { version = "0.10.0", package = "ra-ap-rustc_lexer" } +rustc_parse_format = { version = "0.10.0", package = "ra-ap-rustc_parse_format", default-features = false } + +# Upstream broke this for us so we can't update it +rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } +rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } diff --git a/crates/anymap/Cargo.toml b/crates/anymap/Cargo.toml new file mode 100644 index 00000000000..5f3976edcbd --- /dev/null +++ b/crates/anymap/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "anymap" +version = "0.0.0" +description = "This crate is a port of only the necessary features from https://github.com/chris-morgan/anymap for use within rust-analyzer. Copyright © 2014–2022 Chris Morgan. COPYING: https://github.com/chris-morgan/anymap/blob/master/COPYING" + +authors.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[package.metadata.docs.rs] +all-features = true + +[features] +default = ["std"] +std = [] + +[dependencies] +# The hashbrown feature, disabled by default, is exposed under different stability guarantees than the usual SemVer ones: by preference the version range will only be extended, but it may be shrunk in a MINOR release. See README.md. +hashbrown = { version = "0.14.0", optional = true } diff --git a/crates/anymap/src/any.rs b/crates/anymap/src/any.rs new file mode 100644 index 00000000000..1e205e7c9d1 --- /dev/null +++ b/crates/anymap/src/any.rs @@ -0,0 +1,134 @@ +//! Copyright © 2014–2022 Chris Morgan +//! https://github.com/chris-morgan/anymap/blob/master/COPYING +//! impl some traits for dyn Any +use core::any::{Any, TypeId}; +use core::fmt; + +#[doc(hidden)] +pub trait CloneToAny { + /// Clone `self` into a new `Box` object. + fn clone_to_any(&self) -> Box; +} + +impl CloneToAny for T { + #[inline] + fn clone_to_any(&self) -> Box { + Box::new(self.clone()) + } +} + +macro_rules! impl_clone { + ($t:ty) => { + impl Clone for Box<$t> { + #[inline] + fn clone(&self) -> Box<$t> { + // SAFETY: this dance is to reapply any Send/Sync marker. I’m not happy about this + // approach, given that I used to do it in safe code, but then came a dodgy + // future-compatibility warning where_clauses_object_safety, which is spurious for + // auto traits but still super annoying (future-compatibility lints seem to mean + // your bin crate needs a corresponding allow!). Although I explained my plight¹ + // and it was all explained and agreed upon, no action has been taken. So I finally + // caved and worked around it by doing it this way, which matches what’s done for + // core::any², so it’s probably not *too* bad. + // + // ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013 + // ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616 + let clone: Box = (**self).clone_to_any(); + let raw: *mut dyn CloneAny = Box::into_raw(clone); + unsafe { Box::from_raw(raw as *mut $t) } + } + } + + impl fmt::Debug for $t { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad(stringify!($t)) + } + } + }; +} + +/// Methods for downcasting from an `Any`-like trait object. +/// +/// This should only be implemented on trait objects for subtraits of `Any`, though you can +/// implement it for other types and it’ll work fine, so long as your implementation is correct. +pub trait Downcast { + /// Gets the `TypeId` of `self`. + fn type_id(&self) -> TypeId; + + // Note the bound through these downcast methods is 'static, rather than the inexpressible + // concept of Self-but-as-a-trait (where Self is `dyn Trait`). This is sufficient, exceeding + // TypeId’s requirements. Sure, you *can* do CloneAny.downcast_unchecked::() and the + // type system won’t protect you, but that doesn’t introduce any unsafety: the method is + // already unsafe because you can specify the wrong type, and if this were exposing safe + // downcasting, CloneAny.downcast::() would just return an error, which is just as + // correct. + // + // Now in theory we could also add T: ?Sized, but that doesn’t play nicely with the common + // implementation, so I’m doing without it. + + /// Downcast from `&Any` to `&T`, without checking the type matches. + /// + /// # Safety + /// + /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. + unsafe fn downcast_ref_unchecked(&self) -> &T; + + /// Downcast from `&mut Any` to `&mut T`, without checking the type matches. + /// + /// # Safety + /// + /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. + unsafe fn downcast_mut_unchecked(&mut self) -> &mut T; +} + +/// A trait for the conversion of an object into a boxed trait object. +pub trait IntoBox: Any { + /// Convert self into the appropriate boxed form. + fn into_box(self) -> Box; +} + +macro_rules! implement { + ($any_trait:ident $(+ $auto_traits:ident)*) => { + impl Downcast for dyn $any_trait $(+ $auto_traits)* { + #[inline] + fn type_id(&self) -> TypeId { + self.type_id() + } + + #[inline] + unsafe fn downcast_ref_unchecked(&self) -> &T { + &*(self as *const Self as *const T) + } + + #[inline] + unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { + &mut *(self as *mut Self as *mut T) + } + } + + impl IntoBox for T { + #[inline] + fn into_box(self) -> Box { + Box::new(self) + } + } + } +} + +implement!(Any); +implement!(Any + Send); +implement!(Any + Send + Sync); + +/// [`Any`], but with cloning. +/// +/// Every type with no non-`'static` references that implements `Clone` implements `CloneAny`. +/// See [`core::any`] for more details on `Any` in general. +pub trait CloneAny: Any + CloneToAny {} +impl CloneAny for T {} +implement!(CloneAny); +implement!(CloneAny + Send); +implement!(CloneAny + Send + Sync); +impl_clone!(dyn CloneAny); +impl_clone!(dyn CloneAny + Send); +impl_clone!(dyn CloneAny + Send + Sync); diff --git a/crates/anymap/src/lib.rs b/crates/anymap/src/lib.rs new file mode 100644 index 00000000000..967c3769ec6 --- /dev/null +++ b/crates/anymap/src/lib.rs @@ -0,0 +1,275 @@ +//! Copyright © 2014–2022 Chris Morgan +//! https://github.com/chris-morgan/anymap/blob/master/COPYING +//! +//! This crate provides a safe and convenient store for one value of each type. +//! +//! Your starting point is [`Map`]. It has an example. +//! +//! # Cargo features +//! +//! This crate has two independent features, each of which provides an implementation providing +//! types `Map`, `AnyMap`, `OccupiedEntry`, `VacantEntry`, `Entry` and `RawMap`: +//! +#![cfg_attr(feature = "std", doc = " - **std** (default, *enabled* in this build):")] +#![cfg_attr(not(feature = "std"), doc = " - **std** (default, *disabled* in this build):")] +//! an implementation using `std::collections::hash_map`, placed in the crate root +//! (e.g. `anymap::AnyMap`). +//! +#![cfg_attr(feature = "hashbrown", doc = " - **hashbrown** (optional; *enabled* in this build):")] +#![cfg_attr( + not(feature = "hashbrown"), + doc = " - **hashbrown** (optional; *disabled* in this build):" +)] +//! an implementation using `alloc` and `hashbrown::hash_map`, placed in a module `hashbrown` +//! (e.g. `anymap::hashbrown::AnyMap`). + +#![warn(missing_docs, unused_results)] +#![cfg_attr(not(feature = "std"), no_std)] + +use core::convert::TryInto; +use core::hash::Hasher; + +pub use crate::any::CloneAny; + +mod any; + +/// A hasher designed to eke a little more speed out, given `TypeId`’s known characteristics. +/// +/// Specifically, this is a no-op hasher that expects to be fed a u64’s worth of +/// randomly-distributed bits. It works well for `TypeId` (eliminating start-up time, so that my +/// get_missing benchmark is ~30ns rather than ~900ns, and being a good deal faster after that, so +/// that my insert_and_get_on_260_types benchmark is ~12μs instead of ~21.5μs), but will +/// panic in debug mode and always emit zeros in release mode for any other sorts of inputs, so +/// yeah, don’t use it! 😀 +#[derive(Default)] +pub struct TypeIdHasher { + value: u64, +} + +impl Hasher for TypeIdHasher { + #[inline] + fn write(&mut self, bytes: &[u8]) { + // This expects to receive exactly one 64-bit value, and there’s no realistic chance of + // that changing, but I don’t want to depend on something that isn’t expressly part of the + // contract for safety. But I’m OK with release builds putting everything in one bucket + // if it *did* change (and debug builds panicking). + debug_assert_eq!(bytes.len(), 8); + let _ = bytes.try_into().map(|array| self.value = u64::from_ne_bytes(array)); + } + + #[inline] + fn finish(&self) -> u64 { + self.value + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +macro_rules! everything { + ($example_init:literal, $($parent:ident)::+ $(, $entry_generics:ty)?) => { + use core::any::{Any, TypeId}; + use core::hash::BuildHasherDefault; + use core::marker::PhantomData; + + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; + + use ::$($parent)::+::hash_map::{self, HashMap}; + + use crate::any::{Downcast, IntoBox}; + + /// Raw access to the underlying `HashMap`. + /// + /// This alias is provided for convenience because of the ugly third generic parameter. + pub type RawMap = HashMap, BuildHasherDefault>; + + /// A collection containing zero or one values for any given type and allowing convenient, + /// type-safe access to those values. + /// + /// The type parameter `A` allows you to use a different value type; normally you will want + /// it to be `core::any::Any` (also known as `std::any::Any`), but there are other choices: + /// + /// - If you want the entire map to be cloneable, use `CloneAny` instead of `Any`; with + /// that, you can only add types that implement `Clone` to the map. + /// - You can add on `+ Send` or `+ Send + Sync` (e.g. `Map`) to add those + /// auto traits. + /// + /// Cumulatively, there are thus six forms of map: + /// + /// - [Map]<dyn [core::any::Any]>, + /// also spelled [`AnyMap`] for convenience. + /// - [Map]<dyn [core::any::Any] + Send> + /// - [Map]<dyn [core::any::Any] + Send + Sync> + /// - [Map]<dyn [CloneAny]> + /// - [Map]<dyn [CloneAny] + Send> + /// - [Map]<dyn [CloneAny] + Send + Sync> + /// + /// ## Example + /// + /// (Here using the [`AnyMap`] convenience alias; the first line could use + /// [anymap::Map][Map]::<[core::any::Any]>::new() instead if desired.) + /// + /// ```rust + #[doc = $example_init] + /// assert_eq!(data.get(), None::<&i32>); + /// ``` + /// + /// Values containing non-static references are not permitted. + #[derive(Debug)] + pub struct Map { + raw: RawMap, + } + + /// The most common type of `Map`: just using `Any`; [Map]<dyn [Any]>. + /// + /// Why is this a separate type alias rather than a default value for `Map`? + /// `Map::new()` doesn’t seem to be happy to infer that it should go with the default + /// value. It’s a bit sad, really. Ah well, I guess this approach will do. + pub type AnyMap = Map; + impl Default for Map { + #[inline] + fn default() -> Map { + Map::new() + } + } + + impl Map { + /// Create an empty collection. + #[inline] + pub fn new() -> Map { + Map { + raw: RawMap::with_hasher(Default::default()), + } + } + + /// Returns a reference to the value stored in the collection for the type `T`, + /// if it exists. + #[inline] + pub fn get>(&self) -> Option<&T> { + self.raw.get(&TypeId::of::()) + .map(|any| unsafe { any.downcast_ref_unchecked::() }) + } + + /// Gets the entry for the given type in the collection for in-place manipulation + #[inline] + pub fn entry>(&mut self) -> Entry { + match self.raw.entry(TypeId::of::()) { + hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry { + inner: e, + type_: PhantomData, + }), + hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry { + inner: e, + type_: PhantomData, + }), + } + } + + } + + /// A view into a single occupied location in an `Map`. + pub struct OccupiedEntry<'a, A: ?Sized + Downcast, V: 'a> { + inner: hash_map::OccupiedEntry<'a, TypeId, Box, $($entry_generics)?>, + type_: PhantomData, + } + + /// A view into a single empty location in an `Map`. + pub struct VacantEntry<'a, A: ?Sized + Downcast, V: 'a> { + inner: hash_map::VacantEntry<'a, TypeId, Box, $($entry_generics)?>, + type_: PhantomData, + } + + /// A view into a single location in an `Map`, which may be vacant or occupied. + pub enum Entry<'a, A: ?Sized + Downcast, V: 'a> { + /// An occupied Entry + Occupied(OccupiedEntry<'a, A, V>), + /// A vacant Entry + Vacant(VacantEntry<'a, A, V>), + } + + impl<'a, A: ?Sized + Downcast, V: IntoBox> Entry<'a, A, V> { + + + /// Ensures a value is in the entry by inserting the result of the default function if + /// empty, and returns a mutable reference to the value in the entry. + #[inline] + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Entry::Occupied(inner) => inner.into_mut(), + Entry::Vacant(inner) => inner.insert(default()), + } + } + } + + impl<'a, A: ?Sized + Downcast, V: IntoBox> OccupiedEntry<'a, A, V> { + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the collection itself + #[inline] + pub fn into_mut(self) -> &'a mut V { + unsafe { self.inner.into_mut().downcast_mut_unchecked() } + } + } + + impl<'a, A: ?Sized + Downcast, V: IntoBox> VacantEntry<'a, A, V> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it + #[inline] + pub fn insert(self, value: V) -> &'a mut V { + unsafe { self.inner.insert(value.into_box()).downcast_mut_unchecked() } + } + } + + #[cfg(test)] + mod tests { + use crate::CloneAny; + use super::*; + + #[derive(Clone, Debug, PartialEq)] struct A(i32); + #[derive(Clone, Debug, PartialEq)] struct B(i32); + #[derive(Clone, Debug, PartialEq)] struct C(i32); + #[derive(Clone, Debug, PartialEq)] struct D(i32); + #[derive(Clone, Debug, PartialEq)] struct E(i32); + #[derive(Clone, Debug, PartialEq)] struct F(i32); + #[derive(Clone, Debug, PartialEq)] struct J(i32); + + #[test] + fn test_varieties() { + fn assert_send() { } + fn assert_sync() { } + fn assert_debug() { } + assert_send::>(); + assert_send::>(); + assert_sync::>(); + assert_debug::>(); + assert_debug::>(); + assert_debug::>(); + assert_send::>(); + assert_send::>(); + assert_sync::>(); + assert_debug::>(); + assert_debug::>(); + assert_debug::>(); + } + } + }; +} + +#[test] +fn type_id_hasher() { + use core::any::TypeId; + use core::hash::Hash; + fn verify_hashing_with(type_id: TypeId) { + let mut hasher = TypeIdHasher::default(); + type_id.hash(&mut hasher); + // SAFETY: u64 is valid for all bit patterns. + let _ = hasher.finish(); + } + // Pick a variety of types, just to demonstrate it’s all sane. Normal, zero-sized, unsized, &c. + verify_hashing_with(TypeId::of::()); + verify_hashing_with(TypeId::of::<()>()); + verify_hashing_with(TypeId::of::()); + verify_hashing_with(TypeId::of::<&str>()); + verify_hashing_with(TypeId::of::>()); +} + +#[cfg(feature = "std")] +everything!("let mut data = anymap::AnyMap::new();", std::collections); diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 092aab01120..261172ad946 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -12,7 +12,6 @@ rust-version.workspace = true doctest = false [dependencies] -anymap = "1.0.0-beta.2" arrayvec = "0.7.2" bitflags = "2.1.0" cov-mark = "2.0.0-pre.1" @@ -34,6 +33,7 @@ triomphe.workspace = true rustc-dependencies.workspace = true # local deps +anymap.workspace = true stdx.workspace = true intern.workspace = true base-db.workspace = true @@ -45,6 +45,7 @@ cfg.workspace = true tt.workspace = true limit.workspace = true + [dev-dependencies] expect-test = "1.4.0" diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index 8b5c92c6602..fdc710f7c84 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -157,7 +157,6 @@ fn check_licenses() { Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT Apache-2.0/MIT BSD-3-Clause -BlueOak-1.0.0 OR MIT OR Apache-2.0 CC0-1.0 ISC MIT From 00e238e99bdd0f2f8ed285fbb160b3755164cbbf Mon Sep 17 00:00:00 2001 From: shogo-nakano-desu <61229807+shogo-nakano-desu@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:34:14 +0900 Subject: [PATCH 2/6] refactor: remove unnecesary cfg_attr and inline macro --- crates/anymap/src/lib.rs | 400 +++++++++++++++++++-------------------- 1 file changed, 191 insertions(+), 209 deletions(-) diff --git a/crates/anymap/src/lib.rs b/crates/anymap/src/lib.rs index 967c3769ec6..d8c6c889f78 100644 --- a/crates/anymap/src/lib.rs +++ b/crates/anymap/src/lib.rs @@ -10,21 +10,15 @@ //! This crate has two independent features, each of which provides an implementation providing //! types `Map`, `AnyMap`, `OccupiedEntry`, `VacantEntry`, `Entry` and `RawMap`: //! -#![cfg_attr(feature = "std", doc = " - **std** (default, *enabled* in this build):")] -#![cfg_attr(not(feature = "std"), doc = " - **std** (default, *disabled* in this build):")] +//! - **std** (default, *enabled* in this build): //! an implementation using `std::collections::hash_map`, placed in the crate root //! (e.g. `anymap::AnyMap`). //! -#![cfg_attr(feature = "hashbrown", doc = " - **hashbrown** (optional; *enabled* in this build):")] -#![cfg_attr( - not(feature = "hashbrown"), - doc = " - **hashbrown** (optional; *disabled* in this build):" -)] +//! - **hashbrown** (optional; *enabled* in this build): //! an implementation using `alloc` and `hashbrown::hash_map`, placed in a module `hashbrown` //! (e.g. `anymap::hashbrown::AnyMap`). #![warn(missing_docs, unused_results)] -#![cfg_attr(not(feature = "std"), no_std)] use core::convert::TryInto; use core::hash::Hasher; @@ -63,213 +57,201 @@ fn finish(&self) -> u64 { } } -#[cfg(any(feature = "std", feature = "hashbrown"))] -macro_rules! everything { - ($example_init:literal, $($parent:ident)::+ $(, $entry_generics:ty)?) => { - use core::any::{Any, TypeId}; - use core::hash::BuildHasherDefault; - use core::marker::PhantomData; +use core::any::{Any, TypeId}; +use core::hash::BuildHasherDefault; +use core::marker::PhantomData; - #[cfg(not(feature = "std"))] - use alloc::boxed::Box; +use ::std::collections::hash_map::{self, HashMap}; - use ::$($parent)::+::hash_map::{self, HashMap}; +use crate::any::{Downcast, IntoBox}; - use crate::any::{Downcast, IntoBox}; +/// Raw access to the underlying `HashMap`. +/// +/// This alias is provided for convenience because of the ugly third generic parameter. +pub type RawMap = HashMap, BuildHasherDefault>; - /// Raw access to the underlying `HashMap`. - /// - /// This alias is provided for convenience because of the ugly third generic parameter. - pub type RawMap = HashMap, BuildHasherDefault>; - - /// A collection containing zero or one values for any given type and allowing convenient, - /// type-safe access to those values. - /// - /// The type parameter `A` allows you to use a different value type; normally you will want - /// it to be `core::any::Any` (also known as `std::any::Any`), but there are other choices: - /// - /// - If you want the entire map to be cloneable, use `CloneAny` instead of `Any`; with - /// that, you can only add types that implement `Clone` to the map. - /// - You can add on `+ Send` or `+ Send + Sync` (e.g. `Map`) to add those - /// auto traits. - /// - /// Cumulatively, there are thus six forms of map: - /// - /// - [Map]<dyn [core::any::Any]>, - /// also spelled [`AnyMap`] for convenience. - /// - [Map]<dyn [core::any::Any] + Send> - /// - [Map]<dyn [core::any::Any] + Send + Sync> - /// - [Map]<dyn [CloneAny]> - /// - [Map]<dyn [CloneAny] + Send> - /// - [Map]<dyn [CloneAny] + Send + Sync> - /// - /// ## Example - /// - /// (Here using the [`AnyMap`] convenience alias; the first line could use - /// [anymap::Map][Map]::<[core::any::Any]>::new() instead if desired.) - /// - /// ```rust - #[doc = $example_init] - /// assert_eq!(data.get(), None::<&i32>); - /// ``` - /// - /// Values containing non-static references are not permitted. - #[derive(Debug)] - pub struct Map { - raw: RawMap, - } - - /// The most common type of `Map`: just using `Any`; [Map]<dyn [Any]>. - /// - /// Why is this a separate type alias rather than a default value for `Map`? - /// `Map::new()` doesn’t seem to be happy to infer that it should go with the default - /// value. It’s a bit sad, really. Ah well, I guess this approach will do. - pub type AnyMap = Map; - impl Default for Map { - #[inline] - fn default() -> Map { - Map::new() - } - } - - impl Map { - /// Create an empty collection. - #[inline] - pub fn new() -> Map { - Map { - raw: RawMap::with_hasher(Default::default()), - } - } - - /// Returns a reference to the value stored in the collection for the type `T`, - /// if it exists. - #[inline] - pub fn get>(&self) -> Option<&T> { - self.raw.get(&TypeId::of::()) - .map(|any| unsafe { any.downcast_ref_unchecked::() }) - } - - /// Gets the entry for the given type in the collection for in-place manipulation - #[inline] - pub fn entry>(&mut self) -> Entry { - match self.raw.entry(TypeId::of::()) { - hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry { - inner: e, - type_: PhantomData, - }), - hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry { - inner: e, - type_: PhantomData, - }), - } - } - - } - - /// A view into a single occupied location in an `Map`. - pub struct OccupiedEntry<'a, A: ?Sized + Downcast, V: 'a> { - inner: hash_map::OccupiedEntry<'a, TypeId, Box, $($entry_generics)?>, - type_: PhantomData, - } - - /// A view into a single empty location in an `Map`. - pub struct VacantEntry<'a, A: ?Sized + Downcast, V: 'a> { - inner: hash_map::VacantEntry<'a, TypeId, Box, $($entry_generics)?>, - type_: PhantomData, - } - - /// A view into a single location in an `Map`, which may be vacant or occupied. - pub enum Entry<'a, A: ?Sized + Downcast, V: 'a> { - /// An occupied Entry - Occupied(OccupiedEntry<'a, A, V>), - /// A vacant Entry - Vacant(VacantEntry<'a, A, V>), - } - - impl<'a, A: ?Sized + Downcast, V: IntoBox> Entry<'a, A, V> { - - - /// Ensures a value is in the entry by inserting the result of the default function if - /// empty, and returns a mutable reference to the value in the entry. - #[inline] - pub fn or_insert_with V>(self, default: F) -> &'a mut V { - match self { - Entry::Occupied(inner) => inner.into_mut(), - Entry::Vacant(inner) => inner.insert(default()), - } - } - } - - impl<'a, A: ?Sized + Downcast, V: IntoBox> OccupiedEntry<'a, A, V> { - /// Converts the OccupiedEntry into a mutable reference to the value in the entry - /// with a lifetime bound to the collection itself - #[inline] - pub fn into_mut(self) -> &'a mut V { - unsafe { self.inner.into_mut().downcast_mut_unchecked() } - } - } - - impl<'a, A: ?Sized + Downcast, V: IntoBox> VacantEntry<'a, A, V> { - /// Sets the value of the entry with the VacantEntry's key, - /// and returns a mutable reference to it - #[inline] - pub fn insert(self, value: V) -> &'a mut V { - unsafe { self.inner.insert(value.into_box()).downcast_mut_unchecked() } - } - } - - #[cfg(test)] - mod tests { - use crate::CloneAny; - use super::*; - - #[derive(Clone, Debug, PartialEq)] struct A(i32); - #[derive(Clone, Debug, PartialEq)] struct B(i32); - #[derive(Clone, Debug, PartialEq)] struct C(i32); - #[derive(Clone, Debug, PartialEq)] struct D(i32); - #[derive(Clone, Debug, PartialEq)] struct E(i32); - #[derive(Clone, Debug, PartialEq)] struct F(i32); - #[derive(Clone, Debug, PartialEq)] struct J(i32); - - #[test] - fn test_varieties() { - fn assert_send() { } - fn assert_sync() { } - fn assert_debug() { } - assert_send::>(); - assert_send::>(); - assert_sync::>(); - assert_debug::>(); - assert_debug::>(); - assert_debug::>(); - assert_send::>(); - assert_send::>(); - assert_sync::>(); - assert_debug::>(); - assert_debug::>(); - assert_debug::>(); - } - } - }; +/// A collection containing zero or one values for any given type and allowing convenient, +/// type-safe access to those values. +/// +/// The type parameter `A` allows you to use a different value type; normally you will want +/// it to be `core::any::Any` (also known as `std::any::Any`), but there are other choices: +/// +/// - If you want the entire map to be cloneable, use `CloneAny` instead of `Any`; with +/// that, you can only add types that implement `Clone` to the map. +/// - You can add on `+ Send` or `+ Send + Sync` (e.g. `Map`) to add those +/// auto traits. +/// +/// Cumulatively, there are thus six forms of map: +/// +/// - [Map]<dyn [core::any::Any]>, +/// also spelled [`AnyMap`] for convenience. +/// - [Map]<dyn [core::any::Any] + Send> +/// - [Map]<dyn [core::any::Any] + Send + Sync> +/// - [Map]<dyn [CloneAny]> +/// - [Map]<dyn [CloneAny] + Send> +/// - [Map]<dyn [CloneAny] + Send + Sync> +/// +/// ## Example +/// +/// (Here using the [`AnyMap`] convenience alias; the first line could use +/// [anymap::Map][Map]::<[core::any::Any]>::new() instead if desired.) +/// +/// ```rust +#[doc = "let mut data = anymap::AnyMap::new();"] +/// assert_eq!(data.get(), None::<&i32>); +/// ``` +/// +/// Values containing non-static references are not permitted. +#[derive(Debug)] +pub struct Map { + raw: RawMap, } -#[test] -fn type_id_hasher() { - use core::any::TypeId; - use core::hash::Hash; - fn verify_hashing_with(type_id: TypeId) { - let mut hasher = TypeIdHasher::default(); - type_id.hash(&mut hasher); - // SAFETY: u64 is valid for all bit patterns. - let _ = hasher.finish(); +/// The most common type of `Map`: just using `Any`; [Map]<dyn [Any]>. +/// +/// Why is this a separate type alias rather than a default value for `Map`? +/// `Map::new()` doesn’t seem to be happy to infer that it should go with the default +/// value. It’s a bit sad, really. Ah well, I guess this approach will do. +pub type AnyMap = Map; +impl Default for Map { + #[inline] + fn default() -> Map { + Map::new() } - // Pick a variety of types, just to demonstrate it’s all sane. Normal, zero-sized, unsized, &c. - verify_hashing_with(TypeId::of::()); - verify_hashing_with(TypeId::of::<()>()); - verify_hashing_with(TypeId::of::()); - verify_hashing_with(TypeId::of::<&str>()); - verify_hashing_with(TypeId::of::>()); } -#[cfg(feature = "std")] -everything!("let mut data = anymap::AnyMap::new();", std::collections); +impl Map { + /// Create an empty collection. + #[inline] + pub fn new() -> Map { + Map { raw: RawMap::with_hasher(Default::default()) } + } + + /// Returns a reference to the value stored in the collection for the type `T`, + /// if it exists. + #[inline] + pub fn get>(&self) -> Option<&T> { + self.raw.get(&TypeId::of::()).map(|any| unsafe { any.downcast_ref_unchecked::() }) + } + + /// Gets the entry for the given type in the collection for in-place manipulation + #[inline] + pub fn entry>(&mut self) -> Entry { + match self.raw.entry(TypeId::of::()) { + hash_map::Entry::Occupied(e) => { + Entry::Occupied(OccupiedEntry { inner: e, type_: PhantomData }) + } + hash_map::Entry::Vacant(e) => { + Entry::Vacant(VacantEntry { inner: e, type_: PhantomData }) + } + } + } +} + +/// A view into a single occupied location in an `Map`. +pub struct OccupiedEntry<'a, A: ?Sized + Downcast, V: 'a> { + inner: hash_map::OccupiedEntry<'a, TypeId, Box>, + type_: PhantomData, +} + +/// A view into a single empty location in an `Map`. +pub struct VacantEntry<'a, A: ?Sized + Downcast, V: 'a> { + inner: hash_map::VacantEntry<'a, TypeId, Box>, + type_: PhantomData, +} + +/// A view into a single location in an `Map`, which may be vacant or occupied. +pub enum Entry<'a, A: ?Sized + Downcast, V: 'a> { + /// An occupied Entry + Occupied(OccupiedEntry<'a, A, V>), + /// A vacant Entry + Vacant(VacantEntry<'a, A, V>), +} + +impl<'a, A: ?Sized + Downcast, V: IntoBox> Entry<'a, A, V> { + /// Ensures a value is in the entry by inserting the result of the default function if + /// empty, and returns a mutable reference to the value in the entry. + #[inline] + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Entry::Occupied(inner) => inner.into_mut(), + Entry::Vacant(inner) => inner.insert(default()), + } + } +} + +impl<'a, A: ?Sized + Downcast, V: IntoBox> OccupiedEntry<'a, A, V> { + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the collection itself + #[inline] + pub fn into_mut(self) -> &'a mut V { + unsafe { self.inner.into_mut().downcast_mut_unchecked() } + } +} + +impl<'a, A: ?Sized + Downcast, V: IntoBox> VacantEntry<'a, A, V> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it + #[inline] + pub fn insert(self, value: V) -> &'a mut V { + unsafe { self.inner.insert(value.into_box()).downcast_mut_unchecked() } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::CloneAny; + + #[derive(Clone, Debug, PartialEq)] + struct A(i32); + #[derive(Clone, Debug, PartialEq)] + struct B(i32); + #[derive(Clone, Debug, PartialEq)] + struct C(i32); + #[derive(Clone, Debug, PartialEq)] + struct D(i32); + #[derive(Clone, Debug, PartialEq)] + struct E(i32); + #[derive(Clone, Debug, PartialEq)] + struct F(i32); + #[derive(Clone, Debug, PartialEq)] + struct J(i32); + + #[test] + fn test_varieties() { + fn assert_send() {} + fn assert_sync() {} + fn assert_debug() {} + assert_send::>(); + assert_send::>(); + assert_sync::>(); + assert_debug::>(); + assert_debug::>(); + assert_debug::>(); + assert_send::>(); + assert_send::>(); + assert_sync::>(); + assert_debug::>(); + assert_debug::>(); + assert_debug::>(); + } + + #[test] + fn type_id_hasher() { + use core::any::TypeId; + use core::hash::Hash; + fn verify_hashing_with(type_id: TypeId) { + let mut hasher = TypeIdHasher::default(); + type_id.hash(&mut hasher); + // SAFETY: u64 is valid for all bit patterns. + let _ = hasher.finish(); + } + // Pick a variety of types, just to demonstrate it’s all sane. Normal, zero-sized, unsized, &c. + verify_hashing_with(TypeId::of::()); + verify_hashing_with(TypeId::of::<()>()); + verify_hashing_with(TypeId::of::()); + verify_hashing_with(TypeId::of::<&str>()); + verify_hashing_with(TypeId::of::>()); + } +} From f671b0b8644aff682616025af5e1a1780ffab0c3 Mon Sep 17 00:00:00 2001 From: shogo-nakano-desu <61229807+shogo-nakano-desu@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:48:15 +0900 Subject: [PATCH 3/6] refactor: move implementation inside anymap crate into stdx crate --- Cargo.lock | 9 +- Cargo.toml | 1 - crates/anymap/Cargo.toml | 20 --- crates/anymap/src/any.rs | 134 ---------------- crates/hir-def/Cargo.toml | 1 - crates/hir-def/src/dyn_map.rs | 2 +- crates/stdx/Cargo.toml | 1 + .../{anymap/src/lib.rs => stdx/src/anymap.rs} | 145 ++++++++++++++++-- crates/stdx/src/lib.rs | 1 + 9 files changed, 140 insertions(+), 174 deletions(-) delete mode 100644 crates/anymap/Cargo.toml delete mode 100644 crates/anymap/src/any.rs rename crates/{anymap/src/lib.rs => stdx/src/anymap.rs} (63%) diff --git a/Cargo.lock b/Cargo.lock index 6b0f7efc6ca..5dcf68e0ce7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,13 +49,6 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" -[[package]] -name = "anymap" -version = "0.0.0" -dependencies = [ - "hashbrown 0.14.0", -] - [[package]] name = "arbitrary" version = "1.3.0" @@ -540,7 +533,6 @@ dependencies = [ name = "hir-def" version = "0.0.0" dependencies = [ - "anymap", "arrayvec", "base-db", "bitflags 2.3.2", @@ -1831,6 +1823,7 @@ dependencies = [ "always-assert", "backtrace", "crossbeam-channel", + "hashbrown 0.14.0", "jod-thread", "libc", "miow", diff --git a/Cargo.toml b/Cargo.toml index 0b3ec8ed000..dd4348785df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,6 @@ debug = 0 [workspace.dependencies] # local crates -anymap = { path = "./crates/anymap", version = "0.0.0" } base-db = { path = "./crates/base-db", version = "0.0.0" } cfg = { path = "./crates/cfg", version = "0.0.0" } flycheck = { path = "./crates/flycheck", version = "0.0.0" } diff --git a/crates/anymap/Cargo.toml b/crates/anymap/Cargo.toml deleted file mode 100644 index 5f3976edcbd..00000000000 --- a/crates/anymap/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "anymap" -version = "0.0.0" -description = "This crate is a port of only the necessary features from https://github.com/chris-morgan/anymap for use within rust-analyzer. Copyright © 2014–2022 Chris Morgan. COPYING: https://github.com/chris-morgan/anymap/blob/master/COPYING" - -authors.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -[package.metadata.docs.rs] -all-features = true - -[features] -default = ["std"] -std = [] - -[dependencies] -# The hashbrown feature, disabled by default, is exposed under different stability guarantees than the usual SemVer ones: by preference the version range will only be extended, but it may be shrunk in a MINOR release. See README.md. -hashbrown = { version = "0.14.0", optional = true } diff --git a/crates/anymap/src/any.rs b/crates/anymap/src/any.rs deleted file mode 100644 index 1e205e7c9d1..00000000000 --- a/crates/anymap/src/any.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! Copyright © 2014–2022 Chris Morgan -//! https://github.com/chris-morgan/anymap/blob/master/COPYING -//! impl some traits for dyn Any -use core::any::{Any, TypeId}; -use core::fmt; - -#[doc(hidden)] -pub trait CloneToAny { - /// Clone `self` into a new `Box` object. - fn clone_to_any(&self) -> Box; -} - -impl CloneToAny for T { - #[inline] - fn clone_to_any(&self) -> Box { - Box::new(self.clone()) - } -} - -macro_rules! impl_clone { - ($t:ty) => { - impl Clone for Box<$t> { - #[inline] - fn clone(&self) -> Box<$t> { - // SAFETY: this dance is to reapply any Send/Sync marker. I’m not happy about this - // approach, given that I used to do it in safe code, but then came a dodgy - // future-compatibility warning where_clauses_object_safety, which is spurious for - // auto traits but still super annoying (future-compatibility lints seem to mean - // your bin crate needs a corresponding allow!). Although I explained my plight¹ - // and it was all explained and agreed upon, no action has been taken. So I finally - // caved and worked around it by doing it this way, which matches what’s done for - // core::any², so it’s probably not *too* bad. - // - // ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013 - // ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616 - let clone: Box = (**self).clone_to_any(); - let raw: *mut dyn CloneAny = Box::into_raw(clone); - unsafe { Box::from_raw(raw as *mut $t) } - } - } - - impl fmt::Debug for $t { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad(stringify!($t)) - } - } - }; -} - -/// Methods for downcasting from an `Any`-like trait object. -/// -/// This should only be implemented on trait objects for subtraits of `Any`, though you can -/// implement it for other types and it’ll work fine, so long as your implementation is correct. -pub trait Downcast { - /// Gets the `TypeId` of `self`. - fn type_id(&self) -> TypeId; - - // Note the bound through these downcast methods is 'static, rather than the inexpressible - // concept of Self-but-as-a-trait (where Self is `dyn Trait`). This is sufficient, exceeding - // TypeId’s requirements. Sure, you *can* do CloneAny.downcast_unchecked::() and the - // type system won’t protect you, but that doesn’t introduce any unsafety: the method is - // already unsafe because you can specify the wrong type, and if this were exposing safe - // downcasting, CloneAny.downcast::() would just return an error, which is just as - // correct. - // - // Now in theory we could also add T: ?Sized, but that doesn’t play nicely with the common - // implementation, so I’m doing without it. - - /// Downcast from `&Any` to `&T`, without checking the type matches. - /// - /// # Safety - /// - /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. - unsafe fn downcast_ref_unchecked(&self) -> &T; - - /// Downcast from `&mut Any` to `&mut T`, without checking the type matches. - /// - /// # Safety - /// - /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. - unsafe fn downcast_mut_unchecked(&mut self) -> &mut T; -} - -/// A trait for the conversion of an object into a boxed trait object. -pub trait IntoBox: Any { - /// Convert self into the appropriate boxed form. - fn into_box(self) -> Box; -} - -macro_rules! implement { - ($any_trait:ident $(+ $auto_traits:ident)*) => { - impl Downcast for dyn $any_trait $(+ $auto_traits)* { - #[inline] - fn type_id(&self) -> TypeId { - self.type_id() - } - - #[inline] - unsafe fn downcast_ref_unchecked(&self) -> &T { - &*(self as *const Self as *const T) - } - - #[inline] - unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { - &mut *(self as *mut Self as *mut T) - } - } - - impl IntoBox for T { - #[inline] - fn into_box(self) -> Box { - Box::new(self) - } - } - } -} - -implement!(Any); -implement!(Any + Send); -implement!(Any + Send + Sync); - -/// [`Any`], but with cloning. -/// -/// Every type with no non-`'static` references that implements `Clone` implements `CloneAny`. -/// See [`core::any`] for more details on `Any` in general. -pub trait CloneAny: Any + CloneToAny {} -impl CloneAny for T {} -implement!(CloneAny); -implement!(CloneAny + Send); -implement!(CloneAny + Send + Sync); -impl_clone!(dyn CloneAny); -impl_clone!(dyn CloneAny + Send); -impl_clone!(dyn CloneAny + Send + Sync); diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 261172ad946..99b8e9bf0e1 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -33,7 +33,6 @@ triomphe.workspace = true rustc-dependencies.workspace = true # local deps -anymap.workspace = true stdx.workspace = true intern.workspace = true base-db.workspace = true diff --git a/crates/hir-def/src/dyn_map.rs b/crates/hir-def/src/dyn_map.rs index 63138aa6ad7..a59bbf7e221 100644 --- a/crates/hir-def/src/dyn_map.rs +++ b/crates/hir-def/src/dyn_map.rs @@ -29,8 +29,8 @@ ops::{Index, IndexMut}, }; -use anymap::Map; use rustc_hash::FxHashMap; +use stdx::anymap::Map; pub struct Key { _phantom: PhantomData<(K, V, P)>, diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index 536f000a44b..fae16fa2e42 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -17,6 +17,7 @@ backtrace = { version = "0.3.67", optional = true } always-assert = { version = "0.1.2", features = ["log"] } jod-thread = "0.1.2" crossbeam-channel = "0.5.5" +hashbrown = { version = "0.14.0" } # Think twice before adding anything here [target.'cfg(windows)'.dependencies] diff --git a/crates/anymap/src/lib.rs b/crates/stdx/src/anymap.rs similarity index 63% rename from crates/anymap/src/lib.rs rename to crates/stdx/src/anymap.rs index d8c6c889f78..0f4ae0a440c 100644 --- a/crates/anymap/src/lib.rs +++ b/crates/stdx/src/anymap.rs @@ -1,5 +1,7 @@ //! Copyright © 2014–2022 Chris Morgan //! https://github.com/chris-morgan/anymap/blob/master/COPYING +//! Copyright © 2014–2022 Chris Morgan +//! https://github.com/chris-morgan/anymap/blob/master/COPYING //! //! This crate provides a safe and convenient store for one value of each type. //! @@ -23,10 +25,6 @@ use core::convert::TryInto; use core::hash::Hasher; -pub use crate::any::CloneAny; - -mod any; - /// A hasher designed to eke a little more speed out, given `TypeId`’s known characteristics. /// /// Specifically, this is a no-op hasher that expects to be fed a u64’s worth of @@ -63,8 +61,6 @@ fn finish(&self) -> u64 { use ::std::collections::hash_map::{self, HashMap}; -use crate::any::{Downcast, IntoBox}; - /// Raw access to the underlying `HashMap`. /// /// This alias is provided for convenience because of the ugly third generic parameter. @@ -136,7 +132,7 @@ pub fn get>(&self) -> Option<&T> { /// Gets the entry for the given type in the collection for in-place manipulation #[inline] - pub fn entry>(&mut self) -> Entry { + pub fn entry>(&mut self) -> Entry<'_, A, T> { match self.raw.entry(TypeId::of::()) { hash_map::Entry::Occupied(e) => { Entry::Occupied(OccupiedEntry { inner: e, type_: PhantomData }) @@ -161,7 +157,7 @@ pub struct VacantEntry<'a, A: ?Sized + Downcast, V: 'a> { } /// A view into a single location in an `Map`, which may be vacant or occupied. -pub enum Entry<'a, A: ?Sized + Downcast, V: 'a> { +pub enum Entry<'a, A: ?Sized + Downcast, V> { /// An occupied Entry Occupied(OccupiedEntry<'a, A, V>), /// A vacant Entry @@ -201,7 +197,6 @@ pub fn insert(self, value: V) -> &'a mut V { #[cfg(test)] mod tests { use super::*; - use crate::CloneAny; #[derive(Clone, Debug, PartialEq)] struct A(i32); @@ -255,3 +250,135 @@ fn verify_hashing_with(type_id: TypeId) { verify_hashing_with(TypeId::of::>()); } } + +// impl some traits for dyn Any +use core::fmt; + +#[doc(hidden)] +pub trait CloneToAny { + /// Clone `self` into a new `Box` object. + fn clone_to_any(&self) -> Box; +} + +impl CloneToAny for T { + #[inline] + fn clone_to_any(&self) -> Box { + Box::new(self.clone()) + } +} + +macro_rules! impl_clone { + ($t:ty) => { + impl Clone for Box<$t> { + #[inline] + fn clone(&self) -> Box<$t> { + // SAFETY: this dance is to reapply any Send/Sync marker. I’m not happy about this + // approach, given that I used to do it in safe code, but then came a dodgy + // future-compatibility warning where_clauses_object_safety, which is spurious for + // auto traits but still super annoying (future-compatibility lints seem to mean + // your bin crate needs a corresponding allow!). Although I explained my plight¹ + // and it was all explained and agreed upon, no action has been taken. So I finally + // caved and worked around it by doing it this way, which matches what’s done for + // core::any², so it’s probably not *too* bad. + // + // ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013 + // ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616 + let clone: Box = (**self).clone_to_any(); + let raw: *mut dyn CloneAny = Box::into_raw(clone); + unsafe { Box::from_raw(raw as *mut $t) } + } + } + + impl fmt::Debug for $t { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad(stringify!($t)) + } + } + }; +} + +/// Methods for downcasting from an `Any`-like trait object. +/// +/// This should only be implemented on trait objects for subtraits of `Any`, though you can +/// implement it for other types and it’ll work fine, so long as your implementation is correct. +pub trait Downcast { + /// Gets the `TypeId` of `self`. + fn type_id(&self) -> TypeId; + + // Note the bound through these downcast methods is 'static, rather than the inexpressible + // concept of Self-but-as-a-trait (where Self is `dyn Trait`). This is sufficient, exceeding + // TypeId’s requirements. Sure, you *can* do CloneAny.downcast_unchecked::() and the + // type system won’t protect you, but that doesn’t introduce any unsafety: the method is + // already unsafe because you can specify the wrong type, and if this were exposing safe + // downcasting, CloneAny.downcast::() would just return an error, which is just as + // correct. + // + // Now in theory we could also add T: ?Sized, but that doesn’t play nicely with the common + // implementation, so I’m doing without it. + + /// Downcast from `&Any` to `&T`, without checking the type matches. + /// + /// # Safety + /// + /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. + unsafe fn downcast_ref_unchecked(&self) -> &T; + + /// Downcast from `&mut Any` to `&mut T`, without checking the type matches. + /// + /// # Safety + /// + /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. + unsafe fn downcast_mut_unchecked(&mut self) -> &mut T; +} + +/// A trait for the conversion of an object into a boxed trait object. +pub trait IntoBox: Any { + /// Convert self into the appropriate boxed form. + fn into_box(self) -> Box; +} + +macro_rules! implement { + ($any_trait:ident $(+ $auto_traits:ident)*) => { + impl Downcast for dyn $any_trait $(+ $auto_traits)* { + #[inline] + fn type_id(&self) -> TypeId { + self.type_id() + } + + #[inline] + unsafe fn downcast_ref_unchecked(&self) -> &T { + &*(self as *const Self as *const T) + } + + #[inline] + unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { + &mut *(self as *mut Self as *mut T) + } + } + + impl IntoBox for T { + #[inline] + fn into_box(self) -> Box { + Box::new(self) + } + } + } +} + +implement!(Any); +implement!(Any + Send); +implement!(Any + Send + Sync); + +/// [`Any`], but with cloning. +/// +/// Every type with no non-`'static` references that implements `Clone` implements `CloneAny`. +/// See [`core::any`] for more details on `Any` in general. +pub trait CloneAny: Any + CloneToAny {} +impl CloneAny for T {} +implement!(CloneAny); +implement!(CloneAny + Send); +implement!(CloneAny + Send + Sync); +impl_clone!(dyn CloneAny); +impl_clone!(dyn CloneAny + Send); +impl_clone!(dyn CloneAny + Send + Sync); diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 24990d6a0e7..1e9a54e996b 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -12,6 +12,7 @@ pub mod non_empty_vec; pub mod rand; pub mod thread; +pub mod anymap; pub use always_assert::{always, never}; From 62121827390ae06bd8af9594459629cfc5a49f8d Mon Sep 17 00:00:00 2001 From: shogo-nakano-desu <61229807+shogo-nakano-desu@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:59:07 +0900 Subject: [PATCH 4/6] refactor: remove hashbrown deps since we can use std --- Cargo.lock | 22 ---------------------- crates/stdx/Cargo.toml | 1 - crates/stdx/src/anymap.rs | 4 ---- 3 files changed, 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5dcf68e0ce7..72aaefb3275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,23 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - [[package]] name = "always-assert" version = "0.1.3" @@ -475,10 +458,6 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "heck" @@ -1823,7 +1802,6 @@ dependencies = [ "always-assert", "backtrace", "crossbeam-channel", - "hashbrown 0.14.0", "jod-thread", "libc", "miow", diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index fae16fa2e42..536f000a44b 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -17,7 +17,6 @@ backtrace = { version = "0.3.67", optional = true } always-assert = { version = "0.1.2", features = ["log"] } jod-thread = "0.1.2" crossbeam-channel = "0.5.5" -hashbrown = { version = "0.14.0" } # Think twice before adding anything here [target.'cfg(windows)'.dependencies] diff --git a/crates/stdx/src/anymap.rs b/crates/stdx/src/anymap.rs index 0f4ae0a440c..0165235009a 100644 --- a/crates/stdx/src/anymap.rs +++ b/crates/stdx/src/anymap.rs @@ -15,10 +15,6 @@ //! - **std** (default, *enabled* in this build): //! an implementation using `std::collections::hash_map`, placed in the crate root //! (e.g. `anymap::AnyMap`). -//! -//! - **hashbrown** (optional; *enabled* in this build): -//! an implementation using `alloc` and `hashbrown::hash_map`, placed in a module `hashbrown` -//! (e.g. `anymap::hashbrown::AnyMap`). #![warn(missing_docs, unused_results)] From 2b891ca084f38aa88842cd8c568913adbf0e88a2 Mon Sep 17 00:00:00 2001 From: shogo-nakano-desu <61229807+shogo-nakano-desu@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:07:58 +0900 Subject: [PATCH 5/6] chore: add comments to mention anymap is a port from another repo --- crates/stdx/src/anymap.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/stdx/src/anymap.rs b/crates/stdx/src/anymap.rs index 0165235009a..fd44e6c6d0f 100644 --- a/crates/stdx/src/anymap.rs +++ b/crates/stdx/src/anymap.rs @@ -1,15 +1,14 @@ -//! Copyright © 2014–2022 Chris Morgan -//! https://github.com/chris-morgan/anymap/blob/master/COPYING -//! Copyright © 2014–2022 Chris Morgan -//! https://github.com/chris-morgan/anymap/blob/master/COPYING +//! This file is a port of only the necessary features from https://github.com/chris-morgan/anymap version 1.0.0-beta.2 for use within rust-analyzer. +//! Copyright © 2014–2022 Chris Morgan. COPYING: https://github.com/chris-morgan/anymap/blob/master/COPYING" +//! Note that the license is changed from Blue Oak Model 1.0.0 or MIT or Apache-2.0 to MIT OR Apache-2.0 //! -//! This crate provides a safe and convenient store for one value of each type. +//! This implementation provides a safe and convenient store for one value of each type. //! //! Your starting point is [`Map`]. It has an example. //! //! # Cargo features //! -//! This crate has two independent features, each of which provides an implementation providing +//! This implementation has two independent features, each of which provides an implementation providing //! types `Map`, `AnyMap`, `OccupiedEntry`, `VacantEntry`, `Entry` and `RawMap`: //! //! - **std** (default, *enabled* in this build): From 1e11a55f98d9b3f1f7ac2a6779e10b060fe3c896 Mon Sep 17 00:00:00 2001 From: shogo-nakano-desu <61229807+shogo-nakano-desu@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:55:52 +0900 Subject: [PATCH 6/6] refactor: remove unnecesary deps that are blended in when rebase --- Cargo.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd4348785df..c382a5a37d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,10 +105,3 @@ triomphe = { version = "0.1.8", default-features = false, features = ["std"] } hashbrown = { version = "0.12.3", features = [ "inline-more", ], default-features = false } - -rustc_lexer = { version = "0.10.0", package = "ra-ap-rustc_lexer" } -rustc_parse_format = { version = "0.10.0", package = "ra-ap-rustc_parse_format", default-features = false } - -# Upstream broke this for us so we can't update it -rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } -rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }