diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 39275272e42..4c69b9503a2 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -335,6 +335,10 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.extend(probestack_attr(cx)); to_add.extend(stackprotector_attr(cx)); + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) { + to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins")); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { to_add.push(AttributeKind::Cold.create_attr(cx.llcx)); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index d6c23012762..0c7b8a79612 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_ast::{ast, MetaItemKind, NestedMetaItem}; +use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -60,6 +60,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } + // When `no_builtins` is applied at the crate level, we should add the + // `no-builtins` attribute to each function to ensure it takes effect in LTO. + let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); + if no_builtins { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; + } + let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); let mut inline_span = None; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 42cc0a6b143..102bae2a744 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1200,7 +1200,7 @@ fn check_matcher_core<'tt>( err.span_label(sp, format!("not allowed after `{}` fragments", kind)); if kind == NonterminalKind::PatWithOr - && sess.edition.rust_2021() + && sess.edition.at_least_rust_2021() && next_token.is_token(&BinOp(token::BinOpToken::Or)) { let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index 05a3ab63d5c..ff55174f97a 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -86,7 +86,7 @@ pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) )); } - if self_ty.span.edition().rust_2021() { + if self_ty.span.edition().at_least_rust_2021() { let msg = "trait objects must include the `dyn` keyword"; let label = "add `dyn` keyword before this trait"; let mut diag = diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index bd7e7a671e6..94f64a1ffdb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -860,7 +860,7 @@ pub fn resolve_ty_and_res_fully_qualified_call( .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) .and_then(|r| { // lint bare trait if the method is found in the trait - if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { + if span.edition().at_least_rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { diag.emit(); } Ok(r) @@ -890,7 +890,7 @@ pub fn resolve_ty_and_res_fully_qualified_call( } // emit or cancel the diagnostic for bare traits - if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { + if span.edition().at_least_rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { if trait_missing_method { // cancel the diag for bare traits when meeting `MyTrait::missing_method` diag.cancel(); @@ -908,7 +908,7 @@ pub fn resolve_ty_and_res_fully_qualified_call( error, None, Expectation::NoExpectation, - trait_missing_method && span.edition().rust_2021(), // emits missing method for trait only after edition 2021 + trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021 ) { e.emit(); } diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index ec4e7f7f88a..4efe95c4dc5 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -32,7 +32,7 @@ pub(super) fn lint_dot_call_from_2018( ); // Rust 2021 and later is already using the new prelude - if span.rust_2021() { + if span.at_least_rust_2021() { return; } @@ -203,7 +203,7 @@ pub(super) fn lint_fully_qualified_call_from_2018( pick: &Pick<'tcx>, ) { // Rust 2021 and later is already using the new prelude - if span.rust_2021() { + if span.at_least_rust_2021() { return; } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9ef97243c5e..05eed1923d1 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -437,7 +437,7 @@ fn probe_op( // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - if self.tcx.sess.rust_2018() { + if self.tcx.sess.at_least_rust_2018() { self.tcx.sess.emit_err(MethodCallOnUnknownRawPointee { span }); } else { self.tcx.struct_span_lint_hir( @@ -1592,7 +1592,7 @@ fn consider_probe( if let Some(method_name) = self.method_name { // Some trait methods are excluded for arrays before 2021. // (`array.into_iter()` wants a slice iterator for compatibility.) - if self_ty.is_array() && !method_name.span.rust_2021() { + if self_ty.is_array() && !method_name.span.at_least_rust_2021() { let trait_def = self.tcx.trait_def(trait_ref.def_id); if trait_def.skip_array_during_method_dispatch { return ProbeResult::NoMatch; diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index f77a4d16b5f..fb81a8395d7 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -2001,7 +2001,7 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( tcx: TyCtxt<'_>, closure_id: hir::HirId, ) -> bool { - if tcx.sess.rust_2021() { + if tcx.sess.at_least_rust_2021() { return false; } @@ -2247,5 +2247,5 @@ fn truncate_capture_for_optimization( fn enable_precise_capture(span: Span) -> bool { // We use span here to ensure that if the closure was generated by a macro with a different // edition. - span.rust_2021() + span.at_least_rust_2021() } diff --git a/compiler/rustc_metadata/src/foreign_modules.rs b/compiler/rustc_metadata/src/foreign_modules.rs index d1c2f3104d0..154eb684f11 100644 --- a/compiler/rustc_metadata/src/foreign_modules.rs +++ b/compiler/rustc_metadata/src/foreign_modules.rs @@ -1,19 +1,28 @@ +use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::ForeignModule; -pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec { - let mut modules = Vec::new(); +pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> FxIndexMap { + let mut modules = FxIndexMap::default(); + + // We need to collect all the `ForeignMod`, even if they are empty. for id in tcx.hir().items() { if !matches!(tcx.def_kind(id.owner_id), DefKind::ForeignMod) { continue; } + + let def_id = id.owner_id.to_def_id(); let item = tcx.hir().item(id); - if let hir::ItemKind::ForeignMod { items, .. } = item.kind { + + if let hir::ItemKind::ForeignMod { abi, items } = item.kind { let foreign_items = items.iter().map(|it| it.id.owner_id.to_def_id()).collect(); - modules.push(ForeignModule { foreign_items, def_id: id.owner_id.to_def_id() }); + modules.insert(def_id, ForeignModule { def_id, abi, foreign_items }); } } + modules } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 9e0bf81d58d..ca5043cc263 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,15 +1,17 @@ use rustc_ast::{NestedMetaItem, CRATE_NODE_ID}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_session::config::CrateType; -use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib, PeImportNameType}; +use rustc_session::cstore::{ + DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType, +}; use rustc_session::parse::feature_err; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; use rustc_session::Session; +use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; @@ -66,10 +68,12 @@ fn find_bundled_library( None } -pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec { +pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec { let mut collector = Collector { tcx, libs: Vec::new() }; - for id in tcx.hir().items() { - collector.process_item(id); + if tcx.sess.opts.unstable_opts.link_directives { + for module in tcx.foreign_modules(LOCAL_CRATE).values() { + collector.process_module(module); + } } collector.process_command_line(); collector.libs @@ -88,29 +92,20 @@ struct Collector<'tcx> { } impl<'tcx> Collector<'tcx> { - fn process_item(&mut self, id: rustc_hir::ItemId) { - if !matches!(self.tcx.def_kind(id.owner_id), DefKind::ForeignMod) { - return; - } + fn process_module(&mut self, module: &ForeignModule) { + let ForeignModule { def_id, abi, ref foreign_items } = *module; + let def_id = def_id.expect_local(); - let it = self.tcx.hir().item(id); - let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else { - return; - }; + let sess = self.tcx.sess; if matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic) { return; } // Process all of the #[link(..)]-style arguments - let sess = self.tcx.sess; let features = self.tcx.features(); - if !sess.opts.unstable_opts.link_directives { - return; - } - - for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) { + for m in self.tcx.get_attrs(def_id, sym::link) { let Some(items) = m.meta_item_list() else { continue; }; @@ -340,9 +335,9 @@ fn process_item(&mut self, id: rustc_hir::ItemId) { if name.as_str().contains('\0') { sess.emit_err(errors::RawDylibNoNul { span: name_span }); } - foreign_mod_items + foreign_items .iter() - .map(|child_item| { + .map(|&child_item| { self.build_dll_import( abi, import_name_type.map(|(import_name_type, _)| import_name_type), @@ -352,21 +347,12 @@ fn process_item(&mut self, id: rustc_hir::ItemId) { .collect() } _ => { - for child_item in foreign_mod_items { - if self.tcx.def_kind(child_item.id.owner_id).has_codegen_attrs() - && self - .tcx - .codegen_fn_attrs(child_item.id.owner_id) - .link_ordinal - .is_some() + for &child_item in foreign_items { + if self.tcx.def_kind(child_item).has_codegen_attrs() + && self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some() { - let link_ordinal_attr = self - .tcx - .hir() - .attrs(child_item.id.owner_id.into()) - .iter() - .find(|a| a.has_name(sym::link_ordinal)) - .unwrap(); + let link_ordinal_attr = + self.tcx.get_attr(child_item, sym::link_ordinal).unwrap(); sess.emit_err(errors::LinkOrdinalRawDylib { span: link_ordinal_attr.span, }); @@ -384,7 +370,7 @@ fn process_item(&mut self, id: rustc_hir::ItemId) { filename, kind, cfg, - foreign_module: Some(it.owner_id.to_def_id()), + foreign_module: Some(def_id.to_def_id()), verbatim, dll_imports, }); @@ -476,10 +462,10 @@ fn process_command_line(&mut self) { } } - fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize { + fn i686_arg_list_size(&self, item: DefId) -> usize { let argument_types: &List> = self.tcx.erase_late_bound_regions( self.tcx - .type_of(item.id.owner_id) + .type_of(item) .instantiate_identity() .fn_sig(self.tcx) .inputs() @@ -505,8 +491,10 @@ fn build_dll_import( &self, abi: Abi, import_name_type: Option, - item: &hir::ForeignItemRef, + item: DefId, ) -> DllImport { + let span = self.tcx.def_span(item); + let calling_convention = if self.tcx.sess.target.arch == "x86" { match abi { Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C, @@ -520,29 +508,29 @@ fn build_dll_import( DllCallingConvention::Vectorcall(self.i686_arg_list_size(item)) } _ => { - self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span: item.span }); + self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span }); } } } else { match abi { Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C, _ => { - self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span: item.span }); + self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span }); } } }; - let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item.id.owner_id); + let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item); let import_name_type = codegen_fn_attrs .link_ordinal .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord))); DllImport { - name: codegen_fn_attrs.link_name.unwrap_or(item.ident.name), + name: codegen_fn_attrs.link_name.unwrap_or(self.tcx.item_name(item)), import_name_type, calling_convention, - span: item.span, - is_fn: self.tcx.def_kind(item.id.owner_id).is_fn_like(), + span, + is_fn: self.tcx.def_kind(item).is_fn_like(), } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 848535fb395..a8815ee0908 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -403,10 +403,8 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { .contains(&id) }) }, - native_libraries: |tcx, LocalCrate| native_libs::collect(tcx), - foreign_modules: |tcx, LocalCrate| { - foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect() - }, + native_libraries: native_libs::collect, + foreign_modules: foreign_modules::collect, // Returns a map from a sufficiently visible external item (i.e., an // external item that is visible from at least one local module) to a diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index c4601a1fb41..02fd6ed7ba6 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -100,6 +100,8 @@ pub struct CodegenFnAttrFlags: u32 { const REALLOCATOR = 1 << 18; /// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory. const ALLOCATOR_ZEROED = 1 << 19; + /// `#[no_builtins]`: indicates that disable implicit builtin knowledge of functions for the function. + const NO_BUILTINS = 1 << 20; } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c304245ca39..062adc9ec54 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1579,7 +1579,7 @@ } /// Returns a list of all `extern` blocks of a crate. - query foreign_modules(_: CrateNum) -> &'tcx FxHashMap { + query foreign_modules(_: CrateNum) -> &'tcx FxIndexMap { arena_cache desc { "looking up the foreign modules of a linked crate" } separate_provide_extern diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 70eb389b406..e5633223464 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1932,7 +1932,7 @@ fn print_const(self, ct: ty::Const<'tcx>) -> Result { fn path_crate(mut self, cnum: CrateNum) -> Result { self.empty_path = true; if cnum == LOCAL_CRATE { - if self.tcx.sess.rust_2018() { + if self.tcx.sess.at_least_rust_2018() { // We add the `crate::` keyword on Rust 2018, only when desired. if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) { write!(self, "{}", kw::Crate)?; diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index c122230b841..7756d5d4879 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -730,5 +730,5 @@ fn read_fake_borrows( /// Precise capture is enabled if user is using Rust Edition 2021 or higher. fn enable_precise_capture(closure_span: Span) -> bool { - closure_span.rust_2021() + closure_span.at_least_rust_2021() } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7ede4fbc3d9..3ecdbc36248 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1309,7 +1309,7 @@ fn parse_expr_index(&mut self, lo: Span, base: P) -> PResult<'a, P> /// Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { - if self.token.uninterpolated_span().rust_2018() && self.eat_keyword(kw::Await) { + if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await) { return Ok(self.mk_await_expr(self_arg, lo)); } @@ -1442,8 +1442,8 @@ fn parse_expr_bottom(&mut self) -> PResult<'a, P> { self.parse_expr_let() } else if self.eat_keyword(kw::Underscore) { Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore)) - } else if self.token.uninterpolated_span().rust_2018() { - // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. + } else if self.token.uninterpolated_span().at_least_rust_2018() { + // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. if self.check_keyword(kw::Async) { if self.is_async_block() { // Check for `async {` and `async move {`. @@ -2230,7 +2230,7 @@ fn parse_expr_closure(&mut self) -> PResult<'a, P> { let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; - let asyncness = if self.token.uninterpolated_span().rust_2018() { + let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() { self.parse_asyncness(Case::Sensitive) } else { Async::No @@ -3014,7 +3014,7 @@ fn is_do_yeet(&self) -> bool { fn is_try_block(&self) -> bool { self.token.is_keyword(kw::Try) && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace)) - && self.token.uninterpolated_span().rust_2018() + && self.token.uninterpolated_span().at_least_rust_2018() } /// Parses an `async move? {...}` expression. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e4d843b7c8b..2e1a61e634e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1210,7 +1210,8 @@ fn parse_closure_constness(&mut self) -> Const { fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const { // Avoid const blocks and const closures to be parsed as const items if (self.check_const_closure() == is_closure) - && self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) + && !self + .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()) && self.eat_keyword_case(kw::Const, case) { Const::Yes(self.prev_token.uninterpolated_span()) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a29b696aea8..3bb50b05aa3 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -608,7 +608,7 @@ fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { /// Is a `dyn B0 + ... + Bn` type allowed here? fn is_explicit_dyn_type(&mut self) -> bool { self.check_keyword(kw::Dyn) - && (self.token.uninterpolated_span().rust_2018() + && (self.token.uninterpolated_span().at_least_rust_2018() || self.look_ahead(1, |t| { (t.can_begin_bound() || t.kind == TokenKind::BinOp(token::Star)) && !can_continue_type_after_non_fn_ident(t) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 7de84db211e..88452ccdf05 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -109,6 +109,8 @@ pub struct Argument<'a> { pub struct FormatSpec<'a> { /// Optionally specified character to fill alignment with. pub fill: Option, + /// Span of the optionally specified fill character. + pub fill_span: Option, /// Optionally specified alignment. pub align: Alignment, /// The `+` or `-` flag. @@ -264,7 +266,7 @@ fn next(&mut self) -> Option> { Some(String(self.string(pos + 1))) } else { let arg = self.argument(lbrace_end); - if let Some(rbrace_pos) = self.must_consume('}') { + if let Some(rbrace_pos) = self.consume_closing_brace(&arg) { if self.is_source_literal { let lbrace_byte_pos = self.to_span_index(pos); let rbrace_byte_pos = self.to_span_index(rbrace_pos); @@ -450,69 +452,51 @@ fn span(&self, start_pos: usize, end_pos: usize) -> InnerSpan { /// Forces consumption of the specified character. If the character is not /// found, an error is emitted. - fn must_consume(&mut self, c: char) -> Option { + fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option { self.ws(); - if let Some(&(pos, maybe)) = self.cur.peek() { - if c == maybe { + let pos; + let description; + + if let Some(&(peek_pos, maybe)) = self.cur.peek() { + if maybe == '}' { self.cur.next(); - Some(pos) - } else { - let pos = self.to_span_index(pos); - let description = format!("expected `'}}'`, found `{maybe:?}`"); - let label = "expected `}`".to_owned(); - let (note, secondary_label) = if c == '}' { - ( - Some( - "if you intended to print `{`, you can escape it using `{{`".to_owned(), - ), - self.last_opening_brace - .map(|sp| ("because of this opening brace".to_owned(), sp)), - ) - } else { - (None, None) - }; - self.errors.push(ParseError { - description, - note, - label, - span: pos.to(pos), - secondary_label, - should_be_replaced_with_positional_argument: false, - }); - None + return Some(peek_pos); } + + pos = peek_pos; + description = format!("expected `'}}'`, found `{maybe:?}`"); } else { - let description = format!("expected `{c:?}` but string was terminated"); + description = "expected `'}'` but string was terminated".to_owned(); // point at closing `"` - let pos = self.input.len() - if self.append_newline { 1 } else { 0 }; - let pos = self.to_span_index(pos); - if c == '}' { - let label = format!("expected `{c:?}`"); - let (note, secondary_label) = if c == '}' { - ( - Some( - "if you intended to print `{`, you can escape it using `{{`".to_owned(), - ), - self.last_opening_brace - .map(|sp| ("because of this opening brace".to_owned(), sp)), - ) - } else { - (None, None) - }; - self.errors.push(ParseError { - description, - note, - label, - span: pos.to(pos), - secondary_label, - should_be_replaced_with_positional_argument: false, - }); - } else { - self.err(description, format!("expected `{c:?}`"), pos.to(pos)); - } - None + pos = self.input.len() - if self.append_newline { 1 } else { 0 }; } + + let pos = self.to_span_index(pos); + + let label = "expected `'}'`".to_owned(); + let (note, secondary_label) = if arg.format.fill == Some('}') { + ( + Some("the character `'}'` is interpreted as a fill character because of the `:` that precedes it".to_owned()), + arg.format.fill_span.map(|sp| ("this is not interpreted as a formatting closing brace".to_owned(), sp)), + ) + } else { + ( + Some("if you intended to print `{`, you can escape it using `{{`".to_owned()), + self.last_opening_brace.map(|sp| ("because of this opening brace".to_owned(), sp)), + ) + }; + + self.errors.push(ParseError { + description, + note, + label, + span: pos.to(pos), + secondary_label, + should_be_replaced_with_positional_argument: false, + }); + + None } /// Consumes all whitespace characters until the first non-whitespace character @@ -608,6 +592,7 @@ fn current_pos(&mut self) -> usize { fn format(&mut self) -> FormatSpec<'a> { let mut spec = FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -625,9 +610,10 @@ fn format(&mut self) -> FormatSpec<'a> { } // fill character - if let Some(&(_, c)) = self.cur.peek() { + if let Some(&(idx, c)) = self.cur.peek() { if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) { spec.fill = Some(c); + spec.fill_span = Some(self.span(idx, idx + 1)); self.cur.next(); } } @@ -722,6 +708,7 @@ fn format(&mut self) -> FormatSpec<'a> { fn inline_asm(&mut self) -> FormatSpec<'a> { let mut spec = FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 45314e2fb55..0c594f9104c 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -9,6 +9,7 @@ fn same(fmt: &'static str, p: &[Piece<'static>]) { fn fmtdflt() -> FormatSpec<'static> { return FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -128,6 +129,7 @@ fn format_type() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -152,6 +154,7 @@ fn format_align_fill() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignRight, sign: None, alternate: false, @@ -173,6 +176,7 @@ fn format_align_fill() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: Some('0'), + fill_span: Some(InnerSpan::new(4, 5)), align: AlignLeft, sign: None, alternate: false, @@ -194,6 +198,7 @@ fn format_align_fill() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: Some('*'), + fill_span: Some(InnerSpan::new(4, 5)), align: AlignLeft, sign: None, alternate: false, @@ -218,6 +223,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -239,6 +245,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -260,6 +267,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -281,6 +289,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -302,6 +311,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -323,6 +333,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -344,6 +355,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -368,6 +380,7 @@ fn format_flags() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: Some(Sign::Minus), alternate: false, @@ -389,6 +402,7 @@ fn format_flags() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: Some(Sign::Plus), alternate: true, @@ -415,6 +429,7 @@ fn format_mixture() { position_span: InnerSpan { start: 7, end: 8 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index dc35c8b176f..3228e8d52f0 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -440,7 +440,7 @@ pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { // If we are not in Rust 2018 edition, then we don't make any further // suggestions. - if !tcx.sess.rust_2018() { + if !tcx.sess.at_least_rust_2018() { continue; } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d3dcdfa4275..2cfde2f62d8 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1203,7 +1203,7 @@ fn lookup_import_candidates_from_module( if filter_fn(res) { // create the path let mut segms = path_segments.clone(); - if lookup_ident.span.rust_2018() { + if lookup_ident.span.at_least_rust_2018() { // crate-local absolute paths start with `crate::` in edition 2018 // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) segms.insert(0, ast::PathSegment::from_ident(crate_name)); @@ -1268,7 +1268,7 @@ fn lookup_import_candidates_from_module( path_segments.push(ast::PathSegment::from_ident(ident)); let is_extern_crate_that_also_appears_in_prelude = - name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); + name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); if !is_extern_crate_that_also_appears_in_prelude { // add the module to the lookup @@ -1315,7 +1315,7 @@ pub(crate) fn lookup_import_candidates( &filter_fn, ); - if lookup_ident.span.rust_2018() { + if lookup_ident.span.at_least_rust_2018() { let extern_prelude_names = self.extern_prelude.clone(); for (ident, _) in extern_prelude_names.into_iter() { if ident.span.from_expansion() { @@ -1568,7 +1568,7 @@ fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError<'_>) { "consider adding an explicit import of `{ident}` to disambiguate" )) } - if b.is_extern_crate() && ident.span.rust_2018() { + if b.is_extern_crate() && ident.span.at_least_rust_2018() { help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) } match misc { @@ -1973,7 +1973,7 @@ pub(crate) fn make_path_suggestion( if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {} // `ident::...` on 2018. (Some(fst), _) - if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() => + if fst.ident.span.at_least_rust_2018() && !fst.ident.is_path_segment_keyword() => { // Insert a placeholder that's later replaced by `self`/`super`/etc. path.insert(0, Segment::from_ident(Ident::empty())); diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 520fab1f0c8..de431444769 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1417,13 +1417,13 @@ pub(crate) fn resolve_path_with_ribs( )); continue; } - if name == kw::PathRoot && ident.span.rust_2018() { + if name == kw::PathRoot && ident.span.at_least_rust_2018() { module = Some(ModuleOrUniformRoot::ExternPrelude); continue; } if name == kw::PathRoot && ident.span.is_rust_2015() - && self.tcx.sess.rust_2018() + && self.tcx.sess.at_least_rust_2018() { // `::a::b` from 2015 macro on 2018 global edition module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude); diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index dc475e8c6d5..c53a355b533 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -13,6 +13,7 @@ use rustc_span::hygiene::{ExpnHash, ExpnId}; use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_target::spec::abi::Abi; use rustc_target::spec::Target; use std::any::Any; @@ -147,6 +148,7 @@ pub enum DllCallingConvention { pub struct ForeignModule { pub foreign_items: Vec, pub def_id: DefId, + pub abi: Abi, } #[derive(Copy, Clone, Debug, HashStable_Generic)] diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5be122ffbde..c65d933bd6d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -995,18 +995,18 @@ pub fn is_rust_2015(&self) -> bool { } /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.edition().rust_2018() + pub fn at_least_rust_2018(&self) -> bool { + self.edition().at_least_rust_2018() } /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(&self) -> bool { - self.edition().rust_2021() + pub fn at_least_rust_2021(&self) -> bool { + self.edition().at_least_rust_2021() } /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(&self) -> bool { - self.edition().rust_2024() + pub fn at_least_rust_2024(&self) -> bool { + self.edition().at_least_rust_2024() } /// Returns `true` if we should use the PLT for shared library calls. diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index f16db69aae2..608b8c24bde 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -82,17 +82,17 @@ pub fn is_rust_2015(self) -> bool { } /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(self) -> bool { + pub fn at_least_rust_2018(self) -> bool { self >= Edition::Edition2018 } /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(self) -> bool { + pub fn at_least_rust_2021(self) -> bool { self >= Edition::Edition2021 } /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(self) -> bool { + pub fn at_least_rust_2024(self) -> bool { self >= Edition::Edition2024 } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5c56337d1e0..ecaa82874a3 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -707,24 +707,28 @@ pub fn edition(self) -> edition::Edition { self.ctxt().edition() } + /// Is this edition 2015? #[inline] pub fn is_rust_2015(self) -> bool { self.edition().is_rust_2015() } + /// Are we allowed to use features from the Rust 2018 edition? #[inline] - pub fn rust_2018(self) -> bool { - self.edition().rust_2018() + pub fn at_least_rust_2018(self) -> bool { + self.edition().at_least_rust_2018() } + /// Are we allowed to use features from the Rust 2021 edition? #[inline] - pub fn rust_2021(self) -> bool { - self.edition().rust_2021() + pub fn at_least_rust_2021(self) -> bool { + self.edition().at_least_rust_2021() } + /// Are we allowed to use features from the Rust 2024 edition? #[inline] - pub fn rust_2024(self) -> bool { - self.edition().rust_2024() + pub fn at_least_rust_2024(self) -> bool { + self.edition().at_least_rust_2024() } /// Returns the source callee. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2d00c53951f..d14953f1bb7 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -90,6 +90,19 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< } v })); + items.extend(doc.inlined_foreigns.iter().flat_map(|((_, renamed), (res, local_import_id))| { + let Some(def_id) = res.opt_def_id() else { return Vec::new() }; + let name = renamed.unwrap_or_else(|| cx.tcx.item_name(def_id)); + let import = cx.tcx.hir().expect_item(*local_import_id); + match import.kind { + hir::ItemKind::Use(path, kind) => { + let hir::UsePath { segments, span, .. } = *path; + let path = hir::Path { segments, res: *res, span }; + clean_use_statement_inner(import, name, &path, kind, cx, &mut Default::default()) + } + _ => unreachable!(), + } + })); items.extend(doc.items.values().flat_map(|(item, renamed, _)| { // Now we actually lower the imports, skipping everything else. if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { @@ -2652,9 +2665,6 @@ fn clean_use_statement<'tcx>( let mut items = Vec::new(); let hir::UsePath { segments, ref res, span } = *path; for &res in res { - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { - continue; - } let path = hir::Path { segments, res, span }; items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); } @@ -2669,6 +2679,9 @@ fn clean_use_statement_inner<'tcx>( cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Vec { + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res { + return Vec::new(); + } // We need this comparison because some imports (for std types for example) // are "inserted" as well but directly by the compiler and they should not be // taken into account. diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 265123ddf6c..66737a01521 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -35,6 +35,8 @@ pub(crate) struct Module<'hir> { (LocalDefId, Option), (&'hir hir::Item<'hir>, Option, Option), >, + /// Same as for `items`. + pub(crate) inlined_foreigns: FxIndexMap<(DefId, Option), (Res, LocalDefId)>, pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option)>, } @@ -54,6 +56,7 @@ pub(crate) fn new( import_id, mods: Vec::new(), items: FxIndexMap::default(), + inlined_foreigns: FxIndexMap::default(), foreigns: Vec::new(), } } @@ -272,21 +275,30 @@ fn maybe_inline_local( return false; } - // For cross-crate impl inlining we need to know whether items are - // reachable in documentation -- a previously unreachable item can be - // made reachable by cross-crate inlining which we're checking here. - // (this is done here because we need to know this upfront). - if !ori_res_did.is_local() && !is_no_inline { - crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did); - return false; - } - + let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did); let Some(res_did) = ori_res_did.as_local() else { - return false; + // For cross-crate impl inlining we need to know whether items are + // reachable in documentation -- a previously unreachable item can be + // made reachable by cross-crate inlining which we're checking here. + // (this is done here because we need to know this upfront). + crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did); + if is_hidden { + return false; + } + // We store inlined foreign items otherwise, it'd mean that the `use` item would be kept + // around. It's not a problem unless this `use` imports both a local AND a foreign item. + // If a local item is inlined, its `use` is not supposed to still be around in `clean`, + // which would make appear the `use` in the generated documentation like the local item + // was not inlined even though it actually was. + self.modules + .last_mut() + .unwrap() + .inlined_foreigns + .insert((ori_res_did, renamed), (res, def_id)); + return true; }; let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did); - let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did); let item = tcx.hir().get_by_def_id(res_did); if !please_inline { @@ -314,7 +326,7 @@ fn maybe_inline_local( return false; } - let inlined = match tcx.hir().get_by_def_id(res_did) { + let inlined = match item { // Bang macros are handled a bit on their because of how they are handled by the // compiler. If they have `#[doc(hidden)]` and the re-export doesn't have // `#[doc(inline)]`, then we don't inline it. @@ -346,7 +358,7 @@ fn maybe_inline_local( }; self.view_item_stack.remove(&res_did); if inlined { - self.cx.cache.inlined_items.insert(res_did.to_def_id()); + self.cx.cache.inlined_items.insert(ori_res_did); } inlined } @@ -483,7 +495,6 @@ fn visit_item_inner( continue; } } - self.add_to_current_mod(item, renamed, import_id); } } diff --git a/tests/codegen/no_builtins-at-crate.rs b/tests/codegen/no_builtins-at-crate.rs new file mode 100644 index 00000000000..02ed670900e --- /dev/null +++ b/tests/codegen/no_builtins-at-crate.rs @@ -0,0 +1,24 @@ +// compile-flags: -C opt-level=1 + +#![no_builtins] +#![crate_type = "lib"] + +// CHECK: define +// CHECK-SAME: @__aeabi_memcpy +// CHECK-SAME: #0 +#[no_mangle] +pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usize) { + // CHECK: call + // CHECK-SAME: @memcpy( + memcpy(dest, src, size); +} + +// CHECK: declare +// CHECK-SAME: @memcpy +// CHECK-SAME: #0 +extern "C" { + pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; +} + +// CHECK: attributes #0 +// CHECK-SAME: "no-builtins" diff --git a/tests/run-make/no-builtins-attribute/Makefile b/tests/run-make/no-builtins-attribute/Makefile new file mode 100644 index 00000000000..0ce95facaea --- /dev/null +++ b/tests/run-make/no-builtins-attribute/Makefile @@ -0,0 +1,9 @@ +include ../tools.mk + +# We want to check if `no-builtins` is also added to the function declarations in the used crate. + +all: + $(RUSTC) no_builtins.rs --emit=link + $(RUSTC) main.rs --emit=llvm-ir + + cat "$(TMPDIR)"/main.ll | "$(LLVM_FILECHECK)" filecheck.main.txt diff --git a/tests/run-make/no-builtins-attribute/filecheck.main.txt b/tests/run-make/no-builtins-attribute/filecheck.main.txt new file mode 100644 index 00000000000..ecd650bdca8 --- /dev/null +++ b/tests/run-make/no-builtins-attribute/filecheck.main.txt @@ -0,0 +1,5 @@ +CHECK: declare void @foo() +CHECK-SAME: #[[ATTR_3:[0-9]+]] + +CHECK: attributes #[[ATTR_3]] +CHECK-SAME: no-builtins diff --git a/tests/run-make/no-builtins-attribute/main.rs b/tests/run-make/no-builtins-attribute/main.rs new file mode 100644 index 00000000000..77754b37e31 --- /dev/null +++ b/tests/run-make/no-builtins-attribute/main.rs @@ -0,0 +1,10 @@ +extern crate no_builtins; + +#[no_mangle] +fn call_foo() { + no_builtins::foo(); +} + +fn main() { + call_foo(); +} diff --git a/tests/run-make/no-builtins-attribute/no_builtins.rs b/tests/run-make/no-builtins-attribute/no_builtins.rs new file mode 100644 index 00000000000..8ca862d2f77 --- /dev/null +++ b/tests/run-make/no-builtins-attribute/no_builtins.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] +#![no_builtins] + +#[no_mangle] +pub fn foo() {} diff --git a/tests/rustdoc/issue-105735-overlapping-reexport-2.rs b/tests/rustdoc/issue-105735-overlapping-reexport-2.rs new file mode 100644 index 00000000000..91082483948 --- /dev/null +++ b/tests/rustdoc/issue-105735-overlapping-reexport-2.rs @@ -0,0 +1,25 @@ +// Regression test to ensure that both `AtomicU8` items are displayed but not the re-export. + +#![crate_name = "foo"] +#![no_std] + +// @has 'foo/index.html' +// @has - '//*[@class="item-name"]/a[@class="type"]' 'AtomicU8' +// @has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8' +// We also ensure we don't have another item displayed. +// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 2 +// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Type Definitions' +// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Constants' + +mod other { + pub type AtomicU8 = (); +} + +mod thing { + pub use crate::other::AtomicU8; + + #[allow(non_upper_case_globals)] + pub const AtomicU8: () = (); +} + +pub use crate::thing::AtomicU8; diff --git a/tests/rustdoc/issue-105735-overlapping-reexport.rs b/tests/rustdoc/issue-105735-overlapping-reexport.rs new file mode 100644 index 00000000000..50f2450b90a --- /dev/null +++ b/tests/rustdoc/issue-105735-overlapping-reexport.rs @@ -0,0 +1,21 @@ +// Regression test to ensure that both `AtomicU8` items are displayed but not the re-export. + +#![crate_name = "foo"] +#![no_std] + +// @has 'foo/index.html' +// @has - '//*[@class="item-name"]/a[@class="struct"]' 'AtomicU8' +// @has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8' +// We also ensure we don't have another item displayed. +// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 2 +// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs' +// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Constants' + +mod thing { + pub use core::sync::atomic::AtomicU8; + + #[allow(non_upper_case_globals)] + pub const AtomicU8: () = (); +} + +pub use crate::thing::AtomicU8; diff --git a/tests/ui/fmt/closing-brace-as-fill.rs b/tests/ui/fmt/closing-brace-as-fill.rs new file mode 100644 index 00000000000..6ad257f943e --- /dev/null +++ b/tests/ui/fmt/closing-brace-as-fill.rs @@ -0,0 +1,8 @@ +// issue: 112732 + +// `}` is typoed since it is interpreted as a fill character rather than a closing bracket + +fn main() { + println!("Hello, world! {0:}<3", 2); + //~^ ERROR invalid format string: expected `'}'` but string was terminated +} diff --git a/tests/ui/fmt/closing-brace-as-fill.stderr b/tests/ui/fmt/closing-brace-as-fill.stderr new file mode 100644 index 00000000000..aa1e5aff652 --- /dev/null +++ b/tests/ui/fmt/closing-brace-as-fill.stderr @@ -0,0 +1,12 @@ +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/closing-brace-as-fill.rs:6:35 + | +LL | println!("Hello, world! {0:}<3", 2); + | - ^ expected `'}'` in format string + | | + | this is not interpreted as a formatting closing brace + | + = note: the character `'}'` is interpreted as a fill character because of the `:` that precedes it + +error: aborting due to previous error + diff --git a/tests/ui/fmt/format-string-error-2.stderr b/tests/ui/fmt/format-string-error-2.stderr index 76cdfbb93bf..dfd24bf60ad 100644 --- a/tests/ui/fmt/format-string-error-2.stderr +++ b/tests/ui/fmt/format-string-error-2.stderr @@ -10,7 +10,7 @@ error: invalid format string: expected `'}'`, found `'a'` LL | format!("{ | - because of this opening brace LL | a"); - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -21,7 +21,7 @@ LL | format!("{ \ | - because of this opening brace LL | \ LL | b"); - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -29,7 +29,7 @@ error: invalid format string: expected `'}'`, found `'\'` --> $DIR/format-string-error-2.rs:11:18 | LL | format!(r#"{ \ - | - ^ expected `}` in format string + | - ^ expected `'}'` in format string | | | because of this opening brace | @@ -39,7 +39,7 @@ error: invalid format string: expected `'}'`, found `'\'` --> $DIR/format-string-error-2.rs:15:18 | LL | format!(r#"{ \n - | - ^ expected `}` in format string + | - ^ expected `'}'` in format string | | | because of this opening brace | @@ -52,7 +52,7 @@ LL | format!("{ \n | - because of this opening brace LL | \n LL | e"); - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -62,7 +62,7 @@ error: invalid format string: expected `'}'`, found `'a'` LL | { | - because of this opening brace LL | a"); - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -72,7 +72,7 @@ error: invalid format string: expected `'}'`, found `'a'` LL | { | - because of this opening brace LL | a - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -83,7 +83,7 @@ LL | { \ | - because of this opening brace LL | \ LL | b"); - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -94,7 +94,7 @@ LL | { \ | - because of this opening brace LL | \ LL | b \ - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -102,7 +102,7 @@ error: invalid format string: expected `'}'`, found `'\'` --> $DIR/format-string-error-2.rs:45:8 | LL | raw { \ - | - ^ expected `}` in format string + | - ^ expected `'}'` in format string | | | because of this opening brace | @@ -112,7 +112,7 @@ error: invalid format string: expected `'}'`, found `'\'` --> $DIR/format-string-error-2.rs:50:8 | LL | raw { \n - | - ^ expected `}` in format string + | - ^ expected `'}'` in format string | | | because of this opening brace | @@ -125,7 +125,7 @@ LL | { \n | - because of this opening brace LL | \n LL | e"); - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` @@ -135,7 +135,7 @@ error: invalid format string: expected `'}'`, found `'a'` LL | { | - because of this opening brace LL | asdf} - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` diff --git a/tests/ui/fmt/format-string-error.stderr b/tests/ui/fmt/format-string-error.stderr index 8a32c225485..37a181e6fcb 100644 --- a/tests/ui/fmt/format-string-error.stderr +++ b/tests/ui/fmt/format-string-error.stderr @@ -62,7 +62,7 @@ error: invalid format string: expected `'}'`, found `'\'` --> $DIR/format-string-error.rs:19:23 | LL | let _ = format!("{\}"); - | -^ expected `}` in format string + | -^ expected `'}'` in format string | | | because of this opening brace | diff --git a/tests/ui/fmt/format-string-wrong-order.stderr b/tests/ui/fmt/format-string-wrong-order.stderr index 461af354a4e..0a2e04026d9 100644 --- a/tests/ui/fmt/format-string-wrong-order.stderr +++ b/tests/ui/fmt/format-string-wrong-order.stderr @@ -26,7 +26,7 @@ error: invalid format string: expected `'}'`, found `'?'` --> $DIR/format-string-wrong-order.rs:9:15 | LL | format!("{??}", bar); - | -^ expected `}` in format string + | -^ expected `'}'` in format string | | | because of this opening brace | @@ -36,7 +36,7 @@ error: invalid format string: expected `'}'`, found `'?'` --> $DIR/format-string-wrong-order.rs:11:15 | LL | format!("{?;bar}"); - | -^ expected `}` in format string + | -^ expected `'}'` in format string | | | because of this opening brace | diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr index ed008c454a3..09ce3dca411 100644 --- a/tests/ui/fmt/ifmt-bad-arg.stderr +++ b/tests/ui/fmt/ifmt-bad-arg.stderr @@ -178,7 +178,7 @@ error: invalid format string: expected `'}'`, found `'t'` LL | ninth number: { | - because of this opening brace LL | tenth number: {}", - | ^ expected `}` in format string + | ^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` diff --git a/tests/ui/inline-const/interpolated.rs b/tests/ui/inline-const/interpolated.rs new file mode 100644 index 00000000000..3fcc621c946 --- /dev/null +++ b/tests/ui/inline-const/interpolated.rs @@ -0,0 +1,32 @@ +// check-pass + +#![feature(inline_const)] + +// This used to be unsupported since the parser first tries to check if we have +// any nested items, and then checks for statements (and expressions). The heuristic +// that we were using to detect the beginning of a const item was incorrect, so +// this used to fail. +macro_rules! m { + ($b:block) => { + fn foo() { + const $b + } + } +} + +// This has worked since inline-consts were implemented, since the position that +// the const block is located at doesn't support nested items (e.g. because +// `let x = const X: u32 = 1;` is invalid), so there's no ambiguity parsing the +// inline const. +macro_rules! m2 { + ($b:block) => { + fn foo2() { + let _ = const $b; + } + } +} + +m!({}); +m2!({}); + +fn main() {} diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr index 51010840548..dfd24566953 100644 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr +++ b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr @@ -2,7 +2,7 @@ error: multiple declarations of external function `f` from library `foo.dll` hav --> $DIR/multiple-declarations.rs:13:9 | LL | fn f(x: i32); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: aborting due to previous error diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr index f8265ae6919..f69275a0125 100644 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr +++ b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr @@ -2,7 +2,7 @@ error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture --> $DIR/unsupported-abi.rs:6:5 | LL | fn f(x: i32); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: aborting due to previous error diff --git a/tests/ui/traits/new-solver/alias-bound-preference.rs b/tests/ui/traits/new-solver/alias-bound-preference.rs new file mode 100644 index 00000000000..e4e0f634ef7 --- /dev/null +++ b/tests/ui/traits/new-solver/alias-bound-preference.rs @@ -0,0 +1,39 @@ +// revisions: old next +//[next] compile-flags: -Ztrait-solver=next +// run-pass + +// A test for https://github.com/rust-lang/trait-system-refactor-initiative/issues/45. + +trait Trait { + type Assoc: Into; +} +impl> Trait for T { + type Assoc = T; +} +fn prefer_alias_bound_projection(x: T::Assoc) { + // There are two possible types for `x`: + // - `u32` by using the "alias bound" of `::Assoc` + // - `::Assoc`, i.e. `u16`, by using `impl From for T` + // + // We infer the type of `x` to be `u32` here as it is highly likely + // that this is expected by the user. + let x = x.into(); + assert_eq!(std::mem::size_of_val(&x), 4); +} + +fn impl_trait() -> impl Into { + 0u16 +} + +fn main() { + // There are two possible types for `x`: + // - `u32` by using the "alias bound" of `impl Into` + // - `impl Into`, i.e. `u16`, by using `impl From for T` + // + // We infer the type of `x` to be `u32` here as it is highly likely + // that this is expected by the user. + let x = impl_trait().into(); + assert_eq!(std::mem::size_of_val(&x), 4); + + prefer_alias_bound_projection::(1); +}