Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c027844
Fill in things needed to stabilize int_error_matching
eopb Oct 6, 2020
83d294f
Bring char along with InvalidDigit
eopb Oct 6, 2020
8eaf0de
Remove incorrect plural
eopb Oct 6, 2020
1e7e2e4
remove OnlySign in favour of InvalidDigit
eopb Oct 6, 2020
f233abb
Add comment to helper function
eopb Oct 7, 2020
91a9f83
Define `fs::hard_link` to not follow symlinks.
sunfishcode Oct 16, 2020
23a5c21
Fix a typo in a comment.
sunfishcode Oct 19, 2020
ce00b3e
Use `link` on platforms which lack `linkat`.
sunfishcode Oct 19, 2020
d0178b4
Make it platform-specific whether `hard_link` follows symlinks.
sunfishcode Oct 20, 2020
6249cda
Disable use of `linkat` on Android as well.
sunfishcode Oct 23, 2020
199c361
Fix spelling eror
eopb Oct 26, 2020
69c301f
Small reword
eopb Oct 26, 2020
75e6dee
asci -> ASCII
eopb Oct 26, 2020
ad2d93d
Apply suggested changes
eopb Oct 26, 2020
e750238
Fix typo
eopb Oct 26, 2020
e099138
BTreeMap: stop mistaking node for an orderly place
ssomers Oct 26, 2020
b0df3f7
fix some incorrect aliasing in the BTree
RalfJung Oct 28, 2020
54a0a98
ci: gate on aarch64-gnu passing tests
pietroalbini Oct 22, 2020
1274fae
doc/rustc: promote aarch64-unknown-linux-gnu to tier 1
pietroalbini Oct 22, 2020
874cbb8
ci: build docs for aarch64-unknown-linux-gnu
pietroalbini Oct 22, 2020
eed0ceb
Recognize `private_intra_doc_links` as a lint
jyn514 Oct 19, 2020
47b21b8
Add PRIVATE_INTRA_DOC_LINKS to rustdoc special-casing
jyn514 Nov 5, 2020
8a8ee1a
inliner: Use substs_for_mir_body
tmiasko Nov 6, 2020
3a7a997
Implement destructuring assignment for tuples
fanzier Nov 4, 2020
67d0db6
Fix handling of item names for HIR
jyn514 Oct 22, 2020
f60fd49
Remove unused `from_hir` call
jyn514 Nov 7, 2020
9dc5dfb
Fix tab focus on restyled switches
notriddle Nov 8, 2020
b13817a
Avoid overlapping cfg attributes when both macOS and aarch64
shepmaster Nov 8, 2020
3904617
Nicer hunk headers for rust files
bjorn3 Nov 8, 2020
d69ee57
Rollup merge of #77640 - ethanboxx:int_error_matching_attempt_2, r=Ko…
Dylan-DPC Nov 9, 2020
41134be
Rollup merge of #78026 - sunfishcode:symlink-hard-link, r=dtolnay
Dylan-DPC Nov 9, 2020
b9671ae
Rollup merge of #78114 - jyn514:private, r=oli-obk
Dylan-DPC Nov 9, 2020
50086af
Rollup merge of #78228 - pietroalbini:finally, r=Mark-Simulacrum
Dylan-DPC Nov 9, 2020
12c5f78
Rollup merge of #78345 - jyn514:proper-names, r=varkor
Dylan-DPC Nov 9, 2020
4e5b7ad
Rollup merge of #78437 - ssomers:btree_no_ord_at_node_level, r=Mark-S…
Dylan-DPC Nov 9, 2020
5639d97
Rollup merge of #78476 - RalfJung:btree-alias, r=Mark-Simulacrum
Dylan-DPC Nov 9, 2020
b4589a8
Rollup merge of #78674 - tmiasko:inline-substs-for-mir-body, r=oli-obk
Dylan-DPC Nov 9, 2020
abaa78b
Rollup merge of #78748 - fanzier:tuple-assignment, r=petrochenkov
Dylan-DPC Nov 9, 2020
479817a
Rollup merge of #78868 - notriddle:master, r=GuillaumeGomez
Dylan-DPC Nov 9, 2020
a8beaa3
Rollup merge of #78878 - shepmaster:intersecting-ignores, r=Mark-Simu…
Dylan-DPC Nov 9, 2020
92adac9
Rollup merge of #78882 - bjorn3:nicer_hunk_headers, r=Mark-Simulacrum
Dylan-DPC Nov 9, 2020
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
131 changes: 130 additions & 1 deletion compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_session::parse::feature_err;
use rustc_span::hygiene::ForLoopLoc;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol};
Expand Down Expand Up @@ -146,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
}
ExprKind::Assign(ref el, ref er, span) => {
hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span)
self.lower_expr_assign(el, er, span, e.span)
}
ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
self.lower_binop(op),
Expand Down Expand Up @@ -840,6 +841,134 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}

