Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow #[link(kind = "dylib")] without a name #131966

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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(<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
27 changes: 14 additions & 13 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 @@ -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
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 @@ -1820,7 +1820,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 @@ -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" }
}
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)]
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 @@ -482,6 +482,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`.
Loading