From 831a2e85ab1c323e94e1bde969b1fc0b81bb31fb Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 7 Jul 2026 11:01:21 +0000 Subject: [PATCH] delegation: support mapping of all arguments with `Self` type * Support mapping of all arguments with Self type * Address some review comments * Review: use #[cfg(debug_assertions)] on the whole module * Review: emit error when trying to map non-receiver argument when block contains definitions * Use `cfg_select` * Fix tidy * Unify mapping with `should_generate_block` * Better naming and message for error * loop -> `extend` --- .../src/delegation/generics.rs | 23 ++-- .../rustc_ast_lowering/src/delegation/mod.rs | 123 +++++++++++------ .../src/delegation/resolution.rs | 126 ++++++++++++------ .../rustc_ast_lowering/src/diagnostics.rs | 9 ++ compiler/rustc_ast_lowering/src/lib.rs | 63 +++++++-- compiler/rustc_hir/src/hir.rs | 2 +- compiler/rustc_hir_typeck/src/callee.rs | 6 +- tests/ui/delegation/bad-resolve.rs | 1 + tests/ui/delegation/bad-resolve.stderr | 23 +++- .../self-mapping-arguments-errors.rs | 47 +++++++ .../self-mapping-arguments-errors.stderr | 98 ++++++++++++++ tests/ui/delegation/self-mapping-arguments.rs | 59 ++++++++ .../self-mapping-arguments.run.stdout | 5 + 13 files changed, 471 insertions(+), 114 deletions(-) create mode 100644 tests/ui/delegation/self-mapping-arguments-errors.rs create mode 100644 tests/ui/delegation/self-mapping-arguments-errors.stderr create mode 100644 tests/ui/delegation/self-mapping-arguments.rs create mode 100644 tests/ui/delegation/self-mapping-arguments.run.stdout diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index cd7b23c18971d..4d9bc09faeecb 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::symbol::kw; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, sym}; use crate::LoweringContext; use crate::delegation::resolution::resolver::DelegationResolver; @@ -281,7 +281,7 @@ impl<'hir> DelegationResolver<'_, 'hir> { &self, delegation: &'a Delegation, sig_id: DefId, - ) -> GenericsResolution<'a, 'hir> { + ) -> Result, ErrorGuaranteed> { let tcx = self.tcx(); let delegation_parent_kind = tcx.def_kind(tcx.local_parent(self.owner_id())); @@ -300,9 +300,8 @@ impl<'hir> DelegationResolver<'_, 'hir> { 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) - { + let res = self.get_resolution_id(parent_segment.id)?; + if 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)) @@ -314,7 +313,7 @@ impl<'hir> DelegationResolver<'_, 'hir> { ParentSegmentArgs::Invalid }; - GenericsResolution { + Ok(GenericsResolution { parent_args, sig_parent_params, qself_is_none, @@ -326,7 +325,7 @@ impl<'hir> DelegationResolver<'_, 'hir> { 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> { @@ -350,14 +349,14 @@ impl<'hir> DelegationResolver<'_, 'hir> { &self, delegation: &Delegation, sig_id: DefId, - ) -> GenericsGenerationResults<'hir> { + ) -> Result, ErrorGuaranteed> { let res @ GenericsResolution { trait_impl, generate_self, sig_child_params, sig_parent_params, .. - } = self.resolve_generics(delegation, sig_id); + } = self.resolve_generics(delegation, sig_id)?; // If we are in trait impl always generate function whose generics matches // those that are defined in trait. @@ -374,7 +373,7 @@ impl<'hir> DelegationResolver<'_, 'hir> { let child = GenericsGenerationResult::new(child); - return GenericsGenerationResults { parent, child, self_ty_propagation_kind: None }; + return Ok(GenericsGenerationResults { parent, child, self_ty_propagation_kind: None }); } let tcx = self.tcx(); @@ -421,7 +420,7 @@ impl<'hir> DelegationResolver<'_, 'hir> { DelegationGenerics::generate_all(sig_child_params, GenericsPosition::Child, trait_impl) }; - GenericsGenerationResults { + Ok(GenericsGenerationResults { parent: GenericsGenerationResult::new(parent_generics), child: GenericsGenerationResult::new(child_generics), self_ty_propagation_kind: match res.free_to_trait_delegation { @@ -435,7 +434,7 @@ impl<'hir> DelegationResolver<'_, 'hir> { }), false => None, }, - } + }) } /// Generates generic argument slots for user-specified `args` and diff --git a/compiler/rustc_ast_lowering/src/delegation/mod.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs index cd742a6c30671..3b85dd85985d2 100644 --- a/compiler/rustc_ast_lowering/src/delegation/mod.rs +++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs @@ -45,6 +45,7 @@ use hir::def::Res; use rustc_abi::ExternAbi; use rustc_ast as ast; use rustc_ast::*; +use rustc_hir::def::DefKind; use rustc_hir::{self as hir, FnDeclFlags}; use rustc_middle::ty::Asyncness; use rustc_span::def_id::DefId; @@ -249,20 +250,19 @@ impl<'hir> LoweringContext<'_, 'hir> { 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 &DelegationResolution { param_info, span, is_method, .. } = res; let ParamInfo { param_count, .. } = param_info; + let arguments_to_map = &res.sig_mapping.arguments_to_map; 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 mut stmts = vec![]; // Consider non-specified target expression as generated, // as we do not want to emit error when target expression is // not specified. - unused_target_expr = block.is_some() && (param_count == 0 || !should_generate_block); + unused_target_expr = + block.is_some() && (param_count == 0 || arguments_to_map.is_empty()); for idx in 0..param_count { let (param, pat_node_id) = this.generate_param(is_method, idx, span); @@ -271,41 +271,38 @@ impl<'hir> LoweringContext<'_, 'hir> { let generate_arg = |this: &mut Self| this.generate_arg(is_method, idx, param.pat.hir_id, span); - let arg = if let Some(block) = block - && idx == 0 - && should_generate_block - { - let mut self_resolver = SelfResolver { - ctxt: this, - path_id: delegation.id, - self_param_id: pat_node_id, - }; - self_resolver.visit_block(block); - // Target expr needs to lower `self` path. - this.ident_and_label_to_local_id.insert(pat_node_id, param.pat.hir_id.local_id); - - // Lower with `HirId::INVALID` as we will use only expr and stmts. - // FIXME(fn_delegation): Alternatives for target expression lowering: - // https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600. - let block = this.lower_block_noalloc(HirId::INVALID, block, false); - - stmts = block.stmts; - - // The behavior of the delegation's target expression differs from the - // behavior of the usual block, where if there is no final expression - // the `()` is returned. In case of the similar situation in delegation - // (no final expression) we propagate first argument instead of replacing - // it with `()`. - if let Some(&expr) = block.expr { expr } else { generate_arg(this) } - } else { - generate_arg(this) - }; + let arg = block + .filter(|_| arguments_to_map.contains(&idx)) + .and_then(|block| { + let block = this.lower_block_maybe_more_than_once( + block, + pat_node_id, + param.pat.hir_id.local_id, + delegation.id, + ); + + stmts.push(block.stmts); + + // The behavior of the delegation's target expression differs from the + // behavior of the usual block, where if there is no final expression + // the `()` is returned. In case of the similar situation in delegation + // (no final expression) we propagate first argument instead of replacing + // it with `()`. + block.expr.copied() + }) + .unwrap_or_else(|| generate_arg(this)); args.push(arg); } - let (final_expr, hir_id) = - this.finalize_body_lowering(delegation, stmts, args, res, generics, span); + let (final_expr, hir_id) = this.finalize_body_lowering( + delegation, + this.arena.alloc_from_iter(stmts.into_iter().flatten().copied()), + args, + res, + generics, + span, + ); call_expr_id = hir_id; @@ -317,6 +314,48 @@ impl<'hir> LoweringContext<'_, 'hir> { (block_id, call_expr_id, unused_target_expr) } + fn lower_block_maybe_more_than_once( + &mut self, + block: &Block, + pat_node_id: NodeId, + param_local_id: hir::ItemLocalId, + delegation_id: NodeId, + ) -> hir::Block<'hir> { + let mut self_resolver = SelfResolver { + ctxt: self, + path_id: delegation_id, + self_param_id: pat_node_id, + overwrites: vec![], + }; + + self_resolver.visit_block(block); + + let overwrites = self_resolver.overwrites; + + // Target expr needs to lower `self` path. + self.ident_and_label_to_local_id.insert(pat_node_id, param_local_id); + + let block = cfg_select! { + debug_assertions => { + crate::re_lowering::ReloweringChecker::allow_relowering(self, |this| { + this.lower_block_noalloc(HirId::INVALID, block, false) + }) + }, + _ => self.lower_block_noalloc(HirId::INVALID, block, false) + }; + + // Remove node ids for which we overwrote resolution to generated param + // before block lowering as block can be relowered. We need to do it because + // check in `SelfResolver` uses `get_partial_res` to decide whether to overwrite + // resolution, and if it is already overwritten from previous block lowering this + // check will not pass. + for id in overwrites { + self.partial_res_overrides.remove(&id); + } + + block + } + fn finalize_body_lowering( &mut self, delegation: &Delegation, @@ -392,8 +431,12 @@ 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)) = res.output_self_mapping { - let res = Res::SelfTyAlias { alias_to: parent.to_def_id(), is_trait_impl: of_trait }; + let expr = if res.sig_mapping.map_return { + let res = Res::SelfTyAlias { + alias_to: res.parent.to_def_id(), + is_trait_impl: self.tcx.def_kind(res.parent) == DefKind::Impl { of_trait: true }, + }; + let ident = Ident::new(kw::SelfUpper, span); let path = self.create_resolved_path(res, ident, span); @@ -538,6 +581,7 @@ struct SelfResolver<'a, 'b, 'hir> { ctxt: &'a mut LoweringContext<'b, 'hir>, path_id: NodeId, self_param_id: NodeId, + overwrites: Vec, } impl SelfResolver<'_, '_, '_> { @@ -546,6 +590,7 @@ impl SelfResolver<'_, '_, '_> { && let Some(Res::Local(sig_id)) = res.full_res() && sig_id == self.path_id { + self.overwrites.push(id); self.ctxt.partial_res_overrides.insert(id, self.self_param_id); } } diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs index 3cb40d256ba8b..21fbed81b3e0b 100644 --- a/compiler/rustc_ast_lowering/src/delegation/resolution.rs +++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs @@ -6,7 +6,7 @@ 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_middle::{span_bug, ty}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{ErrorGuaranteed, Span}; @@ -14,7 +14,8 @@ use crate::delegation::generics::GenericsGenerationResults; use crate::delegation::resolution::resolver::DelegationResolver; use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, - DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, + DelegationAttemptedBlockWithDefsRelowering, DelegationBlockSpecifiedWhenNoParams, + UnresolvedDelegationCallee, }; /// Summary info about function parameters. @@ -30,21 +31,28 @@ pub(super) struct ParamInfo { pub splatted: Option, } +#[derive(Default)] +pub(super) struct SigMapping { + pub map_return: bool, + pub arguments_to_map: FxHashSet, +} + 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 call_path_res: DefId, pub source: DelegationSource, - pub output_self_mapping: Option<(LocalDefId, bool)>, + pub parent: LocalDefId, + pub sig_mapping: SigMapping, } pub(super) mod resolver { use rustc_ast::NodeId; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::TyCtxt; + use rustc_span::ErrorGuaranteed; use crate::LoweringContext; @@ -87,8 +95,10 @@ pub(super) mod resolver { } #[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()) + pub(crate) fn get_resolution_id(&self, id: NodeId) -> Result { + self.0.get_partial_res(id).and_then(|r| r.expect_full_res().opt_def_id()).ok_or_else( + || self.tcx().dcx().delayed_bug(format!("failed to resolve node {id:?}")), + ) } } } @@ -126,25 +136,31 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { let sig = tcx.fn_sig(sig_id).skip_binder().skip_binder(); let param_count = sig.inputs().len() + usize::from(sig.c_variadic()); + let parent = tcx.local_parent(def_id); + + let (should_generate_block, contains_defs) = + self.check_block_soundness(delegation, sig_id, is_method, param_count)?; let res = DelegationResolution { is_method, span, sig_id, + parent, // 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( + source: delegation.source, + call_path_res: self.get_resolution_id(delegation.id)?, + sig_mapping: self.create_self_mapping( delegation, - sig_id, - is_method, - param_count, + span, + should_generate_block, + parent, + sig, + contains_defs, )?, - 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))) + Ok((res, self.resolve_and_generate_generics(delegation, sig_id)?)) } fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> { @@ -180,13 +196,13 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { sig_id: DefId, is_method: bool, param_count: usize, - ) -> Result { + ) -> Result<(/* should generate block */ bool, /* contains defs */ bool), ErrorGuaranteed> { 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) }; + let Some(block) = &delegation.body else { return Ok((should_generate_block, false)) }; // Report an error if user has explicitly specified delegation's target expression // in a single delegation when reused function has no params. @@ -194,44 +210,84 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { let err = DelegationBlockSpecifiedWhenNoParams { span: block.span }; return Err(tcx.dcx().emit_err(err)); } - struct DefinitionsFinder<'a, 'hir> { - ctx: &'a DelegationResolver<'a, 'hir>, + resolver: &'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) { + match self.resolver.is_definition(id) { true => ControlFlow::Break(()), false => ControlFlow::Continue(()), } } } - let mut collector = DefinitionsFinder { ctx: self }; + let mut collector = DefinitionsFinder { resolver: 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) + Ok((should_generate_block, contains_defs)) } else { Err(tcx.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span })) } } - fn should_map_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> { + fn create_self_mapping( + &self, + delegation: &Delegation, + span: Span, + should_generate_block: bool, + parent: LocalDefId, + sig: ty::FnSig<'tcx>, + contains_defs: bool, + ) -> Result { + let mut mapping = SigMapping::default(); + if should_generate_block { + mapping.arguments_to_map.insert(0); + } + + if self.can_perform_self_mapping(delegation, parent)? { + mapping.map_return = sig.output().is_param(0); + + let arguments_to_map = sig + .inputs() + .iter() + .enumerate() + .skip(1) // Already checked above. + .filter_map(|(idx, param)| param.is_param(0).then_some(idx)); + + mapping.arguments_to_map.extend(arguments_to_map); + } + + // We can't yet map more than one argument if there are definitions inside. + // FIXME(fn_delegation): support relowering with defs inside + if contains_defs && mapping.arguments_to_map.len() > 1 { + return Err(self + .tcx() + .dcx() + .emit_err(DelegationAttemptedBlockWithDefsRelowering { span })); + } + + Ok(mapping) + } + + fn can_perform_self_mapping( + &self, + delegation: &Delegation, + parent: LocalDefId, + ) -> Result { // Heuristic: don't do wrapping if there is no target expression. if delegation.body.is_none() { - return None; + return Ok(false); } 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 @@ -244,21 +300,13 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { // 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; + return Ok(false); } - 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) - }) - }) + // After previous check we are sure that `sig_id` and `delegation.id` + // point to the same function. + let id = self.get_resolution_id(delegation.id)?; + Ok(tcx.def_kind(id) == DefKind::AssocFn && tcx.def_kind(tcx.parent(id)) == DefKind::Trait) } } diff --git a/compiler/rustc_ast_lowering/src/diagnostics.rs b/compiler/rustc_ast_lowering/src/diagnostics.rs index 238a16d9b859d..2d559f97ae1e8 100644 --- a/compiler/rustc_ast_lowering/src/diagnostics.rs +++ b/compiler/rustc_ast_lowering/src/diagnostics.rs @@ -569,3 +569,12 @@ pub(crate) struct DelegationInfersMismatch { pub expected: Symbol, pub actual: Symbol, } + +#[derive(Diagnostic)] +#[diag( + "attempted to lower target expression with definitions more than once while mapping argument" +)] +pub(crate) struct DelegationAttemptedBlockWithDefsRelowering { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 35e16ce78a8a8..33ccf6db6ea7e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -100,6 +100,49 @@ pub fn provide(providers: &mut Providers) { providers.lower_to_hir = lower_to_hir; } +#[cfg(debug_assertions)] +pub(crate) mod re_lowering { + use rustc_ast::NodeId; + use rustc_ast::node_id::NodeMap; + use rustc_hir::{self as hir}; + + use crate::LoweringContext; + + #[derive(Debug, Default)] + pub(crate) struct ReloweringChecker { + node_id_to_local_id: NodeMap, + can_relower: bool, + } + + impl ReloweringChecker { + pub(crate) fn assert_node_is_not_relowered( + &mut self, + ast_node_id: NodeId, + local_id: hir::ItemLocalId, + ) { + if !self.can_relower { + let old = self.node_id_to_local_id.insert(ast_node_id, local_id); + assert_eq!(old, None); + } + } + + pub(crate) fn allow_relowering<'a, 'hir, TRes>( + ctx: &mut LoweringContext<'a, 'hir>, + op: impl FnOnce(&mut LoweringContext<'a, 'hir>) -> TRes, + ) -> TRes { + assert!(!ctx.relowering_checker.can_relower, "reentrant relowering is not supported"); + + ctx.relowering_checker.can_relower = true; + + let res = op(ctx); + + ctx.relowering_checker.can_relower = false; + + res + } + } +} + struct LoweringContext<'a, 'hir> { tcx: TyCtxt<'hir>, resolver: &'a ResolverAstLowering<'hir>, @@ -146,7 +189,7 @@ struct LoweringContext<'a, 'hir> { ident_and_label_to_local_id: NodeMap, /// NodeIds that are lowered inside the current HIR owner. Only used for duplicate lowering check. #[cfg(debug_assertions)] - node_id_to_local_id: NodeMap, + relowering_checker: re_lowering::ReloweringChecker, /// The `NodeId` space is split in two. /// `0..resolver.next_node_id` are created by the resolver on the AST. /// The higher part `resolver.next_node_id..next_node_id` are created during lowering. @@ -205,8 +248,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // and we never call `lower_node_id(owner)`. item_local_id_counter: hir::ItemLocalId::new(1), ident_and_label_to_local_id: Default::default(), + #[cfg(debug_assertions)] - node_id_to_local_id: Default::default(), + relowering_checker: Default::default(), + trait_map: Default::default(), next_node_id: resolver.next_node_id, node_id_to_def_id: NodeMap::default(), @@ -808,7 +853,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let current_ident_and_label_to_local_id = mem::take(&mut self.ident_and_label_to_local_id); #[cfg(debug_assertions)] - let current_node_id_to_local_id = mem::take(&mut self.node_id_to_local_id); + let current_relowering_checker = mem::take(&mut self.relowering_checker); let current_trait_map = mem::take(&mut self.trait_map); let current_owner = mem::replace(&mut self.current_hir_id_owner, owner_id); let current_local_counter = @@ -824,10 +869,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Always allocate the first `HirId` for the owner itself. #[cfg(debug_assertions)] - { - let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::ZERO); - debug_assert_eq!(_old, None); - } + self.relowering_checker.assert_node_is_not_relowered(owner, hir::ItemLocalId::ZERO); let item = f(self); assert_eq!(owner_id, item.def_id()); @@ -845,7 +887,7 @@ impl<'hir> LoweringContext<'_, 'hir> { #[cfg(debug_assertions)] { - self.node_id_to_local_id = current_node_id_to_local_id; + self.relowering_checker = current_relowering_checker; } self.trait_map = current_trait_map; self.current_hir_id_owner = current_owner; @@ -936,10 +978,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Check whether the same `NodeId` is lowered more than once. #[cfg(debug_assertions)] - { - let old = self.node_id_to_local_id.insert(ast_node_id, local_id); - assert_eq!(old, None); - } + self.relowering_checker.assert_node_is_not_relowered(ast_node_id, local_id); hir_id } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 254d5fdd5f80c..153212def0731 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3877,7 +3877,7 @@ pub enum DelegationSelfTyPropagationKind { #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] pub struct DelegationInfo { pub call_expr_id: HirId, - pub call_path_res: Option, + pub call_path_res: DefId, /// Id of the child segment in delegation: `reuse Trait::foo`, /// `child_seg_id` points to `foo`. diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index bb822f8d0f68d..c8e54aed87661 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -724,17 +724,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let Some(path_res_id) = info.call_path_res else { return None }; - // Check that delegation has first provided arg and that the call path // resolves to a trait method (inherent methods are not yet supported). if arg_exprs.is_empty() - || !self.tcx.opt_associated_item(path_res_id).is_some_and(|i| i.is_method()) + || !self.tcx.opt_associated_item(info.call_path_res).is_some_and(|i| i.is_method()) { return None; } - Some(ProbeScope::Single(path_res_id)) + Some(ProbeScope::Single(info.call_path_res)) } /// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)` diff --git a/tests/ui/delegation/bad-resolve.rs b/tests/ui/delegation/bad-resolve.rs index 2c2c622e09006..3700b04160292 100644 --- a/tests/ui/delegation/bad-resolve.rs +++ b/tests/ui/delegation/bad-resolve.rs @@ -33,6 +33,7 @@ impl Trait for S { reuse foo { &self.0 } //~^ ERROR cannot find function `foo` in this scope + //~| ERROR: method `foo` has a `&self` declaration in the trait, but not in the impl reuse Trait::foo2 { self.0 } //~^ ERROR cannot find function `foo2` in trait `Trait` //~| ERROR method `foo2` is not a member of trait `Trait` diff --git a/tests/ui/delegation/bad-resolve.stderr b/tests/ui/delegation/bad-resolve.stderr index d1b3974e77081..29460e0ce982f 100644 --- a/tests/ui/delegation/bad-resolve.stderr +++ b/tests/ui/delegation/bad-resolve.stderr @@ -26,7 +26,7 @@ LL | reuse ::baz; | not a member of trait `Trait` error[E0407]: method `foo2` is not a member of trait `Trait` - --> $DIR/bad-resolve.rs:36:5 + --> $DIR/bad-resolve.rs:37:5 | LL | reuse Trait::foo2 { self.0 } | ^^^^^^^^^^^^^----^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | reuse foo { &self.0 } | ^^^ not found in this scope error[E0425]: cannot find function `foo2` in trait `Trait` - --> $DIR/bad-resolve.rs:36:18 + --> $DIR/bad-resolve.rs:37:18 | LL | fn foo(&self, x: i32) -> i32 { x } | ---------------------------- similarly named associated function `foo` defined here @@ -83,11 +83,20 @@ LL + reuse Trait::foo { self.0 } | error[E0423]: expected function, found module `prefix::self` - --> $DIR/bad-resolve.rs:43:7 + --> $DIR/bad-resolve.rs:44:7 | LL | reuse prefix::{self, super, crate}; | ^^^^^^ not a function +error[E0186]: method `foo` has a `&self` declaration in the trait, but not in the impl + --> $DIR/bad-resolve.rs:34:11 + | +LL | fn foo(&self, x: i32) -> i32 { x } + | ---------------------------- `&self` used in trait +... +LL | reuse foo { &self.0 } + | ^^^ expected `&self` in impl + error[E0046]: not all trait items implemented, missing: `Type` --> $DIR/bad-resolve.rs:21:1 | @@ -98,7 +107,7 @@ LL | impl Trait for S { | ^^^^^^^^^^^^^^^^ missing `Type` in implementation error[E0433]: cannot find module or crate `unresolved_prefix` in this scope - --> $DIR/bad-resolve.rs:42:7 + --> $DIR/bad-resolve.rs:43:7 | LL | reuse unresolved_prefix::{a, b, c}; | ^^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved_prefix` @@ -106,12 +115,12 @@ LL | reuse unresolved_prefix::{a, b, c}; = help: you might be missing a crate named `unresolved_prefix` error[E0433]: `crate` in paths can only be used in start position - --> $DIR/bad-resolve.rs:43:29 + --> $DIR/bad-resolve.rs:44:29 | LL | reuse prefix::{self, super, crate}; | ^^^^^ can only be used in path start position -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors -Some errors have detailed explanations: E0046, E0324, E0407, E0423, E0425, E0433, E0575, E0576. +Some errors have detailed explanations: E0046, E0186, E0324, E0407, E0423, E0425, E0433, E0575, E0576. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/delegation/self-mapping-arguments-errors.rs b/tests/ui/delegation/self-mapping-arguments-errors.rs new file mode 100644 index 0000000000000..c7789c69be805 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments-errors.rs @@ -0,0 +1,47 @@ +#![feature(fn_delegation)] + +mod target_expr_doesnt_relower_when_defs_inside { + trait MyAdd { + fn add(self, other: Self) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> usize { self + other } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { + //~^ ERROR: attempted to lower target expression with definitions more than once while mapping argument + //~| ERROR: method `add` has a `self` declaration in the trait, but not in the impl + //~| ERROR: the trait bound `(): target_expr_doesnt_relower_when_defs_inside::MyAdd` is not satisfied + //~| ERROR: this function takes 2 arguments but 1 argument was supplied + println!("{self:?}"); + fn foo() { + println!("hello"); + } + + reuse foo as bar; + bar(); + bar(); + + self.0 + } +} + +mod complex_Self_doesnt_map { + trait MyAdd { + fn add(self, other: Box) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: Box) -> usize { self + *other.as_ref() } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { self.0 } + //~^ ERROR: mismatched types +} + +fn main() {} diff --git a/tests/ui/delegation/self-mapping-arguments-errors.stderr b/tests/ui/delegation/self-mapping-arguments-errors.stderr new file mode 100644 index 0000000000000..6208a1463c443 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments-errors.stderr @@ -0,0 +1,98 @@ +error: attempted to lower target expression with definitions more than once while mapping argument + --> $DIR/self-mapping-arguments-errors.rs:14:5 + | +LL | / reuse impl MyAdd for W { +... | +LL | | self.0 +LL | | } + | |_____^ + +error[E0186]: method `add` has a `self` declaration in the trait, but not in the impl + --> $DIR/self-mapping-arguments-errors.rs:14:5 + | +LL | fn add(self, other: Self) -> Self; + | ---------------------------------- `self` used in trait +... +LL | / reuse impl MyAdd for W { +... | +LL | | self.0 +LL | | } + | |_____^ expected `self` in impl + +error[E0277]: the trait bound `(): target_expr_doesnt_relower_when_defs_inside::MyAdd` is not satisfied + --> $DIR/self-mapping-arguments-errors.rs:14:5 + | +LL | / reuse impl MyAdd for W { +... | +LL | | self.0 +LL | | } + | |_____^ the trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` is not implemented for `()` + | +help: the following other types implement trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` + --> $DIR/self-mapping-arguments-errors.rs:8:5 + | +LL | impl MyAdd for usize { + | ^^^^^^^^^^^^^^^^^^^^ `usize` +... +LL | reuse impl MyAdd for W { + | ^^^^^^^^^^^^^^^^^^^^^^ `target_expr_doesnt_relower_when_defs_inside::W` + +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/self-mapping-arguments-errors.rs:14:5 + | +LL | / reuse impl MyAdd for W { +... | +LL | | self.0 +LL | | } + | | ^ + | | | + | |_____argument #2 of type `()` is missing + | this implicit `()` return type influences the call expression's return type + | +note: method defined here + --> $DIR/self-mapping-arguments-errors.rs:5:12 + | +LL | fn add(self, other: Self) -> Self; + | ^^^ ----- +help: provide the argument + | +LL ~ }({ +LL + +LL + +LL + +LL + +LL + println!("{self:?}"); +LL + fn foo() { +LL + println!("hello"); +LL + } +LL + +LL + reuse foo as bar; +LL + bar(); +LL + bar(); +LL + +LL + self.0 +LL + }, ()) + | + +error[E0308]: mismatched types + --> $DIR/self-mapping-arguments-errors.rs:43:5 + | +LL | reuse impl MyAdd for W { self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected `Box`, found `Box` + | arguments to this function are incorrect + | this return type influences the call expression's return type + | + = note: expected struct `Box` + found struct `Box` +note: method defined here + --> $DIR/self-mapping-arguments-errors.rs:34:12 + | +LL | fn add(self, other: Box) -> Self; + | ^^^ ----- + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0061, E0186, E0277, E0308. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/delegation/self-mapping-arguments.rs b/tests/ui/delegation/self-mapping-arguments.rs new file mode 100644 index 0000000000000..f1b5279927af7 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments.rs @@ -0,0 +1,59 @@ +//@ run-pass +//@ check-run-results + +#![feature(fn_delegation)] + + +mod default_test { + trait MyAdd { + fn add(self, other: Self) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> usize { self + other } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { + println!("{self:?}"); + let _x = 213; + + self.0 + } + + pub fn check() { + assert_eq!(W(1).add(W(2)), W(3)) + } +} + +mod arguments_mapping_works_without_return_self { + trait MyAdd { + fn add(self, other: Self); + } + + impl MyAdd for usize { + fn add(self, other: usize) { + let result = self + other; + println!("{result}"); + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { + println!("{self:?}"); + let _x = 213; + + self.0 + } + + pub fn check() { + W(2).add(W(10)); + } +} + +fn main() { + default_test::check(); + arguments_mapping_works_without_return_self::check(); +} diff --git a/tests/ui/delegation/self-mapping-arguments.run.stdout b/tests/ui/delegation/self-mapping-arguments.run.stdout new file mode 100644 index 0000000000000..d8177420647a8 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments.run.stdout @@ -0,0 +1,5 @@ +W(1) +W(2) +W(2) +W(10) +12