diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 206a706979204..22ffa097e11f5 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -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); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 33a85adeb8781..e30fdcb8988ca 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -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); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8182eb1e9735b..a9990ddcdec36 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -368,6 +368,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", Some(132061)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg()]`. diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 1e6bc413118ec..af3f3f5f2cc58 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -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; @@ -173,7 +173,7 @@ fn find_bundled_library( None } -pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec { +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() { @@ -181,7 +181,7 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec } } collector.process_command_line(); - collector.libs + collector.libs.into() } pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { @@ -191,6 +191,20 @@ pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { } } +pub(crate) fn find_by_id<'a, I: Iterator>( + 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, @@ -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 { @@ -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 }); } @@ -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, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 71c7231a78826..69c2d8c556317 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -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; @@ -443,22 +444,22 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { 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 diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index e06c86ae4c03e..11da74873b2d5 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1820,7 +1820,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_native_libraries(&mut self) -> LazyArray { 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 { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d03fc39c9ade1..21815bac50452 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -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; @@ -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 { + query native_libraries(_: CrateNum) -> &'tcx NativeLibs { arena_cache desc { "looking up the native libraries of a linked crate" } separate_provide_extern @@ -1746,6 +1746,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" } } diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 041a10475eaad..08cf0fa62e19e 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -88,6 +88,31 @@ impl NativeLib { } } +#[derive(Debug, HashStable_Generic)] +pub struct NativeLibs { + libs: Vec, +} +impl NativeLibs { + pub fn iter(&self) -> impl Iterator { + // Hide entries without a library name. + self.iter_all_items().filter(|l| !l.name.is_empty()) + } + + pub fn iter_all_items(&self) -> impl Iterator { + self.libs.iter() + } +} +impl From> for NativeLibs { + fn from(libs: Vec) -> Self { + Self { libs } + } +} +impl FromIterator for NativeLibs { + fn from_iter>(iter: T) -> Self { + Self { libs: FromIterator::from_iter(iter) } + } +} + /// Different ways that the PE Format can decorate a symbol name. /// From #[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, PartialEq, Eq)] diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 9182789cf0269..ae852cac5a303 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -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)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bf5f948fe9199..3831eb7f131d8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -482,6 +482,7 @@ symbols! { avx512f, await_macro, bang, + bare_link_kind, begin_panic, bench, bin, diff --git a/tests/ui/extern/auxiliary/bare_link_kind_cdylib.rs b/tests/ui/extern/auxiliary/bare_link_kind_cdylib.rs new file mode 100644 index 0000000000000..021a71b09a47d --- /dev/null +++ b/tests/ui/extern/auxiliary/bare_link_kind_cdylib.rs @@ -0,0 +1,4 @@ +#![crate_type = "cdylib"] + +#[no_mangle] +pub static FOO: u32 = 0xFEDCBA98; diff --git a/tests/ui/extern/bare_link_kind.rs b/tests/ui/extern/bare_link_kind.rs new file mode 100644 index 0000000000000..939e245eed2e6 --- /dev/null +++ b/tests/ui/extern/bare_link_kind.rs @@ -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); + } +} diff --git a/tests/ui/feature-gates/feature-gate-bare-link-kind.rs b/tests/ui/feature-gates/feature-gate-bare-link-kind.rs new file mode 100644 index 0000000000000..f4a0adb94bb9c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-bare-link-kind.rs @@ -0,0 +1,6 @@ +#[link(kind = "dylib")] //~ ERROR `#[link]` attribute requires a `name = "string"` argument +extern "C" { + static FOO: u32; +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-bare-link-kind.stderr b/tests/ui/feature-gates/feature-gate-bare-link-kind.stderr new file mode 100644 index 0000000000000..a40770c0b9062 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-bare-link-kind.stderr @@ -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`.