Skip to content

Commit

Permalink
Auto merge of rust-lang#131966 - ChrisDenton:bare-link, r=<try>
Browse files Browse the repository at this point in the history
Allow #[link(kind = "dylib")] without a name

This PR allows `#[link(kind = "dylib")]` without a library name (see the [dllimport RFC](https://rust-lang.github.io/rfcs/1717-dllimport.html) for how this affects `extern {}` blocks).

This will need a lang fcp but I wanted to investigate how feasible this is. I want a bare `kind` to act the same as any other `#[link]` for the purposes of applying (or not) dllimport so it gets added to the same array. However, this then means they need to be filtered out from normal queries. To facilitate this I've added a wrapper type for the `NativeLib` container and a separate query for `is_dllimport`.

try-job: x86_64-msvc
try-job: x86_64-mingw
  • Loading branch information
bors committed Oct 20, 2024
2 parents bfab34a + 701594c commit d23d8b4
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 55 deletions.
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_llvm/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
// MinGW: For backward compatibility we rely on the linker to decide whether it
// should use dllimport for functions.
if cx.use_dll_storage_attrs
&& let Some(library) = tcx.native_library(instance_def_id)
&& library.kind.is_dllimport()
&& tcx.is_dllimport(instance_def_id)
&& !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc")
{
llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,7 @@ impl<'ll> CodegenCx<'ll, '_> {
}
}

if self.use_dll_storage_attrs
&& let Some(library) = self.tcx.native_library(def_id)
&& library.kind.is_dllimport()
{
if self.use_dll_storage_attrs && self.tcx.is_dllimport(def_id) {
// For foreign (native) libs we know the exact storage type to use.
unsafe {
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ declare_features! (
(unstable, async_fn_track_caller, "1.73.0", Some(110011)),
/// Allows `for await` loops.
(unstable, async_for_loop, "1.77.0", Some(118898)),
/// Allows `#[link(kind = "dylib")]` without a library name.
(unstable, bare_link_kind, "CURRENT_RUSTC_VERSION", None),
/// Allows using C-variadics.
(unstable, c_variadic, "1.34.0", Some(44930)),
/// Allows the use of `#[cfg(<true/false>)]`.
Expand Down
77 changes: 54 additions & 23 deletions compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use rustc_session::Session;
use rustc_session::config::CrateType;
use rustc_session::cstore::{
DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType,
DllCallingConvention, DllImport, ForeignModule, NativeLib, NativeLibs, PeImportNameType,
};
use rustc_session::parse::feature_err;
use rustc_session::search_paths::PathKind;
use rustc_session::utils::NativeLibKind;
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::symbol::{Symbol, sym};
use rustc_span::symbol::{Symbol, kw, sym};
use rustc_target::spec::LinkSelfContainedComponents;
use rustc_target::spec::abi::Abi;

Expand Down Expand Up @@ -173,15 +173,15 @@ fn find_bundled_library(
None
}

pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> NativeLibs {
let mut collector = Collector { tcx, libs: Vec::new() };
if tcx.sess.opts.unstable_opts.link_directives {
for module in tcx.foreign_modules(LOCAL_CRATE).values() {
collector.process_module(module);
}
}
collector.process_command_line();
collector.libs
collector.libs.into()
}

pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
Expand All @@ -191,6 +191,20 @@ pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
}
}

pub(crate) fn find_by_id<'a, I: Iterator<Item = &'a NativeLib>>(
tcx: TyCtxt<'_>,
id: DefId,
iter: I,
) -> Option<&'a NativeLib> {
iter.filter(|lib| relevant_lib(tcx.sess, lib)).find(|lib| {
let Some(fm_id) = lib.foreign_module else {
return false;
};
let map = tcx.foreign_modules(id.krate);
map.get(&fm_id).expect("failed to find foreign module").foreign_items.contains(&id)
})
}

struct Collector<'tcx> {
tcx: TyCtxt<'tcx>,
libs: Vec<NativeLib>,
Expand Down Expand Up @@ -445,10 +459,6 @@ impl<'tcx> Collector<'tcx> {
if wasm_import_module.is_some() {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
}
let Some((name, name_span)) = name else {
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
continue;
};

// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
if let Some((_, span)) = import_name_type {
Expand All @@ -457,8 +467,25 @@ impl<'tcx> Collector<'tcx> {
}
}

if kind != Some(NativeLibKind::RawDylib) {
for &child_item in foreign_items {
if self.tcx.def_kind(child_item).has_codegen_attrs()
&& self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
{
let link_ordinal_attr =
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
sess.dcx()
.emit_err(errors::LinkOrdinalRawDylib { span: link_ordinal_attr.span });
}
}
}

let dll_imports = match kind {
Some(NativeLibKind::RawDylib) => {
let Some((name, name_span)) = name else {
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
continue;
};
if name.as_str().contains('\0') {
sess.dcx().emit_err(errors::RawDylibNoNul { span: name_span });
}
Expand All @@ -473,25 +500,29 @@ impl<'tcx> Collector<'tcx> {
})
.collect()
}
_ => {
for &child_item in foreign_items {
if self.tcx.def_kind(child_item).has_codegen_attrs()
&& self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
{
let link_ordinal_attr =
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
span: link_ordinal_attr.span,
});
}
}
_ => Vec::new(),
};

Vec::new()
}
// Allow kind of "dylib" or "static" without adding a native lib.
let name = if self.tcx.features().bare_link_kind
&& matches!(kind, Some(NativeLibKind::Dylib { .. } | NativeLibKind::Static { .. }))
&& items.len() == 1
{
kw::Empty
} else {
let Some((name, _)) = name else {
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
continue;
};
name
};

