1
Fork 0

Auto merge of #110807 - petrochenkov:strictflavor, r=lqd,wesleywiser

linker: Report linker flavors incompatible with the current target

The linker flavor is checked for target compatibility even if linker is never used (e.g. we are producing a rlib).
If it causes trouble, we can move the check to `link.rs` so it will run if the linker (flavor) is actually used.

And also feature gate explicitly specifying linker flavors for tier 3 targets.

The next step is supporting all the internal linker flavors in user-visible interfaces (command line and json).
This commit is contained in:
bors 2023-05-31 22:40:25 +00:00
commit 9af3865dec
13 changed files with 156 additions and 60 deletions

View file

@ -11,6 +11,7 @@
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(exhaustive_patterns)]
#![feature(iter_intersperse)]
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(rustc_attrs)]

View file

@ -205,15 +205,11 @@ impl ToJson for LldFlavor {
}
impl LinkerFlavor {
pub fn from_cli(cli: LinkerFlavorCli, target: &TargetOptions) -> LinkerFlavor {
Self::from_cli_impl(cli, target.linker_flavor.lld_flavor(), target.linker_flavor.is_gnu())
}
/// The passed CLI flavor is preferred over other args coming from the default target spec,
/// so this function can produce a flavor that is incompatible with the current target.
/// FIXME: Produce errors when `-Clinker-flavor` is set to something incompatible
/// with the current target.
fn from_cli_impl(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor {
/// At this point the target's reference linker flavor doesn't yet exist and we need to infer
/// it. The inference always succeds and gives some result, and we don't report any flavor
/// incompatibility errors for json target specs. The CLI flavor is used as the main source
/// of truth, other flags are used in case of ambiguities.
fn from_cli_json(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor {
match cli {
LinkerFlavorCli::Gcc => match lld_flavor {
LldFlavor::Ld if is_gnu => LinkerFlavor::Gnu(Cc::Yes, Lld::No),
@ -257,6 +253,85 @@ impl LinkerFlavor {
}
}
fn infer_cli_hints(cli: LinkerFlavorCli) -> (Option<Cc>, Option<Lld>) {
match cli {
LinkerFlavorCli::Gcc | LinkerFlavorCli::Em => (Some(Cc::Yes), None),
LinkerFlavorCli::Lld(_) => (Some(Cc::No), Some(Lld::Yes)),
LinkerFlavorCli::Ld | LinkerFlavorCli::Msvc => (Some(Cc::No), Some(Lld::No)),
LinkerFlavorCli::BpfLinker | LinkerFlavorCli::PtxLinker => (None, None),
}
}
fn infer_linker_hints(linker_stem: &str) -> (Option<Cc>, Option<Lld>) {
// Remove any version postfix.
let stem = linker_stem
.rsplit_once('-')
.and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs))
.unwrap_or(linker_stem);
// GCC/Clang can have an optional target prefix.
if stem == "emcc"
|| stem == "gcc"
|| stem.ends_with("-gcc")
|| stem == "g++"
|| stem.ends_with("-g++")
|| stem == "clang"
|| stem.ends_with("-clang")
|| stem == "clang++"
|| stem.ends_with("-clang++")
{
(Some(Cc::Yes), None)
} else if stem == "wasm-ld"
|| stem.ends_with("-wasm-ld")
|| stem == "ld.lld"
|| stem == "lld"
|| stem == "rust-lld"
|| stem == "lld-link"
{
(Some(Cc::No), Some(Lld::Yes))
} else if stem == "ld" || stem.ends_with("-ld") || stem == "link" {
(Some(Cc::No), Some(Lld::No))
} else {
(None, None)
}
}
fn with_hints(self, (cc_hint, lld_hint): (Option<Cc>, Option<Lld>)) -> LinkerFlavor {
match self {
LinkerFlavor::Gnu(cc, lld) => {
LinkerFlavor::Gnu(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld))
}
LinkerFlavor::Darwin(cc, lld) => {
LinkerFlavor::Darwin(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld))
}
LinkerFlavor::WasmLld(cc) => LinkerFlavor::WasmLld(cc_hint.unwrap_or(cc)),
LinkerFlavor::Unix(cc) => LinkerFlavor::Unix(cc_hint.unwrap_or(cc)),
LinkerFlavor::Msvc(lld) => LinkerFlavor::Msvc(lld_hint.unwrap_or(lld)),
LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => self,
}
}
pub fn with_cli_hints(self, cli: LinkerFlavorCli) -> LinkerFlavor {
self.with_hints(LinkerFlavor::infer_cli_hints(cli))
}
pub fn with_linker_hints(self, linker_stem: &str) -> LinkerFlavor {
self.with_hints(LinkerFlavor::infer_linker_hints(linker_stem))
}
pub fn check_compatibility(self, cli: LinkerFlavorCli) -> Option<String> {
// The CLI flavor should be compatible with the target if it survives this roundtrip.
let compatible = |cli| cli == self.with_cli_hints(cli).to_cli();
(!compatible(cli)).then(|| {
LinkerFlavorCli::all()
.iter()
.filter(|cli| compatible(**cli))
.map(|cli| cli.desc())
.intersperse(", ")
.collect()
})
}
pub fn lld_flavor(self) -> LldFlavor {
match self {
LinkerFlavor::Gnu(..)
@ -278,6 +353,10 @@ impl LinkerFlavor {
macro_rules! linker_flavor_cli_impls {
($(($($flavor:tt)*) $string:literal)*) => (
impl LinkerFlavorCli {
const fn all() -> &'static [LinkerFlavorCli] {
&[$($($flavor)*,)*]
}
pub const fn one_of() -> &'static str {
concat!("one of: ", $($string, " ",)*)
}
@ -289,8 +368,8 @@ macro_rules! linker_flavor_cli_impls {
})
}
pub fn desc(&self) -> &str {
match *self {
pub fn desc(self) -> &'static str {
match self {
$($($flavor)* => $string,)*
}
}
@ -1801,7 +1880,7 @@ impl TargetOptions {
}
fn update_from_cli(&mut self) {
self.linker_flavor = LinkerFlavor::from_cli_impl(
self.linker_flavor = LinkerFlavor::from_cli_json(
self.linker_flavor_json,
self.lld_flavor_json,
self.linker_is_gnu_json,
@ -1815,12 +1894,7 @@ impl TargetOptions {
] {
args.clear();
for (flavor, args_json) in args_json {
// Cannot use `from_cli` due to borrow checker.
let linker_flavor = LinkerFlavor::from_cli_impl(
*flavor,
self.lld_flavor_json,
self.linker_is_gnu_json,
);
let linker_flavor = self.linker_flavor.with_cli_hints(*flavor);
// Normalize to no lld to avoid asserts.
let linker_flavor = match linker_flavor {
LinkerFlavor::Gnu(cc, _) => LinkerFlavor::Gnu(cc, Lld::No),