Skip to content

Commit 33a33f7

Browse files
committed
Auto merge of rust-lang#134767 - Bryanskiy:dylibs-3, r=petrochenkov
Initial support for dynamically linked crates This PR is an initial implementation of [rust-lang/rfcs#3435](rust-lang/rfcs#3435) proposal. ### component 1: interface generator Interface generator - a tool for generating a stripped version of crate source code. The interface is like a C header, where all function bodies are omitted. For example, initial crate: ```rust #[export] #[repr(C)] pub struct S { pub x: i32 } #[export] pub extern "C" fn foo(x: S) { m1::bar(x); } pub fn bar(x: crate::S) { // some computations } ``` generated interface: ```rust #[export] #[repr(C)] pub struct S { pub x: i32, } #[export] pub extern "C" fn foo(x: S); pub fn bar(x: crate::S); ``` The interface generator was implemented as part of the pretty-printer. Ideally interface should only contain exportable items, but here is the first problem: - pass for determining exportable items relies on privacy information, which is totally available only in HIR - HIR pretty-printer uses pseudo-code(at least for attributes) So, the interface generator was implemented in AST. This has led to the fact that non-exportable items cannot be filtered out, but I don't think this is a major issue at the moment. To emit an interface use a new `sdylib` crate type which is basically the same as `dylib`, but it doesn't contain metadata, and also produces the interface as a second artifact. The current interface name is `lib{crate_name}.rs`. #### Why was it decided to use a design with an auto-generated interface? One of the main objectives of this proposal is to allow building the library and the application with different compiler versions. This requires either a metadata format compatible across rustc versions or some form of a source code. The option with a stable metadata format has not been investigated in detail, but it is not part of RFC either. Here is the the related discussion: rust-lang/rfcs#3435 (comment) Original proposal suggests using the source code for the dynamic library and all its dependencies. Metadata is obtained from `cargo check`. I decided to use interface files since it is more or less compatible with the original proposal, but also allows users to hide the source code. ##### Regarding the design with interfaces in Rust, files generally do not have a special meaning, unlike C++. A translation unit i.e. a crate is not a single file, it consists of modules. Modules, in turn, can be declared either in one file or divided into several. That's why the "interface file" isn't a very coherent concept in Rust. I would like to avoid adding an additional level of complexity for users until it is proven necessary. Therefore, the initial plan was to make the interfaces completely invisible to users i. e. make them auto-generated. I also planned to put them in the dylib, but this has not been done yet. (since the PR is already big enough, I decided to postpone it) There is one concern, though, which has not yet been investigated(rust-lang#134767 (comment)): > Compiling the interface as pretty-printed source code doesn't use correct macro hygiene (mostly relevant to macros 2.0, stable macros do not affect item hygiene). I don't have much hope for encoding hygiene data in any stable way, we should rather support a way for the interface file to be provided manually, instead of being auto-generated, if there are any non-trivial requirements. ### component 2: crate loader When building dynamic dependencies, the crate loader searches for the interface in the file system, builds the interface without codegen and loads it's metadata. Routing rules for interface files are almost the same as for `rlibs` and `dylibs`. Firstly, the compiler checks `extern` options and then tries to deduce the path himself. Here are the code and commands that corresponds to the compilation process: ```rust // simple-lib.rs #![crate_type = "sdylib"] #[extern] pub extern "C" fn foo() -> i32 { 42 } ``` ```rust // app.rs extern crate simple_lib; fn main() { assert!(simple_lib::foo(), 42); } ``` ``` // Generate interface, build library. rustc +toolchain1 lib.rs // Build app. Perhaps with a different compiler version. rustc +toolchain2 app.rs -L. ``` P.S. The interface name/format and rules for file system routing can be changed further. ### component 3: exportable items collector Query for collecting exportable items. Which items are exportable is defined [here](https://github.com/m-ou-se/rfcs/blob/export/text/0000-export.md#the-export-attribute) . ### component 4: "stable" mangling scheme The mangling scheme proposed in the RFC consists of two parts: a mangled item path and a hash of the signature. #### mangled item path For the first part of the symbol it has been decided to reuse the `v0` mangling scheme as it much less dependent on compiler internals compared to the `legacy` scheme. The exception is disambiguators (https://doc.rust-lang.org/rustc/symbol-mangling/v0.html#disambiguator): For example, during symbol mangling rustc uses a special index to distinguish between two impls of the same type in the same module(See `DisambiguatedDefPathData`). The calculation of this index may depend on private items, but private items should not affect the ABI. Example: ```rust #[export] #[repr(C)] pub struct S<T>(pub T); struct S1; pub struct S2; impl S<S1> { extern "C" fn foo() -> i32 { 1 } } #[export] impl S<S2> { // Different symbol names can be generated for this item // when compiling the interface and source code. pub extern "C" fn foo() -> i32 { 2 } } ``` In order to make disambiguation independent of the compiler version we can assign an id to each impl according to their relative order in the source code. The second example is `StableCrateId` which is used to disambiguate different crates. `StableCrateId` consists of crate name, `-Cmetadata` arguments and compiler version. At the moment, I have decided to keep only the crate name, but a more consistent approach to crate disambiguation could be added in the future. Actually, there are more cases where such disambiguation can be used. For instance, when mangling internal rustc symbols, but it also hasn't been investigated in detail yet. #### hash of the signature Exportable functions from stable dylibs can be called from safe code. In order to provide type safety, 128 bit hash with relevant type information is appended to the symbol ([description from RFC](https://github.com/m-ou-se/rfcs/blob/export/text/0000-export.md#name-mangling-and-safety)). For now, it includes: - hash of the type name for primitive types - for ADT types with public fields the implementation follows [this](https://github.com/m-ou-se/rfcs/blob/export/text/0000-export.md#types-with-public-fields) rules `#[export(unsafe_stable_abi = "hash")]` syntax for ADT types with private fields is not yet implemented. Type safety is a subtle thing here. I used the approach from RFC, but there is the ongoing research project about it. [https://rust-lang.github.io/rust-project-goals/2025h1/safe-linking.html](https://rust-lang.github.io/rust-project-goals/2025h1/safe-linking.html) ### Unresolved questions Interfaces: 1. Move the interface generator to HIR and add an exportable items filter. 2. Compatibility of auto-generated interfaces and macro hygiene. 3. There is an open issue with interface files compilation: rust-lang#134767 (comment) 4. Put an interface into a dylib. Mangling scheme: 1. Which information is required to ensure type safety and how should it be encoded? ([https://rust-lang.github.io/rust-project-goals/2025h1/safe-linking.html](https://rust-lang.github.io/rust-project-goals/2025h1/safe-linking.html)) 2. Determine all other possible cases, where path disambiguation is used. Make it compiler independent. We also need a semi-stable API to represent types. For example, the order of fields in the `VariantDef` must be stable. Or a semi-stable representation for AST, which ensures that the order of the items in the code is preserved. There are some others, mentioned in the proposal.
2 parents d6a325d + 9bd8618 commit 33a33f7

File tree

70 files changed

+1534
-117
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1534
-117
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4031,6 +4031,7 @@ dependencies = [
40314031
"rustc_session",
40324032
"rustc_span",
40334033
"rustc_target",
4034+
"tempfile",
40344035
"tracing",
40354036
]
40364037

compiler/rustc_ast_lowering/src/item.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
13101310
// create a fake body so that the entire rest of the compiler doesn't have to deal with
13111311
// this as a special case.
13121312
return self.lower_fn_body(decl, contract, |this| {
1313-
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)) {
1313+
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic))
1314+
|| this.tcx.is_sdylib_interface_build()
1315+
{
13141316
let span = this.lower_span(span);
13151317
let empty_block = hir::Block {
13161318
hir_id: this.next_id(),

compiler/rustc_ast_passes/src/ast_validation.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ struct AstValidator<'a> {
8282
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
8383
extern_mod_safety: Option<Safety>,
8484

85+
is_sdylib_interface: bool,
86+
8587
lint_buffer: &'a mut LintBuffer,
8688
}
8789

@@ -948,7 +950,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
948950
self.check_defaultness(item.span, *defaultness);
949951

950952
let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
951-
if body.is_none() && !is_intrinsic {
953+
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
952954
self.dcx().emit_err(errors::FnWithoutBody {
953955
span: item.span,
954956
replace_span: self.ending_semi_or_hi(item.span),
@@ -1435,7 +1437,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14351437
});
14361438
}
14371439
AssocItemKind::Fn(box Fn { body, .. }) => {
1438-
if body.is_none() {
1440+
if body.is_none() && !self.is_sdylib_interface {
14391441
self.dcx().emit_err(errors::AssocFnWithoutBody {
14401442
span: item.span,
14411443
replace_span: self.ending_semi_or_hi(item.span),
@@ -1683,6 +1685,7 @@ pub fn check_crate(
16831685
sess: &Session,
16841686
features: &Features,
16851687
krate: &Crate,
1688+
is_sdylib_interface: bool,
16861689
lints: &mut LintBuffer,
16871690
) -> bool {
16881691
let mut validator = AstValidator {
@@ -1694,6 +1697,7 @@ pub fn check_crate(
16941697
outer_impl_trait_span: None,
16951698
disallow_tilde_const: Some(TildeConstReason::Item),
16961699
extern_mod_safety: None,
1700+
is_sdylib_interface,
16971701
lint_buffer: lints,
16981702
};
16991703
visit::walk_crate(&mut validator, krate);

compiler/rustc_ast_pretty/src/pprust/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use std::borrow::Cow;
77
use rustc_ast as ast;
88
use rustc_ast::token::{Token, TokenKind};
99
use rustc_ast::tokenstream::{TokenStream, TokenTree};
10-
pub use state::{AnnNode, Comments, PpAnn, PrintState, State, print_crate};
10+
pub use state::{
11+
AnnNode, Comments, PpAnn, PrintState, State, print_crate, print_crate_as_interface,
12+
};
1113

1214
/// Print the token kind precisely, without converting `$crate` into its respective crate name.
1315
pub fn token_kind_to_string(tok: &TokenKind) -> Cow<'static, str> {

compiler/rustc_ast_pretty/src/pprust/state.rs

+31-4
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ pub struct State<'a> {
221221
pub s: pp::Printer,
222222
comments: Option<Comments<'a>>,
223223
ann: &'a (dyn PpAnn + 'a),
224+
is_sdylib_interface: bool,
224225
}
225226

226227
const INDENT_UNIT: isize = 4;
@@ -236,10 +237,37 @@ pub fn print_crate<'a>(
236237
is_expanded: bool,
237238
edition: Edition,
238239
g: &AttrIdGenerator,
240+
) -> String {
241+
let mut s = State {
242+
s: pp::Printer::new(),
243+
comments: Some(Comments::new(sm, filename, input)),
244+
ann,
245+
is_sdylib_interface: false,
246+
};
247+
248+
print_crate_inner(&mut s, krate, is_expanded, edition, g);
249+
s.s.eof()
250+
}
251+
252+
pub fn print_crate_as_interface(
253+
krate: &ast::Crate,
254+
edition: Edition,
255+
g: &AttrIdGenerator,
239256
) -> String {
240257
let mut s =
241-
State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
258+
State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: true };
242259

260+
print_crate_inner(&mut s, krate, false, edition, g);
261+
s.s.eof()
262+
}
263+
264+
fn print_crate_inner<'a>(
265+
s: &mut State<'a>,
266+
krate: &ast::Crate,
267+
is_expanded: bool,
268+
edition: Edition,
269+
g: &AttrIdGenerator,
270+
) {
243271
// We need to print shebang before anything else
244272
// otherwise the resulting code will not compile
245273
// and shebang will be useless.
@@ -282,8 +310,7 @@ pub fn print_crate<'a>(
282310
s.print_item(item);
283311
}
284312
s.print_remaining_comments();
285-
s.ann.post(&mut s, AnnNode::Crate(krate));
286-
s.s.eof()
313+
s.ann.post(s, AnnNode::Crate(krate));
287314
}
288315

289316
/// Should two consecutive tokens be printed with a space between them?
@@ -1111,7 +1138,7 @@ impl<'a> PrintState<'a> for State<'a> {
11111138

11121139
impl<'a> State<'a> {
11131140
pub fn new() -> State<'a> {
1114-
State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
1141+
State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: false }
11151142
}
11161143

11171144
fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)

