forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			296 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! 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);
 | |
|     }
 | |
| }
 | 
