From bf5e79bb2274c4515a04d18c5d82d62f172e46ed Mon Sep 17 00:00:00 2001 From: Christian Poveda Ruiz <31802960+pvdrz@users.noreply.github.com> Date: Fri, 28 Jul 2023 12:20:01 -0500 Subject: [PATCH] Refactor the `features` module (#2592) * Refactor the `features` module Co-authored-by: Tshepang Mbambo --- bindgen-cli/options.rs | 4 +- bindgen/codegen/mod.rs | 2 +- bindgen/features.rs | 395 ++++++++++++++++++----------------------- bindgen/lib.rs | 7 +- bindgen/options/mod.rs | 2 +- 5 files changed, 184 insertions(+), 226 deletions(-) diff --git a/bindgen-cli/options.rs b/bindgen-cli/options.rs index 8031a138f9..66f068e474 100644 --- a/bindgen-cli/options.rs +++ b/bindgen-cli/options.rs @@ -12,9 +12,9 @@ use std::process::exit; fn rust_target_help() -> String { format!( - "Version of the Rust compiler to target. Valid options are: {:?}. Defaults to {:?}.", + "Version of the Rust compiler to target. Valid options are: {:?}. Defaults to {}.", RUST_TARGET_STRINGS, - String::from(RustTarget::default()) + RustTarget::default() ) } diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 31a4b23a2e..d207d5a98f 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4348,7 +4348,7 @@ fn unsupported_abi_diagnostic( .add_annotation( format!( "The configured Rust version is {}.", - String::from(ctx.options().rust_target) + ctx.options().rust_target ), Level::Note, ); diff --git a/bindgen/features.rs b/bindgen/features.rs index 12e78db44e..67c6fb4565 100644 --- a/bindgen/features.rs +++ b/bindgen/features.rs @@ -4,270 +4,227 @@ #![deny(clippy::missing_docs_in_private_items)] #![allow(deprecated)] +use std::cmp::Ordering; use std::io; use std::str::FromStr; -/// Define RustTarget struct definition, Default impl, and conversions -/// between RustTarget and String. -macro_rules! rust_target_def { - ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { +/// 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. - #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash)] #[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, $( - $( - #[$attr] - )* - $release, + #[doc = concat!("Rust 1.", stringify!($minor))] + $(#[doc = concat!( + "- [`", stringify!($feature), "`]", + "(", $("https://github.com/rust-lang/rust/pull/", stringify!($pull),)* ")", + )])* + $(#[$attrs])* + $variant, )* } - impl Default for RustTarget { - /// Gives the latest stable Rust version - fn default() -> RustTarget { - LATEST_STABLE_RUST + impl RustTarget { + fn minor(self) -> Option { + match self { + $( Self::$variant => Some($minor),)* + Self::Nightly => None + } + } + + const fn stable_releases() -> [(Self, u64); [$($minor,)*].len()] { + [$((Self::$variant, $minor),)*] } } - impl FromStr for RustTarget { - type Err = io::Error; + #[cfg(feature = "__cli")] + /// Strings of allowed `RustTarget` values + pub const RUST_TARGET_STRINGS: &[&str] = &[$(concat!("1.", stringify!($minor)),)*]; - /// Create a `RustTarget` from a string. - /// - /// * The stable/beta versions of Rust are of the form "1.0", - /// "1.19", etc. - /// * The nightly version should be specified with "nightly". - fn from_str(s: &str) -> Result { - match s.as_ref() { - $( - stringify!($value) => Ok(RustTarget::$release), - )* - _ => Err( - io::Error::new( - io::ErrorKind::InvalidInput, - concat!( - "Got an invalid rust target. Accepted values ", - "are of the form ", - "\"1.0\" or \"nightly\"."))), - } - } + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub(crate) struct RustFeatures { + $($(pub(crate) $feature: bool,)*)* + $(pub(crate) $nightly_feature: bool,)* } - impl From for String { + impl From for RustFeatures { fn from(target: RustTarget) -> Self { - match target { - $( - RustTarget::$release => stringify!($value), - )* - }.into() + 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 } } - } + }; } -/// Defines an array slice with all RustTarget values -macro_rules! rust_target_values_def { - ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { - /// Strings of allowed `RustTarget` values - pub static RUST_TARGET_STRINGS: &'static [&str] = &[ - $( - stringify!($value), - )* - ]; +// 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 => { + thiscall_abi: #42202, + vectorcall_abi, + }, + 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 } } -/// Defines macro which takes a macro -macro_rules! rust_target_base { - ( $x_macro:ident ) => { - $x_macro!( - /// Rust stable 1.0 - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_0 => 1.0; - /// Rust stable 1.17 - /// * Static lifetime elision ([RFC 1623](https://github.com/rust-lang/rfcs/blob/master/text/1623-static.md)) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_17 => 1.17; - /// Rust stable 1.19 - /// * Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_19 => 1.19; - /// Rust stable 1.20 - /// * Associated constants ([PR](https://github.com/rust-lang/rust/pull/42809)) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_20 => 1.20; - /// Rust stable 1.21 - /// * Builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690)) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_21 => 1.21; - /// Rust stable 1.25 - /// * `repr(align)` ([PR](https://github.com/rust-lang/rust/pull/47006)) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_25 => 1.25; - /// Rust stable 1.26 - /// * [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_26 => 1.26; - /// Rust stable 1.27 - /// * `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925)) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_27 => 1.27; - /// Rust stable 1.28 - /// * `repr(transparent)` ([PR](https://github.com/rust-lang/rust/pull/51562)) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_28 => 1.28; - /// Rust stable 1.30 - /// * `const fn` support for limited cases ([PR](https://github.com/rust-lang/rust/pull/54835/) - /// * [c_void available in core](https://doc.rust-lang.org/core/ffi/enum.c_void.html) - #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_30 => 1.30; - /// Rust stable 1.33 - /// * repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049)) - => Stable_1_33 => 1.33; - /// Rust stable 1.36 - /// * `MaybeUninit` instead of `mem::uninitialized()` ([PR](https://github.com/rust-lang/rust/pull/60445)) - => Stable_1_36 => 1.36; - /// Rust stable 1.40 - /// * `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109)) - => Stable_1_40 => 1.40; - /// Rust stable 1.47 - /// * `larger_arrays` ([Tracking issue](https://github.com/rust-lang/rust/pull/74060)) - => Stable_1_47 => 1.47; - /// Rust stable 1.59 - /// * `CStr::from_bytes_with_nul_unchecked` in `const` contexts ([PR](https://github.com/rust-lang/rust/pull/54745)) - => Stable_1_59 => 1.59; - /// Rust stable 1.64 - /// * `core_ffi_c` ([Tracking issue](https://github.com/rust-lang/rust/issues/94501)) - => Stable_1_64 => 1.64; - /// Rust stable 1.68 - /// * `abi_efiapi` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/65815)) - => Stable_1_68 => 1.68; - /// Rust stable 1.71 - /// * `c_unwind` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/74990)) - => Stable_1_71 => 1.71; - /// Nightly rust - /// * `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202)) - /// * `vectorcall` calling convention (no tracking issue) - => Nightly => nightly; - ); +impl PartialOrd for RustTarget { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } -rust_target_base!(rust_target_def); -rust_target_base!(rust_target_values_def); +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, + } + } +} -/// Latest stable release of Rust -pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_71; +impl FromStr for RustTarget { + type Err = io::Error; -/// Create RustFeatures struct definition, new(), and a getter for each field -macro_rules! rust_feature_def { - ( - $( $rust_target:ident { - $( $( #[$attr:meta] )* => $feature:ident; )* - } )* - ) => { - /// Features supported by a rust target - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] - #[allow(missing_docs)] // Documentation should go into the relevant variants. - pub(crate) struct RustFeatures { - $( $( - $( - #[$attr] - )* - pub $feature: bool, - )* )* + fn from_str(s: &str) -> Result { + if s == "nightly" { + return Ok(Self::Nightly); } - impl RustFeatures { - /// Gives a RustFeatures struct with all features disabled - fn new() -> Self { - RustFeatures { - $( $( - $feature: false, - )* )* + if let Some(("1", str_minor)) = s.split_once('.') { + if let Ok(minor) = str_minor.parse::() { + for (target, target_minor) in Self::stable_releases() { + if minor == target_minor { + return Ok(target); + } } } } - impl From for RustFeatures { - fn from(rust_target: RustTarget) -> Self { - let mut features = RustFeatures::new(); - - $( - if rust_target >= RustTarget::$rust_target { - $( - features.$feature = true; - )* - } - )* - - features - } - } + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Got an invalid Rust target. Accepted values are of the form \"1.71\" or \"nightly\"." + )) } } -// NOTE(emilio): When adding or removing features here, make sure to update the -// documentation for the relevant variant in the rust_target_base macro -// definition. -rust_feature_def!( - Stable_1_17 { - => static_lifetime_elision; - } - Stable_1_19 { - => untagged_union; - } - Stable_1_20 { - => associated_const; - } - Stable_1_21 { - => builtin_clone_impls; - } - Stable_1_25 { - => repr_align; - } - Stable_1_26 { - => i128_and_u128; - } - Stable_1_27 { - => must_use_function; - } - Stable_1_28 { - => repr_transparent; - } - Stable_1_30 { - => min_const_fn; - => core_ffi_c_void; - } - Stable_1_33 { - => repr_packed_n; - } - Stable_1_36 { - => maybe_uninit; - } - Stable_1_40 { - => non_exhaustive; - } - Stable_1_47 { - => larger_arrays; - } - Stable_1_59 { - => const_cstr; - } - Stable_1_64 { - => core_ffi_c; - } - Stable_1_68 { - => abi_efiapi; - } - Stable_1_71 { - => c_unwind_abi; - } - Nightly { - => thiscall_abi; - => vectorcall_abi; +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 { - let default_rust_target: RustTarget = Default::default(); - Self::from(default_rust_target) + RustTarget::default().into() } } @@ -321,7 +278,7 @@ mod test { } fn test_target(target_str: &str, target: RustTarget) { - let target_string: String = target.into(); + let target_string = target.to_string(); assert_eq!(target_str, target_string); assert_eq!(target, RustTarget::from_str(target_str).unwrap()); } diff --git a/bindgen/lib.rs b/bindgen/lib.rs index 59496823c8..a6c929f4c7 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -52,7 +52,9 @@ mod regex_set; pub use codegen::{ AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle, }; -pub use features::{RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS}; +#[cfg(feature = "__cli")] +pub use features::RUST_TARGET_STRINGS; +pub use features::{RustTarget, LATEST_STABLE_RUST}; pub use ir::annotations::FieldVisibilityKind; pub use ir::function::Abi; pub use regex_set::RegexSet; @@ -568,8 +570,7 @@ impl BindgenOptions { } fn deprecated_target_diagnostic(target: RustTarget, _options: &BindgenOptions) { - let target = String::from(target); - warn!("The {} Rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", target,); + warn!("The {} Rust target is deprecated. If you have a need to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", target); #[cfg(feature = "experimental")] if _options.emit_diagnostics { diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index cda04ee8c7..91620ce0d6 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -1543,7 +1543,7 @@ options! { }, as_args: |rust_target, args| { args.push("--rust-target".to_owned()); - args.push((*rust_target).into()); + args.push(rust_target.to_string()); }, }, /// Features to be enabled. They are derived from `rust_target`.