compiler/rustc_ast_pretty/src/pprust/state/item.rs

+11
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ impl<'a> State<'a> {
160160

161161
/// Pretty-prints an item.
162162
pub(crate) fn print_item(&mut self, item: &ast::Item) {
163+
if self.is_sdylib_interface && item.span.is_dummy() {
164+
// Do not print prelude for interface files.
165+
return;
166+
}
163167
self.hardbreak_if_not_bol();
164168
self.maybe_print_comment(item.span.lo());
165169
self.print_outer_attributes(&item.attrs);
@@ -682,6 +686,13 @@ impl<'a> State<'a> {
682686
self.print_contract(contract);
683687
}
684688
if let Some((body, (cb, ib))) = body_cb_ib {
689+
if self.is_sdylib_interface {
690+
self.word(";");
691+
self.end(ib); // end inner head-block
692+
self.end(cb); // end outer head-block
693+
return;
694+
}
695+
685696
self.nbsp();
686697
self.print_block_with_attrs(body, attrs, cb, ib);
687698
} else {

compiler/rustc_codegen_gcc/src/back/lto.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level};
4444

4545
pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
4646
match crate_type {
47-
CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true,
47+
CrateType::Executable
48+
| CrateType::Dylib
49+
| CrateType::Staticlib
50+
| CrateType::Cdylib
51+
| CrateType::Sdylib => true,
4852
CrateType::Rlib | CrateType::ProcMacro => false,
4953
}
5054
}

compiler/rustc_codegen_llvm/src/back/lto.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool {
4242
| CrateType::Dylib
4343
| CrateType::Staticlib
4444
| CrateType::Cdylib
45-
| CrateType::ProcMacro => true,
45+
| CrateType::ProcMacro
46+
| CrateType::Sdylib => true,
4647
CrateType::Rlib => false,
4748
}
4849
}

compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ pub(crate) fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
9595
// in the `.debug_gdb_scripts` section. For that reason, we make sure that the
9696
// section is only emitted for leaf crates.
9797
let embed_visualizers = cx.tcx.crate_types().iter().any(|&crate_type| match crate_type {
98-
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => {
98+
CrateType::Executable
99+
| CrateType::Dylib
100+
| CrateType::Cdylib
101+
| CrateType::Staticlib
102+
| CrateType::Sdylib => {
99103
// These are crate types for which we will embed pretty printers since they
100104
// are treated as leaf crates.
101105
true

compiler/rustc_codegen_ssa/src/back/link.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1053,9 +1053,10 @@ fn link_natively(
10531053
strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-debug"])
10541054
}
10551055
// Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988)
1056-
(Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => {
1057-
strip_with_external_utility(sess, stripcmd, out_filename, &["-x"])
1058-
}
1056+
(
1057+
Strip::Symbols,
1058+
CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib,
1059+
) => strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]),
10591060
(Strip::Symbols, _) => {
10601061
strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-all"])
10611062
}
@@ -1243,8 +1244,10 @@ fn add_sanitizer_libraries(
12431244
// which should be linked to both executables and dynamic libraries.
12441245
// Everywhere else the runtimes are currently distributed as static
12451246
// libraries which should be linked to executables only.
1246-
if matches!(crate_type, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro)
1247-
&& !(sess.target.is_like_darwin || sess.target.is_like_msvc)
1247+
if matches!(
1248+
crate_type,
1249+
CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib
1250+
) && !(sess.target.is_like_darwin || sess.target.is_like_msvc)
12481251
{
12491252
return;
12501253
}
@@ -1938,6 +1941,7 @@ fn add_late_link_args(
19381941
codegen_results: &CodegenResults,
19391942
) {
19401943
let any_dynamic_crate = crate_type == CrateType::Dylib
1944+
|| crate_type == CrateType::Sdylib
19411945
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
19421946
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
19431947
});

compiler/rustc_codegen_ssa/src/back/linker.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1817,7 +1817,7 @@ pub(crate) fn linked_symbols(
18171817
crate_type: CrateType,
18181818
) -> Vec<(String, SymbolExportKind)> {
18191819
match crate_type {
1820-
CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => (),
1820+
CrateType::Executable | CrateType::Cdylib | CrateType::Dylib | CrateType::Sdylib => (),
18211821
CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => {
18221822
return Vec::new();
18231823
}

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel {
2929
CrateType::Executable | CrateType::Staticlib | CrateType::ProcMacro | CrateType::Cdylib => {
3030
SymbolExportLevel::C
3131
}
32-
CrateType::Rlib | CrateType::Dylib => SymbolExportLevel::Rust,
32+
CrateType::Rlib | CrateType::Dylib | CrateType::Sdylib => SymbolExportLevel::Rust,
3333
}
3434
}
3535

@@ -45,7 +45,7 @@ pub fn crates_export_threshold(crate_types: &[CrateType]) -> SymbolExportLevel {
4545
}
4646

4747
fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<SymbolExportInfo> {
48-
if !tcx.sess.opts.output_types.should_codegen() {
48+
if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() {
4949
return Default::default();
5050
}
5151

@@ -168,7 +168,7 @@ fn exported_symbols_provider_local<'tcx>(
168168
tcx: TyCtxt<'tcx>,
169169
_: LocalCrate,
170170
) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
171-
if !tcx.sess.opts.output_types.should_codegen() {
171+
if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() {
172172
return &[];
173173
}
174174

compiler/rustc_codegen_ssa/src/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ impl CrateInfo {
10921092
}
10931093

10941094
let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type {
1095-
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => {
1095+
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => {
10961096
// These are crate types for which we invoke the linker and can embed
10971097
// NatVis visualizers.
10981098
true

compiler/rustc_driver_impl/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ use rustc_metadata::locator;
5454
use rustc_middle::ty::TyCtxt;
5555
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
5656
use rustc_session::config::{
57-
CG_OPTIONS, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, UnstableOptions,
58-
Z_OPTIONS, nightly_options, parse_target_triple,
57+
CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType,
58+
UnstableOptions, Z_OPTIONS, nightly_options, parse_target_triple,
5959
};
6060
use rustc_session::getopts::{self, Matches};
6161
use rustc_session::lint::{Lint, LintId};
@@ -352,6 +352,8 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
352352

353353
passes::write_dep_info(tcx);
354354

355+
passes::write_interface(tcx);
356+
355357
if sess.opts.output_types.contains_key(&OutputType::DepInfo)
356358
&& sess.opts.output_types.len() == 1
357359
{
@@ -816,6 +818,7 @@ fn print_crate_info(
816818
let supported_crate_types = CRATE_TYPES
817819
.iter()
818820
.filter(|(_, crate_type)| !invalid_output_for_target(&sess, *crate_type))
821+
.filter(|(_, crate_type)| *crate_type != CrateType::Sdylib)
819822
.map(|(crate_type_sym, _)| *crate_type_sym)
820823
.collect::<BTreeSet<_>>();
821824
for supported_crate_type in supported_crate_types {

compiler/rustc_feature/src/builtin_attrs.rs

+6
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
536536
// Unstable attributes:
537537
// ==========================================================================
538538

539+
// Linking:
540+
gated!(
541+
export, Normal, template!(Word), WarnFollowing,
542+
EncodeCrossCrate::No, experimental!(export)
543+
),
544+
539545
// Testing:
540546
gated!(
541547
test_runner, CrateLevel, template!(List: "path"), ErrorFollowing,

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ declare_features! (
483483
(unstable, explicit_extern_abis, "CURRENT_RUSTC_VERSION", Some(134986)),
484484
/// Allows explicit tail calls via `become` expression.
485485
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
486+
/// Allows using `#[export]` which indicates that an item is exportable.
487+
(incomplete, export, "CURRENT_RUSTC_VERSION", Some(139939)),
486488
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
487489
/// for functions with varargs.
488490
(unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),

compiler/rustc_interface/src/passes.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ use rustc_resolve::Resolver;
3131
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
3232
use rustc_session::cstore::Untracked;
3333
use rustc_session::output::{collect_crate_types, filename_for_input};
34+
use rustc_session::parse::feature_err;
3435
use rustc_session::search_paths::PathKind;
3536
use rustc_session::{Limit, Session};
3637
use rustc_span::{
37-
ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
38+
DUMMY_SP, ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
3839
};
3940
use rustc_target::spec::PanicStrategy;
4041
use rustc_trait_selection::traits;
@@ -237,6 +238,7 @@ fn configure_and_expand(
237238
sess,
238239
features,
239240
&krate,
241+
tcx.is_sdylib_interface_build(),
240242
resolver.lint_buffer(),
241243
)
242244
});
@@ -253,6 +255,9 @@ fn configure_and_expand(
253255
sess.dcx().emit_err(errors::MixedProcMacroCrate);
254256
}
255257
}
258+
if crate_types.contains(&CrateType::Sdylib) && !tcx.features().export() {
259+
feature_err(sess, sym::export, DUMMY_SP, "`sdylib` crate type is unstable").emit();
260+
}
256261

257262
if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort {
258263
sess.dcx().emit_warn(errors::ProcMacroCratePanicAbort);
@@ -742,6 +747,25 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) {
742747
}
743748
}
744749

750+
pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) {
751+
if !tcx.crate_types().contains(&rustc_session::config::CrateType::Sdylib) {
752+
return;
753+
}
754+
let _timer = tcx.sess.timer("write_interface");
755+
let (_, krate) = &*tcx.resolver_for_lowering().borrow();
756+
757+
let krate = rustc_ast_pretty::pprust::print_crate_as_interface(
758+
krate,
759+
tcx.sess.psess.edition,
760+
&tcx.sess.psess.attr_id_generator,
761+
);
762+
let export_output = tcx.output_filenames(()).interface_path();
763+
let mut file = fs::File::create_buffered(export_output).unwrap();
764+
if let Err(err) = write!(file, "{}", krate) {
765+
tcx.dcx().fatal(format!("error writing interface file: {}", err));
766+
}
767+
}
768+
745769
pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
746770
let providers = &mut Providers::default();
747771
providers.analysis = analysis;
@@ -930,6 +954,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
930954
CStore::from_tcx(tcx).report_unused_deps(tcx);
931955
},
932956
{
957+
tcx.ensure_ok().exportable_items(LOCAL_CRATE);
958+
tcx.ensure_ok().stable_order_of_exportable_impls(LOCAL_CRATE);
933959
tcx.par_hir_for_each_module(|module| {
934960
tcx.ensure_ok().check_mod_loops(module);
935961
tcx.ensure_ok().check_mod_attrs(module);

0 commit comments

Comments
 (0)