]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #78809 - vn-ki:fix-issue-76064, r=oli-obk
authorbors <bors@rust-lang.org>
Sat, 14 Nov 2020 18:03:17 +0000 (18:03 +0000)
committerbors <bors@rust-lang.org>
Sat, 14 Nov 2020 18:03:17 +0000 (18:03 +0000)
add error_occured field to ConstQualifs,

fix #76064

I wasn't sure what `in_return_place` actually did and not sure why it returns `ConstQualifs` while it's sibling functions return `bool`. So I tried to make as minimal changes to the structure as possible. Please point out whether I have to refactor it or not.

r? `@oli-obk`
cc `@RalfJung`

125 files changed:
Cargo.lock
compiler/rustc_ast/src/ast.rs
compiler/rustc_ast/src/mut_visit.rs
compiler/rustc_ast/src/tokenstream.rs
compiler/rustc_ast/src/visit.rs
compiler/rustc_ast_lowering/src/expr.rs
compiler/rustc_ast_lowering/src/item.rs
compiler/rustc_ast_lowering/src/lib.rs
compiler/rustc_ast_passes/src/feature_gate.rs
compiler/rustc_ast_pretty/src/pprust/state.rs
compiler/rustc_builtin_macros/src/deriving/debug.rs
compiler/rustc_codegen_llvm/src/asm.rs
compiler/rustc_codegen_llvm/src/back/write.rs
compiler/rustc_codegen_ssa/src/back/write.rs
compiler/rustc_expand/src/build.rs
compiler/rustc_hir/src/def.rs
compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
compiler/rustc_infer/src/infer/canonical/mod.rs
compiler/rustc_infer/src/infer/higher_ranked/mod.rs
compiler/rustc_lint/src/levels.rs
compiler/rustc_metadata/src/rmeta/decoder.rs
compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
compiler/rustc_middle/src/infer/canonical.rs
compiler/rustc_middle/src/ty/codec.rs
compiler/rustc_middle/src/ty/consts/kind.rs
compiler/rustc_middle/src/ty/context.rs
compiler/rustc_middle/src/ty/mod.rs
compiler/rustc_parse/src/parser/expr.rs
compiler/rustc_parse/src/parser/mod.rs
compiler/rustc_resolve/src/build_reduced_graph.rs
compiler/rustc_resolve/src/diagnostics.rs
compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/lib.rs
compiler/rustc_resolve/src/macros.rs
compiler/rustc_save_analysis/src/dump_visitor.rs
compiler/rustc_span/src/lib.rs
compiler/rustc_target/src/asm/mod.rs
compiler/rustc_target/src/asm/spirv.rs [new file with mode: 0644]
compiler/rustc_target/src/spec/aarch64_unknown_none.rs
compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
compiler/rustc_target/src/spec/armv7a_none_eabi.rs
compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
compiler/rustc_target/src/spec/armv7r_none_eabi.rs
compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
compiler/rustc_target/src/spec/avr_gnu_base.rs
compiler/rustc_target/src/spec/fuchsia_base.rs
compiler/rustc_target/src/spec/mipsel_unknown_none.rs
compiler/rustc_target/src/spec/mod.rs
compiler/rustc_target/src/spec/msp430_none_elf.rs
compiler/rustc_target/src/spec/tests/tests_impl.rs
compiler/rustc_target/src/spec/thumb_base.rs
compiler/rustc_target/src/spec/uefi_msvc_base.rs
compiler/rustc_target/src/spec/wasm32_wasi.rs
compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
library/alloc/src/collections/binary_heap.rs
library/std/src/collections/hash/map.rs
library/std/src/fs.rs
library/std/src/io/copy.rs [new file with mode: 0644]
library/std/src/io/mod.rs
library/std/src/io/stdio.rs
library/std/src/io/tests.rs
library/std/src/io/util.rs
library/std/src/lib.rs
library/std/src/sys/unix/fs.rs
library/std/src/sys/unix/kernel_copy.rs [new file with mode: 0644]
library/std/src/sys/unix/kernel_copy/tests.rs [new file with mode: 0644]
library/std/src/sys/unix/mod.rs
src/librustdoc/clean/blanket_impl.rs
src/librustdoc/config.rs
src/librustdoc/lib.rs
src/librustdoc/passes/collect_intra_doc_links.rs
src/test/run-make-fulldeps/coverage-reports-base/Makefile
src/test/run-make-fulldeps/coverage-spanview-base/Makefile
src/test/run-make/incr-prev-body-beyond-eof/Makefile [new file with mode: 0644]
src/test/run-make/incr-prev-body-beyond-eof/a.rs [new file with mode: 0644]
src/test/run-make/incr-prev-body-beyond-eof/b.rs [new file with mode: 0644]
src/test/run-make/issue-36710/Makefile
src/test/rustdoc-ui/check-fail.rs [new file with mode: 0644]
src/test/rustdoc-ui/check-fail.stderr [new file with mode: 0644]
src/test/rustdoc-ui/check.rs [new file with mode: 0644]
src/test/rustdoc-ui/check.stderr [new file with mode: 0644]
src/test/rustdoc/async-fn.rs
src/test/rustdoc/check.rs [new file with mode: 0644]
src/test/rustdoc/const-generics/const-generics-docs.rs
src/test/rustdoc/issue-78673.rs [new file with mode: 0644]
src/test/ui-fulldeps/pprust-expr-roundtrip.rs
src/test/ui/const-generics/core-types.rs [new file with mode: 0644]
src/test/ui/const-generics/min_const_generics/complex-types.rs
src/test/ui/const-generics/min_const_generics/complex-types.stderr
src/test/ui/const-generics/promotion.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/nested_destructure.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/note-unsupported.rs
src/test/ui/destructuring-assignment/note-unsupported.stderr
src/test/ui/destructuring-assignment/slice_destructure.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/slice_destructure_fail.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/slice_destructure_fail.stderr [new file with mode: 0644]
src/test/ui/destructuring-assignment/struct_destructure.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/struct_destructure_fail.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/struct_destructure_fail.stderr [new file with mode: 0644]
src/test/ui/destructuring-assignment/tuple_struct_destructure.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr [new file with mode: 0644]
src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr [new file with mode: 0644]
src/test/ui/dropck/issue-38868.rs [new file with mode: 0644]
src/test/ui/dropck/issue-38868.stderr [new file with mode: 0644]
src/test/ui/dropck/reject-specialized-drops-8142.rs [new file with mode: 0644]
src/test/ui/dropck/reject-specialized-drops-8142.stderr [new file with mode: 0644]
src/test/ui/issues/issue-38868.rs [deleted file]
src/test/ui/issues/issue-38868.stderr [deleted file]
src/test/ui/issues/issue-76042.rs [new file with mode: 0644]
src/test/ui/issues/issue-77218.rs
src/test/ui/issues/issue-77218.stderr
src/test/ui/reject-specialized-drops-8142.rs [deleted file]
src/test/ui/reject-specialized-drops-8142.stderr [deleted file]
src/test/ui/suggestions/if-let-typo.rs
src/test/ui/suggestions/if-let-typo.stderr
src/tools/cargo
src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
src/tools/clippy/tests/ui/crashes/ice-6250.stderr
src/tools/miri
src/tools/rustfmt
src/version

index 45a19fd79634e68747f18d72d0d39ea5d5136ca9..c72c7b8481b5bb5d2b35cd9a7e60f06643d5aa1e 100644 (file)
@@ -4329,7 +4329,7 @@ dependencies = [
 
 [[package]]
 name = "rustfmt-nightly"
-version = "1.4.24"
+version = "1.4.25"
 dependencies = [
  "annotate-snippets 0.6.1",
  "anyhow",
index 6961905038ffa34224182877364c252c60d0af44..3e953729aabecaabac7e5c6b87fc583b5746bd1c 100644 (file)
@@ -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<Expr>),
+    /// `..`.
+    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<Expr>, Ident),
     /// An indexing operation (e.g., `foo[2]`).
     Index(P<Expr>, P<Expr>),
-    /// 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<P<Expr>>, Option<P<Expr>>, 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<Expr>`.
-    Struct(Path, Vec<Field>, Option<P<Expr>>),
+    /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`.
+    Struct(Path, Vec<Field>, StructRest),
 
     /// An array literal constructed from one repeated element.
     ///
index 2697b0175728c5046688c2ea37acf310130a0324..26097980e8be441c4a7b1c7273334d154cff05a9 100644 (file)
@@ -1288,7 +1288,11 @@ pub fn noop_visit_expr<T: MutVisitor>(
         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);
index 1e7001c2b2353e1c1fa90b1854e8079f454c0737..fe67b905bf30f01a9b3eead31fc7cd2f5472c3ce 100644 (file)
@@ -221,7 +221,7 @@ pub fn add_comma(&self) -> Option<(TokenStream, Span)> {
             }
         }
         if let Some((pos, comma, sp)) = suggestion {
-            let mut new_stream = vec![];
+            let mut new_stream = Vec::with_capacity(self.0.len() + 1);
             let parts = self.0.split_at(pos + 1);
             new_stream.extend_from_slice(parts.0);
             new_stream.push(comma);
index 72e901fb90f81709dd93de3747687d2a41a98cfa..49b521afcdc782df7c80eee1abe38fdc663332b6 100644 (file)
@@ -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);
index 1f2aba2b27e685a8acc74cf6dbdd093cfa895fcd..b94fb1d8437ed7089a6e84708e70116a4b851edd 100644 (file)
@@ -187,8 +187,18 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'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 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'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 @@ fn lower_expr_assign(
         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 @@ fn is_ordinary(lhs: &Expr) -> bool {
         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 @@ fn destructure_assign(
         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) =
@@ -1255,14 +1367,18 @@ fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> {
 
         let mut used_input_regs = FxHashMap::default();
         let mut used_output_regs = FxHashMap::default();
+        let mut required_features: Vec<&str> = vec![];
         for (idx, op) in operands.iter().enumerate() {
             let op_sp = asm.operands[idx].1;
             if let Some(reg) = op.reg() {
+                // Make sure we don't accidentally carry features from the
+                // previous iteration.
+                required_features.clear();
+
                 // Validate register classes against currently enabled target
                 // features. We check that at least one type is available for
                 // the current target.
                 let reg_class = reg.reg_class();
-                let mut required_features: Vec<&str> = vec![];
                 for &(_, feature) in reg_class.supported_types(asm_arch) {
                     if let Some(feature) = feature {
                         if self.sess.target_features.contains(&Symbol::intern(feature)) {
index 617cacee0e7f1d580a2003e6b68931a632fc587b..d353bc19f7aef510d759169cc2446527bafd4ef6 100644 (file)
@@ -1096,8 +1096,18 @@ fn lower_maybe_async_body(
                 // Check if this is a binding pattern, if so, we can optimize and avoid adding a
                 // `let <pat> = __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.
