Skip to content

Commit 163404e

Browse files
committed
Rebased work from rust-lang#68577
1 parent 38030ff commit 163404e

File tree

25 files changed

+518
-749
lines changed

25 files changed

+518
-749
lines changed

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 59 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
8585
hir::ExprKind::AddrOf(k, m, ohs)
8686
}
8787
ExprKind::Let(ref pat, ref scrutinee) => {
88-
self.lower_expr_let(e.span, pat, scrutinee)
88+
hir::ExprKind::Let(self.lower_pat(pat), self.lower_expr(scrutinee))
8989
}
9090
ExprKind::If(ref cond, ref then, ref else_opt) => {
9191
self.lower_expr_if(e.span, cond, then, else_opt.as_deref())
@@ -268,51 +268,36 @@ impl<'hir> LoweringContext<'_, 'hir> {
268268
}
269269
}
270270

271-
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
272-
/// ```rust
273-
/// match scrutinee { pats => true, _ => false }
274-
/// ```
275-
fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind<'hir> {
276-
// If we got here, the `let` expression is not allowed.
277-
278-
if self.sess.opts.unstable_features.is_nightly_build() {
279-
self.sess
280-
.struct_span_err(span, "`let` expressions are not supported here")
281-
.note("only supported directly in conditions of `if`- and `while`-expressions")
282-
.note("as well as when nested within `&&` and parenthesis in those conditions")
283-
.emit();
284-
} else {
285-
self.sess
286-
.struct_span_err(span, "expected expression, found statement (`let`)")
287-
.note("variable declaration using `let` is a statement")
288-
.emit();
271+
/// Lower the condition of an `if` or `while` expression.
272+
/// This entails some special handling of immediate `let` expressions as conditions.
273+
/// Namely, if the given `cond` is not a `let` expression then it is wrapped in `drop-temps`.
274+
fn lower_expr_cond(&mut self, cond: &Expr) -> &'hir hir::Expr<'hir> {
275+
// Lower the `cond` expression.
276+
let cond = self.lower_expr(cond);
277+
// Normally, the `cond` of `if cond` will drop temporaries before evaluating the blocks.
278+
// This is achieved by using `drop-temps { cond }`, equivalent to `{ let _t = $cond; _t }`.
279+
// However, for backwards compatibility reasons, `if let pat = scrutinee`, like `match`
280+
// does not drop the temporaries of `scrutinee` before evaluating the blocks.
281+
match cond.kind {
282+
hir::ExprKind::Let(..) => cond,
283+
_ => {
284+
let reason = DesugaringKind::CondTemporary;
285+
let span = self.mark_span_with_reason(reason, cond.span, None);
286+
self.expr_drop_temps(span, cond, ThinVec::new())
287+
}
289288
}
289+
}
290290

291-
// For better recovery, we emit:
292-
// ```
293-
// match scrutinee { pat => true, _ => false }
294-
// ```
295-
// While this doesn't fully match the user's intent, it has key advantages:
296-
// 1. We can avoid using `abort_if_errors`.
297-
// 2. We can typeck both `pat` and `scrutinee`.
298-
// 3. `pat` is allowed to be refutable.
299-
// 4. The return type of the block is `bool` which seems like what the user wanted.
300-
let scrutinee = self.lower_expr(scrutinee);
301-
let then_arm = {
302-
let pat = self.lower_pat(pat);
303-
let expr = self.expr_bool(span, true);
304-
self.arm(pat, expr)
305-
};
306-
let else_arm = {
307-
let pat = self.pat_wild(span);
308-
let expr = self.expr_bool(span, false);
309-
self.arm(pat, expr)
310-
};
311-
hir::ExprKind::Match(
312-
scrutinee,
313-
arena_vec![self; then_arm, else_arm],
314-
hir::MatchSource::Normal,
315-
)
291+
/// Lower `then` into `true => then`.
292+
fn lower_then_arm(&mut self, span: Span, then: &Block) -> hir::Arm<'hir> {
293+
let then_expr = self.lower_block_expr(then);
294+
let then_pat = self.pat_bool(span, true);
295+
self.arm(then_pat, self.arena.alloc(then_expr))
296+
}
297+
298+
fn lower_else_arm(&mut self, span: Span, else_expr: &'hir hir::Expr<'hir>) -> hir::Arm<'hir> {
299+
let else_pat = self.pat_wild(span);
300+
self.arm(else_pat, else_expr)
316301
}
317302

