Skip to content

Commit 417ae70

Browse files
Rollup merge of rust-lang#157248 - aerooneqq:delegation-statements-out-of-first-arg, r=petrochenkov
delegation: move statements out of the first arg This PR moves statements that precede final block expression out of the first argument, so we can apply adjustments to final block expression in more cases. This changes behavior a bit, for example if we specify an empty block: ```rust fn b<C>(e: C) {} reuse b {} // Desugaring: fn b<C>(arg0: _) -> _ { b::<C>(arg0) } ``` So we generated the first arg and block is no-op. In general scenario: ```rust trait Trait: Sized { //~ WARN trait `Trait` is never used fn by_value(self, x: i32) -> i32 { x } fn by_ref(&self, x: i32) -> i32 { x } fn by_mut_ref(&mut self, x: i32) -> i32 { x } } struct F; //~ WARN struct `F` is never constructed impl Trait for F {} struct S(F); //~ WARN struct `S` is never constructed impl Trait for S { reuse <F as Trait>::* { println!(); // Final expression is adjusted even if it is in a block. self.0 } } // Desugaring: #[attr = Inline(Hint)] fn by_value(self: _, arg1: _) -> _ { // Final expression is adjusted even if it is in a block. { ::std::io::_print(format_arguments::from_str("\n")); }; <F as Trait>::by_value(self.0, arg1) } #[attr = Inline(Hint)] fn by_ref(self: _, arg1: _) -> _ { { ::std::io::_print(format_arguments::from_str("\n")); }; <F as Trait>::by_ref(self.0, arg1) } #[attr = Inline(Hint)] fn by_mut_ref(self: _, arg1: _) -> _ { { ::std::io::_print(format_arguments::from_str("\n")); }; <F as Trait>::by_mut_ref(self.0, arg1) } ``` If we change target expression to ```rust reuse <F as Trait>::* { println!(); let x = 1; } // Desugaring: #[attr = Inline(Hint)] fn by_value(self: _, arg1: _) -> _ { { ::std::io::_print(format_arguments::from_str("\n")); }; let x = 1; <F as Trait>::by_value(self, arg1) } ``` Once again we generated default arguments for function and block is no-op now (meaning it does not affect callee arguments). Part of rust-lang#118212. r? @petrochenkov
2 parents 03e3b67 + 28cfce7 commit 417ae70

15 files changed

Lines changed: 546 additions & 222 deletions

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
408408
let block_id = self.lower_body(|this| {
409409
let mut parameters: Vec<hir::Param<'_>> = Vec::with_capacity(param_count);
410410
let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);
411+
let mut stmts: &[hir::Stmt<'hir>] = &[];
411412

412413
for idx in 0..param_count {
413414
let (param, pat_node_id) = this.generate_param(is_method, idx, span);
414415
parameters.push(param);
415416

417+
let generate_arg =
418+
|this: &mut Self| this.generate_arg(is_method, idx, param.pat.hir_id, span);
419+
416420
let arg = if let Some(block) = block
417421
&& idx == 0
418422
{
@@ -424,10 +428,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
424428
self_resolver.visit_block(block);
425429
// Target expr needs to lower `self` path.
426430
this.ident_and_label_to_local_id.insert(pat_node_id, param.pat.hir_id.local_id);
427-
this.lower_target_expr(&block)
431+
432+
// Lower with `HirId::INVALID` as we will use only expr and stmts.
433+
// FIXME(fn_delegation): Alternatives for target expression lowering:
434+
// https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600.
435+
let block = this.lower_block_noalloc(HirId::INVALID, block, false);
436+
437+
stmts = block.stmts;
438+
439+
// The behavior of the delegation's target expression differs from the
440+
// behavior of the usual block, where if there is no final expression
441+
// the `()` is returned. In case of the similar situation in delegation
442+
// (no final expression) we propagate first argument instead of replacing
443+
// it with `()`.
444+
if let Some(&expr) = block.expr { expr } else { generate_arg(this) }
428445
} else {
429-
this.generate_arg(is_method, idx, param.pat.hir_id, span)
446+
generate_arg(this)
430447
};
448+
431449
args.push(arg);
432450
}
433451

@@ -439,11 +457,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
439457
if param_count == 0
440458
&& let Some(block) = block
441459
{
442-
args.push(this.lower_target_expr(&block));
460+
args.push(this.lower_block_expr(&block));
443461
}
444462

445463
let (final_expr, hir_id) =
446-
this.finalize_body_lowering(delegation, args, generics, span);
464+
this.finalize_body_lowering(delegation, stmts, args, generics, span);
447465

448466
call_expr_id = hir_id;
449467

@@ -455,22 +473,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
455473
(block_id, call_expr_id)
456474
}
457475

458-
// FIXME(fn_delegation): Alternatives for target expression lowering:
459-
// https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600.
460-
fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> {
461-
if let [stmt] = block.stmts.as_slice()
462-
&& let StmtKind::Expr(expr) = &stmt.kind
463-
{
464-
return self.lower_expr_mut(expr);
465-
}
466-
467-
let block = self.lower_block(block, false);
468-
self.mk_expr(hir::ExprKind::Block(block, None), block.span)
469-
}
470-
471476
fn finalize_body_lowering(
472477
&mut self,
473478
delegation: &Delegation,
479+
stmts: &'hir [hir::Stmt<'hir>],
474480
args: Vec<hir::Expr<'hir>>,
475481
generics: &mut GenericsGenerationResults<'hir>,
476482
span: Span,
@@ -522,7 +528,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
522528
let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span));
523529