index 549b66e2d36844d7209df68376f1ae36effd7490..88ad2706eacf5899c459935f8e1a92fe9fba9479 100644 (file)
@@ -2011,17 +2011,17 @@ fn lower_async_fn_ret_ty(
         //
         // For the "output" lifetime parameters, we just want to
         // generate `'_`.
-        let mut generic_args: Vec<_> = lifetime_params[..input_lifetimes_count]
-            .iter()
-            .map(|&(span, hir_name)| {
+        let mut generic_args = Vec::with_capacity(lifetime_params.len());
+        generic_args.extend(lifetime_params[..input_lifetimes_count].iter().map(
+            |&(span, hir_name)| {
                 // Input lifetime like `'a` or `'1`:
                 GenericArg::Lifetime(hir::Lifetime {
                     hir_id: self.next_id(),
                     span,
                     name: hir::LifetimeName::Param(hir_name),
                 })
-            })
-            .collect();
+            },
+        ));
         generic_args.extend(lifetime_params[input_lifetimes_count..].iter().map(|&(span, _)|
             // Output lifetime like `'_`.
             GenericArg::Lifetime(hir::Lifetime {
@@ -2312,29 +2312,30 @@ fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> &'hir hir::Bloc
     }
 
     fn lower_block_noalloc(&mut self, b: &Block, targeted_by_break: bool) -> hir::Block<'hir> {
-        let mut stmts = vec![];
         let mut expr: Option<&'hir _> = None;
 
-        for (index, stmt) in b.stmts.iter().enumerate() {
-            if index == b.stmts.len() - 1 {
-                if let StmtKind::Expr(ref e) = stmt.kind {
-                    expr = Some(self.lower_expr(e));
-                } else {
-                    stmts.extend(self.lower_stmt(stmt));
-                }
-            } else {
-                stmts.extend(self.lower_stmt(stmt));
-            }
-        }
+        let stmts = self.arena.alloc_from_iter(
+            b.stmts
+                .iter()
+                .enumerate()
+                .filter_map(|(index, stmt)| {
+                    if index == b.stmts.len() - 1 {
+                        if let StmtKind::Expr(ref e) = stmt.kind {
+                            expr = Some(self.lower_expr(e));
+                            None
+                        } else {
+                            Some(self.lower_stmt(stmt))
+                        }
+                    } else {
+                        Some(self.lower_stmt(stmt))
+                    }
+                })
+                .flatten(),
+        );
+        let rules = self.lower_block_check_mode(&b.rules);
+        let hir_id = self.lower_node_id(b.id);
 
-        hir::Block {
-            hir_id: self.lower_node_id(b.id),
-            stmts: self.arena.alloc_from_iter(stmts),
-            expr,
-            rules: self.lower_block_check_mode(&b.rules),
-            span: b.span,
-            targeted_by_break,
-        }
+        hir::Block { hir_id, stmts, expr, rules, span: b.span, targeted_by_break }
     }
 
     /// Lowers a block directly to an expression, presuming that it
index f20084497671f3941da94154c440aed824daa665..2831675cb36712558a7fb6da536bf69696925e84 100644 (file)
@@ -630,6 +630,7 @@ macro_rules! gate_all {
     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).
index a64014f5acbb828b0e2f8550975c4926b0b66184..a566200c3389678f511c95689dfc379ffedb4fc6 100644 (file)
@@ -1729,7 +1729,7 @@ fn print_expr_struct(
         &mut self,
         path: &ast::Path,
         fields: &[ast::Field],
-        wth: &Option<P<ast::Expr>>,
+        rest: &ast::StructRest,
         attrs: &[ast::Attribute],
     ) {
         self.print_path(path, true, 0);
@@ -1750,22 +1750,21 @@ fn print_expr_struct(
             },
             |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 @@ fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
             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);
index d84b3956475d848be81faa7645c34327b4a5c08c..9381264f498f34938949cba477cd6c113b9d1e85 100644 (file)
@@ -66,7 +66,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
 
     let fmt = substr.nonself_args[0].clone();
 
-    let mut stmts = vec![];
+    let mut stmts = Vec::with_capacity(fields.len() + 2);
     match vdata {
         ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
             // tuple struct/"normal" variant
index d856280158f2d5d55d1e724334362aec0b9bb947..b5d279eeb6f2fd1bf6b7638c32936137e036e582 100644 (file)
@@ -12,8 +12,8 @@
 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 @@ fn codegen_inline_asm(
                 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")
+        }
     }
 }
 
index e6acb6860be9efa3869755caeb68d320d3d36c44..6237a4c0020ebb58e5313bdcaf7d219ae17ddb50 100644 (file)
@@ -925,9 +925,7 @@ unsafe fn embed_bitcode(
         || cgcx.opts.target_triple.triple().starts_with("asmjs")
     {
         // nothing to do here
-    } else if cgcx.opts.target_triple.triple().contains("windows")
-        || cgcx.opts.target_triple.triple().contains("uefi")
-    {
+    } else if cgcx.is_pe_coff {
         let asm = "
             .section .llvmbc,\"n\"
             .section .llvmcmd,\"n\"
index b34bee3358b40615403b29b7dcb2243ed71ba1ef..7f2bb7b5bcdaffd7b4189f0f8c1699d6f326f87e 100644 (file)
@@ -307,6 +307,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     pub allocator_module_config: Arc<ModuleConfig>,
     pub tm_factory: TargetMachineFactory<B>,
     pub msvc_imps_needed: bool,
+    pub is_pe_coff: bool,
     pub target_pointer_width: u32,
     pub target_arch: String,
     pub debuginfo: config::DebugInfo,
@@ -1022,6 +1023,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)),
         total_cgus,
         msvc_imps_needed: msvc_imps_needed(tcx),
+        is_pe_coff: tcx.sess.target.is_like_windows,
         target_pointer_width: tcx.sess.target.pointer_width,
         target_arch: tcx.sess.target.arch.clone(),
         debuginfo: tcx.sess.opts.debuginfo,
index 1c9bfb902d61a95ae7da7e33e3dc123352d9b83a..30f0fc6cddfa2cbd08d9101247bb76ef576bb396 100644 (file)
@@ -298,7 +298,7 @@ pub fn expr_struct(
         path: ast::Path,
         fields: Vec<ast::Field>,
     ) -> P<ast::Expr> {
-        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,
index 193247af584bb579d240582f8de712df42b22aed..298cfcc254c86d745c35a6d600225ea01ad987d7 100644 (file)
@@ -484,4 +484,9 @@ pub fn ns(&self) -> Option<Namespace> {
     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(..))
+    }
 }
index 871fc4fafe269f02f7bcea52454b2d1c4e2563b8..f554b51800a72bc14e8c5e2d756e3f084a791e21 100644 (file)
@@ -277,7 +277,7 @@ fn any(&self) -> bool {
 struct Canonicalizer<'cx, 'tcx> {
     infcx: Option<&'cx InferCtxt<'cx, 'tcx>>,
     tcx: TyCtxt<'tcx>,
-    variables: SmallVec<[CanonicalVarInfo; 8]>,
+    variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
     query_state: &'cx mut OriginalQueryValues<'tcx>,
     // Note that indices is only used once `var_values` is big enough to be
     // heap-allocated.
@@ -542,7 +542,7 @@ fn canonicalize<V>(
     /// or returns an existing variable if `kind` has already been
     /// seen. `kind` is expected to be an unbound variable (or
     /// potentially a free region).
-    fn canonical_var(&mut self, info: CanonicalVarInfo, kind: GenericArg<'tcx>) -> BoundVar {
+    fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar {
         let Canonicalizer { variables, query_state, indices, .. } = self;
 
         let var_values = &mut query_state.var_values;
@@ -621,7 +621,7 @@ fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
     /// representing the region `r`; return a region referencing it.
     fn canonical_var_for_region(
         &mut self,
-        info: CanonicalVarInfo,
+        info: CanonicalVarInfo<'tcx>,
         r: ty::Region<'tcx>,
     ) -> ty::Region<'tcx> {
         let var = self.canonical_var(info, r.into());
@@ -633,7 +633,7 @@ fn canonical_var_for_region(
     /// if `ty_var` is bound to anything; if so, canonicalize
     /// *that*. Otherwise, create a new canonical variable for
     /// `ty_var`.
-    fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo, ty_var: Ty<'tcx>) -> Ty<'tcx> {
+    fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
         let infcx = self.infcx.expect("encountered ty-var without infcx");
         let bound_to = infcx.shallow_resolve(ty_var);
         if bound_to != ty_var {
@@ -650,7 +650,7 @@ fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo, ty_var: Ty<'tcx>) -> T
     /// `const_var`.
     fn canonicalize_const_var(
         &mut self,
-        info: CanonicalVarInfo,
+        info: CanonicalVarInfo<'tcx>,
         const_var: &'tcx ty::Const<'tcx>,
     ) -> &'tcx ty::Const<'tcx> {
         let infcx = self.infcx.expect("encountered const-var without infcx");
index 2b8c46f1de42def2944d6e2af76d3b18bd56279e..0c26639e9b0fec31a947e491217ac87dac5c5c61 100644 (file)
@@ -82,7 +82,7 @@ pub fn instantiate_canonical_with_fresh_inference_vars<T>(
     fn instantiate_canonical_vars(
         &self,
         span: Span,
-        variables: &List<CanonicalVarInfo>,
+        variables: &List<CanonicalVarInfo<'tcx>>,
         universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
     ) -> CanonicalVarValues<'tcx> {
         let var_values: IndexVec<BoundVar, GenericArg<'tcx>> = variables
@@ -100,7 +100,7 @@ fn instantiate_canonical_vars(
     fn instantiate_canonical_var(
         &self,
         span: Span,
-        cv_info: CanonicalVarInfo,
+        cv_info: CanonicalVarInfo<'tcx>,
         universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
     ) -> GenericArg<'tcx> {
         match cv_info.kind {
@@ -154,7 +154,7 @@ fn instantiate_canonical_var(
                 self.tcx
                     .mk_const(ty::Const {
                         val: ty::ConstKind::Placeholder(placeholder_mapped),
-                        ty: self.tcx.ty_error(), // FIXME(const_generics)
+                        ty: name.ty,
                     })
                     .into()
             }
index e3365e8590b5edecf7d25beb7e04ee9fe2a46e10..4a5fd4b2aa5c83ef18e900a59c7b2c1ed544395a 100644 (file)
@@ -95,7 +95,7 @@ pub fn replace_bound_vars_with_placeholders<T>(&self, binder: &ty::Binder<T>) ->
             self.tcx.mk_const(ty::Const {
                 val: ty::ConstKind::Placeholder(ty::PlaceholderConst {
                     universe: next_universe,
-                    name: bound_var,
+                    name: ty::BoundConst { var: bound_var, ty },
                 }),
                 ty,
             })
index aca28988364e6802a0d83e9ea3528cd58f80e330..02da85d25d5c552de3f232edc2ba6accb0e4380a 100644 (file)
@@ -30,6 +30,8 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> LintLevelMap {
     let mut builder = LintLevelMapBuilder { levels, tcx, store };
     let krate = tcx.hir().krate();
 
+    builder.levels.id_to_set.reserve(krate.exported_macros.len() + 1);
+
     let push = builder.levels.push(&krate.item.attrs, &store, true);
     builder.levels.register_id(hir::CRATE_HIR_ID);
     for macro_def in krate.exported_macros {
index 746c3b6af12f017d04e6740acc23f491163f67f7..a1df1a63fc58d3f265cbb4bd602f8ca13913b1e6 100644 (file)
@@ -784,6 +784,7 @@ fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension {
             }
         };
 
+        let attrs: Vec<_> = self.get_item_attrs(id, sess).collect();
         SyntaxExtension::new(
             sess,
             kind,
@@ -791,7 +792,7 @@ fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension {
             helper_attrs,
             self.root.edition,
             Symbol::intern(name),
-            &self.get_item_attrs(id, sess),
+            &attrs,
         )
     }
 
@@ -1157,7 +1158,8 @@ fn each_child_of_item<F>(&self, id: DefIndex, mut callback: F, sess: &Session)
                                 // within the crate. We only need this for fictive constructors,
                                 // for other constructors correct visibilities
                                 // were already encoded in metadata.
-                                let attrs = self.get_item_attrs(def_id.index, sess);
+                                let attrs: Vec<_> =
+                                    self.get_item_attrs(def_id.index, sess).collect();
                                 if sess.contains_name(&attrs, sym::non_exhaustive) {
                                     let crate_def_id = self.local_def_id(CRATE_DEF_INDEX);
                                     vis = ty::Visibility::Restricted(crate_def_id);
@@ -1283,8 +1285,8 @@ fn get_associated_item(&self, id: DefIndex, sess: &Session) -> ty::AssocItem {
         }
     }
 
-    fn get_item_variances(&self, id: DefIndex) -> Vec<ty::Variance> {
-        self.root.tables.variances.get(self, id).unwrap_or_else(Lazy::empty).decode(self).collect()
+    fn get_item_variances(&'a self, id: DefIndex) -> impl Iterator<Item = ty::Variance> + 'a {
+        self.root.tables.variances.get(self, id).unwrap_or_else(Lazy::empty).decode(self)
     }
 
     fn get_ctor_kind(&self, node_id: DefIndex) -> CtorKind {
@@ -1308,7 +1310,11 @@ fn get_ctor_def_id(&self, node_id: DefIndex) -> Option<DefId> {
         }
     }
 
-    fn get_item_attrs(&self, node_id: DefIndex, sess: &Session) -> Vec<ast::Attribute> {
+    fn get_item_attrs(
+        &'a self,
+        node_id: DefIndex,
+        sess: &'a Session,
+    ) -> impl Iterator<Item = ast::Attribute> + 'a {
         // The attributes for a tuple struct/variant are attached to the definition, not the ctor;
         // we assume that someone passing in a tuple struct ctor is actually wanting to
         // look at the definition
@@ -1325,7 +1331,6 @@ fn get_item_attrs(&self, node_id: DefIndex, sess: &Session) -> Vec<ast::Attribut
             .get(self, item_id)
             .unwrap_or_else(Lazy::empty)
             .decode((self, sess))
-            .collect::<Vec<_>>()
     }
 
     fn get_struct_field_names(&self, id: DefIndex, sess: &Session) -> Vec<Spanned<Symbol>> {
index ddd85ab7aaa849dfe19a0f0648d4a3be8f2e7463..85dc60d7eed6d57b61745fdca12410b0207ce7c8 100644 (file)
@@ -138,7 +138,7 @@ fn into_args(self) -> (DefId, DefId) {
         cdata.get_deprecation(def_id.index).map(DeprecationEntry::external)
     }
     item_attrs => { tcx.arena.alloc_from_iter(
-        cdata.get_item_attrs(def_id.index, tcx.sess).into_iter()
+        cdata.get_item_attrs(def_id.index, tcx.sess)
     ) }
     fn_arg_names => { cdata.get_fn_param_names(tcx, def_id.index) }
     rendered_const => { cdata.get_rendered_const(def_id.index) }
@@ -415,11 +415,7 @@ pub fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro {
 
         let span = data.get_span(id.index, sess);
 
-        // Mark the attrs as used
-        let attrs = data.get_item_attrs(id.index, sess);
-        for attr in attrs.iter() {
-            sess.mark_attr_used(attr);
-        }
+        let attrs = data.get_item_attrs(id.index, sess).collect();
 
         let ident = data.item_ident(id.index, sess);
 
@@ -428,7 +424,7 @@ pub fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro {
                 ident,
                 id: ast::DUMMY_NODE_ID,
                 span,
-                attrs: attrs.to_vec(),
+                attrs,
                 kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)),
                 vis: ast::Visibility {
                     span: span.shrink_to_lo(),
index 1e15ae49a0c384e1a25ad4fcc6dd9ed5e56b3a16..947b016a1fc93df413067a1a36d5730f98612424 100644 (file)
@@ -40,7 +40,7 @@ pub struct Canonical<'tcx, V> {
     pub value: V,
 }
 
-pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo>;
+pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
 
 /// A set of values corresponding to the canonical variables from some
 /// `Canonical`. You can give these values to
@@ -88,11 +88,11 @@ fn default() -> Self {
 /// a copy of the canonical value in some other inference context,
 /// with fresh inference variables replacing the canonical values.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)]
-pub struct CanonicalVarInfo {
-    pub kind: CanonicalVarKind,
+pub struct CanonicalVarInfo<'tcx> {
+    pub kind: CanonicalVarKind<'tcx>,
 }
 
-impl CanonicalVarInfo {
+impl<'tcx> CanonicalVarInfo<'tcx> {
     pub fn universe(&self) -> ty::UniverseIndex {
         self.kind.universe()
     }
@@ -113,7 +113,7 @@ pub fn is_existential(&self) -> bool {
 /// in the type-theory sense of the term -- i.e., a "meta" type system
 /// that analyzes type-like values.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)]
-pub enum CanonicalVarKind {
+pub enum CanonicalVarKind<'tcx> {
     /// Some kind of type inference variable.
     Ty(CanonicalTyVarKind),
 
@@ -132,10 +132,10 @@ pub enum CanonicalVarKind {
     Const(ty::UniverseIndex),
 
     /// A "placeholder" that represents "any const".
-    PlaceholderConst(ty::PlaceholderConst),
+    PlaceholderConst(ty::PlaceholderConst<'tcx>),
 }
 
-impl CanonicalVarKind {
+impl<'tcx> CanonicalVarKind<'tcx> {
     pub fn universe(self) -> ty::UniverseIndex {
         match self {
             CanonicalVarKind::Ty(kind) => match kind {
@@ -287,9 +287,11 @@ pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<'tcx, W>
     ty::Binder<ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>>;
 
 CloneTypeFoldableAndLiftImpls! {
-    crate::infer::canonical::Certainty,
-    crate::infer::canonical::CanonicalVarInfo,
-    crate::infer::canonical::CanonicalVarKind,
+    for <'tcx> {
+        crate::infer::canonical::Certainty,
+        crate::infer::canonical::CanonicalVarInfo<'tcx>,
+        crate::infer::canonical::CanonicalVarKind<'tcx>,
+    }
 }
 
 CloneTypeFoldableImpls! {
index aaf6a8570437cf953731d11110a8beaf2a78b5bc..1def4936860f17f1b8b486a19548e2f89e3685ed 100644 (file)
@@ -278,7 +278,7 @@ fn decode(decoder: &mut D) -> Result<Self, D::Error> {
 impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for CanonicalVarInfos<'tcx> {
     fn decode(decoder: &mut D) -> Result<Self, D::Error> {
         let len = decoder.read_usize()?;
-        let interned: Result<Vec<CanonicalVarInfo>, _> =
+        let interned: Result<Vec<CanonicalVarInfo<'tcx>>, _> =
             (0..len).map(|_| Decodable::decode(decoder)).collect();
         Ok(decoder.tcx().intern_canonical_var_infos(interned?.as_slice()))
     }
index ede28522000afcde48b0b1d645b2db3737845030..ca51f2a941174001d221c44284855dc29874a8c0 100644 (file)
@@ -23,7 +23,7 @@ pub enum ConstKind<'tcx> {
     Bound(ty::DebruijnIndex, ty::BoundVar),
 
     /// A placeholder const - universally quantified higher-ranked const.
-    Placeholder(ty::PlaceholderConst),
+    Placeholder(ty::PlaceholderConst<'tcx>),
 
     /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
     /// variants when the code is monomorphic enough for that.
index 1c6937e685c65d3c6ceba23d3d10744cd8733261..3838e1b006f70939363788281f23362f02ef2f54 100644 (file)
@@ -83,7 +83,7 @@ pub struct CtxtInterners<'tcx> {
     type_: InternedSet<'tcx, TyS<'tcx>>,
     type_list: InternedSet<'tcx, List<Ty<'tcx>>>,
     substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
-    canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo>>,
+    canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
     region: InternedSet<'tcx, RegionKind>,
     existential_predicates: InternedSet<'tcx, List<ExistentialPredicate<'tcx>>>,
     predicate: InternedSet<'tcx, PredicateInner<'tcx>>,
@@ -1613,7 +1613,7 @@ fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
 nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>}
 nop_list_lift! {existential_predicates; ExistentialPredicate<'a> => ExistentialPredicate<'tcx>}
 nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>}
-nop_list_lift! {canonical_var_infos; CanonicalVarInfo => CanonicalVarInfo}
+nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
 nop_list_lift! {projs; ProjectionKind => ProjectionKind}
 
 // This is the impl for `&'a InternalSubsts<'a>`.
@@ -2049,7 +2049,7 @@ impl<'tcx> TyCtxt<'tcx> {
 slice_interners!(
     type_list: _intern_type_list(Ty<'tcx>),
     substs: _intern_substs(GenericArg<'tcx>),
-    canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo),
+    canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
     existential_predicates: _intern_existential_predicates(ExistentialPredicate<'tcx>),
     predicates: _intern_predicates(Predicate<'tcx>),
     projs: _intern_projs(ProjectionKind),
@@ -2448,7 +2448,10 @@ pub fn intern_place_elems(self, ts: &[PlaceElem<'tcx>]) -> &'tcx List<PlaceElem<
         if ts.is_empty() { List::empty() } else { self._intern_place_elems(ts) }
     }
 
-    pub fn intern_canonical_var_infos(self, ts: &[CanonicalVarInfo]) -> CanonicalVarInfos<'tcx> {
+    pub fn intern_canonical_var_infos(
+        self,
+        ts: &[CanonicalVarInfo<'tcx>],
+    ) -> CanonicalVarInfos<'tcx> {
         if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) }
     }
 
index 0042b4a3a42793aa2fb545b12a182604b2aa27d6..06e69a0009b1fe29c674f72dc6328b6d54efd95c 100644 (file)
@@ -1580,11 +1580,9 @@ pub fn cannot_name(self, other: UniverseIndex) -> bool {
     }
 }
 
-/// The "placeholder index" fully defines a placeholder region.
-/// Placeholder regions are identified by both a **universe** as well
-/// as a "bound-region" within that universe. The `bound_region` is
-/// basically a name -- distinct bound regions within the same
-/// universe are just two regions with an unknown relationship to one
+/// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are
+/// identified by both a universe, as well as a name residing within that universe. Distinct bound
+/// regions/types/consts within the same universe simply have an unknown relationship to one
 /// another.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, PartialOrd, Ord)]
 pub struct Placeholder<T> {
@@ -1606,7 +1604,14 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
 
 pub type PlaceholderType = Placeholder<BoundVar>;
 
-pub type PlaceholderConst = Placeholder<BoundVar>;
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
+#[derive(TyEncodable, TyDecodable, PartialOrd, Ord)]
+pub struct BoundConst<'tcx> {
+    pub var: BoundVar,
+    pub ty: Ty<'tcx>,
+}
+
+pub type PlaceholderConst<'tcx> = Placeholder<BoundConst<'tcx>>;
 
 /// A `DefId` which is potentially bundled with its corresponding generic parameter
 /// in case `did` is a const argument.
index c2a13d4b0dec145291161acae8970b2f08c911ba..188bf227c4249ead979ceb1f9d8284985462c487 100644 (file)
@@ -2087,7 +2087,7 @@ pub(super) fn parse_struct_expr(
         recover: bool,
     ) -> PResult<'a, P<Expr>> {
         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 @@ pub(super) fn parse_struct_expr(
         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();
index da1c54e88b5e20ea997054a67c34485e23d3ac56..40aa2db58c720f374d581be7962ac695d21c0c79 100644 (file)
@@ -1180,8 +1180,7 @@ fn parse_abi(&mut self) -> Option<StrLit> {
     /// Records all tokens consumed by the provided callback,
     /// including the current token. These tokens are collected
     /// into a `LazyTokenStream`, and returned along with the result
-    /// of the callback. The returned `LazyTokenStream` will be `None`
-    /// if not tokens were captured.
+    /// of the callback.
     ///
     /// Note: If your callback consumes an opening delimiter
     /// (including the case where you call `collect_tokens`
@@ -1203,17 +1202,14 @@ pub fn collect_tokens<R>(
 
         let ret = f(self)?;
 
-        // We didn't capture any tokens
-        let num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls;
-        if num_calls == 0 {
-            return Ok((ret, None));
-        }
-
         // Produces a `TokenStream` on-demand. Using `cursor_snapshot`
         // and `num_calls`, we can reconstruct the `TokenStream` seen
         // by the callback. This allows us to avoid producing a `TokenStream`
         // if it is never needed - for example, a captured `macro_rules!`
         // argument that is never passed to a proc macro.
+        // In practice token stream creation happens rarely compared to
+        // calls to `collect_tokens` (see some statistics in #78736),
+        // so we are doing as little up-front work as possible.
         //
         // This also makes `Parser` very cheap to clone, since
         // there is no intermediate collection buffer to clone.
@@ -1247,8 +1243,8 @@ fn create_token_stream(&self) -> TokenStream {
 
         let lazy_impl = LazyTokenStreamImpl {
             start_token,
+            num_calls: self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls,
             cursor_snapshot,
-            num_calls,
             desugar_doc_comments: self.desugar_doc_comments,
         };
         Ok((ret, Some(LazyTokenStream::new(lazy_impl))))
index 83016ed36a7293ead6e8f0f163998371db29fcbc..34145c3c138a1c938cb8525ab88c9c89a72248f9 100644 (file)
@@ -7,7 +7,7 @@
 
 use crate::def_collector::collect_definitions;
 use crate::imports::{Import, ImportKind};
-use crate::macros::{MacroRulesBinding, MacroRulesScope};
+use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
 use crate::{CrateLint, Determinacy, PathResult, ResolutionError, VisResolutionError};
 use crate::{
@@ -209,7 +209,7 @@ fn nearest_mod_parent(&mut self, def_id: DefId) -> Module<'a> {
         &mut self,
         fragment: &AstFragment,
         parent_scope: ParentScope<'a>,
-    ) -> MacroRulesScope<'a> {
+    ) -> MacroRulesScopeRef<'a> {
         collect_definitions(self, fragment, parent_scope.expansion);
         let mut visitor = BuildReducedGraphVisitor { r: self, parent_scope };
         fragment.visit_with(&mut visitor);
@@ -220,7 +220,8 @@ fn nearest_mod_parent(&mut self, def_id: DefId) -> Module<'a> {
         let def_id = module.def_id().expect("unpopulated module without a def-id");
         for child in self.cstore().item_children_untracked(def_id, self.session) {
             let child = child.map_id(|_| panic!("unexpected id"));
-            BuildReducedGraphVisitor { r: self, parent_scope: ParentScope::module(module) }
+            let parent_scope = ParentScope::module(module, self);
+            BuildReducedGraphVisitor { r: self, parent_scope }
                 .build_reduced_graph_for_external_crate_res(child);
         }
     }
@@ -1154,7 +1155,7 @@ fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
         false
     }
 
-    fn visit_invoc(&mut self, id: NodeId) -> MacroRulesScope<'a> {
+    fn visit_invoc(&mut self, id: NodeId) -> MacroRulesScopeRef<'a> {
         let invoc_id = id.placeholder_to_expn_id();
 
         self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id);
@@ -1162,7 +1163,9 @@ fn visit_invoc(&mut self, id: NodeId) -> MacroRulesScope<'a> {
         let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope);
         assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation");
 
-        MacroRulesScope::Invocation(invoc_id)
+        let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id));
+        self.r.invocation_macro_rules_scopes.entry(invoc_id).or_default().insert(scope);
+        scope
     }
 
     fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
@@ -1196,7 +1199,7 @@ fn insert_unused_macro(
         }
     }
 
-    fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScope<'a> {
+    fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'a> {
         let parent_scope = self.parent_scope;
         let expansion = parent_scope.expansion;
         let def_id = self.r.local_def_id(item.id);
@@ -1239,11 +1242,13 @@ fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScope<'a> {
                 self.insert_unused_macro(ident, def_id, item.id, span);
             }
             self.r.visibilities.insert(def_id, vis);
-            MacroRulesScope::Binding(self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
-                parent_macro_rules_scope: parent_scope.macro_rules,
-                binding,
-                ident,
-            }))
+            self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
+                self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
+                    parent_macro_rules_scope: parent_scope.macro_rules,
+                    binding,
+                    ident,
+                }),
+            ))
         } else {
             let module = parent_scope.module;
             let vis = match item.kind {
index 5c7a7c1d0ae3156f4171811ffd9e4e9be3fddd07..acd88af1806ca696c4219537263ad64832cfa14e 100644 (file)
@@ -630,7 +630,7 @@ fn early_lookup_typo_candidate(
                     }
                 }
                 Scope::MacroRules(macro_rules_scope) => {
-                    if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope {
+                    if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope.get() {
                         let res = macro_rules_binding.binding.res();
                         if filter_fn(res) {
                             suggestions
index 2337f0d09abb740e3622026b38a8aa7f8960ce8c..f156caf23ba9dc470467fd1d15be5b4aa312c5c7 100644 (file)
@@ -298,9 +298,7 @@ fn is_call(self) -> bool {
                     _,
                 )
                 | 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
@@ -677,7 +675,7 @@ fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> {
         // During late resolution we only track the module component of the parent scope,
         // although it may be useful to track other components as well for diagnostics.
         let graph_root = resolver.graph_root;
-        let parent_scope = ParentScope::module(graph_root);
+        let parent_scope = ParentScope::module(graph_root, resolver);
         let start_rib_kind = ModuleRibKind(graph_root);
         LateResolutionVisitor {
             r: resolver,
index f1e30470f8ea078dd6f0719163eba77ce8ac1224..4e85c88c0e50407a226fb254a32f67da3154adc8 100644 (file)
@@ -65,7 +65,7 @@
 use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
 use imports::{Import, ImportKind, ImportResolver, NameResolution};
 use late::{HasGenericParams, PathSource, Rib, RibKind::*};
-use macros::{MacroRulesBinding, MacroRulesScope};
+use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 
 type Res = def::Res<NodeId>;
 
@@ -101,7 +101,7 @@ fn determined(determined: bool) -> Determinacy {
 enum Scope<'a> {
     DeriveHelpers(ExpnId),
     DeriveHelpersCompat,
-    MacroRules(MacroRulesScope<'a>),
+    MacroRules(MacroRulesScopeRef<'a>),
     CrateRoot,
     Module(Module<'a>),
     RegisteredAttrs,
@@ -134,18 +134,18 @@ enum ScopeSet {
 pub struct ParentScope<'a> {
     module: Module<'a>,
     expansion: ExpnId,
-    macro_rules: MacroRulesScope<'a>,
+    macro_rules: MacroRulesScopeRef<'a>,
     derives: &'a [ast::Path],
 }
 
 impl<'a> ParentScope<'a> {
     /// Creates a parent scope with the passed argument used as the module scope component,
     /// and other scope components set to default empty values.
-    pub fn module(module: Module<'a>) -> ParentScope<'a> {
+    pub fn module(module: Module<'a>, resolver: &Resolver<'a>) -> ParentScope<'a> {
         ParentScope {
             module,
             expansion: ExpnId::root(),
-            macro_rules: MacroRulesScope::Empty,
+            macro_rules: resolver.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty),
             derives: &[],
         }
     }
@@ -975,7 +975,10 @@ pub struct Resolver<'a> {
     invocation_parent_scopes: FxHashMap<ExpnId, ParentScope<'a>>,
     /// `macro_rules` scopes *produced* by expanding the macro invocations,
     /// include all the `macro_rules` items and other invocations generated by them.
-    output_macro_rules_scopes: FxHashMap<ExpnId, MacroRulesScope<'a>>,
+    output_macro_rules_scopes: FxHashMap<ExpnId, MacroRulesScopeRef<'a>>,
+    /// References to all `MacroRulesScope::Invocation(invoc_id)`s, used to update such scopes
+    /// when their corresponding `invoc_id`s get expanded.
+    invocation_macro_rules_scopes: FxHashMap<ExpnId, FxHashSet<MacroRulesScopeRef<'a>>>,
     /// Helper attributes that are in scope for the given expansion.
     helper_attrs: FxHashMap<ExpnId, Vec<Ident>>,
 
@@ -1044,6 +1047,9 @@ fn alloc_import(&'a self, import: Import<'a>) -> &'a Import<'_> {
     fn alloc_name_resolution(&'a self) -> &'a RefCell<NameResolution<'a>> {
         self.name_resolutions.alloc(Default::default())
     }
+    fn alloc_macro_rules_scope(&'a self, scope: MacroRulesScope<'a>) -> MacroRulesScopeRef<'a> {
+        PtrKey(self.dropless.alloc(Cell::new(scope)))
+    }
     fn alloc_macro_rules_binding(
         &'a self,
         binding: MacroRulesBinding<'a>,
@@ -1231,14 +1237,11 @@ pub fn new(
         let (registered_attrs, registered_tools) =
             macros::registered_attrs_and_tools(session, &krate.attrs);
 
-        let mut invocation_parent_scopes = FxHashMap::default();
-        invocation_parent_scopes.insert(ExpnId::root(), ParentScope::module(graph_root));
-
         let features = session.features_untracked();
         let non_macro_attr =
             |mark_used| Lrc::new(SyntaxExtension::non_macro_attr(mark_used, session.edition()));
 
-        Resolver {
+        let mut resolver = Resolver {
             session,
 
             definitions,
@@ -1305,8 +1308,9 @@ pub fn new(
             dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(session.edition())),
             dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(session.edition())),
             non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)],
-            invocation_parent_scopes,
+            invocation_parent_scopes: Default::default(),
             output_macro_rules_scopes: Default::default(),
+            invocation_macro_rules_scopes: Default::default(),
             helper_attrs: Default::default(),
             local_macro_def_scopes: FxHashMap::default(),
             name_already_seen: FxHashMap::default(),
@@ -1333,7 +1337,12 @@ pub fn new(
             invocation_parents,
             next_disambiguator: Default::default(),
             trait_impl_items: Default::default(),
-        }
+        };
+
+        let root_parent_scope = ParentScope::module(graph_root, &resolver);
+        resolver.invocation_parent_scopes.insert(ExpnId::root(), root_parent_scope);
+
+        resolver
     }
 
     pub fn next_node_id(&mut self) -> NodeId {
@@ -1703,7 +1712,7 @@ fn visit_scopes<T>(
                 }
                 Scope::DeriveHelpers(..) => Scope::DeriveHelpersCompat,
                 Scope::DeriveHelpersCompat => Scope::MacroRules(parent_scope.macro_rules),
-                Scope::MacroRules(macro_rules_scope) => match macro_rules_scope {
+                Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() {
                     MacroRulesScope::Binding(binding) => {
                         Scope::MacroRules(binding.parent_macro_rules_scope)
                     }
@@ -3200,7 +3209,7 @@ pub fn resolve_str_path_error(
             }
         };
         let module = self.get_module(module_id);
-        let parent_scope = &ParentScope::module(module);
+        let parent_scope = &ParentScope::module(module, self);
         let res = self.resolve_ast_path(&path, ns, parent_scope).map_err(|_| ())?;
         Ok((path, res))
     }
index b5b281b93bcae5376115c7588b4a64d1adf42576..6bc9419ea84110ac697ca82503c54346479cc010 100644 (file)
@@ -11,6 +11,7 @@
 use rustc_ast_pretty::pprust;
 use rustc_attr::StabilityLevel;
 use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::ptr_key::PtrKey;
 use rustc_errors::struct_span_err;
 use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension};
 use rustc_expand::compile_declarative_macro;
@@ -29,6 +30,7 @@
 
 use rustc_data_structures::sync::Lrc;
 use rustc_span::hygiene::{AstPass, MacroKind};
+use std::cell::Cell;
 use std::{mem, ptr};
 
 type Res = def::Res<NodeId>;
@@ -39,7 +41,7 @@
 pub struct MacroRulesBinding<'a> {
     crate binding: &'a NameBinding<'a>,
     /// `macro_rules` scope into which the `macro_rules` item was planted.
-    crate parent_macro_rules_scope: MacroRulesScope<'a>,
+    crate parent_macro_rules_scope: MacroRulesScopeRef<'a>,
     crate ident: Ident,
 }
 
@@ -59,6 +61,14 @@ pub enum MacroRulesScope<'a> {
     Invocation(ExpnId),
 }
 
+/// `macro_rules!` scopes are always kept by reference and inside a cell.
+/// The reason is that we update all scopes with value `MacroRulesScope::Invocation(invoc_id)`
+/// in-place immediately after `invoc_id` gets expanded.
+/// This helps to avoid uncontrollable growth of `macro_rules!` scope chains,
+/// which usually grow lineraly with the number of macro invocations
+/// in a module (including derives) and hurt performance.
+pub(crate) type MacroRulesScopeRef<'a> = PtrKey<'a, Cell<MacroRulesScope<'a>>>;
+
 // Macro namespace is separated into two sub-namespaces, one for bang macros and
 // one for attribute-like macros (attributes, derives).
 // We ignore resolutions from one sub-namespace when searching names in scope for another.
@@ -163,6 +173,22 @@ fn visit_ast_fragment_with_placeholders(&mut self, expansion: ExpnId, fragment:
         let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope);
         self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
 
+        // Update all `macro_rules` scopes referring to this invocation. This is an optimization
+        // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`.
+        if let Some(invocation_scopes) = self.invocation_macro_rules_scopes.remove(&expansion) {
+            for invocation_scope in &invocation_scopes {
+                invocation_scope.set(output_macro_rules_scope.get());
+            }
+            // All `macro_rules` scopes that previously referred to `expansion`
+            // are now rerouted to its output scope, if it's also an invocation.
+            if let MacroRulesScope::Invocation(invoc_id) = output_macro_rules_scope.get() {
+                self.invocation_macro_rules_scopes
+                    .entry(invoc_id)
+                    .or_default()
+                    .extend(invocation_scopes);
+            }
+        }
+
         parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
     }
 
@@ -655,7 +681,7 @@ struct Flags: u8 {
                         }
                         result
                     }
-                    Scope::MacroRules(macro_rules_scope) => match macro_rules_scope {
+                    Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() {
                         MacroRulesScope::Binding(macro_rules_binding)
                             if ident == macro_rules_binding.ident =>
                         {
index dbb5e3cc9f06620f3a03041096dc62ac75607400..40d60a8394be362662519ae2fcd1bf14d643a450 100644 (file)
@@ -816,7 +816,7 @@ fn process_struct_lit(
         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 @@ fn process_struct_lit(
             }
         }
 
-        walk_list!(self, visit_expr, base);
+        if let Some(base) = rest {
+            self.visit_expr(&base);
+        }
     }
 
     fn process_method_call(
@@ -1399,7 +1401,7 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'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 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'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)
index f52b64f488345edf1cfde7957f451d9cec3edbc6..0926561f4c5134038ddd78e9116389934a80a0b0 100644 (file)
@@ -1894,16 +1894,37 @@ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
             return;
         }
 
+        let (_, line_hi, col_hi) = match ctx.byte_pos_to_line_and_col(span.hi) {
+            Some(pos) => pos,
+            None => {
+                Hash::hash(&TAG_INVALID_SPAN, hasher);
+                span.ctxt.hash_stable(ctx, hasher);
+                return;
+            }
+        };
+
         Hash::hash(&TAG_VALID_SPAN, hasher);
         // We truncate the stable ID hash and line and column numbers. The chances
         // of causing a collision this way should be minimal.
         Hash::hash(&(file_lo.name_hash as u64), hasher);
 
-        let col = (col_lo.0 as u64) & 0xFF;
-        let line = ((line_lo as u64) & 0xFF_FF_FF) << 8;
-        let len = ((span.hi - span.lo).0 as u64) << 32;
-        let line_col_len = col | line | len;
-        Hash::hash(&line_col_len, hasher);
+        // Hash both the length and the end location (line/column) of a span. If we
+        // hash only the length, for example, then two otherwise equal spans with
+        // different end locations will have the same hash. This can cause a problem
+        // during incremental compilation wherein a previous result for a query that
+        // depends on the end location of a span will be incorrectly reused when the
+        // end location of the span it depends on has changed (see issue #74890). A
+        // similar analysis applies if some query depends specifically on the length
+        // of the span, but we only hash the end location. So hash both.
+
+        let col_lo_trunc = (col_lo.0 as u64) & 0xFF;
+        let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8;
+        let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32;
+        let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40;
+        let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc;
+        let len = (span.hi - span.lo).0;
+        Hash::hash(&col_line, hasher);
+        Hash::hash(&len, hasher);
         span.ctxt.hash_stable(ctx, hasher);
     }
 }
index 8e60085262afdd695d05fc83f536e73c203e53e4..5ebd6c4a2349f5a689656f22b6ee9ce77462cca4 100644 (file)
@@ -155,6 +155,7 @@ macro_rules! types {
 mod mips;
 mod nvptx;
 mod riscv;
+mod spirv;
 mod x86;
 
 pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass};
@@ -163,6 +164,7 @@ macro_rules! types {
 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 @@ fn from_str(s: &str) -> Result<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 @@ pub fn parse(
             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 @@ pub fn name(self) -> &'static str {
             Self::Nvptx(r) => r.name(),
             Self::Hexagon(r) => r.name(),
             Self::Mips(r) => r.name(),
+            Self::SpirV(r) => r.name(),
         }
     }
 
@@ -333,6 +343,7 @@ pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<Sel
             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 @@ pub fn suggest_modifier(
             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 @@ pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str
             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 @@ pub fn supported_types(
             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 @@ pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> {
                 InlineAsmArch::Mips | InlineAsmArch::Mips64 => {
                     Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?)
                 }
+                InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?),
             })
         })
     }
@@ -429,6 +444,7 @@ pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] {
             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 (file)
index 0000000..da82749
--- /dev/null
@@ -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<Self> {
+        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 {}
+}
index d0ad45153d677decb2f7049f83cb2ad4241f550a..c9f622820dea76e69df3de38faf1c9cd167d46fb 100644 (file)
@@ -10,7 +10,6 @@
 
 pub fn target() -> Target {
     let opts = TargetOptions {
-        vendor: String::new(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".to_owned()),
         features: "+strict-align,+neon,+fp-armv8".to_string(),
index 41bd2182905c942a9b75abb22672bd945eaa4334..0811871c993aa2cc4810647f019cee536cbfbe8c 100644 (file)
@@ -10,7 +10,6 @@
 
 pub fn target() -> Target {
     let opts = TargetOptions {
-        vendor: String::new(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".to_owned()),
         features: "+strict-align,-neon,-fp-armv8".to_string(),
index 36856305723711fcf7b8967579f5fbc5edf78a17..c6586b79b87f832dd920f0510d606604cad076bc 100644 (file)
@@ -12,7 +12,6 @@ pub fn target() -> Target {
 
         options: TargetOptions {
             endian: "big".to_string(),
-            vendor: String::new(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
             executables: true,
             linker: Some("rust-lld".to_owned()),
index 2ff3c8950c4837d38ff04221a7c49926774f5784..e3d4397f6123f226a98530e73c49a3f6af064dbe 100644 (file)
@@ -12,7 +12,6 @@ pub fn target() -> Target {
 
         options: TargetOptions {
             endian: "big".to_string(),
-            vendor: String::new(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
             executables: true,
             linker: Some("rust-lld".to_owned()),
index 742b403cff90cbfc09d9ae0108ce42bb9c0d7b75..74deab0191652355a2ea14d00a23ec56e7d29275 100644 (file)
@@ -10,9 +10,6 @@
 // bare-metal binaries (the `gcc` linker has the advantage that it knows where C
 // libraries and crt*.o are but it's not much of an advantage here); LLD is also
 // faster
-// - `os` set to `none`. rationale: matches `thumb` targets
-// - `env` and `vendor` are set to an empty string. rationale: matches `thumb`
-// targets
 // - `panic_strategy` set to `abort`. rationale: matches `thumb` targets
 // - `relocation-model` set to `static`; also no PIE, no relro and no dynamic
 // linking. rationale: matches `thumb` targets
@@ -21,7 +18,6 @@
 
 pub fn target() -> Target {
     let opts = TargetOptions {
-        vendor: String::new(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".to_owned()),
         features: "+v7,+thumb2,+soft-float,-neon,+strict-align".to_string(),
index b9cda18d6b46f4ec533025e0198f5d7da758a6d6..c5c720f5fbde43f2d05e8901a4aace8ce36b7cd5 100644 (file)
@@ -9,7 +9,6 @@
 
 pub fn target() -> Target {
     let opts = TargetOptions {
-        vendor: String::new(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".to_owned()),
         features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".to_string(),
index 440c2434907beaecefe5a8adace15367c692a258..3f49bd87869374a453a8b7eae7a16740b6f0b8f2 100644 (file)
@@ -11,7 +11,6 @@ pub fn target() -> Target {
         arch: "arm".to_string(),
 
         options: TargetOptions {
-            vendor: String::new(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
             executables: true,
             linker: Some("rust-lld".to_owned()),
index c1bf332a72ddfbd6132fc8ecb8f8d83f51f64651..9b2e8a8058fe7741f4222487e5f29652ef97caff 100644 (file)
@@ -11,7 +11,6 @@ pub fn target() -> Target {
         arch: "arm".to_string(),
 
         options: TargetOptions {
-            vendor: String::new(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
             executables: true,
             linker: Some("rust-lld".to_owned()),
index 9cc10032c71dad12cc0d2497573fce47c9d66755..67a7684da2c70434bc917388fb3cfd1e7865dde5 100644 (file)
@@ -11,7 +11,6 @@ pub fn target(target_cpu: String) -> Target {
         pointer_width: 16,
         options: TargetOptions {
             c_int_width: "16".to_string(),
-            os: "unknown".to_string(),
             cpu: target_cpu.clone(),
             exe_suffix: ".elf".to_string(),
 
index e467c7c8f21e9989c6fab7b59792a3a71efaf70e..5c39773cbe381743bebe5668a8829807bfab808f 100644 (file)
@@ -21,7 +21,6 @@ pub fn opts() -> TargetOptions {
 
     TargetOptions {
         os: "fuchsia".to_string(),
-        vendor: String::new(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".to_owned()),
         lld_flavor: LldFlavor::Ld,
index a8005927a7bebd4bb037718ec318889ba7cfdbfb..0f9d3c3de1543b25f67dd421d28d888835769f67 100644 (file)
@@ -14,7 +14,6 @@ pub fn target() -> Target {
         arch: "mips".to_string(),
 
         options: TargetOptions {
-            vendor: String::new(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
             cpu: "mips32r2".to_string(),
             features: "+mips32r2,+soft-float,+noabicalls".to_string(),
index f949bf95a502af67c9d209fb8ddb2616e1d56550..b37783c5820bf59cb8fab1b3c0f3ba91dcff0e32 100644 (file)
@@ -713,11 +713,14 @@ pub struct TargetOptions {
     pub endian: String,
     /// Width of c_int type. Defaults to "32".
     pub c_int_width: String,
-    /// OS name to use for conditional compilation. Defaults to "none".
+    /// OS name to use for conditional compilation (`target_os`). Defaults to "none".
+    /// "none" implies a bare metal target without `std` library.
+    /// A couple of targets having `std` also use "unknown" as an `os` value,
+    /// but they are exceptions.
     pub os: String,
-    /// Environment name to use for conditional compilation. Defaults to "".
+    /// Environment name to use for conditional compilation (`target_env`). Defaults to "".
     pub env: String,
-    /// Vendor name to use for conditional compilation. Defaults to "unknown".
+    /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown".
     pub vendor: String,
     /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed
     /// on the command line. Defaults to `LinkerFlavor::Gcc`.
@@ -819,10 +822,23 @@ pub struct TargetOptions {
     /// Only useful for compiling against Illumos/Solaris,
     /// as they have a different set of linker flags. Defaults to false.
     pub is_like_solaris: bool,
-    /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows,
-    /// only really used for figuring out how to find libraries, since Windows uses its own
-    /// library naming convention. Defaults to false.
+    /// Whether the target is like Windows.
+    /// This is a combination of several more specific properties represented as a single flag:
+    ///   - The target uses a Windows ABI,
+    ///   - uses PE/COFF as a format for object code,
+    ///   - uses Windows-style dllexport/dllimport for shared libraries,
+    ///   - uses import libraries and .def files for symbol exports,
+    ///   - executables support setting a subsystem.
     pub is_like_windows: bool,
+    /// Whether the target is like MSVC.
+    /// This is a combination of several more specific properties represented as a single flag:
+    ///   - The target has all the properties from `is_like_windows`
+    ///     (for in-tree targets "is_like_msvc â‡’ is_like_windows" is ensured by a unit test),
+    ///   - has some MSVC-specific Windows ABI properties,
+    ///   - uses a link.exe-like linker,
+    ///   - uses CodeView/PDB for debuginfo and natvis for its visualization,
+    ///   - uses SEH-based unwinding,
+    ///   - supports control flow guard mechanism.
     pub is_like_msvc: bool,
     /// Whether the target toolchain is like Emscripten's. Only useful for compiling with
     /// Emscripten toolchain.
index ef966cb702ec451fe5e3afd085ab757e137dd46b..cc2578aa578e8d54796b5c72d7ec98b30d123708 100644 (file)
@@ -9,7 +9,6 @@ pub fn target() -> Target {
 
         options: TargetOptions {
             c_int_width: "16".to_string(),
-            vendor: String::new(),
             executables: true,
 
             // The LLVM backend currently can't generate object files. To
index f348df7d5a716ddd9dfc49296134aebe52ae9296..9ec8467e0ac449507f9aa80e2d97b3ca7a8ae757 100644 (file)
@@ -8,6 +8,7 @@ pub(super) fn test_target(target: Target) {
 
 impl Target {
     fn check_consistency(&self) {
+        assert!(self.is_like_windows || !self.is_like_msvc);
         // Check that LLD with the given flavor is treated identically to the linker it emulates.
         // If your target really needs to deviate from the rules below, except it and document the
         // reasons.
@@ -16,6 +17,7 @@ fn check_consistency(&self) {
                 || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link),
             self.lld_flavor == LldFlavor::Link,
         );
+        assert_eq!(self.is_like_msvc, self.lld_flavor == LldFlavor::Link);
         for args in &[
             &self.pre_link_args,
             &self.late_link_args,
@@ -36,5 +38,18 @@ fn check_consistency(&self) {
                 && self.post_link_objects_fallback.is_empty())
                 || self.crt_objects_fallback.is_some()
         );
+        // Keep the default "unknown" vendor instead.
+        assert_ne!(self.vendor, "");
+        if !self.can_use_os_unknown() {
+            // Keep the default "none" for bare metal targets instead.
+            assert_ne!(self.os, "unknown");
+        }
+    }
+
+    // Add your target to the whitelist if it has `std` library
+    // and you certainly want "unknown" for the OS name.
+    fn can_use_os_unknown(&self) -> bool {
+        self.llvm_target == "wasm32-unknown-unknown"
+            || (self.env == "sgx" && self.vendor == "fortanix")
     }
 }
index e55046750275433a6f4190e7fbafa3cf87cac1a9..ec24807fec4ea407e6ef5aed01f7d6fd1d8046cf 100644 (file)
@@ -32,7 +32,6 @@
 pub fn opts() -> TargetOptions {
     // See rust-lang/rfcs#1645 for a discussion about these defaults
     TargetOptions {
-        vendor: String::new(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         executables: true,
         // In most cases, LLD is good enough
index 79fe77495e73154e2936e3c8c32fae92c282a99f..322b6f530e9fdc9cd87a5e6d03bba1dcf98327d4 100644 (file)
@@ -46,15 +46,6 @@ pub fn opts() -> TargetOptions {
         stack_probes: true,
         singlethread: true,
         linker: Some("rust-lld".to_string()),
-        // FIXME: This should likely be `true` inherited from `msvc_base`
-        // because UEFI follows Windows ABI and uses PE/COFF.
-        // The `false` is probably causing ABI bugs right now.
-        is_like_windows: false,
-        // FIXME: This should likely be `true` inherited from `msvc_base`
-        // because UEFI follows Windows ABI and uses PE/COFF.
-        // The `false` is probably causing ABI bugs right now.
-        is_like_msvc: false,
-
         ..base
     }
 }
index 9c697674f397ad8d178c3363dfb8f9c26797bf87..3f44acdc36b2da1adcc9191868c32bf30a4da11e 100644 (file)
@@ -79,7 +79,6 @@ pub fn target() -> Target {
     let mut options = wasm32_base::options();
 
     options.os = "wasi".to_string();
-    options.vendor = String::new();
     options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
     options
         .pre_link_args
index b0bfb4ad173719c791a3f228f87997aac1c11a50..ea18a689065e30adc5462fde012afbdabf261dc3 100644 (file)
@@ -353,16 +353,13 @@ fn assemble_candidates_from_caller_bounds<'o>(
             all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id());
 
         // Keep only those bounds which may apply, and propagate overflow if it occurs.
-        let mut param_candidates = vec![];
         for bound in matching_bounds {
             let wc = self.evaluate_where_clause(stack, bound)?;
             if wc.may_apply() {
-                param_candidates.push(ParamCandidate(bound));
+                candidates.vec.push(ParamCandidate(bound));
             }
         }
 
-        candidates.vec.extend(param_candidates);
-
         Ok(())
     }
 
index b67c72d7136a67e13ba993993723976124efded5..97ebc12175f73c6149a0b49189636b9d74cd2651 100644 (file)
@@ -495,7 +495,14 @@ pub fn into_sorted_vec(mut self) -> Vec<T> {
         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 @@ fn sift_down_range(&mut self, pos: usize, end: usize) {
         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 @@ fn sift_down_to_bottom(&mut self, mut pos: usize) {
         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);
index fa229251703a55cba6ea69218a8b687167ea50b8..27d90e6613748bdadf34799a91a97984324d551a 100644 (file)
@@ -34,8 +34,8 @@
 /// attacks such as HashDoS.
 ///
 /// The hashing algorithm can be replaced on a per-`HashMap` basis using the
-/// [`default`], [`with_hasher`], and [`with_capacity_and_hasher`] methods. Many
-/// alternative algorithms are available on crates.io, such as the [`fnv`] crate.
+/// [`default`], [`with_hasher`], and [`with_capacity_and_hasher`] methods.
+/// There are many alternative [hashing algorithms available on crates.io].
 ///
 /// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although
 /// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`.
@@ -57,6 +57,7 @@
 /// The original C++ version of SwissTable can be found [here], and this
 /// [CppCon talk] gives an overview of how the algorithm works.
 ///
+/// [hashing algorithms available on crates.io]: https://crates.io/keywords/hasher
 /// [SwissTable]: https://abseil.io/blog/20180927-swisstables
 /// [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h
 /// [CppCon talk]: https://www.youtube.com/watch?v=ncHmEUmJZf4
 /// [`default`]: Default::default
 /// [`with_hasher`]: Self::with_hasher
 /// [`with_capacity_and_hasher`]: Self::with_capacity_and_hasher
-/// [`fnv`]: https://crates.io/crates/fnv
 ///
 /// ```
 /// use std::collections::HashMap;
index c256f556b3c8f207fcc307dabf6a4b633fc492fc..a4123cc15b8767c5d3d2c27403dd8c2cfd119387 100644 (file)
@@ -1656,7 +1656,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
 /// the length of the `to` file as reported by `metadata`.
 ///
 /// If you’re wanting to copy the contents of one file to another and you’re
-/// working with [`File`]s, see the [`io::copy`] function.
+/// working with [`File`]s, see the [`io::copy()`] function.
 ///
 /// # Platform-specific behavior
 ///
diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs
new file mode 100644 (file)
index 0000000..b88bca2
--- /dev/null
@@ -0,0 +1,88 @@
+use crate::io::{self, ErrorKind, Read, Write};
+use crate::mem::MaybeUninit;
+
+/// Copies the entire contents of a reader into a writer.
+///
+/// This function will continuously read data from `reader` and then
+/// write it into `writer` in a streaming fashion until `reader`
+/// returns EOF.
+///
+/// On success, the total number of bytes that were copied from
+/// `reader` to `writer` is returned.
+///
+/// If you’re wanting to copy the contents of one file to another and you’re
+/// working with filesystem paths, see the [`fs::copy`] function.
+///
+/// [`fs::copy`]: crate::fs::copy
+///
+/// # Errors
+///
+/// This function will return an error immediately if any call to [`read`] or
+/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
+/// handled by this function and the underlying operation is retried.
+///
+/// [`read`]: Read::read
+/// [`write`]: Write::write
+///
+/// # Examples
+///
+/// ```
+/// use std::io;
+///
+/// fn main() -> io::Result<()> {
+///     let mut reader: &[u8] = b"hello";
+///     let mut writer: Vec<u8> = vec![];
+///
+///     io::copy(&mut reader, &mut writer)?;
+///
+///     assert_eq!(&b"hello"[..], &writer[..]);
+///     Ok(())
+/// }
+/// ```
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
+where
+    R: Read,
+    W: Write,
+{
+    cfg_if::cfg_if! {
+        if #[cfg(any(target_os = "linux", target_os = "android"))] {
+            crate::sys::kernel_copy::copy_spec(reader, writer)
+        } else {
+            generic_copy(reader, writer)
+        }
+    }
+}
+
+/// The general read-write-loop implementation of
+/// `io::copy` that is used when specializations are not available or not applicable.
+pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
+where
+    R: Read,
+    W: Write,
+{
+    let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit();
+    // FIXME: #42788
+    //
+    //   - This creates a (mut) reference to a slice of
+    //     _uninitialized_ integers, which is **undefined behavior**
+    //
+    //   - Only the standard library gets to soundly "ignore" this,
+    //     based on its privileged knowledge of unstable rustc
+    //     internals;
+    unsafe {
+        reader.initializer().initialize(buf.assume_init_mut());
+    }
+
+    let mut written = 0;
+    loop {
+        let len = match reader.read(unsafe { buf.assume_init_mut() }) {
+            Ok(0) => return Ok(written),
+            Ok(len) => len,
+            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+            Err(e) => return Err(e),
+        };
+        writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
+        written += len as u64;
+    }
+}
index e6efe6ec57eeafd58f4e601e1d48f4642003952b..57413f9bc406348fcff6086cd7c39e392ee68eb6 100644 (file)
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::buffered::{BufReader, BufWriter, LineWriter};
 #[stable(feature = "rust1", since = "1.0.0")]
+pub use self::copy::copy;
+#[stable(feature = "rust1", since = "1.0.0")]
 pub use self::cursor::Cursor;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::error::{Error, ErrorKind, Result};
 #[doc(no_inline, hidden)]
 pub use self::stdio::{set_panic, set_print, LocalOutput};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink};
+pub use self::util::{empty, repeat, sink, Empty, Repeat, Sink};
 
 pub(crate) use self::stdio::clone_io;
 
 mod buffered;
+pub(crate) mod copy;
 mod cursor;
 mod error;
 mod impls;
index 2eb5fb45286201e3d44dbccfee8c9642fa150e8d..8fbce09dd6362673602a42bd2580c43f7de1657c 100644 (file)
@@ -409,6 +409,14 @@ fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
     }
 }
 
+// only used by platform-dependent io::copy specializations, i.e. unused on some platforms
+#[cfg(any(target_os = "linux", target_os = "android"))]
+impl StdinLock<'_> {
+    pub(crate) fn as_mut_buf(&mut self) -> &mut BufReader<impl Read> {
+        &mut self.inner
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Read for StdinLock<'_> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
index 913b28538b7c4d2c63329954006901b621dea6fd..f176c2f088cb314e850d11057fbd3f4f120b5773 100644 (file)
@@ -1,7 +1,7 @@
 use super::{repeat, Cursor, SeekFrom};
 use crate::cmp::{self, min};
-use crate::io::prelude::*;
 use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{BufRead, Read, Seek, Write};
 use crate::ops::Deref;
 
 #[test]
index 2b1f371129eaf5a405b6f32834800042af12629e..db845457c96720397eaa99bfecd88e98a6a46984 100644 (file)
@@ -4,78 +4,7 @@
 mod tests;
 
 use crate::fmt;
-use crate::io::{self, BufRead, ErrorKind, Initializer, IoSlice, IoSliceMut, Read, Write};
-use crate::mem::MaybeUninit;
-
-/// Copies the entire contents of a reader into a writer.
-///
-/// This function will continuously read data from `reader` and then
-/// write it into `writer` in a streaming fashion until `reader`
-/// returns EOF.
-///
-/// On success, the total number of bytes that were copied from
-/// `reader` to `writer` is returned.
-///
-/// If you’re wanting to copy the contents of one file to another and you’re
-/// working with filesystem paths, see the [`fs::copy`] function.
-///
-/// [`fs::copy`]: crate::fs::copy
-///
-/// # Errors
-///
-/// This function will return an error immediately if any call to [`read`] or
-/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
-/// handled by this function and the underlying operation is retried.
-///
-/// [`read`]: Read::read
-/// [`write`]: Write::write
-///
-/// # Examples
-///
-/// ```
-/// use std::io;
-///
-/// fn main() -> io::Result<()> {
-///     let mut reader: &[u8] = b"hello";
-///     let mut writer: Vec<u8> = vec![];
-///
-///     io::copy(&mut reader, &mut writer)?;
-///
-///     assert_eq!(&b"hello"[..], &writer[..]);
-///     Ok(())
-/// }
-/// ```
-#[stable(feature = "rust1", since = "1.0.0")]
-pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
-where
-    R: Read,
-    W: Write,
-{
-    let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit();
-    // FIXME: #42788
-    //
-    //   - This creates a (mut) reference to a slice of
-    //     _uninitialized_ integers, which is **undefined behavior**
-    //
-    //   - Only the standard library gets to soundly "ignore" this,
-    //     based on its privileged knowledge of unstable rustc
-    //     internals;
-    unsafe {
-        reader.initializer().initialize(buf.assume_init_mut());
-    }
-
-    let mut written = 0;
-    loop {
-        let len = match reader.read(unsafe { buf.assume_init_mut() }) {
-            Ok(0) => return Ok(written),
-            Ok(len) => len,
-            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
-            Err(e) => return Err(e),
-        };
-        writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
-        written += len as u64;
-    }
-}
+use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Write};
 
 /// A reader which is always at EOF.
 ///
index fa2608e6fb6d3385a6a70cd501d15d16316087af..ffc9cf3f2eb14b0ab505c1276d7a40a0a31e7b04 100644 (file)
 #![feature(toowned_clone_into)]
 #![feature(total_cmp)]
 #![feature(trace_macros)]
+#![feature(try_blocks)]
 #![feature(try_reserve)]
 #![feature(unboxed_closures)]
 #![feature(unsafe_block_in_unsafe_fn)]
index 96594095cc36df46d0cf69572815460a261b9b49..13cf930379cbca9666c2ecd6e7750e04a0b51b71 100644 (file)
@@ -1204,88 +1204,19 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
 
 #[cfg(any(target_os = "linux", target_os = "android"))]
 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
-    use crate::cmp;
-    use crate::sync::atomic::{AtomicBool, Ordering};
-
-    // Kernel prior to 4.5 don't have copy_file_range
-    // We store the availability in a global to avoid unnecessary syscalls
-    static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
-
-    unsafe fn copy_file_range(
-        fd_in: libc::c_int,
-        off_in: *mut libc::loff_t,
-        fd_out: libc::c_int,
-        off_out: *mut libc::loff_t,
-        len: libc::size_t,
-        flags: libc::c_uint,
-    ) -> libc::c_long {
-        libc::syscall(libc::SYS_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags)
-    }
-
     let (mut reader, reader_metadata) = open_from(from)?;
     let max_len = u64::MAX;
     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
 
-    let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
-    let mut written = 0u64;
-    while written < max_len {
-        let copy_result = if has_copy_file_range {
-            let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64) as usize;
-            let copy_result = unsafe {
-                // We actually don't have to adjust the offsets,
-                // because copy_file_range adjusts the file offset automatically
-                cvt(copy_file_range(
-                    reader.as_raw_fd(),
-                    ptr::null_mut(),
-                    writer.as_raw_fd(),
-                    ptr::null_mut(),
-                    bytes_to_copy,
-                    0,
-                ))
-            };
-            if let Err(ref copy_err) = copy_result {
-                match copy_err.raw_os_error() {
-                    Some(libc::ENOSYS | libc::EPERM | libc::EOPNOTSUPP) => {
-                        HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
-                    }
-                    _ => {}
-                }
-            }
-            copy_result
-        } else {
-            Err(io::Error::from_raw_os_error(libc::ENOSYS))
-        };
-        match copy_result {
-            Ok(0) if written == 0 => {
-                // fallback to work around several kernel bugs where copy_file_range will fail to
-                // copy any bytes and return 0 instead of an error if
-                // - reading virtual files from the proc filesystem which appear to have 0 size
-                //   but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
-                // - copying from an overlay filesystem in docker. reported to occur on fedora 32.
-                return io::copy(&mut reader, &mut writer);
-            }
-            Ok(0) => return Ok(written), // reached EOF
-            Ok(ret) => written += ret as u64,
-            Err(err) => {
-                match err.raw_os_error() {
-                    Some(
-                        libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP,
-                    ) => {
-                        // Try fallback io::copy if either:
-                        // - Kernel version is < 4.5 (ENOSYS)
-                        // - Files are mounted on different fs (EXDEV)
-                        // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
-                        // - copy_file_range is disallowed, for example by seccomp (EPERM)
-                        // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
-                        assert_eq!(written, 0);
-                        return io::copy(&mut reader, &mut writer);
-                    }
-                    _ => return Err(err),
-                }
-            }
-        }
+    use super::kernel_copy::{copy_regular_files, CopyResult};
+
+    match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
+        CopyResult::Ended(result) => result,
+        CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
+            Ok(bytes) => Ok(bytes + written),
+            Err(e) => Err(e),
+        },
     }
-    Ok(written)
 }
 
 #[cfg(any(target_os = "macos", target_os = "ios"))]
diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs
new file mode 100644 (file)
index 0000000..ac2fcfc
--- /dev/null
@@ -0,0 +1,603 @@
+//! This module contains specializations that can offload `io::copy()` operations on file descriptor
+//! containing types (`File`, `TcpStream`, etc.) to more efficient syscalls than `read(2)` and `write(2)`.
+//!
+//! Specialization is only applied to wholly std-owned types so that user code can't observe
+//! that the `Read` and `Write` traits are not used.
+//!
+//! Since a copy operation involves a reader and writer side where each can consist of different types
+//! and also involve generic wrappers (e.g. `Take`, `BufReader`) it is not practical to specialize
+//! a single method on all possible combinations.
+//!
+//! Instead readers and writers are handled separately by the `CopyRead` and `CopyWrite` specialization
+//! traits and then specialized on by the `Copier::copy` method.
+//!
+//! `Copier` uses the specialization traits to unpack the underlying file descriptors and
+//! additional prerequisites and constraints imposed by the wrapper types.
+//!
+//! Once it has obtained all necessary pieces and brought any wrapper types into a state where they
+//! can be safely bypassed it will attempt to use the `copy_file_range(2)`,
+//! `sendfile(2)` or `splice(2)` syscalls to move data directly between file descriptors.
+//! Since those syscalls have requirements that cannot be fully checked in advance and
+//! gathering additional information about file descriptors would require additional syscalls
+//! anyway it simply attempts to use them one after another (guided by inaccurate hints) to
+//! figure out which one works and and falls back to the generic read-write copy loop if none of them
+//! does.
+//! Once a working syscall is found for a pair of file descriptors it will be called in a loop
+//! until the copy operation is completed.
+//!
+//! Advantages of using these syscalls:
+//!
+//! * fewer context switches since reads and writes are coalesced into a single syscall
+//!   and more bytes are transferred per syscall. This translates to higher throughput
+//!   and fewer CPU cycles, at least for sufficiently large transfers to amortize the initial probing.
+//! * `copy_file_range` creates reflink copies on CoW filesystems, thus moving less data and
+//!   consuming less disk space
+//! * `sendfile` and `splice` can perform zero-copy IO under some circumstances while
+//!   a naive copy loop would move every byte through the CPU.
+//!
+//! Drawbacks:
+//!
+//! * copy operations smaller than the default buffer size can under some circumstances, especially
+//!   on older kernels, incur more syscalls than the naive approach would. As mentioned above
+//!   the syscall selection is guided by hints to minimize this possibility but they are not perfect.
+//! * optimizations only apply to std types. If a user adds a custom wrapper type, e.g. to report
+//!   progress, they can hit a performance cliff.
+//! * complexity
+
+use crate::cmp::min;
+use crate::convert::TryInto;
+use crate::fs::{File, Metadata};
+use crate::io::copy::generic_copy;
+use crate::io::{
+    BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take,
+    Write,
+};
+use crate::mem::ManuallyDrop;
+use crate::net::TcpStream;
+use crate::os::unix::fs::FileTypeExt;
+use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use crate::process::{ChildStderr, ChildStdin, ChildStdout};
+use crate::ptr;
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sys::cvt;
+
+#[cfg(test)]
+mod tests;
+
+pub(crate) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
+    read: &mut R,
+    write: &mut W,
+) -> Result<u64> {
+    let copier = Copier { read, write };
+    SpecCopy::copy(copier)
+}
+
+/// This type represents either the inferred `FileType` of a `RawFd` based on the source
+/// type from which it was extracted or the actual metadata
+///
+/// The methods on this type only provide hints, due to `AsRawFd` and `FromRawFd` the inferred
+/// type may be wrong.
+enum FdMeta {
+    /// We obtained the FD from a type that can contain any type of `FileType` and queried the metadata
+    /// because it is cheaper than probing all possible syscalls (reader side)
+    Metadata(Metadata),
+    Socket,
+    Pipe,
+    /// We don't have any metadata, e.g. because the original type was `File` which can represent
+    /// any `FileType` and we did not query the metadata either since it did not seem beneficial
+    /// (writer side)
+    NoneObtained,
+}
+
+impl FdMeta {
+    fn maybe_fifo(&self) -> bool {
+        match self {
+            FdMeta::Metadata(meta) => meta.file_type().is_fifo(),
+            FdMeta::Socket => false,
+            FdMeta::Pipe => true,
+            FdMeta::NoneObtained => true,
+        }
+    }
+
+    fn potential_sendfile_source(&self) -> bool {
+        match self {
+            // procfs erronously shows 0 length on non-empty readable files.
+            // and if a file is truly empty then a `read` syscall will determine that and skip the write syscall
+            // thus there would be benefit from attempting sendfile
+            FdMeta::Metadata(meta)
+                if meta.file_type().is_file() && meta.len() > 0
+                    || meta.file_type().is_block_device() =>
+            {
+                true
+            }
+            _ => false,
+        }
+    }
+
+    fn copy_file_range_candidate(&self) -> bool {
+        match self {
+            // copy_file_range will fail on empty procfs files. `read` can determine whether EOF has been reached
+            // without extra cost and skip the write, thus there is no benefit in attempting copy_file_range
+            FdMeta::Metadata(meta) if meta.is_file() && meta.len() > 0 => true,
+            FdMeta::NoneObtained => true,
+            _ => false,
+        }
+    }
+}
+
+struct CopyParams(FdMeta, Option<RawFd>);
+
+struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
+    read: &'a mut R,
+    write: &'b mut W,
+}
+
+trait SpecCopy {
+    fn copy(self) -> Result<u64>;
+}
+
+impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
+    default fn copy(self) -> Result<u64> {
+        generic_copy(self.read, self.write)
+    }
+}
+
+impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
+    fn copy(self) -> Result<u64> {
+        let (reader, writer) = (self.read, self.write);
+        let r_cfg = reader.properties();
+        let w_cfg = writer.properties();
+
+        // before direct operations on file descriptors ensure that all source and sink buffers are empty
+        let mut flush = || -> crate::io::Result<u64> {
+            let bytes = reader.drain_to(writer, u64::MAX)?;
+            // BufWriter buffered bytes have already been accounted for in earlier write() calls
+            writer.flush()?;
+            Ok(bytes)
+        };
+
+        let mut written = 0u64;
+
+        if let (CopyParams(input_meta, Some(readfd)), CopyParams(output_meta, Some(writefd))) =
+            (r_cfg, w_cfg)
+        {
+            written += flush()?;
+            let max_write = reader.min_limit();
+
+            if input_meta.copy_file_range_candidate() && output_meta.copy_file_range_candidate() {
+                let result = copy_regular_files(readfd, writefd, max_write);
+
+                match result {
+                    CopyResult::Ended(Ok(bytes_copied)) => return Ok(bytes_copied + written),
+                    CopyResult::Ended(err) => return err,
+                    CopyResult::Fallback(bytes) => written += bytes,
+                }
+            }
+
+            // on modern kernels sendfile can copy from any mmapable type (some but not all regular files and block devices)
+            // to any writable file descriptor. On older kernels the writer side can only be a socket.
+            // So we just try and fallback if needed.
+            // If current file offsets + write sizes overflow it may also fail, we do not try to fix that and instead
+            // fall back to the generic copy loop.
+            if input_meta.potential_sendfile_source() {
+                let result = sendfile_splice(SpliceMode::Sendfile, readfd, writefd, max_write);
+
+                match result {
+                    CopyResult::Ended(Ok(bytes_copied)) => return Ok(bytes_copied + written),
+                    CopyResult::Ended(err) => return err,
+                    CopyResult::Fallback(bytes) => written += bytes,
+                }
+            }
+
+            if input_meta.maybe_fifo() || output_meta.maybe_fifo() {
+                let result = sendfile_splice(SpliceMode::Splice, readfd, writefd, max_write);
+
+                match result {
+                    CopyResult::Ended(Ok(bytes_copied)) => return Ok(bytes_copied + written),
+                    CopyResult::Ended(err) => return err,
+                    CopyResult::Fallback(0) => { /* use the fallback below */ }
+                    CopyResult::Fallback(_) => {
+                        unreachable!("splice should not return > 0 bytes on the fallback path")
+                    }
+                }
+            }
+        }
+
+        // fallback if none of the more specialized syscalls wants to work with these file descriptors
+        match generic_copy(reader, writer) {
+            Ok(bytes) => Ok(bytes + written),
+            err => err,
+        }
+    }
+}
+
+#[rustc_specialization_trait]
+trait CopyRead: Read {
+    /// Implementations that contain buffers (i.e. `BufReader`) must transfer data from their internal
+    /// buffers into `writer` until either the buffers are emptied or `limit` bytes have been
+    /// transferred, whichever occurs sooner.
+    /// If nested buffers are present the outer buffers must be drained first.
+    ///
+    /// This is necessary to directly bypass the wrapper types while preserving the data order
+    /// when operating directly on the underlying file descriptors.
+    fn drain_to<W: Write>(&mut self, _writer: &mut W, _limit: u64) -> Result<u64> {
+        Ok(0)
+    }
+
+    /// The minimum of the limit of all `Take<_>` wrappers, `u64::MAX` otherwise.
+    /// This method does not account for data `BufReader` buffers and would underreport
+    /// the limit of a `Take<BufReader<Take<_>>>` type. Thus its result is only valid
+    /// after draining the buffers via `drain_to`.
+    fn min_limit(&self) -> u64 {
+        u64::MAX
+    }
+
+    /// Extracts the file descriptor and hints/metadata, delegating through wrappers if necessary.
+    fn properties(&self) -> CopyParams;
+}
+
+#[rustc_specialization_trait]
+trait CopyWrite: Write {
+    /// Extracts the file descriptor and hints/metadata, delegating through wrappers if necessary.
+    fn properties(&self) -> CopyParams;
+}
+
+impl<T> CopyRead for &mut T
+where
+    T: CopyRead,
+{
+    fn drain_to<W: Write>(&mut self, writer: &mut W, limit: u64) -> Result<u64> {
+        (**self).drain_to(writer, limit)
+    }
+
+    fn min_limit(&self) -> u64 {
+        (**self).min_limit()
+    }
+
+    fn properties(&self) -> CopyParams {
+        (**self).properties()
+    }
+}
+
+impl<T> CopyWrite for &mut T
+where
+    T: CopyWrite,
+{
+    fn properties(&self) -> CopyParams {
+        (**self).properties()
+    }
+}
+
+impl CopyRead for File {
+    fn properties(&self) -> CopyParams {
+        CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyRead for &File {
+    fn properties(&self) -> CopyParams {
+        CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyWrite for File {
+    fn properties(&self) -> CopyParams {
+        CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyWrite for &File {
+    fn properties(&self) -> CopyParams {
+        CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyRead for TcpStream {
+    fn properties(&self) -> CopyParams {
+        // avoid the stat syscall since we can be fairly sure it's a socket
+        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyRead for &TcpStream {
+    fn properties(&self) -> CopyParams {
+        // avoid the stat syscall since we can be fairly sure it's a socket
+        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyWrite for TcpStream {
+    fn properties(&self) -> CopyParams {
+        // avoid the stat syscall since we can be fairly sure it's a socket
+        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyWrite for &TcpStream {
+    fn properties(&self) -> CopyParams {
+        // avoid the stat syscall since we can be fairly sure it's a socket
+        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyWrite for ChildStdin {
+    fn properties(&self) -> CopyParams {
+        CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyRead for ChildStdout {
+    fn properties(&self) -> CopyParams {
+        CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyRead for ChildStderr {
+    fn properties(&self) -> CopyParams {
+        CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyRead for StdinLock<'_> {
+    fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
+        let buf_reader = self.as_mut_buf();
+        let buf = buf_reader.buffer();
+        let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
+        let bytes_drained = buf.len();
+        writer.write_all(buf)?;
+        buf_reader.consume(bytes_drained);
+
+        Ok(bytes_drained as u64)
+    }
+
+    fn properties(&self) -> CopyParams {
+        CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyWrite for StdoutLock<'_> {
+    fn properties(&self) -> CopyParams {
+        CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+    }
+}
+
+impl CopyWrite for StderrLock<'_> {
+    fn properties(&self) -> CopyParams {
+        CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+    }
+}
+
+impl<T: CopyRead> CopyRead for Take<T> {
+    fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
+        let local_limit = self.limit();
+        let combined_limit = min(outer_limit, local_limit);
+        let bytes_drained = self.get_mut().drain_to(writer, combined_limit)?;
+        // update limit since read() was bypassed
+        self.set_limit(local_limit - bytes_drained);
+
+        Ok(bytes_drained)
+    }
+
+    fn min_limit(&self) -> u64 {
+        min(Take::limit(self), self.get_ref().min_limit())
+    }
+
+    fn properties(&self) -> CopyParams {
+        self.get_ref().properties()
+    }
+}
+
+impl<T: CopyRead> CopyRead for BufReader<T> {
+    fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
+        let buf = self.buffer();
+        let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
+        let bytes = buf.len();
+        writer.write_all(buf)?;
+        self.consume(bytes);
+
+        let remaining = outer_limit - bytes as u64;
+
+        // in case of nested bufreaders we also need to drain the ones closer to the source
+        let inner_bytes = self.get_mut().drain_to(writer, remaining)?;
+
+        Ok(bytes as u64 + inner_bytes)
+    }
+
+    fn min_limit(&self) -> u64 {
+        self.get_ref().min_limit()
+    }
+
+    fn properties(&self) -> CopyParams {
+        self.get_ref().properties()
+    }
+}
+
+impl<T: CopyWrite> CopyWrite for BufWriter<T> {
+    fn properties(&self) -> CopyParams {
+        self.get_ref().properties()
+    }
+}
+
+fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
+    let fd = fd.as_raw_fd();
+    let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
+    match file.metadata() {
+        Ok(meta) => FdMeta::Metadata(meta),
+        Err(_) => FdMeta::NoneObtained,
+    }
+}
+
+pub(super) enum CopyResult {
+    Ended(Result<u64>),
+    Fallback(u64),
+}
+
+/// linux-specific implementation that will attempt to use copy_file_range for copy offloading
+/// as the name says, it only works on regular files
+///
+/// Callers must handle fallback to a generic copy loop.
+/// `Fallback` may indicate non-zero number of bytes already written
+/// if one of the files' cursor +`max_len` would exceed u64::MAX (`EOVERFLOW`).
+pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult {
+    use crate::cmp;
+
+    // Kernel prior to 4.5 don't have copy_file_range
+    // We store the availability in a global to avoid unnecessary syscalls
+    static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
+
+    unsafe fn copy_file_range(
+        fd_in: libc::c_int,
+        off_in: *mut libc::loff_t,
+        fd_out: libc::c_int,
+        off_out: *mut libc::loff_t,
+        len: libc::size_t,
+        flags: libc::c_uint,
+    ) -> libc::c_long {
+        libc::syscall(libc::SYS_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags)
+    }
+
+    let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
+    let mut written = 0u64;
+    while written < max_len {
+        let copy_result = if has_copy_file_range {
+            let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64);
+            // cap to 1GB chunks in case u64::MAX is passed as max_len and the file has a non-zero seek position
+            // this allows us to copy large chunks without hitting EOVERFLOW,
+            // unless someone sets a file offset close to u64::MAX - 1GB, in which case a fallback would be required
+            let bytes_to_copy = cmp::min(bytes_to_copy as usize, 0x4000_0000usize);
+            let copy_result = unsafe {
+                // We actually don't have to adjust the offsets,
+                // because copy_file_range adjusts the file offset automatically
+                cvt(copy_file_range(
+                    reader,
+                    ptr::null_mut(),
+                    writer,
+                    ptr::null_mut(),
+                    bytes_to_copy,
+                    0,
+                ))
+            };
+            if let Err(ref copy_err) = copy_result {
+                match copy_err.raw_os_error() {
+                    Some(libc::ENOSYS | libc::EPERM | libc::EOPNOTSUPP) => {
+                        HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
+                    }
+                    _ => {}
+                }
+            }
+            copy_result
+        } else {
+            Err(Error::from_raw_os_error(libc::ENOSYS))
+        };
+        match copy_result {
+            Ok(0) if written == 0 => {
+                // fallback to work around several kernel bugs where copy_file_range will fail to
+                // copy any bytes and return 0 instead of an error if
+                // - reading virtual files from the proc filesystem which appear to have 0 size
+                //   but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
+                // - copying from an overlay filesystem in docker. reported to occur on fedora 32.
+                return CopyResult::Fallback(0);
+            }
+            Ok(0) => return CopyResult::Ended(Ok(written)), // reached EOF
+            Ok(ret) => written += ret as u64,
+            Err(err) => {
+                return match err.raw_os_error() {
+                    // when file offset + max_length > u64::MAX
+                    Some(libc::EOVERFLOW) => CopyResult::Fallback(written),
+                    Some(
+                        libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP,
+                    ) => {
+                        // Try fallback io::copy if either:
+                        // - Kernel version is < 4.5 (ENOSYS)
+                        // - Files are mounted on different fs (EXDEV)
+                        // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
+                        // - copy_file_range is disallowed, for example by seccomp (EPERM)
+                        // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
+                        assert_eq!(written, 0);
+                        CopyResult::Fallback(0)
+                    }
+                    _ => CopyResult::Ended(Err(err)),
+                };
+            }
+        }
+    }
+    CopyResult::Ended(Ok(written))
+}
+
+#[derive(PartialEq)]
+enum SpliceMode {
+    Sendfile,
+    Splice,
+}
+
+/// performs splice or sendfile between file descriptors
+/// Does _not_ fall back to a generic copy loop.
+fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> CopyResult {
+    static HAS_SENDFILE: AtomicBool = AtomicBool::new(true);
+    static HAS_SPLICE: AtomicBool = AtomicBool::new(true);
+
+    syscall! {
+        fn splice(
+            srcfd: libc::c_int,
+            src_offset: *const i64,
+            dstfd: libc::c_int,
+            dst_offset: *const i64,
+            len: libc::size_t,
+            flags: libc::c_int
+        ) -> libc::ssize_t
+    }
+
+    match mode {
+        SpliceMode::Sendfile if !HAS_SENDFILE.load(Ordering::Relaxed) => {
+            return CopyResult::Fallback(0);
+        }
+        SpliceMode::Splice if !HAS_SPLICE.load(Ordering::Relaxed) => {
+            return CopyResult::Fallback(0);
+        }
+        _ => (),
+    }
+
+    let mut written = 0u64;
+    while written < len {
+        // according to its manpage that's the maximum size sendfile() will copy per invocation
+        let chunk_size = crate::cmp::min(len - written, 0x7ffff000_u64) as usize;
+
+        let result = match mode {
+            SpliceMode::Sendfile => {
+                cvt(unsafe { libc::sendfile(writer, reader, ptr::null_mut(), chunk_size) })
+            }
+            SpliceMode::Splice => cvt(unsafe {
+                splice(reader, ptr::null_mut(), writer, ptr::null_mut(), chunk_size, 0)
+            }),
+        };
+
+        match result {
+            Ok(0) => break, // EOF
+            Ok(ret) => written += ret as u64,
+            Err(err) => {
+                return match err.raw_os_error() {
+                    Some(libc::ENOSYS | libc::EPERM) => {
+                        // syscall not supported (ENOSYS)
+                        // syscall is disallowed, e.g. by seccomp (EPERM)
+                        match mode {
+                            SpliceMode::Sendfile => HAS_SENDFILE.store(false, Ordering::Relaxed),
+                            SpliceMode::Splice => HAS_SPLICE.store(false, Ordering::Relaxed),
+                        }
+                        assert_eq!(written, 0);
+                        CopyResult::Fallback(0)
+                    }
+                    Some(libc::EINVAL) => {
+                        // splice/sendfile do not support this particular file descriptor (EINVAL)
+                        assert_eq!(written, 0);
+                        CopyResult::Fallback(0)
+                    }
+                    Some(os_err) if mode == SpliceMode::Sendfile && os_err == libc::EOVERFLOW => {
+                        CopyResult::Fallback(written)
+                    }
+                    _ => CopyResult::Ended(Err(err)),
+                };
+            }
+        }
+    }
+    CopyResult::Ended(Ok(written))
+}
diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs
new file mode 100644 (file)
index 0000000..21b121c
--- /dev/null
@@ -0,0 +1,213 @@
+use crate::env::temp_dir;
+use crate::fs::OpenOptions;
+use crate::io;
+use crate::io::Result;
+use crate::io::SeekFrom;
+use crate::io::{BufRead, Read, Seek, Write};
+use crate::os::unix::io::AsRawFd;
+
+#[test]
+fn copy_specialization() -> Result<()> {
+    use crate::io::{BufReader, BufWriter};
+
+    let path = crate::env::temp_dir();
+    let source_path = path.join("copy-spec.source");
+    let sink_path = path.join("copy-spec.sink");
+
+    let result: Result<()> = try {
+        let mut source = crate::fs::OpenOptions::new()
+            .read(true)
+            .write(true)
+            .create(true)
+            .truncate(true)
+            .open(&source_path)?;
+        source.write_all(b"abcdefghiklmnopqr")?;
+        source.seek(SeekFrom::Start(8))?;
+        let mut source = BufReader::with_capacity(8, source.take(5));
+        source.fill_buf()?;
+        assert_eq!(source.buffer(), b"iklmn");
+        source.get_mut().set_limit(6);
+        source.get_mut().get_mut().seek(SeekFrom::Start(1))?; // "bcdefg"
+        let mut source = source.take(10); // "iklmnbcdef"
+
+        let mut sink = crate::fs::OpenOptions::new()
+            .read(true)
+            .write(true)
+            .create(true)
+            .truncate(true)
+            .open(&sink_path)?;
+        sink.write_all(b"000000")?;
+        let mut sink = BufWriter::with_capacity(5, sink);
+        sink.write_all(b"wxyz")?;
+        assert_eq!(sink.buffer(), b"wxyz");
+
+        let copied = crate::io::copy(&mut source, &mut sink)?;
+        assert_eq!(copied, 10);
+        assert_eq!(sink.buffer().len(), 0);
+
+        let mut sink = sink.into_inner()?;
+        sink.seek(SeekFrom::Start(0))?;
+        let mut copied = Vec::new();
+        sink.read_to_end(&mut copied)?;
+        assert_eq!(&copied, b"000000wxyziklmnbcdef");
+    };
+
+    let rm1 = crate::fs::remove_file(source_path);
+    let rm2 = crate::fs::remove_file(sink_path);
+
+    result.and(rm1).and(rm2)
+}
+
+#[bench]
+fn bench_file_to_file_copy(b: &mut test::Bencher) {
+    const BYTES: usize = 128 * 1024;
+    let src_path = temp_dir().join("file-copy-bench-src");
+    let mut src = crate::fs::OpenOptions::new()
+        .create(true)
+        .truncate(true)
+        .read(true)
+        .write(true)
+        .open(src_path)
+        .unwrap();
+    src.write(&vec![0u8; BYTES]).unwrap();
+
+    let sink_path = temp_dir().join("file-copy-bench-sink");
+    let mut sink = crate::fs::OpenOptions::new()
+        .create(true)
+        .truncate(true)
+        .write(true)
+        .open(sink_path)
+        .unwrap();
+
+    b.bytes = BYTES as u64;
+    b.iter(|| {
+        src.seek(SeekFrom::Start(0)).unwrap();
+        sink.seek(SeekFrom::Start(0)).unwrap();
+        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
+    });
+}
+
+#[bench]
+fn bench_file_to_socket_copy(b: &mut test::Bencher) {
+    const BYTES: usize = 128 * 1024;
+    let src_path = temp_dir().join("pipe-copy-bench-src");
+    let mut src = OpenOptions::new()
+        .create(true)
+        .truncate(true)
+        .read(true)
+        .write(true)
+        .open(src_path)
+        .unwrap();
+    src.write(&vec![0u8; BYTES]).unwrap();
+
+    let sink_drainer = crate::net::TcpListener::bind("localhost:0").unwrap();
+    let mut sink = crate::net::TcpStream::connect(sink_drainer.local_addr().unwrap()).unwrap();
+    let mut sink_drainer = sink_drainer.accept().unwrap().0;
+
+    crate::thread::spawn(move || {
+        let mut sink_buf = vec![0u8; 1024 * 1024];
+        loop {
+            sink_drainer.read(&mut sink_buf[..]).unwrap();
+        }
+    });
+
+    b.bytes = BYTES as u64;
+    b.iter(|| {
+        src.seek(SeekFrom::Start(0)).unwrap();
+        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
+    });
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[bench]
+fn bench_socket_pipe_socket_copy(b: &mut test::Bencher) {
+    use super::CopyResult;
+    use crate::io::ErrorKind;
+    use crate::process::{ChildStdin, ChildStdout};
+    use crate::sys_common::FromInner;
+
+    let (read_end, write_end) = crate::sys::pipe::anon_pipe().unwrap();
+
+    let mut read_end = ChildStdout::from_inner(read_end);
+    let write_end = ChildStdin::from_inner(write_end);
+
+    let acceptor = crate::net::TcpListener::bind("localhost:0").unwrap();
+    let mut remote_end = crate::net::TcpStream::connect(acceptor.local_addr().unwrap()).unwrap();
+
+    let local_end = crate::sync::Arc::new(acceptor.accept().unwrap().0);
+
+    // the data flow in this benchmark:
+    //
+    //                      socket(tx)  local_source
+    // remote_end (write)  +-------->   (splice to)
+    //                                  write_end
+    //                                     +
+    //                                     |
+    //                                     | pipe
+    //                                     v
+    //                                  read_end
+    // remote_end (read)   <---------+  (splice to) *
+    //                      socket(rx)  local_end
+    //
+    // * benchmark loop using io::copy
+
+    crate::thread::spawn(move || {
+        let mut sink_buf = vec![0u8; 1024 * 1024];
+        remote_end.set_nonblocking(true).unwrap();
+        loop {
+            match remote_end.write(&mut sink_buf[..]) {
+                Err(err) if err.kind() == ErrorKind::WouldBlock => {}
+                Ok(_) => {}
+                err => {
+                    err.expect("write failed");
+                }
+            };
+            match remote_end.read(&mut sink_buf[..]) {
+                Err(err) if err.kind() == ErrorKind::WouldBlock => {}
+                Ok(_) => {}
+                err => {
+                    err.expect("read failed");
+                }
+            };
+        }
+    });
+
+    // check that splice works, otherwise the benchmark would hang
+    let probe = super::sendfile_splice(
+        super::SpliceMode::Splice,
+        local_end.as_raw_fd(),
+        write_end.as_raw_fd(),
+        1,
+    );
+
+    match probe {
+        CopyResult::Ended(Ok(1)) => {
+            // splice works
+        }
+        _ => {
+            eprintln!("splice failed, skipping benchmark");
+            return;
+        }
+    }
+
+    let local_source = local_end.clone();
+    crate::thread::spawn(move || {
+        loop {
+            super::sendfile_splice(
+                super::SpliceMode::Splice,
+                local_source.as_raw_fd(),
+                write_end.as_raw_fd(),
+                u64::MAX,
+            );
+        }
+    });
+
+    const BYTES: usize = 128 * 1024;
+    b.bytes = BYTES as u64;
+    b.iter(|| {
+        assert_eq!(
+            BYTES as u64,
+            io::copy(&mut (&mut read_end).take(BYTES as u64), &mut &*local_end).unwrap()
+        );
+    });
+}
index b28c6d85b7c725b7b6667378248d00aa77f426c9..7609afbdd76aca9a13a10f53ab0e24cce0e15a7d 100644 (file)
@@ -51,6 +51,8 @@
 pub mod fs;
 pub mod futex;
 pub mod io;
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub mod kernel_copy;
 #[cfg(target_os = "l4re")]
 mod l4re;
 pub mod memchr;
index de5a9a615557cf82282b51dfddbd1eb3efd84851..7030fd9b7f23b2053e9a323491a9d268488a3e3e 100644 (file)
@@ -62,14 +62,30 @@ pub fn get_blanket_impls(&self, ty: Ty<'tcx>, param_env_def_id: DefId) -> Vec<It
                             "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}",
                             param_env, trait_ref, ty
                         );
-                        match infcx.evaluate_obligation(&traits::Obligation::new(
-                            cause,
-                            param_env,
-                            trait_ref.without_const().to_predicate(infcx.tcx),
-                        )) {
-                            Ok(eval_result) => eval_result.may_apply(),
-                            Err(traits::OverflowError) => true, // overflow doesn't mean yes *or* no
+                        let predicates = self
+                            .cx
+                            .tcx
+                            .predicates_of(impl_def_id)
+                            .instantiate(self.cx.tcx, impl_substs)
+                            .predicates
+                            .into_iter()
+                            .chain(Some(trait_ref.without_const().to_predicate(infcx.tcx)));
+                        for predicate in predicates {
+                            debug!("testing predicate {:?}", predicate);
+                            let obligation = traits::Obligation::new(
+                                traits::ObligationCause::dummy(),
+                                param_env,
+                                predicate,
+                            );
+                            match infcx.evaluate_obligation(&obligation) {
+                                Ok(eval_result) if eval_result.may_apply() => {}
+                                Err(traits::OverflowError) => {}
+                                _ => {
+                                    return false;
+                                }
+                            }
                         }
+                        true
                     } else {
                         false
                     }
index 02885f519363c1b0b06ec735eb1f0f48eca3a205..c248d57a9ddf4bc4f9dc16de9b85c215ea603111 100644 (file)
@@ -145,6 +145,9 @@ pub struct Options {
     pub render_options: RenderOptions,
     /// Output format rendering (used only for "show-coverage" option for the moment)
     pub output_format: Option<OutputFormat>,
+    /// If this option is set to `true`, rustdoc will only run checks and not generate
+    /// documentation.
+    pub run_check: bool,
 }
 
 impl fmt::Debug for Options {
@@ -185,6 +188,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             .field("runtool", &self.runtool)
             .field("runtool_args", &self.runtool_args)
             .field("enable-per-target-ignores", &self.enable_per_target_ignores)
+            .field("run_check", &self.run_check)
             .finish()
     }
 }
@@ -581,6 +585,7 @@ fn println_condition(condition: Condition) {
         let enable_per_target_ignores = matches.opt_present("enable-per-target-ignores");
         let document_private = matches.opt_present("document-private-items");
         let document_hidden = matches.opt_present("document-hidden-items");
+        let run_check = matches.opt_present("check");
 
         let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
@@ -616,6 +621,7 @@ fn println_condition(condition: Condition) {
             runtool_args,
             enable_per_target_ignores,
             test_builder,
+            run_check,
             render_options: RenderOptions {
                 output,
                 external_html,
index 616b031814fa5fd8766aaae30539fb9481bf1f53..a88efba77b41cc7efb0406d4971166a098c0022e 100644 (file)
@@ -423,6 +423,7 @@ fn opts() -> Vec<RustcOptGroup> {
                 "specified the rustc-like binary to use as the test builder",
             )
         }),
+        unstable("check", |o| o.optflag("", "check", "Run rustdoc checks")),
     ]
 }
 
@@ -515,6 +516,7 @@ fn main_options(options: config::Options) -> MainResult {
     // but we can't crates the Handler ahead of time because it's not Send
     let diag_opts = (options.error_format, options.edition, options.debugging_opts.clone());
     let show_coverage = options.show_coverage;
+    let run_check = options.run_check;
 
     // First, parse the crate and extract all relevant information.
     info!("starting to run rustc");
@@ -540,6 +542,9 @@ fn main_options(options: config::Options) -> MainResult {
         // if we ran coverage, bail early, we don't need to also generate docs at this point
         // (also we didn't load in any of the useful passes)
         return Ok(());
+    } else if run_check {
+        // Since we're in "check" mode, no need to generate anything beyond this point.
+        return Ok(());
     }
 
     info!("going to format");
index c9d4f51cbf3a7e4a78d0d387ae109bfcae01db0e..e0cb5bf1a4ee614481a962bbdfff514011f09b6c 100644 (file)
@@ -352,7 +352,7 @@ fn resolve_macro(
             if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
                 &path,
                 None,
-                &ParentScope::module(resolver.graph_root()),
+                &ParentScope::module(resolver.graph_root(), resolver),
                 false,
                 false,
             ) {
index b175768e1996286d54f2a96b8f9ecffb368561eb..1e2aa056e400888d5f0730bc3a5bd50a62c8fb8d 100644 (file)
 BASEDIR=../coverage-reports-base
 SOURCEDIR=../coverage
 
-ifeq ($(UNAME),Darwin)
-# FIXME(richkadel): It appears that --debug is not available on MacOS even when not running
-# under CI.
-NO_LLVM_ASSERTIONS=1
-endif
-
 # The `llvm-cov show` flag `--debug`, used to generate the `counters` output files, is only enabled
-# if LLVM assertions are enabled. Some CI builds disable debug assertions.
-ifndef NO_LLVM_ASSERTIONS
+# if LLVM assertions are enabled. Requires Rust config `llvm/optimize` and not
+# `llvm/release_debuginfo`. Note that some CI builds disable debug assertions (by setting
+# `NO_LLVM_ASSERTIONS=1`), so it is not OK to fail the test, but `bless`ed test results cannot be
+# generated without debug assertions.
+LLVM_COV_DEBUG := $(shell "$(LLVM_BIN_DIR)"/llvm-cov show --debug 2>&1 | grep -q "Unknown command line argument '--debug'"; echo $$?)
+ifeq ($(LLVM_COV_DEBUG), 1)
 DEBUG_FLAG=--debug
 endif
 
index fb9f5215fe8ee8e41a9c6989b6a1b24efc1eaa72..03ef04776a03b2bc12161aec14ade6ea9da9fb5c 100644 (file)
@@ -9,9 +9,20 @@
 BASEDIR=../coverage-spanview-base
 SOURCEDIR=../coverage
 
-ifeq ($(UNAME),Darwin)
-SED_HAS_ISSUES=1
-endif
+define SPANVIEW_HEADER
+<!DOCTYPE html>
+<!--
+
+Preview this file as rendered HTML from the github source at:
+https://htmlpreview.github.io/?https://github.com/rust-lang/rust/blob/master/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.%s/%s
+
+For revisions in Pull Requests (PR):
+  * Replace "rust-lang" with the github PR author
+  * Replace "master" with the PR branch name
+
+-->
+endef
+export SPANVIEW_HEADER
 
 all: $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs))
 
@@ -33,31 +44,12 @@ endif
                        -Zdump-mir-spanview \
                        -Zdump-mir-dir="$(TMPDIR)"/mir_dump.$@
 
-ifdef SED_HAS_ISSUES
-       # FIXME(richkadel): MacOS's default sed has some significant limitations. Until I've come up
-       # with a better workaround, I'm disabling this test for MacOS.
-       #
-       # For future reference, see if `gsed` is available as an alternative.
-       which gsed || echo "no gsed"
-else
-
        for path in "$(TMPDIR)"/mir_dump.$@/*; do \
-               echo $$path; \
                file="$$(basename "$$path")"; \
-               echo $$file; \
                urlescaped="$$("$(PYTHON)" $(BASEDIR)/escape_url.py $$file)" || exit $$?; \
-               echo $$urlescaped; \
-               sed -i -e '1a\
-<!--\
-\
-Preview this file as rendered HTML from the github source at:\
-https://htmlpreview.github.io/?https://github.com/rust-lang/rust/blob/master/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.$@/'"$$urlescaped"'\
-\
-For revisions in Pull Requests (PR):\
-  * Replace "rust-lang" with the github PR author\
-  * Replace "master" with the PR branch name\
-\
--->' "$$path"; \
+               printf "$$SPANVIEW_HEADER\n" "$@" "$$urlescaped" > "$$path".modified; \
+               tail -n +2 "$$path" >> "$$path".modified; \
+               mv "$$path".modified "$$path"; \
        done && true # for/done ends in non-zero status
 
 ifdef RUSTC_BLESS_TEST
@@ -70,5 +62,3 @@ else
        cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html "$(TMPDIR)"/actual_mir_dump.$@/
        $(DIFF) -r expected_mir_dump.$@/ "$(TMPDIR)"/actual_mir_dump.$@/
 endif
-
-endif
diff --git a/src/test/run-make/incr-prev-body-beyond-eof/Makefile b/src/test/run-make/incr-prev-body-beyond-eof/Makefile
new file mode 100644 (file)
index 0000000..49a7ee5
--- /dev/null
@@ -0,0 +1,19 @@
+include ../../run-make-fulldeps/tools.mk
+
+# FIXME https://github.com/rust-lang/rust/issues/78911
+# ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64)
+
+# Tests that we don't ICE during incremental compilation after modifying a
+# function span such that its previous end line exceeds the number of lines
+# in the new file, but its start line/column and length remain the same.
+
+SRC=$(TMPDIR)/src
+INCR=$(TMPDIR)/incr
+
+all:
+       mkdir $(SRC)
+       mkdir $(INCR)
+       cp a.rs $(SRC)/main.rs
+       $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs
+       cp b.rs $(SRC)/main.rs
+       $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs
diff --git a/src/test/run-make/incr-prev-body-beyond-eof/a.rs b/src/test/run-make/incr-prev-body-beyond-eof/a.rs
new file mode 100644 (file)
index 0000000..ca70fb5
--- /dev/null
@@ -0,0 +1,16 @@
+fn main() {
+    // foo must be used.
+    foo();
+}
+
+// For this test to operate correctly, foo's body must start on exactly the same
+// line and column and have the exact same length in bytes in a.rs and b.rs. In
+// a.rs, the body must end on a line number which does not exist in b.rs.
+// Basically, avoid modifying this file, including adding or removing whitespace!
+fn foo() {
+    assert_eq!(1, 1);
+
+
+
+
+}
diff --git a/src/test/run-make/incr-prev-body-beyond-eof/b.rs b/src/test/run-make/incr-prev-body-beyond-eof/b.rs
new file mode 100644 (file)
index 0000000..a272e44
--- /dev/null
@@ -0,0 +1,12 @@
+fn main() {
+    // foo must be used.
+    foo();
+}
+
+// For this test to operate correctly, foo's body must start on exactly the same
+// line and column and have the exact same length in bytes in a.rs and b.rs. In
+// a.rs, the body must end on a line number which does not exist in b.rs.
+// Basically, avoid modifying this file, including adding or removing whitespace!
+fn foo() {
+    assert_eq!(1, 1);////
+}
index b7bf366c918e25d69742503115f26872ca9f41a6..b0e8451ff5d0174f34628f9de924b8ec6cd22bab 100644 (file)
@@ -1,5 +1,6 @@
 include ../../run-make-fulldeps/tools.mk
 
+# FIXME https://github.com/rust-lang/rust/issues/78911
 # ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64)
 
 all: foo
diff --git a/src/test/rustdoc-ui/check-fail.rs b/src/test/rustdoc-ui/check-fail.rs
new file mode 100644 (file)
index 0000000..291fc11
--- /dev/null
@@ -0,0 +1,21 @@
+// compile-flags: -Z unstable-options --check
+
+#![deny(missing_docs)]
+#![deny(rustdoc)]
+
+//! ```rust,testharness
+//~^ ERROR
+//! let x = 12;
+//! ```
+
+pub fn foo() {}
+//~^ ERROR
+//~^^ ERROR
+
+/// hello
+//~^ ERROR
+///
+/// ```rust,testharness
+/// let x = 12;
+/// ```
+pub fn bar() {}
diff --git a/src/test/rustdoc-ui/check-fail.stderr b/src/test/rustdoc-ui/check-fail.stderr
new file mode 100644 (file)
index 0000000..b4f2556
--- /dev/null
@@ -0,0 +1,57 @@
+error: missing documentation for a function
+  --> $DIR/check-fail.rs:11:1
+   |
+LL | pub fn foo() {}
+   | ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/check-fail.rs:3:9
+   |
+LL | #![deny(missing_docs)]
+   |         ^^^^^^^^^^^^
+
+error: missing code example in this documentation
+  --> $DIR/check-fail.rs:11:1
+   |
+LL | pub fn foo() {}
+   | ^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/check-fail.rs:4:9
+   |
+LL | #![deny(rustdoc)]
+   |         ^^^^^^^
+   = note: `#[deny(missing_doc_code_examples)]` implied by `#[deny(rustdoc)]`
+
+error: unknown attribute `testharness`. Did you mean `test_harness`?
+  --> $DIR/check-fail.rs:6:1
+   |
+LL | / //! ```rust,testharness
+LL | |
+LL | | //! let x = 12;
+LL | | //! ```
+   | |_______^
+   |
+note: the lint level is defined here
+  --> $DIR/check-fail.rs:4:9
+   |
+LL | #![deny(rustdoc)]
+   |         ^^^^^^^
+   = note: `#[deny(invalid_codeblock_attributes)]` implied by `#[deny(rustdoc)]`
+   = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
+
+error: unknown attribute `testharness`. Did you mean `test_harness`?
+  --> $DIR/check-fail.rs:15:1
+   |
+LL | / /// hello
+LL | |
+LL | | ///
+LL | | /// ```rust,testharness
+LL | | /// let x = 12;
+LL | | /// ```
+   | |_______^
+   |
+   = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/rustdoc-ui/check.rs b/src/test/rustdoc-ui/check.rs
new file mode 100644 (file)
index 0000000..022c562
--- /dev/null
@@ -0,0 +1,11 @@
+// check-pass
+// compile-flags: -Z unstable-options --check
+
+#![warn(missing_docs)]
+//~^ WARN
+//~^^ WARN
+#![warn(rustdoc)]
+
+pub fn foo() {}
+//~^ WARN
+//~^^ WARN
diff --git a/src/test/rustdoc-ui/check.stderr b/src/test/rustdoc-ui/check.stderr
new file mode 100644 (file)
index 0000000..27e5a73
--- /dev/null
@@ -0,0 +1,49 @@
+warning: missing documentation for the crate
+  --> $DIR/check.rs:4:1
+   |
+LL | / #![warn(missing_docs)]
+LL | |
+LL | |
+LL | | #![warn(rustdoc)]
+LL | |
+LL | | pub fn foo() {}
+   | |_______________^
+   |
+note: the lint level is defined here
+  --> $DIR/check.rs:4:9
+   |
+LL | #![warn(missing_docs)]
+   |         ^^^^^^^^^^^^
+
+warning: missing documentation for a function
+  --> $DIR/check.rs:9:1
+   |
+LL | pub fn foo() {}
+   | ^^^^^^^^^^^^
+
+warning: missing code example in this documentation
+  --> $DIR/check.rs:4:1
+   |
+LL | / #![warn(missing_docs)]
+LL | |
+LL | |
+LL | | #![warn(rustdoc)]
+LL | |
+LL | | pub fn foo() {}
+   | |_______________^
+   |
+note: the lint level is defined here
+  --> $DIR/check.rs:7:9
+   |
+LL | #![warn(rustdoc)]
+   |         ^^^^^^^
+   = note: `#[warn(missing_doc_code_examples)]` implied by `#[warn(rustdoc)]`
+
+warning: missing code example in this documentation
+  --> $DIR/check.rs:9:1
+   |
+LL | pub fn foo() {}
+   | ^^^^^^^^^^^^^^^
+
+warning: 4 warnings emitted
+
index 5a03e821e8a2ff637b7adbc2117dd987652ba04a..e7a7d1831f73efbef707ef40b05e0671b4d0b30f 100644 (file)
@@ -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<Foo>'
 pub async fn foo() -> Option<Foo> {
@@ -20,6 +21,12 @@ pub async fn baz<T>(a: T) -> T {
     'âš '
 }
 
+// @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<const N: usize> {}
+// @has async_fn/fn.const_generics.html
+// @has - '//pre[@class="rust fn"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)'
+pub async fn const_generics<const N: usize>(_: impl Trait<N>) {}
diff --git a/src/test/rustdoc/check.rs b/src/test/rustdoc/check.rs
new file mode 100644 (file)
index 0000000..1fb4b35
--- /dev/null
@@ -0,0 +1,5 @@
+// compile-flags: -Z unstable-options --check
+
+// @!has check/fn.foo.html
+// @!has check/index.html
+pub fn foo() {}
index 8dcba36600d26da5194630481ad11e3eefdc9ead..9c68e067c6f8ffda550c9faba2dd8d5b04c398d9 100644 (file)
@@ -70,8 +70,7 @@ pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N> {
 }
 
 // @has foo/fn.b_sink.html '//pre[@class="rust fn"]' \
-//      'pub async fn b_sink<const N: usize>(__arg0: impl Trait<N>)'
-// FIXME(const_generics): This should be `_` not `__arg0`.
+//      'pub async fn b_sink<const N: usize>(_: impl Trait<N>)'
 pub async fn b_sink<const N: usize>(_: impl Trait<N>) {}
 
 // @has foo/fn.concrete.html '//pre[@class="rust fn"]' \
diff --git a/src/test/rustdoc/issue-78673.rs b/src/test/rustdoc/issue-78673.rs
new file mode 100644 (file)
index 0000000..d09141c
--- /dev/null
@@ -0,0 +1,24 @@
+#![crate_name = "issue_78673"]
+
+pub trait Something {}
+
+pub trait AnAmazingTrait {}
+
+impl<T: Something> AnAmazingTrait for T {}
+
+// @has 'issue_78673/struct.MyStruct.html'
+// @has  - '//*[@class="impl"]' 'AnAmazingTrait for MyStruct'
+// @!has - '//*[@class="impl"]' 'AnAmazingTrait for T'
+pub struct MyStruct;
+
+impl AnAmazingTrait for MyStruct {}
+
+// generic structs may have _both_ specific and blanket impls that apply
+
+// @has 'issue_78673/struct.AnotherStruct.html'
+// @has - '//*[@class="impl"]' 'AnAmazingTrait for AnotherStruct<()>'
+// @has - '//*[@class="impl"]' 'AnAmazingTrait for T'
+pub struct AnotherStruct<T>(T);
+
+impl<T: Something> Something for AnotherStruct<T> {}
+impl AnAmazingTrait for AnotherStruct<()> {}
index caf55bec53ddde6463c1ccb040907db7e31cc69b..bff92d8607ece1936cc71b5dcd2f1232e101648b 100644 (file)
@@ -155,7 +155,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
             },
             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 (file)
index 0000000..c4351e0
--- /dev/null
@@ -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<const N: u8>;
+struct B<const N: u16>;
+struct C<const N: u32>;
+struct D<const N: u64>;
+struct E<const N: u128>;
+struct F<const N: usize>;
+struct G<const N: i8>;
+struct H<const N: i16>;
+struct I<const N: i32>;
+struct J<const N: i64>;
+struct K<const N: i128>;
+struct L<const N: isize>;
+struct M<const N: char>;
+struct N<const N: bool>;
+
+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::<true>;
+}
index 98bc99d019421f3c6f69344d3df62502678d3999..2aaf2c39875583ca9ec703a4ad04e5d9b103013b 100644 (file)
@@ -1,4 +1,5 @@
 #![feature(min_const_generics)]
+#![feature(never_type)]
 
 struct Foo<const N: [u8; 0]>;
 //~^ ERROR `[u8; 0]` is forbidden
 struct Faz<const N: &'static u8>;
 //~^ ERROR `&'static u8` is forbidden
 
+struct Fiz<const N: !>;
+//~^ ERROR `!` is forbidden
+
+enum Goo<const N: ()> { A, B }
+//~^ ERROR `()` is forbidden
+
+union Boo<const N: ()> { a: () }
+//~^ ERROR `()` is forbidden
+
+
 fn main() {}
index 4772aaf1b3e0c252c2fdec741953387892dc79b5..52ed3c1c6ee8c80341234a9a915fb9997218a947 100644 (file)
@@ -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<const N: [u8; 0]>;
    |                     ^^^^^^^
@@ -8,7 +8,7 @@ LL | struct Foo<const N: [u8; 0]>;
    = 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<const N: ()>;
    |                     ^^
@@ -17,7 +17,7 @@ LL | struct Bar<const N: ()>;
    = 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<const N: No>;
    |                     ^^
@@ -26,7 +26,7 @@ LL | struct Fez<const N: No>;
    = 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<const N: &'static u8>;
    |                     ^^^^^^^^^^^
@@ -34,5 +34,32 @@ LL | struct Faz<const N: &'static u8>;
    = 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<const N: !>;
+   |                     ^
+   |
+   = 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<const N: ()> { 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<const N: ()> { 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/const-generics/promotion.rs b/src/test/ui/const-generics/promotion.rs
new file mode 100644 (file)
index 0000000..ac568bb
--- /dev/null
@@ -0,0 +1,11 @@
+// run-pass
+// tests that promoting expressions containing const parameters is allowed.
+#![feature(min_const_generics)]
+
+fn promotion_test<const N: usize>() -> &'static usize {
+    &(3 + N)
+}
+
+fn main() {
+    assert_eq!(promotion_test::<13>(), &16);
+}
diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs
new file mode 100644 (file)
index 0000000..393dfc1
--- /dev/null
@@ -0,0 +1,17 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+
+struct Struct<S, T> {
+    a: S,
+    b: T,
+}
+
+struct TupleStruct<S, T>(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));
+}
index e0cb9dc9158e247c3c61eb623fddf39458d4d005..249fba7f920bc81153abc4d72338d1540a691fbf 100644 (file)
@@ -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;
 
index c5543fab825eb03cce20cb53a654f1dce64fa0c6..a81324b99e5868c4ad3509244348e9e541e4f004 100644 (file)
@@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 (file)
index 0000000..3dd10af
--- /dev/null
@@ -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 (file)
index 0000000..f636ea3
--- /dev/null
@@ -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 (file)
index 0000000..728687d
--- /dev/null
@@ -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 (file)
index 0000000..b3a96ee
--- /dev/null
@@ -0,0 +1,19 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+struct Struct<S, T> {
+    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 (file)
index 0000000..c22695e
--- /dev/null
@@ -0,0 +1,15 @@
+#![feature(destructuring_assignment)]
+struct Struct<S, T> {
+    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 (file)
index 0000000..4da4698
--- /dev/null
@@ -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 (file)
index 0000000..106a9b1
--- /dev/null
@@ -0,0 +1,34 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+
+struct TupleStruct<S, T>(S, T);
+
+impl<S, T> TupleStruct<S, T> {
+    fn assign(self, first: &mut S, second: &mut T) {
+        // Test usage of `Self` instead of the struct name:
+        Self(*first, *second) = self
+    }
+}
+
+enum Enum<S, T> {
+    SingleVariant(S, T)
+}
+
+type Alias<S> = Enum<S, isize>;
+
+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 (file)
index 0000000..61ae42a
--- /dev/null
@@ -0,0 +1,42 @@
+#![feature(destructuring_assignment)]
+
+struct TupleStruct<S, T>(S, T);
+
+enum Enum<S, T> {
+    SingleVariant(S, T)
+}
+
+type Alias<S> = Enum<S, isize>;
+
+trait Test {
+    fn test() -> TupleStruct<isize, isize> {
+        TupleStruct(0, 0)
+    }
+}
+
+impl Test for Alias<isize> {}
+
+fn test() -> TupleStruct<isize, isize> {
+    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
+    <Alias::<isize> 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 (file)
index 0000000..863eede
--- /dev/null
@@ -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>(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 |     <Alias::<isize> 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 (file)
index 0000000..b41f2f5
--- /dev/null
@@ -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 (file)
index 0000000..442e36c
--- /dev/null
@@ -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 <https://github.com/rust-lang/rust/issues/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/dropck/issue-38868.rs b/src/test/ui/dropck/issue-38868.rs
new file mode 100644 (file)
index 0000000..b0e5c37
--- /dev/null
@@ -0,0 +1,13 @@
+pub struct List<T> {
+    head: T,
+}
+
+impl Drop for List<i32> { //~ ERROR E0366
+    fn drop(&mut self) {
+        panic!()
+    }
+}
+
+fn main() {
+    List { head: 0 };
+}
diff --git a/src/test/ui/dropck/issue-38868.stderr b/src/test/ui/dropck/issue-38868.stderr
new file mode 100644 (file)
index 0000000..10d1e7c
--- /dev/null
@@ -0,0 +1,21 @@
+error[E0366]: `Drop` impls cannot be specialized
+  --> $DIR/issue-38868.rs:5:1
+   |
+LL | / impl Drop for List<i32> {
+LL | |     fn drop(&mut self) {
+LL | |         panic!()
+LL | |     }
+LL | | }
+   | |_^
+   |
+note: use the same sequence of generic type, lifetime and const parameters as the struct definition
+  --> $DIR/issue-38868.rs:1:1
+   |
+LL | / pub struct List<T> {
+LL | |     head: T,
+LL | | }
+   | |_^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0366`.
diff --git a/src/test/ui/dropck/reject-specialized-drops-8142.rs b/src/test/ui/dropck/reject-specialized-drops-8142.rs
new file mode 100644 (file)
index 0000000..02e8665
--- /dev/null
@@ -0,0 +1,79 @@
+// Issue 8142: Test that Drop impls cannot be specialized beyond the
+// predicates attached to the type definition itself.
+#![feature(min_const_generics)]
+
+trait Bound { fn foo(&self) { } }
+struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
+struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
+struct M<'m> { x: &'m i8 }
+struct N<'n> { x: &'n i8 }
+struct O<To> { x: *const To }
+struct P<Tp> { x: *const Tp }
+struct Q<Tq> { x: *const Tq }
+struct R<Tr> { x: *const Tr }
+struct S<Ts:Bound> { x: *const Ts }
+struct T<'t,Ts:'t> { x: &'t Ts }
+struct U;
+struct V<Tva, Tvb> { x: *const Tva, y: *const Tvb }
+struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
+struct X<const Ca: usize>;
+struct Y<const Ca: usize, const Cb: usize>;
+
+enum Enum<T> { Variant(T) }
+struct TupleStruct<T>(T);
+union Union<T: Copy> { f: T }
+
+impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> {                        // REJECT
+    //~^ ERROR `Drop` impl requires `'adds_bnd: 'al`
+    fn drop(&mut self) { } }
+
+impl<'al,'adds_bnd>     Drop for L<'al,'adds_bnd> where 'adds_bnd:'al {    // REJECT
+    //~^ ERROR `Drop` impl requires `'adds_bnd: 'al`
+    fn drop(&mut self) { } }
+
+impl<'ml>               Drop for M<'ml>         { fn drop(&mut self) { } } // ACCEPT
+
+impl                    Drop for N<'static>     { fn drop(&mut self) { } } // REJECT
+//~^ ERROR mismatched types
+//~| expected struct `N<'n>`
+//~|    found struct `N<'static>`
+
+impl<COkNoBound> Drop for O<COkNoBound> { fn drop(&mut self) { } } // ACCEPT
+
+impl              Drop for P<i8>          { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impls cannot be specialized
+
+impl<AddsBnd:Bound> Drop for Q<AddsBnd> { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
+
+impl<'rbnd,AddsRBnd:'rbnd> Drop for R<AddsRBnd> { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impl requires `AddsRBnd: 'rbnd`
+
+impl<Bs:Bound>    Drop for S<Bs>          { fn drop(&mut self) { } } // ACCEPT
+
+impl<'t,Bt:'t>    Drop for T<'t,Bt>       { fn drop(&mut self) { } } // ACCEPT
+
+impl              Drop for U              { fn drop(&mut self) { } } // ACCEPT
+
+impl<One>         Drop for V<One,One>     { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impls cannot be specialized
+
+impl<'lw>         Drop for W<'lw,'lw>     { fn drop(&mut self) { } } // REJECT
+//~^ ERROR cannot infer an appropriate lifetime for lifetime parameter `'lw`
+
+impl              Drop for X<3>           { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impls cannot be specialized
+
+impl<const Ca: usize> Drop for Y<Ca, Ca>     { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impls cannot be specialized
+
+impl<AddsBnd:Bound> Drop for Enum<AddsBnd> { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
+
+impl<AddsBnd:Bound> Drop for TupleStruct<AddsBnd> { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
+
+impl<AddsBnd:Copy + Bound> Drop for Union<AddsBnd> { fn drop(&mut self) { } } // REJECT
+//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
+
+pub fn main() { }
diff --git a/src/test/ui/dropck/reject-specialized-drops-8142.stderr b/src/test/ui/dropck/reject-specialized-drops-8142.stderr
new file mode 100644 (file)
index 0000000..284cf59
--- /dev/null
@@ -0,0 +1,175 @@
+error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not
+  --> $DIR/reject-specialized-drops-8142.rs:26:20
+   |
+LL | impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> {                        // REJECT
+   |                    ^^^
+   |
+note: the implementor must specify the same requirement
+  --> $DIR/reject-specialized-drops-8142.rs:6:1
+   |
+LL | struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not
+  --> $DIR/reject-specialized-drops-8142.rs:30:67
+   |
+LL | impl<'al,'adds_bnd>     Drop for L<'al,'adds_bnd> where 'adds_bnd:'al {    // REJECT
+   |                                                                   ^^^
+   |
+note: the implementor must specify the same requirement
+  --> $DIR/reject-specialized-drops-8142.rs:7:1
+   |
+LL | struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/reject-specialized-drops-8142.rs:36:1
+   |
+LL | impl                    Drop for N<'static>     { fn drop(&mut self) { } } // REJECT
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
+   |
+   = note: expected struct `N<'n>`
+              found struct `N<'static>`
+note: the lifetime `'n` as defined on the struct at 9:10...
+  --> $DIR/reject-specialized-drops-8142.rs:9:10
+   |
+LL | struct N<'n> { x: &'n i8 }
+   |          ^^
+   = note: ...does not necessarily outlive the static lifetime
+
+error[E0366]: `Drop` impls cannot be specialized
+  --> $DIR/reject-specialized-drops-8142.rs:43:1
+   |
+LL | impl              Drop for P<i8>          { fn drop(&mut self) { } } // REJECT
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: use the same sequence of generic type, lifetime and const parameters as the struct definition
+  --> $DIR/reject-specialized-drops-8142.rs:11:1
+   |
+LL | struct P<Tp> { x: *const Tp }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not
+  --> $DIR/reject-specialized-drops-8142.rs:46:14
+   |
+LL | impl<AddsBnd:Bound> Drop for Q<AddsBnd> { fn drop(&mut self) { } } // REJECT
+   |              ^^^^^
+   |
+note: the implementor must specify the same requirement
+  --> $DIR/reject-specialized-drops-8142.rs:12:1
+   |
+LL | struct Q<Tq> { x: *const Tq }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0367]: `Drop` impl requires `AddsRBnd: 'rbnd` but the struct it is implemented for does not
+  --> $DIR/reject-specialized-drops-8142.rs:49:21
+   |
+LL | impl<'rbnd,AddsRBnd:'rbnd> Drop for R<AddsRBnd> { fn drop(&mut self) { } } // REJECT
+   |                     ^^^^^
+   |
+note: the implementor must specify the same requirement
+  --> $DIR/reject-specialized-drops-8142.rs:13:1
+   |
+LL | struct R<Tr> { x: *const Tr }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0366]: `Drop` impls cannot be specialized
+  --> $DIR/reject-specialized-drops-8142.rs:58:1
+   |
+LL | impl<One>         Drop for V<One,One>     { fn drop(&mut self) { } } // REJECT
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: use the same sequence of generic type, lifetime and const parameters as the struct definition
+  --> $DIR/reject-specialized-drops-8142.rs:17:1
+   |
+LL | struct V<Tva, Tvb> { x: *const Tva, y: *const Tvb }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'lw` due to conflicting requirements
+  --> $DIR/reject-specialized-drops-8142.rs:61:1
+   |
+LL | impl<'lw>         Drop for W<'lw,'lw>     { fn drop(&mut self) { } } // REJECT
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: first, the lifetime cannot outlive the lifetime `'l1` as defined on the struct at 18:10...
+  --> $DIR/reject-specialized-drops-8142.rs:18:10
+   |
+LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
+   |          ^^^
+note: ...but the lifetime must also be valid for the lifetime `'l2` as defined on the struct at 18:15...
+  --> $DIR/reject-specialized-drops-8142.rs:18:15
+   |
+LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
+   |               ^^^
+note: ...so that the types are compatible
+  --> $DIR/reject-specialized-drops-8142.rs:61:1
+   |
+LL | impl<'lw>         Drop for W<'lw,'lw>     { fn drop(&mut self) { } } // REJECT
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: expected `W<'l1, 'l2>`
+              found `W<'_, '_>`
+
+error[E0366]: `Drop` impls cannot be specialized
+  --> $DIR/reject-specialized-drops-8142.rs:64:1
+   |
+LL | impl              Drop for X<3>           { fn drop(&mut self) { } } // REJECT
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: use the same sequence of generic type, lifetime and const parameters as the struct definition
+  --> $DIR/reject-specialized-drops-8142.rs:19:1
+   |
+LL | struct X<const Ca: usize>;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0366]: `Drop` impls cannot be specialized
+  --> $DIR/reject-specialized-drops-8142.rs:67:1
+   |
+LL | impl<const Ca: usize> Drop for Y<Ca, Ca>     { fn drop(&mut self) { } } // REJECT
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: use the same sequence of generic type, lifetime and const parameters as the struct definition
+  --> $DIR/reject-specialized-drops-8142.rs:20:1
+   |
+LL | struct Y<const Ca: usize, const Cb: usize>;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the enum it is implemented for does not
+  --> $DIR/reject-specialized-drops-8142.rs:70:14
+   |
+LL | impl<AddsBnd:Bound> Drop for Enum<AddsBnd> { fn drop(&mut self) { } } // REJECT
+   |              ^^^^^
+   |
+note: the implementor must specify the same requirement
+  --> $DIR/reject-specialized-drops-8142.rs:22:1
+   |
+LL | enum Enum<T> { Variant(T) }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not
+  --> $DIR/reject-specialized-drops-8142.rs:73:14
+   |
+LL | impl<AddsBnd:Bound> Drop for TupleStruct<AddsBnd> { fn drop(&mut self) { } } // REJECT
+   |              ^^^^^
+   |
+note: the implementor must specify the same requirement
+  --> $DIR/reject-specialized-drops-8142.rs:23:1
+   |
+LL | struct TupleStruct<T>(T);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the union it is implemented for does not
+  --> $DIR/reject-specialized-drops-8142.rs:76:21
+   |
+LL | impl<AddsBnd:Copy + Bound> Drop for Union<AddsBnd> { fn drop(&mut self) { } } // REJECT
+   |                     ^^^^^
+   |
+note: the implementor must specify the same requirement
+  --> $DIR/reject-specialized-drops-8142.rs:24:1
+   |
+LL | union Union<T: Copy> { f: T }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 13 previous errors
+
+Some errors have detailed explanations: E0308, E0366, E0367, E0495.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/issues/issue-38868.rs b/src/test/ui/issues/issue-38868.rs
deleted file mode 100644 (file)
index b0e5c37..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-pub struct List<T> {
-    head: T,
-}
-
-impl Drop for List<i32> { //~ ERROR E0366
-    fn drop(&mut self) {
-        panic!()
-    }
-}
-
-fn main() {
-    List { head: 0 };
-}
diff --git a/src/test/ui/issues/issue-38868.stderr b/src/test/ui/issues/issue-38868.stderr
deleted file mode 100644 (file)
index 10d1e7c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0366]: `Drop` impls cannot be specialized
-  --> $DIR/issue-38868.rs:5:1
-   |
-LL | / impl Drop for List<i32> {
-LL | |     fn drop(&mut self) {
-LL | |         panic!()
-LL | |     }
-LL | | }
-   | |_^
-   |
-note: use the same sequence of generic type, lifetime and const parameters as the struct definition
-  --> $DIR/issue-38868.rs:1:1
-   |
-LL | / pub struct List<T> {
-LL | |     head: T,
-LL | | }
-   | |_^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0366`.
diff --git a/src/test/ui/issues/issue-76042.rs b/src/test/ui/issues/issue-76042.rs
new file mode 100644 (file)
index 0000000..34d5293
--- /dev/null
@@ -0,0 +1,16 @@
+// run-pass
+// compile-flags: -Coverflow-checks=off -Ccodegen-units=1 -Copt-level=0
+
+fn foo(a: i128, b: i128, s: u32) -> (i128, i128) {
+    if s == 128 {
+        (0, 0)
+    } else {
+        (b >> s, a >> s)
+    }
+}
+fn main() {
+    let r = foo(0, 8, 1);
+    if r.0 != 4 {
+        panic!();
+    }
+}
index bc992c21dca5c98f853d35bfb7e651c6bb24e063..a6a2401795ff4690de26464185566ef459dcd38e 100644 (file)
@@ -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
     }
 }
index eca44725eb258cb479f046a24300025d71a11077..4f6fbaa2265d5ac01ca8de66553da8e8ddca240d 100644 (file)
@@ -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 <https://github.com/rust-lang/rust/issues/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/reject-specialized-drops-8142.rs b/src/test/ui/reject-specialized-drops-8142.rs
deleted file mode 100644 (file)
index c467173..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-// Issue 8142: Test that Drop impls cannot be specialized beyond the
-// predicates attached to the type definition itself.
-
-trait Bound { fn foo(&self) { } }
-struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
-struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
-struct M<'m> { x: &'m i8 }
-struct N<'n> { x: &'n i8 }
-struct O<To> { x: *const To }
-struct P<Tp> { x: *const Tp }
-struct Q<Tq> { x: *const Tq }
-struct R<Tr> { x: *const Tr }
-struct S<Ts:Bound> { x: *const Ts }
-struct T<'t,Ts:'t> { x: &'t Ts }
-struct U;
-struct V<Tva, Tvb> { x: *const Tva, y: *const Tvb }
-struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
-
-enum Enum<T> { Variant(T) }
-struct TupleStruct<T>(T);
-union Union<T: Copy> { f: T }
-
-impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> {                        // REJECT
-    //~^ ERROR `Drop` impl requires `'adds_bnd: 'al`
-    fn drop(&mut self) { } }
-
-impl<'al,'adds_bnd>     Drop for L<'al,'adds_bnd> where 'adds_bnd:'al {    // REJECT
-    //~^ ERROR `Drop` impl requires `'adds_bnd: 'al`
-    fn drop(&mut self) { } }
-
-impl<'ml>               Drop for M<'ml>         { fn drop(&mut self) { } } // ACCEPT
-
-impl                    Drop for N<'static>     { fn drop(&mut self) { } } // REJECT
-//~^ ERROR mismatched types
-//~| expected struct `N<'n>`
-//~|    found struct `N<'static>`
-
-impl<COkNoBound> Drop for O<COkNoBound> { fn drop(&mut self) { } } // ACCEPT
-
-impl              Drop for P<i8>          { fn drop(&mut self) { } } // REJECT
-//~^ ERROR `Drop` impls cannot be specialized
-
-impl<AddsBnd:Bound> Drop for Q<AddsBnd> { fn drop(&mut self) { } } // REJECT
-//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
-
-impl<'rbnd,AddsRBnd:'rbnd> Drop for R<AddsRBnd> { fn drop(&mut self) { } } // REJECT
-//~^ ERROR `Drop` impl requires `AddsRBnd: 'rbnd`
-
-impl<Bs:Bound>    Drop for S<Bs>          { fn drop(&mut self) { } } // ACCEPT
-
-impl<'t,Bt:'t>    Drop for T<'t,Bt>       { fn drop(&mut self) { } } // ACCEPT
-
-impl              Drop for U              { fn drop(&mut self) { } } // ACCEPT
-
-impl<One>         Drop for V<One,One>     { fn drop(&mut self) { } } // REJECT
-//~^ ERROR `Drop` impls cannot be specialized
-
-impl<'lw>         Drop for W<'lw,'lw>     { fn drop(&mut self) { } } // REJECT
-//~^ ERROR cannot infer an appropriate lifetime for lifetime parameter `'lw`
-
-impl<AddsBnd:Bound> Drop for Enum<AddsBnd> { fn drop(&mut self) { } } // REJECT
-//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
-
-impl<AddsBnd:Bound> Drop for TupleStruct<AddsBnd> { fn drop(&mut self) { } } // REJECT
-//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
-
-impl<AddsBnd:Copy + Bound> Drop for Union<AddsBnd> { fn drop(&mut self) { } } // REJECT
-//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
-
-pub fn main() { }
diff --git a/src/test/ui/reject-specialized-drops-8142.stderr b/src/test/ui/reject-specialized-drops-8142.stderr
deleted file mode 100644 (file)
index eac2461..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not
-  --> $DIR/reject-specialized-drops-8142.rs:23:20
-   |
-LL | impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> {                        // REJECT
-   |                    ^^^
-   |
-note: the implementor must specify the same requirement
-  --> $DIR/reject-specialized-drops-8142.rs:5:1
-   |
-LL | struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not
-  --> $DIR/reject-specialized-drops-8142.rs:27:67
-   |
-LL | impl<'al,'adds_bnd>     Drop for L<'al,'adds_bnd> where 'adds_bnd:'al {    // REJECT
-   |                                                                   ^^^
-   |
-note: the implementor must specify the same requirement
-  --> $DIR/reject-specialized-drops-8142.rs:6:1
-   |
-LL | struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0308]: mismatched types
-  --> $DIR/reject-specialized-drops-8142.rs:33:1
-   |
-LL | impl                    Drop for N<'static>     { fn drop(&mut self) { } } // REJECT
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
-   |
-   = note: expected struct `N<'n>`
-              found struct `N<'static>`
-note: the lifetime `'n` as defined on the struct at 8:10...
-  --> $DIR/reject-specialized-drops-8142.rs:8:10
-   |
-LL | struct N<'n> { x: &'n i8 }
-   |          ^^
-   = note: ...does not necessarily outlive the static lifetime
-
-error[E0366]: `Drop` impls cannot be specialized
-  --> $DIR/reject-specialized-drops-8142.rs:40:1
-   |
-LL | impl              Drop for P<i8>          { fn drop(&mut self) { } } // REJECT
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: use the same sequence of generic type, lifetime and const parameters as the struct definition
-  --> $DIR/reject-specialized-drops-8142.rs:10:1
-   |
-LL | struct P<Tp> { x: *const Tp }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not
-  --> $DIR/reject-specialized-drops-8142.rs:43:14
-   |
-LL | impl<AddsBnd:Bound> Drop for Q<AddsBnd> { fn drop(&mut self) { } } // REJECT
-   |              ^^^^^
-   |
-note: the implementor must specify the same requirement
-  --> $DIR/reject-specialized-drops-8142.rs:11:1
-   |
-LL | struct Q<Tq> { x: *const Tq }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0367]: `Drop` impl requires `AddsRBnd: 'rbnd` but the struct it is implemented for does not
-  --> $DIR/reject-specialized-drops-8142.rs:46:21
-   |
-LL | impl<'rbnd,AddsRBnd:'rbnd> Drop for R<AddsRBnd> { fn drop(&mut self) { } } // REJECT
-   |                     ^^^^^
-   |
-note: the implementor must specify the same requirement
-  --> $DIR/reject-specialized-drops-8142.rs:12:1
-   |
-LL | struct R<Tr> { x: *const Tr }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0366]: `Drop` impls cannot be specialized
-  --> $DIR/reject-specialized-drops-8142.rs:55:1
-   |
-LL | impl<One>         Drop for V<One,One>     { fn drop(&mut self) { } } // REJECT
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: use the same sequence of generic type, lifetime and const parameters as the struct definition
-  --> $DIR/reject-specialized-drops-8142.rs:16:1
-   |
-LL | struct V<Tva, Tvb> { x: *const Tva, y: *const Tvb }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'lw` due to conflicting requirements
-  --> $DIR/reject-specialized-drops-8142.rs:58:1
-   |
-LL | impl<'lw>         Drop for W<'lw,'lw>     { fn drop(&mut self) { } } // REJECT
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: first, the lifetime cannot outlive the lifetime `'l1` as defined on the struct at 17:10...
-  --> $DIR/reject-specialized-drops-8142.rs:17:10
-   |
-LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
-   |          ^^^
-note: ...but the lifetime must also be valid for the lifetime `'l2` as defined on the struct at 17:15...
-  --> $DIR/reject-specialized-drops-8142.rs:17:15
-   |
-LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
-   |               ^^^
-note: ...so that the types are compatible
-  --> $DIR/reject-specialized-drops-8142.rs:58:1
-   |
-LL | impl<'lw>         Drop for W<'lw,'lw>     { fn drop(&mut self) { } } // REJECT
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: expected `W<'l1, 'l2>`
-              found `W<'_, '_>`
-
-error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the enum it is implemented for does not
-  --> $DIR/reject-specialized-drops-8142.rs:61:14
-   |
-LL | impl<AddsBnd:Bound> Drop for Enum<AddsBnd> { fn drop(&mut self) { } } // REJECT
-   |              ^^^^^
-   |
-note: the implementor must specify the same requirement
-  --> $DIR/reject-specialized-drops-8142.rs:19:1
-   |
-LL | enum Enum<T> { Variant(T) }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not
-  --> $DIR/reject-specialized-drops-8142.rs:64:14
-   |
-LL | impl<AddsBnd:Bound> Drop for TupleStruct<AddsBnd> { fn drop(&mut self) { } } // REJECT
-   |              ^^^^^
-   |
-note: the implementor must specify the same requirement
-  --> $DIR/reject-specialized-drops-8142.rs:20:1
-   |
-LL | struct TupleStruct<T>(T);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the union it is implemented for does not
-  --> $DIR/reject-specialized-drops-8142.rs:67:21
-   |
-LL | impl<AddsBnd:Copy + Bound> Drop for Union<AddsBnd> { fn drop(&mut self) { } } // REJECT
-   |                     ^^^^^
-   |
-note: the implementor must specify the same requirement
-  --> $DIR/reject-specialized-drops-8142.rs:21:1
-   |
-LL | union Union<T: Copy> { f: T }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 11 previous errors
-
-Some errors have detailed explanations: E0308, E0366, E0367, E0495.
-For more information about an error, try `rustc --explain E0308`.
index 87def13c476c7d5c803f80b34cfed1db0b9cc3ee..688b6e826582610956fc49e3e50f6c56734652a2 100644 (file)
@@ -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
 }
index d8e50cae55ad12c9ee7052ed407f50dd180f4948..ce1ee0cd06d4854788b78cd9ca1a7e61213d00e7 100644 (file)
@@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.
index d5556aeb8405b1fe696adb6e297ad7a1f2989b62..2af662e22177a839763ac8fb70d245a680b15214 160000 (submodule)
@@ -1 +1 @@
-Subproject commit d5556aeb8405b1fe696adb6e297ad7a1f2989b62
+Subproject commit 2af662e22177a839763ac8fb70d245a680b15214
index b68e33f101d2b6d15c5b577ae2d6e46483ade234..9050b9b2d9ab820c04332248b383375048eb67a5 100644 (file)
@@ -107,6 +107,15 @@ pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> 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,
     }
index 8241dcd8feb7ba1d3a5142a900cc7bcb5ac478d6..c38727316cd4e8dff0d860c058609953eb1e42da 100644 (file)
@@ -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 <https://github.com/rust-lang/rust/issues/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`.
index 30e0c303a019737cb0e22db464c774ac66b14e07..df4109151b6870cdb6d170326d1c099746990ea8 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 30e0c303a019737cb0e22db464c774ac66b14e07
+Subproject commit df4109151b6870cdb6d170326d1c099746990ea8
index eb894d53708122a67762de9489881c11aa8ce257..0f29ff6da0c5ff622e739beb8fc3bbe77119b3c1 160000 (submodule)
@@ -1 +1 @@
-Subproject commit eb894d53708122a67762de9489881c11aa8ce257
+Subproject commit 0f29ff6da0c5ff622e739beb8fc3bbe77119b3c1
index 7f3a46a841e5d26c7f6b68ce374a49a7872342d2..5a5c7211dc68eef95f4a70b06921bde691afe888 100644 (file)
@@ -1 +1 @@
-1.49.0
+1.50.0