Rollup merge of #149843 - aerooneqq:inherit-attributes-in-delegation, r=petrochenkov
Inherit attributes in delegation This PR adds support for inheriting attributes of the original function of the delegation and is a part of rust-lang/rust#118212, for now we inherit only #[must_use] attribute, but we can add other attributes to inherit. The list of processed attributes can be found [here](https://github.com/aerooneqq/public-docs/blob/master/rust/delegation/processed_attributes.md). r? ``@petrochenkov``
This commit is contained in:
commit
e7ba4a5e6d
6 changed files with 305 additions and 26 deletions
|
|
@ -43,13 +43,15 @@ use hir::def::{DefKind, PartialRes, Res};
|
|||
use hir::{BodyId, HirId};
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::*;
|
||||
use rustc_attr_parsing::{AttributeParser, ShouldEmit};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::Target;
|
||||
use rustc_hir::attrs::{AttributeKind, InlineAttr};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{Asyncness, ResolverAstLowering};
|
||||
use rustc_middle::ty::{Asyncness, DelegationFnSigAttrs, ResolverAstLowering};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{Ident, Span, Symbol};
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode};
|
||||
|
|
@ -62,6 +64,41 @@ pub(crate) struct DelegationResults<'hir> {
|
|||
pub generics: &'hir hir::Generics<'hir>,
|
||||
}
|
||||
|
||||
struct AttributeAdditionInfo {
|
||||
pub equals: fn(&hir::Attribute) -> bool,
|
||||
pub kind: AttributeAdditionKind,
|
||||
}
|
||||
|
||||
enum AttributeAdditionKind {
|
||||
Default { factory: fn(Span) -> hir::Attribute },
|
||||
Inherit { flag: DelegationFnSigAttrs, factory: fn(Span, &hir::Attribute) -> hir::Attribute },
|
||||
}
|
||||
|
||||
const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO;
|
||||
|
||||
static ATTRIBUTES_ADDITIONS: &[AttributeAdditionInfo] = &[
|
||||
AttributeAdditionInfo {
|
||||
equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })),
|
||||
kind: AttributeAdditionKind::Inherit {
|
||||
factory: |span, original_attribute| {
|
||||
let reason = match original_attribute {
|
||||
hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
hir::Attribute::Parsed(AttributeKind::MustUse { span, reason })
|
||||
},
|
||||
flag: DelegationFnSigAttrs::MUST_USE,
|
||||
},
|
||||
},
|
||||
AttributeAdditionInfo {
|
||||
equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))),
|
||||
kind: AttributeAdditionKind::Default {
|
||||
factory: |span| hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
impl<'hir> LoweringContext<'_, 'hir> {
|
||||
fn is_method(&self, def_id: DefId, span: Span) -> bool {
|
||||
match self.tcx.def_kind(def_id) {
|
||||
|
|
@ -88,7 +125,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl);
|
||||
match sig_id {
|
||||
Ok(sig_id) => {
|
||||
self.add_inline_attribute_if_needed(span);
|
||||
self.add_attributes_if_needed(span, sig_id);
|
||||
|
||||
let is_method = self.is_method(sig_id, span);
|
||||
let (param_count, c_variadic) = self.param_count(sig_id);
|
||||
|
|
@ -103,29 +140,100 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_inline_attribute_if_needed(&mut self, span: Span) {
|
||||
const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO;
|
||||
let create_inline_attr_slice =
|
||||
|| [hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span))];
|
||||
fn add_attributes_if_needed(&mut self, span: Span, sig_id: DefId) {
|
||||
let new_attributes = self.create_new_attributes(
|
||||
ATTRIBUTES_ADDITIONS,
|
||||
span,
|
||||
sig_id,
|
||||
self.attrs.get(&PARENT_ID),
|
||||
);
|
||||
|
||||
let new_attributes = match self.attrs.get(&PARENT_ID) {
|
||||
Some(attrs) => {
|
||||
// Check if reuse already specifies any inline attribute, if so, do nothing
|
||||
if attrs
|
||||
.iter()
|
||||
.any(|a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if new_attributes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.arena.alloc_from_iter(
|
||||
attrs.into_iter().map(|a| a.clone()).chain(create_inline_attr_slice()),
|
||||
)
|
||||
}
|
||||
None => self.arena.alloc_from_iter(create_inline_attr_slice()),
|
||||
let new_arena_allocated_attributes = match self.attrs.get(&PARENT_ID) {
|
||||
Some(existing_attrs) => self.arena.alloc_from_iter(
|
||||
existing_attrs.iter().map(|a| a.clone()).chain(new_attributes.into_iter()),
|
||||
),
|
||||
None => self.arena.alloc_from_iter(new_attributes.into_iter()),
|
||||
};
|
||||
|
||||
self.attrs.insert(PARENT_ID, new_attributes);
|
||||
self.attrs.insert(PARENT_ID, new_arena_allocated_attributes);
|
||||
}
|
||||
|
||||
fn create_new_attributes(
|
||||
&self,
|
||||
candidate_additions: &[AttributeAdditionInfo],
|
||||
span: Span,
|
||||
sig_id: DefId,
|
||||
existing_attrs: Option<&&[hir::Attribute]>,
|
||||
) -> Vec<hir::Attribute> {
|
||||
let local_original_attributes = self.parse_local_original_attributes(sig_id);
|
||||
|
||||
candidate_additions
|
||||
.iter()
|
||||
.filter_map(|addition_info| {
|
||||
if let Some(existing_attrs) = existing_attrs
|
||||
&& existing_attrs
|
||||
.iter()
|
||||
.any(|existing_attr| (addition_info.equals)(existing_attr))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
match addition_info.kind {
|
||||
AttributeAdditionKind::Default { factory } => Some(factory(span)),
|
||||
AttributeAdditionKind::Inherit { flag, factory } => {
|
||||
let original_attribute = match sig_id.as_local() {
|
||||
Some(local_id) => self
|
||||
.resolver
|
||||
.delegation_fn_sigs
|
||||
.get(&local_id)
|
||||
.is_some_and(|sig| sig.attrs_flags.contains(flag))
|
||||
.then(|| {
|
||||
local_original_attributes
|
||||
.as_ref()
|
||||
.map(|attrs| {
|
||||
attrs
|
||||
.iter()
|
||||
.find(|base_attr| (addition_info.equals)(base_attr))
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
.flatten(),
|
||||
None => self
|
||||
.tcx
|
||||
.get_all_attrs(sig_id)
|
||||
.iter()
|
||||
.find(|base_attr| (addition_info.equals)(base_attr)),
|
||||
};
|
||||
|
||||
original_attribute.map(|a| factory(span, a))
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn parse_local_original_attributes(&self, sig_id: DefId) -> Option<Vec<hir::Attribute>> {
|
||||
if let Some(local_id) = sig_id.as_local()
|
||||
&& let Some(info) = self.resolver.delegation_fn_sigs.get(&local_id)
|
||||
&& !info.to_inherit_attrs.is_empty()
|
||||
{
|
||||
Some(AttributeParser::parse_limited_all(
|
||||
self.tcx.sess,
|
||||
info.to_inherit_attrs.as_slice(),
|
||||
None,
|
||||
Target::Fn,
|
||||
DUMMY_SP,
|
||||
DUMMY_NODE_ID,
|
||||
Some(self.tcx.features()),
|
||||
ShouldEmit::Nothing,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_delegation_sig_id(
|
||||
|
|
@ -220,7 +328,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// We are not forwarding the attributes, as the delegation fn sigs are collected on the ast,
|
||||
// and here we need the hir attributes.
|
||||
let default_safety =
|
||||
if sig.target_feature || self.tcx.def_kind(parent) == DefKind::ForeignMod {
|
||||
if sig.attrs_flags.contains(DelegationFnSigAttrs::TARGET_FEATURE)
|
||||
|| self.tcx.def_kind(parent) == DefKind::ForeignMod
|
||||
{
|
||||
hir::Safety::Unsafe
|
||||
} else {
|
||||
hir::Safety::Safe
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub use generic_args::{GenericArgKind, TermKind, *};
|
|||
pub use generics::*;
|
||||
pub use intrinsic::IntrinsicDef;
|
||||
use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx};
|
||||
use rustc_ast::AttrVec;
|
||||
use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree};
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
pub use rustc_ast_ir::{Movability, Mutability, try_visit};
|
||||
|
|
@ -221,13 +222,24 @@ pub struct ResolverAstLowering {
|
|||
pub delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct DelegationFnSigAttrs: u8 {
|
||||
const TARGET_FEATURE = 1 << 0;
|
||||
const MUST_USE = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub const DELEGATION_INHERIT_ATTRS_START: DelegationFnSigAttrs = DelegationFnSigAttrs::MUST_USE;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DelegationFnSig {
|
||||
pub header: ast::FnHeader,
|
||||
pub param_count: usize,
|
||||
pub has_self: bool,
|
||||
pub c_variadic: bool,
|
||||
pub target_feature: bool,
|
||||
pub attrs_flags: DelegationFnSigAttrs,
|
||||
pub to_inherit_attrs: AttrVec,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, HashStable)]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, Par
|
|||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate};
|
||||
use rustc_middle::middle::resolve_bound_vars::Set1;
|
||||
use rustc_middle::ty::{AssocTag, DelegationFnSig, Visibility};
|
||||
use rustc_middle::ty::{
|
||||
AssocTag, DELEGATION_INHERIT_ATTRS_START, DelegationFnSig, DelegationFnSigAttrs, Visibility,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::config::{CrateType, ResolveDocLinks};
|
||||
use rustc_session::lint;
|
||||
|
|
@ -5297,13 +5299,37 @@ impl ItemInfoCollector<'_, '_, '_> {
|
|||
id: NodeId,
|
||||
attrs: &[Attribute],
|
||||
) {
|
||||
static NAMES_TO_FLAGS: &[(Symbol, DelegationFnSigAttrs)] = &[
|
||||
(sym::target_feature, DelegationFnSigAttrs::TARGET_FEATURE),
|
||||
(sym::must_use, DelegationFnSigAttrs::MUST_USE),
|
||||
];
|
||||
|
||||
let mut to_inherit_attrs = AttrVec::new();
|
||||
let mut attrs_flags = DelegationFnSigAttrs::empty();
|
||||
|
||||
'attrs_loop: for attr in attrs {
|
||||
for &(name, flag) in NAMES_TO_FLAGS {
|
||||
if attr.has_name(name) {
|
||||
attrs_flags.set(flag, true);
|
||||
|
||||
if flag.bits() >= DELEGATION_INHERIT_ATTRS_START.bits() {
|
||||
to_inherit_attrs.push(attr.clone());
|
||||
}
|
||||
|
||||
continue 'attrs_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sig = DelegationFnSig {
|
||||
header,
|
||||
param_count: decl.inputs.len(),
|
||||
has_self: decl.has_self(),
|
||||
c_variadic: decl.c_variadic(),
|
||||
target_feature: attrs.iter().any(|attr| attr.has_name(sym::target_feature)),
|
||||
attrs_flags,
|
||||
to_inherit_attrs,
|
||||
};
|
||||
|
||||
self.r.delegation_fn_sigs.insert(self.r.local_def_id(id), sig);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
tests/pretty/auxiliary/to-reuse-functions.rs
Normal file
14
tests/pretty/auxiliary/to-reuse-functions.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ edition:2021
|
||||
|
||||
#[must_use]
|
||||
#[cold]
|
||||
pub unsafe fn unsafe_fn_extern() -> usize { 1 }
|
||||
|
||||
#[must_use = "extern_fn_extern: some reason"]
|
||||
#[deprecated]
|
||||
pub extern "C" fn extern_fn_extern() -> usize { 1 }
|
||||
|
||||
pub const fn const_fn_extern() -> usize { 1 }
|
||||
|
||||
#[must_use]
|
||||
pub async fn async_fn_extern() { }
|
||||
61
tests/pretty/delegation-inherit-attributes.pp
Normal file
61
tests/pretty/delegation-inherit-attributes.pp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//@ edition:2021
|
||||
//@ aux-crate:to_reuse_functions=to-reuse-functions.rs
|
||||
//@ pretty-mode:hir
|
||||
//@ pretty-compare-only
|
||||
//@ pp-exact:delegation-inherit-attributes.pp
|
||||
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(fn_delegation)]
|
||||
#[attr = MacroUse {arguments: UseAll}]
|
||||
extern crate std;
|
||||
#[prelude_import]
|
||||
use std::prelude::rust_2021::*;
|
||||
|
||||
extern crate to_reuse_functions;
|
||||
|
||||
mod to_reuse {
|
||||
#[attr = MustUse {reason: "foo: some reason"}]
|
||||
#[attr = Cold]
|
||||
fn foo(x: usize) -> usize { x }
|
||||
|
||||
#[attr = MustUse]
|
||||
#[attr = Cold]
|
||||
fn foo_no_reason(x: usize) -> usize { x }
|
||||
|
||||
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
|
||||
#[attr = Cold]
|
||||
fn bar(x: usize) -> usize { x }
|
||||
}
|
||||
|
||||
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
|
||||
#[attr = MustUse {reason: "foo: some reason"}]
|
||||
#[attr = Inline(Hint)]
|
||||
fn foo1(arg0: _) -> _ { to_reuse::foo(self + 1) }
|
||||
|
||||
#[attr = MustUse]
|
||||
#[attr = Inline(Hint)]
|
||||
fn foo_no_reason(arg0: _) -> _ { to_reuse::foo_no_reason(self + 1) }
|
||||
|
||||
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
|
||||
#[attr = MustUse {reason: "some reason"}]
|
||||
#[attr = Inline(Hint)]
|
||||
fn foo2(arg0: _) -> _ { to_reuse::foo(self + 1) }
|
||||
|
||||
#[attr = Inline(Hint)]
|
||||
fn bar(arg0: _) -> _ { to_reuse::bar(arg0) }
|
||||
|
||||
#[attr = MustUse]
|
||||
#[attr = Inline(Hint)]
|
||||
unsafe fn unsafe_fn_extern() -> _ { to_reuse_functions::unsafe_fn_extern() }
|
||||
#[attr = MustUse {reason: "extern_fn_extern: some reason"}]
|
||||
#[attr = Inline(Hint)]
|
||||
extern "C" fn extern_fn_extern()
|
||||
-> _ { to_reuse_functions::extern_fn_extern() }
|
||||
#[attr = Inline(Hint)]
|
||||
const fn const_fn_extern() -> _ { to_reuse_functions::const_fn_extern() }
|
||||
#[attr = MustUse {reason: "some reason"}]
|
||||
#[attr = Inline(Hint)]
|
||||
async fn async_fn_extern() -> _ { to_reuse_functions::async_fn_extern() }
|
||||
|
||||
|
||||
fn main() { }
|
||||
56
tests/pretty/delegation-inherit-attributes.rs
Normal file
56
tests/pretty/delegation-inherit-attributes.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//@ edition:2021
|
||||
//@ aux-crate:to_reuse_functions=to-reuse-functions.rs
|
||||
//@ pretty-mode:hir
|
||||
//@ pretty-compare-only
|
||||
//@ pp-exact:delegation-inherit-attributes.pp
|
||||
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(fn_delegation)]
|
||||
|
||||
extern crate to_reuse_functions;
|
||||
|
||||
mod to_reuse {
|
||||
#[must_use = "foo: some reason"]
|
||||
#[cold]
|
||||
pub fn foo(x: usize) -> usize {
|
||||
x
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[cold]
|
||||
pub fn foo_no_reason(x: usize) -> usize {
|
||||
x
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[deprecated]
|
||||
pub fn bar(x: usize) -> usize {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
reuse to_reuse::foo as foo1 {
|
||||
self + 1
|
||||
}
|
||||
|
||||
reuse to_reuse::foo_no_reason {
|
||||
self + 1
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[must_use = "some reason"]
|
||||
reuse to_reuse::foo as foo2 {
|
||||
self + 1
|
||||
}
|
||||
|
||||
reuse to_reuse::bar;
|
||||
|
||||
reuse to_reuse_functions::unsafe_fn_extern;
|
||||
reuse to_reuse_functions::extern_fn_extern;
|
||||
reuse to_reuse_functions::const_fn_extern;
|
||||
#[must_use = "some reason"]
|
||||
reuse to_reuse_functions::async_fn_extern;
|
||||
|
||||
|
||||
fn main() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue