Support derive(Hash)

This commit is contained in:
David Tolnay 2020-11-27 17:36:16 -08:00
parent a05f9402db
commit 7da38209bf
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
6 changed files with 96 additions and 2 deletions

View File

@ -134,6 +134,8 @@ fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
_ => {}
}
}
write_std_specializations(out, apis);
}
for api in apis {
@ -148,6 +150,36 @@ fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
}
}
fn write_std_specializations(out: &mut OutFile, apis: &[Api]) {
out.set_namespace(Default::default());
out.begin_block(Block::Namespace("std"));
for api in apis {
if let Api::Struct(strct) = api {
if derive::contains(&strct.derives, Trait::Hash) {
out.next_section();
let qualified = strct.name.to_fully_qualified();
writeln!(out, "template <> struct hash<{}> {{", qualified);
writeln!(
out,
" size_t operator()(const {} &self) const noexcept {{",
qualified,
);
let link_name = mangle::operator(&strct.name, "hash");
write!(out, " return ::");
for name in &strct.name.namespace {
write!(out, "{}::", name);
}
writeln!(out, "{}(self);", link_name);
writeln!(out, " }}");
writeln!(out, "}};");
}
}
}
out.end_block(Block::Namespace("std"));
}
fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
for api in apis {
if let Api::Include(include) = api {
@ -432,6 +464,15 @@ fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
}
}
if derive::contains(&strct.derives, Trait::Hash) {
let link_name = mangle::operator(&strct.name, "hash");
writeln!(
out,
"size_t {}(const {} &) noexcept;",
link_name, strct.name.cxx,
);
}
out.end_block(Block::ExternC);
}

View File

@ -16,6 +16,7 @@ pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -
Trait::Debug => expanded.extend(struct_debug(strct, span)),
Trait::Default => expanded.extend(struct_default(strct, span)),
Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
Trait::Hash => expanded.extend(struct_hash(strct, span)),
Trait::Ord => expanded.extend(struct_ord(strct, span)),
Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
@ -56,6 +57,7 @@ pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> Toke
traits.push(quote_spanned!(span=> ::std::cmp::Eq));
has_eq = true;
}
Trait::Hash => expanded.extend(enum_hash(enm, span)),
Trait::Ord => expanded.extend(enum_ord(enm, span)),
Trait::PartialEq => {
traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
@ -155,6 +157,21 @@ fn struct_default(strct: &Struct, span: Span) -> TokenStream {
}
}
fn struct_hash(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let fields = strct.fields.iter().map(|field| &field.ident);
quote_spanned! {span=>
impl ::std::hash::Hash for #ident {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
#(
::std::hash::Hash::hash(&self.#fields, state);
)*
}
}
}
}
fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let fields = strct.fields.iter().map(|field| &field.ident);
@ -246,6 +263,18 @@ fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
}
}
fn enum_hash(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::std::hash::Hash for #ident {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
::std::hash::Hash::hash(&self.repr, state);
}
}
}
}
fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;

View File

@ -230,6 +230,19 @@ fn expand_struct_operators(strct: &Struct) -> TokenStream {
});
}
}
Trait::Hash => {
let link_name = mangle::operator(&strct.name, "hash");
let local_name = format_ident!("__operator_hash_{}", strct.name.rust);
operators.extend(quote_spanned! {span=>
#[doc(hidden)]
#[export_name = #link_name]
extern "C" fn #local_name(this: &#ident) -> usize {
let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
::std::hash::Hash::hash(this, &mut hasher);
::std::hash::Hasher::finish(&hasher) as usize
}
});
}
_ => {}
}
}

View File

@ -13,6 +13,7 @@ pub enum Trait {
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
@ -26,6 +27,7 @@ impl Derive {
"Debug" => Trait::Debug,
"Default" => Trait::Default,
"Eq" => Trait::Eq,
"Hash" => Trait::Hash,
"Ord" => Trait::Ord,
"PartialEq" => Trait::PartialEq,
"PartialOrd" => Trait::PartialOrd,
@ -50,6 +52,7 @@ impl AsRef<str> for Trait {
Trait::Debug => "Debug",
Trait::Default => "Default",
Trait::Eq => "Eq",
Trait::Hash => "Hash",
Trait::Ord => "Ord",
Trait::PartialEq => "PartialEq",
Trait::PartialOrd => "PartialOrd",

View File

@ -26,7 +26,13 @@ pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
}
pub fn operator(receiver: &Pair, operator: &'static str) -> Symbol {
join!(receiver.namespace, CXXBRIDGE, receiver.cxx, "operator", operator)
join!(
receiver.namespace,
CXXBRIDGE,
receiver.cxx,
"operator",
operator,
)
}
// The C half of a function pointer trampoline.

View File

@ -25,7 +25,7 @@ pub mod ffi {
msg: String,
}
#[derive(Debug)]
#[derive(Debug, Hash)]
enum Enum {
AVal,
BVal = 2020,
@ -64,6 +64,7 @@ pub mod ffi {
}
#[namespace = "second"]
#[derive(Hash)]
struct Second {
i: i32,
e: COwnedEnum,
@ -184,6 +185,7 @@ pub mod ffi {
}
#[repr(u32)]
#[derive(Hash)]
enum COwnedEnum {
CVal1,
CVal2,