Auto merge of #146348 - jdonszelmann:eiiv3, r=lcnr,oli-obk
Externally implementable items Supersedes https://github.com/rust-lang/rust/pull/140010 Tracking issue: https://github.com/rust-lang/rust/issues/125418 Getting started: ```rust #![feature(eii)] #[eii(eii1)] pub fn decl1(x: u64) // body optional (it's the default) { println!("default {x}"); } // in another crate, maybe #[eii1] pub fn decl2(x: u64) { println!("explicit {x}"); } fn main() { decl1(4); } ``` - tiny perf regression, underlying issue makes multiple things in the compiler slow, not just EII, planning to solve those separately. - No codegen_gcc support, they don't have bindings for weak symbols yet but could - No windows support yet for weak definitions This PR merges the implementation of EII for just llvm + not windows, doesn't yet contain like a new panic handler implementation or alloc handler. With this implementation, it would support implementing the panic handler in terms of EII already since it requires no default implementation so no weak symbols The PR has been open in various forms for about a year now, but I feel that having some implementation merged to build upon
This commit is contained in:
commit
3f4dc1e02d
140 changed files with 3452 additions and 65 deletions
|
|
@ -2107,6 +2107,19 @@ pub struct MacroDef {
|
|||
pub body: Box<DelimArgs>,
|
||||
/// `true` if macro was defined with `macro_rules`.
|
||||
pub macro_rules: bool,
|
||||
|
||||
/// If this is a macro used for externally implementable items,
|
||||
/// it refers to an extern item which is its "target". This requires
|
||||
/// name resolution so can't just be an attribute, so we store it in this field.
|
||||
pub eii_extern_target: Option<EiiExternTarget>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
|
||||
pub struct EiiExternTarget {
|
||||
/// path to the extern item we're targetting
|
||||
pub extern_item_path: Path,
|
||||
pub impl_unsafe: bool,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
|
||||
|
|
@ -3746,6 +3759,21 @@ pub struct Fn {
|
|||
pub contract: Option<Box<FnContract>>,
|
||||
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
|
||||
pub body: Option<Box<Block>>,
|
||||
|
||||
/// This function is an implementation of an externally implementable item (EII).
|
||||
/// This means, there was an EII declared somewhere and this function is the
|
||||
/// implementation that should be run when the declaration is called.
|
||||
pub eii_impls: ThinVec<EiiImpl>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub struct EiiImpl {
|
||||
pub node_id: NodeId,
|
||||
pub eii_macro_path: Path,
|
||||
pub impl_safety: Safety,
|
||||
pub span: Span,
|
||||
pub inner_span: Span,
|
||||
pub is_default: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
|
|
@ -4112,7 +4140,7 @@ mod size_asserts {
|
|||
static_assert_size!(Block, 32);
|
||||
static_assert_size!(Expr, 72);
|
||||
static_assert_size!(ExprKind, 40);
|
||||
static_assert_size!(Fn, 184);
|
||||
static_assert_size!(Fn, 192);
|
||||
static_assert_size!(ForeignItem, 80);
|
||||
static_assert_size!(ForeignItemKind, 16);
|
||||
static_assert_size!(GenericArg, 24);
|
||||
|
|
|
|||
|
|
@ -393,6 +393,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
ThinVec<Pat>,
|
||||
ThinVec<Box<Ty>>,
|
||||
ThinVec<TyPat>,
|
||||
ThinVec<EiiImpl>,
|
||||
);
|
||||
|
||||
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
|
||||
|
|
@ -486,6 +487,8 @@ macro_rules! common_visitor_and_walkers {
|
|||
WhereEqPredicate,
|
||||
WhereRegionPredicate,
|
||||
YieldKind,
|
||||
EiiExternTarget,
|
||||
EiiImpl,
|
||||
);
|
||||
|
||||
/// Each method of this trait is a hook to be potentially
|
||||
|
|
@ -920,13 +923,13 @@ macro_rules! common_visitor_and_walkers {
|
|||
_ctxt,
|
||||
// Visibility is visited as a part of the item.
|
||||
_vis,
|
||||
Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
|
||||
Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls },
|
||||
) => {
|
||||
let FnSig { header, decl, span } = sig;
|
||||
visit_visitable!($($mut)? vis,
|
||||
defaultness, ident, header, generics, decl,
|
||||
contract, body, span, define_opaque
|
||||
)
|
||||
contract, body, span, define_opaque, eii_impls
|
||||
);
|
||||
}
|
||||
FnKind::Closure(binder, coroutine_kind, decl, body) =>
|
||||
visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
use rustc_hir::attrs::{AttributeKind, EiiDecl};
|
||||
use rustc_hir::def::{DefKind, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
|
||||
use rustc_hir::{
|
||||
|
|
@ -11,6 +11,7 @@ use rustc_hir::{
|
|||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
|
@ -133,10 +134,92 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_extra_attrs_for_item_kind(
|
||||
&mut self,
|
||||
id: NodeId,
|
||||
i: &ItemKind,
|
||||
) -> Vec<hir::Attribute> {
|
||||
match i {
|
||||
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(),
|
||||
))]
|
||||
}
|
||||
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),
|
||||
}))]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
ItemKind::ExternCrate(..)
|
||||
| ItemKind::Use(..)
|
||||
| ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::Mod(..)
|
||||
| ItemKind::ForeignMod(..)
|
||||
| ItemKind::GlobalAsm(..)
|
||||
| ItemKind::TyAlias(..)
|
||||
| ItemKind::Enum(..)
|
||||
| ItemKind::Struct(..)
|
||||
| ItemKind::Union(..)
|
||||
| ItemKind::Trait(..)
|
||||
| ItemKind::TraitAlias(..)
|
||||
| ItemKind::Impl(..)
|
||||
| ItemKind::MacCall(..)
|
||||
| ItemKind::MacroDef(..)
|
||||
| ItemKind::Delegation(..)
|
||||
| ItemKind::DelegationMac(..) => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
|
||||
let vis_span = self.lower_span(i.vis.span);
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_ast_item(i));
|
||||
|
||||
let extra_hir_attributes = self.generate_extra_attrs_for_item_kind(i.id, &i.kind);
|
||||
let attrs = self.lower_attrs_with_extra(
|
||||
hir_id,
|
||||
&i.attrs,
|
||||
i.span,
|
||||
Target::from_ast_item(i),
|
||||
&extra_hir_attributes,
|
||||
);
|
||||
|
||||
let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind);
|
||||
let item = hir::Item {
|
||||
owner_id: hir_id.expect_owner(),
|
||||
|
|
@ -144,6 +227,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
vis_span,
|
||||
span: self.lower_span(i.span),
|
||||
has_delayed_lints: !self.delayed_lints.is_empty(),
|
||||
eii: find_attr!(
|
||||
attrs,
|
||||
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
|
||||
),
|
||||
};
|
||||
self.arena.alloc(item)
|
||||
}
|
||||
|
|
@ -435,7 +522,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
);
|
||||
hir::ItemKind::TraitAlias(constness, ident, generics, bounds)
|
||||
}
|
||||
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
|
||||
ItemKind::MacroDef(ident, MacroDef { body, macro_rules, eii_extern_target: _ }) => {
|
||||
let ident = self.lower_ident(*ident);
|
||||
let body = Box::new(self.lower_delim_args(body));
|
||||
let def_id = self.local_def_id(id);
|
||||
|
|
@ -446,7 +533,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
def_kind.descr(def_id.to_def_id())
|
||||
);
|
||||
};
|
||||
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
|
||||
let macro_def = self.arena.alloc(ast::MacroDef {
|
||||
body,
|
||||
macro_rules: *macro_rules,
|
||||
eii_extern_target: None,
|
||||
});
|
||||
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
|
||||
}
|
||||
ItemKind::Delegation(box delegation) => {
|
||||
|
|
@ -465,6 +556,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_path_simple_eii(&mut self, id: NodeId, path: &Path) -> Option<DefId> {
|
||||
let res = self.resolver.get_partial_res(id)?;
|
||||
let Some(did) = res.expect_full_res().opt_def_id() else {
|
||||
self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(did)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_use_tree(
|
||||
&mut self,
|
||||
|
|
@ -573,6 +674,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
vis_span,
|
||||
span: this.lower_span(use_tree.span),
|
||||
has_delayed_lints: !this.delayed_lints.is_empty(),
|
||||
eii: find_attr!(
|
||||
attrs,
|
||||
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
|
||||
),
|
||||
};
|
||||
hir::OwnerNode::Item(this.arena.alloc(item))
|
||||
});
|
||||
|
|
|
|||
|
|
@ -971,11 +971,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
target_span: Span,
|
||||
target: Target,
|
||||
) -> &'hir [hir::Attribute] {
|
||||
if attrs.is_empty() {
|
||||
self.lower_attrs_with_extra(id, attrs, target_span, target, &[])
|
||||
}
|
||||
|
||||
fn lower_attrs_with_extra(
|
||||
&mut self,
|
||||
id: HirId,
|
||||
attrs: &[Attribute],
|
||||
target_span: Span,
|
||||
target: Target,
|
||||
extra_hir_attributes: &[hir::Attribute],
|
||||
) -> &'hir [hir::Attribute] {
|
||||
if attrs.is_empty() && extra_hir_attributes.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
let lowered_attrs =
|
||||
let mut lowered_attrs =
|
||||
self.lower_attrs_vec(attrs, self.lower_span(target_span), id, target);
|
||||
lowered_attrs.extend(extra_hir_attributes.iter().cloned());
|
||||
|
||||
assert_eq!(id.owner, self.current_hir_id_owner);
|
||||
let ret = self.arena.alloc_from_iter(lowered_attrs);
|
||||
|
|
|
|||
|
|
@ -1179,11 +1179,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
contract: _,
|
||||
body,
|
||||
define_opaque: _,
|
||||
eii_impls,
|
||||
},
|
||||
) => {
|
||||
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
|
||||
self.check_defaultness(item.span, *defaultness);
|
||||
|
||||
for EiiImpl { eii_macro_path, .. } in eii_impls {
|
||||
self.visit_path(eii_macro_path);
|
||||
}
|
||||
|
||||
let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
|
||||
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
|
||||
self.dcx().emit_err(errors::FnWithoutBody {
|
||||
|
|
|
|||
|
|
@ -865,6 +865,17 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
sp: Span,
|
||||
print_visibility: impl FnOnce(&mut Self),
|
||||
) {
|
||||
if let Some(eii_extern_target) = ¯o_def.eii_extern_target {
|
||||
self.word("#[eii_extern_target(");
|
||||
self.print_path(&eii_extern_target.extern_item_path, false, 0);
|
||||
if eii_extern_target.impl_unsafe {
|
||||
self.word(",");
|
||||
self.space();
|
||||
self.word("unsafe");
|
||||
}
|
||||
self.word(")]");
|
||||
self.hardbreak();
|
||||
}
|
||||
let (kw, has_bang) = if macro_def.macro_rules {
|
||||
("macro_rules", true)
|
||||
} else {
|
||||
|
|
@ -2162,6 +2173,15 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_meta_item(&mut self, item: &ast::MetaItem) {
|
||||
let ib = self.ibox(INDENT_UNIT);
|
||||
|
||||
match item.unsafety {
|
||||
ast::Safety::Unsafe(_) => {
|
||||
self.word("unsafe");
|
||||
self.popen();
|
||||
}
|
||||
ast::Safety::Default | ast::Safety::Safe(_) => {}
|
||||
}
|
||||
|
||||
match &item.kind {
|
||||
ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
|
||||
ast::MetaItemKind::NameValue(value) => {
|
||||
|
|
@ -2177,6 +2197,12 @@ impl<'a> State<'a> {
|
|||
self.pclose();
|
||||
}
|
||||
}
|
||||
|
||||
match item.unsafety {
|
||||
ast::Safety::Unsafe(_) => self.pclose(),
|
||||
ast::Safety::Default | ast::Safety::Safe(_) => {}
|
||||
}
|
||||
|
||||
self.end(ib);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use ast::StaticItem;
|
||||
use itertools::{Itertools, Position};
|
||||
use rustc_ast::{self as ast, ModKind, TraitAlias};
|
||||
use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias};
|
||||
use rustc_span::Ident;
|
||||
|
||||
use crate::pp::BoxMarker;
|
||||
|
|
@ -671,10 +671,25 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) {
|
||||
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque } = func;
|
||||
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impls } =
|
||||
func;
|
||||
|
||||
self.print_define_opaques(define_opaque.as_deref());
|
||||
|
||||
for EiiImpl { eii_macro_path, impl_safety, .. } in eii_impls {
|
||||
self.word("#[");
|
||||
if let Safety::Unsafe(..) = impl_safety {
|
||||
self.word("unsafe");
|
||||
self.popen();
|
||||
}
|
||||
self.print_path(eii_macro_path, false, 0);
|
||||
if let Safety::Unsafe(..) = impl_safety {
|
||||
self.pclose();
|
||||
}
|
||||
self.word("]");
|
||||
self.hardbreak();
|
||||
}
|
||||
|
||||
let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));
|
||||
|
||||
self.print_visibility(vis);
|
||||
|
|
|
|||
|
|
@ -699,3 +699,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisPa
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
|
||||
}
|
||||
|
||||
pub(crate) struct EiiExternItemParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for EiiExternItemParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_eii_extern_item];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiExternItem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{
|
|||
};
|
||||
use crate::attributes::body::CoroutineParser;
|
||||
use crate::attributes::codegen_attrs::{
|
||||
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
|
||||
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
|
||||
ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser,
|
||||
NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
|
||||
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
|
||||
TrackCallerParser, UsedParser,
|
||||
};
|
||||
|
|
@ -227,6 +227,7 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<CoroutineParser>>,
|
||||
Single<WithoutArgs<DenyExplicitImplParser>>,
|
||||
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
|
||||
Single<WithoutArgs<EiiExternItemParser>>,
|
||||
Single<WithoutArgs<ExportStableParser>>,
|
||||
Single<WithoutArgs<FfiConstParser>>,
|
||||
Single<WithoutArgs<FfiPureParser>>,
|
||||
|
|
|
|||
|
|
@ -152,6 +152,17 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
|
|||
|
||||
builtin_macros_duplicate_macro_attribute = duplicated attribute
|
||||
|
||||
builtin_macros_eii_extern_target_expected_list = `#[eii_extern_target(...)]` expects a list of one or two elements
|
||||
builtin_macros_eii_extern_target_expected_macro = `#[eii_extern_target(...)]` is only valid on macros
|
||||
builtin_macros_eii_extern_target_expected_unsafe = expected this argument to be "unsafe"
|
||||
.note = the second argument is optional
|
||||
|
||||
builtin_macros_eii_only_once = `#[{$name}]` can only be specified once
|
||||
.note = specified again here
|
||||
|
||||
builtin_macros_eii_shared_macro_expected_function = `#[{$name}]` is only valid on functions
|
||||
builtin_macros_eii_shared_macro_expected_max_one_argument = `#[{$name}]` expected no arguments or a single argument: `#[{$name}(default)]`
|
||||
|
||||
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
|
||||
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
|
||||
.cargo_typo = there is a similar Cargo environment variable: `{$suggested_var}`
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
|
|||
contract: None,
|
||||
body,
|
||||
define_opaque: None,
|
||||
eii_impls: ThinVec::new(),
|
||||
}));
|
||||
|
||||
let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)];
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ mod llvm_enzyme {
|
|||
contract: None,
|
||||
body: Some(d_body),
|
||||
define_opaque: None,
|
||||
eii_impls: ThinVec::new(),
|
||||
});
|
||||
let mut rustc_ad_attr =
|
||||
Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));
|
||||
|
|
|
|||
|
|
@ -1092,6 +1092,7 @@ impl<'a> MethodDef<'a> {
|
|||
contract: None,
|
||||
body: Some(body_block),
|
||||
define_opaque: None,
|
||||
eii_impls: ThinVec::new(),
|
||||
})),
|
||||
tokens: None,
|
||||
})
|
||||
|
|
|
|||
447
compiler/rustc_builtin_macros/src/eii.rs
Normal file
447
compiler/rustc_builtin_macros/src/eii.rs
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
use rustc_ast::token::{Delimiter, TokenKind};
|
||||
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::{
|
||||
DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, Stmt, StmtKind, ast, token, tokenstream,
|
||||
};
|
||||
use rustc_ast_pretty::pprust::path_to_string;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::{Ident, Span, kw, sym};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
|
||||
use crate::errors::{
|
||||
EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe,
|
||||
EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction,
|
||||
};
|
||||
|
||||
/// ```rust
|
||||
/// #[eii]
|
||||
/// fn panic_handler();
|
||||
///
|
||||
/// // or:
|
||||
///
|
||||
/// #[eii(panic_handler)]
|
||||
/// fn panic_handler();
|
||||
///
|
||||
/// // expansion:
|
||||
///
|
||||
/// extern "Rust" {
|
||||
/// fn panic_handler();
|
||||
/// }
|
||||
///
|
||||
/// #[rustc_builtin_macro(eii_shared_macro)]
|
||||
/// #[eii_extern_target(panic_handler)]
|
||||
/// macro panic_handler() {}
|
||||
/// ```
|
||||
pub(crate) fn eii(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
eii_(ecx, span, meta_item, item, false)
|
||||
}
|
||||
|
||||
pub(crate) fn unsafe_eii(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
eii_(ecx, span, meta_item, item, true)
|
||||
}
|
||||
|
||||
fn eii_(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
impl_unsafe: bool,
|
||||
) -> Vec<Annotatable> {
|
||||
let span = ecx.with_def_site_ctxt(span);
|
||||
|
||||
let (item, stmt) = if let Annotatable::Item(item) = item {
|
||||
(item, false)
|
||||
} else if let Annotatable::Stmt(ref stmt) = item
|
||||
&& let StmtKind::Item(ref item) = stmt.kind
|
||||
{
|
||||
(item.clone(), true)
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
|
||||
span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
let orig_item = item.clone();
|
||||
|
||||
let item = *item;
|
||||
|
||||
let ast::Item { attrs, id: _, span: item_span, vis, kind: ItemKind::Fn(mut func), tokens: _ } =
|
||||
item
|
||||
else {
|
||||
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
|
||||
span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![Annotatable::Item(Box::new(item))];
|
||||
};
|
||||
|
||||
// Detect when this is the *second* eii attribute on an item.
|
||||
let mut new_attrs = ThinVec::new();
|
||||
for i in attrs {
|
||||
if i.has_name(sym::eii) {
|
||||
ecx.dcx().emit_err(EiiOnlyOnce {
|
||||
span: i.span,
|
||||
first_span: span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
} else {
|
||||
new_attrs.push(i);
|
||||
}
|
||||
}
|
||||
let attrs = new_attrs;
|
||||
|
||||
let macro_name = if meta_item.is_word() {
|
||||
func.ident
|
||||
} else if let Some([first]) = meta_item.meta_item_list()
|
||||
&& let Some(m) = first.meta_item()
|
||||
&& m.path.segments.len() == 1
|
||||
{
|
||||
m.path.segments[0].ident
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
|
||||
span: meta_item.span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![Annotatable::Item(orig_item)];
|
||||
};
|
||||
|
||||
let mut return_items = Vec::new();
|
||||
|
||||
if func.body.is_some() {
|
||||
let mut default_func = func.clone();
|
||||
func.body = None;
|
||||
default_func.eii_impls.push(ast::EiiImpl {
|
||||
node_id: DUMMY_NODE_ID,
|
||||
eii_macro_path: ast::Path::from_ident(macro_name),
|
||||
impl_safety: if impl_unsafe { ast::Safety::Unsafe(span) } else { ast::Safety::Default },
|
||||
span,
|
||||
inner_span: macro_name.span,
|
||||
is_default: true, // important!
|
||||
});
|
||||
|
||||
return_items.push(Box::new(ast::Item {
|
||||
attrs: ThinVec::new(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
|
||||
kind: ast::ItemKind::Const(Box::new(ast::ConstItem {
|
||||
ident: Ident { name: kw::Underscore, span },
|
||||
defaultness: ast::Defaultness::Final,
|
||||
generics: ast::Generics::default(),
|
||||
ty: Box::new(ast::Ty {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::TyKind::Tup(ThinVec::new()),
|
||||
span,
|
||||
tokens: None,
|
||||
}),
|
||||
rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::ExprKind::Block(
|
||||
Box::new(ast::Block {
|
||||
stmts: thin_vec![ast::Stmt {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::StmtKind::Item(Box::new(ast::Item {
|
||||
attrs: thin_vec![], // FIXME: re-add some original attrs
|
||||
id: DUMMY_NODE_ID,
|
||||
span: item_span,
|
||||
vis: ast::Visibility {
|
||||
span,
|
||||
kind: ast::VisibilityKind::Inherited,
|
||||
tokens: None
|
||||
},
|
||||
kind: ItemKind::Fn(default_func),
|
||||
tokens: None,
|
||||
})),
|
||||
span
|
||||
}],
|
||||
id: DUMMY_NODE_ID,
|
||||
rules: ast::BlockCheckMode::Default,
|
||||
span,
|
||||
tokens: None,
|
||||
}),
|
||||
None,
|
||||
),
|
||||
span,
|
||||
attrs: ThinVec::new(),
|
||||
tokens: None,
|
||||
}))),
|
||||
define_opaque: None,
|
||||
})),
|
||||
tokens: None,
|
||||
}))
|
||||
}
|
||||
|
||||
let decl_span = span.to(func.sig.span);
|
||||
|
||||
let abi = match func.sig.header.ext {
|
||||
// extern "X" fn => extern "X" {}
|
||||
ast::Extern::Explicit(lit, _) => Some(lit),
|
||||
// extern fn => extern {}
|
||||
ast::Extern::Implicit(_) => None,
|
||||
// fn => extern "Rust" {}
|
||||
ast::Extern::None => Some(ast::StrLit {
|
||||
symbol: sym::Rust,
|
||||
suffix: None,
|
||||
symbol_unescaped: sym::Rust,
|
||||
style: ast::StrStyle::Cooked,
|
||||
span,
|
||||
}),
|
||||
};
|
||||
|
||||
// ABI has been moved to the extern {} block, so we remove it from the fn item.
|
||||
func.sig.header.ext = ast::Extern::None;
|
||||
|
||||
// And mark safe functions explicitly as `safe fn`.
|
||||
if func.sig.header.safety == ast::Safety::Default {
|
||||
func.sig.header.safety = ast::Safety::Safe(func.sig.span);
|
||||
}
|
||||
|
||||
// extern "…" { safe fn item(); }
|
||||
let mut extern_item_attrs = attrs.clone();
|
||||
extern_item_attrs.push(ast::Attribute {
|
||||
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
|
||||
item: ast::AttrItem {
|
||||
unsafety: ast::Safety::Default,
|
||||
// Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled.
|
||||
// This attribute makes sure that we later know that this foreign item's symbol should not be.
|
||||
path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)),
|
||||
args: ast::AttrArgs::Empty,
|
||||
tokens: None,
|
||||
},
|
||||
tokens: None,
|
||||
})),
|
||||
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
|
||||
style: ast::AttrStyle::Outer,
|
||||
span,
|
||||
});
|
||||
|
||||
let extern_block = Box::new(ast::Item {
|
||||
attrs: ast::AttrVec::default(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
|
||||
kind: ast::ItemKind::ForeignMod(ast::ForeignMod {
|
||||
extern_span: span,
|
||||
safety: ast::Safety::Unsafe(span),
|
||||
abi,
|
||||
items: From::from([Box::new(ast::ForeignItem {
|
||||
attrs: extern_item_attrs,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: item_span,
|
||||
vis,
|
||||
kind: ast::ForeignItemKind::Fn(func.clone()),
|
||||
tokens: None,
|
||||
})]),
|
||||
}),
|
||||
tokens: None,
|
||||
});
|
||||
|
||||
let mut macro_attrs = attrs.clone();
|
||||
macro_attrs.push(
|
||||
// #[builtin_macro(eii_shared_macro)]
|
||||
ast::Attribute {
|
||||
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
|
||||
item: ast::AttrItem {
|
||||
unsafety: ast::Safety::Default,
|
||||
path: ast::Path::from_ident(Ident::new(sym::rustc_builtin_macro, span)),
|
||||
args: ast::AttrArgs::Delimited(ast::DelimArgs {
|
||||
dspan: DelimSpan::from_single(span),
|
||||
delim: Delimiter::Parenthesis,
|
||||
tokens: TokenStream::new(vec![tokenstream::TokenTree::token_alone(
|
||||
token::TokenKind::Ident(sym::eii_shared_macro, token::IdentIsRaw::No),
|
||||
span,
|
||||
)]),
|
||||
}),
|
||||
tokens: None,
|
||||
},
|
||||
tokens: None,
|
||||
})),
|
||||
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
|
||||
style: ast::AttrStyle::Outer,
|
||||
span,
|
||||
},
|
||||
);
|
||||
|
||||
let macro_def = Box::new(ast::Item {
|
||||
attrs: macro_attrs,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
// pub
|
||||
vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
|
||||
kind: ast::ItemKind::MacroDef(
|
||||
// macro macro_name
|
||||
macro_name,
|
||||
ast::MacroDef {
|
||||
// { () => {} }
|
||||
body: Box::new(ast::DelimArgs {
|
||||
dspan: DelimSpan::from_single(span),
|
||||
delim: Delimiter::Brace,
|
||||
tokens: TokenStream::from_iter([
|
||||
TokenTree::Delimited(
|
||||
DelimSpan::from_single(span),
|
||||
DelimSpacing::new(Spacing::Alone, Spacing::Alone),
|
||||
Delimiter::Parenthesis,
|
||||
TokenStream::default(),
|
||||
),
|
||||
TokenTree::token_alone(TokenKind::FatArrow, span),
|
||||
TokenTree::Delimited(
|
||||
DelimSpan::from_single(span),
|
||||
DelimSpacing::new(Spacing::Alone, Spacing::Alone),
|
||||
Delimiter::Brace,
|
||||
TokenStream::default(),
|
||||
),
|
||||
]),
|
||||
}),
|
||||
macro_rules: false,
|
||||
// #[eii_extern_target(func.ident)]
|
||||
eii_extern_target: Some(ast::EiiExternTarget {
|
||||
extern_item_path: ast::Path::from_ident(func.ident),
|
||||
impl_unsafe,
|
||||
span: decl_span,
|
||||
}),
|
||||
},
|
||||
),
|
||||
tokens: None,
|
||||
});
|
||||
|
||||
return_items.push(extern_block);
|
||||
return_items.push(macro_def);
|
||||
|
||||
if stmt {
|
||||
return_items
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
Annotatable::Stmt(Box::new(Stmt {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: StmtKind::Item(i),
|
||||
span,
|
||||
}))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
return_items.into_iter().map(|i| Annotatable::Item(i)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn eii_extern_target(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
mut item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
let i = if let Annotatable::Item(ref mut item) = item {
|
||||
item
|
||||
} else if let Annotatable::Stmt(ref mut stmt) = item
|
||||
&& let StmtKind::Item(ref mut item) = stmt.kind
|
||||
{
|
||||
item
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
let ItemKind::MacroDef(_, d) = &mut i.kind else {
|
||||
ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
let Some(list) = meta_item.meta_item_list() else {
|
||||
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
if list.len() > 2 {
|
||||
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
|
||||
return vec![item];
|
||||
}
|
||||
|
||||
let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
|
||||
else {
|
||||
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
let impl_unsafe = if let Some(i) = list.get(1) {
|
||||
if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
|
||||
true
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
|
||||
return vec![item];
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe, span });
|
||||
|
||||
// Return the original item and the new methods.
|
||||
vec![item]
|
||||
}
|
||||
|
||||
/// all Eiis share this function as the implementation for their attribute.
|
||||
pub(crate) fn eii_shared_macro(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
mut item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
let i = if let Annotatable::Item(ref mut item) = item {
|
||||
item
|
||||
} else if let Annotatable::Stmt(ref mut stmt) = item
|
||||
&& let StmtKind::Item(ref mut item) = stmt.kind
|
||||
{
|
||||
item
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
|
||||
span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
let ItemKind::Fn(f) = &mut i.kind else {
|
||||
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
|
||||
span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
let is_default = if meta_item.is_word() {
|
||||
false
|
||||
} else if let Some([first]) = meta_item.meta_item_list()
|
||||
&& let Some(m) = first.meta_item()
|
||||
&& m.path.segments.len() == 1
|
||||
{
|
||||
m.path.segments[0].ident.name == kw::Default
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
|
||||
span: meta_item.span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
f.eii_impls.push(EiiImpl {
|
||||
node_id: DUMMY_NODE_ID,
|
||||
eii_macro_path: meta_item.path.clone(),
|
||||
impl_safety: meta_item.unsafety,
|
||||
span,
|
||||
inner_span: meta_item.path.span,
|
||||
is_default,
|
||||
});
|
||||
|
||||
vec![item]
|
||||
}
|
||||
|
|
@ -1010,3 +1010,51 @@ pub(crate) struct CfgSelectUnreachable {
|
|||
#[label]
|
||||
pub wildcard_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_eii_extern_target_expected_macro)]
|
||||
pub(crate) struct EiiExternTargetExpectedMacro {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_eii_extern_target_expected_list)]
|
||||
pub(crate) struct EiiExternTargetExpectedList {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_eii_extern_target_expected_unsafe)]
|
||||
pub(crate) struct EiiExternTargetExpectedUnsafe {
|
||||
#[primary_span]
|
||||
#[note]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_eii_shared_macro_expected_function)]
|
||||
pub(crate) struct EiiSharedMacroExpectedFunction {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_eii_only_once)]
|
||||
pub(crate) struct EiiOnlyOnce {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[note]
|
||||
pub first_span: Span,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_eii_shared_macro_expected_max_one_argument)]
|
||||
pub(crate) struct EiiMacroExpectedMaxOneArgument {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ impl AllocFnFactory<'_, '_> {
|
|||
contract: None,
|
||||
body,
|
||||
define_opaque: None,
|
||||
eii_impls: ThinVec::new(),
|
||||
}));
|
||||
let item = self.cx.item(self.span, self.attrs(method), kind);
|
||||
self.cx.stmt_item(self.ty_span, item)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ mod define_opaque;
|
|||
mod derive;
|
||||
mod deriving;
|
||||
mod edition_panic;
|
||||
mod eii;
|
||||
mod env;
|
||||
mod errors;
|
||||
mod format;
|
||||
|
|
@ -117,9 +118,13 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
define_opaque: define_opaque::expand,
|
||||
derive: derive::Expander { is_const: false },
|
||||
derive_const: derive::Expander { is_const: true },
|
||||
eii: eii::eii,
|
||||
eii_extern_target: eii::eii_extern_target,
|
||||
eii_shared_macro: eii::eii_shared_macro,
|
||||
global_allocator: global_allocator::expand,
|
||||
test: test::expand_test,
|
||||
test_case: test::expand_test_case,
|
||||
unsafe_eii: eii::unsafe_eii,
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -345,6 +345,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box<ast::Item> {
|
|||
contract: None,
|
||||
body: Some(main_body),
|
||||
define_opaque: None,
|
||||
eii_impls: ThinVec::new(),
|
||||
}));
|
||||
|
||||
let main = Box::new(ast::Item {
|
||||
|
|
|
|||
|
|
@ -2583,4 +2583,12 @@ unsafe extern "C" {
|
|||
|
||||
pub(crate) fn LLVMRustSetNoSanitizeAddress(Global: &Value);
|
||||
pub(crate) fn LLVMRustSetNoSanitizeHWAddress(Global: &Value);
|
||||
|
||||
pub(crate) fn LLVMAddAlias2<'ll>(
|
||||
M: &'ll Module,
|
||||
ValueTy: &Type,
|
||||
AddressSpace: c_uint,
|
||||
Aliasee: &Value,
|
||||
Name: *const c_char,
|
||||
) -> &'ll Value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::ptr;
|
|||
use std::string::FromUtf8Error;
|
||||
|
||||
use libc::c_uint;
|
||||
use rustc_abi::{Align, Size, WrappingRange};
|
||||
use rustc_abi::{AddressSpace, Align, Size, WrappingRange};
|
||||
use rustc_llvm::RustString;
|
||||
|
||||
pub(crate) use self::CallConv::*;
|
||||
|
|
@ -452,3 +452,14 @@ pub(crate) fn append_module_inline_asm<'ll>(llmod: &'ll Module, asm: &[u8]) {
|
|||
LLVMAppendModuleInlineAsm(llmod, asm.as_ptr(), asm.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe wrapper for `LLVMAddAlias2`
|
||||
pub(crate) fn add_alias<'ll>(
|
||||
module: &'ll Module,
|
||||
ty: &Type,
|
||||
address_space: AddressSpace,
|
||||
aliasee: &Value,
|
||||
name: &CStr,
|
||||
) -> &'ll Value {
|
||||
unsafe { LLVMAddAlias2(module, ty, address_space.0, aliasee, name.as_ptr()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
use std::ffi::CString;
|
||||
|
||||
use rustc_abi::AddressSpace;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_hir::attrs::Linkage;
|
||||
use rustc_hir::def::DefKind;
|
||||
|
|
@ -7,6 +10,7 @@ use rustc_middle::mir::mono::Visibility;
|
|||
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf};
|
||||
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::spec::{Arch, RelocModel};
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -41,6 +45,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
llvm::set_visibility(g, base::visibility_to_llvm(visibility));
|
||||
self.assume_dso_local(g, false);
|
||||
|
||||
let attrs = self.tcx.codegen_instance_attrs(instance.def);
|
||||
self.add_aliases(g, &attrs.foreign_item_symbol_aliases);
|
||||
|
||||
self.instances.borrow_mut().insert(instance, g);
|
||||
}
|
||||
|
||||
|
|
@ -78,11 +85,31 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
|
||||
self.assume_dso_local(lldecl, false);
|
||||
|
||||
self.add_aliases(lldecl, &attrs.foreign_item_symbol_aliases);
|
||||
|
||||
self.instances.borrow_mut().insert(instance, lldecl);
|
||||
}
|
||||
}
|
||||
|
||||
impl CodegenCx<'_, '_> {
|
||||
fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(Symbol, Linkage, Visibility)]) {
|
||||
let ty = self.get_type_of_global(aliasee);
|
||||
|
||||
for (alias, linkage, visibility) in aliases {
|
||||
tracing::debug!("ALIAS: {alias:?} {linkage:?} {visibility:?}");
|
||||
let lldecl = llvm::add_alias(
|
||||
self.llmod,
|
||||
ty,
|
||||
AddressSpace::ZERO,
|
||||
aliasee,
|
||||
&CString::new(alias.as_str()).unwrap(),
|
||||
);
|
||||
|
||||
llvm::set_visibility(lldecl, base::visibility_to_llvm(*visibility));
|
||||
llvm::set_linkage(lldecl, base::linkage_to_llvm(*linkage));
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a definition or declaration can be assumed to be local to a group of
|
||||
/// libraries that form a single DSO or executable.
|
||||
/// Marks the local as DSO if so.
|
||||
|
|
|
|||
|
|
@ -127,7 +127,10 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|
|||
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER),
|
||||
rustc_std_internal_symbol: codegen_attrs
|
||||
.flags
|
||||
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL),
|
||||
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|
||||
|| codegen_attrs
|
||||
.flags
|
||||
.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM),
|
||||
};
|
||||
(def_id.to_def_id(), info)
|
||||
})
|
||||
|
|
@ -519,10 +522,12 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel
|
|||
let is_extern = codegen_fn_attrs.contains_extern_indicator();
|
||||
let std_internal =
|
||||
codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
|
||||
let eii = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM);
|
||||
|
||||
if is_extern && !std_internal {
|
||||
if is_extern && !std_internal && !eii {
|
||||
let target = &tcx.sess.target.llvm_target;
|
||||
// WebAssembly cannot export data symbols, so reduce their export level
|
||||
// FIXME(jdonszelmann) don't do a substring match here.
|
||||
if target.contains("emscripten") {
|
||||
if let DefKind::Static { .. } = tcx.def_kind(sym_def_id) {
|
||||
return SymbolExportLevel::Rust;
|
||||
|
|
|
|||
|
|
@ -3,19 +3,22 @@ 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, InstructionSetAttr, RtsanSetting, UsedBy};
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, InlineAttr, InstructionSetAttr, 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};
|
||||
use rustc_middle::middle::codegen_fn_attrs::{
|
||||
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
|
||||
};
|
||||
use rustc_middle::mir::mono::Visibility;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{self as ty, TyCtxt};
|
||||
use rustc_middle::ty::{self as ty, Instance, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Ident, Span, sym};
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
use rustc_target::spec::Os;
|
||||
|
||||
use crate::errors;
|
||||
|
|
@ -310,6 +313,43 @@ fn process_builtin_attrs(
|
|||
AttributeKind::ObjcSelector { methname, .. } => {
|
||||
codegen_fn_attrs.objc_selector = Some(*methname);
|
||||
}
|
||||
AttributeKind::EiiExternItem => {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
|
||||
}
|
||||
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 symbol_name = tcx.symbol_name(Instance::mono(tcx, extern_item));
|
||||
|
||||
// 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
|
||||
// what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent.
|
||||
// However, the fact that the default one of has weak linkage isn't considered and you sometimes get that
|
||||
// the default implementation is used while an explicit implementation is given.
|
||||
if
|
||||
// if this is a default impl
|
||||
i.is_default
|
||||
// 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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
codegen_fn_attrs.foreign_item_symbol_aliases.push((
|
||||
Symbol::intern(symbol_name.name),
|
||||
if i.is_default { Linkage::LinkOnceAny } else { Linkage::External },
|
||||
Visibility::Default,
|
||||
));
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -411,6 +451,12 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
|
|||
// * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way
|
||||
// both for exports and imports through foreign items. This is handled further,
|
||||
// during symbol mangling logic.
|
||||
} else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM)
|
||||
{
|
||||
// * externally implementable items keep their mangled symbol name.
|
||||
// multiple EIIs can have the same name, so not mangling them would be a bug.
|
||||
// Implementing an EII does the appropriate name resolution to make sure the implementations
|
||||
// get the same symbol name as the *mangled* foreign item they refer to so that's all good.
|
||||
} else if codegen_fn_attrs.symbol_name.is_some() {
|
||||
// * This can be overridden with the `#[link_name]` attribute
|
||||
} else {
|
||||
|
|
|
|||
68
compiler/rustc_error_codes/src/error_codes/E0806.md
Normal file
68
compiler/rustc_error_codes/src/error_codes/E0806.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
An externally implementable item is not compatible with its declaration.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```rust,edition2021,compile_fail,E0806
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(foo)]
|
||||
fn x();
|
||||
|
||||
#[foo]
|
||||
fn y(a: u64) -> u64 {
|
||||
//~^ ERROR E0806
|
||||
a
|
||||
}
|
||||
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
The error here is caused by the fact that `y` implements the
|
||||
externally implementable item `foo`.
|
||||
It can only do so if the signature of the implementation `y` matches
|
||||
that of the declaration of `foo`.
|
||||
So, to fix this, `y`'s signature must be changed to match that of `x`:
|
||||
|
||||
```rust,edition2021
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(foo)]
|
||||
fn x();
|
||||
|
||||
#[foo]
|
||||
fn y() {}
|
||||
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
One common way this can be triggered is by using the wrong
|
||||
signature for `#[panic_handler]`.
|
||||
The signature is provided by `core`.
|
||||
|
||||
```rust,edition2021,ignore
|
||||
#![no_std]
|
||||
|
||||
#[panic_handler]
|
||||
fn on_panic() -> ! {
|
||||
//~^ ERROR E0806
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
Should be:
|
||||
|
||||
```rust,edition2021,ignore
|
||||
#![no_std]
|
||||
|
||||
#[panic_handler]
|
||||
fn on_panic(info: &core::panic::PanicInfo<'_>) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
|
@ -543,6 +543,7 @@ macro_rules! error_codes {
|
|||
0803,
|
||||
0804,
|
||||
0805,
|
||||
0806,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -961,6 +961,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
|
||||
),
|
||||
gated!(
|
||||
rustc_eii_extern_item, Normal, template!(Word),
|
||||
ErrorFollowing, EncodeCrossCrate::Yes, eii_internals,
|
||||
"used internally to mark types with a `transparent` representation when it is guaranteed by the documentation",
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_allowed_through_unstable_modules, Normal, template!(NameValueStr: "deprecation message"),
|
||||
WarnFollowing, EncodeCrossCrate::No,
|
||||
|
|
|
|||
|
|
@ -221,6 +221,8 @@ declare_features! (
|
|||
(internal, compiler_builtins, "1.13.0", None),
|
||||
/// Allows writing custom MIR
|
||||
(internal, custom_mir, "1.65.0", None),
|
||||
/// Implementation details of externally implementatble items
|
||||
(internal, eii_internals, "CURRENT_RUSTC_VERSION", None),
|
||||
/// Outputs useful `assert!` messages
|
||||
(unstable, generic_assert, "1.63.0", None),
|
||||
/// Allows using the #[rustc_intrinsic] attribute.
|
||||
|
|
@ -501,6 +503,8 @@ declare_features! (
|
|||
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
|
||||
/// Allows using `#[export_stable]` which indicates that an item is exportable.
|
||||
(incomplete, export_stable, "1.88.0", Some(139939)),
|
||||
/// Externally implementatble items
|
||||
(unstable, extern_item_impls, "CURRENT_RUSTC_VERSION", Some(125418)),
|
||||
/// Allows defining `extern type`s.
|
||||
(unstable, extern_types, "1.23.0", Some(43467)),
|
||||
/// Allow using 128-bit (quad precision) floating point numbers.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,23 @@ 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 struct EiiImpl {
|
||||
pub eii_macro: DefId,
|
||||
pub impl_marked_unsafe: bool,
|
||||
pub span: Span,
|
||||
pub inner_span: Span,
|
||||
pub is_default: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
|
||||
pub enum InlineAttr {
|
||||
None,
|
||||
|
|
@ -692,6 +709,15 @@ pub enum AttributeKind {
|
|||
/// Represents `#[rustc_dummy]`.
|
||||
Dummy,
|
||||
|
||||
/// Implementation detail of `#[eii]`
|
||||
EiiExternItem,
|
||||
|
||||
/// Implementation detail of `#[eii]`
|
||||
EiiExternTarget(EiiDecl),
|
||||
|
||||
/// Implementation detail of `#[eii]`
|
||||
EiiImpls(ThinVec<EiiImpl>),
|
||||
|
||||
/// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute).
|
||||
ExportName {
|
||||
/// The name to export this item with.
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ impl AttributeKind {
|
|||
Doc(_) => Yes,
|
||||
DocComment { .. } => Yes,
|
||||
Dummy => No,
|
||||
EiiExternItem => No,
|
||||
EiiExternTarget(_) => Yes,
|
||||
EiiImpls(..) => No,
|
||||
ExportName { .. } => Yes,
|
||||
ExportStable => No,
|
||||
FfiConst(..) => No,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use rustc_ast::token::{CommentKind, DocFragmentKind};
|
|||
use rustc_ast::{AttrStyle, IntTy, UintTy};
|
||||
use rustc_ast_pretty::pp::Printer;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
|
|
@ -170,4 +171,5 @@ print_debug!(
|
|||
DocFragmentKind,
|
||||
Transparency,
|
||||
SanitizerSet,
|
||||
DefId,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4164,6 +4164,9 @@ pub struct Item<'hir> {
|
|||
pub span: Span,
|
||||
pub vis_span: Span,
|
||||
pub has_delayed_lints: bool,
|
||||
/// hint to speed up collection: true if the item is a static or function and has
|
||||
/// either an `EiiImpls` or `EiiExternTarget` attribute
|
||||
pub eii: bool,
|
||||
}
|
||||
|
||||
impl<'hir> Item<'hir> {
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) ->
|
|||
}
|
||||
|
||||
pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::Result {
|
||||
let Item { owner_id: _, kind, span: _, vis_span: _, has_delayed_lints: _ } = item;
|
||||
let Item { owner_id: _, kind, span: _, vis_span: _, has_delayed_lints: _, eii: _ } = item;
|
||||
try_visit!(visitor.visit_id(item.hir_id()));
|
||||
match *kind {
|
||||
ItemKind::ExternCrate(orig_name, ident) => {
|
||||
|
|
|
|||
|
|
@ -165,6 +165,10 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
|
|||
hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice
|
||||
.label = parameter captured again here
|
||||
|
||||
hir_analysis_eii_with_generics =
|
||||
#[{$eii_name}] cannot have generic parameters other than lifetimes
|
||||
.label = required by this attribute
|
||||
|
||||
hir_analysis_empty_specialization = specialization impl does not specialize any associated items
|
||||
.note = impl is a specialization of this impl
|
||||
|
||||
|
|
@ -300,6 +304,13 @@ hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, b
|
|||
.label = lifetime captured due to being mentioned in the bounds of the `impl Trait`
|
||||
.param_label = this lifetime parameter is captured
|
||||
|
||||
hir_analysis_lifetimes_or_bounds_mismatch_on_eii =
|
||||
lifetime parameters or bounds of `{$ident}` do not match the declaration
|
||||
.label = lifetimes do not match
|
||||
.generics_label = lifetimes in impl do not match this signature
|
||||
.where_label = this `where` clause might not match the one in the declaration
|
||||
.bounds_label = this bound might be missing in the implementation
|
||||
|
||||
hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
|
||||
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
|
||||
.label = lifetimes do not match {$item_kind} in trait
|
||||
|
|
|
|||
433
compiler/rustc_hir_analysis/src/check/compare_eii.rs
Normal file
433
compiler/rustc_hir_analysis/src/check/compare_eii.rs
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
//! This module is very similar to `compare_impl_item`.
|
||||
//! Most logic is taken from there,
|
||||
//! since in a very similar way we're comparing some declaration of a signature to an implementation.
|
||||
//! The major difference is that we don't bother with self types, since for EIIs we're comparing freestanding item.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, E0806, struct_span_code_err};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, FnSig, HirId, ItemKind};
|
||||
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
|
||||
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::regions::InferCtxtRegionExt;
|
||||
use rustc_trait_selection::traits::{self, ObligationCtxt};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::potentially_plural_count;
|
||||
use crate::check::compare_impl_item::{
|
||||
CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions,
|
||||
};
|
||||
use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii};
|
||||
|
||||
/// Checks whether the signature of some `external_impl`, matches
|
||||
/// the signature of `declaration`, which it is supposed to be compatible
|
||||
/// with in order to implement the item.
|
||||
pub(crate) fn compare_eii_function_types<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
external_impl: LocalDefId,
|
||||
declaration: DefId,
|
||||
eii_name: Symbol,
|
||||
eii_attr_span: Span,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
check_is_structurally_compatible(tcx, external_impl, declaration, 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 },
|
||||
);
|
||||
|
||||
// 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 infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
|
||||
|
||||
// We now need to check that the signature of the implementation is
|
||||
// compatible with that of the declaration. We do this by
|
||||
// checking that `impl_fty <: trait_fty`.
|
||||
//
|
||||
// FIXME: We manually instantiate the declaration here as we need
|
||||
// to manually compute its implied bounds. Otherwise this could just
|
||||
// be ocx.sub(impl_sig, trait_sig).
|
||||
|
||||
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.liberate_late_bound_regions(external_impl.into(), declaration_sig);
|
||||
debug!(?declaration_sig);
|
||||
|
||||
let unnormalized_external_impl_sig = infcx.instantiate_binder_with_fresh_vars(
|
||||
external_impl_span,
|
||||
infer::BoundRegionConversionTime::HigherRankedType,
|
||||
tcx.fn_sig(external_impl).instantiate(
|
||||
tcx,
|
||||
infcx.fresh_args_for_item(external_impl_span, external_impl.to_def_id()),
|
||||
),
|
||||
);
|
||||
let external_impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_external_impl_sig);
|
||||
debug!(?external_impl_sig);
|
||||
|
||||
// Next, add all inputs and output as well-formed tys. Importantly,
|
||||
// we have to do this before normalization, since the normalized ty may
|
||||
// not contain the input parameters. See issue #87748.
|
||||
wf_tys.extend(declaration_sig.inputs_and_output.iter());
|
||||
let declaration_sig = ocx.normalize(&norm_cause, param_env, declaration_sig);
|
||||
// We also have to add the normalized declaration
|
||||
// as we don't normalize during implied bounds computation.
|
||||
wf_tys.extend(external_impl_sig.inputs_and_output.iter());
|
||||
|
||||
// FIXME: Copied over from compare impl items, same issue:
|
||||
// We'd want to keep more accurate spans than "the method signature" when
|
||||
// processing the comparison between the trait and impl fn, but we sadly lose them
|
||||
// and point at the whole signature when a trait bound or specific input or output
|
||||
// type would be more appropriate. In other places we have a `Vec<Span>`
|
||||
// corresponding to their `Vec<Predicate>`, but we don't have that here.
|
||||
// Fixing this would improve the output of test `issue-83765.rs`.
|
||||
let result = ocx.sup(&cause, param_env, declaration_sig, external_impl_sig);
|
||||
|
||||
if let Err(terr) = result {
|
||||
debug!(?external_impl_sig, ?declaration_sig, ?terr, "sub_types failed");
|
||||
|
||||
let emitted = report_eii_mismatch(
|
||||
infcx,
|
||||
cause,
|
||||
param_env,
|
||||
terr,
|
||||
(declaration, declaration_sig),
|
||||
(external_impl, external_impl_sig),
|
||||
eii_attr_span,
|
||||
eii_name,
|
||||
);
|
||||
return Err(emitted);
|
||||
}
|
||||
|
||||
if !(declaration_sig, external_impl_sig).references_error() {
|
||||
for ty in unnormalized_external_impl_sig.inputs_and_output {
|
||||
ocx.register_obligation(traits::Obligation::new(
|
||||
infcx.tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
ty::ClauseKind::WellFormed(ty.into()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all obligations are satisfied by the implementation's
|
||||
// version.
|
||||
let errors = ocx.evaluate_obligations_error_on_ambiguity();
|
||||
if !errors.is_empty() {
|
||||
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||
return Err(reported);
|
||||
}
|
||||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
// lifetime parameters.
|
||||
let errors = infcx.resolve_regions(external_impl, param_env, wf_tys);
|
||||
if !errors.is_empty() {
|
||||
return Err(infcx
|
||||
.tainted_by_errors()
|
||||
.unwrap_or_else(|| infcx.err_ctxt().report_region_errors(external_impl, &errors)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks a bunch of different properties of the impl/trait methods for
|
||||
/// compatibility, such as asyncness, number of argument, self receiver kind,
|
||||
/// and number of early- and late-bound generics.
|
||||
///
|
||||
/// Corresponds to `check_method_is_structurally_compatible` for impl method compatibility checks.
|
||||
fn check_is_structurally_compatible<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
external_impl: LocalDefId,
|
||||
declaration: DefId,
|
||||
eii_name: Symbol,
|
||||
eii_attr_span: Span,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
|
||||
check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
|
||||
check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// externally implementable items can't have generics
|
||||
fn check_no_generics<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
external_impl: LocalDefId,
|
||||
_declaration: DefId,
|
||||
eii_name: Symbol,
|
||||
eii_attr_span: Span,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let generics = tcx.generics_of(external_impl);
|
||||
if generics.own_requires_monomorphization() {
|
||||
tcx.dcx().emit_err(EiiWithGenerics {
|
||||
span: tcx.def_span(external_impl),
|
||||
attr: eii_attr_span,
|
||||
eii_name,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_early_region_bounds<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
external_impl: LocalDefId,
|
||||
declaration: DefId,
|
||||
eii_attr_span: Span,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let external_impl_generics = tcx.generics_of(external_impl.to_def_id());
|
||||
let external_impl_params = external_impl_generics.own_counts().lifetimes;
|
||||
|
||||
let declaration_generics = tcx.generics_of(declaration);
|
||||
let declaration_params = declaration_generics.own_counts().lifetimes;
|
||||
|
||||
let Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span }) =
|
||||
check_number_of_early_bound_regions(
|
||||
tcx,
|
||||
external_impl,
|
||||
declaration,
|
||||
external_impl_generics,
|
||||
external_impl_params,
|
||||
declaration_generics,
|
||||
declaration_params,
|
||||
)
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut diag = tcx.dcx().create_err(LifetimesOrBoundsMismatchOnEii {
|
||||
span,
|
||||
ident: tcx.item_name(external_impl.to_def_id()),
|
||||
generics_span,
|
||||
bounds_span,
|
||||
where_span,
|
||||
});
|
||||
|
||||
diag.span_label(eii_attr_span, format!("required because of this attribute"));
|
||||
return Err(diag.emit());
|
||||
}
|
||||
|
||||
fn check_number_of_arguments<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
external_impl: LocalDefId,
|
||||
declaration: DefId,
|
||||
eii_name: Symbol,
|
||||
eii_attr_span: Span,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let external_impl_fty = tcx.fn_sig(external_impl);
|
||||
let declaration_fty = tcx.fn_sig(declaration);
|
||||
let declaration_number_args = declaration_fty.skip_binder().inputs().skip_binder().len();
|
||||
let external_impl_number_args = external_impl_fty.skip_binder().inputs().skip_binder().len();
|
||||
|
||||
// if the number of args are equal, we're trivially done
|
||||
if declaration_number_args == external_impl_number_args {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(report_number_of_arguments_mismatch(
|
||||
tcx,
|
||||
external_impl,
|
||||
declaration,
|
||||
eii_name,
|
||||
eii_attr_span,
|
||||
declaration_number_args,
|
||||
external_impl_number_args,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn report_number_of_arguments_mismatch<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
external_impl: LocalDefId,
|
||||
declaration: DefId,
|
||||
eii_name: Symbol,
|
||||
eii_attr_span: Span,
|
||||
declaration_number_args: usize,
|
||||
external_impl_number_args: usize,
|
||||
) -> ErrorGuaranteed {
|
||||
let external_impl_name = tcx.item_name(external_impl.to_def_id());
|
||||
|
||||
let declaration_span = declaration
|
||||
.as_local()
|
||||
.and_then(|def_id| {
|
||||
let declaration_sig = get_declaration_sig(tcx, def_id).expect("foreign item sig");
|
||||
let pos = declaration_number_args.saturating_sub(1);
|
||||
declaration_sig.decl.inputs.get(pos).map(|arg| {
|
||||
if pos == 0 {
|
||||
arg.span
|
||||
} else {
|
||||
arg.span.with_lo(declaration_sig.decl.inputs[0].span.lo())
|
||||
}
|
||||
})
|
||||
})
|
||||
.or_else(|| tcx.hir_span_if_local(declaration))
|
||||
.unwrap_or_else(|| tcx.def_span(declaration));
|
||||
|
||||
let (_, external_impl_sig, _, _) = &tcx.hir_expect_item(external_impl).expect_fn();
|
||||
let pos = external_impl_number_args.saturating_sub(1);
|
||||
let impl_span = external_impl_sig
|
||||
.decl
|
||||
.inputs
|
||||
.get(pos)
|
||||
.map(|arg| {
|
||||
if pos == 0 {
|
||||
arg.span
|
||||
} else {
|
||||
arg.span.with_lo(external_impl_sig.decl.inputs[0].span.lo())
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| tcx.def_span(external_impl));
|
||||
|
||||
let mut err = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
impl_span,
|
||||
E0806,
|
||||
"`{external_impl_name}` has {} but #[{eii_name}] requires it to have {}",
|
||||
potentially_plural_count(external_impl_number_args, "parameter"),
|
||||
declaration_number_args
|
||||
);
|
||||
|
||||
err.span_label(
|
||||
declaration_span,
|
||||
format!("requires {}", potentially_plural_count(declaration_number_args, "parameter")),
|
||||
);
|
||||
|
||||
err.span_label(
|
||||
impl_span,
|
||||
format!(
|
||||
"expected {}, found {}",
|
||||
potentially_plural_count(declaration_number_args, "parameter"),
|
||||
external_impl_number_args
|
||||
),
|
||||
);
|
||||
|
||||
err.span_label(eii_attr_span, format!("required because of this attribute"));
|
||||
|
||||
err.emit()
|
||||
}
|
||||
|
||||
fn report_eii_mismatch<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
mut cause: ObligationCause<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
terr: TypeError<'tcx>,
|
||||
(declaration_did, declaration_sig): (DefId, ty::FnSig<'tcx>),
|
||||
(external_impl_did, external_impl_sig): (LocalDefId, ty::FnSig<'tcx>),
|
||||
eii_attr_span: Span,
|
||||
eii_name: Symbol,
|
||||
) -> ErrorGuaranteed {
|
||||
let tcx = infcx.tcx;
|
||||
let (impl_err_span, trait_err_span, external_impl_name) =
|
||||
extract_spans_for_error_reporting(infcx, terr, &cause, declaration_did, external_impl_did);
|
||||
|
||||
let mut diag = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
impl_err_span,
|
||||
E0806,
|
||||
"function `{}` has a type that is incompatible with the declaration of `#[{eii_name}]`",
|
||||
external_impl_name
|
||||
);
|
||||
|
||||
diag.span_note(eii_attr_span, "expected this because of this attribute");
|
||||
|
||||
match &terr {
|
||||
TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
|
||||
if declaration_sig.inputs().len() == *i {
|
||||
// Suggestion to change output type. We do not suggest in `async` functions
|
||||
// to avoid complex logic or incorrect output.
|
||||
if let ItemKind::Fn { sig, .. } = &tcx.hir_expect_item(external_impl_did).kind
|
||||
&& !sig.header.asyncness.is_async()
|
||||
{
|
||||
let msg = "change the output type to match the declaration";
|
||||
let ap = Applicability::MachineApplicable;
|
||||
match sig.decl.output {
|
||||
hir::FnRetTy::DefaultReturn(sp) => {
|
||||
let sugg = format!(" -> {}", declaration_sig.output());
|
||||
diag.span_suggestion_verbose(sp, msg, sugg, ap);
|
||||
}
|
||||
hir::FnRetTy::Return(hir_ty) => {
|
||||
let sugg = declaration_sig.output();
|
||||
diag.span_suggestion_verbose(hir_ty.span, msg, sugg, ap);
|
||||
}
|
||||
};
|
||||
};
|
||||
} else if let Some(trait_ty) = declaration_sig.inputs().get(*i) {
|
||||
diag.span_suggestion_verbose(
|
||||
impl_err_span,
|
||||
"change the parameter type to match the declaration",
|
||||
trait_ty,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
cause.span = impl_err_span;
|
||||
infcx.err_ctxt().note_type_err(
|
||||
&mut diag,
|
||||
&cause,
|
||||
trait_err_span.map(|sp| (sp, Cow::from("type in declaration"), false)),
|
||||
Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound {
|
||||
expected: ty::Binder::dummy(declaration_sig),
|
||||
found: ty::Binder::dummy(external_impl_sig),
|
||||
}))),
|
||||
terr,
|
||||
false,
|
||||
None,
|
||||
);
|
||||
|
||||
diag.emit()
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(infcx))]
|
||||
fn extract_spans_for_error_reporting<'tcx>(
|
||||
infcx: &infer::InferCtxt<'tcx>,
|
||||
terr: TypeError<'_>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
declaration: DefId,
|
||||
external_impl: LocalDefId,
|
||||
) -> (Span, Option<Span>, Ident) {
|
||||
let tcx = infcx.tcx;
|
||||
let (mut external_impl_args, external_impl_name) = {
|
||||
let item = tcx.hir_expect_item(external_impl);
|
||||
let (ident, sig, _, _) = item.expect_fn();
|
||||
(sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())), ident)
|
||||
};
|
||||
|
||||
let declaration_args = declaration.as_local().map(|def_id| {
|
||||
if let Some(sig) = get_declaration_sig(tcx, def_id) {
|
||||
sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
|
||||
} else {
|
||||
panic!("expected {def_id:?} to be a foreign function");
|
||||
}
|
||||
});
|
||||
|
||||
match terr {
|
||||
TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => (
|
||||
external_impl_args.nth(i).unwrap(),
|
||||
declaration_args.and_then(|mut args| args.nth(i)),
|
||||
external_impl_name,
|
||||
),
|
||||
_ => (
|
||||
cause.span,
|
||||
tcx.hir_span_if_local(declaration).or_else(|| Some(tcx.def_span(declaration))),
|
||||
external_impl_name,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_declaration_sig<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Option<&'tcx FnSig<'tcx>> {
|
||||
let hir_id: HirId = tcx.local_def_id_to_hir_id(def_id);
|
||||
tcx.hir_fn_sig_by_hir_id(hir_id)
|
||||
}
|
||||
|
|
@ -14,8 +14,9 @@ use rustc_infer::infer::{self, BoundRegionConversionTime, InferCtxt, TyCtxtInfer
|
|||
use rustc_infer::traits::util;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::{
|
||||
self, BottomUpFolder, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder,
|
||||
TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
|
||||
self, BottomUpFolder, GenericArgs, GenericParamDefKind, Generics, Ty, TyCtxt, TypeFoldable,
|
||||
TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
|
||||
Upcast,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
|
|
@ -352,6 +353,7 @@ fn compare_method_predicate_entailment<'tcx>(
|
|||
// type would be more appropriate. In other places we have a `Vec<Span>`
|
||||
// corresponding to their `Vec<Predicate>`, but we don't have that here.
|
||||
// Fixing this would improve the output of test `issue-83765.rs`.
|
||||
// There's the same issue in compare_eii code.
|
||||
let result = ocx.sup(&cause, param_env, trait_sig, impl_sig);
|
||||
|
||||
if let Err(terr) = result {
|
||||
|
|
@ -1093,6 +1095,55 @@ fn check_region_bounds_on_impl_item<'tcx>(
|
|||
let trait_generics = tcx.generics_of(trait_m.def_id);
|
||||
let trait_params = trait_generics.own_counts().lifetimes;
|
||||
|
||||
let Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span }) =
|
||||
check_number_of_early_bound_regions(
|
||||
tcx,
|
||||
impl_m.def_id.expect_local(),
|
||||
trait_m.def_id,
|
||||
impl_generics,
|
||||
impl_params,
|
||||
trait_generics,
|
||||
trait_params,
|
||||
)
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
let reported = tcx
|
||||
.dcx()
|
||||
.create_err(LifetimesOrBoundsMismatchOnTrait {
|
||||
span,
|
||||
item_kind: impl_m.descr(),
|
||||
ident: impl_m.ident(tcx),
|
||||
generics_span,
|
||||
bounds_span,
|
||||
where_span,
|
||||
})
|
||||
.emit_unless_delay(delay);
|
||||
|
||||
Err(reported)
|
||||
}
|
||||
|
||||
pub(super) struct CheckNumberOfEarlyBoundRegionsError {
|
||||
pub(super) span: Span,
|
||||
pub(super) generics_span: Span,
|
||||
pub(super) bounds_span: Vec<Span>,
|
||||
pub(super) where_span: Option<Span>,
|
||||
}
|
||||
|
||||
pub(super) fn check_number_of_early_bound_regions<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_def_id: LocalDefId,
|
||||
trait_def_id: DefId,
|
||||
impl_generics: &Generics,
|
||||
impl_params: usize,
|
||||
trait_generics: &Generics,
|
||||
trait_params: usize,
|
||||
) -> Result<(), CheckNumberOfEarlyBoundRegionsError> {
|
||||
debug!(?trait_generics, ?impl_generics);
|
||||
|
||||
// Must have same number of early-bound lifetime parameters.
|
||||
|
|
@ -1108,20 +1159,16 @@ fn check_region_bounds_on_impl_item<'tcx>(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
let span = tcx
|
||||
.hir_get_generics(impl_m.def_id.expect_local())
|
||||
.hir_get_generics(impl_def_id)
|
||||
.expect("expected impl item to have generics or else we can't compare them")
|
||||
.span;
|
||||
|
||||
let mut generics_span = tcx.def_span(trait_m.def_id);
|
||||
let mut generics_span = tcx.def_span(trait_def_id);
|
||||
let mut bounds_span = vec![];
|
||||
let mut where_span = None;
|
||||
|
||||
if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
|
||||
if let Some(trait_node) = tcx.hir_get_if_local(trait_def_id)
|
||||
&& let Some(trait_generics) = trait_node.generics()
|
||||
{
|
||||
generics_span = trait_generics.span;
|
||||
|
|
@ -1146,7 +1193,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id)
|
||||
if let Some(impl_node) = tcx.hir_get_if_local(impl_def_id.into())
|
||||
&& let Some(impl_generics) = impl_node.generics()
|
||||
{
|
||||
let mut impl_bounds = 0;
|
||||
|
|
@ -1177,19 +1224,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let reported = tcx
|
||||
.dcx()
|
||||
.create_err(LifetimesOrBoundsMismatchOnTrait {
|
||||
span,
|
||||
item_kind: impl_m.descr(),
|
||||
ident: impl_m.ident(tcx),
|
||||
generics_span,
|
||||
bounds_span,
|
||||
where_span,
|
||||
})
|
||||
.emit_unless_delay(delay);
|
||||
|
||||
Err(reported)
|
||||
Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span })
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ a type parameter).
|
|||
|
||||
pub mod always_applicable;
|
||||
mod check;
|
||||
mod compare_eii;
|
||||
mod compare_impl_item;
|
||||
mod entry;
|
||||
pub mod intrinsic;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_abi::ExternAbi;
|
|||
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;
|
||||
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
|
|
@ -39,6 +39,7 @@ use rustc_trait_selection::traits::{
|
|||
use tracing::{debug, instrument};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use super::compare_eii::compare_eii_function_types;
|
||||
use crate::autoderef::Autoderef;
|
||||
use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params};
|
||||
use crate::errors::InvalidReceiverTyHint;
|
||||
|
|
@ -1170,12 +1171,41 @@ fn check_item_fn(
|
|||
decl: &hir::FnDecl<'_>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
enter_wf_checking_ctxt(tcx, def_id, |wfcx| {
|
||||
check_eiis(tcx, def_id);
|
||||
|
||||
let sig = tcx.fn_sig(def_id).instantiate_identity();
|
||||
check_fn_or_method(wfcx, sig, decl, def_id);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
pub(crate) fn check_static_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
|
|||
|
|
@ -1697,3 +1697,28 @@ pub(crate) struct AsyncDropWithoutSyncDrop {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_lifetimes_or_bounds_mismatch_on_eii)]
|
||||
pub(crate) struct LifetimesOrBoundsMismatchOnEii {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[label(hir_analysis_generics_label)]
|
||||
pub generics_span: Span,
|
||||
#[label(hir_analysis_where_label)]
|
||||
pub where_span: Option<Span>,
|
||||
#[label(hir_analysis_bounds_label)]
|
||||
pub bounds_span: Vec<Span>,
|
||||
pub ident: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_eii_with_generics)]
|
||||
pub(crate) struct EiiWithGenerics {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub attr: Span,
|
||||
pub eii_name: Symbol,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1057,6 +1057,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
|
|||
parallel!(
|
||||
{
|
||||
sess.time("looking_for_entry_point", || tcx.ensure_ok().entry_fn(()));
|
||||
sess.time("check_externally_implementable_items", || {
|
||||
tcx.ensure_ok().check_externally_implementable_items(())
|
||||
});
|
||||
|
||||
sess.time("looking_for_derive_registrar", || {
|
||||
tcx.ensure_ok().proc_macro_decls_static(())
|
||||
|
|
|
|||
47
compiler/rustc_metadata/src/eii.rs
Normal file
47
compiler/rustc_metadata/src/eii.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::find_attr;
|
||||
use rustc_middle::query::LocalCrate;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
// basically the map below but flattened out
|
||||
pub(crate) type EiiMapEncodedKeyValue = (DefId, (EiiDecl, Vec<(DefId, EiiImpl)>));
|
||||
|
||||
pub(crate) type EiiMap = FxIndexMap<
|
||||
DefId, // the defid of the macro that declared the eii
|
||||
(
|
||||
// the corresponding declaration
|
||||
EiiDecl,
|
||||
// all the given implementations, indexed by defid.
|
||||
// We expect there to be only one, but collect them all to give errors if there are more
|
||||
// (or if there are none) in the final crate we build.
|
||||
FxIndexMap<DefId, EiiImpl>,
|
||||
),
|
||||
>;
|
||||
|
||||
pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap {
|
||||
let mut eiis = EiiMap::default();
|
||||
|
||||
// iterate over all items in the current crate
|
||||
for id in tcx.hir_crate_items(()).eiis() {
|
||||
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(|| {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
pub use rmeta::provide;
|
||||
|
||||
mod dependency_format;
|
||||
mod eii;
|
||||
mod foreign_modules;
|
||||
mod native_libs;
|
||||
mod rmeta;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ use rustc_span::{
|
|||
use tracing::debug;
|
||||
|
||||
use crate::creader::CStore;
|
||||
use crate::eii::EiiMapEncodedKeyValue;
|
||||
use crate::rmeta::table::IsDefault;
|
||||
use crate::rmeta::*;
|
||||
|
||||
|
|
@ -1487,6 +1488,13 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
fn get_externally_implementable_items(
|
||||
self,
|
||||
tcx: TyCtxt<'_>,
|
||||
) -> impl Iterator<Item = EiiMapEncodedKeyValue> {
|
||||
self.root.externally_implementable_items.decode((self, tcx))
|
||||
}
|
||||
|
||||
fn get_missing_lang_items<'tcx>(self, tcx: TyCtxt<'tcx>) -> &'tcx [LangItem] {
|
||||
tcx.arena.alloc_from_iter(self.root.lang_items_missing.decode((self, tcx)))
|
||||
}
|
||||
|
|
@ -1530,7 +1538,7 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
.get((self, tcx), id)
|
||||
.unwrap()
|
||||
.decode((self, tcx));
|
||||
ast::MacroDef { macro_rules, body: Box::new(body) }
|
||||
ast::MacroDef { macro_rules, body: Box::new(body), eii_extern_target: None }
|
||||
}
|
||||
_ => bug!(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use super::{Decodable, DecodeIterator};
|
|||
use crate::creader::{CStore, LoadedMacro};
|
||||
use crate::rmeta::AttrFlags;
|
||||
use crate::rmeta::table::IsDefault;
|
||||
use crate::{foreign_modules, native_libs};
|
||||
use crate::{eii, foreign_modules, native_libs};
|
||||
|
||||
trait ProcessQueryValue<'tcx, T> {
|
||||
fn process_decoded(self, _tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> T;
|
||||
|
|
@ -330,9 +330,22 @@ provide! { tcx, def_id, other, cdata,
|
|||
is_private_dep => { cdata.private_dep }
|
||||
is_panic_runtime => { cdata.root.panic_runtime }
|
||||
is_compiler_builtins => { cdata.root.compiler_builtins }
|
||||
|
||||
// FIXME: to be replaced with externally_implementable_items below
|
||||
has_global_allocator => { cdata.root.has_global_allocator }
|
||||
// FIXME: to be replaced with externally_implementable_items below
|
||||
has_alloc_error_handler => { cdata.root.has_alloc_error_handler }
|
||||
// FIXME: to be replaced with externally_implementable_items below
|
||||
has_panic_handler => { cdata.root.has_panic_handler }
|
||||
|
||||
externally_implementable_items => {
|
||||
cdata.get_externally_implementable_items(tcx)
|
||||
.map(|(decl_did, (decl, impls))| (
|
||||
decl_did,
|
||||
(decl, impls.into_iter().collect())
|
||||
)).collect()
|
||||
}
|
||||
|
||||
is_profiler_runtime => { cdata.root.profiler_runtime }
|
||||
required_panic_strategy => { cdata.root.required_panic_strategy }
|
||||
panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy }
|
||||
|
|
@ -430,6 +443,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
|
|||
},
|
||||
native_libraries: native_libs::collect,
|
||||
foreign_modules: foreign_modules::collect,
|
||||
externally_implementable_items: eii::collect,
|
||||
|
||||
// Returns a map from a sufficiently visible external item (i.e., an
|
||||
// external item that is visible from at least one local module) to a
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ use rustc_span::{
|
|||
};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use crate::eii::EiiMapEncodedKeyValue;
|
||||
use crate::errors::{FailCreateFileEncoder, FailWriteFile};
|
||||
use crate::rmeta::*;
|
||||
|
||||
|
|
@ -616,6 +617,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
// We have already encoded some things. Get their combined size from the current position.
|
||||
stats.push(("preamble", self.position()));
|
||||
|
||||
let externally_implementable_items = stat!("externally-implementable-items", || self
|
||||
.encode_externally_implementable_items());
|
||||
|
||||
let (crate_deps, dylib_dependency_formats) =
|
||||
stat!("dep", || (self.encode_crate_deps(), self.encode_dylib_dependency_formats()));
|
||||
|
||||
|
|
@ -734,6 +738,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
attrs,
|
||||
sym::default_lib_allocator,
|
||||
),
|
||||
externally_implementable_items,
|
||||
proc_macro_data,
|
||||
debugger_visualizers,
|
||||
compiler_builtins: ast::attr::contains_name(attrs, sym::compiler_builtins),
|
||||
|
|
@ -1645,6 +1650,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn encode_externally_implementable_items(&mut self) -> LazyArray<EiiMapEncodedKeyValue> {
|
||||
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()))
|
||||
}))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn encode_info_for_adt(&mut self, local_def_id: LocalDefId) {
|
||||
let def_id = local_def_id.to_def_id();
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ use table::TableBuilder;
|
|||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use crate::creader::CrateMetadataRef;
|
||||
use crate::eii::EiiMapEncodedKeyValue;
|
||||
|
||||
mod decoder;
|
||||
mod def_path_hash_map;
|
||||
|
|
@ -250,6 +251,7 @@ pub(crate) struct CrateRoot {
|
|||
has_alloc_error_handler: bool,
|
||||
has_panic_handler: bool,
|
||||
has_default_lib_allocator: bool,
|
||||
externally_implementable_items: LazyArray<EiiMapEncodedKeyValue>,
|
||||
|
||||
crate_deps: LazyArray<CrateDep>,
|
||||
dylib_dependency_formats: LazyArray<Option<LinkagePreference>>,
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ trivially_parameterized_over_tcx! {
|
|||
rustc_hir::Safety,
|
||||
rustc_hir::Stability,
|
||||
rustc_hir::attrs::Deprecation,
|
||||
rustc_hir::attrs::EiiDecl,
|
||||
rustc_hir::attrs::EiiImpl,
|
||||
rustc_hir::attrs::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
|
||||
rustc_hir::def::DefKind,
|
||||
rustc_hir::def::DocLinkResMap,
|
||||
|
|
|
|||
|
|
@ -1224,6 +1224,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
|
|||
body_owners,
|
||||
opaques,
|
||||
nested_bodies,
|
||||
eiis,
|
||||
..
|
||||
} = collector;
|
||||
ModuleItems {
|
||||
|
|
@ -1237,6 +1238,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
|
|||
opaques: opaques.into_boxed_slice(),
|
||||
nested_bodies: nested_bodies.into_boxed_slice(),
|
||||
delayed_lint_items: Box::new([]),
|
||||
eiis: eiis.into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1259,6 +1261,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
|
|||
opaques,
|
||||
nested_bodies,
|
||||
mut delayed_lint_items,
|
||||
eiis,
|
||||
..
|
||||
} = collector;
|
||||
|
||||
|
|
@ -1281,6 +1284,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
|
|||
opaques: opaques.into_boxed_slice(),
|
||||
nested_bodies: nested_bodies.into_boxed_slice(),
|
||||
delayed_lint_items: delayed_lint_items.into_boxed_slice(),
|
||||
eiis: eiis.into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1298,6 +1302,7 @@ struct ItemCollector<'tcx> {
|
|||
opaques: Vec<LocalDefId>,
|
||||
nested_bodies: Vec<LocalDefId>,
|
||||
delayed_lint_items: Vec<OwnerId>,
|
||||
eiis: Vec<LocalDefId>,
|
||||
}
|
||||
|
||||
impl<'tcx> ItemCollector<'tcx> {
|
||||
|
|
@ -1314,6 +1319,7 @@ impl<'tcx> ItemCollector<'tcx> {
|
|||
opaques: Vec::default(),
|
||||
nested_bodies: Vec::default(),
|
||||
delayed_lint_items: Vec::default(),
|
||||
eiis: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1335,6 +1341,12 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
|
|||
self.delayed_lint_items.push(item.item_id().owner_id);
|
||||
}
|
||||
|
||||
if let ItemKind::Static(..) | ItemKind::Fn { .. } | ItemKind::Macro(..) = &item.kind
|
||||
&& item.eii
|
||||
{
|
||||
self.eiis.push(item.owner_id.def_id)
|
||||
}
|
||||
|
||||
// Items that are modules are handled here instead of in visit_mod.
|
||||
if let ItemKind::Mod(_, module) = &item.kind {
|
||||
self.submodules.push(item.owner_id);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ pub struct ModuleItems {
|
|||
nested_bodies: Box<[LocalDefId]>,
|
||||
// only filled with hir_crate_items, not with hir_module_items
|
||||
delayed_lint_items: Box<[OwnerId]>,
|
||||
|
||||
/// Statics and functions with an `EiiImpls` or `EiiExternTarget` attribute
|
||||
eiis: Box<[LocalDefId]>,
|
||||
}
|
||||
|
||||
impl ModuleItems {
|
||||
|
|
@ -58,6 +61,10 @@ impl ModuleItems {
|
|||
self.delayed_lint_items.iter().copied()
|
||||
}
|
||||
|
||||
pub fn eiis(&self) -> impl Iterator<Item = LocalDefId> {
|
||||
self.eiis.iter().copied()
|
||||
}
|
||||
|
||||
/// Returns all items that are associated with some `impl` block (both inherent and trait impl
|
||||
/// blocks).
|
||||
pub fn impl_items(&self) -> impl Iterator<Item = ImplItemId> {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
|
|||
use rustc_span::Symbol;
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
|
||||
use crate::mir::mono::Visibility;
|
||||
use crate::ty::{InstanceKind, TyCtxt};
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
|
|
@ -42,6 +43,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
|
||||
}
|
||||
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM) {
|
||||
attrs.to_mut().flags.remove(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM);
|
||||
}
|
||||
|
||||
if attrs.symbol_name.is_some() {
|
||||
attrs.to_mut().symbol_name = None;
|
||||
}
|
||||
|
|
@ -62,6 +67,12 @@ pub struct CodegenFnAttrs {
|
|||
/// using the `#[export_name = "..."]` or `#[link_name = "..."]` attribute
|
||||
/// depending on if this is a function definition or foreign function.
|
||||
pub symbol_name: Option<Symbol>,
|
||||
/// Defids of foreign items somewhere that this function should "satisfy".
|
||||
/// i.e., if a foreign function has some symbol foo,
|
||||
/// generate this function under its real name,
|
||||
/// but *also* under the same name as this foreign function so that the foreign function has an implementation.
|
||||
// FIXME: make "SymbolName<'tcx>"
|
||||
pub foreign_item_symbol_aliases: Vec<(Symbol, Linkage, Visibility)>,
|
||||
/// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an
|
||||
/// imported function has in the dynamic library. Note that this must not
|
||||
/// be set when `link_name` is set. This is for foreign items with the
|
||||
|
|
@ -192,6 +203,12 @@ bitflags::bitflags! {
|
|||
const FOREIGN_ITEM = 1 << 16;
|
||||
/// `#[rustc_offload_kernel]`: indicates that this is an offload kernel, an extra ptr arg will be added.
|
||||
const OFFLOAD_KERNEL = 1 << 17;
|
||||
/// Externally implementable item symbols act a little like `RUSTC_STD_INTERNAL_SYMBOL`.
|
||||
/// When a crate declares an EII and dependencies expect the symbol to exist,
|
||||
/// they will refer to this symbol name before a definition is given.
|
||||
/// As such, we must make sure these symbols really do exist in the final binary/library.
|
||||
/// This flag is put on both the implementations of EIIs and the foreign item they implement.
|
||||
const EXTERNALLY_IMPLEMENTABLE_ITEM = 1 << 18;
|
||||
}
|
||||
}
|
||||
rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags }
|
||||
|
|
@ -207,6 +224,7 @@ impl CodegenFnAttrs {
|
|||
symbol_name: None,
|
||||
link_ordinal: None,
|
||||
target_features: vec![],
|
||||
foreign_item_symbol_aliases: vec![],
|
||||
safe_target_features: false,
|
||||
linkage: None,
|
||||
import_linkage: None,
|
||||
|
|
@ -234,6 +252,9 @@ impl CodegenFnAttrs {
|
|||
|
||||
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|
||||
|| self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|
||||
// note: for these we do also set a symbol name so technically also handled by the
|
||||
// condition below. However, I think that regardless these should be treated as extern.
|
||||
|| self.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM)
|
||||
|| self.symbol_name.is_some()
|
||||
|| match self.linkage {
|
||||
// These are private, so make sure we don't try to consider
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ pub struct SymbolExportInfo {
|
|||
/// Was the symbol marked as `#[used(compiler)]` or `#[used(linker)]`?
|
||||
pub used: bool,
|
||||
/// Was the symbol marked as `#[rustc_std_internal_symbol]`?
|
||||
/// We also use this for externally implementable items.
|
||||
pub rustc_std_internal_symbol: bool,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ pub struct MonoItemData {
|
|||
/// Visibility doesn't have any effect when linkage is internal.
|
||||
///
|
||||
/// DSO means dynamic shared object, that is a dynamically linked executable or dylib.
|
||||
#[derive(Copy, Clone, PartialEq, Debug, HashStable)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum Visibility {
|
||||
/// Export the symbol from the DSO and apply overrides of the symbol by outside DSOs to within
|
||||
/// the DSO if the object file format supports this.
|
||||
|
|
|
|||
|
|
@ -296,6 +296,8 @@ trivial! {
|
|||
rustc_ast::expand::allocator::AllocatorKind,
|
||||
rustc_hir::DefaultBodyStability,
|
||||
rustc_hir::attrs::Deprecation,
|
||||
rustc_hir::attrs::EiiDecl,
|
||||
rustc_hir::attrs::EiiImpl,
|
||||
rustc_data_structures::svh::Svh,
|
||||
rustc_errors::ErrorGuaranteed,
|
||||
rustc_hir::Constness,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ use rustc_data_structures::steal::Steal;
|
|||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::attrs::StrippedCfgItem;
|
||||
use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem};
|
||||
use rustc_hir::def::{DefKind, DocLinkResMap};
|
||||
use rustc_hir::def_id::{
|
||||
CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId,
|
||||
|
|
@ -2753,6 +2753,18 @@ rustc_queries! {
|
|||
desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) }
|
||||
feedable
|
||||
}
|
||||
|
||||
query check_externally_implementable_items(_: ()) {
|
||||
desc { "check externally implementable items" }
|
||||
}
|
||||
|
||||
/// Returns a list of all `externally implementable items` crate.
|
||||
query externally_implementable_items(cnum: CrateNum) -> &'tcx FxIndexMap<DefId, (EiiDecl, FxIndexMap<DefId, EiiImpl>)> {
|
||||
arena_cache
|
||||
desc { "looking up the externally implementable items of a crate" }
|
||||
cache_on_disk_if { *cnum == LOCAL_CRATE }
|
||||
separate_provide_extern
|
||||
}
|
||||
}
|
||||
|
||||
rustc_with_all_queries! { define_callbacks! }
|
||||
|
|
|
|||
|
|
@ -420,6 +420,13 @@ pub enum ObligationCauseCode<'tcx> {
|
|||
/// Only reachable if the `unsized_fn_params` feature is used. Unsized function arguments must
|
||||
/// be place expressions because we can't store them in MIR locals as temporaries.
|
||||
UnsizedNonPlaceExpr(Span),
|
||||
|
||||
/// Error derived when checking an impl item is compatible with
|
||||
/// its corresponding trait item's definition
|
||||
CompareEii {
|
||||
external_impl: LocalDefId,
|
||||
declaration: DefId,
|
||||
},
|
||||
}
|
||||
|
||||
/// Whether a value can be extracted into a const.
|
||||
|
|
|
|||
|
|
@ -1643,11 +1643,13 @@ impl<'v> RootCollector<'_, 'v> {
|
|||
MonoItemCollectionStrategy::Lazy => {
|
||||
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
|
||||
|| self.tcx.is_reachable_non_generic(def_id)
|
||||
|| self
|
||||
.tcx
|
||||
.codegen_fn_attrs(def_id)
|
||||
.flags
|
||||
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|
||||
|| {
|
||||
let flags = self.tcx.codegen_fn_attrs(def_id).flags;
|
||||
flags.intersects(
|
||||
CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
|
||||
| CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -872,7 +872,7 @@ fn mono_item_visibility<'tcx>(
|
|||
// visibility. In some situations though we'll want to prevent this
|
||||
// symbol from being internalized.
|
||||
//
|
||||
// There's two categories of items here:
|
||||
// There's three categories of items here:
|
||||
//
|
||||
// * First is weak lang items. These are basically mechanisms for
|
||||
// libcore to forward-reference symbols defined later in crates like
|
||||
|
|
@ -902,8 +902,16 @@ fn mono_item_visibility<'tcx>(
|
|||
// visibility below. Like the weak lang items, though, we can't let
|
||||
// LLVM internalize them as this decision is left up to the linker to
|
||||
// omit them, so prevent them from being internalized.
|
||||
//
|
||||
// * Externally implementable items. They work (in this case) pretty much the same as
|
||||
// RUSTC_STD_INTERNAL_SYMBOL in that their implementation is also chosen later in
|
||||
// the compilation process and we can't let them be internalized and they can't
|
||||
// show up as an external interface.
|
||||
let attrs = tcx.codegen_fn_attrs(def_id);
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
|
||||
if attrs.flags.intersects(
|
||||
CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
|
||||
| CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM,
|
||||
) {
|
||||
*can_be_internalized = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ impl<'a> Parser<'a> {
|
|||
contract,
|
||||
body,
|
||||
define_opaque: None,
|
||||
eii_impls: ThinVec::new(),
|
||||
}))
|
||||
} else if self.eat_keyword_case(exp!(Extern), case) {
|
||||
if self.eat_keyword_case(exp!(Crate), case) {
|
||||
|
|
@ -2203,7 +2204,10 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
|
||||
self.psess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_token.span));
|
||||
Ok(ItemKind::MacroDef(ident, ast::MacroDef { body, macro_rules: false }))
|
||||
Ok(ItemKind::MacroDef(
|
||||
ident,
|
||||
ast::MacroDef { body, macro_rules: false, eii_extern_target: None },
|
||||
))
|
||||
}
|
||||
|
||||
/// Is this a possibly malformed start of a `macro_rules! foo` item definition?
|
||||
|
|
@ -2250,7 +2254,10 @@ impl<'a> Parser<'a> {
|
|||
self.eat_semi_for_macro_if_needed(&body);
|
||||
self.complain_if_pub_macro(vis, true);
|
||||
|
||||
Ok(ItemKind::MacroDef(ident, ast::MacroDef { body, macro_rules: true }))
|
||||
Ok(ItemKind::MacroDef(
|
||||
ident,
|
||||
ast::MacroDef { body, macro_rules: true, eii_extern_target: None },
|
||||
))
|
||||
}
|
||||
|
||||
/// Item macro invocations or `macro_rules!` definitions need inherited visibility.
|
||||
|
|
|
|||
|
|
@ -164,6 +164,17 @@ passes_duplicate_diagnostic_item_in_crate =
|
|||
duplicate diagnostic item in crate `{$crate_name}`: `{$name}`
|
||||
.note = the diagnostic item is first defined in crate `{$orig_crate_name}`
|
||||
|
||||
passes_duplicate_eii_impls =
|
||||
multiple implementations of `#[{$name}]`
|
||||
.first = first implemented here in crate `{$first_crate}`
|
||||
.second = also implemented here in crate `{$second_crate}`
|
||||
.note = in addition to these two, { $num_additional_crates ->
|
||||
[one] another implementation was found in crate {$additional_crate_names}
|
||||
*[other] more implementations were also found in the following crates: {$additional_crate_names}
|
||||
}
|
||||
|
||||
.help = an "externally implementable item" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict
|
||||
|
||||
passes_duplicate_feature_err =
|
||||
the feature `{$feature}` has already been enabled
|
||||
|
||||
|
|
@ -197,6 +208,22 @@ passes_duplicate_lang_item_crate_depends =
|
|||
.first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
|
||||
.second_definition_path = second definition in `{$crate_name}` loaded from {$path}
|
||||
|
||||
passes_eii_fn_with_track_caller =
|
||||
`#[{$name}]` is not allowed to have `#[track_caller]`
|
||||
.label = `#[{$name}]` is not allowed to have `#[track_caller]`
|
||||
|
||||
passes_eii_impl_not_function =
|
||||
`eii_macro_for` is only valid on functions
|
||||
|
||||
passes_eii_impl_requires_unsafe =
|
||||
`#[{$name}]` is unsafe to implement
|
||||
passes_eii_impl_requires_unsafe_suggestion = wrap the attribute in `unsafe(...)`
|
||||
|
||||
passes_eii_without_impl =
|
||||
`#[{$name}]` required, but not found
|
||||
.label = expected because `#[{$name}]` was declared here in crate `{$decl_crate_name}`
|
||||
.help = expected at least one implementation in crate `{$current_crate_name}` or any of its dependencies
|
||||
|
||||
passes_enum_variant_same_name =
|
||||
it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ use rustc_feature::{
|
|||
BuiltinAttribute,
|
||||
};
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, DocAttribute, DocInline, InlineAttr, MirDialect, MirPhase, ReprAttr,
|
||||
SanitizerSet,
|
||||
AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, InlineAttr, MirDialect, MirPhase,
|
||||
ReprAttr, SanitizerSet,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalModDefId;
|
||||
|
|
@ -212,8 +212,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
self.check_macro_export(hir_id, *span, target)
|
||||
},
|
||||
Attribute::Parsed(AttributeKind::Doc(attr)) => self.check_doc_attrs(attr, hir_id, target),
|
||||
Attribute::Parsed(AttributeKind::EiiImpls(impls)) => {
|
||||
self.check_eii_impl(impls, target)
|
||||
},
|
||||
Attribute::Parsed(
|
||||
AttributeKind::BodyStability { .. }
|
||||
AttributeKind::EiiExternTarget { .. }
|
||||
| AttributeKind::EiiExternItem
|
||||
| AttributeKind::BodyStability { .. }
|
||||
| AttributeKind::ConstStabilityIndirect
|
||||
| AttributeKind::MacroTransparency(_)
|
||||
| AttributeKind::Pointee(..)
|
||||
|
|
@ -459,6 +464,30 @@ 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 {
|
||||
match target {
|
||||
Target::Fn => {}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::EiiImplNotFunction { span: *span });
|
||||
}
|
||||
}
|
||||
|
||||
if 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 {
|
||||
span: *span,
|
||||
name: self.tcx.item_name(*eii_macro),
|
||||
suggestion: errors::EiiImplRequiresUnsafeSuggestion {
|
||||
left: inner_span.shrink_to_lo(),
|
||||
right: inner_span.shrink_to_hi(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl and that it has no
|
||||
/// arguments.
|
||||
fn check_do_not_recommend(
|
||||
|
|
@ -684,6 +713,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
sig_span: sig.span,
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
self.dcx().emit_err(errors::EiiWithTrackCaller {
|
||||
attr_span,
|
||||
name: self.tcx.item_name(i.eii_macro),
|
||||
sig_span: sig.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
158
compiler/rustc_passes/src/eii.rs
Normal file
158
compiler/rustc_passes/src/eii.rs
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
//! Checks necessary for externally implementable items:
|
||||
//! Are all items implemented etc.?
|
||||
|
||||
use std::iter;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::attrs::{EiiDecl, EiiImpl};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::CrateType;
|
||||
|
||||
use crate::errors::{DuplicateEiiImpls, EiiWithoutImpl};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum CheckingMode {
|
||||
CheckDuplicates,
|
||||
CheckExistence,
|
||||
}
|
||||
|
||||
fn get_checking_mode(tcx: TyCtxt<'_>) -> CheckingMode {
|
||||
// if any of the crate types is not rlib or dylib, we must check for existence.
|
||||
if tcx.crate_types().iter().any(|i| !matches!(i, CrateType::Rlib | CrateType::Dylib)) {
|
||||
CheckingMode::CheckExistence
|
||||
} else {
|
||||
CheckingMode::CheckDuplicates
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for a given crate, what EIIs need to be generated in it.
|
||||
/// This is usually a small subset of all EIIs.
|
||||
///
|
||||
/// EII implementations come in two varieties: explicit and default.
|
||||
/// This query is called once for every crate, to check whether there aren't any duplicate explicit implementations.
|
||||
/// A duplicate may be caused by an implementation in the current crate,
|
||||
/// though it's also entirely possible that the source is two dependencies with an explicit implementation.
|
||||
/// Those work fine on their own but the combination of the two is a conflict.
|
||||
///
|
||||
/// However, if the current crate is a "root" crate, one that generates a final artifact like a binary,
|
||||
/// then we check one more thing, namely that every EII actually has an implementation, either default or not.
|
||||
/// If one EII has no implementation, that's an error at that point.
|
||||
///
|
||||
/// These two behaviors are implemented using `CheckingMode`.
|
||||
pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) {
|
||||
let checking_mode = get_checking_mode(tcx);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FoundImpl {
|
||||
imp: EiiImpl,
|
||||
impl_crate: CrateNum,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FoundEii {
|
||||
decl: EiiDecl,
|
||||
decl_crate: CrateNum,
|
||||
impls: FxIndexMap<DefId, FoundImpl>,
|
||||
}
|
||||
|
||||
let mut eiis = FxIndexMap::<DefId, FoundEii>::default();
|
||||
|
||||
// collect all the EII declarations, and possibly implementations from all descendent crates
|
||||
for &cnum in tcx.crates(()).iter().chain(iter::once(&LOCAL_CRATE)) {
|
||||
// get the eiis for the crate we're currently looking at
|
||||
let crate_eiis = tcx.externally_implementable_items(cnum);
|
||||
|
||||
// update or insert the corresponding entries
|
||||
for (did, (decl, impls)) in crate_eiis {
|
||||
eiis.entry(*did)
|
||||
.or_insert_with(|| FoundEii {
|
||||
decl: *decl,
|
||||
decl_crate: cnum,
|
||||
impls: Default::default(),
|
||||
})
|
||||
.impls
|
||||
.extend(
|
||||
impls
|
||||
.into_iter()
|
||||
.map(|(did, i)| (*did, FoundImpl { imp: *i, impl_crate: cnum })),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
let mut default_impls = Vec::new();
|
||||
let mut explicit_impls = Vec::new();
|
||||
|
||||
for (impl_did, FoundImpl { imp, impl_crate }) in impls {
|
||||
if imp.is_default {
|
||||
default_impls.push((impl_did, impl_crate));
|
||||
} else {
|
||||
explicit_impls.push((impl_did, impl_crate));
|
||||
}
|
||||
}
|
||||
|
||||
// more than one explicit implementation (across all crates)
|
||||
// is instantly an error.
|
||||
if explicit_impls.len() > 1 {
|
||||
tcx.dcx().emit_err(DuplicateEiiImpls {
|
||||
name: tcx.item_name(decl_did),
|
||||
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),
|
||||
second_crate: tcx.crate_name(explicit_impls[1].1),
|
||||
|
||||
help: (),
|
||||
|
||||
additional_crates: (explicit_impls.len() > 2).then_some(()),
|
||||
num_additional_crates: explicit_impls.len() - 2,
|
||||
additional_crate_names: explicit_impls[2..]
|
||||
.iter()
|
||||
.map(|i| format!("`{}`", tcx.crate_name(i.1)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
});
|
||||
}
|
||||
|
||||
if default_impls.len() > 1 {
|
||||
panic!("multiple not supported right now");
|
||||
}
|
||||
|
||||
let (local_impl, is_default) =
|
||||
// note, for a single crate we never need to generate both a default and an explicit implementation.
|
||||
// In that case, generating the explicit implementation is enough!
|
||||
match (checking_mode, explicit_impls.first(), default_impls.first()) {
|
||||
// If we find an explicit implementation, it's instantly the chosen implementation.
|
||||
(_, Some((explicit, _)), _) => (explicit, false),
|
||||
// if we find a default implementation, we can emit it but the alias should be weak
|
||||
(_, _, Some((deflt, _))) => (deflt, true),
|
||||
|
||||
// if we find no explicit implementation,
|
||||
// that's fine if we're only checking for duplicates.
|
||||
// The existence will be checked somewhere else in a crate downstream.
|
||||
(CheckingMode::CheckDuplicates, None, _) => continue,
|
||||
|
||||
// We have a target to generate, but no impl to put in it. error!
|
||||
(CheckingMode::CheckExistence, None, None) => {
|
||||
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,
|
||||
help: (),
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// if it's not local, who cares about generating it.
|
||||
// That's the local crates' responsibility
|
||||
let Some(chosen_impl) = local_impl.as_local() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
tracing::debug!("generating EII {chosen_impl:?} (default={is_default})");
|
||||
}
|
||||
}
|
||||
|
|
@ -1305,3 +1305,80 @@ pub(crate) struct CustomMirIncompatibleDialectAndPhase {
|
|||
#[label]
|
||||
pub phase_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_eii_impl_not_function)]
|
||||
pub(crate) struct EiiImplNotFunction {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_eii_impl_requires_unsafe)]
|
||||
pub(crate) struct EiiImplRequiresUnsafe {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: EiiImplRequiresUnsafeSuggestion,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
passes_eii_impl_requires_unsafe_suggestion,
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
pub(crate) struct EiiImplRequiresUnsafeSuggestion {
|
||||
#[suggestion_part(code = "unsafe(")]
|
||||
pub left: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
pub right: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_eii_fn_with_track_caller)]
|
||||
pub(crate) struct EiiWithTrackCaller {
|
||||
#[primary_span]
|
||||
pub attr_span: Span,
|
||||
pub name: Symbol,
|
||||
#[label]
|
||||
pub sig_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_eii_without_impl)]
|
||||
pub(crate) struct EiiWithoutImpl {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
|
||||
pub current_crate_name: Symbol,
|
||||
pub decl_crate_name: Symbol,
|
||||
#[help]
|
||||
pub help: (),
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_duplicate_eii_impls)]
|
||||
pub(crate) struct DuplicateEiiImpls {
|
||||
pub name: Symbol,
|
||||
|
||||
#[primary_span]
|
||||
#[label(passes_first)]
|
||||
pub first_span: Span,
|
||||
pub first_crate: Symbol,
|
||||
|
||||
#[label(passes_second)]
|
||||
pub second_span: Span,
|
||||
pub second_crate: Symbol,
|
||||
|
||||
#[note]
|
||||
pub additional_crates: Option<()>,
|
||||
|
||||
pub num_additional_crates: usize,
|
||||
pub additional_crate_names: String,
|
||||
|
||||
#[help]
|
||||
pub help: (),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ mod check_export;
|
|||
pub mod dead;
|
||||
mod debugger_visualizer;
|
||||
mod diagnostic_items;
|
||||
mod eii;
|
||||
pub mod entry;
|
||||
mod errors;
|
||||
pub mod hir_id_validator;
|
||||
|
|
@ -43,4 +44,5 @@ pub fn provide(providers: &mut Providers) {
|
|||
stability::provide(providers);
|
||||
upvars::provide(providers);
|
||||
check_export::provide(providers);
|
||||
providers.check_externally_implementable_items = eii::check_externally_implementable_items;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,13 @@ impl<'tcx> ReachableContext<'tcx> {
|
|||
CodegenFnAttrs::EMPTY
|
||||
};
|
||||
let is_extern = codegen_attrs.contains_extern_indicator();
|
||||
if is_extern {
|
||||
// Right now, the only way to get "foreign item symbol aliases" is by being an EII-implementation.
|
||||
// EII implementations will generate under their own name but also under the name of some foreign item
|
||||
// (hence alias) that may be in another crate. These functions are marked as always-reachable since
|
||||
// it's very hard to track whether the original foreign item was reachable. It may live in another crate
|
||||
// and may be reachable from sibling crates.
|
||||
let has_foreign_aliases_eii = !codegen_attrs.foreign_item_symbol_aliases.is_empty();
|
||||
if is_extern || has_foreign_aliases_eii {
|
||||
self.reachable_symbols.insert(search_item);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -429,6 +435,12 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
|||
// `SymbolExportLevel::Rust` export level but may end up being exported in dylibs.
|
||||
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
|
||||
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
|
||||
// Right now, the only way to get "foreign item symbol aliases" is by being an EII-implementation.
|
||||
// EII implementations will generate under their own name but also under the name of some foreign item
|
||||
// (hence alias) that may be in another crate. These functions are marked as always-reachable since
|
||||
// it's very hard to track whether the original foreign item was reachable. It may live in another crate
|
||||
// and may be reachable from sibling crates.
|
||||
|| !codegen_attrs.foreign_item_symbol_aliases.is_empty()
|
||||
}
|
||||
|
||||
/// See module-level doc comment above.
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ impl Resolver<'_, '_> {
|
|||
&& !tcx.is_panic_runtime(cnum)
|
||||
&& !tcx.has_global_allocator(cnum)
|
||||
&& !tcx.has_panic_handler(cnum)
|
||||
&& tcx.externally_implementable_items(cnum).is_empty()
|
||||
}) {
|
||||
maybe_unused_extern_crates.insert(id, import.span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -314,7 +314,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
ignore_binding: Option<NameBinding<'ra>>,
|
||||
diag_metadata: Option<&DiagMetadata<'_>>,
|
||||
) -> Option<LexicalScopeBinding<'ra>> {
|
||||
assert!(ns == TypeNS || ns == ValueNS);
|
||||
let orig_ident = ident;
|
||||
let (general_span, normalized_span) = if ident.name == kw::SelfUpper {
|
||||
// FIXME(jseyfried) improve `Self` hygiene
|
||||
|
|
|
|||
|
|
@ -449,6 +449,8 @@ pub(crate) enum PathSource<'a, 'ast, 'ra> {
|
|||
ReturnTypeNotation,
|
||||
/// Paths from `#[define_opaque]` attributes
|
||||
DefineOpaques,
|
||||
/// Resolving a macro
|
||||
Macro,
|
||||
}
|
||||
|
||||
impl PathSource<'_, '_, '_> {
|
||||
|
|
@ -465,6 +467,7 @@ impl PathSource<'_, '_, '_> {
|
|||
| PathSource::ReturnTypeNotation => ValueNS,
|
||||
PathSource::TraitItem(ns, _) => ns,
|
||||
PathSource::PreciseCapturingArg(ns) => ns,
|
||||
PathSource::Macro => MacroNS,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -480,7 +483,8 @@ impl PathSource<'_, '_, '_> {
|
|||
| PathSource::TraitItem(..)
|
||||
| PathSource::DefineOpaques
|
||||
| PathSource::Delegation
|
||||
| PathSource::PreciseCapturingArg(..) => false,
|
||||
| PathSource::PreciseCapturingArg(..)
|
||||
| PathSource::Macro => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,6 +526,7 @@ impl PathSource<'_, '_, '_> {
|
|||
},
|
||||
PathSource::ReturnTypeNotation | PathSource::Delegation => "function",
|
||||
PathSource::PreciseCapturingArg(..) => "type or const parameter",
|
||||
PathSource::Macro => "macro",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -616,6 +621,7 @@ impl PathSource<'_, '_, '_> {
|
|||
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }
|
||||
),
|
||||
PathSource::PreciseCapturingArg(MacroNS) => false,
|
||||
PathSource::Macro => matches!(res, Res::Def(DefKind::Macro(_), _)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -635,6 +641,7 @@ impl PathSource<'_, '_, '_> {
|
|||
(PathSource::TraitItem(..) | PathSource::ReturnTypeNotation, false) => E0576,
|
||||
(PathSource::PreciseCapturingArg(..), true) => E0799,
|
||||
(PathSource::PreciseCapturingArg(..), false) => E0800,
|
||||
(PathSource::Macro, _) => E0425,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1060,6 +1067,12 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a value rib for the function.
|
||||
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
|
||||
// Create a label rib for the function.
|
||||
|
|
@ -2132,7 +2145,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
| PathSource::TraitItem(..)
|
||||
| PathSource::Type
|
||||
| PathSource::PreciseCapturingArg(..)
|
||||
| PathSource::ReturnTypeNotation => false,
|
||||
| PathSource::ReturnTypeNotation
|
||||
| PathSource::Macro => false,
|
||||
PathSource::Expr(..)
|
||||
| PathSource::Pat
|
||||
| PathSource::Struct(_)
|
||||
|
|
@ -2889,6 +2903,17 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
let def_id = self.r.local_def_id(item.id);
|
||||
self.parent_scope.macro_rules = self.r.macro_rules_scopes[&def_id];
|
||||
}
|
||||
|
||||
if let Some(EiiExternTarget { extern_item_path, impl_unsafe: _, span: _ }) =
|
||||
¯o_def.eii_extern_target
|
||||
{
|
||||
self.smart_resolve_path(
|
||||
item.id,
|
||||
&None,
|
||||
extern_item_path,
|
||||
PathSource::Expr(None),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ItemKind::ForeignMod(_) | ItemKind::GlobalAsm(_) => {
|
||||
|
|
|
|||
|
|
@ -929,6 +929,11 @@ symbols! {
|
|||
effects,
|
||||
eh_catch_typeinfo,
|
||||
eh_personality,
|
||||
eii,
|
||||
eii_extern_target,
|
||||
eii_impl,
|
||||
eii_internals,
|
||||
eii_shared_macro,
|
||||
emit,
|
||||
emit_enum,
|
||||
emit_enum_variant,
|
||||
|
|
@ -988,6 +993,7 @@ symbols! {
|
|||
extern_crate_item_prelude,
|
||||
extern_crate_self,
|
||||
extern_in_paths,
|
||||
extern_item_impls,
|
||||
extern_prelude,
|
||||
extern_system_varargs,
|
||||
extern_types,
|
||||
|
|
@ -1938,6 +1944,7 @@ symbols! {
|
|||
rustc_dump_user_args,
|
||||
rustc_dump_vtable,
|
||||
rustc_effective_visibility,
|
||||
rustc_eii_extern_item,
|
||||
rustc_evaluate_where_clauses,
|
||||
rustc_expected_cgu_reuse,
|
||||
rustc_force_inline,
|
||||
|
|
@ -2386,6 +2393,7 @@ symbols! {
|
|||
unsafe_block_in_unsafe_fn,
|
||||
unsafe_cell,
|
||||
unsafe_cell_raw_get,
|
||||
unsafe_eii,
|
||||
unsafe_extern_blocks,
|
||||
unsafe_fields,
|
||||
unsafe_no_drop_flag,
|
||||
|
|
|
|||
|
|
@ -3843,6 +3843,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
"unsized values must be place expressions and cannot be put in temporaries",
|
||||
);
|
||||
}
|
||||
ObligationCauseCode::CompareEii { .. } => {
|
||||
panic!("trait bounds on EII not yet supported ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1890,4 +1890,29 @@ pub(crate) mod builtin {
|
|||
pub macro From($item: item) {
|
||||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Externally Implementable Item: Defines an attribute macro that can override the item
|
||||
/// this is applied to.
|
||||
#[unstable(feature = "extern_item_impls", issue = "125418")]
|
||||
#[rustc_builtin_macro]
|
||||
#[allow_internal_unstable(eii_internals, decl_macro, rustc_attrs)]
|
||||
pub macro eii($item:item) {
|
||||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Unsafely Externally Implementable Item: Defines an unsafe attribute macro that can override
|
||||
/// the item this is applied to.
|
||||
#[unstable(feature = "extern_item_impls", issue = "125418")]
|
||||
#[rustc_builtin_macro]
|
||||
#[allow_internal_unstable(eii_internals, decl_macro, rustc_attrs)]
|
||||
pub macro unsafe_eii($item:item) {
|
||||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Impl detail of EII
|
||||
#[unstable(feature = "eii_internals", issue = "none")]
|
||||
#[rustc_builtin_macro]
|
||||
pub macro eii_extern_target($item:item) {
|
||||
/* compiler built-in */
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,3 +117,16 @@ pub use crate::macros::builtin::deref;
|
|||
reason = "`type_alias_impl_trait` has open design concerns"
|
||||
)]
|
||||
pub use crate::macros::builtin::define_opaque;
|
||||
|
||||
#[unstable(
|
||||
feature = "derive_from",
|
||||
issue = "144889",
|
||||
reason = "`derive(From)` is unstable"
|
||||
)]
|
||||
pub use crate::macros::builtin::From;
|
||||
|
||||
#[unstable(feature = "extern_item_impls", issue = "125418")]
|
||||
pub use crate::macros::builtin::{eii, unsafe_eii};
|
||||
|
||||
#[unstable(feature = "eii_internals", issue = "none")]
|
||||
pub use crate::macros::builtin::eii_extern_target;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,12 @@ pub use core::prelude::v1::deref;
|
|||
)]
|
||||
pub use core::prelude::v1::define_opaque;
|
||||
|
||||
#[unstable(feature = "extern_item_impls", issue = "125418")]
|
||||
pub use core::prelude::v1::{eii, unsafe_eii};
|
||||
|
||||
#[unstable(feature = "eii_internals", issue = "none")]
|
||||
pub use core::prelude::v1::eii_extern_target;
|
||||
|
||||
// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated
|
||||
// rather than glob imported because we want docs to show these re-exports as
|
||||
// pointing to within `std`.
|
||||
|
|
|
|||
|
|
@ -382,6 +382,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
|
|||
contract: lc,
|
||||
body: lb,
|
||||
define_opaque: _,
|
||||
eii_impls: _,
|
||||
}),
|
||||
Fn(box ast::Fn {
|
||||
defaultness: rd,
|
||||
|
|
@ -391,6 +392,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
|
|||
contract: rc,
|
||||
body: rb,
|
||||
define_opaque: _,
|
||||
eii_impls: _,
|
||||
}),
|
||||
) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
|
|
@ -554,6 +556,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
|
|||
contract: lc,
|
||||
body: lb,
|
||||
define_opaque: _,
|
||||
eii_impls: _,
|
||||
}),
|
||||
Fn(box ast::Fn {
|
||||
defaultness: rd,
|
||||
|
|
@ -563,6 +566,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
|
|||
contract: rc,
|
||||
body: rb,
|
||||
define_opaque: _,
|
||||
eii_impls: _,
|
||||
}),
|
||||
) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
|
|
@ -638,6 +642,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
|
|||
contract: lc,
|
||||
body: lb,
|
||||
define_opaque: _,
|
||||
eii_impls: _,
|
||||
}),
|
||||
Fn(box ast::Fn {
|
||||
defaultness: rd,
|
||||
|
|
@ -647,6 +652,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
|
|||
contract: rc,
|
||||
body: rb,
|
||||
define_opaque: _,
|
||||
eii_impls: _,
|
||||
}),
|
||||
) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
|
|
|
|||
18
tests/ui/eii/auxiliary/codegen1.rs
Normal file
18
tests/ui/eii/auxiliary/codegen1.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//@ no-prefer-dynamic
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(eii1)]
|
||||
fn decl1(x: u64);
|
||||
|
||||
mod private {
|
||||
#[eii(eii2)]
|
||||
pub fn decl2(x: u64);
|
||||
}
|
||||
|
||||
pub use private::eii2 as eii3;
|
||||
pub use private::decl2 as decl3;
|
||||
|
||||
pub fn local_call_decl1(x: u64) {
|
||||
decl1(x)
|
||||
}
|
||||
6
tests/ui/eii/auxiliary/codegen2.rs
Normal file
6
tests/ui/eii/auxiliary/codegen2.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
//@ no-prefer-dynamic
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(eii1)]
|
||||
pub fn decl1(x: u64);
|
||||
22
tests/ui/eii/auxiliary/codegen3.rs
Normal file
22
tests/ui/eii/auxiliary/codegen3.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//@ no-prefer-dynamic
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
// does have an impl but can't be called
|
||||
#[eii(eii1)]
|
||||
fn decl1(x: u64);
|
||||
|
||||
#[eii(eii2)]
|
||||
pub fn decl2(x: u64);
|
||||
|
||||
mod private {
|
||||
#[eii(eii3)]
|
||||
pub fn decl3(x: u64);
|
||||
}
|
||||
|
||||
pub use private::eii3 as eii4;
|
||||
pub use private::decl3 as decl4;
|
||||
|
||||
pub fn local_call_decl1(x: u64) {
|
||||
decl1(x)
|
||||
}
|
||||
14
tests/ui/eii/auxiliary/cross_crate_eii_declaration.rs
Normal file
14
tests/ui/eii/auxiliary/cross_crate_eii_declaration.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ no-prefer-dynamic
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(eii_internals)]
|
||||
|
||||
#[eii_extern_target(bar)]
|
||||
#[rustc_builtin_macro(eii_shared_macro)]
|
||||
pub macro foo() {}
|
||||
|
||||
unsafe extern "Rust" {
|
||||
pub safe fn bar(x: u64) -> u64;
|
||||
}
|
||||
24
tests/ui/eii/codegen_cross_crate.rs
Normal file
24
tests/ui/eii/codegen_cross_crate.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ aux-build: codegen2.rs
|
||||
//@ compile-flags: -O
|
||||
//@ ignore-backends: gcc
|
||||
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
|
||||
//@ ignore-windows
|
||||
// Tests whether calling EIIs works with the declaration in another crate.
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate codegen2 as codegen;
|
||||
|
||||
#[codegen::eii1]
|
||||
fn eii1_impl(x: u64) {
|
||||
println!("{x:?}")
|
||||
}
|
||||
|
||||
// what you would write:
|
||||
fn main() {
|
||||
// directly
|
||||
eii1_impl(21);
|
||||
// through the alias
|
||||
codegen::decl1(42);
|
||||
}
|
||||
2
tests/ui/eii/codegen_cross_crate.run.stdout
Normal file
2
tests/ui/eii/codegen_cross_crate.run.stdout
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
21
|
||||
42
|
||||
23
tests/ui/eii/codegen_single_crate.rs
Normal file
23
tests/ui/eii/codegen_single_crate.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ ignore-backends: gcc
|
||||
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
|
||||
//@ ignore-windows
|
||||
// Tests whether calling EIIs works with the declaration in the same crate.
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii]
|
||||
fn hello(x: u64);
|
||||
|
||||
#[hello]
|
||||
fn hello_impl(x: u64) {
|
||||
println!("{x:?}")
|
||||
}
|
||||
|
||||
// what you would write:
|
||||
fn main() {
|
||||
// directly
|
||||
hello_impl(21);
|
||||
// through the alias
|
||||
hello(42);
|
||||
}
|
||||
2
tests/ui/eii/codegen_single_crate.run.stdout
Normal file
2
tests/ui/eii/codegen_single_crate.run.stdout
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
21
|
||||
42
|
||||
19
tests/ui/eii/cross_crate.rs
Normal file
19
tests/ui/eii/cross_crate.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//@ compile-flags: --crate-type rlib
|
||||
//@ check-pass
|
||||
//@ aux-build: cross_crate_eii_declaration.rs
|
||||
// Tests whether calling EIIs works with the declaration in another crate.
|
||||
#![feature(extern_item_impls)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(eii_internals)]
|
||||
|
||||
extern crate cross_crate_eii_declaration;
|
||||
|
||||
#[unsafe(cross_crate_eii_declaration::foo)]
|
||||
fn other(x: u64) -> u64 {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
cross_crate_eii_declaration::bar(0);
|
||||
}
|
||||
19
tests/ui/eii/cross_crate_wrong_ty.rs
Normal file
19
tests/ui/eii/cross_crate_wrong_ty.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//@ compile-flags: --crate-type rlib
|
||||
//@ aux-build: cross_crate_eii_declaration.rs
|
||||
// Tests whether the type checking still works properly when the declaration is in another crate.
|
||||
#![feature(extern_item_impls)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(eii_internals)]
|
||||
|
||||
extern crate cross_crate_eii_declaration;
|
||||
|
||||
#[unsafe(cross_crate_eii_declaration::foo)]
|
||||
fn other() -> u64 {
|
||||
//~^ ERROR `other` has 0 parameters but #[foo] requires it to have 1
|
||||
0
|
||||
}
|
||||
|
||||
fn main() {
|
||||
cross_crate_eii_declaration::bar(0);
|
||||
}
|
||||
16
tests/ui/eii/cross_crate_wrong_ty.stderr
Normal file
16
tests/ui/eii/cross_crate_wrong_ty.stderr
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
error[E0806]: `other` has 0 parameters but #[foo] requires it to have 1
|
||||
--> $DIR/cross_crate_wrong_ty.rs:12:1
|
||||
|
|
||||
LL | #[unsafe(cross_crate_eii_declaration::foo)]
|
||||
| ------------------------------------------- required because of this attribute
|
||||
LL | fn other() -> u64 {
|
||||
| ^^^^^^^^^^^^^^^^^ expected 1 parameter, found 0
|
||||
|
|
||||
::: $DIR/auxiliary/cross_crate_eii_declaration.rs:13:5
|
||||
|
|
||||
LL | pub safe fn bar(x: u64) -> u64;
|
||||
| ------------------------------- requires 1 parameter
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0806`.
|
||||
8
tests/ui/eii/default/auxiliary/decl_with_default.rs
Normal file
8
tests/ui/eii/default/auxiliary/decl_with_default.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
//@ no-prefer-dynamic
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(eii1)]
|
||||
pub fn decl1(x: u64) {
|
||||
println!("default {x}");
|
||||
}
|
||||
12
tests/ui/eii/default/auxiliary/impl1.rs
Normal file
12
tests/ui/eii/default/auxiliary/impl1.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: decl_with_default.rs
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate decl_with_default as decl;
|
||||
|
||||
|
||||
#[unsafe(decl::eii1)] //~ ERROR multiple implementations of `#[eii1]`
|
||||
fn other(x: u64) {
|
||||
println!("1{x}");
|
||||
}
|
||||
26
tests/ui/eii/default/call_default.rs
Normal file
26
tests/ui/eii/default/call_default.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: decl_with_default.rs
|
||||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ ignore-backends: gcc
|
||||
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
|
||||
//@ ignore-windows
|
||||
// Functions can have target-cpu applied. On apple-darwin this is super important,
|
||||
// since you can have binaries which mix x86 and aarch64 code that are compatible
|
||||
// with both architectures. So we can't just reject target_cpu on EIIs since apple
|
||||
// puts them on by default. The problem: we generate aliases. And aliases cannot
|
||||
// get target_cpu applied to them. So, instead we should, in the case of functions,
|
||||
// generate a shim function. For statics aliases should keep working in theory.
|
||||
// In fact, aliases are only necessary for statics. For functions we could just
|
||||
// always generate a shim and a previous version of EII did so but I was sad
|
||||
// that that'd never support statics.
|
||||
//@ ignore-macos
|
||||
// Tests EIIs with default implementations.
|
||||
// When there's no explicit declaration, the default should be called from the declaring crate.
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate decl_with_default;
|
||||
|
||||
fn main() {
|
||||
decl_with_default::decl1(10);
|
||||
}
|
||||
1
tests/ui/eii/default/call_default.run.stdout
Normal file
1
tests/ui/eii/default/call_default.run.stdout
Normal file
|
|
@ -0,0 +1 @@
|
|||
default 10
|
||||
19
tests/ui/eii/default/call_impl.rs
Normal file
19
tests/ui/eii/default/call_impl.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: decl_with_default.rs
|
||||
//@ aux-build: impl1.rs
|
||||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ ignore-backends: gcc
|
||||
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
|
||||
//@ ignore-windows
|
||||
// Tests EIIs with default implementations.
|
||||
// When an explicit implementation is given in one dependency, and the declaration is in another,
|
||||
// the explicit implementation is preferred.
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate decl_with_default;
|
||||
extern crate impl1;
|
||||
|
||||
fn main() {
|
||||
decl_with_default::decl1(10);
|
||||
}
|
||||
1
tests/ui/eii/default/call_impl.run.stdout
Normal file
1
tests/ui/eii/default/call_impl.run.stdout
Normal file
|
|
@ -0,0 +1 @@
|
|||
110
|
||||
17
tests/ui/eii/default/local_crate.rs
Normal file
17
tests/ui/eii/default/local_crate.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ ignore-backends: gcc
|
||||
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
|
||||
//@ ignore-windows
|
||||
// Tests EIIs with default implementations.
|
||||
// In the same crate, when there's no explicit declaration, the default should be called.
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(eii1)]
|
||||
pub fn decl1(x: u64) {
|
||||
println!("default {x}");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
decl1(4);
|
||||
}
|
||||
1
tests/ui/eii/default/local_crate.run.stdout
Normal file
1
tests/ui/eii/default/local_crate.run.stdout
Normal file
|
|
@ -0,0 +1 @@
|
|||
default 4
|
||||
23
tests/ui/eii/default/local_crate_explicit.rs
Normal file
23
tests/ui/eii/default/local_crate_explicit.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ ignore-backends: gcc
|
||||
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
|
||||
//@ ignore-windows
|
||||
// Tests EIIs with default implementations.
|
||||
// In the same crate, the explicit implementation should get priority.
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(eii1)]
|
||||
pub fn decl1(x: u64) {
|
||||
//~^ WARN function `decl1` is never used
|
||||
println!("default {x}");
|
||||
}
|
||||
|
||||
#[eii1]
|
||||
pub fn decl2(x: u64) {
|
||||
println!("explicit {x}");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
decl1(4);
|
||||
}
|
||||
1
tests/ui/eii/default/local_crate_explicit.run.stdout
Normal file
1
tests/ui/eii/default/local_crate_explicit.run.stdout
Normal file
|
|
@ -0,0 +1 @@
|
|||
explicit 4
|
||||
10
tests/ui/eii/default/local_crate_explicit.stderr
Normal file
10
tests/ui/eii/default/local_crate_explicit.stderr
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
warning: function `decl1` is never used
|
||||
--> $DIR/local_crate_explicit.rs:11:8
|
||||
|
|
||||
LL | pub fn decl1(x: u64) {
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
6
tests/ui/eii/duplicate/auxiliary/decl.rs
Normal file
6
tests/ui/eii/duplicate/auxiliary/decl.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
//@ no-prefer-dynamic
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
#[eii(eii1)]
|
||||
fn decl1(x: u64);
|
||||
12
tests/ui/eii/duplicate/auxiliary/impl1.rs
Normal file
12
tests/ui/eii/duplicate/auxiliary/impl1.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: decl.rs
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate decl;
|
||||
|
||||
|
||||
#[unsafe(decl::eii1)]
|
||||
fn other(x: u64) {
|
||||
println!("1{x}");
|
||||
}
|
||||
12
tests/ui/eii/duplicate/auxiliary/impl2.rs
Normal file
12
tests/ui/eii/duplicate/auxiliary/impl2.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: decl.rs
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate decl;
|
||||
|
||||
|
||||
#[unsafe(decl::eii1)]
|
||||
fn other(x: u64) {
|
||||
println!("2{x}");
|
||||
}
|
||||
12
tests/ui/eii/duplicate/auxiliary/impl3.rs
Normal file
12
tests/ui/eii/duplicate/auxiliary/impl3.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: decl.rs
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate decl;
|
||||
|
||||
|
||||
#[unsafe(decl::eii1)]
|
||||
fn other(x: u64) {
|
||||
println!("3{x}");
|
||||
}
|
||||
12
tests/ui/eii/duplicate/auxiliary/impl4.rs
Normal file
12
tests/ui/eii/duplicate/auxiliary/impl4.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: decl.rs
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
extern crate decl;
|
||||
|
||||
|
||||
#[unsafe(decl::eii1)]
|
||||
fn other(x: u64) {
|
||||
println!("4{x}");
|
||||
}
|
||||
16
tests/ui/eii/duplicate/duplicate1.rs
Normal file
16
tests/ui/eii/duplicate/duplicate1.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//@ no-prefer-dynamic
|
||||
//@ aux-build: impl1.rs
|
||||
//@ aux-build: impl2.rs
|
||||
//@ ignore-backends: gcc
|
||||
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
|
||||
//@ ignore-windows
|
||||
// tests that EIIs error properly, even if the conflicting implementations live in another crate.
|
||||
#![feature(extern_item_impls)]
|
||||
|
||||
// has a span but in the other crate
|
||||
//~? ERROR multiple implementations of `#[eii1]`
|
||||
|
||||
extern crate impl1;
|
||||
extern crate impl2;
|
||||
|
||||
fn main() {}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue