Skip to content

Commit 3bf7e84

Browse files
committed
introduce terminating scope in tail expressions of breakable scopes
1 parent 1429899 commit 3bf7e84

File tree

6 files changed

+236
-0
lines changed

6 files changed

+236
-0
lines changed

compiler/rustc_hir_analysis/src/check/region.rs

+7
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
166166
hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
167167
}
168168
}
169+
170+
// We must introduce a terminating scope for tail expressions in breakable
171+
// scopes, otherwise incorrect drops and unwind paths are created, see #104736.
172+
if blk.targeted_by_break && let Some(expr) = blk.expr {
173+
visitor.terminating_scopes.insert(expr.hir_id.local_id);
174+
}
175+
169176
walk_list!(visitor, visit_expr, &blk.expr);
170177
}
171178

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// EMIT_MIR breakable_scope_drops.weird_temporary.built.after.mir
2+
3+
struct A;
4+
struct B;
5+
6+
impl Drop for A {
7+
fn drop(&mut self) {}
8+
}
9+
impl Drop for B {
10+
fn drop(&mut self) {}
11+
}
12+
13+
#[inline(always)]
14+
fn no_unwind() {}
15+
16+
17+
fn weird_temporary(a: A, b: B, nothing: ((), (), ()), x: bool) -> ((), (), ()) {
18+
let _temp = 'scope: {
19+
(
20+
{
21+
let _z = b;
22+
if x {
23+
break 'scope nothing;
24+
}
25+
},
26+
match { a } {
27+
_ => (),
28+
},
29+
no_unwind(),
30+
)
31+
};
32+
33+
nothing
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// MIR for `weird_temporary` after built
2+
3+
fn weird_temporary(_1: A, _2: B, _3: ((), (), ()), _4: bool) -> ((), (), ()) {
4+
debug a => _1; // in scope 0 at $DIR/breakable-scope-drops.rs:+0:20: +0:21
5+
debug b => _2; // in scope 0 at $DIR/breakable-scope-drops.rs:+0:26: +0:27
6+
debug nothing => _3; // in scope 0 at $DIR/breakable-scope-drops.rs:+0:32: +0:39
7+
debug x => _4; // in scope 0 at $DIR/breakable-scope-drops.rs:+0:55: +0:56
8+
let mut _0: ((), (), ()); // return place in scope 0 at $DIR/breakable-scope-drops.rs:+0:67: +0:79
9+
let _5: ((), (), ()); // in scope 0 at $DIR/breakable-scope-drops.rs:+1:9: +1:14
10+
let mut _6: (); // in scope 0 at $DIR/breakable-scope-drops.rs:+3:13: +8:14
11+
let _7: B; // in scope 0 at $DIR/breakable-scope-drops.rs:+4:21: +4:23
12+
let mut _8: bool; // in scope 0 at $DIR/breakable-scope-drops.rs:+5:20: +5:21
13+
let mut _9: !; // in scope 0 at $DIR/breakable-scope-drops.rs:+5:22: +7:18
14+
let mut _10: (); // in scope 0 at $DIR/breakable-scope-drops.rs:+9:13: +11:14
15+
let mut _11: A; // in scope 0 at $DIR/breakable-scope-drops.rs:+9:19: +9:24
16+
let mut _12: (); // in scope 0 at $DIR/breakable-scope-drops.rs:+12:13: +12:24
17+
scope 1 {
18+
debug _temp => _5; // in scope 1 at $DIR/breakable-scope-drops.rs:+1:9: +1:14
19+
}
20+
scope 2 {
21+
debug _z => _7; // in scope 2 at $DIR/breakable-scope-drops.rs:+4:21: +4:23
22+
}
23+
24+
bb0: {
25+
StorageLive(_5); // scope 0 at $DIR/breakable-scope-drops.rs:+1:9: +1:14
26+
StorageLive(_6); // scope 0 at $DIR/breakable-scope-drops.rs:+3:13: +8:14
27+
StorageLive(_7); // scope 0 at $DIR/breakable-scope-drops.rs:+4:21: +4:23
28+
_7 = move _2; // scope 0 at $DIR/breakable-scope-drops.rs:+4:26: +4:27
29+
FakeRead(ForLet(None), _7); // scope 0 at $DIR/breakable-scope-drops.rs:+4:21: +4:23
30+
StorageLive(_8); // scope 2 at $DIR/breakable-scope-drops.rs:+5:20: +5:21
31+
_8 = _4; // scope 2 at $DIR/breakable-scope-drops.rs:+5:20: +5:21
32+
switchInt(move _8) -> [0: bb2, otherwise: bb1]; // scope 2 at $DIR/breakable-scope-drops.rs:+5:20: +5:21
33+
}
34+
35+
bb1: {
36+
_5 = _3; // scope 2 at $DIR/breakable-scope-drops.rs:+6:34: +6:41
37+
goto -> bb11; // scope 2 at $DIR/breakable-scope-drops.rs:+6:21: +6:41
38+
}
39+
40+
bb2: {
41+
goto -> bb5; // scope 2 at $DIR/breakable-scope-drops.rs:+5:20: +5:21
42+
}
43+
44+
bb3: {
45+
unreachable; // scope 2 at $DIR/breakable-scope-drops.rs:+5:22: +7:18
46+
}
47+
48+
bb4: {
49+
goto -> bb6; // scope 2 at $DIR/breakable-scope-drops.rs:+5:17: +7:18
50+
}
51+
52+
bb5: {
53+
_6 = const (); // scope 2 at $DIR/breakable-scope-drops.rs:+7:18: +7:18
54+
goto -> bb6; // scope 2 at $DIR/breakable-scope-drops.rs:+5:17: +7:18
55+
}
56+
57+
bb6: {
58+
StorageDead(_8); // scope 2 at $DIR/breakable-scope-drops.rs:+7:17: +7:18
59+
drop(_7) -> [return: bb7, unwind: bb17]; // scope 0 at $DIR/breakable-scope-drops.rs:+8:13: +8:14
60+
}
61+
62+
bb7: {
63+
StorageDead(_7); // scope 0 at $DIR/breakable-scope-drops.rs:+8:13: +8:14
64+
StorageLive(_10); // scope 0 at $DIR/breakable-scope-drops.rs:+9:13: +11:14
65+
StorageLive(_11); // scope 0 at $DIR/breakable-scope-drops.rs:+9:19: +9:24
66+
_11 = move _1; // scope 0 at $DIR/breakable-scope-drops.rs:+9:21: +9:22
67+
FakeRead(ForMatchedPlace(None), _11); // scope 0 at $DIR/breakable-scope-drops.rs:+9:19: +9:24
68+
_10 = (); // scope 0 at $DIR/breakable-scope-drops.rs:+10:22: +10:24
69+
goto -> bb8; // scope 0 at $DIR/breakable-scope-drops.rs:+10:22: +10:24
70+
}
71+
72+
bb8: {
73+
StorageLive(_12); // scope 0 at $DIR/breakable-scope-drops.rs:+12:13: +12:24
74+
_12 = no_unwind() -> [return: bb9, unwind: bb16]; // scope 0 at $DIR/breakable-scope-drops.rs:+12:13: +12:24
75+
// mir::Constant
76+
// + span: $DIR/breakable-scope-drops.rs:29:13: 29:22
77+
// + literal: Const { ty: fn() {no_unwind}, val: Value(<ZST>) }
78+
}
79+
80+
bb9: {
81+
_5 = (move _6, move _10, move _12); // scope 0 at $DIR/breakable-scope-drops.rs:+2:9: +13:10
82+
StorageDead(_12); // scope 0 at $DIR/breakable-scope-drops.rs:+13:9: +13:10
83+
drop(_11) -> [return: bb10, unwind: bb17]; // scope 0 at $DIR/breakable-scope-drops.rs:+13:9: +13:10
84+
}
85+
86+
bb10: {
87+
StorageDead(_11); // scope 0 at $DIR/breakable-scope-drops.rs:+13:9: +13:10
88+
StorageDead(_10); // scope 0 at $DIR/breakable-scope-drops.rs:+13:9: +13:10
89+
StorageDead(_6); // scope 0 at $DIR/breakable-scope-drops.rs:+13:9: +13:10
90+
goto -> bb13; // scope 0 at $DIR/breakable-scope-drops.rs:+1:17: +14:6
91+
}
92+
93+
bb11: {
94+
StorageDead(_8); // scope 2 at $DIR/breakable-scope-drops.rs:+7:17: +7:18
95+
drop(_7) -> [return: bb12, unwind: bb17]; // scope 0 at $DIR/breakable-scope-drops.rs:+8:13: +8:14
96+
}
97+
98+
bb12: {
99+
StorageDead(_7); // scope 0 at $DIR/breakable-scope-drops.rs:+8:13: +8:14
100+
StorageDead(_6); // scope 0 at $DIR/breakable-scope-drops.rs:+13:9: +13:10
101+
goto -> bb13; // scope 0 at $DIR/breakable-scope-drops.rs:+1:17: +14:6
102+
}
103+
104+
bb13: {
105+
FakeRead(ForLet(None), _5); // scope 0 at $DIR/breakable-scope-drops.rs:+1:9: +1:14
106+
_0 = _3; // scope 1 at $DIR/breakable-scope-drops.rs:+16:5: +16:12
107+
StorageDead(_5); // scope 0 at $DIR/breakable-scope-drops.rs:+17:1: +17:2
108+
drop(_2) -> [return: bb14, unwind: bb18]; // scope 0 at $DIR/breakable-scope-drops.rs:+17:1: +17:2
109+
}
110+
111+
bb14: {
112+
drop(_1) -> [return: bb15, unwind: bb19]; // scope 0 at $DIR/breakable-scope-drops.rs:+17:1: +17:2
113+
}
114+
115+
bb15: {
116+
return; // scope 0 at $DIR/breakable-scope-drops.rs:+17:2: +17:2
117+
}
118+
119+
bb16 (cleanup): {
120+
drop(_11) -> bb17; // scope 0 at $DIR/breakable-scope-drops.rs:+13:9: +13:10
121+
}
122+
123+
bb17 (cleanup): {
124+
drop(_2) -> bb18; // scope 0 at $DIR/breakable-scope-drops.rs:+17:1: +17:2
125+
}
126+
127+
bb18 (cleanup): {
128+
drop(_1) -> bb19; // scope 0 at $DIR/breakable-scope-drops.rs:+17:1: +17:2
129+
}
130+
131+
bb19 (cleanup): {
132+
resume; // scope 0 at $DIR/breakable-scope-drops.rs:+0:1: +17:2
133+
}
134+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
struct A;
2+
3+
impl Drop for A {
4+
fn drop(&mut self) {}
5+
}
6+
7+
fn takes_a_ref<'a>(_arg: &'a A) {}
8+
9+
fn returns_a() -> A {
10+
A
11+
}
12+
13+
fn weird_temporary<'a>(a: &'a A, x: bool) {
14+
takes_a_ref('scope: {
15+
if x {
16+
break 'scope a;
17+
}
18+
19+
&returns_a()
20+
//~^ ERROR temporary value dropped while borrowed
21+
});
22+
}
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0716]: temporary value dropped while borrowed
2+
--> $DIR/tail-expression-breakable-scope.rs:19:10
3+
|
4+
LL | &returns_a()
5+
| -^^^^^^^^^^-
6+
| || |
7+
| || temporary value is freed at the end of this statement
8+
| |creates a temporary value which is freed while still in use
9+
| borrow later used here
10+
|
11+
= note: consider using a `let` binding to create a longer lived value
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0716`.

src/test/ui/try-block/dot-dot.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// compile-flags: --crate-type=lib -Copt-level=3 -Zvalidate-mir --edition=2021
2+
// build-pass
3+
4+
#![feature(try_blocks)]
5+
6+
#[derive(Default, Debug)]
7+
struct Response {
8+
bookmarks: String,
9+
continue_after: String,
10+
}
11+
12+
fn format_response(page: Result<String, String>) -> Result<Response, String> {
13+
try {
14+
Response {
15+
bookmarks: String::new(),
16+
continue_after: page?,
17+
..Default::default()
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)