Skip to content

Commit 7b10eec

Browse files
committed
Handle .. except in structs
1 parent 397e914 commit 7b10eec

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -900,35 +900,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
900900
eq_sign_span: Span,
901901
assignments: &mut Vec<hir::Stmt<'hir>>,
902902
) -> &'hir hir::Pat<'hir> {
903-
// TODO: Handle `..` and `_`
903+
// TODO: Handle `_`, requires changes to the parser
904904
match &lhs.kind {
905905
// slices:
906906
ExprKind::Array(elements) => {
907-
let pats = self.arena.alloc_from_iter(
908-
elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)),
909-
);
910-
let slice_pat = hir::PatKind::Slice(pats, None, &[]);
907+
let (pats, rest) =
908+
self.destructure_sequence(elements, "slice", eq_sign_span, assignments);
909+
let slice_pat = if let Some((i, span)) = rest {
910+
let (before, after) = pats.split_at(i);
911+
hir::PatKind::Slice(before, Some(self.pat(span, hir::PatKind::Wild)), after)
912+
} else {
913+
hir::PatKind::Slice(pats, None, &[])
914+
};
911915
return self.pat(lhs.span, slice_pat);
912916
}
913917
// tuple structs:
914918
ExprKind::Call(callee, args) => {
915919
if let ExprKind::Path(qself, path) = &callee.kind {
916-
let pats = self.arena.alloc_from_iter(
917-
args.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)),
918-
);
920+
let (pats, rest) =
921+
self.destructure_sequence(args, "tuple struct", eq_sign_span, assignments);
919922
let qpath = self.lower_qpath(
920923
callee.id,
921924
qself,
922925
path,
923926
ParamMode::Optional,
924927
ImplTraitContext::disallowed(),
925928
);
926-
let tuple_struct_pat = hir::PatKind::TupleStruct(qpath, pats, None);
929+
let tuple_struct_pat =
930+
hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0));
927931
return self.pat(lhs.span, tuple_struct_pat);
928932
}
929933
}
930934
// structs:
931-
ExprKind::Struct(path, fields, _rest) => {
935+
// TODO: support `..` here, requires changes to the parser
936+
ExprKind::Struct(path, fields, rest) => {
932937
let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| {
933938
let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments);
934939
hir::FieldPat {
@@ -951,10 +956,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
951956
}
952957
// tuples:
953958
ExprKind::Tup(elements) => {
954-
let pats = self.arena.alloc_from_iter(
955-
elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)),
956-
);
957-
let tuple_pat = hir::PatKind::Tuple(pats, None);
959+
let (pats, rest) =
960+
self.destructure_sequence(elements, "tuple", eq_sign_span, assignments);
961+
let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0));
958962
return self.pat(lhs.span, tuple_pat);
959963
}
960964
_ => {}
@@ -969,6 +973,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
969973
pat
970974
}
971975

976+
/// Destructure a sequence of expressions occurring on the LHS of an assignment.
977+
/// Such a sequence occurs in a tuple (struct)/slice.
978+
/// Return a sequence of corresponding patterns and the index and span of `..`, if any.
979+
/// Along the way, introduce additional assignments in the parameter `assignments`.
980+
fn destructure_sequence(
981+
&mut self,
982+
elements: &[AstP<Expr>],
983+
ctx: &str,
984+
eq_sign_span: Span,
985+
assignments: &mut Vec<hir::Stmt<'hir>>,
986+
) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) {
987+
let mut rest = None;
988+
let elements =
989+
self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
990+
// Check for `..` pattern.
991+
if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
992+
if let Some((_, prev_span)) = rest {
993+
self.ban_extra_rest_pat(e.span, prev_span, ctx);
994+
} else {
995+
rest = Some((i, e.span));
996+
}
997+
None
998+
} else {
999+
Some(self.destructure_assign(e, eq_sign_span, assignments))
1000+
}
1001+
}));
1002+
(elements, rest)
1003+
}
1004+
9721005
/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
9731006
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
9741007
let e1 = self.lower_expr_mut(e1);

compiler/rustc_ast_lowering/src/pat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
277277
}
278278

279279
/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
280-
fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
280+
crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
281281
self.diagnostic()
282282
.struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
283283
.span_label(sp, &format!("can only be used once per {} pattern", ctx))

0 commit comments

Comments
 (0)