diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index c8d4a1bf2c9..e2cd27d0339 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -109,13 +109,99 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) ); } + StmtKind::Let { + remainder_scope, + init_scope, + pattern, + initializer: Some(initializer), + lint_level, + else_block: Some(else_block), + } => { + let ignores_expr_result = matches!(pattern.kind, PatKind::Wild); + this.block_context.push(BlockFrame::Statement { ignores_expr_result }); + // This place is not really used because this destination place + // should never be used to take values at the end of the failure + // block. + let else_block_span = this.thir[*else_block].span; + let dummy_place = this.temp(this.tcx.types.never, else_block_span); + let failure_entry = this.cfg.start_new_block(); + let failure_block; + unpack!( + failure_block = this.ast_block( + dummy_place, + failure_entry, + *else_block, + this.source_info(else_block_span), + ) + ); + this.cfg.terminate( + failure_block, + this.source_info(else_block_span), + TerminatorKind::Unreachable, + ); + + // Declare the bindings, which may create a source scope. + let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree); + this.push_scope((*remainder_scope, source_info)); + let_scope_stack.push(remainder_scope); + + let visibility_scope = + Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); + + let init = &this.thir[*initializer]; + let initializer_span = init.span; + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + this.visit_primary_bindings( + pattern, + UserTypeProjections::none(), + &mut |this, _, _, _, node, span, _, _| { + this.storage_live_binding(block, node, span, OutsideGuard, false); + }, + ); + let failure = unpack!( + block = this.in_opt_scope( + opt_destruction_scope.map(|de| (de, source_info)), + |this| { + let scope = (*init_scope, source_info); + this.in_scope(scope, *lint_level, |this| { + this.ast_let_else( + block, + init, + initializer_span, + *else_block, + &last_remainder_scope, + pattern, + ) + }) + } + ) + ); + this.cfg.goto(failure, source_info, failure_entry); + + if let Some(source_scope) = visibility_scope { + this.source_scope = source_scope; + } + last_remainder_scope = *remainder_scope; + } + StmtKind::Let { init_scope, initializer: None, else_block: Some(_), .. } => { + span_bug!( + init_scope.span(this.tcx, this.region_scope_tree), + "initializer is missing, but else block is present in this let binding", + ) + } StmtKind::Let { remainder_scope, init_scope, ref pattern, initializer, lint_level, - else_block, + else_block: None, } => { let ignores_expr_result = matches!(pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); @@ -141,27 +227,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { |this| { let scope = (*init_scope, source_info); this.in_scope(scope, *lint_level, |this| { - if let Some(else_block) = else_block { - this.ast_let_else( - block, - init, - initializer_span, - *else_block, - visibility_scope, - last_remainder_scope, - remainder_span, - pattern, - ) - } else { - this.declare_bindings( - visibility_scope, - remainder_span, - pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); - this.expr_into_pattern(block, pattern, init) // irrefutable pattern - } + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + this.expr_into_pattern(block, &pattern, init) // irrefutable pattern }) }, ) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index b1cb9b9f084..e0727725f68 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -699,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); // Although there is almost always scope for given variable in corner cases // like #92893 we might get variable with no scope. - if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) && schedule_drop{ + if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) && schedule_drop { self.schedule_drop(span, region_scope, local_id, DropKind::Storage); } Place::from(local_id) @@ -2274,23 +2274,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { init: &Expr<'tcx>, initializer_span: Span, else_block: BlockId, - visibility_scope: Option, - remainder_scope: region::Scope, - remainder_span: Span, + let_else_scope: ®ion::Scope, pattern: &Pat<'tcx>, - ) -> BlockAnd<()> { + ) -> BlockAnd { let else_block_span = self.thir[else_block].span; - let (matching, failure) = self.in_if_then_scope(remainder_scope, |this| { + let (matching, failure) = self.in_if_then_scope(*let_else_scope, |this| { let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span)); let pat = Pat { ty: init.ty, span: else_block_span, kind: PatKind::Wild }; let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); - this.declare_bindings( - visibility_scope, - remainder_span, - pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); let fake_borrow_temps = this.lower_match_tree( block, @@ -2321,28 +2312,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None, None, ); - this.break_for_else(failure, remainder_scope, this.source_info(initializer_span)); + this.break_for_else(failure, *let_else_scope, this.source_info(initializer_span)); matching.unit() }); - - // This place is not really used because this destination place - // should never be used to take values at the end of the failure - // block. - let dummy_place = self.temp(self.tcx.types.never, else_block_span); - let failure_block; - unpack!( - failure_block = self.ast_block( - dummy_place, - failure, - else_block, - self.source_info(else_block_span), - ) - ); - self.cfg.terminate( - failure_block, - self.source_info(else_block_span), - TerminatorKind::Unreachable, - ); - matching.unit() + matching.and(failure) } } diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index b779713482e..594a7e450b8 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -126,6 +126,20 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h for (i, statement) in blk.stmts.iter().enumerate() { match statement.kind { + hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => { + // Let-else has a special lexical structure for variables. + let mut prev_cx = visitor.cx; + visitor.enter_scope(Scope { + id: blk.hir_id.local_id, + data: ScopeData::Remainder(FirstStatementIndex::new(i)), + }); + visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement); + mem::swap(&mut prev_cx, &mut visitor.cx); + // We need to back out temporarily and + visitor.visit_block(els); + visitor.cx = prev_cx; + } hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => { // Each declaration introduces a subscope for bindings // introduced by the declaration; this subscope covers a @@ -138,10 +152,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement) } - hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), } - visitor.visit_stmt(statement) } walk_list!(visitor, visit_expr, &blk.expr); } @@ -460,7 +474,6 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, - els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -547,9 +560,6 @@ fn resolve_local<'tcx>( if let Some(pat) = pat { visitor.visit_pat(pat); } - if let Some(els) = els { - visitor.visit_block(els); - } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -766,7 +776,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value), None); + resolve_local(self, None, Some(&body.value)); } if body.generator_kind.is_some() { @@ -793,7 +803,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init, l.els) + resolve_local(self, Some(&l.pat), l.init) } } diff --git a/src/test/ui/async-await/async-await-let-else.no-drop-tracking.stderr b/src/test/ui/async-await/async-await-let-else.no-drop-tracking.stderr index 435cc845870..0058f7016dc 100644 --- a/src/test/ui/async-await/async-await-let-else.no-drop-tracking.stderr +++ b/src/test/ui/async-await/async-await-let-else.no-drop-tracking.stderr @@ -35,7 +35,8 @@ LL | bar2(Rc::new(())).await | | | has type `Rc<()>` which is not `Send` LL | }; - | - `Rc::new(())` is later dropped here +LL | } + | - `Rc::new(())` is later dropped here note: required by a bound in `is_send` --> $DIR/async-await-let-else.rs:19:15 | diff --git a/src/test/ui/let-else/let-else-temporary-lifetime.rs b/src/test/ui/let-else/let-else-temporary-lifetime.rs index 07fcc16e7bb..8542c3496b0 100644 --- a/src/test/ui/let-else/let-else-temporary-lifetime.rs +++ b/src/test/ui/let-else/let-else-temporary-lifetime.rs @@ -1,4 +1,5 @@ // run-pass +// compile-flags: -Zvalidate-mir #![feature(let_else)] use std::fmt::Display;