From 05c88a31e7d421b311710571b80add5c0959a6d3 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_metadata/messages.ftl | 10 + compiler/rustc_metadata/src/creader.rs | 97 ++++- compiler/rustc_metadata/src/errors.rs | 25 ++ compiler/rustc_metadata/src/rmeta/decoder.rs | 15 + compiler/rustc_metadata/src/rmeta/encoder.rs | 10 +- compiler/rustc_metadata/src/rmeta/mod.rs | 5 +- compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_session/src/config.rs | 8 +- compiler/rustc_session/src/lib.rs | 3 + compiler/rustc_session/src/options.rs | 337 +++++++++++++++++- src/librustdoc/config.rs | 9 +- .../auxiliary/default_reg_struct_return.rs | 20 ++ .../auxiliary/wrong_regparm.rs | 20 ++ .../auxiliary/wrong_regparm_and_ret.rs | 20 ++ .../defaults_check.error.stderr | 13 + tests/ui/target_modifiers/defaults_check.rs | 27 ++ ...incompatible_regparm.allow_no_value.stderr | 2 + ...ncompatible_regparm.error_generated.stderr | 13 + .../target_modifiers/incompatible_regparm.rs | 23 ++ tests/ui/target_modifiers/two_flags.rs | 23 ++ .../two_flags.unknown_allowed.stderr | 8 + 22 files changed, 666 insertions(+), 24 deletions(-) create mode 100644 tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs create mode 100644 tests/ui/target_modifiers/auxiliary/wrong_regparm.rs create mode 100644 tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs create mode 100644 tests/ui/target_modifiers/defaults_check.error.stderr create mode 100644 tests/ui/target_modifiers/defaults_check.rs create mode 100644 tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr create mode 100644 tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr create mode 100644 tests/ui/target_modifiers/incompatible_regparm.rs create mode 100644 tests/ui/target_modifiers/two_flags.rs create mode 100644 tests/ui/target_modifiers/two_flags.unknown_allowed.stderr diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 241bc35857a4..f9cb5f217a59 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -269,6 +269,7 @@ fn configure_and_expand( resolver.resolve_crate(&krate); + CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate); krate } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index d2b5ae531859..6fc84b06647f 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -113,6 +113,14 @@ metadata_incompatible_rustc = found crate `{$crate_name}` compiled by an incompatible version of rustc{$add_info} .help = please recompile that crate using this compiler ({$rustc_version}) (consider running `cargo clean` first) +metadata_incompatible_target_modifiers = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + +metadata_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error +metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$flag_extern_value}` in this crate or `{$flag_name_prefixed}={$flag_local_value}` in `{$extern_crate}` + metadata_incompatible_wasm_link = `wasm_import_module` is incompatible with other arguments in `#[link]` attributes @@ -284,6 +292,8 @@ metadata_unknown_link_kind = metadata_unknown_link_modifier = unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed +metadata_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$flag_name}`, requested by `-Cunsafe-allow-abi-mismatch={$flag_name}` + metadata_unsupported_abi = ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index a6cd0ecafd0d..6bad83127900 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -23,7 +23,10 @@ use rustc_hir::definitions::Definitions; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; -use rustc_session::config::{self, CrateType, ExternLocation}; +use rustc_session::config::{ + self, CrateType, ExtendedTargetModifierInfo, ExternLocation, OptionsTargetModifiers, + TargetModifier, +}; use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; @@ -35,7 +38,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 @@ -296,6 +301,94 @@ impl CStore { } } + fn report_target_modifiers_extended( + tcx: TyCtxt<'_>, + krate: &Crate, + mods: &TargetModifiers, + dep_mods: &TargetModifiers, + data: &CrateMetadata, + ) { + let span = krate.spans.inner_span.shrink_to_lo(); + let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch; + let name = tcx.crate_name(LOCAL_CRATE); + let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone()); + let report_diff = |prefix: &String, + opt_name: &String, + flag_local_value: &String, + flag_extern_value: &String| { + if allowed_flag_mismatches.contains(&opt_name) { + return; + } + tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { + span, + extern_crate: data.name(), + local_crate: name, + flag_name: opt_name.clone(), + flag_name_prefixed: format!("-{}{}", prefix, opt_name), + flag_local_value: flag_local_value.to_string(), + flag_extern_value: flag_extern_value.to_string(), + }); + }; + let mut it1 = mods.iter().map(tmod_extender); + let mut it2 = dep_mods.iter().map(tmod_extender); + let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; + let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = 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.1.opt.cmp(&r.1.opt) { + cmp::Ordering::Equal => { + if l.0.tech_value != r.0.tech_value { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &r.1.value_name); + } + left_name_val = None; + right_name_val = None; + } + cmp::Ordering::Greater => { + report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + right_name_val = None; + } + cmp::Ordering::Less => { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + left_name_val = None; + } + }, + (Some(l), None) => { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + left_name_val = None; + } + (None, Some(r)) => { + report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + right_name_val = None; + } + (None, None) => break, + } + } + } + + pub fn report_incompatible_target_modifiers(&self, tcx: TyCtxt<'_>, krate: &Crate) { + for flag_name in &tcx.sess.opts.cg.unsafe_allow_abi_mismatch { + if !OptionsTargetModifiers::is_target_modifier(flag_name) { + tcx.dcx().emit_err(errors::UnknownTargetModifierUnsafeAllowed { + span: krate.spans.inner_span.shrink_to_lo(), + flag_name: flag_name.clone(), + }); + } + } + let mods = tcx.sess.opts.gather_target_modifiers(); + for (_cnum, data) in self.iter_crate_data() { + if data.is_proc_macro_crate() { + continue; + } + let dep_mods = data.target_modifiers(); + if mods != dep_mods { + Self::report_target_modifiers_extended(tcx, krate, &mods, &dep_mods, data); + } + } + } + pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index cefc6498f68f..a77f9bc623b5 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -739,3 +739,28 @@ pub(crate) struct WasmCAbi { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_target_modifiers)] +#[help] +#[note] +#[help(metadata_incompatible_target_modifiers_help_fix)] +#[help(metadata_incompatible_target_modifiers_help_allow)] +pub struct IncompatibleTargetModifiers { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_prefixed: String, + pub flag_local_value: String, + pub flag_extern_value: String, +} + +#[derive(Diagnostic)] +#[diag(metadata_unknown_target_modifier_unsafe_allowed)] +pub struct UnknownTargetModifierUnsafeAllowed { + #[primary_span] + pub span: Span, + pub flag_name: String, +} diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e02c4871f35b..2ae4a6a6066d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -29,6 +29,7 @@ use rustc_middle::{bug, implement_ty_decoder}; use rustc_serialize::opaque::MemDecoder; use rustc_serialize::{Decodable, Decoder}; use rustc_session::Session; +use rustc_session::config::TargetModifier; use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_span::hygiene::HygieneDecodeContext; use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw}; @@ -73,6 +74,9 @@ impl MetadataBlob { /// own crate numbers. pub(crate) type CrateNumMap = IndexVec; +/// Target modifiers - abi or exploit mitigations flags +pub(crate) type TargetModifiers = Vec; + pub(crate) struct CrateMetadata { /// The primary crate data - binary metadata blob. blob: MetadataBlob, @@ -961,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> { @@ -1883,6 +1894,10 @@ impl CrateMetadata { self.dependencies.push(cnum); } + pub(crate) fn target_modifiers(&self) -> TargetModifiers { + self.root.decode_target_modifiers(&self.blob).collect() + } + 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 c538ab99fb54..161388a290b0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -25,7 +25,7 @@ use rustc_middle::ty::{AssocItemContainer, SymbolName}; use rustc_middle::util::common::to_readable_str; use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; -use rustc_session::config::{CrateType, OptLevel}; +use rustc_session::config::{CrateType, OptLevel, TargetModifier}; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext, @@ -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(); @@ -735,6 +736,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { native_libraries, foreign_modules, source_map, + target_modifiers, traits, impls, incoherent_impls, @@ -2009,6 +2011,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 4f9cdc9a474b..fd0f9bd05b31 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; @@ -32,7 +32,7 @@ use rustc_middle::ty::{ use rustc_middle::util::Providers; use rustc_middle::{mir, trivially_parameterized_over_tcx}; use rustc_serialize::opaque::FileEncoder; -use rustc_session::config::SymbolManglingVersion; +use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; @@ -282,6 +282,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_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 6b6c6f3c72fe..8eaf0a58f708 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -101,6 +101,7 @@ trivially_parameterized_over_tcx! { rustc_session::cstore::ForeignModule, rustc_session::cstore::LinkagePreference, rustc_session::cstore::NativeLib, + rustc_session::config::TargetModifier, rustc_span::ExpnData, rustc_span::ExpnHash, rustc_span::ExpnId, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 97bd2670aa64..a301a0458018 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1191,6 +1191,7 @@ impl Default for Options { color: ColorConfig::Auto, logical_env: FxIndexMap::default(), verbose: false, + target_modifiers: BTreeMap::default(), } } } @@ -2337,14 +2338,16 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let crate_types = parse_crate_types_from_list(unparsed_crate_types) .unwrap_or_else(|e| early_dcx.early_fatal(e)); - let mut unstable_opts = UnstableOptions::build(early_dcx, matches); + let mut target_modifiers = BTreeMap::::new(); + + let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches); check_error_format_stability(early_dcx, &unstable_opts, error_format); let output_types = parse_output_types(early_dcx, &unstable_opts, matches); - let mut cg = CodegenOptions::build(early_dcx, matches); + let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( early_dcx, &output_types, @@ -2615,6 +2618,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M color, logical_env, verbose, + target_modifiers, } } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index dcf86d1a4089..924350b8cbcd 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -4,6 +4,9 @@ #![feature(let_chains)] #![feature(map_many_mut)] #![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"] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 63aaa3abc8e5..b804a5881ccd 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -10,6 +10,7 @@ use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::stable_hasher::Hash64; use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl}; use rustc_feature::UnstableFeatures; +use rustc_macros::{Decodable, Encodable}; use rustc_span::edition::Edition; use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ @@ -59,18 +60,194 @@ macro_rules! hash_substruct { }; } +/// Extended target modifier info. +/// For example, when external target modifier is '-Zregparm=2': +/// Target modifier enum value + user value ('2') from external crate +/// is converted into description: prefix ('Z'), name ('regparm'), tech value ('Some(2)'). +pub struct ExtendedTargetModifierInfo { + /// Flag prefix (usually, 'C' for codegen flags or 'Z' for unstable flags) + pub prefix: String, + /// Flag name + pub name: String, + /// Flag parsed technical value + pub tech_value: String, +} + +/// A recorded -Zopt_name=opt_value (or -Copt_name=opt_value) +/// which alter the ABI or effectiveness of exploit mitigations. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] +pub struct TargetModifier { + /// Option enum value + pub opt: OptionsTargetModifiers, + /// User-provided option value (before parsing) + pub value_name: String, +} + +impl TargetModifier { + pub fn extend(&self) -> ExtendedTargetModifierInfo { + self.opt.reparse(&self.value_name) + } +} + +fn tmod_push_impl( + opt: OptionsTargetModifiers, + tmod_vals: &BTreeMap, + tmods: &mut Vec, +) { + tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() }) +} + +macro_rules! tmod_push { + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => { + 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, $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, $mods:expr, $tmod_vals:expr, + [UNTRACKED], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr: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, $mods:expr, $tmod_vals:expr, + [UNTRACKED], []) => {{}}; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED], []) => {{}}; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr: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 + ( + @parse + {$($variant:tt($substruct_enum:tt))*}, + ($user_value:ident){$($pout:tt)*}; + ) => { + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)] + pub enum OptionsTargetModifiers { + $($variant($substruct_enum)),* + } + impl OptionsTargetModifiers { + #[allow(unused_variables)] + pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { + #[allow(unreachable_patterns)] + match self { + $($pout)* + _ => panic!("unknown target modifier option: {:?}", *self) + } + } + pub fn is_target_modifier(flag_name: &str) -> bool { + $($substruct_enum::is_target_modifier(flag_name))||* + } + } + }; + // 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], + $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] )* pub struct Options { $( $( #[$attr] )* pub $opt: $t - ),* + ),*, + pub target_modifiers: BTreeMap, } impl Options { @@ -98,6 +275,17 @@ 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, &self.target_modifiers, + [$dep_tracking_marker $($tmod),*]); + })* + mods.sort_by(|a, b| a.opt.cmp(&b.opt)); + mods + } } ); } @@ -165,9 +353,9 @@ top_level_options!( #[rustc_lint_opt_deny_field_access("should only be used via `Config::hash_untracked_state`")] untracked_state_hash: Hash64 [TRACKED_NO_CRATE_HASH], - unstable_opts: UnstableOptions [SUBSTRUCT], + unstable_opts: UnstableOptions [SUBSTRUCT UnstableOptionsTargetModifiers UnstableOptions], prints: Vec [UNTRACKED], - cg: CodegenOptions [SUBSTRUCT], + cg: CodegenOptions [SUBSTRUCT CodegenOptionsTargetModifiers CodegenOptions], externs: Externs [UNTRACKED], crate_name: Option [TRACKED], /// Indicates how the compiler should treat unstable features. @@ -226,6 +414,98 @@ 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 + ( + $tmod_enum_name:ident, $prefix:expr, + @parse + {$($eout:tt)*}, + ($user_value:ident){$($pout:tt)*}; + ) => { + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)] + pub enum $tmod_enum_name { + $($eout),* + } + impl $tmod_enum_name { + #[allow(unused_variables)] + pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { + #[allow(unreachable_patterns)] + match self { + $($pout)* + _ => panic!("unknown target modifier option: {:?}", *self) + } + } + pub fn is_target_modifier(flag_name: &str) -> bool { + match flag_name.replace('-', "_").as_str() { + $(stringify!($eout) => 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(); + parse::$parse(&mut parsed, Some($puser_value)); + 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. @@ -235,11 +515,11 @@ top_level_options!( /// 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, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, + ($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], + [$dep_tracking_marker:ident $( $tmod:ident )?], $desc:expr $(, deprecated_do_nothing: $dnn:literal )?) ),* ,) => @@ -248,6 +528,8 @@ macro_rules! options { #[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),* } @@ -258,8 +540,9 @@ macro_rules! options { pub fn build( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, + target_modifiers: &mut BTreeMap, ) -> $struct_name { - build_options(early_dcx, matches, $stat, $prefix, $outputname) + build_options(early_dcx, matches, target_modifiers, $stat, $prefix, $outputname) } fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 { @@ -279,11 +562,23 @@ macro_rules! options { ); hasher.finish() } + + pub fn gather_target_modifiers( + &self, + _mods: &mut Vec, + _tmod_vals: &BTreeMap, + ) { + $({ + gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals, + [$dep_tracking_marker], [$($tmod),*]); + })* + } } pub const $stat: OptionDescrs<$struct_name> = &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, - type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )? } ),* ]; + type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )?, + tmod: tmod_enum_opt!($struct_name, $tmod_enum_name, $opt, $($tmod),*) } ),* ]; mod $optmod { $( @@ -328,6 +623,7 @@ pub struct OptionDesc { // description for option from options table desc: &'static str, is_deprecated_and_do_nothing: bool, + tmod: Option, } impl OptionDesc { @@ -344,6 +640,7 @@ impl OptionDesc { fn build_options( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, + target_modifiers: &mut BTreeMap, descrs: OptionDescrs, prefix: &str, outputname: &str, @@ -357,7 +654,14 @@ fn build_options( let option_to_lookup = key.replace('-', "_"); match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { - Some(OptionDesc { name: _, setter, type_desc, desc, is_deprecated_and_do_nothing }) => { + Some(OptionDesc { + name: _, + setter, + type_desc, + desc, + is_deprecated_and_do_nothing, + tmod, + }) => { if *is_deprecated_and_do_nothing { // deprecation works for prefixed options only assert!(!prefix.is_empty()); @@ -377,6 +681,11 @@ fn build_options( ), } } + if let Some(tmod) = *tmod + && let Some(value) = value + { + target_modifiers.insert(tmod, value.to_string()); + } } None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")), } @@ -1581,7 +1890,7 @@ pub mod parse { } options! { - CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen", + CodegenOptions, CodegenOptionsTargetModifiers, CG_OPTIONS, cgopts, "C", "codegen", // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs @@ -1712,6 +2021,8 @@ 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: Vec = (Vec::new(), parse_comma_list, [UNTRACKED], + "Allow incompatible target modifiers in dependency crates (comma separated list)"), // tidy-alphabetical-end // If you add a new option, please update: @@ -1720,7 +2031,7 @@ options! { } options! { - UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable", + UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs @@ -2052,10 +2363,10 @@ 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)"), - reg_struct_return: bool = (false, parse_bool, [TRACKED], + reg_struct_return: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], "On x86-32 targets, it overrides the default ABI to return small structs in registers. It is UNSOUND to link together crates that use different values for this flag!"), - regparm: Option = (None, parse_opt_number, [TRACKED], + regparm: Option = (None, parse_opt_number, [TRACKED TARGET_MODIFIER], "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/src/librustdoc/config.rs b/src/librustdoc/config.rs index 80bc6cebd2aa..7437f2045c59 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; use rustc_session::config::{ self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns, - UnstableOptions, get_cmd_lint_options, nightly_options, parse_crate_types_from_list, - parse_externs, parse_target_triple, + OptionsTargetModifiers, UnstableOptions, get_cmd_lint_options, nightly_options, + parse_crate_types_from_list, parse_externs, parse_target_triple, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -387,8 +387,9 @@ impl Options { config::parse_error_format(early_dcx, matches, color, json_color, json_rendered); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); - let codegen_options = CodegenOptions::build(early_dcx, matches); - let unstable_opts = UnstableOptions::build(early_dcx, matches); + let mut target_modifiers = BTreeMap::::new(); + let codegen_options = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); + let unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); let remap_path_prefix = match parse_remap_path_prefix(matches) { Ok(prefix_mappings) => prefix_mappings, diff --git a/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs b/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs new file mode 100644 index 000000000000..355e7c56e946 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![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/auxiliary/wrong_regparm.rs b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs new file mode 100644 index 000000000000..2e16f1ee747c --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![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/auxiliary/wrong_regparm_and_ret.rs b/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs new file mode 100644 index 000000000000..39c6be9d5892 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Zreg-struct-return=true -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![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/defaults_check.error.stderr b/tests/ui/target_modifiers/defaults_check.error.stderr new file mode 100644 index 000000000000..c545dd710690 --- /dev/null +++ b/tests/ui/target_modifiers/defaults_check.error.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `defaults_check` + --> $DIR/defaults_check.rs:20:1 + | +LL | #![crate_type = "rlib"] + | ^ + | + = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zreg-struct-return=true` in this crate is incompatible with `-Zreg-struct-return=` in dependency `default_reg_struct_return` + = help: set `-Zreg-struct-return=` in this crate or `-Zreg-struct-return=true` in `default_reg_struct_return` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/defaults_check.rs b/tests/ui/target_modifiers/defaults_check.rs new file mode 100644 index 000000000000..b8f4848d3a42 --- /dev/null +++ b/tests/ui/target_modifiers/defaults_check.rs @@ -0,0 +1,27 @@ +// Tests that default unspecified target modifier value in dependency crate is ok linked +// with the same value, explicitly specified +//@ aux-crate:default_reg_struct_return=default_reg_struct_return.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +//@ revisions:error ok ok_explicit +//@[ok] compile-flags: +//@[ok_explicit] compile-flags: -Zreg-struct-return=false +//@[error] compile-flags: -Zreg-struct-return=true + +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@[ok] build-pass +//@[ok_explicit] build-pass + +#![crate_type = "rlib"] +//[error]~^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `defaults_check` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + default_reg_struct_return::somefun(); +} diff --git a/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr b/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr new file mode 100644 index 000000000000..8597332825b5 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr @@ -0,0 +1,2 @@ +error: codegen option `unsafe-allow-abi-mismatch` requires a comma-separated list of strings (C unsafe-allow-abi-mismatch=) + 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 000000000000..692fc7a4e3fa --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zregparm` will cause an ABI mismatch in crate `incompatible_regparm` + --> $DIR/incompatible_regparm.rs:16:1 + | +LL | #![crate_type = "rlib"] + | ^ + | + = help: the `-Zregparm` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zregparm=1` in this crate is incompatible with `-Zregparm=2` in dependency `wrong_regparm` + = help: set `-Zregparm=2` in this crate or `-Zregparm=1` in `wrong_regparm` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=regparm` to silence this error + +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 000000000000..e866c5aa891b --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.rs @@ -0,0 +1,23 @@ +//@ aux-crate:wrong_regparm=wrong_regparm.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=1 -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@ revisions:error_generated allow_regparm_mismatch allow_no_value + +//@[allow_regparm_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=regparm +//@[allow_regparm_mismatch] build-pass +//@[allow_no_value] compile-flags: -Cunsafe-allow-abi-mismatch + +#![crate_type = "rlib"] +//[error_generated]~^ ERROR mixing `-Zregparm` will cause an ABI mismatch in crate `incompatible_regparm` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + wrong_regparm::somefun(); +} diff --git a/tests/ui/target_modifiers/two_flags.rs b/tests/ui/target_modifiers/two_flags.rs new file mode 100644 index 000000000000..ca17117a267b --- /dev/null +++ b/tests/ui/target_modifiers/two_flags.rs @@ -0,0 +1,23 @@ +//@ aux-crate:wrong_regparm_and_ret=wrong_regparm_and_ret.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@ revisions:two_allowed unknown_allowed + +//@[two_allowed] compile-flags: -Cunsafe-allow-abi-mismatch=regparm,reg-struct-return +//@[two_allowed] build-pass +//@[unknown_allowed] compile-flags: -Cunsafe-allow-abi-mismatch=unknown_flag -Zregparm=2 -Zreg-struct-return=true + +#![crate_type = "rlib"] +//[unknown_allowed]~^ ERROR unknown target modifier `unknown_flag`, requested by `-Cunsafe-allow-abi-mismatch=unknown_flag` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + wrong_regparm_and_ret::somefun(); +} diff --git a/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr b/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr new file mode 100644 index 000000000000..c8040c6e389b --- /dev/null +++ b/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr @@ -0,0 +1,8 @@ +error: unknown target modifier `unknown_flag`, requested by `-Cunsafe-allow-abi-mismatch=unknown_flag` + --> $DIR/two_flags.rs:16:1 + | +LL | #![crate_type = "rlib"] + | ^ + +error: aborting due to 1 previous error +