Skip to content

Commit 8cf3564

Browse files
fanziervarkor
andcommitted
Add underscore expressions for destructuring assignments
Co-authored-by: varkor <[email protected]>
1 parent a38f8fb commit 8cf3564

27 files changed

+243
-45
lines changed

compiler/rustc_ast/src/ast.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,7 @@ impl Expr {
11921192
ExprKind::Field(..) => ExprPrecedence::Field,
11931193
ExprKind::Index(..) => ExprPrecedence::Index,
11941194
ExprKind::Range(..) => ExprPrecedence::Range,
1195+
ExprKind::Underscore => ExprPrecedence::Path,
11951196
ExprKind::Path(..) => ExprPrecedence::Path,
11961197
ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
11971198
ExprKind::Break(..) => ExprPrecedence::Break,
@@ -1324,6 +1325,8 @@ pub enum ExprKind {
13241325
Index(P<Expr>, P<Expr>),
13251326
/// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment).
13261327
Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
1328+
/// An underscore, used in destructuring assignment to ignore a value.
1329+
Underscore,
13271330

13281331
/// Variable reference, possibly containing `::` and/or type
13291332
/// parameters (e.g., `foo::bar::<baz>`).

compiler/rustc_ast/src/mut_visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
12321232
visit_opt(e1, |e1| vis.visit_expr(e1));
12331233
visit_opt(e2, |e2| vis.visit_expr(e2));
12341234
}
1235+
ExprKind::Underscore => {}
12351236
ExprKind::Path(qself, path) => {
12361237
vis.visit_qself(qself);
12371238
vis.visit_path(path);

compiler/rustc_ast/src/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
806806
walk_list!(visitor, visit_expr, start);
807807
walk_list!(visitor, visit_expr, end);
808808
}
809+
ExprKind::Underscore => {}
809810
ExprKind::Path(ref maybe_qself, ref path) => {
810811
if let Some(ref qself) = *maybe_qself {
811812
visitor.visit_ty(&qself.ty);

compiler/rustc_ast_lowering/src/expr.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
164164
ExprKind::Range(ref e1, ref e2, lims) => {
165165
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
166166
}
167+
ExprKind::Underscore => {
168+
self.sess
169+
.struct_span_err(
170+
e.span,
171+
"in expressions, `_` can only be used on the left-hand side of an assignment",
172+
)
173+
.span_label(e.span, "`_` not allowed here")
174+
.emit();
175+
hir::ExprKind::Err
176+
}
167177
ExprKind::Path(ref qself, ref path) => {
168178
let qpath = self.lower_qpath(
169179
e.id,
@@ -863,7 +873,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
863873
// Return early in case of an ordinary assignment.
864874
fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool {
865875
match &lhs.kind {
866-
ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false,
876+
ExprKind::Array(..)
877+
| ExprKind::Struct(..)
878+
| ExprKind::Tup(..)
879+
| ExprKind::Underscore => false,
867880
// Check for tuple struct constructor.
868881
ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
869882
ExprKind::Paren(e) => {
@@ -943,6 +956,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
943956
assignments: &mut Vec<hir::Stmt<'hir>>,
944957
) -> &'hir hir::Pat<'hir> {
945958
match &lhs.kind {
959+
// Underscore pattern.
960+
ExprKind::Underscore => {
961+
return self.pat_without_dbm(lhs.span, hir::PatKind::Wild);
962+
}
946963
// Slice patterns.
947964
ExprKind::Array(elements) => {
948965
let (pats, rest) =

compiler/rustc_ast_passes/src/feature_gate.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
630630
gate_all!(const_trait_impl, "const trait impls are experimental");
631631
gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
632632
gate_all!(inline_const, "inline-const is experimental");
633-
gate_all!(destructuring_assignment, "destructuring assignments are unstable");
633+
if sess.parse_sess.span_diagnostic.err_count() == 0 {
634+
// Errors for `destructuring_assignment` can get quite noisy, especially where `_` is
635+
// involved, so we only emit errors where there are no other parsing errors.
636+
gate_all!(destructuring_assignment, "destructuring assignments are unstable");
637+
}
634638

635639
// All uses of `gate_all!` below this point were added in #65742,
636640
// and subsequently disabled (with the non-early gating readded).

compiler/rustc_ast_pretty/src/pprust/state.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,7 @@ impl<'a> State<'a> {
20682068
self.print_expr_maybe_paren(e, fake_prec);
20692069
}
20702070
}
2071+
ast::ExprKind::Underscore => self.s.word("_"),
20712072
ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
20722073
ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
20732074
ast::ExprKind::Break(opt_label, ref opt_expr) => {

compiler/rustc_parse/src/parser/expr.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,9 @@ impl<'a> Parser<'a> {
10891089
self.parse_yield_expr(attrs)
10901090
} else if self.eat_keyword(kw::Let) {
10911091
self.parse_let_expr(attrs)
1092+
} else if self.eat_keyword(kw::Underscore) {
1093+
self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span);
1094+
Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
10921095
} else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
10931096
// Don't complain about bare semicolons after unclosed braces
10941097
// recovery in order to keep the error count down. Fixing the

src/test/ui/cross/cross-file-errors/main.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ mod underscore;
33

44
fn main() {
55
underscore!();
6-
//~^ ERROR expected expression, found reserved identifier `_`
6+
//~^ ERROR `_` can only be used on the left-hand side of an assignment
7+
//~| ERROR destructuring assignments are unstable
78
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1-
error: expected expression, found reserved identifier `_`
1+
error[E0658]: destructuring assignments are unstable
22
--> $DIR/underscore.rs:8:9
33
|
44
LL | _
5-
| ^ expected expression
5+
| ^
66
|
77
::: $DIR/main.rs:5:5
88
|
99
LL | underscore!();
1010
| -------------- in this macro invocation
1111
|
12+
= note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
13+
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
1214
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
1315

14-
error: aborting due to previous error
16+
error: in expressions, `_` can only be used on the left-hand side of an assignment
17+
--> $DIR/underscore.rs:8:9
18+
|
19+
LL | _
20+
| ^ `_` not allowed here
21+
|
22+
::: $DIR/main.rs:5:5
23+
|
24+
LL | underscore!();
25+
| -------------- in this macro invocation
26+
|
27+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
28+
29+
error: aborting due to 2 previous errors
1530

31+
For more information about this error, try `rustc --explain E0658`.

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

+3
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ fn main() {
1414
Struct { a: TupleStruct((a, b), c), b: [d] } =
1515
Struct { a: TupleStruct((0, 1), 2), b: [3] };
1616
assert_eq!((a, b, c, d), (0, 1, 2, 3));
17+
18+
// unnested underscore: just discard
19+
_ = 1;
1720
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ fn main() {
99
let mut c;
1010
[a, .., b, c] = [1, 2, 3, 4, 5];
1111
assert_eq!((a, b, c), (1, 4, 5));
12+
[_, a, _] = [1, 2, 3];
13+
assert_eq!((a, b), (2, 4));
1214
[..] = [1, 2, 3];
1315
[c, ..] = [5, 6, 6];
1416
assert_eq!(c, 5);

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

+1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ fn main() {
44
let (mut a, mut b);
55
[a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern
66
[a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2
7+
[_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2
78
}

src/test/ui/destructuring-assignment/slice_destructure_fail.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ error[E0527]: pattern requires 3 elements but array has 2
1212
LL | [a, a, b] = [1, 2];
1313
| ^^^^^^^^^ expected 2 elements
1414

15-
error: aborting due to 2 previous errors
15+
error[E0527]: pattern requires 1 element but array has 2
16+
--> $DIR/slice_destructure_fail.rs:7:3
17+
|
18+
LL | [_] = [1, 2];
19+
| ^^^ expected 2 elements
20+
21+
error: aborting due to 3 previous errors
1622

1723
For more information about this error, try `rustc --explain E0527`.

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ fn main() {
1212
assert_eq!((a, b), (0, 1));
1313
Struct { a: b, b: a } = Struct { a: 1, b: 2 };
1414
assert_eq!((a,b), (2, 1));
15+
Struct { a: _, b } = Struct { a: 1, b: 2 };
16+
assert_eq!((a, b), (2, 2));
1517
Struct { a, .. } = Struct { a: 1, b: 3 };
16-
assert_eq!((a, b), (1, 1));
18+
assert_eq!((a, b), (1, 2));
1719
Struct { .. } = Struct { a: 1, b: 4 };
18-
assert_eq!((a, b), (1, 1));
20+
assert_eq!((a, b), (1, 2));
1921
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ fn main() {
99
let mut c;
1010
let d = Struct { a: 0, b: 1 };
1111
Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c`
12+
Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b`
13+
//~| ERROR expected identifier, found reserved identifier `_`
1214
Struct { a, ..d } = Struct { a: 1, b: 2 };
1315
//~^ ERROR functional record updates are not allowed in destructuring assignments
1416
Struct { a, .. }; //~ ERROR base expression required after `..`
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1+
error: expected identifier, found reserved identifier `_`
2+
--> $DIR/struct_destructure_fail.rs:12:17
3+
|
4+
LL | Struct { a, _ } = Struct { a: 1, b: 2 };
5+
| ------ ^ expected identifier, found reserved identifier
6+
| |
7+
| while parsing this struct
8+
19
error: functional record updates are not allowed in destructuring assignments
2-
--> $DIR/struct_destructure_fail.rs:12:19
10+
--> $DIR/struct_destructure_fail.rs:14:19
311
|
412
LL | Struct { a, ..d } = Struct { a: 1, b: 2 };
513
| ^ help: consider removing the trailing pattern
614

715
error: base expression required after `..`
8-
--> $DIR/struct_destructure_fail.rs:14:19
16+
--> $DIR/struct_destructure_fail.rs:16:19
917
|
1018
LL | Struct { a, .. };
1119
| ^ add a base expression here
@@ -16,6 +24,22 @@ error[E0026]: struct `Struct` does not have a field named `c`
1624
LL | Struct { a, b, c } = Struct { a: 0, b: 1 };
1725
| ^ struct `Struct` does not have this field
1826

19-
error: aborting due to 3 previous errors
27+
error[E0027]: pattern does not mention field `b`
28+
--> $DIR/struct_destructure_fail.rs:12:5
29+
|
30+
LL | Struct { a, _ } = Struct { a: 1, b: 2 };
31+
| ^^^^^^^^^^^^^^^ missing field `b`
32+
|
33+
help: include the missing field in the pattern
34+
|
35+
LL | Struct { a, b, _ } = Struct { a: 1, b: 2 };
36+
| ^^^
37+
help: if you don't care about this missing field, you can explicitly ignore it
38+
|
39+
LL | Struct { a, .., _ } = Struct { a: 1, b: 2 };
40+
| ^^^^
41+
42+
error: aborting due to 5 previous errors
2043

21-
For more information about this error, try `rustc --explain E0026`.
44+
Some errors have detailed explanations: E0026, E0027.
45+
For more information about an error, try `rustc --explain E0026`.

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

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ fn main() {
1616
assert_eq!((a, b), (2, 2));
1717
(b, ..) = (5, 6, 7);
1818
assert_eq!(b, 5);
19+
(a, _) = (8, 9);
20+
assert_eq!(a, 8);
1921

2022
// Test for a non-Copy type (String):
2123
let (mut c, mut d);

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

+1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ fn main() {
77
(a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern
88
(a, a, b) = (1, 2); //~ ERROR mismatched types
99
(C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment
10+
(_,) = (1, 2); //~ ERROR mismatched types
1011
}

src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr

+12-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,18 @@ LL | (C, ..) = (0,1);
2525
| |
2626
| cannot assign to this expression
2727

28-
error: aborting due to 3 previous errors
28+
error[E0308]: mismatched types
29+
--> $DIR/tuple_destructure_fail.rs:10:5
30+
|
31+
LL | (_,) = (1, 2);
32+
| ^^^^ ------ this expression has type `({integer}, {integer})`
33+
| |
34+
| expected a tuple with 2 elements, found one with 1 element
35+
|
36+
= note: expected type `({integer}, {integer})`
37+
found tuple `(_,)`
38+
39+
error: aborting due to 4 previous errors
2940

3041
Some errors have detailed explanations: E0070, E0308.
3142
For more information about an error, try `rustc --explain E0070`.

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ fn main() {
2323
assert_eq!((a, b), (0, 1));
2424
TupleStruct(a, .., b) = TupleStruct(1, 2);
2525
assert_eq!((a, b), (1, 2));
26+
TupleStruct(_, a) = TupleStruct(2, 2);
27+
assert_eq!((a, b), (2, 2));
2628
TupleStruct(..) = TupleStruct(3, 4);
27-
assert_eq!((a, b), (1, 2));
29+
assert_eq!((a, b), (2, 2));
2830
TupleStruct(5,6).assign(&mut a, &mut b);
2931
assert_eq!((a, b), (5, 6));
3032
Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8);

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

+4
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ fn main() {
2929

3030
TupleStruct(a, a, b) = TupleStruct(1, 2);
3131
//~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields
32+
TupleStruct(_) = TupleStruct(1, 2);
33+
//~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
3234
Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
3335
//~^ ERROR this pattern has 3 fields, but the corresponding tuple variant has 2 fields
36+
Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
37+
//~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
3438

3539
// Check if `test` is recognized as not a tuple struct but a function call:
3640
test() = TupleStruct(0, 0);

src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr

+23-5
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,58 @@ LL | struct TupleStruct<S, T>(S, T);
2323
LL | TupleStruct(a, a, b) = TupleStruct(1, 2);
2424
| ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
2525

26-
error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
26+
error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
2727
--> $DIR/tuple_struct_destructure_fail.rs:32:5
2828
|
29+
LL | struct TupleStruct<S, T>(S, T);
30+
| ------------------------------- tuple struct defined here
31+
...
32+
LL | TupleStruct(_) = TupleStruct(1, 2);
33+
| ^^^^^^^^^^^^^^ expected 2 fields, found 1
34+
35+
error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
36+
--> $DIR/tuple_struct_destructure_fail.rs:34:5
37+
|
2938
LL | SingleVariant(S, T)
3039
| ------------------- tuple variant defined here
3140
...
3241
LL | Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
3342
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
3443

44+
error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
45+
--> $DIR/tuple_struct_destructure_fail.rs:36:5
46+
|
47+
LL | SingleVariant(S, T)
48+
| ------------------- tuple variant defined here
49+
...
50+
LL | Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
51+
| ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1
52+
3553
error[E0070]: invalid left-hand side of assignment
36-
--> $DIR/tuple_struct_destructure_fail.rs:36:12
54+
--> $DIR/tuple_struct_destructure_fail.rs:40:12
3755
|
3856
LL | test() = TupleStruct(0, 0);
3957
| ------ ^
4058
| |
4159
| cannot assign to this expression
4260

4361
error[E0070]: invalid left-hand side of assignment
44-
--> $DIR/tuple_struct_destructure_fail.rs:38:14
62+
--> $DIR/tuple_struct_destructure_fail.rs:42:14
4563
|
4664
LL | (test)() = TupleStruct(0, 0);
4765
| -------- ^
4866
| |
4967
| cannot assign to this expression
5068

5169
error[E0070]: invalid left-hand side of assignment
52-
--> $DIR/tuple_struct_destructure_fail.rs:40:38
70+
--> $DIR/tuple_struct_destructure_fail.rs:44:38
5371
|
5472
LL | <Alias::<isize> as Test>::test() = TupleStruct(0, 0);
5573
| -------------------------------- ^
5674
| |
5775
| cannot assign to this expression
5876

59-
error: aborting due to 7 previous errors
77+
error: aborting due to 9 previous errors
6078

6179
Some errors have detailed explanations: E0023, E0070.
6280
For more information about an error, try `rustc --explain E0023`.

src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ struct S { x : u32 }
44

55
#[cfg(FALSE)]
66
fn foo() {
7+
_; //~ ERROR destructuring assignments are unstable
8+
79
S { x: 5, .. }; //~ ERROR destructuring assignments are unstable
810
}

0 commit comments

Comments
 (0)