Rollup merge of #150822 - fix-149981, r=@Kivooeo

Fix for ICE: eii: fn / macro rules None in find_attr()

Closes rust-lang/rust#149981

This used to ICE:
```rust
macro_rules! foo_impl {}
#[eii]
fn foo_impl() {}
```

`#[eii]` generates a macro (called `foo_impl`) and a default impl. So the partial expansion used to roughly look like the following:

```rust
macro_rules! foo_impl {} // actually resolves here

extern "Rust" {
    fn foo_impl();
}

#[eii_extern_target(foo_impl)]
macro foo_impl {
    () => {};
}

const _: () = {
    #[implements_eii(foo_impl)] // assumed to name resolve to the macro v2 above
    fn foo_impl() {}
};
```

Now, shadowing rules for macrov2 and macrov1 are super weird! Take a look at this: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=23f21421921360478b0ec0276711ad36

So instead of resolving to the macrov2, we resolve the macrov1 named the same thing.

A regression test was added to this, and some span_delayed_bugs were added to make sure we catch this in the right places. But that didn't fix the root cause.

To make sure this simply cannot happen again, I made it so that we don't even need to do a name resolution for the default. In other words, the new partial expansion looks more like:

```rust
macro_rules! foo_impl {}

extern "Rust" {
    fn foo_impl(); // resolves to here now!!!
}

#[eii_extern_target(foo_impl)]
macro foo_impl {
    () => {};
}

const _: () = {
    #[implements_eii(known_extern_target=foo_impl)] // still name resolved, but directly to the foreign function.
    fn foo_impl() {}
};
```

The reason this helps is that name resolution for non-macros is much more predictable. It's not possible to have two functions like that with the same name in scope.

We used to key externally implementable items off of the defid of the macro, but now the unique identifier is the foreign function's defid which seems much more sane.

Finally, I lied a tiny bit because the above partial expansion doesn't actually work.
```rust
extern "Rust" {
    fn foo_impl(); // not to here
}

const _: () = {
    #[implements_eii(known_extern_target=foo_impl)] // actually resolves to this function itself
    fn foo_impl() {} // <--- so to here
};
```

So the last few commits change the expansion to actually be this:

```rust
macro_rules! foo_impl {}

extern "Rust" {
    fn foo_impl(); // resolves to here now!!!
}

#[eii_extern_target(foo_impl)]
macro foo_impl {
    () => {};
}

const _: () = {
    mod dflt { // necessary, otherwise `super` doesn't work
        use super::*;
        #[implements_eii(known_extern_target=super::foo_impl)] // now resolves to outside the `dflt` module, so the foreign item.
        fn foo_impl() {}
    }
};
```

I apologize to whoever needs to review this, this is very subtle and I hope this makes it clear enough 😭.
This commit is contained in:
Urgau 2026-01-09 23:28:16 +01:00 committed by GitHub
commit a8028abedb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 280 additions and 115 deletions

View file

