Skip to content

Commit a8ed992

Browse files
authored
Unrolled build for #156798
Rollup merge of #156798 - aerooneqq:delegation-dont-generate-first-arg, r=petrochenkov delegation: do not always generate first argument This PR supports not generating delegation's "dead code" - situation when there is no parameters in a source function or we expanded a reuse of a static function without self, in this case we do not have to generate delegation's block even if the user has explicitly specified it. We generate block: - If there is a single delegation and source function has first param, otherwise error is emitted, - If there is a glob delegation we generate block if the source function has first param and it is method or free function. Fixes #154427. Part of #118212. r? @petrochenkov
2 parents ac6f3a3 + 31e3588 commit a8ed992

26 files changed

Lines changed: 1548 additions & 79 deletions

compiler/rustc_ast/src/ast.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3908,6 +3908,13 @@ pub struct EiiImpl {
39083908
pub is_default: bool,
39093909
}
39103910

3911+
#[derive(Clone, Encodable, Decodable, Debug, Walkable, PartialEq, Eq)]
3912+
pub enum DelegationSource {
3913+
Single,
3914+
List,
3915+
Glob,
3916+
}
3917+
39113918
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
39123919
pub struct Delegation {
39133920
/// Path resolution id.
@@ -3918,7 +3925,7 @@ pub struct Delegation {
39183925
pub rename: Option<Ident>,
39193926
pub body: Option<Box<Block>>,
39203927
/// The item was expanded from a glob delegation item.
3921-
pub from_glob: bool,
3928+
pub source: DelegationSource,
39223929
}
39233930

39243931
impl Delegation {

compiler/rustc_ast/src/visit.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ macro_rules! common_visitor_and_walkers {
431431
Delegation,
432432
DelegationMac,
433433
DelegationSuffixes,
434+
DelegationSource,
434435
DelimArgs,
435436
DelimSpan,
436437
EnumDef,

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,29 @@
3737
//! also be emitted during HIR ty lowering.
3838
3939
use std::iter;
40+
use std::ops::ControlFlow;
4041

4142
use ast::visit::Visitor;
4243
use hir::def::{DefKind, Res};
4344
use hir::{BodyId, HirId};
4445
use rustc_abi::ExternAbi;
4546
use rustc_ast as ast;
47+
use rustc_ast::node_id::NodeMap;
4648
use rustc_ast::*;
4749
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
4850
use rustc_hir::attrs::{AttributeKind, InlineAttr};
4951
use rustc_hir::def_id::{DefId, LocalDefId};
5052
use rustc_hir::{self as hir, FnDeclFlags};
5153
use rustc_middle::span_bug;
52-
use rustc_middle::ty::{Asyncness, TyCtxt};
54+
use rustc_middle::ty::{Asyncness, PerOwnerResolverData, TyCtxt};
5355
use rustc_span::symbol::kw;
5456
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
5557

5658
use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults};
57-
use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee};
59+
use crate::errors::{
60+
CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion,
61+
DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee,
62+
};
5863
use 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

compiler/rustc_ast_lowering/src/errors.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,3 +535,17 @@ pub(crate) struct CycleInDelegationSignatureResolution {
535535
#[primary_span]
536536
pub span: Span,
537537
}
538+
539+
#[derive(Diagnostic)]
540+
#[diag("delegation's target expression is specified for function with no params")]
541+
pub(crate) struct DelegationBlockSpecifiedWhenNoParams {
542+
#[primary_span]
543+
pub span: Span,
544+
}
545+
546+
#[derive(Diagnostic)]
547+
#[diag("attempted to delete delegation's target expression that contains definitions inside")]
548+
pub(crate) struct DelegationAttemptedBlockWithDefsDeletion {
549+
#[primary_span]
550+
pub span: Span,
551+
}

compiler/rustc_expand/src/expand.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2066,7 +2066,11 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>(
20662066
ident: rename.unwrap_or(ident),
20672067
rename,
20682068
body: deleg.body.clone(),
2069-
from_glob,
2069+
source: if from_glob {
2070+
ast::DelegationSource::Glob
2071+
} else {
2072+
ast::DelegationSource::List
2073+
},
20702074
})),
20712075
tokens: None,
20722076
}

compiler/rustc_parse/src/parser/item.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,7 @@ impl<'a> Parser<'a> {
909909
ident,
910910
rename,
911911
body: self.parse_delegation_body()?,
912-
from_glob: false,
912+
source: DelegationSource::Single,
913913
}))
914914
})
915915
}

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use std::sync::Arc;
99

