Skip to content

Commit dfc410a

Browse files
committed
Reject EII overrides of dylib defaults
1 parent 0490dd9 commit dfc410a

4 files changed

Lines changed: 77 additions & 10 deletions

File tree

compiler/rustc_passes/src/eii.rs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ 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::middle::dependency_format::Linkage;
910
use rustc_middle::ty::TyCtxt;
1011
use rustc_session::config::CrateType;
1112

@@ -26,6 +27,23 @@ fn get_checking_mode(tcx: TyCtxt<'_>) -> CheckingMode {
2627
}
2728
}
2829

30+
fn linked_in_dylib(tcx: TyCtxt<'_>, cnum: CrateNum) -> bool {
31+
if cnum == LOCAL_CRATE {
32+
return false;
33+
}
34+
35+
let source = tcx.used_crate_source(cnum);
36+
// If the only code artifact available is a dylib, any default impl inside it
37+
// has already been materialized and cannot be overridden downstream.
38+
let only_available_as_dylib =
39+
source.rlib.is_none() && (source.dylib.is_some() || source.sdylib_interface.is_some());
40+
41+
only_available_as_dylib
42+
|| tcx.dependency_formats(()).iter().any(|(_, formats)| {
43+
matches!(formats.get(cnum), Some(Linkage::Dynamic | Linkage::IncludedFromDylib))
44+
})
45+
}
46+
2947
/// Checks for a given crate, what EIIs need to be generated in it.
3048
/// This is usually a small subset of all EIIs.
3149
///
@@ -93,21 +111,31 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, ():
93111
}
94112
}
95113

96-
// more than one explicit implementation (across all crates)
97-
// is instantly an error.
98-
if explicit_impls.len() > 1 {
114+
let mut duplicate_impls = explicit_impls.clone();
115+
if !duplicate_impls.is_empty() {
116+
duplicate_impls.extend(
117+
default_impls
118+
.iter()
119+
.copied()
120+
.filter(|(_, impl_crate)| linked_in_dylib(tcx, *impl_crate)),
121+
);
122+
}
123+
124+
// more than one explicit implementation (across all crates), or an explicit
125+
// implementation overriding a default implementation from a dylib, is instantly an error.
126+
if duplicate_impls.len() > 1 {
99127
tcx.dcx().emit_err(DuplicateEiiImpls {
100128
name: decl.name.name,
101-
first_span: tcx.def_span(explicit_impls[0].0),
102-
first_crate: tcx.crate_name(explicit_impls[0].1),
103-
second_span: tcx.def_span(explicit_impls[1].0),
104-
second_crate: tcx.crate_name(explicit_impls[1].1),
129+
first_span: tcx.def_span(duplicate_impls[0].0),
130+
first_crate: tcx.crate_name(duplicate_impls[0].1),
131+
second_span: tcx.def_span(duplicate_impls[1].0),
132+
second_crate: tcx.crate_name(duplicate_impls[1].1),
105133

106134
help: (),
107135

108-
additional_crates: (explicit_impls.len() > 2).then_some(()),
109-
num_additional_crates: explicit_impls.len() - 2,
110-
additional_crate_names: explicit_impls[2..]
136+
additional_crates: (duplicate_impls.len() > 2).then_some(()),
137+
num_additional_crates: duplicate_impls.len() - 2,
138+
additional_crate_names: duplicate_impls[2..]
111139
.iter()
112140
.map(|i| format!("`{}`", tcx.crate_name(i.1)))
113141
.collect::<Vec<_>>()
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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ aux-build: dylib_default.rs
2+
//@ needs-crate-type: dylib
3+
//@ ignore-backends: gcc
4+
// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418
5+
//@ ignore-windows
6+
// Regression test for https://github.com/rust-lang/rust/issues/156320.
7+
// A default implementation from an upstream dylib has already been selected and
8+
// must not be overridden by a downstream explicit implementation.
9+
#![feature(extern_item_impls)]
10+
11+
extern crate dylib_default;
12+
13+
#[unsafe(dylib_default::eii1)]
14+
fn other(x: u64) {
15+
//~^ ERROR multiple implementations of `#[eii1]`
16+
println!("1{x}");
17+
}
18+
19+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: multiple implementations of `#[eii1]`
2+
--> $DIR/dylib_default_duplicate.rs:14:1
3+
|
4+
LL | fn other(x: u64) {
5+
| ^^^^^^^^^^^^^^^^ first implemented here in crate `dylib_default_duplicate`
6+
|
7+
::: $DIR/auxiliary/dylib_default.rs:5:1
8+
|
9+
LL | fn decl1(x: u64) {}
10+
| ---------------- also implemented here in crate `dylib_default`
11+
|
12+
= help: 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
13+
14+
error: aborting due to 1 previous error
15+

0 commit comments

Comments
 (0)