@@ -109,13 +109,170 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
109
109
)
110
110
) ;
111
111
}
112
+ StmtKind :: Let {
113
+ remainder_scope,
114
+ init_scope,
115
+ pattern,
116
+ initializer : Some ( initializer) ,
117
+ lint_level,
118
+ else_block : Some ( else_block) ,
119
+ } => {
120
+ // When lowering the statement `let <pat> = <expr> else { <else> };`,
121
+ // the `<else>` block is nested in the parent scope enclosing this statment.
122
+ // That scope is usually either the enclosing block scope,
123
+ // or the remainder scope of the last statement.
124
+ // This is to make sure that temporaries instantiated in `<expr>` are dropped
125
+ // as well.
126
+ // In addition, even though bindings in `<pat>` only come into scope if
127
+ // the pattern matching passes, in the MIR building the storages for them
128
+ // are declared as live any way.
129
+ // This is similar to `let x;` statements without an initializer expression,
130
+ // where the value of `x` in this example may or may be assigned,
131
+ // because the storage for their values may not be live after all due to
132
+ // failure in pattern matching.
133
+ // For this reason, we declare those storages as live but we do not schedule
134
+ // any drop yet- they are scheduled later after the pattern matching.
135
+ // The generated MIR will have `StorageDead` whenever the control flow breaks out
136
+ // of the parent scope, regardless of the result of the pattern matching.
137
+ // However, the drops are inserted in MIR only when the control flow breaks out of
138
+ // the scope of the remainder scope associated with this `let .. else` statement.
139
+ // Pictorial explanation of the scope structure:
140
+ // ┌─────────────────────────────────┐
141
+ // │ Scope of the enclosing block, │
142
+ // │ or the last remainder scope │
143
+ // │ ┌───────────────────────────┐ │
144
+ // │ │ Scope for <else> block │ │
145
+ // │ └───────────────────────────┘ │
146
+ // │ ┌───────────────────────────┐ │
147
+ // │ │ Remainder scope of │ │
148
+ // │ │ this let-else statement │ │
149
+ // │ │ ┌─────────────────────┐ │ │
150
+ // │ │ │ <expr> scope │ │ │
151
+ // │ │ └─────────────────────┘ │ │
152
+ // │ │ extended temporaries in │ │
153
+ // │ │ <expr> lives in this │ │
154
+ // │ │ scope │ │
155
+ // │ │ ┌─────────────────────┐ │ │
156
+ // │ │ │ Scopes for the rest │ │ │
157
+ // │ │ └─────────────────────┘ │ │
158
+ // │ └───────────────────────────┘ │
159
+ // └─────────────────────────────────┘
160
+ // Generated control flow:
161
+ // │ let Some(x) = y() else { return; }
162
+ // │
163
+ // ┌────────▼───────┐
164
+ // │ evaluate y() │
165
+ // └────────┬───────┘
166
+ // │ ┌────────────────┐
167
+ // ┌────────▼───────┐ │Drop temporaries│
168
+ // │Test the pattern├──────►in y() │
169
+ // └────────┬───────┘ │because breaking│
170
+ // │ │out of <expr> │
171
+ // ┌────────▼───────┐ │scope │
172
+ // │Move value into │ └───────┬────────┘
173
+ // │binding x │ │
174
+ // └────────┬───────┘ ┌───────▼────────┐
175
+ // │ │Drop extended │
176
+ // ┌────────▼───────┐ │temporaries in │
177
+ // │Drop temporaries│ │<expr> because │
178
+ // │in y() │ │breaking out of │
179
+ // │because breaking│ │remainder scope │
180
+ // │out of <expr> │ └───────┬────────┘
181
+ // │scope │ │
182
+ // └────────┬───────┘ ┌───────▼────────┐
183
+ // │ │Enter <else> ├────────►
184
+ // ┌────────▼───────┐ │block │ return;
185
+ // │Continue... │ └────────────────┘
186
+ // └────────────────┘
187
+
188
+ let ignores_expr_result = matches ! ( pattern. kind, PatKind :: Wild ) ;
189
+ this. block_context . push ( BlockFrame :: Statement { ignores_expr_result } ) ;
190
+
191
+ // Lower the `else` block first because its parent scope is actually
192
+ // enclosing the rest of the `let .. else ..` parts.
193
+ let else_block_span = this. thir [ * else_block] . span ;
194
+ // This place is not really used because this destination place
195
+ // should never be used to take values at the end of the failure
196
+ // block.
197
+ let dummy_place = this. temp ( this. tcx . types . never , else_block_span) ;
198
+ let failure_entry = this. cfg . start_new_block ( ) ;
199
+ let failure_block;
200
+ unpack ! (
201
+ failure_block = this. ast_block(
202
+ dummy_place,
203
+ failure_entry,
204
+ * else_block,
205
+ this. source_info( else_block_span) ,
206
+ )
207
+ ) ;
208
+ this. cfg . terminate (
209
+ failure_block,
210
+ this. source_info ( else_block_span) ,
211
+ TerminatorKind :: Unreachable ,
212
+ ) ;
213
+
214
+ // Declare the bindings, which may create a source scope.
215
+ let remainder_span = remainder_scope. span ( this. tcx , this. region_scope_tree ) ;
216
+ this. push_scope ( ( * remainder_scope, source_info) ) ;
217
+ let_scope_stack. push ( remainder_scope) ;
218
+
219
+ let visibility_scope =
220
+ Some ( this. new_source_scope ( remainder_span, LintLevel :: Inherited , None ) ) ;
221
+
222
+ let init = & this. thir [ * initializer] ;
223
+ let initializer_span = init. span ;
224
+ this. declare_bindings (
225
+ visibility_scope,
226
+ remainder_span,
227
+ pattern,
228
+ ArmHasGuard ( false ) ,
229
+ Some ( ( None , initializer_span) ) ,
230
+ ) ;
231
+ this. visit_primary_bindings (
232
+ pattern,
233
+ UserTypeProjections :: none ( ) ,
234
+ & mut |this, _, _, _, node, span, _, _| {
235
+ this. storage_live_binding ( block, node, span, OutsideGuard , false ) ;
236
+ } ,
237
+ ) ;
238
+ let failure = unpack ! (
239
+ block = this. in_opt_scope(
240
+ opt_destruction_scope. map( |de| ( de, source_info) ) ,
241
+ |this| {
242
+ let scope = ( * init_scope, source_info) ;
243
+ this. in_scope( scope, * lint_level, |this| {
244
+ this. ast_let_else(
245
+ block,
246
+ init,
247
+ initializer_span,
248
+ * else_block,
249
+ & last_remainder_scope,
250
+ pattern,
251
+ )
252
+ } )
253
+ }
254
+ )
255
+ ) ;
256
+ this. cfg . goto ( failure, source_info, failure_entry) ;
257
+
258
+ if let Some ( source_scope) = visibility_scope {
259
+ this. source_scope = source_scope;
260
+ }
261
+ last_remainder_scope = * remainder_scope;
262
+ }
263
+ StmtKind :: Let { init_scope, initializer : None , else_block : Some ( _) , .. } => {
264
+ span_bug ! (
265
+ init_scope. span( this. tcx, this. region_scope_tree) ,
266
+ "initializer is missing, but else block is present in this let binding" ,
267
+ )
268
+ }
112
269
StmtKind :: Let {
113
270
remainder_scope,
114
271
init_scope,
115
272
ref pattern,
116
273
initializer,
117
274
lint_level,
118
- else_block,
275
+ else_block : None ,
119
276
} => {
120
277
let ignores_expr_result = matches ! ( pattern. kind, PatKind :: Wild ) ;
121
278
this. block_context . push ( BlockFrame :: Statement { ignores_expr_result } ) ;
@@ -141,27 +298,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
141
298
|this| {
142
299
let scope = ( * init_scope, source_info) ;
143
300
this. in_scope( scope, * lint_level, |this| {
144
- if let Some ( else_block) = else_block {
145
- this. ast_let_else(
146
- block,
147
- init,
148
- initializer_span,
149
- * else_block,
150
- visibility_scope,
151
- last_remainder_scope,
152
- remainder_span,
153
- pattern,
154
- )
155
- } else {
156
- this. declare_bindings(
157
- visibility_scope,
158
- remainder_span,
159
- pattern,
160
- ArmHasGuard ( false ) ,
161
- Some ( ( None , initializer_span) ) ,
162
- ) ;
163
- this. expr_into_pattern( block, pattern, init) // irrefutable pattern
164
- }
301
+ this. declare_bindings(
302
+ visibility_scope,
303
+ remainder_span,
304
+ pattern,
305
+ ArmHasGuard ( false ) ,
306
+ Some ( ( None , initializer_span) ) ,
307
+ ) ;
308
+ this. expr_into_pattern( block, & pattern, init) // irrefutable pattern
165
309
} )
166
310
} ,
167
311
)
0 commit comments