Skip to content

Commit

Permalink
Require Cargo feature opt-in for min_specialize
Browse files Browse the repository at this point in the history
This commit switches away from implicitly enabling the `min_specialize`
Rust nightly feature whenever a Nightly compiler is used to instead
requiring an explicit opt-in with a new Cargo feature
`nightly-specialize`. The goal of this commit is to fix tkaitchuck#238 and has two
primary motivations:

1. In tkaitchuck#238 I'm trying to build something that depends on this crate as
   part of the Rust bootstrap process but this crate fails to build due
   to `min_specialize` not being allowed but a nightly compiler is in
   use. This is due to the fact that the way `-Zallow-features` is
   managed in the bootstrap is different than the standard Cargo way of
   doing so.

2. This removes a failure mode where if one day the `min_specialize`
   feature changes this crate won't break when built on nightly. Users
   of Nightly compilers will be able to continue using this crate if the
   feature was not explicitly opted-in to.
  • Loading branch information
alexcrichton committed Jul 8, 2024
1 parent 7d5c661 commit 0f0fb48
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 51 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ jobs:
run: cargo check --features serde
- name: test serde
run: cargo test --features serde
- name: check specialize
run: cargo check --features nightly-specialize
linux_arm7:
name: Linux ARMv7
runs-on: ubuntu-latest
Expand Down Expand Up @@ -153,4 +155,4 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- run: cargo build --manifest-path=no_std_test/Cargo.toml
- run: cargo build --manifest-path=no_std_test/Cargo.toml
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ atomic-polyfill = [ "dep:portable-atomic", "once_cell/critical-section"]
# Nightly-only support for AES intrinsics on 32-bit ARM
nightly-arm-aes = []

# Nightly-only support for impls that work with `min_specialization`
nightly-specialize = []

[[bench]]
name = "ahash"
path = "tests/bench.rs"
Expand Down Expand Up @@ -75,9 +78,6 @@ lto = 'fat'
debug-assertions = false
codegen-units = 1

[build-dependencies]
version_check = "0.9.4"

[dependencies]
const-random = { version = "0.1.17", optional = true }
serde = { version = "1.0.117", optional = true }
Expand Down
3 changes: 0 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ use std::env;

