Skip to content

Commit d23d8b4

Browse files
committed
Auto merge of #131966 - ChrisDenton:bare-link, r=<try>
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
2 parents bfab34a + 701594c commit d23d8b4

File tree

14 files changed

+143
-55
lines changed

14 files changed

+143
-55
lines changed

compiler/rustc_codegen_llvm/src/callee.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
142142
// MinGW: For backward compatibility we rely on the linker to decide whether it
143143
// should use dllimport for functions.
144144
if cx.use_dll_storage_attrs
145-
&& let Some(library) = tcx.native_library(instance_def_id)
146-
&& library.kind.is_dllimport()
145+
&& tcx.is_dllimport(instance_def_id)
147146
&& !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc")
148147
{
149148
llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);

compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,7 @@ impl<'ll> CodegenCx<'ll, '_> {
358358
}
359359
}
360360

361-
if self.use_dll_storage_attrs
362-
&& let Some(library) = self.tcx.native_library(def_id)
363-
&& library.kind.is_dllimport()
364-
{
361+
if self.use_dll_storage_attrs && self.tcx.is_dllimport(def_id) {
365362
// For foreign (native) libs we know the exact storage type to use.
366363
unsafe {
367364
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ declare_features! (
377377
(unstable, async_fn_track_caller, "1.73.0", Some(110011)),
378378
/// Allows `for await` loops.
379379
(unstable, async_for_loop, "1.77.0", Some(118898)),
380+
/// Allows `#[link(kind = "dylib")]` without a library name.
381+
(unstable, bare_link_kind, "CURRENT_RUSTC_VERSION", None),
380382
/// Allows using C-variadics.
381383
(unstable, c_variadic, "1.34.0", Some(44930)),
382384
/// Allows the use of `#[cfg(<true/false>)]`.

compiler/rustc_metadata/src/native_libs.rs

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
99
use rustc_session::Session;
1010
use rustc_session::config::CrateType;
1111
use rustc_session::cstore::{
12-
DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType,
12+
DllCallingConvention, DllImport, ForeignModule, NativeLib, NativeLibs, PeImportNameType,
1313
};
1414
use rustc_session::parse::feature_err;
1515
use rustc_session::search_paths::PathKind;
1616
use rustc_session::utils::NativeLibKind;
1717
use rustc_span::def_id::{DefId, LOCAL_CRATE};
18-
use rustc_span::symbol::{Symbol, sym};
18+
use rustc_span::symbol::{Symbol, kw, sym};
1919
use rustc_target::spec::LinkSelfContainedComponents;
2020
use rustc_target::spec::abi::Abi;
2121

@@ -173,15 +173,15 @@ fn find_bundled_library(
173173
None
174174
}
175175

176-
pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
176+
pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> NativeLibs {
177177
let mut collector = Collector { tcx, libs: Vec::new() };
178178
if tcx.sess.opts.unstable_opts.link_directives {
179179
for module in tcx.foreign_modules(LOCAL_CRATE).values() {
180180
collector.process_module(module);
181181
}
182182
}
183183
collector.process_command_line();
184-
collector.libs
184+
collector.libs.into()
185185
}
186186

187187
pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
@@ -191,6 +191,20 @@ pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
191191
}
192192
}
193193

194+
pub(crate) fn find_by_id<'a, I: Iterator<Item = &'a NativeLib>>(
195+
tcx: TyCtxt<'_>,
196+
id: DefId,
197+
iter: I,
198+
) -> Option<&'a NativeLib> {
199+
iter.filter(|lib| relevant_lib(tcx.sess, lib)).find(|lib| {
200+
let Some(fm_id) = lib.foreign_module else {
201+
return false;
202+
};
203+
let map = tcx.foreign_modules(id.krate);
204+
map.get(&fm_id).expect("failed to find foreign module").foreign_items.contains(&id)
205+
})
206+
}
207+
194208
struct Collector<'tcx> {
195209
tcx: TyCtxt<'tcx>,
196210
libs: Vec<NativeLib>,
@@ -445,10 +459,6 @@ impl<'tcx> Collector<'tcx> {
445459
if wasm_import_module.is_some() {
446460
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
447461
}
448-
let Some((name, name_span)) = name else {
449-
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
450-
continue;
451-
};
452462

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

470+
if kind != Some(NativeLibKind::RawDylib) {
471+
for &child_item in foreign_items {
472+
if self.tcx.def_kind(child_item).has_codegen_attrs()
473+
&& self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
474+
{
475+
let link_ordinal_attr =
476+
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
477+
sess.dcx()
478+
.emit_err(errors::LinkOrdinalRawDylib { span: link_ordinal_attr.span });
479+
}
480+
}
481+
}
482+
460483
let dll_imports = match kind {
461484
Some(NativeLibKind::RawDylib) => {
485+
let Some((name, name_span)) = name else {
486+
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
487+
continue;
488+
};
462489
if name.as_str().contains('\0') {
463490
sess.dcx().emit_err(errors::RawDylibNoNul { span: name_span });
464491
}
@@ -473,25 +500,29 @@ impl<'tcx> Collector<'tcx> {
473500
})
474501
.collect()
475502
}
476-
_ => {
477-
for &child_item in foreign_items {
478-
if self.tcx.def_kind(child_item).has_codegen_attrs()
479-
&& self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
480-
{
481-
let link_ordinal_attr =
482-
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
483-
sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
484-
span: link_ordinal_attr.span,
485-
});
486-
}
487-
}
503+
_ => Vec::new(),
504+
};
488505

