Skip to content
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

Initial support for dynamically linked crates #134767

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
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 @@ -1240,7 +1240,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.name_or_empty() == sym::rustc_intrinsic) {
if attrs.iter().any(|a| a.name_or_empty() == 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 @@ -82,6 +82,8 @@ struct AstValidator<'a> {
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
extern_mod_safety: Option<Safety>,

is_sdylib_interface: bool,

lint_buffer: &'a mut LintBuffer,
}

Expand Down Expand Up @@ -926,7 +928,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

let is_intrinsic =
item.attrs.iter().any(|a| a.name_or_empty() == 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 @@ -1423,7 +1425,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 @@ -1673,6 +1675,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 @@ -1684,6 +1687,7 @@ pub fn check_crate(
outer_impl_trait: None,
disallow_tilde_const: Some(TildeConstReason::Item),
extern_mod_safety: None,
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::{Nonterminal, 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,
};

pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
State::new().nonterminal_to_string(nt)
Expand Down
36 changes: 32 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,39 @@ 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.ann.post(&mut s, AnnNode::Crate(krate));
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,
) {
if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) {
// We need to print `#![no_std]` (and its feature gate) so that
// compiling pretty-printed source won't inject libstd again.
Expand Down Expand Up @@ -277,8 +307,6 @@ pub fn print_crate<'a>(
s.print_item(item);
}
s.print_remaining_comments();
s.ann.post(&mut s, AnnNode::Crate(krate));
s.s.eof()
}

/// Should two consecutive tokens be printed with a space between them?
Expand Down Expand Up @@ -1093,7 +1121,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
10 changes: 10 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,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 @@ -670,6 +674,12 @@ impl<'a> State<'a> {
self.nbsp();
self.print_contract(contract);
}
if self.is_sdylib_interface {
self.word(";");
self.end(); // end inner head-block
self.end(); // end outer head-block
return;
}
if let Some(body) = body {
self.nbsp();
self.print_block_with_attrs(body, attrs);
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 @@ -41,7 +41,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 @@ -1050,9 +1050,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 @@ -1240,8 +1241,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_osx || sess.target.is_like_msvc)
if matches!(
crate_type,
CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib
) && !(sess.target.is_like_osx || sess.target.is_like_msvc)
{
return;
}
Expand Down Expand Up @@ -1935,6 +1938,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 @@ -1814,7 +1814,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 @@ -173,7 +173,7 @@ fn exported_symbols_provider_local(
tcx: TyCtxt<'_>,
_: LocalCrate,
) -> &[(ExportedSymbol<'_>, 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 @@ -1024,7 +1024,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 @@ -56,8 +56,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 @@ -358,6 +358,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 @@ -794,6 +796,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
5 changes: 5 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
naked_functions, experimental!(naked)
),

gated!(
export, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::Yes, experimental!(export)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
EncodeCrossCrate::Yes, experimental!(export)
EncodeCrossCrate::No, experimental!(export)

The results of exportable_items are encoded instead.

),

// 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 @@ -209,6 +209,8 @@ declare_features! (
(internal, compiler_builtins, "1.13.0", None),
/// Allows writing custom MIR
(internal, custom_mir, "1.65.0", None),
/// Allows using `#[export]` which indicates that an item is exportable.
(internal, export, "CURRENT_RUSTC_VERSION", None),
/// Outputs useful `assert!` messages
(unstable, generic_assert, "1.63.0", None),
/// Allows using the `rust-intrinsic`'s "ABI".
Expand Down
32 changes: 31 additions & 1 deletion compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,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 @@ -235,6 +236,7 @@ fn configure_and_expand(
sess,
features,
&krate,
tcx.is_sdylib_interface_build(),
resolver.lint_buffer(),
)
});
Expand All @@ -251,6 +253,9 @@ fn configure_and_expand(
sess.dcx().emit_err(errors::MixedProcMacroCrate);
}
}
if crate_types.contains(&CrateType::Sdylib) && !tcx.features().export() {
feature_err(sess, sym::export, 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 @@ -715,6 +720,27 @@ 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant now that interfaces are built as rlibs.

|| tcx.is_sdylib_interface_build()
{
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 @@ -883,6 +909,10 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
CStore::from_tcx(tcx).report_unused_deps(tcx);
},
{
if tcx.crate_types().contains(&CrateType::Sdylib) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works accidentally because all the tests use #![crate_type = "sdylib"].
I think it's supposed to use if is_inteface_buid instead.

The is_inteface_buid optimization can be put inside the exportable_items and stable_order_of_exportable_impls queries, then we won't need to do it explicitly neither here nor in metadata encoder.

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