diff --git a/compiler/rustc_ast_lowering/src/delegation/attributes.rs b/compiler/rustc_ast_lowering/src/delegation/attributes.rs new file mode 100644 index 0000000000000..88bd03389c14c --- /dev/null +++ b/compiler/rustc_ast_lowering/src/delegation/attributes.rs @@ -0,0 +1,86 @@ +use rustc_hir::attrs::{AttributeKind, InlineAttr}; +use rustc_hir::{self as hir}; +use rustc_span::Span; +use rustc_span::def_id::DefId; + +use crate::LoweringContext; +use crate::delegation::DelegationResolution; + +struct AdditionInfo { + pub equals: fn(&hir::Attribute) -> bool, + pub kind: AdditionKind, +} + +enum AdditionKind { + Default { factory: fn(Span) -> hir::Attribute }, + Inherit { factory: fn(Span, &hir::Attribute) -> hir::Attribute }, +} + +static ADDITIONS: &[AdditionInfo] = &[ + AdditionInfo { + equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })), + kind: AdditionKind::Inherit { + factory: |span, original_attr| { + let reason = match original_attr { + hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason, + _ => None, + }; + + hir::Attribute::Parsed(AttributeKind::MustUse { span, reason }) + }, + }, + }, + AdditionInfo { + equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))), + kind: AdditionKind::Default { + factory: |span| hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)), + }, + }, +]; + +impl<'hir> LoweringContext<'_, 'hir> { + pub(super) fn add_attrs_if_needed(&mut self, resolution: &DelegationResolution) { + let &DelegationResolution { span, sig_id, .. } = resolution; + + const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; + let new_attrs = self.create_new_attrs(span, sig_id, self.attrs.get(&PARENT_ID)); + + if !new_attrs.is_empty() { + let new_attrs = match self.attrs.get(&PARENT_ID) { + Some(existing_attrs) => self.arena.alloc_from_iter( + existing_attrs.iter().map(|a| a.clone()).chain(new_attrs.into_iter()), + ), + None => self.arena.alloc_from_iter(new_attrs.into_iter()), + }; + + self.attrs.insert(PARENT_ID, new_attrs); + } + } + + fn create_new_attrs( + &self, + span: Span, + sig_id: DefId, + existing: Option<&&[hir::Attribute]>, + ) -> Vec { + ADDITIONS + .iter() + .filter_map(|addition| { + existing + .is_none_or(|attrs| !attrs.iter().any(|a| (addition.equals)(a))) + .then(|| match addition.kind { + AdditionKind::Default { factory } => Some(factory(span)), + AdditionKind::Inherit { factory, .. } => + { + #[allow(deprecated)] + self.tcx + .get_all_attrs(sig_id) + .iter() + .find_map(|a| (addition.equals)(a).then(|| factory(span, a))) + } + }) + .flatten() + }) + .collect::>() + } +} diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index bdbaa9c39e21a..cd7b23c18971d 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -4,12 +4,13 @@ use rustc_ast::*; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::symbol::kw; use rustc_span::{Ident, Span, sym}; use crate::LoweringContext; +use crate::delegation::resolution::resolver::DelegationResolver; use crate::diagnostics::DelegationInfersMismatch; #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -237,74 +238,130 @@ impl<'hir> GenericsGenerationResult<'hir> { } } -impl<'hir> GenericsGenerationResults<'hir> { - pub(super) fn all_params(&self) -> impl Iterator> { - let parent = self.parent.generics.hir_generics_or_empty().params; - let child = self.child.generics.hir_generics_or_empty().params; +enum ParentSegmentArgs<'a> { + /// Parent segment is valid and generic args are specified: + /// `reuse Trait::<'static, ()>::foo;`. + Specified(&'a AngleBracketedArgs), + /// Parent segment is valid and args are not specified: + /// `reuse Trait::foo;`. + NotSpecified, + /// Parent segment does not exist (`reuse foo`) or we can not + /// add generics to it: + /// ```rust + /// mod to_reuse { + /// fn foo() {} + /// } + /// + /// // Can't add generic args to module. + /// reuse to_reuse::foo; + /// ``` + Invalid, +} - // Order generics, first we have parent and child lifetimes, - // then parent and child types and consts. - // `generics_of` in `rustc_hir_analysis` will order them anyway, - // however we want the order to be consistent in HIR too. - parent - .iter() - .filter(|p| p.is_lifetime()) - .chain(child.iter().filter(|p| p.is_lifetime())) - .chain(parent.iter().filter(|p| !p.is_lifetime())) - .chain(child.iter().filter(|p| !p.is_lifetime())) - .copied() - } +struct GenericsResolution<'a, 'tcx> { + trait_impl: bool, - /// As we add hack predicates(`'a: 'a`) for all lifetimes (see `uplift_delegation_generic_params` - /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics. - /// Those predicates will not affect resulting predicate inheritance and folding - /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature. - pub(super) fn all_predicates(&self) -> impl Iterator> { - self.parent - .generics - .hir_generics_or_empty() - .predicates - .into_iter() - .chain(self.child.generics.hir_generics_or_empty().predicates) - .copied() - } + parent_args: ParentSegmentArgs<'a>, + child_args: Option<&'a AngleBracketedArgs>, + + sig_parent_params: &'tcx [ty::GenericParamDef], + sig_child_params: &'tcx [ty::GenericParamDef], + + free_to_trait_delegation: bool, + /// `reuse Trait::foo;`. + qself_is_none: bool, + /// `reuse <_ as Trait>::foo;`. + qself_is_infer: bool, + /// Whether we should generate `Self` generic param. + generate_self: bool, } -impl<'hir> LoweringContext<'_, 'hir> { - pub(super) fn uplift_delegation_generics( - &mut self, - delegation: &Delegation, +impl<'hir> DelegationResolver<'_, 'hir> { + fn resolve_generics<'a>( + &self, + delegation: &'a Delegation, sig_id: DefId, - ) -> GenericsGenerationResults<'hir> { - let delegation_parent_kind = self.tcx.def_kind(self.tcx.local_parent(self.owner.def_id)); + ) -> GenericsResolution<'a, 'hir> { + let tcx = self.tcx(); + let delegation_parent_kind = tcx.def_kind(tcx.local_parent(self.owner_id())); - let segments = &delegation.path.segments; - let len = segments.len(); + let delegation_in_free_ctx = + !matches!(delegation_parent_kind, DefKind::Trait | DefKind::Impl { .. }); - let get_user_args = |idx: usize| -> Option<&AngleBracketedArgs> { - let segment = &segments[idx]; + let sig_parent = tcx.parent(sig_id); + let sig_in_trait = matches!(tcx.def_kind(sig_parent), DefKind::Trait); + let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait; - let Some(args) = segment.args.as_ref() else { return None }; - let GenericArgs::AngleBracketed(args) = args else { - self.tcx.dcx().span_delayed_bug( - segment.span(), - "expected angle-bracketed generic args in delegation segment", - ); + let mut sig_parent_params: &[ty::GenericParamDef] = &[]; - return None; - }; + let qself_is_infer = + delegation.qself.as_ref().is_some_and(|qself| qself.ty.is_maybe_parenthesised_infer()); - // Treat empty args `reuse foo::<> as bar` as `reuse foo as bar`, - // the same logic applied when we call function `fn f(t: T)` - // like that `f::<>(())`, in HIR no `<>` will be generated. - (!args.args.is_empty()).then(|| args) + let qself_is_none = delegation.qself.is_none(); + + let parent_args = if let [.., parent_segment, _] = &delegation.path.segments[..] { + if let Some(res) = self.get_resolution_id(parent_segment.id) + && matches!(tcx.def_kind(res), DefKind::Trait | DefKind::TraitAlias) + { + sig_parent_params = &tcx.generics_of(sig_parent).own_params; + self.get_user_args(parent_segment) + .map(|args| ParentSegmentArgs::Specified(args)) + .unwrap_or(ParentSegmentArgs::NotSpecified) + } else { + ParentSegmentArgs::Invalid + } + } else { + ParentSegmentArgs::Invalid }; - let sig_params = &self.tcx.generics_of(sig_id).own_params[..]; + GenericsResolution { + parent_args, + sig_parent_params, + qself_is_none, + qself_is_infer, + free_to_trait_delegation, + generate_self: free_to_trait_delegation && (qself_is_none || qself_is_infer), + trait_impl: matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }), + sig_child_params: &tcx.generics_of(sig_id).own_params, + child_args: self.get_user_args( + delegation.path.segments.last().expect("must be at least one segment"), + ), + } + } + + fn get_user_args<'a>(&self, segment: &'a PathSegment) -> Option<&'a AngleBracketedArgs> { + let Some(args) = &segment.args else { return None }; + let GenericArgs::AngleBracketed(args) = args else { + self.tcx().dcx().span_delayed_bug( + segment.span(), + "expected angle-bracketed generic args in delegation segment", + ); + + return None; + }; + + // Treat empty args `reuse foo::<> as bar` as `reuse foo as bar`, + // the same logic applied when we call function `fn f(t: T)` + // like that `f::<>(())`, in HIR no `<>` will be generated. + (!args.args.is_empty()).then(|| args) + } + + pub(super) fn resolve_and_generate_generics( + &self, + delegation: &Delegation, + sig_id: DefId, + ) -> GenericsGenerationResults<'hir> { + let res @ GenericsResolution { + trait_impl, + generate_self, + sig_child_params, + sig_parent_params, + .. + } = self.resolve_generics(delegation, sig_id); // If we are in trait impl always generate function whose generics matches // those that are defined in trait. - if matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }) { + if trait_impl { // Considering parent generics, during signature inheritance // we will take those args that are in trait impl header trait ref. let parent = @@ -312,78 +369,65 @@ impl<'hir> LoweringContext<'_, 'hir> { let parent = GenericsGenerationResult::new(parent); - let child = DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, true); + let child = + DelegationGenerics::generate_all(sig_child_params, GenericsPosition::Child, true); + let child = GenericsGenerationResult::new(child); return GenericsGenerationResults { parent, child, self_ty_propagation_kind: None }; } - let delegation_in_free_ctx = - !matches!(delegation_parent_kind, DefKind::Trait | DefKind::Impl { .. }); - - let sig_parent = self.tcx.parent(sig_id); - let sig_in_trait = matches!(self.tcx.def_kind(sig_parent), DefKind::Trait); - let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait; - - let qself_is_infer = - delegation.qself.as_ref().is_some_and(|qself| qself.ty.is_maybe_parenthesised_infer()); - - let qself_is_none = delegation.qself.is_none(); - - let generate_self = free_to_trait_delegation && (qself_is_none || qself_is_infer); - - let can_add_generics_to_parent = len >= 2 - && self.get_resolution_id(segments[len - 2].id).is_some_and(|def_id| { - matches!(self.tcx.def_kind(def_id), DefKind::Trait | DefKind::TraitAlias) - }); - - let parent_generics = if can_add_generics_to_parent { - let sig_parent_params = &self.tcx.generics_of(sig_parent).own_params; - - if let Some(args) = get_user_args(len - 2) { - DelegationGenerics { - data: self.create_slots_from_args( - args, - &sig_parent_params[usize::from(!generate_self)..], - generate_self, - ), - pos: GenericsPosition::Parent, - trait_impl: false, - } - } else { - DelegationGenerics::generate_all( + let tcx = self.tcx(); + let parent_generics = match res.parent_args { + ParentSegmentArgs::Specified(args) => DelegationGenerics { + data: Self::create_slots_from_args( + tcx, + args, &sig_parent_params[usize::from(!generate_self)..], - GenericsPosition::Parent, - false, - ) + generate_self, + ), + pos: GenericsPosition::Parent, + trait_impl, + }, + ParentSegmentArgs::NotSpecified => DelegationGenerics::generate_all( + &sig_parent_params[usize::from(!generate_self)..], + GenericsPosition::Parent, + trait_impl, + ), + ParentSegmentArgs::Invalid => { + DelegationGenerics { data: vec![], pos: GenericsPosition::Parent, trait_impl } } - } else { - DelegationGenerics { data: vec![], pos: GenericsPosition::Parent, trait_impl: false } }; - let child_generics = if let Some(args) = get_user_args(len - 1) { - let synth_params_index = - sig_params.iter().position(|p| p.kind.is_synthetic()).unwrap_or(sig_params.len()); + let child_generics = if let Some(args) = res.child_args { + let synth_params_index = sig_child_params + .iter() + .position(|p| p.kind.is_synthetic()) + .unwrap_or(sig_child_params.len()); - let mut slots = - self.create_slots_from_args(args, &sig_params[..synth_params_index], false); + let mut slots = Self::create_slots_from_args( + tcx, + args, + &sig_child_params[..synth_params_index], + trait_impl, + ); - for synth_param in &sig_params[synth_params_index..] { + for synth_param in &sig_child_params[synth_params_index..] { slots.push(GenericArgSlot::Generate(synth_param, None)); } - DelegationGenerics { data: slots, pos: GenericsPosition::Child, trait_impl: false } + DelegationGenerics { data: slots, pos: GenericsPosition::Child, trait_impl } } else { - DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, false) + DelegationGenerics::generate_all(sig_child_params, GenericsPosition::Child, trait_impl) }; GenericsGenerationResults { parent: GenericsGenerationResult::new(parent_generics), child: GenericsGenerationResult::new(child_generics), - self_ty_propagation_kind: match free_to_trait_delegation { - true => Some(match qself_is_none { + self_ty_propagation_kind: match res.free_to_trait_delegation { + true => Some(match res.qself_is_none { true => hir::DelegationSelfTyPropagationKind::SelfParam, - false => match qself_is_infer { + false => match res.qself_is_infer { true => hir::DelegationSelfTyPropagationKind::SelfParam, // HirId is filled during generic args propagation. false => hir::DelegationSelfTyPropagationKind::SelfTy(HirId::INVALID), @@ -403,7 +447,7 @@ impl<'hir> LoweringContext<'_, 'hir> { /// infers than generic params then we will not process all infers thus not generating /// more generic params then needed (anyway it is an error). fn create_slots_from_args( - &self, + tcx: TyCtxt<'_>, args: &AngleBracketedArgs, params: &'hir [ty::GenericParamDef], add_first_self: bool, @@ -444,11 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (kw::Underscore, kw::UnderscoreLifetime) }; - self.tcx.dcx().emit_err(DelegationInfersMismatch { - span: arg.span(), - actual, - expected, - }); + tcx.dcx().emit_err(DelegationInfersMismatch { span: arg.span(), actual, expected }); } slots.push(match is_infer { @@ -459,7 +499,42 @@ impl<'hir> LoweringContext<'_, 'hir> { slots } +} + +impl<'hir> GenericsGenerationResults<'hir> { + pub(super) fn all_params(&self) -> impl Iterator> { + let parent = self.parent.generics.hir_generics_or_empty().params; + let child = self.child.generics.hir_generics_or_empty().params; + + // Order generics, first we have parent and child lifetimes, + // then parent and child types and consts. + // `generics_of` in `rustc_hir_analysis` will order them anyway, + // however we want the order to be consistent in HIR too. + parent + .iter() + .filter(|p| p.is_lifetime()) + .chain(child.iter().filter(|p| p.is_lifetime())) + .chain(parent.iter().filter(|p| !p.is_lifetime())) + .chain(child.iter().filter(|p| !p.is_lifetime())) + .copied() + } + + /// As we add hack predicates(`'a: 'a`) for all lifetimes (see `uplift_delegation_generic_params` + /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics. + /// Those predicates will not affect resulting predicate inheritance and folding + /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature. + pub(super) fn all_predicates(&self) -> impl Iterator> { + self.parent + .generics + .hir_generics_or_empty() + .predicates + .into_iter() + .chain(self.child.generics.hir_generics_or_empty().predicates) + .copied() + } +} +impl<'hir> LoweringContext<'_, 'hir> { fn uplift_delegation_generic_params( &mut self, span: Span, diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs similarity index 58% rename from compiler/rustc_ast_lowering/src/delegation.rs rename to compiler/rustc_ast_lowering/src/delegation/mod.rs index a2a7b542258d1..cd742a6c30671 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs @@ -37,36 +37,30 @@ //! also be emitted during HIR ty lowering. use std::iter; -use std::ops::ControlFlow; use ast::visit::Visitor; -use hir::def::{DefKind, Res}; -use hir::{BodyId, HirId}; +use generics::GenericsGenerationResult; +use hir::HirId; +use hir::def::Res; use rustc_abi::ExternAbi; use rustc_ast as ast; -use rustc_ast::node_id::NodeMap; use rustc_ast::*; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::{self as hir, FnDeclFlags}; -use rustc_middle::span_bug; -use rustc_middle::ty::{Asyncness, PerOwnerResolverData}; -use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::Asyncness; +use rustc_span::def_id::DefId; use rustc_span::symbol::kw; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; -use crate::delegation::generics::{ - GenericsGenerationResult, GenericsGenerationResults, GenericsPosition, -}; -use crate::diagnostics::{ - CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, - DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, -}; +use crate::delegation::generics::{GenericsGenerationResults, GenericsPosition}; +use crate::delegation::resolution::resolver::DelegationResolver; +use crate::delegation::resolution::{DelegationResolution, ParamInfo}; use crate::{ AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, }; +mod attributes; mod generics; +mod resolution; pub(crate) struct DelegationResults<'hir> { pub body_id: hir::BodyId, @@ -75,149 +69,24 @@ pub(crate) struct DelegationResults<'hir> { pub generics: &'hir hir::Generics<'hir>, } -struct AttrAdditionInfo { - pub equals: fn(&hir::Attribute) -> bool, - pub kind: AttrAdditionKind, -} - -enum AttrAdditionKind { - Default { factory: fn(Span) -> hir::Attribute }, - Inherit { factory: fn(Span, &hir::Attribute) -> hir::Attribute }, -} - -/// Summary info about function parameters. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -struct ParamInfo { - /// The number of function parameters, including any C variadic `...` parameter. - pub param_count: usize, - - /// Whether the function arguments end in a C variadic `...` parameter. - pub c_variadic: bool, - - /// The index of the splatted parameter, if any. - pub splatted: Option, -} - -const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; - -static ATTRS_ADDITIONS: &[AttrAdditionInfo] = &[ - AttrAdditionInfo { - equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })), - kind: AttrAdditionKind::Inherit { - factory: |span, original_attr| { - let reason = match original_attr { - hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason, - _ => None, - }; - - hir::Attribute::Parsed(AttributeKind::MustUse { span, reason }) - }, - }, - }, - AttrAdditionInfo { - equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))), - kind: AttrAdditionKind::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) { - DefKind::Fn => false, - DefKind::AssocFn => self.tcx.associated_item(def_id).is_method(), - _ => span_bug!(span, "unexpected DefKind for delegation item"), - } - } - - fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> { - let mut visited: FxHashSet = Default::default(); - - loop { - visited.insert(def_id); - - // If def_id is in local crate and it corresponds to another delegation - // it means that we refer to another delegation as a callee, so in order to obtain - // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it. - if let Some(local_id) = def_id.as_local() - && let Some(info) = self.tcx.resolutions(()).delegation_infos.get(&local_id) - && let Ok(id) = info.resolution_id - { - def_id = id; - if visited.contains(&def_id) { - return Err(match visited.len() { - 1 => self.dcx().emit_err(UnresolvedDelegationCallee { span }), - _ => self.dcx().emit_err(CycleInDelegationSignatureResolution { span }), - }); - } - } else { - return Ok(()); - } - } - } - - pub(crate) fn lower_delegation( - &mut self, - delegation: &Delegation, - item_id: NodeId, - ) -> DelegationResults<'hir> { + pub(crate) fn lower_delegation(&mut self, delegation: &Delegation) -> DelegationResults<'hir> { let span = self.lower_span(delegation.last_segment_span()); - let Some(info) = self.tcx.resolutions(()).delegation_infos.get(&self.owner.def_id) else { - self.dcx().span_delayed_bug( - span, - format!("delegation resolution record was not found for {:?}", self.owner.def_id), - ); - - return self.generate_delegation_error(span, delegation); - }; - - let sig_id = info.resolution_id.and_then(|id| self.check_for_cycles(id, span).map(|_| id)); - - // Delegation can be missing from the `delegations_resolutions` table - // in illegal places such as function bodies in extern blocks (see #151356). - let Ok(sig_id) = sig_id else { - self.dcx().span_delayed_bug( - span, - format!("LoweringContext: the delegation {:?} is unresolved", item_id), - ); - + let resolver = DelegationResolver::new(self); + let Ok((res, mut generics)) = resolver.resolve_delegation(delegation, span) else { return self.generate_delegation_error(span, delegation); }; - self.add_attrs_if_needed(span, sig_id); + self.add_attrs_if_needed(&res); - let is_method = self.is_method(sig_id, span); + let (body_id, call_expr_id, unused_target_expr) = + self.lower_delegation_body(delegation, &res, &mut generics); - let param_info = self.param_info(sig_id); + let decl = self.lower_delegation_decl(&res, &generics, call_expr_id, unused_target_expr); - if !self.check_block_soundness(delegation, sig_id, is_method, param_info.param_count) { - return self.generate_delegation_error(span, delegation); - } + let sig = self.lower_delegation_sig(res.sig_id, decl, span); - let mut generics = self.uplift_delegation_generics(delegation, sig_id); - - let (body_id, call_expr_id, unused_target_expr) = self.lower_delegation_body( - delegation, - sig_id, - param_info.param_count, - &mut generics, - span, - ); - - let decl = self.lower_delegation_decl( - delegation.source, - sig_id, - param_info, - span, - &generics, - delegation.id, - call_expr_id, - unused_target_expr, - ); - - let sig = self.lower_delegation_sig(sig_id, decl, span); let ident = self.lower_ident(delegation.ident); let generics = self.arena.alloc(hir::Generics { @@ -231,160 +100,15 @@ impl<'hir> LoweringContext<'_, 'hir> { DelegationResults { body_id, sig, ident, generics } } - fn check_block_soundness( - &self, - delegation: &Delegation, - sig_id: DefId, - is_method: bool, - param_count: usize, - ) -> bool { - let Some(block) = delegation.body.as_ref() else { return true }; - let should_generate_block = self.should_generate_block(delegation, sig_id, is_method); - - // Report an error if user has explicitly specified delegation's target expression - // in a single delegation when reused function has no params. - if param_count == 0 && should_generate_block { - self.dcx().emit_err(DelegationBlockSpecifiedWhenNoParams { span: block.span }); - return false; - } - - struct DefinitionsFinder<'a> { - all_owners: &'a NodeMap>, - // `self.owner.node_id_to_def_id` - nested_def_ids: &'a NodeMap, - } - - impl<'a> ast::visit::Visitor<'a> for DefinitionsFinder<'a> { - type Result = ControlFlow<()>; - - fn visit_id(&mut self, id: NodeId) -> Self::Result { - /* - (from `tests\ui\delegation\target-expr-removal-defs-inside.rs`): - ```rust - reuse impl Trait for S1 { - some::path::<{ fn foo() {} }>::xd(); - fn foo() {} - self.0 - } - ``` - - Constant from unresolved path will be in `nested_owners`, - `fn foo() {}` will not be in `nested_owners` but will be in `owners`, - both have `LocalDefId`, so we check those two maps. - */ - match self.all_owners.contains_key(&id) || self.nested_def_ids.contains_key(&id) { - true => ControlFlow::Break(()), - false => ControlFlow::Continue(()), - } - } - } - - let mut collector = DefinitionsFinder { - all_owners: &self.resolver.owners, - nested_def_ids: &self.owner.node_id_to_def_id, - }; - - let contains_defs = collector.visit_block(block).is_break(); - - // If there are definitions inside and we can't delete target expression, so report an error. - // FIXME(fn_delegation): support deletion of target expression with defs inside. - if !should_generate_block && contains_defs { - self.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }); - return false; - } - - true - } - - fn should_generate_block( - &self, - delegation: &Delegation, - sig_id: DefId, - is_method: bool, - ) -> bool { - is_method - || matches!(self.tcx.def_kind(sig_id), DefKind::Fn) - || matches!(delegation.source, DelegationSource::Single) - } - - fn add_attrs_if_needed(&mut self, span: Span, sig_id: DefId) { - let new_attrs = - self.create_new_attrs(ATTRS_ADDITIONS, span, sig_id, self.attrs.get(&PARENT_ID)); - - if new_attrs.is_empty() { - return; - } - - let new_arena_allocated_attrs = match self.attrs.get(&PARENT_ID) { - Some(existing_attrs) => self.arena.alloc_from_iter( - existing_attrs.iter().map(|a| a.clone()).chain(new_attrs.into_iter()), - ), - None => self.arena.alloc_from_iter(new_attrs.into_iter()), - }; - - self.attrs.insert(PARENT_ID, new_arena_allocated_attrs); - } - - fn create_new_attrs( - &self, - candidate_additions: &[AttrAdditionInfo], - span: Span, - sig_id: DefId, - existing_attrs: Option<&&[hir::Attribute]>, - ) -> Vec { - 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 { - AttrAdditionKind::Default { factory } => Some(factory(span)), - AttrAdditionKind::Inherit { factory, .. } => - { - #[allow(deprecated)] - self.tcx - .get_all_attrs(sig_id) - .iter() - .find_map(|a| (addition_info.equals)(a).then(|| factory(span, a))) - } - } - }) - .collect::>() - } - - fn get_resolution_id(&self, node_id: NodeId) -> Option { - self.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id()) - } - - /// Returns function parameter info, including C variadic `...` and `#[splat]` if present. - fn param_info(&self, def_id: DefId) -> ParamInfo { - let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); - - ParamInfo { - param_count: sig.inputs().len() + usize::from(sig.c_variadic()), - c_variadic: sig.c_variadic(), - splatted: sig.splatted(), - } - } - fn lower_delegation_decl( &mut self, - source: DelegationSource, - sig_id: DefId, - param_info: ParamInfo, - span: Span, + res: &DelegationResolution, generics: &GenericsGenerationResults<'hir>, - call_path_node_id: NodeId, call_expr_id: HirId, unused_target_expr: bool, ) -> &'hir hir::FnDecl<'hir> { - let ParamInfo { param_count, c_variadic, splatted } = param_info; + let &DelegationResolution { source, call_path_res, span, sig_id, .. } = res; + let ParamInfo { param_count, c_variadic, splatted } = res.param_info; // The last parameter in C variadic functions is skipped in the signature, // like during regular lowering. @@ -404,7 +128,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig_id, hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationInfo { call_expr_id, - call_path_res: self.get_resolution_id(call_path_node_id), + call_path_res, child_seg_id: generics.child.args_segment_id, child_seg_id_for_sig: generics.child.segment_id_for_sig(), parent_seg_id_for_sig: generics.parent.segment_id_for_sig(), @@ -517,23 +241,24 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_delegation_body( &mut self, delegation: &Delegation, - sig_id: DefId, - param_count: usize, + res: &DelegationResolution, generics: &mut GenericsGenerationResults<'hir>, - span: Span, - ) -> (BodyId, HirId, bool) { + ) -> (hir::BodyId, HirId, bool) { let block = delegation.body.as_deref(); let mut call_expr_id = HirId::INVALID; let mut unused_target_expr = false; let block_id = self.lower_body(|this| { + let &DelegationResolution { + param_info, span, should_generate_block, is_method, .. + } = res; + + let ParamInfo { param_count, .. } = param_info; + let mut parameters: Vec> = Vec::with_capacity(param_count); let mut args: Vec> = Vec::with_capacity(param_count); let mut stmts: &[hir::Stmt<'hir>] = &[]; - let is_method = this.is_method(sig_id, span); - let should_generate_block = this.should_generate_block(delegation, sig_id, is_method); - // Consider non-specified target expression as generated, // as we do not want to emit error when target expression is // not specified. @@ -580,7 +305,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } let (final_expr, hir_id) = - this.finalize_body_lowering(delegation, stmts, args, generics, span); + this.finalize_body_lowering(delegation, stmts, args, res, generics, span); call_expr_id = hir_id; @@ -597,6 +322,7 @@ impl<'hir> LoweringContext<'_, 'hir> { delegation: &Delegation, stmts: &'hir [hir::Stmt<'hir>], args: Vec>, + res: &DelegationResolution, generics: &mut GenericsGenerationResults<'hir>, span: Span, ) -> (hir::Expr<'hir>, HirId) { @@ -666,7 +392,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let args = self.arena.alloc_from_iter(args); let call = self.mk_expr(hir::ExprKind::Call(callee_path, args), span); - let expr = if let Some((parent, of_trait)) = self.should_wrap_return_value(delegation) { + let expr = if let Some((parent, of_trait)) = res.output_self_mapping { let res = Res::SelfTyAlias { alias_to: parent.to_def_id(), is_trait_impl: of_trait }; let ident = Ident::new(kw::SelfUpper, span); let path = self.create_resolved_path(res, ident, span); @@ -701,45 +427,6 @@ impl<'hir> LoweringContext<'_, 'hir> { (self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id) } - fn should_wrap_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> { - // Heuristic: don't do wrapping if there is no target expression. - if delegation.body.is_none() { - return None; - } - - let tcx = self.tcx; - let parent = tcx.local_parent(self.owner.def_id); - let parent_kind = tcx.def_kind(parent); - - // Apply wrapping for delegations inside - // 1) Trait impls, as the return type of both signature function - // and generated delegation has `Self` generic param returned - // (checked below). - // FIXME(fn_delegation): think of enabling wrapping in more scenarios: - // trait-(impl)-to-free - // trait-(impl)-to-inherent - // inherent-to-free - // 2) Inherent methods when delegating to trait, as we change the type of - // `Self` to type of struct or enum we delegate from. - if !matches!(tcx.def_kind(parent), DefKind::Impl { .. }) { - return None; - } - - let is_trait_impl = parent_kind == DefKind::Impl { of_trait: true }; - - // Check that delegation path resolves to a trait AssocFn, not to a free method. - Some((parent, is_trait_impl)).filter(|_| { - self.get_resolution_id(delegation.id).is_some_and(|id| { - tcx.def_kind(id) == DefKind::AssocFn - // Check that the return type of the callee is `Self` param. - // After previous check we are sure that `sig_id` and `delegation.id` - // point to the same function. - && tcx.def_kind(tcx.parent(id)) == DefKind::Trait - && tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0) - }) - }) - } - fn process_segment( &mut self, span: Span, @@ -808,7 +495,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let callee_path = this.arena.alloc(this.mk_expr(hir::ExprKind::Path(path), span)); - let args = if let Some(block) = delegation.body.as_ref() { + let args = if let Some(block) = &delegation.body { this.arena.alloc_slice(&[this.lower_block_expr(block)]) } else { &mut [] diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs new file mode 100644 index 0000000000000..3cb40d256ba8b --- /dev/null +++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs @@ -0,0 +1,264 @@ +use std::ops::ControlFlow; + +use ast::visit::Visitor; +use hir::def::DefKind; +use rustc_ast as ast; +use rustc_ast::*; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_middle::span_bug; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::{ErrorGuaranteed, Span}; + +use crate::delegation::generics::GenericsGenerationResults; +use crate::delegation::resolution::resolver::DelegationResolver; +use crate::diagnostics::{ + CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, + DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, +}; + +/// Summary info about function parameters. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub(super) struct ParamInfo { + /// The number of function parameters, including any C variadic `...` parameter. + pub param_count: usize, + + /// Whether the function arguments end in a C variadic `...` parameter. + pub c_variadic: bool, + + /// The index of the splatted parameter, if any. + pub splatted: Option, +} + +pub(super) struct DelegationResolution { + pub sig_id: DefId, + pub is_method: bool, + pub param_info: ParamInfo, + pub span: Span, + pub should_generate_block: bool, + pub call_path_res: Option, + pub source: DelegationSource, + pub output_self_mapping: Option<(LocalDefId, bool)>, +} + +pub(super) mod resolver { + use rustc_ast::NodeId; + use rustc_hir::def_id::{DefId, LocalDefId}; + use rustc_middle::ty::TyCtxt; + + use crate::LoweringContext; + + /// Abstracts operations that are needed for delegation's resolution, so resolution + /// is independent of `LoweringContext`. Placed in a separate module so `LoweringContext` + /// can not be accessed directly. + pub(crate) struct DelegationResolver<'a, 'hir>(&'a LoweringContext<'a, 'hir>); + + impl<'a, 'tcx> DelegationResolver<'a, 'tcx> { + pub(crate) fn new(ctx: &'a LoweringContext<'a, 'tcx>) -> Self { + DelegationResolver(ctx) + } + + #[inline] + pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { + self.0.tcx + } + + #[inline] + pub(crate) fn owner_id(&self) -> LocalDefId { + self.0.owner.def_id + } + + /// (from `tests\ui\delegation\target-expr-removal-defs-inside.rs`): + /// ```rust + /// reuse impl Trait for S1 { + /// some::path::<{ fn foo() {} }>::xd(); + /// fn foo() {} + /// self.0 + /// } + /// ``` + /// + /// Constant from unresolved path will be in `node_id_to_def_id`, + /// `fn foo() {}` will not be in `node_id_to_def_id` but will be in `owners`, + /// both have `LocalDefId`, so we check those two maps. + #[inline] + pub(crate) fn is_definition(&self, id: NodeId) -> bool { + self.0.resolver.owners.contains_key(&id) + || self.0.owner.node_id_to_def_id.contains_key(&id) + } + + #[inline] + pub(crate) fn get_resolution_id(&self, id: NodeId) -> Option { + self.0.get_partial_res(id).and_then(|r| r.expect_full_res().opt_def_id()) + } + } +} + +impl<'tcx> DelegationResolver<'_, 'tcx> { + pub(super) fn resolve_delegation( + &self, + delegation: &Delegation, + span: Span, + ) -> Result<(DelegationResolution, GenericsGenerationResults<'tcx>), ErrorGuaranteed> { + let tcx = self.tcx(); + let def_id = self.owner_id(); + + // Delegation can be missing from the `delegations_resolutions` table + // in illegal places such as function bodies in extern blocks (see #151356). + let sig_id = tcx + .resolutions(()) + .delegation_infos + .get(&def_id) + .map(|info| { + info.resolution_id.and_then(|id| self.check_for_cycles(id, span).map(|_| id)) + }) + .unwrap_or_else(|| { + Err(tcx.dcx().span_delayed_bug( + span, + format!("delegation resolution record was not found for {:?}", def_id), + )) + })?; + + let is_method = match tcx.def_kind(sig_id) { + DefKind::Fn => false, + DefKind::AssocFn => tcx.associated_item(sig_id).is_method(), + _ => span_bug!(span, "unexpected DefKind for delegation item"), + }; + + let sig = tcx.fn_sig(sig_id).skip_binder().skip_binder(); + let param_count = sig.inputs().len() + usize::from(sig.c_variadic()); + + let res = DelegationResolution { + is_method, + span, + sig_id, + // FIXME(splat): use `sig.splatted()` once FnSig has it + param_info: ParamInfo { param_count, c_variadic: sig.c_variadic(), splatted: None }, + should_generate_block: self.check_block_soundness( + delegation, + sig_id, + is_method, + param_count, + )?, + source: delegation.source, + call_path_res: self.get_resolution_id(delegation.id), + output_self_mapping: self.should_map_return_value(delegation), + }; + + Ok((res, self.resolve_and_generate_generics(delegation, sig_id))) + } + + fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> { + let tcx = self.tcx(); + let mut visited: FxHashSet = Default::default(); + + loop { + visited.insert(def_id); + + // If def_id is in local crate and it corresponds to another delegation + // it means that we refer to another delegation as a callee, so in order to obtain + // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it. + if let Some(local_id) = def_id.as_local() + && let Some(info) = tcx.resolutions(()).delegation_infos.get(&local_id) + && let Ok(id) = info.resolution_id + { + def_id = id; + if visited.contains(&def_id) { + return Err(match visited.len() { + 1 => tcx.dcx().emit_err(UnresolvedDelegationCallee { span }), + _ => tcx.dcx().emit_err(CycleInDelegationSignatureResolution { span }), + }); + } + } else { + return Ok(()); + } + } + } + + fn check_block_soundness( + &self, + delegation: &Delegation, + sig_id: DefId, + is_method: bool, + param_count: usize, + ) -> Result { + let tcx = self.tcx(); + let should_generate_block = is_method + || matches!(tcx.def_kind(sig_id), DefKind::Fn) + || matches!(delegation.source, DelegationSource::Single); + + let Some(block) = &delegation.body else { return Ok(should_generate_block) }; + + // Report an error if user has explicitly specified delegation's target expression + // in a single delegation when reused function has no params. + if param_count == 0 && should_generate_block { + let err = DelegationBlockSpecifiedWhenNoParams { span: block.span }; + return Err(tcx.dcx().emit_err(err)); + } + + struct DefinitionsFinder<'a, 'hir> { + ctx: &'a DelegationResolver<'a, 'hir>, + } + + impl<'a> Visitor<'a> for DefinitionsFinder<'a, '_> { + type Result = ControlFlow<()>; + + fn visit_id(&mut self, id: NodeId) -> Self::Result { + match self.ctx.is_definition(id) { + true => ControlFlow::Break(()), + false => ControlFlow::Continue(()), + } + } + } + + let mut collector = DefinitionsFinder { ctx: self }; + + let contains_defs = collector.visit_block(block).is_break(); + + // If there are definitions inside and we can't delete target expression, then report an error. + // FIXME(fn_delegation): support deletion of target expression with defs inside. + if should_generate_block || !contains_defs { + Ok(should_generate_block) + } else { + Err(tcx.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span })) + } + } + + fn should_map_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> { + // Heuristic: don't do wrapping if there is no target expression. + if delegation.body.is_none() { + return None; + } + + let tcx = self.tcx(); + let parent = tcx.local_parent(self.owner_id()); + let parent_kind = tcx.def_kind(parent); + + // Apply wrapping for delegations inside + // 1) Trait impls, as the return type of both signature function + // and generated delegation has `Self` generic param returned + // (checked below). + // FIXME(fn_delegation): think of enabling wrapping in more scenarios: + // trait-(impl)-to-free + // trait-(impl)-to-inherent + // inherent-to-free + // 2) Inherent methods when delegating to trait, as we change the type of + // `Self` to type of struct or enum we delegate from. + if !matches!(tcx.def_kind(parent), DefKind::Impl { .. }) { + return None; + } + + let is_trait_impl = parent_kind == DefKind::Impl { of_trait: true }; + + // Check that delegation path resolves to a trait AssocFn, not to a free method. + Some((parent, is_trait_impl)).filter(|_| { + self.get_resolution_id(delegation.id).is_some_and(|id| { + tcx.def_kind(id) == DefKind::AssocFn + // Check that the return type of the callee is `Self` param. + // After previous check we are sure that `sig_id` and `delegation.id` + // point to the same function. + && tcx.def_kind(tcx.parent(id)) == DefKind::Trait + && tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0) + }) + }) + } +} diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index c54777d98070a..8ebecb222b940 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -567,7 +567,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ItemKind::Macro(ident, macro_def, macro_kinds) } ItemKind::Delegation(delegation) => { - let delegation_results = self.lower_delegation(delegation, id); + let delegation_results = self.lower_delegation(delegation); hir::ItemKind::Fn { sig: delegation_results.sig, ident: delegation_results.ident, @@ -1052,7 +1052,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (*ident, generics, kind, ty.is_some()) } AssocItemKind::Delegation(delegation) => { - let delegation_results = self.lower_delegation(delegation, i.id); + let delegation_results = self.lower_delegation(delegation); let item_kind = hir::TraitItemKind::Fn( delegation_results.sig, hir::TraitFn::Provided(delegation_results.body_id), @@ -1264,7 +1264,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } AssocItemKind::Delegation(delegation) => { - let delegation_results = self.lower_delegation(delegation, i.id); + let delegation_results = self.lower_delegation(delegation); ( delegation.ident, (