@ -2117,10 +2117,9 @@ pub struct MacroDef {
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
pub struct EiiExternTarget {
/// path to the extern item we're targetting
/// path to the extern item we're targeting
pub extern_item_path: Path,
pub impl_unsafe: bool,
pub span: Span,
}
#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
@ -3813,6 +3812,19 @@ pub struct Fn {
pub struct EiiImpl {
pub node_id: NodeId,
pub eii_macro_path: Path,
/// This field is an implementation detail that prevents a lot of bugs.
/// See <https://github.com/rust-lang/rust/issues/149981> for an example.
///
/// The problem is, that if we generate a declaration *together* with its default,
/// we generate both a declaration and an implementation. The generated implementation
/// uses the same mechanism to register itself as a user-defined implementation would,
/// despite being invisible to users. What does happen is a name resolution step.
/// The invisible default implementation has to find the declaration.
/// Both are generated at the same time, so we can skip that name resolution step.
///
/// 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<EiiExternTarget>,
pub impl_safety: Safety,
pub span: Span,
pub inner_span: Span,

View file

@ -2,7 +2,7 @@ use rustc_abi::ExternAbi;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
use rustc_hir::attrs::{AttributeKind, EiiDecl};
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImplResolution};
use rustc_hir::def::{DefKind, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{
@ -134,6 +134,56 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_eii_extern_target(
&mut self,
id: NodeId,
eii_name: Ident,
EiiExternTarget { extern_item_path, impl_unsafe }: &EiiExternTarget,
) -> Option<EiiDecl> {
self.lower_path_simple_eii(id, extern_item_path).map(|did| EiiDecl {
eii_extern_target: did,
impl_unsafe: *impl_unsafe,
name: eii_name,
})
}
fn lower_eii_impl(
&mut self,
EiiImpl {
node_id,
eii_macro_path,
impl_safety,
span,
inner_span,
is_default,
known_eii_macro_resolution,
}: &EiiImpl,
) -> hir::attrs::EiiImpl {
let resolution = if let Some(target) = known_eii_macro_resolution
&& let Some(decl) = self.lower_eii_extern_target(
*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)
} else if let Some(macro_did) = self.lower_path_simple_eii(*node_id, eii_macro_path) {
EiiImplResolution::Macro(macro_did)
} else {
EiiImplResolution::Error(
self.dcx().span_delayed_bug(*span, "eii never resolved without errors given"),
)
};
hir::attrs::EiiImpl {
span: self.lower_span(*span),
inner_span: self.lower_span(*inner_span),
impl_marked_unsafe: self.lower_safety(*impl_safety, hir::Safety::Safe).is_unsafe(),
is_default: *is_default,
resolution,
}
}
fn generate_extra_attrs_for_item_kind(
&mut self,
id: NodeId,
@ -143,49 +193,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(),
ItemKind::Fn(box Fn { eii_impls, .. }) => {
vec![hir::Attribute::Parsed(AttributeKind::EiiImpls(
eii_impls
.iter()
.flat_map(
|EiiImpl {
node_id,
eii_macro_path,
impl_safety,
span,
inner_span,
is_default,
}| {
self.lower_path_simple_eii(*node_id, eii_macro_path).map(|did| {
hir::attrs::EiiImpl {
eii_macro: did,
span: self.lower_span(*span),
inner_span: self.lower_span(*inner_span),
impl_marked_unsafe: self
.lower_safety(*impl_safety, hir::Safety::Safe)
.is_unsafe(),
is_default: *is_default,
}
})
},
)
.collect(),
eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(),
))]
}
ItemKind::MacroDef(
_,
MacroDef {
eii_extern_target: Some(EiiExternTarget { extern_item_path, impl_unsafe, span }),
..
},
) => self
.lower_path_simple_eii(id, extern_item_path)
.map(|did| {
vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(EiiDecl {
eii_extern_target: did,
impl_unsafe: *impl_unsafe,
span: self.lower_span(*span),
}))]
})
ItemKind::MacroDef(name, MacroDef { eii_extern_target: Some(target), .. }) => self
.lower_eii_extern_target(id, *name, target)
.map(|decl| vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(decl))])
.unwrap_or_default(),
ItemKind::ExternCrate(..)
| ItemKind::Use(..)
| ItemKind::Static(..)

View file

@ -103,19 +103,19 @@ fn eii_(
// span of the declaring item without attributes
let item_span = func.sig.span;
// span of the eii attribute and the item below it, i.e. the full declaration
let decl_span = eii_attr_span.to(item_span);
let foreign_item_name = func.ident;
let mut return_items = Vec::new();
if func.body.is_some() {
return_items.push(Box::new(generate_default_impl(
ecx,
&func,
impl_unsafe,
macro_name,
eii_attr_span,
item_span,
foreign_item_name,
)))
}
@ -133,7 +133,6 @@ fn eii_(
macro_name,
foreign_item_name,
impl_unsafe,
decl_span,
)));
return_items.into_iter().map(wrap_item).collect()
@ -187,11 +186,13 @@ fn filter_attrs_for_multiple_eii_attr(
}
fn generate_default_impl(
ecx: &mut ExtCtxt<'_>,
func: &ast::Fn,
impl_unsafe: bool,
macro_name: Ident,
eii_attr_span: Span,
item_span: Span,
foreign_item_name: Ident,
) -> ast::Item {
// FIXME: re-add some original attrs
let attrs = ThinVec::new();
@ -208,6 +209,21 @@ fn generate_default_impl(
},
span: eii_attr_span,
is_default: true,
known_eii_macro_resolution: Some(ast::EiiExternTarget {
extern_item_path: ast::Path {
span: foreign_item_name.span,
segments: thin_vec![
ast::PathSegment {
ident: Ident::from_str_and_span("super", foreign_item_name.span,),
id: DUMMY_NODE_ID,
args: None
},
ast::PathSegment { ident: foreign_item_name, id: DUMMY_NODE_ID, args: None },
],
tokens: None,
},
impl_unsafe,
}),
});
ast::Item {
@ -236,18 +252,66 @@ fn generate_default_impl(
stmts: thin_vec![ast::Stmt {
id: DUMMY_NODE_ID,
kind: ast::StmtKind::Item(Box::new(ast::Item {
attrs,
attrs: ThinVec::new(),
id: DUMMY_NODE_ID,
span: item_span,
vis: ast::Visibility {
span: eii_attr_span,
span: item_span,
kind: ast::VisibilityKind::Inherited,
tokens: None
},
kind: ItemKind::Fn(Box::new(default_func)),
kind: ItemKind::Mod(
ast::Safety::Default,
Ident::from_str_and_span("dflt", item_span),
ast::ModKind::Loaded(
thin_vec![
Box::new(ast::Item {
attrs: thin_vec![ecx.attr_nested_word(
sym::allow,
sym::unused_imports,
item_span
),],
id: DUMMY_NODE_ID,
span: item_span,
vis: ast::Visibility {
span: eii_attr_span,
kind: ast::VisibilityKind::Inherited,
tokens: None
},
kind: ItemKind::Use(ast::UseTree {
prefix: ast::Path::from_ident(
Ident::from_str_and_span(
"super", item_span,
)
),
kind: ast::UseTreeKind::Glob,
span: item_span,
}),
tokens: None,
}),
Box::new(ast::Item {
attrs,
id: DUMMY_NODE_ID,
span: item_span,
vis: ast::Visibility {
span: eii_attr_span,
kind: ast::VisibilityKind::Inherited,
tokens: None
},
kind: ItemKind::Fn(Box::new(default_func)),
tokens: None,
}),
],
ast::Inline::Yes,
ast::ModSpans {
inner_span: item_span,
inject_use_span: item_span,
}
)
),
tokens: None,
})),
span: eii_attr_span
span: eii_attr_span,
}],
id: DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default,
@ -352,7 +416,6 @@ fn generate_attribute_macro_to_implement(
macro_name: Ident,
foreign_item_name: Ident,
impl_unsafe: bool,
decl_span: Span,
) -> ast::Item {
let mut macro_attrs = ThinVec::new();
@ -394,7 +457,6 @@ fn generate_attribute_macro_to_implement(
eii_extern_target: Some(ast::EiiExternTarget {
extern_item_path: ast::Path::from_ident(foreign_item_name),
impl_unsafe,
span: decl_span,
}),
},
),
@ -451,7 +513,7 @@ pub(crate) fn eii_extern_target(
false
};
d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe, span });
d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe });
// Return the original item and the new methods.
vec![item]
@ -508,6 +570,7 @@ pub(crate) fn eii_shared_macro(
impl_safety: meta_item.unsafety,
span,
is_default,
known_eii_macro_resolution: None,
});
vec![item]

View file

