Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make _ an expression, to discard values in destructuring assignments #79016

Merged
merged 1 commit into from
Nov 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,7 @@ impl Expr {
ExprKind::Field(..) => ExprPrecedence::Field,
ExprKind::Index(..) => ExprPrecedence::Index,
ExprKind::Range(..) => ExprPrecedence::Range,
ExprKind::Underscore => ExprPrecedence::Path,
ExprKind::Path(..) => ExprPrecedence::Path,
ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
ExprKind::Break(..) => ExprPrecedence::Break,
Expand Down Expand Up @@ -1324,6 +1325,8 @@ pub enum ExprKind {
Index(P<Expr>, P<Expr>),
/// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment).
Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
/// An underscore, used in destructuring assignment to ignore a value.
Underscore,

/// Variable reference, possibly containing `::` and/or type
/// parameters (e.g., `foo::bar::<baz>`).
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
visit_opt(e1, |e1| vis.visit_expr(e1));
visit_opt(e2, |e2| vis.visit_expr(e2));
}
ExprKind::Underscore => {}
ExprKind::Path(qself, path) => {
vis.visit_qself(qself);
vis.visit_path(path);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
walk_list!(visitor, visit_expr, start);
walk_list!(visitor, visit_expr, end);
}
ExprKind::Underscore => {}
ExprKind::Path(ref maybe_qself, ref path) => {
if let Some(ref qself) = *maybe_qself {
visitor.visit_ty(&qself.ty);
Expand Down
19 changes: 18 additions & 1 deletion compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
ExprKind::Range(ref e1, ref e2, lims) => {
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
}
ExprKind::Underscore => {
self.sess
.struct_span_err(
e.span,
"in expressions, `_` can only be used on the left-hand side of an assignment",
)
.span_label(e.span, "`_` not allowed here")
.emit();
hir::ExprKind::Err
}
ExprKind::Path(ref qself, ref path) => {
let qpath = self.lower_qpath(
e.id,
Expand Down Expand Up @@ -863,7 +873,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Return early in case of an ordinary assignment.
fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool {
match &lhs.kind {
ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false,
ExprKind::Array(..)
| ExprKind::Struct(..)
| ExprKind::Tup(..)
| ExprKind::Underscore => false,
// Check for tuple struct constructor.
ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
ExprKind::Paren(e) => {
Expand Down Expand Up @@ -943,6 +956,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
assignments: &mut Vec<hir::Stmt<'hir>>,
) -> &'hir hir::Pat<'hir> {
match &lhs.kind {
// Underscore pattern.
ExprKind::Underscore => {
return self.pat_without_dbm(lhs.span, hir::PatKind::Wild);
}
// Slice patterns.
ExprKind::Array(elements) => {
let (pats, rest) =
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(const_trait_impl, "const trait impls are experimental");
gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
gate_all!(inline_const, "inline-const is experimental");
gate_all!(destructuring_assignment, "destructuring assignments are unstable");
if sess.parse_sess.span_diagnostic.err_count() == 0 {
// Errors for `destructuring_assignment` can get quite noisy, especially where `_` is
// involved, so we only emit errors where there are no other parsing errors.
gate_all!(destructuring_assignment, "destructuring assignments are unstable");
}

// All uses of `gate_all!` below this point were added in #65742,
// and subsequently disabled (with the non-early gating readded).
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2068,6 +2068,7 @@ impl<'a> State<'a> {
self.print_expr_maybe_paren(e, fake_prec);
}
}
ast::ExprKind::Underscore => self.s.word("_"),
ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
ast::ExprKind::Break(opt_label, ref opt_expr) => {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,9 @@ impl<'a> Parser<'a> {
self.parse_yield_expr(attrs)
} else if self.eat_keyword(kw::Let) {
self.parse_let_expr(attrs)
} else if self.eat_keyword(kw::Underscore) {
self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span);
Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
} else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
// Don't complain about bare semicolons after unclosed braces
// recovery in order to keep the error count down. Fixing the
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/cross/cross-file-errors/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ mod underscore;

fn main() {
underscore!();
//~^ ERROR expected expression, found reserved identifier `_`
//~^ ERROR `_` can only be used on the left-hand side of an assignment
//~| ERROR destructuring assignments are unstable
}
22 changes: 19 additions & 3 deletions src/test/ui/cross/cross-file-errors/main.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
error: expected expression, found reserved identifier `_`
error[E0658]: destructuring assignments are unstable
--> $DIR/underscore.rs:8:9
|
LL | _
| ^ expected expression
| ^
|
::: $DIR/main.rs:5:5
|
LL | underscore!();
| -------------- in this macro invocation
|
= note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error
error: in expressions, `_` can only be used on the left-hand side of an assignment
--> $DIR/underscore.rs:8:9
|
LL | _
| ^ `_` not allowed here
|
::: $DIR/main.rs:5:5
|
LL | underscore!();
| -------------- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0658`.
3 changes: 3 additions & 0 deletions src/test/ui/destructuring-assignment/nested_destructure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ fn main() {
Struct { a: TupleStruct((a, b), c), b: [d] } =
Struct { a: TupleStruct((0, 1), 2), b: [3] };
assert_eq!((a, b, c, d), (0, 1, 2, 3));

// unnested underscore: just discard
_ = 1;
}
2 changes: 2 additions & 0 deletions src/test/ui/destructuring-assignment/slice_destructure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ fn main() {
let mut c;
[a, .., b, c] = [1, 2, 3, 4, 5];
assert_eq!((a, b, c), (1, 4, 5));
[_, a, _] = [1, 2, 3];
assert_eq!((a, b), (2, 4));
[..] = [1, 2, 3];
[c, ..] = [5, 6, 6];
assert_eq!(c, 5);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ fn main() {
let (mut a, mut b);
[a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern
[a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2
[_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ error[E0527]: pattern requires 3 elements but array has 2
LL | [a, a, b] = [1, 2];
| ^^^^^^^^^ expected 2 elements

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

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0527`.
6 changes: 4 additions & 2 deletions src/test/ui/destructuring-assignment/struct_destructure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ fn main() {
assert_eq!((a, b), (0, 1));
Struct { a: b, b: a } = Struct { a: 1, b: 2 };
assert_eq!((a,b), (2, 1));
Struct { a: _, b } = Struct { a: 1, b: 2 };
assert_eq!((a, b), (2, 2));
Struct { a, .. } = Struct { a: 1, b: 3 };
assert_eq!((a, b), (1, 1));
assert_eq!((a, b), (1, 2));
Struct { .. } = Struct { a: 1, b: 4 };
assert_eq!((a, b), (1, 1));
assert_eq!((a, b), (1, 2));
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ fn main() {
let mut c;
let d = Struct { a: 0, b: 1 };
Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c`
Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b`
//~| ERROR expected identifier, found reserved identifier `_`
Struct { a, ..d } = Struct { a: 1, b: 2 };
//~^ ERROR functional record updates are not allowed in destructuring assignments
Struct { a, .. }; //~ ERROR base expression required after `..`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
error: expected identifier, found reserved identifier `_`
--> $DIR/struct_destructure_fail.rs:12:17
|
LL | Struct { a, _ } = Struct { a: 1, b: 2 };
| ------ ^ expected identifier, found reserved identifier
| |
| while parsing this struct

error: functional record updates are not allowed in destructuring assignments
--> $DIR/struct_destructure_fail.rs:12:19
--> $DIR/struct_destructure_fail.rs:14:19
|
LL | Struct { a, ..d } = Struct { a: 1, b: 2 };
| ^ help: consider removing the trailing pattern

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

error: aborting due to 3 previous errors
error[E0027]: pattern does not mention field `b`
--> $DIR/struct_destructure_fail.rs:12:5
|
LL | Struct { a, _ } = Struct { a: 1, b: 2 };
| ^^^^^^^^^^^^^^^ missing field `b`
|
help: include the missing field in the pattern
|
LL | Struct { a, b, _ } = Struct { a: 1, b: 2 };
| ^^^
help: if you don't care about this missing field, you can explicitly ignore it
|
LL | Struct { a, .., _ } = Struct { a: 1, b: 2 };
| ^^^^

error: aborting due to 5 previous errors

For more information about this error, try `rustc --explain E0026`.
Some errors have detailed explanations: E0026, E0027.
For more information about an error, try `rustc --explain E0026`.
2 changes: 2 additions & 0 deletions src/test/ui/destructuring-assignment/tuple_destructure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ fn main() {
assert_eq!((a, b), (2, 2));
(b, ..) = (5, 6, 7);
assert_eq!(b, 5);
(a, _) = (8, 9);
assert_eq!(a, 8);

// Test for a non-Copy type (String):
let (mut c, mut d);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ fn main() {
(a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern
(a, a, b) = (1, 2); //~ ERROR mismatched types
(C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment
(_,) = (1, 2); //~ ERROR mismatched types
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,18 @@ LL | (C, ..) = (0,1);
| |
| cannot assign to this expression

error: aborting due to 3 previous errors
error[E0308]: mismatched types
--> $DIR/tuple_destructure_fail.rs:10:5
|
LL | (_,) = (1, 2);
| ^^^^ ------ this expression has type `({integer}, {integer})`
| |
| expected a tuple with 2 elements, found one with 1 element
|
= note: expected type `({integer}, {integer})`
found tuple `(_,)`

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0070, E0308.
For more information about an error, try `rustc --explain E0070`.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ fn main() {
assert_eq!((a, b), (0, 1));
TupleStruct(a, .., b) = TupleStruct(1, 2);
assert_eq!((a, b), (1, 2));
TupleStruct(_, a) = TupleStruct(2, 2);
assert_eq!((a, b), (2, 2));
TupleStruct(..) = TupleStruct(3, 4);
assert_eq!((a, b), (1, 2));
assert_eq!((a, b), (2, 2));
TupleStruct(5,6).assign(&mut a, &mut b);
assert_eq!((a, b), (5, 6));
Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ fn main() {

TupleStruct(a, a, b) = TupleStruct(1, 2);
//~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields
TupleStruct(_) = TupleStruct(1, 2);
//~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
//~^ ERROR this pattern has 3 fields, but the corresponding tuple variant has 2 fields
Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
//~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields

// Check if `test` is recognized as not a tuple struct but a function call:
test() = TupleStruct(0, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,58 @@ LL | struct TupleStruct<S, T>(S, T);
LL | TupleStruct(a, a, b) = TupleStruct(1, 2);
| ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3

error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
--> $DIR/tuple_struct_destructure_fail.rs:32:5
|
LL | struct TupleStruct<S, T>(S, T);
| ------------------------------- tuple struct defined here
...
LL | TupleStruct(_) = TupleStruct(1, 2);
| ^^^^^^^^^^^^^^ expected 2 fields, found 1

error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
--> $DIR/tuple_struct_destructure_fail.rs:34:5
|
LL | SingleVariant(S, T)
| ------------------- tuple variant defined here
...
LL | Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3

error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
--> $DIR/tuple_struct_destructure_fail.rs:36:5
|
LL | SingleVariant(S, T)
| ------------------- tuple variant defined here
...
LL | Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
| ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1

error[E0070]: invalid left-hand side of assignment
--> $DIR/tuple_struct_destructure_fail.rs:36:12
--> $DIR/tuple_struct_destructure_fail.rs:40:12
|
LL | test() = TupleStruct(0, 0);
| ------ ^
| |
| cannot assign to this expression

error[E0070]: invalid left-hand side of assignment
--> $DIR/tuple_struct_destructure_fail.rs:38:14
--> $DIR/tuple_struct_destructure_fail.rs:42:14
|
LL | (test)() = TupleStruct(0, 0);
| -------- ^
| |
| cannot assign to this expression

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

error: aborting due to 7 previous errors
error: aborting due to 9 previous errors

Some errors have detailed explanations: E0023, E0070.
For more information about an error, try `rustc --explain E0023`.
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ struct S { x : u32 }

#[cfg(FALSE)]
fn foo() {
_; //~ ERROR destructuring assignments are unstable

S { x: 5, .. }; //~ ERROR destructuring assignments are unstable
}
Loading