diff --git a/Cargo.lock b/Cargo.lock index 45a19fd79634e..c72c7b8481b5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4329,7 +4329,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.24" +version = "1.4.25" dependencies = [ "annotate-snippets 0.6.1", "anyhow", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 6961905038ffa..3e953729aabec 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1061,7 +1061,7 @@ pub struct Expr { // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 112); +rustc_data_structures::static_assert_size!(Expr, 120); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1218,6 +1218,16 @@ pub enum RangeLimits { Closed, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum StructRest { + /// `..x`. + Base(P), + /// `..`. + Rest(Span), + /// No trailing `..` or expression. + None, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// A `box x` expression. @@ -1312,7 +1322,7 @@ pub enum ExprKind { Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). Index(P, P), - /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`). + /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment). Range(Option>, Option>, RangeLimits), /// Variable reference, possibly containing `::` and/or type @@ -1340,9 +1350,8 @@ pub enum ExprKind { /// A struct literal expression. /// - /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, - /// where `base` is the `Option`. - Struct(Path, Vec, Option>), + /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`. + Struct(Path, Vec, StructRest), /// An array literal constructed from one repeated element. /// diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 2697b0175728c..26097980e8be4 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1288,7 +1288,11 @@ pub fn noop_visit_expr( ExprKind::Struct(path, fields, expr) => { vis.visit_path(path); fields.flat_map_in_place(|field| vis.flat_map_field(field)); - visit_opt(expr, |expr| vis.visit_expr(expr)); + match expr { + StructRest::Base(expr) => vis.visit_expr(expr), + StructRest::Rest(_span) => {} + StructRest::None => {} + } } ExprKind::Paren(expr) => { vis.visit_expr(expr); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 72e901fb90f81..49b521afcdc78 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -719,7 +719,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Struct(ref path, ref fields, ref optional_base) => { visitor.visit_path(path, expression.id); walk_list!(visitor, visit_field, fields); - walk_list!(visitor, visit_expr, optional_base); + match optional_base { + StructRest::Base(expr) => visitor.visit_expr(expr), + StructRest::Rest(_span) => {} + StructRest::None => {} + } } ExprKind::Tup(ref subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 1f2aba2b27e68..330776fc8c598 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 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) = diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 617cacee0e7f1..d353bc19f7aef 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1096,8 +1096,18 @@ impl<'hir> LoweringContext<'_, 'hir> { // Check if this is a binding pattern, if so, we can optimize and avoid adding a // `let = __argN;` statement. In this case, we do not rename the parameter. let (ident, is_simple_parameter) = match parameter.pat.kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, _) => { - (ident, true) + hir::PatKind::Binding( + hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, + _, + ident, + _, + ) => (ident, true), + // For `ref mut` or wildcard arguments, we can't reuse the binding, but + // we can keep the same name for the parameter. + // This lets rustdoc render it correctly in documentation. + hir::PatKind::Binding(_, _, ident, _) => (ident, false), + hir::PatKind::Wild => { + (Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false) } _ => { // Replace the ident for bindings that aren't simple. diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index f20084497671f..2831675cb3671 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,6 +630,7 @@ 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"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a64014f5acbb8..a566200c33896 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1729,7 +1729,7 @@ impl<'a> State<'a> { &mut self, path: &ast::Path, fields: &[ast::Field], - wth: &Option>, + rest: &ast::StructRest, attrs: &[ast::Attribute], ) { self.print_path(path, true, 0); @@ -1750,22 +1750,21 @@ impl<'a> State<'a> { }, |f| f.span, ); - match *wth { - Some(ref expr) => { + match rest { + ast::StructRest::Base(_) | ast::StructRest::Rest(_) => { self.ibox(INDENT_UNIT); if !fields.is_empty() { self.s.word(","); self.s.space(); } self.s.word(".."); - self.print_expr(expr); - self.end(); - } - _ => { - if !fields.is_empty() { - self.s.word(",") + if let ast::StructRest::Base(ref expr) = *rest { + self.print_expr(expr); } + self.end(); } + ast::StructRest::None if !fields.is_empty() => self.s.word(","), + _ => {} } self.s.word("}"); } @@ -1891,8 +1890,8 @@ impl<'a> State<'a> { ast::ExprKind::Repeat(ref element, ref count) => { self.print_expr_repeat(element, count, attrs); } - ast::ExprKind::Struct(ref path, ref fields, ref wth) => { - self.print_expr_struct(path, &fields[..], wth, attrs); + ast::ExprKind::Struct(ref path, ref fields, ref rest) => { + self.print_expr_struct(path, &fields[..], rest, attrs); } ast::ExprKind::Tup(ref exprs) => { self.print_expr_tup(&exprs[..], attrs); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index d856280158f2d..b5d279eeb6f2f 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -12,8 +12,8 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::{bug, span_bug}; use rustc_span::{Pos, Span}; use rustc_target::abi::*; use rustc_target::asm::*; @@ -260,6 +260,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { InlineAsmArch::Nvptx64 => {} InlineAsmArch::Hexagon => {} InlineAsmArch::Mips | InlineAsmArch::Mips64 => {} + InlineAsmArch::SpirV => {} } } if !options.contains(InlineAsmOptions::NOMEM) { @@ -518,6 +519,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk", + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + } } .to_string(), } @@ -580,6 +584,9 @@ fn modifier_to_llvm( _ => unreachable!(), }, InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + } } } @@ -619,6 +626,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + } } } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 1c9bfb902d61a..30f0fc6cddfa2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -298,7 +298,7 @@ impl<'a> ExtCtxt<'a> { path: ast::Path, fields: Vec, ) -> P { - self.expr(span, ast::ExprKind::Struct(path, fields, None)) + self.expr(span, ast::ExprKind::Struct(path, fields, ast::StructRest::None)) } pub fn expr_struct_ident( &self, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 193247af584bb..298cfcc254c86 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -484,4 +484,9 @@ impl Res { pub fn matches_ns(&self, ns: Namespace) -> bool { self.ns().map_or(true, |actual_ns| actual_ns == ns) } + + /// Returns whether such a resolved path can occur in a tuple struct/variant pattern + pub fn expected_in_tuple_struct_pat(&self) -> bool { + matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..)) + } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index c2a13d4b0dec1..188bf227c4249 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2087,7 +2087,7 @@ impl<'a> Parser<'a> { recover: bool, ) -> PResult<'a, P> { let mut fields = Vec::new(); - let mut base = None; + let mut base = ast::StructRest::None; let mut recover_async = false; attrs.extend(self.parse_inner_attributes()?); @@ -2102,8 +2102,14 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { let exp_span = self.prev_token.span; + // We permit `.. }` on the left-hand side of a destructuring assignment. + if self.check(&token::CloseDelim(token::Brace)) { + self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); + base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); + break; + } match self.parse_expr() { - Ok(e) => base = Some(e), + Ok(e) => base = ast::StructRest::Base(e), Err(mut e) if recover => { e.emit(); self.recover_stmt(); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2337f0d09abb7..2149fd7e01184 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -298,9 +298,7 @@ impl<'a> PathSource<'a> { _, ) | Res::SelfCtor(..)), - PathSource::TupleStruct(..) => { - matches!(res, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..)) - } + PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), PathSource::Struct => matches!(res, Res::Def( DefKind::Struct | DefKind::Union diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index dbb5e3cc9f066..40d60a8394be3 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -816,7 +816,7 @@ impl<'tcx> DumpVisitor<'tcx> { path: &'tcx hir::QPath<'tcx>, fields: &'tcx [hir::Field<'tcx>], variant: &'tcx ty::VariantDef, - base: Option<&'tcx hir::Expr<'tcx>>, + rest: Option<&'tcx hir::Expr<'tcx>>, ) { if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { if let hir::QPath::Resolved(_, path) = path { @@ -836,7 +836,9 @@ impl<'tcx> DumpVisitor<'tcx> { } } - walk_list!(self, visit_expr, base); + if let Some(base) = rest { + self.visit_expr(&base); + } } fn process_method_call( @@ -1399,7 +1401,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { debug!("visit_expr {:?}", ex.kind); self.process_macro_use(ex.span); match ex.kind { - hir::ExprKind::Struct(ref path, ref fields, ref base) => { + hir::ExprKind::Struct(ref path, ref fields, ref rest) => { let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.hir_id); let adt = match self.save_ctxt.typeck_results().expr_ty_opt(&hir_expr) { Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(), @@ -1409,7 +1411,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { } }; let res = self.save_ctxt.get_path_res(hir_expr.hir_id); - self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *base) + self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *rest) } hir::ExprKind::MethodCall(ref seg, _, args, _) => { self.process_method_call(ex, seg, args) diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 8e60085262afd..5ebd6c4a2349f 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -155,6 +155,7 @@ mod hexagon; mod mips; mod nvptx; mod riscv; +mod spirv; mod x86; pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; @@ -163,6 +164,7 @@ pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass}; pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; +pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass}; pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] @@ -177,6 +179,7 @@ pub enum InlineAsmArch { Hexagon, Mips, Mips64, + SpirV, } impl FromStr for InlineAsmArch { @@ -194,6 +197,7 @@ impl FromStr for InlineAsmArch { "hexagon" => Ok(Self::Hexagon), "mips" => Ok(Self::Mips), "mips64" => Ok(Self::Mips64), + "spirv" => Ok(Self::SpirV), _ => Err(()), } } @@ -208,6 +212,7 @@ pub enum InlineAsmReg { Nvptx(NvptxInlineAsmReg), Hexagon(HexagonInlineAsmReg), Mips(MipsInlineAsmReg), + SpirV(SpirVInlineAsmReg), } impl InlineAsmReg { @@ -264,6 +269,9 @@ impl InlineAsmReg { InlineAsmArch::Mips | InlineAsmArch::Mips64 => { Self::Mips(MipsInlineAsmReg::parse(arch, has_feature, target, &name)?) } + InlineAsmArch::SpirV => { + Self::SpirV(SpirVInlineAsmReg::parse(arch, has_feature, target, &name)?) + } }) } @@ -306,6 +314,7 @@ pub enum InlineAsmRegClass { Nvptx(NvptxInlineAsmRegClass), Hexagon(HexagonInlineAsmRegClass), Mips(MipsInlineAsmRegClass), + SpirV(SpirVInlineAsmRegClass), } impl InlineAsmRegClass { @@ -318,6 +327,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.name(), Self::Hexagon(r) => r.name(), Self::Mips(r) => r.name(), + Self::SpirV(r) => r.name(), } } @@ -333,6 +343,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips), + Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV), } } @@ -355,6 +366,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.suggest_modifier(arch, ty), Self::Hexagon(r) => r.suggest_modifier(arch, ty), Self::Mips(r) => r.suggest_modifier(arch, ty), + Self::SpirV(r) => r.suggest_modifier(arch, ty), } } @@ -373,6 +385,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.default_modifier(arch), Self::Hexagon(r) => r.default_modifier(arch), Self::Mips(r) => r.default_modifier(arch), + Self::SpirV(r) => r.default_modifier(arch), } } @@ -390,6 +403,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.supported_types(arch), Self::Hexagon(r) => r.supported_types(arch), Self::Mips(r) => r.supported_types(arch), + Self::SpirV(r) => r.supported_types(arch), } } @@ -414,6 +428,7 @@ impl InlineAsmRegClass { InlineAsmArch::Mips | InlineAsmArch::Mips64 => { Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) } + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), }) }) } @@ -429,6 +444,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.valid_modifiers(arch), Self::Hexagon(r) => r.valid_modifiers(arch), Self::Mips(r) => r.valid_modifiers(arch), + Self::SpirV(r) => r.valid_modifiers(arch), } } } @@ -571,5 +587,10 @@ pub fn allocatable_registers( mips::fill_reg_map(arch, has_feature, target, &mut map); map } + InlineAsmArch::SpirV => { + let mut map = spirv::regclass_map(); + spirv::fill_reg_map(arch, has_feature, target, &mut map); + map + } } } diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs new file mode 100644 index 0000000000000..da82749e96a16 --- /dev/null +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -0,0 +1,46 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; + +def_reg_class! { + SpirV SpirVInlineAsmRegClass { + reg, + } +} + +impl SpirVInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg => { + types! { _: I8, I16, I32, I64, F32, F64; } + } + } + } +} + +def_regs! { + // SPIR-V is SSA-based, it does not have registers. + SpirV SpirVInlineAsmReg SpirVInlineAsmRegClass {} +} diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index b67c72d7136a6..97ebc12175f73 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -495,7 +495,14 @@ impl BinaryHeap { let mut end = self.len(); while end > 1 { end -= 1; - self.data.swap(0, end); + // SAFETY: `end` goes from `self.len() - 1` to 1 (both included), + // so it's always a valid index to access. + // It is safe to access index 0 (i.e. `ptr`), because + // 1 <= end < self.len(), which means self.len() >= 2. + unsafe { + let ptr = self.data.as_mut_ptr(); + ptr::swap(ptr, ptr.add(end)); + } self.sift_down_range(0, end); } self.into_vec() @@ -531,19 +538,19 @@ impl BinaryHeap { unsafe { let mut hole = Hole::new(&mut self.data, pos); let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; + while child < end - 1 { // compare with the greater of the two children - if right < end && hole.get(child) <= hole.get(right) { - child = right; - } + child += (hole.get(child) <= hole.get(child + 1)) as usize; // if we are already in order, stop. if hole.element() >= hole.get(child) { - break; + return; } hole.move_to(child); child = 2 * hole.pos() + 1; } + if child == end - 1 && hole.element() < hole.get(child) { + hole.move_to(child); + } } } @@ -563,15 +570,14 @@ impl BinaryHeap { unsafe { let mut hole = Hole::new(&mut self.data, pos); let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; - // compare with the greater of the two children - if right < end && hole.get(child) <= hole.get(right) { - child = right; - } + while child < end - 1 { + child += (hole.get(child) <= hole.get(child + 1)) as usize; hole.move_to(child); child = 2 * hole.pos() + 1; } + if child == end - 1 { + hole.move_to(child); + } pos = hole.pos; } self.sift_up(start, pos); diff --git a/src/test/rustdoc/async-fn.rs b/src/test/rustdoc/async-fn.rs index 5a03e821e8a2f..e7a7d1831f73e 100644 --- a/src/test/rustdoc/async-fn.rs +++ b/src/test/rustdoc/async-fn.rs @@ -1,4 +1,5 @@ // edition:2018 +#![feature(min_const_generics)] // @has async_fn/fn.foo.html '//pre[@class="rust fn"]' 'pub async fn foo() -> Option' pub async fn foo() -> Option { @@ -20,6 +21,12 @@ pub async unsafe fn qux() -> char { '⚠' } +// @has async_fn/fn.mut_args.html '//pre[@class="rust fn"]' 'pub async fn mut_args(a: usize)' +pub async fn mut_args(mut a: usize) {} + +// @has async_fn/fn.mut_ref.html '//pre[@class="rust fn"]' 'pub async fn mut_ref(x: i32)' +pub async fn mut_ref(ref mut x: i32) {} + trait Bar {} impl Bar for () {} @@ -32,9 +39,16 @@ pub async fn quux() -> impl Bar { // @has async_fn/struct.Foo.html // @matches - '//code' 'pub async fn f\(\)$' // @matches - '//code' 'pub async unsafe fn g\(\)$' +// @matches - '//code' 'pub async fn mut_self\(self, first: usize\)$' pub struct Foo; impl Foo { pub async fn f() {} pub async unsafe fn g() {} + pub async fn mut_self(mut self, mut first: usize) {} } + +pub trait Trait {} +// @has async_fn/fn.const_generics.html +// @has - '//pre[@class="rust fn"]' 'pub async fn const_generics(_: impl Trait)' +pub async fn const_generics(_: impl Trait) {} diff --git a/src/test/rustdoc/const-generics/const-generics-docs.rs b/src/test/rustdoc/const-generics/const-generics-docs.rs index 8dcba36600d26..9c68e067c6f8f 100644 --- a/src/test/rustdoc/const-generics/const-generics-docs.rs +++ b/src/test/rustdoc/const-generics/const-generics-docs.rs @@ -70,8 +70,7 @@ pub async fn a_sink(v: [u8; N]) -> impl Trait { } // @has foo/fn.b_sink.html '//pre[@class="rust fn"]' \ -// 'pub async fn b_sink(__arg0: impl Trait)' -// FIXME(const_generics): This should be `_` not `__arg0`. +// 'pub async fn b_sink(_: impl Trait)' pub async fn b_sink(_: impl Trait) {} // @has foo/fn.concrete.html '//pre[@class="rust fn"]' \ diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index caf55bec53ddd..bff92d8607ece 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -155,7 +155,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { }, 17 => { let path = Path::from_ident(Ident::from_str("S")); - g(ExprKind::Struct(path, vec![], Some(make_x()))); + g(ExprKind::Struct(path, vec![], StructRest::Base(make_x()))); }, 18 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); diff --git a/src/test/ui/const-generics/core-types.rs b/src/test/ui/const-generics/core-types.rs new file mode 100644 index 0000000000000..c4351e059dec6 --- /dev/null +++ b/src/test/ui/const-generics/core-types.rs @@ -0,0 +1,51 @@ +// Check that all types allowed with `min_const_generics` work. +// run-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct A; +struct B; +struct C; +struct D; +struct E; +struct F; +struct G; +struct H; +struct I; +struct J; +struct K; +struct L; +struct M; +struct N; + +fn main() { + let _ = A::<{u8::MIN}>; + let _ = A::<{u8::MAX}>; + let _ = B::<{u16::MIN}>; + let _ = B::<{u16::MAX}>; + let _ = C::<{u32::MIN}>; + let _ = C::<{u32::MAX}>; + let _ = D::<{u64::MIN}>; + let _ = D::<{u64::MAX}>; + let _ = E::<{u128::MIN}>; + let _ = E::<{u128::MAX}>; + let _ = F::<{usize::MIN}>; + let _ = F::<{usize::MAX}>; + let _ = G::<{i8::MIN}>; + let _ = G::<{i8::MAX}>; + let _ = H::<{i16::MIN}>; + let _ = H::<{i16::MAX}>; + let _ = I::<{i32::MIN}>; + let _ = I::<{i32::MAX}>; + let _ = J::<{i64::MIN}>; + let _ = J::<{i64::MAX}>; + let _ = K::<{i128::MIN}>; + let _ = K::<{i128::MAX}>; + let _ = L::<{isize::MIN}>; + let _ = L::<{isize::MAX}>; + let _ = M::<'A'>; + let _ = N::; +} diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.rs b/src/test/ui/const-generics/min_const_generics/complex-types.rs index 98bc99d019421..2aaf2c3987558 100644 --- a/src/test/ui/const-generics/min_const_generics/complex-types.rs +++ b/src/test/ui/const-generics/min_const_generics/complex-types.rs @@ -1,4 +1,5 @@ #![feature(min_const_generics)] +#![feature(never_type)] struct Foo; //~^ ERROR `[u8; 0]` is forbidden @@ -14,4 +15,14 @@ struct Fez; struct Faz; //~^ ERROR `&'static u8` is forbidden +struct Fiz; +//~^ ERROR `!` is forbidden + +enum Goo { A, B } +//~^ ERROR `()` is forbidden + +union Boo { a: () } +//~^ ERROR `()` is forbidden + + fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.stderr b/src/test/ui/const-generics/min_const_generics/complex-types.stderr index 4772aaf1b3e0c..52ed3c1c6ee8c 100644 --- a/src/test/ui/const-generics/min_const_generics/complex-types.stderr +++ b/src/test/ui/const-generics/min_const_generics/complex-types.stderr @@ -1,5 +1,5 @@ error: `[u8; 0]` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:3:21 + --> $DIR/complex-types.rs:4:21 | LL | struct Foo; | ^^^^^^^ @@ -8,7 +8,7 @@ LL | struct Foo; = note: more complex types are supported with `#[feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:6:21 + --> $DIR/complex-types.rs:7:21 | LL | struct Bar; | ^^ @@ -17,7 +17,7 @@ LL | struct Bar; = note: more complex types are supported with `#[feature(const_generics)]` error: `No` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:11:21 + --> $DIR/complex-types.rs:12:21 | LL | struct Fez; | ^^ @@ -26,7 +26,7 @@ LL | struct Fez; = note: more complex types are supported with `#[feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:14:21 + --> $DIR/complex-types.rs:15:21 | LL | struct Faz; | ^^^^^^^^^^^ @@ -34,5 +34,32 @@ LL | struct Faz; = note: the only supported types are integers, `bool` and `char` = note: more complex types are supported with `#[feature(const_generics)]` -error: aborting due to 4 previous errors +error: `!` is forbidden as the type of a const generic parameter + --> $DIR/complex-types.rs:18:21 + | +LL | struct Fiz; + | ^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `()` is forbidden as the type of a const generic parameter + --> $DIR/complex-types.rs:21:19 + | +LL | enum Goo { A, B } + | ^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `()` is forbidden as the type of a const generic parameter + --> $DIR/complex-types.rs:24:20 + | +LL | union Boo { a: () } + | ^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 7 previous errors diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs new file mode 100644 index 0000000000000..393dfc16c0a1c --- /dev/null +++ b/src/test/ui/destructuring-assignment/nested_destructure.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(destructuring_assignment)] + +struct Struct { + a: S, + b: T, +} + +struct TupleStruct(S, T); + +fn main() { + let (a, b, c, d); + 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)); +} diff --git a/src/test/ui/destructuring-assignment/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs index e0cb9dc9158e2..249fba7f920bc 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.rs +++ b/src/test/ui/destructuring-assignment/note-unsupported.rs @@ -7,18 +7,19 @@ fn main() { (a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment //~| ERROR binary assignment operation `+=` cannot be applied - [a, b] = [3, 4]; //~ ERROR invalid left-hand side of assignment + [a, b] = [3, 4]; //~ ERROR destructuring assignments are unstable [a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment //~| ERROR binary assignment operation `+=` cannot be applied let s = S { x: 3, y: 4 }; - S { x: a, y: b } = s; //~ ERROR invalid left-hand side of assignment + S { x: a, y: b } = s; //~ ERROR destructuring assignments are unstable S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment //~| ERROR binary assignment operation `+=` cannot be applied S { x: a, ..s } = S { x: 3, y: 4 }; - //~^ ERROR invalid left-hand side of assignment + //~^ ERROR functional record updates are not allowed in destructuring assignments + //~| ERROR destructuring assignments are unstable let c = 3; diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr index c5543fab825eb..a81324b99e586 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -10,7 +10,46 @@ LL | (a, b) = (3, 4); = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0658]: destructuring assignments are unstable - --> $DIR/note-unsupported.rs:25:17 + --> $DIR/note-unsupported.rs:10:12 + | +LL | [a, b] = [3, 4]; + | ------ ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/note-unsupported.rs:16:22 + | +LL | S { x: a, y: b } = s; + | ---------------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/note-unsupported.rs:20:21 + | +LL | S { x: a, ..s } = S { x: 3, y: 4 }; + | --------------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error: functional record updates are not allowed in destructuring assignments + --> $DIR/note-unsupported.rs:20:17 + | +LL | S { x: a, ..s } = S { x: 3, y: 4 }; + | ^ help: consider removing the trailing pattern + +error[E0658]: destructuring assignments are unstable + --> $DIR/note-unsupported.rs:26:17 | LL | ((a, b), c) = ((3, 4), 5); | ----------- ^ @@ -36,14 +75,6 @@ LL | (a, b) += (3, 4); | | | cannot assign to this expression -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:10:12 - | -LL | [a, b] = [3, 4]; - | ------ ^ - | | - | cannot assign to this expression - error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]` --> $DIR/note-unsupported.rs:11:5 | @@ -60,14 +91,6 @@ LL | [a, b] += [3, 4]; | | | cannot assign to this expression -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:16:22 - | -LL | S { x: a, y: b } = s; - | ---------------- ^ - | | - | cannot assign to this expression - error[E0368]: binary assignment operation `+=` cannot be applied to type `S` --> $DIR/note-unsupported.rs:17:5 | @@ -86,15 +109,7 @@ LL | S { x: a, y: b } += s; | | | cannot assign to this expression -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:20:21 - | -LL | S { x: a, ..s } = S { x: 3, y: 4 }; - | --------------- ^ - | | - | cannot assign to this expression - -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors -Some errors have detailed explanations: E0067, E0070, E0368, E0658. +Some errors have detailed explanations: E0067, E0368, E0658. For more information about an error, try `rustc --explain E0067`. diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs new file mode 100644 index 0000000000000..3dd10aff19c72 --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure.rs @@ -0,0 +1,15 @@ +// run-pass + +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + [a, b] = [0, 1]; + assert_eq!((a, b), (0, 1)); + let mut c; + [a, .., b, c] = [1, 2, 3, 4, 5]; + assert_eq!((a, b, c), (1, 4, 5)); + [..] = [1, 2, 3]; + [c, ..] = [5, 6, 6]; + assert_eq!(c, 5); +} diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs new file mode 100644 index 0000000000000..f636ea3511c26 --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs @@ -0,0 +1,7 @@ +#![feature(destructuring_assignment)] + +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 +} diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr new file mode 100644 index 0000000000000..728687deb8bbb --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -0,0 +1,17 @@ +error: `..` can only be used once per slice pattern + --> $DIR/slice_destructure_fail.rs:5:14 + | +LL | [a, .., b, ..] = [0, 1]; + | -- ^^ can only be used once per slice pattern + | | + | previously used here + +error[E0527]: pattern requires 3 elements but array has 2 + --> $DIR/slice_destructure_fail.rs:6:3 + | +LL | [a, a, b] = [1, 2]; + | ^^^^^^^^^ expected 2 elements + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0527`. diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs new file mode 100644 index 0000000000000..b3a96ee157346 --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure.rs @@ -0,0 +1,19 @@ +// run-pass + +#![feature(destructuring_assignment)] +struct Struct { + a: S, + b: T, +} + +fn main() { + let (mut a, mut b); + Struct { a, b } = Struct { a: 0, b: 1 }; + assert_eq!((a, b), (0, 1)); + Struct { a: b, b: a } = Struct { a: 1, b: 2 }; + assert_eq!((a,b), (2, 1)); + Struct { a, .. } = Struct { a: 1, b: 3 }; + assert_eq!((a, b), (1, 1)); + Struct { .. } = Struct { a: 1, b: 4 }; + assert_eq!((a, b), (1, 1)); +} diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs new file mode 100644 index 0000000000000..c22695ed38849 --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -0,0 +1,15 @@ +#![feature(destructuring_assignment)] +struct Struct { + a: S, + b: T, +} + +fn main() { + let (mut a, b); + 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, ..d } = Struct { a: 1, b: 2 }; + //~^ ERROR functional record updates are not allowed in destructuring assignments + Struct { a, .. }; //~ ERROR base expression required after `..` +} diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr new file mode 100644 index 0000000000000..4da4698804f1a --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -0,0 +1,21 @@ +error: functional record updates are not allowed in destructuring assignments + --> $DIR/struct_destructure_fail.rs:12: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 + | +LL | Struct { a, .. }; + | ^ add a base expression here + +error[E0026]: struct `Struct` does not have a field named `c` + --> $DIR/struct_destructure_fail.rs:11:20 + | +LL | Struct { a, b, c } = Struct { a: 0, b: 1 }; + | ^ struct `Struct` does not have this field + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0026`. diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs new file mode 100644 index 0000000000000..106a9b16db459 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(destructuring_assignment)] + +struct TupleStruct(S, T); + +impl TupleStruct { + fn assign(self, first: &mut S, second: &mut T) { + // Test usage of `Self` instead of the struct name: + Self(*first, *second) = self + } +} + +enum Enum { + SingleVariant(S, T) +} + +type Alias = Enum; + +fn main() { + let (mut a, mut b); + TupleStruct(a, b) = TupleStruct(0, 1); + assert_eq!((a, b), (0, 1)); + TupleStruct(a, .., b) = TupleStruct(1, 2); + assert_eq!((a, b), (1, 2)); + TupleStruct(..) = TupleStruct(3, 4); + assert_eq!((a, b), (1, 2)); + TupleStruct(5,6).assign(&mut a, &mut b); + assert_eq!((a, b), (5, 6)); + Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8); + assert_eq!((a, b), (7, 8)); + Alias::SingleVariant(a, b) = Alias::SingleVariant(9, 10); + assert_eq!((a, b), (9, 10)); +} diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs new file mode 100644 index 0000000000000..61ae42a51751f --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs @@ -0,0 +1,42 @@ +#![feature(destructuring_assignment)] + +struct TupleStruct(S, T); + +enum Enum { + SingleVariant(S, T) +} + +type Alias = Enum; + +trait Test { + fn test() -> TupleStruct { + TupleStruct(0, 0) + } +} + +impl Test for Alias {} + +fn test() -> TupleStruct { + TupleStruct(0, 0) +} + +fn main() { + let (mut a, mut b); + TupleStruct(a, .., b, ..) = TupleStruct(0, 1); + //~^ ERROR `..` can only be used once per tuple struct or variant pattern + Enum::SingleVariant(a, .., b, ..) = Enum::SingleVariant(0, 1); + //~^ ERROR `..` can only be used once per tuple struct or variant pattern + + TupleStruct(a, a, b) = TupleStruct(1, 2); + //~^ ERROR this pattern has 3 fields, 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 + + // Check if `test` is recognized as not a tuple struct but a function call: + test() = TupleStruct(0, 0); + //~^ ERROR invalid left-hand side of assignment + (test)() = TupleStruct(0, 0); + //~^ ERROR invalid left-hand side of assignment + as Test>::test() = TupleStruct(0, 0); + //~^ ERROR invalid left-hand side of assignment +} diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr new file mode 100644 index 0000000000000..863eedecf7697 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -0,0 +1,62 @@ +error: `..` can only be used once per tuple struct or variant pattern + --> $DIR/tuple_struct_destructure_fail.rs:25:27 + | +LL | TupleStruct(a, .., b, ..) = TupleStruct(0, 1); + | -- ^^ can only be used once per tuple struct or variant pattern + | | + | previously used here + +error: `..` can only be used once per tuple struct or variant pattern + --> $DIR/tuple_struct_destructure_fail.rs:27:35 + | +LL | Enum::SingleVariant(a, .., b, ..) = Enum::SingleVariant(0, 1); + | -- ^^ can only be used once per tuple struct or variant pattern + | | + | previously used here + +error[E0023]: this pattern has 3 fields, but the corresponding tuple struct has 2 fields + --> $DIR/tuple_struct_destructure_fail.rs:30:5 + | +LL | struct TupleStruct(S, T); + | ------------------------------- tuple struct defined here +... +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 + --> $DIR/tuple_struct_destructure_fail.rs:32: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[E0070]: invalid left-hand side of assignment + --> $DIR/tuple_struct_destructure_fail.rs:36: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 + | +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 + | +LL | as Test>::test() = TupleStruct(0, 0); + | -------------------------------- ^ + | | + | cannot assign to this expression + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0023, E0070. +For more information about an error, try `rustc --explain E0023`. diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs new file mode 100644 index 0000000000000..b41f2f52a3d6f --- /dev/null +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs @@ -0,0 +1,8 @@ +fn main() {} + +struct S { x : u32 } + +#[cfg(FALSE)] +fn foo() { + S { x: 5, .. }; //~ ERROR destructuring assignments are unstable +} diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr new file mode 100644 index 0000000000000..442e36cd3065e --- /dev/null +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr @@ -0,0 +1,12 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/underscore-range-expr-gating.rs:7:15 + | +LL | S { x: 5, .. }; + | ^^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/issues/issue-77218.rs b/src/test/ui/issues/issue-77218.rs index bc992c21dca5c..a6a2401795ff4 100644 --- a/src/test/ui/issues/issue-77218.rs +++ b/src/test/ui/issues/issue-77218.rs @@ -1,7 +1,11 @@ fn main() { let value = [7u8]; - while Some(0) = value.get(0) { //~ ERROR mismatched types - //~^ NOTE expected `bool`, found `()` - //~| HELP you might have meant to use pattern matching + while Some(0) = value.get(0) { //~ ERROR destructuring assignments are unstable + //~| ERROR invalid left-hand side of assignment + //~| ERROR mismatched types + //~| ERROR mismatched types + + // FIXME The following diagnostic should also be emitted + // HELP you might have meant to use pattern matching } } diff --git a/src/test/ui/issues/issue-77218.stderr b/src/test/ui/issues/issue-77218.stderr index eca44725eb258..4f6fbaa2265d5 100644 --- a/src/test/ui/issues/issue-77218.stderr +++ b/src/test/ui/issues/issue-77218.stderr @@ -1,14 +1,38 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/issue-77218.rs:3:19 + | +LL | while Some(0) = value.get(0) { + | ------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0070]: invalid left-hand side of assignment + --> $DIR/issue-77218.rs:3:19 + | +LL | while Some(0) = value.get(0) { + | - ^ + | | + | cannot assign to this expression + +error[E0308]: mismatched types + --> $DIR/issue-77218.rs:3:16 + | +LL | while Some(0) = value.get(0) { + | ^ + | | + | expected integer, found `&u8` + | help: consider dereferencing the borrow: `*0` + error[E0308]: mismatched types --> $DIR/issue-77218.rs:3:11 | LL | while Some(0) = value.get(0) { | ^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` - | -help: you might have meant to use pattern matching - | -LL | while let Some(0) = value.get(0) { - | ^^^ -error: aborting due to previous error +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0070, E0308, E0658. +For more information about an error, try `rustc --explain E0070`. diff --git a/src/test/ui/suggestions/if-let-typo.rs b/src/test/ui/suggestions/if-let-typo.rs index 87def13c476c7..688b6e8265826 100644 --- a/src/test/ui/suggestions/if-let-typo.rs +++ b/src/test/ui/suggestions/if-let-typo.rs @@ -2,7 +2,12 @@ fn main() { let foo = Some(0); let bar = None; if Some(x) = foo {} //~ ERROR cannot find value `x` in this scope + //~^ ERROR mismatched types + //~^^ ERROR destructuring assignments are unstable if Some(foo) = bar {} //~ ERROR mismatched types + //~^ ERROR destructuring assignments are unstable if 3 = foo {} //~ ERROR mismatched types if Some(3) = foo {} //~ ERROR mismatched types + //~^ ERROR destructuring assignments are unstable + //~^^ ERROR invalid left-hand side of assignment } diff --git a/src/test/ui/suggestions/if-let-typo.stderr b/src/test/ui/suggestions/if-let-typo.stderr index d8e50cae55ad1..ce1ee0cd06d48 100644 --- a/src/test/ui/suggestions/if-let-typo.stderr +++ b/src/test/ui/suggestions/if-let-typo.stderr @@ -9,23 +9,53 @@ help: you might have meant to use pattern matching LL | if let Some(x) = foo {} | ^^^ -error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:5:8 +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:4:16 + | +LL | if Some(x) = foo {} + | ------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:7:18 | LL | if Some(foo) = bar {} - | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + | --------- ^ + | | + | cannot assign to this expression | -help: you might have meant to use pattern matching + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:10:16 | -LL | if let Some(foo) = bar {} - | ^^^ -help: you might have meant to compare for equality +LL | if Some(3) = foo {} + | ------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:4:8 + | +LL | if Some(x) = foo {} + | ^^^^^^^^^^^^^ expected `bool`, found `()` + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:7:8 | -LL | if Some(foo) == bar {} - | ^^ +LL | if Some(foo) = bar {} + | ^^^^^^^^^^^^^^^ expected `bool`, found `()` error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:6:8 + --> $DIR/if-let-typo.rs:9:8 | LL | if 3 = foo {} | ^^^^^^^ expected `bool`, found `()` @@ -35,22 +65,21 @@ help: you might have meant to use pattern matching LL | if let 3 = foo {} | ^^^ +error[E0070]: invalid left-hand side of assignment + --> $DIR/if-let-typo.rs:10:16 + | +LL | if Some(3) = foo {} + | - ^ + | | + | cannot assign to this expression + error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:7:8 + --> $DIR/if-let-typo.rs:10:8 | LL | if Some(3) = foo {} | ^^^^^^^^^^^^^ expected `bool`, found `()` - | -help: you might have meant to use pattern matching - | -LL | if let Some(3) = foo {} - | ^^^ -help: you might have meant to compare for equality - | -LL | if Some(3) == foo {} - | ^^ -error: aborting due to 4 previous errors +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0308, E0425. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0070, E0308, E0425, E0658. +For more information about an error, try `rustc --explain E0070`. diff --git a/src/tools/cargo b/src/tools/cargo index d5556aeb8405b..8662ab427a8d6 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit d5556aeb8405b1fe696adb6e297ad7a1f2989b62 +Subproject commit 8662ab427a8d6ad8047811cc4d78dbd20dd07699 diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs index b68e33f101d2b..9050b9b2d9ab8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -107,6 +107,15 @@ pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { both(l, r, |l, r| eq_expr(l, r)) } +pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { + match (l, r) { + (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), + (StructRest::Rest(_), StructRest::Rest(_)) => true, + (StructRest::None, StructRest::None) => true, + _ => false, + } +} + pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { @@ -150,7 +159,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { - eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + eq_path(lp, rp) && eq_struct_rest(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) }, _ => false, } diff --git a/src/tools/clippy/tests/ui/crashes/ice-6250.stderr b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr index 8241dcd8feb7b..c38727316cd4e 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6250.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr @@ -1,3 +1,14 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/ice-6250.rs:12:25 + | +LL | Some(reference) = cache.data.get(key) { + | --------------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + error[E0601]: `main` function not found in crate `ice_6250` --> $DIR/ice-6250.rs:4:1 | @@ -10,18 +21,22 @@ LL | | } LL | | } | |_^ consider adding a `main` function to `$DIR/ice-6250.rs` +error[E0308]: mismatched types + --> $DIR/ice-6250.rs:12:14 + | +LL | Some(reference) = cache.data.get(key) { + | ^^^^^^^^^ + | | + | expected integer, found `&i32` + | help: consider dereferencing the borrow: `*reference` + error[E0308]: mismatched types --> $DIR/ice-6250.rs:12:9 | LL | Some(reference) = cache.data.get(key) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` - | -help: you might have meant to use pattern matching - | -LL | let Some(reference) = cache.data.get(key) { - | ^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0308, E0601. +Some errors have detailed explanations: E0308, E0601, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/src/tools/rustfmt b/src/tools/rustfmt index eb894d5370812..0f29ff6da0c5f 160000 --- a/src/tools/rustfmt +++ b/src/tools/rustfmt @@ -1 +1 @@ -Subproject commit eb894d53708122a67762de9489881c11aa8ce257 +Subproject commit 0f29ff6da0c5ff622e739beb8fc3bbe77119b3c1