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.1 91 => Stable_1_1 => 1.1; 92 /// Rust stable 1.19 93 /// * Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) 94 => Stable_1_19 => 1.19; 95 /// Rust stable 1.20 96 /// * Associated constants ([PR](https://github.com/rust-lang/rust/pull/42809)) 97 => Stable_1_20 => 1.20; 98 /// Rust stable 1.21 99 /// * Builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690)) 100 => Stable_1_21 => 1.21; 101 /// Rust stable 1.25 102 /// * `repr(align)` ([PR](https://github.com/rust-lang/rust/pull/47006)) 103 => Stable_1_25 => 1.25; 104 /// Rust stable 1.26 105 /// * [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html) 106 => Stable_1_26 => 1.26; 107 /// Rust stable 1.27 108 /// * `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925)) 109 => Stable_1_27 => 1.27; 110 /// Rust stable 1.28 111 /// * `repr(transparent)` ([PR](https://github.com/rust-lang/rust/pull/51562)) 112 => Stable_1_28 => 1.28; 113 /// Rust stable 1.30 114 /// * `const fn` support for limited cases ([PR](https://github.com/rust-lang/rust/pull/54835/) 115 /// * [c_void available in core](https://doc.rust-lang.org/core/ffi/enum.c_void.html) 116 => Stable_1_30 => 1.30; 117 /// Rust stable 1.33 118 /// * repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049)) 119 => Stable_1_33 => 1.33; 120 /// Rust stable 1.36 121 /// * `MaybeUninit` instead of `mem::uninitialized()` ([PR](https://github.com/rust-lang/rust/pull/60445)) 122 => Stable_1_36 => 1.36; 123 /// Rust stable 1.40 124 /// * `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109)) 125 => Stable_1_40 => 1.40; 126 /// Nightly rust 127 /// * `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202)) 128 => Nightly => nightly; 129 ); 130 } 131 } 132 133 rust_target_base!(rust_target_def); 134 rust_target_base!(rust_target_values_def); 135 136 /// Latest stable release of Rust 137 pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_40; 138 139 /// Create RustFeatures struct definition, new(), and a getter for each field 140 macro_rules! rust_feature_def { 141 ( 142 $( $rust_target:ident { 143 $( $( #[$attr:meta] )* => $feature:ident; )* 144 } )* 145 ) => { 146 /// Features supported by a rust target 147 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 148 #[allow(missing_docs)] // Documentation should go into the relevant variants. 149 pub(crate) struct RustFeatures { 150 $( $( 151 $( 152 #[$attr] 153 )* 154 pub $feature: bool, 155 )* )* 156 } 157 158 impl RustFeatures { 159 /// Gives a RustFeatures struct with all features disabled 160 fn new() -> Self { 161 RustFeatures { 162 $( $( 163 $feature: false, 164 )* )* 165 } 166 } 167 } 168 169 impl From<RustTarget> for RustFeatures { 170 fn from(rust_target: RustTarget) -> Self { 171 let mut features = RustFeatures::new(); 172 173 $( 174 if rust_target >= RustTarget::$rust_target { 175 $( 176 features.$feature = true; 177 )* 178 } 179 )* 180 181 features 182 } 183 } 184 } 185 } 186 187 // NOTE(emilio): When adding or removing features here, make sure to update the 188 // documentation for the relevant variant in the rust_target_base macro 189 // definition. 190 rust_feature_def!( 191 Stable_1_19 { 192 => untagged_union; 193 } 194 Stable_1_20 { 195 => associated_const; 196 } 197 Stable_1_21 { 198 => builtin_clone_impls; 199 } 200 Stable_1_25 { 201 => repr_align; 202 } 203 Stable_1_26 { 204 => i128_and_u128; 205 } 206 Stable_1_27 { 207 => must_use_function; 208 } 209 Stable_1_28 { 210 => repr_transparent; 211 } 212 Stable_1_30 { 213 => min_const_fn; 214 => core_ffi_c_void; 215 } 216 Stable_1_33 { 217 => repr_packed_n; 218 } 219 Stable_1_36 { 220 => maybe_uninit; 221 } 222 Stable_1_40 { 223 => non_exhaustive; 224 } 225 Nightly { 226 => thiscall_abi; 227 } 228 ); 229 230 impl Default for RustFeatures { default() -> Self231 fn default() -> Self { 232 let default_rust_target: RustTarget = Default::default(); 233 Self::from(default_rust_target) 234 } 235 } 236 237 #[cfg(test)] 238 mod test { 239 #![allow(unused_imports)] 240 use super::*; 241 242 #[test] target_features()243 fn target_features() { 244 let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0); 245 assert!( 246 !f_1_0.core_ffi_c_void && 247 !f_1_0.untagged_union && 248 !f_1_0.associated_const && 249 !f_1_0.builtin_clone_impls && 250 !f_1_0.repr_align && 251 !f_1_0.thiscall_abi 252 ); 253 let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21); 254 assert!( 255 !f_1_21.core_ffi_c_void && 256 f_1_21.untagged_union && 257 f_1_21.associated_const && 258 f_1_21.builtin_clone_impls && 259 !f_1_21.repr_align && 260 !f_1_21.thiscall_abi 261 ); 262 let f_nightly = RustFeatures::from(RustTarget::Nightly); 263 assert!( 264 f_nightly.core_ffi_c_void && 265 f_nightly.untagged_union && 266 f_nightly.associated_const && 267 f_nightly.builtin_clone_impls && 268 f_nightly.maybe_uninit && 269 f_nightly.repr_align && 270 f_nightly.thiscall_abi 271 ); 272 } 273 test_target(target_str: &str, target: RustTarget)274 fn test_target(target_str: &str, target: RustTarget) { 275 let target_string: String = target.into(); 276 assert_eq!(target_str, target_string); 277 assert_eq!(target, RustTarget::from_str(target_str).unwrap()); 278 } 279 280 #[test] str_to_target()281 fn str_to_target() { 282 test_target("1.0", RustTarget::Stable_1_0); 283 test_target("1.19", RustTarget::Stable_1_19); 284 test_target("1.21", RustTarget::Stable_1_21); 285 test_target("1.25", RustTarget::Stable_1_25); 286 test_target("nightly", RustTarget::Nightly); 287 } 288 } 289