From 26a8487a900481d2bd0b56aa33785d3919628b5a Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 1 Dec 2022 18:48:14 -0800 Subject: [PATCH] Evaluate gfx_select's #[cfg] conditions at the right time. See the comments in the code for details. --- wgpu-core/src/lib.rs | 120 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 21 deletions(-) diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index ce0f7f50871..9c98aeb867c 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -243,6 +243,100 @@ If you are running this program on native and not in a browser and wish to work Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \ platform supports."; +// #[cfg] attributes in exported macros are interesting! +// +// The #[cfg] conditions in a macro's expansion are evaluated using the +// configuration options (features, target architecture and os, etc.) in force +// where the macro is *used*, not where it is *defined*. That is, if crate A +// defines a macro like this: +// +// #[macro_export] +// macro_rules! if_bleep { +// { } => { +// #[cfg(feature = "bleep")] +// bleep(); +// } +// } +// +// and then crate B uses it like this: +// +// fn f() { +// if_bleep! { } +// } +// +// then it is crate B's `"bleep"` feature, not crate A's, that determines +// whether the macro expands to a function call or an empty statement. The +// entire configuration predicate is evaluated in the use's context, not the +// definition's. +// +// Since `wgpu-core` selects back ends using features, we need to make sure the +// arms of the `gfx_select!` macro are pruned according to `wgpu-core`'s +// features, not those of whatever crate happens to be using `gfx_select!`. This +// means we can't use `#[cfg]` attributes in `gfx_select!`s definition itself. +// Instead, `gfx_select!` must use a macro for each backend whose definition is +// selected by `#[cfg]` in `wgpu-core`. The configuration predicate is still +// evaluated in the use; we've just moved that use from `gfx_select!` to the +// definitions of the per-backend macros here in `wgpu-core`. + +/// Define an exported macro named `$public` that expands to a call if the +/// configuration predicate `$condition` is true, or to a panic otherwise. +/// +/// Because of odd technical limitations on exporting macros expanded by other +/// macros, you must supply a both a public-facing name for the macro and a +/// private name, which is never used outside this macro. For details: +/// +macro_rules! define_backend_caller { + { $public:ident, $private:ident if $condition:meta } => { + #[cfg( $condition )] + #[macro_export] + macro_rules! $private { + ( $backend:literal, $call:expr ) => ( $call ) + } + + #[cfg(not( $condition ))] + #[macro_export] + macro_rules! $private { + ( $backend:literal, $call:expr ) => ( + panic!("Unexpected backend {:?}", $backend) + ) + } + + #[doc(hidden)] pub use $private as $public; + } +} + +define_backend_caller! { + gfx_if_vulkan, gfx_if_vulkan_hidden + if any( + all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")), + feature = "vulkan-portability" + ) +} + +define_backend_caller! { + gfx_if_metal, gfx_if_metal_hidden + if all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")) +} + +define_backend_caller! { + gfx_if_dx12, gfx_if_dx12_hidden + if all(not(target_arch = "wasm32"), windows) +} + +define_backend_caller! { + gfx_if_dx11, gfx_if_dx11_hidden + if all(not(target_arch = "wasm32"), windows) +} + +define_backend_caller! { + gfx_if_gles, gfx_if_gles_hidden + if any( + all(unix, not(target_os = "macos"), not(target_os = "ios")), + feature = "angle", + target_arch = "wasm32" + ) +} + /// Dispatch on an [`Id`]'s backend to a backend-generic method. /// /// Uses of this macro have the form: @@ -291,29 +385,13 @@ platform supports."; #[macro_export] macro_rules! gfx_select { ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => { - // Note: For some reason the cfg aliases defined in build.rs - // don't succesfully apply in this macro so we must specify - // their equivalents manually. match $id.backend() { - #[cfg(any( - all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")), - feature = "vulkan-portability" - ))] - wgt::Backend::Vulkan => $global.$method::<$crate::api::Vulkan>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))] - wgt::Backend::Metal => $global.$method::<$crate::api::Metal>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), windows))] - wgt::Backend::Dx12 => $global.$method::<$crate::api::Dx12>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), windows))] - wgt::Backend::Dx11 => $global.$method::<$crate::api::Dx11>( $($param),* ), - #[cfg(any( - all(unix, not(target_os = "macos"), not(target_os = "ios")), - feature = "angle", - target_arch = "wasm32" - ))] - wgt::Backend::Gl => $global.$method::<$crate::api::Gles>( $($param),+ ), + wgt::Backend::Vulkan => $crate::gfx_if_vulkan!("Vulkan", $global.$method::<$crate::api::Vulkan>( $($param),* )), + wgt::Backend::Metal => $crate::gfx_if_metal!("Metal", $global.$method::<$crate::api::Metal>( $($param),* )), + wgt::Backend::Dx12 => $crate::gfx_if_dx12!("Dx12", $global.$method::<$crate::api::Dx12>( $($param),* )), + wgt::Backend::Dx11 => $crate::gfx_if_dx11!("Dx11", $global.$method::<$crate::api::Dx11>( $($param),* )), + wgt::Backend::Gl => $crate::gfx_if_gles!("Gles", $global.$method::<$crate::api::Gles>( $($param),+ )), other => panic!("Unexpected backend {:?}", other), - } }; }