@@ -109,13 +109,170 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
109109 )
110110 ) ;
111111 }
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+ }
112269 StmtKind :: Let {
113270 remainder_scope,
114271 init_scope,
115272 ref pattern,
116273 initializer,
117274 lint_level,
118- else_block,
275+ else_block : None ,
119276 } => {
120277 let ignores_expr_result = matches ! ( pattern. kind, PatKind :: Wild ) ;
121278 this. block_context . push ( BlockFrame :: Statement { ignores_expr_result } ) ;
@@ -141,27 +298,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
141298 |this| {
142299 let scope = ( * init_scope, source_info) ;
143300 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
165309 } )
166310 } ,
167311 )
0 commit comments