@ -3,7 +3,9 @@ use std::str::FromStr;
use rustc_abi::{Align, ExternAbi};
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
use rustc_hir::attrs::{AttributeKind, InlineAttr, Linkage, RtsanSetting, UsedBy};
use rustc_hir::attrs::{
AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy,
};
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};
@ -285,11 +287,23 @@ fn process_builtin_attrs(
}
AttributeKind::EiiImpls(impls) => {
for i in impls {
let extern_item = find_attr!(
tcx.get_all_attrs(i.eii_macro),
AttributeKind::EiiExternTarget(target) => target.eii_extern_target
)
.expect("eii should have declaration macro with extern target attribute");
let extern_item = match i.resolution {
EiiImplResolution::Macro(def_id) => {
let Some(extern_item) = find_attr!(
tcx.get_all_attrs(def_id),
AttributeKind::EiiExternTarget(target) => target.eii_extern_target
) else {
tcx.dcx().span_delayed_bug(
i.span,
"resolved to something that's not an EII",
);
continue;
};
extern_item
}
EiiImplResolution::Known(decl) => decl.eii_extern_target,
EiiImplResolution::Error(_eg) => continue,
};
// this is to prevent a bug where a single crate defines both the default and explicit implementation
// for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure
@ -302,7 +316,7 @@ 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(&i.eii_macro).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default)
&& tcx.externally_implementable_items(LOCAL_CRATE).get(&extern_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default)
{
continue;
}

View file

@ -11,7 +11,7 @@ use rustc_error_messages::{DiagArgValue, IntoDiagArg};
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::def_id::DefId;
use rustc_span::hygiene::Transparency;
use rustc_span::{Ident, Span, Symbol};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
pub use rustc_target::spec::SanitizerSet;
use thin_vec::ThinVec;
@ -19,9 +19,22 @@ use crate::attrs::pretty_printing::PrintAttribute;
use crate::limit::Limit;
use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum EiiImplResolution {
/// Usually, finding the extern item that an EII implementation implements means finding
/// the defid of the associated attribute macro, and looking at *its* attributes to find
/// 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),
/// For when resolution failed, but we want to continue compilation
Error(ErrorGuaranteed),
}
#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub struct EiiImpl {
pub eii_macro: DefId,
pub resolution: EiiImplResolution,
pub impl_marked_unsafe: bool,
pub span: Span,
pub inner_span: Span,
@ -33,7 +46,7 @@ pub struct EiiDecl {
pub eii_extern_target: DefId,
/// whether or not it is unsafe to implement this EII
pub impl_unsafe: bool,
pub span: Span,
pub name: Ident,
}
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]

View file

@ -32,21 +32,21 @@ use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii};
pub(crate) fn compare_eii_function_types<'tcx>(
tcx: TyCtxt<'tcx>,
external_impl: LocalDefId,
declaration: DefId,
foreign_item: DefId,
eii_name: Symbol,
eii_attr_span: Span,
) -> Result<(), ErrorGuaranteed> {
check_is_structurally_compatible(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
check_is_structurally_compatible(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?;
let external_impl_span = tcx.def_span(external_impl);
let cause = ObligationCause::new(
external_impl_span,
external_impl,
ObligationCauseCode::CompareEii { external_impl, declaration },
ObligationCauseCode::CompareEii { external_impl, declaration: foreign_item },
);
// FIXME(eii): even if we don't support generic functions, we should support explicit outlive bounds here
let param_env = tcx.param_env(declaration);
let param_env = tcx.param_env(foreign_item);
let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
@ -62,7 +62,7 @@ pub(crate) fn compare_eii_function_types<'tcx>(
let mut wf_tys = FxIndexSet::default();
let norm_cause = ObligationCause::misc(external_impl_span, external_impl);
let declaration_sig = tcx.fn_sig(declaration).instantiate_identity();
let declaration_sig = tcx.fn_sig(foreign_item).instantiate_identity();
let declaration_sig = tcx.liberate_late_bound_regions(external_impl.into(), declaration_sig);
debug!(?declaration_sig);
@ -103,7 +103,7 @@ pub(crate) fn compare_eii_function_types<'tcx>(
cause,
param_env,
terr,
(declaration, declaration_sig),
(foreign_item, declaration_sig),
(external_impl, external_impl_sig),
eii_attr_span,
eii_name,

View file

@ -6,7 +6,7 @@ use rustc_abi::{ExternAbi, ScalableElt};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl};
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
@ -1196,27 +1196,28 @@ fn check_item_fn(
fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) {
// does the function have an EiiImpl attribute? that contains the defid of a *macro*
// that was used to mark the implementation. This is a two step process.
for EiiImpl { eii_macro, span, .. } in
for EiiImpl { resolution, span, .. } in
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiImpls(impls) => impls)
.into_iter()
.flatten()
{
// we expect this macro to have the `EiiMacroFor` attribute, that points to a function
// signature that we'd like to compare the function we're currently checking with
if let Some(eii_extern_target) = find_attr!(tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl {eii_extern_target, ..}) => *eii_extern_target)
{
let _ = compare_eii_function_types(
tcx,
def_id,
eii_extern_target,
tcx.item_name(*eii_macro),
*span,
);
} else {
panic!(
"EII impl macro {eii_macro:?} did not have an eii extern target attribute pointing to a foreign function"
)
}
let (foreign_item, name) = match resolution {
EiiImplResolution::Macro(def_id) => {
// we expect this macro to have the `EiiMacroFor` attribute, that points to a function
// signature that we'd like to compare the function we're currently checking with
if let Some(foreign_item) = find_attr!(tcx.get_all_attrs(*def_id), AttributeKind::EiiExternTarget(EiiDecl {eii_extern_target: t, ..}) => *t)
{
(foreign_item, tcx.item_name(*def_id))
} else {
tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII");
continue;
}
}
EiiImplResolution::Known(decl) => (decl.eii_extern_target, decl.name.name),
EiiImplResolution::Error(_eg) => continue,
};
let _ = compare_eii_function_types(tcx, def_id, foreign_item, name, *span);
}
}

View file

@ -1,5 +1,5 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl};
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution};
use rustc_hir::def_id::DefId;
use rustc_hir::find_attr;
use rustc_middle::query::LocalCrate;
@ -9,7 +9,7 @@ use rustc_middle::ty::TyCtxt;
pub(crate) type EiiMapEncodedKeyValue = (DefId, (EiiDecl, Vec<(DefId, EiiImpl)>));
pub(crate) type EiiMap = FxIndexMap<
DefId, // the defid of the macro that declared the eii
DefId, // the defid of the foreign item associated with the eii
(
// the corresponding declaration
EiiDecl,
@ -28,18 +28,34 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap
for i in
find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiImpls(e) => e).into_iter().flatten()
{
eiis.entry(i.eii_macro)
.or_insert_with(|| {
let 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)
(find_attr!(tcx.get_all_attrs(i.eii_macro), AttributeKind::EiiExternTarget(d) => *d).unwrap(), Default::default())
}).1.insert(id.into(), *i);
let Some(decl) = find_attr!(tcx.get_all_attrs(macro_defid), AttributeKind::EiiExternTarget(d) => *d)
else {
// skip if it doesn't have eii_extern_target (if we resolved to another macro that's not an EII)
tcx.dcx()
.span_delayed_bug(i.span, "resolved to something that's not an EII");
continue;
};
decl
}
EiiImplResolution::Known(decl) => decl,
EiiImplResolution::Error(_eg) => continue,
};
// FIXME(eii) remove extern target from encoded decl
eiis.entry(decl.eii_extern_target)
.or_insert_with(|| (decl, Default::default()))
.1
.insert(id.into(), *i);
}
// if we find a new declaration, add it to the list without a known implementation
if let Some(decl) =
find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiExternTarget(d) => *d)
{
eiis.entry(id.into()).or_insert((decl, Default::default()));
eiis.entry(decl.eii_extern_target).or_insert((decl, Default::default()));
}
}

View file

@ -1657,9 +1657,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
empty_proc_macro!(self);
let externally_implementable_items = self.tcx.externally_implementable_items(LOCAL_CRATE);
self.lazy_array(externally_implementable_items.iter().map(|(decl_did, (decl, impls))| {
(*decl_did, (decl.clone(), impls.iter().map(|(impl_did, i)| (*impl_did, *i)).collect()))
}))
self.lazy_array(externally_implementable_items.iter().map(
|(foreign_item, (decl, impls))| {
(
*foreign_item,
(decl.clone(), impls.iter().map(|(impl_did, i)| (*impl_did, *i)).collect()),
)
},
))
}
#[instrument(level = "trace", skip(self))]

View file

@ -21,8 +21,8 @@ use rustc_feature::{
BuiltinAttribute,
};
use rustc_hir::attrs::{
AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, InlineAttr, MirDialect, MirPhase,
ReprAttr, SanitizerSet,
AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr,
MirDialect, MirPhase, ReprAttr, SanitizerSet,
};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalModDefId;
@ -506,7 +506,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) {
for EiiImpl { span, inner_span, eii_macro, impl_marked_unsafe, is_default: _ } in impls {
for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls {
match target {
Target::Fn => {}
_ => {
@ -514,7 +514,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
if find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl { impl_unsafe, .. }) if *impl_unsafe)
if let EiiImplResolution::Macro(eii_macro) = resolution
&& find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl { impl_unsafe, .. }) if *impl_unsafe)
&& !impl_marked_unsafe
{
self.dcx().emit_err(errors::EiiImplRequiresUnsafe {
@ -758,9 +759,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
if let Some(impls) = find_attr!(attrs, AttributeKind::EiiImpls(impls) => impls) {
let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
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::Error(_eg) => continue,
};
self.dcx().emit_err(errors::EiiWithTrackCaller {
attr_span,
name: self.tcx.item_name(i.eii_macro),
name,
sig_span: sig.span,
});
}

View file

@ -81,7 +81,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, ():
}
// now we have all eiis! For each of them, choose one we want to actually generate.
for (decl_did, FoundEii { decl, decl_crate, impls }) in eiis {
for (foreign_item, FoundEii { decl, decl_crate, impls }) in eiis {
let mut default_impls = Vec::new();
let mut explicit_impls = Vec::new();
@ -97,7 +97,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, ():
// is instantly an error.
if explicit_impls.len() > 1 {
tcx.dcx().emit_err(DuplicateEiiImpls {
name: tcx.item_name(decl_did),
name: decl.name.name,
first_span: tcx.def_span(explicit_impls[0].0),
first_crate: tcx.crate_name(explicit_impls[0].1),
second_span: tcx.def_span(explicit_impls[1].0),
@ -116,7 +116,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, ():
}
if default_impls.len() > 1 {
let decl_span = tcx.def_ident_span(decl_did).unwrap();
let decl_span = tcx.def_ident_span(foreign_item).unwrap();
tcx.dcx().span_delayed_bug(decl_span, "multiple not supported right now");
}
@ -139,8 +139,9 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, ():
tcx.dcx().emit_err(EiiWithoutImpl {
current_crate_name: tcx.crate_name(LOCAL_CRATE),
decl_crate_name: tcx.crate_name(decl_crate),
name: tcx.item_name(decl_did),
span: decl.span,
// FIXME: shouldn't call `item_name`
name: decl.name.name,
span: decl.name.span,
help: (),
});

View file

@ -1069,8 +1069,20 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
debug!("(resolving function) entering function");
if let FnKind::Fn(_, _, f) = fn_kind {
for EiiImpl { node_id, eii_macro_path, .. } in &f.eii_impls {
self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro);
for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in &f.eii_impls
{
// 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.extern_item_path,
PathSource::Expr(None),
);
} else {
self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro);
}
}
}
@ -2917,7 +2929,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.parent_scope.macro_rules = self.r.macro_rules_scopes[&def_id];
}
if let Some(EiiExternTarget { extern_item_path, impl_unsafe: _, span: _ }) =
if let Some(EiiExternTarget { extern_item_path, impl_unsafe: _ }) =
&macro_def.eii_extern_target
{
self.smart_resolve_path(

View file

@ -0,0 +1,7 @@
//@ compile-flags: --crate-type rlib
//@ build-pass
#![feature(extern_item_impls)]
macro_rules! foo_impl { () => {} }
#[eii]
fn foo_impl() {}

View file

@ -1,5 +1,5 @@
//@ aux-build:codegen3.rs
// Tests whether name resulution respects privacy properly.
// Tests whether name resolution respects privacy properly.
#![feature(extern_item_impls)]
extern crate codegen3 as codegen;

View file

@ -17,18 +17,18 @@ LL | fn decl1(x: u64);
| ^^^^^^^^^^^^^^^^^
error: `#[eii2]` required, but not found
--> $DIR/auxiliary/codegen3.rs:9:1
--> $DIR/auxiliary/codegen3.rs:9:7
|
LL | #[eii(eii2)]
| ^^^^^^^^^^^^ expected because `#[eii2]` was declared here in crate `codegen3`
| ^^^^ expected because `#[eii2]` was declared here in crate `codegen3`
|
= help: expected at least one implementation in crate `privacy2` or any of its dependencies
error: `#[eii3]` required, but not found
--> $DIR/auxiliary/codegen3.rs:13:5
--> $DIR/auxiliary/codegen3.rs:13:11
|
LL | #[eii(eii3)]
| ^^^^^^^^^^^^ expected because `#[eii3]` was declared here in crate `codegen3`
| ^^^^ expected because `#[eii3]` was declared here in crate `codegen3`
|
= help: expected at least one implementation in crate `privacy2` or any of its dependencies