1 //! Contains code for selecting features 2 3 #![deny(missing_docs)] 4 #![deny(unused_extern_crates)] 5 6 use std::io; 7 use std::str::FromStr; 8 9 /// Define RustTarget struct definition, Default impl, and conversions 10 /// between RustTarget and String. 11 macro_rules! rust_target_def { 12 ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { 13 /// Represents the version of the Rust language to target. 14 /// 15 /// To support a beta release, use the corresponding stable release. 16 /// 17 /// This enum will have more variants added as necessary. 18 #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash)] 19 #[allow(non_camel_case_types)] 20 pub enum RustTarget { 21 $( 22 $( 23 #[$attr] 24 )* 25 $release, 26 )* 27 } 28 29 impl Default for RustTarget { 30 /// Gives the latest stable Rust version 31 fn default() -> RustTarget { 32 LATEST_STABLE_RUST 33 } 34 } 35 36 impl FromStr for RustTarget { 37 type Err = io::Error; 38 39 /// Create a `RustTarget` from a string. 40 /// 41 /// * The stable/beta versions of Rust are of the form "1.0", 42 /// "1.19", etc. 43 /// * The nightly version should be specified with "nightly". 44 fn from_str(s: &str) -> Result<Self, Self::Err> { 45 match s.as_ref() { 46 $( 47 stringify!($value) => Ok(RustTarget::$release), 48 )* 49 _ => Err( 50 io::Error::new( 51 io::ErrorKind::InvalidInput, 52 concat!( 53 "Got an invalid rust target. Accepted values ", 54 "are of the form ", 55 "\"1.0\" or \"nightly\"."))), 56 } 57 } 58 } 59 60 impl From<RustTarget> for String { 61 fn from(target: RustTarget) -> Self { 62 match target { 63 $( 64 RustTarget::$release => stringify!($value), 65 )* 66 }.into() 67 } 68 } 69 } 70 } 71 72 /// Defines an array slice with all RustTarget values 73 macro_rules! rust_target_values_def { 74 ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { 75 /// Strings of allowed `RustTarget` values 76 pub static RUST_TARGET_STRINGS: &'static [&str] = &[ 77 $( 78 stringify!($value), 79 )* 80 ]; 81 } 82 } 83 84 /// Defines macro which takes a macro 85 macro_rules! rust_target_base { 86 ( $x_macro:ident ) => { 87 $x_macro!( 88 /// Rust stable 1.0 89 => Stable_1_0 => 1.0; 90 /// Rust stable 1.17 91 /// * Static lifetime elision ([RFC 1623](https://github.com/rust-lang/rfcs/blob/master/text/1623-static.md)) 92 => Stable_1_17 => 1.17; 93 /// Rust stable 1.19 94 /// * Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) 95 => Stable_1_19 => 1.19; 96 /// Rust stable 1.20 97 /// * Associated constants ([PR](https://github.com/rust-lang/rust/pull/42809)) 98 => Stable_1_20 => 1.20; 99 /// Rust stable 1.21 100 /// * Builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690)) 101 => Stable_1_21 => 1.21; 102 /// Rust stable 1.25 103 /// * `repr(align)` ([PR](https://github.com/rust-lang/rust/pull/47006)) 104 => Stable_1_25 => 1.25; 105 /// Rust stable 1.26 106 /// * [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html) 107 => Stable_1_26 => 1.26; 108 /// Rust stable 1.27 109 /// * `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925)) 110 => Stable_1_27 => 1.27; 111 /// Rust stable 1.28 112 /// * `repr(transparent)` ([PR](https://github.com/rust-lang/rust/pull/51562)) 113 => Stable_1_28 => 1.28; 114 /// Rust stable 1.30 115 /// * `const fn` support for limited cases ([PR](https://github.com/rust-lang/rust/pull/54835/) 116 /// * [c_void available in core](https://doc.rust-lang.org/core/ffi/enum.c_void.html) 117 => Stable_1_30 => 1.30; 118 /// Rust stable 1.33 119 /// * repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049)) 120 => Stable_1_33 => 1.33; 121 /// Rust stable 1.36 122 /// * `MaybeUninit` instead of `mem::uninitialized()` ([PR](https://github.com/rust-lang/rust/pull/60445)) 123 => Stable_1_36 => 1.36; 124 /// Rust stable 1.40 125 /// * `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109)) 126 => Stable_1_40 => 1.40; 127 /// Rust stable 1.47 128 /// * `larger_arrays` ([Tracking issue](https://github.com/rust-lang/rust/pull/74060)) 129 => Stable_1_47 => 1.47; 130 /// Nightly rust 131 /// * `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202)) 132 => Nightly => nightly; 133 ); 134 } 135 } 136 137 rust_target_base!(rust_target_def); 138 rust_target_base!(rust_target_values_def); 139 140 /// Latest stable release of Rust 141 pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_47; 142 143 /// Create RustFeatures struct definition, new(), and a getter for each field 144 macro_rules! rust_feature_def { 145 ( 146 $( $rust_target:ident { 147 $( $( #[$attr:meta] )* => $feature:ident; )* 148 } )* 149 ) => { 150 /// Features supported by a rust target 151 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 152 #[allow(missing_docs)] // Documentation should go into the relevant variants. 153 pub(crate) struct RustFeatures { 154 $( $( 155 $( 156 #[$attr] 157 )* 158 pub $feature: bool, 159 )* )* 160 } 161 162 impl RustFeatures { 163 /// Gives a RustFeatures struct with all features disabled 164 fn new() -> Self { 165 RustFeatures { 166 $( $( 167 $feature: false, 168 )* )* 169 } 170 } 171 } 172 173 impl From<RustTarget> for RustFeatures { 174 fn from(rust_target: RustTarget) -> Self { 175 let mut features = RustFeatures::new(); 176 177 $( 178 if rust_target >= RustTarget::$rust_target { 179 $( 180 features.$feature = true; 181 )* 182 } 183 )* 184 185 features 186 } 187 } 188 } 189 } 190 191 // NOTE(emilio): When adding or removing features here, make sure to update the 192 // documentation for the relevant variant in the rust_target_base macro 193 // definition. 194 rust_feature_def!( 195 Stable_1_17 { 196 => static_lifetime_elision; 197 } 198 Stable_1_19 { 199 => untagged_union; 200 } 201 Stable_1_20 { 202 => associated_const; 203 } 204 Stable_1_21 { 205 => builtin_clone_impls; 206 } 207 Stable_1_25 { 208 => repr_align; 209 } 210 Stable_1_26 { 211 => i128_and_u128; 212 } 213 Stable_1_27 { 214 => must_use_function; 215 } 216 Stable_1_28 { 217 => repr_transparent; 218 } 219 Stable_1_30 { 220 => min_const_fn; 221 => core_ffi_c_void; 222 } 223 Stable_1_33 { 224 => repr_packed_n; 225 } 226 Stable_1_36 { 227 => maybe_uninit; 228 } 229 Stable_1_40 { 230 => non_exhaustive; 231 } 232 Stable_1_47 { 233 => larger_arrays; 234 } 235 Nightly { 236 => thiscall_abi; 237 } 238 ); 239 240 impl Default for RustFeatures { default() -> Self241 fn default() -> Self { 242 let default_rust_target: RustTarget = Default::default(); 243 Self::from(default_rust_target) 244 } 245 } 246 247 #[cfg(test)] 248 mod test { 249 #![allow(unused_imports)] 250 use super::*; 251 252 #[test] target_features()253 fn target_features() { 254 let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0); 255 assert!( 256 !f_1_0.static_lifetime_elision && 257 !f_1_0.core_ffi_c_void && 258 !f_1_0.untagged_union && 259 !f_1_0.associated_const && 260 !f_1_0.builtin_clone_impls && 261 !f_1_0.repr_align && 262 !f_1_0.thiscall_abi 263 ); 264 let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21); 265 assert!( 266 f_1_21.static_lifetime_elision && 267 !f_1_21.core_ffi_c_void && 268 f_1_21.untagged_union && 269 f_1_21.associated_const && 270 f_1_21.builtin_clone_impls && 271 !f_1_21.repr_align && 272 !f_1_21.thiscall_abi 273 ); 274 let f_nightly = RustFeatures::from(RustTarget::Nightly); 275 assert!( 276 f_nightly.static_lifetime_elision && 277 f_nightly.core_ffi_c_void && 278 f_nightly.untagged_union && 279 f_nightly.associated_const && 280 f_nightly.builtin_clone_impls && 281 f_nightly.maybe_uninit && 282 f_nightly.repr_align && 283 f_nightly.thiscall_abi 284 ); 285 } 286 test_target(target_str: &str, target: RustTarget)287 fn test_target(target_str: &str, target: RustTarget) { 288 let target_string: String = target.into(); 289 assert_eq!(target_str, target_string); 290 assert_eq!(target, RustTarget::from_str(target_str).unwrap()); 291 } 292 293 #[test] str_to_target()294 fn str_to_target() { 295 test_target("1.0", RustTarget::Stable_1_0); 296 test_target("1.17", RustTarget::Stable_1_17); 297 test_target("1.19", RustTarget::Stable_1_19); 298 test_target("1.21", RustTarget::Stable_1_21); 299 test_target("1.25", RustTarget::Stable_1_25); 300 test_target("nightly", RustTarget::Nightly); 301 } 302 } 303