@@ -38,8 +38,8 @@ impl RustTarget {
3838 // fixes.
3939 minor >= other_minor
4040 }
41- ( _, Version :: Nightly ) => false ,
4241 ( Version :: Nightly , _) => true ,
42+ ( _, Version :: Nightly ) => false ,
4343 }
4444 }
4545}
@@ -77,12 +77,92 @@ impl fmt::Display for InvalidRustTarget {
7777 }
7878}
7979
80+ /// This macro defines the Rust editions supported by bindgen.
81+ macro_rules! define_rust_editions {
82+ ( $( $variant: ident( $value: literal) => $minor: literal, ) * ) => {
83+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , Hash , PartialOrd , Ord ) ]
84+ pub enum RustEdition {
85+ $(
86+ #[ doc = concat!( "The " , stringify!( $value) , " edition of Rust." ) ]
87+ $variant,
88+ ) *
89+ }
90+
91+ impl FromStr for RustEdition {
92+ type Err = InvalidRustEdition ;
93+
94+ fn from_str( s: & str ) -> Result <Self , Self :: Err > {
95+ match s {
96+ $( stringify!( $value) => Ok ( Self :: $variant) , ) *
97+ _ => Err ( InvalidRustEdition ( s. to_owned( ) ) ) ,
98+ }
99+ }
100+ }
101+
102+ impl fmt:: Display for RustEdition {
103+ fn fmt( & self , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
104+ match self {
105+ $( Self :: $variant => stringify!( $value) . fmt( f) , ) *
106+ }
107+ }
108+ }
109+
110+ impl RustEdition {
111+ pub ( crate ) const ALL : [ Self ; [ $( $value, ) * ] . len( ) ] = [ $( Self :: $variant, ) * ] ;
112+
113+ pub ( crate ) fn is_available( self , target: RustTarget ) -> bool {
114+ let Some ( minor) = target. minor( ) else {
115+ return true ;
116+ } ;
117+
118+ match self {
119+ $( Self :: $variant => $minor <= minor, ) *
120+ }
121+ }
122+ }
123+ }
124+ }
125+
126+ #[ derive( Debug ) ]
127+ pub struct InvalidRustEdition ( String ) ;
128+
129+ impl fmt:: Display for InvalidRustEdition {
130+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
131+ write ! ( f, "\" {}\" is not a valid Rust edition" , self . 0 )
132+ }
133+ }
134+
135+ impl std:: error:: Error for InvalidRustEdition { }
136+
137+ define_rust_editions ! {
138+ Edition2018 ( 2018 ) => 31 ,
139+ Edition2021 ( 2021 ) => 56 ,
140+ }
141+
142+ impl RustTarget {
143+ /// Returns the latest edition supported by this target.
144+ pub ( crate ) fn latest_edition ( self ) -> RustEdition {
145+ RustEdition :: ALL
146+ . iter ( )
147+ . rev ( )
148+ . find ( |edition| edition. is_available ( self ) )
149+ . copied ( )
150+ . expect ( "bindgen should always support at least one edition" )
151+ }
152+ }
153+
154+ impl Default for RustEdition {
155+ fn default ( ) -> Self {
156+ RustTarget :: default ( ) . latest_edition ( )
157+ }
158+ }
159+
80160/// This macro defines the [`RustTarget`] and [`RustFeatures`] types.
81161macro_rules! define_rust_targets {
82162 (
83- Nightly => { $( $nightly_feature: ident $( : #$issue: literal) ?) ,* $( , ) ?} $( , ) ?
163+ Nightly => { $( $nightly_feature: ident $( ( $nightly_edition : literal ) ) * $ ( : #$issue: literal) ?) ,* $( , ) ?} $( , ) ?
84164 $(
85- $variant: ident( $minor: literal) => { $( $feature: ident $( : #$pull: literal) ?) ,* $( , ) ?} ,
165+ $variant: ident( $minor: literal) => { $( $feature: ident $( ( $edition : literal ) ) * $ ( : #$pull: literal) ?) ,* $( , ) ?} ,
86166 ) *
87167 $( , ) ?
88168 ) => {
@@ -126,23 +206,35 @@ macro_rules! define_rust_targets {
126206 $( pub ( crate ) $nightly_feature: bool , ) *
127207 }
128208
129- impl From <RustTarget > for RustFeatures {
130- fn from( target: RustTarget ) -> Self {
131- if target == RustTarget :: Nightly {
132- return Self {
133- $( $( $feature: true , ) * ) *
134- $( $nightly_feature: true , ) *
135- } ;
136- }
137-
209+ impl RustFeatures {
210+ /// Compute the features that must be enabled in a specific Rust target with a specific edition.
211+ pub ( crate ) fn new( target: RustTarget , edition: RustEdition ) -> Self {
138212 let mut features = Self {
139213 $( $( $feature: false , ) * ) *
140214 $( $nightly_feature: false , ) *
141215 } ;
142216
143- $( if target. is_compatible( & RustTarget :: $variant) {
144- $( features. $feature = true ; ) *
145- } ) *
217+ if target. is_compatible( & RustTarget :: nightly( ) ) {
218+ $(
219+ let editions: & [ RustEdition ] = & [ $( stringify!( $nightly_edition) . parse:: <RustEdition >( ) . ok( ) . expect( "invalid edition" ) , ) * ] ;
220+
221+ if editions. is_empty( ) || editions. contains( & edition) {
222+ features. $nightly_feature = true ;
223+ }
224+ ) *
225+ }
226+
227+ $(
228+ if target. is_compatible( & RustTarget :: $variant) {
229+ $(
230+ let editions: & [ RustEdition ] = & [ $( stringify!( $edition) . parse:: <RustEdition >( ) . ok( ) . expect( "invalid edition" ) , ) * ] ;
231+
232+ if editions. is_empty( ) || editions. contains( & edition) {
233+ features. $feature = true ;
234+ }
235+ ) *
236+ }
237+ ) *
146238
147239 features
148240 }
@@ -161,7 +253,7 @@ define_rust_targets! {
161253 } ,
162254 Stable_1_77 ( 77 ) => {
163255 offset_of: #106655 ,
164- literal_cstr: #117472 ,
256+ literal_cstr( 2021 ) : #117472 ,
165257 } ,
166258 Stable_1_73 ( 73 ) => { thiscall_abi: #42202 } ,
167259 Stable_1_71 ( 71 ) => { c_unwind_abi: #106075 } ,
@@ -294,9 +386,17 @@ impl FromStr for RustTarget {
294386 }
295387}
296388
389+ impl RustFeatures {
390+ /// Compute the features that must be enabled in a specific Rust target with the latest edition
391+ /// available in that target.
392+ pub ( crate ) fn new_with_latest_edition ( target : RustTarget ) -> Self {
393+ Self :: new ( target, target. latest_edition ( ) )
394+ }
395+ }
396+
297397impl Default for RustFeatures {
298398 fn default ( ) -> Self {
299- RustTarget :: default ( ) . into ( )
399+ Self :: new_with_latest_edition ( RustTarget :: default ( ) )
300400 }
301401}
302402
@@ -306,24 +406,39 @@ mod test {
306406
307407 #[ test]
308408 fn target_features ( ) {
309- let features = RustFeatures :: from ( RustTarget :: Stable_1_71 ) ;
409+ let features =
410+ RustFeatures :: new_with_latest_edition ( RustTarget :: Stable_1_71 ) ;
310411 assert ! (
311412 features. c_unwind_abi &&
312413 features. abi_efiapi &&
313414 !features. thiscall_abi
314415 ) ;
315- let f_nightly = RustFeatures :: from ( RustTarget :: Nightly ) ;
416+
417+ let features = RustFeatures :: new (
418+ RustTarget :: Stable_1_77 ,
419+ RustEdition :: Edition2018 ,
420+ ) ;
421+ assert ! ( !features. literal_cstr) ;
422+
423+ let features =
424+ RustFeatures :: new_with_latest_edition ( RustTarget :: Stable_1_77 ) ;
425+ assert ! ( features. literal_cstr) ;
426+
427+ let f_nightly =
428+ RustFeatures :: new_with_latest_edition ( RustTarget :: Nightly ) ;
316429 assert ! (
317- f_nightly. maybe_uninit &&
318- f_nightly. thiscall_abi &&
319- f_nightly. vectorcall_abi
430+ f_nightly. vectorcall_abi &&
431+ f_nightly. ptr_metadata &&
432+ f_nightly. layout_for_ptr
320433 ) ;
321434 }
322435
323436 fn test_target ( input : & str , expected : RustTarget ) {
324437 // Two targets are equivalent if they enable the same set of features
325- let expected = RustFeatures :: from ( expected) ;
326- let found = RustFeatures :: from ( input. parse :: < RustTarget > ( ) . unwrap ( ) ) ;
438+ let expected = RustFeatures :: new_with_latest_edition ( expected) ;
439+ let found = RustFeatures :: new_with_latest_edition (
440+ input. parse :: < RustTarget > ( ) . unwrap ( ) ,
441+ ) ;
327442 assert_eq ! (
328443 expected,
329444 found,
0 commit comments