1
Fork 0

Target modifiers (special marked options) are recorded in metainfo and compared to be equal in different crates

This commit is contained in:
Andrew Zhogin 2024-11-18 04:07:29 +07:00
parent 01a26c026d
commit 05c88a31e7
22 changed files with 666 additions and 24 deletions

View file

@ -269,6 +269,7 @@ fn configure_and_expand(
resolver.resolve_crate(&krate); resolver.resolve_crate(&krate);
CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate);
krate krate
} }

View file

@ -113,6 +113,14 @@ metadata_incompatible_rustc =
found crate `{$crate_name}` compiled by an incompatible version of rustc{$add_info} 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) .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 = metadata_incompatible_wasm_link =
`wasm_import_module` is incompatible with other arguments in `#[link]` attributes `wasm_import_module` is incompatible with other arguments in `#[link]` attributes
@ -284,6 +292,8 @@ metadata_unknown_link_kind =
metadata_unknown_link_modifier = metadata_unknown_link_modifier =
unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed 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 = metadata_unsupported_abi =
ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture

View file

@ -23,7 +23,10 @@ use rustc_hir::definitions::Definitions;
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; 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::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource};
use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::lint::{self, BuiltinLintDiag};
use rustc_session::output::validate_crate_name; use rustc_session::output::validate_crate_name;
@ -35,7 +38,9 @@ use tracing::{debug, info, trace};
use crate::errors; use crate::errors;
use crate::locator::{CrateError, CrateLocator, CratePaths}; 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. /// 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 /// 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<MetadataLoaderDyn>) -> CStore { pub fn new(metadata_loader: Box<MetadataLoaderDyn>) -> CStore {
CStore { CStore {
metadata_loader, metadata_loader,

View file

@ -739,3 +739,28 @@ pub(crate) struct WasmCAbi {
#[primary_span] #[primary_span]
pub span: 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,
}

View file

@ -29,6 +29,7 @@ use rustc_middle::{bug, implement_ty_decoder};
use rustc_serialize::opaque::MemDecoder; use rustc_serialize::opaque::MemDecoder;
use rustc_serialize::{Decodable, Decoder}; use rustc_serialize::{Decodable, Decoder};
use rustc_session::Session; use rustc_session::Session;
use rustc_session::config::TargetModifier;
use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_session::cstore::{CrateSource, ExternCrate};
use rustc_span::hygiene::HygieneDecodeContext; use rustc_span::hygiene::HygieneDecodeContext;
use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw}; use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw};
@ -73,6 +74,9 @@ impl MetadataBlob {
/// own crate numbers. /// own crate numbers.
pub(crate) type CrateNumMap = IndexVec<CrateNum, CrateNum>; pub(crate) type CrateNumMap = IndexVec<CrateNum, CrateNum>;
/// Target modifiers - abi or exploit mitigations flags
pub(crate) type TargetModifiers = Vec<TargetModifier>;
pub(crate) struct CrateMetadata { pub(crate) struct CrateMetadata {
/// The primary crate data - binary metadata blob. /// The primary crate data - binary metadata blob.
blob: MetadataBlob, blob: MetadataBlob,
@ -961,6 +965,13 @@ impl CrateRoot {
) -> impl ExactSizeIterator<Item = CrateDep> + Captures<'a> { ) -> impl ExactSizeIterator<Item = CrateDep> + Captures<'a> {
self.crate_deps.decode(metadata) self.crate_deps.decode(metadata)
} }
pub(crate) fn decode_target_modifiers<'a>(
&self,
metadata: &'a MetadataBlob,
) -> impl ExactSizeIterator<Item = TargetModifier> + Captures<'a> {
self.target_modifiers.decode(metadata)
}
} }
impl<'a> CrateMetadataRef<'a> { impl<'a> CrateMetadataRef<'a> {
@ -1883,6 +1894,10 @@ impl CrateMetadata {
self.dependencies.push(cnum); 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 { pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool {
let update = let update =
Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank); Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank);

View file

@ -25,7 +25,7 @@ use rustc_middle::ty::{AssocItemContainer, SymbolName};
use rustc_middle::util::common::to_readable_str; use rustc_middle::util::common::to_readable_str;
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; 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::hygiene::HygieneEncodeContext;
use rustc_span::{ use rustc_span::{
ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext, 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 // Encode source_map. This needs to be done last, because encoding `Span`s tells us which
// `SourceFiles` we actually need to encode. // `SourceFiles` we actually need to encode.
let source_map = stat!("source-map", || self.encode_source_map()); 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 root = stat!("final", || {
let attrs = tcx.hir().krate_attrs(); let attrs = tcx.hir().krate_attrs();
@ -735,6 +736,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
native_libraries, native_libraries,
foreign_modules, foreign_modules,
source_map, source_map,
target_modifiers,
traits, traits,
impls, impls,
incoherent_impls, incoherent_impls,
@ -2009,6 +2011,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.lazy_array(deps.iter().map(|(_, dep)| dep)) self.lazy_array(deps.iter().map(|(_, dep)| dep))
} }
fn encode_target_modifiers(&mut self) -> LazyArray<TargetModifier> {
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)> { fn encode_lib_features(&mut self) -> LazyArray<(Symbol, FeatureStability)> {
empty_proc_macro!(self); empty_proc_macro!(self);
let tcx = self.tcx; let tcx = self.tcx;

View file

@ -1,7 +1,7 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::num::NonZero; use std::num::NonZero;
pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob, TargetModifiers};
use decoder::{DecodeContext, Metadata}; use decoder::{DecodeContext, Metadata};
use def_path_hash_map::DefPathHashMapRef; use def_path_hash_map::DefPathHashMapRef;
use encoder::EncodeContext; use encoder::EncodeContext;
@ -32,7 +32,7 @@ use rustc_middle::ty::{
use rustc_middle::util::Providers; use rustc_middle::util::Providers;
use rustc_middle::{mir, trivially_parameterized_over_tcx}; use rustc_middle::{mir, trivially_parameterized_over_tcx};
use rustc_serialize::opaque::FileEncoder; 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_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData};
@ -282,6 +282,7 @@ pub(crate) struct CrateRoot {
def_path_hash_map: LazyValue<DefPathHashMapRef<'static>>, def_path_hash_map: LazyValue<DefPathHashMapRef<'static>>,
source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>, source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>,
target_modifiers: LazyArray<TargetModifier>,
compiler_builtins: bool, compiler_builtins: bool,
needs_allocator: bool, needs_allocator: bool,

View file

@ -101,6 +101,7 @@ trivially_parameterized_over_tcx! {
rustc_session::cstore::ForeignModule, rustc_session::cstore::ForeignModule,
rustc_session::cstore::LinkagePreference, rustc_session::cstore::LinkagePreference,
rustc_session::cstore::NativeLib, rustc_session::cstore::NativeLib,
rustc_session::config::TargetModifier,
rustc_span::ExpnData, rustc_span::ExpnData,
rustc_span::ExpnHash, rustc_span::ExpnHash,
rustc_span::ExpnId, rustc_span::ExpnId,

View file

@ -1191,6 +1191,7 @@ impl Default for Options {
color: ColorConfig::Auto, color: ColorConfig::Auto,
logical_env: FxIndexMap::default(), logical_env: FxIndexMap::default(),
verbose: false, 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) let crate_types = parse_crate_types_from_list(unparsed_crate_types)
.unwrap_or_else(|e| early_dcx.early_fatal(e)); .unwrap_or_else(|e| early_dcx.early_fatal(e));
let mut unstable_opts = UnstableOptions::build(early_dcx, matches); let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::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); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
check_error_format_stability(early_dcx, &unstable_opts, error_format); check_error_format_stability(early_dcx, &unstable_opts, error_format);
let output_types = parse_output_types(early_dcx, &unstable_opts, matches); 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( let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto(
early_dcx, early_dcx,
&output_types, &output_types,
@ -2615,6 +2618,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
color, color,
logical_env, logical_env,
verbose, verbose,
target_modifiers,
} }
} }

View file

@ -4,6 +4,9 @@
#![feature(let_chains)] #![feature(let_chains)]
#![feature(map_many_mut)] #![feature(map_many_mut)]
#![feature(rustc_attrs)] #![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)] #![warn(unreachable_pub)]
// tidy-alphabetical-end // tidy-alphabetical-end

View file

@ -10,6 +10,7 @@ use rustc_data_structures::profiling::TimePassesFormat;
use rustc_data_structures::stable_hasher::Hash64; use rustc_data_structures::stable_hasher::Hash64;
use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl}; use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl};
use rustc_feature::UnstableFeatures; use rustc_feature::UnstableFeatures;
use rustc_macros::{Decodable, Encodable};
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_span::{RealFileName, SourceFileHashAlgorithm};
use rustc_target::spec::{ 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<OptionsTargetModifiers, String>,
tmods: &mut Vec<TargetModifier>,
) {
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 { macro_rules! top_level_options {
( $( #[$top_level_attr:meta] )* pub struct Options { $( ( $( #[$top_level_attr:meta] )* pub struct Options { $(
$( #[$attr:meta] )* $( #[$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)] #[derive(Clone)]
$( #[$top_level_attr] )* $( #[$top_level_attr] )*
pub struct Options { pub struct Options {
$( $(
$( #[$attr] )* $( #[$attr] )*
pub $opt: $t pub $opt: $t
),* ),*,
pub target_modifiers: BTreeMap<OptionsTargetModifiers, String>,
} }
impl Options { impl Options {
@ -98,6 +275,17 @@ macro_rules! top_level_options {
})* })*
hasher.finish() hasher.finish()
} }
pub fn gather_target_modifiers(&self) -> Vec<TargetModifier> {
let mut mods = Vec::<TargetModifier>::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`")] #[rustc_lint_opt_deny_field_access("should only be used via `Config::hash_untracked_state`")]
untracked_state_hash: Hash64 [TRACKED_NO_CRATE_HASH], untracked_state_hash: Hash64 [TRACKED_NO_CRATE_HASH],
unstable_opts: UnstableOptions [SUBSTRUCT], unstable_opts: UnstableOptions [SUBSTRUCT UnstableOptionsTargetModifiers UnstableOptions],
prints: Vec<PrintRequest> [UNTRACKED], prints: Vec<PrintRequest> [UNTRACKED],
cg: CodegenOptions [SUBSTRUCT], cg: CodegenOptions [SUBSTRUCT CodegenOptionsTargetModifiers CodegenOptions],
externs: Externs [UNTRACKED], externs: Externs [UNTRACKED],
crate_name: Option<String> [TRACKED], crate_name: Option<String> [TRACKED],
/// Indicates how the compiler should treat unstable features. /// 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 /// 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 /// 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. /// 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 /// 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. /// hand-written parsers for parsing specific types of values in this module.
macro_rules! options { 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 = ( $($( #[$attr:meta] )* $opt:ident : $t:ty = (
$init:expr, $init:expr,
$parse:ident, $parse:ident,
[$dep_tracking_marker:ident], [$dep_tracking_marker:ident $( $tmod:ident )?],
$desc:expr $desc:expr
$(, deprecated_do_nothing: $dnn:literal )?) $(, deprecated_do_nothing: $dnn:literal )?)
),* ,) => ),* ,) =>
@ -248,6 +528,8 @@ macro_rules! options {
#[rustc_lint_opt_ty] #[rustc_lint_opt_ty]
pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* }
tmod_enum!( $tmod_enum_name, $prefix, {$($opt, $parse, $t, [$($tmod),*])|*} );
impl Default for $struct_name { impl Default for $struct_name {
fn default() -> $struct_name { fn default() -> $struct_name {
$struct_name { $($opt: $init),* } $struct_name { $($opt: $init),* }
@ -258,8 +540,9 @@ macro_rules! options {
pub fn build( pub fn build(
early_dcx: &EarlyDiagCtxt, early_dcx: &EarlyDiagCtxt,
matches: &getopts::Matches, matches: &getopts::Matches,
target_modifiers: &mut BTreeMap<OptionsTargetModifiers, String>,
) -> $struct_name { ) -> $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 { fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
@ -279,11 +562,23 @@ macro_rules! options {
); );
hasher.finish() hasher.finish()
} }
pub fn gather_target_modifiers(
&self,
_mods: &mut Vec<TargetModifier>,
_tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
) {
$({
gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals,
[$dep_tracking_marker], [$($tmod),*]);
})*
}
} }
pub const $stat: OptionDescrs<$struct_name> = pub const $stat: OptionDescrs<$struct_name> =
&[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, &[ $( 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 { mod $optmod {
$( $(
@ -328,6 +623,7 @@ pub struct OptionDesc<O> {
// description for option from options table // description for option from options table
desc: &'static str, desc: &'static str,
is_deprecated_and_do_nothing: bool, is_deprecated_and_do_nothing: bool,
tmod: Option<OptionsTargetModifiers>,
} }
impl<O> OptionDesc<O> { impl<O> OptionDesc<O> {
@ -344,6 +640,7 @@ impl<O> OptionDesc<O> {
fn build_options<O: Default>( fn build_options<O: Default>(
early_dcx: &EarlyDiagCtxt, early_dcx: &EarlyDiagCtxt,
matches: &getopts::Matches, matches: &getopts::Matches,
target_modifiers: &mut BTreeMap<OptionsTargetModifiers, String>,
descrs: OptionDescrs<O>, descrs: OptionDescrs<O>,
prefix: &str, prefix: &str,
outputname: &str, outputname: &str,
@ -357,7 +654,14 @@ fn build_options<O: Default>(
let option_to_lookup = key.replace('-', "_"); let option_to_lookup = key.replace('-', "_");
match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { 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 { if *is_deprecated_and_do_nothing {
// deprecation works for prefixed options only // deprecation works for prefixed options only
assert!(!prefix.is_empty()); assert!(!prefix.is_empty());
@ -377,6 +681,11 @@ fn build_options<O: Default>(
), ),
} }
} }
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}`")), None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")),
} }
@ -1581,7 +1890,7 @@ pub mod parse {
} }
options! { options! {
CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen", CodegenOptions, CodegenOptionsTargetModifiers, CG_OPTIONS, cgopts, "C", "codegen",
// If you add a new option, please update: // If you add a new option, please update:
// - compiler/rustc_interface/src/tests.rs // - compiler/rustc_interface/src/tests.rs
@ -1712,6 +2021,8 @@ options! {
target_feature: String = (String::new(), parse_target_feature, [TRACKED], target_feature: String = (String::new(), parse_target_feature, [TRACKED],
"target specific attributes. (`rustc --print target-features` for details). \ "target specific attributes. (`rustc --print target-features` for details). \
This feature is unsafe."), This feature is unsafe."),
unsafe_allow_abi_mismatch: Vec<String> = (Vec::new(), parse_comma_list, [UNTRACKED],
"Allow incompatible target modifiers in dependency crates (comma separated list)"),
// tidy-alphabetical-end // tidy-alphabetical-end
// If you add a new option, please update: // If you add a new option, please update:
@ -1720,7 +2031,7 @@ options! {
} }
options! { options! {
UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable", UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable",
// If you add a new option, please update: // If you add a new option, please update:
// - compiler/rustc_interface/src/tests.rs // - compiler/rustc_interface/src/tests.rs
@ -2052,10 +2363,10 @@ options! {
"enable queries of the dependency graph for regression testing (default: no)"), "enable queries of the dependency graph for regression testing (default: no)"),
randomize_layout: bool = (false, parse_bool, [TRACKED], randomize_layout: bool = (false, parse_bool, [TRACKED],
"randomize the layout of types (default: no)"), "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. "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!"), It is UNSOUND to link together crates that use different values for this flag!"),
regparm: Option<u32> = (None, parse_opt_number, [TRACKED], regparm: Option<u32> = (None, parse_opt_number, [TRACKED TARGET_MODIFIER],
"On x86-32 targets, setting this to N causes the compiler to pass N arguments \ "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\ in registers EAX, EDX, and ECX instead of on the stack for\
\"C\", \"cdecl\", and \"stdcall\" fn.\ \"C\", \"cdecl\", and \"stdcall\" fn.\

View file

@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::DiagCtxtHandle; use rustc_errors::DiagCtxtHandle;
use rustc_session::config::{ use rustc_session::config::{
self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns, self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns,
UnstableOptions, get_cmd_lint_options, nightly_options, parse_crate_types_from_list, OptionsTargetModifiers, UnstableOptions, get_cmd_lint_options, nightly_options,
parse_externs, parse_target_triple, parse_crate_types_from_list, parse_externs, parse_target_triple,
}; };
use rustc_session::lint::Level; use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath; use rustc_session::search_paths::SearchPath;
@ -387,8 +387,9 @@ impl Options {
config::parse_error_format(early_dcx, matches, color, json_color, json_rendered); config::parse_error_format(early_dcx, matches, color, json_color, json_rendered);
let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default();
let codegen_options = CodegenOptions::build(early_dcx, matches); let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::new();
let unstable_opts = UnstableOptions::build(early_dcx, matches); 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) { let remap_path_prefix = match parse_remap_path_prefix(matches) {
Ok(prefix_mappings) => prefix_mappings, Ok(prefix_mappings) => prefix_mappings,

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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();
}

View file

@ -0,0 +1,2 @@
error: codegen option `unsafe-allow-abi-mismatch` requires a comma-separated list of strings (C unsafe-allow-abi-mismatch=<value>)

View file

@ -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

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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