diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index ff5edfc799439..6265a06410d59 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -878,7 +878,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind)) } - LifetimeRes::Static { .. } | LifetimeRes::Error => return None, + LifetimeRes::Static { .. } | LifetimeRes::Error(..) => return None, res => panic!( "Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, ident.span @@ -1931,26 +1931,29 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { source: LifetimeSource, syntax: LifetimeSyntax, ) -> &'hir hir::Lifetime { - let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error); - let res = match res { - LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param), - LifetimeRes::Fresh { param, .. } => { - assert_eq!(ident.name, kw::UnderscoreLifetime); - let param = self.local_def_id(param); - hir::LifetimeKind::Param(param) - } - LifetimeRes::Infer => { - assert_eq!(ident.name, kw::UnderscoreLifetime); - hir::LifetimeKind::Infer - } - LifetimeRes::Static { .. } => { - assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime)); - hir::LifetimeKind::Static - } - LifetimeRes::Error => hir::LifetimeKind::Error, - LifetimeRes::ElidedAnchor { .. } => { - panic!("Unexpected `ElidedAnchar` {:?} at {:?}", ident, ident.span); + let res = if let Some(res) = self.resolver.get_lifetime_res(id) { + match res { + LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param), + LifetimeRes::Fresh { param, .. } => { + assert_eq!(ident.name, kw::UnderscoreLifetime); + let param = self.local_def_id(param); + hir::LifetimeKind::Param(param) + } + LifetimeRes::Infer => { + assert_eq!(ident.name, kw::UnderscoreLifetime); + hir::LifetimeKind::Infer + } + LifetimeRes::Static { .. } => { + assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime)); + hir::LifetimeKind::Static + } + LifetimeRes::Error(guar) => hir::LifetimeKind::Error(guar), + LifetimeRes::ElidedAnchor { .. } => { + panic!("Unexpected `ElidedAnchar` {:?} at {:?}", ident, ident.span); + } } + } else { + hir::LifetimeKind::Error(self.dcx().span_delayed_bug(ident.span, "unresolved lifetime")) }; debug!(?res); @@ -2014,12 +2017,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // AST resolution emitted an error on those parameters, so we lower them using // `ParamName::Error`. let ident = self.lower_ident(param.ident); - let param_name = - if let Some(LifetimeRes::Error) = self.resolver.get_lifetime_res(param.id) { - ParamName::Error(ident) - } else { - ParamName::Plain(ident) - }; + let param_name = if let Some(LifetimeRes::Error(..)) = + self.resolver.get_lifetime_res(param.id) + { + ParamName::Error(ident) + } else { + ParamName::Plain(ident) + }; let kind = hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index d535998a10816..2c94a708a90c8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use rustc_ast::{LitIntType, LitKind, MetaItemLit}; +use rustc_hir::LangItem; use rustc_hir::attrs::{ BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior, DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType, @@ -12,7 +13,7 @@ use rustc_span::Symbol; use super::prelude::*; use super::util::parse_single_integer; use crate::session_diagnostics::{ - AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, + AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem, }; pub(crate) struct RustcMainParser; @@ -626,6 +627,32 @@ impl SingleAttributeParser for RustcScalableVectorParser { } } +pub(crate) struct LangParser; + +impl SingleAttributeParser for LangParser { + const PATH: &[Symbol] = &[sym::lang]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes` + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(name) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + let Some(lang_item) = LangItem::from_name(name) else { + cx.emit_err(UnknownLangItem { span: cx.attr_span, name }); + return None; + }; + Some(AttributeKind::Lang(lang_item, cx.attr_span)) + } +} + pub(crate) struct RustcHasIncoherentInherentImplsParser; impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParser { @@ -641,6 +668,15 @@ impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParse const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls; } +pub(crate) struct PanicHandlerParser; + +impl NoArgsAttributeParser for PanicHandlerParser { + const PATH: &[Symbol] = &[sym::panic_handler]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes` + const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::Lang(LangItem::PanicImpl, span); +} + pub(crate) struct RustcHiddenTypeOfOpaquesParser; impl NoArgsAttributeParser for RustcHiddenTypeOfOpaquesParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index e000d68520739..e11bb66685399 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -181,6 +181,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, @@ -254,6 +255,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 2eb585671fff9..e98969dda300b 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1013,6 +1013,15 @@ pub(crate) struct DocAliasMalformed { pub span: Span, } +#[derive(Diagnostic)] +#[diag("definition of an unknown lang item: `{$name}`", code = E0522)] +pub(crate) struct UnknownLangItem { + #[primary_span] + #[label("definition of unknown lang item `{$name}`")] + pub span: Span, + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag("target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`")] pub(crate) struct UnsupportedInstructionSet<'a> { diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 0924ce25c52ea..45108bfcb79ba 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -182,8 +182,7 @@ impl LocalizedConstraintGraphVisitor for LoanLivenessVisitor<'_> { // // FIXME: analyze potential unsoundness, possibly in concert with a borrowck // implementation in a-mir-formality, fuzzing, or manually crafting counter-examples. - let location = self.liveness.location_from_point(node.point); - if self.liveness.is_live_at(node.region, location) { + if self.liveness.is_live_at_point(node.region, node.point) { self.live_loans.insert(node.point, loan); } } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 1dd3bc831f45a..c1fed1260340f 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -131,9 +131,17 @@ impl LivenessValues { } } - /// Returns whether `region` is marked live at the given `location`. + /// Returns whether `region` is marked live at the given + /// [`location`][rustc_middle::mir::Location]. pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool { let point = self.location_map.point_from_location(location); + self.is_live_at_point(region, point) + } + + /// Returns whether `region` is marked live at the given + /// [`point`][rustc_mir_dataflow::points::PointIndex]. + #[inline] + pub(crate) fn is_live_at_point(&self, region: RegionVid, point: PointIndex) -> bool { if let Some(points) = &self.points { points.row(region).is_some_and(|r| r.contains(point)) } else { diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 97d6cecf68481..1daf428acf766 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -106,6 +106,7 @@ fn codegen_global_asm_inner<'tcx>( match *piece { InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s), InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => { + use rustc_codegen_ssa::back::symbol_export::escape_symbol_name; match operands[operand_idx] { GlobalAsmOperandRef::Const { ref string } => { global_asm.push_str(string); @@ -121,7 +122,7 @@ fn codegen_global_asm_inner<'tcx>( let symbol = tcx.symbol_name(instance); // FIXME handle the case where the function was made private to the // current codegen unit - global_asm.push_str(symbol.name); + global_asm.push_str(&escape_symbol_name(tcx, symbol.name, span)); } GlobalAsmOperandRef::SymStatic { def_id } => { if cfg!(not(feature = "inline_asm_sym")) { @@ -133,7 +134,7 @@ fn codegen_global_asm_inner<'tcx>( let instance = Instance::mono(tcx, def_id); let symbol = tcx.symbol_name(instance); - global_asm.push_str(symbol.name); + global_asm.push_str(&escape_symbol_name(tcx, symbol.name, span)); } } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index fd25fba2daac7..80d77be1cc384 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -400,6 +400,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { match *piece { InlineAsmTemplatePiece::String(ref s) => template_str.push_str(s), InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => { + use rustc_codegen_ssa::back::symbol_export::escape_symbol_name; match operands[operand_idx] { GlobalAsmOperandRef::Const { ref string } => { // Const operands get injected directly into the @@ -414,7 +415,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { llvm::LLVMRustGetMangledName(llval, s); }) .expect("symbol is not valid UTF-8"); - template_str.push_str(&escape_symbol_name(self, symbol, span)); + template_str.push_str(&escape_symbol_name(self.tcx, &symbol, span)); } GlobalAsmOperandRef::SymStatic { def_id } => { let llval = self @@ -428,7 +429,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { llvm::LLVMRustGetMangledName(llval, s); }) .expect("symbol is not valid UTF-8"); - template_str.push_str(&escape_symbol_name(self, symbol, span)); + template_str.push_str(&escape_symbol_name(self.tcx, &symbol, span)); } } } @@ -1390,42 +1391,3 @@ fn llvm_fixup_output_type<'ll, 'tcx>( _ => layout.llvm_type(cx), } } - -fn escape_symbol_name<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, symbol: String, span: Span) -> String { - use rustc_target::spec::{Arch, BinaryFormat}; - if !symbol.is_empty() - && symbol.chars().all(|c| matches!(c, '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '$' | '.')) - { - return symbol; - } - if cx.tcx.sess.target.binary_format == BinaryFormat::Xcoff { - cx.tcx.sess.dcx().span_fatal( - span, - format!( - "symbol escaping is not supported for the binary format {}", - cx.tcx.sess.target.binary_format - ), - ); - } - if cx.tcx.sess.target.arch == Arch::Nvptx64 { - cx.tcx.sess.dcx().span_fatal( - span, - format!( - "symbol escaping is not supported for the architecture {}", - cx.tcx.sess.target.arch - ), - ); - } - let mut escaped_symbol = String::new(); - escaped_symbol.push('\"'); - for c in symbol.chars() { - match c { - '\n' => escaped_symbol.push_str("\\\n"), - '"' => escaped_symbol.push_str("\\\""), - '\\' => escaped_symbol.push_str("\\\\"), - c => escaped_symbol.push(c), - } - } - escaped_symbol.push('\"'); - escaped_symbol -} diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 838db689e7292..1a8fbf67f0bcc 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -41,7 +41,8 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { }); llvm::set_linkage(g, base::linkage_to_llvm(linkage)); - llvm::set_visibility(g, base::visibility_to_llvm(visibility)); + self.set_visibility(g, linkage, visibility); + self.assume_dso_local(g, false); let attrs = self.tcx.codegen_instance_attrs(instance.def); @@ -69,16 +70,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { { llvm::SetUniqueComdat(self.llmod, lldecl); } - - // If we're compiling the compiler-builtins crate, e.g., the equivalent of - // compiler-rt, then we want to implicitly compile everything with hidden - // visibility as we're going to link this object all over the place but - // don't want the symbols to get exported. - if linkage != Linkage::Internal && self.tcx.is_compiler_builtins(LOCAL_CRATE) { - llvm::set_visibility(lldecl, llvm::Visibility::Hidden); - } else { - llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility)); - } + self.set_visibility(lldecl, linkage, visibility); debug!("predefine_fn: instance = {:?}", instance); @@ -122,6 +114,18 @@ impl CodegenCx<'_, '_> { assume } + fn set_visibility(&self, lldecl: &llvm::Value, linkage: Linkage, visibility: Visibility) { + // If we're compiling the compiler-builtins crate, i.e., the equivalent of + // compiler-rt, then we want to implicitly compile everything with hidden + // visibility as we're going to link this object all over the place but + // don't want the symbols to get exported. + if linkage != Linkage::Internal && self.tcx.is_compiler_builtins(LOCAL_CRATE) { + llvm::set_visibility(lldecl, llvm::Visibility::Hidden); + } else { + llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility)); + } + } + fn should_assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool { let linkage = llvm::get_linkage(llval); let visibility = llvm::get_visibility(llval); diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index a491f30fd9161..460f4afea963f 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1122,7 +1122,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( AllowHigherAlign::Yes, ForceRightAdjust::No, ), - Arch::Wasm32 => emit_ptr_va_arg( + Arch::Wasm32 | Arch::Wasm64 => emit_ptr_va_arg( bx, addr, target_ty, @@ -1135,7 +1135,6 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( AllowHigherAlign::Yes, ForceRightAdjust::No, ), - Arch::Wasm64 => bug!("c-variadic functions are not fully implemented for wasm64"), Arch::CSky => emit_ptr_va_arg( bx, addr, diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index c6e9456919215..557b00b911aa9 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -14,6 +14,7 @@ use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Instance, SymbolName, Ty, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::config::CrateType; +use rustc_span::Span; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{Arch, Os, TlsModel}; use tracing::debug; @@ -764,3 +765,43 @@ fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap { ret } + +pub fn escape_symbol_name(tcx: TyCtxt<'_>, symbol: &str, span: Span) -> String { + // https://github.com/llvm/llvm-project/blob/a55fbab0cffc9b4af497b9e4f187b61143743e06/llvm/lib/MC/MCSymbol.cpp + use rustc_target::spec::{Arch, BinaryFormat}; + if !symbol.is_empty() + && symbol.chars().all(|c| matches!(c, '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '$' | '.')) + { + return symbol.to_string(); + } + if tcx.sess.target.binary_format == BinaryFormat::Xcoff { + tcx.sess.dcx().span_fatal( + span, + format!( + "symbol escaping is not supported for the binary format {}", + tcx.sess.target.binary_format + ), + ); + } + if tcx.sess.target.arch == Arch::Nvptx64 { + tcx.sess.dcx().span_fatal( + span, + format!( + "symbol escaping is not supported for the architecture {}", + tcx.sess.target.arch + ), + ); + } + let mut escaped_symbol = String::new(); + escaped_symbol.push('\"'); + for c in symbol.chars() { + match c { + '\n' => escaped_symbol.push_str("\\\n"), + '"' => escaped_symbol.push_str("\\\""), + '\\' => escaped_symbol.push_str("\\\\"), + c => escaped_symbol.push(c), + } + } + escaped_symbol.push('\"'); + escaped_symbol +} diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 9ecb7ab9fd457..edd73f418036b 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -8,7 +8,7 @@ use rustc_hir::attrs::{ }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items}; +use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, }; @@ -504,7 +504,7 @@ fn handle_lang_items( attrs: &[Attribute], codegen_fn_attrs: &mut CodegenFnAttrs, ) { - let lang_item = lang_items::extract(attrs).and_then(|(name, _)| LangItem::from_name(name)); + let lang_item = find_attr!(attrs, AttributeKind::Lang(lang, _) => lang); // Weak lang items have the same semantics as "std internal" symbols in the // sense that they're preserved through all our LTO passes and only diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 4bbb7470debe0..f12410c65d986 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,5 +1,6 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_hir::attrs::{InstructionSetAttr, Linkage}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::{MonoItemData, Visibility}; use rustc_middle::mir::{InlineAsmOperand, START_BLOCK}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; @@ -128,6 +129,15 @@ fn prefix_and_suffix<'tcx>( let is_arm = tcx.sess.target.arch == Arch::Arm; let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); + // If we're compiling the compiler-builtins crate, e.g., the equivalent of + // compiler-rt, then we want to implicitly compile everything with hidden + // visibility as we're going to link this object all over the place but + // don't want the symbols to get exported. For naked asm we set the visibility here. + let mut visibility = item_data.visibility; + if item_data.linkage != Linkage::Internal && tcx.is_compiler_builtins(LOCAL_CRATE) { + visibility = Visibility::Hidden; + } + let attrs = tcx.codegen_instance_attrs(instance.def); let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); @@ -217,7 +227,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); - match item_data.visibility { + match visibility { Visibility::Default => {} Visibility::Protected => writeln!(begin, ".protected {asm_name}").unwrap(), Visibility::Hidden => writeln!(begin, ".hidden {asm_name}").unwrap(), @@ -243,7 +253,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); - match item_data.visibility { + match visibility { Visibility::Default | Visibility::Protected => {} Visibility::Hidden => writeln!(begin, ".private_extern {asm_name}").unwrap(), } @@ -280,7 +290,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".section {section},\"\",@").unwrap(); // wasm functions cannot be aligned, so skip write_linkage(&mut begin).unwrap(); - if let Visibility::Hidden = item_data.visibility { + if let Visibility::Hidden = visibility { writeln!(begin, ".hidden {asm_name}").unwrap(); } writeln!(begin, ".type {asm_name}, @function").unwrap(); @@ -313,7 +323,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".align {}", align_bytes).unwrap(); write_linkage(&mut begin).unwrap(); - if let Visibility::Hidden = item_data.visibility { + if let Visibility::Hidden = visibility { // FIXME apparently `.globl {asm_name}, hidden` is valid // but due to limitations with `.weak` (see above) we can't really use that in general yet } diff --git a/compiler/rustc_error_codes/src/error_codes/E0264.md b/compiler/rustc_error_codes/src/error_codes/E0264.md index 33ddf3405acca..b8d0f7d4d6af0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0264.md +++ b/compiler/rustc_error_codes/src/error_codes/E0264.md @@ -7,8 +7,8 @@ Erroneous code example: #![allow(internal_features)] extern "C" { - #[lang = "cake"] // error: unknown external lang item: `cake` - fn cake(); + #[lang = "copy"] // error: unknown external lang item: `copy` + fn copy(); } ``` diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 568d189becf7f..8d9b53498934e 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -8,6 +8,7 @@ use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrStyle, Path, ast}; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; +use rustc_hir::LangItem; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; @@ -953,6 +954,9 @@ pub enum AttributeKind { /// Represents `#[instruction_set]` InstructionSet(InstructionSetAttr), + /// Represents `#[lang]` + Lang(LangItem, Span), + /// Represents `#[link]`. Link(ThinVec, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 3ee4785420fa2..ec4d543fdc208 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -50,6 +50,7 @@ impl AttributeKind { Ignore { .. } => No, Inline(..) => No, InstructionSet(..) => No, + Lang(..) => Yes, Link(..) => No, LinkName { .. } => Yes, // Needed for rustdoc LinkOrdinal { .. } => No, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index e93deaa84944e..dd2cd4939432b 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -990,7 +990,7 @@ pub enum LifetimeRes { /// `'static` lifetime. Static, /// Resolution failure. - Error, + Error(rustc_span::ErrorGuaranteed), /// HACK: This is used to recover the NodeId of an elided lifetime. ElidedAnchor { start: NodeId, end: NodeId }, } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 68fc7653b6368..f1f7350c1e790 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -238,7 +238,7 @@ pub enum LifetimeKind { /// Indicates an error during lowering (usually `'_` in wrong place) /// that was already reported. - Error, + Error(ErrorGuaranteed), /// User wrote an anonymous lifetime, either `'_` or nothing (which gets /// converted to `'_`). The semantics of this lifetime should be inferred @@ -258,7 +258,7 @@ impl LifetimeKind { // -- but this is because, as far as the code in the compiler is // concerned -- `Fresh` variants act equivalently to "some fresh name". // They correspond to early-bound regions on an impl, in other words. - LifetimeKind::Error | LifetimeKind::Param(..) | LifetimeKind::Static => false, + LifetimeKind::Error(..) | LifetimeKind::Param(..) | LifetimeKind::Static => false, } } } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 557f76208bfe6..0325fd2ceab94 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -7,12 +7,12 @@ //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. //! * Functions called by the compiler itself. -use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic}; -use rustc_span::{Span, Symbol, kw, sym}; +use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic, PrintAttribute}; +use rustc_span::{Symbol, kw, sym}; +use crate::attrs::PrintAttribute; use crate::def_id::DefId; use crate::{MethodKind, Target}; @@ -75,7 +75,7 @@ macro_rules! language_item_table { $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )* ) => { /// A representation of all the valid lang items in Rust. - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, BlobDecodable)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, BlobDecodable, PrintAttribute)] pub enum LangItem { $( #[doc = concat!("The `", stringify!($name), "` lang item.")] @@ -150,18 +150,6 @@ impl HashStable for LangItem { } } -/// Extracts the first `lang = "$name"` out of a list of attributes. -/// The `#[panic_handler]` attribute is also extracted out when found. -pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { - attrs.iter().find_map(|attr| { - Some(match attr { - _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()), - _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()), - _ => return None, - }) - }) -} - language_item_table! { // Variant name, Name, Getter method name, Target Generic requirements; Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 3e687700f11c6..ff919bd58ba16 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -663,7 +663,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { LifetimeKind::Param(def_id) => { self.resolve_lifetime_ref(def_id, lt); } - LifetimeKind::Error => {} + LifetimeKind::Error(..) => {} LifetimeKind::ImplicitObjectLifetimeDefault | LifetimeKind::Infer | LifetimeKind::Static => { @@ -804,7 +804,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // If the user wrote an explicit name, use that. self.visit_lifetime(&*lifetime); } - LifetimeKind::Error => {} + LifetimeKind::Error(..) => {} } } hir::TyKind::Ref(lifetime_ref, ref mt) => { @@ -891,8 +891,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { hir::LifetimeKind::Param(param_def_id) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } - // If we've already reported an error, just ignore `lifetime_ref`. - hir::LifetimeKind::Error => {} + // Keep track of lifetimes about which errors have already been reported + hir::LifetimeKind::Error(guar) => { + self.insert_lifetime(lifetime_ref, ResolvedArg::Error(guar)) + } // Those will be resolved by typechecking. hir::LifetimeKind::ImplicitObjectLifetimeDefault | hir::LifetimeKind::Infer => {} } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index a0b9b60cde5c5..00765498b061b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -451,17 +451,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else { let reason = if let hir::LifetimeKind::ImplicitObjectLifetimeDefault = lifetime.kind { - if let hir::Node::Ty(hir::Ty { - kind: hir::TyKind::Ref(parent_lifetime, _), - .. - }) = tcx.parent_hir_node(hir_id) - && tcx.named_bound_var(parent_lifetime.hir_id).is_none() - { - // Parent lifetime must have failed to resolve. Don't emit a redundant error. - RegionInferReason::ExplicitObjectLifetime - } else { - RegionInferReason::ObjectLifetimeDefault(span.shrink_to_hi()) - } + RegionInferReason::ObjectLifetimeDefault(span.shrink_to_hi()) } else { RegionInferReason::ExplicitObjectLifetime }; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2d76363464a77..3362ea667b931 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -256,6 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Fundamental | AttributeKind::Ignore { .. } | AttributeKind::InstructionSet(..) + | AttributeKind::Lang(..) | AttributeKind::LinkName { .. } | AttributeKind::LinkOrdinal { .. } | AttributeKind::LinkSection { .. } @@ -394,8 +395,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::deny | sym::forbid // internal - | sym::panic_handler - | sym::lang | sym::default_lib_allocator | sym::rustc_nonnull_optimization_guaranteed | sym::rustc_inherit_overflow_checks @@ -784,15 +783,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Fn => { // `#[track_caller]` is not valid on weak lang items because they are called via // `extern` declarations and `#[track_caller]` would alter their ABI. - if let Some((lang_item, _)) = hir::lang_items::extract(attrs) - && let Some(item) = hir::LangItem::from_name(lang_item) + if let Some(item) = find_attr!(attrs, AttributeKind::Lang(item, _) => item) && item.is_weak() { let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); self.dcx().emit_err(errors::LangItemWithTrackCaller { attr_span, - name: lang_item, + name: item.name(), sig_span: sig.span, }); } @@ -856,7 +854,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) | Target::Fn => { // `#[target_feature]` is not allowed in lang items. - if let Some((lang_item, _)) = hir::lang_items::extract(attrs) + if let Some(lang_item) = find_attr!(attrs, AttributeKind::Lang(lang, _) => lang) // Calling functions with `#[target_feature]` is // not unsafe on WASM, see #84988 && !self.tcx.sess.target.is_like_wasm @@ -866,7 +864,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::LangItemWithTargetFeature { attr_span, - name: lang_item, + name: lang_item.name(), sig_span: sig.span, }); } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 75bac9eff2c71..e56d27721bddd 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::DEAD_CODE; use rustc_session::lint::{self, LintExpectationId}; -use rustc_span::{Symbol, kw, sym}; +use rustc_span::{Symbol, kw}; use crate::errors::{ ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, @@ -706,12 +706,6 @@ fn has_allow_dead_code_or_lang_attr( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> Option { - fn has_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - tcx.has_attr(def_id, sym::lang) - // Stable attribute for #[lang = "panic_impl"] - || tcx.has_attr(def_id, sym::panic_handler) - } - fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let hir_id = tcx.local_def_id_to_hir_id(def_id); let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level; @@ -732,7 +726,9 @@ fn has_allow_dead_code_or_lang_attr( if has_allow_expect_dead_code(tcx, def_id) { Some(ComesFromAllowExpect::Yes) - } else if has_used_like_attr(tcx, def_id) || has_lang_attr(tcx, def_id) { + } else if has_used_like_attr(tcx, def_id) + || find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Lang(..)) + { Some(ComesFromAllowExpect::No) } else { None diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 8a70302c4c4e8..128a07292a03f 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -465,15 +465,6 @@ pub(crate) struct LangItemOnIncorrectTarget { pub actual_target: Target, } -#[derive(Diagnostic)] -#[diag("definition of an unknown lang item: `{$name}`", code = E0522)] -pub(crate) struct UnknownLangItem { - #[primary_span] - #[label("definition of unknown lang item `{$name}`")] - pub span: Span, - pub name: Symbol, -} - pub(crate) struct InvalidAttrAtCrateLevel { pub span: Span, pub sugg_span: Option, diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index b9417af13b113..25aea8e9f82a0 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -11,16 +11,15 @@ use rustc_ast as ast; use rustc_ast::visit; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::lang_items::{GenericRequirement, extract}; +use rustc_hir::lang_items::GenericRequirement; use rustc_hir::{LangItem, LanguageItems, MethodKind, Target}; use rustc_middle::query::Providers; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::cstore::ExternCrate; -use rustc_span::Span; +use rustc_span::{Span, Symbol, sym}; use crate::errors::{ DuplicateLangItem, IncorrectCrateType, IncorrectTarget, LangItemOnIncorrectTarget, - UnknownLangItem, }; use crate::weak_lang_items; @@ -62,7 +61,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { item_span: Span, generics: Option<&'ast ast::Generics>, ) { - if let Some((name, attr_span)) = extract(attrs) { + if let Some((name, attr_span)) = extract_ast(attrs) { match LangItem::from_name(name) { // Known lang item with attribute on correct target. Some(lang_item) if actual_target == lang_item.target() => { @@ -86,7 +85,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { } // Unknown lang item. _ => { - self.tcx.dcx().emit_err(UnknownLangItem { span: attr_span, name }); + self.tcx.dcx().delayed_bug("unknown lang item"); } } } @@ -359,6 +358,20 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { } } +/// Extracts the first `lang = "$name"` out of a list of attributes. +/// The `#[panic_handler]` attribute is also extracted out when found. +/// +/// This function is used for `ast::Attribute`, for `hir::Attribute` use the `find_attr!` macro with `AttributeKind::Lang` +pub(crate) fn extract_ast(attrs: &[rustc_ast::ast::Attribute]) -> Option<(Symbol, Span)> { + attrs.iter().find_map(|attr| { + Some(match attr { + _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()), + _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()), + _ => return None, + }) + }) +} + pub(crate) fn provide(providers: &mut Providers) { providers.get_lang_items = get_lang_items; } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 811b5e9300141..4200003ea1d1a 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -13,6 +13,7 @@ use rustc_target::spec::Os; use crate::errors::{ MissingLangItem, MissingPanicHandler, PanicUnwindWithoutStd, UnknownExternLangItem, }; +use crate::lang_items::extract_ast; /// Checks the crate for usage of weak lang items, returning a vector of all the /// lang items required by this crate, but not defined yet. @@ -46,7 +47,7 @@ struct WeakLangItemVisitor<'a, 'tcx> { impl<'ast> visit::Visitor<'ast> for WeakLangItemVisitor<'_, '_> { fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { - if let Some((lang_item, _)) = lang_items::extract(&i.attrs) { + if let Some((lang_item, _)) = extract_ast(&i.attrs) { if let Some(item) = LangItem::from_name(lang_item) && item.is_weak() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index fe6346f6c6e92..0b92792611a11 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -9,13 +9,14 @@ use std::borrow::Cow; use std::collections::hash_map::Entry; use std::mem::{replace, swap, take}; -use std::ops::ControlFlow; +use std::ops::{ControlFlow, Range}; use rustc_ast::visit::{ AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list, }; use rustc_ast::*; use rustc_data_structures::debug_assert_matches; +use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; @@ -746,7 +747,8 @@ pub(crate) struct DiagMetadata<'ast> { /// Accumulate the errors due to missed lifetime elision, /// and report them all at once for each function. - current_elision_failures: Vec, + current_elision_failures: + Vec<(MissingLifetime, LifetimeElisionCandidate, Either>)>, } struct LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { @@ -1782,19 +1784,19 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match rib.kind { LifetimeRibKind::Item => break, LifetimeRibKind::ConstParamTy => { - self.emit_non_static_lt_in_const_param_ty_error(lifetime); + let guar = self.emit_non_static_lt_in_const_param_ty_error(lifetime); self.record_lifetime_res( lifetime.id, - LifetimeRes::Error, + LifetimeRes::Error(guar), LifetimeElisionCandidate::Ignore, ); return; } LifetimeRibKind::ConcreteAnonConst(cause) => { - self.emit_forbidden_non_static_lifetime_error(cause, lifetime); + let guar = self.emit_forbidden_non_static_lifetime_error(cause, lifetime); self.record_lifetime_res( lifetime.id, - LifetimeRes::Error, + LifetimeRes::Error(guar), LifetimeElisionCandidate::Ignore, ); return; @@ -1812,8 +1814,12 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let outer_res = lifetime_rib_iter .find_map(|rib| rib.bindings.get_key_value(&normalized_ident).map(|(&outer, _)| outer)); - self.emit_undeclared_lifetime_error(lifetime, outer_res); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named); + let guar = self.emit_undeclared_lifetime_error(lifetime, outer_res); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error(guar), + LifetimeElisionCandidate::Named, + ); } #[instrument(level = "debug", skip(self))] @@ -1878,7 +1884,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } LifetimeRibKind::AnonymousReportError => { - if elided { + let guar = if elided { let suggestion = self.lifetime_ribs[i..].iter().rev().find_map(|rib| { if let LifetimeRibKind::Generics { span, @@ -1910,7 +1916,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.r.dcx().emit_err(errors::LendingIteratorReportError { lifetime: lifetime.ident.span, ty: ty.span, - }); + }) } else { let decl = if !trait_id.is_local() && let Some(assoc) = self.diag_metadata.current_impl_item @@ -1940,20 +1946,24 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }, ); self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span); - err.emit(); + err.emit() } } else { self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError { span: lifetime.ident.span, suggestion, - }); + }) } } else { self.r.dcx().emit_err(errors::ExplicitAnonymousLifetimeReportError { span: lifetime.ident.span, - }); + }) }; - self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error(guar), + elision_candidate, + ); return; } LifetimeRibKind::Elided(res) => { @@ -1961,8 +1971,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { return; } LifetimeRibKind::ElisionFailure => { - self.diag_metadata.current_elision_failures.push(missing_lifetime); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); + self.diag_metadata.current_elision_failures.push(( + missing_lifetime, + elision_candidate, + Either::Left(lifetime.id), + )); return; } LifetimeRibKind::Item => break, @@ -1973,8 +1986,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } } - self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); - self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); + let guar = self.report_missing_lifetime_specifiers([&missing_lifetime], None); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error(guar), elision_candidate); } fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) { @@ -2222,16 +2235,17 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { !segment.has_generic_args, elided_lifetime_span, ); - self.r.dcx().emit_err(errors::ImplicitElidedLifetimeNotAllowedHere { - span: path_span, - subdiag, - }); + let guar = + self.r.dcx().emit_err(errors::ImplicitElidedLifetimeNotAllowedHere { + span: path_span, + subdiag, + }); should_lint = false; for id in node_ids { self.record_lifetime_res( id, - LifetimeRes::Error, + LifetimeRes::Error(guar), LifetimeElisionCandidate::Named, ); } @@ -2263,14 +2277,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { break; } LifetimeRibKind::ElisionFailure => { - self.diag_metadata.current_elision_failures.push(missing_lifetime); - for id in node_ids { - self.record_lifetime_res( - id, - LifetimeRes::Error, - LifetimeElisionCandidate::Ignore, - ); - } + self.diag_metadata.current_elision_failures.push(( + missing_lifetime, + LifetimeElisionCandidate::Ignore, + Either::Right(node_ids), + )); break; } // `LifetimeRes::Error`, which would usually be used in the case of @@ -2278,14 +2289,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // we simply resolve to an implicit lifetime, which will be checked later, at // which point a suitable error will be emitted. LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { + let guar = + self.report_missing_lifetime_specifiers([&missing_lifetime], None); for id in node_ids { self.record_lifetime_res( id, - LifetimeRes::Error, + LifetimeRes::Error(guar), LifetimeElisionCandidate::Ignore, ); } - self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); break; } LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstParamTy => {} @@ -2329,7 +2341,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { candidates.push((res, candidate)); } } - LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} + LifetimeRes::Infer | LifetimeRes::Error(..) | LifetimeRes::ElidedAnchor { .. } => {} } } @@ -2373,7 +2385,23 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { replace(&mut this.diag_metadata.current_elision_failures, outer_failures); if !elision_failures.is_empty() { let Err(failure_info) = elision_lifetime else { bug!() }; - this.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); + let guar = this.report_missing_lifetime_specifiers( + elision_failures.iter().map(|(missing_lifetime, ..)| missing_lifetime), + Some(failure_info), + ); + let mut record_res = |lifetime, candidate| { + this.record_lifetime_res(lifetime, LifetimeRes::Error(guar), candidate) + }; + for (_, candidate, nodes) in elision_failures { + match nodes { + Either::Left(node_id) => record_res(node_id, candidate), + Either::Right(node_ids) => { + for lifetime in node_ids { + record_res(lifetime, candidate) + } + } + } + } } }); } @@ -3025,9 +3053,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { if let GenericParamKind::Lifetime = param.kind && let Some(&original) = seen_lifetimes.get(&ident) { - diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident); + let guar = diagnostics::signal_lifetime_shadowing( + self.r.tcx.sess, + original, + param.ident, + ); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error(guar)); continue; } @@ -3035,11 +3067,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { Entry::Occupied(entry) => { let span = *entry.get(); let err = ResolutionError::NameAlreadyUsedInParameterList(ident, span); - self.report_error(param.ident.span, err); + let guar = self.r.report_error(param.ident.span, err); let rib = match param.kind { GenericParamKind::Lifetime => { // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error(guar)); continue; } GenericParamKind::Type { .. } => &mut function_type_rib, @@ -3068,22 +3100,23 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .iter() .any(|span| span == param.span()); - self.r + let guar = self + .r .dcx() .create_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }) .emit_unless_delay(is_raw_underscore_lifetime); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error(guar)); continue; } if param.ident.name == kw::StaticLifetime { - self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { + let guar = self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { span: param.ident.span, lifetime: param.ident, }); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error(guar)); continue; } @@ -5442,6 +5475,32 @@ fn create_delegation_attrs(attrs: &[Attribute]) -> DelegationAttrs { DelegationAttrs { flags, to_inherit: to_inherit_attrs } } +fn required_generic_args_suggestion(generics: &ast::Generics) -> Option { + let required = generics + .params + .iter() + .filter_map(|param| match ¶m.kind { + ast::GenericParamKind::Lifetime => Some("'_"), + ast::GenericParamKind::Type { default } => { + if default.is_none() { + Some("_") + } else { + None + } + } + ast::GenericParamKind::Const { default, .. } => { + if default.is_none() { + Some("_") + } else { + None + } + } + }) + .collect::>(); + + if required.is_empty() { None } else { Some(format!("<{}>", required.join(", "))) } +} + impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { fn visit_item(&mut self, item: &'ast Item) { match &item.kind { @@ -5500,6 +5559,13 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { self.collect_fn_info(sig.header, &sig.decl, item.id, &item.attrs); } + + if let AssocItemKind::Type(box ast::TyAlias { generics, .. }) = &item.kind { + let def_id = self.r.local_def_id(item.id); + if let Some(suggestion) = required_generic_args_suggestion(generics) { + self.r.item_required_generic_args_suggestions.insert(def_id, suggestion); + } + } visit::walk_assoc_item(self, item, ctxt); } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index ec577a1aea83f..94f8444db142d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -9,8 +9,8 @@ use rustc_ast::{ self as ast, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind, }; -use rustc_ast_pretty::pprust::where_bound_predicate_to_string; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_ast_pretty::pprust::{path_to_string, where_bound_predicate_to_string}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize, @@ -79,6 +79,23 @@ fn is_self_value(path: &[Segment], namespace: Namespace) -> bool { namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower } +fn path_to_string_without_assoc_item_bindings(path: &Path) -> String { + let mut path = path.clone(); + for segment in &mut path.segments { + let mut remove_args = false; + if let Some(args) = segment.args.as_deref_mut() + && let ast::GenericArgs::AngleBracketed(angle_bracketed) = args + { + angle_bracketed.args.retain(|arg| matches!(arg, ast::AngleBracketedArg::Arg(_))); + remove_args = angle_bracketed.args.is_empty(); + } + if remove_args { + segment.args = None; + } + } + path_to_string(&path) +} + /// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant. fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) { let variant_path = &suggestion.path; @@ -131,7 +148,7 @@ pub(super) struct ElisionFnParameter { /// Description of lifetimes that appear as candidates for elision. /// This is used to suggest introducing an explicit lifetime. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub(super) enum LifetimeElisionCandidate { /// This is not a real lifetime. Ignore, @@ -169,6 +186,201 @@ impl TypoCandidate { } impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { + fn trait_assoc_type_def_id_by_name( + &mut self, + trait_def_id: DefId, + assoc_name: Symbol, + ) -> Option { + let module = self.r.get_module(trait_def_id)?; + self.r.resolutions(module).borrow().iter().find_map(|(key, resolution)| { + if key.ident.name != assoc_name { + return None; + } + let resolution = resolution.borrow(); + let binding = resolution.best_decl()?; + match binding.res() { + Res::Def(DefKind::AssocTy, def_id) => Some(def_id), + _ => None, + } + }) + } + + /// This does best-effort work to generate suggestions for associated types. + fn suggest_assoc_type_from_bounds( + &mut self, + err: &mut Diag<'_>, + source: PathSource<'_, 'ast, 'ra>, + path: &[Segment], + ident_span: Span, + ) -> bool { + // Filter out cases where we cannot emit meaningful suggestions. + if source.namespace() != TypeNS { + return false; + } + let [segment] = path else { return false }; + if segment.has_generic_args { + return false; + } + if !ident_span.can_be_used_for_suggestions() { + return false; + } + let assoc_name = segment.ident.name; + if assoc_name == kw::Underscore { + return false; + } + + // Map: type parameter name -> (trait def id -> (assoc type def id, trait paths as written)). + // We keep a set of paths per trait so we can detect cases like + // `T: Trait + Trait` where suggesting `T::Assoc` would be ambiguous. + let mut matching_bounds: FxIndexMap< + Symbol, + FxIndexMap)>, + > = FxIndexMap::default(); + + let mut record_bound = |this: &mut Self, + ty_param: Symbol, + poly_trait_ref: &ast::PolyTraitRef| { + // Avoid generating suggestions we can't print in a well-formed way. + if !poly_trait_ref.bound_generic_params.is_empty() { + return; + } + if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE { + return; + } + let Some(trait_seg) = poly_trait_ref.trait_ref.path.segments.last() else { + return; + }; + let Some(partial_res) = this.r.partial_res_map.get(&trait_seg.id) else { + return; + }; + let Some(trait_def_id) = partial_res.full_res().and_then(|res| res.opt_def_id()) else { + return; + }; + let Some(assoc_type_def_id) = + this.trait_assoc_type_def_id_by_name(trait_def_id, assoc_name) + else { + return; + }; + + // Preserve `::` and generic args so we don't generate broken suggestions like + // `::Assoc` for bounds written as `T: ::Foo<'a>`, while stripping + // associated-item bindings that are rejected in qualified paths. + let trait_path = + path_to_string_without_assoc_item_bindings(&poly_trait_ref.trait_ref.path); + let trait_bounds = matching_bounds.entry(ty_param).or_default(); + let trait_bounds = trait_bounds + .entry(trait_def_id) + .or_insert_with(|| (assoc_type_def_id, FxIndexSet::default())); + debug_assert_eq!(trait_bounds.0, assoc_type_def_id); + trait_bounds.1.insert(trait_path); + }; + + let mut record_from_generics = |this: &mut Self, generics: &ast::Generics| { + for param in &generics.params { + let ast::GenericParamKind::Type { .. } = param.kind else { continue }; + for bound in ¶m.bounds { + let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue }; + record_bound(this, param.ident.name, poly_trait_ref); + } + } + + for predicate in &generics.where_clause.predicates { + let ast::WherePredicateKind::BoundPredicate(where_bound) = &predicate.kind else { + continue; + }; + + let ast::TyKind::Path(None, bounded_path) = &where_bound.bounded_ty.kind else { + continue; + }; + let [ast::PathSegment { ident, args: None, .. }] = &bounded_path.segments[..] + else { + continue; + }; + + // Only suggest for bounds that are explicitly on an in-scope type parameter. + let Some(partial_res) = this.r.partial_res_map.get(&where_bound.bounded_ty.id) + else { + continue; + }; + if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) { + continue; + } + + for bound in &where_bound.bounds { + let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue }; + record_bound(this, ident.name, poly_trait_ref); + } + } + }; + + if let Some(item) = self.diag_metadata.current_item + && let Some(generics) = item.kind.generics() + { + record_from_generics(self, generics); + } + + if let Some(item) = self.diag_metadata.current_item + && matches!(item.kind, ItemKind::Impl(..)) + && let Some(assoc) = self.diag_metadata.current_impl_item + { + let generics = match &assoc.kind { + AssocItemKind::Const(box ast::ConstItem { generics, .. }) + | AssocItemKind::Fn(box ast::Fn { generics, .. }) + | AssocItemKind::Type(box ast::TyAlias { generics, .. }) => Some(generics), + AssocItemKind::Delegation(..) + | AssocItemKind::MacCall(..) + | AssocItemKind::DelegationMac(..) => None, + }; + if let Some(generics) = generics { + record_from_generics(self, generics); + } + } + + let mut suggestions: FxIndexSet = FxIndexSet::default(); + for (ty_param, traits) in matching_bounds { + let ty_param = ty_param.to_ident_string(); + let trait_paths_len: usize = traits.values().map(|(_, paths)| paths.len()).sum(); + if traits.len() == 1 && trait_paths_len == 1 { + let assoc_type_def_id = traits.values().next().unwrap().0; + let assoc_segment = format!( + "{}{}", + assoc_name, + self.r.item_required_generic_args_suggestion(assoc_type_def_id) + ); + suggestions.insert(format!("{ty_param}::{assoc_segment}")); + } else { + for (assoc_type_def_id, trait_paths) in traits.into_values() { + let assoc_segment = format!( + "{}{}", + assoc_name, + self.r.item_required_generic_args_suggestion(assoc_type_def_id) + ); + for trait_path in trait_paths { + suggestions + .insert(format!("<{ty_param} as {trait_path}>::{assoc_segment}")); + } + } + } + } + + if suggestions.is_empty() { + return false; + } + + let mut suggestions: Vec = suggestions.into_iter().collect(); + suggestions.sort(); + + err.span_suggestions_with_style( + ident_span, + "you might have meant to use an associated type of the same name", + suggestions, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + + true + } + fn make_base_error( &mut self, path: &[Segment], @@ -1038,6 +1250,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ) -> bool { let is_expected = &|res| source.is_expected(res); let ident_span = path.last().map_or(span, |ident| ident.ident.span); + + // Prefer suggestions based on associated types from in-scope bounds (e.g. `T::Item`) + // over purely edit-distance-based identifier suggestions. + // Otherwise suggestions could be verbose. + if self.suggest_assoc_type_from_bounds(err, source, path, ident_span) { + return false; + } + let typo_sugg = self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected); let mut fallback = false; @@ -3274,7 +3494,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &self, lifetime_ref: &ast::Lifetime, outer_lifetime_ref: Option, - ) { + ) -> ErrorGuaranteed { debug_assert_ne!(lifetime_ref.ident.name, kw::UnderscoreLifetime); let mut err = if let Some(outer) = outer_lifetime_ref { struct_span_code_err!( @@ -3319,7 +3539,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ); } - err.emit(); + err.emit() } fn suggest_introducing_lifetime( @@ -3473,14 +3693,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } - pub(crate) fn emit_non_static_lt_in_const_param_ty_error(&self, lifetime_ref: &ast::Lifetime) { + pub(crate) fn emit_non_static_lt_in_const_param_ty_error( + &self, + lifetime_ref: &ast::Lifetime, + ) -> ErrorGuaranteed { self.r .dcx() .create_err(errors::ParamInTyOfConstParam { span: lifetime_ref.ident.span, name: lifetime_ref.ident.name, }) - .emit(); + .emit() } /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. @@ -3490,18 +3713,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &self, cause: NoConstantGenericsReason, lifetime_ref: &ast::Lifetime, - ) { + ) -> ErrorGuaranteed { match cause { - NoConstantGenericsReason::IsEnumDiscriminant => { - self.r - .dcx() - .create_err(errors::ParamInEnumDiscriminant { - span: lifetime_ref.ident.span, - name: lifetime_ref.ident.name, - param_kind: errors::ParamKindInEnumDiscriminant::Lifetime, - }) - .emit(); - } + NoConstantGenericsReason::IsEnumDiscriminant => self + .r + .dcx() + .create_err(errors::ParamInEnumDiscriminant { + span: lifetime_ref.ident.span, + name: lifetime_ref.ident.name, + param_kind: errors::ParamKindInEnumDiscriminant::Lifetime, + }) + .emit(), NoConstantGenericsReason::NonTrivialConstArg => { assert!(!self.r.tcx.features().generic_const_exprs()); self.r @@ -3512,18 +3734,18 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { param_kind: errors::ParamKindInNonTrivialAnonConst::Lifetime, help: self.r.tcx.sess.is_nightly_build(), }) - .emit(); + .emit() } } } - pub(crate) fn report_missing_lifetime_specifiers( + pub(crate) fn report_missing_lifetime_specifiers<'a>( &mut self, - lifetime_refs: Vec, + lifetime_refs: impl Clone + IntoIterator, function_param_lifetimes: Option<(Vec, Vec)>, ) -> ErrorGuaranteed { - let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum(); - let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); + let num_lifetimes: usize = lifetime_refs.clone().into_iter().map(|lt| lt.count).sum(); + let spans: Vec<_> = lifetime_refs.clone().into_iter().map(|lt| lt.span).collect(); let mut err = struct_span_code_err!( self.r.dcx(), @@ -3540,13 +3762,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { err.emit() } - fn add_missing_lifetime_specifiers_label( + fn add_missing_lifetime_specifiers_label<'a>( &mut self, err: &mut Diag<'_>, - lifetime_refs: Vec, + lifetime_refs: impl Clone + IntoIterator, function_param_lifetimes: Option<(Vec, Vec)>, ) { - for < in &lifetime_refs { + for < in lifetime_refs.clone() { err.span_label( lt.span, format!( @@ -3675,7 +3897,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { (lt.span.shrink_to_hi(), sugg) } }; - for < in &lifetime_refs { + for < in lifetime_refs.clone() { spans_suggs.push(build_sugg(lt)); } debug!(?spans_suggs); @@ -3703,7 +3925,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } 1 => { let post = if maybe_static { - let owned = if let [lt] = &lifetime_refs[..] + let mut lifetime_refs = lifetime_refs.clone().into_iter(); + let owned = if let Some(lt) = lifetime_refs.next() + && lifetime_refs.next().is_none() && lt.kind != MissingLifetimeKind::Ampersand { ", or if you will only have owned values" @@ -3728,7 +3952,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // we identified that the return expression references only one argument, we // would suggest borrowing only that argument, and we'd skip the prior // "use `'static`" suggestion entirely. - if let [lt] = &lifetime_refs[..] + let mut lifetime_refs = lifetime_refs.clone().into_iter(); + if let Some(lt) = lifetime_refs.next() + && lifetime_refs.next().is_none() && (lt.kind == MissingLifetimeKind::Ampersand || lt.kind == MissingLifetimeKind::Underscore) { @@ -4024,7 +4250,11 @@ fn mk_where_bound_predicate( } /// Report lifetime/lifetime shadowing as an error. -pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { +pub(super) fn signal_lifetime_shadowing( + sess: &Session, + orig: Ident, + shadower: Ident, +) -> ErrorGuaranteed { struct_span_code_err!( sess.dcx(), shadower.span, @@ -4034,7 +4264,7 @@ pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: I ) .with_span_label(orig.span, "first declared here") .with_span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)) - .emit(); + .emit() } struct LifetimeFinder<'ast> { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index fc863c84f0990..ff1c30458a987 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1337,6 +1337,8 @@ pub struct Resolver<'ra, 'tcx> { /// Amount of lifetime parameters for each item in the crate. item_generics_num_lifetimes: FxHashMap = default::fx_hash_map(), + /// Generic args to suggest for required params (e.g. `<'_>`, `<_, _>`), if any. + item_required_generic_args_suggestions: FxHashMap = default::fx_hash_map(), delegation_fn_sigs: LocalDefIdMap = Default::default(), delegation_infos: LocalDefIdMap = Default::default(), @@ -1555,6 +1557,32 @@ impl<'tcx> Resolver<'_, 'tcx> { } } + fn item_required_generic_args_suggestion(&self, def_id: DefId) -> String { + if let Some(def_id) = def_id.as_local() { + self.item_required_generic_args_suggestions.get(&def_id).cloned().unwrap_or_default() + } else { + let required = self + .tcx + .generics_of(def_id) + .own_params + .iter() + .filter_map(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => Some("'_"), + ty::GenericParamDefKind::Type { has_default, .. } + | ty::GenericParamDefKind::Const { has_default } => { + if has_default { + None + } else { + Some("_") + } + } + }) + .collect::>(); + + if required.is_empty() { String::new() } else { format!("<{}>", required.join(", ")) } + } + } + pub fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index bebc0707e26d9..5a41e46f3a6bd 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -777,7 +777,9 @@ fn layout_of_uncached<'tcx>( let err = if ty.has_param() || !cx.typing_env.param_env.caller_bounds().is_empty() { LayoutError::TooGeneric(ty) } else { - unreachable!("invalid rigid alias in layout_of after normalization: {ty:?}"); + LayoutError::ReferencesError(cx.tcx().dcx().delayed_bug(format!( + "unexpected rigid alias in layout_of after normalization: {ty:?}" + ))) }; return Err(error(cx, err)); } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c145929534d97..9687d014c8727 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -431,6 +431,10 @@ impl Item { self.deprecation(tcx).is_some_and(|deprecation| deprecation.is_in_effect()) } + pub(crate) fn is_unstable(&self) -> bool { + self.stability.is_some_and(|x| x.is_unstable()) + } + pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { self.item_id.as_def_id().map(|did| inner_docs(tcx.get_all_attrs(did))).unwrap_or(false) } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 64e361d566c32..5a97d8e5b5f42 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -594,6 +594,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It ); let aliases = item.attrs.get_doc_aliases(); let is_deprecated = item.is_deprecated(tcx); + let is_unstable = item.is_unstable(); let index_item = IndexItem { ty: item.type_(), defid: Some(defid), @@ -609,6 +610,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It search_type, aliases, is_deprecated, + is_unstable, }; cache.search_index.push(index_item); diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 86e8167dc3cda..57428b6f481e8 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -142,6 +142,7 @@ pub(crate) struct IndexItem { pub(crate) search_type: Option, pub(crate) aliases: Box<[Symbol]>, pub(crate) is_deprecated: bool, + pub(crate) is_unstable: bool, } /// A type used for the search index. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 30b534003da17..7c493653e77c0 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -616,6 +616,7 @@ impl SerializedSearchIndex { parent, trait_parent, deprecated, + unstable, associated_item_disambiguator, }| EntryData { krate: *map.get(krate).unwrap(), @@ -626,6 +627,7 @@ impl SerializedSearchIndex { parent: parent.and_then(|path_id| map.get(&path_id).copied()), trait_parent: trait_parent.and_then(|path_id| map.get(&path_id).copied()), deprecated: *deprecated, + unstable: *unstable, associated_item_disambiguator: associated_item_disambiguator.clone(), }, ), @@ -896,6 +898,7 @@ struct EntryData { parent: Option, trait_parent: Option, deprecated: bool, + unstable: bool, associated_item_disambiguator: Option, } @@ -912,6 +915,7 @@ impl Serialize for EntryData { seq.serialize_element(&self.parent.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&self.trait_parent.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&if self.deprecated { 1 } else { 0 })?; + seq.serialize_element(&if self.unstable { 1 } else { 0 })?; if let Some(disambig) = &self.associated_item_disambiguator { seq.serialize_element(&disambig)?; } @@ -946,6 +950,7 @@ impl<'de> Deserialize<'de> for EntryData { v.next_element()?.ok_or_else(|| A::Error::missing_field("trait_parent"))?; let deprecated: u32 = v.next_element()?.unwrap_or(0); + let unstable: u32 = v.next_element()?.unwrap_or(0); let associated_item_disambiguator: Option = v.next_element()?; Ok(EntryData { krate, @@ -956,6 +961,7 @@ impl<'de> Deserialize<'de> for EntryData { parent: Option::::from(parent).map(|path| path as usize), trait_parent: Option::::from(trait_parent).map(|path| path as usize), deprecated: deprecated != 0, + unstable: unstable != 0, associated_item_disambiguator, }) } @@ -1283,6 +1289,7 @@ pub(crate) fn build_index( ), aliases: item.attrs.get_doc_aliases(), is_deprecated: item.is_deprecated(tcx), + is_unstable: item.is_unstable(), }); } } @@ -1382,6 +1389,7 @@ pub(crate) fn build_index( parent: None, trait_parent: None, deprecated: false, + unstable: false, associated_item_disambiguator: None, }), crate_doc, @@ -1520,6 +1528,7 @@ pub(crate) fn build_index( module_path, exact_module_path, deprecated: item.is_deprecated, + unstable: item.is_unstable, associated_item_disambiguator: if let Some(impl_id) = item.impl_id && let Some(parent_idx) = item.parent_idx && associated_item_duplicates diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 42cd321849cf9..491be052bca2e 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -231,7 +231,6 @@ function preLoadCss(cssUrl) { // When loading settings.html as a standalone page, the equivalent HTML is // generated in context.rs. setTimeout(() => { - // @ts-expect-error const themes = getVar("themes").split(","); for (const theme of themes) { // if there are no themes, do nothing @@ -415,12 +414,10 @@ function preLoadCss(cssUrl) { } window.StringdexOnload.push(() => { loadScript( - // @ts-expect-error getVar("static-root-path") + getVar("search-js"), sendSearchForm, ); }); - // @ts-expect-error loadScript(getVar("static-root-path") + getVar("stringdex-js"), sendSearchForm); loadScript(resourcePath("search.index/root", ".js"), sendSearchForm); } @@ -622,8 +619,7 @@ function preLoadCss(cssUrl) { */ function openParentDetails(elem) { while (elem) { - if (elem.tagName === "DETAILS") { - // @ts-expect-error + if (elem instanceof HTMLDetailsElement) { elem.open = true; } elem = elem.parentElement; @@ -659,10 +655,8 @@ function preLoadCss(cssUrl) { } if (document.activeElement && - document.activeElement.tagName === "INPUT" && - // @ts-expect-error + document.activeElement instanceof HTMLInputElement && document.activeElement.type !== "checkbox" && - // @ts-expect-error document.activeElement.type !== "radio") { switch (getVirtualKey(ev)) { case "Escape": @@ -969,19 +963,19 @@ function preLoadCss(cssUrl) { const selfPath = script ? script.getAttribute("data-self-path") : null; // These sidebar blocks need filled in, too. - const mainContent = document.querySelector("#main-content"); - const sidebarSection = document.querySelector(".sidebar section"); + const mainContent = nonnull(document.querySelector("#main-content")); + const sidebarSection = nonnull(document.querySelector(".sidebar section")); let methods = document.querySelector(".sidebar .block.method"); let associatedTypes = document.querySelector(".sidebar .block.associatedtype"); let associatedConstants = document.querySelector(".sidebar .block.associatedconstant"); let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation"); - // @ts-expect-error - for (const impList of imp[window.currentCrate]) { + for (const impList of imp[nonnull(window.currentCrate)]) { const types = impList.slice(2); const text = impList[0]; - const isTrait = impList[1] !== 0; const traitName = impList[1]; + const isTrait = typeof traitName === "string"; + // @ts-expect-error if (types.indexOf(selfPath) === -1) { continue; } @@ -1005,28 +999,19 @@ function preLoadCss(cssUrl) { h.appendChild(link); trait_implementations = outputList; trait_implementations_header = outputListHeader; - // @ts-expect-error sidebarSection.appendChild(h); sidebarTraitList = document.createElement("ul"); sidebarTraitList.className = "block trait-implementation"; - // @ts-expect-error sidebarSection.appendChild(sidebarTraitList); - // @ts-expect-error mainContent.appendChild(outputListHeader); - // @ts-expect-error mainContent.appendChild(outputList); } else { implementations = outputList; if (trait_implementations) { - // @ts-expect-error mainContent.insertBefore(outputListHeader, trait_implementations_header); - // @ts-expect-error mainContent.insertBefore(outputList, trait_implementations_header); } else { - const mainContent = document.querySelector("#main-content"); - // @ts-expect-error mainContent.appendChild(outputListHeader); - // @ts-expect-error mainContent.appendChild(outputList); } } @@ -1071,8 +1056,7 @@ function preLoadCss(cssUrl) { if (isTrait) { const li = document.createElement("li"); const a = document.createElement("a"); - // @ts-expect-error - a.href = `#${template.content.querySelector(".impl").id}`; + a.href = `#${nonnull(template.content.querySelector(".impl")).id}`; a.textContent = traitName; li.appendChild(a); // @ts-expect-error @@ -1099,14 +1083,10 @@ function preLoadCss(cssUrl) { const insertionReference = methods || sidebarTraitList; if (insertionReference) { const insertionReferenceH = insertionReference.previousElementSibling; - // @ts-expect-error sidebarSection.insertBefore(blockHeader, insertionReferenceH); - // @ts-expect-error sidebarSection.insertBefore(block, insertionReferenceH); } else { - // @ts-expect-error sidebarSection.appendChild(blockHeader); - // @ts-expect-error sidebarSection.appendChild(block); } if (hasClass(item, "associatedtype")) { diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 60df4fc10b8c5..3857e6e9fd878 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -243,6 +243,7 @@ declare namespace rustdoc { parent: number?, traitParent: number?, deprecated: boolean, + unstable: boolean, associatedItemDisambiguator: string?, } @@ -292,6 +293,7 @@ declare namespace rustdoc { path: PathData?, functionData: FunctionData?, deprecated: boolean, + unstable: boolean, parent: RowParent, traitParent: RowParent, } @@ -524,7 +526,8 @@ declare namespace rustdoc { } type TypeImpls = { - [cratename: string]: Array> + /* [text, traitName (0 if not a trait), ...types] */ + [cratename: string]: Array<[string, string|0, ...string[]]> } /** @@ -576,4 +579,16 @@ declare namespace rustdoc { "typeNameIdOfHof": number, "typeNameIdOfNever": number, }; + + type VarName = "name" + | "root-path" + | "static-root-path" + | "current-crate" + | "themes" + | "resource-suffix" + | "rustdoc-version" + | "channel" + | "search-js" + | "stringdex-js" + | "settings-js"; } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 55ff48846dcb3..e540b3942d249 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1634,6 +1634,7 @@ class DocSearch { * parent, * trait_parent, * deprecated, + * unstable, * associated_item_disambiguator * @type {rustdoc.ArrayWithOptionals<[ * number, @@ -1643,6 +1644,7 @@ class DocSearch { * number, * number, * number, + * number, * ], [string]>} */ const raw = JSON.parse(encoded); @@ -1654,7 +1656,8 @@ class DocSearch { parent: raw[4] === 0 ? null : raw[4] - 1, traitParent: raw[5] === 0 ? null : raw[5] - 1, deprecated: raw[6] === 1 ? true : false, - associatedItemDisambiguator: raw.length === 7 ? null : raw[7], + unstable: raw[7] === 1 ? true : false, + associatedItemDisambiguator: raw.length === 8 ? null : raw[8], }; } @@ -1947,6 +1950,7 @@ class DocSearch { path, functionData, deprecated: entry ? entry.deprecated : false, + unstable: entry ? entry.unstable : false, parent, traitParent, }; @@ -2859,6 +2863,13 @@ class DocSearch { return a - b; } + // sort unstable items later + a = Number(aai.unstable); + b = Number(bbi.unstable); + if (a !== b) { + return a - b; + } + // sort by crate (current crate comes first) a = Number(aai.crate !== preferredCrate); b = Number(bbi.crate !== preferredCrate); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 1ec7cb4ced06e..29b94ea424e22 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -199,12 +199,16 @@ function getCurrentValue(name) { * Get a value from the rustdoc-vars div, which is used to convey data from * Rust to the JS. If there is no such element, return null. * - * @param {string} name - * @returns {string|null} + * @param {rustdoc.VarName} name + * @returns {string} */ function getVar(name) { const el = document.querySelector("head > meta[name='rustdoc-vars']"); - return el ? el.getAttribute("data-" + name) : null; + const v = el ? el.getAttribute("data-" + name) : null; + if (v !== null) { + return v; + } + throw `rustdoc var "${name}" is missing`; } /** @@ -294,6 +298,8 @@ const updateTheme = (function() { return updateTheme; })(); +// typescript thinks we're forgetting to call window.matchMedia, +// but we're checking browser support of media queries. // @ts-ignore if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { // update the preferred dark theme if the user is already using a dark theme diff --git a/src/tools/nix-dev-shell/envrc-flake b/src/tools/nix-dev-shell/envrc-flake index f3e5442b86b2a..2feac5ac56992 100644 --- a/src/tools/nix-dev-shell/envrc-flake +++ b/src/tools/nix-dev-shell/envrc-flake @@ -1,5 +1,5 @@ # If you want to use this as an .envrc file to create a shell with necessary components -# to develop rustc, use the following command in the root of the rusr checkout: +# to develop rustc, use the following command in the root of the rust checkout: # # ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc && nix flake update --flake ./src/tools/nix-dev-shell diff --git a/src/tools/nix-dev-shell/envrc-shell b/src/tools/nix-dev-shell/envrc-shell index 4080d7d538406..047da54c1f145 100644 --- a/src/tools/nix-dev-shell/envrc-shell +++ b/src/tools/nix-dev-shell/envrc-shell @@ -1,5 +1,5 @@ # If you want to use this as an .envrc file to create a shell with necessary components -# to develop rustc, use the following command in the root of the rusr checkout: +# to develop rustc, use the following command in the root of the rust checkout: # # ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc diff --git a/tests/rustdoc-js-std/core-transmute.js b/tests/rustdoc-js-std/core-transmute.js index 8c9910a32d7f1..b15f398902c31 100644 --- a/tests/rustdoc-js-std/core-transmute.js +++ b/tests/rustdoc-js-std/core-transmute.js @@ -3,9 +3,9 @@ const EXPECTED = [ { 'query': 'generic:T -> generic:U', 'others': [ + { 'path': 'core::mem', 'name': 'transmute' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'core::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute-fail.js b/tests/rustdoc-js-std/transmute-fail.js index ddfb276194818..459e8dc3f0029 100644 --- a/tests/rustdoc-js-std/transmute-fail.js +++ b/tests/rustdoc-js-std/transmute-fail.js @@ -6,9 +6,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:T', 'others': [ + { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute.js b/tests/rustdoc-js-std/transmute.js index f52e0ab14362d..a85b02e29947e 100644 --- a/tests/rustdoc-js-std/transmute.js +++ b/tests/rustdoc-js-std/transmute.js @@ -5,9 +5,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:U', 'others': [ + { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js/sort-stability.js b/tests/rustdoc-js/sort-stability.js new file mode 100644 index 0000000000000..8c095619a0866 --- /dev/null +++ b/tests/rustdoc-js/sort-stability.js @@ -0,0 +1,9 @@ +const EXPECTED = [ + { + 'query': 'foo', + 'others': [ + {"path": "sort_stability::old", "name": "foo"}, + {"path": "sort_stability::new", "name": "foo"}, + ], + }, +]; diff --git a/tests/rustdoc-js/sort-stability.rs b/tests/rustdoc-js/sort-stability.rs new file mode 100644 index 0000000000000..68662bb3aab6c --- /dev/null +++ b/tests/rustdoc-js/sort-stability.rs @@ -0,0 +1,16 @@ +#![feature(staged_api)] +#![stable(feature = "foo_lib", since = "1.0.0")] + +#[stable(feature = "old_foo", since = "1.0.1")] +pub mod old { + /// Old, stable foo + #[stable(feature = "old_foo", since = "1.0.1")] + pub fn foo() {} +} + +#[unstable(feature = "new_foo", issue = "none")] +pub mod new { + /// New, unstable foo + #[unstable(feature = "new_foo", issue = "none")] + pub fn foo() {} +} diff --git a/tests/ui/associated-types/associated-types-eq-1.stderr b/tests/ui/associated-types/associated-types-eq-1.stderr index 869583df644fd..54e50f36fb6ad 100644 --- a/tests/ui/associated-types/associated-types-eq-1.stderr +++ b/tests/ui/associated-types/associated-types-eq-1.stderr @@ -1,16 +1,13 @@ error[E0425]: cannot find type `A` in this scope --> $DIR/associated-types-eq-1.rs:10:12 | -LL | fn foo2(x: I) { - | - similarly named type parameter `I` defined here LL | let _: A = x.boo(); | ^ | -help: a type parameter with a similar name exists - | -LL - let _: A = x.boo(); -LL + let _: I = x.boo(); +help: you might have meant to use an associated type of the same name | +LL | let _: I::A = x.boo(); + | +++ help: you might be missing a type parameter | LL | fn foo2(x: I) { diff --git a/tests/ui/associated-types/suggest-assoc-type-from-bounds.rs b/tests/ui/associated-types/suggest-assoc-type-from-bounds.rs new file mode 100644 index 0000000000000..8b349f325cd73 --- /dev/null +++ b/tests/ui/associated-types/suggest-assoc-type-from-bounds.rs @@ -0,0 +1,64 @@ +pub trait Trait { + type Assoc; +} + +fn f + Trait>() { + let _: Assoc = todo!(); //~ ERROR cannot find type `Assoc` in this scope +} + +pub trait Foo<'a> { + type A; +} + +pub mod inner { + pub trait Foo<'a> { + type A; + } +} + +fn g<'a, T: ::Foo<'a> + inner::Foo<'a>>() { + let _: A = todo!(); //~ ERROR cannot find type `A` in this scope +} + +pub trait First { + type Assoc; +} + +pub trait Second { + type Assoc; +} + +fn h + Second>() { + let _: Assoc = todo!(); //~ ERROR cannot find type `Assoc` in this scope +} + +pub trait Gat { + type Assoc<'a>; +} + +fn i() { + let _: Assoc = todo!(); //~ ERROR cannot find type `Assoc` in this scope +} + +fn j() { + struct Local; + impl Local { + fn method() { + let _: Assoc = todo!(); //~ ERROR cannot find type `Assoc` in this scope + } + } + + let _ = std::marker::PhantomData::; +} + +pub struct S; +impl S { + fn method() { + fn inner() { + let _: Assoc = todo!(); //~ ERROR cannot find type `Assoc` in this scope + } + inner(); + } +} + +fn main() {} diff --git a/tests/ui/associated-types/suggest-assoc-type-from-bounds.stderr b/tests/ui/associated-types/suggest-assoc-type-from-bounds.stderr new file mode 100644 index 0000000000000..b5ce2d91ca4d2 --- /dev/null +++ b/tests/ui/associated-types/suggest-assoc-type-from-bounds.stderr @@ -0,0 +1,75 @@ +error[E0425]: cannot find type `Assoc` in this scope + --> $DIR/suggest-assoc-type-from-bounds.rs:6:12 + | +LL | let _: Assoc = todo!(); + | ^^^^^ + | +help: you might have meant to use an associated type of the same name + | +LL | let _: >::Assoc = todo!(); + | +++++++++++++++++++ +LL | let _: >::Assoc = todo!(); + | +++++++++++++++++++ + +error[E0425]: cannot find type `A` in this scope + --> $DIR/suggest-assoc-type-from-bounds.rs:20:12 + | +LL | let _: A = todo!(); + | ^ + | +help: you might have meant to use an associated type of the same name + | +LL | let _: >::A = todo!(); + | ++++++++++++++++++ +LL | let _: >::A = todo!(); + | +++++++++++++++++++++++ +help: you might be missing a type parameter + | +LL | fn g<'a, T: ::Foo<'a> + inner::Foo<'a>, A>() { + | +++ + +error[E0425]: cannot find type `Assoc` in this scope + --> $DIR/suggest-assoc-type-from-bounds.rs:32:12 + | +LL | let _: Assoc = todo!(); + | ^^^^^ + | +help: you might have meant to use an associated type of the same name + | +LL | let _: ::Assoc = todo!(); + | ++++++++++++++ +LL | let _: ::Assoc = todo!(); + | +++++++++++++++ + +error[E0425]: cannot find type `Assoc` in this scope + --> $DIR/suggest-assoc-type-from-bounds.rs:40:12 + | +LL | let _: Assoc = todo!(); + | ^^^^^ + | +help: you might have meant to use an associated type of the same name + | +LL - let _: Assoc = todo!(); +LL + let _: T::Assoc<'_> = todo!(); + | + +error[E0425]: cannot find type `Assoc` in this scope + --> $DIR/suggest-assoc-type-from-bounds.rs:47:20 + | +LL | let _: Assoc = todo!(); + | ^^^^^ + | +help: you might have meant to use an associated type of the same name + | +LL | let _: U::Assoc = todo!(); + | +++ + +error[E0425]: cannot find type `Assoc` in this scope + --> $DIR/suggest-assoc-type-from-bounds.rs:58:20 + | +LL | let _: Assoc = todo!(); + | ^^^^^ not found in this scope + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cast/ice-cast-type-with-error-124848.rs b/tests/ui/cast/ice-cast-type-with-error-124848.rs index 9b3732b02db43..0c623de8050b0 100644 --- a/tests/ui/cast/ice-cast-type-with-error-124848.rs +++ b/tests/ui/cast/ice-cast-type-with-error-124848.rs @@ -14,5 +14,4 @@ fn main() { let bad_addr = &unpinned as *const Cell>> as usize; //~^ ERROR use of undeclared lifetime name `'a` //~| ERROR use of undeclared lifetime name `'a` - //~| ERROR casting `&MyType<'_>` as `*const Cell>>` is invalid } diff --git a/tests/ui/cast/ice-cast-type-with-error-124848.stderr b/tests/ui/cast/ice-cast-type-with-error-124848.stderr index dff2277273248..665b0e0a0dd49 100644 --- a/tests/ui/cast/ice-cast-type-with-error-124848.stderr +++ b/tests/ui/cast/ice-cast-type-with-error-124848.stderr @@ -58,13 +58,7 @@ help: provide the argument LL | let mut unpinned = MyType(Cell::new(None), /* value */); | +++++++++++++ -error[E0606]: casting `&MyType<'_>` as `*const Cell>>` is invalid - --> $DIR/ice-cast-type-with-error-124848.rs:14:20 - | -LL | let bad_addr = &unpinned as *const Cell>> as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0061, E0261, E0425, E0606. +Some errors have detailed explanations: E0061, E0261, E0425. For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/closures/binder/implicit-stuff.rs b/tests/ui/closures/binder/implicit-stuff.rs index c976c200b0c8e..09e4c747afee8 100644 --- a/tests/ui/closures/binder/implicit-stuff.rs +++ b/tests/ui/closures/binder/implicit-stuff.rs @@ -24,5 +24,4 @@ fn main() { //~| ERROR `'_` cannot be used here let _ = for<'a> |x: &()| -> &'a () { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here let _ = for<'a> |x: &'a ()| -> &() { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here - //~^ ERROR: lifetime may not live long enough } diff --git a/tests/ui/closures/binder/implicit-stuff.stderr b/tests/ui/closures/binder/implicit-stuff.stderr index 330a05a79bae3..cec2a60ba28c8 100644 --- a/tests/ui/closures/binder/implicit-stuff.stderr +++ b/tests/ui/closures/binder/implicit-stuff.stderr @@ -102,15 +102,6 @@ LL | let _ = for<'a> |x: &'a _, y, z: _| -> &'a _ { | | | `for<...>` is here -error: lifetime may not live long enough - --> $DIR/implicit-stuff.rs:26:42 - | -LL | let _ = for<'a> |x: &'a ()| -> &() { x }; - | -- - ^ returning this value requires that `'a` must outlive `'1` - | | | - | | let's call the lifetime of this reference `'1` - | lifetime `'a` defined here - -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0637`. diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.rs b/tests/ui/delegation/ice-line-bounds-issue-148732.rs index e44c784760216..699e7d86f2581 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.rs +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.rs @@ -3,7 +3,6 @@ reuse a as b { //~| ERROR functions delegation is not yet fully implemented dbg!(b); //~^ ERROR missing lifetime specifier - //~| ERROR `fn() {b}` doesn't implement `Debug` } fn main() {} diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index 83cc238b5e0c4..c4f261181b109 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -17,7 +17,7 @@ LL | / reuse a as b { LL | | LL | | LL | | dbg!(b); -... | +LL | | LL | | } | |_^ | @@ -25,18 +25,7 @@ LL | | } = help: add `#![feature(fn_delegation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0277]: `fn() {b}` doesn't implement `Debug` - --> $DIR/ice-line-bounds-issue-148732.rs:4:5 - | -LL | reuse a as b { - | - consider calling this function -... -LL | dbg!(b); - | ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}` - | - = help: use parentheses to call this function: `b()` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0106, E0277, E0425, E0658. +Some errors have detailed explanations: E0106, E0425, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/error-codes/E0264.rs b/tests/ui/error-codes/E0264.rs index 6adaf01fb5244..855644796ed45 100644 --- a/tests/ui/error-codes/E0264.rs +++ b/tests/ui/error-codes/E0264.rs @@ -1,8 +1,8 @@ #![feature(lang_items)] extern "C" { - #[lang = "cake"] - fn cake(); //~ ERROR E0264 + #[lang = "copy"] + fn copy(); //~ ERROR E0264 } fn main() {} diff --git a/tests/ui/error-codes/E0264.stderr b/tests/ui/error-codes/E0264.stderr index 3503fb229e4f3..6442f42e689d6 100644 --- a/tests/ui/error-codes/E0264.stderr +++ b/tests/ui/error-codes/E0264.stderr @@ -1,7 +1,7 @@ -error[E0264]: unknown external lang item: `cake` +error[E0264]: unknown external lang item: `copy` --> $DIR/E0264.rs:5:5 | -LL | fn cake(); +LL | fn copy(); | ^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/layout/rigid-alias-due-to-broken-impl.rs b/tests/ui/layout/rigid-alias-due-to-broken-impl.rs new file mode 100644 index 0000000000000..912c660cb7cad --- /dev/null +++ b/tests/ui/layout/rigid-alias-due-to-broken-impl.rs @@ -0,0 +1,17 @@ +// Make sure we don't ICE if `layout_of` encounters an alias +// which is rigid due to a malformed program. A regression test +// for #152545. +// +// This specific ICE happens in the `KnownPanicsLint` visitor. + +//@ compile-flags: --crate-type=rlib +trait Foo { + type Assoc; +} + +// The trait solver only treats missng associated items +// as rigid if the self-type is known to be unsized. +impl Foo for str {} +//~^ ERROR not all trait items implemented + +fn foo(_: [u32; std::mem::size_of::<::Assoc>()]) {} diff --git a/tests/ui/layout/rigid-alias-due-to-broken-impl.stderr b/tests/ui/layout/rigid-alias-due-to-broken-impl.stderr new file mode 100644 index 0000000000000..e9ba6df2fdc71 --- /dev/null +++ b/tests/ui/layout/rigid-alias-due-to-broken-impl.stderr @@ -0,0 +1,12 @@ +error[E0046]: not all trait items implemented, missing: `Assoc` + --> $DIR/rigid-alias-due-to-broken-impl.rs:14:1 + | +LL | type Assoc; + | ---------- `Assoc` from trait +... +LL | impl Foo for str {} + | ^^^^^^^^^^^^^^^^ missing `Assoc` in implementation + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/lowering/issue-96847.rs b/tests/ui/lowering/issue-96847.rs index 9408f6b9b4abc..a1fd105d9dd4a 100644 --- a/tests/ui/lowering/issue-96847.rs +++ b/tests/ui/lowering/issue-96847.rs @@ -1,4 +1,4 @@ -//@ run-pass +//@ check-fail // Test that this doesn't abort during AST lowering. In #96847 it did abort // because the attribute was being lowered twice. @@ -9,6 +9,7 @@ fn main() { for _ in [1,2,3] { #![lang="foo"] + //~^ ERROR definition of an unknown lang item: `foo` [E0522] println!("foo"); } } diff --git a/tests/ui/lowering/issue-96847.stderr b/tests/ui/lowering/issue-96847.stderr new file mode 100644 index 0000000000000..2cded32f9fb86 --- /dev/null +++ b/tests/ui/lowering/issue-96847.stderr @@ -0,0 +1,9 @@ +error[E0522]: definition of an unknown lang item: `foo` + --> $DIR/issue-96847.rs:11:9 + | +LL | #![lang="foo"] + | ^^^^^^^^^^^^^^ definition of unknown lang item `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0522`. diff --git a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr index e16300886b0b9..0ab90faabcf0f 100644 --- a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr +++ b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr @@ -35,8 +35,21 @@ LL | | } LL | | } | |_____- returning this value requires that `*map` is borrowed for `'r` +error[E0499]: cannot borrow `*map` as mutable more than once at a time + --> $DIR/nll-problem-case-3-issue-21906.rs:45:41 + | +LL | fn get_priority_mut_entry<'a, K, V>( + | -- lifetime `'a` defined here +... +LL | match map.entry(key1) { + | --- first mutable borrow occurs here +LL | Entry::Occupied(occupied) => Some(occupied.into_mut()), + | ------------------------- returning this value requires that `*map` is borrowed for `'a` +LL | Entry::Vacant(_vacant) => match map.entry(key2) { + | ^^^ second mutable borrow occurs here + error[E0499]: cannot borrow `*self` as mutable more than once at a time - --> $DIR/nll-problem-case-3-issue-21906.rs:44:21 + --> $DIR/nll-problem-case-3-issue-21906.rs:64:21 | LL | fn two(&mut self) -> &i32 { | - let's call the lifetime of this reference `'1` @@ -48,7 +61,7 @@ LL | return k; | - returning this value requires that `*self` is borrowed for `'1` error[E0502]: cannot borrow `x.data` as immutable because it is also borrowed as mutable - --> $DIR/nll-problem-case-3-issue-21906.rs:62:22 + --> $DIR/nll-problem-case-3-issue-21906.rs:82:22 | LL | fn foo(x: &mut Foo) -> Option<&mut i32> { | - let's call the lifetime of this reference `'1` @@ -61,7 +74,7 @@ LL | println!("{:?}", x.data); | ^^^^^^ immutable borrow occurs here error[E0499]: cannot borrow `*vec` as mutable more than once at a time - --> $DIR/nll-problem-case-3-issue-21906.rs:77:9 + --> $DIR/nll-problem-case-3-issue-21906.rs:97:9 | LL | fn f(vec: &mut Vec) -> &u8 { | - let's call the lifetime of this reference `'1` @@ -75,7 +88,7 @@ LL | vec.push(10); | ^^^ second mutable borrow occurs here error[E0502]: cannot borrow `*vec` as immutable because it is also borrowed as mutable - --> $DIR/nll-problem-case-3-issue-21906.rs:78:9 + --> $DIR/nll-problem-case-3-issue-21906.rs:98:9 | LL | fn f(vec: &mut Vec) -> &u8 { | - let's call the lifetime of this reference `'1` @@ -88,7 +101,7 @@ LL | n LL | vec.last().unwrap() | ^^^ immutable borrow occurs here -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0499, E0502. For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.rs b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.rs index b025ea78f8b49..45b56e32522f0 100644 --- a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.rs +++ b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.rs @@ -29,6 +29,26 @@ fn from_the_rfc<'r, K: Hash + Eq + Copy, V: Default>( } } +// A variant that's similar to the RFC example above, but using the entry API, and requested in +// https://internals.rust-lang.org/t/get-mut-map-back-from-entry-api/24003 +fn get_priority_mut_entry<'a, K, V>( + map: &'a mut HashMap, + key1: K, + key2: K, +) -> Option<&'a mut V> +where + K: Eq + Hash, +{ + use std::collections::hash_map::Entry; + match map.entry(key1) { + Entry::Occupied(occupied) => Some(occupied.into_mut()), + Entry::Vacant(_vacant) => match map.entry(key2) { + Entry::Occupied(occupied2) => Some(occupied2.into_mut()), + Entry::Vacant(_) => None, + }, + } +} + // MCVE 1 from issue #21906 struct A { a: i32, diff --git a/tests/ui/object-lifetime/undeclared-object-lifetime.rs b/tests/ui/object-lifetime/undeclared-object-lifetime.rs new file mode 100644 index 0000000000000..7c0e1100fe7bb --- /dev/null +++ b/tests/ui/object-lifetime/undeclared-object-lifetime.rs @@ -0,0 +1,7 @@ +//! E0228 (lifetime bound for trait object cannot be deduced from context) should not be emitted +//! when an undeclared lifetime bound has been specified. +//! +//! Regression test for https://github.com/rust-lang/rust/issues/152014 + +fn f(_: std::cell::Ref<'undefined, dyn std::fmt::Debug>) {} //~ ERROR use of undeclared lifetime name `'undefined` +fn main() {} diff --git a/tests/ui/object-lifetime/undeclared-object-lifetime.stderr b/tests/ui/object-lifetime/undeclared-object-lifetime.stderr new file mode 100644 index 0000000000000..b6646a05420b3 --- /dev/null +++ b/tests/ui/object-lifetime/undeclared-object-lifetime.stderr @@ -0,0 +1,14 @@ +error[E0261]: use of undeclared lifetime name `'undefined` + --> $DIR/undeclared-object-lifetime.rs:6:24 + | +LL | fn f(_: std::cell::Ref<'undefined, dyn std::fmt::Debug>) {} + | ^^^^^^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'undefined` here + | +LL | fn f<'undefined>(_: std::cell::Ref<'undefined, dyn std::fmt::Debug>) {} + | ++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs index 26fa6fdb57f95..35a2ef10c2e3c 100644 --- a/tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs +++ b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs @@ -9,6 +9,5 @@ static NON_ELIDABLE_FN: &fn(&u8, &u8) -> &u8 = //~^ ERROR missing lifetime specifier [E0106] &(non_elidable as fn(&u8, &u8) -> &u8); //~^ ERROR missing lifetime specifier [E0106] - //~| ERROR non-primitive cast fn main() {} diff --git a/tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr index 77fc3f0412ebf..05ea6e44486fd 100644 --- a/tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr +++ b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr @@ -23,13 +23,6 @@ help: consider making the type lifetime-generic with a new `'a` lifetime LL | &(non_elidable as for<'a> fn(&'a u8, &'a u8) -> &'a u8); | +++++++ ++ ++ ++ -error[E0605]: non-primitive cast: `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {non_elidable}` as `for<'a, 'b> fn(&'a u8, &'b u8) -> &u8` - --> $DIR/rfc1623-3.rs:10:6 - | -LL | &(non_elidable as fn(&u8, &u8) -> &u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0106, E0605. -For more information about an error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr index d74372c665a45..c217672b0050d 100644 --- a/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr +++ b/tests/ui/typeck/sugg-swap-equality-in-macro-issue-139050.stderr @@ -2,7 +2,12 @@ error[E0425]: cannot find type `Item` in this scope --> $DIR/sugg-swap-equality-in-macro-issue-139050.rs:29:5 | LL | Item: Eq + Debug, - | ^^^^ not found in this scope + | ^^^^ + | +help: you might have meant to use an associated type of the same name + | +LL | I::Item: Eq + Debug, + | +++ error[E0308]: mismatched types --> $DIR/sugg-swap-equality-in-macro-issue-139050.rs:31:5