524530
let block = self.arena.alloc(hir::Block {
525-
stmts: &[],
531+
stmts,
526532
expr: Some(call),
527533
hir_id: self.next_id(),
528534
rules: hir::BlockCheckMode::DefaultBlock,
@@ -587,7 +593,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
587593

588594
let callee_path = this.arena.alloc(this.mk_expr(hir::ExprKind::Path(path), span));
589595
let args = if let Some(block) = delegation.body.as_ref() {
590-
this.arena.alloc_slice(&[this.lower_target_expr(block)])
596+
this.arena.alloc_slice(&[this.lower_block_expr(block)])
591597
} else {
592598
&mut []
593599
};

tests/pretty/delegation-inline-attribute.pp

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,34 +38,32 @@
3838
fn foo(self: _)
3939
->
4040
_ {
41-
Trait::foo(
42-
// Check that #[inline(hint)] is added to foo0 reuse inside another reuse
43-
44-
// Check that #[inline(hint)] is added when other attributes present in inner reuse
45-
46-
// Check that #[inline(never)] is preserved in inner reuse
47-
48-
// Check that #[inline(always)] is preserved in inner reuse
49-
50-
// Check that #[inline(never)] is preserved when there are other attributes in inner reuse
51-
{
52-
#[attr = Inline(Hint)]
53-
fn foo0(arg0: _) -> _ { to_reuse::foo(self + 1) }
54-
#[attr = Cold]
55-
#[attr = MustUse]
56-
#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}]
57-
#[attr = Inline(Hint)]
58-
fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) }
59-
#[attr = Inline(Never)]
60-
fn foo2(arg0: _) -> _ { to_reuse::foo(self / 2) }
61-
#[attr = Inline(Always)]
62-
fn foo3(arg0: _) -> _ { to_reuse::foo(self / 2) }
63-
#[attr = Cold]
64-
#[attr = MustUse]
65-
#[attr = Inline(Never)]
66-
#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}]
67-
fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) }
68-
})
41+
// Check that #[inline(hint)] is added to foo0 reuse inside another reuse
42+
#[attr = Inline(Hint)]
43+
fn foo0(arg0: _) -> _ { to_reuse::foo(self + 1) }
44+
45+
// Check that #[inline(hint)] is added when other attributes present in inner reuse
46+
#[attr = Cold]
47+
#[attr = MustUse]
48+
#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}]
49+
#[attr = Inline(Hint)]
50+
fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) }
51+
52+
// Check that #[inline(never)] is preserved in inner reuse
53+
#[attr = Inline(Never)]
54+
fn foo2(arg0: _) -> _ { to_reuse::foo(self / 2) }
55+
56+
// Check that #[inline(always)] is preserved in inner reuse
57+
#[attr = Inline(Always)]
58+
fn foo3(arg0: _) -> _ { to_reuse::foo(self / 2) }
59+
60+
// Check that #[inline(never)] is preserved when there are other attributes in inner reuse
61+
#[attr = Cold]
62+
#[attr = MustUse]
63+
#[attr = Inline(Never)]
64+
#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}]
65+
fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) }
66+
Trait::foo(self)
6967
}
7068

7169
// Check that #[inline(hint)] is added when there are other attributes present in trait reuse