/// Destructure the LHS of complex assignments.
/// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`.
fn lower_expr_assign(
&mut self,
lhs: &Expr,
rhs: &Expr,
eq_sign_span: Span,
whole_span: Span,
) -> hir::ExprKind<'hir> {
// Return early in case of an ordinary assignment.
fn is_ordinary(lhs: &Expr) -> bool {
match &lhs.kind {
ExprKind::Tup(..) => false,
ExprKind::Paren(e) => {
match e.kind {
// We special-case `(..)` for consistency with patterns.
ExprKind::Range(None, None, RangeLimits::HalfOpen) => false,
_ => is_ordinary(e),
}
}
_ => true,
}
}
if is_ordinary(lhs) {
return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span);
}
if !self.sess.features_untracked().destructuring_assignment {
feature_err(
&self.sess.parse_sess,
sym::destructuring_assignment,
eq_sign_span,
"destructuring assignments are unstable",
)
.span_label(lhs.span, "cannot assign to this expression")
.emit();
}

let mut assignments = vec![];

// The LHS becomes a pattern: `(lhs1, lhs2)`.
let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments);
let rhs = self.lower_expr(rhs);

// Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
let destructure_let = self.stmt_let_pat(
ThinVec::new(),
whole_span,
Some(rhs),
pat,
hir::LocalSource::AssignDesugar(eq_sign_span),
);

// `a = lhs1; b = lhs2;`.
let stmts = self
.arena
.alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));

// Wrap everything in a block.
hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
}

/// Convert the LHS of a destructuring assignment to a pattern.
/// Each sub-assignment is recorded in `assignments`.
fn destructure_assign(
&mut self,
lhs: &Expr,
eq_sign_span: Span,
assignments: &mut Vec<hir::Stmt<'hir>>,
) -> &'hir hir::Pat<'hir> {
match &lhs.kind {
// Tuples.
ExprKind::Tup(elements) => {
let (pats, rest) =
self.destructure_sequence(elements, "tuple", eq_sign_span, assignments);
let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0));
return self.pat_without_dbm(lhs.span, tuple_pat);
}
ExprKind::Paren(e) => {
// We special-case `(..)` for consistency with patterns.
if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
let tuple_pat = hir::PatKind::Tuple(&[], Some(0));
return self.pat_without_dbm(lhs.span, tuple_pat);
} else {
return self.destructure_assign(e, eq_sign_span, assignments);
}
}
_ => {}
}
// Treat all other cases as normal lvalue.
let ident = Ident::new(sym::lhs, lhs.span);
let (pat, binding) = self.pat_ident(lhs.span, ident);
let ident = self.expr_ident(lhs.span, ident, binding);
let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span);
let expr = self.expr(lhs.span, assign, ThinVec::new());
assignments.push(self.stmt_expr(lhs.span, expr));
pat
}

/// Destructure a sequence of expressions occurring on the LHS of an assignment.
/// Such a sequence occurs in a tuple (struct)/slice.
/// Return a sequence of corresponding patterns, and the index and the span of `..` if it
/// exists.
/// Each sub-assignment is recorded in `assignments`.
fn destructure_sequence(
&mut self,
elements: &[AstP<Expr>],
ctx: &str,
eq_sign_span: Span,
assignments: &mut Vec<hir::Stmt<'hir>>,
) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) {
let mut rest = None;
let elements =
self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
// Check for `..` pattern.
if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
if let Some((_, prev_span)) = rest {
self.ban_extra_rest_pat(e.span, prev_span, ctx);
} else {
rest = Some((i, e.span));
}
None
} else {
Some(self.destructure_assign(e, eq_sign_span, assignments))
}
}));
(elements, rest)
}

/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
let e1 = self.lower_expr_mut(e1);
Expand Down
17 changes: 16 additions & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2531,6 +2531,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir_id,
kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None),
span,
default_binding_modes: true,
}),
hir_id,
)
Expand All @@ -2541,7 +2542,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}

fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span })
self.arena.alloc(hir::Pat {
hir_id: self.next_id(),
kind,
span,
default_binding_modes: true,
})
}

fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
self.arena.alloc(hir::Pat {
hir_id: self.next_id(),
kind,
span,
default_binding_modes: false,
})
}

fn ty_path(
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_ast_lowering/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

/// Construct a `Pat` with the `HirId` of `p.id` lowered.
fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
self.arena.alloc(hir::Pat {
hir_id: self.lower_node_id(p.id),
kind,
span: p.span,
default_binding_modes: true,
})
}

/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
self.diagnostic()
.struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
.span_label(sp, &format!("can only be used once per {} pattern", ctx))
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,9 @@ declare_features! (
/// Allows unsized fn parameters.
(active, unsized_fn_params, "1.49.0", Some(48055), None),

/// Allows the use of destructuring assignments.
(active, destructuring_assignment, "1.49.0", Some(71126), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,9 @@ pub struct Pat<'hir> {
pub hir_id: HirId,
pub kind: PatKind<'hir>,
pub span: Span,
// Whether to use default binding modes.
// At present, this is false only for destructuring assignment.
pub default_binding_modes: bool,
}

impl Pat<'_> {
Expand Down Expand Up @@ -1680,6 +1683,9 @@ pub enum LocalSource {
AsyncFn,
/// A desugared `<expr>.await`.
AwaitDesugar,
/// A desugared `expr = expr`, where the LHS is a tuple, struct or array.
/// The span is that of the `=` sign.
AssignDesugar(Span),
}

/// Hints at the original code for a `match _ { .. }`.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
hir::LocalSource::AsyncFn => ("async fn binding", None),
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
};
self.check_irrefutable(&loc.pat, msg, sp);
self.check_patterns(&loc.pat);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ symbols! {
deref_mut,
deref_target,
derive,
destructuring_assignment,
diagnostic,
direct,
discriminant_kind,
Expand Down
37 changes: 11 additions & 26 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,39 +718,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}

fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool {
match &expr.kind {
ExprKind::Array(comps) | ExprKind::Tup(comps) => {
comps.iter().all(|e| self.is_destructuring_place_expr(e))
}
ExprKind::Struct(_path, fields, rest) => {
rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true)
&& fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
}
_ => expr.is_syntactic_place_expr(),
}
}

pub(crate) fn check_lhs_assignable(
&self,
lhs: &'tcx hir::Expr<'tcx>,
err_code: &'static str,
expr_span: &Span,
) {
if !lhs.is_syntactic_place_expr() {
// FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
let mut err = self.tcx.sess.struct_span_err_with_code(
*expr_span,
"invalid left-hand side of assignment",
DiagnosticId::Error(err_code.into()),
);
err.span_label(lhs.span, "cannot assign to this expression");
if self.is_destructuring_place_expr(lhs) {
err.note("destructuring assignments are not currently supported");
err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372");
}
err.emit();
if lhs.is_syntactic_place_expr() {
return;
}

// FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
let mut err = self.tcx.sess.struct_span_err_with_code(
*expr_span,
"invalid left-hand side of assignment",
DiagnosticId::Error(err_code.into()),
);
err.span_label(lhs.span, "cannot assign to this expression");
err.emit();
}

/// Type check assignment expression `expr` of form `lhs = rhs`.
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_typeck/src/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
///
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
// When we perform destructuring assignment, we disable default match bindings, which are
// unintuitive in this context.
if !pat.default_binding_modes {
return AdjustMode::Reset;
}
match &pat.kind {
// Type checking these product-like types successfully always require
// that the expected type be of those types and not reference types.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_typeck/src/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) {
debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat);
ignore_err!(self.with_mc(|mc| {
mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id }| {
mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id, .. }| {
// `ref x` pattern
if let PatKind::Binding(..) = kind {
if let Some(ty::BindByReference(mutbl)) =
Expand Down
6 changes: 4 additions & 2 deletions src/test/ui/bad/bad-expr-lhs.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
fn main() {
1 = 2; //~ ERROR invalid left-hand side of assignment
1 += 2; //~ ERROR invalid left-hand side of assignment
(1, 2) = (3, 4); //~ ERROR invalid left-hand side of assignment
(1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable
//~| ERROR invalid left-hand side of assignment
//~| ERROR invalid left-hand side of assignment

let (a, b) = (1, 2);
(a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment
(a, b) = (3, 4); //~ ERROR destructuring assignments are unstable

None = Some(3); //~ ERROR invalid left-hand side of assignment
}
Loading