Skip to content

Initial support for dynamically linked crates #134767

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4031,6 +4031,7 @@ dependencies = [
"rustc_session",
"rustc_span",
"rustc_target",
"tempfile",
"tracing",
]

Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1310,7 +1310,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
// create a fake body so that the entire rest of the compiler doesn't have to deal with
// this as a special case.
return self.lower_fn_body(decl, contract, |this| {
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)) {
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic))
|| this.tcx.is_sdylib_interface_build()
{
let span = this.lower_span(span);
let empty_block = hir::Block {
hir_id: this.next_id(),
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ struct AstValidator<'a> {

lint_node_id: NodeId,

is_sdylib_interface: bool,

lint_buffer: &'a mut LintBuffer,
}

Expand Down Expand Up @@ -952,7 +954,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_defaultness(item.span, *defaultness);

let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
if body.is_none() && !is_intrinsic {
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
self.dcx().emit_err(errors::FnWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
Expand Down Expand Up @@ -1441,7 +1443,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
});
}
AssocItemKind::Fn(box Fn { body, .. }) => {
if body.is_none() {
if body.is_none() && !self.is_sdylib_interface {
self.dcx().emit_err(errors::AssocFnWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
Expand Down Expand Up @@ -1689,6 +1691,7 @@ pub fn check_crate(
sess: &Session,
features: &Features,
krate: &Crate,
is_sdylib_interface: bool,
lints: &mut LintBuffer,
) -> bool {
let mut validator = AstValidator {
Expand All @@ -1701,6 +1704,7 @@ pub fn check_crate(
disallow_tilde_const: Some(TildeConstReason::Item),
extern_mod_safety: None,
lint_node_id: CRATE_NODE_ID,
is_sdylib_interface,
lint_buffer: lints,
};
visit::walk_crate(&mut validator, krate);
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_ast_pretty/src/pprust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use std::borrow::Cow;
use rustc_ast as ast;
use rustc_ast::token::{Token, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
pub use state::{AnnNode, Comments, PpAnn, PrintState, State, print_crate};
pub use state::{
AnnNode, Comments, PpAnn, PrintState, State, print_crate, print_crate_as_interface,
};

/// Print the token kind precisely, without converting `$crate` into its respective crate name.
pub fn token_kind_to_string(tok: &TokenKind) -> Cow<'static, str> {
Expand Down
35 changes: 31 additions & 4 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ pub struct State<'a> {
pub s: pp::Printer,
comments: Option<Comments<'a>>,
ann: &'a (dyn PpAnn + 'a),
is_sdylib_interface: bool,
}

const INDENT_UNIT: isize = 4;
Expand All @@ -236,10 +237,37 @@ pub fn print_crate<'a>(
is_expanded: bool,
edition: Edition,
g: &AttrIdGenerator,
) -> String {
let mut s = State {
s: pp::Printer::new(),
comments: Some(Comments::new(sm, filename, input)),
ann,
is_sdylib_interface: false,
};

print_crate_inner(&mut s, krate, is_expanded, edition, g);
s.s.eof()
}

pub fn print_crate_as_interface(
krate: &ast::Crate,
edition: Edition,
g: &AttrIdGenerator,
) -> String {
let mut s =
State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: true };

print_crate_inner(&mut s, krate, false, edition, g);
s.s.eof()
}

fn print_crate_inner<'a>(
s: &mut State<'a>,
krate: &ast::Crate,
is_expanded: bool,
edition: Edition,
g: &AttrIdGenerator,
) {
// We need to print shebang before anything else
// otherwise the resulting code will not compile
// and shebang will be useless.
Expand Down Expand Up @@ -282,8 +310,7 @@ pub fn print_crate<'a>(
s.print_item(item);
}
s.print_remaining_comments();
s.ann.post(&mut s, AnnNode::Crate(krate));
s.s.eof()
s.ann.post(s, AnnNode::Crate(krate));
}

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

impl<'a> State<'a> {
pub fn new() -> State<'a> {
State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: false }
}

fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ impl<'a> State<'a> {

/// Pretty-prints an item.
pub(crate) fn print_item(&mut self, item: &ast::Item) {
if self.is_sdylib_interface && item.span.is_dummy() {
// Do not print prelude for interface files.
return;
}
self.hardbreak_if_not_bol();
self.maybe_print_comment(item.span.lo());
self.print_outer_attributes(&item.attrs);
Expand Down Expand Up @@ -682,6 +686,13 @@ impl<'a> State<'a> {
self.print_contract(contract);
}
if let Some((body, (cb, ib))) = body_cb_ib {
if self.is_sdylib_interface {
self.word(";");
self.end(ib); // end inner head-block
self.end(cb); // end outer head-block
return;
}

self.nbsp();
self.print_block_with_attrs(body, attrs, cb, ib);
} else {
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_gcc/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level};

pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
match crate_type {
CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true,
CrateType::Executable
| CrateType::Dylib
| CrateType::Staticlib
| CrateType::Cdylib
| CrateType::Sdylib => true,
CrateType::Rlib | CrateType::ProcMacro => false,
}
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_llvm/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool {
| CrateType::Dylib
| CrateType::Staticlib
| CrateType::Cdylib
| CrateType::ProcMacro => true,
| CrateType::ProcMacro
| CrateType::Sdylib => true,
CrateType::Rlib => false,
}
}
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ pub(crate) fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
// in the `.debug_gdb_scripts` section. For that reason, we make sure that the
// section is only emitted for leaf crates.
let embed_visualizers = cx.tcx.crate_types().iter().any(|&crate_type| match crate_type {
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => {
CrateType::Executable
| CrateType::Dylib
| CrateType::Cdylib
| CrateType::Staticlib
| CrateType::Sdylib => {
// These are crate types for which we will embed pretty printers since they
// are treated as leaf crates.
true
Expand Down
14 changes: 9 additions & 5 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,9 +1053,10 @@ fn link_natively(
strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-debug"])
}
// Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988)
(Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => {
strip_with_external_utility(sess, stripcmd, out_filename, &["-x"])
}
(
Strip::Symbols,
CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib,
) => strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]),
(Strip::Symbols, _) => {
strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-all"])
}
Expand Down Expand Up @@ -1243,8 +1244,10 @@ fn add_sanitizer_libraries(
// which should be linked to both executables and dynamic libraries.
// Everywhere else the runtimes are currently distributed as static
// libraries which should be linked to executables only.
if matches!(crate_type, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro)
&& !(sess.target.is_like_darwin || sess.target.is_like_msvc)
if matches!(
crate_type,
CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib
) && !(sess.target.is_like_darwin || sess.target.is_like_msvc)
{
return;
}
Expand Down Expand Up @@ -1938,6 +1941,7 @@ fn add_late_link_args(
codegen_results: &CodegenResults,
) {
let any_dynamic_crate = crate_type == CrateType::Dylib
|| crate_type == CrateType::Sdylib
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1817,7 +1817,7 @@ pub(crate) fn linked_symbols(
crate_type: CrateType,
) -> Vec<(String, SymbolExportKind)> {
match crate_type {
CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => (),
CrateType::Executable | CrateType::Cdylib | CrateType::Dylib | CrateType::Sdylib => (),
CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => {
return Vec::new();
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel {
CrateType::Executable | CrateType::Staticlib | CrateType::ProcMacro | CrateType::Cdylib => {
SymbolExportLevel::C
}
CrateType::Rlib | CrateType::Dylib => SymbolExportLevel::Rust,
CrateType::Rlib | CrateType::Dylib | CrateType::Sdylib => SymbolExportLevel::Rust,
}
}

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

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

Expand Down Expand Up @@ -168,7 +168,7 @@ fn exported_symbols_provider_local<'tcx>(
tcx: TyCtxt<'tcx>,
_: LocalCrate,
) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
if !tcx.sess.opts.output_types.should_codegen() {
if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() {
return &[];
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ impl CrateInfo {
}

let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type {
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => {
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => {
// These are crate types for which we invoke the linker and can embed
// NatVis visualizers.
true
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ use rustc_metadata::locator;
use rustc_middle::ty::TyCtxt;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_session::config::{
CG_OPTIONS, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, UnstableOptions,
Z_OPTIONS, nightly_options, parse_target_triple,
CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType,
UnstableOptions, Z_OPTIONS, nightly_options, parse_target_triple,
};
use rustc_session::getopts::{self, Matches};
use rustc_session::lint::{Lint, LintId};
Expand Down Expand Up @@ -352,6 +352,8 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))

passes::write_dep_info(tcx);

passes::write_interface(tcx);

if sess.opts.output_types.contains_key(&OutputType::DepInfo)
&& sess.opts.output_types.len() == 1
{
Expand Down Expand Up @@ -816,6 +818,7 @@ fn print_crate_info(
let supported_crate_types = CRATE_TYPES
.iter()
.filter(|(_, crate_type)| !invalid_output_for_target(&sess, *crate_type))
.filter(|(_, crate_type)| *crate_type != CrateType::Sdylib)
.map(|(crate_type_sym, _)| *crate_type_sym)
.collect::<BTreeSet<_>>();
for supported_crate_type in supported_crate_types {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Unstable attributes:
// ==========================================================================

// Linking:
gated!(
export_stable, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::No, experimental!(export_stable)
),

// Testing:
gated!(
test_runner, CrateLevel, template!(List: "path"), ErrorFollowing,
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 @@ -485,6 +485,8 @@ declare_features! (
(unstable, explicit_extern_abis, "CURRENT_RUSTC_VERSION", Some(134986)),
/// Allows explicit tail calls via `become` expression.
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
/// Allows using `#[export_stable]` which indicates that an item is exportable.
(incomplete, export_stable, "CURRENT_RUSTC_VERSION", Some(139939)),
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
/// for functions with varargs.
(unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),
Expand Down
28 changes: 27 additions & 1 deletion compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ use rustc_resolve::Resolver;
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
use rustc_session::cstore::Untracked;
use rustc_session::output::{collect_crate_types, filename_for_input};
use rustc_session::parse::feature_err;
use rustc_session::search_paths::PathKind;
use rustc_session::{Limit, Session};
use rustc_span::{
ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
DUMMY_SP, ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
};
use rustc_target::spec::PanicStrategy;
use rustc_trait_selection::traits;
Expand Down Expand Up @@ -237,6 +238,7 @@ fn configure_and_expand(
sess,
features,
&krate,
tcx.is_sdylib_interface_build(),
resolver.lint_buffer(),
)
});
Expand All @@ -253,6 +255,9 @@ fn configure_and_expand(
sess.dcx().emit_err(errors::MixedProcMacroCrate);
}
}
if crate_types.contains(&CrateType::Sdylib) && !tcx.features().export_stable() {
feature_err(sess, sym::export_stable, DUMMY_SP, "`sdylib` crate type is unstable").emit();
}

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

pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) {
if !tcx.crate_types().contains(&rustc_session::config::CrateType::Sdylib) {
return;
}
let _timer = tcx.sess.timer("write_interface");
let (_, krate) = &*tcx.resolver_for_lowering().borrow();

let krate = rustc_ast_pretty::pprust::print_crate_as_interface(
krate,
tcx.sess.psess.edition,
&tcx.sess.psess.attr_id_generator,
);
let export_output = tcx.output_filenames(()).interface_path();
let mut file = fs::File::create_buffered(export_output).unwrap();
if let Err(err) = write!(file, "{}", krate) {
tcx.dcx().fatal(format!("error writing interface file: {}", err));
}
}

pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
let providers = &mut Providers::default();
providers.analysis = analysis;
Expand Down Expand Up @@ -930,6 +954,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
CStore::from_tcx(tcx).report_unused_deps(tcx);
},
{
tcx.ensure_ok().exportable_items(LOCAL_CRATE);
tcx.ensure_ok().stable_order_of_exportable_impls(LOCAL_CRATE);
tcx.par_hir_for_each_module(|module| {
tcx.ensure_ok().check_mod_loops(module);
tcx.ensure_ok().check_mod_attrs(module);
Expand Down
Loading
Loading