tests/pretty/hir-delegation.pp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
trait G {
1414
#[attr = Inline(Hint)]
15-
fn b<C>(arg0: _) -> _ { b::<C>({ }) }
15+
fn b<C>(arg0: _) -> _ { b::<C>(arg0) }
1616
}
1717

1818
mod m {

tests/ui/delegation/ice-issue-124347.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied
1414
--> $DIR/ice-issue-124347.rs:4:18
1515
|
1616
LL | reuse Trait::foo { &self.0 }
17-
| ^^^ ------- unexpected argument
17+
| ^^^ ----------- unexpected argument
1818
|
1919
note: associated function defined here
2020
--> $DIR/ice-issue-124347.rs:4:18
@@ -24,7 +24,7 @@ LL | reuse Trait::foo { &self.0 }
2424
help: remove the extra argument
2525
|
2626
LL - reuse Trait::foo { &self.0 }
27-
LL + reuse Trait::fo&self.0 }
27+
LL + reuse Trait::fo{ &self.0 }
2828
|
2929

3030
warning: function cannot return without recursing

tests/ui/delegation/inner-attr.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied
1313
--> $DIR/inner-attr.rs:5:7
1414
|
1515
LL | reuse a as b { #![rustc_dummy] self }
16-
| ^ ---- unexpected argument
16+
| ^ ------------------------ unexpected argument
1717
|
1818
note: function defined here
1919
--> $DIR/inner-attr.rs:3:4
@@ -23,7 +23,7 @@ LL | fn a() {}
2323
help: remove the extra argument
2424
|
2525
LL - reuse a as b { #![rustc_dummy] self }
26-
LL + reuse self }
26+
LL + reuse { #![rustc_dummy] self }
2727
|
2828

2929
error: aborting due to 2 previous errors
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Test different scenarios with impossible adjustments due to blocks
2+
// or no-op target expressions.
3+
4+
#![feature(fn_delegation)]
5+
6+
trait Trait: Sized {
7+
fn by_value(self) -> i32 { 1 }
8+
fn by_mut_ref(&mut self) -> i32 { 2 }
9+
fn by_ref(&self) -> i32 { 3 }
10+
}
11+
12+
struct F;
13+
impl Trait for F {}
14+
15+
struct Struct(F);
16+
reuse impl Trait for Struct { self.0 }
17+
18+
struct S(F);
19+
reuse impl Trait for S { { self.0 } }
20+
//~^ ERROR: cannot move out of `self` which is behind a shared reference
21+
//~| ERROR: cannot move out of `self` which is behind a mutable reference
22+
23+
struct S1(F);
24+
reuse impl Trait for S1 { { { { { { self.0 } } } } } }
25+
//~^ ERROR: cannot move out of `self` which is behind a shared reference
26+
//~| ERROR: cannot move out of `self` which is behind a mutable reference
27+
28+
struct S2(F);
29+
reuse impl Trait for S2 { }
30+
//~^ WARN: function cannot return without recursing [unconditional_recursion]
31+
//~| WARN: function cannot return without recursing [unconditional_recursion]
32+
//~| WARN: function cannot return without recursing [unconditional_recursion]
33+
34+
struct S3(F);
35+
reuse impl Trait for S3 { (); }
36+
//~^ WARN: function cannot return without recursing [unconditional_recursion]
37+
//~| WARN: function cannot return without recursing [unconditional_recursion]
38+
//~| WARN: function cannot return without recursing [unconditional_recursion]
39+
40+
struct S4(F);
41+
reuse impl Trait for S4 { println!(); }
42+
//~^ WARN: function cannot return without recursing [unconditional_recursion]
43+
//~| WARN: function cannot return without recursing [unconditional_recursion]
44+
//~| WARN: function cannot return without recursing [unconditional_recursion]
45+
46+
struct S5(F);
47+
reuse impl Trait for S5 { fn foo() {} }
48+
//~^ WARN: function cannot return without recursing [unconditional_recursion]
49+
//~| WARN: function cannot return without recursing [unconditional_recursion]
50+
//~| WARN: function cannot return without recursing [unconditional_recursion]
51+
52+
struct S6(F);
53+
reuse impl Trait for S6;
54+
//~^ WARN: function cannot return without recursing [unconditional_recursion]
55+
//~| WARN: function cannot return without recursing [unconditional_recursion]
56+
//~| WARN: function cannot return without recursing [unconditional_recursion]
57+
58+
fn main() {}

0 commit comments

Comments
 (0)