Skip to content

Commit fc4fd10

Browse files
committed
Allow recursive tuples on the LHS of assignments
1 parent a8c784c commit fc4fd10

File tree

4 files changed

+96
-46
lines changed

4 files changed

+96
-46
lines changed

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
146146
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
147147
}
148148
ExprKind::Assign(ref el, ref er, span) => {
149-
hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span)
149+
self.lower_expr_assign(el, er, span, e.span)
150150
}
151151
ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
152152
self.lower_binop(op),
@@ -163,6 +163,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
163163
ExprKind::Range(ref e1, ref e2, lims) => {
164164
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
165165
}
166+
ExprKind::Underscore => {
167+
self.sess
168+
.struct_span_err(
169+
e.span,
170+
"expected expression, found reserved identifier `_`",
171+
)
172+
.span_label(e.span, "expected expression")
173+
.emit();
174+
hir::ExprKind::Err
175+
}
166176
ExprKind::Path(ref qself, ref path) => {
167177
let qpath = self.lower_qpath(
168178
e.id,
@@ -186,8 +196,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
186196
}
187197
ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm),
188198
ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm),
189-
ExprKind::Struct(ref path, ref fields, ref maybe_expr) => {
190-
let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x));
199+
ExprKind::Struct(ref path, ref fields, ref rest) => {
200+
let rest = match rest {
201+
StructRest::Base(e) => Some(self.lower_expr(e)),
202+
StructRest::Rest(sp) => {
203+
self.sess
204+
.struct_span_err(*sp, "base expression required after `..`")
205+
.span_label(*sp, "add a base expression here")
206+
.emit();
207+
Some(&*self.arena.alloc(self.expr_err(*sp)))
208+
}
209+
StructRest::None => None,
210+
};
191211
hir::ExprKind::Struct(
192212
self.arena.alloc(self.lower_qpath(
193213
e.id,
@@ -197,7 +217,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
197217
ImplTraitContext::disallowed(),
198218
)),
199219
self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))),
200-
maybe_expr,
220+
rest,
201221
)
202222
}
203223
ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
@@ -840,6 +860,66 @@ impl<'hir> LoweringContext<'_, 'hir> {
840860
})
841861
}
842862

863+
/// Lower `(a,b) = t` to `{ let (lhs1,lhs2) = t; a = lhs1; b = lhs2; }`.
864+
fn lower_expr_assign(
865+
&mut self,
866+
lhs: &Expr,
867+
rhs: &Expr,
868+
eq_sign_span: Span,
869+
whole_span: Span,
870+
) -> hir::ExprKind<'hir> {
871+
let mut assignments = Vec::new();
872+
873+
// The LHS becomes a pattern: `(lhs1, lhs2)`
874+
let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments);
875+
let rhs = self.lower_expr(rhs);
876+
877+
// Introduce a let for destructuring: `let (lhs1,lhs2) = t`.
878+
let destructure_let = self.stmt_let_pat(
879+
ThinVec::new(),
880+
whole_span,
881+
Some(rhs),
882+
pat,
883+
hir::LocalSource::AssignDesugar(eq_sign_span),
884+
);
885+
886+
// `a = lhs1; b = lhs2;`.
887+
let stmts = self
888+
.arena
889+
.alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));
890+
891+
// Wrap everything in a block.
892+
hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
893+
}
894+
895+
/// Convert the LHS of a destructuring assignment to a pattern.
896+
/// Along the way, introduce additional assignments in the parameter assignments.
897+
fn destructure_assign(
898+
&mut self,
899+
lhs: &Expr,
900+
eq_sign_span: Span,
901+
assignments: &mut Vec<hir::Stmt<'hir>>,
902+
) -> &'hir hir::Pat<'hir> {
903+
match &lhs.kind {
904+
ExprKind::Tup(elements) => {
905+
let pats = self.arena.alloc_from_iter(
906+
elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)),
907+
);
908+
let tuple_pat = hir::PatKind::Tuple(pats, None);
909+
self.pat(lhs.span, tuple_pat)
910+
}
911+
_ => {
912+
let ident = Ident::new(sym::lhs, lhs.span);
913+
let (pat, binding) = self.pat_ident(lhs.span, ident);
914+
let ident = self.expr_ident(lhs.span, ident, binding);
915+
let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span);
916+
let expr = self.expr(lhs.span, assign, ThinVec::new());
917+
assignments.push(self.stmt_expr(lhs.span, expr));
918+
pat
919+
}
920+
}
921+
}
922+
843923
/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
844924
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
845925
let e1 = self.lower_expr_mut(e1);