fn main() {
println!("cargo:rerun-if-changed=build.rs");
if let Some(true) = version_check::supports_feature("specialize") {
println!("cargo:rustc-cfg=feature=\"specialize\"");
}
let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH was not set");
if arch.eq_ignore_ascii_case("x86_64")
|| arch.eq_ignore_ascii_case("aarch64")
Expand Down
14 changes: 7 additions & 7 deletions src/aes_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl AHasher {
}

#[inline]
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
fn short_finish(&self) -> u64 {
let combined = aesenc(self.sum, self.enc);
let result: [u64; 2] = aesdec(combined, combined).convert();
Expand Down Expand Up @@ -214,14 +214,14 @@ impl Hasher for AHasher {
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
pub(crate) struct AHasherU64 {
pub(crate) buffer: u64,
pub(crate) pad: u64,
}

/// A specialized hasher for only primitives under 64 bits.
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl Hasher for AHasherU64 {
#[inline]
fn finish(&self) -> u64 {
Expand Down Expand Up @@ -264,11 +264,11 @@ impl Hasher for AHasherU64 {
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
pub(crate) struct AHasherFixed(pub AHasher);

/// A specialized hasher for fixed size primitives larger than 64 bits.
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl Hasher for AHasherFixed {
#[inline]
fn finish(&self) -> u64 {
Expand Down Expand Up @@ -311,12 +311,12 @@ impl Hasher for AHasherFixed {
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
pub(crate) struct AHasherStr(pub AHasher);

/// A specialized hasher for strings
/// Note that the other types don't panic because the hash impl for String tacks on an unneeded call. (As does vec)
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl Hasher for AHasherStr {
#[inline]
fn finish(&self) -> u64 {
Expand Down
14 changes: 7 additions & 7 deletions src/fallback_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl AHasher {
}

#[inline]
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
fn short_finish(&self) -> u64 {
folded_multiply(self.buffer, self.pad)
}
Expand Down Expand Up @@ -199,14 +199,14 @@ impl Hasher for AHasher {
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
pub(crate) struct AHasherU64 {
pub(crate) buffer: u64,
pub(crate) pad: u64,
}

/// A specialized hasher for only primitives under 64 bits.
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl Hasher for AHasherU64 {
#[inline]
fn finish(&self) -> u64 {
Expand Down Expand Up @@ -250,11 +250,11 @@ impl Hasher for AHasherU64 {
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
pub(crate) struct AHasherFixed(pub AHasher);

/// A specialized hasher for fixed size primitives larger than 64 bits.
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl Hasher for AHasherFixed {
#[inline]
fn finish(&self) -> u64 {
Expand Down Expand Up @@ -297,12 +297,12 @@ impl Hasher for AHasherFixed {
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
pub(crate) struct AHasherStr(pub AHasher);

/// A specialized hasher for a single string
/// Note that the other types don't panic because the hash impl for String tacks on an unneeded call. (As does vec)
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl Hasher for AHasherStr {
#[inline]
fn finish(&self) -> u64 {
Expand Down
4 changes: 2 additions & 2 deletions src/hash_quality_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ mod fallback_tests {
#[test]
fn fallback_keys_affect_every_byte() {
//For fallback second key is not used in every hash.
#[cfg(all(not(feature = "specialize"), feature = "folded_multiply"))]
#[cfg(all(not(feature = "nightly-specialize"), feature = "folded_multiply"))]
test_keys_affect_every_byte(0, |a, b| AHasher::new_with_keys(a ^ b, a));
test_keys_affect_every_byte("", |a, b| AHasher::new_with_keys(a ^ b, a));
test_keys_affect_every_byte((0, 0), |a, b| AHasher::new_with_keys(a ^ b, a));
Expand Down Expand Up @@ -504,7 +504,7 @@ mod aes_tests {

#[test]
fn aes_keys_affect_every_byte() {
#[cfg(not(feature = "specialize"))]
#[cfg(not(feature = "nightly-specialize"))]
test_keys_affect_every_byte(0, AHasher::test_with_keys);
test_keys_affect_every_byte("", AHasher::test_with_keys);
test_keys_affect_every_byte((0, 0), AHasher::test_with_keys);
Expand Down
14 changes: 7 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Note the import of [HashMapExt]. This is needed for the constructor.
#![deny(clippy::correctness, clippy::complexity, clippy::perf)]
#![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
#![cfg_attr(feature = "specialize", feature(min_specialization))]
#![cfg_attr(feature = "nightly-specialize", feature(min_specialization))]
#![cfg_attr(feature = "nightly-arm-aes", feature(stdarch_arm_neon_intrinsics))]

#[macro_use]
Expand Down Expand Up @@ -262,42 +262,42 @@ pub(crate) trait BuildHasherExt: BuildHasher {

impl<B: BuildHasher> BuildHasherExt for B {
#[inline]
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
default fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
let mut hasher = self.build_hasher();
value.hash(&mut hasher);
hasher.finish()
}
#[inline]
#[cfg(not(feature = "specialize"))]
#[cfg(not(feature = "nightly-specialize"))]
fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
let mut hasher = self.build_hasher();
value.hash(&mut hasher);
hasher.finish()
}
#[inline]
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
default fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64 {
let mut hasher = self.build_hasher();
value.hash(&mut hasher);
hasher.finish()
}
#[inline]
#[cfg(not(feature = "specialize"))]
#[cfg(not(feature = "nightly-specialize"))]
fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64 {
let mut hasher = self.build_hasher();
value.hash(&mut hasher);
hasher.finish()
}
#[inline]
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
default fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64 {
let mut hasher = self.build_hasher();
value.hash(&mut hasher);
hasher.finish()
}
#[inline]
#[cfg(not(feature = "specialize"))]
#[cfg(not(feature = "nightly-specialize"))]
fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64 {
let mut hasher = self.build_hasher();
value.hash(&mut hasher);
Expand Down
6 changes: 3 additions & 3 deletions src/random_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ cfg_if::cfg_if! {
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "specialize")]{
if #[cfg(feature = "nightly-specialize")]{
use crate::BuildHasherExt;
}
}
Expand Down Expand Up @@ -458,14 +458,14 @@ impl BuildHasher for RandomState {
/// implementation of [`Hash`]. The way to create a combined hash of
/// multiple values is to call [`Hash::hash`] multiple times using the same
/// [`Hasher`], not to call this method repeatedly and combine the results.
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
#[inline]
fn hash_one<T: Hash>(&self, x: T) -> u64 {
RandomState::hash_one(self, x)
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl BuildHasherExt for RandomState {
#[inline]
fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
Expand Down
28 changes: 14 additions & 14 deletions src/specialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std as alloc;

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
use crate::BuildHasherExt;
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
use alloc::string::String;
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
use alloc::vec::Vec;

/// Provides a way to get an optimized hasher for a given data type.
Expand All @@ -21,7 +21,7 @@ pub(crate) trait CallHasher {
fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64;
}

#[cfg(not(feature = "specialize"))]
#[cfg(not(feature = "nightly-specialize"))]
impl<T> CallHasher for T
where
T: Hash + ?Sized,
Expand All @@ -34,7 +34,7 @@ where
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl<T> CallHasher for T
where
T: Hash + ?Sized,
Expand All @@ -49,7 +49,7 @@ where

macro_rules! call_hasher_impl_u64 {
($typ:ty) => {
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl CallHasher for $typ {
#[inline]
fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
Expand Down Expand Up @@ -77,7 +77,7 @@ call_hasher_impl_u64!(&i64);

macro_rules! call_hasher_impl_fixed_length{
($typ:ty) => {
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl CallHasher for $typ {
#[inline]
fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
Expand All @@ -96,31 +96,31 @@ call_hasher_impl_fixed_length!(&i128);
call_hasher_impl_fixed_length!(&usize);
call_hasher_impl_fixed_length!(&isize);

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl CallHasher for [u8] {
#[inline]
fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
build_hasher.hash_as_str(value)
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl CallHasher for Vec<u8> {
#[inline]
fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
build_hasher.hash_as_str(value)
}
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
impl CallHasher for str {
#[inline]
fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
build_hasher.hash_as_str(value)
}
}

#[cfg(all(feature = "specialize"))]
#[cfg(all(feature = "nightly-specialize"))]
impl CallHasher for String {
#[inline]
fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
Expand All @@ -134,7 +134,7 @@ mod test {
use crate::*;

#[test]
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
pub fn test_specialized_invoked() {
let build_hasher = RandomState::with_seeds(1, 2, 3, 4);
let shortened = u64::get_hash(&0, &build_hasher);
Expand Down Expand Up @@ -186,7 +186,7 @@ mod test {
str::get_hash(&"test", &build_hasher),
String::get_hash(&"test".to_string(), &build_hasher)
);
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
assert_eq!(
str::get_hash(&"test", &build_hasher),
<[u8]>::get_hash("test".as_bytes(), &build_hasher)
Expand All @@ -206,7 +206,7 @@ mod test {
str::get_hash(&&"test", &build_hasher),
String::get_hash(&"test".to_string(), &build_hasher)
);
#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
assert_eq!(
str::get_hash(&&"test", &build_hasher),
<[u8]>::get_hash(&"test".to_string().into_bytes(), &build_hasher)
Expand Down
2 changes: 1 addition & 1 deletion tests/bench.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))]
#![cfg_attr(feature = "nightly-specialize", feature(build_hasher_simple_hash_one))]

use ahash::{AHasher, RandomState};
use criterion::*;
Expand Down
6 changes: 3 additions & 3 deletions tests/map_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))]
#![cfg_attr(feature = "nightly-specialize", feature(build_hasher_simple_hash_one))]

use std::hash::{BuildHasher, Hash, Hasher};

Expand Down Expand Up @@ -151,13 +151,13 @@ fn check_for_collisions<H: Hash, B: BuildHasher>(build_hasher: &B, items: &[H],
);
}

#[cfg(feature = "specialize")]
#[cfg(feature = "nightly-specialize")]
#[allow(unused)] // False positive
fn hash<H: Hash, B: BuildHasher>(b: &H, build_hasher: &B) -> u64 {
build_hasher.hash_one(b)
}

#[cfg(not(feature = "specialize"))]
#[cfg(not(feature = "nightly-specialize"))]
#[allow(unused)] // False positive
fn hash<H: Hash, B: BuildHasher>(b: &H, build_hasher: &B) -> u64 {
let mut hasher = build_hasher.build_hasher();
Expand Down

0 comments on commit 0f0fb48

Please # to comment.