1 //! Contains code for selecting features 2 3 #![deny(unused_extern_crates)] 4 #![deny(clippy::missing_docs_in_private_items)] 5 #![allow(deprecated)] 6 7 use std::cmp::Ordering; 8 use std::io; 9 use std::str::FromStr; 10 11 /// This macro defines the [`RustTarget`] and [`RustFeatures`] types. 12 macro_rules! define_rust_targets { 13 ( 14 Nightly => {$($nightly_feature:ident $(: #$issue:literal)?),* $(,)?} $(,)? 15 $( 16 $(#[$attrs:meta])* 17 $variant:ident($minor:literal) => {$($feature:ident $(: #$pull:literal)?),* $(,)?}, 18 )* 19 $(,)? 20 ) => { 21 /// Represents the version of the Rust language to target. 22 /// 23 /// To support a beta release, use the corresponding stable release. 24 /// 25 /// This enum will have more variants added as necessary. 26 #[allow(non_camel_case_types)] 27 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 28 pub enum RustTarget { 29 /// Rust Nightly 30 $(#[doc = concat!( 31 "- [`", stringify!($nightly_feature), "`]", 32 "(", $("https://github.com/rust-lang/rust/pull/", stringify!($issue),)* ")", 33 )])* 34 Nightly, 35 $( 36 #[doc = concat!("Rust 1.", stringify!($minor))] 37 $(#[doc = concat!( 38 "- [`", stringify!($feature), "`]", 39 "(", $("https://github.com/rust-lang/rust/pull/", stringify!($pull),)* ")", 40 )])* 41 $(#[$attrs])* 42 $variant, 43 )* 44 } 45 46 impl RustTarget { 47 fn minor(self) -> Option<u64> { 48 match self { 49 $( Self::$variant => Some($minor),)* 50 Self::Nightly => None 51 } 52 } 53 54 const fn stable_releases() -> [(Self, u64); [$($minor,)*].len()] { 55 [$((Self::$variant, $minor),)*] 56 } 57 } 58 59 #[cfg(feature = "__cli")] 60 /// Strings of allowed `RustTarget` values 61 pub const RUST_TARGET_STRINGS: &[&str] = &[$(concat!("1.", stringify!($minor)),)*]; 62 63 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 64 pub(crate) struct RustFeatures { 65 $($(pub(crate) $feature: bool,)*)* 66 $(pub(crate) $nightly_feature: bool,)* 67 } 68 69 impl From<RustTarget> for RustFeatures { 70 fn from(target: RustTarget) -> Self { 71 if target == RustTarget::Nightly { 72 return Self { 73 $($($feature: true,)*)* 74 $($nightly_feature: true,)* 75 }; 76 } 77 78 let mut features = Self { 79 $($($feature: false,)*)* 80 $($nightly_feature: false,)* 81 }; 82 83 $(if target >= RustTarget::$variant { 84 $(features.$feature = true;)* 85 })* 86 87 features 88 } 89 } 90 }; 91 } 92 93 // NOTE: When adding or removing features here, make sure to add the stabilization PR 94 // number for the feature if it has been stabilized or the tracking issue number if the feature is 95 // not stable. 96 define_rust_targets! { 97 Nightly => { 98 vectorcall_abi, 99 }, 100 Stable_1_73(73) => { thiscall_abi: #42202 }, 101 Stable_1_71(71) => { c_unwind_abi: #106075 }, 102 Stable_1_68(68) => { abi_efiapi: #105795 }, 103 Stable_1_64(64) => { core_ffi_c: #94503 }, 104 Stable_1_59(59) => { const_cstr: #54745 }, 105 Stable_1_47(47) => { larger_arrays: #74060 }, 106 Stable_1_40(40) => { non_exhaustive: #44109 }, 107 Stable_1_36(36) => { maybe_uninit: #60445 }, 108 Stable_1_33(33) => { repr_packed_n: #57049 }, 109 #[deprecated] 110 Stable_1_30(30) => { 111 core_ffi_c_void: #53910, 112 min_const_fn: #54835, 113 }, 114 #[deprecated] 115 Stable_1_28(28) => { repr_transparent: #51562 }, 116 #[deprecated] 117 Stable_1_27(27) => { must_use_function: #48925 }, 118 #[deprecated] 119 Stable_1_26(26) => { i128_and_u128: #49101 }, 120 #[deprecated] 121 Stable_1_25(25) => { repr_align: #47006 }, 122 #[deprecated] 123 Stable_1_21(21) => { builtin_clone_impls: #43690 }, 124 #[deprecated] 125 Stable_1_20(20) => { associated_const: #42809 }, 126 #[deprecated] 127 Stable_1_19(19) => { untagged_union: #42068 }, 128 #[deprecated] 129 Stable_1_17(17) => { static_lifetime_elision: #39265 }, 130 #[deprecated] 131 Stable_1_0(0) => {}, 132 } 133 134 /// Latest stable release of Rust 135 pub const LATEST_STABLE_RUST: RustTarget = { 136 // FIXME: replace all this code by 137 // ``` 138 // RustTarget::stable_releases() 139 // .into_iter() 140 // .max_by_key(|(_, m)| m) 141 // .map(|(t, _)| t) 142 // .unwrap_or(RustTarget::Nightly) 143 // ``` 144 // once those operations can be used in constants. 145 let targets = RustTarget::stable_releases(); 146 147 let mut i = 0; 148 let mut latest_target = None; 149 let mut latest_minor = 0; 150 151 while i < targets.len() { 152 let (target, minor) = targets[i]; 153 154 if latest_minor < minor { 155 latest_minor = minor; 156 latest_target = Some(target); 157 } 158 159 i += 1; 160 } 161 162 match latest_target { 163 Some(target) => target, 164 None => unreachable!(), 165 } 166 }; 167 168 impl Default for RustTarget { default() -> Self169 fn default() -> Self { 170 LATEST_STABLE_RUST 171 } 172 } 173 174 impl PartialOrd for RustTarget { partial_cmp(&self, other: &Self) -> Option<Ordering>175 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 176 Some(self.cmp(other)) 177 } 178 } 179 180 impl Ord for RustTarget { cmp(&self, other: &Self) -> Ordering181 fn cmp(&self, other: &Self) -> Ordering { 182 match (self.minor(), other.minor()) { 183 (Some(a), Some(b)) => a.cmp(&b), 184 (Some(_), None) => Ordering::Less, 185 (None, Some(_)) => Ordering::Greater, 186 (None, None) => Ordering::Equal, 187 } 188 } 189 } 190 191 impl FromStr for RustTarget { 192 type Err = io::Error; 193 from_str(s: &str) -> Result<Self, Self::Err>194 fn from_str(s: &str) -> Result<Self, Self::Err> { 195 if s == "nightly" { 196 return Ok(Self::Nightly); 197 } 198 199 if let Some(("1", str_minor)) = s.split_once('.') { 200 if let Ok(minor) = str_minor.parse::<u64>() { 201 for (target, target_minor) in Self::stable_releases() { 202 if minor == target_minor { 203 return Ok(target); 204 } 205 } 206 } 207 } 208 209 Err(io::Error::new( 210 io::ErrorKind::InvalidInput, 211 "Got an invalid Rust target. Accepted values are of the form \"1.71\" or \"nightly\"." 212 )) 213 } 214 } 215 216 impl std::fmt::Display for RustTarget { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 218 match self.minor() { 219 Some(minor) => write!(f, "1.{}", minor), 220 None => "nightly".fmt(f), 221 } 222 } 223 } 224 225 impl Default for RustFeatures { default() -> Self226 fn default() -> Self { 227 RustTarget::default().into() 228 } 229 } 230 231 #[cfg(test)] 232 mod test { 233 #![allow(unused_imports)] 234 use super::*; 235 236 #[test] target_features()237 fn target_features() { 238 let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0); 239 assert!( 240 !f_1_0.static_lifetime_elision && 241 !f_1_0.core_ffi_c_void && 242 !f_1_0.untagged_union && 243 !f_1_0.associated_const && 244 !f_1_0.builtin_clone_impls && 245 !f_1_0.repr_align && 246 !f_1_0.thiscall_abi && 247 !f_1_0.vectorcall_abi 248 ); 249 let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21); 250 assert!( 251 f_1_21.static_lifetime_elision && 252 !f_1_21.core_ffi_c_void && 253 f_1_21.untagged_union && 254 f_1_21.associated_const && 255 f_1_21.builtin_clone_impls && 256 !f_1_21.repr_align && 257 !f_1_21.thiscall_abi && 258 !f_1_21.vectorcall_abi 259 ); 260 let features = RustFeatures::from(RustTarget::Stable_1_71); 261 assert!( 262 features.c_unwind_abi && 263 features.abi_efiapi && 264 !features.thiscall_abi 265 ); 266 let f_nightly = RustFeatures::from(RustTarget::Nightly); 267 assert!( 268 f_nightly.static_lifetime_elision && 269 f_nightly.core_ffi_c_void && 270 f_nightly.untagged_union && 271 f_nightly.associated_const && 272 f_nightly.builtin_clone_impls && 273 f_nightly.maybe_uninit && 274 f_nightly.repr_align && 275 f_nightly.thiscall_abi && 276 f_nightly.vectorcall_abi 277 ); 278 } 279 test_target(target_str: &str, target: RustTarget)280 fn test_target(target_str: &str, target: RustTarget) { 281 let target_string = target.to_string(); 282 assert_eq!(target_str, target_string); 283 assert_eq!(target, RustTarget::from_str(target_str).unwrap()); 284 } 285 286 #[test] str_to_target()287 fn str_to_target() { 288 test_target("1.0", RustTarget::Stable_1_0); 289 test_target("1.17", RustTarget::Stable_1_17); 290 test_target("1.19", RustTarget::Stable_1_19); 291 test_target("1.21", RustTarget::Stable_1_21); 292 test_target("1.25", RustTarget::Stable_1_25); 293 test_target("1.71", RustTarget::Stable_1_71); 294 test_target("nightly", RustTarget::Nightly); 295 } 296 } 297