From 6793451ca319d644459a480ab99ea7b756d39403 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Mon, 18 Nov 2024 04:07:29 +0700 Subject: [PATCH] Target modifiers (special marked options) are recorded in metainfo and compared to be equal in different crates --- compiler/rustc_interface/src/passes.rs | 1 + compiler/rustc_lint/messages.ftl | 10 ++ .../rustc_lint/src/context/diagnostics.rs | 16 +++ compiler/rustc_lint/src/lints.rs | 14 +++ compiler/rustc_lint_defs/src/builtin.rs | 38 ++++++ compiler/rustc_lint_defs/src/lib.rs | 8 ++ compiler/rustc_metadata/src/creader.rs | 115 +++++++++++++++++- compiler/rustc_metadata/src/rmeta/decoder.rs | 18 +++ compiler/rustc_metadata/src/rmeta/encoder.rs | 8 ++ compiler/rustc_metadata/src/rmeta/mod.rs | 3 +- compiler/rustc_session/src/options.rs | 94 +++++++++++++- .../auxiliary/wrong_regparm.rs | 14 +++ ...ncompatible_regparm.error_generated.stderr | 15 +++ .../target_modifiers/incompatible_regparm.rs | 21 ++++ 14 files changed, 369 insertions(+), 6 deletions(-) create mode 100644 tests/ui/target_modifiers/auxiliary/wrong_regparm.rs create mode 100644 tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr create mode 100644 tests/ui/target_modifiers/incompatible_regparm.rs diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index fd850d2f39a5f..7e9513428a14d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -281,6 +281,7 @@ fn configure_and_expand( resolver.resolve_crate(&krate); + CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate, resolver.lint_buffer()); krate } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c4d709aa1f985..3f7f6758099b2 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -415,6 +415,16 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_incompatible_target_modifiers = + mixing `{$flag_name_suffixed}` will cause an ABI mismatch + .note1 = `{$flag_name_suffixed}`=`{$flag_local_value}` in crate `{$local_crate}`, `{$flag_name_suffixed}`=`{$flag_extern_value}` in crate `{$extern_crate}` + .help = This error occurs because the `{$flag_name_suffixed}` flag modifies the ABI, + and different crates in your project were compiled with inconsistent settings + .suggestion = To resolve this, ensure that `{$flag_name_suffixed}` is set to the same value + for all crates during compilation + .note2 = To ignore this error, recompile with the following flag: + -Cunsafe-allow-abi-mismatch=`{$flag_name}` + lint_incomplete_include = include macro expected single expression in source diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 565c3c0425256..31c68c29c4a39 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -422,6 +422,22 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag), + BuiltinLintDiag::IncompatibleTargetModifiers { + extern_crate, + local_crate, + flag_name, + flag_name_suffixed, + flag_local_value, + flag_extern_value, + } => lints::IncompatibleTargetModifiers { + extern_crate, + local_crate, + flag_name, + flag_name_suffixed, + flag_local_value, + flag_extern_value, + } + .decorate_lint(diag), BuiltinLintDiag::IllFormedAttributeInput { suggestions } => { lints::IllFormedAttributeInput { num_suggestions: suggestions.len(), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 16cfae17d4020..b071d7232d30d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2470,6 +2470,20 @@ pub(crate) struct UnusedCrateDependency { pub local_crate: Symbol, } +#[derive(LintDiagnostic)] +#[diag(lint_incompatible_target_modifiers)] +#[help] +#[note(lint_note1)] +#[note(lint_note2)] +pub(crate) struct IncompatibleTargetModifiers { + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_suffixed: String, + pub flag_local_value: String, + pub flag_extern_value: String, +} + #[derive(LintDiagnostic)] #[diag(lint_wasm_c_abi)] pub(crate) struct WasmCAbi; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a4c49a1590535..fa847ee2fe2a1 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -49,6 +49,7 @@ declare_lint_pass! { FUZZY_PROVENANCE_CASTS, HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, + INCOMPATIBLE_TARGET_MODIFIERS, INCOMPLETE_INCLUDE, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, INLINE_NO_SANITIZE, @@ -578,6 +579,43 @@ declare_lint! { crate_level_only } +declare_lint! { + /// The `incompatible_target_modifiers` lint detects crates with incompatible target modifiers + /// (abi-changing or vulnerability-affecting flags). + /// + /// ### Example + /// + /// ```rust,ignore (needs extern crate) + /// #![deny(incompatible_target_modifiers)] + /// ``` + /// + /// When main and dependency crates are compiled with `-Zregparm=1` and `-Zregparm=2` correspondingly. + /// + /// This will produce: + /// + /// ```text + /// error: crate `incompatible_regparm` has incompatible target modifier with extern crate `wrong_regparm`: `regparm = ( Some(1) | Some(2) )` + /// --> $DIR/incompatible_regparm.rs:5:1 + /// | + /// 1 | #![no_core] + /// | ^ + /// | + /// = note: `#[deny(incompatible_target_modifiers)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// `Target modifiers` are compilation flags that affects abi or vulnerability resistance. + /// Linking together crates with incompatible target modifiers would produce incorrect code + /// or degradation of vulnerability resistance. + /// So this lint should find such inconsistency. + /// + pub INCOMPATIBLE_TARGET_MODIFIERS, + Deny, + "Incompatible target modifiers", + crate_level_only +} + declare_lint! { /// The `unused_qualifications` lint detects unnecessarily qualified /// names. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index c01fa5c54d65e..61996c222bcff 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -719,6 +719,14 @@ pub enum BuiltinLintDiag { AvoidUsingIntelSyntax, AvoidUsingAttSyntax, IncompleteInclude, + IncompatibleTargetModifiers { + extern_crate: Symbol, + local_crate: Symbol, + flag_name: String, + flag_name_suffixed: String, + flag_local_value: String, + flag_extern_value: String, + }, UnnameableTestItems, DuplicateMacroAttribute, CfgAttrNoAttributes, diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 8adec7554a834..ae0f4584e0aeb 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -24,7 +24,7 @@ use rustc_middle::bug; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; use rustc_session::config::{self, CrateType, ExternLocation}; use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; -use rustc_session::lint::{self, BuiltinLintDiag}; +use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; use rustc_span::edition::Edition; @@ -35,7 +35,9 @@ use tracing::{debug, info, trace}; use crate::errors; use crate::locator::{CrateError, CrateLocator, CratePaths}; -use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; +use crate::rmeta::{ + CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers, +}; /// The backend's way to give the crate store access to the metadata in a library. /// Note that it returns the raw metadata bytes stored in the library file, whether @@ -290,6 +292,94 @@ impl CStore { } } + pub fn report_incompatible_target_modifiers( + &self, + tcx: TyCtxt<'_>, + krate: &Crate, + lints: &mut LintBuffer, + ) { + if tcx.crate_types().contains(&CrateType::ProcMacro) { + return; + } + let sess = tcx.sess; + if let Some(vec) = &sess.opts.cg.unsafe_allow_abi_mismatch + && vec.is_empty() + { + return; + } + let span = krate.spans.inner_span.shrink_to_lo(); + + let splitter = |v: &String| { + let splitted: Vec<_> = v.split("=").collect(); + (splitted[0].to_string(), splitted[1].to_string()) + }; + let name = tcx.crate_name(LOCAL_CRATE); + let mods = sess.opts.gather_target_modifiers(); + for (_cnum, data) in self.iter_crate_data() { + if data.is_proc_macro_crate() { + continue; + } + let mut report_diff = + |flag_name: &String, flag_local_value: &String, flag_extern_value: &String| { + if let Some(vec) = &sess.opts.cg.unsafe_allow_abi_mismatch { + if vec.contains(flag_name) { + return; + } + } + lints.buffer_lint( + lint::builtin::INCOMPATIBLE_TARGET_MODIFIERS, + ast::CRATE_NODE_ID, + span, + BuiltinLintDiag::IncompatibleTargetModifiers { + extern_crate: data.name(), + local_crate: name, + flag_name: flag_name.to_string(), + flag_name_suffixed: flag_name.to_string(), + flag_local_value: flag_local_value.to_string(), + flag_extern_value: flag_extern_value.to_string(), + }, + ); + }; + let mut it1 = mods.iter().map(splitter); + let mut it2 = data.target_modifiers().map(splitter); + let mut left_name_val: Option<(String, String)> = None; + let mut right_name_val: Option<(String, String)> = None; + let no_val = "*".to_string(); + loop { + left_name_val = left_name_val.or_else(|| it1.next()); + right_name_val = right_name_val.or_else(|| it2.next()); + match (&left_name_val, &right_name_val) { + (Some(l), Some(r)) => match l.0.cmp(&r.0) { + cmp::Ordering::Equal => { + if l.1 != r.1 { + report_diff(&l.0, &l.1, &r.1); + } + left_name_val = None; + right_name_val = None; + } + cmp::Ordering::Greater => { + report_diff(&r.0, &no_val, &r.1); + right_name_val = None; + } + cmp::Ordering::Less => { + report_diff(&l.0, &l.1, &no_val); + left_name_val = None; + } + }, + (Some(l), None) => { + report_diff(&l.0, &l.1, &no_val); + left_name_val = None; + } + (None, Some(r)) => { + report_diff(&r.0, &no_val, &r.1); + right_name_val = None; + } + (None, None) => break, + } + } + } + } + pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, @@ -432,6 +522,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { }; let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, dep_kind)?; + let target_modifiers = self.resolve_target_modifiers(&crate_root, &metadata, cnum)?; let raw_proc_macros = if crate_root.is_proc_macro_crate() { let temp_root; @@ -456,6 +547,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { raw_proc_macros, cnum, cnum_map, + target_modifiers, dep_kind, source, private_dep, @@ -689,6 +781,25 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { Ok(crate_num_map) } + fn resolve_target_modifiers( + &mut self, + crate_root: &CrateRoot, + metadata: &MetadataBlob, + krate: CrateNum, + ) -> Result { + debug!("resolving target modifiers of external crate"); + if crate_root.is_proc_macro_crate() { + return Ok(TargetModifiers::new()); + } + let mods = crate_root.decode_target_modifiers(metadata); + let mut target_modifiers = TargetModifiers::with_capacity(mods.len()); + for modifier in mods { + target_modifiers.push(modifier); + } + debug!("resolve_target_modifiers: target mods for {:?} is {:?}", krate, target_modifiers); + Ok(target_modifiers) + } + fn dlsym_proc_macros( &self, path: &Path, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 4b406496337a7..402a928e3ff6b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -73,6 +73,9 @@ impl MetadataBlob { /// own crate numbers. pub(crate) type CrateNumMap = IndexVec; +/// Target modifiers - abi / vulnerability-resist affecting flags +pub(crate) type TargetModifiers = Vec; + pub(crate) struct CrateMetadata { /// The primary crate data - binary metadata blob. blob: MetadataBlob, @@ -110,6 +113,8 @@ pub(crate) struct CrateMetadata { cnum_map: CrateNumMap, /// Same ID set as `cnum_map` plus maybe some injected crates like panic runtime. dependencies: Vec, + /// Target modifiers - abi and vulnerability-resist affecting flags the crate was compiled with + target_modifiers: TargetModifiers, /// How to link (or not link) this crate to the currently compiled crate. dep_kind: CrateDepKind, /// Filesystem location of this crate. @@ -960,6 +965,13 @@ impl CrateRoot { ) -> impl ExactSizeIterator + Captures<'a> { self.crate_deps.decode(metadata) } + + pub(crate) fn decode_target_modifiers<'a>( + &self, + metadata: &'a MetadataBlob, + ) -> impl ExactSizeIterator + Captures<'a> { + self.target_modifiers.decode(metadata) + } } impl<'a> CrateMetadataRef<'a> { @@ -1815,6 +1827,7 @@ impl CrateMetadata { raw_proc_macros: Option<&'static [ProcMacro]>, cnum: CrateNum, cnum_map: CrateNumMap, + target_modifiers: TargetModifiers, dep_kind: CrateDepKind, source: CrateSource, private_dep: bool, @@ -1846,6 +1859,7 @@ impl CrateMetadata { cnum, cnum_map, dependencies, + target_modifiers, dep_kind, source: Lrc::new(source), private_dep, @@ -1875,6 +1889,10 @@ impl CrateMetadata { self.dependencies.push(cnum); } + pub(crate) fn target_modifiers(&self) -> impl Iterator + '_ { + self.target_modifiers.iter() + } + pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool { let update = Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index afe03531861c9..a7e279f64118f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -692,6 +692,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode source_map. This needs to be done last, because encoding `Span`s tells us which // `SourceFiles` we actually need to encode. let source_map = stat!("source-map", || self.encode_source_map()); + let target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers()); let root = stat!("final", || { let attrs = tcx.hir().krate_attrs(); @@ -732,6 +733,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { native_libraries, foreign_modules, source_map, + target_modifiers, traits, impls, incoherent_impls, @@ -1978,6 +1980,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(deps.iter().map(|(_, dep)| dep)) } + fn encode_target_modifiers(&mut self) -> LazyArray { + empty_proc_macro!(self); + let tcx = self.tcx; + self.lazy_array(tcx.sess.opts.gather_target_modifiers()) + } + fn encode_lib_features(&mut self) -> LazyArray<(Symbol, FeatureStability)> { empty_proc_macro!(self); let tcx = self.tcx; diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 79bd1c13b1216..c54062e813112 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::num::NonZero; -pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; +pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob, TargetModifiers}; use decoder::{DecodeContext, Metadata}; use def_path_hash_map::DefPathHashMapRef; use encoder::EncodeContext; @@ -283,6 +283,7 @@ pub(crate) struct CrateRoot { def_path_hash_map: LazyValue>, source_map: LazyTable>>, + target_modifiers: LazyArray, compiler_builtins: bool, needs_allocator: bool, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 54a4621db2462..b6e9adf2faeaa 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -58,10 +58,58 @@ macro_rules! hash_substruct { }; } +macro_rules! gather_tmods { + ($_opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, [SUBSTRUCT], [TMOD]) => { + compile_error!("SUBSTRUCT can't be TMOD (target modifier)"); + }; + ($opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, [UNTRACKED], [TMOD]) => { + if *$opt_expr != $init { + $mods.push(format!("{}={:?}", stringify!($opt_name), $opt_expr)); + } + }; + ($opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, [TRACKED], [TMOD]) => { + if *$opt_expr != $init { + $mods.push(format!("{}={:?}", stringify!($opt_name), $opt_expr)); + } + }; + ($opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, [TRACKED_NO_CRATE_HASH], [TMOD]) => { + if *$opt_expr != $init { + $mods.push(format!("{}={:?}", stringify!($opt_name), $opt_expr)); + } + }; + ($_opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, [SUBSTRUCT], []) => { + $opt_expr.gather_target_modifiers($mods); + }; + ($_opt_name:ident, $init:expr, $_opt_expr:expr, $_mods:expr, [UNTRACKED], []) => {{}}; + ($_opt_name:ident, $init:expr, $_opt_expr:expr, $_mods:expr, [TRACKED], []) => {{}}; + ($_opt_name:ident, $init:expr, $_opt_expr:expr, $_mods:expr, [TRACKED_NO_CRATE_HASH], []) => {{}}; +} + +macro_rules! gather_tmods_top_level { + ($_opt_name:ident, $opt_expr:expr, $mods:expr, [SUBSTRUCT], [TMOD]) => { + compile_error!("SUBSTRUCT can't be TMOD (target modifier)"); + }; + ($opt_name:ident, $opt_expr:expr, $mods:expr, [UNTRACKED], [TMOD]) => { + compile_error!("Top level option can't be TMOD (target modifier)"); + }; + ($opt_name:ident, $opt_expr:expr, $mods:expr, [TRACKED], [TMOD]) => { + compile_error!("Top level option can't be TMOD (target modifier)"); + }; + ($opt_name:ident, $opt_expr:expr, $mods:expr, [TRACKED_NO_CRATE_HASH], [TMOD]) => { + compile_error!("Top level option can't be TMOD (target modifier)"); + }; + ($_opt_name:ident, $opt_expr:expr, $mods:expr, [SUBSTRUCT], []) => { + $opt_expr.gather_target_modifiers($mods); + }; + ($_opt_name:ident, $_opt_expr:expr, $_mods:expr, [UNTRACKED], []) => {{}}; + ($_opt_name:ident, $_opt_expr:expr, $_mods:expr, [TRACKED], []) => {{}}; + ($_opt_name:ident, $_opt_expr:expr, $_mods:expr, [TRACKED_NO_CRATE_HASH], []) => {{}}; +} + macro_rules! top_level_options { ( $( #[$top_level_attr:meta] )* pub struct Options { $( $( #[$attr:meta] )* - $opt:ident : $t:ty [$dep_tracking_marker:ident], + $opt:ident : $t:ty [$dep_tracking_marker:ident $( $tmod:ident )*], )* } ) => ( #[derive(Clone)] $( #[$top_level_attr] )* @@ -97,6 +145,16 @@ macro_rules! top_level_options { })* hasher.finish() } + + pub fn gather_target_modifiers(&self) -> Vec { + let mut mods = Vec::::new(); + $({ + gather_tmods_top_level!($opt, + &self.$opt, &mut mods, [$dep_tracking_marker], [$($tmod),*]); + })* + mods.sort_unstable(); + mods + } } ); } @@ -238,7 +296,7 @@ macro_rules! options { $($( #[$attr:meta] )* $opt:ident : $t:ty = ( $init:expr, $parse:ident, - [$dep_tracking_marker:ident], + [$dep_tracking_marker:ident $( $tmod:ident )*], $desc:expr) ),* ,) => ( @@ -277,6 +335,13 @@ macro_rules! options { ); hasher.finish() } + + pub fn gather_target_modifiers(&self, _mods: &mut Vec) { + $({ + gather_tmods!($opt, $init, + &self.$opt, _mods, [$dep_tracking_marker], [$($tmod),*]); + })* + } } pub const $stat: OptionDescrs<$struct_name> = @@ -372,6 +437,7 @@ mod desc { "a comma-separated list of strings, with elements beginning with + or -"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; + pub(crate) const parse_opt_comma_list_or_empty: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; pub(crate) const parse_opt_number: &str = parse_number; pub(crate) const parse_frame_pointer: &str = "one of `true`/`yes`/`on`, `false`/`no`/`off`, or (with -Zunstable-options) `non-leaf` or `always`"; @@ -656,6 +722,25 @@ mod parse { } } + // Allows both "-C(Z)flag=value1,value2,...,valueN" and "-C(Z)flag" (setting empty vector) + pub(crate) fn parse_opt_comma_list_or_empty( + slot: &mut Option>, + v: Option<&str>, + ) -> bool { + match v { + Some(s) => { + let mut v: Vec<_> = s.split(',').map(|s| s.to_string()).collect(); + v.sort_unstable(); + *slot = Some(v); + true + } + None => { + *slot = Some(Vec::::new()); + true + } + } + } + pub(crate) fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { match v.and_then(|s| s.parse().ok()) { Some(0) => { @@ -1656,6 +1741,9 @@ options! { target_feature: String = (String::new(), parse_target_feature, [TRACKED], "target specific attributes. (`rustc --print target-features` for details). \ This feature is unsafe."), + unsafe_allow_abi_mismatch: Option> = (None, parse_opt_comma_list_or_empty, [UNTRACKED], + "Allow incompatible target modifiers in dependency crates \ + (comma separated list or empty to allow all)"), // tidy-alphabetical-end // If you add a new option, please update: @@ -2000,7 +2088,7 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), - regparm: Option = (None, parse_opt_number, [TRACKED], + regparm: Option = (None, parse_opt_number, [TRACKED TMOD], "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ in registers EAX, EDX, and ECX instead of on the stack for\ \"C\", \"cdecl\", and \"stdcall\" fn.\ diff --git a/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs new file mode 100644 index 0000000000000..50cd64d2df4d1 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs @@ -0,0 +1,14 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Cpanic=abort +//@ needs-llvm-components: x86 +#![crate_type = "lib"] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub fn somefun() {} + +pub struct S; diff --git a/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr b/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr new file mode 100644 index 0000000000000..a5cae79d43a36 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr @@ -0,0 +1,15 @@ +error: mixing `regparm` will cause an ABI mismatch + --> $DIR/incompatible_regparm.rs:12:1 + | +LL | #![crate_type = "lib"] + | ^ + | + = help: This error occurs because the `regparm` flag modifies the ABI, + and different crates in your project were compiled with inconsistent settings + = note: `regparm`=`Some(1)` in crate `incompatible_regparm`, `regparm`=`Some(2)` in crate `wrong_regparm` + = note: To ignore this error, recompile with the following flag: + -Cunsafe-allow-abi-mismatch=`regparm` + = note: `#[deny(incompatible_target_modifiers)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/incompatible_regparm.rs b/tests/ui/target_modifiers/incompatible_regparm.rs new file mode 100644 index 0000000000000..35e21bc38afed --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.rs @@ -0,0 +1,21 @@ +//@ aux-crate:wrong_regparm=wrong_regparm.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=1 -Cpanic=abort +//@ needs-llvm-components: x86 +//@ revisions:error_generated allow_regparm_mismatch allow_any_mismatch allow_attr + +//@[allow_regparm_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=regparm +//@[allow_any_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch +//@[allow_regparm_mismatch] build-pass +//@[allow_any_mismatch] build-pass +//@[allow_attr] build-pass + +#![crate_type = "lib"] +//[error_generated]~^ ERROR 12:1: 12:1: mixing `regparm` will cause an ABI mismatch [incompatible_target_modifiers] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#![cfg_attr(allow_attr, allow(incompatible_target_modifiers))] + +fn foo() { + wrong_regparm::somefun(); +}