489-
Vec::new()
490-
}
506+
// Allow kind of "dylib" or "static" without adding a native lib.
507+
let name = if self.tcx.features().bare_link_kind
508+
&& matches!(kind, Some(NativeLibKind::Dylib { .. } | NativeLibKind::Static { .. }))
509+
&& items.len() == 1
510+
{
511+
kw::Empty
512+
} else {
513+
let Some((name, _)) = name else {
514+
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
515+
continue;
516+
};
517+
name
491518
};
492519

493520
let kind = kind.unwrap_or(NativeLibKind::Unspecified);
494-
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
521+
let filename = if !name.is_empty() {
522+
find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx)
523+
} else {
524+
None
525+
};
495526
self.libs.push(NativeLib {
496527
name,
497528
filename,

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType;
1717
use rustc_middle::ty::{self, TyCtxt};
1818
use rustc_middle::util::Providers;
1919
use rustc_session::cstore::{CrateStore, ExternCrate};
20+
use rustc_session::utils::NativeLibKind;
2021
use rustc_session::{Session, StableCrateId};
2122
use rustc_span::Span;
2223
use rustc_span::hygiene::ExpnId;
@@ -437,30 +438,27 @@ provide! { tcx, def_id, other, cdata,
437438

438439
pub(in crate::rmeta) fn provide(providers: &mut Providers) {
439440
provide_cstore_hooks(providers);
440-
// FIXME(#44234) - almost all of these queries have no sub-queries and
441-
// therefore no actual inputs, they're just reading tables calculated in
442-
// resolve! Does this work? Unsure! That's what the issue is about
443441
providers.queries = rustc_middle::query::Providers {
444442
allocator_kind: |tcx, ()| CStore::from_tcx(tcx).allocator_kind(),
445443
alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(),
446444
is_private_dep: |_tcx, LocalCrate| false,
447445
native_library: |tcx, id| {
448-
tcx.native_libraries(id.krate)
449-
.iter()
450-
.filter(|lib| native_libs::relevant_lib(tcx.sess, lib))
451-
.find(|lib| {
452-
let Some(fm_id) = lib.foreign_module else {
453-
return false;
454-
};
455-
let map = tcx.foreign_modules(id.krate);
456-
map.get(&fm_id)
457-
.expect("failed to find foreign module")
458-
.foreign_items
459-
.contains(&id)
460-
})
446+
native_libs::find_by_id(tcx, id, tcx.native_libraries(id.krate).iter())
461447
},
462448
native_libraries: native_libs::collect,
463449
foreign_modules: foreign_modules::collect,
450+
is_dllimport: |tcx, id| {
451+
native_libs::find_by_id(tcx, id, tcx.native_libraries(id.krate).iter_all_items())
452+
.map(|l| {
453+
matches!(
454+
l.kind,
455+
NativeLibKind::Dylib { .. }
456+
| NativeLibKind::RawDylib
457+
| NativeLibKind::Unspecified
458+
)
459+
})
460+
.unwrap_or(false)
461+
},
464462

465463
// Returns a map from a sufficiently visible external item (i.e., an
466464
// external item that is visible from at least one local module) to a

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1813,7 +1813,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
18131813
fn encode_native_libraries(&mut self) -> LazyArray<NativeLib> {
18141814
empty_proc_macro!(self);
18151815
let used_libraries = self.tcx.native_libraries(LOCAL_CRATE);
1816-
self.lazy_array(used_libraries.iter())
1816+
self.lazy_array(used_libraries.iter_all_items())
18171817
}
18181818

18191819
fn encode_foreign_modules(&mut self) -> LazyArray<ForeignModule> {

compiler/rustc_middle/src/query/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use rustc_query_system::query::{QueryCache, QueryMode, QueryState, try_get_cache
3535
use rustc_session::Limits;
3636
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
3737
use rustc_session::cstore::{
38-
CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib,
38+
CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, NativeLibs,
3939
};
4040
use rustc_session::lint::LintExpectationId;
4141
use rustc_span::def_id::LOCAL_CRATE;
@@ -406,7 +406,7 @@ rustc_queries! {
406406
/// These are assembled from the following places:
407407
/// - `extern` blocks (depending on their `link` attributes)
408408
/// - the `libs` (`-l`) option
409-
query native_libraries(_: CrateNum) -> &'tcx Vec<NativeLib> {
409+
query native_libraries(_: CrateNum) -> &'tcx NativeLibs {
410410
arena_cache
411411
desc { "looking up the native libraries of a linked crate" }
412412
separate_provide_extern
@@ -1734,6 +1734,10 @@ rustc_queries! {
17341734
desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) }
17351735
}
17361736

1737+
query is_dllimport(def_id: DefId) -> bool {
1738+
desc { |tcx| "determining dllimport status of `{}`", tcx.def_path_str(def_id) }
1739+
}
1740+
17371741
query inherit_sig_for_delegation_item(def_id: LocalDefId) -> &'tcx [Ty<'tcx>] {
17381742
desc { "inheriting delegation signature" }
17391743
}

compiler/rustc_session/src/cstore.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,31 @@ impl NativeLib {
8888
}
8989
}
9090

91+
#[derive(Debug, HashStable_Generic)] // Encodable, Decodable,
92+
pub struct NativeLibs {
93+
libs: Vec<NativeLib>,
94+
}
95+
impl NativeLibs {
96+
pub fn iter(&self) -> impl Iterator<Item = &NativeLib> {
97+
// Hide entries without a library name.
98+
self.iter_all_items().filter(|l| !l.name.is_empty())
99+
}
100+
101+
pub fn iter_all_items(&self) -> impl Iterator<Item = &NativeLib> {
102+
self.libs.iter()
103+
}
104+
}
105+
impl From<Vec<NativeLib>> for NativeLibs {
106+
fn from(libs: Vec<NativeLib>) -> Self {
107+
Self { libs }
108+
}
109+
}
110+
impl FromIterator<NativeLib> for NativeLibs {
111+
fn from_iter<T: IntoIterator<Item = NativeLib>>(iter: T) -> Self {
112+
Self { libs: FromIterator::from_iter(iter) }
113+
}
114+
}
115+
91116
/// Different ways that the PE Format can decorate a symbol name.
92117
/// From <https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-name-type>
93118
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, PartialEq, Eq)]

compiler/rustc_session/src/utils.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,6 @@ impl NativeLibKind {
7070
pub fn is_statically_included(&self) -> bool {
7171
matches!(self, NativeLibKind::Static { .. })
7272
}
73-
74-
pub fn is_dllimport(&self) -> bool {
75-
matches!(
76-
self,
77-
NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified
78-
)
79-
}
8073
}
8174

8275
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ symbols! {
490490
avx512f,
491491
await_macro,
492492
bang,
493+
bare_link_kind,
493494
begin_panic,
494495
bench,
495496
bin,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#![crate_type = "cdylib"]
2+
3+
#[no_mangle]
4+
pub static FOO: u32 = 0xFEDCBA98;

tests/ui/extern/bare_link_kind.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ run-pass
2+
//@ aux-build:bare_link_kind_cdylib.rs
3+
4+
#![feature(bare_link_kind)]
5+
6+
#[link(kind = "dylib")]
7+
extern "C" {
8+
static FOO: u32;
9+
}
10+
11+
#[cfg_attr(not(target_env = "msvc"), link(name = "bare_link_kind_cdylib", kind = "dylib"))]
12+
#[cfg_attr(target_env = "msvc", link(name = "bare_link_kind_cdylib.dll", kind = "dylib"))]
13+
extern "C" {}
14+
15+
fn main() {
16+
unsafe {
17+
assert_eq!(FOO, 0xFEDCBA98);
18+
}
19+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#[link(kind = "dylib")] //~ ERROR `#[link]` attribute requires a `name = "string"` argument
2+
extern "C" {
3+
static FOO: u32;
4+
}
5+
6+
fn main() {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0459]: `#[link]` attribute requires a `name = "string"` argument
2+
--> $DIR/feature-gate-bare-link-kind.rs:1:1
3+
|
4+
LL | #[link(kind = "dylib")]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `name` argument
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0459`.

0 commit comments

Comments
 (0)