Skip to content

Commit 1f65f5d

Browse files
committed
Allow .. in struct expressions on the LHS of assignments
1 parent 3631ab5 commit 1f65f5d

File tree

7 files changed

+61
-13
lines changed

7 files changed

+61
-13
lines changed

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
872872
// Return early in case of an ordinary assignment.
873873
let is_ordinary = match &lhs.kind {
874874
ExprKind::Array(..)
875-
| ExprKind::Struct(_, _, None)
875+
| ExprKind::Struct(..)
876876
| ExprKind::Tup(..)
877877
| ExprKind::Underscore => false,
878878
ExprKind::Call(callee, ..) => {
@@ -980,8 +980,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
980980
}
981981
}
982982
// structs:
983-
// FIXME: support `..` here, requires changes to the parser
984-
ExprKind::Struct(path, fields, None) => {
983+
ExprKind::Struct(path, fields, base) => {
985984
let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| {
986985
let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments);
987986
hir::FieldPat {
@@ -999,7 +998,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
999998
ParamMode::Optional,
1000999
ImplTraitContext::disallowed(),
10011000
);
1002-
let struct_pat = hir::PatKind::Struct(qpath, field_pats, false);
1001+
let fields_omitted = match base {
1002+
None => false,
1003+
Some(e) => {
1004+
match e.kind {
1005+
ExprKind::Underscore => {}
1006+
_ => self
1007+
.sess
1008+
.struct_span_err(e.span, "base expression not allowed here")
1009+
.span_suggestion(
1010+
e.span,
1011+
"consider removing this",
1012+
String::new(),
1013+
rustc_errors::Applicability::MachineApplicable,
1014+
)
1015+
.emit(),
1016+
}
1017+
true
1018+
}
1019+
};
1020+
let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted);
10031021
return self.pat(lhs.span, struct_pat);
10041022
}
10051023
// tuples:

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,6 +2104,16 @@ impl<'a> Parser<'a> {
21042104
while self.token != token::CloseDelim(token::Brace) {
21052105
if self.eat(&token::DotDot) {
21062106
let exp_span = self.prev_token.span;
2107+
// If the struct ends in `.. }`, treat it like `.. _ }`.
2108+
// AST lowering will then report an error if it's not on the LHS of an assignment.
2109+
if self.token == token::CloseDelim(token::Brace) {
2110+
base = Some(self.mk_expr(
2111+
self.prev_token.span,
2112+
ExprKind::Underscore,
2113+
AttrVec::new(),
2114+
));
2115+
break;
2116+
}
21072117
match self.parse_expr() {
21082118
Ok(e) => base = Some(e),
21092119
Err(mut e) if recover => {

src/test/ui/destructuring-assignment/note-unsupported.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ fn main() {
1717
S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment
1818
//~| ERROR binary assignment operation `+=` cannot be applied
1919

20-
S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR invalid left-hand side of assignment
20+
S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR base expression not allowed
21+
//~| ERROR destructuring assignments are unstable
2122

2223
let c = 3;
2324

src/test/ui/destructuring-assignment/note-unsupported.stderr

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
error: base expression not allowed here
2+
--> $DIR/note-unsupported.rs:20:17
3+
|
4+
LL | S { x: a, ..s } = S { x: 3, y: 4 };
5+
| ^ help: consider removing this
6+
17
error[E0658]: destructuring assignments are unstable
28
--> $DIR/note-unsupported.rs:6:12
39
|
@@ -81,16 +87,19 @@ LL | S { x: a, y: b } += s;
8187
| |
8288
| cannot assign to this expression
8389

84-
error[E0070]: invalid left-hand side of assignment
90+
error[E0658]: destructuring assignments are unstable
8591
--> $DIR/note-unsupported.rs:20:21
8692
|
8793
LL | S { x: a, ..s } = S { x: 3, y: 4 };
8894
| --------------- ^
8995
| |
9096
| cannot assign to this expression
97+
|
98+
= note: see issue #372 <https://github.com/rust-lang/rust/issues/372> for more information
99+
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
91100

92101
error[E0658]: destructuring assignments are unstable
93-
--> $DIR/note-unsupported.rs:24:17
102+
--> $DIR/note-unsupported.rs:25:17
94103
|
95104
LL | ((a, b), c) = ((3, 4), 5);
96105
| ----------- ^
@@ -100,7 +109,7 @@ LL | ((a, b), c) = ((3, 4), 5);
100109
= note: see issue #372 <https://github.com/rust-lang/rust/issues/372> for more information
101110
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
102111

103-
error: aborting due to 11 previous errors
112+
error: aborting due to 12 previous errors
104113

105-
Some errors have detailed explanations: E0067, E0070, E0368, E0658.
114+
Some errors have detailed explanations: E0067, E0368, E0658.
106115
For more information about an error, try `rustc --explain E0067`.

src/test/ui/destructuring-assignment/struct_destructure.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ fn main() {
1414
assert_eq!((a,b), (2,1));
1515
Struct { a: _, b } = Struct { a: 1, b: 2 };
1616
assert_eq!((a,b), (2,2));
17+
Struct { a, .. } = Struct { a: 1, b: 3};
18+
assert_eq!((a,b), (1,2));
1719
}

src/test/ui/destructuring-assignment/struct_destructure_fail.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ struct Struct<S, T> {
77
fn main() {
88
let (mut a, b);
99
let mut c;
10+
let d = Struct { a: 0, b: 1 };
1011
Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c`
1112
Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b`
1213
//~| ERROR expected identifier, found reserved identifier `_`
14+
Struct { a, ..d } = Struct { a: 1, b: 2 }; //~ ERROR base expression not allowed here
1315
}
Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
error: expected identifier, found reserved identifier `_`
2-
--> $DIR/struct_destructure_fail.rs:11:17
2+
--> $DIR/struct_destructure_fail.rs:12:17
33
|
44
LL | Struct { a, _ } = Struct { a: 1, b: 2 };
55
| ------ ^ expected identifier, found reserved identifier
66
| |
77
| while parsing this struct
88

9+
error: base expression not allowed here
10+
--> $DIR/struct_destructure_fail.rs:14:19
11+
|
12+
LL | Struct { a, ..d } = Struct { a: 1, b: 2 };
13+
| ^ help: consider removing this
14+
915
error[E0026]: struct `Struct` does not have a field named `c`
10-
--> $DIR/struct_destructure_fail.rs:10:20
16+
--> $DIR/struct_destructure_fail.rs:11:20
1117
|
1218
LL | Struct { a, b, c } = Struct { a: 0, b: 1 };
1319
| ^ struct `Struct` does not have this field
1420

1521
error[E0027]: pattern does not mention field `b`
16-
--> $DIR/struct_destructure_fail.rs:11:5
22+
--> $DIR/struct_destructure_fail.rs:12:5
1723
|
1824
LL | Struct { a, _ } = Struct { a: 1, b: 2 };
1925
| ^^^^^^^^^^^^^^^ missing field `b`
2026

21-
error: aborting due to 3 previous errors
27+
error: aborting due to 4 previous errors
2228

2329
Some errors have detailed explanations: E0026, E0027.
2430
For more information about an error, try `rustc --explain E0026`.

0 commit comments

Comments
 (0)