Initial support for dynamically linked crates

This commit is contained in:
Bryanskiy 2024-09-30 21:07:36 +03:00
parent 62c5f58f57
commit 14535312b5
70 changed files with 1536 additions and 119 deletions

View file

@ -4031,6 +4031,7 @@ dependencies = [
"rustc_session",
"rustc_span",
"rustc_target",
"tempfile",
"tracing",
]

View file

@ -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(),

View file

@ -84,6 +84,8 @@ struct AstValidator<'a> {
lint_node_id: NodeId,
is_sdylib_interface: bool,
lint_buffer: &'a mut LintBuffer,
}
@ -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),
@ -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),
@ -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 {
@ -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);

View file

@ -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> {

View file

@ -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;
@ -237,9 +238,36 @@ pub fn print_crate<'a>(
edition: Edition,
g: &AttrIdGenerator,
) -> String {
let mut s =
State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
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: 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.
@ -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?
@ -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)

View file

@ -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);
@ -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 {

View file

@ -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,
}
}

View file

@ -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,
}
}

View file

@ -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

View file

@ -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"])
}
@ -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;
}
@ -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)
});

View file

@ -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();
}

View file

@ -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,
}
}
@ -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();
}
@ -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 &[];
}

View file

@ -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

View file

@ -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};
@ -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
{
@ -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 {

View file

@ -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,

View file

@ -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)),

View file

@ -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;
@ -237,6 +238,7 @@ fn configure_and_expand(
sess,
features,
&krate,
tcx.is_sdylib_interface_build(),
resolver.lint_buffer(),
)
});
@ -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);
@ -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;
@ -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);

View file

@ -26,6 +26,7 @@ rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
tempfile = "3.7.1"
tracing = "0.1"
# tidy-alphabetical-end

View file

@ -148,7 +148,7 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
writeln!(fmt, " hash: {}", data.hash())?;
writeln!(fmt, " reqd: {:?}", data.dep_kind())?;
writeln!(fmt, " priv: {:?}", data.is_private_dep())?;
let CrateSource { dylib, rlib, rmeta } = data.source();
let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source();
if let Some(dylib) = dylib {
writeln!(fmt, " dylib: {}", dylib.0.display())?;
}
@ -158,6 +158,9 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
if let Some(rmeta) = rmeta {
writeln!(fmt, " rmeta: {}", rmeta.0.display())?;
}
if let Some(sdylib_interface) = sdylib_interface {
writeln!(fmt, " sdylib interface: {}", sdylib_interface.0.display())?;
}
}
Ok(())
}

View file

@ -88,45 +88,42 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
return IndexVec::new();
}
let preferred_linkage = match ty {
// Generating a dylib without `-C prefer-dynamic` means that we're going
// to try to eagerly statically link all dependencies. This is normally
// done for end-product dylibs, not intermediate products.
//
// Treat cdylibs and staticlibs similarly. If `-C prefer-dynamic` is set,
// the caller may be code-size conscious, but without it, it makes sense
// to statically link a cdylib or staticlib. For staticlibs we use
// `-Z staticlib-prefer-dynamic` for now. This may be merged into
// `-C prefer-dynamic` in the future.
CrateType::Dylib | CrateType::Cdylib => {
if sess.opts.cg.prefer_dynamic {
Linkage::Dynamic
} else {
let preferred_linkage =
match ty {
// Generating a dylib without `-C prefer-dynamic` means that we're going
// to try to eagerly statically link all dependencies. This is normally
// done for end-product dylibs, not intermediate products.
//
// Treat cdylibs and staticlibs similarly. If `-C prefer-dynamic` is set,
// the caller may be code-size conscious, but without it, it makes sense
// to statically link a cdylib or staticlib. For staticlibs we use
// `-Z staticlib-prefer-dynamic` for now. This may be merged into
// `-C prefer-dynamic` in the future.
CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => {
if sess.opts.cg.prefer_dynamic { Linkage::Dynamic } else { Linkage::Static }
}
CrateType::Staticlib => {
if sess.opts.unstable_opts.staticlib_prefer_dynamic {
Linkage::Dynamic
} else {
Linkage::Static
}
}
// If the global prefer_dynamic switch is turned off, or the final
// executable will be statically linked, prefer static crate linkage.
CrateType::Executable if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => {
Linkage::Static
}
}
CrateType::Staticlib => {
if sess.opts.unstable_opts.staticlib_prefer_dynamic {
Linkage::Dynamic
} else {
Linkage::Static
}
}
CrateType::Executable => Linkage::Dynamic,
// If the global prefer_dynamic switch is turned off, or the final
// executable will be statically linked, prefer static crate linkage.
CrateType::Executable if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => {
Linkage::Static
}
CrateType::Executable => Linkage::Dynamic,
// proc-macro crates are mostly cdylibs, but we also need metadata.
CrateType::ProcMacro => Linkage::Static,
// proc-macro crates are mostly cdylibs, but we also need metadata.
CrateType::ProcMacro => Linkage::Static,
// No linkage happens with rlibs, we just needed the metadata (which we
// got long ago), so don't bother with anything.
CrateType::Rlib => Linkage::NotLinked,
};
// No linkage happens with rlibs, we just needed the metadata (which we
// got long ago), so don't bother with anything.
CrateType::Rlib => Linkage::NotLinked,
};
let mut unavailable_as_static = Vec::new();
@ -165,7 +162,9 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
let all_dylibs = || {
tcx.crates(()).iter().filter(|&&cnum| {
!tcx.dep_kind(cnum).macros_only() && tcx.used_crate_source(cnum).dylib.is_some()
!tcx.dep_kind(cnum).macros_only()
&& (tcx.used_crate_source(cnum).dylib.is_some()
|| tcx.used_crate_source(cnum).sdylib_interface.is_some())
})
};
@ -273,7 +272,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
match *kind {
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
Linkage::Static if src.rlib.is_some() => continue,
Linkage::Dynamic if src.dylib.is_some() => continue,
Linkage::Dynamic if src.dylib.is_some() || src.sdylib_interface.is_some() => continue,
kind => {
let kind = match kind {
Linkage::Static => "rlib",

View file

@ -220,7 +220,7 @@ use std::{cmp, fmt};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::owned_slice::slice_owned;
use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned};
use rustc_data_structures::svh::Svh;
use rustc_errors::{DiagArgValue, IntoDiagArg};
use rustc_fs_util::try_canonicalize;
@ -231,6 +231,7 @@ use rustc_session::search_paths::PathKind;
use rustc_session::utils::CanonicalizedPath;
use rustc_span::{Span, Symbol};
use rustc_target::spec::{Target, TargetTuple};
use tempfile::Builder as TempFileBuilder;
use tracing::{debug, info};
use crate::creader::{Library, MetadataLoader};
@ -277,6 +278,7 @@ pub(crate) enum CrateFlavor {
Rlib,
Rmeta,
Dylib,
SDylib,
}
impl fmt::Display for CrateFlavor {
@ -285,6 +287,7 @@ impl fmt::Display for CrateFlavor {
CrateFlavor::Rlib => "rlib",
CrateFlavor::Rmeta => "rmeta",
CrateFlavor::Dylib => "dylib",
CrateFlavor::SDylib => "sdylib",
})
}
}
@ -295,6 +298,7 @@ impl IntoDiagArg for CrateFlavor {
CrateFlavor::Rlib => DiagArgValue::Str(Cow::Borrowed("rlib")),
CrateFlavor::Rmeta => DiagArgValue::Str(Cow::Borrowed("rmeta")),
CrateFlavor::Dylib => DiagArgValue::Str(Cow::Borrowed("dylib")),
CrateFlavor::SDylib => DiagArgValue::Str(Cow::Borrowed("sdylib")),
}
}
}
@ -379,14 +383,18 @@ impl<'a> CrateLocator<'a> {
&format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix);
let staticlib_prefix =
&format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix);
let interface_prefix = rmeta_prefix;
let rmeta_suffix = ".rmeta";
let rlib_suffix = ".rlib";
let dylib_suffix = &self.target.dll_suffix;
let staticlib_suffix = &self.target.staticlib_suffix;
let interface_suffix = ".rs";
let mut candidates: FxIndexMap<_, (FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>)> =
Default::default();
let mut candidates: FxIndexMap<
_,
(FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>),
> = Default::default();
// First, find all possible candidate rlibs and dylibs purely based on
// the name of the files themselves. We're trying to match against an
@ -417,6 +425,7 @@ impl<'a> CrateLocator<'a> {
(rlib_prefix.as_str(), rlib_suffix, CrateFlavor::Rlib),
(rmeta_prefix.as_str(), rmeta_suffix, CrateFlavor::Rmeta),
(dylib_prefix, dylib_suffix, CrateFlavor::Dylib),
(interface_prefix, interface_suffix, CrateFlavor::SDylib),
] {
if prefix == staticlib_prefix && suffix == staticlib_suffix {
should_check_staticlibs = false;
@ -425,7 +434,7 @@ impl<'a> CrateLocator<'a> {
for (hash, spf) in matches {
info!("lib candidate: {}", spf.path.display());
let (rlibs, rmetas, dylibs) =
let (rlibs, rmetas, dylibs, interfaces) =
candidates.entry(hash.to_string()).or_default();
{
// As a perforamnce optimisation we canonicalize the path and skip
@ -446,6 +455,7 @@ impl<'a> CrateLocator<'a> {
CrateFlavor::Rlib => rlibs.insert(path, search_path.kind),
CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind),
CrateFlavor::Dylib => dylibs.insert(path, search_path.kind),
CrateFlavor::SDylib => interfaces.insert(path, search_path.kind),
};
}
}
@ -472,8 +482,8 @@ impl<'a> CrateLocator<'a> {
// libraries corresponds to the crate id and hash criteria that this
// search is being performed for.
let mut libraries = FxIndexMap::default();
for (_hash, (rlibs, rmetas, dylibs)) in candidates {
if let Some((svh, lib)) = self.extract_lib(rlibs, rmetas, dylibs)? {
for (_hash, (rlibs, rmetas, dylibs, interfaces)) in candidates {
if let Some((svh, lib)) = self.extract_lib(rlibs, rmetas, dylibs, interfaces)? {
libraries.insert(svh, lib);
}
}
@ -508,6 +518,7 @@ impl<'a> CrateLocator<'a> {
rlibs: FxIndexMap<PathBuf, PathKind>,
rmetas: FxIndexMap<PathBuf, PathKind>,
dylibs: FxIndexMap<PathBuf, PathKind>,
interfaces: FxIndexMap<PathBuf, PathKind>,
) -> Result<Option<(Svh, Library)>, CrateError> {
let mut slot = None;
// Order here matters, rmeta should come first.
@ -515,12 +526,17 @@ impl<'a> CrateLocator<'a> {
// Make sure there's at most one rlib and at most one dylib.
//
// See comment in `extract_one` below.
let source = CrateSource {
rmeta: self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot)?,
rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?,
dylib: self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?,
};
Ok(slot.map(|(svh, metadata, _)| (svh, Library { source, metadata })))
let rmeta = self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot)?;
let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?;
let sdylib_interface = self.extract_one(interfaces, CrateFlavor::SDylib, &mut slot)?;
let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?;
if sdylib_interface.is_some() && dylib.is_none() {
return Err(CrateError::FullMetadataNotFound(self.crate_name, CrateFlavor::SDylib));
}
let source = CrateSource { rmeta, rlib, dylib, sdylib_interface };
Ok(slot.map(|(svh, metadata, _, _)| (svh, Library { source, metadata })))
}
fn needs_crate_flavor(&self, flavor: CrateFlavor) -> bool {
@ -550,7 +566,7 @@ impl<'a> CrateLocator<'a> {
&mut self,
m: FxIndexMap<PathBuf, PathKind>,
flavor: CrateFlavor,
slot: &mut Option<(Svh, MetadataBlob, PathBuf)>,
slot: &mut Option<(Svh, MetadataBlob, PathBuf, CrateFlavor)>,
) -> Result<Option<(PathBuf, PathKind)>, CrateError> {
// If we are producing an rlib, and we've already loaded metadata, then
// we should not attempt to discover further crate sources (unless we're
@ -586,6 +602,7 @@ impl<'a> CrateLocator<'a> {
&lib,
self.metadata_loader,
self.cfg_version,
Some(self.crate_name),
) {
Ok(blob) => {
if let Some(h) = self.crate_matches(&blob, &lib) {
@ -610,6 +627,11 @@ impl<'a> CrateLocator<'a> {
}
Err(MetadataError::LoadFailure(err)) => {
info!("no metadata found: {}", err);
// Metadata was loaded from interface file earlier.
if let Some((.., CrateFlavor::SDylib)) = slot {
ret = Some((lib, kind));
continue;
}
// The file was present and created by the same compiler version, but we
// couldn't load it for some reason. Give a hard error instead of silently
// ignoring it, but only if we would have given an error anyway.
@ -679,7 +701,7 @@ impl<'a> CrateLocator<'a> {
return Err(CrateError::FullMetadataNotFound(self.crate_name, flavor));
}
} else {
*slot = Some((hash, metadata, lib.clone()));
*slot = Some((hash, metadata, lib.clone(), flavor));
}
ret = Some((lib, kind));
}
@ -736,6 +758,7 @@ impl<'a> CrateLocator<'a> {
let mut rlibs = FxIndexMap::default();
let mut rmetas = FxIndexMap::default();
let mut dylibs = FxIndexMap::default();
let mut sdylib_interfaces = FxIndexMap::default();
for loc in &self.exact_paths {
let loc_canon = loc.canonicalized();
let loc_orig = loc.original();
@ -763,6 +786,9 @@ impl<'a> CrateLocator<'a> {
rmetas.insert(loc_canon.clone(), PathKind::ExternFlag);
continue;
}
if file.ends_with(".rs") {
sdylib_interfaces.insert(loc_canon.clone(), PathKind::ExternFlag);
}
}
let dll_prefix = self.target.dll_prefix.as_ref();
let dll_suffix = self.target.dll_suffix.as_ref();
@ -776,7 +802,8 @@ impl<'a> CrateLocator<'a> {
}
// Extract the dylib/rlib/rmeta triple.
self.extract_lib(rlibs, rmetas, dylibs).map(|opt| opt.map(|(_, lib)| lib))
self.extract_lib(rlibs, rmetas, dylibs, sdylib_interfaces)
.map(|opt| opt.map(|(_, lib)| lib))
}
pub(crate) fn into_error(self, dep_root: Option<CratePaths>) -> CrateError {
@ -797,6 +824,7 @@ fn get_metadata_section<'p>(
filename: &'p Path,
loader: &dyn MetadataLoader,
cfg_version: &'static str,
crate_name: Option<Symbol>,
) -> Result<MetadataBlob, MetadataError<'p>> {
if !filename.exists() {
return Err(MetadataError::NotPresent(filename));
@ -805,6 +833,55 @@ fn get_metadata_section<'p>(
CrateFlavor::Rlib => {
loader.get_rlib_metadata(target, filename).map_err(MetadataError::LoadFailure)?
}
CrateFlavor::SDylib => {
let compiler = std::env::current_exe().map_err(|_err| {
MetadataError::LoadFailure(
"couldn't obtain current compiler binary when loading sdylib interface"
.to_string(),
)
})?;
let tmp_path = match TempFileBuilder::new().prefix("rustc").tempdir() {
Ok(tmp_path) => tmp_path,
Err(error) => {
return Err(MetadataError::LoadFailure(format!(
"couldn't create a temp dir: {}",
error
)));
}
};
let crate_name = crate_name.unwrap();
debug!("compiling {}", filename.display());
// FIXME: This will need to be done either within the current compiler session or
// as a separate compiler session in the same process.
let res = std::process::Command::new(compiler)
.arg(&filename)
.arg("--emit=metadata")
.arg(format!("--crate-name={}", crate_name))
.arg(format!("--out-dir={}", tmp_path.path().display()))
.arg("-Zbuild-sdylib-interface")
.output()
.map_err(|err| {
MetadataError::LoadFailure(format!("couldn't compile interface: {}", err))
})?;
if !res.status.success() {
return Err(MetadataError::LoadFailure(format!(
"couldn't compile interface: {}",
std::str::from_utf8(&res.stderr).unwrap_or_default()
)));
}
// Load interface metadata instead of crate metadata.
let interface_metadata_name = format!("lib{}.rmeta", crate_name);
let rmeta_file = tmp_path.path().join(interface_metadata_name);
debug!("loading interface metadata from {}", rmeta_file.display());
let rmeta = get_rmeta_metadata_section(&rmeta_file)?;
let _ = std::fs::remove_file(rmeta_file);
rmeta
}
CrateFlavor::Dylib => {
let buf =
loader.get_dylib_metadata(target, filename).map_err(MetadataError::LoadFailure)?;
@ -834,24 +911,7 @@ fn get_metadata_section<'p>(
// Header is okay -> inflate the actual metadata
buf.slice(|buf| &buf[data_start..(data_start + metadata_len)])
}
CrateFlavor::Rmeta => {
// mmap the file, because only a small fraction of it is read.
let file = std::fs::File::open(filename).map_err(|_| {
MetadataError::LoadFailure(format!(
"failed to open rmeta metadata: '{}'",
filename.display()
))
})?;
let mmap = unsafe { Mmap::map(file) };
let mmap = mmap.map_err(|_| {
MetadataError::LoadFailure(format!(
"failed to mmap rmeta metadata: '{}'",
filename.display()
))
})?;
slice_owned(mmap, Deref::deref)
}
CrateFlavor::Rmeta => get_rmeta_metadata_section(filename)?,
};
let Ok(blob) = MetadataBlob::new(raw_bytes) else {
return Err(MetadataError::LoadFailure(format!(
@ -877,6 +937,25 @@ fn get_metadata_section<'p>(
}
}
fn get_rmeta_metadata_section<'a, 'p>(filename: &'p Path) -> Result<OwnedSlice, MetadataError<'a>> {
// mmap the file, because only a small fraction of it is read.
let file = std::fs::File::open(filename).map_err(|_| {
MetadataError::LoadFailure(format!(
"failed to open rmeta metadata: '{}'",
filename.display()
))
})?;
let mmap = unsafe { Mmap::map(file) };
let mmap = mmap.map_err(|_| {
MetadataError::LoadFailure(format!(
"failed to mmap rmeta metadata: '{}'",
filename.display()
))
})?;
Ok(slice_owned(mmap, Deref::deref))
}
/// A diagnostic function for dumping crate metadata to an output stream.
pub fn list_file_metadata(
target: &Target,
@ -887,7 +966,7 @@ pub fn list_file_metadata(
cfg_version: &'static str,
) -> IoResult<()> {
let flavor = get_flavor_from_path(path);
match get_metadata_section(target, flavor, path, metadata_loader, cfg_version) {
match get_metadata_section(target, flavor, path, metadata_loader, cfg_version, None) {
Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds),
Err(msg) => write!(out, "{msg}\n"),
}

View file

@ -1489,6 +1489,17 @@ impl<'a> CrateMetadataRef<'a> {
tcx.arena.alloc_from_iter(self.root.lang_items_missing.decode(self))
}
fn get_exportable_items(self) -> impl Iterator<Item = DefId> {
self.root.exportable_items.decode(self).map(move |index| self.local_def_id(index))
}
fn get_stable_order_of_exportable_impls(self) -> impl Iterator<Item = (DefId, usize)> {
self.root
.stable_order_of_exportable_impls
.decode(self)
.map(move |v| (self.local_def_id(v.0), v.1))
}
fn exported_symbols<'tcx>(
self,
tcx: TyCtxt<'tcx>,

View file

@ -406,6 +406,8 @@ provide! { tcx, def_id, other, cdata,
used_crate_source => { Arc::clone(&cdata.source) }
debugger_visualizers => { cdata.get_debugger_visualizers() }
exportable_items => { tcx.arena.alloc_from_iter(cdata.get_exportable_items()) }
stable_order_of_exportable_impls => { tcx.arena.alloc(cdata.get_stable_order_of_exportable_impls().collect()) }
exported_symbols => {
let syms = cdata.exported_symbols(tcx);

View file

@ -673,6 +673,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let debugger_visualizers =
stat!("debugger-visualizers", || self.encode_debugger_visualizers());
let exportable_items = stat!("exportable-items", || self.encode_exportable_items());
let stable_order_of_exportable_impls =
stat!("exportable-items", || self.encode_stable_order_of_exportable_impls());
// Encode exported symbols info. This is prefetched in `encode_metadata`.
let exported_symbols = stat!("exported-symbols", || {
self.encode_exported_symbols(tcx.exported_symbols(LOCAL_CRATE))
@ -740,6 +745,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
traits,
impls,
incoherent_impls,
exportable_items,
stable_order_of_exportable_impls,
exported_symbols,
interpret_alloc_index,
tables,
@ -2149,6 +2156,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.lazy_array(&all_impls)
}
fn encode_exportable_items(&mut self) -> LazyArray<DefIndex> {
empty_proc_macro!(self);
self.lazy_array(self.tcx.exportable_items(LOCAL_CRATE).iter().map(|def_id| def_id.index))
}
fn encode_stable_order_of_exportable_impls(&mut self) -> LazyArray<(DefIndex, usize)> {
empty_proc_macro!(self);
let stable_order_of_exportable_impls =
self.tcx.stable_order_of_exportable_impls(LOCAL_CRATE);
self.lazy_array(
stable_order_of_exportable_impls.iter().map(|(def_id, idx)| (def_id.index, *idx)),
)
}
// Encodes all symbols exported from this crate into the metadata.
//
// This pass is seeded off the reachability list calculated in the

View file

@ -280,6 +280,8 @@ pub(crate) struct CrateRoot {
tables: LazyTables,
debugger_visualizers: LazyArray<DebuggerVisualizerFile>,
exportable_items: LazyArray<DefIndex>,
stable_order_of_exportable_impls: LazyArray<(DefIndex, usize)>,
exported_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>,
syntax_contexts: SyntaxContextTable,

View file

@ -91,6 +91,8 @@ macro_rules! arena_types {
[] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem,
[] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::Symbol>,
[] valtree: rustc_middle::ty::ValTreeKind<'tcx>,
[] stable_order_of_exportable_impls:
rustc_data_structures::fx::FxIndexMap<rustc_hir::def_id::DefId, usize>,
// Note that this deliberately duplicates items in the `rustc_hir::arena`,
// since we need to allocate this type on both the `rustc_hir` arena

View file

@ -2239,6 +2239,16 @@ rustc_queries! {
separate_provide_extern
}
query stable_order_of_exportable_impls(_: CrateNum) -> &'tcx FxIndexMap<DefId, usize> {
desc { "fetching the stable impl's order" }
separate_provide_extern
}
query exportable_items(_: CrateNum) -> &'tcx [DefId] {
desc { "fetching all exportable items in a crate" }
separate_provide_extern
}
/// The list of symbols exported from the given crate.
///
/// - All names contained in `exported_symbols(cnum)` are guaranteed to

View file

@ -1828,9 +1828,10 @@ impl<'tcx> TyCtxt<'tcx> {
self.crate_types()
.iter()
.map(|ty| match *ty {
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => {
MetadataKind::None
}
CrateType::Executable
| CrateType::Staticlib
| CrateType::Cdylib
| CrateType::Sdylib => MetadataKind::None,
CrateType::Rlib => MetadataKind::Uncompressed,
CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
})
@ -2133,7 +2134,8 @@ impl<'tcx> TyCtxt<'tcx> {
CrateType::Executable
| CrateType::Staticlib
| CrateType::ProcMacro
| CrateType::Cdylib => false,
| CrateType::Cdylib
| CrateType::Sdylib => false,
// FIXME rust-lang/rust#64319, rust-lang/rust#64872:
// We want to block export of generics from dylibs,
@ -3315,6 +3317,10 @@ impl<'tcx> TyCtxt<'tcx> {
&& self.impl_trait_header(def_id).unwrap().constness == hir::Constness::Const
}
pub fn is_sdylib_interface_build(self) -> bool {
self.sess.opts.unstable_opts.build_sdylib_interface
}
pub fn intrinsic(self, def_id: impl IntoQueryParam<DefId> + Copy) -> Option<ty::IntrinsicDef> {
match self.def_kind(def_id) {
DefKind::Fn | DefKind::AssocFn => {}

View file

@ -1978,6 +1978,10 @@ impl<'tcx> TyCtxt<'tcx> {
None
}
pub fn is_exportable(self, def_id: DefId) -> bool {
self.exportable_items(def_id.krate).contains(&def_id)
}
/// Check if the given `DefId` is `#\[automatically_derived\]`, *and*
/// whether it was produced by expanding a builtin derive macro.
pub fn is_builtin_derived(self, def_id: DefId) -> bool {

View file

@ -998,7 +998,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.source_scope = source_scope;
}
if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) {
if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden)
|| self.tcx.is_sdylib_interface_build()
{
let source_info = self.source_info(rustc_span::DUMMY_SP);
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
self.cfg.start_new_block().unit()

View file

@ -356,6 +356,8 @@ passes_ignored_derived_impls =
passes_implied_feature_not_exist =
feature `{$implied_by}` implying `{$feature}` does not exist
passes_incorrect_crate_type = lang items are not allowed in stable dylibs
passes_incorrect_do_not_recommend_args =
`#[diagnostic::do_not_recommend]` does not expect any arguments
@ -742,6 +744,23 @@ passes_trait_impl_const_stable =
passes_transparent_incompatible =
transparent {$target} cannot have other repr hints
passes_unexportable_adt_with_private_fields = ADT types with private fields are not exportable
.note = `{$field_name}` is private
passes_unexportable_fn_abi = only functions with "C" ABI are exportable
passes_unexportable_generic_fn = generic functions are not exportable
passes_unexportable_item = {$descr}'s are not exportable
passes_unexportable_priv_item = private items are not exportable
.note = is only usable at visibility `{$vis_descr}`
passes_unexportable_type_in_interface = {$desc} with `#[export_stable]` attribute uses type `{$ty}`, which is not exportable
.label = not exportable
passes_unexportable_type_repr = types with unstable layout are not exportable
passes_unknown_external_lang_item =
unknown external lang item: `{$lang_item}`

View file

@ -277,6 +277,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::cfg_attr
| sym::cfg_trace
| sym::cfg_attr_trace
| sym::export_stable // handled in `check_export`
// need to be fixed
| sym::cfi_encoding // FIXME(cfi_encoding)
| sym::pointee // FIXME(derive_coerce_pointee)

View file

@ -0,0 +1,398 @@
use std::iter;
use std::ops::ControlFlow;
use rustc_abi::ExternAbi;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::privacy::{EffectiveVisibility, Level};
use rustc_middle::query::{LocalCrate, Providers};
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Visibility,
};
use rustc_session::config::CrateType;
use rustc_span::{Span, sym};
use crate::errors::UnexportableItem;
struct ExportableItemCollector<'tcx> {
tcx: TyCtxt<'tcx>,
exportable_items: FxIndexSet<DefId>,
in_exportable_mod: bool,
seen_exportable_in_mod: bool,
}
impl<'tcx> ExportableItemCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> ExportableItemCollector<'tcx> {
ExportableItemCollector {
tcx,
exportable_items: Default::default(),
in_exportable_mod: false,
seen_exportable_in_mod: false,
}
}
fn report_wrong_site(&self, def_id: LocalDefId) {
let def_descr = self.tcx.def_descr(def_id.to_def_id());
self.tcx.dcx().emit_err(UnexportableItem::Item {
descr: &format!("{}", def_descr),
span: self.tcx.def_span(def_id),
});
}
fn item_is_exportable(&self, def_id: LocalDefId) -> bool {
let has_attr = self.tcx.has_attr(def_id, sym::export_stable);
if !self.in_exportable_mod && !has_attr {
return false;
}
let visibilities = self.tcx.effective_visibilities(());
let is_pub = visibilities.is_directly_public(def_id);
if has_attr && !is_pub {
let vis = visibilities.effective_vis(def_id).cloned().unwrap_or(
EffectiveVisibility::from_vis(Visibility::Restricted(
self.tcx.parent_module_from_def_id(def_id).to_local_def_id(),
)),
);
let vis = vis.at_level(Level::Direct);
let span = self.tcx.def_span(def_id);
self.tcx.dcx().emit_err(UnexportableItem::PrivItem {
vis_note: span,
vis_descr: &vis.to_string(def_id, self.tcx),
span,
});
return false;
}
is_pub && (has_attr || self.in_exportable_mod)
}
fn add_exportable(&mut self, def_id: LocalDefId) {
self.seen_exportable_in_mod = true;
self.exportable_items.insert(def_id.to_def_id());
}
fn walk_item_with_mod(&mut self, item: &'tcx hir::Item<'tcx>) {
let def_id = item.hir_id().owner.def_id;
let old_exportable_mod = self.in_exportable_mod;
if self.tcx.get_attr(def_id, sym::export_stable).is_some() {
self.in_exportable_mod = true;
}
let old_seen_exportable_in_mod = std::mem::replace(&mut self.seen_exportable_in_mod, false);
intravisit::walk_item(self, item);
if self.seen_exportable_in_mod || self.in_exportable_mod {
self.exportable_items.insert(def_id.to_def_id());
}
self.seen_exportable_in_mod = old_seen_exportable_in_mod;
self.in_exportable_mod = old_exportable_mod;
}
}
impl<'tcx> Visitor<'tcx> for ExportableItemCollector<'tcx> {
type NestedFilter = nested_filter::All;
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.tcx
}
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let def_id = item.hir_id().owner.def_id;
// Applying #[extern] attribute to modules is simply equivalent to
// applying the attribute to every public item within it.
match item.kind {
hir::ItemKind::Mod(..) => {
self.walk_item_with_mod(item);
return;
}
hir::ItemKind::Impl(impl_) if impl_.of_trait.is_none() => {
self.walk_item_with_mod(item);
return;
}
_ => {}
}
if !self.item_is_exportable(def_id) {
return;
}
match item.kind {
hir::ItemKind::Fn { .. }
| hir::ItemKind::Struct(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Union(..)
| hir::ItemKind::TyAlias(..) => {
self.add_exportable(def_id);
}
hir::ItemKind::Use(path, _) => {
for res in &path.res {
// Only local items are exportable.
if let Some(res_id) = res.opt_def_id()
&& let Some(res_id) = res_id.as_local()
{
self.add_exportable(res_id);
}
}
}
// handled above
hir::ItemKind::Mod(..) => unreachable!(),
hir::ItemKind::Impl(impl_) if impl_.of_trait.is_none() => {
unreachable!();
}
_ => self.report_wrong_site(def_id),
}
}
fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'tcx>) {
let def_id = item.hir_id().owner.def_id;
if !self.item_is_exportable(def_id) {
return;
}
match item.kind {
hir::ImplItemKind::Fn(..) | hir::ImplItemKind::Type(..) => {
self.add_exportable(def_id);
}
_ => self.report_wrong_site(def_id),
}
}
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
let def_id = item.hir_id().owner.def_id;
if !self.item_is_exportable(def_id) {
self.report_wrong_site(def_id);
}
}
fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'tcx>) {
let def_id = item.hir_id().owner.def_id;
if !self.item_is_exportable(def_id) {
self.report_wrong_site(def_id);
}
}
}
struct ExportableItemsChecker<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
exportable_items: &'a FxIndexSet<DefId>,
item_id: DefId,
}
impl<'tcx, 'a> ExportableItemsChecker<'tcx, 'a> {
fn check(&mut self) {
match self.tcx.def_kind(self.item_id) {
DefKind::Fn | DefKind::AssocFn => self.check_fn(),
DefKind::Enum | DefKind::Struct | DefKind::Union => self.check_ty(),
_ => {}
}
}
fn check_fn(&mut self) {
let def_id = self.item_id.expect_local();
let span = self.tcx.def_span(def_id);
if self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) {
self.tcx.dcx().emit_err(UnexportableItem::GenericFn(span));
return;
}
let sig = self.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
if !matches!(sig.abi, ExternAbi::C { .. }) {
self.tcx.dcx().emit_err(UnexportableItem::FnAbi(span));
return;
}
let sig = self
.tcx
.try_normalize_erasing_regions(ty::TypingEnv::non_body_analysis(self.tcx, def_id), sig)
.unwrap_or(sig);
let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
let decl = self.tcx.hir_fn_decl_by_hir_id(hir_id).unwrap();
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
self.check_nested_types_are_exportable(*input_ty, input_hir.span);
}
if let hir::FnRetTy::Return(ret_hir) = decl.output {
self.check_nested_types_are_exportable(sig.output(), ret_hir.span);
}
}
fn check_ty(&mut self) {
let ty = self.tcx.type_of(self.item_id).skip_binder();
if let ty::Adt(adt_def, _) = ty.kind() {
if !adt_def.repr().inhibit_struct_field_reordering() {
self.tcx
.dcx()
.emit_err(UnexportableItem::TypeRepr(self.tcx.def_span(self.item_id)));
}
// FIXME: support `#[export(unsafe_stable_abi = "hash")]` syntax
for variant in adt_def.variants() {
for field in &variant.fields {
if !field.vis.is_public() {
self.tcx.dcx().emit_err(UnexportableItem::AdtWithPrivFields {
span: self.tcx.def_span(self.item_id),
vis_note: self.tcx.def_span(field.did),
field_name: field.name.as_str(),
});
}
}
}
}
}
fn check_nested_types_are_exportable(&mut self, ty: Ty<'tcx>, ty_span: Span) {
let res = ty.visit_with(self);
if let Some(err_cause) = res.break_value() {
self.tcx.dcx().emit_err(UnexportableItem::TypeInInterface {
span: self.tcx.def_span(self.item_id),
desc: self.tcx.def_descr(self.item_id),
ty: &format!("{}", err_cause),
ty_span,
});
}
}
}
impl<'tcx, 'a> TypeVisitor<TyCtxt<'tcx>> for ExportableItemsChecker<'tcx, 'a> {
type Result = ControlFlow<Ty<'tcx>>;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
match ty.kind() {
ty::Adt(adt_def, _) => {
let did = adt_def.did();
let exportable = if did.is_local() {
self.exportable_items.contains(&did)
} else {
self.tcx.is_exportable(did)
};
if !exportable {
return ControlFlow::Break(ty);
}
for variant in adt_def.variants() {
for field in &variant.fields {
let field_ty = self.tcx.type_of(field.did).instantiate_identity();
field_ty.visit_with(self)?;
}
}
return ty.super_visit_with(self);
}
ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Bool | ty::Char | ty::Error(_) => {}
ty::Array(_, _)
| ty::Ref(_, _, _)
| ty::Param(_)
| ty::Closure(_, _)
| ty::Dynamic(_, _, _)
| ty::Coroutine(_, _)
| ty::Foreign(_)
| ty::Str
| ty::Tuple(_)
| ty::Pat(..)
| ty::Slice(_)
| ty::RawPtr(_, _)
| ty::FnDef(_, _)
| ty::FnPtr(_, _)
| ty::CoroutineClosure(_, _)
| ty::CoroutineWitness(_, _)
| ty::Never
| ty::UnsafeBinder(_)
| ty::Alias(ty::AliasTyKind::Opaque, _) => {
return ControlFlow::Break(ty);
}
ty::Alias(..) | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => unreachable!(),
}
ControlFlow::Continue(())
}
}
/// Exportable items:
///
/// 1. Structs/enums/unions with a stable representation (e.g. repr(i32) or repr(C)).
/// 2. Primitive types.
/// 3. Non-generic functions with a stable ABI (e.g. extern "C") for which every user
/// defined type used in the signature is also marked as `#[export]`.
fn exportable_items_provider_local<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> &'tcx [DefId] {
if !tcx.crate_types().contains(&CrateType::Sdylib) && !tcx.is_sdylib_interface_build() {
return &[];
}
let mut visitor = ExportableItemCollector::new(tcx);
tcx.hir_walk_toplevel_module(&mut visitor);
let exportable_items = visitor.exportable_items;
for item_id in exportable_items.iter() {
let mut validator =
ExportableItemsChecker { tcx, exportable_items: &exportable_items, item_id: *item_id };
validator.check();
}
tcx.arena.alloc_from_iter(exportable_items.into_iter())
}
struct ImplsOrderVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
order: FxIndexMap<DefId, usize>,
}
impl<'tcx> ImplsOrderVisitor<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> ImplsOrderVisitor<'tcx> {
ImplsOrderVisitor { tcx, order: Default::default() }
}
}
impl<'tcx> Visitor<'tcx> for ImplsOrderVisitor<'tcx> {
type NestedFilter = nested_filter::All;
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.tcx
}
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
if let hir::ItemKind::Impl(impl_) = item.kind
&& impl_.of_trait.is_none()
&& self.tcx.is_exportable(item.owner_id.def_id.to_def_id())
{
self.order.insert(item.owner_id.def_id.to_def_id(), self.order.len());
}
intravisit::walk_item(self, item);
}
}
/// During symbol mangling rustc uses a special index to distinguish between two impls of
/// the same type in the same module(See `DisambiguatedDefPathData`). For exportable items
/// we cannot use the current approach because it is dependent on the compiler's
/// implementation.
///
/// In order to make disambiguation independent of the compiler version we can assign an
/// id to each impl according to the relative order of elements in the source code.
fn stable_order_of_exportable_impls<'tcx>(
tcx: TyCtxt<'tcx>,
_: LocalCrate,
) -> &'tcx FxIndexMap<DefId, usize> {
if !tcx.crate_types().contains(&CrateType::Sdylib) && !tcx.is_sdylib_interface_build() {
return tcx.arena.alloc(FxIndexMap::<DefId, usize>::default());
}
let mut vis = ImplsOrderVisitor::new(tcx);
tcx.hir_walk_toplevel_module(&mut vis);
tcx.arena.alloc(vis.order)
}
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
exportable_items: exportable_items_provider_local,
stable_order_of_exportable_impls,
..*providers
};
}

View file

@ -1422,6 +1422,13 @@ pub(crate) struct IncorrectTarget<'a> {
pub at_least: bool,
}
#[derive(Diagnostic)]
#[diag(passes_incorrect_crate_type)]
pub(crate) struct IncorrectCrateType {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_useless_assignment)]
pub(crate) struct UselessAssignment<'a> {
@ -1919,3 +1926,50 @@ pub(crate) struct UnsupportedAttributesInWhere {
#[primary_span]
pub span: MultiSpan,
}
#[derive(Diagnostic)]
pub(crate) enum UnexportableItem<'a> {
#[diag(passes_unexportable_item)]
Item {
#[primary_span]
span: Span,
descr: &'a str,
},
#[diag(passes_unexportable_generic_fn)]
GenericFn(#[primary_span] Span),
#[diag(passes_unexportable_fn_abi)]
FnAbi(#[primary_span] Span),
#[diag(passes_unexportable_type_repr)]
TypeRepr(#[primary_span] Span),
#[diag(passes_unexportable_type_in_interface)]
TypeInInterface {
#[primary_span]
span: Span,
desc: &'a str,
ty: &'a str,
#[label]
ty_span: Span,
},
#[diag(passes_unexportable_priv_item)]
PrivItem {
#[primary_span]
span: Span,
#[note]
vis_note: Span,
vis_descr: &'a str,
},
#[diag(passes_unexportable_adt_with_private_fields)]
AdtWithPrivFields {
#[primary_span]
span: Span,
#[note]
vis_note: Span,
field_name: &'a str,
},
}

View file

@ -19,7 +19,8 @@ use rustc_session::cstore::ExternCrate;
use rustc_span::Span;
use crate::errors::{
DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem,
DuplicateLangItem, IncorrectCrateType, IncorrectTarget, LangItemOnIncorrectTarget,
UnknownLangItem,
};
use crate::weak_lang_items;
@ -236,6 +237,10 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
}
}
if self.tcx.crate_types().contains(&rustc_session::config::CrateType::Sdylib) {
self.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span });
}
self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span));
}
}

