diff --git a/Cargo.lock b/Cargo.lock index 4adee6166f7b7..774ca16699eaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4031,6 +4031,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", + "tempfile", "tracing", ] diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index c009abd729da9..f48a571b86a7d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -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(), diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 9b64bcc6df442..cbf4f2f5eb2be 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -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); diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index 551506f2aef8a..a05e2bd6a5d72 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -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> { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 28d5eb87c27c6..0990c9b27eb09 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -221,6 +221,7 @@ pub struct State<'a> { pub s: pp::Printer, comments: Option>, ann: &'a (dyn PpAnn + 'a), + is_sdylib_interface: bool, } const INDENT_UNIT: isize = 4; @@ -236,10 +237,37 @@ pub fn print_crate<'a>( is_expanded: bool, edition: Edition, g: &AttrIdGenerator, +) -> String { + let mut s = State { + s: pp::Printer::new(), + comments: Some(Comments::new(sm, filename, input)), + ann, + is_sdylib_interface: false, + }; + + print_crate_inner(&mut s, krate, is_expanded, edition, g); + s.s.eof() +} + +pub fn print_crate_as_interface( + krate: &ast::Crate, + edition: Edition, + g: &AttrIdGenerator, ) -> String { let mut s = - State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann }; + State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: true }; + print_crate_inner(&mut s, krate, false, edition, g); + s.s.eof() +} + +fn print_crate_inner<'a>( + s: &mut State<'a>, + krate: &ast::Crate, + is_expanded: bool, + edition: Edition, + g: &AttrIdGenerator, +) { // We need to print shebang before anything else // otherwise the resulting code will not compile // and shebang will be useless. @@ -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(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 1e02ac8fd5d15..70cf2f2a45982 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -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 { diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index e5221c7da3197..faeb2643ecb88 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -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, } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 39b3a23e0b1b0..cb329323f5d72 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -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, } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 4ffe551df09b5..8f0948b8183bf 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -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 diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 323538969d73b..159c17b0af757 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -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) }); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e1f903726fbdd..8fc83908efbcc 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -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(); } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 50fb08b286801..1bfdbc0b620ea 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -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 { - 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 &[]; } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f5480da2808fb..89439e4093786 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -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 diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d18fa89281404..fdf8053b15a67 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -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::>(); for supported_crate_type in supported_crate_types { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a5e6b1c00d693..c117e0fcf7ccc 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1a011dfff3f7e..f1bc2c5ea8859 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -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)), diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 493b1d5eaa934..f4d11a7c0be2d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -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 = 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); diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index b11f9260be7c0..26878c488b74a 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -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 diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 1c3222bbfeb61..07fb2de8a3e0c 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -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(()) } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index be31aa629c86e..fcae33c73c9c0 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -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 { - Linkage::Static + 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 + 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::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", diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index f0a898d678cae..10123cb9a9ddb 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -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, rmetas: FxIndexMap, dylibs: FxIndexMap, + interfaces: FxIndexMap, ) -> Result, 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, flavor: CrateFlavor, - slot: &mut Option<(Svh, MetadataBlob, PathBuf)>, + slot: &mut Option<(Svh, MetadataBlob, PathBuf, CrateFlavor)>, ) -> Result, 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) -> CrateError { @@ -797,6 +824,7 @@ fn get_metadata_section<'p>( filename: &'p Path, loader: &dyn MetadataLoader, cfg_version: &'static str, + crate_name: Option, ) -> Result> { 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> { + // 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"), } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 3c2245347f97c..bd813cadedcd4 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -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 { + self.root.exportable_items.decode(self).map(move |index| self.local_def_id(index)) + } + + fn get_stable_order_of_exportable_impls(self) -> impl Iterator { + 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>, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index ecc2dcc5318d0..76bae39ef8c00 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -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); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3ea61d1b40a60..bbff570d6c6c3 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -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 { + 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 diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 5aa81f41b7b4d..c86cf567283fe 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -280,6 +280,8 @@ pub(crate) struct CrateRoot { tables: LazyTables, debugger_visualizers: LazyArray, + exportable_items: LazyArray, + stable_order_of_exportable_impls: LazyArray<(DefIndex, usize)>, exported_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>, syntax_contexts: SyntaxContextTable, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 2dcb0de92b78b..a0f4597408939 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -91,6 +91,8 @@ macro_rules! arena_types { [] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, [] valtree: rustc_middle::ty::ValTreeKind<'tcx>, + [] stable_order_of_exportable_impls: + rustc_data_structures::fx::FxIndexMap, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 88f4c4ae4d361..6af9d4aae3018 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2239,6 +2239,16 @@ rustc_queries! { separate_provide_extern } + query stable_order_of_exportable_impls(_: CrateNum) -> &'tcx FxIndexMap { + 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 diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0f7f8527088c4..d660234618e57 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -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 + Copy) -> Option { match self.def_kind(def_id) { DefKind::Fn | DefKind::AssocFn => {} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2f4c03f0953d3..43dd03f72f063 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -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 { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 59a52ae67cb1f..9cf051a8760be 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -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() diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 413726ddd82d3..6d815e510ea20 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -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}` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e5b20901c0c26..c68f8df49fc70 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -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) diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs new file mode 100644 index 0000000000000..2bb698689bee2 --- /dev/null +++ b/compiler/rustc_passes/src/check_export.rs @@ -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, + 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, + 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> for ExportableItemsChecker<'tcx, 'a> { + type Result = ControlFlow>; + + 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, +} + +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 { + if !tcx.crate_types().contains(&CrateType::Sdylib) && !tcx.is_sdylib_interface_build() { + return tcx.arena.alloc(FxIndexMap::::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 + }; +} diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b1b4b9ee92765..00682a9c7a794 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -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, + }, +} diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 664bd4ad0a252..275714c2d0e44 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -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)); } } diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 424bce9d4d478..001725e28827d 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -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); } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 321e5729b7273..f0e8fa986feae 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -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, diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 701f500e4f60b..93d164e7d01f8 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -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 { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e2d36f6a4e2ff..a9d9236d3188c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -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) -> Result 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), } } } diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index c8a5c22ad1230..4cfc745dec28a 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -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 { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5f4695fb1841b..440e8f808c70a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2113,6 +2113,8 @@ options! { "emit noalias metadata for box (default: yes)"), branch_protection: Option = (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], diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 46dae9144cd40..cba70b5bd5d17 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -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 { @@ -187,6 +188,11 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { + 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::() + }; + + hash.as_u128().to_string() +} diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index ca8918e06aaf3..a51d7da878a29 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -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!( diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index f8f2714ee42d6..ad391d5699209 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -26,6 +26,7 @@ pub(super) fn mangle<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, instantiating_crate: Option, + 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, 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(()) diff --git a/rustfmt.toml b/rustfmt.toml index 7c384b876bf7d..d9857a7e3e788 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -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. diff --git a/tests/run-make/export/compile-interface-error/app.rs b/tests/run-make/export/compile-interface-error/app.rs new file mode 100644 index 0000000000000..f619745a7112a --- /dev/null +++ b/tests/run-make/export/compile-interface-error/app.rs @@ -0,0 +1,3 @@ +extern crate libr; + +fn main() {} diff --git a/tests/run-make/export/compile-interface-error/liblibr.rs b/tests/run-make/export/compile-interface-error/liblibr.rs new file mode 100644 index 0000000000000..906d8d7be12ca --- /dev/null +++ b/tests/run-make/export/compile-interface-error/liblibr.rs @@ -0,0 +1,5 @@ +#![feature(export_stable)] + +// interface file is broken(priv fn): +#[export_stable] +extern "C" fn foo(); diff --git a/tests/run-make/export/compile-interface-error/rmake.rs b/tests/run-make/export/compile-interface-error/rmake.rs new file mode 100644 index 0000000000000..89474e9d4fbff --- /dev/null +++ b/tests/run-make/export/compile-interface-error/rmake.rs @@ -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"); +} diff --git a/tests/run-make/export/disambiguator/app.rs b/tests/run-make/export/disambiguator/app.rs new file mode 100644 index 0000000000000..27e0e2280e54e --- /dev/null +++ b/tests/run-make/export/disambiguator/app.rs @@ -0,0 +1,7 @@ +extern crate libr; + +use libr::*; + +fn main() { + assert_eq!(S::::foo(), 2); +} diff --git a/tests/run-make/export/disambiguator/libr.rs b/tests/run-make/export/disambiguator/libr.rs new file mode 100644 index 0000000000000..b294d5c9e8edc --- /dev/null +++ b/tests/run-make/export/disambiguator/libr.rs @@ -0,0 +1,27 @@ +// `S::::foo` and `S::::foo` have same `DefPath` modulo disambiguator. +// `libr.rs` interface may not contain `S::::foo` as private items aren't +// exportable. We should make sure that original `S::::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(pub T); + +struct S1; +pub struct S2; + +impl S { + extern "C" fn foo() -> i32 { + 1 + } +} + +#[export_stable] +impl S { + pub extern "C" fn foo() -> i32 { + 2 + } +} diff --git a/tests/run-make/export/disambiguator/rmake.rs b/tests/run-make/export/disambiguator/rmake.rs new file mode 100644 index 0000000000000..743db1933fb4e --- /dev/null +++ b/tests/run-make/export/disambiguator/rmake.rs @@ -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(); +} diff --git a/tests/run-make/export/extern-opt/app.rs b/tests/run-make/export/extern-opt/app.rs new file mode 100644 index 0000000000000..765c9925d5f7c --- /dev/null +++ b/tests/run-make/export/extern-opt/app.rs @@ -0,0 +1,6 @@ +extern crate libr; +use libr::*; + +fn main() { + assert_eq!(foo(1), 1); +} diff --git a/tests/run-make/export/extern-opt/libinterface.rs b/tests/run-make/export/extern-opt/libinterface.rs new file mode 100644 index 0000000000000..313cfbe7d59cc --- /dev/null +++ b/tests/run-make/export/extern-opt/libinterface.rs @@ -0,0 +1,4 @@ +#![feature(export_stable)] + +#[export_stable] +pub extern "C" fn foo(x: i32) -> i32; diff --git a/tests/run-make/export/extern-opt/libr.rs b/tests/run-make/export/extern-opt/libr.rs new file mode 100644 index 0000000000000..026ebb4233dfd --- /dev/null +++ b/tests/run-make/export/extern-opt/libr.rs @@ -0,0 +1,5 @@ +#![feature(export_stable)] +#![crate_type = "sdylib"] + +#[export_stable] +pub extern "C" fn foo(x: i32) -> i32 { x } diff --git a/tests/run-make/export/extern-opt/rmake.rs b/tests/run-make/export/extern-opt/rmake.rs new file mode 100644 index 0000000000000..821e2eb214919 --- /dev/null +++ b/tests/run-make/export/extern-opt/rmake.rs @@ -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"); +} diff --git a/tests/run-make/export/simple/app.rs b/tests/run-make/export/simple/app.rs new file mode 100644 index 0000000000000..ba34bdd7b566f --- /dev/null +++ b/tests/run-make/export/simple/app.rs @@ -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); +} diff --git a/tests/run-make/export/simple/libr.rs b/tests/run-make/export/simple/libr.rs new file mode 100644 index 0000000000000..e10b76a6e52ed --- /dev/null +++ b/tests/run-make/export/simple/libr.rs @@ -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 + } + } +} diff --git a/tests/run-make/export/simple/rmake.rs b/tests/run-make/export/simple/rmake.rs new file mode 100644 index 0000000000000..743db1933fb4e --- /dev/null +++ b/tests/run-make/export/simple/rmake.rs @@ -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(); +} diff --git a/tests/ui/attributes/export/crate-type-2.rs b/tests/ui/attributes/export/crate-type-2.rs new file mode 100644 index 0000000000000..f0379f6d797c2 --- /dev/null +++ b/tests/ui/attributes/export/crate-type-2.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --crate-type=sdylib +//~^ ERROR `sdylib` crate type is unstable diff --git a/tests/ui/attributes/export/crate-type-2.stderr b/tests/ui/attributes/export/crate-type-2.stderr new file mode 100644 index 0000000000000..7ce6a500113fb --- /dev/null +++ b/tests/ui/attributes/export/crate-type-2.stderr @@ -0,0 +1,9 @@ +error[E0658]: `sdylib` crate type is unstable + | + = note: see issue #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`. diff --git a/tests/ui/attributes/export/crate-type.rs b/tests/ui/attributes/export/crate-type.rs new file mode 100644 index 0000000000000..bd092bbb1a1ab --- /dev/null +++ b/tests/ui/attributes/export/crate-type.rs @@ -0,0 +1,2 @@ +#![crate_type = "sdylib"] +//~^ ERROR `sdylib` crate type is unstable diff --git a/tests/ui/attributes/export/crate-type.stderr b/tests/ui/attributes/export/crate-type.stderr new file mode 100644 index 0000000000000..7ce6a500113fb --- /dev/null +++ b/tests/ui/attributes/export/crate-type.stderr @@ -0,0 +1,9 @@ +error[E0658]: `sdylib` crate type is unstable + | + = note: see issue #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`. diff --git a/tests/ui/attributes/export/exportable.rs b/tests/ui/attributes/export/exportable.rs new file mode 100644 index 0000000000000..f592fce88cd49 --- /dev/null +++ b/tests/ui/attributes/export/exportable.rs @@ -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) -> i32 { 0 } + //~^ ERROR function with `#[export_stable]` attribute uses type `Box`, 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); + + impl S2 { + #[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: ::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() {} diff --git a/tests/ui/attributes/export/exportable.stderr b/tests/ui/attributes/export/exportable.stderr new file mode 100644 index 0000000000000..0f6469d35c3b7 --- /dev/null +++ b/tests/ui/attributes/export/exportable.stderr @@ -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`, which is not exportable + --> $DIR/exportable.rs:44:5 + | +LL | pub extern "C" fn foo3(x: Box) -> 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: ::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 + diff --git a/tests/ui/attributes/export/lang-item.rs b/tests/ui/attributes/export/lang-item.rs new file mode 100644 index 0000000000000..b923b41a95759 --- /dev/null +++ b/tests/ui/attributes/export/lang-item.rs @@ -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 {} diff --git a/tests/ui/attributes/export/lang-item.stderr b/tests/ui/attributes/export/lang-item.stderr new file mode 100644 index 0000000000000..8c0741bdb6f2d --- /dev/null +++ b/tests/ui/attributes/export/lang-item.stderr @@ -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 + diff --git a/tests/ui/feature-gates/feature-gate-export_stable.rs b/tests/ui/feature-gates/feature-gate-export_stable.rs new file mode 100644 index 0000000000000..5d05fee059b82 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-export_stable.rs @@ -0,0 +1,5 @@ +#![crate_type="lib"] + +#[export_stable] +//~^ ERROR the `#[export_stable]` attribute is an experimental feature +pub mod a {} diff --git a/tests/ui/feature-gates/feature-gate-export_stable.stderr b/tests/ui/feature-gates/feature-gate-export_stable.stderr new file mode 100644 index 0000000000000..6beb52a77e5cc --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-export_stable.stderr @@ -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 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`.