about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/codegen/intrinsics/carrying_mul_add.rs137
-rw-r--r--tests/run-make/branch-protection-check-IBT/Makefile21
-rw-r--r--tests/run-make/branch-protection-check-IBT/_rmake.rs29
-rw-r--r--tests/run-make/branch-protection-check-IBT/main.rs8
-rw-r--r--tests/run-make/branch-protection-check-IBT/rmake.rs53
-rw-r--r--tests/run-make/libs-through-symlinks/Makefile22
-rw-r--r--tests/run-make/libs-through-symlinks/rmake.rs48
-rw-r--r--tests/ui-fulldeps/pprust-parenthesis-insertion.rs7
-rw-r--r--tests/ui/consts/error-is-freeze.rs14
-rw-r--r--tests/ui/consts/error-is-freeze.stderr14
-rw-r--r--tests/ui/impl-trait/auto-trait-contains-err.rs (renamed from tests/crashes/131050.rs)4
-rw-r--r--tests/ui/impl-trait/auto-trait-contains-err.stderr11
-rw-r--r--tests/ui/structs/manual-default-impl-could-be-derived.rs194
-rw-r--r--tests/ui/structs/manual-default-impl-could-be-derived.stderr144
14 files changed, 630 insertions, 76 deletions
diff --git a/tests/codegen/intrinsics/carrying_mul_add.rs b/tests/codegen/intrinsics/carrying_mul_add.rs
new file mode 100644
index 00000000000..174c4077f09
--- /dev/null
+++ b/tests/codegen/intrinsics/carrying_mul_add.rs
@@ -0,0 +1,137 @@
+//@ revisions: RAW OPT
+//@ compile-flags: -C opt-level=1
+//@[RAW] compile-flags: -C no-prepopulate-passes
+//@[OPT] min-llvm-version: 19
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+#![feature(core_intrinsics_fallbacks)]
+
+// Note that LLVM seems to sometimes permute the order of arguments to mul and add,
+// so these tests don't check the arguments in the optimized revision.
+
+use std::intrinsics::{carrying_mul_add, fallback};
+
+// The fallbacks are emitted even when they're never used, but optimize out.
+
+// RAW: wide_mul_u128
+// OPT-NOT: wide_mul_u128
+
+// CHECK-LABEL: @cma_u8
+#[no_mangle]
+pub unsafe fn cma_u8(a: u8, b: u8, c: u8, d: u8) -> (u8, u8) {
+    // CHECK: [[A:%.+]] = zext i8 %a to i16
+    // CHECK: [[B:%.+]] = zext i8 %b to i16
+    // CHECK: [[C:%.+]] = zext i8 %c to i16
+    // CHECK: [[D:%.+]] = zext i8 %d to i16
+    // CHECK: [[AB:%.+]] = mul nuw i16
+    // RAW-SAME: [[A]], [[B]]
+    // CHECK: [[ABC:%.+]] = add nuw i16
+    // RAW-SAME: [[AB]], [[C]]
+    // CHECK: [[ABCD:%.+]] = add nuw i16
+    // RAW-SAME: [[ABC]], [[D]]
+    // CHECK: [[LOW:%.+]] = trunc i16 [[ABCD]] to i8
+    // CHECK: [[HIGHW:%.+]] = lshr i16 [[ABCD]], 8
+    // RAW: [[HIGH:%.+]] = trunc i16 [[HIGHW]] to i8
+    // OPT: [[HIGH:%.+]] = trunc nuw i16 [[HIGHW]] to i8
+    // CHECK: [[PAIR0:%.+]] = insertvalue { i8, i8 } poison, i8 [[LOW]], 0
+    // CHECK: [[PAIR1:%.+]] = insertvalue { i8, i8 } [[PAIR0]], i8 [[HIGH]], 1
+    // OPT: ret { i8, i8 } [[PAIR1]]
+    carrying_mul_add(a, b, c, d)
+}
+
+// CHECK-LABEL: @cma_u32
+#[no_mangle]
+pub unsafe fn cma_u32(a: u32, b: u32, c: u32, d: u32) -> (u32, u32) {
+    // CHECK: [[A:%.+]] = zext i32 %a to i64
+    // CHECK: [[B:%.+]] = zext i32 %b to i64
+    // CHECK: [[C:%.+]] = zext i32 %c to i64
+    // CHECK: [[D:%.+]] = zext i32 %d to i64
+    // CHECK: [[AB:%.+]] = mul nuw i64
+    // RAW-SAME: [[A]], [[B]]
+    // CHECK: [[ABC:%.+]] = add nuw i64
+    // RAW-SAME: [[AB]], [[C]]
+    // CHECK: [[ABCD:%.+]] = add nuw i64
+    // RAW-SAME: [[ABC]], [[D]]
+    // CHECK: [[LOW:%.+]] = trunc i64 [[ABCD]] to i32
+    // CHECK: [[HIGHW:%.+]] = lshr i64 [[ABCD]], 32
+    // RAW: [[HIGH:%.+]] = trunc i64 [[HIGHW]] to i32
+    // OPT: [[HIGH:%.+]] = trunc nuw i64 [[HIGHW]] to i32
+    // CHECK: [[PAIR0:%.+]] = insertvalue { i32, i32 } poison, i32 [[LOW]], 0
+    // CHECK: [[PAIR1:%.+]] = insertvalue { i32, i32 } [[PAIR0]], i32 [[HIGH]], 1
+    // OPT: ret { i32, i32 } [[PAIR1]]
+    carrying_mul_add(a, b, c, d)
+}
+
+// CHECK-LABEL: @cma_u128
+// CHECK-SAME: sret{{.+}}dereferenceable(32){{.+}}%_0,{{.+}}%a,{{.+}}%b,{{.+}}%c,{{.+}}%d
+#[no_mangle]
+pub unsafe fn cma_u128(a: u128, b: u128, c: u128, d: u128) -> (u128, u128) {
+    // CHECK: [[A:%.+]] = zext i128 %a to i256
+    // CHECK: [[B:%.+]] = zext i128 %b to i256
+    // CHECK: [[C:%.+]] = zext i128 %c to i256
+    // CHECK: [[D:%.+]] = zext i128 %d to i256
+    // CHECK: [[AB:%.+]] = mul nuw i256
+    // RAW-SAME: [[A]], [[B]]
+    // CHECK: [[ABC:%.+]] = add nuw i256
+    // RAW-SAME: [[AB]], [[C]]
+    // CHECK: [[ABCD:%.+]] = add nuw i256
+    // RAW-SAME: [[ABC]], [[D]]
+    // CHECK: [[LOW:%.+]] = trunc i256 [[ABCD]] to i128
+    // CHECK: [[HIGHW:%.+]] = lshr i256 [[ABCD]], 128
+    // RAW: [[HIGH:%.+]] = trunc i256 [[HIGHW]] to i128
+    // OPT: [[HIGH:%.+]] = trunc nuw i256 [[HIGHW]] to i128
+    // RAW: [[PAIR0:%.+]] = insertvalue { i128, i128 } poison, i128 [[LOW]], 0
+    // RAW: [[PAIR1:%.+]] = insertvalue { i128, i128 } [[PAIR0]], i128 [[HIGH]], 1
+    // OPT: store i128 [[LOW]], ptr %_0
+    // OPT: [[P1:%.+]] = getelementptr inbounds i8, ptr %_0, {{i32|i64}} 16
+    // OPT: store i128 [[HIGH]], ptr [[P1]]
+    // CHECK: ret void
+    carrying_mul_add(a, b, c, d)
+}
+
+// CHECK-LABEL: @cma_i128
+// CHECK-SAME: sret{{.+}}dereferenceable(32){{.+}}%_0,{{.+}}%a,{{.+}}%b,{{.+}}%c,{{.+}}%d
+#[no_mangle]
+pub unsafe fn cma_i128(a: i128, b: i128, c: i128, d: i128) -> (u128, i128) {
+    // CHECK: [[A:%.+]] = sext i128 %a to i256
+    // CHECK: [[B:%.+]] = sext i128 %b to i256
+    // CHECK: [[C:%.+]] = sext i128 %c to i256
+    // CHECK: [[D:%.+]] = sext i128 %d to i256
+    // CHECK: [[AB:%.+]] = mul nsw i256
+    // RAW-SAME: [[A]], [[B]]
+    // CHECK: [[ABC:%.+]] = add nsw i256
+    // RAW-SAME: [[AB]], [[C]]
+    // CHECK: [[ABCD:%.+]] = add nsw i256
+    // RAW-SAME: [[ABC]], [[D]]
+    // CHECK: [[LOW:%.+]] = trunc i256 [[ABCD]] to i128
+    // CHECK: [[HIGHW:%.+]] = lshr i256 [[ABCD]], 128
+    // RAW: [[HIGH:%.+]] = trunc i256 [[HIGHW]] to i128
+    // OPT: [[HIGH:%.+]] = trunc nuw i256 [[HIGHW]] to i128
+    // RAW: [[PAIR0:%.+]] = insertvalue { i128, i128 } poison, i128 [[LOW]], 0
+    // RAW: [[PAIR1:%.+]] = insertvalue { i128, i128 } [[PAIR0]], i128 [[HIGH]], 1
+    // OPT: store i128 [[LOW]], ptr %_0
+    // OPT: [[P1:%.+]] = getelementptr inbounds i8, ptr %_0, {{i32|i64}} 16
+    // OPT: store i128 [[HIGH]], ptr [[P1]]
+    // CHECK: ret void
+    carrying_mul_add(a, b, c, d)
+}
+
+// CHECK-LABEL: @fallback_cma_u32
+#[no_mangle]
+pub unsafe fn fallback_cma_u32(a: u32, b: u32, c: u32, d: u32) -> (u32, u32) {
+    // OPT-DAG: [[A:%.+]] = zext i32 %a to i64
+    // OPT-DAG: [[B:%.+]] = zext i32 %b to i64
+    // OPT-DAG: [[AB:%.+]] = mul nuw i64
+    // OPT-DAG: [[C:%.+]] = zext i32 %c to i64
+    // OPT-DAG: [[ABC:%.+]] = add nuw i64{{.+}}[[C]]
+    // OPT-DAG: [[D:%.+]] = zext i32 %d to i64
+    // OPT-DAG: [[ABCD:%.+]] = add nuw i64{{.+}}[[D]]
+    // OPT-DAG: [[LOW:%.+]] = trunc i64 [[ABCD]] to i32
+    // OPT-DAG: [[HIGHW:%.+]] = lshr i64 [[ABCD]], 32
+    // OPT-DAG: [[HIGH:%.+]] = trunc nuw i64 [[HIGHW]] to i32
+    // OPT-DAG: [[PAIR0:%.+]] = insertvalue { i32, i32 } poison, i32 [[LOW]], 0
+    // OPT-DAG: [[PAIR1:%.+]] = insertvalue { i32, i32 } [[PAIR0]], i32 [[HIGH]], 1
+    // OPT-DAG: ret { i32, i32 } [[PAIR1]]
+    fallback::CarryingMulAdd::carrying_mul_add(a, b, c, d)
+}
diff --git a/tests/run-make/branch-protection-check-IBT/Makefile b/tests/run-make/branch-protection-check-IBT/Makefile
deleted file mode 100644
index ee0e034627f..00000000000
--- a/tests/run-make/branch-protection-check-IBT/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-# Check for GNU Property Note
-
-include ../tools.mk
-
-# How to run this
-# python3 x.py test --target x86_64-unknown-linux-gnu  tests/run-make/branch-protection-check-IBT/
-
-# only-x86_64
-
-# ignore-test
-# FIXME(jieyouxu): This test never runs because the `ifeq` check on line 17
-# compares `x86` to `x86_64`, which always evaluates to false.
-# When the test does run, the compilation does not include `.note.gnu.property`.
-# See https://github.com/rust-lang/rust/pull/126720 for more information.
-
-all:
-ifeq ($(filter x86,$(LLVM_COMPONENTS)),x86_64)
-	$(RUSTC) --target x86_64-unknown-linux-gnu -Z cf-protection=branch -L$(TMPDIR) -C link-args='-nostartfiles'  -C save-temps  ./main.rs -o $(TMPDIR)/rsmain
-	 readelf -nW $(TMPDIR)/rsmain | $(CGREP) -e ".note.gnu.property"
-endif
-
diff --git a/tests/run-make/branch-protection-check-IBT/_rmake.rs b/tests/run-make/branch-protection-check-IBT/_rmake.rs
deleted file mode 100644
index 91151408785..00000000000
--- a/tests/run-make/branch-protection-check-IBT/_rmake.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Check for GNU Property Note
-
-// How to run this
-// python3 x.py test --target x86_64-unknown-linux-gnu  tests/run-make/branch-protection-check-IBT/
-
-//@ only-x86_64
-
-//@ ignore-test
-// FIXME(jieyouxu): see the FIXME in the Makefile
-
-use run_make_support::{cwd, env_var, llvm_readobj, rustc};
-
-fn main() {
-    let llvm_components = env_var("LLVM_COMPONENTS");
-    if !format!(" {llvm_components} ").contains(" x86 ") {
-        return;
-    }
-
-    rustc()
-        .input("main.rs")
-        .target("x86_64-unknown-linux-gnu")
-        .arg("-Zcf-protection=branch")
-        .arg(format!("-L{}", cwd().display()))
-        .arg("-Clink-args=-nostartfiles")
-        .arg("-Csave-temps")
-        .run();
-
-    llvm_readobj().arg("-nW").input("main").run().assert_stdout_contains(".note.gnu.property");
-}
diff --git a/tests/run-make/branch-protection-check-IBT/main.rs b/tests/run-make/branch-protection-check-IBT/main.rs
index ad379d6ea43..445b8795134 100644
--- a/tests/run-make/branch-protection-check-IBT/main.rs
+++ b/tests/run-make/branch-protection-check-IBT/main.rs
@@ -1,3 +1,5 @@
-fn main() {
-    println!("hello world");
-}
+#![feature(no_core)]
+#![allow(internal_features)]
+#![no_core]
+#![no_std]
+#![no_main]
diff --git a/tests/run-make/branch-protection-check-IBT/rmake.rs b/tests/run-make/branch-protection-check-IBT/rmake.rs
new file mode 100644
index 00000000000..73109df12ae
--- /dev/null
+++ b/tests/run-make/branch-protection-check-IBT/rmake.rs
@@ -0,0 +1,53 @@
+// ignore-tidy-linelength
+//! A basic smoke test to check for GNU Property Note to see that for `x86_64` targets when [`-Z
+//! cf-protection=branch`][intel-cet-tracking-issue] is requested, that the
+//!
+//! ```text
+//! NT_GNU_PROPERTY_TYPE_0 Properties: x86 feature: IBT
+//! ```
+//!
+//! Intel Indirect Branch Tracking (IBT) property is emitted. This was generated in
+//! <https://github.com/rust-lang/rust/pull/110304> in order to address
+//! <https://github.com/rust-lang/rust/issues/103001>.
+//!
+//! Note that the precompiled std currently is not compiled with `-Z cf-protection=branch`!
+//!
+//! In particular, it is expected that:
+//!
+//! > IBT to only be enabled for the process if `.note.gnu.property` indicates that the executable
+//! > was compiled with IBT support and the linker to only tell that IBT is supported if all input
+//! > object files indicate that they support IBT, which in turn requires the standard library to be
+//! > compiled with IBT enabled.
+//!
+//! Note that Intel IBT (Indirect Branch Tracking) is not to be confused with Arm's BTI (Branch
+//! Target Identification). See below for link to Intel IBT docs.
+//!
+//! ## Related links
+//!
+//! - [Tracking Issue for Intel Control Enforcement Technology (CET)][intel-cet-tracking-issue]
+//! - Zulip question about this test:
+//! <https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp/topic/.E2.9C.94.20Branch.20protection.20and.20.60.2Enote.2Egnu.2Eproperty.60>
+//! - Intel IBT docs:
+//!   <https://edc.intel.com/content/www/us/en/design/ipla/software-development-platforms/client/platforms/alder-lake-desktop/12th-generation-intel-core-processors-datasheet-volume-1-of-2/006/indirect-branch-tracking/>
+//!
+//! [intel-cet-tracking-issue]: https://github.com/rust-lang/rust/issues/93754
+
+//@ needs-llvm-components: x86
+
+// FIXME(#93754): increase the test coverage of this test.
+//@ only-x86_64-unknown-linux-gnu
+//@ ignore-cross-compile
+
+use run_make_support::{bare_rustc, llvm_readobj};
+
+fn main() {
+    // `main.rs` is `#![no_std]` to not pull in the currently not-compiled-with-IBT precompiled std.
+    bare_rustc()
+        .input("main.rs")
+        .target("x86_64-unknown-linux-gnu")
+        .arg("-Zcf-protection=branch")
+        .arg("-Clink-args=-nostartfiles")
+        .run();
+
+    llvm_readobj().arg("-nW").input("main").run().assert_stdout_contains(".note.gnu.property");
+}
diff --git a/tests/run-make/libs-through-symlinks/Makefile b/tests/run-make/libs-through-symlinks/Makefile
deleted file mode 100644
index c6ff566a0e8..00000000000
--- a/tests/run-make/libs-through-symlinks/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-# ignore-windows
-
-# The option -n for the AIX ln command has a different purpose than it does
-# on Linux. On Linux, the -n option is used to treat the destination path as
-# normal file if it is a symbolic link to a directory, which is the default
-# behavior of the AIX ln command.
-ifeq ($(UNAME),AIX)
-LN_FLAGS := -sf
-else
-LN_FLAGS := -nsf
-endif
-
-NAME := $(shell $(RUSTC) --print file-names foo.rs)
-
-all:
-	mkdir -p $(TMPDIR)/outdir
-	$(RUSTC) foo.rs -o $(TMPDIR)/outdir/$(NAME)
-	ln $(LN_FLAGS) outdir/$(NAME) $(TMPDIR)
-	RUSTC_LOG=rustc_metadata::loader $(RUSTC) bar.rs
diff --git a/tests/run-make/libs-through-symlinks/rmake.rs b/tests/run-make/libs-through-symlinks/rmake.rs
new file mode 100644
index 00000000000..4bb3d05abb7
--- /dev/null
+++ b/tests/run-make/libs-through-symlinks/rmake.rs
@@ -0,0 +1,48 @@
+//! Regression test for [rustc doesn't handle relative symlinks to libraries
+//! #13890](https://github.com/rust-lang/rust/issues/13890).
+//!
+//! This smoke test checks that for a given library search path `P`:
+//!
+//! - `rustc` is able to locate a library available via a symlink, where:
+//!     - the symlink is under the directory subtree of `P`,
+//!     - but the actual library is not (it's in a different directory subtree).
+//!
+//! For example:
+//!
+//! ```text
+//! actual_dir/
+//!     libfoo.rlib
+//! symlink_dir/  # $CWD set; rustc -L . bar.rs that depends on foo
+//!     libfoo.rlib --> ../actual_dir/libfoo.rlib
+//! ```
+//!
+//! Previously, if `rustc` was invoked with CWD set to `symlink_dir/`, it would fail to traverse the
+//! symlink to locate `actual_dir/libfoo.rlib`. This was originally fixed in
+//! <https://github.com/rust-lang/rust/pull/13903>.
+
+//@ ignore-cross-compile
+
+use run_make_support::{bare_rustc, cwd, path, rfs, rust_lib_name};
+
+fn main() {
+    let actual_lib_dir = path("actual_lib_dir");
+    let symlink_lib_dir = path("symlink_lib_dir");
+    rfs::create_dir_all(&actual_lib_dir);
+    rfs::create_dir_all(&symlink_lib_dir);
+
+    // NOTE: `bare_rustc` is used because it does not introduce an implicit `-L .` library search
+    // flag.
+    bare_rustc().input("foo.rs").output(actual_lib_dir.join(rust_lib_name("foo"))).run();
+
+    rfs::symlink_file(
+        actual_lib_dir.join(rust_lib_name("foo")),
+        symlink_lib_dir.join(rust_lib_name("foo")),
+    );
+
+    // Make rustc's $CWD be in the directory containing the symlink-to-lib.
+    bare_rustc()
+        .current_dir(&symlink_lib_dir)
+        .library_search_path(".")
+        .input(cwd().join("bar.rs"))
+        .run();
+}
diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
index 94c7964392d..2b41020d307 100644
--- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
+++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
@@ -77,6 +77,9 @@ static EXPRS: &[&str] = &[
     // These mean different things.
     "if let _ = true && false {}",
     "if let _ = (true && false) {}",
+    // Parentheses to call a named field, but not an unnamed field.
+    "(self.fun)()",
+    "self.0()",
     // Conditions end at the first curly brace, so struct expressions need to be
     // parenthesized. Except in a match guard, where conditions end at arrow.
     "if let _ = (Struct {}) {}",
@@ -108,6 +111,10 @@ static EXPRS: &[&str] = &[
     "{ (match 2 {})() - 1 }",
     "{ (match 2 {})[0] - 1 }",
     "{ (loop {}) - 1 }",
+    "match 2 { _ => (loop {}) - 1 }",
+    // No eager statement boundary if followed by `.` or `?`.
+    "{ loop {}.to_string() - 1 }",
+    "match 2 { _ => loop {}.to_string() - 1 }",
     // Angle bracket is eagerly parsed as a path's generic argument list.
     "(2 as T) < U",
     "(2 as T<U>) < V", // FIXME: no parentheses needed.
diff --git a/tests/ui/consts/error-is-freeze.rs b/tests/ui/consts/error-is-freeze.rs
new file mode 100644
index 00000000000..fe27d029e66
--- /dev/null
+++ b/tests/ui/consts/error-is-freeze.rs
@@ -0,0 +1,14 @@
+// Make sure we treat the error type as freeze to suppress useless errors.
+
+struct MyStruct {
+    foo: Option<UndefinedType>,
+    //~^ ERROR cannot find type `UndefinedType` in this scope
+}
+impl MyStruct {
+    pub const EMPTY_REF: &'static Self = &Self::EMPTY;
+    pub const EMPTY: Self = Self {
+        foo: None,
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/consts/error-is-freeze.stderr b/tests/ui/consts/error-is-freeze.stderr
new file mode 100644
index 00000000000..f3bfd1ea5e2
--- /dev/null
+++ b/tests/ui/consts/error-is-freeze.stderr
@@ -0,0 +1,14 @@
+error[E0412]: cannot find type `UndefinedType` in this scope
+  --> $DIR/error-is-freeze.rs:4:17
+   |
+LL |     foo: Option<UndefinedType>,
+   |                 ^^^^^^^^^^^^^ not found in this scope
+   |
+help: you might be missing a type parameter
+   |
+LL | struct MyStruct<UndefinedType> {
+   |                +++++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/crashes/131050.rs b/tests/ui/impl-trait/auto-trait-contains-err.rs
index 3e3a600ef3d..d7f094211d7 100644
--- a/tests/crashes/131050.rs
+++ b/tests/ui/impl-trait/auto-trait-contains-err.rs
@@ -1,9 +1,9 @@
-//@ known-bug: #131050
 //@ compile-flags: --edition=2021
 
 use std::future::Future;
 
 fn invalid_future() -> impl Future {}
+//~^ ERROR `()` is not a future
 
 fn create_complex_future() -> impl Future<Output = impl ReturnsSend> {
     async { &|| async { invalid_future().await } }
@@ -21,3 +21,5 @@ where
     R: Send,
 {
 }
+
+fn main() {}
diff --git a/tests/ui/impl-trait/auto-trait-contains-err.stderr b/tests/ui/impl-trait/auto-trait-contains-err.stderr
new file mode 100644
index 00000000000..4da6b285ae1
--- /dev/null
+++ b/tests/ui/impl-trait/auto-trait-contains-err.stderr
@@ -0,0 +1,11 @@
+error[E0277]: `()` is not a future
+  --> $DIR/auto-trait-contains-err.rs:5:24
+   |
+LL | fn invalid_future() -> impl Future {}
+   |                        ^^^^^^^^^^^ `()` is not a future
+   |
+   = help: the trait `Future` is not implemented for `()`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/structs/manual-default-impl-could-be-derived.rs b/tests/ui/structs/manual-default-impl-could-be-derived.rs
new file mode 100644
index 00000000000..773b7389988
--- /dev/null
+++ b/tests/ui/structs/manual-default-impl-could-be-derived.rs
@@ -0,0 +1,194 @@
+// Warn when we encounter a manual `Default` impl that could be derived.
+// Restricted only to types using `default_field_values`.
+#![feature(default_field_values)]
+#![allow(dead_code)]
+#![deny(default_overrides_default_fields)]
+struct S(i32);
+fn s() -> S { S(1) }
+
+struct A {
+    x: S,
+    y: i32 = 1,
+}
+
+impl Default for A { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        A {
+            y: 0,
+            x: s(),
+        }
+    }
+}
+
+struct B {
+    x: S = S(3),
+    y: i32 = 1,
+}
+
+impl Default for B { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        B {
+            x: s(),
+            y: 0,
+        }
+    }
+}
+
+struct C {
+    x: S,
+    y: i32 = 1,
+    z: i32 = 1,
+}
+
+impl Default for C { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        C {
+            x: s(),
+            y: 0,
+            ..
+        }
+    }
+}
+
+struct D {
+    x: S,
+    y: i32 = 1,
+    z: i32 = 1,
+}
+
+impl Default for D { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        D {
+            y: 0,
+            x: s(),
+            ..
+        }
+    }
+}
+
+struct E {
+    x: S,
+    y: i32 = 1,
+    z: i32 = 1,
+}
+
+impl Default for E { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        E {
+            y: 0,
+            z: 0,
+            x: s(),
+        }
+    }
+}
+
+// Let's ensure that the span for `x` and the span for `y` don't overlap when suggesting their
+// removal in favor of their default field values.
+struct E2 {
+    x: S,
+    y: i32 = 1,
+    z: i32 = 1,
+}
+
+impl Default for E2 { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        E2 {
+            x: s(),
+            y: i(),
+            z: 0,
+        }
+    }
+}
+
+fn i() -> i32 {
+    1
+}
+
+// Account for a `const fn` being the `Default::default()` of a field's type.
+struct F {
+    x: G,
+    y: i32 = 1,
+}
+
+impl Default for F { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        F {
+            x: g_const(),
+            y: 0,
+        }
+    }
+}
+
+struct G;
+
+impl Default for G { // ok
+    fn default() -> Self {
+        g_const()
+    }
+}
+
+const fn g_const() -> G {
+    G
+}
+
+// Account for a `const fn` being used in `Default::default()`, even if the type doesn't use it as
+// its own `Default`. We suggest setting the default field value in that case.
+struct H {
+    x: I,
+    y: i32 = 1,
+}
+
+impl Default for H { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        H {
+            x: i_const(),
+            y: 0,
+        }
+    }
+}
+
+struct I;
+
+const fn i_const() -> I {
+    I
+}
+
+// Account for a `const` and struct literal being the `Default::default()` of a field's type.
+struct M {
+    x: N,
+    y: i32 = 1,
+    z: A,
+}
+
+impl Default for M { // ok, `y` is not specified
+    fn default() -> Self {
+        M {
+            x: N_CONST,
+            z: A {
+                x: S(0),
+                y: 0,
+            },
+            ..
+        }
+    }
+}
+
+struct N;
+
+const N_CONST: N = N;
+
+struct O {
+    x: Option<i32>,
+    y: i32 = 1,
+}
+
+impl Default for O { //~ ERROR default_overrides_default_fields
+    fn default() -> Self {
+        O {
+            x: None,
+            y: 1,
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/structs/manual-default-impl-could-be-derived.stderr b/tests/ui/structs/manual-default-impl-could-be-derived.stderr
new file mode 100644
index 00000000000..e8f607fac7e
--- /dev/null
+++ b/tests/ui/structs/manual-default-impl-could-be-derived.stderr
@@ -0,0 +1,144 @@
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:14:1
+   |
+LL | / impl Default for A {
+LL | |     fn default() -> Self {
+LL | |         A {
+LL | |             y: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+note: the lint level is defined here
+  --> $DIR/manual-default-impl-could-be-derived.rs:5:9
+   |
+LL | #![deny(default_overrides_default_fields)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:28:1
+   |
+LL | / impl Default for B {
+LL | |     fn default() -> Self {
+LL | |         B {
+LL | |             x: s(),
+   | |                --- this field has a default value
+LL | |             y: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as Default>::default()`, derive the `Default`
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:43:1
+   |
+LL | / impl Default for C {
+LL | |     fn default() -> Self {
+LL | |         C {
+LL | |             x: s(),
+LL | |             y: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:59:1
+   |
+LL | / impl Default for D {
+LL | |     fn default() -> Self {
+LL | |         D {
+LL | |             y: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:75:1
+   |
+LL | / impl Default for E {
+LL | |     fn default() -> Self {
+LL | |         E {
+LL | |             y: 0,
+   | |                - this field has a default value
+LL | |             z: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:93:1
+   |
+LL | / impl Default for E2 {
+LL | |     fn default() -> Self {
+LL | |         E2 {
+LL | |             x: s(),
+LL | |             y: i(),
+   | |                --- this field has a default value
+LL | |             z: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:113:1
+   |
+LL | / impl Default for F {
+LL | |     fn default() -> Self {
+LL | |         F {
+LL | |             x: g_const(),
+LL | |             y: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:141:1
+   |
+LL | / impl Default for H {
+LL | |     fn default() -> Self {
+LL | |         H {
+LL | |             x: i_const(),
+LL | |             y: 0,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+
+error: `Default` impl doesn't use the declared default field values
+  --> $DIR/manual-default-impl-could-be-derived.rs:185:1
+   |
+LL | / impl Default for O {
+LL | |     fn default() -> Self {
+LL | |         O {
+LL | |             x: None,
+LL | |             y: 1,
+   | |                - this field has a default value
+...  |
+LL | | }
+   | |_^
+   |
+   = help: use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them diverging over time
+
+error: aborting due to 9 previous errors
+