View file

@ -19,6 +19,7 @@ use rustc_middle::query::Providers;
pub mod abi_test;
mod check_attr;
mod check_export;
pub mod dead;
mod debugger_visualizer;
mod diagnostic_items;
@ -54,4 +55,5 @@ pub fn provide(providers: &mut Providers) {
reachable::provide(providers);
stability::provide(providers);
upvars::provide(providers);
check_export::provide(providers);
}

View file

@ -435,10 +435,12 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
let effective_visibilities = &tcx.effective_visibilities(());
let any_library = tcx
.crate_types()
.iter()
.any(|ty| *ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro);
let any_library = tcx.crate_types().iter().any(|ty| {
*ty == CrateType::Rlib
|| *ty == CrateType::Dylib
|| *ty == CrateType::ProcMacro
|| *ty == CrateType::Sdylib
});
let mut reachable_context = ReachableContext {
tcx,
maybe_typeck_results: None,

View file

@ -67,7 +67,8 @@ fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) {
| CrateType::ProcMacro
| CrateType::Cdylib
| CrateType::Executable
| CrateType::Staticlib => true,
| CrateType::Staticlib
| CrateType::Sdylib => true,
CrateType::Rlib => false,
});
if !needs_check {

View file

@ -1169,6 +1169,10 @@ impl OutputFilenames {
.unwrap_or_else(|| OutFileName::Real(self.output_path(flavor)))
}
pub fn interface_path(&self) -> PathBuf {
self.out_directory.join(format!("lib{}.rs", self.crate_stem))
}
/// Gets the output path where a compilation artifact of the given type
/// should be placed on disk.
fn output_path(&self, flavor: OutputType) -> PathBuf {
@ -1452,13 +1456,17 @@ pub enum CrateType {
Staticlib,
Cdylib,
ProcMacro,
Sdylib,
}
impl CrateType {
pub fn has_metadata(self) -> bool {
match self {
CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true,
CrateType::Executable | CrateType::Cdylib | CrateType::Staticlib => false,
CrateType::Executable
| CrateType::Cdylib
| CrateType::Staticlib
| CrateType::Sdylib => false,
}
}
}
@ -2818,6 +2826,7 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
"cdylib" => CrateType::Cdylib,
"bin" => CrateType::Executable,
"proc-macro" => CrateType::ProcMacro,
"sdylib" => CrateType::Sdylib,
_ => {
return Err(format!(
"unknown crate type: `{part}`, expected one of: \
@ -2915,6 +2924,7 @@ impl fmt::Display for CrateType {
CrateType::Staticlib => "staticlib".fmt(f),
CrateType::Cdylib => "cdylib".fmt(f),
CrateType::ProcMacro => "proc-macro".fmt(f),
CrateType::Sdylib => "sdylib".fmt(f),
}
}
}

View file

@ -27,6 +27,7 @@ pub struct CrateSource {
pub dylib: Option<(PathBuf, PathKind)>,
pub rlib: Option<(PathBuf, PathKind)>,
pub rmeta: Option<(PathBuf, PathKind)>,
pub sdylib_interface: Option<(PathBuf, PathKind)>,
}
impl CrateSource {

View file

@ -2113,6 +2113,8 @@ options! {
"emit noalias metadata for box (default: yes)"),
branch_protection: Option<BranchProtection> = (None, parse_branch_protection, [TRACKED],
"set options for branch target identification and pointer authentication on AArch64"),
build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED],
"whether the stable interface is being built"),
cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED],
"instrument control-flow architecture protection"),
check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED],

