Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 133 additions & 23 deletions compiler/rustc_ast_lowering/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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))];

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(..))))
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),
);

if new_attributes.is_empty() {
return;
}

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_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;
return None;
}

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()),
};
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)),
};

self.attrs.insert(PARENT_ID, new_attributes);
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(
Expand Down Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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)]
Expand Down
30 changes: 28 additions & 2 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -5294,13 +5296,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);
}
}
Expand Down
14 changes: 14 additions & 0 deletions tests/pretty/auxiliary/to-reuse-functions.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions tests/pretty/delegation-inherit-attributes.pp
Original file line number Diff line number Diff line change
@@ -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() { }
Loading
Loading