diff options
| author | Fabian Zaiser <fabian.zaiser@gmail.com> | 2020-11-07 14:28:55 +0000 |
|---|---|---|
| committer | Fabian Zaiser <fabian.zaiser@gmail.com> | 2020-11-11 12:10:52 +0000 |
| commit | de84ad95b4b097e504171c7c606bf3715803c13b (patch) | |
| tree | 79e31aeb6e1fa6c147157399446837511900dcfc /compiler/rustc_ast_lowering/src/expr.rs | |
| parent | cf9cf7c923eb01146971429044f216a3ca905e06 (diff) | |
| download | rust-de84ad95b4b097e504171c7c606bf3715803c13b.tar.gz rust-de84ad95b4b097e504171c7c606bf3715803c13b.zip | |
Implement destructuring assignment for structs and slices
Co-authored-by: varkor <github@varkor.com>
Diffstat (limited to 'compiler/rustc_ast_lowering/src/expr.rs')
| -rw-r--r-- | compiler/rustc_ast_lowering/src/expr.rs | 126 |
1 files changed, 119 insertions, 7 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 1f2aba2b27e..330776fc8c5 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -187,8 +187,18 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), - ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { - let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); + ExprKind::Struct(ref path, ref fields, ref rest) => { + let rest = match rest { + StructRest::Base(e) => Some(self.lower_expr(e)), + StructRest::Rest(sp) => { + self.sess + .struct_span_err(*sp, "base expression required after `..`") + .span_label(*sp, "add a base expression here") + .emit(); + Some(&*self.arena.alloc(self.expr_err(*sp))) + } + StructRest::None => None, + }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( e.id, @@ -198,7 +208,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::disallowed(), )), self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), - maybe_expr, + rest, ) } ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), @@ -851,20 +861,22 @@ impl<'hir> LoweringContext<'_, 'hir> { whole_span: Span, ) -> hir::ExprKind<'hir> { // Return early in case of an ordinary assignment. - fn is_ordinary(lhs: &Expr) -> bool { + fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool { match &lhs.kind { - ExprKind::Tup(..) => false, + ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false, + // Check for tuple struct constructor. + ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(), ExprKind::Paren(e) => { match e.kind { // We special-case `(..)` for consistency with patterns. ExprKind::Range(None, None, RangeLimits::HalfOpen) => false, - _ => is_ordinary(e), + _ => is_ordinary(lower_ctx, e), } } _ => true, } } - if is_ordinary(lhs) { + if is_ordinary(self, lhs) { return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span); } if !self.sess.features_untracked().destructuring_assignment { @@ -902,6 +914,26 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None) } + /// If the given expression is a path to a tuple struct, returns that path. + /// It is not a complete check, but just tries to reject most paths early + /// if they are not tuple structs. + /// Type checking will take care of the full validation later. + fn extract_tuple_struct_path<'a>(&mut self, expr: &'a Expr) -> Option<&'a Path> { + // For tuple struct destructuring, it must be a non-qualified path (like in patterns). + if let ExprKind::Path(None, path) = &expr.kind { + // Does the path resolves to something disallowed in a tuple struct/variant pattern? + if let Some(partial_res) = self.resolver.get_partial_res(expr.id) { + if partial_res.unresolved_segments() == 0 + && !partial_res.base_res().expected_in_tuple_struct_pat() + { + return None; + } + } + return Some(path); + } + None + } + /// Convert the LHS of a destructuring assignment to a pattern. /// Each sub-assignment is recorded in `assignments`. fn destructure_assign( @@ -911,6 +943,86 @@ impl<'hir> LoweringContext<'_, 'hir> { assignments: &mut Vec<hir::Stmt<'hir>>, ) -> &'hir hir::Pat<'hir> { match &lhs.kind { + // Slice patterns. + ExprKind::Array(elements) => { + let (pats, rest) = + self.destructure_sequence(elements, "slice", eq_sign_span, assignments); + let slice_pat = if let Some((i, span)) = rest { + let (before, after) = pats.split_at(i); + hir::PatKind::Slice( + before, + Some(self.pat_without_dbm(span, hir::PatKind::Wild)), + after, + ) + } else { + hir::PatKind::Slice(pats, None, &[]) + }; + return self.pat_without_dbm(lhs.span, slice_pat); + } + // Tuple structs. + ExprKind::Call(callee, args) => { + if let Some(path) = self.extract_tuple_struct_path(callee) { + let (pats, rest) = self.destructure_sequence( + args, + "tuple struct or variant", + eq_sign_span, + assignments, + ); + let qpath = self.lower_qpath( + callee.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + // Destructure like a tuple struct. + let tuple_struct_pat = + hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); + return self.pat_without_dbm(lhs.span, tuple_struct_pat); + } + } + // Structs. + ExprKind::Struct(path, fields, rest) => { + let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { + let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); + hir::FieldPat { + hir_id: self.next_id(), + ident: f.ident, + pat, + is_shorthand: f.is_shorthand, + span: f.span, + } + })); + let qpath = self.lower_qpath( + lhs.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + let fields_omitted = match rest { + StructRest::Base(e) => { + self.sess + .struct_span_err( + e.span, + "functional record updates are not allowed in destructuring \ + assignments", + ) + .span_suggestion( + e.span, + "consider removing the trailing pattern", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit(); + true + } + StructRest::Rest(_) => true, + StructRest::None => false, + }; + let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); + return self.pat_without_dbm(lhs.span, struct_pat); + } // Tuples. ExprKind::Tup(elements) => { let (pats, rest) = |
