From 5454bca1b4b14706529e496ae6df8069682d24ae Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen@gmail.com>
Date: Fri, 10 Jun 2022 19:54:53 +0100
Subject: [PATCH 01/26] net listen backlog set to negative on Linux.

it will be 4076 (from 5.4) or 128.
---
 library/std/src/os/unix/net/listener.rs | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs
index 8e11d32f13071..3b31c000f252a 100644
--- a/library/std/src/os/unix/net/listener.rs
+++ b/library/std/src/os/unix/net/listener.rs
@@ -73,9 +73,13 @@ impl UnixListener {
         unsafe {
             let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
             let (addr, len) = sockaddr_un(path.as_ref())?;
+            #[cfg(target_os = "linux")]
+            const backlog: libc::c_int = -1;
+            #[cfg(not(target_os = "linux"))]
+            const backlog: libc::c_int = 128;
 
             cvt(libc::bind(inner.as_inner().as_raw_fd(), &addr as *const _ as *const _, len as _))?;
-            cvt(libc::listen(inner.as_inner().as_raw_fd(), 128))?;
+            cvt(libc::listen(inner.as_inner().as_raw_fd(), backlog))?;
 
             Ok(UnixListener(inner))
         }
@@ -109,12 +113,16 @@ impl UnixListener {
     pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
         unsafe {
             let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+            #[cfg(target_os = "linux")]
+            const backlog: libc::c_int = -1;
+            #[cfg(not(target_os = "linux"))]
+            const backlog: libc::c_int = 128;
             cvt(libc::bind(
                 inner.as_raw_fd(),
                 &socket_addr.addr as *const _ as *const _,
                 socket_addr.len as _,
             ))?;
-            cvt(libc::listen(inner.as_raw_fd(), 128))?;
+            cvt(libc::listen(inner.as_raw_fd(), backlog))?;
             Ok(UnixListener(inner))
         }
     }

From 7913b8543e90602e98092f23be31259841671ff1 Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Thu, 16 Jun 2022 09:37:01 -0400
Subject: [PATCH 02/26] unit tests that inspect LLVM output directly. This
 relies on a human being to confirm that the entries actually correspond to
 what is specified in each of the respective ABIs...

updated to incorporate feedback: fix x86_64/i686 tests to use correct name for the corresponding llvm component.
---
 .../some-abis-do-extend-params-to-32-bits.rs  | 204 ++++++++++++++++++
 1 file changed, 204 insertions(+)
 create mode 100644 src/test/codegen/some-abis-do-extend-params-to-32-bits.rs

diff --git a/src/test/codegen/some-abis-do-extend-params-to-32-bits.rs b/src/test/codegen/some-abis-do-extend-params-to-32-bits.rs
new file mode 100644
index 0000000000000..7fc34af3da72a
--- /dev/null
+++ b/src/test/codegen/some-abis-do-extend-params-to-32-bits.rs
@@ -0,0 +1,204 @@
+// compile-flags: -Cno-prepopulate-passes
+
+// revisions:x86_64 i686 aarch64-apple aarch64-windows aarch64-linux arm riscv
+
+//[x86_64] compile-flags: --target x86_64-unknown-uefi
+//[x86_64] needs-llvm-components: x86
+//[i686] compile-flags: --target i686-unknown-linux-musl
+//[i686] needs-llvm-components: x86
+//[aarch64-windows] compile-flags: --target aarch64-pc-windows-msvc
+//[aarch64-windows] needs-llvm-components: aarch64
+//[aarch64-linux] compile-flags: --target aarch64-unknown-linux-gnu
+//[aarch64-linux] needs-llvm-components: aarch64
+//[aarch64-apple] compile-flags: --target aarch64-apple-darwin
+//[aarch64-apple] needs-llvm-components: aarch64
+//[arm] compile-flags: --target armv7r-none-eabi
+//[arm] needs-llvm-components: arm
+//[riscv] compile-flags: --target riscv64gc-unknown-none-elf
+//[riscv] needs-llvm-components: riscv
+
+// See bottom of file for a corresponding C source file that is meant to yield
+// equivalent declarations.
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_std]
+#![no_core]
+
+#[lang="sized"] trait Sized { }
+#[lang="freeze"] trait Freeze { }
+#[lang="copy"] trait Copy { }
+
+// The patterns in this file are written in the style of a table to make the
+// uniformities and distinctions more apparent.
+//
+//                  ZERO/SIGN-EXTENDING TO 32 BITS            NON-EXTENDING
+//                  ==============================  =======================
+// x86_64:          void @c_arg_u8(i8 zeroext %_a)
+// i686:            void @c_arg_u8(i8 zeroext %_a)
+// aarch64-apple:   void @c_arg_u8(i8 zeroext %_a)
+// aarch64-windows:                                  void @c_arg_u8(i8 %_a)
+// aarch64-linux:                                    void @c_arg_u8(i8 %_a)
+// arm:             void @c_arg_u8(i8 zeroext %_a)
+// riscv:           void @c_arg_u8(i8 zeroext %_a)
+#[no_mangle] pub extern "C" fn c_arg_u8(_a: u8) { }
+
+// x86_64:          void @c_arg_u16(i16 zeroext %_a)
+// i686:            void @c_arg_u16(i16 zeroext %_a)
+// aarch64-apple:   void @c_arg_u16(i16 zeroext %_a)
+// aarch64-windows:                                 void @c_arg_u16(i16 %_a)
+// aarch64-linux:                                   void @c_arg_u16(i16 %_a)
+// arm:             void @c_arg_u16(i16 zeroext %_a)
+// riscv:           void @c_arg_u16(i16 zeroext %_a)
+#[no_mangle] pub extern "C" fn c_arg_u16(_a: u16) { }
+
+// x86_64:          void @c_arg_u32(i32 %_a)
+// i686:            void @c_arg_u32(i32 %_a)
+// aarch64-apple:   void @c_arg_u32(i32 %_a)
+// aarch64-windows:                                 void @c_arg_u32(i32 %_a)
+// aarch64-linux:                                   void @c_arg_u32(i32 %_a)
+// arm:             void @c_arg_u32(i32 %_a)
+// riscv:           void @c_arg_u32(i32 signext %_a)
+#[no_mangle] pub extern "C" fn c_arg_u32(_a: u32) { }
+
+// x86_64:          void @c_arg_u64(i64 %_a)
+// i686:            void @c_arg_u64(i64 %_a)
+// aarch64-apple:   void @c_arg_u64(i64 %_a)
+// aarch64-windows:                                 void @c_arg_u64(i64 %_a)
+// aarch64-linux:                                   void @c_arg_u64(i64 %_a)
+// arm:             void @c_arg_u64(i64 %_a)
+// riscv:           void @c_arg_u64(i64 %_a)
+#[no_mangle] pub extern "C" fn c_arg_u64(_a: u64) { }
+
+// x86_64:          void @c_arg_i8(i8 signext %_a)
+// i686:            void @c_arg_i8(i8 signext %_a)
+// aarch64-apple:   void @c_arg_i8(i8 signext %_a)
+// aarch64-windows:                                  void @c_arg_i8(i8 %_a)
+// aarch64-linux:                                    void @c_arg_i8(i8 %_a)
+// arm:             void @c_arg_i8(i8 signext %_a)
+// riscv:           void @c_arg_i8(i8 signext %_a)
+#[no_mangle] pub extern "C" fn c_arg_i8(_a: i8) { }
+
+// x86_64:          void @c_arg_i16(i16 signext %_a)
+// i686:            void @c_arg_i16(i16 signext %_a)
+// aarch64-apple:   void @c_arg_i16(i16 signext %_a)
+// aarch64-windows:                                 void @c_arg_i16(i16 %_a)
+// aarch64-linux:                                   void @c_arg_i16(i16 %_a)
+// arm:             void @c_arg_i16(i16 signext %_a)
+// riscv:           void @c_arg_i16(i16 signext %_a)
+#[no_mangle] pub extern "C" fn c_arg_i16(_a: i16) { }
+
+// x86_64:          void @c_arg_i32(i32 %_a)
+// i686:            void @c_arg_i32(i32 %_a)
+// aarch64-apple:   void @c_arg_i32(i32 %_a)
+// aarch64-windows:                                 void @c_arg_i32(i32 %_a)
+// aarch64-linux:                                   void @c_arg_i32(i32 %_a)
+// arm:             void @c_arg_i32(i32 %_a)
+// riscv:           void @c_arg_i32(i32 signext %_a)
+#[no_mangle] pub extern "C" fn c_arg_i32(_a: i32) { }
+
+// x86_64:          void @c_arg_i64(i64 %_a)
+// i686:            void @c_arg_i64(i64 %_a)
+// aarch64-apple:   void @c_arg_i64(i64 %_a)
+// aarch64-windows:                                 void @c_arg_i64(i64 %_a)
+// aarch64-linux:                                   void @c_arg_i64(i64 %_a)
+// arm:             void @c_arg_i64(i64 %_a)
+// riscv:           void @c_arg_i64(i64 %_a)
+#[no_mangle] pub extern "C" fn c_arg_i64(_a: i64) { }
+
+// x86_64:          zeroext i8 @c_ret_u8()
+// i686:            zeroext i8 @c_ret_u8()
+// aarch64-apple:   zeroext i8 @c_ret_u8()
+// aarch64-windows:                                 i8 @c_ret_u8()
+// aarch64-linux:                                   i8 @c_ret_u8()
+// arm:             zeroext i8 @c_ret_u8()
+// riscv:           zeroext i8 @c_ret_u8()
+#[no_mangle] pub extern "C" fn c_ret_u8() -> u8 { 0 }
+
+// x86_64:          zeroext i16 @c_ret_u16()
+// i686:            zeroext i16 @c_ret_u16()
+// aarch64-apple:   zeroext i16 @c_ret_u16()
+// aarch64-windows:                                 i16 @c_ret_u16()
+// aarch64-linux:                                   i16 @c_ret_u16()
+// arm:             zeroext i16 @c_ret_u16()
+// riscv:           zeroext i16 @c_ret_u16()
+#[no_mangle] pub extern "C" fn c_ret_u16() -> u16 { 0 }
+
+// x86_64:          i32 @c_ret_u32()
+// i686:            i32 @c_ret_u32()
+// aarch64-apple:   i32 @c_ret_u32()
+// aarch64-windows:                                 i32 @c_ret_u32()
+// aarch64-linux:                                   i32 @c_ret_u32()
+// arm:             i32 @c_ret_u32()
+// riscv:           signext i32 @c_ret_u32()
+#[no_mangle] pub extern "C" fn c_ret_u32() -> u32 { 0 }
+
+// x86_64:          i64 @c_ret_u64()
+// i686:            i64 @c_ret_u64()
+// aarch64-apple:   i64 @c_ret_u64()
+// aarch64-windows:                                 i64 @c_ret_u64()
+// aarch64-linux:                                   i64 @c_ret_u64()
+// arm:             i64 @c_ret_u64()
+// riscv:           i64 @c_ret_u64()
+#[no_mangle] pub extern "C" fn c_ret_u64() -> u64 { 0 }
+
+// x86_64:          signext i8 @c_ret_i8()
+// i686:            signext i8 @c_ret_i8()
+// aarch64-apple:   signext i8 @c_ret_i8()
+// aarch64-windows:                                 i8 @c_ret_i8()
+// aarch64-linux:                                   i8 @c_ret_i8()
+// arm:             signext i8 @c_ret_i8()
+// riscv:           signext i8 @c_ret_i8()
+#[no_mangle] pub extern "C" fn c_ret_i8() -> i8 { 0 }
+
+// x86_64:          signext i16 @c_ret_i16()
+// i686:            signext i16 @c_ret_i16()
+// aarch64-apple:   signext i16 @c_ret_i16()
+// aarch64-windows:                                 i16 @c_ret_i16()
+// aarch64-linux:                                   i16 @c_ret_i16()
+// arm:             signext i16 @c_ret_i16()
+// riscv:           signext i16 @c_ret_i16()
+#[no_mangle] pub extern "C" fn c_ret_i16() -> i16 { 0 }
+
+// x86_64:          i32 @c_ret_i32()
+// i686:            i32 @c_ret_i32()
+// aarch64-apple:   i32 @c_ret_i32()
+// aarch64-windows:                                 i32 @c_ret_i32()
+// aarch64-linux:                                   i32 @c_ret_i32()
+// arm:             i32 @c_ret_i32()
+// riscv:           signext i32 @c_ret_i32()
+#[no_mangle] pub extern "C" fn c_ret_i32() -> i32 { 0 }
+
+// x86_64:          i64 @c_ret_i64()
+// i686:            i64 @c_ret_i64()
+// aarch64-apple:   i64 @c_ret_i64()
+// aarch64-windows:                                 i64 @c_ret_i64()
+// aarch64-linux:                                   i64 @c_ret_i64()
+// arm:             i64 @c_ret_i64()
+// riscv:           i64 @c_ret_i64()
+#[no_mangle] pub extern "C" fn c_ret_i64() -> i64 { 0 }
+
+const C_SOURCE_FILE: &'static str = r##"
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+
+void c_arg_u8(uint8_t _a) { }
+void c_arg_u16(uint16_t _a) { }
+void c_arg_u32(uint32_t _a) { }
+void c_arg_u64(uint64_t _a) { }
+
+void c_arg_i8(int8_t _a) { }
+void c_arg_i16(int16_t _a) { }
+void c_arg_i32(int32_t _a) { }
+void c_arg_i64(int64_t _a) { }
+
+uint8_t  c_ret_u8()  { return 0; }
+uint16_t c_ret_u16() { return 0; }
+uint32_t c_ret_u32() { return 0; }
+uint64_t c_ret_u64() { return 0; }
+
+int8_t   c_ret_i8()  { return 0; }
+int16_t  c_ret_i16() { return 0; }
+int32_t  c_ret_i32() { return 0; }
+int64_t  c_ret_i64() { return 0; }
+"##;

From b2777aba757dd92042932d79309132bd953e130d Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Mon, 6 Jun 2022 12:39:58 -0400
Subject: [PATCH 03/26] End-to-end regression test for 97463.