let kind = kind.unwrap_or(NativeLibKind::Unspecified);
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
let filename = if !name.is_empty() {
find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx)
} else {
None
};
self.libs.push(NativeLib {
name,
filename,
Expand Down
30 changes: 14 additions & 16 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::util::Providers;
use rustc_session::cstore::{CrateStore, ExternCrate};
use rustc_session::utils::NativeLibKind;
use rustc_session::{Session, StableCrateId};
use rustc_span::Span;
use rustc_span::hygiene::ExpnId;
Expand Down Expand Up @@ -437,30 +438,27 @@ provide! { tcx, def_id, other, cdata,

pub(in crate::rmeta) fn provide(providers: &mut Providers) {
provide_cstore_hooks(providers);
// FIXME(#44234) - almost all of these queries have no sub-queries and
// therefore no actual inputs, they're just reading tables calculated in
// resolve! Does this work? Unsure! That's what the issue is about
providers.queries = rustc_middle::query::Providers {
allocator_kind: |tcx, ()| CStore::from_tcx(tcx).allocator_kind(),
alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(),
is_private_dep: |_tcx, LocalCrate| false,
native_library: |tcx, id| {
tcx.native_libraries(id.krate)
.iter()
.filter(|lib| native_libs::relevant_lib(tcx.sess, lib))
.find(|lib| {
let Some(fm_id) = lib.foreign_module else {
return false;
};
let map = tcx.foreign_modules(id.krate);
map.get(&fm_id)
.expect("failed to find foreign module")
.foreign_items
.contains(&id)
})
native_libs::find_by_id(tcx, id, tcx.native_libraries(id.krate).iter())
},
native_libraries: native_libs::collect,
foreign_modules: foreign_modules::collect,
is_dllimport: |tcx, id| {
native_libs::find_by_id(tcx, id, tcx.native_libraries(id.krate).iter_all_items())
.map(|l| {
matches!(
l.kind,
NativeLibKind::Dylib { .. }
| NativeLibKind::RawDylib
| NativeLibKind::Unspecified
)
})
.unwrap_or(false)
},

// Returns a map from a sufficiently visible external item (i.e., an
// external item that is visible from at least one local module) to a
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1813,7 +1813,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
fn encode_native_libraries(&mut self) -> LazyArray<NativeLib> {
empty_proc_macro!(self);
let used_libraries = self.tcx.native_libraries(LOCAL_CRATE);
self.lazy_array(used_libraries.iter())
self.lazy_array(used_libraries.iter_all_items())
}

fn encode_foreign_modules(&mut self) -> LazyArray<ForeignModule> {
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use rustc_query_system::query::{QueryCache, QueryMode, QueryState, try_get_cache
use rustc_session::Limits;
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
use rustc_session::cstore::{
CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib,
CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, NativeLibs,
};
use rustc_session::lint::LintExpectationId;
use rustc_span::def_id::LOCAL_CRATE;
Expand Down Expand Up @@ -406,7 +406,7 @@ rustc_queries! {
/// These are assembled from the following places:
/// - `extern` blocks (depending on their `link` attributes)
/// - the `libs` (`-l`) option
query native_libraries(_: CrateNum) -> &'tcx Vec<NativeLib> {
query native_libraries(_: CrateNum) -> &'tcx NativeLibs {
arena_cache
desc { "looking up the native libraries of a linked crate" }
separate_provide_extern
Expand Down Expand Up @@ -1734,6 +1734,10 @@ rustc_queries! {
desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) }
}

query is_dllimport(def_id: DefId) -> bool {
desc { |tcx| "determining dllimport status of `{}`", tcx.def_path_str(def_id) }
}

query inherit_sig_for_delegation_item(def_id: LocalDefId) -> &'tcx [Ty<'tcx>] {
desc { "inheriting delegation signature" }
}
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_session/src/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,31 @@ impl NativeLib {
}
}

#[derive(Debug, HashStable_Generic)] // Encodable, Decodable,
pub struct NativeLibs {
libs: Vec<NativeLib>,
}
impl NativeLibs {
pub fn iter(&self) -> impl Iterator<Item = &NativeLib> {
// Hide entries without a library name.
self.iter_all_items().filter(|l| !l.name.is_empty())
}

pub fn iter_all_items(&self) -> impl Iterator<Item = &NativeLib> {
self.libs.iter()
}
}
impl From<Vec<NativeLib>> for NativeLibs {
fn from(libs: Vec<NativeLib>) -> Self {
Self { libs }
}
}
impl FromIterator<NativeLib> for NativeLibs {
fn from_iter<T: IntoIterator<Item = NativeLib>>(iter: T) -> Self {
Self { libs: FromIterator::from_iter(iter) }
}
}

/// Different ways that the PE Format can decorate a symbol name.
/// From <https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-name-type>
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, PartialEq, Eq)]
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_session/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,6 @@ impl NativeLibKind {
pub fn is_statically_included(&self) -> bool {
matches!(self, NativeLibKind::Static { .. })
}

pub fn is_dllimport(&self) -> bool {
matches!(
self,
NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified
)
}
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ symbols! {
avx512f,
await_macro,
bang,
bare_link_kind,
begin_panic,
bench,
bin,
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/extern/auxiliary/bare_link_kind_cdylib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#![crate_type = "cdylib"]

#[no_mangle]
pub static FOO: u32 = 0xFEDCBA98;
19 changes: 19 additions & 0 deletions tests/ui/extern/bare_link_kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@ run-pass
//@ aux-build:bare_link_kind_cdylib.rs

#![feature(bare_link_kind)]

#[link(kind = "dylib")]
extern "C" {
static FOO: u32;
}

#[cfg_attr(not(target_env = "msvc"), link(name = "bare_link_kind_cdylib", kind = "dylib"))]
#[cfg_attr(target_env = "msvc", link(name = "bare_link_kind_cdylib.dll", kind = "dylib"))]
extern "C" {}

fn main() {
unsafe {
assert_eq!(FOO, 0xFEDCBA98);
}
}
6 changes: 6 additions & 0 deletions tests/ui/feature-gates/feature-gate-bare-link-kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[link(kind = "dylib")] //~ ERROR `#[link]` attribute requires a `name = "string"` argument
extern "C" {
static FOO: u32;
}

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/feature-gates/feature-gate-bare-link-kind.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0459]: `#[link]` attribute requires a `name = "string"` argument
--> $DIR/feature-gate-bare-link-kind.rs:1:1
|
LL | #[link(kind = "dylib")]
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `name` argument

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0459`.

0 comments on commit d23d8b4

Please sign in to comment.