1
Fork 0

Implement RFC 2951: Native link modifiers

This commit implements both the native linking modifiers infrastructure
as well as an initial attempt at the individual modifiers from the RFC.
It also introduces a feature flag for the general syntax along with
individual feature flags for each modifier.
This commit is contained in:
Luqman Aden 2021-03-24 21:45:09 -07:00
parent bacf770f29
commit db555e1284
38 changed files with 829 additions and 170 deletions

View file

@ -8,8 +8,8 @@ use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
use rustc_session::utils::NativeLibKind;
use rustc_session::Session;
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
@ -56,6 +56,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
cfg: None,
foreign_module: Some(it.def_id.to_def_id()),
wasm_import_module: None,
verbatim: None,
};
let mut kind_specified = false;
@ -67,10 +68,18 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
None => continue, // skip like historical compilers
};
lib.kind = match &*kind.as_str() {
"static" => NativeLibKind::StaticBundle,
"static-nobundle" => NativeLibKind::StaticNoBundle,
"dylib" => NativeLibKind::Dylib,
"framework" => NativeLibKind::Framework,
"static" => NativeLibKind::Static { bundle: None, whole_archive: None },
"static-nobundle" => {
sess.struct_span_warn(
item.span(),
"library kind `static-nobundle` has been superseded by specifying \
modifier `-bundle` with library kind `static`",
)
.emit();
NativeLibKind::Static { bundle: Some(false), whole_archive: None }
}
"dylib" => NativeLibKind::Dylib { as_needed: None },
"framework" => NativeLibKind::Framework { as_needed: None },
"raw-dylib" => NativeLibKind::RawDylib,
k => {
struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
@ -108,6 +117,71 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
}
}
// Do this outside the above loop so we don't depend on modifiers coming
// after kinds
if let Some(item) = items.iter().find(|item| item.has_name(sym::modifiers)) {
if let Some(modifiers) = item.value_str() {
let span = item.name_value_literal_span().unwrap();
for modifier in modifiers.as_str().split(',') {
let (modifier, value) = match modifier.strip_prefix(&['+', '-'][..]) {
Some(m) => (m, modifier.starts_with('+')),
None => {
sess.span_err(
span,
"invalid linking modifier syntax, expected '+' or '-' prefix \
before one of: bundle, verbatim, whole-archive, as-needed",
);
continue;
}
};
match (modifier, &mut lib.kind) {
("bundle", NativeLibKind::Static { bundle, .. }) => {
*bundle = Some(value);
}
("bundle", _) => sess.span_err(
span,
"bundle linking modifier is only compatible with \
`static` linking kind",
),
("verbatim", _) => lib.verbatim = Some(value),
("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
*whole_archive = Some(value);
}
("whole-archive", _) => sess.span_err(
span,
"whole-archive linking modifier is only compatible with \
`static` linking kind",
),
("as-needed", NativeLibKind::Dylib { as_needed })
| ("as-needed", NativeLibKind::Framework { as_needed }) => {
*as_needed = Some(value);
}
("as-needed", _) => sess.span_err(
span,
"as-needed linking modifier is only compatible with \
`dylib` and `framework` linking kinds",
),
_ => sess.span_err(
span,
&format!(
"unrecognized linking modifier `{}`, expected one \
of: bundle, verbatim, whole-archive, as-needed",
modifier
),
),
}
}
} else {
let msg = "must be of the form `#[link(modifiers = \"...\")]`";
sess.span_err(item.span(), msg);
}
}
// In general we require #[link(name = "...")] but we allow
// #[link(wasm_import_module = "...")] without the `name`.
let requires_name = kind_specified || lib.wasm_import_module.is_none();
@ -152,7 +226,7 @@ impl Collector<'tcx> {
return;
}
let is_osx = self.tcx.sess.target.is_like_osx;
if lib.kind == NativeLibKind::Framework && !is_osx {
if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx {
let msg = "native frameworks are only available on macOS targets";
match span {
Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(),
@ -168,7 +242,9 @@ impl Collector<'tcx> {
)
.emit();
}
if lib.kind == NativeLibKind::StaticNoBundle && !self.tcx.features().static_nobundle {
if matches!(lib.kind, NativeLibKind::Static { bundle: Some(false), .. })
&& !self.tcx.features().static_nobundle
{
feature_err(
&self.tcx.sess.parse_sess,
sym::static_nobundle,
@ -193,30 +269,30 @@ impl Collector<'tcx> {
fn process_command_line(&mut self) {
// First, check for errors
let mut renames = FxHashSet::default();
for (name, new_name, _) in &self.tcx.sess.opts.libs {
if let Some(ref new_name) = new_name {
for lib in &self.tcx.sess.opts.libs {
if let Some(ref new_name) = lib.new_name {
let any_duplicate = self
.libs
.iter()
.filter_map(|lib| lib.name.as_ref())
.any(|n| &n.as_str() == name);
.any(|n| &n.as_str() == &lib.name);
if new_name.is_empty() {
self.tcx.sess.err(&format!(
"an empty renaming target was specified for library `{}`",
name
lib.name
));
} else if !any_duplicate {
self.tcx.sess.err(&format!(
"renaming of the library `{}` was specified, \
however this crate contains no `#[link(...)]` \
attributes referencing this library.",
name
lib.name
));
} else if !renames.insert(name) {
} else if !renames.insert(&lib.name) {
self.tcx.sess.err(&format!(
"multiple renamings were \
specified for library `{}` .",
name
lib.name
));
}
}
@ -229,7 +305,7 @@ impl Collector<'tcx> {
// it. (This ensures that the linker is able to see symbols from
// all possible dependent libraries before linking in the library
// in question.)
for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
for passed_lib in &self.tcx.sess.opts.libs {
// If we've already added any native libraries with the same
// name, they will be pulled out into `existing`, so that we
// can move them to the end of the list below.
@ -237,13 +313,14 @@ impl Collector<'tcx> {
.libs
.drain_filter(|lib| {
if let Some(lib_name) = lib.name {
if lib_name.as_str() == *name {
if kind != NativeLibKind::Unspecified {
lib.kind = kind;
if lib_name.as_str() == passed_lib.name {
if passed_lib.kind != NativeLibKind::Unspecified {
lib.kind = passed_lib.kind;
}
if let Some(new_name) = new_name {
if let Some(new_name) = &passed_lib.new_name {
lib.name = Some(Symbol::intern(new_name));
}
lib.verbatim = passed_lib.verbatim;
return true;
}
}
@ -252,13 +329,14 @@ impl Collector<'tcx> {
.collect::<Vec<_>>();
if existing.is_empty() {
// Add if not found
let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
let new_name = passed_lib.new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
let lib = NativeLib {
name: Some(Symbol::intern(new_name.unwrap_or(name))),
kind,
name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
kind: passed_lib.kind,
cfg: None,
foreign_module: None,
wasm_import_module: None,
verbatim: passed_lib.verbatim,
};
self.register_native_lib(None, lib);
} else {

View file

@ -256,16 +256,13 @@ pub fn provide(providers: &mut Providers) {
// resolve! Does this work? Unsure! That's what the issue is about
*providers = Providers {
is_dllimport_foreign_item: |tcx, id| match tcx.native_library_kind(id) {
Some(NativeLibKind::Dylib | NativeLibKind::RawDylib | NativeLibKind::Unspecified) => {
true
}
Some(
NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified,
) => true,
_ => false,
},
is_statically_included_foreign_item: |tcx, id| {
matches!(
tcx.native_library_kind(id),
Some(NativeLibKind::StaticBundle | NativeLibKind::StaticNoBundle)
)
matches!(tcx.native_library_kind(id), Some(NativeLibKind::Static { .. }))
},
native_library_kind: |tcx, id| {
tcx.native_libraries(id.krate)