compiler/rustc_hir/src/hir.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,9 @@ pub enum LocalSource {
16801680
AsyncFn,
16811681
/// A desugared `<expr>.await`.
16821682
AwaitDesugar,
1683+
/// A desugared expr = expr where the LHS is a tuple, struct or array.
1684+
/// The span is for the `=` sign.
1685+
AssignDesugar(Span),
16831686
}
16841687

16851688
/// Hints at the original code for a `match _ { .. }`.

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
6969
hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
7070
hir::LocalSource::AsyncFn => ("async fn binding", None),
7171
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
72+
hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
7273
};
7374
self.check_irrefutable(&loc.pat, msg, sp);
7475
self.check_patterns(&loc.pat);

compiler/rustc_typeck/src/check/expr.rs

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
3939
use rustc_middle::ty::Ty;
4040
use rustc_middle::ty::TypeFoldable;
4141
use rustc_middle::ty::{AdtKind, Visibility};
42-
use rustc_session::parse::feature_err;
4342
use rustc_span::hygiene::DesugaringKind;
4443
use rustc_span::source_map::Span;
4544
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -719,19 +718,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
719718
);
720719
}
721720

722-
fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool {
723-
match &expr.kind {
724-
ExprKind::Array(comps) | ExprKind::Tup(comps) => {
725-
comps.iter().all(|e| self.is_destructuring_place_expr(e))
726-
}
727-
ExprKind::Struct(_path, fields, rest) => {
728-
rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true)
729-
&& fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
730-
}
731-
_ => expr.is_syntactic_place_expr(),
732-
}
733-
}
734-
735721
pub(crate) fn check_lhs_assignable(
736722
&self,
737723
lhs: &'tcx hir::Expr<'tcx>,
@@ -742,34 +728,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
742728
return;
743729
}
744730

745-
let da = self.is_destructuring_place_expr(lhs);
746-
match (da, self.tcx.features().destructuring_assignment) {
747-
// Valid destructuring assignment.
748-
(true, true) => {}
749-
750-
// Destructuring assignment, but the feature is not enabled.
751-
(true, false) => {
752-
feature_err(
753-
&self.tcx.sess.parse_sess,
754-
sym::destructuring_assignment,
755-
*expr_span,
756-
"destructuring assignments are unstable",
757-
)
758-
.emit();
759-
}
760-
761-
// Invalid assignment.
762-
(false, _) => {
763-
// FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
764-
let mut err = self.tcx.sess.struct_span_err_with_code(
765-
*expr_span,
766-
"invalid left-hand side of assignment",
767-
DiagnosticId::Error(err_code.into()),
768-
);
769-
err.span_label(lhs.span, "cannot assign to this expression");
770-
err.emit();
771-
}
772-
}
731+
// FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
732+
let mut err = self.tcx.sess.struct_span_err_with_code(
733+
*expr_span,
734+
"invalid left-hand side of assignment",
735+
DiagnosticId::Error(err_code.into()),
736+
);
737+
err.span_label(lhs.span, "cannot assign to this expression");
738+
err.emit();
773739
}
774740

775741
/// Type check assignment expression `expr` of form `lhs = rhs`.

0 commit comments

Comments
 (0)