Skip to content
/ rust Public
forked from rust-lang/rust

Commit 019294a

Browse files
authored
Rollup merge of rust-lang#156370 - qaijuang:eii-dylib-default-override, r=bjorn3
Reject linked dylib EII default overrides This PR rejects explicit EII implementations that would override a default implementation already selected through a linked dylib. The check is intentionally split: - rustc_passes keeps the early, format-independent checks for missing impls and duplicate explicit impls. - rustc_codegen_ssa checks the default-vs-explicit conflict during linking, using the dependency formats selected for the final artifact. Fixes rust-lang#156320.
2 parents fa20c08 + 41bdaed commit 019294a

11 files changed

Lines changed: 237 additions & 38 deletions

File tree

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_metadata::{
3030
walk_native_lib_search_dirs,
3131
};
3232
use rustc_middle::bug;
33+
use rustc_middle::error::DuplicateEiiImpls;
3334
use rustc_middle::lint::emit_lint_base;
3435
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
3536
use rustc_middle::middle::dependency_format::Linkage;
@@ -76,6 +77,64 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
7677
}
7778
}
7879

80+
fn eii_impl_crate_name(crate_info: &CrateInfo, cnum: CrateNum) -> Symbol {
81+
if cnum == LOCAL_CRATE { crate_info.local_crate_name } else { crate_info.crate_name[&cnum] }
82+
}
83+
84+
fn check_externally_implementable_item_linkage(sess: &Session, crate_info: &CrateInfo) {
85+
if crate_info.eii_linkage.is_empty() {
86+
return;
87+
}
88+
89+
// A crate can request multiple linked outputs with overlapping dependency
90+
// formats, so report each underlying conflict once.
91+
let mut emitted = FxHashSet::default();
92+
93+
// This needs the dependency formats selected for the final artifact. The
94+
// earlier EII pass still handles missing impls and duplicate explicit impls.
95+
for dependency_formats in crate_info.dependency_formats.values() {
96+
for (eii_index, eii) in crate_info.eii_linkage.iter().enumerate() {
97+
let Some(explicit_impl) = eii.impls.first() else {
98+
continue;
99+
};
100+
// If the explicit impl is already coming from a dylib, that dylib
101+
// has already resolved the default-vs-explicit choice.
102+
if matches!(
103+
dependency_formats.get(explicit_impl.impl_crate),
104+
Some(Linkage::Dynamic | Linkage::IncludedFromDylib)
105+
) {
106+
continue;
107+
}
108+
109+
let Some(default_impl) = &eii.default_impl else {
110+
continue;
111+
};
112+
if !matches!(
113+
dependency_formats.get(default_impl.impl_crate),
114+
Some(Linkage::Dynamic | Linkage::IncludedFromDylib)
115+
) {
116+
continue;
117+
}
118+
119+
if !emitted.insert(eii_index) {
120+
continue;
121+
}
122+
123+
sess.dcx().emit_err(DuplicateEiiImpls {
124+
name: eii.name,
125+
first_span: explicit_impl.span,
126+
first_crate: eii_impl_crate_name(crate_info, explicit_impl.impl_crate),
127+
second_span: default_impl.span,
128+
second_crate: eii_impl_crate_name(crate_info, default_impl.impl_crate),
129+
help: (),
130+
additional_crates: None,
131+
num_additional_crates: 0,
132+
additional_crate_names: String::new(),
133+
});
134+
}
135+
}
136+
}
137+
79138
/// Performs the linkage portion of the compilation phase. This will generate all
80139
/// of the requested outputs for this compilation session.
81140
pub fn link_binary(
@@ -91,6 +150,14 @@ pub fn link_binary(
91150
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
92151
let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
93152
let mut rmeta_link_cache = RmetaLinkCache::default();
153+
154+
if outputs.outputs.should_link() {
155+
sess.time("check_externally_implementable_item_linkage", || {
156+
check_externally_implementable_item_linkage(sess, &crate_info);
157+
});
158+
sess.dcx().abort_if_errors();
159+
}
160+
94161
for &crate_type in &crate_info.crate_types {
95162
// Ignore executable crates if we have -Z no-codegen, as they will error.
96163
if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())

compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
use std::cmp;
21
use std::collections::BTreeSet;
32
use std::sync::Arc;
43
use std::time::{Duration, Instant};
4+
use std::{cmp, iter};
55

66
use itertools::Itertools;
77
use rustc_abi::FIRST_VARIANT;
88
use rustc_ast::expand::allocator::{
99
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorMethodInput,
1010
AllocatorTy,
1111
};
12-
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
12+
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
1313
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
1414
use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
1515
use rustc_data_structures::unord::UnordMap;
16-
use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr};
17-
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
16+
use rustc_hir::attrs::{DebuggerVisualizerType, EiiDecl, EiiImpl, OptimizeAttr};
17+
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
1818
use rustc_hir::lang_items::LangItem;
1919
use rustc_hir::{ItemId, Target, find_attr};
2020
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
2121
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
22-
use rustc_middle::middle::dependency_format::Dependencies;
22+
use rustc_middle::middle::dependency_format::{Dependencies, Linkage};
2323
use rustc_middle::middle::exported_symbols::{self, SymbolExportKind};
2424
use rustc_middle::middle::lang_items;
2525
use rustc_middle::mir::BinOp;
@@ -50,7 +50,8 @@ use crate::mir::operand::OperandValue;
5050
use crate::mir::place::PlaceRef;
5151
use crate::traits::*;
5252
use crate::{
53-
CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, ModuleCodegen, errors, meth, mir,
53+
CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, EiiLinkageImplInfo, EiiLinkageInfo,
54+
ModuleCodegen, errors, meth, mir,
5455
};
5556

5657
pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
@@ -914,6 +915,71 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>(
914915
&& !is_extern_call_to_local_crate(tcx, instance)
915916
}
916917

918+
fn collect_eii_linkage(tcx: TyCtxt<'_>) -> Vec<EiiLinkageInfo> {
919+
#[derive(Debug)]
920+
struct FoundImpl {
921+
imp: EiiImpl,
922+
impl_crate: CrateNum,
923+
}
924+
925+
#[derive(Debug)]
926+
struct FoundEii {
927+
decl: EiiDecl,
928+
impls: FxIndexMap<DefId, FoundImpl>,
929+
}
930+
931+
let mut eiis = FxIndexMap::<DefId, FoundEii>::default();
932+
933+
for &cnum in tcx.crates(()).iter().chain(iter::once(&LOCAL_CRATE)) {
934+
for (&did, &(decl, ref impls)) in tcx.externally_implementable_items(cnum) {
935+
eiis.entry(did)
936+
.or_insert_with(|| FoundEii { decl, impls: Default::default() })
937+
.impls
938+
.extend(
939+
impls
940+
.into_iter()
941+
.map(|(&did, &imp)| (did, FoundImpl { imp, impl_crate: cnum })),
942+
);
943+
}
944+
}
945+
946+
eiis.into_iter()
947+
.filter_map(|(_, FoundEii { decl, impls })| {
948+
let mut explicit_impls = Vec::new();
949+
let mut default_impl = None;
950+
951+
for (impl_did, FoundImpl { imp, impl_crate }) in impls {
952+
let impl_info = EiiLinkageImplInfo { span: tcx.def_span(impl_did), impl_crate };
953+
if imp.is_default {
954+
default_impl = Some(impl_info);
955+
} else {
956+
explicit_impls.push(impl_info);
957+
}
958+
}
959+
960+
// Link time check is only needed when there may be a default impl in a dylib.
961+
// Other cases emit an error in `rustc_passes` already.
962+
if let Some(default_impl) = default_impl {
963+
Some(EiiLinkageInfo {
964+
name: decl.name.name,
965+
impls: explicit_impls,
966+
default_impl: Some(default_impl),
967+
})
968+
} else {
969+
None
970+
}
971+
})
972+
.collect()
973+
}
974+
975+
fn eii_linkage_needed(dependency_formats: &Dependencies) -> bool {
976+
dependency_formats.values().any(|formats| {
977+
formats
978+
.iter()
979+
.any(|&linkage| matches!(linkage, Linkage::Dynamic | Linkage::IncludedFromDylib))
980+
})
981+
}
982+
917983
impl CrateInfo {
918984
pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
919985
let crate_types = tcx.crate_types().to_vec();
@@ -925,6 +991,12 @@ impl CrateInfo {
925991
crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect();
926992
let local_crate_name = tcx.crate_name(LOCAL_CRATE);
927993
let windows_subsystem = find_attr!(tcx, crate, WindowsSubsystem(kind) => *kind);
994+
let dependency_formats = Arc::clone(tcx.dependency_formats(()));
995+
let eii_linkage = if eii_linkage_needed(&dependency_formats) {
996+
collect_eii_linkage(tcx)
997+
} else {
998+
Vec::new()
999+
};
9281000

9291001
// This list is used when generating the command line to pass through to
9301002
// system linker. The linker expects undefined symbols on the left of the
@@ -969,7 +1041,8 @@ impl CrateInfo {
9691041
crate_name: UnordMap::with_capacity(n_crates),
9701042
used_crates,
9711043
used_crate_source: UnordMap::with_capacity(n_crates),
972-
dependency_formats: Arc::clone(tcx.dependency_formats(())),
1044+
dependency_formats,
1045+
eii_linkage,
9731046
windows_subsystem,
9741047
natvis_debugger_visualizers: Default::default(),
9751048
lint_level_specs: CodegenLintLevelSpecs::from_tcx(tcx),

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use rustc_session::Session;
4040
use rustc_session::config::{CrateType, OutputFilenames, OutputType};
4141
use rustc_session::cstore::{self, CrateSource};
4242
use rustc_session::lint::builtin::LINKER_MESSAGES;
43-
use rustc_span::Symbol;
43+
use rustc_span::{Span, Symbol};
4444

4545
pub mod assert_module_sources;
4646
pub mod back;
@@ -256,6 +256,19 @@ impl SymbolExport {
256256
}
257257
}
258258

259+
#[derive(Clone, Debug, Encodable, Decodable)]
260+
pub struct EiiLinkageImplInfo {
261+
pub span: Span,
262+
pub impl_crate: CrateNum,
263+
}
264+
265+
#[derive(Clone, Debug, Encodable, Decodable)]
266+
pub struct EiiLinkageInfo {
267+
pub name: Symbol,
268+
pub impls: Vec<EiiLinkageImplInfo>,
269+
pub default_impl: Option<EiiLinkageImplInfo>,
270+
}
271+
259272
/// Misc info we load from metadata to persist beyond the tcx.
260273
///
261274
/// Note: though `CrateNum` is only meaningful within the same tcx, information within `CrateInfo`
@@ -282,6 +295,9 @@ pub struct CrateInfo {
282295
pub used_crate_source: UnordMap<CrateNum, Arc<CrateSource>>,
283296
pub used_crates: Vec<CrateNum>,
284297
pub dependency_formats: Arc<Dependencies>,
298+
/// EII implementations used by the link-time duplicate check, so `-Zno-link` can serialize the data needed by a
299+
/// later `-Zlink-only` invocation.
300+
pub eii_linkage: Vec<EiiLinkageInfo>,
285301
pub windows_subsystem: Option<WindowsSubsystemKind>,
286302
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
287303
pub lint_level_specs: CodegenLintLevelSpecs,

compiler/rustc_middle/src/error.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,32 @@ pub(crate) struct IncrementCompilation {
169169
pub run_cmd: String,
170170
pub dep_node: String,
171171
}
172+
173+
#[derive(Diagnostic)]
174+
#[diag("multiple implementations of `#[{$name}]`")]
175+
pub struct DuplicateEiiImpls {
176+
pub name: Symbol,
177+
178+
#[primary_span]
179+
#[label("first implemented here in crate `{$first_crate}`")]
180+
pub first_span: Span,
181+
pub first_crate: Symbol,
182+
183+
#[label("also implemented here in crate `{$second_crate}`")]
184+
pub second_span: Span,
185+
pub second_crate: Symbol,
186+
187+
#[note("in addition to these two, { $num_additional_crates ->
188+
[one] another implementation was found in crate {$additional_crate_names}
189+
*[other] more implementations were also found in the following crates: {$additional_crate_names}
190+
}")]
191+
pub additional_crates: Option<()>,
192+
193+
pub num_additional_crates: usize,
194+
pub additional_crate_names: String,
195+
196+
#[help(
197+
"an \"externally implementable item\" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict"
198+
)]
199+
pub help: (),
200+
}

compiler/rustc_passes/src/diagnostics.rs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,35 +1104,6 @@ pub(crate) struct EiiWithoutImpl {
11041104
pub help: (),
11051105
}
11061106

1107-
#[derive(Diagnostic)]
1108-
#[diag("multiple implementations of `#[{$name}]`")]
1109-
pub(crate) struct DuplicateEiiImpls {
1110-
pub name: Symbol,
1111-
1112-
#[primary_span]
1113-
#[label("first implemented here in crate `{$first_crate}`")]
1114-
pub first_span: Span,
1115-
pub first_crate: Symbol,
1116-
1117-
#[label("also implemented here in crate `{$second_crate}`")]
1118-
pub second_span: Span,
1119-
pub second_crate: Symbol,
1120-
1121-
#[note("in addition to these two, { $num_additional_crates ->
1122-
[one] another implementation was found in crate {$additional_crate_names}
1123-
*[other] more implementations were also found in the following crates: {$additional_crate_names}
1124-
}")]
1125-
pub additional_crates: Option<()>,
1126-
1127-
pub num_additional_crates: usize,
1128-
pub additional_crate_names: String,
1129-
1130-
#[help(
1131-
"an \"externally implementable item\" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict"
1132-
)]
1133-
pub help: (),
1134-
}
1135-
11361107
#[derive(Diagnostic)]
11371108
#[diag("function doesn't have a default implementation")]
11381109
pub(crate) struct FunctionNotHaveDefaultImplementation {

compiler/rustc_passes/src/eii.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use std::iter;
66
use rustc_data_structures::fx::FxIndexMap;
77
use rustc_hir::attrs::{EiiDecl, EiiImpl};
88
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
9+
use rustc_middle::error::DuplicateEiiImpls;
910
use rustc_middle::ty::TyCtxt;
1011
use rustc_session::config::CrateType;
1112

12-
use crate::diagnostics::{DuplicateEiiImpls, EiiWithoutImpl};
13+
use crate::diagnostics::EiiWithoutImpl;
1314

1415
#[derive(Clone, Copy, Debug)]
1516
enum CheckingMode {

tests/ui/eii/default/auxiliary/decl_with_default.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ no-prefer-dynamic
12
#![crate_type = "rlib"]
23
#![feature(extern_item_impls)]
34

tests/ui/eii/default/auxiliary/impl1.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ no-prefer-dynamic
12
//@ aux-build: decl_with_default.rs
23
#![crate_type = "rlib"]
34
#![feature(extern_item_impls)]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![crate_type = "dylib"]
2+
#![feature(extern_item_impls)]
3+
4+
#[eii(eii1)]
5+
fn decl1(x: u64) {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ aux-build: dylib_default.rs
2+
//@ needs-crate-type: dylib
3+
//@ compile-flags: --emit link
4+
//@ ignore-backends: gcc
5+
// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418
6+
//@ ignore-windows
7+
// Regression test for https://github.com/rust-lang/rust/issues/156320.
8+
// A default implementation from an upstream dylib has already been selected and
9+
// must not be overridden by a downstream explicit implementation.
10+
#![feature(extern_item_impls)]
11+
12+
extern crate dylib_default;
13+
14+
#[unsafe(dylib_default::eii1)]
15+
fn other(x: u64) {
16+
//~^ ERROR multiple implementations of `#[eii1]`
17+
println!("1{x}");
18+
}
19+
20+
fn main() {}

0 commit comments

Comments
 (0)