View file

@ -98,7 +98,7 @@ pub fn filename_for_input(
CrateType::Rlib => {
OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib")))
}
CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => {
CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib | CrateType::Sdylib => {
let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix);
OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
}
@ -167,6 +167,7 @@ pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[
(sym::staticlib, CrateType::Staticlib),
(sym::proc_dash_macro, CrateType::ProcMacro),
(sym::bin, CrateType::Executable),
(sym::sdylib, CrateType::Sdylib),
];
pub fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
@ -187,6 +188,11 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
return vec![CrateType::Executable];
}
// Shadow `sdylib` crate type in interface build.
if session.opts.unstable_opts.build_sdylib_interface {
return vec![CrateType::Rlib];
}
// Only check command line flags if present. If no types are specified by
// command line, then reuse the empty `base` Vec to hold the types that
// will be found in crate attributes.

View file

@ -914,6 +914,7 @@ symbols! {
explicit_generic_args_with_impl_trait,
explicit_tail_calls,
export_name,
export_stable,
expr,
expr_2021,
expr_fragment_specifier_2024,
@ -1878,6 +1879,7 @@ symbols! {
saturating_add,
saturating_div,
saturating_sub,
sdylib,
search_unbox,
select_unpredictable,
self_in_typedefs,

View file

@ -0,0 +1,181 @@
use std::assert_matches::debug_assert_matches;
use rustc_abi::IntegerType;
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_hashes::Hash128;
use rustc_hir::def::DefKind;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_span::symbol::{Symbol, sym};
trait AbiHashStable<'tcx> {
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher);
}
macro_rules! default_hash_impl {
($($t:ty,)+) => {
$(impl<'tcx> AbiHashStable<'tcx> for $t {
#[inline]
fn abi_hash(&self, _tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
::std::hash::Hash::hash(self, hasher);
}
})*
};
}
default_hash_impl! { i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, }
impl<'tcx> AbiHashStable<'tcx> for bool {
#[inline]
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
(if *self { 1u8 } else { 0u8 }).abi_hash(tcx, hasher);
}
}
impl<'tcx> AbiHashStable<'tcx> for str {
#[inline]
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
self.as_bytes().abi_hash(tcx, hasher);
}
}
impl<'tcx> AbiHashStable<'tcx> for String {
#[inline]
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
self[..].abi_hash(tcx, hasher);
}
}
impl<'tcx> AbiHashStable<'tcx> for Symbol {
#[inline]
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
self.as_str().abi_hash(tcx, hasher);
}
}
impl<'tcx, T: AbiHashStable<'tcx>> AbiHashStable<'tcx> for [T] {
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
self.len().abi_hash(tcx, hasher);
for item in self {
item.abi_hash(tcx, hasher);
}
}
}
impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> {
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
match self.kind() {
ty::Bool => sym::bool.abi_hash(tcx, hasher),
ty::Char => sym::char.abi_hash(tcx, hasher),
ty::Int(int_ty) => int_ty.name_str().abi_hash(tcx, hasher),
ty::Uint(uint_ty) => uint_ty.name_str().abi_hash(tcx, hasher),
ty::Float(float_ty) => float_ty.name_str().abi_hash(tcx, hasher),
ty::Adt(adt_def, args) => {
adt_def.is_struct().abi_hash(tcx, hasher);
adt_def.is_enum().abi_hash(tcx, hasher);
adt_def.is_union().abi_hash(tcx, hasher);
if let Some(align) = adt_def.repr().align {
align.bits().abi_hash(tcx, hasher);
}
if let Some(integer) = adt_def.repr().int {
match integer {
IntegerType::Pointer(sign) => sign.abi_hash(tcx, hasher),
IntegerType::Fixed(integer, sign) => {
integer.int_ty_str().abi_hash(tcx, hasher);
sign.abi_hash(tcx, hasher);
}
}
}
if let Some(pack) = adt_def.repr().pack {
pack.bits().abi_hash(tcx, hasher);
}
adt_def.repr().c().abi_hash(tcx, hasher);
for variant in adt_def.variants() {
variant.name.abi_hash(tcx, hasher);
for field in &variant.fields {
field.name.abi_hash(tcx, hasher);
let field_ty = tcx.type_of(field.did).instantiate_identity();
field_ty.abi_hash(tcx, hasher);
}
}
args.abi_hash(tcx, hasher);
}
ty::Tuple(args) if args.len() == 0 => {}
// FIXME: Not yet supported.
ty::Foreign(_)
| ty::Ref(_, _, _)
| ty::Str
| ty::Array(_, _)
| ty::Pat(_, _)
| ty::Slice(_)
| ty::RawPtr(_, _)
| ty::FnDef(_, _)
| ty::FnPtr(_, _)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(_, _)
| ty::Never
| ty::Tuple(_)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Bound(_, _)
| ty::Placeholder(_)
| ty::Infer(_)
| ty::UnsafeBinder(_) => unreachable!(),
ty::Error(_) => {}
}
}
}
impl<'tcx> AbiHashStable<'tcx> for ty::FnSig<'tcx> {
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
for ty in self.inputs_and_output {
ty.abi_hash(tcx, hasher);
}
self.safety.is_safe().abi_hash(tcx, hasher);
}
}
impl<'tcx> AbiHashStable<'tcx> for ty::GenericArg<'tcx> {
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
self.unpack().abi_hash(tcx, hasher);
}
}
impl<'tcx> AbiHashStable<'tcx> for ty::GenericArgKind<'tcx> {
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
match self {
ty::GenericArgKind::Type(t) => t.abi_hash(tcx, hasher),
ty::GenericArgKind::Lifetime(_) | ty::GenericArgKind::Const(_) => unimplemented!(),
}
}
}
pub(crate) fn compute_hash_of_export_fn<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
) -> String {
let def_id = instance.def_id();
debug_assert_matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn);
let args = instance.args;
let sig_ty = tcx.fn_sig(def_id).instantiate(tcx, args);
let sig_ty = tcx.instantiate_bound_regions_with_erased(sig_ty);
let hash = {
let mut hasher = StableHasher::new();
sig_ty.abi_hash(tcx, &mut hasher);
hasher.finish::<Hash128>()
};
hash.as_u128().to_string()
}

