3737//! also be emitted during HIR ty lowering.
3838
3939use std:: iter;
40+ use std:: ops:: ControlFlow ;
4041
4142use ast:: visit:: Visitor ;
4243use hir:: def:: { DefKind , Res } ;
4344use hir:: { BodyId , HirId } ;
4445use rustc_abi:: ExternAbi ;
4546use rustc_ast as ast;
47+ use rustc_ast:: node_id:: NodeMap ;
4648use rustc_ast:: * ;
4749use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
4850use rustc_hir:: attrs:: { AttributeKind , InlineAttr } ;
4951use rustc_hir:: def_id:: { DefId , LocalDefId } ;
5052use rustc_hir:: { self as hir, FnDeclFlags } ;
5153use rustc_middle:: span_bug;
52- use rustc_middle:: ty:: { Asyncness , TyCtxt } ;
54+ use rustc_middle:: ty:: { Asyncness , PerOwnerResolverData , TyCtxt } ;
5355use rustc_span:: symbol:: kw;
5456use rustc_span:: { ErrorGuaranteed , Ident , Span , Symbol } ;
5557
5658use crate :: delegation:: generics:: { GenericsGenerationResult , GenericsGenerationResults } ;
57- use crate :: errors:: { CycleInDelegationSignatureResolution , UnresolvedDelegationCallee } ;
59+ use crate :: errors:: {
60+ CycleInDelegationSignatureResolution , DelegationAttemptedBlockWithDefsDeletion ,
61+ DelegationBlockSpecifiedWhenNoParams , UnresolvedDelegationCallee ,
62+ } ;
5863use crate :: {
5964 AllowReturnTypeNotation , ImplTraitContext , ImplTraitPosition , LoweringContext , ParamMode ,
6065 ResolverAstLoweringExt , index_crate,
@@ -198,10 +203,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
198203
199204 let ( param_count, c_variadic) = self . param_count ( sig_id) ;
200205
206+ if !self . check_block_soundness ( delegation, sig_id, is_method, param_count) {
207+ return self . generate_delegation_error ( span, delegation) ;
208+ }
209+
201210 let mut generics = self . uplift_delegation_generics ( delegation, sig_id, is_method) ;
202211
203212 let ( body_id, call_expr_id) =
204- self . lower_delegation_body ( delegation, is_method , param_count, & mut generics, span) ;
213+ self . lower_delegation_body ( delegation, sig_id , param_count, & mut generics, span) ;
205214
206215 let decl = self . lower_delegation_decl (
207216 sig_id,
@@ -227,6 +236,82 @@ impl<'hir> LoweringContext<'_, 'hir> {
227236 DelegationResults { body_id, sig, ident, generics }
228237 }
229238
239+ fn check_block_soundness (
240+ & self ,
241+ delegation : & Delegation ,
242+ sig_id : DefId ,
243+ is_method : bool ,
244+ param_count : usize ,
245+ ) -> bool {
246+ let Some ( block) = delegation. body . as_ref ( ) else { return true } ;
247+ let should_generate_block = self . should_generate_block ( delegation, sig_id, is_method) ;
248+
249+ // Report an error if user has explicitly specified delegation's target expression
250+ // in a single delegation when reused function has no params.
251+ if param_count == 0 && should_generate_block {
252+ self . dcx ( ) . emit_err ( DelegationBlockSpecifiedWhenNoParams { span : block. span } ) ;
253+ return false ;
254+ }
255+
256+ struct DefinitionsFinder < ' a > {
257+ all_owners : & ' a NodeMap < PerOwnerResolverData < ' a > > ,
258+ // `self.owner.node_id_to_def_id`
259+ nested_def_ids : & ' a NodeMap < LocalDefId > ,
260+ }
261+
262+ impl < ' a > ast:: visit:: Visitor < ' a > for DefinitionsFinder < ' a > {
263+ type Result = ControlFlow < ( ) > ;
264+
265+ fn visit_id ( & mut self , id : NodeId ) -> Self :: Result {
266+ /*
267+ (from `tests\ui\delegation\target-expr-removal-defs-inside.rs`):
268+ ```rust
269+ reuse impl Trait for S1 {
270+ some::path::<{ fn foo() {} }>::xd();
271+ fn foo() {}
272+ self.0
273+ }
274+ ```
275+
276+ Constant from unresolved path will be in `nested_owners`,
277+ `fn foo() {}` will not be in `nested_owners` but will be in `owners`,
278+ both have `LocalDefId`, so we check those two maps.
279+ */
280+ match self . all_owners . contains_key ( & id) || self . nested_def_ids . contains_key ( & id) {
281+ true => ControlFlow :: Break ( ( ) ) ,
282+ false => ControlFlow :: Continue ( ( ) ) ,
283+ }
284+ }
285+ }
286+
287+ let mut collector = DefinitionsFinder {
288+ all_owners : & self . resolver . owners ,
289+ nested_def_ids : & self . owner . node_id_to_def_id ,
290+ } ;
291+
292+ let contains_defs = collector. visit_block ( block) . is_break ( ) ;
293+
294+ // If there are definitions inside and we can't delete target expression, so report an error.
295+ // FIXME(fn_delegation): support deletion of target expression with defs inside.
296+ if !should_generate_block && contains_defs {
297+ self . dcx ( ) . emit_err ( DelegationAttemptedBlockWithDefsDeletion { span : block. span } ) ;
298+ return false ;
299+ }
300+
301+ true
302+ }
303+
304+ fn should_generate_block (
305+ & self ,
306+ delegation : & Delegation ,
307+ sig_id : DefId ,
308+ is_method : bool ,
309+ ) -> bool {
310+ is_method
311+ || matches ! ( self . tcx. def_kind( sig_id) , DefKind :: Fn )
312+ || matches ! ( delegation. source, DelegationSource :: Single )
313+ }
314+
230315 fn add_attrs_if_needed ( & mut self , span : Span , sig_id : DefId ) {
231316 let new_attrs =
232317 self . create_new_attrs ( ATTRS_ADDITIONS , span, sig_id, self . attrs . get ( & PARENT_ID ) ) ;
@@ -415,7 +500,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
415500 fn lower_delegation_body (
416501 & mut self ,
417502 delegation : & Delegation ,
418- is_method : bool ,
503+ sig_id : DefId ,
419504 param_count : usize ,
420505 generics : & mut GenericsGenerationResults < ' hir > ,
421506 span : Span ,
@@ -428,6 +513,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
428513 let mut args: Vec < hir:: Expr < ' _ > > = Vec :: with_capacity ( param_count) ;
429514 let mut stmts: & [ hir:: Stmt < ' hir > ] = & [ ] ;
430515
516+ let is_method = this. is_method ( sig_id, span) ;
517+
431518 for idx in 0 ..param_count {
432519 let ( param, pat_node_id) = this. generate_param ( is_method, idx, span) ;
433520 parameters. push ( param) ;
@@ -437,6 +524,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
437524
438525 let arg = if let Some ( block) = block
439526 && idx == 0
527+ && this. should_generate_block ( delegation, sig_id, is_method)
440528 {
441529 let mut self_resolver = SelfResolver {
442530 ctxt : this,
@@ -467,17 +555,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
467555 args. push ( arg) ;
468556 }
469557
470- // If we have no params in signature function but user still wrote some code in
471- // delegation body, then add this code as first arg, eventually an error will be shown,
472- // also nested delegations may need to access information about this code (#154332),
473- // so it is better to leave this code as opposed to bodies of extern functions,
474- // which are completely erased from existence.
475- if param_count == 0
476- && let Some ( block) = block
477- {
478- args. push ( this. lower_block_expr ( & block) ) ;
479- }
480-
481558 let ( final_expr, hir_id) =
482559 this. finalize_body_lowering ( delegation, stmts, args, generics, span) ;
483560
0 commit comments