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:
commit
a8028abedb
15 changed files with 280 additions and 115 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(..)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: (),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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: _ }) =
|
||||
¯o_def.eii_extern_target
|
||||
{
|
||||
self.smart_resolve_path(
|
||||
|
|
|
|||
7
tests/ui/eii/duplicate/eii_conflict_with_macro.rs
Normal file
7
tests/ui/eii/duplicate/eii_conflict_with_macro.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//@ compile-flags: --crate-type rlib
|
||||
//@ build-pass
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
macro_rules! foo_impl { () => {} }
|
||||
#[eii]
|
||||
fn foo_impl() {}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue