From a433bcee1300bf3f68e56a6f85bd813006071724 Mon Sep 17 00:00:00 2001 From: kellda <59569234+kellda@users.noreply.github.com> Date: Wed, 3 Feb 2021 22:16:47 +0000 Subject: [PATCH 1/4] Make `sigval` an union --- libc-test/build.rs | 82 ++-------------------------------------------- src/fuchsia/mod.rs | 40 ++++++++++------------ src/unix/mod.rs | 28 +++++++++++++--- 3 files changed, 42 insertions(+), 108 deletions(-) diff --git a/libc-test/build.rs b/libc-test/build.rs index eb2a5dfe72c46..4622798104465 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -325,9 +325,6 @@ fn test_apple(target: &str) { return true; } match ty { - // FIXME: actually a union - "sigval" => true, - // FIXME: The size is changed in recent macOSes. "malloc_zone_t" => true, // it is a moving target, changing through versions @@ -423,14 +420,6 @@ fn test_apple(target: &str) { } }); - cfg.skip_field_type(move |struct_, field| { - match (struct_, field) { - // FIXME: actually a union - ("sigevent", "sigev_value") => true, - _ => false, - } - }); - cfg.volatile_item(|i| { use ctest::VolatileItemKind::*; match i { @@ -570,27 +559,6 @@ fn test_openbsd(target: &str) { "sys/param.h", } - cfg.skip_struct(move |ty| { - if ty.starts_with("__c_anonymous_") { - return true; - } - match ty { - // FIXME: actually a union - "sigval" => true, - - _ => false, - } - }); - - cfg.skip_const(move |name| { - match name { - // Removed in OpenBSD 7.7 (unused since 1991) - "ATF_COM" | "ATF_PERM" | "ATF_PUBL" | "ATF_USETRAILERS" => true, - - _ => false, - } - }); - cfg.skip_fn(move |name| { match name { // futex() has volatile arguments, but that doesn't exist in Rust. @@ -985,8 +953,6 @@ fn test_solarish(target: &str) { return true; } match ty { - // union, not a struct - "sigval" => true, // a bunch of solaris-only fields "utmpx" if is_illumos => true, _ => false, @@ -1006,8 +972,6 @@ fn test_solarish(target: &str) { "sigaction" if field == "sa_sigaction" => true, // Missing in illumos "sigevent" if field == "ss_sp" => true, - // Avoid sigval union issues - "sigevent" if field == "sigev_value" => true, // const issues "sigevent" if field == "sigev_notify_attributes" => true, @@ -1230,8 +1194,6 @@ fn test_netbsd(target: &str) { cfg.skip_struct(move |ty| { match ty { - // This is actually a union, not a struct - "sigval" => true, // These are tested as part of the linux_fcntl tests since there are // header conflicts when including them with all the other structs. "termios2" => true, @@ -1282,8 +1244,6 @@ fn test_netbsd(target: &str) { (struct_ == "ifaddrs" && field == "ifa_ifu") || // sighandler_t type is super weird (struct_ == "sigaction" && field == "sa_sigaction") || - // sigval is actually a union, but we pretend it's a struct - (struct_ == "sigevent" && field == "sigev_value") || // aio_buf is "volatile void*" and Rust doesn't understand volatile (struct_ == "aiocb" && field == "aio_buf") }); @@ -1412,9 +1372,6 @@ fn test_dragonflybsd(target: &str) { t if t.ends_with("_t") => t.to_string(), - // sigval is a struct in Rust, but a union in C: - "sigval" => format!("union sigval"), - // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -1507,8 +1464,6 @@ fn test_dragonflybsd(target: &str) { (struct_ == "ifaddrs" && field == "ifa_ifu") || // sighandler_t type is super weird (struct_ == "sigaction" && field == "sa_sigaction") || - // sigval is actually a union, but we pretend it's a struct - (struct_ == "sigevent" && field == "sigev_value") || // aio_buf is "volatile void*" and Rust doesn't understand volatile (struct_ == "aiocb" && field == "aio_buf") }); @@ -1799,9 +1754,6 @@ fn test_android(target: &str) { t if t.ends_with("_t") => t.to_string(), - // sigval is a struct in Rust, but a union in C: - "sigval" => format!("union sigval"), - // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -2096,8 +2048,6 @@ fn test_android(target: &str) { cfg.skip_field_type(move |struct_, field| { // This is a weird union, don't check the type. (struct_ == "ifaddrs" && field == "ifa_ifu") || - // sigval is actually a union, but we pretend it's a struct - (struct_ == "sigevent" && field == "sigev_value") || // this one is an anonymous union (struct_ == "ff_effect" && field == "u") || // FIXME: `sa_sigaction` has type `sighandler_t` but that type is @@ -2306,9 +2256,6 @@ fn test_freebsd(target: &str) { t if t.ends_with("_t") => t.to_string(), - // sigval is a struct in Rust, but a union in C: - "sigval" => format!("union sigval"), - // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -2928,9 +2875,6 @@ fn test_emscripten(target: &str) { return true; } match ty { - // This is actually a union, not a struct - "sigval" => true, - // FIXME: Investigate why the test fails. // Skip for now to unblock CI. "pthread_condattr_t" => true, @@ -3006,9 +2950,7 @@ fn test_emscripten(target: &str) { // This is a weird union, don't check the type. (struct_ == "ifaddrs" && field == "ifa_ifu") || // sighandler_t type is super weird - (struct_ == "sigaction" && field == "sa_sigaction") || - // sigval is actually a union, but we pretend it's a struct - (struct_ == "sigevent" && field == "sigev_value") + (struct_ == "sigaction" && field == "sa_sigaction") }); cfg.skip_field(move |struct_, field| { @@ -3204,9 +3146,6 @@ fn test_neutrino(target: &str) { match ty { "Elf64_Phdr" | "Elf32_Phdr" => true, - // FIXME(union): This is actually a union, not a struct - "sigval" => true, - // union "_channel_connect_attr" => true, @@ -3258,8 +3197,6 @@ fn test_neutrino(target: &str) { }); cfg.skip_field_type(move |struct_, field| { - // sigval is actually a union, but we pretend it's a struct - struct_ == "sigevent" && field == "sigev_value" || // Anonymous structures struct_ == "_idle_hook" && field == "time" }); @@ -3268,8 +3205,6 @@ fn test_neutrino(target: &str) { (struct_ == "__sched_param" && field == "reserved") || (struct_ == "sched_param" && field == "reserved") || (struct_ == "sigevent" && field == "__padding1") || // ensure alignment - (struct_ == "sigevent" && field == "__padding2") || // union - (struct_ == "sigevent" && field == "__sigev_un2") || // union // sighandler_t type is super weird (struct_ == "sigaction" && field == "sa_sigaction") || // does not exist @@ -3378,10 +3313,8 @@ fn test_vxworks(target: &str) { // FIXME(vxworks) cfg.skip_fn(move |name| match name { - // sigval - "sigqueue" | "_sigqueue" // sighandler_t - | "signal" + "signal" // not used in static linking by default | "dlerror" => true, _ => false, @@ -3770,9 +3703,6 @@ fn test_linux(target: &str) { // which is absent in glibc, has to be defined. "__timeval" => true, - // FIXME(union): This is actually a union, not a struct - "sigval" => true, - // This type is tested in the `linux_termios.rs` file since there // are header conflicts when including them with all the other // structs. @@ -4374,8 +4304,6 @@ fn test_linux(target: &str) { (struct_ == "sigaction" && field == "sa_sigaction") || // __timeval type is a patch which doesn't exist in glibc (struct_ == "utmpx" && field == "ut_tv") || - // sigval is actually a union, but we pretend it's a struct - (struct_ == "sigevent" && field == "sigev_value") || // this one is an anonymous union (struct_ == "ff_effect" && field == "u") || // `__exit_status` type is a patch which is absent in musl @@ -4847,8 +4775,6 @@ fn test_haiku(target: &str) { return true; } match ty { - // FIXME: actually a union - "sigval" => true, // FIXME: locale_t does not exist on Haiku "locale_t" => true, // FIXME: rusage has a different layout on Haiku @@ -4955,10 +4881,8 @@ fn test_haiku(target: &str) { ("stat", "st_crtime_nsec") => true, // these are actually unions, but we cannot represent it well - ("siginfo_t", "sigval") => true, ("sem_t", "named_sem_id") => true, ("sigaction", "sa_sigaction") => true, - ("sigevent", "sigev_value") => true, ("fpu_state", "_fpreg") => true, ("cpu_topology_node_info", "data") => true, // these fields have a simplified data definition in libc @@ -5009,8 +4933,6 @@ fn test_haiku(target: &str) { ty.to_string() } - // is actually a union - "sigval" => format!("union sigval"), t if is_union => format!("union {}", t), t if t.ends_with("_t") => t.to_string(), t if is_struct => format!("struct {}", t), diff --git a/src/fuchsia/mod.rs b/src/fuchsia/mod.rs index f9a7ed929eaaf..441ba6e75419c 100644 --- a/src/fuchsia/mod.rs +++ b/src/fuchsia/mod.rs @@ -253,11 +253,6 @@ s! { pub l_linger: c_int, } - pub struct sigval { - // Actually a union of an int and a void* - pub sival_ptr: *mut c_void, - } - // pub struct itimerval { pub it_interval: crate::timeval, @@ -1054,6 +1049,11 @@ s_no_extra_traits! { pub struct pthread_cond_t { size: [u8; crate::__SIZEOF_PTHREAD_COND_T], } + + pub union sigval { + pub sival_int: c_int, + pub sival_ptr: *mut c_void, + } } cfg_if! { @@ -1360,17 +1360,6 @@ cfg_if! { } } impl Eq for sigevent {} - impl fmt::Debug for sigevent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("sigevent") - .field("sigev_value", &self.sigev_value) - .field("sigev_signo", &self.sigev_signo) - .field("sigev_notify", &self.sigev_notify) - .field("sigev_notify_function", &self.sigev_notify_function) - .field("sigev_notify_attributes", &self.sigev_notify_attributes) - .finish() - } - } impl hash::Hash for sigevent { fn hash(&self, state: &mut H) { self.sigev_value.hash(state); @@ -1425,18 +1414,23 @@ cfg_if! { } } impl Eq for pthread_rwlock_t {} - impl fmt::Debug for pthread_rwlock_t { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("pthread_rwlock_t") - // FIXME: .field("size", &self.size) - .finish() - } - } impl hash::Hash for pthread_rwlock_t { fn hash(&self, state: &mut H) { self.size.hash(state); } } + + impl PartialEq for sigval { + fn eq(&self, other: &sigval) -> bool { + unimplemented!("traits") + } + } + impl Eq for sigval {} + impl hash::Hash for sigval { + fn hash(&self, state: &mut H) { + unimplemented!("traits") + } + } } } diff --git a/src/unix/mod.rs b/src/unix/mod.rs index 84298804c594f..0c069165da8ec 100644 --- a/src/unix/mod.rs +++ b/src/unix/mod.rs @@ -176,11 +176,6 @@ s! { pub l_linger: c_int, } - pub struct sigval { - // Actually a union of an int and a void* - pub sival_ptr: *mut c_void, - } - // pub struct itimerval { pub it_interval: crate::timeval, @@ -214,6 +209,29 @@ s! { } } +s_no_extra_traits! { + pub union sigval { + pub sival_int: c_int, + pub sival_ptr: *mut c_void, + } +} + +cfg_if! { + if #[cfg(feature = "extra_traits")] { + impl PartialEq for sigval { + fn eq(&self, other: &sigval) -> bool { + unimplemented!("traits") + } + } + impl Eq for sigval {} + impl hash::Hash for sigval { + fn hash(&self, state: &mut H) { + unimplemented!("traits") + } + } + } +} + pub const INT_MIN: c_int = -2147483648; pub const INT_MAX: c_int = 2147483647; From 10ff5d453c9ffb8387566b0248c101fcabadda82 Mon Sep 17 00:00:00 2001 From: kellda <59569234+kellda@users.noreply.github.com> Date: Thu, 4 Feb 2021 12:40:05 +0000 Subject: [PATCH 2/4] Make `ifaddrs.ifa_ifu` an union --- src/fuchsia/mod.rs | 19 ++++++++++++++++++- src/unix/linux_like/mod.rs | 28 +++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/fuchsia/mod.rs b/src/fuchsia/mod.rs index 441ba6e75419c..27ec248483d27 100644 --- a/src/fuchsia/mod.rs +++ b/src/fuchsia/mod.rs @@ -473,7 +473,7 @@ s! { pub ifa_flags: c_uint, pub ifa_addr: *mut crate::sockaddr, pub ifa_netmask: *mut crate::sockaddr, - pub ifa_ifu: *mut crate::sockaddr, // FIXME This should be a union + pub ifa_ifu: __c_anonymous_ifa_ifu, pub ifa_data: *mut c_void, } @@ -1054,6 +1054,11 @@ s_no_extra_traits! { pub sival_int: c_int, pub sival_ptr: *mut c_void, } + + pub union __c_anonymous_ifa_ifu { + ifu_broadaddr: *mut sockaddr, + ifu_dstaddr: *mut sockaddr, + } } cfg_if! { @@ -1431,6 +1436,18 @@ cfg_if! { unimplemented!("traits") } } + + impl PartialEq for __c_anonymous_ifa_ifu { + fn eq(&self, other: &__c_anonymous_ifa_ifu) -> bool { + unimplemented!("traits") + } + } + impl Eq for __c_anonymous_ifa_ifu {} + impl hash::Hash for __c_anonymous_ifa_ifu { + fn hash(&self, state: &mut H) { + unimplemented!("traits") + } + } } } diff --git a/src/unix/linux_like/mod.rs b/src/unix/linux_like/mod.rs index 6678cb6d74870..c5c7c256a5dd9 100644 --- a/src/unix/linux_like/mod.rs +++ b/src/unix/linux_like/mod.rs @@ -166,7 +166,7 @@ s! { pub ifa_flags: c_uint, pub ifa_addr: *mut crate::sockaddr, pub ifa_netmask: *mut crate::sockaddr, - pub ifa_ifu: *mut crate::sockaddr, // FIXME(union) This should be a union + pub ifa_ifu: __c_anonymous_ifa_ifu, pub ifa_data: *mut c_void, } @@ -303,6 +303,11 @@ s_no_extra_traits! { pub sigev_notify: c_int, pub _sigev_un: __c_anonymous_sigev_un, } + + pub union __c_anonymous_ifa_ifu { + ifu_broadaddr: *mut sockaddr, + ifu_dstaddr: *mut sockaddr, + } } cfg_if! { @@ -459,6 +464,27 @@ cfg_if! { .finish() } } + + impl hash::Hash for sigevent { + fn hash(&self, state: &mut H) { + self.sigev_value.hash(state); + self.sigev_signo.hash(state); + self.sigev_notify.hash(state); + self.sigev_notify_thread_id.hash(state); + } + } + + impl PartialEq for __c_anonymous_ifa_ifu { + fn eq(&self, other: &__c_anonymous_ifa_ifu) -> bool { + unimplemented!("traits") + } + } + impl Eq for __c_anonymous_ifa_ifu {} + impl hash::Hash for __c_anonymous_ifa_ifu { + fn hash(&self, state: &mut H) { + unimplemented!("traits") + } + } } } From 81e2b6c48e65ab842ce50bc4f6df0d29a3c09aa6 Mon Sep 17 00:00:00 2001 From: kellda <59569234+kellda@users.noreply.github.com> Date: Thu, 4 Feb 2021 13:56:20 +0000 Subject: [PATCH 3/4] Implement `epoll_data` union --- libc-test/build.rs | 12 ------------ src/fuchsia/mod.rs | 7 +++++++ src/unix/linux_like/mod.rs | 35 ++++++++++++++++++++------------- src/unix/solarish/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 25 deletions(-) diff --git a/libc-test/build.rs b/libc-test/build.rs index 4622798104465..96087fe1a082a 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -906,8 +906,6 @@ fn test_solarish(target: &str) { cfg.field_name(move |struct_, field| { match struct_ { - // rust struct uses raw u64, rather than union - "epoll_event" if field == "u64" => "data.u64".to_string(), // rust struct was committed with typo for Solaris "door_arg_t" if field == "dec_num" => "desc_num".to_string(), "stat" if field.ends_with("_nsec") => { @@ -1176,7 +1174,6 @@ fn test_netbsd(target: &str) { s if s.ends_with("_nsec") && struct_.starts_with("stat") => { s.replace("e_nsec", ".tv_nsec") } - "u64" if struct_ == "epoll_event" => "data.u64".to_string(), s => s.to_string(), } }); @@ -1386,7 +1383,6 @@ fn test_dragonflybsd(target: &str) { s if s.ends_with("_nsec") && struct_.starts_with("stat") => { s.replace("e_nsec", ".tv_nsec") } - "u64" if struct_ == "epoll_event" => "data.u64".to_string(), // Field is named `type` in C but that is a Rust keyword, // so these fields are translated to `type_` in the bindings. "type_" if struct_ == "rtprio" => "type".to_string(), @@ -1766,8 +1762,6 @@ fn test_android(target: &str) { // Our stat *_nsec fields normally don't actually exist but are part // of a timeval struct s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.to_string(), - // FIXME: appears that `epoll_event.data` is an union - "u64" if struct_ == "epoll_event" => "data.u64".to_string(), // The following structs have a field called `type` in C, // but `type` is a Rust keyword, so these fields are translated // to `type_` in Rust. @@ -2848,8 +2842,6 @@ fn test_emscripten(target: &str) { s if s.ends_with("_nsec") && struct_.starts_with("stat") => { s.replace("e_nsec", ".tv_nsec") } - // Rust struct uses raw u64, rather than union - "u64" if struct_ == "epoll_event" => "data.u64".to_string(), s => s.to_string(), } }); @@ -3585,10 +3577,6 @@ fn test_linux(target: &str) { s if s.ends_with("_nsec") && struct_.starts_with("stat") => { s.replace("e_nsec", ".tv_nsec") } - // FIXME(linux): epoll_event.data is actually a union in C, but in Rust - // it is only a u64 because we only expose one field - // http://man7.org/linux/man-pages/man2/epoll_wait.2.html - "u64" if struct_ == "epoll_event" => "data.u64".to_string(), // The following structs have a field called `type` in C, // but `type` is a Rust keyword, so these fields are translated // to `type_` in Rust. diff --git a/src/fuchsia/mod.rs b/src/fuchsia/mod.rs index 27ec248483d27..a87ae57dcb645 100644 --- a/src/fuchsia/mod.rs +++ b/src/fuchsia/mod.rs @@ -419,6 +419,13 @@ s! { pub struct epoll_event { pub events: u32, + pub data: epoll_data, + } + + pub union epoll_data { + pub ptr: *mut c_void, + pub fd: c_int, + pub u32: u32, pub u64: u64, } diff --git a/src/unix/linux_like/mod.rs b/src/unix/linux_like/mod.rs index c5c7c256a5dd9..6f89f2c596f49 100644 --- a/src/unix/linux_like/mod.rs +++ b/src/unix/linux_like/mod.rs @@ -265,6 +265,13 @@ s_no_extra_traits! { )] pub struct epoll_event { pub events: u32, + pub data: epoll_data, + } + + pub union epoll_data { + pub ptr: *mut c_void, + pub fd: c_int, + pub u32: u32, pub u64: u64, } @@ -314,26 +321,28 @@ cfg_if! { if #[cfg(feature = "extra_traits")] { impl PartialEq for epoll_event { fn eq(&self, other: &epoll_event) -> bool { - self.events == other.events && self.u64 == other.u64 + unimplemented!("traits") } } impl Eq for epoll_event {} - impl fmt::Debug for epoll_event { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let events = self.events; - let u64 = self.u64; - f.debug_struct("epoll_event") - .field("events", &events) - .field("u64", &u64) - .finish() - } - } impl hash::Hash for epoll_event { fn hash(&self, state: &mut H) { let events = self.events; - let u64 = self.u64; + let data = self.data; events.hash(state); - u64.hash(state); + data.hash(state); + } + } + + impl PartialEq for epoll_data { + fn eq(&self, other: &epoll_data) -> bool { + unimplemented!("traits") + } + } + impl Eq for epoll_data {} + impl hash::Hash for epoll_data { + fn hash(&self, state: &mut H) { + unimplemented!("traits") } } diff --git a/src/unix/solarish/mod.rs b/src/unix/solarish/mod.rs index 228ba04b84455..9a209363805cb 100644 --- a/src/unix/solarish/mod.rs +++ b/src/unix/solarish/mod.rs @@ -501,6 +501,22 @@ s! { } s_no_extra_traits! { + #[cfg_attr(any( + target_arch = "x86", target_arch = "x86_64"), + repr(packed(4)) + )] + pub struct epoll_event { + pub events: u32, + pub data: epoll_data, + } + + pub union epoll_data { + pub ptr: *mut c_void, + pub fd: c_int, + pub u32: u32, + pub u64: u64, + } + pub struct sockaddr_un { pub sun_family: sa_family_t, pub sun_path: [c_char; 108], @@ -573,6 +589,30 @@ s_no_extra_traits! { cfg_if! { if #[cfg(feature = "extra_traits")] { + impl PartialEq for epoll_event { + fn eq(&self, other: &epoll_event) -> bool { + unimplemented!("traits") + } + } + impl Eq for epoll_event {} + impl hash::Hash for epoll_event { + fn hash(&self, state: &mut H) { + unimplemented!("traits") + } + } + + impl PartialEq for epoll_data { + fn eq(&self, other: &epoll_data) -> bool { + unimplemented!("traits") + } + } + impl Eq for epoll_data {} + impl hash::Hash for epoll_data { + fn hash(&self, state: &mut H) { + unimplemented!("traits") + } + } + impl PartialEq for sockaddr_un { fn eq(&self, other: &sockaddr_un) -> bool { self.sun_family == other.sun_family From 5ea0e7056557e364a2c4ad433aed17c906021cfc Mon Sep 17 00:00:00 2001 From: kellda <59569234+kellda@users.noreply.github.com> Date: Thu, 4 Feb 2021 14:50:57 +0000 Subject: [PATCH 4/4] Make `fpreg_t` an union --- libc-test/build.rs | 2 -- src/unix/linux_like/linux/gnu/b64/s390x.rs | 16 ++++------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/libc-test/build.rs b/libc-test/build.rs index 96087fe1a082a..56f5591ac5e56 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -4398,8 +4398,6 @@ fn test_linux(target: &str) { cfg.skip_roundtrip(move |s| match s { // FIXME: "mcontext_t" if s390x => true, - // FIXME: This is actually a union. - "fpreg_t" if s390x => true, // The test doesn't work on some env: "ipv6_mreq" diff --git a/src/unix/linux_like/linux/gnu/b64/s390x.rs b/src/unix/linux_like/linux/gnu/b64/s390x.rs index 7a48de58c967b..d8850e88b892f 100644 --- a/src/unix/linux_like/linux/gnu/b64/s390x.rs +++ b/src/unix/linux_like/linux/gnu/b64/s390x.rs @@ -212,10 +212,9 @@ s! { } s_no_extra_traits! { - // FIXME(union): This is actually a union. - pub struct fpreg_t { + pub union fpreg_t { pub d: c_double, - // f: c_float, + pub f: c_float, } } @@ -223,22 +222,15 @@ cfg_if! { if #[cfg(feature = "extra_traits")] { impl PartialEq for fpreg_t { fn eq(&self, other: &fpreg_t) -> bool { - self.d == other.d + unimplemented!("traits") } } impl Eq for fpreg_t {} - impl fmt::Debug for fpreg_t { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("fpreg_t").field("d", &self.d).finish() - } - } - impl hash::Hash for fpreg_t { fn hash(&self, state: &mut H) { - let d: u64 = unsafe { mem::transmute(self.d) }; - d.hash(state); + unimplemented!("traits") } } }