incorporated review feedback, with comment explaining why this is calling CC
instead of COMPILE_OBJ or NATIVE_STATICLIB. As drive-by, removed some other
unnecessary commands from the recipe.
---
 .../issue-97463-abi-param-passing/Makefile    | 12 ++++++
 .../issue-97463-abi-param-passing/bad.c       | 24 ++++++++++++
 .../param_passing.rs                          | 38 +++++++++++++++++++
 3 files changed, 74 insertions(+)
 create mode 100644 src/test/run-make-fulldeps/issue-97463-abi-param-passing/Makefile
 create mode 100644 src/test/run-make-fulldeps/issue-97463-abi-param-passing/bad.c
 create mode 100644 src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs

diff --git a/src/test/run-make-fulldeps/issue-97463-abi-param-passing/Makefile b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/Makefile
new file mode 100644
index 0000000000000..b3db6bcb3f636
--- /dev/null
+++ b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/Makefile
@@ -0,0 +1,12 @@
+-include ../tools.mk
+
+# The issue exercised by this test, rust-lang/rust#97463, explicitly needs `-O`
+# flags (like `-O3`) to reproduce. Thus, we call $(CC) instead of nicer
+# alternatives provided by tools.mk like using `COMPILE_OBJ` or using a
+# `NATIVE_STATICLIB` dependency.
+
+all:
+	$(CC) -c -O3 -o $(TMPDIR)/bad.o bad.c
+	$(AR) rcs $(TMPDIR)/libbad.a $(TMPDIR)/bad.o
+	$(RUSTC) param_passing.rs -L$(TMPDIR) -lbad -C opt-level=3
+	$(call RUN,param_passing)
diff --git a/src/test/run-make-fulldeps/issue-97463-abi-param-passing/bad.c b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/bad.c
new file mode 100644
index 0000000000000..013314ab20dc4
--- /dev/null
+++ b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/bad.c
@@ -0,0 +1,24 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+
+
+struct bloc {
+    uint16_t a;
+    uint16_t b;
+    uint16_t c;
+};
+
+uint16_t c_read_value(uint32_t a, uint32_t b, uint32_t c) {
+    struct bloc *data = malloc(sizeof(struct bloc));
+
+    data->a = a & 0xFFFF;
+    data->b = b & 0xFFFF;
+    data->c = c & 0xFFFF;
+
+    printf("C struct: a = %u, b = %u, c = %u\n",
+        (unsigned) data->a, (unsigned) data->b, (unsigned) data->c);
+    printf("C function returns %u\n", (unsigned) data->b);
+
+    return data->b; /* leak data */
+}
diff --git a/src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs
new file mode 100644
index 0000000000000..b4744a3b96e77
--- /dev/null
+++ b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs
@@ -0,0 +1,38 @@
+// NOTE: Exposing the bug encoded in this test is sensitive to
+// LLVM optimization choices. See additional note below for an
+// example.
+
+#[link(name = "bad")]
+extern "C" {
+    pub fn c_read_value(a: u32, b: u32, c: u32) -> u16;
+}
+
+fn main() {
+    const C1: usize = 0x327b23c6;
+    const C2: usize = C1 & 0xFFFF;
+
+    let r1: usize = 0x0;
+    let r2: usize = C1;
+    let r3: usize = 0x0;
+    let value: u16 = unsafe { c_read_value(r1 as u32, r2 as u32, r3 as u32) };
+
+    // NOTE: as an example of the sensitivity of this test to optimization choices,
+    // uncommenting this block of code makes the bug go away on pnkfeix's machine.
+    // (But observing via `dbg!` doesn't hide the bug. At least sometimes.)
+    /*
+    println!("{}", value);
+    println!("{}", value as usize);
+    println!("{}", usize::from(value));
+    println!("{}", (value as usize) & 0xFFFF);
+     */
+
+    let d1 = value;
+    let d2 = value as usize;
+    let d3 = usize::from(value);
+    let d4 = (value as usize) & 0xFFFF;
+
+    let d = (&d1, &d2, &d3, &d4);
+    let d_ = (d1, d2, d3, d4);
+
+    assert_eq!(((&(C2 as u16), &C2, &C2, &C2), (C2 as u16, C2, C2, C2)), (d, d_));
+}

From 8ae5a55ba55a434995d2e2e87f8ef15237ff8124 Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Mon, 6 Jun 2022 12:40:10 -0400
Subject: [PATCH 04/26] fix issue 97463 using change suggested by nbdd0121.

parameterized on target details to decide value-extension policy on calls, in order to address how Apple's aarch64 ABI differs from that on Linux and Windows.

Updated to incorporate review feedback: adjust comment on new enum specifying
param extension policy.

Updated to incorporate review feedback: shorten enum names and those of its
variants to make it less unwieldy.

placate tidy.
---
 compiler/rustc_target/src/abi/call/aarch64.rs | 41 +++++++++++++++----
 compiler/rustc_target/src/abi/call/mod.rs     |  9 +++-
 compiler/rustc_target/src/spec/mod.rs         |  2 +
 3 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs
index 4613a459c51d6..b307cc3e0beb1 100644
--- a/compiler/rustc_target/src/abi/call/aarch64.rs
+++ b/compiler/rustc_target/src/abi/call/aarch64.rs
@@ -1,6 +1,27 @@
 use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
 use crate::abi::{HasDataLayout, TyAbiInterface};
 
+/// Given integer-types M and register width N (e.g. M=u16 and N=32 bits), the
+/// `ParamExtension` policy specifies how a uM value should be treated when
+/// passed via register or stack-slot of width N. See also rust-lang/rust#97463.
+#[derive(Copy, Clone, PartialEq)]
+pub enum ParamExtension {
+    /// Indicates that when passing an i8/i16, either as a function argument or
+    /// as a return value, it must be sign-extended to 32 bits, and likewise a
+    /// u8/u16 must be zero-extended to 32-bits. (This variant is here to
+    /// accommodate Apple's deviation from the usual AArch64 ABI as defined by
+    /// ARM.)
+    ///
+    /// See also: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly
+    ExtendTo32Bits,
+
+    /// Indicates that no sign- nor zero-extension is performed: if a value of
+    /// type with bitwidth M is passed as function argument or return value,
+    /// then M bits are copied into the least significant M bits, and the
+    /// remaining bits of the register (or word of memory) are untouched.
+    NoExtension,
+}
+
 fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
 where
     Ty: TyAbiInterface<'a, C> + Copy,
@@ -24,13 +45,16 @@ where
     })
 }
 
-fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
+fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
 where
     Ty: TyAbiInterface<'a, C> + Copy,
     C: HasDataLayout,
 {
     if !ret.layout.is_aggregate() {
-        ret.extend_integer_width_to(32);
+        match param_policy {
+            ParamExtension::ExtendTo32Bits => ret.extend_integer_width_to(32),
+            ParamExtension::NoExtension => {}
+        }
         return;
     }
     if let Some(uniform) = is_homogeneous_aggregate(cx, ret) {
@@ -46,13 +70,16 @@ where
     ret.make_indirect();
 }
 
-fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
 where
     Ty: TyAbiInterface<'a, C> + Copy,
     C: HasDataLayout,
 {
     if !arg.layout.is_aggregate() {
-        arg.extend_integer_width_to(32);
+        match param_policy {
+            ParamExtension::ExtendTo32Bits => arg.extend_integer_width_to(32),
+            ParamExtension::NoExtension => {}
+        }
         return;
     }
     if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
@@ -68,19 +95,19 @@ where
     arg.make_indirect();
 }
 
-pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, param_policy: ParamExtension)
 where
     Ty: TyAbiInterface<'a, C> + Copy,
     C: HasDataLayout,
 {
     if !fn_abi.ret.is_ignore() {
-        classify_ret(cx, &mut fn_abi.ret);
+        classify_ret(cx, &mut fn_abi.ret, param_policy);
     }
 
     for arg in &mut fn_abi.args {
         if arg.is_ignore() {
             continue;
         }
-        classify_arg(cx, arg);
+        classify_arg(cx, arg, param_policy);
     }
 }
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index ca1d1302ec68a..c87c726cb877d 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -687,7 +687,14 @@ impl<'a, Ty> FnAbi<'a, Ty> {
                     }
                 }
             },
-            "aarch64" => aarch64::compute_abi_info(cx, self),
+            "aarch64" => {
+                let param_policy = if cx.target_spec().is_like_osx {
+                    aarch64::ParamExtension::ExtendTo32Bits
+                } else {
+                    aarch64::ParamExtension::NoExtension
+                };
+                aarch64::compute_abi_info(cx, self, param_policy)
+            }
             "amdgpu" => amdgpu::compute_abi_info(cx, self),
             "arm" => arm::compute_abi_info(cx, self),
             "avr" => avr::compute_abi_info(self),
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index a08603da04095..e78fefdcfad2a 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1250,6 +1250,8 @@ pub struct TargetOptions {
     pub abi_return_struct_as_int: bool,
     /// Whether the target toolchain is like macOS's. Only useful for compiling against iOS/macOS,
     /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false.
+    /// Also indiates whether to use Apple-specific ABI changes, such as extending function
+    /// parameters to 32-bits.
     pub is_like_osx: bool,
     /// Whether the target toolchain is like Solaris's.
     /// Only useful for compiling against Illumos/Solaris,

From dfdb017a9bb6284f58ff8447cd2a49c778552f62 Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Thu, 30 Jun 2022 00:31:41 -0400
Subject: [PATCH 05/26] experiment: trying to encode the end-to-end test as a
 ui test via rust_test_helpers. This instance is almost certainly insufficient
 because we need to force optimization flags for both the C and Rust sides of
 the code. but lets find out for sure.

---
 src/test/auxiliary/rust_test_helpers.c        | 12 ++++++
 ...sue-97463-broken-abi-leaked-uninit-data.rs | 38 +++++++++++++++++++
 2 files changed, 50 insertions(+)
 create mode 100644 src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs

diff --git a/src/test/auxiliary/rust_test_helpers.c b/src/test/auxiliary/rust_test_helpers.c
index 92b7dd4b7c516..977ea487a9804 100644
--- a/src/test/auxiliary/rust_test_helpers.c
+++ b/src/test/auxiliary/rust_test_helpers.c
@@ -1,6 +1,7 @@
 // Helper functions used only in tests
 
 #include <stdint.h>
+#include <stdlib.h>
 #include <assert.h>
 #include <stdarg.h>
 
@@ -415,3 +416,14 @@ rust_dbg_unpack_option_u64u64(struct U8TaggedEnumOptionU64U64 o, uint64_t *a, ui
         return 0;
     }
 }
+
+uint16_t issue_97463_leak_uninit_data(uint32_t a, uint32_t b, uint32_t c) {
+    struct bloc { uint16_t a; uint16_t b; uint16_t c; };
+    struct bloc *data = malloc(sizeof(struct bloc));
+
+    data->a = a & 0xFFFF;
+    data->b = b & 0xFFFF;
+    data->c = c & 0xFFFF;
+
+    return data->b; /* leak data */
+}
diff --git a/src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs b/src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
new file mode 100644
index 0000000000000..d04375acb30a2
--- /dev/null
+++ b/src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
@@ -0,0 +1,38 @@
+// run-pass
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn issue_97463_leak_uninit_data(a: u32, b: u32, c: u32) -> u16;
+}
+
+fn main() {
+    const C1: usize = 0x327b23c6;
+    const C2: usize = C1 & 0xFFFF;
+
+    let r1: usize = 0x0;
+    let r2: usize = C1;
+    let r3: usize = 0x0;
+    let value: u16 = unsafe { issue_97463_leak_uninit_data(r1 as u32, r2 as u32, r3 as u32) };
+
+    // NOTE: as an example of the sensitivity of this test to optimization choices,
+    // uncommenting this block of code makes the bug go away on pnkfeix's machine.
+    // (But observing via `dbg!` doesn't hide the bug. At least sometimes.)
+    /*
+    println!("{}", value);
+    println!("{}", value as usize);
+    println!("{}", usize::from(value));
+    println!("{}", (value as usize) & 0xFFFF);
+     */
+
+    let d1 = value;
+    let d2 = value as usize;
+    let d3 = usize::from(value);
+    let d4 = (value as usize) & 0xFFFF;
+
+    let d = (&d1, &d2, &d3, &d4);
+    let d_ = (d1, d2, d3, d4);
+
+    assert_eq!(((&(C2 as u16), &C2, &C2, &C2), (C2 as u16, C2, C2, C2)), (d, d_));
+}

From 99c0f91a4dd235824c353a11a2e75c462c9bbb74 Mon Sep 17 00:00:00 2001
From: Felix S Klock II <pnkfelix@pnkfx.org>
Date: Thu, 4 Aug 2022 10:41:47 -0400
Subject: [PATCH 06/26] fix typo, thanks wesley

Co-authored-by: Wesley Wiser <wwiser@gmail.com>
---
 .../issue-97463-abi-param-passing/param_passing.rs              | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs
index b4744a3b96e77..c11f3cc72bdf2 100644
--- a/src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs
+++ b/src/test/run-make-fulldeps/issue-97463-abi-param-passing/param_passing.rs
@@ -17,7 +17,7 @@ fn main() {
     let value: u16 = unsafe { c_read_value(r1 as u32, r2 as u32, r3 as u32) };
 
     // NOTE: as an example of the sensitivity of this test to optimization choices,
-    // uncommenting this block of code makes the bug go away on pnkfeix's machine.
+    // uncommenting this block of code makes the bug go away on pnkfelix's machine.
     // (But observing via `dbg!` doesn't hide the bug. At least sometimes.)
     /*
     println!("{}", value);

From 9bf3d5a82b689484b12bc5cc053d10f632ea6095 Mon Sep 17 00:00:00 2001
From: Wesley Wiser <wwiser@gmail.com>
Date: Fri, 5 Aug 2022 10:15:59 -0400
Subject: [PATCH 07/26] Ignore test on wasm

---
 .../ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs b/src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
index d04375acb30a2..da782f8bbbf81 100644
--- a/src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
+++ b/src/test/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
@@ -1,4 +1,5 @@
 // run-pass
+// ignore-wasm
 #![allow(dead_code)]
 #![allow(improper_ctypes)]
 

From 4b162141631e900f760752eacdd6d5e510ac4e42 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sat, 13 Aug 2022 18:39:30 +0200
Subject: [PATCH 08/26] Ban references to `Self` in trait object substs for
 projection predicates too.

---
 compiler/rustc_typeck/src/astconv/mod.rs      | 46 +++++++++++++------
 src/test/ui/traits/alias/self-in-generics.rs  |  7 +++
 .../ui/traits/alias/self-in-generics.stderr   |  2 +-
 3 files changed, 41 insertions(+), 14 deletions(-)

diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 1e6cb53f3eeae..ff81747cafe6f 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -35,7 +35,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT
 use rustc_span::edition::Edition;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use rustc_target::spec::abi;
 use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::astconv_object_safety_violations;
@@ -1458,16 +1458,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             if ty == dummy_self {
                                 let param = &generics.params[index];
                                 missing_type_params.push(param.name);
-                                tcx.ty_error().into()
+                                return tcx.ty_error().into();
                             } else if ty.walk().any(|arg| arg == dummy_self.into()) {
                                 references_self = true;
-                                tcx.ty_error().into()
-                            } else {
-                                arg
+                                return tcx.ty_error().into();
                             }
-                        } else {
-                            arg
                         }
+                        arg
                     })
                     .collect();
                 let substs = tcx.intern_substs(&substs[..]);
@@ -1506,13 +1503,36 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         });
 
         let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| {
-            bound.map_bound(|b| {
-                if b.projection_ty.self_ty() != dummy_self {
-                    tcx.sess.delay_span_bug(
-                        DUMMY_SP,
-                        &format!("trait_ref_to_existential called on {:?} with non-dummy Self", b),
-                    );
+            bound.map_bound(|mut b| {
+                assert_eq!(b.projection_ty.self_ty(), dummy_self);
+
+                // Like for trait refs, verify that `dummy_self` did not leak inside default type
+                // parameters.
+                let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
+                    if let ty::GenericArgKind::Type(ty) = arg.unpack() {
+                        if ty == dummy_self || ty.walk().any(|arg| arg == dummy_self.into()) {
+                            return true;
+                        }
+                    }
+                    false
+                });
+                if references_self {
+                    tcx.sess
+                        .delay_span_bug(span, "trait object projection bounds reference `Self`");
+                    let substs: Vec<_> = b
+                        .projection_ty
+                        .substs
+                        .iter()
+                        .map(|arg| {
+                            if let ty::GenericArgKind::Type(_) = arg.unpack() {
+                                return tcx.ty_error().into();
+                            }
+                            arg
+                        })
+                        .collect();
+                    b.projection_ty.substs = tcx.intern_substs(&substs[..]);
                 }
+
                 ty::ExistentialProjection::erase_self_ty(tcx, b)
             })
         });
diff --git a/src/test/ui/traits/alias/self-in-generics.rs b/src/test/ui/traits/alias/self-in-generics.rs
index 6b99431f5bbcf..0bb6335f91e4b 100644
--- a/src/test/ui/traits/alias/self-in-generics.rs
+++ b/src/test/ui/traits/alias/self-in-generics.rs
@@ -1,3 +1,10 @@
+// astconv uses `FreshTy(0)` as a dummy `Self` type when instanciating trait objects.
+// This `FreshTy(0)` can leak into substs, causing ICEs in several places.
+// Using `save-analysis` triggers type-checking `f` that would be normally skipped
+// as `type_of` emitted an error.
+//
+// compile-flags: -Zsave-analysis
+
 #![feature(trait_alias)]
 
 pub trait SelfInput = Fn(&mut Self);
diff --git a/src/test/ui/traits/alias/self-in-generics.stderr b/src/test/ui/traits/alias/self-in-generics.stderr
index a1056872ea641..110d60e6e9116 100644
--- a/src/test/ui/traits/alias/self-in-generics.stderr
+++ b/src/test/ui/traits/alias/self-in-generics.stderr
@@ -1,5 +1,5 @@
 error[E0038]: the trait alias `SelfInput` cannot be made into an object
-  --> $DIR/self-in-generics.rs:5:19
+  --> $DIR/self-in-generics.rs:12:19
    |
 LL | pub fn f(_f: &dyn SelfInput) {}
    |                   ^^^^^^^^^

From 9233298e71f08404838a1051a5b211255de4b79d Mon Sep 17 00:00:00 2001
From: Tim van Elsloo <tim@glacyr.com>
Date: Tue, 16 Aug 2022 17:36:25 +0200
Subject: [PATCH 09/26] Revert "Revert "Allow dynamic linking for iOS/tvOS
 targets.""

This reverts commit 16e10bf81ee73f61cf813acef3d5dbbce4f66da2.

# Conflicts:
#	compiler/rustc_target/src/spec/apple_sdk_base.rs
---
 compiler/rustc_target/src/spec/apple_sdk_base.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs
index bf3ebaa2840f6..49e302676a7b1 100644
--- a/compiler/rustc_target/src/spec/apple_sdk_base.rs
+++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs
@@ -65,7 +65,6 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
     TargetOptions {
         abi: target_abi(arch).into(),
         cpu: target_cpu(arch).into(),
-        dynamic_linking: false,
         link_env_remove: link_env_remove(arch),
         has_thread_local: false,
         ..super::apple_base::opts(os, target_arch_name(arch), target_abi(arch))

From e9e46c95ce60469f596b8188c439dc278dff6bc4 Mon Sep 17 00:00:00 2001
From: Christopher Durham <cad97@cad97.com>
Date: Wed, 17 Aug 2022 06:07:33 -0500
Subject: [PATCH 10/26] Don't treat stashed warnings as errors

---
 compiler/rustc_errors/src/lib.rs | 74 ++++++++++++++++++++++++++------
 1 file changed, 62 insertions(+), 12 deletions(-)

diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 395bf5aad01b6..fe2afdf5a20b8 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -625,19 +625,13 @@ impl Handler {
     /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing.
     pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) {
         let mut inner = self.inner.borrow_mut();
-        // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
-        // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
-        // See the PR for a discussion.
-        inner.stashed_diagnostics.insert((span, key), diag);
+        inner.stash((span, key), diag);
     }
 
     /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key.
     pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
-        self.inner
-            .borrow_mut()
-            .stashed_diagnostics
-            .remove(&(span, key))
-            .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
+        let mut inner = self.inner.borrow_mut();
+        inner.steal((span, key)).map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
     }
 
     /// Emit all stashed diagnostics.
@@ -1105,13 +1099,31 @@ impl HandlerInner {
 
     /// Emit all stashed diagnostics.
     fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
+        let has_errors = self.has_errors();
         let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::<Vec<_>>();
         let mut reported = None;
         for mut diag in diags {
+            // Decrement the count tracking the stash; emitting will increment it.
             if diag.is_error() {
-                reported = Some(ErrorGuaranteed(()));
+                if matches!(diag.level, Level::Error { lint: true }) {
+                    self.lint_err_count -= 1;
+                } else {
+                    self.err_count -= 1;
+                }
+            } else {
+                if diag.is_force_warn() {
+                    self.warn_count -= 1;
+                } else {
+                    // Unless they're forced, don't flush stashed warnings when
+                    // there are errors, to avoid causing warning overload. The
+                    // stash would've been stolen already if it were important.
+                    if has_errors {
+                        continue;
+                    }
+                }
             }
-            self.emit_diagnostic(&mut diag);
+            let reported_this = self.emit_diagnostic(&mut diag);
+            reported = reported.or(reported_this);
         }
         reported
     }
@@ -1301,9 +1313,47 @@ impl HandlerInner {
         }
     }
 
+    fn stash(&mut self, key: (Span, StashKey), diagnostic: Diagnostic) {
+        // Track the diagnostic for counts, but don't panic-if-treat-err-as-bug
+        // yet; that happens when we actually emit the diagnostic.
+        if diagnostic.is_error() {
+            if matches!(diagnostic.level, Level::Error { lint: true }) {
+                self.lint_err_count += 1;
+            } else {
+                self.err_count += 1;
+            }
+        } else {
+            // Warnings are only automatically flushed if they're forced.
+            if diagnostic.is_force_warn() {
+                self.warn_count += 1;
+            }
+        }
+
+        // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
+        // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
+        // See the PR for a discussion.
+        self.stashed_diagnostics.insert(key, diagnostic);
+    }
+
+    fn steal(&mut self, key: (Span, StashKey)) -> Option<Diagnostic> {
+        let diagnostic = self.stashed_diagnostics.remove(&key)?;
+        if diagnostic.is_error() {
+            if matches!(diagnostic.level, Level::Error { lint: true }) {
+                self.lint_err_count -= 1;
+            } else {
+                self.err_count -= 1;
+            }
+        } else {
+            if diagnostic.is_force_warn() {
+                self.warn_count -= 1;
+            }
+        }
+        Some(diagnostic)
+    }
+
     #[inline]
     fn err_count(&self) -> usize {
-        self.err_count + self.stashed_diagnostics.len()
+        self.err_count
     }
 
     fn has_errors(&self) -> bool {

From 767239f7403a3808e534da02ca388632eac2bc18 Mon Sep 17 00:00:00 2001
From: Christopher Durham <cad97@cad97.com>
Date: Wed, 17 Aug 2022 06:52:47 -0500
Subject: [PATCH 11/26] Reenable early feature-gates as future-compat warnings

---
 compiler/rustc_ast_passes/src/feature_gate.rs | 58 +++++++++++--------
 compiler/rustc_errors/src/lib.rs              |  1 +
 compiler/rustc_lint_defs/src/builtin.rs       | 51 ++++++++++++++++
 compiler/rustc_session/src/parse.rs           | 55 +++++++++++++++++-
 src/test/ui/macros/stringify.rs               |  7 +++
 .../or-patterns/or-patterns-syntactic-pass.rs | 16 ++---
 .../or-patterns-syntactic-pass.stderr         | 13 +++++
 ...ints-before-generic-args-syntactic-pass.rs |  4 ++
 ...-before-generic-args-syntactic-pass.stderr | 24 ++++++++
 src/test/ui/pattern/rest-pat-syntactic.rs     |  5 +-
 src/test/ui/pattern/rest-pat-syntactic.stderr | 24 ++++++++
 11 files changed, 224 insertions(+), 34 deletions(-)
 create mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr
 create mode 100644 src/test/ui/parser/constraints-before-generic-args-syntactic-pass.stderr
 create mode 100644 src/test/ui/pattern/rest-pat-syntactic.stderr

diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 97eee56f94807..68fca08101891 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -2,10 +2,10 @@ use rustc_ast as ast;
 use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
 use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId};
 use rustc_ast::{PatKind, RangeEnd, VariantData};
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::{struct_span_err, Applicability, StashKey};
+use rustc_feature::Features;
 use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
-use rustc_feature::{Features, GateIssue};
-use rustc_session::parse::{feature_err, feature_err_issue};
+use rustc_session::parse::{feature_err, feature_warn};
 use rustc_session::Session;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
@@ -20,9 +20,7 @@ macro_rules! gate_feature_fn {
         let has_feature: bool = has_feature(visitor.features);
         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
         if !has_feature && !span.allows_unstable($name) {
-            feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
-                .help(help)
-                .emit();
+            feature_err(&visitor.sess.parse_sess, name, span, explain).help(help).emit();
         }
     }};
     ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
@@ -31,8 +29,19 @@ macro_rules! gate_feature_fn {
         let has_feature: bool = has_feature(visitor.features);
         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
         if !has_feature && !span.allows_unstable($name) {
-            feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
-                .emit();
+            feature_err(&visitor.sess.parse_sess, name, span, explain).emit();
+        }
+    }};
+    (future_incompatible; $visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
+        let (visitor, has_feature, span, name, explain) =
+            (&*$visitor, $has_feature, $span, $name, $explain);
+        let has_feature: bool = has_feature(visitor.features);
+        debug!(
+            "gate_feature(feature = {:?}, span = {:?}); has? {} (future_incompatible)",
+            name, span, has_feature
+        );
+        if !has_feature && !span.allows_unstable($name) {
+            feature_warn(&visitor.sess.parse_sess, name, span, explain);
         }
     }};
 }
@@ -44,6 +53,9 @@ macro_rules! gate_feature_post {
     ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
         gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
     };
+    (future_incompatible; $visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
+        gate_feature_fn!(future_incompatible; $visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
+    };
 }
 
 pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
@@ -588,11 +600,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         {
             // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type
             // ascription error, but the likely intention was to write a `let` statement. (#78907).
-            feature_err_issue(
+            feature_err(
                 &self.sess.parse_sess,
                 sym::type_ascription,
                 lhs.span,
-                GateIssue::Language,
                 "type ascription is experimental",
             ).span_suggestion_verbose(
                 lhs.span.shrink_to_lo(),
@@ -615,15 +626,22 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 );
             }
             ast::ExprKind::Type(..) => {
-                // To avoid noise about type ascription in common syntax errors, only emit if it
-                // is the *only* error.
                 if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
+                    // To avoid noise about type ascription in common syntax errors,
+                    // only emit if it is the *only* error.
                     gate_feature_post!(
                         &self,
                         type_ascription,
                         e.span,
                         "type ascription is experimental"
                     );
+                } else {
+                    // And if it isn't, cancel the early-pass warning.
+                    self.sess
+                        .parse_sess
+                        .span_diagnostic
+                        .steal_diagnostic(e.span, StashKey::EarlySyntaxWarning)
+                        .map(|err| err.cancel());
                 }
             }
             ast::ExprKind::TryBlock(_) => {
@@ -789,14 +807,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
+    // We emit an early future-incompatible warning for these.
+    // New syntax gates should go above here to get a hard error gate.
     macro_rules! gate_all {
         ($gate:ident, $msg:literal) => {
-            // FIXME(eddyb) do something more useful than always
-            // disabling these uses of early feature-gatings.
-            if false {
-                for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
-                    gate_feature_post!(&visitor, $gate, *span, $msg);
-                }
+            for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
+                gate_feature_post!(future_incompatible; &visitor, $gate, *span, $msg);
             }
         };
     }
@@ -809,11 +825,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     gate_all!(try_blocks, "`try` blocks are unstable");
     gate_all!(label_break_value, "labels on blocks are unstable");
     gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
-    // To avoid noise about type ascription in common syntax errors,
-    // only emit if it is the *only* error. (Also check it last.)
-    if sess.parse_sess.span_diagnostic.err_count() == 0 {
-        gate_all!(type_ascription, "type ascription is experimental");
-    }
+    gate_all!(type_ascription, "type ascription is experimental");
 
     visit::walk_crate(&mut visitor, krate);
 }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index fe2afdf5a20b8..43b8288d8b627 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -458,6 +458,7 @@ struct HandlerInner {
 pub enum StashKey {
     ItemNoType,
     UnderscoreForArrayLengths,
+    EarlySyntaxWarning,
 }
 
 fn default_track_diagnostic(_: &Diagnostic) {}
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index f00165cd3b370..95e34da734d6a 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3212,6 +3212,56 @@ declare_lint! {
     };
 }
 
+declare_lint! {
+    /// The `unstable_syntax_pre_expansion` lint detects the use of unstable
+    /// syntax that is discarded during attribute expansion.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #[cfg(FALSE)]
+    /// macro foo() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The input to active attributes such as `#[cfg]` or procedural macro
+    /// attributes is required to be valid syntax. Previously, the compiler only
+    /// gated the use of unstable syntax features after resolving `#[cfg]` gates
+    /// and expanding procedural macros.
+    ///
+    /// To avoid relying on unstable syntax, move the use of unstable syntax
+    /// into a position where the compiler does not parse the syntax, such as a
+    /// functionlike macro.
+    ///
+    /// ```rust
+    /// # #![deny(unstable_syntax_pre_expansion)]
+    ///
+    /// macro_rules! identity {
+    ///    ( $($tokens:tt)* ) => { $($tokens)* }
+    /// }
+    ///
+    /// #[cfg(FALSE)]
+    /// identity! {
+    ///    macro foo() {}
+    /// }
+    /// ```
+    ///
+    /// This is a [future-incompatible] lint to transition this
+    /// to a hard error in the future. See [issue #65860] for more details.
+    ///
+    /// [issue #65860]: https://github.com/rust-lang/rust/issues/65860
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
+    pub UNSTABLE_SYNTAX_PRE_EXPANSION,
+    Warn,
+    "unstable syntax can change at any point in the future, causing a hard error!",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #65860 <https://github.com/rust-lang/rust/issues/65860>",
+    };
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
@@ -3280,6 +3330,7 @@ declare_lint_pass! {
         POINTER_STRUCTURAL_MATCH,
         NONTRIVIAL_STRUCTURAL_MATCH,
         SOFT_UNSTABLE,
+        UNSTABLE_SYNTAX_PRE_EXPANSION,
         INLINE_NO_SANITIZE,
         BAD_ASM_STYLE,
         ASM_SUB_REGISTER,
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index f31d52147b47b..9f0886cb2089c 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -2,15 +2,17 @@
 //! It also serves as an input to the parser itself.
 
 use crate::config::CheckCfg;
-use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
+use crate::lint::{
+    builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId,
+};
 use crate::SessionDiagnostic;
 use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
 use rustc_errors::{
-    error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder,
-    DiagnosticMessage, ErrorGuaranteed, MultiSpan,
+    error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
+    DiagnosticMessage, ErrorGuaranteed, MultiSpan, StashKey,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
@@ -101,11 +103,58 @@ pub fn feature_err_issue<'a>(
     issue: GateIssue,
     explain: &str,
 ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    let span = span.into();
+
+    // Cancel an earlier warning for this same error, if it exists.
+    if let Some(span) = span.primary_span() {
+        sess.span_diagnostic
+            .steal_diagnostic(span, StashKey::EarlySyntaxWarning)
+            .map(|err| err.cancel());
+    }
+
     let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
     add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
     err
 }
 
+/// Construct a future incompatibility diagnostic for a feature gate.
+///
+/// This diagnostic is only a warning and *does not cause compilation to fail*.
+pub fn feature_warn<'a>(sess: &'a ParseSess, feature: Symbol, span: Span, explain: &str) {
+    feature_warn_issue(sess, feature, span, GateIssue::Language, explain);
+}
+
+/// Construct a future incompatibility diagnostic for a feature gate.
+///
+/// This diagnostic is only a warning and *does not cause compilation to fail*.
+///
+/// This variant allows you to control whether it is a library or language feature.
+/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`.
+pub fn feature_warn_issue<'a>(
+    sess: &'a ParseSess,
+    feature: Symbol,
+    span: Span,
+    issue: GateIssue,
+    explain: &str,
+) {
+    let mut err = sess.span_diagnostic.struct_span_warn(span, explain);
+    add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
+
+    // Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level
+    let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
+    let future_incompatible = lint.future_incompatible.as_ref().unwrap();
+    err.code(DiagnosticId::Lint {
+        name: lint.name_lower(),
+        has_future_breakage: false,
+        is_force_warn: false,
+    });
+    err.warn(lint.desc);
+    err.note(format!("for more information, see {}", future_incompatible.reference));
+
+    // A later feature_err call can steal and cancel this warning.
+    err.stash(span, StashKey::EarlySyntaxWarning);
+}
+
 /// Adds the diagnostics for a feature to an existing error.
 pub fn add_feature_diagnostics<'a>(err: &mut Diagnostic, sess: &'a ParseSess, feature: Symbol) {
     add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language);
diff --git a/src/test/ui/macros/stringify.rs b/src/test/ui/macros/stringify.rs
index 8e71ed7c1120f..082c1abb8f2f4 100644
--- a/src/test/ui/macros/stringify.rs
+++ b/src/test/ui/macros/stringify.rs
@@ -3,11 +3,18 @@
 // compile-flags: --test
 
 #![feature(async_closure)]
+#![feature(box_patterns)]
+#![feature(box_syntax)]
 #![feature(const_trait_impl)]
+#![feature(decl_macro)]
 #![feature(generators)]
 #![feature(half_open_range_patterns)]
+#![feature(label_break_value)]
 #![feature(more_qualified_paths)]
 #![feature(raw_ref_op)]
+#![feature(trait_alias)]
+#![feature(try_blocks)]
+#![feature(type_ascription)]
 #![deny(unused_macros)]
 
 macro_rules! stringify_block {
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs
index 6f9a631b092a7..dda5c0bb59d23 100644
--- a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs
@@ -7,7 +7,7 @@ fn main() {}
 
 // Test the `pat` macro fragment parser:
 macro_rules! accept_pat {
-    ($p:pat) => {}
+    ($p:pat) => {};
 }
 
 accept_pat!((p | q));
@@ -21,28 +21,28 @@ accept_pat!([p | q]);
 #[cfg(FALSE)]
 fn or_patterns() {
     // Top level of `let`:
-    let (| A | B);
+    let (A | B);
     let (A | B);
     let (A | B): u8;
     let (A | B) = 0;
     let (A | B): u8 = 0;
 
     // Top level of `for`:
-    for | A | B in 0 {}
+    for A | B in 0 {}
     for A | B in 0 {}
 
     // Top level of `while`:
-    while let | A | B = 0 {}
+    while let A | B = 0 {}
     while let A | B = 0 {}
 
     // Top level of `if`:
-    if let | A | B = 0 {}
+    if let A | B = 0 {}
     if let A | B = 0 {}
 
     // Top level of `match` arms:
     match 0 {
-        | A | B => {},
-        A | B => {},
+        A | B => {}
+        A | B => {}
     }
 
     // Functions:
@@ -68,6 +68,8 @@ fn or_patterns() {
 
     // These bind as `(prefix p) | q` as opposed to `prefix (p | q)`:
     let (box 0 | 1); // Unstable; we *can* change the precedence if we want.
+                     //~^ WARN box pattern syntax is experimental
+                     //~| WARN unstable syntax
     let (&0 | 1);
     let (&mut 0 | 1);
     let (x @ 0 | 1);
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr
new file mode 100644
index 0000000000000..c43fe192a73b8
--- /dev/null
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr
@@ -0,0 +1,13 @@
+warning: box pattern syntax is experimental
+  --> $DIR/or-patterns-syntactic-pass.rs:70:10
+   |
+LL |     let (box 0 | 1); // Unstable; we *can* change the precedence if we want.
+   |          ^^^^^
+   |
+   = note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
+   = help: add `#![feature(box_patterns)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs
index afbd13e6fd9a8..d8346653c25aa 100644
--- a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs
+++ b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs
@@ -3,7 +3,11 @@
 #[cfg(FALSE)]
 fn syntax() {
     foo::<T = u8, T: Ord, String>();
+    //~^ WARN associated type bounds are unstable
+    //~| WARN unstable syntax
     foo::<T = u8, 'a, T: Ord>();
+    //~^ WARN associated type bounds are unstable
+    //~| WARN unstable syntax
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.stderr b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.stderr
new file mode 100644
index 0000000000000..7e843c7f4d006
--- /dev/null
+++ b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.stderr
@@ -0,0 +1,24 @@
+warning: associated type bounds are unstable
+  --> $DIR/constraints-before-generic-args-syntactic-pass.rs:5:19
+   |
+LL |     foo::<T = u8, T: Ord, String>();
+   |                   ^^^^^^
+   |
+   = note: see issue #52662 <https://github.com/rust-lang/rust/issues/52662> for more information
+   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: associated type bounds are unstable
+  --> $DIR/constraints-before-generic-args-syntactic-pass.rs:8:23
+   |
+LL |     foo::<T = u8, 'a, T: Ord>();
+   |                       ^^^^^^
+   |
+   = note: see issue #52662 <https://github.com/rust-lang/rust/issues/52662> for more information
+   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/pattern/rest-pat-syntactic.rs b/src/test/ui/pattern/rest-pat-syntactic.rs
index 9656a0b5de9ce..4da5a2db76743 100644
--- a/src/test/ui/pattern/rest-pat-syntactic.rs
+++ b/src/test/ui/pattern/rest-pat-syntactic.rs
@@ -19,6 +19,8 @@ fn rest_patterns() {
 
     // Box patterns:
     let box ..;
+    //~^ WARN box pattern syntax is experimental
+    //~| WARN unstable syntax
 
     // In or-patterns:
     match x {
@@ -57,7 +59,7 @@ fn rest_patterns() {
         .. |
         [
             (
-                box ..,
+                box .., //~ WARN box pattern syntax is experimental
                 &(..),
                 &mut ..,
                 x @ ..
@@ -67,4 +69,5 @@ fn rest_patterns() {
         ref mut x @ ..
         => {}
     }
+    //~| WARN unstable syntax
 }
diff --git a/src/test/ui/pattern/rest-pat-syntactic.stderr b/src/test/ui/pattern/rest-pat-syntactic.stderr
new file mode 100644
index 0000000000000..37019b7d5ba7a
--- /dev/null
+++ b/src/test/ui/pattern/rest-pat-syntactic.stderr
@@ -0,0 +1,24 @@
+warning: box pattern syntax is experimental
+  --> $DIR/rest-pat-syntactic.rs:21:9
+   |
+LL |     let box ..;
+   |         ^^^^^^
+   |
+   = note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
+   = help: add `#![feature(box_patterns)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: box pattern syntax is experimental
+  --> $DIR/rest-pat-syntactic.rs:62:17
+   |
+LL |                 box ..,
+   |                 ^^^^^^
+   |
+   = note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
+   = help: add `#![feature(box_patterns)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: 2 warnings emitted
+

From 944c6b6d760d392552fc60ec4e3ba3ad84598350 Mon Sep 17 00:00:00 2001
From: Christopher Durham <cad97@cad97.com>
Date: Wed, 17 Aug 2022 06:59:46 -0500
Subject: [PATCH 12/26] New ui tests for new soft feature gates

---
 .../soft-syntax-gates-with-errors.rs          | 30 +++++++++++++++++++
 .../soft-syntax-gates-with-errors.stderr      | 21 +++++++++++++
 .../soft-syntax-gates-without-errors.rs       | 26 ++++++++++++++++
 .../soft-syntax-gates-without-errors.stderr   | 24 +++++++++++++++
 .../ui/suggestions/many-type-ascription.rs    |  4 +++
 .../suggestions/many-type-ascription.stderr   | 12 ++++++++
 .../type-ascription-and-other-error.rs        |  6 ++++
 .../type-ascription-and-other-error.stderr    |  8 +++++
 8 files changed, 131 insertions(+)
 create mode 100644 src/test/ui/feature-gates/soft-syntax-gates-with-errors.rs
 create mode 100644 src/test/ui/feature-gates/soft-syntax-gates-with-errors.stderr
 create mode 100644 src/test/ui/feature-gates/soft-syntax-gates-without-errors.rs
 create mode 100644 src/test/ui/feature-gates/soft-syntax-gates-without-errors.stderr
 create mode 100644 src/test/ui/suggestions/many-type-ascription.rs
 create mode 100644 src/test/ui/suggestions/many-type-ascription.stderr
 create mode 100644 src/test/ui/suggestions/type-ascription-and-other-error.rs
 create mode 100644 src/test/ui/suggestions/type-ascription-and-other-error.stderr

diff --git a/src/test/ui/feature-gates/soft-syntax-gates-with-errors.rs b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.rs
new file mode 100644
index 0000000000000..49f1cba7151ef
--- /dev/null
+++ b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.rs
@@ -0,0 +1,30 @@
+// check-fail
+// This file is used to test the behavior of the early-pass syntax warnings.
+// If macro syntax is stabilized, replace with a different unstable syntax.
+
+macro a() {}
+//~^ ERROR: `macro` is experimental
+
+#[cfg(FALSE)]
+macro b() {}
+
+macro_rules! identity {
+    ($($x:tt)*) => ($($x)*);
+}
+
+identity! {
+    macro c() {}
+    //~^ ERROR: `macro` is experimental
+}
+
+#[cfg(FALSE)]
+identity! {
+    macro d() {} // No error
+}
+
+identity! {
+    #[cfg(FALSE)]
+    macro e() {}
+}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/soft-syntax-gates-with-errors.stderr b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.stderr
new file mode 100644
index 0000000000000..49550d811ba52
--- /dev/null
+++ b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.stderr
@@ -0,0 +1,21 @@
+error[E0658]: `macro` is experimental
+  --> $DIR/soft-syntax-gates-with-errors.rs:5:1
+   |
+LL | macro a() {}
+   | ^^^^^^^^^^^^
+   |
+   = note: see issue #39412 <https://github.com/rust-lang/rust/issues/39412> for more information
+   = help: add `#![feature(decl_macro)]` to the crate attributes to enable
+
+error[E0658]: `macro` is experimental
+  --> $DIR/soft-syntax-gates-with-errors.rs:16:5
+   |
+LL |     macro c() {}
+   |     ^^^^^^^^^^^^
+   |
+   = note: see issue #39412 <https://github.com/rust-lang/rust/issues/39412> for more information
+   = help: add `#![feature(decl_macro)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/soft-syntax-gates-without-errors.rs b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.rs
new file mode 100644
index 0000000000000..ca4ad2320f657
--- /dev/null
+++ b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.rs
@@ -0,0 +1,26 @@
+// check-pass
+// This file is used to test the behavior of the early-pass syntax warnings.
+// If macro syntax is stabilized, replace with a different unstable syntax.
+
+#[cfg(FALSE)]
+macro b() {}
+//~^ WARN: `macro` is experimental
+//~| WARN: unstable syntax
+
+macro_rules! identity {
+    ($($x:tt)*) => ($($x)*);
+}
+
+#[cfg(FALSE)]
+identity! {
+    macro d() {} // No error
+}
+
+identity! {
+    #[cfg(FALSE)]
+    macro e() {}
+    //~^ WARN: `macro` is experimental
+    //~| WARN: unstable syntax
+}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/soft-syntax-gates-without-errors.stderr b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.stderr
new file mode 100644
index 0000000000000..3d9c22e548710
--- /dev/null
+++ b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.stderr
@@ -0,0 +1,24 @@
+warning: `macro` is experimental
+  --> $DIR/soft-syntax-gates-without-errors.rs:6:1
+   |
+LL | macro b() {}
+   | ^^^^^^^^^^^^
+   |
+   = note: see issue #39412 <https://github.com/rust-lang/rust/issues/39412> for more information
+   = help: add `#![feature(decl_macro)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: `macro` is experimental
+  --> $DIR/soft-syntax-gates-without-errors.rs:21:5
+   |
+LL |     macro e() {}
+   |     ^^^^^^^^^^^^
+   |
+   = note: see issue #39412 <https://github.com/rust-lang/rust/issues/39412> for more information
+   = help: add `#![feature(decl_macro)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/suggestions/many-type-ascription.rs b/src/test/ui/suggestions/many-type-ascription.rs
new file mode 100644
index 0000000000000..31ac556b9447c
--- /dev/null
+++ b/src/test/ui/suggestions/many-type-ascription.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let _ = 0: i32; //~ ERROR: type ascription is experimental
+    let _ = 0: i32; // (error only emitted once)
+}
diff --git a/src/test/ui/suggestions/many-type-ascription.stderr b/src/test/ui/suggestions/many-type-ascription.stderr
new file mode 100644
index 0000000000000..3706bbae9df9f
--- /dev/null
+++ b/src/test/ui/suggestions/many-type-ascription.stderr
@@ -0,0 +1,12 @@
+error[E0658]: type ascription is experimental
+  --> $DIR/many-type-ascription.rs:2:13
+   |
+LL |     let _ = 0: i32;
+   |             ^^^^^^
+   |
+   = note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
+   = help: add `#![feature(type_ascription)]` 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/suggestions/type-ascription-and-other-error.rs b/src/test/ui/suggestions/type-ascription-and-other-error.rs
new file mode 100644
index 0000000000000..99ab2f3c858b9
--- /dev/null
+++ b/src/test/ui/suggestions/type-ascription-and-other-error.rs
@@ -0,0 +1,6 @@
+fn main() {
+    not rust; //~ ERROR
+    let _ = 0: i32; // (error hidden by existing error)
+    #[cfg(FALSE)]
+    let _ = 0: i32; // (warning hidden by existing error)
+}
diff --git a/src/test/ui/suggestions/type-ascription-and-other-error.stderr b/src/test/ui/suggestions/type-ascription-and-other-error.stderr
new file mode 100644
index 0000000000000..eadf634bb14fd
--- /dev/null
+++ b/src/test/ui/suggestions/type-ascription-and-other-error.stderr
@@ -0,0 +1,8 @@
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `rust`
+  --> $DIR/type-ascription-and-other-error.rs:2:9
+   |
+LL |     not rust;
+   |         ^^^^ expected one of 8 possible tokens
+
+error: aborting due to previous error
+

From be2641a61f2d34fe4f937a49dacfcb82bf0b9075 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Wed, 17 Aug 2022 20:22:52 +0200
Subject: [PATCH 13/26] Fortify check for const generics.

---
 compiler/rustc_typeck/src/astconv/mod.rs | 25 ++++++++++--------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index ff81747cafe6f..5bb02bc246caf 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -1453,16 +1453,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     .enumerate()
                     .skip(1) // Remove `Self` for `ExistentialPredicate`.
                     .map(|(index, arg)| {
-                        if let ty::GenericArgKind::Type(ty) = arg.unpack() {
-                            debug!(?ty);
-                            if ty == dummy_self {
-                                let param = &generics.params[index];
-                                missing_type_params.push(param.name);
-                                return tcx.ty_error().into();
-                            } else if ty.walk().any(|arg| arg == dummy_self.into()) {
-                                references_self = true;
-                                return tcx.ty_error().into();
-                            }
+                        if arg == dummy_self.into() {
+                            let param = &generics.params[index];
+                            missing_type_params.push(param.name);
+                            return tcx.ty_error().into();
+                        } else if arg.walk().any(|arg| arg == dummy_self.into()) {
+                            references_self = true;
+                            return tcx.ty_error().into();
                         }
                         arg
                     })
@@ -1509,10 +1506,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 // Like for trait refs, verify that `dummy_self` did not leak inside default type
                 // parameters.
                 let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
-                    if let ty::GenericArgKind::Type(ty) = arg.unpack() {
-                        if ty == dummy_self || ty.walk().any(|arg| arg == dummy_self.into()) {
-                            return true;
-                        }
+                    if arg.walk().any(|arg| arg == dummy_self.into()) {
+                        return true;
                     }
                     false
                 });
@@ -1524,7 +1519,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         .substs
                         .iter()
                         .map(|arg| {
-                            if let ty::GenericArgKind::Type(_) = arg.unpack() {
+                            if arg.walk().any(|arg| arg == dummy_self.into()) {
                                 return tcx.ty_error().into();
                             }
                             arg

From 72acd94117e784d4e7425e827c60d6a6748d7a88 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Wed, 17 Aug 2022 21:50:59 +0200
Subject: [PATCH 14/26] Add const-generics test.

---
 src/test/ui/traits/alias/self-in-const-generics.rs   | 12 ++++++++++++
 .../ui/traits/alias/self-in-const-generics.stderr    | 11 +++++++++++
 2 files changed, 23 insertions(+)
 create mode 100644 src/test/ui/traits/alias/self-in-const-generics.rs
 create mode 100644 src/test/ui/traits/alias/self-in-const-generics.stderr

diff --git a/src/test/ui/traits/alias/self-in-const-generics.rs b/src/test/ui/traits/alias/self-in-const-generics.rs
new file mode 100644
index 0000000000000..b0de8ccd67847
--- /dev/null
+++ b/src/test/ui/traits/alias/self-in-const-generics.rs
@@ -0,0 +1,12 @@
+#![allow(incomplete_features)]
+#![feature(generic_const_exprs)]
+#![feature(trait_alias)]
+
+trait Bar<const N: usize> {}
+
+trait BB = Bar<{ 2 + 1 }>;
+
+fn foo(x: &dyn BB) {}
+//~^ ERROR the trait alias `BB` cannot be made into an object [E0038]
+
+fn main() {}
diff --git a/src/test/ui/traits/alias/self-in-const-generics.stderr b/src/test/ui/traits/alias/self-in-const-generics.stderr
new file mode 100644
index 0000000000000..61cc217cfbce6
--- /dev/null
+++ b/src/test/ui/traits/alias/self-in-const-generics.stderr
@@ -0,0 +1,11 @@
+error[E0038]: the trait alias `BB` cannot be made into an object
+  --> $DIR/self-in-const-generics.rs:9:16
+   |
+LL | fn foo(x: &dyn BB) {}
+   |                ^^
+   |
+   = note: it cannot use `Self` as a type parameter in a supertrait or `where`-clause
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0038`.

From fe6956b7be4d619d98154a5ade5b5c21cb128732 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Thu, 18 Aug 2022 15:47:39 +0200
Subject: [PATCH 15/26] Fix item-info display

---
 src/librustdoc/html/render/mod.rs          | 12 ++++++++----
 src/librustdoc/html/static/css/rustdoc.css |  1 +
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5ed5299e09bc0..13d955c618a4f 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -569,7 +569,10 @@ fn short_item_info(
             message.push_str(&format!(": {}", html.into_string()));
         }
         extra_info.push(format!(
-            "<div class=\"stab deprecated\"><span class=\"emoji\">👎</span> {}</div>",
+            "<div class=\"stab deprecated\">\
+                 <span class=\"emoji\">👎</span>\
+                 <span>{}</span>\
+             </div>",
             message,
         ));
     }
@@ -582,8 +585,9 @@ fn short_item_info(
         .filter(|stab| stab.feature != sym::rustc_private)
         .map(|stab| (stab.level, stab.feature))
     {
-        let mut message =
-            "<span class=\"emoji\">🔬</span> This is a nightly-only experimental API.".to_owned();
+        let mut message = "<span class=\"emoji\">🔬</span>\
+             <span>This is a nightly-only experimental API."
+            .to_owned();
 
         let mut feature = format!("<code>{}</code>", Escape(feature.as_str()));
         if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
@@ -594,7 +598,7 @@ fn short_item_info(
             ));
         }
 
-        message.push_str(&format!(" ({})", feature));
+        message.push_str(&format!(" ({})</span>", feature));
 
         extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message));
     }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 710ca3ee7c7e7..52a7d04458409 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1172,6 +1172,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
 
 .stab .emoji {
 	font-size: 1.25rem;
+	margin-right: 0.3rem;
 }
 
 /* Black one-pixel outline around emoji shapes */

From 0df1c690231c108fcfaa3ba3e22be37fa4fb8fc4 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Thu, 18 Aug 2022 17:47:40 +0200
Subject: [PATCH 16/26] Update rustdoc test

---
 src/test/rustdoc/issue-32374.rs | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/test/rustdoc/issue-32374.rs b/src/test/rustdoc/issue-32374.rs
index 9c585497d354f..8d2c27cf3d77d 100644
--- a/src/test/rustdoc/issue-32374.rs
+++ b/src/test/rustdoc/issue-32374.rs
@@ -8,20 +8,24 @@
 //      'Experimental'
 // @matches issue_32374/index.html '//*[@class="item-right docblock-short"]/text()' 'Docs'
 
-// @has issue_32374/struct.T.html '//*[@class="stab deprecated"]' \
-//      '👎 Deprecated since 1.0.0: text'
+// @has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' '👎'
+// @has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' \
+//      'Deprecated since 1.0.0: text'
 // @hasraw - '<code>test</code>&nbsp;<a href="https://issue_url/32374">#32374</a>'
+// @matches issue_32374/struct.T.html '//*[@class="stab unstable"]' '🔬'
 // @matches issue_32374/struct.T.html '//*[@class="stab unstable"]' \
-//      '🔬 This is a nightly-only experimental API. \(test\s#32374\)$'
+//     'This is a nightly-only experimental API. \(test\s#32374\)$'
 /// Docs
 #[deprecated(since = "1.0.0", note = "text")]
 #[unstable(feature = "test", issue = "32374")]
 pub struct T;
 
+// @has issue_32374/struct.U.html '//*[@class="stab deprecated"]' '👎'
 // @has issue_32374/struct.U.html '//*[@class="stab deprecated"]' \
-//      '👎 Deprecated since 1.0.0: deprecated'
+//     'Deprecated since 1.0.0: deprecated'
+// @has issue_32374/struct.U.html '//*[@class="stab unstable"]' '🔬'
 // @has issue_32374/struct.U.html '//*[@class="stab unstable"]' \
-//      '🔬 This is a nightly-only experimental API. (test #32374)'
+//     'This is a nightly-only experimental API. (test #32374)'
 #[deprecated(since = "1.0.0", note = "deprecated")]
 #[unstable(feature = "test", issue = "32374", reason = "unstable")]
 pub struct U;

From ac66baad1a9787f60ff86fac125da8176e053dbc Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Wed, 3 Aug 2022 22:48:12 -0400
Subject: [PATCH 17/26] add miri-test-libstd support to libstd

---
 library/alloc/src/lib.rs | 1 +
 library/std/src/lib.rs   | 8 ++++++++
 2 files changed, 9 insertions(+)

diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 8b6f4054851dd..a6f3593addc10 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -60,6 +60,7 @@
 // able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
 // rustc itself never sets the feature, so this line has no affect there.
 #![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
+//
 #![allow(unused_attributes)]
 #![stable(feature = "alloc", since = "1.36.0")]
 #![doc(
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index a8d6645794ae5..bbd7041c50bd0 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -187,6 +187,14 @@
 //! [rust-discord]: https://discord.gg/rust-lang
 //! [array]: prim@array
 //! [slice]: prim@slice
+
+// To run libstd tests without x.py without ending up with two copies of libstd, Miri needs to be
+// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
+// rustc itself never sets the feature, so this line has no affect there.
+#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
+// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies.
+#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))]
+//
 #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))]
 #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))]
 #![doc(

From 27b044433367d7305f71bcf67dd9b5be0a0bd65a Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Thu, 4 Aug 2022 09:01:00 -0400
Subject: [PATCH 18/26] add some Miri-only tests

---
 library/alloc/src/sync/tests.rs | 19 +++++++++++++++++++
 library/std/src/thread/tests.rs | 19 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs
index 202d0e7f02057..0fae8953aa2c7 100644
--- a/library/alloc/src/sync/tests.rs
+++ b/library/alloc/src/sync/tests.rs
@@ -618,3 +618,22 @@ fn test_arc_cyclic_two_refs() {
     assert_eq!(Arc::strong_count(&two_refs), 3);
     assert_eq!(Arc::weak_count(&two_refs), 2);
 }
+
+/// Test for Arc::drop bug (https://github.com/rust-lang/rust/issues/55005)
+#[test]
+#[cfg(miri)] // relies on Stacked Borrows in Miri
+fn arc_drop_dereferenceable_race() {
+    // The bug seems to take up to 700 iterations to reproduce with most seeds (tested 0-9).
+    for _ in 0..750 {
+        let arc_1 = Arc::new(());
+        let arc_2 = arc_1.clone();
+        let thread = thread::spawn(|| drop(arc_2));
+        // Spin a bit; makes the race more likely to appear
+        let mut i = 0;
+        while i < 256 {
+            i += 1;
+        }
+        drop(arc_1);
+        thread.join().unwrap();
+    }
+}
diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs
index ec68b52918802..130e47c8d44f0 100644
--- a/library/std/src/thread/tests.rs
+++ b/library/std/src/thread/tests.rs
@@ -329,3 +329,22 @@ fn test_scoped_threads_nll() {
     let x = 42_u8;
     foo(&x);
 }
+
+// Regression test for https://github.com/rust-lang/rust/issues/98498.
+#[test]
+#[cfg(miri)] // relies on Miri's data race detector
+fn scope_join_race() {
+    for _ in 0..100 {
+        let a_bool = AtomicBool::new(false);
+
+        thread::scope(|s| {
+            for _ in 0..5 {
+                s.spawn(|| a_bool.load(Ordering::Relaxed));
+            }
+
+            for _ in 0..5 {
+                s.spawn(|| a_bool.load(Ordering::Relaxed));
+            }
+        });
+    }
+}

From 8c8dc125b1bfca49ac8f064b365cb72a658e7cba Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Thu, 4 Aug 2022 09:16:42 -0400
Subject: [PATCH 19/26] make many std tests work in Miri

---
 library/std/src/collections/hash/map/tests.rs | 24 +++++++++++--------
 library/std/src/io/tests.rs                   |  3 ++-
 library/std/src/path/tests.rs                 |  5 ++++
 library/std/src/sync/mpsc/mpsc_queue/tests.rs |  2 +-
 library/std/src/sync/mpsc/spsc_queue/tests.rs |  5 ++--
 library/std/src/sync/mpsc/sync_tests.rs       | 21 +++++++++-------
 library/std/src/sync/mpsc/tests.rs            | 12 ++++++----
 library/std/src/sync/rwlock/tests.rs          |  2 +-
 library/std/src/time/tests.rs                 |  3 ++-
 9 files changed, 47 insertions(+), 30 deletions(-)

diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs
index 7ebc41588b3df..cb3032719fa64 100644
--- a/library/std/src/collections/hash/map/tests.rs
+++ b/library/std/src/collections/hash/map/tests.rs
@@ -268,10 +268,13 @@ fn test_lots_of_insertions() {
 
     // Try this a few times to make sure we never screw up the hashmap's
     // internal state.
-    for _ in 0..10 {
+    let loops = if cfg!(miri) { 2 } else { 10 };
+    for _ in 0..loops {
         assert!(m.is_empty());
 
-        for i in 1..1001 {
+        let count = if cfg!(miri) { 101 } else { 1001 };
+
+        for i in 1..count {
             assert!(m.insert(i, i).is_none());
 
             for j in 1..=i {
@@ -279,42 +282,42 @@ fn test_lots_of_insertions() {
                 assert_eq!(r, Some(&j));
             }
 
-            for j in i + 1..1001 {
+            for j in i + 1..count {
                 let r = m.get(&j);
                 assert_eq!(r, None);
             }
         }
 
-        for i in 1001..2001 {
+        for i in count..(2 * count) {
             assert!(!m.contains_key(&i));
         }
 
         // remove forwards
-        for i in 1..1001 {
+        for i in 1..count {
             assert!(m.remove(&i).is_some());
 
             for j in 1..=i {
                 assert!(!m.contains_key(&j));
             }
 
-            for j in i + 1..1001 {
+            for j in i + 1..count {
                 assert!(m.contains_key(&j));
             }
         }
 
-        for i in 1..1001 {
+        for i in 1..count {
             assert!(!m.contains_key(&i));
         }
 
-        for i in 1..1001 {
+        for i in 1..count {
             assert!(m.insert(i, i).is_none());
         }
 
         // remove backwards
-        for i in (1..1001).rev() {
+        for i in (1..count).rev() {
             assert!(m.remove(&i).is_some());
 
-            for j in i..1001 {
+            for j in i..count {
                 assert!(!m.contains_key(&j));
             }
 
@@ -817,6 +820,7 @@ fn test_retain() {
 }
 
 #[test]
+#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
 #[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
 fn test_try_reserve() {
     let mut empty_bytes: HashMap<u8, u8> = HashMap::new();
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index f357f33ec52c5..68a19eccc0e7c 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -94,7 +94,7 @@ fn read_to_end() {
     assert_eq!(c.read_to_end(&mut v).unwrap(), 1);
     assert_eq!(v, b"1");
 
-    let cap = 1024 * 1024;
+    let cap = if cfg!(miri) { 1024 } else { 1024 * 1024 };
     let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
     let mut v = Vec::new();
     let (a, b) = data.split_at(data.len() / 2);
@@ -309,6 +309,7 @@ fn chain_zero_length_read_is_not_eof() {
 
 #[bench]
 #[cfg_attr(target_os = "emscripten", ignore)]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
 fn bench_read_to_end(b: &mut test::Bencher) {
     b.iter(|| {
         let mut lr = repeat(1).take(10000000);
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index 351cf698810f1..dd307022c6d05 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -1768,6 +1768,7 @@ fn test_windows_absolute() {
 }
 
 #[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
 fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
     let prefix = "my/home";
     let mut paths: Vec<_> =
@@ -1781,6 +1782,7 @@ fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
 }
 
 #[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
 fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) {
     let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
     let paths: Vec<_> =
@@ -1799,6 +1801,7 @@ fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) {
 }
 
 #[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
 fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) {
     let prefix = "my/home";
     let paths: Vec<_> =
@@ -1817,6 +1820,7 @@ fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) {
 }
 
 #[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
 fn bench_path_hashset(b: &mut test::Bencher) {
     let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
     let paths: Vec<_> =
@@ -1835,6 +1839,7 @@ fn bench_path_hashset(b: &mut test::Bencher) {
 }
 
 #[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
 fn bench_path_hashset_miss(b: &mut test::Bencher) {
     let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
     let paths: Vec<_> =
diff --git a/library/std/src/sync/mpsc/mpsc_queue/tests.rs b/library/std/src/sync/mpsc/mpsc_queue/tests.rs
index 9f4f31ed05145..34b2a9a98ac36 100644
--- a/library/std/src/sync/mpsc/mpsc_queue/tests.rs
+++ b/library/std/src/sync/mpsc/mpsc_queue/tests.rs
@@ -13,7 +13,7 @@ fn test_full() {
 #[test]
 fn test() {
     let nthreads = 8;
-    let nmsgs = 1000;
+    let nmsgs = if cfg!(miri) { 100 } else { 1000 };
     let q = Queue::new();
     match q.pop() {
         Empty => {}
diff --git a/library/std/src/sync/mpsc/spsc_queue/tests.rs b/library/std/src/sync/mpsc/spsc_queue/tests.rs
index 467ef3dbdcbbd..eb6d5c2cf66d8 100644
--- a/library/std/src/sync/mpsc/spsc_queue/tests.rs
+++ b/library/std/src/sync/mpsc/spsc_queue/tests.rs
@@ -77,12 +77,13 @@ fn stress() {
     }
 
     unsafe fn stress_bound(bound: usize) {
+        let count = if cfg!(miri) { 1000 } else { 100000 };
         let q = Arc::new(Queue::with_additions(bound, (), ()));
 
         let (tx, rx) = channel();
         let q2 = q.clone();
         let _t = thread::spawn(move || {
-            for _ in 0..100000 {
+            for _ in 0..count {
                 loop {
                     match q2.pop() {
                         Some(1) => break,
@@ -93,7 +94,7 @@ fn stress() {
             }
             tx.send(()).unwrap();
         });
-        for _ in 0..100000 {
+        for _ in 0..count {
             q.push(1);
         }
         rx.recv().unwrap();
diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs
index e58649bab6e42..63c79436974d5 100644
--- a/library/std/src/sync/mpsc/sync_tests.rs
+++ b/library/std/src/sync/mpsc/sync_tests.rs
@@ -113,23 +113,25 @@ fn chan_gone_concurrent() {
 
 #[test]
 fn stress() {
+    let count = if cfg!(miri) { 100 } else { 10000 };
     let (tx, rx) = sync_channel::<i32>(0);
     thread::spawn(move || {
-        for _ in 0..10000 {
+        for _ in 0..count {
             tx.send(1).unwrap();
         }
     });
-    for _ in 0..10000 {
+    for _ in 0..count {
         assert_eq!(rx.recv().unwrap(), 1);
     }
 }
 
 #[test]
 fn stress_recv_timeout_two_threads() {
+    let count = if cfg!(miri) { 100 } else { 10000 };
     let (tx, rx) = sync_channel::<i32>(0);
 
     thread::spawn(move || {
-        for _ in 0..10000 {
+        for _ in 0..count {
             tx.send(1).unwrap();
         }
     });
@@ -146,12 +148,12 @@ fn stress_recv_timeout_two_threads() {
         }
     }
 
-    assert_eq!(recv_count, 10000);
+    assert_eq!(recv_count, count);
 }
 
 #[test]
 fn stress_recv_timeout_shared() {
-    const AMT: u32 = 1000;
+    const AMT: u32 = if cfg!(miri) { 100 } else { 1000 };
     const NTHREADS: u32 = 8;
     let (tx, rx) = sync_channel::<i32>(0);
     let (dtx, drx) = sync_channel::<()>(0);
@@ -191,7 +193,7 @@ fn stress_recv_timeout_shared() {
 
 #[test]
 fn stress_shared() {
-    const AMT: u32 = 1000;
+    const AMT: u32 = if cfg!(miri) { 100 } else { 1000 };
     const NTHREADS: u32 = 8;
     let (tx, rx) = sync_channel::<i32>(0);
     let (dtx, drx) = sync_channel::<()>(0);
@@ -438,12 +440,13 @@ fn stream_send_recv_stress() {
 
 #[test]
 fn recv_a_lot() {
+    let count = if cfg!(miri) { 1000 } else { 10000 };
     // Regression test that we don't run out of stack in scheduler context
-    let (tx, rx) = sync_channel(10000);
-    for _ in 0..10000 {
+    let (tx, rx) = sync_channel(count);
+    for _ in 0..count {
         tx.send(()).unwrap();
     }
-    for _ in 0..10000 {
+    for _ in 0..count {
         rx.recv().unwrap();
     }
 }
diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs
index 4deb3e5961577..f6d0796f604fa 100644
--- a/library/std/src/sync/mpsc/tests.rs
+++ b/library/std/src/sync/mpsc/tests.rs
@@ -120,13 +120,14 @@ fn chan_gone_concurrent() {
 
 #[test]
 fn stress() {
+    let count = if cfg!(miri) { 100 } else { 10000 };
     let (tx, rx) = channel::<i32>();
     let t = thread::spawn(move || {
-        for _ in 0..10000 {
+        for _ in 0..count {
             tx.send(1).unwrap();
         }
     });
-    for _ in 0..10000 {
+    for _ in 0..count {
         assert_eq!(rx.recv().unwrap(), 1);
     }
     t.join().ok().expect("thread panicked");
@@ -134,7 +135,7 @@ fn stress() {
 
 #[test]
 fn stress_shared() {
-    const AMT: u32 = 10000;
+    const AMT: u32 = if cfg!(miri) { 100 } else { 10000 };
     const NTHREADS: u32 = 8;
     let (tx, rx) = channel::<i32>();
 
@@ -504,12 +505,13 @@ fn very_long_recv_timeout_wont_panic() {
 
 #[test]
 fn recv_a_lot() {
+    let count = if cfg!(miri) { 1000 } else { 10000 };
     // Regression test that we don't run out of stack in scheduler context
     let (tx, rx) = channel();
-    for _ in 0..10000 {
+    for _ in 0..count {
         tx.send(()).unwrap();
     }
-    for _ in 0..10000 {
+    for _ in 0..count {
         rx.recv().unwrap();
     }
 }
diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs
index 08255c985f5eb..b5b3ad9898edb 100644
--- a/library/std/src/sync/rwlock/tests.rs
+++ b/library/std/src/sync/rwlock/tests.rs
@@ -19,7 +19,7 @@ fn smoke() {
 #[test]
 fn frob() {
     const N: u32 = 10;
-    const M: usize = 1000;
+    const M: usize = if cfg!(miri) { 100 } else { 1000 };
 
     let r = Arc::new(RwLock::new(()));
 
diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs
index d710a574465ce..6229556c85fee 100644
--- a/library/std/src/time/tests.rs
+++ b/library/std/src/time/tests.rs
@@ -31,7 +31,8 @@ fn instant_monotonic_concurrent() -> crate::thread::Result<()> {
         .map(|_| {
             crate::thread::spawn(|| {
                 let mut old = Instant::now();
-                for _ in 0..5_000_000 {
+                let count = if cfg!(miri) { 1_000 } else { 5_000_000 };
+                for _ in 0..count {
                     let new = Instant::now();
                     assert!(new >= old);
                     old = new;

From 438e49c1cba71d7ff8a138a1b96058c2d9a9d217 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Sat, 6 Aug 2022 14:46:30 -0400
Subject: [PATCH 20/26] silence some unused-fn warnings in miri std builds

---
 library/std/src/sys/unix/fs.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 7778033eaa98c..f38d2fd3d704e 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -829,6 +829,7 @@ impl DirEntry {
         target_os = "fuchsia",
         target_os = "redox"
     )))]
+    #[cfg_attr(miri, allow(unused))]
     fn name_cstr(&self) -> &CStr {
         unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
     }
@@ -840,6 +841,7 @@ impl DirEntry {
         target_os = "fuchsia",
         target_os = "redox"
     ))]
+    #[cfg_attr(miri, allow(unused))]
     fn name_cstr(&self) -> &CStr {
         &self.name
     }

From fbcdf2a383f6b17f80d8c285af3fa9739aaf223c Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Thu, 18 Aug 2022 18:07:35 -0400
Subject: [PATCH 21/26] clarify lib.rs attribute structure

---
 library/alloc/src/lib.rs |  9 ++++-----
 library/std/src/lib.rs   | 23 +++++++++++++----------
 2 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index a6f3593addc10..aea84ac90c1bc 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -56,11 +56,6 @@
 //! [`Rc`]: rc
 //! [`RefCell`]: core::cell
 
-// To run liballoc tests without x.py without ending up with two copies of liballoc, Miri needs to be
-// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
-// rustc itself never sets the feature, so this line has no affect there.
-#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
-//
 #![allow(unused_attributes)]
 #![stable(feature = "alloc", since = "1.36.0")]
 #![doc(
@@ -78,6 +73,10 @@
 ))]
 #![no_std]
 #![needs_allocator]
+// To run liballoc tests without x.py without ending up with two copies of liballoc, Miri needs to be
+// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
+// rustc itself never sets the feature, so this line has no affect there.
+#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
 //
 // Lints:
 #![deny(unsafe_op_in_unsafe_fn)]
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index bbd7041c50bd0..ead06f40d87c4 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -188,13 +188,6 @@
 //! [array]: prim@array
 //! [slice]: prim@slice
 
-// To run libstd tests without x.py without ending up with two copies of libstd, Miri needs to be
-// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
-// rustc itself never sets the feature, so this line has no affect there.
-#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
-// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies.
-#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))]
-//
 #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))]
 #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))]
 #![doc(
@@ -209,25 +202,35 @@
     no_global_oom_handling,
     not(no_global_oom_handling)
 ))]
+// To run libstd tests without x.py without ending up with two copies of libstd, Miri needs to be
+// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
+// rustc itself never sets the feature, so this line has no affect there.
+#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
+// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies.
+#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))]
 // Don't link to std. We are std.
 #![no_std]
+// Tell the compiler to link to either panic_abort or panic_unwind
+#![needs_panic_runtime]
+//
+// Lints:
 #![warn(deprecated_in_future)]
 #![warn(missing_docs)]
 #![warn(missing_debug_implementations)]
 #![allow(explicit_outlives_requirements)]
 #![allow(unused_lifetimes)]
-// Tell the compiler to link to either panic_abort or panic_unwind
-#![needs_panic_runtime]
+#![deny(rustc::existing_doc_keyword)]
 // Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind`
 #![deny(ffi_unwind_calls)]
 // std may use features in a platform-specific way
 #![allow(unused_features)]
+//
+// Features:
 #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))]
 #![cfg_attr(
     all(target_vendor = "fortanix", target_env = "sgx"),
     feature(slice_index_methods, coerce_unsized, sgx_platform)
 )]
-#![deny(rustc::existing_doc_keyword)]
 //
 // Language features:
 #![feature(alloc_error_handler)]

From 64b3e4af2031fb2faf3330ab01c27ae084412079 Mon Sep 17 00:00:00 2001
From: Takayuki Maeda <takoyaki0316@gmail.com>
Date: Sat, 20 Aug 2022 02:51:20 +0900
Subject: [PATCH 22/26] suggest adding a reference to a trait assoc item

---
 .../src/traits/error_reporting/suggestions.rs | 186 ++++++++++--------
 ...adding-reference-to-trait-assoc-item.fixed |  15 ++
 ...st-adding-reference-to-trait-assoc-item.rs |  15 ++
 ...dding-reference-to-trait-assoc-item.stderr |  25 +++
 4 files changed, 156 insertions(+), 85 deletions(-)
 create mode 100644 src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed
 create mode 100644 src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs
 create mode 100644 src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr

diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 219413121d812..28b3d886b50b5 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -882,6 +882,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             obligation.cause.code()
         {
             &parent_code
+        } else if let ObligationCauseCode::ItemObligation(_) = obligation.cause.code() {
+            obligation.cause.code()
         } else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) =
             span.ctxt().outer_expn_data().kind
         {
@@ -906,102 +908,116 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         let param_env = obligation.param_env;
 
         // Try to apply the original trait binding obligation by borrowing.
-        let mut try_borrowing =
-            |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool {
-                if blacklist.contains(&old_pred.def_id()) {
-                    return false;
-                }
-                // We map bounds to `&T` and `&mut T`
-                let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
-                    (
-                        trait_pred,
-                        self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
-                    )
-                });
-                let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
-                    (
-                        trait_pred,
-                        self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
-                    )
-                });
+        let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,
+                                 blacklist: &[DefId]|
+         -> bool {
+            if blacklist.contains(&old_pred.def_id()) {
+                return false;
+            }
+            // We map bounds to `&T` and `&mut T`
+            let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
+                (
+                    trait_pred,
+                    self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                )
+            });
+            let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
+                (
+                    trait_pred,
+                    self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                )
+            });
 
-                let mk_result = |trait_pred_and_new_ty| {
-                    let obligation =
-                        self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
-                    self.predicate_must_hold_modulo_regions(&obligation)
+            let mk_result = |trait_pred_and_new_ty| {
+                let obligation =
+                    self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
+                self.predicate_must_hold_modulo_regions(&obligation)
+            };
+            let imm_result = mk_result(trait_pred_and_imm_ref);
+            let mut_result = mk_result(trait_pred_and_mut_ref);
+
+            let ref_inner_ty_result =
+                if let ObligationCauseCode::ItemObligation(_) = obligation.cause.code()
+                    && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()
+                {
+                    Some((mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), mutability))
+                } else {
+                    None
                 };
-                let imm_result = mk_result(trait_pred_and_imm_ref);
-                let mut_result = mk_result(trait_pred_and_mut_ref);
-
-                if imm_result || mut_result {
-                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
-                        // We have a very specific type of error, where just borrowing this argument
-                        // might solve the problem. In cases like this, the important part is the
-                        // original type obligation, not the last one that failed, which is arbitrary.
-                        // Because of this, we modify the error to refer to the original obligation and
-                        // return early in the caller.
-
-                        let msg = format!("the trait bound `{}` is not satisfied", old_pred);
-                        if has_custom_message {
-                            err.note(&msg);
-                        } else {
-                            err.message =
-                                vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
-                        }
-                        if snippet.starts_with('&') {
-                            // This is already a literal borrow and the obligation is failing
-                            // somewhere else in the obligation chain. Do not suggest non-sense.
-                            return false;
-                        }
-                        err.span_label(
-                            span,
-                            &format!(
-                                "expected an implementor of trait `{}`",
-                                old_pred.print_modifiers_and_trait_path(),
-                            ),
-                        );
 
-                        // This if is to prevent a special edge-case
-                        if matches!(
-                            span.ctxt().outer_expn_data().kind,
-                            ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
-                        ) {
-                            // We don't want a borrowing suggestion on the fields in structs,
-                            // ```
-                            // struct Foo {
-                            //  the_foos: Vec<Foo>
-                            // }
-                            // ```
+            if imm_result || mut_result || ref_inner_ty_result.map_or(false, |(result, _)| result) {
+                if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                    // We have a very specific type of error, where just borrowing this argument
+                    // might solve the problem. In cases like this, the important part is the
+                    // original type obligation, not the last one that failed, which is arbitrary.
+                    // Because of this, we modify the error to refer to the original obligation and
+                    // return early in the caller.
+
+                    let msg = format!("the trait bound `{}` is not satisfied", old_pred);
+                    if has_custom_message {
+                        err.note(&msg);
+                    } else {
+                        err.message =
+                            vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
+                    }
+                    if snippet.starts_with('&') {
+                        // This is already a literal borrow and the obligation is failing
+                        // somewhere else in the obligation chain. Do not suggest non-sense.
+                        return false;
+                    }
+                    err.span_label(
+                        span,
+                        &format!(
+                            "expected an implementor of trait `{}`",
+                            old_pred.print_modifiers_and_trait_path(),
+                        ),
+                    );
 
-                            if imm_result && mut_result {
-                                err.span_suggestions(
-                                    span.shrink_to_lo(),
-                                    "consider borrowing here",
-                                    ["&".to_string(), "&mut ".to_string()].into_iter(),
-                                    Applicability::MaybeIncorrect,
-                                );
-                            } else {
-                                err.span_suggestion_verbose(
-                                    span.shrink_to_lo(),
-                                    &format!(
-                                        "consider{} borrowing here",
-                                        if mut_result { " mutably" } else { "" }
-                                    ),
-                                    format!("&{}", if mut_result { "mut " } else { "" }),
-                                    Applicability::MaybeIncorrect,
-                                );
-                            }
+                    // This if is to prevent a special edge-case
+                    if matches!(
+                        span.ctxt().outer_expn_data().kind,
+                        ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
+                    ) {
+                        // We don't want a borrowing suggestion on the fields in structs,
+                        // ```
+                        // struct Foo {
+                        //  the_foos: Vec<Foo>
+                        // }
+                        // ```
+
+                        if imm_result && mut_result {
+                            err.span_suggestions(
+                                span.shrink_to_lo(),
+                                "consider borrowing here",
+                                ["&".to_string(), "&mut ".to_string()].into_iter(),
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            let is_mut = mut_result
+                                || ref_inner_ty_result.map_or(false, |(_, mutabl)| {
+                                    matches!(mutabl, hir::Mutability::Mut)
+                                });
+                            err.span_suggestion_verbose(
+                                span.shrink_to_lo(),
+                                &format!(
+                                    "consider{} borrowing here",
+                                    if is_mut { " mutably" } else { "" }
+                                ),
+                                format!("&{}", if is_mut { "mut " } else { "" }),
+                                Applicability::MaybeIncorrect,
+                            );
                         }
-                        return true;
                     }
+                    return true;
                 }
-                return false;
-            };
+            }
+            return false;
+        };
 
         if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code {
             try_borrowing(cause.derived.parent_trait_pred, &[])
         } else if let ObligationCauseCode::BindingObligation(_, _)
-        | ObligationCauseCode::ItemObligation(_) = code
+        | ObligationCauseCode::ItemObligation(..) = code
         {
             try_borrowing(poly_trait_pred, &never_suggest_borrow)
         } else {
diff --git a/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed b/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed
new file mode 100644
index 0000000000000..e9b8a9caa484a
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+#![allow(unused_variables)]
+
+fn foo(foo: &mut usize) {
+    todo!()
+}
+
+fn bar(bar: &usize) {
+    todo!()
+}
+
+fn main() {
+    foo(&mut Default::default()); //~ the trait bound `&mut usize: Default` is not satisfied
+    bar(&Default::default()); //~ the trait bound `&usize: Default` is not satisfied
+}
diff --git a/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs b/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs
new file mode 100644
index 0000000000000..5fae21cccef23
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+#![allow(unused_variables)]
+
+fn foo(foo: &mut usize) {
+    todo!()
+}
+
+fn bar(bar: &usize) {
+    todo!()
+}
+
+fn main() {
+    foo(Default::default()); //~ the trait bound `&mut usize: Default` is not satisfied
+    bar(Default::default()); //~ the trait bound `&usize: Default` is not satisfied
+}
diff --git a/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr b/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr
new file mode 100644
index 0000000000000..b930d22a3915e
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr
@@ -0,0 +1,25 @@
+error[E0277]: the trait bound `&mut usize: Default` is not satisfied
+  --> $DIR/suggest-adding-reference-to-trait-assoc-item.rs:13:9
+   |
+LL |     foo(Default::default());
+   |         ^^^^^^^^^^^^^^^^ expected an implementor of trait `Default`
+   |
+help: consider mutably borrowing here
+   |
+LL |     foo(&mut Default::default());
+   |         ++++
+
+error[E0277]: the trait bound `&usize: Default` is not satisfied
+  --> $DIR/suggest-adding-reference-to-trait-assoc-item.rs:14:9
+   |
+LL |     bar(Default::default());
+   |         ^^^^^^^^^^^^^^^^ expected an implementor of trait `Default`
+   |
+help: consider borrowing here
+   |
+LL |     bar(&Default::default());
+   |         +
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.

From 59cc718e76faee1f844040d60615bacc6bad8643 Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Fri, 19 Aug 2022 16:15:15 -0400
Subject: [PATCH 23/26] Update codegen tests to accommodate the potential
 presence/absence of the extension operation depending on target architecture.

---
 src/test/codegen/abi-repr-ext.rs         | 46 ++++++++++++++++++++++--
 src/test/codegen/pic-relocation-model.rs |  5 ++-
 2 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/src/test/codegen/abi-repr-ext.rs b/src/test/codegen/abi-repr-ext.rs
index 2b34eaf94172b..23ade3c7216d3 100644
--- a/src/test/codegen/abi-repr-ext.rs
+++ b/src/test/codegen/abi-repr-ext.rs
@@ -1,6 +1,32 @@
 // compile-flags: -O
 
-#![crate_type="lib"]
+// revisions:x86_64 i686 aarch64-apple aarch64-windows aarch64-linux arm riscv
+
+//[x86_64] compile-flags: --target x86_64-unknown-uefi
+//[x86_64] needs-llvm-components: x86
+//[i686] compile-flags: --target i686-unknown-linux-musl
+//[i686] needs-llvm-components: x86
+//[aarch64-windows] compile-flags: --target aarch64-pc-windows-msvc
+//[aarch64-windows] needs-llvm-components: aarch64
+//[aarch64-linux] compile-flags: --target aarch64-unknown-linux-gnu
+//[aarch64-linux] needs-llvm-components: aarch64
+//[aarch64-apple] compile-flags: --target aarch64-apple-darwin
+//[aarch64-apple] needs-llvm-components: aarch64
+//[arm] compile-flags: --target armv7r-none-eabi
+//[arm] needs-llvm-components: arm
+//[riscv] compile-flags: --target riscv64gc-unknown-none-elf
+//[riscv] needs-llvm-components: riscv
+
+// See bottom of file for a corresponding C source file that is meant to yield
+// equivalent declarations.
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_std]
+#![no_core]
+
+#[lang="sized"] trait Sized { }
+#[lang="freeze"] trait Freeze { }
+#[lang="copy"] trait Copy { }
 
 #[repr(i8)]
 pub enum Type {
@@ -8,7 +34,23 @@ pub enum Type {
     Type2 = 1
 }
 
-// CHECK: define{{( dso_local)?}} noundef signext i8 @test()
+// To accommodate rust#97800, one might consider writing the below as:
+//
+// `define{{( dso_local)?}} noundef{{( signext)?}} i8 @test()`
+//
+// but based on rust#80556, it seems important to actually check for the
+// presence of the `signext` for those targets where we expect it.
+
+// CHECK: define{{( dso_local)?}} noundef
+// x86_64-SAME:                 signext
+// aarch64-apple-SAME:          signext
+// aarch64-windows-NOT: signext
+// aarch64-linux-NOT:   signext
+// arm-SAME:                    signext
+// riscv-SAME:                  signext
+// CHECK-SAME: i8 @test()
+
+
 #[no_mangle]
 pub extern "C" fn test() -> Type {
     Type::Type1
diff --git a/src/test/codegen/pic-relocation-model.rs b/src/test/codegen/pic-relocation-model.rs
index 6e1d5a6c3f271..9b378ecb4e590 100644
--- a/src/test/codegen/pic-relocation-model.rs
+++ b/src/test/codegen/pic-relocation-model.rs
@@ -10,7 +10,10 @@ pub fn call_foreign_fn() -> u8 {
     }
 }
 
-// CHECK: declare zeroext i8 @foreign_fn()
+// (Allow but do not require `zeroext` here, because it is not worth effort to
+// spell out which targets have it and which ones do not; see rust#97800.)
+
+// CHECK: declare{{( zeroext)?}} i8 @foreign_fn()
 extern "C" {fn foreign_fn() -> u8;}
 
 // CHECK: !{i32 7, !"PIC Level", i32 2}

From f47b61d19e1c17bac9fff34e9b304bad4f132ffe Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Fri, 19 Aug 2022 16:40:26 -0400
Subject: [PATCH 24/26] elaborate how revisions work with FileCheck stuff in
 src/test/codegen

---
 src/test/codegen/README.md | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/test/codegen/README.md b/src/test/codegen/README.md
index 00de55eeab1f4..8f2daaafcc77a 100644
--- a/src/test/codegen/README.md
+++ b/src/test/codegen/README.md
@@ -1,2 +1,24 @@
 The files here use the LLVM FileCheck framework, documented at
 <https://llvm.org/docs/CommandGuide/FileCheck.html>.
+
+One extension worth noting is the use of revisions as custom prefixes for
+FileCheck. If your codegen test has different behavior based on the chosen
+target or different compiler flags that you want to exercise, you can use a
+revisions annotation, like so:
+
+```rust
+// revisions: aaa bbb
+// [bbb] compile-flags: --flags-for-bbb
+```
+
+After specifying those variations, you can write different expected, or
+explicitly *unexpected* output by using `<prefix>-SAME:` and `<prefix>-NOT:`,
+like so:
+
+```rust
+// CHECK: expected code
+// aaa-SAME: emitted-only-for-aaa
+// aaa-NOT:                        emitted-only-for-bbb
+// bbb-NOT:  emitted-only-for-aaa
+// bbb-SAME:                       emitted-only-for-bbb
+```

From 973510749d955d72a8bf6f27775df3a5f8d652f2 Mon Sep 17 00:00:00 2001
From: Takayuki Maeda <takoyaki0316@gmail.com>
Date: Sat, 20 Aug 2022 15:54:39 +0900
Subject: [PATCH 25/26] remove unnecessary string searchings

remove unnecessary string searchings for checking if function arguments have `&` and `&mut`
---
 .../src/traits/error_reporting/suggestions.rs | 31 ++++++++++---------
 .../suggest-deferences/issue-39029.stderr     | 10 +++---
 .../suggest-deferences/issue-62530.stderr     | 10 +++---
 .../suggest-deferences/multiple-0.stderr      | 10 +++---
 4 files changed, 35 insertions(+), 26 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 219413121d812..168834159e705 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -671,7 +671,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         // It only make sense when suggesting dereferences for arguments
-        let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else {
+        let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() else {
             return false;
         };
         let param_env = obligation.param_env;
@@ -702,19 +702,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     Some(steps).filter(|_| self.predicate_may_hold(&obligation))
                 }) {
                     if steps > 0 {
-                        if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
-                            // Don't care about `&mut` because `DerefMut` is used less
-                            // often and user will not expect autoderef happens.
-                            if src.starts_with('&') && !src.starts_with("&mut ") {
-                                let derefs = "*".repeat(steps);
-                                err.span_suggestion(
-                                    span,
-                                    "consider dereferencing here",
-                                    format!("&{}{}", derefs, &src[1..]),
-                                    Applicability::MachineApplicable,
-                                );
-                                return true;
-                            }
+                        // Don't care about `&mut` because `DerefMut` is used less
+                        // often and user will not expect autoderef happens.
+                        if let Some(hir::Node::Expr(hir::Expr {
+                            kind:
+                                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr),
+                            ..
+                        })) = self.tcx.hir().find(*arg_hir_id)
+                        {
+                            let derefs = "*".repeat(steps);
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_lo(),
+                                "consider dereferencing here",
+                                derefs,
+                                Applicability::MachineApplicable,
+                            );
+                            return true;
                         }
                     }
                 } else if real_trait_pred != trait_pred {
diff --git a/src/test/ui/traits/suggest-deferences/issue-39029.stderr b/src/test/ui/traits/suggest-deferences/issue-39029.stderr
index 5c324cd38a3ab..d256431c4cca8 100644
--- a/src/test/ui/traits/suggest-deferences/issue-39029.stderr
+++ b/src/test/ui/traits/suggest-deferences/issue-39029.stderr
@@ -2,10 +2,8 @@ error[E0277]: the trait bound `NoToSocketAddrs: ToSocketAddrs` is not satisfied
   --> $DIR/issue-39029.rs:16:37
    |
 LL |     let _errors = TcpListener::bind(&bad);
-   |                   ----------------- ^^^^
-   |                   |                 |
-   |                   |                 the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
-   |                   |                 help: consider dereferencing here: `&*bad`
+   |                   ----------------- ^^^^ the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
+   |                   |
    |                   required by a bound introduced by this call
    |
    = note: required because of the requirements on the impl of `ToSocketAddrs` for `&NoToSocketAddrs`
@@ -14,6 +12,10 @@ note: required by a bound in `TcpListener::bind`
    |
 LL |     pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
    |                    ^^^^^^^^^^^^^ required by this bound in `TcpListener::bind`
+help: consider dereferencing here
+   |
+LL |     let _errors = TcpListener::bind(&*bad);
+   |                                      +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/traits/suggest-deferences/issue-62530.stderr b/src/test/ui/traits/suggest-deferences/issue-62530.stderr
index d129328dae8ef..e47ae0b65af0e 100644
--- a/src/test/ui/traits/suggest-deferences/issue-62530.stderr
+++ b/src/test/ui/traits/suggest-deferences/issue-62530.stderr
@@ -2,10 +2,8 @@ error[E0277]: the trait bound `&String: SomeTrait` is not satisfied
   --> $DIR/issue-62530.rs:13:26
    |
 LL |     takes_type_parameter(&string);  // Error
-   |     -------------------- ^^^^^^^
-   |     |                    |
-   |     |                    the trait `SomeTrait` is not implemented for `&String`
-   |     |                    help: consider dereferencing here: `&*string`
+   |     -------------------- ^^^^^^^ the trait `SomeTrait` is not implemented for `&String`
+   |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `takes_type_parameter`
@@ -13,6 +11,10 @@ note: required by a bound in `takes_type_parameter`
    |
 LL | fn takes_type_parameter<T>(_x: T) where T: SomeTrait {}
    |                                            ^^^^^^^^^ required by this bound in `takes_type_parameter`
+help: consider dereferencing here
+   |
+LL |     takes_type_parameter(&*string);  // Error
+   |                           +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/traits/suggest-deferences/multiple-0.stderr b/src/test/ui/traits/suggest-deferences/multiple-0.stderr
index efb3c7d123f70..6a4d4b8d5212f 100644
--- a/src/test/ui/traits/suggest-deferences/multiple-0.stderr
+++ b/src/test/ui/traits/suggest-deferences/multiple-0.stderr
@@ -2,10 +2,8 @@ error[E0277]: the trait bound `&Baz: Happy` is not satisfied
   --> $DIR/multiple-0.rs:34:9
    |
 LL |     foo(&baz);
-   |     --- ^^^^
-   |     |   |
-   |     |   the trait `Happy` is not implemented for `&Baz`
-   |     |   help: consider dereferencing here: `&***baz`
+   |     --- ^^^^ the trait `Happy` is not implemented for `&Baz`
+   |     |
    |     required by a bound introduced by this call
    |
 note: required by a bound in `foo`
@@ -13,6 +11,10 @@ note: required by a bound in `foo`
    |
 LL | fn foo<T>(_: T) where T: Happy {}
    |                          ^^^^^ required by this bound in `foo`
+help: consider dereferencing here
+   |
+LL |     foo(&***baz);
+   |          +++
 
 error: aborting due to previous error
 

From a311b8a4c5ed3dfbf80a94c48768fda2e6785e52 Mon Sep 17 00:00:00 2001
From: Takayuki Maeda <takoyaki0316@gmail.com>
Date: Sat, 20 Aug 2022 19:35:17 +0900
Subject: [PATCH 26/26] use more descriptive names

---
 .../src/traits/error_reporting/suggestions.rs | 188 +++++++++---------
 1 file changed, 95 insertions(+), 93 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 28b3d886b50b5..ffc4f8cf3d3b3 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -908,111 +908,113 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         let param_env = obligation.param_env;
 
         // Try to apply the original trait binding obligation by borrowing.
-        let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,
-                                 blacklist: &[DefId]|
-         -> bool {
-            if blacklist.contains(&old_pred.def_id()) {
-                return false;
-            }
-            // We map bounds to `&T` and `&mut T`
-            let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
-                (
-                    trait_pred,
-                    self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
-                )
-            });
-            let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
-                (
-                    trait_pred,
-                    self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
-                )
-            });
+        let mut try_borrowing =
+            |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool {
+                if blacklist.contains(&old_pred.def_id()) {
+                    return false;
+                }
+                // We map bounds to `&T` and `&mut T`
+                let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
+                    (
+                        trait_pred,
+                        self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                    )
+                });
+                let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
+                    (
+                        trait_pred,
+                        self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                    )
+                });
 
-            let mk_result = |trait_pred_and_new_ty| {
-                let obligation =
-                    self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
-                self.predicate_must_hold_modulo_regions(&obligation)
-            };
-            let imm_result = mk_result(trait_pred_and_imm_ref);
-            let mut_result = mk_result(trait_pred_and_mut_ref);
+                let mk_result = |trait_pred_and_new_ty| {
+                    let obligation =
+                        self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
+                    self.predicate_must_hold_modulo_regions(&obligation)
+                };
+                let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);
+                let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);
 
-            let ref_inner_ty_result =
+                let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) =
                 if let ObligationCauseCode::ItemObligation(_) = obligation.cause.code()
                     && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()
                 {
-                    Some((mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), mutability))
+                    (
+                        mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))),
+                        matches!(mutability, hir::Mutability::Mut),
+                    )
                 } else {
-                    None
+                    (false, false)
                 };
 
-            if imm_result || mut_result || ref_inner_ty_result.map_or(false, |(result, _)| result) {
-                if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
-                    // We have a very specific type of error, where just borrowing this argument
-                    // might solve the problem. In cases like this, the important part is the
-                    // original type obligation, not the last one that failed, which is arbitrary.
-                    // Because of this, we modify the error to refer to the original obligation and
-                    // return early in the caller.
-
-                    let msg = format!("the trait bound `{}` is not satisfied", old_pred);
-                    if has_custom_message {
-                        err.note(&msg);
-                    } else {
-                        err.message =
-                            vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
-                    }
-                    if snippet.starts_with('&') {
-                        // This is already a literal borrow and the obligation is failing
-                        // somewhere else in the obligation chain. Do not suggest non-sense.
-                        return false;
-                    }
-                    err.span_label(
-                        span,
-                        &format!(
-                            "expected an implementor of trait `{}`",
-                            old_pred.print_modifiers_and_trait_path(),
-                        ),
-                    );
-
-                    // This if is to prevent a special edge-case
-                    if matches!(
-                        span.ctxt().outer_expn_data().kind,
-                        ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
-                    ) {
-                        // We don't want a borrowing suggestion on the fields in structs,
-                        // ```
-                        // struct Foo {
-                        //  the_foos: Vec<Foo>
-                        // }
-                        // ```
-
-                        if imm_result && mut_result {
-                            err.span_suggestions(
-                                span.shrink_to_lo(),
-                                "consider borrowing here",
-                                ["&".to_string(), "&mut ".to_string()].into_iter(),
-                                Applicability::MaybeIncorrect,
-                            );
+                if imm_ref_self_ty_satisfies_pred
+                    || mut_ref_self_ty_satisfies_pred
+                    || ref_inner_ty_satisfies_pred
+                {
+                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                        // We have a very specific type of error, where just borrowing this argument
+                        // might solve the problem. In cases like this, the important part is the
+                        // original type obligation, not the last one that failed, which is arbitrary.
+                        // Because of this, we modify the error to refer to the original obligation and
+                        // return early in the caller.
+
+                        let msg = format!("the trait bound `{}` is not satisfied", old_pred);
+                        if has_custom_message {
+                            err.note(&msg);
                         } else {
-                            let is_mut = mut_result
-                                || ref_inner_ty_result.map_or(false, |(_, mutabl)| {
-                                    matches!(mutabl, hir::Mutability::Mut)
-                                });
-                            err.span_suggestion_verbose(
-                                span.shrink_to_lo(),
-                                &format!(
-                                    "consider{} borrowing here",
-                                    if is_mut { " mutably" } else { "" }
-                                ),
-                                format!("&{}", if is_mut { "mut " } else { "" }),
-                                Applicability::MaybeIncorrect,
-                            );
+                            err.message =
+                                vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
                         }
+                        if snippet.starts_with('&') {
+                            // This is already a literal borrow and the obligation is failing
+                            // somewhere else in the obligation chain. Do not suggest non-sense.
+                            return false;
+                        }
+                        err.span_label(
+                            span,
+                            &format!(
+                                "expected an implementor of trait `{}`",
+                                old_pred.print_modifiers_and_trait_path(),
+                            ),
+                        );
+
+                        // This if is to prevent a special edge-case
+                        if matches!(
+                            span.ctxt().outer_expn_data().kind,
+                            ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
+                        ) {
+                            // We don't want a borrowing suggestion on the fields in structs,
+                            // ```
+                            // struct Foo {
+                            //  the_foos: Vec<Foo>
+                            // }
+                            // ```
+
+                            if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred {
+                                err.span_suggestions(
+                                    span.shrink_to_lo(),
+                                    "consider borrowing here",
+                                    ["&".to_string(), "&mut ".to_string()].into_iter(),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            } else {
+                                let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
+                                err.span_suggestion_verbose(
+                                    span.shrink_to_lo(),
+                                    &format!(
+                                        "consider{} borrowing here",
+                                        if is_mut { " mutably" } else { "" }
+                                    ),
+                                    format!("&{}", if is_mut { "mut " } else { "" }),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                        }
+                        return true;
                     }
-                    return true;
                 }
-            }
-            return false;
-        };
+                return false;
+            };
 
         if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code {
             try_borrowing(cause.derived.parent_trait_pred, &[])