about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/iter/adapters/enumerate.rs1
-rw-r--r--library/std/src/sys/pal/windows/mod.rs17
-rw-r--r--library/std/src/sys/pal/windows/os.rs2
-rw-r--r--library/std/src/sys/pal/windows/thread_local_key.rs26
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh2
-rwxr-xr-xsrc/ci/docker/scripts/build-fuchsia-toolchain.sh8
-rwxr-xr-xsrc/ci/docker/scripts/fuchsia-test-runner.py2
-rw-r--r--tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_dfl.rs8
-rw-r--r--tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_ign.rs8
-rw-r--r--tests/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs31
-rw-r--r--tests/ui/fn/fn_def_coercion.rs58
-rw-r--r--tests/ui/fn/fn_def_coercion.stderr154
-rw-r--r--tests/ui/fn/fn_def_opaque_coercion.rs69
15 files changed, 365 insertions, 24 deletions
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 9ebb5f95f05..4165fa7f07d 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1139,7 +1139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // are the same function and their parameters have a LUB.
                         match self.commit_if_ok(|_| {
                             self.at(cause, self.param_env).lub(
-                                DefineOpaqueTypes::No,
+                                DefineOpaqueTypes::Yes,
                                 prev_ty,
                                 new_ty,
                             )
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8abf42e2c13..99591b5e144 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -192,6 +192,7 @@ symbols! {
         Duration,
         Encodable,
         Encoder,
+        Enumerate,
         Eq,
         Equal,
         Err,
diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs
index ef46040f0a7..7adbabf69e4 100644
--- a/library/core/src/iter/adapters/enumerate.rs
+++ b/library/core/src/iter/adapters/enumerate.rs
@@ -15,6 +15,7 @@ use crate::ops::Try;
 #[derive(Clone, Debug)]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "Enumerate")]
 pub struct Enumerate<I> {
     iter: I,
     count: usize,
diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs
index b49585599cb..ff41f6e77be 100644
--- a/library/std/src/sys/pal/windows/mod.rs
+++ b/library/std/src/sys/pal/windows/mod.rs
@@ -201,14 +201,21 @@ pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
 // currently reside in the buffer. This function is an abstraction over these
 // functions by making them easier to call.
 //
-// The first callback, `f1`, is yielded a (pointer, len) pair which can be
+// The first callback, `f1`, is passed a (pointer, len) pair which can be
 // passed to a syscall. The `ptr` is valid for `len` items (u16 in this case).
-// The closure is expected to return what the syscall returns which will be
-// interpreted by this function to determine if the syscall needs to be invoked
-// again (with more buffer space).
+// The closure is expected to:
+// - On success, return the actual length of the written data *without* the null terminator.
+//   This can be 0. In this case the last_error must be left unchanged.
+// - On insufficient buffer space,
+//   - either return the required length *with* the null terminator,
+//   - or set the last-error to ERROR_INSUFFICIENT_BUFFER and return `len`.
+// - On other failure, return 0 and set last_error.
+//
+// This is how most but not all syscalls indicate the required buffer space.
+// Other syscalls may need translation to match this protocol.
 //
 // Once the syscall has completed (errors bail out early) the second closure is
-// yielded the data which has been read from the syscall. The return value
+// passed the data which has been read from the syscall. The return value
 // from this closure is then the return value of the function.
 pub fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T>
 where
diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs
index 374c9845ea4..64d8b72aed2 100644
--- a/library/std/src/sys/pal/windows/os.rs
+++ b/library/std/src/sys/pal/windows/os.rs
@@ -326,6 +326,8 @@ fn home_dir_crt() -> Option<PathBuf> {
 
         super::fill_utf16_buf(
             |buf, mut sz| {
+                // GetUserProfileDirectoryW does not quite use the usual protocol for
+                // negotiating the buffer size, so we have to translate.
                 match c::GetUserProfileDirectoryW(
                     ptr::without_provenance_mut(CURRENT_PROCESS_TOKEN),
                     buf,
diff --git a/library/std/src/sys/pal/windows/thread_local_key.rs b/library/std/src/sys/pal/windows/thread_local_key.rs
index 4c00860dae3..e5ba619fc6b 100644
--- a/library/std/src/sys/pal/windows/thread_local_key.rs
+++ b/library/std/src/sys/pal/windows/thread_local_key.rs
@@ -141,9 +141,15 @@ impl StaticKey {
                     panic!("out of TLS indexes");
                 }
 
-                self.key.store(key + 1, Release);
                 register_dtor(self);
 
+                // Release-storing the key needs to be the last thing we do.
+                // This is because in `fn key()`, other threads will do an acquire load of the key,
+                // and if that sees this write then it will entirely bypass the `InitOnce`. We thus
+                // need to establish synchronization through `key`. In particular that acquire load
+                // must happen-after the register_dtor above, to ensure the dtor actually runs!
+                self.key.store(key + 1, Release);
+
                 let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut());
                 debug_assert_eq!(r, c::TRUE);
 
@@ -313,8 +319,22 @@ unsafe fn run_dtors() {
         // Use acquire ordering to observe key initialization.
         let mut cur = DTORS.load(Acquire);
         while !cur.is_null() {
-            let key = (*cur).key.load(Relaxed) - 1;
+            let pre_key = (*cur).key.load(Acquire);
             let dtor = (*cur).dtor.unwrap();
+            cur = (*cur).next.load(Relaxed);
+
+            // In StaticKey::init, we register the dtor before setting `key`.
+            // So if one thread's `run_dtors` races with another thread executing `init` on the same
+            // `StaticKey`, we can encounter a key of 0 here. That means this key was never
+            // initialized in this thread so we can safely skip it.
+            if pre_key == 0 {
+                continue;
+            }
+            // If this is non-zero, then via the `Acquire` load above we synchronized with
+            // everything relevant for this key. (It's not clear that this is needed, since the
+            // release-acquire pair on DTORS also establishes synchronization, but better safe than
+            // sorry.)
+            let key = pre_key - 1;
 
             let ptr = c::TlsGetValue(key);
             if !ptr.is_null() {
@@ -322,8 +342,6 @@ unsafe fn run_dtors() {
                 dtor(ptr as *mut _);
                 any_run = true;
             }
-
-            cur = (*cur).next.load(Relaxed);
         }
 
         if !any_run {
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
index d6de992913b..9cc508fe928 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
@@ -5,7 +5,7 @@
 
 set -euf -o pipefail
 
-INTEGRATION_SHA=56310bca298872ffb5ea02e665956d9b6dc41171
+INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6
 PICK_REFS=()
 
 checkout=fuchsia
diff --git a/src/ci/docker/scripts/build-fuchsia-toolchain.sh b/src/ci/docker/scripts/build-fuchsia-toolchain.sh
index beea2f522fd..7a0d4fcffc1 100755
--- a/src/ci/docker/scripts/build-fuchsia-toolchain.sh
+++ b/src/ci/docker/scripts/build-fuchsia-toolchain.sh
@@ -4,13 +4,13 @@ set -ex
 source shared.sh
 
 FUCHSIA_SDK_URL=https://chrome-infra-packages.appspot.com/dl/fuchsia/sdk/core/linux-amd64
-FUCHSIA_SDK_ID=MrhQwtmP8CpZre-i_PNOREcThbUcrX3bA-45d6WQr-cC
-FUCHSIA_SDK_SHA256=32b850c2d98ff02a59adefa2fcf34e44471385b51cad7ddb03ee3977a590afe7
+FUCHSIA_SDK_ID=version:20.20240412.3.1
+FUCHSIA_SDK_SHA256=cc52f3497487dd813c89d9316e6967efcea89c7759edccf3e40fcf3662e53f19
 FUCHSIA_SDK_USR_DIR=/usr/local/core-linux-amd64-fuchsia-sdk
 CLANG_DOWNLOAD_URL=\
 https://chrome-infra-packages.appspot.com/dl/fuchsia/third_party/clang/linux-amd64
-CLANG_DOWNLOAD_ID=Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C
-CLANG_DOWNLOAD_SHA256=4e973ce5dd59c12959e942a5d9df7a19150118d03924a86894e29edb8b110ebd
+CLANG_DOWNLOAD_ID=git_revision:c777c011a709dffd4fa5e79cad7947b7c3405d02
+CLANG_DOWNLOAD_SHA256=779167422ad73c292f049dcea5569f84577af9292189ed2749518b966a4d0844
 
 install_clang() {
   mkdir -p clang_download
diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py
index 437b51641fc..8ac00a8863f 100755
--- a/src/ci/docker/scripts/fuchsia-test-runner.py
+++ b/src/ci/docker/scripts/fuchsia-test-runner.py
@@ -280,7 +280,7 @@ class TestEnvironment:
         # Look up the product bundle transfer manifest.
         self.log_info("Looking up the product bundle transfer manifest...")
         product_name = "minimal." + self.triple_to_arch(self.target)
-        fuchsia_version = "14.20230811.2.1"
+        fuchsia_version = "20.20240412.3.1"
 
         # FIXME: We should be able to replace this with the machine parsable
         # `ffx --machine json product lookup ...` once F15 is released.
diff --git a/tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_dfl.rs b/tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_dfl.rs
new file mode 100644
index 00000000000..7f95fa7ebbe
--- /dev/null
+++ b/tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_dfl.rs
@@ -0,0 +1,8 @@
+//@ aux-crate: sigpipe_utils=sigpipe-utils.rs
+
+#![feature(unix_sigpipe)]
+
+#[unix_sigpipe = "inherit"]
+fn main() {
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
+}
diff --git a/tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_ign.rs b/tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_ign.rs
new file mode 100644
index 00000000000..d96e8b8ef84
--- /dev/null
+++ b/tests/ui/attributes/unix_sigpipe/auxiliary/assert-inherit-sig_ign.rs
@@ -0,0 +1,8 @@
+//@ aux-crate: sigpipe_utils=sigpipe-utils.rs
+
+#![feature(unix_sigpipe)]
+
+#[unix_sigpipe = "inherit"]
+fn main() {
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
+}
diff --git a/tests/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs b/tests/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs
index db3407a7d55..694fa460e9b 100644
--- a/tests/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs
+++ b/tests/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs
@@ -1,14 +1,29 @@
+//@ ignore-cross-compile because aux-bin does not yet support it
+//@ only-unix because SIGPIPE is a unix thing
+//@ aux-bin: assert-inherit-sig_dfl.rs
+//@ aux-bin: assert-inherit-sig_ign.rs
 //@ run-pass
-//@ aux-build:sigpipe-utils.rs
 
-#![feature(unix_sigpipe)]
+#![feature(rustc_private, unix_sigpipe)]
 
-#[unix_sigpipe = "inherit"]
+extern crate libc;
+
+// By default the Rust runtime resets SIGPIPE to SIG_DFL before exec:ing child
+// processes so opt-out of that with `#[unix_sigpipe = "sig_dfl"]`. See
+// https://github.com/rust-lang/rust/blob/bf4de3a874753bbee3323081c8b0c133444fed2d/library/std/src/sys/pal/unix/process/process_unix.rs#L359-L384
+#[unix_sigpipe = "sig_dfl"]
 fn main() {
-    extern crate sigpipe_utils;
+    // First expect SIG_DFL in a child process with #[unix_sigpipe = "inherit"].
+    assert_inherit_sigpipe_disposition("auxiliary/bin/assert-inherit-sig_dfl");
+
+    // With SIG_IGN we expect #[unix_sigpipe = "inherit"] to also get SIG_IGN.
+    unsafe {
+        libc::signal(libc::SIGPIPE, libc::SIG_IGN);
+    }
+    assert_inherit_sigpipe_disposition("auxiliary/bin/assert-inherit-sig_ign");
+}
 
-    // #[unix_sigpipe = "inherit"] is active, so SIGPIPE shall NOT be ignored,
-    // instead the default handler shall be installed. (We assume that the
-    // process that runs these tests have the default handler.)
-    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
+fn assert_inherit_sigpipe_disposition(aux_bin: &str) {
+    let mut cmd = std::process::Command::new(aux_bin);
+    assert!(cmd.status().unwrap().success());
 }
diff --git a/tests/ui/fn/fn_def_coercion.rs b/tests/ui/fn/fn_def_coercion.rs
new file mode 100644
index 00000000000..313be6f28cd
--- /dev/null
+++ b/tests/ui/fn/fn_def_coercion.rs
@@ -0,0 +1,58 @@
+//! Test that coercing between function items of the same function,
+//! but with different generic args succeeds in typeck, but then fails
+//! in borrowck when the lifetimes can't actually be merged.
+
+fn foo<T>(t: T) -> T {
+    t
+}
+
+fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+    let mut x = foo::<&'a ()>; //~ ERROR: lifetime may not live long enough
+    x = foo::<&'b ()>; //~ ERROR: lifetime may not live long enough
+    x = foo::<&'c ()>;
+    x(a);
+    x(b);
+    x(c);
+}
+
+fn g<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+    let x = foo::<&'c ()>;
+    let _: &'c () = x(a); //~ ERROR lifetime may not live long enough
+}
+
+fn h<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+    let x = foo::<&'a ()>;
+    let _: &'a () = x(c);
+}
+
+fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+    let mut x = foo::<&'c ()>;
+    x = foo::<&'b ()>; //~ ERROR lifetime may not live long enough
+    x = foo::<&'a ()>; //~ ERROR lifetime may not live long enough
+    x(a);
+    x(b);
+    x(c);
+}
+
+fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+    let x = match true {
+        true => foo::<&'b ()>,  //~ ERROR lifetime may not live long enough
+        false => foo::<&'a ()>, //~ ERROR lifetime may not live long enough
+    };
+    x(a);
+    x(b);
+    x(c);
+}
+
+fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+    let x = match true {
+        true => foo::<&'c ()>,
+        false => foo::<&'a ()>, //~ ERROR lifetime may not live long enough
+    };
+
+    x(a);
+    x(b); //~ ERROR lifetime may not live long enough
+    x(c);
+}
+
+fn main() {}
diff --git a/tests/ui/fn/fn_def_coercion.stderr b/tests/ui/fn/fn_def_coercion.stderr
new file mode 100644
index 00000000000..ec4a1bde7fd
--- /dev/null
+++ b/tests/ui/fn/fn_def_coercion.stderr
@@ -0,0 +1,154 @@
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:10:17
+   |
+LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+LL |     let mut x = foo::<&'a ()>;
+   |                 ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a function pointer to `foo`
+   = note: the function `foo` is invariant over the parameter `T`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:11:5
+   |
+LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+LL |     let mut x = foo::<&'a ()>;
+LL |     x = foo::<&'b ()>;
+   |     ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a function pointer to `foo`
+   = note: the function `foo` is invariant over the parameter `T`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+help: `'a` and `'b` must be the same: replace one with the other
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:20:12
+   |
+LL | fn g<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --      -- lifetime `'c` defined here
+   |      |
+   |      lifetime `'a` defined here
+LL |     let x = foo::<&'c ()>;
+LL |     let _: &'c () = x(a);
+   |            ^^^^^^ type annotation requires that `'a` must outlive `'c`
+   |
+   = help: consider adding the following bound: `'a: 'c`
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:30:5
+   |
+LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+LL |     let mut x = foo::<&'c ()>;
+LL |     x = foo::<&'b ()>;
+   |     ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a function pointer to `foo`
+   = note: the function `foo` is invariant over the parameter `T`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:31:5
+   |
+LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+...
+LL |     x = foo::<&'a ()>;
+   |     ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a function pointer to `foo`
+   = note: the function `foo` is invariant over the parameter `T`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+help: `'a` and `'b` must be the same: replace one with the other
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:39:17
+   |
+LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+LL |     let x = match true {
+LL |         true => foo::<&'b ()>,
+   |                 ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a function pointer to `foo`
+   = note: the function `foo` is invariant over the parameter `T`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:40:18
+   |
+LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+...
+LL |         false => foo::<&'a ()>,
+   |                  ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a function pointer to `foo`
+   = note: the function `foo` is invariant over the parameter `T`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+help: `'a` and `'b` must be the same: replace one with the other
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:50:18
+   |
+LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --      -- lifetime `'c` defined here
+   |      |
+   |      lifetime `'a` defined here
+...
+LL |         false => foo::<&'a ()>,
+   |                  ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'c`
+   |
+   = help: consider adding the following bound: `'a: 'c`
+   = note: requirement occurs because of a function pointer to `foo`
+   = note: the function `foo` is invariant over the parameter `T`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: lifetime may not live long enough
+  --> $DIR/fn_def_coercion.rs:54:5
+   |
+LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+...
+LL |     x(b);
+   |     ^^^^ argument requires that `'b` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: add bound `'a: 'c`
+   = help: add bound `'b: 'a`
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/fn/fn_def_opaque_coercion.rs b/tests/ui/fn/fn_def_opaque_coercion.rs
new file mode 100644
index 00000000000..0a8810cf4f8
--- /dev/null
+++ b/tests/ui/fn/fn_def_opaque_coercion.rs
@@ -0,0 +1,69 @@
+//! Test that coercing between function items of the same function,
+//! but with different generic args works.
+
+//@check-pass
+
+#![feature(type_alias_impl_trait)]
+
+fn foo<T>(t: T) -> T {
+    t
+}
+
+type F = impl Sized;
+
+fn f(a: F) {
+    let mut x = foo::<F>;
+    x = foo::<()>;
+    x(a);
+    x(());
+}
+
+type G = impl Sized;
+
+fn g(a: G) {
+    let x = foo::<()>;
+    let _: () = x(a);
+}
+
+type H = impl Sized;
+
+fn h(a: H) {
+    let x = foo::<H>;
+    let _: H = x(());
+}
+
+type I = impl Sized;
+
+fn i(a: I) {
+    let mut x = foo::<()>;
+    x = foo::<I>;
+    x(a);
+    x(());
+}
+
+type J = impl Sized;
+
+fn j(a: J) {
+    let x = match true {
+        true => foo::<J>,
+        false => foo::<()>,
+    };
+    x(a);
+    x(());
+}
+
+fn k() -> impl Sized {
+    fn bind<T, F: FnOnce(T) -> T>(_: T, f: F) -> F {
+        f
+    }
+    let x = match true {
+        true => {
+            let f = foo;
+            bind(k(), f)
+        }
+        false => foo::<()>,
+    };
+    todo!()
+}
+
+fn main() {}