View file

@ -92,6 +92,7 @@
#![cfg_attr(bootstrap, feature(let_chains))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(rustdoc_internals)]
// tidy-alphabetical-end
@ -104,6 +105,7 @@ use rustc_middle::ty::{self, Instance, TyCtxt};
use rustc_session::config::SymbolManglingVersion;
use tracing::debug;
mod export;
mod hashed;
mod legacy;
mod v0;
@ -296,12 +298,21 @@ fn compute_symbol_name<'tcx>(
tcx.symbol_mangling_version(mangling_version_crate)
};
let symbol = match mangling_version {
SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate),
SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate),
SymbolManglingVersion::Hashed => hashed::mangle(tcx, instance, instantiating_crate, || {
v0::mangle(tcx, instance, instantiating_crate)
}),
let symbol = match tcx.is_exportable(def_id) {
true => format!(
"{}.{}",
v0::mangle(tcx, instance, instantiating_crate, true),
export::compute_hash_of_export_fn(tcx, instance)
),
false => match mangling_version {
SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate),
SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate, false),
SymbolManglingVersion::Hashed => {
hashed::mangle(tcx, instance, instantiating_crate, || {
v0::mangle(tcx, instance, instantiating_crate, false)
})
}
},
};
debug_assert!(

View file

@ -26,6 +26,7 @@ pub(super) fn mangle<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
instantiating_crate: Option<CrateNum>,
is_exportable: bool,
) -> String {
let def_id = instance.def_id();
// FIXME(eddyb) this should ideally not be needed.
@ -35,6 +36,7 @@ pub(super) fn mangle<'tcx>(
let mut cx: SymbolMangler<'_> = SymbolMangler {
tcx,
start_offset: prefix.len(),
is_exportable,
paths: FxHashMap::default(),
types: FxHashMap::default(),
consts: FxHashMap::default(),
@ -93,6 +95,7 @@ pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> Strin
let mut cx: SymbolMangler<'_> = SymbolMangler {
tcx,
start_offset: prefix.len(),
is_exportable: false,
paths: FxHashMap::default(),
types: FxHashMap::default(),
consts: FxHashMap::default(),
@ -135,6 +138,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
let mut cx = SymbolMangler {
tcx,
start_offset: 0,
is_exportable: false,
paths: FxHashMap::default(),
types: FxHashMap::default(),
consts: FxHashMap::default(),
@ -163,6 +167,7 @@ struct SymbolMangler<'tcx> {
tcx: TyCtxt<'tcx>,
binders: Vec<BinderLevel>,
out: String,
is_exportable: bool,
/// The length of the prefix in `out` (e.g. 2 for `_R`).
start_offset: usize,
@ -376,7 +381,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
args,
)?;
} else {
self.push_disambiguator(key.disambiguated_data.disambiguator as u64);
let exported_impl_order = self.tcx.stable_order_of_exportable_impls(impl_def_id.krate);
let disambiguator = match self.is_exportable {
true => exported_impl_order[&impl_def_id] as u64,
false => {
exported_impl_order.len() as u64 + key.disambiguated_data.disambiguator as u64
}
};
self.push_disambiguator(disambiguator);
self.print_def_path(parent_def_id, &[])?;
}
@ -818,8 +830,10 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
self.push("C");
let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id();
self.push_disambiguator(stable_crate_id.as_u64());
if !self.is_exportable {
let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id();
self.push_disambiguator(stable_crate_id.as_u64());
}
let name = self.tcx.crate_name(cnum);
self.push_ident(name.as_str());
Ok(())

