diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 25e2ca5948bfb..a52d2e945536e 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -697,6 +697,13 @@ # FIXME(#61117): Some tests fail when this option is enabled. #rust.debuginfo-level-tests = 0 +# Compress debuginfo of Rust and C/C++ code. +# Valid options: +# - "off" or false: disable compression +# - true: compress debuginfo with the default compression method (currently zlib) +# - "zlib": compress debuginfo with zlib +#rust.compress-debuginfo = "off" + # Should rustc and the standard library be built with split debuginfo? Default # is platform dependent. # @@ -1019,6 +1026,13 @@ # and enabling it causes issues. #split-debuginfo = if linux || windows-gnu { off } else if windows-msvc { packed } else if apple { unpacked } +# Compress debuginfo for Rust and C/C++ code of this target. +# Valid options: +# - "off" or false: disable compression +# - true: compress debuginfo with the default compression method (currently zlib) +# - "zlib": compress debuginfo with zlib +#compress-debuginfo = "off" + # Path to the `llvm-config` binary of the installation of a custom LLVM to link # against. Note that if this is specified we don't compile LLVM at all for this # target. diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 078ebd6987ae3..7021ac8cfedaf 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -55,7 +55,9 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::kw; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; -use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; +use crate::delegation::generics::{ + GenericsGenerationResult, GenericsGenerationResults, GenericsPosition, +}; use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, @@ -505,6 +507,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res: Res::Local(param_id), args: None, infer_args: false, + delegation_child_segment: false, })); let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments }); @@ -714,6 +717,8 @@ impl<'hir> LoweringContext<'_, 'hir> { result.args_segment_id = segment.hir_id; result.use_for_sig_inheritance = !result.generics.is_trait_impl(); + segment.delegation_child_segment = result.generics.pos() == GenericsPosition::Child; + segment } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 79a3961c22a53..2a751ffae3eb7 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -12,7 +12,7 @@ use rustc_span::{Ident, Span, sym}; use crate::LoweringContext; use crate::diagnostics::DelegationInfersMismatch; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(super) enum GenericsPosition { Parent, Child, @@ -155,7 +155,7 @@ impl<'hir> DelegationGenericArgsIterator<'hir> { impl<'hir> HirOrTyGenerics<'hir> { pub(super) fn into_hir_generics(&mut self, ctx: &mut LoweringContext<'_, 'hir>, span: Span) { if let HirOrTyGenerics::Ty(ty) = self { - let rename_self = matches!(ty.pos, GenericsPosition::Child); + let rename_self = ty.pos == GenericsPosition::Child; let params = ctx.uplift_delegation_generic_params(span, &ty.data, rename_self); *self = HirOrTyGenerics::Hir(DelegationGenerics { @@ -218,6 +218,13 @@ impl<'hir> HirOrTyGenerics<'hir> { .expect("`Self` generic param is not found while expected"), } } + + pub(crate) fn pos(&self) -> GenericsPosition { + match self { + HirOrTyGenerics::Ty(ty) => ty.pos, + HirOrTyGenerics::Hir(hir) => hir.pos, + } + } } impl<'hir> GenericsGenerationResult<'hir> { @@ -590,6 +597,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: p.name.ident(), infer_args: false, res, + delegation_child_segment: false, }]), res, span: p.span, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 06e83a7486100..35e16ce78a8a8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1014,6 +1014,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res, args, infer_args: args.is_none(), + delegation_child_segment: false, }]), }) } @@ -2791,7 +2792,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::ConstBlock(anon_const) => { let def_id = self.local_def_id(anon_const.id); - assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id)); + assert_eq!(DefKind::InlineConst, self.tcx.def_kind(def_id)); self.lower_anon_const_to_const_arg(anon_const, span) } _ => overly_complex_const(self), diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 42c4af5add12d..18e037527836a 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -412,6 +412,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { Some(generic_args.into_generic_args(self)) }, + delegation_child_segment: false, } } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index e3865345cae84..f48fbe6324834 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -8,9 +8,9 @@ use rustc_span::edition::Edition::Edition2024; use super::prelude::*; use crate::attributes::AttributeSafety; use crate::session_diagnostics::{ - EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, - NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, - SanitizeInvalidStatic, TargetFeatureOnLangItem, + EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport, + NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral, + ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -795,7 +795,8 @@ pub(crate) struct PatchableFunctionEntryParser; impl SingleAttributeParser for PatchableFunctionEntryParser { const PATH: &[Symbol] = &[sym::patchable_function_entry]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); - const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]); + const TEMPLATE: AttributeTemplate = + template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]); const STABILITY: AttributeStability = unstable!(patchable_function_entry); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -803,74 +804,85 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let mut prefix = None; let mut entry = None; + let mut section = None; if meta_item_list.len() == 0 { cx.adcx().expected_at_least_one_argument(meta_item_list.span); return None; } - let mut errored = false; - for item in meta_item_list.mixed() { let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { - continue; + return None; }; let attrib_to_write = match ident.name { sym::prefix_nops => { // Duplicate prefixes are not allowed if prefix.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::prefix_nops); - continue; + return None; } &mut prefix } sym::entry_nops => { // Duplicate entries are not allowed if entry.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::entry_nops); - continue; + return None; } &mut entry } + sym::section => { + // Duplicate entries are not allowed + if section.is_some() { + cx.adcx().duplicate_key(ident.span, sym::section); + return None; + } + // Only a string type value is allowed. + let Some(value_str) = value.value_as_str() else { + cx.adcx().expect_string_literal(value); + return None; + }; + // The section name does not allow null characters. + if value_str.as_str().contains('\0') { + cx.emit_err(NullOnSection { span: value.value_span }); + } + // The section name is not allowed to be empty, LLVM does + // not allow them. + if value_str.is_empty() { + cx.emit_err(EmptySection { span: value.value_span }); + } + section = Some(value_str); + // Integer parsing is not needed, process next item. + continue; + } _ => { - errored = true; cx.adcx().expected_specific_argument( ident.span, &[sym::prefix_nops, sym::entry_nops], ); - continue; + return None; } }; let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else { - errored = true; cx.adcx().expected_integer_literal(value.value_span); - continue; + return None; }; let Ok(val) = val.get().try_into() else { - errored = true; cx.adcx().expected_integer_literal_in_range( value.value_span, u8::MIN as isize, u8::MAX as isize, ); - continue; + return None; }; *attrib_to_write = Some(val); } - if errored { - None - } else { - Some(AttributeKind::PatchableFunctionEntry { - prefix: prefix.unwrap_or(0), - entry: entry.unwrap_or(0), - }) - } + Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section }) } } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 63e33e0ae4882..9e5d7bb2cf375 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -292,6 +292,13 @@ pub(crate) struct EmptyExportName { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not be empty")] +pub(crate) struct EmptySection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`export_name` may not contain null characters", code = E0648)] pub(crate) struct NullOnExport { @@ -327,6 +334,13 @@ pub(crate) struct NullOnObjcSelector { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not contain null characters", code = E0648)] +pub(crate) struct NullOnSection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`objc::class!` expected a string literal")] pub(crate) struct ObjcClassExpectedStringLiteral { diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 804d2757cf998..b0adbcd6db4ef 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -615,7 +615,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { match tcx.def_kind(self.mir_def) { - DefKind::InlineConst => { + DefKind::InlineConst if !tcx.is_type_system_inline_const(self.mir_def) => { // This is required for `AscribeUserType` canonical query, which will call // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes // into borrowck, which is ICE #78174. diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 9f032fa588195..e2c62e60ecced 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -107,41 +107,42 @@ impl CfgEval<'_> { // our attribute target will correctly configure the tokens as well. let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None); parser.capture_cfg = true; - let res: PResult<'_, Annotatable> = try { - match annotatable { - Annotatable::Item(_) => { - let item = - parser.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?.unwrap(); - Annotatable::Item(self.flat_map_item(item).pop().unwrap()) - } + let res: PResult<'_, Option> = try { + match &annotatable { + Annotatable::Item(_) => parser + .parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)? + .and_then(|item| self.flat_map_item(item).pop().map(Annotatable::Item)), Annotatable::AssocItem(_, ctxt) => { - let item = parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(); - Annotatable::AssocItem( - self.flat_map_assoc_item(item, ctxt).pop().unwrap(), - ctxt, - ) + parser.parse_trait_item(ForceCollect::Yes)?.flatten().and_then(|item| { + self.flat_map_assoc_item(item, *ctxt) + .pop() + .map(|item| Annotatable::AssocItem(item, *ctxt)) + }) } Annotatable::ForeignItem(_) => { - let item = parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(); - Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) - } - Annotatable::Stmt(_) => { - let stmt = parser - .parse_stmt_without_recovery(false, ForceCollect::Yes, false)? - .unwrap(); - Annotatable::Stmt(Box::new(self.flat_map_stmt(stmt).pop().unwrap())) + parser.parse_foreign_item(ForceCollect::Yes)?.flatten().and_then(|item| { + self.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem) + }) } + Annotatable::Stmt(_) => parser + .parse_stmt_without_recovery(false, ForceCollect::Yes, false)? + .and_then(|stmt| { + self.flat_map_stmt(stmt).pop().map(|stmt| Annotatable::Stmt(Box::new(stmt))) + }), Annotatable::Expr(_) => { let mut expr = parser.parse_expr_force_collect()?; self.visit_expr(&mut expr); - Annotatable::Expr(expr) + Some(Annotatable::Expr(expr)) } _ => unreachable!(), } }; match res { - Ok(ann) => ann, + Ok(Some(ann)) => ann, + // Parser recovery may emit errors without reconstructing an annotatable. + // Keep the original node so cfg-eval stays best-effort instead of ICEing. + Ok(None) => annotatable, Err(err) => { err.emit(); annotatable diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index fe36a9865485d..20e712174e0e9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -86,11 +86,26 @@ fn patchable_function_entry_attrs<'ll>( attr: Option, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); - let patchable_spec = attr.unwrap_or_else(|| { - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry) - }); - let entry = patchable_spec.entry(); - let prefix = patchable_spec.prefix(); + + let mut entry = sess.opts.unstable_opts.patchable_function_entry.entry(); + let mut prefix = sess.opts.unstable_opts.patchable_function_entry.prefix(); + let mut section = sess.opts.unstable_opts.patchable_function_entry.section(); + let section_sym; + + // Apply attribute specified overrides, if any. + if let Some(patchable_spec) = attr { + if let Some(sym) = patchable_spec.section() { + section_sym = sym; + section = Some(section_sym.as_str()); + } + // Override the nop counts if either is present. If only one is present, the + // other count is implied to be 0. + if patchable_spec.entry().is_some() || patchable_spec.prefix().is_some() { + entry = patchable_spec.entry().unwrap_or(0); + prefix = patchable_spec.prefix().unwrap_or(0); + } + } + if entry > 0 { attrs.push(llvm::CreateAttrStringValue( cx.llcx, @@ -105,6 +120,13 @@ fn patchable_function_entry_attrs<'ll>( &format!("{}", prefix), )); } + if let Some(section) = section { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "patchable-function-entry-section", + section, + )); + } attrs } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 6198a98e5f7ae..80f13a7a35255 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -14,7 +14,6 @@ use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::PatchableFunctionEntry; use rustc_middle::mono::CodegenUnit; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -343,14 +342,13 @@ pub(crate) unsafe fn create_module<'ll>( // Add "kcfi-offset" module flag with -Z patchable-function-entry (See // https://reviews.llvm.org/D141172). - let pfe = - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry); - if pfe.prefix() > 0 { + let patchable_prefix_nops = sess.opts.unstable_opts.patchable_function_entry.prefix(); + if patchable_prefix_nops > 0 { llvm::add_module_flag_u32( llmod, llvm::ModuleFlagMergeBehavior::Override, "kcfi-offset", - pfe.prefix().into(), + patchable_prefix_nops.into(), ); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4d271447746c1..46bb1182c298c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -290,9 +290,11 @@ fn process_builtin_attrs( AttributeKind::RustcOffloadKernel => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL } - AttributeKind::PatchableFunctionEntry { prefix, entry } => { + AttributeKind::PatchableFunctionEntry { prefix, entry, section } => { codegen_fn_attrs.patchable_function_entry = - Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + Some(PatchableFunctionEntry::from_prefix_entry_and_section( + *prefix, *entry, *section, + )); } AttributeKind::InstrumentFn(instrument_fn) => { codegen_fn_attrs.instrument_fn = match instrument_fn { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 41e550b8d6a3a..65af690611919 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -490,9 +490,11 @@ impl server::Server for Rustc<'_, '_> { fn literal_from_str(&mut self, s: &str) -> Result, String> { let name = FileName::proc_macro_source_code(s); - let mut parser = + let mut parser = rustc_errors::catch_fatal_errors(|| { new_parser_from_source_str(self.psess(), name, s.to_owned(), StripTokens::Nothing) - .map_err(cancel_diags_into_string)?; + }) + .map_err(|_| String::from("failed to parse to literal"))? + .map_err(cancel_diags_into_string)?; let first_span = parser.token.span.data(); let minus_present = parser.eat(exp!(Minus)); @@ -569,12 +571,15 @@ impl server::Server for Rustc<'_, '_> { } fn ts_from_str(&mut self, src: &str) -> Result { - source_str_to_stream( - self.psess(), - FileName::proc_macro_source_code(src), - src.to_string(), - Some(self.call_site), - ) + rustc_errors::catch_fatal_errors(|| { + source_str_to_stream( + self.psess(), + FileName::proc_macro_source_code(src), + src.to_string(), + Some(self.call_site), + ) + }) + .map_err(|_| String::from("failed to parse to tokenstream"))? .map_err(cancel_diags_into_string) } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index acae158acb2b4..17d00863d99d5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1271,8 +1271,9 @@ pub enum AttributeKind { /// Represents `#[patchable_function_entry]` PatchableFunctionEntry { - prefix: u8, - entry: u8, + prefix: Option, + entry: Option, + section: Option, }, /// Represents `#[path]` diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index e473db48b04c2..d0300d63d1213 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -448,43 +448,6 @@ impl DefKind { | DefKind::ExternCrate => false, } } - - /// Returns `true` if `self` is a kind of definition that does not have its own - /// type-checking context, i.e. closure, coroutine or inline const. - #[inline] - pub fn is_typeck_child(self) -> bool { - match self { - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => true, - DefKind::Mod - | DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::Fn - | DefKind::Const { .. } - | DefKind::ConstParam - | DefKind::Static { .. } - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::AssocConst { .. } - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Impl { .. } => false, - } - } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6d2e68d20bf30..254d5fdd5f80c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -387,12 +387,24 @@ pub struct PathSegment<'hir> { /// out of those only the segments with no type parameters /// to begin with, e.g., `Vec::new` is `>::new::<..>`. pub infer_args: bool, + + /// Whether this segment is a delegation's child segment: + /// `reuse Trait::foo`, in this case `foo` is a delegation's child segment. + /// Used for faster check during generic args lowering. + pub delegation_child_segment: bool, } impl<'hir> PathSegment<'hir> { /// Converts an identifier to the corresponding segment. pub fn new(ident: Ident, hir_id: HirId, res: Res) -> PathSegment<'hir> { - PathSegment { ident, hir_id, res, infer_args: true, args: None } + PathSegment { + ident, + hir_id, + res, + infer_args: true, + args: None, + delegation_child_segment: false, + } } pub fn invalid() -> Self { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9e0eaef596420..f1f542e9ce1e9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1468,7 +1468,8 @@ pub fn walk_path_segment<'v, V: Visitor<'v>>( visitor: &mut V, segment: &'v PathSegment<'v>, ) -> V::Result { - let PathSegment { ident, hir_id, res: _, args, infer_args: _ } = segment; + let PathSegment { ident, hir_id, res: _, args, infer_args: _, delegation_child_segment: _ } = + segment; try_visit!(visitor.visit_ident(*ident)); try_visit!(visitor.visit_id(*hir_id)); visit_opt!(visitor, visit_generic_args, *args); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 275fed00a4f8a..05648bf7c2ece 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,7 +15,7 @@ //! crate as a kind of pass. This should eventually be factored away. use std::cell::Cell; -use std::{assert_matches, iter}; +use std::{assert_matches, debug_assert_matches, iter}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; @@ -1635,10 +1635,12 @@ fn const_param_default<'tcx>( } fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKind { + debug_assert_matches!(tcx.def_kind(def), DefKind::AnonConst | DefKind::InlineConst); let hir_id = tcx.local_def_id_to_hir_id(def); let const_arg_id = tcx.parent_hir_id(hir_id); match tcx.hir_node(const_arg_id) { - hir::Node::ConstArg(_) => { + hir::Node::ConstArg(const_arg) => { + debug_assert_matches!(const_arg.kind, hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) if *def_id == def); let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id)); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 810e14a7d6d0b..d730683b132d4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -467,6 +467,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 32678e8a5a12f..9d22d3060e1ff 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -471,6 +471,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index a36bb7bce9bab..45c2ed205c74d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -434,7 +434,7 @@ pub(crate) fn check_generic_arg_count( // Suppress this warning for delegations as it is compiler generated and lifetimes are // propagated while late-bound lifetimes may be present. - let explicit_late_bound = match tcx.hir_is_delegation_child_segment(seg) { + let explicit_late_bound = match seg.delegation_child_segment { true => ExplicitLateBound::No, false => prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos), }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index e8a7156b41fe4..b29e469348f81 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -483,7 +483,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let parent_def_id = self.item_def_id(); if let Res::Def(DefKind::ConstParam, _) = res - && tcx.def_kind(parent_def_id) == DefKind::AnonConst + && matches!(tcx.def_kind(parent_def_id), DefKind::AnonConst | DefKind::InlineConst) && let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id) { let folder = ForbidParamUsesFolder { @@ -512,15 +512,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Inline consts and closures can be nested inside anon consts that forbid generic // params (e.g. an enum discriminant). Walk up the def parent chain to find the // nearest enclosing AnonConst and use that to determine the context. + let parent_def_id = tcx.typeck_root_def_id(parent_def_id.into()); + let anon_const_def_id = match tcx.def_kind(parent_def_id) { DefKind::AnonConst => parent_def_id, - DefKind::InlineConst | DefKind::Closure => { - let root = tcx.typeck_root_def_id(parent_def_id.into()); - match tcx.def_kind(root) { - DefKind::AnonConst => root.expect_local(), - _ => return None, - } - } + DefKind::InlineConst if tcx.is_type_system_inline_const(parent_def_id) => parent_def_id, _ => return None, }; @@ -870,7 +866,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, generic_args: segment.args(), infer_args: segment.infer_args, - create_synth_args: tcx.hir_is_delegation_child_segment(segment), + create_synth_args: segment.delegation_child_segment, incorrect_args: &arg_count.correct, }; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c5a6ce75c0e41..ff00e18783706 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -186,9 +186,13 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } _ => (), } - // Skip `AnonConst`s because we feed their `type_of`. + // Skip `AnonConst`s and type system `InlineConst`s because we feed their `type_of` in + // `feed_anon_const_type`. // Also skip items for which typeck forwards to parent typeck. - if !(matches!(def_kind, DefKind::AnonConst) || def_kind.is_typeck_child()) { + if !(def_kind == DefKind::AnonConst + || def_kind == DefKind::InlineConst && tcx.is_type_system_inline_const(item_def_id) + || tcx.is_typeck_child(item_def_id.to_def_id())) + { tcx.ensure_ok().typeck(item_def_id); } // Ensure we generate the new `DefId` before finishing `check_crate`. diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 859e3463cb79f..bb822f8d0f68d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -9,11 +9,11 @@ use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::{self, FnSig, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; @@ -1186,11 +1186,16 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { ); } None => { - span_bug!( - self.call_expr.span, - "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", - self.closure_ty - ) + let guar = fcx.tainted_by_errors().unwrap_or_else(|| { + fcx.dcx().span_delayed_bug( + self.call_expr.span, + format!( + "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", + self.closure_ty + ), + ) + }); + fcx.write_resolution(self.call_expr.hir_id, Err(guar)); } } } diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index 21aad64f58d38..ebd8842bd753f 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -84,6 +84,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() }; let cx = match tcx.def_kind(def_id) { DefKind::AnonConst => AnonConst, + DefKind::InlineConst => { + // only type system inline consts are typeck roots + debug_assert!(tcx.is_type_system_inline_const(def_id)); + ConstBlock + } _ => Fn, }; check.with_context(cx, |v| v.visit_body(body)); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5933d0fd4b356..a3264d3cc3311 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -866,7 +866,7 @@ fn test_unstable_options_tracking_hash() { tracked!(panic_in_drop, PanicStrategy::Abort); tracked!( patchable_function_entry, - PatchableFunctionEntry::from_total_and_prefix_nops(10, 5) + PatchableFunctionEntry::from_parts(10, 5, None) .expect("total must be greater than or equal to prefix") ); tracked!(plt, Some(true)); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c232f595d4229..0f63c8469953d 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1624,7 +1624,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } - if let DefKind::AnonConst = def_kind { + if let DefKind::AnonConst | DefKind::InlineConst = def_kind { record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if should_encode_const_of_item(self.tcx, def_id, def_kind) { diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 3cab936c45c1f..6a97327504721 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -22,7 +22,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; use crate::hir::{ModuleItems, ProjectedMaybeOwner, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::{IntoQueryKey, LocalCrate}; -use crate::ty::TyCtxt; +use crate::ty::{self, TyCtxt}; /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir_parent_iter(hir_id)`. @@ -879,13 +879,6 @@ impl<'tcx> TyCtxt<'tcx> { self.hir_opt_delegation_info(delegation_id).expect("processing delegation") } - pub fn hir_is_delegation_child_segment(self, segment: &PathSegment<'_>) -> bool { - let parent_def = self.hir_get_parent_item(segment.hir_id).def_id; - - self.hir_opt_delegation_info(parent_def) - .is_some_and(|info| info.child_seg_id == segment.hir_id) - } - #[inline] fn hir_opt_ident(self, id: HirId) -> Option { match self.hir_node(id) { @@ -1115,6 +1108,12 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn is_type_system_inline_const(self, def_id: impl IntoQueryKey) -> bool { + let def_id = def_id.into_query_key(); + debug_assert_eq!(self.def_kind(def_id), DefKind::InlineConst); + self.anon_const_kind(def_id) != ty::AnonConstKind::NonTypeSystem + } + pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option { let local = match expr { Expr { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 713c6597c1966..b6ae4a98a34e3 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -114,7 +114,7 @@ pub struct CodegenFnAttrs { // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity pub alignment: Option, /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around - /// the function entry. + /// the function entry, or override default section to record entry location. pub patchable_function_entry: Option, /// The `#[rustc_objc_class = "..."]` attribute. pub objc_class: Option, @@ -162,24 +162,33 @@ pub struct TargetFeature { #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function - prefix: u8, + prefix: Option, /// Nops after entry, but before body - entry: u8, + entry: Option, + /// Optional, specific section to record entry location in + section: Option, } impl PatchableFunctionEntry { - pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self { - Self { prefix: config.prefix(), entry: config.entry() } + pub fn from_prefix_entry_and_section( + prefix: Option, + entry: Option, + section: Option, + ) -> Self { + Self { prefix, entry, section } } pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self { - Self { prefix, entry } + Self { prefix: Some(prefix), entry: Some(entry), section: None } } - pub fn prefix(&self) -> u8 { + pub fn prefix(&self) -> Option { self.prefix } - pub fn entry(&self) -> u8 { + pub fn entry(&self) -> Option { self.entry } + pub fn section(&self) -> Option { + self.section + } } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, StableHash)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 569a1d5786095..3d7aef0b273f1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -601,7 +601,14 @@ impl<'tcx> TyCtxt<'tcx> { /// effect. However, we do not want this as a general capability, so this interface restricts /// to the only allowed case. pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx, Ty<'tcx>>) { - debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); + if cfg!(debug_assertions) { + match self.def_kind(key) { + DefKind::AnonConst => (), + DefKind::InlineConst => assert!(self.is_type_system_inline_const(key)), + def_kind => bug!("unexpected DefKind in feed_anon_const_type: {def_kind:?}"), + } + } + TyCtxtFeed { tcx: self, key }.type_of(value) } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 5819470765e69..0afe55f700b58 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -244,7 +244,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy { def_id }, DefKind::TyAlias => ty::AliasTermKind::FreeTy { def_id }, DefKind::Const { .. } => ty::AliasTermKind::FreeConst { def_id }, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { + DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { ty::AliasTermKind::AnonConst { def_id } } kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ca3b3f0bbec5c..2c91555e3835f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -597,7 +597,38 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if `def_id` refers to a definition that does not have its own /// type-checking context, i.e. closure, coroutine or inline const. pub fn is_typeck_child(self, def_id: DefId) -> bool { - self.def_kind(def_id).is_typeck_child() + match self.def_kind(def_id) { + DefKind::InlineConst => !self.is_type_system_inline_const(def_id), + DefKind::Closure | DefKind::SyntheticCoroutineBody => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst { .. } + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } => false, + } } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index 25d15c12a7b3b..4b66c757729d3 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -52,11 +52,10 @@ where F: FnOnce() -> B, B: Deref>, { - if !matches!( - tcx.def_kind(def), - DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst - ) { - return None; + match tcx.def_kind(def) { + DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst => (), + DefKind::InlineConst if tcx.is_type_system_inline_const(def) => (), + _ => return None, } // If there are impossible predicates then MIR passes will replace the body with diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index a5049339382f7..de3f8c380fc44 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -17,8 +17,7 @@ use tracing::{debug, instrument}; use crate::macros::MacroRulesScopeRef; use crate::{ - ConstArgContext, ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, - with_owner_tables, + ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, with_owner_tables, }; pub(crate) fn collect_definitions<'ra>( @@ -115,12 +114,6 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { self.invocation_parent.impl_trait_context = orig_itc; } - fn with_const_arg(&mut self, ctxt: ConstArgContext, f: F) { - let orig = mem::replace(&mut self.invocation_parent.const_arg_context, ctxt); - f(self); - self.invocation_parent.const_arg_context = orig; - } - fn collect_field(&mut self, field: &'a FieldDef, index: Option) { let index = |this: &Self| { index.unwrap_or_else(|| { @@ -430,6 +423,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { + // Note that `visit_anon_const` is skipped for AnonConst nodes wrapped in an + // ExprKind::ConstBlock - these are handled in visit_expr, and are DefKind::InlineConst. + // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so // to avoid affecting stable we have to feature gate the not creating // anon consts @@ -441,16 +437,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } match constant.mgca_disambiguation { - MgcaDisambiguation::Direct => self.with_const_arg(ConstArgContext::Direct, |this| { - visit::walk_anon_const(this, constant); - }), + MgcaDisambiguation::Direct => visit::walk_anon_const(self, constant), MgcaDisambiguation::AnonConst => { - self.with_const_arg(ConstArgContext::NonDirect, |this| { - let parent = this - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - this.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - }) + let parent = self + .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) + .def_id(); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } }; } @@ -459,60 +451,28 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_expr(&mut self, expr: &'a Expr) { debug!(?self.invocation_parent); - let parent_def = match &expr.kind { + match &expr.kind { ExprKind::MacCall(..) => { self.visit_macro_invoc(expr.id); self.visit_invoc(expr.id); - return; } ExprKind::Closure(..) | ExprKind::Gen(..) => { - self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id() + let def = self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id(); + self.with_parent(def, |this| visit::walk_expr(this, expr)); } ExprKind::ConstBlock(constant) => { - // Under `min_generic_const_args` a `const { }` block sometimes - // corresponds to an anon const rather than an inline const. - let def_kind = match self.invocation_parent.const_arg_context { - ConstArgContext::Direct => DefKind::AnonConst, - ConstArgContext::NonDirect => DefKind::InlineConst, - }; - - return self.with_const_arg(ConstArgContext::NonDirect, |this| { - for attr in &expr.attrs { - visit::walk_attribute(this, attr); - } - - let def = - this.create_def(constant.id, None, def_kind, constant.value.span).def_id(); - this.with_parent(def, |this| visit::walk_anon_const(this, constant)); - }); - } - - // Avoid overwriting `const_arg_context` as we may want to treat const blocks - // as being anon consts if we are inside a const argument. - ExprKind::Struct(_) | ExprKind::Call(..) | ExprKind::Tup(..) | ExprKind::Array(..) => { - return visit::walk_expr(self, expr); - } - // FIXME(mgca): we may want to handle block labels in some manner - ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind { - // FIXME(mgca): this probably means that mac calls that expand - // to semi'd const blocks are handled differently to just writing - // out a semi'd const block. - StmtKind::Expr(..) | StmtKind::MacCall(..) => return visit::walk_expr(self, expr), - - // Fallback to normal behaviour - StmtKind::Let(..) | StmtKind::Item(..) | StmtKind::Semi(..) | StmtKind::Empty => { - self.invocation_parent.parent_def + for attr in &expr.attrs { + visit::walk_attribute(self, attr); } - }, - _ => self.invocation_parent.parent_def, - }; - - self.with_const_arg(ConstArgContext::NonDirect, |this| { - // Note in some cases the `parent_def` here may be the existing parent - // and this is actually a no-op `with_parent` call. - this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) - }) + let def = self + .create_def(constant.id, None, DefKind::InlineConst, constant.value.span) + .def_id(); + // use specifically walk_anon_const, not walk_expr, to skip self.visit_anon_const + self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + } + _ => visit::walk_expr(self, expr), + } } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index e6cfa469e91d0..6887e847bc87d 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -186,7 +186,6 @@ struct InvocationParent { parent_def: LocalDefId, impl_trait_context: ImplTraitContext, in_attr: bool, - const_arg_context: ConstArgContext, owner: NodeId, } @@ -195,7 +194,6 @@ impl InvocationParent { parent_def: CRATE_DEF_ID, impl_trait_context: ImplTraitContext::Existential, in_attr: false, - const_arg_context: ConstArgContext::NonDirect, owner: CRATE_NODE_ID, }; } @@ -207,13 +205,6 @@ enum ImplTraitContext { InBinding, } -#[derive(Copy, Clone, Debug)] -enum ConstArgContext { - Direct, - /// Either inside of an `AnonConst` or not inside a const argument at all. - NonDirect, -} - /// Used for tracking import use types which will be used for redundant import checking. /// /// ### Used::Scope Example diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c7da2d9a1fc38..4492cdf2d2f8a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3342,23 +3342,29 @@ impl DumpMonoStatsFormat { /// `-Z patchable-function-entry` representation - how many nops to put before and after function /// entry. -#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] +#[derive(Clone, PartialEq, Hash, Debug, Default)] pub struct PatchableFunctionEntry { /// Nops before the entry prefix: u8, /// Nops after the entry entry: u8, + /// An optional section name to record the entry location + section: Option, } impl PatchableFunctionEntry { - pub fn from_total_and_prefix_nops( + pub fn from_parts( total_nops: u8, prefix_nops: u8, + section: Option, ) -> Option { if total_nops < prefix_nops { None + // Section name cannot contain null characters. + } else if section.as_ref().map(|x| x.contains('\0') || x.is_empty()).unwrap_or(false) { + None } else { - Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops }) + Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops, section }) } } pub fn prefix(&self) -> u8 { @@ -3367,6 +3373,9 @@ impl PatchableFunctionEntry { pub fn entry(&self) -> u8 { self.entry } + pub fn section(&self) -> Option<&str> { + self.section.as_ref().map(|x| x.as_str()) + } } /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index efc8a70f4feb2..b7668bd47d8ec 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -784,7 +784,7 @@ mod desc { pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; - pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; + pub(crate) const parse_patchable_function_entry: &str = "a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops"; pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `kernel-hwaddress`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'"; @@ -1206,20 +1206,24 @@ pub mod parse { ) -> bool { let mut total_nops = 0; let mut prefix_nops = 0; + let mut section = None; if !parse_number(&mut total_nops, v) { - let parts = v.and_then(|v| v.split_once(',')).unzip(); - if !parse_number(&mut total_nops, parts.0) { + let parts: Vec<_> = v.unwrap_or("").split(',').collect(); + if parts.len() < 2 || parts.len() > 3 { return false; } - if !parse_number(&mut prefix_nops, parts.1) { + + if !parse_number(&mut total_nops, Some(parts[0])) { + return false; + } + if !parse_number(&mut prefix_nops, Some(parts[1])) { return false; } + section = parts.get(2).map(|x| x.to_string()); } - if let Some(pfe) = - PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops) - { + if let Some(pfe) = PatchableFunctionEntry::from_parts(total_nops, prefix_nops, section) { *slot = pfe; return true; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c75da250efd5f..4bada9712d2a9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1874,6 +1874,7 @@ symbols! { saturating_sub, sdylib, search_unbox, + section, select_unpredictable, self_in_typedefs, self_struct_ctor, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 4e3a31f0e84b5..3fdb1d6defdf6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2556,6 +2556,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.children.clear(); let mut span = obligation.cause.span; + let mut is_async_fn_return = false; if let DefKind::Closure = self.tcx.def_kind(obligation.cause.body_id) && let parent = self.tcx.local_parent(obligation.cause.body_id) && let DefKind::Fn | DefKind::AssocFn = self.tcx.def_kind(parent) @@ -2571,10 +2572,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // and // async fn foo() -> dyn Display Box span = fn_sig.decl.output.span(); + is_async_fn_return = true; err.span(span); } let body = self.tcx.hir_body_owned_by(obligation.cause.body_id); + if !is_async_fn_return + && let Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = + self.tcx.hir_node_by_def_id(obligation.cause.body_id) + && matches!(closure.fn_decl.output, hir::FnRetTy::DefaultReturn(_)) + { + return true; + } + let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 5920a1e900d19..bac5d47e98676 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -317,6 +317,10 @@ fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, ) -> &'tcx ty::List { + // Closures and coroutines are type checked with their parent + // Note that we also support `SyntheticCoroutineBody` since we create + // a MIR body for the def kind, and some MIR passes (like promotion) + // may require doing analysis using its typing env. if tcx.is_typeck_child(item.to_def_id()) { return tcx.opaque_types_defined_by(tcx.local_parent(item)); } @@ -332,7 +336,11 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { + | DefKind::AnonConst + | DefKind::InlineConst => { + // Non-type-system inline consts should be caught by `if tcx.is_typeck_child` above + debug_assert!(kind != DefKind::InlineConst || tcx.is_type_system_inline_const(item)); + collector.collect_taits_declared_in_body(); } DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} @@ -345,15 +353,8 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Trait | DefKind::ForeignTy | DefKind::TraitAlias - - // Closures and coroutines are type checked with their parent - // Note that we also support `SyntheticCoroutineBody` since we create - // a MIR body for the def kind, and some MIR passes (like promotion) - // may require doing analysis using its typing env. | DefKind::Closure - | DefKind::InlineConst | DefKind::SyntheticCoroutineBody - | DefKind::TyParam | DefKind::ConstParam | DefKind::Ctor(_, _) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index ebec1888e6a95..f60a03fa71b26 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -21,6 +21,24 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( ) -> V::Result { let kind = tcx.def_kind(item); trace!(?kind); + let mut visit_alias = || { + if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { + // If the type of the item uses `_`, we're gonna error out anyway, but + // typeck (which type_of invokes below), will call back into opaque_types_defined_by + // causing a cycle. So we just bail out in this case. + if ty.is_suggestable_infer_ty() { + return V::Result::output(); + } + // Associated types in traits don't necessarily have a type that we can visit + try_visit!( + visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) + ); + } + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { + try_visit!(visitor.visit(span, pred.skip_norm_wip())); + } + V::Result::output() + }; match kind { // Walk over the signature of the function DefKind::AssocFn | DefKind::Fn => { @@ -47,24 +65,8 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { - if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { - // If the type of the item uses `_`, we're gonna error out anyway, but - // typeck (which type_of invokes below), will call back into opaque_types_defined_by - // causing a cycle. So we just bail out in this case. - if ty.is_suggestable_infer_ty() { - return V::Result::output(); - } - // Associated types in traits don't necessarily have a type that we can visit - try_visit!( - visitor - .visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) - ); - } - for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { - try_visit!(visitor.visit(span, pred.skip_norm_wip())); - } - } + | DefKind::AnonConst => return visit_alias(), + DefKind::InlineConst if tcx.is_type_system_inline_const(item) => return visit_alias(), DefKind::OpaqueTy => { for (pred, span) in tcx .explicit_item_bounds(item) diff --git a/library/std/src/attribute_docs.rs b/library/core/src/attribute_docs.rs similarity index 100% rename from library/std/src/attribute_docs.rs rename to library/core/src/attribute_docs.rs diff --git a/library/std/src/keyword_docs.rs b/library/core/src/keyword_docs.rs similarity index 98% rename from library/std/src/keyword_docs.rs rename to library/core/src/keyword_docs.rs index 5f94a13dad22a..596765be5e2dd 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/core/src/keyword_docs.rs @@ -195,7 +195,7 @@ mod break_keyword {} /// to be most things that would be reasonable to have in a constant (barring `const fn`s). For /// example, you can't have a [`File`] as a `const`. /// -/// [`File`]: crate::fs::File +/// [`File`]: ../std/fs/struct.File.html /// /// The only lifetime allowed in a constant is `'static`, which is the lifetime that encompasses /// all others in a Rust program. For example, if you wanted to define a constant string, it would @@ -484,7 +484,7 @@ mod extern_keyword {} #[doc(keyword = "false")] // -/// A value of type [`bool`] representing logical **false**. +/// A value of type [`prim@bool`] representing logical **false**. /// /// `false` is the logical opposite of [`true`]. /// @@ -1060,7 +1060,8 @@ mod mod_keyword {} /// /// `move` is often used when [threads] are involved. /// -/// ```rust +#[cfg_attr(target_os = "wasi", doc = "```rust,ignore (thread::spawn not supported)")] +#[cfg_attr(not(target_os = "wasi"), doc = "```rust")] /// let data = vec![1, 2, 3]; /// /// std::thread::spawn(move || { @@ -1235,31 +1236,18 @@ mod ref_keyword {} /// `return` returns from the function immediately (an "early return"): /// /// ```no_run -/// use std::fs::File; -/// use std::io::{Error, ErrorKind, Read, Result}; -/// -/// fn main() -> Result<()> { -/// let mut file = match File::open("foo.txt") { -/// Ok(f) => f, -/// Err(e) => return Err(e), -/// }; +/// fn main() -> Result<(), &'static str> { +/// let contents = "Hello, world!"; /// -/// let mut contents = String::new(); -/// let size = match file.read_to_string(&mut contents) { -/// Ok(s) => s, -/// Err(e) => return Err(e), -/// }; +/// if contents.contains("impossible!") { +/// return Err("oh no!"); +/// } /// -/// if contents.contains("impossible!") { -/// return Err(Error::new(ErrorKind::Other, "oh no!")); -/// } +/// if contents.len() > 9000 { +/// return Err("over 9000!"); +/// } /// -/// if size > 9000 { -/// return Err(Error::new(ErrorKind::Other, "over 9000!")); -/// } -/// -/// assert_eq!(contents, "Hello, world!"); -/// Ok(()) +/// Ok(()) /// } /// ``` /// @@ -1306,7 +1294,8 @@ mod return_keyword {} /// manner to computed goto). /// /// Example of using `become` to implement functional-style `fold`: -/// ``` +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1360,7 +1349,8 @@ mod return_keyword {} /// (unless it's coerced to a function pointer) /// /// It is possible to tail-call a function pointer: -/// ``` +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1631,8 +1621,8 @@ mod self_upper_keyword {} /// [`extern`]: keyword.extern.html /// [`mut`]: keyword.mut.html /// [`unsafe`]: keyword.unsafe.html -/// [`Mutex`]: sync::Mutex -/// [`OnceLock`]: sync::OnceLock +/// [`Mutex`]: ../std/sync/struct.Mutex.html +/// [`OnceLock`]: ../std/sync/struct.OnceLock.html /// [`RefCell`]: cell::RefCell /// [atomic]: sync::atomic /// [Reference]: ../reference/items/static-items.html @@ -1959,7 +1949,7 @@ mod trait_keyword {} #[doc(keyword = "true")] // -/// A value of type [`bool`] representing logical **true**. +/// A value of type [`prim@bool`] representing logical **true**. /// /// Logically `true` is not equal to [`false`]. /// @@ -2312,6 +2302,7 @@ mod type_keyword {} /// [`static`]: keyword.static.html /// [`union`]: keyword.union.html /// [`impl`]: keyword.impl.html +/// [`Vec::set_len`]: ../std/vec/struct.Vec.html#method.set_len /// [raw pointers]: ../reference/types/pointer.html /// [memory safety]: ../book/ch19-01-unsafe-rust.html /// [Rustonomicon]: ../nomicon/index.html @@ -2502,7 +2493,7 @@ mod use_keyword {} /// ``` /// /// `where` is available anywhere generic and lifetime parameters are available, -/// as can be seen with the [`Cow`](crate::borrow::Cow) type from the standard +/// as can be seen with the [`Cow`](../std/borrow/enum.Cow.html) type from the standard /// library: /// /// ```rust diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6fa1aa3b1f172..8e310cc7d2155 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -386,4 +386,17 @@ pub mod simd { pub use crate::core_simd::simd::*; } +// Include private modules that exist solely to provide rustdoc +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("attribute_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for the existing keywords. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("keyword_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. include!("primitive_docs.rs"); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 78e984db6cba5..afa30c8c00f27 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -772,20 +772,23 @@ pub mod from { pub use core::from::From; } -// Include a number of private modules that exist solely to provide -// the rustdoc documentation for primitive types. Using `include!` -// because rustdoc only looks for these modules at the crate level. -include!("../../core/src/primitive_docs.rs"); +// We include the following files here *again* (they are already included in libcore) +// so that they show up in search results for the std crate, and to avoid breaking +// existing links: + +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("../../core/src/attribute_docs.rs"); // Include a number of private modules that exist solely to provide // the rustdoc documentation for the existing keywords. Using `include!` // because rustdoc only looks for these modules at the crate level. -include!("keyword_docs.rs"); +include!("../../core/src/keyword_docs.rs"); -// Include private modules that exist solely to provide rustdoc -// documentation for built-in attributes. Using `include!` because rustdoc -// only looks for these modules at the crate level. -include!("attribute_docs.rs"); +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("../../core/src/primitive_docs.rs"); // This is required to avoid an unstable error when `restricted-std` is not // enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index cce3f068aabcd..03e012079b079 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -26,6 +26,8 @@ download-rustc = false llvm-bitcode-linker = true # Required to make builds reproducible. remap-debuginfo = true +# Compress debuginfo in generated artifacts to reduce their size +compress-debuginfo = "zlib" [dist] # Use better compression when preparing tarballs. diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e7fe8bd1f3858..745cd81c0fa4c 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -5,8 +5,8 @@ use std::path::{Path, PathBuf}; use super::{Builder, Kind}; use crate::core::build_steps::test; use crate::core::build_steps::tool::SourceType; -use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; +use crate::core::config::{CompressDebuginfo, SplitDebuginfo}; use crate::utils::build_stamp; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_flags}; use crate::{ @@ -14,8 +14,11 @@ use crate::{ RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, }; -/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler -/// later. +/// Represents flag values in `String` form with a `\x1f` delimiter to pass to the compiler later. +/// +/// Flags are emitted via `CARGO_ENCODED_RUSTFLAGS` / `CARGO_ENCODED_RUSTDOCFLAGS`, +/// which use `\x1f` (ASCII Unit Separator) as the delimiter and therefore allow spaces +/// within individual flag values (e.g. paths from `llvm-config --libdir`). /// /// `-Z crate-attr` flags will be applied recursively on the target code using the /// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more @@ -51,11 +54,16 @@ impl Rustflags { } fn arg(&mut self, arg: &str) -> &mut Self { - assert_eq!(arg.split(' ').count(), 1); - if !self.0.is_empty() { - self.0.push(' '); + assert!( + !arg.contains('\x1f'), + "rustflag must not contain the ASCII unit separator (\\x1f): {arg:?}" + ); + if !arg.is_empty() { + if !self.0.is_empty() { + self.0.push('\x1f'); + } + self.0.push_str(arg); } - self.0.push_str(arg); self } @@ -340,8 +348,13 @@ impl Cargo { self.rustdocflags.arg(&arg); } - if !builder.config.dry_run() && builder.cc[&target].args().iter().any(|arg| arg == "-gz") { - self.rustflags.arg("-Clink-arg=-gz"); + if !builder.config.dry_run() { + match builder.config.compress_debuginfo(target) { + CompressDebuginfo::Zlib => { + self.rustflags.arg("-Clink-arg=-Wl,--compress-debug-sections=zlib"); + } + CompressDebuginfo::Off => {} + } } // Ignore linker warnings for now. These are complicated to fix and don't affect the build. @@ -457,14 +470,21 @@ impl From for BootstrapCommand { cargo.command.args(cargo.args); + // Always unset the plain RUSTFLAGS/RUSTDOCFLAGS so that downstream + // tools (e.g. build.rs scripts) see only the encoded form. Any flags + // from the caller's environment have already been folded into the + // Rustflags struct via `propagate_cargo_env`. + cargo.command.env_remove("RUSTFLAGS"); + cargo.command.env_remove("RUSTDOCFLAGS"); + let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { - cargo.command.env("RUSTFLAGS", rustflags); + cargo.command.env("CARGO_ENCODED_RUSTFLAGS", rustflags); } let rustdocflags = &cargo.rustdocflags.0; if !rustdocflags.is_empty() { - cargo.command.env("RUSTDOCFLAGS", rustdocflags); + cargo.command.env("CARGO_ENCODED_RUSTDOCFLAGS", rustdocflags); } let encoded_hostflags = cargo.hostflags.encode(); @@ -918,7 +938,10 @@ impl Builder<'_> { } let rustdoc_path = match cmd_kind { - Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc_for_compiler(compiler), + Kind::Doc => self.rustdoc_for_compiler(compiler), + Kind::Test | Kind::MiriTest if self.test_target.runs_doctests() => { + self.rustdoc_for_compiler(compiler) + } _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), }; @@ -1183,8 +1206,9 @@ impl Builder<'_> { if (mode == Mode::ToolRustcPrivate || mode == Mode::Codegen) && let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = + let llvm_libdir_raw = command(llvm_config).cached().arg("--libdir").run_capture_stdout(self).stdout(); + let llvm_libdir = llvm_libdir_raw.trim(); if target.is_msvc() { rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); } else { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 048fcc5c80145..66e14dae9b9ec 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1150,6 +1150,7 @@ impl<'a> Builder<'a> { /// compiler will run on, *not* the target it will build code for). Explicitly does not take /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, /// since it ensures that they are valid (i.e., built and assembled). + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1183,6 +1184,7 @@ impl<'a> Builder<'a> { /// /// However, without this optimization, we would also build stage 2 rustc for **target1**, /// which is completely wasteful. + #[track_caller] pub fn compiler_for_std(&self, stage: u32) -> Compiler { if compile::Std::should_be_uplifted_from_stage_1(self, stage) { self.compiler(1, self.host_target) @@ -1202,6 +1204,7 @@ impl<'a> Builder<'a> { /// sysroot. /// /// See `force_use_stage1` and `force_use_stage2` for documentation on what each argument is. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1249,6 +1252,7 @@ impl<'a> Builder<'a> { /// Prefer using this method rather than manually invoking `Std::new`. /// /// Returns an optional build stamp, if libstd was indeed built. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1297,17 +1301,20 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler } } + #[track_caller] pub fn sysroot(&self, compiler: Compiler) -> PathBuf { self.ensure(compile::Sysroot::new(compiler)) } /// Returns the bindir for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_bindir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("bin") } /// Returns the libdir where the standard library and other artifacts are /// found for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_libdir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("lib") } @@ -1416,6 +1423,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Returns a path to `Rustdoc` that "belongs" to the `target_compiler`. /// It can be either a stage0 rustdoc or a locally built rustdoc that *links* to /// `target_compiler`. + #[track_caller] pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf { self.ensure(tool::Rustdoc { target_compiler }) } @@ -1532,6 +1540,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Ensure that a given step is built, returning its output. This will /// cache the step, so it is safe (and good!) to call this as often as /// needed to ensure that all dependencies are built. + #[track_caller] pub fn ensure(&'a self, step: S) -> S::Output { { let mut stack = self.stack.borrow_mut(); @@ -1589,7 +1598,8 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler // in the step_name field. "step", step_name = pretty_step_name::(), - args = step_debug_args(&step) + args = step_debug_args(&step), + location = crate::utils::tracing::format_location(*std::panic::Location::caller()) ); span.entered() }; diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1a59e54a95cbf..176a61b15c988 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2423,6 +2423,19 @@ mod snapshot { } } + #[test] + fn test_library_tests_only_does_not_build_rustdoc() { + let ctx = TestCtx::new(); + let host = TargetSelection::from_user(&host_target()); + insta::assert_snapshot!( + ctx.config("test").args(&["--tests", "library/core"]).render_steps(), + @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + "); + } + #[test] fn test_cargo_stage_1() { let ctx = TestCtx::new(); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 5a9c7264c006f..ed5dd9c9ee9fa 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -49,8 +49,8 @@ use crate::core::config::toml::target::{ DefaultLinuxLinkerOverride, Target, TomlTarget, default_linux_linker_overrides, }; use crate::core::config::{ - CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, - RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, + CompilerBuiltins, CompressDebuginfo, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, + ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, }; use crate::core::download::{ DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt, @@ -210,6 +210,7 @@ pub struct Config { pub rust_debuginfo_level_std: DebuginfoLevel, pub rust_debuginfo_level_tools: DebuginfoLevel, pub rust_debuginfo_level_tests: DebuginfoLevel, + pub rust_compress_debuginfo: CompressDebuginfo, pub rust_rpath: bool, pub rust_strip: bool, pub rust_frame_pointers: bool, @@ -550,6 +551,7 @@ impl Config { debuginfo_level_std: rust_debuginfo_level_std, debuginfo_level_tools: rust_debuginfo_level_tools, debuginfo_level_tests: rust_debuginfo_level_tests, + compress_debuginfo: rust_compress_debuginfo, backtrace: rust_backtrace, incremental: rust_incremental, randomize_layout: rust_randomize_layout, @@ -1469,6 +1471,7 @@ impl Config { .unwrap_or(vec![CodegenBackendKind::Llvm]), rust_codegen_units: rust_codegen_units.map(threads_from_config), rust_codegen_units_std: rust_codegen_units_std.map(threads_from_config), + rust_compress_debuginfo: rust_compress_debuginfo.unwrap_or_default(), rust_debug_logging: rust_debug_logging .or(rust_rustc_debug_assertions) .unwrap_or(rust_debug == Some(true)), @@ -1925,6 +1928,13 @@ impl Config { .unwrap_or_else(|| SplitDebuginfo::default_for_platform(target)) } + pub fn compress_debuginfo(&self, target: TargetSelection) -> CompressDebuginfo { + self.target_config + .get(&target) + .and_then(|t| t.compress_debuginfo) + .unwrap_or(self.rust_compress_debuginfo) + } + /// Checks if the given target is the same as the host target. pub fn is_host_target(&self, target: TargetSelection) -> bool { self.host_target == target diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 7651def62672a..c29849cf5a5d6 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -32,6 +32,7 @@ use std::path::PathBuf; use build_helper::exit; pub use config::*; +use serde::de::Unexpected; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; pub use target_selection::TargetSelection; @@ -378,6 +379,53 @@ impl std::str::FromStr for SplitDebuginfo { } } +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] +pub enum CompressDebuginfo { + Zlib, + #[default] + Off, +} + +impl CompressDebuginfo { + fn default_on() -> Self { + Self::Zlib + } +} + +impl std::str::FromStr for CompressDebuginfo { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "zlib" => Ok(CompressDebuginfo::Zlib), + "off" => Ok(CompressDebuginfo::Off), + _ => Err(()), + } + } +} + +impl<'de> Deserialize<'de> for CompressDebuginfo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + + Ok(match Deserialize::deserialize(deserializer)? { + StringOrBool::Bool(value) => { + if value { + CompressDebuginfo::default_on() + } else { + CompressDebuginfo::Off + } + } + StringOrBool::String(value) => CompressDebuginfo::from_str(&value).map_err(|_| { + D::Error::invalid_value(Unexpected::Str(&value), &"`zlib` or `off`") + })?, + }) + } +} + /// Describes how to handle conflicts in merging two `TomlConfig` #[derive(Copy, Clone, Debug)] pub enum ReplaceOpt { diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index a872671343405..f190eec3761ed 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -5,7 +5,7 @@ use build_helper::ci::CiEnv; use serde::{Deserialize, Deserializer}; use crate::core::config::toml::TomlConfig; -use crate::core::config::{DebuginfoLevel, Merge, ReplaceOpt, StringOrBool}; +use crate::core::config::{CompressDebuginfo, DebuginfoLevel, Merge, ReplaceOpt, StringOrBool}; use crate::{BTreeSet, CodegenBackendKind, HashSet, PathBuf, TargetSelection, define_config, exit}; define_config! { @@ -28,6 +28,7 @@ define_config! { debuginfo_level_std: Option = "debuginfo-level-std", debuginfo_level_tools: Option = "debuginfo-level-tools", debuginfo_level_tests: Option = "debuginfo-level-tests", + compress_debuginfo: Option = "compress-debuginfo", backtrace: Option = "backtrace", incremental: Option = "incremental", default_linker: Option = "default-linker", @@ -322,6 +323,7 @@ pub fn check_incompatible_options_for_ci_rustc( randomize_layout, debug_logging, debuginfo_level_rustc, + compress_debuginfo, llvm_tools, llvm_bitcode_linker, stack_protector, @@ -389,6 +391,7 @@ pub fn check_incompatible_options_for_ci_rustc( err!(current_rust_config.optimize, optimize, "rust"); err!(current_rust_config.randomize_layout, randomize_layout, "rust"); + err!(current_rust_config.compress_debuginfo, compress_debuginfo, "rust"); err!(current_rust_config.debug_logging, debug_logging, "rust"); err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc, "rust"); err!(current_rust_config.rpath, rpath, "rust"); diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 847b75e696b47..311d9add9a144 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -15,7 +15,8 @@ use serde::de::Error; use serde::{Deserialize, Deserializer}; use crate::core::config::{ - CompilerBuiltins, LlvmLibunwind, Merge, ReplaceOpt, SplitDebuginfo, StringOrBool, + CompilerBuiltins, CompressDebuginfo, LlvmLibunwind, Merge, ReplaceOpt, SplitDebuginfo, + StringOrBool, }; use crate::{CodegenBackendKind, HashSet, PathBuf, define_config, exit}; @@ -68,6 +69,7 @@ pub struct Target { pub default_linker_linux_override: DefaultLinuxLinkerOverride, pub linker: Option, pub split_debuginfo: Option, + pub compress_debuginfo: Option, pub sanitizers: Option, pub profiler: Option, pub rpath: Option, diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index d010226f0dfdb..320dfcc4e69ed 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -25,7 +25,7 @@ use std::collections::HashSet; use std::iter; use std::path::{Path, PathBuf}; -use crate::core::config::TargetSelection; +use crate::core::config::{CompressDebuginfo, TargetSelection}; use crate::utils::exec::{BootstrapCommand, command}; use crate::{Build, CLang, GitRepo}; @@ -36,10 +36,18 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { .opt_level(2) .warnings(false) .debug(false) - // Compress debuginfo - .flag_if_supported("-gz") + // We have to configure out_dir, otherwise flag_if_supported will not work + .out_dir(build.tempdir().join("cc-rs-out-dir")) .target(&target.triple) .host(&build.host_target.triple); + + match build.config.compress_debuginfo(target) { + CompressDebuginfo::Zlib => { + cfg.flag_if_supported("-gz"); + } + CompressDebuginfo::Off => {} + } + match build.crt_static(target) { Some(a) => { cfg.static_crt(a); @@ -100,17 +108,15 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { let config = build.config.target_config.get(&target); if let Some(cc) = config .and_then(|c| c.cc.clone()) - .or_else(|| default_compiler(&mut cfg, Language::C, target, build)) + .or_else(|| default_compiler(&cfg, Language::C, target, build)) { cfg.compiler(cc); } let compiler = cfg.get_compiler(); - let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { - ar - } else { - cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok() - }; + let ar = config + .and_then(|c| c.ar.clone()) + .or_else(|| cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok()); build.cc.insert(target, compiler.clone()); let mut cflags = build.cc_handled_clags(target, CLang::C); @@ -122,7 +128,7 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { cfg.cpp(true); let cxx_configured = if let Some(cxx) = config .and_then(|c| c.cxx.clone()) - .or_else(|| default_compiler(&mut cfg, Language::CPlusPlus, target, build)) + .or_else(|| default_compiler(&cfg, Language::CPlusPlus, target, build)) { cfg.compiler(cxx); true @@ -158,7 +164,7 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { /// Determines the default compiler for a given target and language when not explicitly /// configured in `bootstrap.toml`. fn default_compiler( - cfg: &mut cc::Build, + cfg: &cc::Build, compiler: Language, target: TargetSelection, build: &Build, diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index a6233e6b61c1f..30757a39caa1c 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -86,7 +86,7 @@ fn test_default_compiler_wasi() { build.wasi_sdk_path = Some(wasi_sdk.clone()); let mut cfg = cc::Build::new(); - if let Some(result) = default_compiler(&mut cfg, Language::C, target.clone(), &build) { + if let Some(result) = default_compiler(&cfg, Language::C, target.clone(), &build) { let expected = { let compiler = format!("{}-clang", target.triple); wasi_sdk.join("bin").join(compiler) @@ -105,7 +105,7 @@ fn test_default_compiler_fallback() { let build = Build::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let mut cfg = cc::Build::new(); - let result = default_compiler(&mut cfg, Language::C, target, &build); + let result = default_compiler(&cfg, Language::C, target, &build); assert!(result.is_none(), "default_compiler should return None for generic targets"); } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 566ad003ebe01..b34c1b55700f8 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -631,4 +631,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New `--verbose-run-make-subprocess-output` flag for `x.py test` (defaults to true). Set `--verbose-run-make-subprocess-output=false` to suppress verbose subprocess output for passing run-make tests when using `--no-capture`.", }, + ChangeInfo { + change_id: 158169, + severity: ChangeSeverity::Info, + summary: "New option `rust.compress-debuginfo` allows configuring whether Rust and C/C++ debuginfo should be compressed.", + }, ]; diff --git a/src/bootstrap/src/utils/step_graph.rs b/src/bootstrap/src/utils/step_graph.rs index cffe0dbb3e3a5..d6a2d89482ee4 100644 --- a/src/bootstrap/src/utils/step_graph.rs +++ b/src/bootstrap/src/utils/step_graph.rs @@ -1,10 +1,12 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fmt::Debug; use std::io::BufWriter; +use std::panic::Location; use std::path::Path; use crate::core::builder::{AnyDebug, Step, pretty_step_name}; use crate::t; +use crate::utils::tracing::format_location; /// Records the executed steps and their dependencies in a directed graph, /// which can then be rendered into a DOT file for visualization. @@ -20,6 +22,7 @@ pub struct StepGraph { } impl StepGraph { + #[track_caller] pub fn register_step_execution( &mut self, step: &S, @@ -57,6 +60,7 @@ impl StepGraph { } } + #[track_caller] pub fn register_cached_step( &mut self, step: &S, @@ -97,12 +101,18 @@ struct Node { struct NodeHandle(usize); /// Represents a dependency between two bootstrap steps. -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] -struct Edge { - src: NodeHandle, - dst: NodeHandle, +#[derive(Default)] +struct EdgeData { // Was the corresponding execution of a step cached, or was the step actually executed? cached: bool, + // Locations from where the step was called + locations: Vec>, +} + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +struct EdgeKey { + src: NodeHandle, + dst: NodeHandle, } // We could use a library for this, but they either: @@ -114,8 +124,8 @@ struct Edge { #[derive(Default)] struct DotGraph { nodes: Vec, + edges: HashMap, /// The `NodeHandle` represents an index within `self.nodes` - edges: HashSet, key_to_index: HashMap, } @@ -127,16 +137,19 @@ impl DotGraph { handle } + #[track_caller] fn add_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - self.edges.insert(Edge { src, dst, cached: false }); + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.locations.push(*Location::caller()); } + #[track_caller] fn add_cached_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - // There's no point in rendering both cached and uncached edge - let uncached = Edge { src, dst, cached: false }; - if !self.edges.contains(&uncached) { - self.edges.insert(Edge { src, dst, cached: true }); - } + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.cached = true; + edge.locations.push(*Location::caller()); } fn get_handle_by_key(&self, key: &str) -> Option { @@ -157,11 +170,23 @@ impl DotGraph { )?; } - let mut edges: Vec<&Edge> = self.edges.iter().collect(); - edges.sort(); - for edge in edges { - let style = if edge.cached { "dashed" } else { "solid" }; - writeln!(file, r#"{} -> {} [style="{style}"]"#, edge.src.0, edge.dst.0)?; + let mut edges: Vec<(&EdgeKey, &EdgeData)> = self.edges.iter().collect(); + edges.sort_by_key(|(key, _)| **key); + for (key, data) in edges { + let style = if data.cached { "dashed" } else { "solid" }; + let mut locations = data + .locations + .iter() + .map(|location| format_location(*location)) + .collect::>(); + locations.sort(); + locations.dedup(); + let locations = locations.join(", "); + writeln!( + file, + r#"{} -> {} [style="{style}", tooltip="{locations}"]"#, + key.src.0, key.dst.0, + )?; } writeln!(file, "}}") diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index b1226ed7de7be..4e75347f4cbae 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -351,7 +351,7 @@ mod inner { let field = &values.fields[0]; write!(writer, " {{{}}}", field.1)?; } - write_location(writer, span.metadata())?; + write_with_location(writer)?; } // Executed command COMMAND_SPAN_TARGET => { diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile index 5ab44df7a8033..48be0b9f96e9d 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile @@ -28,6 +28,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xz-utils \ libssl-dev \ pkg-config \ + zlib1g-dev \ && rm -rf /var/lib/apt/lists/* COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/run.sh b/src/ci/run.sh index 18b5bf2e2bbb3..883cc20ba0014 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -145,6 +145,8 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then fi else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo=false" + # No need to compress debuginfo for tests, and we do not want to depend on zlib + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.compress-debuginfo=off" # We almost always want debug assertions enabled, but sometimes this takes too # long for too little benefit, so we just turn them off. diff --git a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md index 4a9bf47a29011..80f0e54907872 100644 --- a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md +++ b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md @@ -2,10 +2,13 @@ -------------------- -The `-Z patchable-function-entry=total_nops,prefix_nops` or `-Z patchable-function-entry=total_nops` +The `-Z patchable-function-entry=total_nops,prefix_nops,record_section`, + `-Z patchable-function-entry=total_nops,prefix_nops`, or + `-Z patchable-function-entry=total_nops` compiler flag enables nop padding of function entries with 'total_nops' nops, with -an offset for the entry of the function at 'prefix_nops' nops. In the second form, -'prefix_nops' defaults to 0. +an offset for the entry of the function at 'prefix_nops' nops. In the third form, +'prefix_nops' defaults to 0. record\_section can specify a specific linker section +to place entry record in, the default is `__patchable_function_entries`. As an illustrative example, `-Z patchable-function-entry=3,2` would produce: diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 882220a0cae03..7083d730de58c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -270,6 +270,18 @@ impl FromClean for Stability { } } +impl FromClean for Box { + fn from_clean(stab: &hir::DefaultBodyStability, _renderer: &JsonRenderer<'_>) -> Self { + let hir::StabilityLevel::Unstable { .. } = stab.level else { + bug!( + "unexpected stable default-body stability, \ + there's no stable equivalent of `#[rustc_default_body_unstable]`" + ) + }; + Box::new(ProvidedDefaultUnstable { feature: stab.feature.to_string() }) + } +} + impl FromClean for Option> { fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArgs::*; @@ -353,18 +365,23 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), FunctionItem(f) => { - ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, true, None, header.unwrap(), renderer)) } ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, false, None, header.unwrap(), renderer)) } TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) - } + MethodItem(m, _) => ItemEnum::Function(from_clean_function( + m, + true, + default_body_stability_for_def_id(renderer.tcx, item.item_id.expect_def_id()) + .map(|stab| stab.into_json(renderer)), + header.unwrap(), + renderer, + )), RequiredMethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(m, false, None, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)), @@ -385,23 +402,41 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum }) } // FIXME(generic_const_items): Add support for generic associated consts. - RequiredAssocConstItem(_generics, ty) => { - ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None } - } + RequiredAssocConstItem(_generics, ty) => ItemEnum::AssocConst { + type_: ty.into_json(renderer), + value: None, + default_unstable: None, + }, // FIXME(generic_const_items): Add support for generic associated consts. - ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst { + ProvidedAssocConstItem(ci) => ItemEnum::AssocConst { type_: ci.type_.into_json(renderer), value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), + }, + ImplAssocConstItem(ci) => ItemEnum::AssocConst { + type_: ci.type_.into_json(renderer), + value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: None, }, RequiredAssocTypeItem(g, b) => ItemEnum::AssocType { generics: g.into_json(renderer), bounds: b.into_json(renderer), type_: None, + default_unstable: None, }, AssocTypeItem(t, b) => ItemEnum::AssocType { generics: t.generics.into_json(renderer), bounds: b.into_json(renderer), type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), }, // `convert_item` early returns `None` for stripped items, keywords, attributes and // "special" macro rules. @@ -815,6 +850,7 @@ impl FromClean for Impl { pub(crate) fn from_clean_function( clean::Function { decl, generics }: &clean::Function, has_body: bool, + default_unstable: Option>, header: rustc_hir::FnHeader, renderer: &JsonRenderer<'_>, ) -> Function { @@ -823,6 +859,7 @@ pub(crate) fn from_clean_function( generics: generics.into_json(renderer), header: header.into_json(renderer), has_body, + default_unstable, } } @@ -972,6 +1009,17 @@ impl FromClean for ItemKind { } } +fn default_body_stability_for_def_id( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Option { + let stability = tcx.lookup_default_body_stability(def_id)?; + match stability.level { + hir::StabilityLevel::Unstable { .. } => Some(stability), + hir::StabilityLevel::Stable { .. } => None, + } +} + fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if !tcx.is_conditionally_const(def_id) { // The item cannot be conditionally-const. No const stability here. @@ -1040,6 +1088,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation. AK::Stability { .. } => return Vec::new(), // Handled separately into Item::stability AK::RustcConstStability { .. } => return Vec::new(), // Handled separately into Item::const_stability. + AK::RustcBodyStability { .. } => return Vec::new(), // Handled separately by `default_unstable`. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index e6af2fa04bb10..be4921d888278 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -114,8 +114,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Add `Item::const_stability`. -pub const FORMAT_VERSION: u32 = 59; +// Latest feature: Add default-body stability metadata. +pub const FORMAT_VERSION: u32 = 60; /// The root of the emitted JSON blob. /// @@ -288,6 +288,9 @@ pub struct Item { /// - `#[stable]` and `#[unstable]` attributes: see the [`Self::stability`] field instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes: /// see the [`Self::const_stability`] field instead. + /// - `#[rustc_default_body_unstable]` attributes: instead see `default_unstable` fields on + /// item kinds that can have unstable default values, such as [`Function::default_unstable`], + /// [`ItemEnum::AssocConst::default_unstable`], and [`ItemEnum::AssocType::default_unstable`]. /// /// Attributes appear in pretty-printed Rust form, regardless of their formatting /// in the original source code. For example: @@ -367,6 +370,19 @@ pub enum StabilityLevel { Unstable, } +/// Information about an unstable default provided by a trait item. +/// +/// Example unstable defaults include: +/// - a stable trait function or method whose body is not stable +/// - a stable trait associated type or const whose default value is not stable +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] +pub struct ProvidedDefaultUnstable { + /// The feature that must be enabled to use the provided default. + pub feature: String, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] @@ -379,6 +395,9 @@ pub enum StabilityLevel { /// - `#[stable]` and `#[unstable]`. These are in [`Item::stability`] instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]`. These are in /// [`Item::const_stability`] instead. +/// - `#[rustc_default_body_unstable]`. These are in the `default_unstable` field on the appropriate +/// item kinds: [`Function::default_unstable`], [`ItemEnum::AssocConst::default_unstable`], +/// and [`ItemEnum::AssocType::default_unstable`]. pub enum Attribute { /// `#[non_exhaustive]` NonExhaustive, @@ -875,6 +894,11 @@ pub enum ItemEnum { /// // ^^^^^^^^^^ /// ``` value: Option, + /// Metadata about an unstable default value provided for the associated constant, if any. + /// + /// Empty if the associated constant has no default (see [`ItemEnum::AssocConst::value`]), + /// or if the default value is stable. + default_unstable: Option>, }, /// An associated type of a trait or a type. AssocType { @@ -899,6 +923,11 @@ pub enum ItemEnum { /// ``` #[serde(rename = "type")] type_: Option, + /// Metadata about an unstable default value provided for the associated type, if any. + /// + /// Empty if the associated type has no default (see [`ItemEnum::AssocType::type_`]), + /// or if the default value is stable. + default_unstable: Option>, }, } @@ -1188,6 +1217,12 @@ pub struct Function { pub header: FunctionHeader, /// Whether the function has a body, i.e. an implementation. pub has_body: bool, + /// Metadata about a possible unstable provided default implementation for trait methods. + /// + /// Only populated for function items inside traits. Empty if the trait method + /// does not have a default implementation (see [`Function::has_body`]), + /// or if its default implementation is stable. + pub default_unstable: Option>, } /// Generic parameters accepted by an item and `where` clauses imposed on it and the parameters. diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 01deafe20b354..76a47c4f4fecf 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -97,7 +97,7 @@ impl<'a> Validator<'a> { ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::Enum(x) => self.check_enum(x), ItemEnum::Variant(x) => self.check_variant(x, id), - ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Function(x) => self.check_function(x, id), ItemEnum::Trait(x) => self.check_trait(x, id), ItemEnum::TraitAlias(x) => self.check_trait_alias(x), ItemEnum::Impl(x) => self.check_impl(x, id), @@ -114,12 +114,35 @@ impl<'a> Validator<'a> { ItemEnum::Module(x) => self.check_module(x, id), // FIXME: Why don't these have their own structs? ItemEnum::ExternCrate { .. } => {} - ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_), - ItemEnum::AssocType { generics, bounds, type_ } => { + ItemEnum::AssocConst { type_, value, default_unstable } => { + self.check_type(type_); + if value.is_none() + && let Some(default_unstable) = default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } + } + ItemEnum::AssocType { generics, bounds, type_, default_unstable } => { self.check_generics(generics); bounds.iter().for_each(|b| self.check_generic_bound(b)); if let Some(ty) = type_ { self.check_type(ty); + } else if let Some(default_unstable) = default_unstable { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); } } } @@ -194,9 +217,21 @@ impl<'a> Validator<'a> { } } - fn check_function(&mut self, x: &'a Function) { + fn check_function(&mut self, x: &'a Function, id: &Id) { self.check_generics(&x.generics); self.check_function_signature(&x.sig); + if !x.has_body + && let Some(default_unstable) = &x.default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } } fn check_trait(&mut self, x: &'a Trait, id: &Id) { diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index ff2fae157f04c..f9b54a2cc0936 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -1,5 +1,7 @@ use rustc_hash::FxHashMap; -use rustdoc_json_types::{Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, Visibility}; +use rustdoc_json_types::{ + Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, ProvidedDefaultUnstable, Visibility, +}; use super::*; use crate::json_find::SelectorPart; @@ -221,6 +223,7 @@ fn errors_on_missing_path() { abi: Abi::Rust, }, has_body: true, + default_unstable: None, }), }, ), @@ -246,6 +249,155 @@ fn errors_on_missing_path() { ); } +fn krate_with_trait_item(inner: ItemEnum) -> Crate { + let item_id = Id(2); + Crate { + root: Id(0), + crate_version: None, + includes_private: false, + index: FxHashMap::from_iter([ + ( + Id(0), + Item { + id: Id(0), + crate_id: 0, + name: Some("root".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![Id(1)], + is_stripped: false, + }), + }, + ), + ( + Id(1), + Item { + id: Id(1), + crate_id: 0, + name: Some("Trait".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Trait(Trait { + is_auto: false, + is_unsafe: false, + is_dyn_compatible: true, + items: vec![item_id], + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + implementations: vec![], + }), + }, + ), + ( + item_id, + Item { + id: item_id, + crate_id: 0, + name: Some("TraitItem".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner, + }, + ), + ]), + paths: FxHashMap::default(), + external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, + format_version: FORMAT_VERSION, + } +} + +#[test] +fn errors_on_default_unstable_without_function_body() { + let krate = krate_with_trait_item(ItemEnum::Function(Function { + sig: FunctionSignature { inputs: vec![], output: None, is_c_variadic: false }, + generics: Generics { params: vec![], where_predicates: vec![] }, + header: FunctionHeader { + is_const: false, + is_unsafe: false, + is_async: false, + abi: Abi::Rust, + }, + has_body: false, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + })); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_const_value() { + let krate = krate_with_trait_item(ItemEnum::AssocConst { + type_: Type::Primitive("usize".to_owned()), + value: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_type_default() { + let krate = krate_with_trait_item(ItemEnum::AssocType { + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + type_: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + #[test] #[should_panic = "LOCAL_CRATE_ID is wrong"] fn checks_local_crate_id_is_correct() { diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 633452f6052d7..3e46ee17c8a65 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -182,8 +182,9 @@ fn miri_config( .map(Into::into) .collect(), envs: vec![ - // Reset `RUSTFLAGS` to work around . + // Reset `RUSTFLAGS`/`CARGO_ENCODED_RUSTFLAGS` to work around . ("RUSTFLAGS".into(), None), + ("CARGO_ENCODED_RUSTFLAGS".into(), None), // Reset `MIRIFLAGS` because it caused trouble in the past and should not be needed. ("MIRIFLAGS".into(), None), // Allow `cargo miri build`. diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs index 72204c78a4906..45aa46d24fe94 100644 --- a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs @@ -39,6 +39,26 @@ pub fn fun5() {} #[patchable_function_entry(prefix_nops = 4)] pub fn fun6() {} +// The attribute should override patchable-function-prefix to 4 +// and patchable-function-entry to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(prefix_nops = 4, section = "foo")] +pub fn fun7() {} + +// The attribute should override patchable-function-entry-section, +// while passing through the commandline options. +#[no_mangle] +#[patchable_function_entry(section = "bar")] +pub fn fun8() {} + +// The attribute should override patchable-function-entry to 5 +// and patchable-function-prefix to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(entry_nops = 5, section = "baz")] +pub fn fun9() {} + // CHECK: @fun0() unnamed_addr #0 // CHECK: @fun1() unnamed_addr #1 // CHECK: @fun2() unnamed_addr #2 @@ -46,6 +66,9 @@ pub fn fun6() {} // CHECK: @fun4() unnamed_addr #4 // CHECK: @fun5() unnamed_addr #5 // CHECK: @fun6() unnamed_addr #6 +// CHECK: @fun7() unnamed_addr #7 +// CHECK: @fun8() unnamed_addr #8 +// CHECK: @fun9() unnamed_addr #9 // CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-prefix"="10" {{.*}} } // CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="2"{{.*}}"patchable-function-prefix"="1" {{.*}} } @@ -62,3 +85,12 @@ pub fn fun6() {} // CHECK: attributes #6 = { {{.*}}"patchable-function-prefix"="4"{{.*}} } // CHECK-NOT: attributes #6 = { {{.*}}patchable-function-entry{{.*}} } +// +// CHECK: attributes #7 = { {{.*}}"patchable-function-entry-section"="foo"{{.*}}"patchable-function-prefix"="4" {{.*}} } +// CHECK-NOT: attributes #7 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #8 = { {{.*}}"patchable-function-entry-section"="bar"{{.*}} } +// CHECK-NOT: attributes #8 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #9 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="baz" {{.*}} } +// CHECK-NOT: attributes #9 = { {{.*}}"patchable-function-prefix{{.*}} } diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs new file mode 100644 index 0000000000000..9ffff5ca7537c --- /dev/null +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Z patchable-function-entry=15,10,default_foo_section +// + +#![feature(patchable_function_entry)] +#![crate_type = "lib"] + +// This should have the default, as set by the compile flags +#[no_mangle] +pub fn fun0() {} + +// This should override the default section name +#[no_mangle] +#[patchable_function_entry(section = "bar_section")] +pub fn fun1() {} + +// CHECK: @fun0() unnamed_addr #0 +// CHECK: @fun1() unnamed_addr #1 + +// CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="default_foo_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } +// CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="bar_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } diff --git a/tests/rustdoc-json/attrs/stability/default_body.rs b/tests/rustdoc-json/attrs/stability/default_body.rs new file mode 100644 index 0000000000000..5a70e47d6c505 --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/default_body.rs @@ -0,0 +1,76 @@ +#![feature(staged_api, rustc_attrs, associated_type_defaults)] + +#[stable(feature = "default_body_trait_feature", since = "1.0.0")] +pub trait TraitWithDefaults { + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.default_unstable.feature" '"method_default_body_feature"' + //@ is "$.index[?(@.docs=='method with unstable default body')].attrs" [] + /// method with unstable default body + #[stable(feature = "default_body_method_feature", since = "1.1.0")] + #[rustc_default_body_unstable(feature = "method_default_body_feature", issue = "none")] + fn method_with_unstable_default() {} + + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.has_body" false + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.default_unstable" null + /// required method without default body + #[stable(feature = "required_method_feature", since = "1.2.0")] + fn required_method(); + + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.default_unstable" null + /// method with stable default body + #[stable(feature = "stable_default_method_feature", since = "1.3.0")] + fn method_with_stable_default() {} + + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.value" '"0"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.default_unstable.feature" '"assoc_const_default_value_feature"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].attrs" [] + /// associated constant with unstable default value + #[stable(feature = "assoc_const_with_unstable_default_feature", since = "1.4.0")] + #[rustc_default_body_unstable(feature = "assoc_const_default_value_feature", issue = "none")] + const UNSTABLE_DEFAULT_CONST: usize = 0; + + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.value" null + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.default_unstable" null + /// required associated constant + #[stable(feature = "required_assoc_const_feature", since = "1.5.0")] + const REQUIRED_CONST: usize; + + //@ is "$.index[?(@.docs=='associated type with unstable default type')].inner.assoc_type.default_unstable.feature" '"assoc_type_default_type_feature"' + //@ is "$.index[?(@.docs=='associated type with unstable default type')].attrs" [] + /// associated type with unstable default type + #[stable(feature = "assoc_type_with_unstable_default_feature", since = "1.6.0")] + #[rustc_default_body_unstable(feature = "assoc_type_default_type_feature", issue = "none")] + type UnstableDefaultType = usize; + + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.type" null + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.default_unstable" null + /// required associated type + #[stable(feature = "required_assoc_type_feature", since = "1.7.0")] + type RequiredType; +} + +#[stable(feature = "default_body_impl_target_feature", since = "2.0.0")] +pub struct ImplTarget; + +// Impl items provide their own definitions, so they do not use the trait's unstable defaults. +#[stable(feature = "default_body_impl_feature", since = "2.1.0")] +impl TraitWithDefaults for ImplTarget { + //@ is "$.index[?(@.docs=='impl override for unstable default body')].inner.function.default_unstable" null + /// impl override for unstable default body + fn method_with_unstable_default() {} + + fn required_method() {} + + //@ is "$.index[?(@.docs=='impl override for unstable default value')].inner.assoc_const.default_unstable" null + /// impl override for unstable default value + const UNSTABLE_DEFAULT_CONST: usize = 1; + + const REQUIRED_CONST: usize = 2; + + //@ is "$.index[?(@.docs=='impl override for unstable default type')].inner.assoc_type.default_unstable" null + /// impl override for unstable default type + type UnstableDefaultType = u8; + + type RequiredType = (); +} diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 7d8bd3700d4bb..96012d8df936b 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -550,8 +550,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0565]: malformed `coroutine` attribute input --> $DIR/malformed-attrs.rs:118:5 diff --git a/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.rs b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.rs new file mode 100644 index 0000000000000..1f0215afadc7b --- /dev/null +++ b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.rs @@ -0,0 +1,17 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148891. + +macro_rules! values { + ($inner:ty) => { + #[derive(Debug)] + pub enum TokenKind { + #[cfg(test)] + STRING([u8; $inner]), + //~^ ERROR expected expression, found `ty` metavariable + //~| ERROR macro expansion ignores `)` and any tokens following + } + }; +} + +values!(String); + +fn main() {} diff --git a/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.stderr b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.stderr new file mode 100644 index 0000000000000..0248b6636fb7a --- /dev/null +++ b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.stderr @@ -0,0 +1,28 @@ +error: expected expression, found `ty` metavariable + --> $DIR/cfg-eval-derive-invalid-reparse-no-ice.rs:8:25 + | +LL | pub enum TokenKind { + | --------- while parsing this enum +LL | #[cfg(test)] +LL | STRING([u8; $inner]), + | ^^^^^^ expected expression +... +LL | values!(String); + | --------------- in this macro invocation + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + = note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: macro expansion ignores `)` and any tokens following + --> $DIR/cfg-eval-derive-invalid-reparse-no-ice.rs:8:32 + | +LL | STRING([u8; $inner]), + | ^ +... +LL | values!(String); + | --------------- caused by the macro expansion here + | + = note: the usage of `values!` is likely invalid in item context + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/mgca/double-inline-const.rs b/tests/ui/const-generics/mgca/double-inline-const.rs new file mode 100644 index 0000000000000..9bfbd1819c695 --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] + +struct S; + +impl S { + const Q: usize = 2; + fn foo(_: S<{ const { const { Self::Q } } }>) {} + //~^ ERROR generic `Self` types are currently not permitted in anonymous constants +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/double-inline-const.stderr b/tests/ui/const-generics/mgca/double-inline-const.stderr new file mode 100644 index 0000000000000..bc73e3d93575e --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.stderr @@ -0,0 +1,15 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/double-inline-const.rs:7:35 + | +LL | fn foo(_: S<{ const { const { Self::Q } } }>) {} + | ^^^^ + | +note: not a concrete type + --> $DIR/double-inline-const.rs:5:22 + | +LL | impl S { + | ^^^^ + = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items + +error: aborting due to 1 previous error + diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs index 5cbeccf1b0e4f..1223c0c5b05f9 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs @@ -24,3 +24,19 @@ pub fn no_parameters_given() {} #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] //~^ ERROR malformed pub fn duplicate_parameter() {} + +#[patchable_function_entry(section = 255)] +//~^ ERROR malformed +pub fn invalid_section_parameter() {} + +#[patchable_function_entry(section = "foo", section = "bar")] +//~^ ERROR malformed +pub fn duplicate_section_parameter() {} + +#[patchable_function_entry(section = "fo\0o")] +//~^ ERROR null characters +pub fn nul_in_section_parameter() {} + +#[patchable_function_entry(section = "")] +//~^ ERROR empty +pub fn empty_section_parameter() {} diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr index 6c32a76834c69..c3f09ff12384a 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr @@ -9,7 +9,7 @@ LL | #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -23,7 +23,7 @@ LL | #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -34,8 +34,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0539]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:16:1 @@ -48,7 +48,7 @@ LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 10, something = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -61,8 +61,8 @@ LL | #[patchable_function_entry()] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0538]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:24:1 @@ -75,10 +75,50 @@ LL | #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | -error: aborting due to 6 previous errors +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:28:1 + | +LL | #[patchable_function_entry(section = 255)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^ + | | + | expected a string literal here + | +help: must be of the form + | +LL - #[patchable_function_entry(section = 255)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0538]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:32:1 + | +LL | #[patchable_function_entry(section = "foo", section = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^^^^^^^^^ + | | + | found `section` used as a key more than once + | +help: must be of the form + | +LL - #[patchable_function_entry(section = "foo", section = "bar")] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0648]: `section` may not contain null characters + --> $DIR/patchable-function-entry-attribute.rs:36:38 + | +LL | #[patchable_function_entry(section = "fo\0o")] + | ^^^^^^^ + +error: `section` may not be empty + --> $DIR/patchable-function-entry-attribute.rs:40:38 + | +LL | #[patchable_function_entry(section = "")] + | ^^ + +error: aborting due to 10 previous errors -Some errors have detailed explanations: E0538, E0539. +Some errors have detailed explanations: E0538, E0539, E0648. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr index b09af94a61541..bde7f4aa9dff6 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr @@ -1,2 +1,2 @@ -error: incorrect value `1,2` for unstable option `patchable-function-entry` - either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops) was expected +error: incorrect value `1,2` for unstable option `patchable-function-entry` - a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops was expected diff --git a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs index 258f77067ce9c..b29c40770b0e7 100644 --- a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs +++ b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs @@ -7,12 +7,14 @@ use proc_macro::*; use self::Mode::*; // FIXME: all cases should become `NormalOk` or `NormalErr` +// +// And .stderr should be empty (no diagnostics should get emitted from fallible parsing in the proc +// macro). #[derive(PartialEq, Clone, Copy)] enum Mode { NormalOk, NormalErr, OtherError, - OtherWithPanic, } fn print_unspanned(s: &str) -> Result @@ -43,12 +45,11 @@ where assert!(t.is_err()); } OtherError => { - print_unspanned::(s); - } - OtherWithPanic => { - if catch_unwind(|| print_unspanned::(s)).is_ok() { - eprintln!("{s} did not panic"); - } + let t = print_unspanned::(s); + // For now we assert OK here, but in the future this should all move to NormalErr. + // Any case that's failing this should go to NormalErr, probably after verifying that a + // diagnostic did get emitted. + assert!(t.is_ok()); } } } @@ -136,9 +137,9 @@ pub fn run() { // FIXME: all of the cases below should return an Err and emit no diagnostics, but don't yet. // emits diagnostics and returns LexError - lit("r'r'", OtherError); - lit("c'r'", OtherError); - lit("\u{2000}", OtherError); + lit("r'r'", NormalErr); + lit("c'r'", NormalErr); + lit("\u{2000}", NormalErr); // emits diagnostic and returns a seemingly valid tokenstream stream("r'r'", OtherError); @@ -146,8 +147,8 @@ pub fn run() { stream("\u{2000}", OtherError); for parse in [stream as fn(&str, Mode), lit] { - // emits diagnostic(s), then panics - parse("r#", OtherWithPanic); + // emits diagnostic(s), then returns LexError + parse("r#", NormalErr); // emits diagnostic(s), then returns Ok(Literal { kind: ErrWithGuar, .. }) parse("0b2", OtherError); @@ -158,9 +159,10 @@ pub fn run() { "' '", OtherError, ); - parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), OtherWithPanic); - - // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar - parse("/*a*/ 0b2 //", OtherError); + parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), NormalErr); } + + // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar + lit("/*a*/ 0b2 //", NormalErr); + stream("/*a*/ 0b2 //", OtherError); } diff --git a/tests/ui/proc-macro/nonfatal-parsing.stderr b/tests/ui/proc-macro/nonfatal-parsing.stderr index cb6e243eebc9b..f015794e2ce67 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stderr +++ b/tests/ui/proc-macro/nonfatal-parsing.stderr @@ -130,15 +130,6 @@ LL | nonfatal_parsing::run!(); | = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid digit for a base 2 literal - --> $DIR/nonfatal-parsing.rs:15:5 - | -LL | nonfatal_parsing::run!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) - error: found invalid character; only `#` is allowed in raw string delimitation: \u{0} --> :1:1 | @@ -199,6 +190,15 @@ error: invalid digit for a base 2 literal LL | /*a*/ 0b2 // | ^ +error: invalid digit for a base 2 literal + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + error: aborting due to 22 previous errors For more information about this error, try `rustc --explain E0768`. diff --git a/tests/ui/proc-macro/nonfatal-parsing.stdout b/tests/ui/proc-macro/nonfatal-parsing.stdout index a46ef66f0f9d0..fcefde22541ba 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stdout +++ b/tests/ui/proc-macro/nonfatal-parsing.stdout @@ -52,15 +52,19 @@ Err(LexError("not a literal")) Ok(TokenStream [Ident { ident: "r", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) Ok(TokenStream [Ident { ident: "c", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) Ok(TokenStream []) +Err(LexError("failed to parse to tokenstream")) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) +Err(LexError("failed to parse to tokenstream")) +Err(LexError("failed to parse to literal")) Ok(Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }) +Err(LexError("failed to parse to literal")) Err(LexError("comment or whitespace around literal")) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs new file mode 100644 index 0000000000000..d1e2a25b29521 --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +fn main() { + let f = |f: dyn Fn()| f; + //~^ ERROR the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + //~| ERROR return type cannot be a trait object without pointer indirection + f(); + //~^ ERROR this function takes 1 argument but 0 arguments were supplied +} diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr new file mode 100644 index 0000000000000..a20e5f331d31a --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:17 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | let f = |f: &dyn Fn()| f; + | + + +error[E0746]: return type cannot be a trait object without pointer indirection + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:27 + | +LL | let f = |f: dyn Fn()| f; + | ^ doesn't have a size known at compile-time + +error[E0057]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:8:5 + | +LL | f(); + | ^-- argument #1 of type `(dyn Fn() + 'static)` is missing + | +note: closure defined here + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:13 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^^^^^^ +help: provide the argument + | +LL | f(/* (dyn Fn() + 'static) */); + | ++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277, E0746. +For more information about an error, try `rustc --explain E0057`.