1010
use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
1111
use rustc_ast::{
12-
self as ast, AssocItem, AssocItemKind, Block, ConstItem, DUMMY_NODE_ID, Delegation, Fn,
13-
ForeignItem, ForeignItemKind, Inline, Item, ItemKind, NodeId, StaticItem, StmtKind, TraitAlias,
14-
TyAlias,
12+
self as ast, AssocItem, AssocItemKind, Block, ConstItem, DUMMY_NODE_ID, Delegation,
13+
DelegationSource, Fn, ForeignItem, ForeignItemKind, Inline, Item, ItemKind, NodeId, StaticItem,
14+
StmtKind, TraitAlias, TyAlias,
1515
};
1616
use rustc_attr_parsing::AttributeParser;
1717
use rustc_expand::base::{ResolverExpand, SyntaxExtension, SyntaxExtensionKind};
@@ -1461,7 +1461,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
14611461
let parent = self.parent_scope.module.expect_local();
14621462
let expansion = self.parent_scope.expansion;
14631463
self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
1464-
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob)
1464+
} else if !matches!(&item.kind, AssocItemKind::Delegation(d) if d.source == DelegationSource::Glob)
14651465
&& ident.name != kw::Underscore
14661466
{
14671467
// Don't add underscore names, they cannot be looked up anyway.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#![feature(fn_delegation)]
2+
3+
trait Trait: Sized {
4+
fn value(self) {}
5+
fn r#ref(&self) {}
6+
fn mut_ref(&mut self) {}
7+
8+
fn static_empty() {}
9+
fn static_one_param(x: usize) {}
10+
}
11+
12+
struct S;
13+
impl Trait for S {}
14+
15+
struct F(S);
16+
// In glob delegations silently remove first arg if no params or generate default
17+
// first arg (`arg0`) if it is a static function.
18+
reuse impl Trait for F { self.0 }
19+
//~^ ERROR: type annotations needed
20+
//~| ERROR: type annotations needed
21+
22+
struct F1(S);
23+
impl F1 {
24+
reuse Trait::{value, r#ref, mut_ref} { self.0 }
25+
26+
// Error is reported as user has explicitly specified block when no params.
27+
reuse <S as Trait>::static_empty { self.0 }
28+
//~^ ERROR: delegation's target expression is specified for function with no params
29+
//~| ERROR: this function takes 0 arguments but 1 argument was supplied
30+
31+
reuse <S as Trait>::static_one_param { self.0 }
32+
//~^ ERROR: `usize` is a primitive type and therefore doesn't have fields
33+
}
34+
35+
struct F2(S);
36+
impl F2 {
37+
// In list delegations silently remove first arg if it is not a method.
38+
reuse <S as Trait>::{value, r#ref, mut_ref, static_empty, static_one_param} { self.0 }
39+
}
40+
41+
mod trait_to_reuse {
42+
use super::Trait;
43+
44+
pub fn value(_: impl Trait) {}
45+
pub fn r#ref(_: &impl Trait) {}
46+
pub fn mut_ref(_: &mut impl Trait) {}
47+
48+
pub fn static_empty() {}
49+
pub fn static_one_param(x: usize) {}
50+
}
51+
52+
struct F3(S);
53+
impl Trait for F3 {
54+
reuse trait_to_reuse::{value, r#ref, mut_ref, static_empty, static_one_param} { self.0 }
55+
//~^ ERROR: mismatched types
56+
//~| ERROR: mismatched types
57+
}
58+
59+
struct F4(S);
60+
impl F4 {
61+
reuse trait_to_reuse::{value, r#ref, mut_ref, static_empty, static_one_param} { self.0 }
62+
//~^ ERROR: no field `0` on type `impl Trait`
63+
//~| ERROR: no field `0` on type `&impl Trait`
64+
//~| ERROR: no field `0` on type `&mut impl Trait`
65+
//~| ERROR: `usize` is a primitive type and therefore doesn't have fields
66+
//~| ERROR: this function takes 0 arguments but 1 argument was supplied
67+
//~| ERROR: delegation's target expression is specified for function with no params
68+
}
69+
70+
mod to_reuse {
71+
pub fn empty() {}
72+
pub fn one_param(x: usize) {}
73+
}
74+
75+
// Error is reported as user has explicitly specified block when no params.
76+
reuse to_reuse::empty { self + 1 }
77+
//~^ ERROR: delegation's target expression is specified for function with no params
78+
//~| ERROR: this function takes 0 arguments but 1 argument was supplied
79+
80+
reuse to_reuse::one_param { self + 1 }
81+
82+
reuse to_reuse::{empty as empty1, one_param as one_param1} { self + 1 }
83+
//~^ ERROR: this function takes 0 arguments but 1 argument was supplied
84+
//~| ERROR: delegation's target expression is specified for function with no params
85+
86+
fn main() {}

0 commit comments

Comments
 (0)