diff --git a/Cargo.lock b/Cargo.lock index dc4499d1fcd..52d80b9ede5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3086,7 +3086,6 @@ dependencies = [ name = "rustpython" version = "0.5.0" dependencies = [ - "cfg-if", "criterion", "dirs-next", "env_logger", @@ -3135,7 +3134,6 @@ version = "0.5.0" dependencies = [ "ascii", "bitflags 2.11.0", - "cfg-if", "getrandom 0.3.4", "itertools 0.14.0", "libc", @@ -3364,7 +3362,6 @@ dependencies = [ "base64", "blake2", "bzip2", - "cfg-if", "chrono", "constant_time_eq", "crc32fast", @@ -3459,7 +3456,6 @@ dependencies = [ "bitflags 2.11.0", "bstr", "caseless", - "cfg-if", "chrono", "constant_time_eq", "crossbeam-utils", diff --git a/Cargo.toml b/Cargo.toml index 35a2323c84d..8b597b0f422 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ rustpython-pylib = { workspace = true, optional = true } rustpython-stdlib = { workspace = true, optional = true, features = ["compiler"] } rustpython-vm = { workspace = true, features = ["compiler", "gc"] } -cfg-if = { workspace = true } log = { workspace = true } flame = { workspace = true, optional = true } @@ -136,7 +135,7 @@ exclude = ["pymath"] version = "0.5.0" authors = ["RustPython Team"] edition = "2024" -rust-version = "1.94.0" +rust-version = "1.95.0" repository = "https://github.com/RustPython/RustPython" license = "MIT" @@ -177,7 +176,6 @@ ascii = "1.1" bitflags = "2.11.0" bitflagset = "0.0.3" bstr = "1" -cfg-if = "1.0" chrono = { version = "0.4.44", default-features = false, features = ["clock", "oldtime", "std"] } constant_time_eq = "0.4" criterion = { version = "0.8", features = ["html_reports"] } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index d93ac6b9ecd..29d7aaeed28 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -20,7 +20,6 @@ rustpython-wtf8 = { workspace = true } ascii = { workspace = true } bitflags = { workspace = true } -cfg-if = { workspace = true } getrandom = { workspace = true } itertools = { workspace = true } libc = { workspace = true } diff --git a/crates/common/src/lock.rs b/crates/common/src/lock.rs index cd7df512d83..08fbc316599 100644 --- a/crates/common/src/lock.rs +++ b/crates/common/src/lock.rs @@ -6,13 +6,13 @@ use lock_api::{ RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, }; -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { pub use parking_lot::{RawMutex, RawRwLock, RawThreadId}; - pub use std::sync::OnceLock as OnceCell; pub use core::cell::LazyCell; - } else { + } + _ => { mod cell_lock; pub use cell_lock::{RawCellMutex as RawMutex, RawCellRwLock as RawRwLock, SingleThreadId as RawThreadId}; @@ -23,10 +23,11 @@ cfg_if::cfg_if! { // LazyLock: uses std::sync::LazyLock when std is available (even without // threading, because Rust test runner uses parallel threads). // Without std, uses a LazyCell wrapper (truly single-threaded only). -cfg_if::cfg_if! { - if #[cfg(any(feature = "threading", feature = "std"))] { +cfg_select! { + any(feature = "threading", feature = "std") => { pub use std::sync::LazyLock; - } else { + } + _ => { pub struct LazyLock T>(core::cell::LazyCell); // SAFETY: This branch is only active when both "std" and "threading" // features are absent — i.e., truly single-threaded no_std environments diff --git a/crates/stdlib/Cargo.toml b/crates/stdlib/Cargo.toml index c812f0036cf..662b42aaed3 100644 --- a/crates/stdlib/Cargo.toml +++ b/crates/stdlib/Cargo.toml @@ -39,7 +39,6 @@ ruff_source_file = { workspace = true } ahash = { workspace = true } ascii = { workspace = true } -cfg-if = { workspace = true } crossbeam-utils = { workspace = true } flame = { workspace = true, optional = true } hex = { workspace = true } diff --git a/crates/stdlib/src/_sqlite3.rs b/crates/stdlib/src/_sqlite3.rs index b25e20e556d..3f166ed3029 100644 --- a/crates/stdlib/src/_sqlite3.rs +++ b/crates/stdlib/src/_sqlite3.rs @@ -2819,14 +2819,15 @@ mod _sqlite3 { db: *mut sqlite3, } - cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { + cfg_select! { + feature = "threading" => { unsafe impl Send for SqliteStatement {} // unsafe impl Sync for SqliteStatement {} unsafe impl Send for Sqlite {} // unsafe impl Sync for Sqlite {} unsafe impl Send for SqliteBlob {} } + _ => {} } impl From for SqliteRaw { diff --git a/crates/stdlib/src/openssl.rs b/crates/stdlib/src/openssl.rs index 9c7161d8048..fb6766ae939 100644 --- a/crates/stdlib/src/openssl.rs +++ b/crates/stdlib/src/openssl.rs @@ -7,17 +7,19 @@ mod cert; mod ssl_error; // Conditional compilation for OpenSSL version-specific error codes -cfg_if::cfg_if! { - if #[cfg(ossl310)] { - // OpenSSL 3.1.0+ +cfg_select! { + // OpenSSL 3.1.0+ + ossl310 => { mod ssl_data_31; use ssl_data_31 as ssl_data; - } else if #[cfg(ossl300)] { - // OpenSSL 3.0.0+ + } + // OpenSSL 3.0.0+ + ossl300 => { mod ssl_data_300; use ssl_data_300 as ssl_data; - } else { - // OpenSSL 1.1.1+ (fallback) + } + // OpenSSL 1.1.1+ (fallback) + _ => { mod ssl_data_111; use ssl_data_111 as ssl_data; } @@ -30,13 +32,10 @@ use rustpython_common::lock::LazyLock; // define our own copy of ProbeResult so we can handle the vendor case // easily, without having to have a bunch of cfgs -cfg_if::cfg_if! { - if #[cfg(openssl_vendored)] { - static PROBE: LazyLock = LazyLock::new(openssl_probe::probe); - } else { - static PROBE: LazyLock = LazyLock::new(|| ProbeResult { cert_file: None, cert_dir: vec![] }); - } -} +static PROBE: LazyLock = cfg_select! { + openssl_vendored => LazyLock::new(openssl_probe::probe) + _ => LazyLock::new(|| ProbeResult { cert_file: None, cert_dir: vec![] }) +}; fn probe() -> &'static ProbeResult { &PROBE @@ -1349,13 +1348,14 @@ mod _ssl { #[pymethod] fn set_default_verify_paths(&self, vm: &VirtualMachine) -> PyResult<()> { - cfg_if::cfg_if! { - if #[cfg(openssl_vendored)] { + cfg_select! { + openssl_vendored => { let (cert_file, cert_dir) = get_cert_file_dir(); self.builder() .load_verify_locations(Some(cert_file), Some(cert_dir)) .map_err(|e| convert_openssl_error(vm, e)) - } else { + } + _ => { self.builder() .set_default_verify_paths() .map_err(|e| convert_openssl_error(vm, e)) diff --git a/crates/stdlib/src/resource.rs b/crates/stdlib/src/resource.rs index 34c8161e0cd..8cc1b01e682 100644 --- a/crates/stdlib/src/resource.rs +++ b/crates/stdlib/src/resource.rs @@ -12,17 +12,18 @@ mod resource { use core::mem; use std::io; - cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { - #[expect(deprecated)] - const RLIM_NLIMITS: i32 = libc::RLIM_NLIMITS; - } else { + #[cfg_attr(target_os = "android", expect(deprecated))] + const RLIM_NLIMITS: i32 = cfg_select! { + target_os = "android" => { + libc::RLIM_NLIMITS + } + _ => { // This constant isn't abi-stable across os versions, so we just // pick a high number so we don't get false positive ValueErrors and just bubble up the // EINVAL that get/setrlimit return on an invalid resource - const RLIM_NLIMITS: i32 = 256; + 256 } - } + }; // TODO: RLIMIT_OFILE, #[pyattr] diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index f14e6ab62d7..fed5019c1b7 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -1568,20 +1568,23 @@ mod _socket { if socket_kind == -1 { socket_kind = sock.r#type().map_err(|e| e.into_pyexception(vm))?.into(); } - cfg_if::cfg_if! { - if #[cfg(any( + + cfg_select! { + any( target_os = "android", target_os = "freebsd", target_os = "fuchsia", target_os = "linux", - ))] { + ) => { if proto == -1 { proto = sock.protocol()?.map_or(0, Into::into); } - } else { + } + _ => { proto = 0; } } + return Ok(zelf.init_inner(family, socket_kind, proto, sock)?); } @@ -3259,14 +3262,9 @@ mod _socket { } fn sock_from_raw(fileno: RawSocket, vm: &VirtualMachine) -> PyResult { - let invalid = { - cfg_if::cfg_if! { - if #[cfg(windows)] { - fileno == INVALID_SOCKET - } else { - fileno < 0 - } - } + let invalid = cfg_select! { + windows => fileno == INVALID_SOCKET, + _ => fileno < 0 }; if invalid { return Err(vm.new_value_error("negative file descriptor")); diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index 1537a8fd517..fe9038f8d87 100644 --- a/crates/vm/Cargo.toml +++ b/crates/vm/Cargo.toml @@ -47,7 +47,6 @@ ascii = { workspace = true } ahash = { workspace = true } bitflags = { workspace = true } bstr = { workspace = true } -cfg-if = { workspace = true } crossbeam-utils = { workspace = true } chrono = { workspace = true } constant_time_eq = { workspace = true } diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs index ce919216c48..1cf8119e9c9 100644 --- a/crates/vm/src/builtins/type.rs +++ b/crates/vm/src/builtins/type.rs @@ -397,11 +397,12 @@ impl AsRef for PointerSlot { pub type PyTypeRef = PyRef; -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { unsafe impl Send for PyType {} unsafe impl Sync for PyType {} } + _ => {} } /// For attributes we do not use a dict, but an IndexMap, which is an Hash Table diff --git a/crates/vm/src/macros.rs b/crates/vm/src/macros.rs index 4fad50ac8f2..00be9a1c597 100644 --- a/crates/vm/src/macros.rs +++ b/crates/vm/src/macros.rs @@ -243,18 +243,15 @@ macro_rules! named_function { // can't use PyThreadingConstraint for stuff like this since it's not an auto trait, and // therefore we can't add it ad-hoc to a trait object -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { - macro_rules! py_dyn_fn { - (dyn Fn($($arg:ty),*$(,)*) -> $ret:ty) => { +macro_rules! py_dyn_fn { + (dyn Fn($($arg:ty),*$(,)*) -> $ret:ty) => { + cfg_select! { + feature = "threading" => { dyn Fn($($arg),*) -> $ret + Send + Sync + 'static - }; - } - } else { - macro_rules! py_dyn_fn { - (dyn Fn($($arg:ty),*$(,)*) -> $ret:ty) => { + } + _ => { dyn Fn($($arg),*) -> $ret + 'static - }; + } } - } + }; } diff --git a/crates/vm/src/object/core.rs b/crates/vm/src/object/core.rs index cf49cc42a95..290298f3652 100644 --- a/crates/vm/src/object/core.rs +++ b/crates/vm/src/object/core.rs @@ -817,11 +817,12 @@ pub struct PyWeak { pub(crate) hash: PyAtomic, } -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { unsafe impl Send for PyWeak {} unsafe impl Sync for PyWeak {} } + _ => {} } impl PyWeak { @@ -1193,11 +1194,12 @@ impl Clone for PyObjectRef { } } -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { unsafe impl Send for PyObjectRef {} unsafe impl Sync for PyObjectRef {} } + _ => {} } #[repr(transparent)] @@ -2014,11 +2016,12 @@ impl fmt::Debug for PyStackRef { } } -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { unsafe impl Send for PyStackRef {} unsafe impl Sync for PyStackRef {} } + _ => {} } // Ensure Option uses niche optimization and matches Option in size @@ -2129,11 +2132,12 @@ pub struct PyRef { ptr: NonNull>, } -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { unsafe impl Send for PyRef {} unsafe impl Sync for PyRef {} } + _ => {} } impl fmt::Debug for PyRef { diff --git a/crates/vm/src/object/ext.rs b/crates/vm/src/object/ext.rs index e39d1c7765f..a87eb3b6dd2 100644 --- a/crates/vm/src/object/ext.rs +++ b/crates/vm/src/object/ext.rs @@ -259,8 +259,8 @@ impl Drop for PyAtomicRef { } } -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { unsafe impl Send for PyAtomicRef {} unsafe impl Sync for PyAtomicRef {} unsafe impl Send for PyAtomicRef> {} @@ -270,6 +270,7 @@ cfg_if::cfg_if! { unsafe impl Send for PyAtomicRef> {} unsafe impl Sync for PyAtomicRef> {} } + _ => {} } impl fmt::Debug for PyAtomicRef { diff --git a/crates/vm/src/object/payload.rs b/crates/vm/src/object/payload.rs index a615123c680..349b239f79f 100644 --- a/crates/vm/src/object/payload.rs +++ b/crates/vm/src/object/payload.rs @@ -7,11 +7,12 @@ use crate::{ }; use core::ptr::NonNull; -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { +cfg_select! { + feature = "threading" => { pub trait PyThreadingConstraint: Send + Sync {} impl PyThreadingConstraint for T {} - } else { + } + _ => { pub trait PyThreadingConstraint {} impl PyThreadingConstraint for T {} } diff --git a/crates/vm/src/stdlib/_io.rs b/crates/vm/src/stdlib/_io.rs index 15111952d56..418431a73ee 100644 --- a/crates/vm/src/stdlib/_io.rs +++ b/crates/vm/src/stdlib/_io.rs @@ -5,19 +5,15 @@ pub(crate) use _io::module_def; #[cfg(all(unix, feature = "threading"))] pub(crate) use _io::reinit_std_streams_after_fork; -cfg_if::cfg_if! { - if #[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] { +cfg_select! { + any(not(target_arch = "wasm32"), target_os = "wasi") => { use rustpython_host_env::crt_fd::Offset; - } else { - type Offset = i64; - } -} - -// EAGAIN constant for BlockingIOError -cfg_if::cfg_if! { - if #[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] { + // EAGAIN constant for BlockingIOError const EAGAIN: i32 = libc::EAGAIN; - } else { + } + _ => { + type Offset = i64; + // EAGAIN constant for BlockingIOError const EAGAIN: i32 = 11; // Standard POSIX value } } @@ -152,12 +148,11 @@ mod _io { #[allow(clippy::let_and_return)] fn validate_whence(whence: i32) -> bool { let x = (0..=2).contains(&whence); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] { + cfg_select! { + any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux") => { x || matches!(whence, libc::SEEK_DATA | libc::SEEK_HOLE) - } else { - x } + _ => x } } @@ -5089,18 +5084,16 @@ mod _io { let is_console = false; let file_io_class: &Py = { - cfg_if::cfg_if! { - if #[cfg(all(feature = "host_env", windows))] { + cfg_select! { + all(feature = "host_env", windows) => { if is_console { Some(super::winconsoleio::WindowsConsoleIO::static_type()) } else { Some(super::fileio::FileIO::static_type()) } - } else if #[cfg(feature = "host_env")] { - Some(super::fileio::FileIO::static_type()) - } else { - None } + feature = "host_env" => Some(super::fileio::FileIO::static_type()), + _ => None, } } .ok_or_else(|| { diff --git a/crates/vm/src/stdlib/_signal.rs b/crates/vm/src/stdlib/_signal.rs index 0d9dfad311d..e80386a670a 100644 --- a/crates/vm/src/stdlib/_signal.rs +++ b/crates/vm/src/stdlib/_signal.rs @@ -22,8 +22,8 @@ pub(crate) mod _signal { #[cfg(not(any(unix, windows)))] type sighandler_t = usize; - cfg_if::cfg_if! { - if #[cfg(windows)] { + cfg_select! { + windows => { type WakeupFdRaw = libc::SOCKET; struct WakeupFd(WakeupFdRaw); const INVALID_WAKEUP: libc::SOCKET = windows_sys::Win32::Networking::WinSock::INVALID_SOCKET; @@ -47,7 +47,8 @@ pub(crate) mod _signal { } } } - } else { + } + _ => { type WakeupFdRaw = i32; type WakeupFd = WakeupFdRaw; const INVALID_WAKEUP: WakeupFd = -1; diff --git a/crates/vm/src/stdlib/_thread.rs b/crates/vm/src/stdlib/_thread.rs index b3f29a7fa34..faba2e552ea 100644 --- a/crates/vm/src/stdlib/_thread.rs +++ b/crates/vm/src/stdlib/_thread.rs @@ -31,18 +31,11 @@ pub(crate) mod _thread { use std::thread; // PYTHREAD_NAME: show current thread name - pub const PYTHREAD_NAME: Option<&str> = { - cfg_if::cfg_if! { - if #[cfg(windows)] { - Some("nt") - } else if #[cfg(unix)] { - Some("pthread") - } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { - Some("solaris") - } else { - None - } - } + pub const PYTHREAD_NAME: Option<&str> = cfg_select! { + windows => Some("nt"), + unix => Some("pthread"), + any(target_os = "solaris", target_os = "illumos") => Some("solaris"), + _ => None, }; // TIMEOUT_MAX_IN_MICROSECONDS is a value in microseconds diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 429fef19eeb..da646c95411 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -27,13 +27,15 @@ pub struct TargetIsDirectory { pub(crate) target_is_directory: bool, } -cfg_if::cfg_if! { - if #[cfg(all(any(unix, target_os = "wasi"), not(target_os = "redox")))] { +cfg_select! { + all(any(unix, target_os = "wasi"), not(target_os = "redox")) => { use libc::AT_FDCWD; - } else { + } + _ => { const AT_FDCWD: i32 = -100; } } + const DEFAULT_DIR_FD: crt_fd::Borrowed<'static> = unsafe { crt_fd::Borrowed::borrow_raw(AT_FDCWD) }; // XXX: AVAILABLE should be a bool, but we can't yet have it as a bool and just cast it to usize @@ -2110,10 +2112,11 @@ pub(super) mod _os { return Ok(None); } - cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", target_os = "redox"))] { + cfg_select! { + any(target_os = "android", target_os = "redox") => { Ok(Some("UTF-8".to_owned())) - } else if #[cfg(windows)] { + } + windows => { use windows_sys::Win32::System::Console; let cp = match fd { 0 => unsafe { Console::GetConsoleCP() }, @@ -2122,7 +2125,8 @@ pub(super) mod _os { }; Ok(Some(format!("cp{cp}"))) - } else { + } + _ => { let encoding = unsafe { let encoding = libc::nl_langinfo(libc::CODESET); if encoding.is_null() || encoding.read() == b'\0' as libc::c_char { diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index b97801d2362..8f4104af65b 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -1720,16 +1720,17 @@ pub mod module { if self.setsid { // Note: POSIX_SPAWN_SETSID may not be available on all platforms - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "haiku", target_os = "solaris", target_os = "illumos", target_os = "hurd", - ))] { + ) => { flags.insert(nix::spawn::PosixSpawnFlags::from_bits_retain(libc::POSIX_SPAWN_SETSID)); - } else { + } + _ => { return Err(vm.new_not_implemented_error( "setsid parameter is not supported on this platform", )); @@ -2047,21 +2048,16 @@ pub mod module { } #[cfg(not(target_os = "redox"))] - cfg_if::cfg_if! { - if #[cfg(all(target_os = "linux", target_env = "gnu"))] { - type PriorityWhichType = libc::__priority_which_t; - } else { - type PriorityWhichType = libc::c_int; - } - } + type PriorityWhichType = cfg_select! { + all(target_os = "linux", target_env = "gnu") => libc::__priority_which_t, + _ => libc::c_int, + }; + #[cfg(not(target_os = "redox"))] - cfg_if::cfg_if! { - if #[cfg(target_os = "freebsd")] { - type PriorityWhoType = i32; - } else { - type PriorityWhoType = u32; - } - } + type PriorityWhoType = cfg_select! { + target_os = "freebsd" => i32, + _ => u32, + }; #[cfg(not(target_os = "redox"))] #[pyfunction] diff --git a/crates/vm/src/stdlib/sys.rs b/crates/vm/src/stdlib/sys.rs index fdf66b457bc..9f36f9768e0 100644 --- a/crates/vm/src/stdlib/sys.rs +++ b/crates/vm/src/stdlib/sys.rs @@ -243,26 +243,18 @@ mod sys { pub(crate) const MAXSIZE: isize = isize::MAX; #[pyattr(name = "maxunicode")] const MAXUNICODE: u32 = core::char::MAX as u32; + #[pyattr(name = "platform")] - pub const PLATFORM: &str = { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - "linux" - } else if #[cfg(target_os = "android")] { - "android" - } else if #[cfg(target_os = "macos")] { - "darwin" - } else if #[cfg(target_os = "ios")] { - "ios" - } else if #[cfg(windows)] { - "win32" - } else if #[cfg(target_os = "wasi")] { - "wasi" - } else { - "unknown" - } - } + pub const PLATFORM: &str = cfg_select! { + target_os = "linux" => "linux", + target_os = "android" => "android", + target_os = "macos" => "darwin", + target_os = "ios" => "ios", + windows => "win32", + target_os = "wasi" => "wasi", + _ => "unknown" }; + #[pyattr(name = "ps1")] const PS1: &str = ">>>>> "; #[pyattr(name = "ps2")] diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 944e936397d..c84c5437db5 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -146,8 +146,8 @@ impl<'vm> ShellHelper<'vm> { } } -cfg_if::cfg_if! { - if #[cfg(not(target_arch = "wasm32"))] { +cfg_select! { + not(target_arch = "wasm32") => { use rustyline::{ completion::Completer, highlight::Highlighter, hint::Hinter, validate::Validator, Context, Helper, @@ -176,4 +176,5 @@ cfg_if::cfg_if! { impl Validator for ShellHelper<'_> {} impl Helper for ShellHelper<'_> {} } + _ => {} }