Skip to content

Commit 4ea9e0d

Browse files
committed
Auto merge of rust-lang#134767 - Bryanskiy:dylibs-3, r=<try>
Initial support for dynamically linked crates This PR is an initial implementation of 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 with only "exported" items included, and 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); ``` In this example `bar` function is not a part of the interface as it doesn't have `#[export]` attribute. To emit interface use a new `sdylib` crate type which is basically the same as `dylib`, but it also produces the interface as a second artifact. The current interface name is `lib{crate_name}.rs`. Interface generator was implemented as part of the HIR pretty-printer. In order to filter out unnecessary items, the `PpAnn` trait was used. #### 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 was rejected because it is not a part of the RFC. ([discussion](rust-lang/rfcs#3435 (comment))) Regarding the design with interfaces there are 2 possibilities: manually written or auto-generated. I would personally prefer the auto-generated interface for the following reason: we can put it in the dynamic library instead of metadata, which will make it completely invisible to users. (this was my initial plan, but since the PR is already big enough, I decided to postpone it) But even if we end up with a different design, I believe the interface generator could be a useful tool for testing and experimenting with the entire feature. ### 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. For now, it's assumed that interface and dynamic lib are located in the same directory. `extern dyn crate` annotation serves as a signal for the building of a dynamic dependency. 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 dyn crate simple_lib; fn main() { assert!(simple_lib::foo(), 42); } ``` ``` // Generate interface, build library. rustc +toolchain1 lib.rs -Csymbol-mangling-version=v0 // Build app. Perhaps with a different compiler version. rustc +toolchain2 app.rs -L. -Csymbol-mangling-version=v0 ``` 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 is _mostly_ independent of the compiler internals. The first exception is an impl's disambiguation. 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; // This impl is not part of the interface. 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. However, I have not found the right way to get this order, so I decided to use: 1. The sequential number of an impl during the `intravisit::Visitor` traversal. 2. A new attribute `#[rustc_stable_impl_id]` that outputs this sequential number as a compiler error. If the visitor's implementation is changed, the corresponding test will fail. Then you will need to rewrite the implementation of `stable_order_of_exportable_impls` query to preserve order. P.S. is it possible to compare spans instead? The second exception 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. #### hash of the signature Second part of the symbol name is 128 bit hash containing _relevant_ type information. 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.
2 parents e643f59 + e213390 commit 4ea9e0d

File tree

68 files changed

+1525
-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.

68 files changed

+1525
-117
lines changed

compiler/rustc_ast_lowering/src/item.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1303,7 +1303,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
13031303
// create a fake body so that the entire rest of the compiler doesn't have to deal with
13041304
// this as a special case.
13051305
return self.lower_fn_body(decl, contract, |this| {
1306-
if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) {
1306+
if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic)
1307+
|| this.tcx.is_sdylib_interface_build()
1308+
{
13071309
let span = this.lower_span(span);
13081310
let empty_block = hir::Block {
13091311
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

@@ -947,7 +949,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
947949

948950
let is_intrinsic =
949951
item.attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic);
950-
if body.is_none() && !is_intrinsic {
952+
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
951953
self.dcx().emit_err(errors::FnWithoutBody {
952954
span: item.span,
953955
replace_span: self.ending_semi_or_hi(item.span),
@@ -1434,7 +1436,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14341436
});
14351437
}
14361438
AssocItemKind::Fn(box Fn { body, .. }) => {
1437-
if body.is_none() {
1439+
if body.is_none() && !self.is_sdylib_interface {
14381440
self.dcx().emit_err(errors::AssocFnWithoutBody {
14391441
span: item.span,
14401442
replace_span: self.ending_semi_or_hi(item.span),
@@ -1682,6 +1684,7 @@ pub fn check_crate(
16821684
sess: &Session,
16831685
features: &Features,
16841686
krate: &Crate,
1687+
is_sdylib_interface: bool,
16851688
lints: &mut LintBuffer,
16861689
) -> bool {
16871690
let mut validator = AstValidator {
@@ -1693,6 +1696,7 @@ pub fn check_crate(
16931696
outer_impl_trait_span: None,
16941697
disallow_tilde_const: Some(TildeConstReason::Item),
16951698
extern_mod_safety: None,
1699+
is_sdylib_interface,
16961700
lint_buffer: lints,
16971701
};
16981702
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::{Nonterminal, 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
pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
1315
State::new().nonterminal_to_string(nt)

compiler/rustc_ast_pretty/src/pprust/state.rs

+32-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,39 @@ 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+
250+
s.ann.post(&mut s, AnnNode::Crate(krate));
251+
s.s.eof()
252+
}
253+
254+
pub fn print_crate_as_interface(
255+
krate: &ast::Crate,
256+
edition: Edition,
257+
g: &AttrIdGenerator,
239258
) -> String {
240259
let mut s =
241-
State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
260+
State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: true };
242261

262+
print_crate_inner(&mut s, krate, false, edition, g);
263+
s.s.eof()
264+
}
265+
266+
fn print_crate_inner<'a>(
267+
s: &mut State<'a>,
268+
krate: &ast::Crate,
269+
is_expanded: bool,
270+
edition: Edition,
271+
g: &AttrIdGenerator,
272+
) {
243273
if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) {
244274
// We need to print `#![no_std]` (and its feature gate) so that
245275
// compiling pretty-printed source won't inject libstd again.
@@ -277,8 +307,6 @@ pub fn print_crate<'a>(
277307
s.print_item(item);
278308
}
279309
s.print_remaining_comments();
280-
s.ann.post(&mut s, AnnNode::Crate(krate));
281-
s.s.eof()
282310
}
283311

284312
/// Should two consecutive tokens be printed with a space between them?
@@ -1093,7 +1121,7 @@ impl<'a> PrintState<'a> for State<'a> {
10931121

10941122
impl<'a> State<'a> {
10951123
pub fn new() -> State<'a> {
1096-
State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
1124+
State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: false }
10971125
}
10981126

10991127
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

+10
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ impl<'a> State<'a> {
159159

160160
/// Pretty-prints an item.
161161
pub(crate) fn print_item(&mut self, item: &ast::Item) {
162+
if self.is_sdylib_interface && item.span.is_dummy() {
163+
// Do not print prelude for interface files.
164+
return;
165+
}
162166
self.hardbreak_if_not_bol();
163167
self.maybe_print_comment(item.span.lo());
164168
self.print_outer_attributes(&item.attrs);
@@ -693,6 +697,12 @@ impl<'a> State<'a> {
693697
self.nbsp();
694698
self.print_contract(contract);
695699
}
700+
if self.is_sdylib_interface {
701+
self.word(";");
702+
self.end(); // end inner head-block
703+
self.end(); // end outer head-block
704+
return;
705+
}
696706
if let Some(body) = body {
697707
self.nbsp();
698708
self.print_block_with_attrs(body, attrs);

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
@@ -41,7 +41,8 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool {
4141
| CrateType::Dylib
4242
| CrateType::Staticlib
4343
| CrateType::Cdylib
44-
| CrateType::ProcMacro => true,
44+
| CrateType::ProcMacro
45+
| CrateType::Sdylib => true,
4546
CrateType::Rlib => false,
4647
}
4748
}

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
@@ -1050,9 +1050,10 @@ fn link_natively(
10501050
strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-debug"])
10511051
}
10521052
// Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988)
1053-
(Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => {
1054-
strip_with_external_utility(sess, stripcmd, out_filename, &["-x"])
1055-
}
1053+
(
1054+
Strip::Symbols,
1055+
CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib,
1056+
) => strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]),
10561057
(Strip::Symbols, _) => {
10571058
strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-all"])
10581059
}
@@ -1240,8 +1241,10 @@ fn add_sanitizer_libraries(
12401241
// which should be linked to both executables and dynamic libraries.
12411242
// Everywhere else the runtimes are currently distributed as static
12421243
// libraries which should be linked to executables only.
1243-
if matches!(crate_type, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro)
1244-
&& !(sess.target.is_like_darwin || sess.target.is_like_msvc)
1244+
if matches!(
1245+
crate_type,
1246+
CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib
1247+
) && !(sess.target.is_like_darwin || sess.target.is_like_msvc)
12451248
{
12461249
return;
12471250
}
@@ -1935,6 +1938,7 @@ fn add_late_link_args(
19351938
codegen_results: &CodegenResults,
19361939
) {
19371940
let any_dynamic_crate = crate_type == CrateType::Dylib
1941+
|| crate_type == CrateType::Sdylib
19381942
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
19391943
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
19401944
});

compiler/rustc_codegen_ssa/src/back/linker.rs

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

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(
168168
tcx: TyCtxt<'_>,
169169
_: LocalCrate,
170170
) -> &[(ExportedSymbol<'_>, 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
@@ -1024,7 +1024,7 @@ impl CrateInfo {
10241024
}
10251025

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

compiler/rustc_driver_impl/src/lib.rs

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

359359
passes::write_dep_info(tcx);
360360

361+
passes::write_interface(tcx);
362+
361363
if sess.opts.output_types.contains_key(&OutputType::DepInfo)
362364
&& sess.opts.output_types.len() == 1
363365
{
@@ -818,6 +820,7 @@ fn print_crate_info(
818820
let supported_crate_types = CRATE_TYPES
819821
.iter()
820822
.filter(|(_, crate_type)| !invalid_output_for_target(&sess, *crate_type))
823+
.filter(|(_, crate_type)| *crate_type != CrateType::Sdylib)
821824
.map(|(crate_type_sym, _)| *crate_type_sym)
822825
.collect::<BTreeSet<_>>();
823826
for supported_crate_type in supported_crate_types {

compiler/rustc_feature/src/builtin_attrs.rs

+5
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
506506
naked_functions, experimental!(naked)
507507
),
508508

509+
gated!(
510+
export, Normal, template!(Word), WarnFollowing,
511+
EncodeCrossCrate::No, experimental!(export)
512+
),
513+
509514
// Testing:
510515
gated!(
511516
test_runner, CrateLevel, template!(List: "path"), ErrorFollowing,

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ declare_features! (
209209
(internal, compiler_builtins, "1.13.0", None),
210210
/// Allows writing custom MIR
211211
(internal, custom_mir, "1.65.0", None),
212+
/// Allows using `#[export]` which indicates that an item is exportable.
213+
(internal, export, "CURRENT_RUSTC_VERSION", None),
212214
/// Outputs useful `assert!` messages
213215
(unstable, generic_assert, "1.63.0", None),
214216
/// Allows using the #[rustc_intrinsic] attribute.

compiler/rustc_interface/src/passes.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ use rustc_resolve::Resolver;
3030
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
3131
use rustc_session::cstore::Untracked;
3232
use rustc_session::output::{collect_crate_types, filename_for_input};
33+
use rustc_session::parse::feature_err;
3334
use rustc_session::search_paths::PathKind;
3435
use rustc_session::{Limit, Session};
3536
use rustc_span::{
36-
ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
37+
DUMMY_SP, ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
3738
};
3839
use rustc_target::spec::PanicStrategy;
3940
use rustc_trait_selection::traits;
@@ -236,6 +237,7 @@ fn configure_and_expand(
236237
sess,
237238
features,
238239
&krate,
240+
tcx.is_sdylib_interface_build(),
239241
resolver.lint_buffer(),
240242
)
241243
});
@@ -252,6 +254,9 @@ fn configure_and_expand(
252254
sess.dcx().emit_err(errors::MixedProcMacroCrate);
253255
}
254256
}
257+
if crate_types.contains(&CrateType::Sdylib) && !tcx.features().export() {
258+
feature_err(sess, sym::export, DUMMY_SP, "`sdylib` crate type is unstable").emit();
259+
}
255260

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

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

0 commit comments

Comments
 (0)