318303
fn lower_expr_if(
@@ -322,112 +307,53 @@ impl<'hir> LoweringContext<'_, 'hir> {
322307
then: &Block,
323308
else_opt: Option<&Expr>,
324309
) -> hir::ExprKind<'hir> {
325-
// FIXME(#53667): handle lowering of && and parens.
326-
327-
// `_ => else_block` where `else_block` is `{}` if there's `None`:
328-
let else_pat = self.pat_wild(span);
329-
let (else_expr, contains_else_clause) = match else_opt {
330-
None => (self.expr_block_empty(span), false),
331-
Some(els) => (self.lower_expr(els), true),
310+
let scrutinee = self.lower_expr_cond(cond);
311+
let then_arm = self.lower_then_arm(span, then);
312+
let else_expr = match else_opt {
313+
None => self.expr_block_empty(span), // Use `{}` if there's no `else` block.
314+
Some(els) => self.lower_expr(els),
332315
};
333-
let else_arm = self.arm(else_pat, else_expr);
334-
335-
// Handle then + scrutinee:
336-
let then_expr = self.lower_block_expr(then);
337-
let (then_pat, scrutinee, desugar) = match cond.kind {
338-
// `<pat> => <then>`:
339-
ExprKind::Let(ref pat, ref scrutinee) => {
340-
let scrutinee = self.lower_expr(scrutinee);
341-
let pat = self.lower_pat(pat);
342-
(pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause })
343-
}
344-
// `true => <then>`:
345-
_ => {
346-
// Lower condition:
347-
let cond = self.lower_expr(cond);
348-
let span_block =
349-
self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None);
350-
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
351-
// to preserve drop semantics since `if cond { ... }` does not
352-
// let temporaries live outside of `cond`.
353-
let cond = self.expr_drop_temps(span_block, cond, ThinVec::new());
354-
let pat = self.pat_bool(span, true);
355-
(pat, cond, hir::MatchSource::IfDesugar { contains_else_clause })
356-
}
357-
};
358-
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
359-
316+
let else_arm = self.lower_else_arm(span, else_expr);
317+
let desugar = hir::MatchSource::IfDesugar { contains_else_clause: else_opt.is_some() };
360318
hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar)
361319
}
362320

321+
/// We desugar: `'label: while $cond $body` into:
322+
///
323+
/// ```
324+
/// 'label: loop {
325+
/// match $cond {
326+
/// true => $body,
327+
/// _ => break,
328+
/// }
329+
/// }
330+
/// ```
331+
///
332+
/// where `$cond` is wrapped in `drop-temps { $cond }` if it isn't a `Let` expression.
363333
fn lower_expr_while_in_loop_scope(
364334
&mut self,
365335
span: Span,
366336
cond: &Expr,
367337
body: &Block,
368338
opt_label: Option<Label>,
369339
) -> hir::ExprKind<'hir> {
370-
// FIXME(#53667): handle lowering of && and parens.
371-
372340
// Note that the block AND the condition are evaluated in the loop scope.
373341
// This is done to allow `break` from inside the condition of the loop.
374342

343+
// Lower the condition:
344+
let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr_cond(cond));
345+
// `true => body`:
346+
let then_arm = self.lower_then_arm(span, body);
375347
// `_ => break`:
376-
let else_arm = {
377-
let else_pat = self.pat_wild(span);
378-
let else_expr = self.expr_break(span, ThinVec::new());
379-
self.arm(else_pat, else_expr)
380-
};
381-
382-
// Handle then + scrutinee:
383-
let then_expr = self.lower_block_expr(body);
384-
let (then_pat, scrutinee, desugar, source) = match cond.kind {
385-
ExprKind::Let(ref pat, ref scrutinee) => {
386-
// to:
387-
//
388-
// [opt_ident]: loop {
389-
// match <sub_expr> {
390-
// <pat> => <body>,
391-
// _ => break
392-
// }
393-
// }
394-
let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
395-
let pat = self.lower_pat(pat);
396-
(pat, scrutinee, hir::MatchSource::WhileLetDesugar, hir::LoopSource::WhileLet)
397-
}
398-
_ => {
399-
// We desugar: `'label: while $cond $body` into:
400-
//
401-
// ```
402-
// 'label: loop {
403-
// match drop-temps { $cond } {
404-
// true => $body,
405-
// _ => break,
406-
// }
407-
// }
408-
// ```
409-
410-
// Lower condition:
411-
let cond = self.with_loop_condition_scope(|this| this.lower_expr(cond));
412-
let span_block =
413-
self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None);
414-
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
415-
// to preserve drop semantics since `while cond { ... }` does not
416-
// let temporaries live outside of `cond`.
417-
let cond = self.expr_drop_temps(span_block, cond, ThinVec::new());
418-
// `true => <then>`:
419-
let pat = self.pat_bool(span, true);
420-
(pat, cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While)
421-
}
422-
};
423-
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
424-
348+
let else_expr = self.expr_break(span, ThinVec::new());
349+
let else_arm = self.lower_else_arm(span, else_expr);
425350
// `match <scrutinee> { ... }`
426-
let match_expr =
427-
self.expr_match(span, scrutinee, arena_vec![self; then_arm, else_arm], desugar);
428-
351+
let match_arms = arena_vec![self; then_arm, else_arm];
352+
let match_desugar = hir::MatchSource::WhileDesugar;
353+
let match_expr = self.expr_match(span, scrutinee, match_arms, match_desugar);
429354
// `[opt_ident]: loop { ... }`
430-
hir::ExprKind::Loop(self.block_expr(self.arena.alloc(match_expr)), opt_label, source)
355+
let loop_block = self.block_expr(self.arena.alloc(match_expr));
356+
hir::ExprKind::Loop(loop_block, opt_label, hir::LoopSource::While)
431357
}
432358