View file

@ -19,6 +19,7 @@ ignore = [
"/tests/debuginfo/", # These tests are somewhat sensitive to source code layout.
"/tests/incremental/", # These tests are somewhat sensitive to source code layout.
"/tests/pretty/", # These tests are very sensitive to source code layout.
"/tests/run-make/export", # These tests contain syntax errors.
"/tests/run-make/translation/test.rs", # This test contains syntax errors.
"/tests/rustdoc/", # Some have syntax errors, some are whitespace-sensitive.
"/tests/rustdoc-gui/", # Some tests are sensitive to source code layout.

View file

@ -0,0 +1,3 @@
extern crate libr;
fn main() {}

View file

@ -0,0 +1,5 @@
#![feature(export_stable)]
// interface file is broken(priv fn):
#[export_stable]
extern "C" fn foo();

View file

@ -0,0 +1,9 @@
use run_make_support::rustc;
fn main() {
// Do not produce the interface, use the broken one.
rustc()
.input("app.rs")
.run_fail()
.assert_stderr_contains("couldn't compile interface");
}

View file

@ -0,0 +1,7 @@
extern crate libr;
use libr::*;
fn main() {
assert_eq!(S::<S2>::foo(), 2);
}

View file

@ -0,0 +1,27 @@
// `S::<S2>::foo` and `S::<S1>::foo` have same `DefPath` modulo disambiguator.
// `libr.rs` interface may not contain `S::<S1>::foo` as private items aren't
// exportable. We should make sure that original `S::<S2>::foo` and the one
// produced during interface generation have same mangled names.
#![feature(export_stable)]
#![crate_type = "sdylib"]
#[export_stable]
#[repr(C)]
pub struct S<T>(pub T);
struct S1;
pub struct S2;
impl S<S1> {
extern "C" fn foo() -> i32 {
1
}
}
#[export_stable]
impl S<S2> {
pub extern "C" fn foo() -> i32 {
2
}
}

View file

@ -0,0 +1,12 @@
use run_make_support::rustc;
fn main() {
rustc()
.env("RUSTC_FORCE_RUSTC_VERSION", "1")
.input("libr.rs")
.run();
rustc()
.env("RUSTC_FORCE_RUSTC_VERSION", "2")
.input("app.rs")
.run();
}

View file

@ -0,0 +1,6 @@
extern crate libr;
use libr::*;
fn main() {
assert_eq!(foo(1), 1);
}

View file

@ -0,0 +1,4 @@
#![feature(export_stable)]
#[export_stable]
pub extern "C" fn foo(x: i32) -> i32;

View file

@ -0,0 +1,5 @@
#![feature(export_stable)]
#![crate_type = "sdylib"]
#[export_stable]
pub extern "C" fn foo(x: i32) -> i32 { x }

View file

@ -0,0 +1,23 @@
use run_make_support::{rustc, dynamic_lib_name};
fn main() {
rustc()
.env("RUSTC_FORCE_RUSTC_VERSION", "1")
.input("libr.rs")
.run();
rustc()
.env("RUSTC_FORCE_RUSTC_VERSION", "2")
.input("app.rs")
.extern_("libr", "libinterface.rs")
.extern_("libr", dynamic_lib_name("libr"))
.run();
rustc()
.env("RUSTC_FORCE_RUSTC_VERSION", "2")
.input("app.rs")
.extern_("libr", "interface.rs") // wrong interface format
.extern_("libr", dynamic_lib_name("libr"))
.run_fail()
.assert_stderr_contains("extern location for libr does not exist");
}

View file

@ -0,0 +1,8 @@
extern crate libr;
use libr::*;
fn main() {
let s = m::S { x: 42 };
assert_eq!(m::foo1(s), 42);
assert_eq!(m::S::foo2(1), 1);
}

View file

@ -0,0 +1,22 @@
#![feature(export_stable)]
#![crate_type = "sdylib"]
#[export_stable]
pub mod m {
#[repr(C)]
pub struct S {
pub x: i32,
}
pub extern "C" fn foo1(x: S) -> i32 {
x.x
}
pub type Integer = i32;
impl S {
pub extern "C" fn foo2(x: Integer) -> Integer {
x
}
}
}

View file

@ -0,0 +1,12 @@
use run_make_support::rustc;
fn main() {
rustc()
.env("RUSTC_FORCE_RUSTC_VERSION", "1")
.input("libr.rs")
.run();
rustc()
.env("RUSTC_FORCE_RUSTC_VERSION", "2")
.input("app.rs")
.run();
}

View file

@ -0,0 +1,2 @@
//@ compile-flags: --crate-type=sdylib
//~^ ERROR `sdylib` crate type is unstable

View file

@ -0,0 +1,9 @@
error[E0658]: `sdylib` crate type is unstable
|
= note: see issue #139939 <https://github.com/rust-lang/rust/issues/139939> for more information
= help: add `#![feature(export_stable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,2 @@
#![crate_type = "sdylib"]
//~^ ERROR `sdylib` crate type is unstable

View file

@ -0,0 +1,9 @@
error[E0658]: `sdylib` crate type is unstable
|
= note: see issue #139939 <https://github.com/rust-lang/rust/issues/139939> for more information
= help: add `#![feature(export_stable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,139 @@
//@ compile-flags: -Zunstable-options -Csymbol-mangling-version=v0
#![crate_type = "sdylib"]
#![allow(incomplete_features, improper_ctypes_definitions)]
#![feature(export_stable)]
#![feature(inherent_associated_types)]
mod m {
#[export_stable]
pub struct S;
//~^ ERROR private items are not exportable
pub fn foo() -> i32 { 0 }
//~^ ERROR only functions with "C" ABI are exportable
}
#[export_stable]
pub use m::foo;
#[export_stable]
pub mod m1 {
#[repr(C)]
pub struct S1; // OK, public type with stable repr
struct S2;
pub struct S3;
//~^ ERROR types with unstable layout are not exportable
}
pub mod fn_sig {
#[export_stable]
pub fn foo1() {}
//~^ ERROR only functions with "C" ABI are exportable
#[export_stable]
#[repr(C)]
pub struct S;
#[export_stable]
pub extern "C" fn foo2(x: S) -> i32 { 0 }
#[export_stable]
pub extern "C" fn foo3(x: Box<S>) -> i32 { 0 }
//~^ ERROR function with `#[export_stable]` attribute uses type `Box<fn_sig::S>`, which is not exportable
}
pub mod impl_item {
pub struct S;
impl S {
#[export_stable]
pub extern "C" fn foo1(&self) -> i32 { 0 }
//~^ ERROR method with `#[export_stable]` attribute uses type `&impl_item::S`, which is not exportable
#[export_stable]
pub extern "C" fn foo2(self) -> i32 { 0 }
//~^ ERROR method with `#[export_stable]` attribute uses type `impl_item::S`, which is not exportable
}
pub struct S2<T>(T);
impl<T> S2<T> {
#[export_stable]
pub extern "C" fn foo1(&self) {}
//~^ ERROR generic functions are not exportable
}
}
pub mod tys {
pub trait Trait {
type Type;
}
pub struct S;
impl Trait for S {
type Type = (u32,);
}
#[export_stable]
pub extern "C" fn foo1(x: <S as Trait>::Type) -> u32 { x.0 }
//~^ ERROR function with `#[export_stable]` attribute uses type `(u32,)`, which is not exportable
#[export_stable]
pub type Type = [i32; 4];
#[export_stable]
pub extern "C" fn foo2(_x: Type) {}
//~^ ERROR function with `#[export_stable]` attribute uses type `[i32; 4]`, which is not exportable
impl S {
#[export_stable]
pub type Type = extern "C" fn();
}
#[export_stable]
pub extern "C" fn foo3(_x: S::Type) {}
//~^ ERROR function with `#[export_stable]` attribute uses type `extern "C" fn()`, which is not exportable
#[export_stable]
pub extern "C" fn foo4() -> impl Copy {
//~^ ERROR function with `#[export_stable]` attribute uses type `impl Copy`, which is not exportable
0
}
}
pub mod privacy {
#[export_stable]
#[repr(C)]
pub struct S1 {
pub x: i32
}
#[export_stable]
#[repr(C)]
pub struct S2 {
//~^ ERROR ADT types with private fields are not exportable
x: i32
}
#[export_stable]
#[repr(i32)]
enum E {
//~^ ERROR private items are not exportable
Variant1 { x: i32 }
}
}
pub mod use_site {
#[export_stable]
pub trait Trait {}
//~^ ERROR trait's are not exportable
#[export_stable]
pub const C: i32 = 0;
//~^ ERROR constant's are not exportable
}
fn main() {}

View file

@ -0,0 +1,130 @@
error: private items are not exportable
--> $DIR/exportable.rs:10:5
|
LL | pub struct S;
| ^^^^^^^^^^^^
|
note: is only usable at visibility `pub(crate)`
--> $DIR/exportable.rs:10:5
|
LL | pub struct S;
| ^^^^^^^^^^^^
error: private items are not exportable
--> $DIR/exportable.rs:123:5
|
LL | enum E {
| ^^^^^^
|
note: is only usable at visibility `pub(self)`
--> $DIR/exportable.rs:123:5
|
LL | enum E {
| ^^^^^^
error: trait's are not exportable
--> $DIR/exportable.rs:131:5
|
LL | pub trait Trait {}
| ^^^^^^^^^^^^^^^
error: constant's are not exportable
--> $DIR/exportable.rs:135:5
|
LL | pub const C: i32 = 0;
| ^^^^^^^^^^^^^^^^
error: only functions with "C" ABI are exportable
--> $DIR/exportable.rs:13:5
|
LL | pub fn foo() -> i32 { 0 }
| ^^^^^^^^^^^^^^^^^^^
error: types with unstable layout are not exportable
--> $DIR/exportable.rs:27:5
|
LL | pub struct S3;
| ^^^^^^^^^^^^^
error: only functions with "C" ABI are exportable
--> $DIR/exportable.rs:33:5
|
LL | pub fn foo1() {}
| ^^^^^^^^^^^^^
error: function with `#[export_stable]` attribute uses type `Box<fn_sig::S>`, which is not exportable
--> $DIR/exportable.rs:44:5
|
LL | pub extern "C" fn foo3(x: Box<S>) -> i32 { 0 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^------^^^^^^^^
| |
| not exportable
error: method with `#[export_stable]` attribute uses type `&impl_item::S`, which is not exportable
--> $DIR/exportable.rs:53:9
|
LL | pub extern "C" fn foo1(&self) -> i32 { 0 }
| ^^^^^^^^^^^^^^^^^^^^^^^-----^^^^^^^^
| |
| not exportable
error: method with `#[export_stable]` attribute uses type `impl_item::S`, which is not exportable
--> $DIR/exportable.rs:57:9
|
LL | pub extern "C" fn foo2(self) -> i32 { 0 }
| ^^^^^^^^^^^^^^^^^^^^^^^----^^^^^^^^
| |
| not exportable
error: generic functions are not exportable
--> $DIR/exportable.rs:65:9
|
LL | pub extern "C" fn foo1(&self) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: function with `#[export_stable]` attribute uses type `(u32,)`, which is not exportable
--> $DIR/exportable.rs:81:5
|
LL | pub extern "C" fn foo1(x: <S as Trait>::Type) -> u32 { x.0 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^^^^^^^
| |
| not exportable
error: function with `#[export_stable]` attribute uses type `[i32; 4]`, which is not exportable
--> $DIR/exportable.rs:88:5
|
LL | pub extern "C" fn foo2(_x: Type) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^----^
| |
| not exportable
error: function with `#[export_stable]` attribute uses type `extern "C" fn()`, which is not exportable
--> $DIR/exportable.rs:97:5
|
LL | pub extern "C" fn foo3(_x: S::Type) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^
| |
| not exportable
error: function with `#[export_stable]` attribute uses type `impl Copy`, which is not exportable
--> $DIR/exportable.rs:101:5
|
LL | pub extern "C" fn foo4() -> impl Copy {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------
| |
| not exportable
error: ADT types with private fields are not exportable
--> $DIR/exportable.rs:116:5
|
LL | pub struct S2 {
| ^^^^^^^^^^^^^
|
note: `x` is private
--> $DIR/exportable.rs:118:9
|
LL | x: i32
| ^^^^^^
error: aborting due to 16 previous errors

View file

@ -0,0 +1,8 @@
#![feature(no_core, lang_items, export_stable)]
#![allow(incomplete_features)]
#![crate_type = "sdylib"]
#![no_core]
#[lang = "sized"]
//~^ ERROR lang items are not allowed in stable dylibs
trait Sized {}

View file

@ -0,0 +1,8 @@
error: lang items are not allowed in stable dylibs
--> $DIR/lang-item.rs:6:1
|
LL | #[lang = "sized"]
| ^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -0,0 +1,5 @@
#![crate_type="lib"]
#[export_stable]
//~^ ERROR the `#[export_stable]` attribute is an experimental feature
pub mod a {}

View file

@ -0,0 +1,13 @@
error[E0658]: the `#[export_stable]` attribute is an experimental feature
--> $DIR/feature-gate-export_stable.rs:3:1
|
LL | #[export_stable]
| ^^^^^^^^^^^^^^^^
|
= note: see issue #139939 <https://github.com/rust-lang/rust/issues/139939> for more information
= help: add `#![feature(export_stable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.