diff options
Diffstat (limited to 'third_party/rust/bindgen/features.rs')
-rw-r--r-- | third_party/rust/bindgen/features.rs | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/features.rs b/third_party/rust/bindgen/features.rs new file mode 100644 index 0000000000..e5e2068053 --- /dev/null +++ b/third_party/rust/bindgen/features.rs @@ -0,0 +1,296 @@ +//! Contains code for selecting features + +#![deny(unused_extern_crates)] +#![deny(clippy::missing_docs_in_private_items)] +#![allow(deprecated)] + +use std::cmp::Ordering; +use std::io; +use std::str::FromStr; + +/// This macro defines the [`RustTarget`] and [`RustFeatures`] types. +macro_rules! define_rust_targets { + ( + Nightly => {$($nightly_feature:ident $(: #$issue:literal)?),* $(,)?} $(,)? + $( + $(#[$attrs:meta])* + $variant:ident($minor:literal) => {$($feature:ident $(: #$pull:literal)?),* $(,)?}, + )* + $(,)? + ) => { + /// Represents the version of the Rust language to target. + /// + /// To support a beta release, use the corresponding stable release. + /// + /// This enum will have more variants added as necessary. + #[allow(non_camel_case_types)] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + pub enum RustTarget { + /// Rust Nightly + $(#[doc = concat!( + "- [`", stringify!($nightly_feature), "`]", + "(", $("https://github.com/rust-lang/rust/pull/", stringify!($issue),)* ")", + )])* + Nightly, + $( + #[doc = concat!("Rust 1.", stringify!($minor))] + $(#[doc = concat!( + "- [`", stringify!($feature), "`]", + "(", $("https://github.com/rust-lang/rust/pull/", stringify!($pull),)* ")", + )])* + $(#[$attrs])* + $variant, + )* + } + + impl RustTarget { + fn minor(self) -> Option<u64> { + match self { + $( Self::$variant => Some($minor),)* + Self::Nightly => None + } + } + + const fn stable_releases() -> [(Self, u64); [$($minor,)*].len()] { + [$((Self::$variant, $minor),)*] + } + } + + #[cfg(feature = "__cli")] + /// Strings of allowed `RustTarget` values + pub const RUST_TARGET_STRINGS: &[&str] = &[$(concat!("1.", stringify!($minor)),)*]; + + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub(crate) struct RustFeatures { + $($(pub(crate) $feature: bool,)*)* + $(pub(crate) $nightly_feature: bool,)* + } + + impl From<RustTarget> for RustFeatures { + fn from(target: RustTarget) -> Self { + if target == RustTarget::Nightly { + return Self { + $($($feature: true,)*)* + $($nightly_feature: true,)* + }; + } + + let mut features = Self { + $($($feature: false,)*)* + $($nightly_feature: false,)* + }; + + $(if target >= RustTarget::$variant { + $(features.$feature = true;)* + })* + + features + } + } + }; +} + +// NOTE: When adding or removing features here, make sure to add the stabilization PR +// number for the feature if it has been stabilized or the tracking issue number if the feature is +// not stable. +define_rust_targets! { + Nightly => { + vectorcall_abi, + }, + Stable_1_73(73) => { thiscall_abi: #42202 }, + Stable_1_71(71) => { c_unwind_abi: #106075 }, + Stable_1_68(68) => { abi_efiapi: #105795 }, + Stable_1_64(64) => { core_ffi_c: #94503 }, + Stable_1_59(59) => { const_cstr: #54745 }, + Stable_1_47(47) => { larger_arrays: #74060 }, + Stable_1_40(40) => { non_exhaustive: #44109 }, + Stable_1_36(36) => { maybe_uninit: #60445 }, + Stable_1_33(33) => { repr_packed_n: #57049 }, + #[deprecated] + Stable_1_30(30) => { + core_ffi_c_void: #53910, + min_const_fn: #54835, + }, + #[deprecated] + Stable_1_28(28) => { repr_transparent: #51562 }, + #[deprecated] + Stable_1_27(27) => { must_use_function: #48925 }, + #[deprecated] + Stable_1_26(26) => { i128_and_u128: #49101 }, + #[deprecated] + Stable_1_25(25) => { repr_align: #47006 }, + #[deprecated] + Stable_1_21(21) => { builtin_clone_impls: #43690 }, + #[deprecated] + Stable_1_20(20) => { associated_const: #42809 }, + #[deprecated] + Stable_1_19(19) => { untagged_union: #42068 }, + #[deprecated] + Stable_1_17(17) => { static_lifetime_elision: #39265 }, + #[deprecated] + Stable_1_0(0) => {}, +} + +/// Latest stable release of Rust +pub const LATEST_STABLE_RUST: RustTarget = { + // FIXME: replace all this code by + // ``` + // RustTarget::stable_releases() + // .into_iter() + // .max_by_key(|(_, m)| m) + // .map(|(t, _)| t) + // .unwrap_or(RustTarget::Nightly) + // ``` + // once those operations can be used in constants. + let targets = RustTarget::stable_releases(); + + let mut i = 0; + let mut latest_target = None; + let mut latest_minor = 0; + + while i < targets.len() { + let (target, minor) = targets[i]; + + if latest_minor < minor { + latest_minor = minor; + latest_target = Some(target); + } + + i += 1; + } + + match latest_target { + Some(target) => target, + None => unreachable!(), + } +}; + +impl Default for RustTarget { + fn default() -> Self { + LATEST_STABLE_RUST + } +} + +impl PartialOrd for RustTarget { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for RustTarget { + fn cmp(&self, other: &Self) -> Ordering { + match (self.minor(), other.minor()) { + (Some(a), Some(b)) => a.cmp(&b), + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + (None, None) => Ordering::Equal, + } + } +} + +impl FromStr for RustTarget { + type Err = io::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + if s == "nightly" { + return Ok(Self::Nightly); + } + + if let Some(("1", str_minor)) = s.split_once('.') { + if let Ok(minor) = str_minor.parse::<u64>() { + for (target, target_minor) in Self::stable_releases() { + if minor == target_minor { + return Ok(target); + } + } + } + } + + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Got an invalid Rust target. Accepted values are of the form \"1.71\" or \"nightly\"." + )) + } +} + +impl std::fmt::Display for RustTarget { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.minor() { + Some(minor) => write!(f, "1.{}", minor), + None => "nightly".fmt(f), + } + } +} + +impl Default for RustFeatures { + fn default() -> Self { + RustTarget::default().into() + } +} + +#[cfg(test)] +mod test { + #![allow(unused_imports)] + use super::*; + + #[test] + fn target_features() { + let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0); + assert!( + !f_1_0.static_lifetime_elision && + !f_1_0.core_ffi_c_void && + !f_1_0.untagged_union && + !f_1_0.associated_const && + !f_1_0.builtin_clone_impls && + !f_1_0.repr_align && + !f_1_0.thiscall_abi && + !f_1_0.vectorcall_abi + ); + let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21); + assert!( + f_1_21.static_lifetime_elision && + !f_1_21.core_ffi_c_void && + f_1_21.untagged_union && + f_1_21.associated_const && + f_1_21.builtin_clone_impls && + !f_1_21.repr_align && + !f_1_21.thiscall_abi && + !f_1_21.vectorcall_abi + ); + let features = RustFeatures::from(RustTarget::Stable_1_71); + assert!( + features.c_unwind_abi && + features.abi_efiapi && + !features.thiscall_abi + ); + let f_nightly = RustFeatures::from(RustTarget::Nightly); + assert!( + f_nightly.static_lifetime_elision && + f_nightly.core_ffi_c_void && + f_nightly.untagged_union && + f_nightly.associated_const && + f_nightly.builtin_clone_impls && + f_nightly.maybe_uninit && + f_nightly.repr_align && + f_nightly.thiscall_abi && + f_nightly.vectorcall_abi + ); + } + + fn test_target(target_str: &str, target: RustTarget) { + let target_string = target.to_string(); + assert_eq!(target_str, target_string); + assert_eq!(target, RustTarget::from_str(target_str).unwrap()); + } + + #[test] + fn str_to_target() { + test_target("1.0", RustTarget::Stable_1_0); + test_target("1.17", RustTarget::Stable_1_17); + test_target("1.19", RustTarget::Stable_1_19); + test_target("1.21", RustTarget::Stable_1_21); + test_target("1.25", RustTarget::Stable_1_25); + test_target("1.71", RustTarget::Stable_1_71); + test_target("nightly", RustTarget::Nightly); + } +} |