433359
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_ok(<expr>) }`,

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ struct AstValidator<'a> {
8080
/// certain positions.
8181
is_assoc_ty_bound_banned: bool,
8282

83+
/// Used to allow `let` expressions in certain syntactic locations.
84+
is_let_allowed: bool,
85+
8386
lint_buffer: &'a mut LintBuffer,
8487
}
8588

@@ -118,6 +121,27 @@ impl<'a> AstValidator<'a> {
118121
self.bound_context = old;
119122
}
120123

124+
fn with_let_allowed(&mut self, allowed: bool, f: impl FnOnce(&mut Self, bool)) {
125+
let old = mem::replace(&mut self.is_let_allowed, allowed);
126+
f(self, old);
127+
self.is_let_allowed = old;
128+
}
129+
130+
/// Emits an error banning the `let` expression provided in the given location.
131+
fn ban_let_expr(&self, expr: &'a Expr) {
132+
let sess = &self.session;
133+
if sess.opts.unstable_features.is_nightly_build() {
134+
sess.struct_span_err(expr.span, "`let` expressions are not supported here")
135+
.note("only supported directly in conditions of `if`- and `while`-expressions")
136+
.note("as well as when nested within `&&` and parenthesis in those conditions")
137+
.emit();
138+
} else {
139+
sess.struct_span_err(expr.span, "expected expression, found statement (`let`)")
140+
.note("variable declaration using `let` is a statement")
141+
.emit();
142+
}
143+
}
144+
121145
fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
122146
match constraint.kind {
123147
AssocTyConstraintKind::Equality { .. } => {}
@@ -795,20 +819,43 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
795819
}
796820

797821
fn visit_expr(&mut self, expr: &'a Expr) {
798-
match &expr.kind {
799-
ExprKind::LlvmInlineAsm(..) if !self.session.target.allow_asm => {
800-
struct_span_err!(
801-
self.session,
802-
expr.span,
803-
E0472,
804-
"llvm_asm! is unsupported on this target"
805-
)
806-
.emit();
822+
self.with_let_allowed(false, |this, let_allowed| {
823+
match &expr.kind {
824+
ExprKind::LlvmInlineAsm(..) if !this.session.target.allow_asm => {
825+
struct_span_err!(
826+
this.session,
827+
expr.span,
828+
E0472,
829+
"llvm_asm! is unsupported on this target"
830+
)
831+
.emit();
832+
}
833+
ExprKind::Let(..) if !let_allowed => this.ban_let_expr(expr),
834+
// `(e)` or `e && e` does not impose additional constraints on `e`.
835+
ExprKind::Paren(_) | ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, ..) => {
836+
this.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr));
837+
return; // We've already walked into `expr`.
838+
}
839+
// However, we do allow it in the condition of the `if` expression.
840+
// We do not allow `let` in `then` and `opt_else` directly.
841+
ExprKind::If(cond, then, opt_else) => {
842+
this.visit_block(then);
843+
walk_list!(this, visit_expr, opt_else);
844+
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
845+
return; // We've already walked into `expr`.
846+
}
847+
// The same logic applies to `While`.
848+
ExprKind::While(cond, then, opt_label) => {
849+
walk_list!(this, visit_label, opt_label);
850+
this.visit_block(then);
851+
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
852+
return; // We've already walked into `expr`.
853+
}
854+
_ => {}
807855
}
808-
_ => {}
809-
}
810856

811-
visit::walk_expr(self, expr);
857+
visit::walk_expr(this, expr);
858+
});
812859
}
813860

814861
fn visit_ty(&mut self, ty: &'a Ty) {
@@ -1443,6 +1490,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
14431490
bound_context: None,
14441491
is_impl_trait_banned: false,
14451492
is_assoc_ty_bound_banned: false,
1493+
is_let_allowed: false,
14461494
lint_buffer: lints,
14471495
};
14481496
visit::walk_crate(&mut validator, krate);

0 commit comments

Comments
 (0)