diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 1741dde90f5cf..8f06a6ceba8ee 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -5,10 +5,8 @@ #![feature(default_field_values)] #![feature(iter_intersperse)] #![feature(macro_derive)] +#![feature(macro_metavar_expr)] #![feature(rustc_attrs)] -// To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums -// with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). -#![recursion_limit = "256"] // tidy-alphabetical-end pub mod errors; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 7abaae1c17606..37385aba3b544 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -51,14 +51,14 @@ macro_rules! hash_substruct { ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [UNTRACKED]) => {{}}; ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [TRACKED]) => {{}}; ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [TRACKED_NO_CRATE_HASH]) => {{}}; - ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [SUBSTRUCT]) => { + ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [SUBSTRUCT]) => {{ use crate::config::dep_tracking::DepTrackingHash; $opt_expr.dep_tracking_hash($for_crate_hash, $error_format).hash( $hasher, $error_format, $for_crate_hash, ); - }; + }}; } /// Extended target modifier info. @@ -158,204 +158,106 @@ impl TargetModifier { } } -fn tmod_push_impl( - opt: OptionsTargetModifiers, - tmod_vals: &BTreeMap, - tmods: &mut Vec, -) { - if let Some(v) = tmod_vals.get(&opt) { - tmods.push(TargetModifier { opt, value_name: v.clone() }) - } -} - -macro_rules! tmod_push { - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr) => { - if *$opt_expr != $init { - tmod_push_impl( - OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name), - $tmod_vals, - $mods, - ); - } - }; -} - -macro_rules! gather_tmods { - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [SUBSTRUCT], [TARGET_MODIFIER]) => { - compile_error!("SUBSTRUCT can't be target modifier"); - }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [UNTRACKED], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) - }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [TRACKED], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) - }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) - }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [SUBSTRUCT], []) => { - $opt_expr.gather_target_modifiers($mods, $tmod_vals); - }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [UNTRACKED], []) => {{}}; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [TRACKED], []) => {{}}; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, - [TRACKED_NO_CRATE_HASH], []) => {{}}; -} - -macro_rules! gather_tmods_top_level { - ($_opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT $substruct_enum:ident]) => { - $opt_expr.gather_target_modifiers($mods, $tmod_vals); - }; - ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident TARGET_MODIFIER]) => { - compile_error!("Top level option can't be target modifier"); - }; - ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident]) => {}; -} - -/// Macro for generating OptionsTargetsModifiers top-level enum with impl. -/// Will generate something like: -/// ```rust,ignore (illustrative) -/// pub enum OptionsTargetModifiers { -/// CodegenOptions(CodegenOptionsTargetModifiers), -/// UnstableOptions(UnstableOptionsTargetModifiers), -/// } -/// impl OptionsTargetModifiers { -/// pub fn reparse(&self, user_value: &str) -> ExtendedTargetModifierInfo { -/// match self { -/// Self::CodegenOptions(v) => v.reparse(user_value), -/// Self::UnstableOptions(v) => v.reparse(user_value), -/// } -/// } -/// pub fn is_target_modifier(flag_name: &str) -> bool { -/// CodegenOptionsTargetModifiers::is_target_modifier(flag_name) || -/// UnstableOptionsTargetModifiers::is_target_modifier(flag_name) -/// } -/// } -/// ``` -macro_rules! top_level_tmod_enum { - ($( {$($optinfo:tt)*} ),* $(,)*) => { - top_level_tmod_enum! { @parse {}, (user_value){}; $($($optinfo)*|)* } - }; - // Termination +macro_rules! top_level_options { ( - @parse - {$($variant:tt($substruct_enum:tt))*}, - ($user_value:ident){$($pout:tt)*}; - ) => { + $(#[$top_level_attr:meta])* + pub struct Options { + $( + $(#[$attr:meta])* + $opt:ident : $t:ty [ + $dep_tracking_marker:ident + $( $tmod:ident $variant:ident )? + ], + )* + } + ) => ( #[allow(non_camel_case_types)] #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, BlobDecodable)] pub enum OptionsTargetModifiers { - $($variant($substruct_enum)),* + $( + $( + $variant($tmod), + )? + )* } + impl OptionsTargetModifiers { - #[allow(unused_variables)] - pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { - #[allow(unreachable_patterns)] + pub fn reparse(&self, user_value: &str) -> ExtendedTargetModifierInfo { match self { - $($pout)* - _ => panic!("unknown target modifier option: {:?}", *self) + $( + $( + Self::$variant(v) => v.reparse(user_value), + )? + )* + #[allow(unreachable_patterns)] + _ => panic!("unknown target modifier option: {self:?}"), } } + pub fn is_target_modifier(flag_name: &str) -> bool { - $($substruct_enum::is_target_modifier(flag_name))||* + $( + $( + if $tmod::is_target_modifier(flag_name) { + return true + } + )? + )* + false } } - }; - // Adding SUBSTRUCT option group into $eout - ( - @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; - [SUBSTRUCT $substruct_enum:ident $variant:ident] | - $($tail:tt)* - ) => { - top_level_tmod_enum! { - @parse - { - $($eout)* - $variant($substruct_enum) - }, - ($puser_value){ - $($pout)* - Self::$variant(v) => v.reparse($puser_value), - }; - $($tail)* - } - }; - // Skipping non-target-modifier and non-substruct - ( - @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; - [$non_substruct:ident] | - $($tail:tt)* - ) => { - top_level_tmod_enum! { - @parse - { - $($eout)* - }, - ($puser_value){ - $($pout)* - }; - $($tail)* - } - }; -} - -macro_rules! top_level_options { - ( $( #[$top_level_attr:meta] )* pub struct Options { $( - $( #[$attr:meta] )* - $opt:ident : $t:ty [$dep_tracking_marker:ident $( $tmod:ident $variant:ident )?], - )* } ) => ( - top_level_tmod_enum!( {$([$dep_tracking_marker $($tmod $variant),*])|*} ); #[derive(Clone)] - $( #[$top_level_attr] )* + $(#[$top_level_attr])* pub struct Options { $( - $( #[$attr] )* - pub $opt: $t - ),*, + $(#[$attr])* + pub $opt: $t, + )* pub target_modifiers: BTreeMap, } impl Options { pub fn dep_tracking_hash(&self, for_crate_hash: bool) -> Hash64 { let mut sub_hashes = BTreeMap::new(); - $({ - hash_opt!($opt, - &self.$opt, - &mut sub_hashes, - for_crate_hash, - [$dep_tracking_marker]); - })* + $( + hash_opt!( + $opt, + &self.$opt, + &mut sub_hashes, + for_crate_hash, + [$dep_tracking_marker] + ); + )* + let mut hasher = StableHasher::new(); - dep_tracking::stable_hash(sub_hashes, - &mut hasher, - self.error_format, - for_crate_hash); - $({ - hash_substruct!($opt, + dep_tracking::stable_hash( + sub_hashes, + &mut hasher, + self.error_format, + for_crate_hash, + ); + + $( + hash_substruct!( + $opt, &self.$opt, self.error_format, for_crate_hash, &mut hasher, - [$dep_tracking_marker]); - })* + [$dep_tracking_marker] + ); + )* hasher.finish() } pub fn gather_target_modifiers(&self) -> Vec { let mut mods = Vec::::new(); - $({ - gather_tmods_top_level!($opt, - &self.$opt, &mut mods, &self.target_modifiers, - [$dep_tracking_marker $($tmod),*]); - })* + $( + $( + ${ignore($tmod)} + self.$opt.gather_target_modifiers(&mut mods, &self.target_modifiers); + )? + )* mods.sort_by(|a, b| a.opt.cmp(&b.opt)); mods } @@ -504,182 +406,183 @@ top_level_options!( } ); -macro_rules! tmod_enum_opt { - ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, $v:ident) => { - Some(OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt)) - }; - ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, ) => { - None - }; -} - -macro_rules! tmod_enum { - ($tmod_enum_name:ident, $prefix:expr, $( {$($optinfo:tt)*} ),* $(,)*) => { - tmod_enum! { $tmod_enum_name, $prefix, @parse {}, (user_value){}; $($($optinfo)*|)* } - }; - // Termination +/// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this +/// macro is to define an interface that can be programmatically used by the option parser +/// to initialize the struct without hardcoding field names all over the place. +/// +/// The goal is to invoke this macro once with the correct fields, and then this macro generates all +/// necessary code. The main gotcha of this macro is the `cgsetters` module which is a bunch of +/// generated code to parse an option into its respective field in the struct. There are a few +/// hand-written parsers for parsing specific types of values in this module. +macro_rules! options { ( - $tmod_enum_name:ident, $prefix:expr, - @parse - {$($eout:tt)*}, - ($user_value:ident){$($pout:tt)*}; + $struct_name:ident, + $tmod_enum_name:ident, + $stat:ident, + $optmod:ident, + $prefix:expr, + $outputname:expr, + + $( + $(#[$attr:meta])* + $opt:ident : $t:ty = ( + $init:expr, + $parse:ident, + [ + $dep_tracking_marker:ident + // HACK: If `TARGET_MODIFIER` is present, then `$tmod:vis` + // will also be present, despite consuming no tokens. + $( $tmod:vis TARGET_MODIFIER )? + ], + $desc:expr + $(, removed: $removed:ident )? + ), + )* ) => { + #[derive(Clone)] + #[rustc_lint_opt_ty] + pub struct $struct_name { + $( + $(#[$attr])* + pub $opt: $t, + )* + } + #[allow(non_camel_case_types)] #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, BlobDecodable)] pub enum $tmod_enum_name { - $($eout),* + $( + $( + ${ignore($tmod)} + $opt, + )? + )* } + impl $tmod_enum_name { - #[allow(unused_variables)] - pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { - #[allow(unreachable_patterns)] + fn reparse(&self, user_value: &str) -> ExtendedTargetModifierInfo { + let _user_val = if user_value.is_empty() { None } else { Some(user_value) }; match self { - $($pout)* - _ => panic!("unknown target modifier option: {:?}", *self) + $( + $( + ${ignore($tmod)} + Self::$opt => { + let mut parsed: $t = Default::default(); + parse::$parse(&mut parsed, _user_val); + + ExtendedTargetModifierInfo { + prefix: $prefix.to_owned(), + name: stringify!($opt).replace('_', "-"), + tech_value: format!("{parsed:?}"), + } + } + )? + )* + #[allow(unreachable_patterns)] + _ => panic!("unknown target modifier option: {self:?}"), } } - pub fn is_target_modifier(flag_name: &str) -> bool { + + fn is_target_modifier(flag_name: &str) -> bool { match flag_name.replace('-', "_").as_str() { - $(stringify!($eout) => true,)* + $( + $( + ${ignore($tmod)} + stringify!($opt) => true, + )? + )* _ => false, } } } - }; - // Adding target-modifier option into $eout - ( - $tmod_enum_name:ident, $prefix:expr, - @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; - $opt:ident, $parse:ident, $t:ty, [TARGET_MODIFIER] | - $($tail:tt)* - ) => { - tmod_enum! { - $tmod_enum_name, $prefix, - @parse - { - $($eout)* - $opt - }, - ($puser_value){ - $($pout)* - Self::$opt => { - let mut parsed : $t = Default::default(); - let val = if $puser_value.is_empty() { None } else { Some($puser_value) }; - parse::$parse(&mut parsed, val); - ExtendedTargetModifierInfo { - prefix: $prefix.to_string(), - name: stringify!($opt).to_string().replace('_', "-"), - tech_value: format!("{:?}", parsed), - } - }, - }; - $($tail)* - } - }; - // Skipping non-target-modifier - ( - $tmod_enum_name:ident, $prefix:expr, - @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; - $opt:ident, $parse:ident, $t:ty, [] | - $($tail:tt)* - ) => { - tmod_enum! { - $tmod_enum_name, $prefix, - @parse - { - $($eout)* - }, - ($puser_value){ - $($pout)* - }; - $($tail)* - } - }; -} -/// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this -/// macro is to define an interface that can be programmatically used by the option parser -/// to initialize the struct without hardcoding field names all over the place. -/// -/// The goal is to invoke this macro once with the correct fields, and then this macro generates all -/// necessary code. The main gotcha of this macro is the `cgsetters` module which is a bunch of -/// generated code to parse an option into its respective field in the struct. There are a few -/// hand-written parsers for parsing specific types of values in this module. -macro_rules! options { - ($struct_name:ident, $tmod_enum_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, - $($( #[$attr:meta] )* $opt:ident : $t:ty = ( - $init:expr, - $parse:ident, - [$dep_tracking_marker:ident $( $tmod:ident )?], - $desc:expr - $(, removed: $removed:ident )?) - ),* ,) => -( - #[derive(Clone)] - #[rustc_lint_opt_ty] - pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } - - tmod_enum!( $tmod_enum_name, $prefix, {$($opt, $parse, $t, [$($tmod),*])|*} ); - - impl Default for $struct_name { - fn default() -> $struct_name { - $struct_name { $($opt: $init),* } + impl Default for $struct_name { + fn default() -> $struct_name { + $struct_name { + $( $opt: $init, )* + } + } } - } - impl $struct_name { - pub fn build( - early_dcx: &EarlyDiagCtxt, - matches: &getopts::Matches, - target_modifiers: &mut BTreeMap, - ) -> $struct_name { - build_options(early_dcx, matches, target_modifiers, $stat, $prefix, $outputname) - } + impl $struct_name { + pub fn build( + early_dcx: &EarlyDiagCtxt, + matches: &getopts::Matches, + target_modifiers: &mut BTreeMap, + ) -> $struct_name { + build_options(early_dcx, matches, target_modifiers, $stat, $prefix, $outputname) + } - fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> Hash64 { - let mut sub_hashes = BTreeMap::new(); - $({ - hash_opt!($opt, - &self.$opt, - &mut sub_hashes, - for_crate_hash, - [$dep_tracking_marker]); - })* - let mut hasher = StableHasher::new(); - dep_tracking::stable_hash(sub_hashes, - &mut hasher, - error_format, - for_crate_hash - ); - hasher.finish() - } + fn dep_tracking_hash( + &self, + for_crate_hash: bool, + error_format: ErrorOutputType, + ) -> Hash64 { + let mut sub_hashes = BTreeMap::new(); + $( + hash_opt!( + $opt, + &self.$opt, + &mut sub_hashes, + for_crate_hash, + [$dep_tracking_marker] + ); + )* + let mut hasher = StableHasher::new(); + dep_tracking::stable_hash(sub_hashes, &mut hasher, error_format, for_crate_hash); + hasher.finish() + } - pub fn gather_target_modifiers( - &self, - _mods: &mut Vec, - _tmod_vals: &BTreeMap, - ) { - $({ - gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, $init, _mods, _tmod_vals, - [$dep_tracking_marker], [$($tmod),*]); - })* + pub fn gather_target_modifiers( + &self, + _mods: &mut Vec, + _tmod_vals: &BTreeMap, + ) { + $( + $( + ${ignore($tmod)} + if self.$opt != $init + && let opt = OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt) + && let Some(v) = _tmod_vals.get(&opt) + { + _mods.push(TargetModifier { opt, value_name: String::clone(v) }) + } + )? + )* + } } - } - pub const $stat: OptionDescrs<$struct_name> = - &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, - type_desc: desc::$parse, desc: $desc, removed: None $( .or(Some(RemovedOption::$removed)) )?, - tmod: tmod_enum_opt!($struct_name, $tmod_enum_name, $opt, $($tmod),*) } ),* ]; + pub const $stat: OptionDescrs<$struct_name> = &[ + $( + OptionDesc { + name: stringify!($opt), + setter: $optmod::$opt, + type_desc: desc::$parse, + desc: $desc, + removed: None $( .or(Some(RemovedOption::$removed)) )?, + tmod: const { + None + $( + ${ignore($tmod)} + .or(Some( + OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt) + )) + )? + }, + } + ),* + ]; - mod $optmod { - $( - pub(super) fn $opt(cg: &mut super::$struct_name, v: Option<&str>) -> bool { - super::parse::$parse(&mut redirect_field!(cg.$opt), v) + mod $optmod { + $( + pub(super) fn $opt(cg: &mut super::$struct_name, v: Option<&str>) -> bool { + super::parse::$parse(&mut redirect_field!(cg.$opt), v) + } + )* } - )* - } -) } + } +} impl CodegenOptions { // JUSTIFICATION: defn of the suggested wrapper fn