diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index bd5ce5d18b839..9b881688a6ba9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3924,7 +3924,7 @@ pub struct EiiImpl { /// /// This field is that shortcut: we prefill the extern target to skip a name resolution step, /// making sure it never fails. It'd be awful UX if we fail name resolution in code invisible to the user. - pub known_eii_macro_resolution: Option, + pub known_eii_macro_resolution: Option, pub impl_safety: Safety, pub span: Span, pub inner_span: Span, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8ebecb222b940..8d4a6f104765f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -141,13 +141,9 @@ impl<'hir> LoweringContext<'_, 'hir> { }: &EiiImpl, ) -> hir::attrs::EiiImpl { let resolution = if let Some(target) = known_eii_macro_resolution - && let Some(decl) = self.lower_eii_decl( - *node_id, - // the expect is ok here since we always generate this path in the eii macro. - eii_macro_path.segments.last().expect("at least one segment").ident, - target, - ) { - EiiImplResolution::Known(decl) + && let Some(foreign_item_did) = self.lower_path_simple_eii(*node_id, target) + { + EiiImplResolution::Known(foreign_item_did) } else if let Some(macro_did) = self.lower_path_simple_eii(*node_id, eii_macro_path) { EiiImplResolution::Macro(macro_did) } else { diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 68d5278c85ef1..66879d5daf7c5 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -302,15 +302,12 @@ fn generate_default_impl( }, span: eii_attr_span, is_default: true, - known_eii_macro_resolution: Some(ast::EiiDecl { - foreign_item: ecx.path( - foreign_item_name.span, - // prefix self to explicitly escape the const block generated below - // NOTE: this is why EIIs can't be used on statements - vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], - ), - impl_unsafe, - }), + known_eii_macro_resolution: Some(ecx.path( + foreign_item_name.span, + // prefix self to explicitly escape the const block generated below + // NOTE: this is why EIIs can't be used on statements + vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], + )), }; let mut item_kind = item_kind.clone(); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 46bb1182c298c..25d82ee0f3c2f 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -7,6 +7,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_macros::Diagnostic; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, InstrumentFnAttr, PatchableFunctionEntry, SanitizerFnAttrs, }; @@ -238,7 +239,7 @@ fn process_builtin_attrs( }; extern_item } - EiiImplResolution::Known(decl) => decl.foreign_item, + EiiImplResolution::Known(def_id) => def_id, EiiImplResolution::Error(_eg) => continue, }; @@ -253,7 +254,10 @@ fn process_builtin_attrs( // iterate over all implementations *in the current crate* // (this is ok since we generate codegen fn attrs in the local crate) // if any of them is *not default* then don't emit the alias. - && tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) + && { + let (_, impls) = tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).unwrap_or_else(|| bug!("EII impl should have an entry")); + impls.iter().any(|(_, imp)| !imp.is_default) + } { continue; } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 17d00863d99d5..f88dcb53b0ad7 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -31,8 +31,8 @@ pub enum EiiImplResolution { /// what foreign item its associated with. Macro(DefId), /// Sometimes though, we already know statically and can skip some name resolution. - /// Stored together with the eii's name for diagnostics. - Known(EiiDecl), + /// DefId of the extern item that the EII implementation implements. + Known(DefId), /// For when resolution failed, but we want to continue compilation Error(ErrorGuaranteed), } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f2a6c2747f380..45db2ffad4747 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1232,7 +1232,7 @@ fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { continue; } } - EiiImplResolution::Known(decl) => (decl.foreign_item, decl.name.name), + EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), EiiImplResolution::Error(_eg) => continue, }; @@ -1259,7 +1259,7 @@ fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) continue; } } - EiiImplResolution::Known(decl) => (decl.foreign_item, decl.name.name), + EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), EiiImplResolution::Error(_eg) => continue, }; diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 11ea4cc492921..4328e8de901d8 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::attrs::{EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; +use rustc_middle::bug; use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; @@ -23,10 +24,19 @@ pub(crate) type EiiMap = FxIndexMap< pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap { let mut eiis = EiiMap::default(); + // Needed because Known only stores a DefId (not the full EiiDecl), + // and we can't call the externally_implementable_items query (cycle). + let decls_by_foreign_item: FxIndexMap = tcx + .hir_crate_items(()) + .eiis() + .filter_map(|id| find_attr!(tcx, id, EiiDeclaration(d) => *d)) + .map(|decl| (decl.foreign_item, decl)) + .collect(); + // iterate over all items in the current crate for id in tcx.hir_crate_items(()).eiis() { for i in find_attr!(tcx, id, EiiImpls(e) => e).into_flat_iter() { - let decl = match i.resolution { + let (foreign_item, decl) = match i.resolution { EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) let Some(decl) = find_attr!(tcx, macro_defid, EiiDeclaration(d) => *d) else { @@ -35,14 +45,22 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap .span_delayed_bug(i.span, "resolved to something that's not an EII"); continue; }; - decl + (decl.foreign_item, decl) + } + // Recover the EiiDecl from the local lookup map. + EiiImplResolution::Known(foreign_item_did) => { + let decl = decls_by_foreign_item.get(&foreign_item_did).unwrap_or_else(|| { + bug!( + "EII impl has Known resolution but can't find EiiDeclaration for {:?}", + foreign_item_did + ) + }); + (foreign_item_did, *decl) } - EiiImplResolution::Known(decl) => decl, EiiImplResolution::Error(_eg) => continue, }; - // FIXME(eii) remove extern target from encoded decl - eiis.entry(decl.foreign_item) + eiis.entry(foreign_item) .or_insert_with(|| (decl, Default::default())) .1 .insert(id.into(), *i); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..88fbbf583064f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -486,13 +486,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - if let EiiImplResolution::Macro(eii_macro) = resolution - && find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) - && !impl_marked_unsafe - { + let needs_unsafe = match resolution { + EiiImplResolution::Macro(eii_macro) => { + find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) + } + EiiImplResolution::Known(foreign_item_did) => { + let foreign_item_did = *foreign_item_did; + self.tcx + .externally_implementable_items(foreign_item_did.krate) + .get(&foreign_item_did) + .map(|(decl, _)| decl.impl_unsafe) + .unwrap_or(false) + } + EiiImplResolution::Error(_) => false, + }; + + if needs_unsafe && !impl_marked_unsafe { + let name = match resolution { + EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), + EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), + EiiImplResolution::Error(_) => unreachable!(), + }; self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { span: *span, - name: self.tcx.item_name(*eii_macro), + name, suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { left: inner_span.shrink_to_lo(), right: inner_span.shrink_to_hi(), @@ -755,7 +772,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for i in impls { let name = match i.resolution { EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id), - EiiImplResolution::Known(decl) => decl.name.name, + EiiImplResolution::Known(def_id) => self.tcx.item_name(def_id), EiiImplResolution::Error(_eg) => continue, }; self.dcx().emit_err(diagnostics::EiiWithTrackCaller { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c94f2f3ffb85d..6f206aadf0ad2 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5609,12 +5609,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // See docs on the `known_eii_macro_resolution` field: // if we already know the resolution statically, don't bother resolving it. if let Some(target) = known_eii_macro_resolution { - self.smart_resolve_path( - *node_id, - &None, - &target.foreign_item, - PathSource::ExternItemImpl, - ); + self.smart_resolve_path(*node_id, &None, target, PathSource::ExternItemImpl); } else { self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); }