diff --git a/crossbeam-channel/src/lib.rs b/crossbeam-channel/src/lib.rs index 1d5160056..d674cc9f9 100644 --- a/crossbeam-channel/src/lib.rs +++ b/crossbeam-channel/src/lib.rs @@ -364,6 +364,7 @@ mod waker; pub mod internal { pub use select::SelectHandle; pub use select::{select, select_timeout, try_select}; + pub use select_macro::unsafe_transmute; } pub use channel::{after, never, tick}; diff --git a/crossbeam-channel/src/select_macro.rs b/crossbeam-channel/src/select_macro.rs index 9aaaa9e93..5e785c0ed 100644 --- a/crossbeam-channel/src/select_macro.rs +++ b/crossbeam-channel/src/select_macro.rs @@ -829,8 +829,7 @@ macro_rules! crossbeam_channel_internal { let _oper = $crate::internal::select(&mut $sel); // Erase the lifetime so that `sel` can be dropped early even without NLL. - #[allow(unsafe_code)] - unsafe { ::std::mem::transmute(_oper) } + $crate::internal::unsafe_transmute(_oper) }; crossbeam_channel_internal! { @@ -852,8 +851,7 @@ macro_rules! crossbeam_channel_internal { let _oper = $crate::internal::try_select(&mut $sel); // Erase the lifetime so that `sel` can be dropped early even without NLL. - #[allow(unsafe_code)] - unsafe { ::std::mem::transmute(_oper) } + $crate::internal::unsafe_transmute(_oper) }; match _oper { @@ -883,8 +881,7 @@ macro_rules! crossbeam_channel_internal { let _oper = $crate::internal::select_timeout(&mut $sel, $timeout); // Erase the lifetime so that `sel` can be dropped early even without NLL. - #[allow(unsafe_code)] - unsafe { ::std::mem::transmute(_oper) } + $crate::internal::unsafe_transmute(_oper) }; match _oper { @@ -922,13 +919,12 @@ macro_rules! crossbeam_channel_internal { ) => {{ match $r { ref _r => { - #[allow(unsafe_code)] - let $var: &$crate::Receiver<_> = unsafe { + let $var: &$crate::Receiver<_> = { let _r: &$crate::Receiver<_> = _r; // Erase the lifetime so that `sel` can be dropped early even without NLL. - unsafe fn unbind<'a, T>(x: &T) -> &'a T { - ::std::mem::transmute(x) + fn unbind<'a, T>(x: &T) -> &'a T { + $crate::internal::unsafe_transmute(x) } unbind(_r) }; @@ -955,13 +951,12 @@ macro_rules! crossbeam_channel_internal { ) => {{ match $s { ref _s => { - #[allow(unsafe_code)] - let $var: &$crate::Sender<_> = unsafe { + let $var: &$crate::Sender<_> = { let _s: &$crate::Sender<_> = _s; // Erase the lifetime so that `sel` can be dropped early even without NLL. - unsafe fn unbind<'a, T>(x: &T) -> &'a T { - ::std::mem::transmute(x) + fn unbind<'a, T>(x: &T) -> &'a T { + $crate::internal::unsafe_transmute(x) } unbind(_s) }; @@ -1212,3 +1207,14 @@ macro_rules! select { ) }; } + +/// Same as `std::mem::transmute`, except the function is not marked with `unsafe`. +/// +/// If the user crate contains `#![forbid(unsafe_code)]`, we must make sure the `select!` macro +/// does not emit any unsafe code or else it won't be usable. Since the macro does have some unsafe +/// code, we need to cheat around the lint by using this "safe" transmute function. +pub fn unsafe_transmute(t: T) -> U { + let u = unsafe { std::mem::transmute_copy(&t) }; + std::mem::forget(t); + u +} diff --git a/crossbeam-channel/tests/select_macro.rs b/crossbeam-channel/tests/select_macro.rs index 0a7eddba8..19f0d5167 100644 --- a/crossbeam-channel/tests/select_macro.rs +++ b/crossbeam-channel/tests/select_macro.rs @@ -1,6 +1,6 @@ //! Tests for the `select!` macro. -#![deny(unsafe_code)] +#![forbid(unsafe_code)] #[macro_use] extern crate crossbeam_channel;