From 3fc35b5b935e390c61ea2bbf744838b2632b2df1 Mon Sep 17 00:00:00 2001
From: BlackHoleFox <blackholefoxdev@gmail.com>
Date: Thu, 25 Aug 2022 15:43:59 -0500
Subject: [PATCH 01/24] Use getentropy when possible on all Apple platforms

---
 library/std/src/sys/unix/rand.rs | 94 +++++++++++++++++++-------------
 1 file changed, 56 insertions(+), 38 deletions(-)

diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index a6fe07873d7ee..40885417308b8 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -137,11 +137,9 @@ mod imp {
     }
 }
 
-#[cfg(target_os = "macos")]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
 mod imp {
-    use crate::fs::File;
-    use crate::io::Read;
-    use crate::sys::os::errno;
+    use crate::io;
     use crate::sys::weak::weak;
     use libc::{c_int, c_void, size_t};
 
@@ -155,7 +153,7 @@ mod imp {
                 for s in v.chunks_mut(256) {
                     let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
                     if ret == -1 {
-                        panic!("unexpected getentropy error: {}", errno());
+                        panic!("unexpected getentropy error: {}", io::Error::last_os_error());
                     }
                 }
                 true
@@ -163,14 +161,64 @@ mod imp {
             .unwrap_or(false)
     }
 
+    #[cfg(target_os = "macos")]
+    fn fallback_fill_bytes(v: &mut [u8]) {
+        use crate::fs::File;
+        use crate::io::Read;
+
+        let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
+        file.read_exact(v).expect("failed to read /dev/urandom")
+    }
+
+    // On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
+    // `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
+    // from `/dev/random` and which runs on its own thread accessed via GCD.
+    //
+    // This is very heavyweight compared to the alternatives, but they may not be usable:
+    // - `getentropy` was added in iOS 10, but we support a minimum of iOS 7
+    // - `/dev/urandom` is not accessible inside the iOS app sandbox.
+    //
+    // Therefore `SecRandomCopyBytes` is only used on older iOS versions where no
+    // better options are present.
+    #[cfg(target_os = "ios")]
+    fn fallback_fill_bytes(v: &mut [u8]) {
+        use crate::ptr;
+
+        enum SecRandom {}
+
+        #[allow(non_upper_case_globals)]
+        const kSecRandomDefault: *const SecRandom = ptr::null();
+
+        extern "C" {
+            fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
+        }
+
+        let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
+        if ret == -1 {
+            panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+        }
+    }
+
+    // All supported versions of watchOS (>= 5) have support for `getentropy`.
+    #[cfg(target_os = "watchos")]
+    #[cold]
+    fn fallback_fill_bytes(_: &mut [u8]) {
+        unreachable!()
+    }
+
     pub fn fill_bytes(v: &mut [u8]) {
         if getentropy_fill_bytes(v) {
             return;
         }
 
-        // for older macos which doesn't support getentropy
-        let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
-        file.read_exact(v).expect("failed to read /dev/urandom")
+        // Older macOS versions (< 10.12) don't support `getentropy`. Fallback to
+        // reading from `/dev/urandom` on these systems.
+        //
+        // Older iOS versions (< 10) don't support it either. Fallback to
+        // `SecRandomCopyBytes` on these systems. On watchOS, this is unreachable
+        // because the minimum supported version is 5 while `getentropy` became accessible
+        // in 3.
+        fallback_fill_bytes(v)
     }
 }
 
@@ -189,36 +237,6 @@ mod imp {
     }
 }
 
-// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
-// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
-// from `/dev/random` and which runs on its own thread accessed via GCD.
-// This seems needlessly heavyweight for the purposes of generating two u64s
-// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
-// only used on iOS where direct access to `/dev/urandom` is blocked by the
-// sandbox.
-#[cfg(any(target_os = "ios", target_os = "watchos"))]
-mod imp {
-    use crate::io;
-    use crate::ptr;
-    use libc::{c_int, size_t};
-
-    enum SecRandom {}
-
-    #[allow(non_upper_case_globals)]
-    const kSecRandomDefault: *const SecRandom = ptr::null();
-
-    extern "C" {
-        fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
-    }
-
-    pub fn fill_bytes(v: &mut [u8]) {
-        let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
-        if ret == -1 {
-            panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
-        }
-    }
-}
-
 #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
 mod imp {
     use crate::ptr;

From 908ac84662fa0e9478f3e44892146c8d1ffafa50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Marie?= <semarie@online.fr>
Date: Fri, 26 Aug 2022 06:15:54 +0000
Subject: [PATCH 02/24] openbsd: rustc_target: reorder spec by name

---
 compiler/rustc_target/src/spec/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index cf6cb75d49a50..46fea26283636 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -893,9 +893,9 @@ supported_targets! {
 
     ("aarch64-unknown-openbsd", aarch64_unknown_openbsd),
     ("i686-unknown-openbsd", i686_unknown_openbsd),
+    ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
     ("sparc64-unknown-openbsd", sparc64_unknown_openbsd),
     ("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
-    ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
 
     ("aarch64-unknown-netbsd", aarch64_unknown_netbsd),
     ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf),

From e89d4fcc7d99bcf76464d9c0994d54d12e00dfb7 Mon Sep 17 00:00:00 2001
From: Jean CASPAR <55629512+JeanCASPAR@users.noreply.github.com>
Date: Fri, 26 Aug 2022 18:03:41 +0200
Subject: [PATCH 03/24] remove span_fatal from ast_lowering

---
 compiler/rustc_ast_lowering/src/errors.rs                  | 7 +++++++
 compiler/rustc_ast_lowering/src/expr.rs                    | 6 +++---
 compiler/rustc_ast_lowering/src/lib.rs                     | 2 ++
 .../rustc_error_messages/locales/en-US/ast_lowering.ftl    | 2 ++
 4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 59f1b7180e4f4..4adeaef9bbfa8 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -327,3 +327,10 @@ pub struct ArbitraryExpressionInPattern {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::inclusive_range_with_no_end)]
+pub struct InclusiveRangeWithNoEnd {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 61f8c0216f1cf..6d8ce62d91327 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1,8 +1,8 @@
 use super::errors::{
     AsyncGeneratorsNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
     BaseExpressionDoubleDot, ClosureCannotBeStatic, FunctionalRecordUpdateDestructuringAssignemnt,
-    GeneratorTooManyParameters, NotSupportedForLifetimeBinderAsyncClosure, RustcBoxAttributeError,
-    UnderscoreExprLhsAssign,
+    GeneratorTooManyParameters, InclusiveRangeWithNoEnd, NotSupportedForLifetimeBinderAsyncClosure,
+    RustcBoxAttributeError, UnderscoreExprLhsAssign,
 };
 use super::ResolverAstLoweringExt;
 use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
@@ -1264,7 +1264,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             (Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
             (None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
             (Some(..), Some(..), Closed) => unreachable!(),
-            (_, None, Closed) => self.diagnostic().span_fatal(span, "inclusive range with no end"),
+            (_, None, Closed) => self.tcx.sess.emit_fatal(InclusiveRangeWithNoEnd { span }),
         };
 
         let fields = self.arena.alloc_from_iter(
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index becb67d116546..f7a204ce663c1 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -35,6 +35,8 @@
 #![feature(never_type)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate tracing;
diff --git a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
index dcb1e2b08306f..f2790531aba44 100644
--- a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
@@ -129,3 +129,5 @@ ast_lowering_not_supported_for_lifetime_binder_async_closure =
 
 ast_lowering_arbitrary_expression_in_pattern =
     arbitrary expressions aren't allowed in patterns
+
+ast_lowering_inclusive_range_with_no_end = inclusive range with no end

From dca5f5bf8f55faaab84fb23db1b4c5ebdc404c0c Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 27 Aug 2022 20:56:14 +0000
Subject: [PATCH 04/24] Drive-by: Rename expr_t to base_ty

---
 compiler/rustc_typeck/src/check/expr.rs | 52 ++++++++++++-------------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 71ae54bedce46..604ba22a1236f 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -2141,15 +2141,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         field: Ident,
     ) -> Ty<'tcx> {
         debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
-        let expr_t = self.check_expr(base);
-        let expr_t = self.structurally_resolved_type(base.span, expr_t);
+        let base_ty = self.check_expr(base);
+        let base_ty = self.structurally_resolved_type(base.span, base_ty);
         let mut private_candidate = None;
-        let mut autoderef = self.autoderef(expr.span, expr_t);
-        while let Some((base_t, _)) = autoderef.next() {
-            debug!("base_t: {:?}", base_t);
-            match base_t.kind() {
+        let mut autoderef = self.autoderef(expr.span, base_ty);
+        while let Some((deref_base_ty, _)) = autoderef.next() {
+            debug!("deref_base_ty: {:?}", deref_base_ty);
+            match deref_base_ty.kind() {
                 ty::Adt(base_def, substs) if !base_def.is_enum() => {
-                    debug!("struct named {:?}", base_t);
+                    debug!("struct named {:?}", deref_base_ty);
                     let (ident, def_scope) =
                         self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
                     let fields = &base_def.non_enum_variant().fields;
@@ -2197,23 +2197,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // (#90483) apply adjustments to avoid ExprUseVisitor from
             // creating erroneous projection.
             self.apply_adjustments(base, adjustments);
-            self.ban_private_field_access(expr, expr_t, field, did);
+            self.ban_private_field_access(expr, base_ty, field, did);
             return field_ty;
         }
 
         if field.name == kw::Empty {
-        } else if self.method_exists(field, expr_t, expr.hir_id, true) {
-            self.ban_take_value_of_method(expr, expr_t, field);
-        } else if !expr_t.is_primitive_ty() {
-            self.ban_nonexisting_field(field, base, expr, expr_t);
+        } else if self.method_exists(field, base_ty, expr.hir_id, true) {
+            self.ban_take_value_of_method(expr, base_ty, field);
+        } else if !base_ty.is_primitive_ty() {
+            self.ban_nonexisting_field(field, base, expr, base_ty);
         } else {
             let field_name = field.to_string();
             let mut err = type_error_struct!(
                 self.tcx().sess,
                 field.span,
-                expr_t,
+                base_ty,
                 E0610,
-                "`{expr_t}` is a primitive type and therefore doesn't have fields",
+                "`{base_ty}` is a primitive type and therefore doesn't have fields",
             );
             let is_valid_suffix = |field: &str| {
                 if field == "f32" || field == "f64" {
@@ -2251,7 +2251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     None
                 }
             };
-            if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
+            if let ty::Infer(ty::IntVar(_)) = base_ty.kind()
                 && let ExprKind::Lit(Spanned {
                     node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
                     ..
@@ -2351,32 +2351,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn ban_nonexisting_field(
         &self,
-        field: Ident,
+        ident: Ident,
         base: &'tcx hir::Expr<'tcx>,
         expr: &'tcx hir::Expr<'tcx>,
-        expr_t: Ty<'tcx>,
+        base_ty: Ty<'tcx>,
     ) {
         debug!(
-            "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
-            field, base, expr, expr_t
+            "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
+            ident, base, expr, base_ty
         );
-        let mut err = self.no_such_field_err(field, expr_t, base.hir_id);
+        let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
 
-        match *expr_t.peel_refs().kind() {
+        match *base_ty.peel_refs().kind() {
             ty::Array(_, len) => {
-                self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
+                self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len);
             }
             ty::RawPtr(..) => {
-                self.suggest_first_deref_field(&mut err, expr, base, field);
+                self.suggest_first_deref_field(&mut err, expr, base, ident);
             }
             ty::Adt(def, _) if !def.is_enum() => {
-                self.suggest_fields_on_recordish(&mut err, def, field, expr.span);
+                self.suggest_fields_on_recordish(&mut err, def, ident, expr.span);
             }
             ty::Param(param_ty) => {
                 self.point_at_param_definition(&mut err, param_ty);
             }
             ty::Opaque(_, _) => {
-                self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
+                self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
             }
             ty::FnDef(def_id, _) => {
                 self.check_call_constructor(&mut err, base, def_id);
@@ -2384,7 +2384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => {}
         }
 
-        if field.name == kw::Await {
+        if ident.name == kw::Await {
             // We know by construction that `<expr>.await` is either on Rust 2015
             // or results in `ExprKind::Await`. Suggest switching the edition to 2018.
             err.note("to `.await` a `Future`, switch to Rust 2018 or later");

From 0734200e801d52662f02aff7fdd5026337798620 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 27 Aug 2022 20:53:04 +0000
Subject: [PATCH 05/24] Use autoderef

---
 .../src/check/fn_ctxt/suggestions.rs          | 94 ++++++++++---------
 src/test/ui/suggestions/call-boxed.rs         |  7 ++
 src/test/ui/suggestions/call-boxed.stderr     | 20 ++++
 3 files changed, 78 insertions(+), 43 deletions(-)
 create mode 100644 src/test/ui/suggestions/call-boxed.rs
 create mode 100644 src/test/ui/suggestions/call-boxed.stderr

diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 57771e0969bac..45872a0b07aeb 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -61,55 +61,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pointing_at_return_type
     }
 
-    /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
-    /// the ctor would successfully solve the type mismatch and if so, suggest it:
+    /// When encountering an fn-like type, try accessing the output of the type
+    /// // and suggesting calling it if it satisfies a predicate (i.e. if the
+    /// output has a method or a field):
     /// ```compile_fail,E0308
     /// fn foo(x: usize) -> usize { x }
     /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
     /// ```
-    fn suggest_fn_call(
+    pub(crate) fn suggest_fn_call(
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
         found: Ty<'tcx>,
+        can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
     ) -> bool {
-        let (def_id, output, inputs) = match *found.kind() {
-            ty::FnDef(def_id, _) => {
-                let fn_sig = found.fn_sig(self.tcx);
-                (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
-            }
-            ty::Closure(def_id, substs) => {
-                let fn_sig = substs.as_closure().sig();
-                (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
-            }
-            ty::Opaque(def_id, substs) => {
-                let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
-                    if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
-                    && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
-                    // args tuple will always be substs[1]
-                    && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
-                    {
-                        Some((
-                            pred.kind().rebind(proj.term.ty().unwrap()),
-                            args.len(),
-                        ))
+        // Autoderef is useful here because sometimes we box callables, etc.
+        let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
+            match *found.kind() {
+                ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())),
+                ty::FnDef(def_id, _) => {
+                    let fn_sig = found.fn_sig(self.tcx);
+                    Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
+                }
+                ty::Closure(def_id, substs) => {
+                    let fn_sig = substs.as_closure().sig();
+                    Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
+                }
+                ty::Opaque(def_id, substs) => {
+                    let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+                        if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+                        && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
+                        // args tuple will always be substs[1]
+                        && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+                        {
+                            Some((
+                                pred.kind().rebind(proj.term.ty().unwrap()),
+                                args.len(),
+                            ))
+                        } else {
+                            None
+                        }
+                    });
+                    if let Some((output, inputs)) = sig {
+                        Some((Some(def_id), output, inputs))
                     } else {
                         None
                     }
-                });
-                if let Some((output, inputs)) = sig {
-                    (def_id, output, inputs)
-                } else {
-                    return false;
                 }
+                _ => None,
             }
-            _ => return false,
-        };
+        }) else { return false; };
 
         let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
-        let output = self.normalize_associated_types_in(expr.span, output);
-        if !output.is_ty_var() && self.can_coerce(output, expected) {
+        // We don't want to register any extra obligations, which should be
+        // implied by wf, but also because that would possibly result in
+        // erroneous errors later on.
+        let infer::InferOk { value: output, obligations: _ } =
+            self.normalize_associated_types_in_as_infer_ok(expr.span, output);
+        if !output.is_ty_var() && can_satisfy(output) {
             let (sugg_call, mut applicability) = match inputs {
                 0 => ("".to_string(), Applicability::MachineApplicable),
                 1..=4 => (
@@ -119,11 +128,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => ("...".to_string(), Applicability::HasPlaceholders),
             };
 
-            let msg = match self.tcx.def_kind(def_id) {
-                DefKind::Fn => "call this function",
-                DefKind::Closure | DefKind::OpaqueTy => "call this closure",
-                DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
-                DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
+            let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) {
+                Some(DefKind::Fn) => "call this function",
+                Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure",
+                Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct",
+                Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant",
                 _ => "call this function",
             };
 
@@ -178,12 +187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 err.span_suggestion(sp, &msg, suggestion, applicability);
             }
-        } else if let (ty::FnDef(def_id, ..), true) =
-            (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
+        } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
+            && let ty::FnDef(def_id, ..) = &found.kind()
+            && let Some(sp) = self.tcx.hir().span_if_local(*def_id)
         {
-            if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
-                err.span_label(sp, format!("{found} defined here"));
-            }
+            err.span_label(sp, format!("{found} defined here"));
         } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
             if !methods.is_empty() {
diff --git a/src/test/ui/suggestions/call-boxed.rs b/src/test/ui/suggestions/call-boxed.rs
new file mode 100644
index 0000000000000..d19e4596a0cc1
--- /dev/null
+++ b/src/test/ui/suggestions/call-boxed.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut x = 1i32;
+    let y = Box::new(|| 1);
+    x = y;
+    //~^ ERROR mismatched types
+    //~| HELP use parentheses to call this closure
+}
diff --git a/src/test/ui/suggestions/call-boxed.stderr b/src/test/ui/suggestions/call-boxed.stderr
new file mode 100644
index 0000000000000..9b619ac9a3f50
--- /dev/null
+++ b/src/test/ui/suggestions/call-boxed.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/call-boxed.rs:4:9
+   |
+LL |     let mut x = 1i32;
+   |                 ---- expected due to this value
+LL |     let y = Box::new(|| 1);
+   |                      -- the found closure
+LL |     x = y;
+   |         ^ expected `i32`, found struct `Box`
+   |
+   = note: expected type `i32`
+            found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>`
+help: use parentheses to call this closure
+   |
+LL |     x = y();
+   |          ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.

From 703603a3623051b9f1a882b389f40f6a6dc5cfb0 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 27 Aug 2022 20:59:07 +0000
Subject: [PATCH 06/24] Only suggest call on nonexistent fields and methods if
 they make sense

---
 compiler/rustc_typeck/src/check/expr.rs       | 50 ++++++-----------
 .../rustc_typeck/src/check/method/suggest.rs  | 55 ++++++-------------
 compiler/rustc_typeck/src/check/mod.rs        | 29 ----------
 .../ui/functions-closures/fn-help-with-err.rs | 28 +++++++---
 .../fn-help-with-err.stderr                   | 38 +++++++++----
 src/test/ui/issues/issue-57362-1.stderr       |  4 +-
 src/test/ui/typeck/issue-29124.stderr         |  8 +--
 .../typeck/issue-87181/empty-tuple-method.rs  |  2 +-
 .../issue-87181/empty-tuple-method.stderr     |  6 +-
 .../ui/typeck/issue-87181/enum-variant.rs     |  2 +-
 .../ui/typeck/issue-87181/enum-variant.stderr |  6 +-
 .../ui/typeck/issue-87181/tuple-field.stderr  |  6 +-
 .../ui/typeck/issue-87181/tuple-method.stderr |  9 +--
 src/test/ui/typeck/issue-96738.stderr         | 18 +-----
 ...ed-closures-static-call-wrong-trait.stderr |  4 +-
 15 files changed, 94 insertions(+), 171 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 604ba22a1236f..1ae4075aab631 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -21,7 +21,6 @@ use crate::errors::{
 };
 use crate::type_error_struct;
 
-use super::suggest_call_constructor;
 use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
@@ -44,7 +43,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
 use rustc_middle::ty::error::TypeError::FieldMisMatch;
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
+use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
 use rustc_session::parse::feature_err;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::lev_distance::find_best_match_for_name;
@@ -2280,35 +2279,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.tcx().ty_error()
     }
 
-    fn check_call_constructor(
-        &self,
-        err: &mut Diagnostic,
-        base: &'tcx hir::Expr<'tcx>,
-        def_id: DefId,
-    ) {
-        if let Some(local_id) = def_id.as_local() {
-            let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
-            let node = self.tcx.hir().get(hir_id);
-
-            if let Some(fields) = node.tuple_fields() {
-                let kind = match self.tcx.opt_def_kind(local_id) {
-                    Some(DefKind::Ctor(of, _)) => of,
-                    _ => return,
-                };
-
-                suggest_call_constructor(base.span, kind, fields.len(), err);
-            }
-        } else {
-            // The logic here isn't smart but `associated_item_def_ids`
-            // doesn't work nicely on local.
-            if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
-                let parent_def_id = self.tcx.parent(def_id);
-                let fields = self.tcx.associated_item_def_ids(parent_def_id);
-                suggest_call_constructor(base.span, of, fields.len(), err);
-            }
-        }
-    }
-
     fn suggest_await_on_field_access(
         &self,
         err: &mut Diagnostic,
@@ -2378,12 +2348,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::Opaque(_, _) => {
                 self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
             }
-            ty::FnDef(def_id, _) => {
-                self.check_call_constructor(&mut err, base, def_id);
-            }
             _ => {}
         }
 
+        self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
+            if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() {
+                def.non_enum_variant().fields.iter().any(|field| {
+                    field.ident(self.tcx) == ident
+                        && field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
+                })
+            } else if let ty::Tuple(tys) = output_ty.kind()
+                && let Ok(idx) = ident.as_str().parse::<usize>()
+            {
+                idx < tys.len()
+            } else {
+                false
+            }
+        });
+
         if ident.name == kw::Await {
             // We know by construction that `<expr>.await` is either on Rust 2015
             // or results in `ExprKind::Await`. Suggest switching the edition to 2018.
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 441a62256de88..cfedd145fed30 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -31,7 +31,7 @@ use std::cmp::Ordering;
 use std::iter;
 
 use super::probe::{Mode, ProbeScope};
-use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
+use super::{CandidateSource, MethodError, NoMatchData};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -363,44 +363,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
 
-                if self.is_fn_ty(rcvr_ty, span) {
-                    if let SelfSource::MethodCall(expr) = source {
-                        let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
-                            if let Some(local_id) = def_id.as_local() {
-                                let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
-                                let node = tcx.hir().get(hir_id);
-                                let fields = node.tuple_fields();
-                                if let Some(fields) = fields
-                                    && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
-                                        Some((fields.len(), of))
-                                } else {
-                                    None
-                                }
-                            } else {
-                                // The logic here isn't smart but `associated_item_def_ids`
-                                // doesn't work nicely on local.
-                                if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) {
-                                    let parent_def_id = tcx.parent(*def_id);
-                                    Some((tcx.associated_item_def_ids(parent_def_id).len(), of))
-                                } else {
-                                    None
-                                }
-                            }
-                        } else {
-                            None
-                        };
-
-                        // If the function is a tuple constructor, we recommend that they call it
-                        if let Some((fields, kind)) = suggest {
-                            suggest_call_constructor(expr.span, kind, fields, &mut err);
-                        } else {
-                            // General case
-                            err.span_label(
-                                expr.span,
-                                "this is a function, perhaps you wish to call it",
-                            );
-                        }
-                    }
+                if let SelfSource::MethodCall(rcvr_expr) = source {
+                    self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
+                        let call_expr = self
+                            .tcx
+                            .hir()
+                            .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
+                        let probe = self.lookup_probe(
+                            span,
+                            item_name,
+                            output_ty,
+                            call_expr,
+                            ProbeScope::AllTraits,
+                        );
+                        probe.is_ok()
+                    });
                 }
 
                 let mut custom_span_label = false;
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index fb675212e3ffb..343ea9e0fa5a1 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -96,7 +96,6 @@ use check::{check_abi, check_fn, check_mod_item_types};
 pub use diverges::Diverges;
 pub use expectation::Expectation;
 pub use fn_ctxt::*;
-use hir::def::CtorOf;
 pub use inherited::{Inherited, InheritedBuilder};
 
 use crate::astconv::AstConv;
@@ -960,31 +959,3 @@ fn has_expected_num_generic_args<'tcx>(
         generics.count() == expected + if generics.has_self { 1 } else { 0 }
     })
 }
-
-/// Suggests calling the constructor of a tuple struct or enum variant
-///
-/// * `snippet` - The snippet of code that references the constructor
-/// * `span` - The span of the snippet
-/// * `params` - The number of parameters the constructor accepts
-/// * `err` - A mutable diagnostic builder to add the suggestion to
-fn suggest_call_constructor(span: Span, kind: CtorOf, params: usize, err: &mut Diagnostic) {
-    // Note: tuple-structs don't have named fields, so just use placeholders
-    let args = vec!["_"; params].join(", ");
-    let applicable = if params > 0 {
-        Applicability::HasPlaceholders
-    } else {
-        // When n = 0, it's an empty-tuple struct/enum variant
-        // so we trivially know how to construct it
-        Applicability::MachineApplicable
-    };
-    let kind = match kind {
-        CtorOf::Struct => "a struct",
-        CtorOf::Variant => "an enum variant",
-    };
-    err.span_label(span, &format!("this is the constructor of {kind}"));
-    err.multipart_suggestion(
-        "call the constructor",
-        vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
-        applicable,
-    );
-}
diff --git a/src/test/ui/functions-closures/fn-help-with-err.rs b/src/test/ui/functions-closures/fn-help-with-err.rs
index 3d2bcb8ad3550..49a514a8b4e34 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.rs
+++ b/src/test/ui/functions-closures/fn-help-with-err.rs
@@ -1,16 +1,28 @@
 // This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error.
+
+struct Foo;
+
+trait Bar {
+    //~^ NOTE `Bar` defines an item `bar`, perhaps you need to implement it
+    //~| NOTE `Bar` defines an item `bar`, perhaps you need to implement it
+    fn bar(&self) {}
+}
+
+impl Bar for Foo {}
+
 fn main() {
     let arc = std::sync::Arc::new(oops);
     //~^ ERROR cannot find value `oops` in this scope
     //~| NOTE not found
-    // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
-    arc.blablabla();
-    //~^ ERROR no method named `blablabla`
+    arc.bar();
+    //~^ ERROR no method named `bar`
     //~| NOTE method not found
-    let arc2 = std::sync::Arc::new(|| 1);
-    // The error "note: this is a function, perhaps you wish to call it" SHOULD appear
-    arc2.blablabla();
-    //~^ ERROR no method named `blablabla`
+    //~| HELP items from traits can only be used if the trait is implemented and in scope
+
+    let arc2 = std::sync::Arc::new(|| Foo);
+    arc2.bar();
+    //~^ ERROR no method named `bar`
     //~| NOTE method not found
-    //~| NOTE this is a function, perhaps you wish to call it
+    //~| HELP items from traits can only be used if the trait is implemented and in scope
+    //~| HELP use parentheses to call this closure
 }
diff --git a/src/test/ui/functions-closures/fn-help-with-err.stderr b/src/test/ui/functions-closures/fn-help-with-err.stderr
index 06e29daef456c..2296666219eef 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.stderr
+++ b/src/test/ui/functions-closures/fn-help-with-err.stderr
@@ -1,22 +1,38 @@
 error[E0425]: cannot find value `oops` in this scope
-  --> $DIR/fn-help-with-err.rs:3:35
+  --> $DIR/fn-help-with-err.rs:14:35
    |
 LL |     let arc = std::sync::Arc::new(oops);
    |                                   ^^^^ not found in this scope
 
-error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope
-  --> $DIR/fn-help-with-err.rs:7:9
+error[E0599]: no method named `bar` found for struct `Arc<_>` in the current scope
+  --> $DIR/fn-help-with-err.rs:17:9
    |
-LL |     arc.blablabla();
-   |         ^^^^^^^^^ method not found in `Arc<_>`
+LL |     arc.bar();
+   |         ^^^ method not found in `Arc<_>`
+   |
+   = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `bar`, perhaps you need to implement it
+  --> $DIR/fn-help-with-err.rs:5:1
+   |
+LL | trait Bar {
+   | ^^^^^^^^^
 
-error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` in the current scope
-  --> $DIR/fn-help-with-err.rs:12:10
+error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope
+  --> $DIR/fn-help-with-err.rs:23:10
+   |
+LL |     arc2.bar();
+   |          ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>`
+   |
+   = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `bar`, perhaps you need to implement it
+  --> $DIR/fn-help-with-err.rs:5:1
+   |
+LL | trait Bar {
+   | ^^^^^^^^^
+help: use parentheses to call this closure
    |
-LL |     arc2.blablabla();
-   |     ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>`
-   |     |
-   |     this is a function, perhaps you wish to call it
+LL |     arc2().bar();
+   |         ++
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/issues/issue-57362-1.stderr b/src/test/ui/issues/issue-57362-1.stderr
index 8e19f14009a0e..b10273f14bd03 100644
--- a/src/test/ui/issues/issue-57362-1.stderr
+++ b/src/test/ui/issues/issue-57362-1.stderr
@@ -2,9 +2,7 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current
   --> $DIR/issue-57362-1.rs:20:7
    |
 LL |     a.f();
-   |     - ^ method not found in `fn(&u8)`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |       ^ method not found in `fn(&u8)`
    |
    = help: items from traits can only be used if the trait is implemented and in scope
 note: `Trait` defines an item `f`, perhaps you need to implement it
diff --git a/src/test/ui/typeck/issue-29124.stderr b/src/test/ui/typeck/issue-29124.stderr
index c5d2ec0840996..a837a7d2d62d1 100644
--- a/src/test/ui/typeck/issue-29124.stderr
+++ b/src/test/ui/typeck/issue-29124.stderr
@@ -2,17 +2,13 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in
   --> $DIR/issue-29124.rs:15:15
    |
 LL |     Obj::func.x();
-   |     --------- ^ method not found in `fn() -> Ret {Obj::func}`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |               ^ method not found in `fn() -> Ret {Obj::func}`
 
 error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
   --> $DIR/issue-29124.rs:17:10
    |
 LL |     func.x();
-   |     ---- ^ method not found in `fn() -> Ret {func}`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |          ^ method not found in `fn() -> Ret {func}`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
index 1875d8280cb62..be68ad32ae55b 100644
--- a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
@@ -4,7 +4,7 @@ struct Bar<T> {
 
 struct Foo();
 impl Foo {
-    fn foo() { }
+    fn foo(&self) { }
 }
 
 fn main() {
diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
index 6ed70b301e4a4..a18c54a29b52c 100644
--- a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
@@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the
   --> $DIR/empty-tuple-method.rs:12:15
    |
 LL |     thing.bar.foo();
-   |     --------- ^^^ method not found in `fn() -> Foo {Foo}`
-   |     |
-   |     this is the constructor of a struct
+   |               ^^^ method not found in `fn() -> Foo {Foo}`
    |
-help: call the constructor
+help: use parentheses to instantiate this tuple struct
    |
 LL |     (thing.bar)().foo();
    |     +         +++
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.rs b/src/test/ui/typeck/issue-87181/enum-variant.rs
index 3b926b90f10bb..d87f99c3c5a19 100644
--- a/src/test/ui/typeck/issue-87181/enum-variant.rs
+++ b/src/test/ui/typeck/issue-87181/enum-variant.rs
@@ -6,7 +6,7 @@ enum Foo{
     Tup()
 }
 impl Foo {
-    fn foo() { }
+    fn foo(&self) { }
 }
 
 fn main() {
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.stderr b/src/test/ui/typeck/issue-87181/enum-variant.stderr
index a3a818696ab5b..90641410d8e96 100644
--- a/src/test/ui/typeck/issue-87181/enum-variant.stderr
+++ b/src/test/ui/typeck/issue-87181/enum-variant.stderr
@@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` i
   --> $DIR/enum-variant.rs:14:15
    |
 LL |     thing.bar.foo();
-   |     --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
-   |     |
-   |     this is the constructor of an enum variant
+   |               ^^^ method not found in `fn() -> Foo {Foo::Tup}`
    |
-help: call the constructor
+help: use parentheses to instantiate this tuple variant
    |
 LL |     (thing.bar)().foo();
    |     +         +++
diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr
index 4d22ada0247e9..0e43ace893396 100644
--- a/src/test/ui/typeck/issue-87181/tuple-field.stderr
+++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr
@@ -2,11 +2,9 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
   --> $DIR/tuple-field.rs:12:15
    |
 LL |     thing.bar.0;
-   |     --------- ^
-   |     |
-   |     this is the constructor of a struct
+   |               ^
    |
-help: call the constructor
+help: use parentheses to instantiate this tuple struct
    |
 LL |     (thing.bar)(_, _).0;
    |     +         +++++++
diff --git a/src/test/ui/typeck/issue-87181/tuple-method.stderr b/src/test/ui/typeck/issue-87181/tuple-method.stderr
index 1e392e17984b0..e27c41858d322 100644
--- a/src/test/ui/typeck/issue-87181/tuple-method.stderr
+++ b/src/test/ui/typeck/issue-87181/tuple-method.stderr
@@ -2,14 +2,7 @@ error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}`
   --> $DIR/tuple-method.rs:12:15
    |
 LL |     thing.bar.foo();
-   |     --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
-   |     |
-   |     this is the constructor of a struct
-   |
-help: call the constructor
-   |
-LL |     (thing.bar)(_, _).foo();
-   |     +         +++++++
+   |               ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/typeck/issue-96738.stderr b/src/test/ui/typeck/issue-96738.stderr
index 32f53849848c7..0d4d87ef47e2b 100644
--- a/src/test/ui/typeck/issue-96738.stderr
+++ b/src/test/ui/typeck/issue-96738.stderr
@@ -2,27 +2,13 @@ error[E0599]: no method named `nonexistent_method` found for fn item `fn(_) -> O
   --> $DIR/issue-96738.rs:2:10
    |
 LL |     Some.nonexistent_method();
-   |     ---- ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
-   |     |
-   |     this is the constructor of an enum variant
-   |
-help: call the constructor
-   |
-LL |     (Some)(_).nonexistent_method();
-   |     +    ++++
+   |          ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
 
 error[E0609]: no field `nonexistent_field` on type `fn(_) -> Option<_> {Option::<_>::Some}`
   --> $DIR/issue-96738.rs:3:10
    |
 LL |     Some.nonexistent_field;
-   |     ---- ^^^^^^^^^^^^^^^^^
-   |     |
-   |     this is the constructor of an enum variant
-   |
-help: call the constructor
-   |
-LL |     (Some)(_).nonexistent_field;
-   |     +    ++++
+   |          ^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
index 4f89afa320d70..e5ca0edd7a91c 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
@@ -2,9 +2,7 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl
   --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
    |
 LL |     mut_.call((0, ));
-   |     ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |          ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
 
 error: aborting due to previous error
 

From cef0482d11ec42d50611beca2dbf551094d5025c Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 27 Aug 2022 21:17:17 +0000
Subject: [PATCH 07/24] Add test

---
 src/test/ui/suggestions/call-on-missing.rs    | 19 ++++++++++++++
 .../ui/suggestions/call-on-missing.stderr     | 26 +++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 src/test/ui/suggestions/call-on-missing.rs
 create mode 100644 src/test/ui/suggestions/call-on-missing.stderr

diff --git a/src/test/ui/suggestions/call-on-missing.rs b/src/test/ui/suggestions/call-on-missing.rs
new file mode 100644
index 0000000000000..611b9d40f0f06
--- /dev/null
+++ b/src/test/ui/suggestions/call-on-missing.rs
@@ -0,0 +1,19 @@
+struct Foo { i: i32 }
+
+impl Foo {
+    fn bar(&self) {}
+}
+
+fn foo() -> Foo {
+    Foo { i: 1 }
+}
+
+fn main() {
+    foo.bar();
+    //~^ ERROR no method named `bar`
+    //~| HELP use parentheses to call this function
+
+    foo.i;
+    //~^ ERROR no field `i`
+    //~| HELP use parentheses to call this function
+}
diff --git a/src/test/ui/suggestions/call-on-missing.stderr b/src/test/ui/suggestions/call-on-missing.stderr
new file mode 100644
index 0000000000000..d8070321e1462
--- /dev/null
+++ b/src/test/ui/suggestions/call-on-missing.stderr
@@ -0,0 +1,26 @@
+error[E0599]: no method named `bar` found for fn item `fn() -> Foo {foo}` in the current scope
+  --> $DIR/call-on-missing.rs:12:9
+   |
+LL |     foo.bar();
+   |         ^^^ method not found in `fn() -> Foo {foo}`
+   |
+help: use parentheses to call this function
+   |
+LL |     foo().bar();
+   |        ++
+
+error[E0609]: no field `i` on type `fn() -> Foo {foo}`
+  --> $DIR/call-on-missing.rs:16:9
+   |
+LL |     foo.i;
+   |         ^
+   |
+help: use parentheses to call this function
+   |
+LL |     foo().i;
+   |        ++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0599, E0609.
+For more information about an error, try `rustc --explain E0599`.

From 2f78dd15a6f29894e5876582cc84e8a5faa539c1 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 27 Aug 2022 23:08:21 +0000
Subject: [PATCH 08/24] Suggest calling trait objects and parameters too, when
 possible

---
 .../src/check/fn_ctxt/suggestions.rs          | 75 ++++++++++++++-----
 .../substs-ppaux.normal.stderr                |  6 +-
 .../substs-ppaux.verbose.stderr               |  6 +-
 src/test/ui/fn/fn-trait-formatting.stderr     |  8 ++
 .../suggest-calling-rpit-closure.stderr       |  2 +-
 src/test/ui/suggestions/call-on-missing.rs    | 20 +++++
 .../ui/suggestions/call-on-missing.stderr     | 51 ++++++++++++-
 .../fn-or-tuple-struct-without-args.stderr    | 16 ++--
 8 files changed, 151 insertions(+), 33 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 45872a0b07aeb..4eb0f045d7791 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -2,6 +2,7 @@ use super::FnCtxt;
 use crate::astconv::AstConv;
 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
 
+use hir::def_id::DefId;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
@@ -75,38 +76,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         found: Ty<'tcx>,
         can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
     ) -> bool {
+        enum DefIdOrName {
+            DefId(DefId),
+            Name(&'static str),
+        }
         // Autoderef is useful here because sometimes we box callables, etc.
-        let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
+        let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
             match *found.kind() {
-                ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())),
+                ty::FnPtr(fn_sig) =>
+                    Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs().skip_binder().len())),
                 ty::FnDef(def_id, _) => {
                     let fn_sig = found.fn_sig(self.tcx);
-                    Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
+                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
                 }
                 ty::Closure(def_id, substs) => {
                     let fn_sig = substs.as_closure().sig();
-                    Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
+                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
                 }
                 ty::Opaque(def_id, substs) => {
-                    let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+                    self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
                         if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
                         && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
                         // args tuple will always be substs[1]
                         && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
                         {
                             Some((
+                                DefIdOrName::DefId(def_id),
                                 pred.kind().rebind(proj.term.ty().unwrap()),
                                 args.len(),
                             ))
                         } else {
                             None
                         }
-                    });
-                    if let Some((output, inputs)) = sig {
-                        Some((Some(def_id), output, inputs))
-                    } else {
-                        None
-                    }
+                    })
+                }
+                ty::Dynamic(data, _) => {
+                    data.iter().find_map(|pred| {
+                        if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+                        && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
+                        // for existential projection, substs are shifted over by 1
+                        && let ty::Tuple(args) = proj.substs.type_at(0).kind()
+                        {
+                            Some((
+                                DefIdOrName::Name("trait object"),
+                                pred.rebind(proj.term.ty().unwrap()),
+                                args.len(),
+                            ))
+                        } else {
+                            None
+                        }
+                    })
+                }
+                ty::Param(param) => {
+                    let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
+                    self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
+                        if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+                        && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
+                        && proj.projection_ty.self_ty() == found
+                        // args tuple will always be substs[1]
+                        && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+                        {
+                            Some((
+                                DefIdOrName::DefId(def_id),
+                                pred.kind().rebind(proj.term.ty().unwrap()),
+                                args.len(),
+                            ))
+                        } else {
+                            None
+                        }
+                    })
                 }
                 _ => None,
             }
@@ -128,12 +166,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => ("...".to_string(), Applicability::HasPlaceholders),
             };
 
-            let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) {
-                Some(DefKind::Fn) => "call this function",
-                Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure",
-                Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct",
-                Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant",
-                _ => "call this function",
+            let msg = match def_id_or_name {
+                DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
+                    DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
+                    DefKind::Ctor(CtorOf::Variant, _) => {
+                        "instantiate this tuple variant".to_string()
+                    }
+                    kind => format!("call this {}", kind.descr(def_id)),
+                },
+                DefIdOrName::Name(name) => format!("call this {name}"),
             };
 
             let sugg = match expr.kind {
diff --git a/src/test/ui/associated-types/substs-ppaux.normal.stderr b/src/test/ui/associated-types/substs-ppaux.normal.stderr
index 501d2cfaa26b7..3f180cf4f1f89 100644
--- a/src/test/ui/associated-types/substs-ppaux.normal.stderr
+++ b/src/test/ui/associated-types/substs-ppaux.normal.stderr
@@ -11,7 +11,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>();
    |                                                                         ++
@@ -29,7 +29,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>();
    |                                                                          ++
@@ -47,7 +47,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz();
    |                                                        ++
diff --git a/src/test/ui/associated-types/substs-ppaux.verbose.stderr b/src/test/ui/associated-types/substs-ppaux.verbose.stderr
index ae3e862dddd20..16dd29de2c543 100644
--- a/src/test/ui/associated-types/substs-ppaux.verbose.stderr
+++ b/src/test/ui/associated-types/substs-ppaux.verbose.stderr
@@ -11,7 +11,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>();
    |                                                                         ++
@@ -29,7 +29,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>();
    |                                                                          ++
@@ -47,7 +47,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz();
    |                                                        ++
diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr
index ea88e401bed87..1d5e0a859a6da 100644
--- a/src/test/ui/fn/fn-trait-formatting.stderr
+++ b/src/test/ui/fn/fn-trait-formatting.stderr
@@ -8,6 +8,10 @@ LL |     let _: () = Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>;
    |
    = note: expected unit type `()`
                  found struct `Box<dyn FnOnce(isize)>`
+help: use parentheses to call this trait object
+   |
+LL |     let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(_);
+   |                 +                                                 ++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-trait-formatting.rs:10:17
@@ -19,6 +23,10 @@ LL |     let _: () = Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>
    |
    = note: expected unit type `()`
                  found struct `Box<dyn Fn(isize, isize)>`
+help: use parentheses to call this trait object
+   |
+LL |     let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(_, _);
+   |                 +                                                           +++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-trait-formatting.rs:14:17
diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
index 2a328a0e6f54d..c10a856d83ba8 100644
--- a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
+++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
@@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 {
    |
    = note:     expected type `i32`
            found opaque type `impl Fn() -> i32`
-help: use parentheses to call this closure
+help: use parentheses to call this opaque type
    |
 LL |     opaque()()
    |             ++
diff --git a/src/test/ui/suggestions/call-on-missing.rs b/src/test/ui/suggestions/call-on-missing.rs
index 611b9d40f0f06..25ced84dd3783 100644
--- a/src/test/ui/suggestions/call-on-missing.rs
+++ b/src/test/ui/suggestions/call-on-missing.rs
@@ -16,4 +16,24 @@ fn main() {
     foo.i;
     //~^ ERROR no field `i`
     //~| HELP use parentheses to call this function
+
+    let callable = Box::new(|| Foo { i: 1 }) as Box<dyn Fn() -> Foo>;
+
+    callable.bar();
+    //~^ ERROR no method named `bar`
+    //~| HELP use parentheses to call this trait object
+
+    callable.i;
+    //~^ ERROR no field `i`
+    //~| HELP use parentheses to call this trait object
+}
+
+fn type_param<T: Fn() -> Foo>(t: T) {
+    t.bar();
+    //~^ ERROR no method named `bar`
+    //~| HELP use parentheses to call this type parameter
+
+    t.i;
+    //~^ ERROR no field `i`
+    //~| HELP use parentheses to call this type parameter
 }
diff --git a/src/test/ui/suggestions/call-on-missing.stderr b/src/test/ui/suggestions/call-on-missing.stderr
index d8070321e1462..ca9abc7e90689 100644
--- a/src/test/ui/suggestions/call-on-missing.stderr
+++ b/src/test/ui/suggestions/call-on-missing.stderr
@@ -20,7 +20,56 @@ help: use parentheses to call this function
 LL |     foo().i;
    |        ++
 
-error: aborting due to 2 previous errors
+error[E0599]: no method named `bar` found for struct `Box<dyn Fn() -> Foo>` in the current scope
+  --> $DIR/call-on-missing.rs:22:14
+   |
+LL |     callable.bar();
+   |              ^^^ method not found in `Box<dyn Fn() -> Foo>`
+   |
+help: use parentheses to call this trait object
+   |
+LL |     callable().bar();
+   |             ++
+
+error[E0609]: no field `i` on type `Box<dyn Fn() -> Foo>`
+  --> $DIR/call-on-missing.rs:26:14
+   |
+LL |     callable.i;
+   |              ^ unknown field
+   |
+help: use parentheses to call this trait object
+   |
+LL |     callable().i;
+   |             ++
+
+error[E0599]: no method named `bar` found for type parameter `T` in the current scope
+  --> $DIR/call-on-missing.rs:32:7
+   |
+LL | fn type_param<T: Fn() -> Foo>(t: T) {
+   |               - method `bar` not found for this type parameter
+LL |     t.bar();
+   |       ^^^ method not found in `T`
+   |
+help: use parentheses to call this type parameter
+   |
+LL |     t().bar();
+   |      ++
+
+error[E0609]: no field `i` on type `T`
+  --> $DIR/call-on-missing.rs:36:7
+   |
+LL | fn type_param<T: Fn() -> Foo>(t: T) {
+   |               - type parameter 'T' declared here
+...
+LL |     t.i;
+   |       ^
+   |
+help: use parentheses to call this type parameter
+   |
+LL |     t().i;
+   |      ++
+
+error: aborting due to 6 previous errors
 
 Some errors have detailed explanations: E0599, E0609.
 For more information about an error, try `rustc --explain E0599`.
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
index e75ce0da82e51..ba710bfa746ae 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
@@ -103,7 +103,7 @@ LL |     let _: usize = T::baz;
    |
    = note: expected type `usize`
            found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = T::baz(_, _);
    |                          ++++++
@@ -121,7 +121,7 @@ LL |     let _: usize = T::bat;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<_ as T>::bat}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = T::bat(_);
    |                          +++
@@ -157,7 +157,7 @@ LL |     let _: usize = X::baz;
    |
    = note: expected type `usize`
            found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = X::baz(_, _);
    |                          ++++++
@@ -175,7 +175,7 @@ LL |     let _: usize = X::bat;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<X as T>::bat}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = X::bat(_);
    |                          +++
@@ -193,7 +193,7 @@ LL |     let _: usize = X::bax;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<X as T>::bax}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = X::bax(_);
    |                          +++
@@ -211,7 +211,7 @@ LL |     let _: usize = X::bach;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<X as T>::bach}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = X::bach(_);
    |                           +++
@@ -229,7 +229,7 @@ LL |     let _: usize = X::ban;
    |
    = note: expected type `usize`
            found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = X::ban(_);
    |                          +++
@@ -247,7 +247,7 @@ LL |     let _: usize = X::bal;
    |
    = note: expected type `usize`
            found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = X::bal(_);
    |                          +++

From 18b640aee5b5435491a9db82c08c8fde8b7b62c9 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 28 Aug 2022 01:08:24 +0000
Subject: [PATCH 09/24] Suggest calling when operator types mismatch

---
 compiler/rustc_errors/src/lib.rs              |  19 +-
 compiler/rustc_lint_defs/src/lib.rs           |   3 +-
 .../src/check/fn_ctxt/suggestions.rs          | 173 +++++++++++++-----
 compiler/rustc_typeck/src/check/op.rs         | 128 ++++---------
 src/test/ui/binop/issue-77910-2.stderr        |   5 +
 src/test/ui/fn/fn-compare-mismatch.stderr     |  10 +-
 src/test/ui/issues/issue-59488.stderr         |  18 +-
 ...70724-add_type_neq_err_label-unwrap.stderr |   7 +-
 8 files changed, 199 insertions(+), 164 deletions(-)

diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 9b9c334d4dfcc..b21e9c2c96cbc 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -1248,9 +1248,13 @@ impl HandlerInner {
     }
 
     fn treat_err_as_bug(&self) -> bool {
-        self.flags
-            .treat_err_as_bug
-            .map_or(false, |c| self.err_count() + self.lint_err_count >= c.get())
+        self.flags.treat_err_as_bug.map_or(false, |c| {
+            self.err_count()
+                + self.lint_err_count
+                + self.delayed_span_bugs.len()
+                + self.delayed_good_path_bugs.len()
+                >= c.get()
+        })
     }
 
     fn print_error_count(&mut self, registry: &Registry) {
@@ -1406,7 +1410,14 @@ impl HandlerInner {
         // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
         // incrementing `err_count` by one, so we need to +1 the comparing.
         // FIXME: Would be nice to increment err_count in a more coherent way.
-        if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) {
+        if self.flags.treat_err_as_bug.map_or(false, |c| {
+            self.err_count()
+                + self.lint_err_count
+                + self.delayed_span_bugs.len()
+                + self.delayed_good_path_bugs.len()
+                + 1
+                >= c.get()
+        }) {
             // FIXME: don't abort here if report_delayed_bugs is off
             self.span_bug(sp, msg);
         }
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 9e7cbba9511b2..9c6530c8a0843 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -41,7 +41,8 @@ macro_rules! pluralize {
 /// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
 /// to determine whether it should be automatically applied or if the user should be consulted
 /// before applying the suggestion.
-#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
+#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
 pub enum Applicability {
     /// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
     /// This suggestion should be automatically applied.
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 4eb0f045d7791..96901b5330312 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -76,10 +76,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         found: Ty<'tcx>,
         can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
     ) -> bool {
-        enum DefIdOrName {
-            DefId(DefId),
-            Name(&'static str),
+        let Some((def_id_or_name, output, num_inputs)) = self.extract_callable_info(expr, found)
+            else { return false; };
+        if can_satisfy(output) {
+            let (sugg_call, mut applicability) = match num_inputs {
+                0 => ("".to_string(), Applicability::MachineApplicable),
+                1..=4 => (
+                    (0..num_inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
+                    Applicability::MachineApplicable,
+                ),
+                _ => ("...".to_string(), Applicability::HasPlaceholders),
+            };
+
+            let msg = match def_id_or_name {
+                DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
+                    DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
+                    DefKind::Ctor(CtorOf::Variant, _) => {
+                        "instantiate this tuple variant".to_string()
+                    }
+                    kind => format!("call this {}", kind.descr(def_id)),
+                },
+                DefIdOrName::Name(name) => format!("call this {name}"),
+            };
+
+            let sugg = match expr.kind {
+                hir::ExprKind::Call(..)
+                | hir::ExprKind::Path(..)
+                | hir::ExprKind::Index(..)
+                | hir::ExprKind::Lit(..) => {
+                    vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
+                }
+                hir::ExprKind::Closure { .. } => {
+                    // Might be `{ expr } || { bool }`
+                    applicability = Applicability::MaybeIncorrect;
+                    vec![
+                        (expr.span.shrink_to_lo(), "(".to_string()),
+                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                    ]
+                }
+                _ => {
+                    vec![
+                        (expr.span.shrink_to_lo(), "(".to_string()),
+                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                    ]
+                }
+            };
+
+            err.multipart_suggestion_verbose(
+                format!("use parentheses to {msg}"),
+                sugg,
+                applicability,
+            );
+
+            return true;
         }
+        false
+    }
+
+    fn extract_callable_info(
+        &self,
+        expr: &Expr<'_>,
+        found: Ty<'tcx>,
+    ) -> Option<(DefIdOrName, Ty<'tcx>, usize)> {
         // Autoderef is useful here because sometimes we box callables, etc.
         let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
             match *found.kind() {
@@ -148,67 +206,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 _ => None,
             }
-        }) else { return false; };
+        }) else { return None; };
 
         let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
+
         // We don't want to register any extra obligations, which should be
         // implied by wf, but also because that would possibly result in
         // erroneous errors later on.
         let infer::InferOk { value: output, obligations: _ } =
             self.normalize_associated_types_in_as_infer_ok(expr.span, output);
-        if !output.is_ty_var() && can_satisfy(output) {
-            let (sugg_call, mut applicability) = match inputs {
-                0 => ("".to_string(), Applicability::MachineApplicable),
-                1..=4 => (
-                    (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
-                    Applicability::MachineApplicable,
-                ),
-                _ => ("...".to_string(), Applicability::HasPlaceholders),
-            };
 
-            let msg = match def_id_or_name {
-                DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
-                    DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
-                    DefKind::Ctor(CtorOf::Variant, _) => {
-                        "instantiate this tuple variant".to_string()
-                    }
-                    kind => format!("call this {}", kind.descr(def_id)),
-                },
-                DefIdOrName::Name(name) => format!("call this {name}"),
-            };
+        if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+    }
 
-            let sugg = match expr.kind {
-                hir::ExprKind::Call(..)
-                | hir::ExprKind::Path(..)
-                | hir::ExprKind::Index(..)
-                | hir::ExprKind::Lit(..) => {
-                    vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
-                }
-                hir::ExprKind::Closure { .. } => {
-                    // Might be `{ expr } || { bool }`
-                    applicability = Applicability::MaybeIncorrect;
-                    vec![
-                        (expr.span.shrink_to_lo(), "(".to_string()),
-                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
-                    ]
-                }
-                _ => {
-                    vec![
-                        (expr.span.shrink_to_lo(), "(".to_string()),
-                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
-                    ]
+    pub fn suggest_two_fn_call(
+        &self,
+        err: &mut Diagnostic,
+        lhs_expr: &'tcx hir::Expr<'tcx>,
+        lhs_ty: Ty<'tcx>,
+        rhs_expr: &'tcx hir::Expr<'tcx>,
+        rhs_ty: Ty<'tcx>,
+        can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
+    ) -> bool {
+        let Some((_, lhs_output_ty, num_lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
+            else { return false; };
+        let Some((_, rhs_output_ty, num_rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+            else { return false; };
+
+        if can_satisfy(lhs_output_ty, rhs_output_ty) {
+            let mut sugg = vec![];
+            let mut applicability = Applicability::MachineApplicable;
+
+            for (expr, num_inputs) in [(lhs_expr, num_lhs_inputs), (rhs_expr, num_rhs_inputs)] {
+                let (sugg_call, this_applicability) = match num_inputs {
+                    0 => ("".to_string(), Applicability::MachineApplicable),
+                    1..=4 => (
+                        (0..num_inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
+                        Applicability::MachineApplicable,
+                    ),
+                    _ => ("...".to_string(), Applicability::HasPlaceholders),
+                };
+
+                applicability = applicability.max(this_applicability);
+
+                match expr.kind {
+                    hir::ExprKind::Call(..)
+                    | hir::ExprKind::Path(..)
+                    | hir::ExprKind::Index(..)
+                    | hir::ExprKind::Lit(..) => {
+                        sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
+                    }
+                    hir::ExprKind::Closure { .. } => {
+                        // Might be `{ expr } || { bool }`
+                        applicability = Applicability::MaybeIncorrect;
+                        sugg.extend([
+                            (expr.span.shrink_to_lo(), "(".to_string()),
+                            (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                        ]);
+                    }
+                    _ => {
+                        sugg.extend([
+                            (expr.span.shrink_to_lo(), "(".to_string()),
+                            (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                        ]);
+                    }
                 }
-            };
+            }
 
             err.multipart_suggestion_verbose(
-                format!("use parentheses to {msg}"),
+                format!("use parentheses to call these"),
                 sugg,
                 applicability,
             );
 
-            return true;
+            true
+        } else {
+            false
         }
-        false
     }
 
     pub fn suggest_deref_ref_or_into(
@@ -959,3 +1033,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 }
+
+enum DefIdOrName {
+    DefId(DefId),
+    Name(&'static str),
+}
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index eb0c51bb2f979..952086e898fc7 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -410,26 +410,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         };
                         let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
                         if !lhs_expr.span.eq(&rhs_expr.span) {
-                            self.add_type_neq_err_label(
-                                &mut err,
-                                lhs_expr.span,
-                                lhs_ty,
-                                rhs_ty,
-                                rhs_expr,
-                                op,
-                                is_assign,
-                                expected,
-                            );
-                            self.add_type_neq_err_label(
-                                &mut err,
-                                rhs_expr.span,
-                                rhs_ty,
-                                lhs_ty,
-                                lhs_expr,
-                                op,
-                                is_assign,
-                                expected,
-                            );
+                            err.span_label(lhs_expr.span, lhs_ty.to_string());
+                            err.span_label(rhs_expr.span, rhs_ty.to_string());
                         }
                         self.note_unmet_impls_on_type(&mut err, errors);
                         (err, missing_trait, use_output)
@@ -468,17 +450,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 };
 
+                let is_compatible = |lhs_ty, rhs_ty| {
+                    self.lookup_op_method(
+                        lhs_ty,
+                        Some(rhs_ty),
+                        Some(rhs_expr),
+                        Op::Binary(op, is_assign),
+                        expected,
+                    )
+                    .is_ok()
+                };
+
                 // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
                 // `a += b` => `*a += b` if a is a mut ref.
-                if is_assign == IsAssign::Yes
-                    && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
-                        suggest_deref_binop(lhs_deref_ty);
+                if !op.span.can_be_used_for_suggestions() {
+                    // Suppress suggestions when lhs and rhs are not in the same span as the error
+                } else if is_assign == IsAssign::Yes
+                    && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
+                {
+                    suggest_deref_binop(lhs_deref_ty);
                 } else if is_assign == IsAssign::No
-                    && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
-                    if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
+                    && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
+                {
+                    if self.type_is_copy_modulo_regions(
+                        self.param_env,
+                        *lhs_deref_ty,
+                        lhs_expr.span,
+                    ) {
                         suggest_deref_binop(*lhs_deref_ty);
                     }
+                } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
+                    is_compatible(lhs_ty, rhs_ty)
+                }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
+                    is_compatible(lhs_ty, rhs_ty)
+                }) || self.suggest_two_fn_call(
+                    &mut err,
+                    rhs_expr,
+                    rhs_ty,
+                    lhs_expr,
+                    lhs_ty,
+                    |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
+                ) {
+                    // Cool
                 }
+
                 if let Some(missing_trait) = missing_trait {
                     let mut visitor = TypeParamVisitor(vec![]);
                     visitor.visit_ty(lhs_ty);
@@ -548,69 +563,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         (lhs_ty, rhs_ty, return_ty)
     }
 
-    /// If one of the types is an uncalled function and calling it would yield the other type,
-    /// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
-    fn add_type_neq_err_label(
-        &self,
-        err: &mut Diagnostic,
-        span: Span,
-        ty: Ty<'tcx>,
-        other_ty: Ty<'tcx>,
-        other_expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
-        is_assign: IsAssign,
-        expected: Expectation<'tcx>,
-    ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
-        err.span_label(span, ty.to_string());
-        if let FnDef(def_id, _) = *ty.kind() {
-            if !self.tcx.has_typeck_results(def_id) {
-                return false;
-            }
-            // FIXME: Instead of exiting early when encountering bound vars in
-            // the function signature, consider keeping the binder here and
-            // propagating it downwards.
-            let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else {
-                return false;
-            };
-
-            let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
-                if !self.tcx.has_typeck_results(def_id) {
-                    return false;
-                }
-                // We're emitting a suggestion, so we can just ignore regions
-                self.tcx.fn_sig(def_id).skip_binder().output()
-            } else {
-                other_ty
-            };
-
-            if self
-                .lookup_op_method(
-                    fn_sig.output(),
-                    Some(other_ty),
-                    Some(other_expr),
-                    Op::Binary(op, is_assign),
-                    expected,
-                )
-                .is_ok()
-            {
-                let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
-                    ("( /* arguments */ )", Applicability::HasPlaceholders)
-                } else {
-                    ("()", Applicability::MaybeIncorrect)
-                };
-
-                err.span_suggestion_verbose(
-                    span.shrink_to_hi(),
-                    "you might have forgotten to call this function",
-                    variable_snippet,
-                    applicability,
-                );
-                return true;
-            }
-        }
-        false
-    }
-
     /// Provide actionable suggestions when trying to add two strings with incorrect types,
     /// like `&str + &str`, `String + String` and `&str + &String`.
     ///
diff --git a/src/test/ui/binop/issue-77910-2.stderr b/src/test/ui/binop/issue-77910-2.stderr
index 5477a5762a8fd..74860a93f3799 100644
--- a/src/test/ui/binop/issue-77910-2.stderr
+++ b/src/test/ui/binop/issue-77910-2.stderr
@@ -5,6 +5,11 @@ LL |     if foo == y {}
    |        --- ^^ - _
    |        |
    |        for<'r> fn(&'r i32) -> &'r i32 {foo}
+   |
+help: use parentheses to call this function
+   |
+LL |     if foo(_) == y {}
+   |           +++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/fn/fn-compare-mismatch.stderr b/src/test/ui/fn/fn-compare-mismatch.stderr
index 096440225b999..df838cb118105 100644
--- a/src/test/ui/fn/fn-compare-mismatch.stderr
+++ b/src/test/ui/fn/fn-compare-mismatch.stderr
@@ -6,14 +6,10 @@ LL |     let x = f == g;
    |             |
    |             fn() {f}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call these
    |
-LL |     let x = f() == g;
-   |              ++
-help: you might have forgotten to call this function
-   |
-LL |     let x = f == g();
-   |                   ++
+LL |     let x = f() == g();
+   |              ++     ++
 
 error[E0308]: mismatched types
   --> $DIR/fn-compare-mismatch.rs:4:18
diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr
index bb6843a19586e..df681eb2489c0 100644
--- a/src/test/ui/issues/issue-59488.stderr
+++ b/src/test/ui/issues/issue-59488.stderr
@@ -6,7 +6,7 @@ LL |     foo > 12;
    |     |
    |     fn() -> i32 {foo}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call this function
    |
 LL |     foo() > 12;
    |        ++
@@ -28,10 +28,10 @@ LL |     bar > 13;
    |     |
    |     fn(i64) -> i64 {bar}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call this function
    |
-LL |     bar( /* arguments */ ) > 13;
-   |        +++++++++++++++++++
+LL |     bar(_) > 13;
+   |        +++
 
 error[E0308]: mismatched types
   --> $DIR/issue-59488.rs:18:11
@@ -50,14 +50,10 @@ LL |     foo > foo;
    |     |
    |     fn() -> i32 {foo}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call these
    |
-LL |     foo() > foo;
-   |        ++
-help: you might have forgotten to call this function
-   |
-LL |     foo > foo();
-   |              ++
+LL |     foo() > foo();
+   |        ++      ++
 
 error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}`
   --> $DIR/issue-59488.rs:25:9
diff --git a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
index c6e6ea1e096af..9239385e64369 100644
--- a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
+++ b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
@@ -8,11 +8,6 @@ LL |     assert_eq!(a, 0);
    |     {integer}
    |
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: you might have forgotten to call this function
-  --> $SRC_DIR/core/src/macros/mod.rs:LL:COL
-   |
-LL |                 if !(*left_val() == *right_val) {
-   |                               ++
 
 error[E0308]: mismatched types
   --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5
@@ -21,7 +16,7 @@ LL |     assert_eq!(a, 0);
    |     ^^^^^^^^^^^^^^^^ expected fn item, found integer
    |
    = note: expected fn item `fn() -> i32 {a}`
-                 found type `i32`
+                 found type `{integer}`
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug`

From 1256530643fdd4762de8de5a47041fc2cf700828 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 28 Aug 2022 01:22:51 +0000
Subject: [PATCH 10/24] More descriptive argument placeholders

---
 .../rustc_middle/src/ty/structural_impls.rs   |  6 ++
 .../src/check/fn_ctxt/suggestions.rs          | 69 ++++++++++++++-----
 src/test/ui/binop/issue-77910-2.stderr        |  4 +-
 src/test/ui/fn/fn-trait-formatting.stderr     |  8 +--
 src/test/ui/issues/issue-35241.stderr         |  4 +-
 src/test/ui/issues/issue-59488.stderr         |  4 +-
 src/test/ui/resolve/privacy-enum-ctor.stderr  | 12 ++--
 .../fn-or-tuple-struct-without-args.stderr    | 44 ++++++------
 .../ui/typeck/issue-87181/tuple-field.stderr  |  4 +-
 9 files changed, 96 insertions(+), 59 deletions(-)

diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 7660a2f3af60a..57555433f55b7 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -844,6 +844,12 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Vec<T> {
     }
 }
 
+impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &[T] {
+    fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        self.iter().try_for_each(|t| t.visit_with(visitor))
+    }
+}
+
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> {
     fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
         self.try_map_id(|t| t.try_fold_with(folder))
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 96901b5330312..d48bdbd7b6d64 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -76,16 +76,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         found: Ty<'tcx>,
         can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
     ) -> bool {
-        let Some((def_id_or_name, output, num_inputs)) = self.extract_callable_info(expr, found)
+        let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
             else { return false; };
         if can_satisfy(output) {
-            let (sugg_call, mut applicability) = match num_inputs {
+            let (sugg_call, mut applicability) = match inputs.len() {
                 0 => ("".to_string(), Applicability::MachineApplicable),
                 1..=4 => (
-                    (0..num_inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
-                    Applicability::MachineApplicable,
+                    inputs
+                        .iter()
+                        .map(|ty| {
+                            if ty.is_suggestable(self.tcx, false) {
+                                format!("/* {ty} */")
+                            } else {
+                                "".to_string()
+                            }
+                        })
+                        .collect::<Vec<_>>()
+                        .join(", "),
+                    Applicability::HasPlaceholders,
                 ),
-                _ => ("...".to_string(), Applicability::HasPlaceholders),
+                _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
             };
 
             let msg = match def_id_or_name {
@@ -137,19 +147,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &Expr<'_>,
         found: Ty<'tcx>,
-    ) -> Option<(DefIdOrName, Ty<'tcx>, usize)> {
+    ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
         // Autoderef is useful here because sometimes we box callables, etc.
         let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
             match *found.kind() {
                 ty::FnPtr(fn_sig) =>
-                    Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs().skip_binder().len())),
+                    Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
                 ty::FnDef(def_id, _) => {
                     let fn_sig = found.fn_sig(self.tcx);
-                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
+                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
                 }
                 ty::Closure(def_id, substs) => {
                     let fn_sig = substs.as_closure().sig();
-                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
+                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
                 }
                 ty::Opaque(def_id, substs) => {
                     self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
@@ -161,7 +171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Some((
                                 DefIdOrName::DefId(def_id),
                                 pred.kind().rebind(proj.term.ty().unwrap()),
-                                args.len(),
+                                pred.kind().rebind(args.as_slice()),
                             ))
                         } else {
                             None
@@ -178,7 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Some((
                                 DefIdOrName::Name("trait object"),
                                 pred.rebind(proj.term.ty().unwrap()),
-                                args.len(),
+                                pred.rebind(args.as_slice()),
                             ))
                         } else {
                             None
@@ -197,7 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Some((
                                 DefIdOrName::DefId(def_id),
                                 pred.kind().rebind(proj.term.ty().unwrap()),
-                                args.len(),
+                                pred.kind().rebind(args.as_slice()),
                             ))
                         } else {
                             None
@@ -209,6 +219,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }) else { return None; };
 
         let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
+        let inputs = inputs
+            .skip_binder()
+            .iter()
+            .map(|ty| {
+                self.replace_bound_vars_with_fresh_vars(
+                    expr.span,
+                    infer::FnCall,
+                    inputs.rebind(*ty),
+                )
+            })
+            .collect();
 
         // We don't want to register any extra obligations, which should be
         // implied by wf, but also because that would possibly result in
@@ -228,23 +249,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rhs_ty: Ty<'tcx>,
         can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
     ) -> bool {
-        let Some((_, lhs_output_ty, num_lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
+        let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
             else { return false; };
-        let Some((_, rhs_output_ty, num_rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+        let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
             else { return false; };
 
         if can_satisfy(lhs_output_ty, rhs_output_ty) {
             let mut sugg = vec![];
             let mut applicability = Applicability::MachineApplicable;
 
-            for (expr, num_inputs) in [(lhs_expr, num_lhs_inputs), (rhs_expr, num_rhs_inputs)] {
-                let (sugg_call, this_applicability) = match num_inputs {
+            for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
+                let (sugg_call, this_applicability) = match inputs.len() {
                     0 => ("".to_string(), Applicability::MachineApplicable),
                     1..=4 => (
-                        (0..num_inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
-                        Applicability::MachineApplicable,
+                        inputs
+                            .iter()
+                            .map(|ty| {
+                                if ty.is_suggestable(self.tcx, false) {
+                                    format!("/* {ty} */")
+                                } else {
+                                    "/* value */".to_string()
+                                }
+                            })
+                            .collect::<Vec<_>>()
+                            .join(", "),
+                        Applicability::HasPlaceholders,
                     ),
-                    _ => ("...".to_string(), Applicability::HasPlaceholders),
+                    _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
                 };
 
                 applicability = applicability.max(this_applicability);
diff --git a/src/test/ui/binop/issue-77910-2.stderr b/src/test/ui/binop/issue-77910-2.stderr
index 74860a93f3799..a334bd8562593 100644
--- a/src/test/ui/binop/issue-77910-2.stderr
+++ b/src/test/ui/binop/issue-77910-2.stderr
@@ -8,8 +8,8 @@ LL |     if foo == y {}
    |
 help: use parentheses to call this function
    |
-LL |     if foo(_) == y {}
-   |           +++
+LL |     if foo(/* &i32 */) == y {}
+   |           ++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr
index 1d5e0a859a6da..2a674d3c1d23d 100644
--- a/src/test/ui/fn/fn-trait-formatting.stderr
+++ b/src/test/ui/fn/fn-trait-formatting.stderr
@@ -10,8 +10,8 @@ LL |     let _: () = Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>;
                  found struct `Box<dyn FnOnce(isize)>`
 help: use parentheses to call this trait object
    |
-LL |     let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(_);
-   |                 +                                                 ++++
+LL |     let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(/* isize */);
+   |                 +                                                 ++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-trait-formatting.rs:10:17
@@ -25,8 +25,8 @@ LL |     let _: () = Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>
                  found struct `Box<dyn Fn(isize, isize)>`
 help: use parentheses to call this trait object
    |
-LL |     let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(_, _);
-   |                 +                                                           +++++++
+LL |     let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(/* isize */, /* isize */);
+   |                 +                                                           +++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-trait-formatting.rs:14:17
diff --git a/src/test/ui/issues/issue-35241.stderr b/src/test/ui/issues/issue-35241.stderr
index a66289a1cf8eb..9ee7654a0885d 100644
--- a/src/test/ui/issues/issue-35241.stderr
+++ b/src/test/ui/issues/issue-35241.stderr
@@ -13,8 +13,8 @@ LL | fn test() -> Foo { Foo }
              found fn item `fn(u32) -> Foo {Foo}`
 help: use parentheses to instantiate this tuple struct
    |
-LL | fn test() -> Foo { Foo(_) }
-   |                       +++
+LL | fn test() -> Foo { Foo(/* u32 */) }
+   |                       +++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr
index df681eb2489c0..e5368ddf1e576 100644
--- a/src/test/ui/issues/issue-59488.stderr
+++ b/src/test/ui/issues/issue-59488.stderr
@@ -30,8 +30,8 @@ LL |     bar > 13;
    |
 help: use parentheses to call this function
    |
-LL |     bar(_) > 13;
-   |        +++
+LL |     bar(/* i64 */) > 13;
+   |        +++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/issue-59488.rs:18:11
diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr
index f885ac2151d61..7cf32775a33ef 100644
--- a/src/test/ui/resolve/privacy-enum-ctor.stderr
+++ b/src/test/ui/resolve/privacy-enum-ctor.stderr
@@ -329,8 +329,8 @@ LL |         let _: Z = Z::Fn;
            found fn item `fn(u8) -> Z {Z::Fn}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |         let _: Z = Z::Fn(_);
-   |                         +++
+LL |         let _: Z = Z::Fn(/* u8 */);
+   |                         ++++++++++
 
 error[E0618]: expected function, found enum variant `Z::Unit`
   --> $DIR/privacy-enum-ctor.rs:31:17
@@ -364,8 +364,8 @@ LL |     let _: E = m::E::Fn;
            found fn item `fn(u8) -> E {E::Fn}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |     let _: E = m::E::Fn(_);
-   |                        +++
+LL |     let _: E = m::E::Fn(/* u8 */);
+   |                        ++++++++++
 
 error[E0618]: expected function, found enum variant `m::E::Unit`
   --> $DIR/privacy-enum-ctor.rs:47:16
@@ -399,8 +399,8 @@ LL |     let _: E = E::Fn;
            found fn item `fn(u8) -> E {E::Fn}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |     let _: E = E::Fn(_);
-   |                     +++
+LL |     let _: E = E::Fn(/* u8 */);
+   |                     ++++++++++
 
 error[E0618]: expected function, found enum variant `E::Unit`
   --> $DIR/privacy-enum-ctor.rs:55:16
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
index ba710bfa746ae..3c7b895e337e7 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
@@ -33,8 +33,8 @@ LL |     let _: usize = foo;
            found fn item `fn(usize, usize) -> usize {foo}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = foo(_, _);
-   |                       ++++++
+LL |     let _: usize = foo(/* usize */, /* usize */);
+   |                       ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:30:16
@@ -51,8 +51,8 @@ LL |     let _: S = S;
              found fn item `fn(usize, usize) -> S {S}`
 help: use parentheses to instantiate this tuple struct
    |
-LL |     let _: S = S(_, _);
-   |                 ++++++
+LL |     let _: S = S(/* usize */, /* usize */);
+   |                 ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:31:20
@@ -105,8 +105,8 @@ LL |     let _: usize = T::baz;
            found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = T::baz(_, _);
-   |                          ++++++
+LL |     let _: usize = T::baz(/* usize */, /* usize */);
+   |                          ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:34:20
@@ -123,8 +123,8 @@ LL |     let _: usize = T::bat;
            found fn item `fn(usize) -> usize {<_ as T>::bat}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = T::bat(_);
-   |                          +++
+LL |     let _: usize = T::bat(/* usize */);
+   |                          +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:35:16
@@ -141,8 +141,8 @@ LL |     let _: E = E::A;
            found fn item `fn(usize) -> E {E::A}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |     let _: E = E::A(_);
-   |                    +++
+LL |     let _: E = E::A(/* usize */);
+   |                    +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:37:20
@@ -159,8 +159,8 @@ LL |     let _: usize = X::baz;
            found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::baz(_, _);
-   |                          ++++++
+LL |     let _: usize = X::baz(/* usize */, /* usize */);
+   |                          ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:38:20
@@ -177,8 +177,8 @@ LL |     let _: usize = X::bat;
            found fn item `fn(usize) -> usize {<X as T>::bat}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bat(_);
-   |                          +++
+LL |     let _: usize = X::bat(/* usize */);
+   |                          +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:39:20
@@ -195,8 +195,8 @@ LL |     let _: usize = X::bax;
            found fn item `fn(usize) -> usize {<X as T>::bax}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bax(_);
-   |                          +++
+LL |     let _: usize = X::bax(/* usize */);
+   |                          +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:40:20
@@ -213,8 +213,8 @@ LL |     let _: usize = X::bach;
            found fn item `fn(usize) -> usize {<X as T>::bach}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bach(_);
-   |                           +++
+LL |     let _: usize = X::bach(/* usize */);
+   |                           +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:41:20
@@ -231,8 +231,8 @@ LL |     let _: usize = X::ban;
            found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::ban(_);
-   |                          +++
+LL |     let _: usize = X::ban(/* &X */);
+   |                          ++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:42:20
@@ -249,8 +249,8 @@ LL |     let _: usize = X::bal;
            found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
 help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bal(_);
-   |                          +++
+LL |     let _: usize = X::bal(/* &X */);
+   |                          ++++++++++
 
 error[E0615]: attempted to take value of method `ban` on type `X`
   --> $DIR/fn-or-tuple-struct-without-args.rs:43:22
diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr
index 0e43ace893396..c1ca26ee9af1d 100644
--- a/src/test/ui/typeck/issue-87181/tuple-field.stderr
+++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr
@@ -6,8 +6,8 @@ LL |     thing.bar.0;
    |
 help: use parentheses to instantiate this tuple struct
    |
-LL |     (thing.bar)(_, _).0;
-   |     +         +++++++
+LL |     (thing.bar)(/* char */, /* u16 */).0;
+   |     +         ++++++++++++++++++++++++
 
 error: aborting due to previous error
 

From 7bb47a6f38d2c4673663fa1370817c7fb5e682c1 Mon Sep 17 00:00:00 2001
From: Chris Denton <christophersdenton@gmail.com>
Date: Sat, 20 Aug 2022 08:03:33 +0100
Subject: [PATCH 11/24] Reinstate preloading of some dll imports

---
 library/std/src/sys/windows/c.rs      |  3 -
 library/std/src/sys/windows/compat.rs | 82 +++++++++++++++++----------
 2 files changed, 52 insertions(+), 33 deletions(-)

diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index ef3f6a9ba1755..891d7e855f0b9 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -228,8 +228,6 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
 pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
 pub const MSG_PEEK: c_int = 0x2;
 
-pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800;
-
 #[repr(C)]
 #[derive(Copy, Clone)]
 pub struct linger {
@@ -1032,7 +1030,6 @@ extern "system" {
     pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void;
     pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE;
     pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
-    pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE;
 
     pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
     pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs
index 9c8ddc3aa1d25..7dff81ecb8dde 100644
--- a/library/std/src/sys/windows/compat.rs
+++ b/library/std/src/sys/windows/compat.rs
@@ -21,9 +21,52 @@
 
 use crate::ffi::{c_void, CStr};
 use crate::ptr::NonNull;
-use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::atomic::Ordering;
 use crate::sys::c;
 
+// This uses a static initializer to preload some imported functions.
+// The CRT (C runtime) executes static initializers before `main`
+// is called (for binaries) and before `DllMain` is called (for DLLs).
+//
+// It works by contributing a global symbol to the `.CRT$XCT` section.
+// The linker builds a table of all static initializer functions.
+// The CRT startup code then iterates that table, calling each
+// initializer function.
+//
+// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
+// If you're reading this and would like a guarantee here, please
+// file an issue for discussion; currently we don't guarantee any functionality
+// before main.
+// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
+#[used]
+#[link_section = ".CRT$XCT"]
+static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
+
+/// Preload some imported functions.
+///
+/// Note that any functions included here will be unconditionally loaded in
+/// the final binary, regardless of whether or not they're actually used.
+///
+/// Therefore, this should be limited to `compat_fn_optional` functions which
+/// must be preloaded or any functions where lazier loading demonstrates a
+/// negative performance impact in practical situations.
+///
+/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
+unsafe extern "C" fn init() {
+    // In an exe this code is executed before main() so is single threaded.
+    // In a DLL the system's loader lock will be held thereby synchronizing
+    // access. So the same best practices apply here as they do to running in DllMain:
+    // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
+    //
+    // DO NOT do anything interesting or complicated in this function! DO NOT call
+    // any Rust functions or CRT functions if those functions touch any global state,
+    // because this function runs during global initialization. For example, DO NOT
+    // do any dynamic allocation, don't call LoadLibrary, etc.
+
+    // Attempt to preload the synch functions.
+    load_synch_functions();
+}
+
 /// Helper macro for creating CStrs from literals and symbol names.
 macro_rules! ansi_str {
     (sym $ident:ident) => {{
@@ -75,20 +118,6 @@ impl Module {
         NonNull::new(module).map(Self)
     }
 
-    /// Load the library (if not already loaded)
-    ///
-    /// # Safety
-    ///
-    /// The module must not be unloaded.
-    pub unsafe fn load_system_library(name: &CStr) -> Option<Self> {
-        let module = c::LoadLibraryExA(
-            name.as_ptr(),
-            crate::ptr::null_mut(),
-            c::LOAD_LIBRARY_SEARCH_SYSTEM32,
-        );
-        NonNull::new(module).map(Self)
-    }
-
     // Try to get the address of a function.
     pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
         // SAFETY:
@@ -182,14 +211,10 @@ macro_rules! compat_fn_optional {
 
                 #[inline(always)]
                 pub fn option() -> Option<F> {
-                    let f = PTR.load(Ordering::Acquire);
-                    if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() }
-                }
-
-                #[cold]
-                fn try_load() -> Option<F> {
-                    $load_functions;
-                    NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(f) })
+                    // Miri does not understand the way we do preloading
+                    // therefore load the function here instead.
+                    #[cfg(miri)] $load_functions;
+                    NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
                 }
             }
         )+
@@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() {
 
         // Try loading the library and all the required functions.
         // If any step fails, then they all fail.
-        let library = unsafe { Module::load_system_library(MODULE_NAME) }?;
+        let library = unsafe { Module::new(MODULE_NAME) }?;
         let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
         let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
 
-        c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Release);
-        c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release);
+        c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
+        c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
         Some(())
     }
 
-    // Try to load the module but skip loading if a previous attempt failed.
-    static LOAD_MODULE: AtomicBool = AtomicBool::new(true);
-    let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some();
-    LOAD_MODULE.store(module_loaded, Ordering::Release)
+    try_load();
 }

From dacb6ee7b00fcd46f1a0e17dae2c81c7580843e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Marie?= <semarie@online.fr>
Date: Sun, 28 Aug 2022 05:16:02 +0000
Subject: [PATCH 12/24] add powerpc64-unknown-openbsd support

---
 compiler/rustc_target/src/spec/mod.rs           |  1 +
 .../src/spec/powerpc64_unknown_openbsd.rs       | 17 +++++++++++++++++
 src/doc/rustc/src/platform-support.md           |  1 +
 src/doc/rustc/src/platform-support/openbsd.md   |  1 +
 4 files changed, 20 insertions(+)
 create mode 100644 compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs

diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 46fea26283636..d579508768313 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -894,6 +894,7 @@ supported_targets! {
     ("aarch64-unknown-openbsd", aarch64_unknown_openbsd),
     ("i686-unknown-openbsd", i686_unknown_openbsd),
     ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
+    ("powerpc64-unknown-openbsd", powerpc64_unknown_openbsd),
     ("sparc64-unknown-openbsd", sparc64_unknown_openbsd),
     ("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
 
diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs
new file mode 100644
index 0000000000000..9cb3a67dc58b3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+    let mut base = super::openbsd_base::opts();
+    base.cpu = "ppc64".into();
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+    base.max_atomic_width = Some(64);
+
+    Target {
+        llvm_target: "powerpc64-unknown-openbsd".into(),
+        pointer_width: 64,
+        data_layout: "E-m:e-i64:64-n32:64".into(),
+        arch: "powerpc64".into(),
+        options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+    }
+}
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index f0f57f9338672..34bd201d1cd07 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -277,6 +277,7 @@ target | std | host | notes
 `powerpc64-unknown-linux-musl` | ? |  |
 `powerpc64-wrs-vxworks` | ? |  |
 `powerpc64le-unknown-linux-musl` | ? |  |
+[`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64
 `riscv32gc-unknown-linux-gnu` |   |   | RISC-V Linux (kernel 5.4, glibc 2.33)
 `riscv32gc-unknown-linux-musl` |   |   | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches)
 `riscv32im-unknown-none-elf` | * |  | Bare RISC-V (RV32IM ISA)
diff --git a/src/doc/rustc/src/platform-support/openbsd.md b/src/doc/rustc/src/platform-support/openbsd.md
index b2ac776eada48..432d8a9999419 100644
--- a/src/doc/rustc/src/platform-support/openbsd.md
+++ b/src/doc/rustc/src/platform-support/openbsd.md
@@ -12,6 +12,7 @@ The target names follow this format: `$ARCH-unknown-openbsd`, where `$ARCH` spec
 |--------------------------------|-------------|------------------|
 | `aarch64-unknown-openbsd`      | libc++      | [64-bit ARM systems](https://www.openbsd.org/arm64.html)  |
 | `i686-unknown-openbsd`         | libc++      | [Standard PC and clones based on the Intel i386 architecture and compatible processors](https://www.openbsd.org/i386.html) |
+| `powerpc64-unknown-openbsd`    | libc++      | [IBM POWER-based PowerNV systems](https://www.openbsd.org/powerpc64.html) |
 | `sparc64-unknown-openbsd`      | estdc++     | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) |
 | `x86_64-unknown-openbsd`       | libc++      | [AMD64-based systems](https://www.openbsd.org/amd64.html) |
 

From 1de5b226782361d9ea1929fd619f4fc2aaab1094 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Marie?= <semarie@online.fr>
Date: Sun, 28 Aug 2022 05:22:21 +0000
Subject: [PATCH 13/24] add riscv64gc-unknown-openbsd support (target
 riscv64-unknown-openbsd on OpenBSD)

- add platform-support documentation
- add riscv64gc-unknown-openbsd spec
- do not try to link with -latomic on openbsd
---
 compiler/rustc_llvm/build.rs                   |  4 ++--
 compiler/rustc_target/src/spec/mod.rs          |  1 +
 .../src/spec/riscv64gc_unknown_openbsd.rs      | 18 ++++++++++++++++++
 src/bootstrap/native.rs                        |  5 +++--
 src/doc/rustc/src/platform-support.md          |  1 +
 src/doc/rustc/src/platform-support/openbsd.md  |  1 +
 6 files changed, 26 insertions(+), 4 deletions(-)
 create mode 100644 compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs

diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs
index abb68f3afe527..28e092c1eb72c 100644
--- a/compiler/rustc_llvm/build.rs
+++ b/compiler/rustc_llvm/build.rs
@@ -342,10 +342,10 @@ fn main() {
     };
 
     // RISC-V GCC erroneously requires libatomic for sub-word
-    // atomic operations. FreeBSD uses Clang as its system
+    // atomic operations. Some BSD uses Clang as its system
     // compiler and provides no libatomic in its base system so
     // does not want this.
-    if !target.contains("freebsd") && target.starts_with("riscv") {
+    if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd") {
         println!("cargo:rustc-link-lib=atomic");
     }
 
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index d579508768313..d1bffa2e6ee30 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -895,6 +895,7 @@ supported_targets! {
     ("i686-unknown-openbsd", i686_unknown_openbsd),
     ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
     ("powerpc64-unknown-openbsd", powerpc64_unknown_openbsd),
+    ("riscv64gc-unknown-openbsd", riscv64gc_unknown_openbsd),
     ("sparc64-unknown-openbsd", sparc64_unknown_openbsd),
     ("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
 
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs
new file mode 100644
index 0000000000000..cd10f3afaac06
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs
@@ -0,0 +1,18 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        llvm_target: "riscv64-unknown-openbsd".into(),
+        pointer_width: 64,
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+        arch: "riscv64".into(),
+        options: TargetOptions {
+            code_model: Some(CodeModel::Medium),
+            cpu: "generic-rv64".into(),
+            features: "+m,+a,+f,+d,+c".into(),
+            llvm_abiname: "lp64d".into(),
+            max_atomic_width: Some(64),
+            ..super::openbsd_base::opts()
+        },
+    }
+}
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 7ecf74d3068d0..e1cc8d671d7fe 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -432,12 +432,13 @@ impl Step for Llvm {
             cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
         }
 
-        if target.starts_with("riscv") && !target.contains("freebsd") {
+        if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd")
+        {
             // RISC-V GCC erroneously requires linking against
             // `libatomic` when using 1-byte and 2-byte C++
             // atomics but the LLVM build system check cannot
             // detect this. Therefore it is set manually here.
-            // FreeBSD uses Clang as its system compiler and
+            // Some BSD uses Clang as its system compiler and
             // provides no libatomic in its base system so does
             // not want this.
             ldflags.exe.push(" -latomic");
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 34bd201d1cd07..742fbe11d9c6f 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -285,6 +285,7 @@ target | std | host | notes
 `riscv32imc-esp-espidf` | ✓ |  | RISC-V ESP-IDF
 `riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD
 `riscv64gc-unknown-linux-musl` |   |   | RISC-V Linux (kernel 4.20, musl 1.2.0)
+[`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
 `s390x-unknown-linux-musl` |  |  | S390x Linux (kernel 3.2, MUSL)
 `sparc-unknown-linux-gnu` | ✓ |  | 32-bit SPARC Linux
 `sparc64-unknown-netbsd` | ✓ | ✓ | NetBSD/sparc64
diff --git a/src/doc/rustc/src/platform-support/openbsd.md b/src/doc/rustc/src/platform-support/openbsd.md
index 432d8a9999419..4ce80157dbf9c 100644
--- a/src/doc/rustc/src/platform-support/openbsd.md
+++ b/src/doc/rustc/src/platform-support/openbsd.md
@@ -13,6 +13,7 @@ The target names follow this format: `$ARCH-unknown-openbsd`, where `$ARCH` spec
 | `aarch64-unknown-openbsd`      | libc++      | [64-bit ARM systems](https://www.openbsd.org/arm64.html)  |
 | `i686-unknown-openbsd`         | libc++      | [Standard PC and clones based on the Intel i386 architecture and compatible processors](https://www.openbsd.org/i386.html) |
 | `powerpc64-unknown-openbsd`    | libc++      | [IBM POWER-based PowerNV systems](https://www.openbsd.org/powerpc64.html) |
+| `riscv64gc-unknown-openbsd`    | libc++      | [64-bit RISC-V systems](https://www.openbsd.org/riscv64.html) |
 | `sparc64-unknown-openbsd`      | estdc++     | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) |
 | `x86_64-unknown-openbsd`       | libc++      | [AMD64-based systems](https://www.openbsd.org/amd64.html) |
 

From 1b8025a24c4b063d2566671f0664e5dfc263c2a4 Mon Sep 17 00:00:00 2001
From: Thom Chiovoloni <thom@shift.click>
Date: Mon, 29 Aug 2022 15:36:01 -0700
Subject: [PATCH 14/24] Fix some possible UB in std::sys::windows

---
 library/std/src/sys/windows/c.rs        |  6 ++++
 library/std/src/sys/windows/fs.rs       | 40 +++++++++++++++----------
 library/std/src/sys/windows/mod.rs      | 23 ++++++++++++++
 library/std/src/sys/windows/os/tests.rs | 18 +++++++++++
 4 files changed, 71 insertions(+), 16 deletions(-)

diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index ef3f6a9ba1755..865e2ad4b4347 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -503,6 +503,8 @@ pub struct FILE_END_OF_FILE_INFO {
     pub EndOfFile: LARGE_INTEGER,
 }
 
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `rest` field!
 #[repr(C)]
 pub struct REPARSE_DATA_BUFFER {
     pub ReparseTag: c_uint,
@@ -511,6 +513,8 @@ pub struct REPARSE_DATA_BUFFER {
     pub rest: (),
 }
 
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
 #[repr(C)]
 pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
     pub SubstituteNameOffset: c_ushort,
@@ -521,6 +525,8 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
     pub PathBuffer: WCHAR,
 }
 
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
 #[repr(C)]
 pub struct MOUNT_POINT_REPARSE_BUFFER {
     pub SubstituteNameOffset: c_ushort,
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 9653b1abfc3d8..01f914617a073 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -11,7 +11,7 @@ use crate::slice;
 use crate::sync::Arc;
 use crate::sys::handle::Handle;
 use crate::sys::time::SystemTime;
-use crate::sys::{c, cvt};
+use crate::sys::{c, cvt, AlignedAs};
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::thread;
 
@@ -47,6 +47,9 @@ pub struct ReadDir {
     first: Option<c::WIN32_FIND_DATAW>,
 }
 
+type AlignedReparseBuf =
+    AlignedAs<c::REPARSE_DATA_BUFFER, [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]>;
+
 struct FindNextFileHandle(c::HANDLE);
 
 unsafe impl Send for FindNextFileHandle {}
@@ -326,9 +329,9 @@ impl File {
             cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
             let mut reparse_tag = 0;
             if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
-                let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+                let mut b = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
-                    reparse_tag = buf.ReparseTag;
+                    reparse_tag = (*buf).ReparseTag;
                 }
             }
             Ok(FileAttr {
@@ -389,7 +392,7 @@ impl File {
             attr.file_size = info.AllocationSize as u64;
             attr.number_of_links = Some(info.NumberOfLinks);
             if attr.file_type().is_reparse_point() {
-                let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+                let mut b = AlignedReparseBuf::new([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
                     attr.reparse_tag = buf.ReparseTag;
                 }
@@ -458,10 +461,13 @@ impl File {
         Ok(Self { handle: self.handle.try_clone()? })
     }
 
-    fn reparse_point<'a>(
+    // NB: returned pointer is derived from `space`, and has provenance to
+    // match. A raw pointer is returned rather than a reference in order to
+    // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
+    fn reparse_point(
         &self,
-        space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
-    ) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
+        space: &mut AlignedReparseBuf,
+    ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
         unsafe {
             let mut bytes = 0;
             cvt({
@@ -470,26 +476,27 @@ impl File {
                     c::FSCTL_GET_REPARSE_POINT,
                     ptr::null_mut(),
                     0,
-                    space.as_mut_ptr() as *mut _,
-                    space.len() as c::DWORD,
+                    space.value.as_mut_ptr() as *mut _,
+                    space.value.len() as c::DWORD,
                     &mut bytes,
                     ptr::null_mut(),
                 )
             })?;
-            Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
+            Ok((bytes, space.value.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
         }
     }
 
     fn readlink(&self) -> io::Result<PathBuf> {
-        let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+        let mut space = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
         let (_bytes, buf) = self.reparse_point(&mut space)?;
         unsafe {
-            let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag {
+            let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
                 c::IO_REPARSE_TAG_SYMLINK => {
                     let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
-                        &buf.rest as *const _ as *const _;
+                        ptr::addr_of!((*buf).rest).cast();
+                    assert!(info.is_aligned());
                     (
-                        &(*info).PathBuffer as *const _ as *const u16,
+                        ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
@@ -497,9 +504,10 @@ impl File {
                 }
                 c::IO_REPARSE_TAG_MOUNT_POINT => {
                     let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
-                        &buf.rest as *const _ as *const _;
+                        ptr::addr_of!((*buf).rest).cast();
+                    assert!(info.is_aligned());
                     (
-                        &(*info).PathBuffer as *const _ as *const u16,
+                        ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         false,
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index a9846a484880b..fbbc5cb79fb8b 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -329,3 +329,26 @@ pub fn abort_internal() -> ! {
     }
     crate::intrinsics::abort();
 }
+
+/// Used for some win32 buffers which are stack allocated, for example:
+/// `AlignedAs<c::REPARSE_DATA_BUFFER, [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]>`
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct AlignedAs<Aligner, Alignee: ?Sized> {
+    /// Use `[Aligner; 0]` as a sort of `PhantomAlignNextField<Aligner>`. This
+    /// is a bit of a hack, and could break (in a way that's caught by tests) if
+    /// #81996 is fixed.
+    aligner: [Aligner; 0],
+    /// The aligned value. Public rather than exposed via accessors so that if
+    /// needed it can be used with `addr_of` and such (also, this is less code).
+    pub value: Alignee,
+}
+
+impl<Aligner, Alignee> AlignedAs<Aligner, Alignee> {
+    // This is frequently used with large stack buffers, so force-inline to
+    // try and avoid using 2x as much stack space in debug builds.
+    #[inline(always)]
+    pub const fn new(value: Alignee) -> Self {
+        Self { aligner: [], value }
+    }
+}
diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs
index 458d6e11c2098..532be0cf083ba 100644
--- a/library/std/src/sys/windows/os/tests.rs
+++ b/library/std/src/sys/windows/os/tests.rs
@@ -11,3 +11,21 @@ fn ntstatus_error() {
             .contains("FormatMessageW() returned error")
     );
 }
+
+#[test]
+fn smoketest_aligned_as() {
+    use crate::{
+        mem::{align_of, size_of},
+        ptr::addr_of,
+        sys::{c, AlignedAs},
+    };
+    type AlignedReparseBuf =
+        AlignedAs<c::REPARSE_DATA_BUFFER, [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]>;
+    assert!(size_of::<AlignedReparseBuf>() >= c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+    assert_eq!(align_of::<AlignedReparseBuf>(), align_of::<c::REPARSE_DATA_BUFFER>());
+    let a = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+    // Quick and dirty offsetof check.
+    assert_eq!(addr_of!(a).cast::<u8>(), addr_of!(a.value).cast::<u8>());
+    // Smoke check that it's actually aligned.
+    assert!(addr_of!(a.value).is_aligned_to(align_of::<c::REPARSE_DATA_BUFFER>()));
+}

From 5a4f7d4ad3e1124291b177af369023c3f181422e Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 30 Aug 2022 06:19:48 +0000
Subject: [PATCH 15/24] Tweak WellFormedLocs a bit

---
 compiler/rustc_typeck/src/check/wfcheck.rs    | 21 ++++++++++++++++---
 compiler/rustc_typeck/src/hir_wf_check.rs     |  4 ++++
 ...ature-gate-object_safe_for_dispatch.stderr |  8 +++----
 ...-trait-in-return-position-dyn-trait.stderr |  4 ++--
 ...bject-safety-associated-consts.curr.stderr |  4 ++--
 .../object-safety/object-safety-bounds.stderr |  4 ++--
 .../object-safety-generics.curr.stderr        |  8 +++----
 .../object-safety-mentions-Self.curr.stderr   |  8 +++----
 .../object-safety-no-static.curr.stderr       |  4 ++--
 .../object-safety-sized-2.curr.stderr         |  4 ++--
 .../object-safety-sized.curr.stderr           |  4 ++--
 src/test/ui/type/type-check-defaults.stderr   |  8 +++----
 src/test/ui/wf/wf-trait-fn-ret.stderr         |  4 ++--
 13 files changed, 52 insertions(+), 33 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index ce42647c837a5..ba42453bd60e9 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -1262,7 +1262,11 @@ fn check_impl<'tcx>(
             }
             None => {
                 let self_ty = tcx.type_of(item.def_id);
-                let self_ty = wfcx.normalize(item.span, None, self_ty);
+                let self_ty = wfcx.normalize(
+                    item.span,
+                    Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
+                    self_ty,
+                );
                 wfcx.register_wf_obligation(
                     ast_self_ty.span,
                     Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
@@ -1307,7 +1311,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
                     // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
                     // be sure if it will error or not as user might always specify the other.
                     if !ty.needs_subst() {
-                        wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into());
+                        wfcx.register_wf_obligation(
+                            tcx.def_span(param.def_id),
+                            Some(WellFormedLoc::Ty(param.def_id.expect_local())),
+                            ty.into(),
+                        );
                     }
                 }
             }
@@ -1512,7 +1520,14 @@ fn check_fn_or_method<'tcx>(
         );
     }
 
-    wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into());
+    wfcx.register_wf_obligation(
+        hir_decl.output.span(),
+        Some(WellFormedLoc::Param {
+            function: def_id,
+            param_idx: sig.inputs().len().try_into().unwrap(),
+        }),
+        sig.output().into(),
+    );
 
     check_where_clauses(wfcx, span, def_id);
 }
diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_typeck/src/hir_wf_check.rs
index fd9715e6ca374..7b080dc2942e0 100644
--- a/compiler/rustc_typeck/src/hir_wf_check.rs
+++ b/compiler/rustc_typeck/src/hir_wf_check.rs
@@ -140,6 +140,10 @@ fn diagnostic_hir_wf_check<'tcx>(
             hir::Node::ForeignItem(ForeignItem {
                 kind: ForeignItemKind::Static(ty, _), ..
             }) => Some(*ty),
+            hir::Node::GenericParam(hir::GenericParam {
+                kind: hir::GenericParamKind::Type { default: Some(ty), .. },
+                ..
+            }) => Some(*ty),
             ref node => bug!("Unexpected node {:?}", node),
         },
         WellFormedLoc::Param { function: _, param_idx } => {
diff --git a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr
index 72cb4cc843cc4..d76c697fe737d 100644
--- a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr
+++ b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr
@@ -13,10 +13,10 @@ LL | trait NonObjectSafe1: Sized {}
    |       this trait cannot be made into an object...
 
 error[E0038]: the trait `NonObjectSafe2` cannot be made into an object
-  --> $DIR/feature-gate-object_safe_for_dispatch.rs:22:36
+  --> $DIR/feature-gate-object_safe_for_dispatch.rs:22:45
    |
 LL | fn return_non_object_safe_ref() -> &'static dyn NonObjectSafe2 {
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object
+   |                                             ^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/feature-gate-object_safe_for_dispatch.rs:7:8
@@ -50,10 +50,10 @@ LL |     fn foo<T>(&self);
    = help: consider moving `foo` to another trait
 
 error[E0038]: the trait `NonObjectSafe4` cannot be made into an object
-  --> $DIR/feature-gate-object_safe_for_dispatch.rs:31:35
+  --> $DIR/feature-gate-object_safe_for_dispatch.rs:31:47
    |
 LL | fn return_non_object_safe_rc() -> std::rc::Rc<dyn NonObjectSafe4> {
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object
+   |                                               ^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/feature-gate-object_safe_for_dispatch.rs:15:22
diff --git a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr
index 365ecd9fcfa1d..687dbe65e6c38 100644
--- a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr
+++ b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr
@@ -21,10 +21,10 @@ LL |     fn foo() -> Self where Self: Sized;
    |                      +++++++++++++++++
 
 error[E0038]: the trait `NotObjectSafe` cannot be made into an object
-  --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:13
+  --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:17
    |
 LL | fn cat() -> Box<dyn NotObjectSafe> {
-   |             ^^^^^^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object
+   |                 ^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:3:8
diff --git a/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr b/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr
index 9dd144fee24a6..5f94c9284ea66 100644
--- a/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr
+++ b/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr
@@ -1,8 +1,8 @@
 error[E0038]: the trait `Bar` cannot be made into an object
-  --> $DIR/object-safety-associated-consts.rs:12:30
+  --> $DIR/object-safety-associated-consts.rs:12:31
    |
 LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
-   |                              ^^^^^^^^ `Bar` cannot be made into an object
+   |                               ^^^^^^^ `Bar` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-associated-consts.rs:9:11
diff --git a/src/test/ui/object-safety/object-safety-bounds.stderr b/src/test/ui/object-safety/object-safety-bounds.stderr
index 89c4f8ced79f6..29ffb5448427d 100644
--- a/src/test/ui/object-safety/object-safety-bounds.stderr
+++ b/src/test/ui/object-safety/object-safety-bounds.stderr
@@ -1,8 +1,8 @@
 error[E0038]: the trait `X` cannot be made into an object
-  --> $DIR/object-safety-bounds.rs:7:11
+  --> $DIR/object-safety-bounds.rs:7:15
    |
 LL | fn f() -> Box<dyn X<U = u32>> {
-   |           ^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object
+   |               ^^^^^^^^^^^^^^ `X` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-bounds.rs:4:13
diff --git a/src/test/ui/object-safety/object-safety-generics.curr.stderr b/src/test/ui/object-safety/object-safety-generics.curr.stderr
index 345950f1ae670..4581037526397 100644
--- a/src/test/ui/object-safety/object-safety-generics.curr.stderr
+++ b/src/test/ui/object-safety/object-safety-generics.curr.stderr
@@ -1,8 +1,8 @@
 error[E0038]: the trait `Bar` cannot be made into an object
-  --> $DIR/object-safety-generics.rs:18:30
+  --> $DIR/object-safety-generics.rs:18:31
    |
 LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
-   |                              ^^^^^^^^ `Bar` cannot be made into an object
+   |                               ^^^^^^^ `Bar` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-generics.rs:10:8
@@ -14,10 +14,10 @@ LL |     fn bar<T>(&self, t: T);
    = help: consider moving `bar` to another trait
 
 error[E0038]: the trait `Bar` cannot be made into an object
-  --> $DIR/object-safety-generics.rs:24:39
+  --> $DIR/object-safety-generics.rs:24:40
    |
 LL | fn make_bar_explicit<T:Bar>(t: &T) -> &dyn Bar {
-   |                                       ^^^^^^^^ `Bar` cannot be made into an object
+   |                                        ^^^^^^^ `Bar` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-generics.rs:10:8
diff --git a/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr b/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr
index f91c9b9856055..de430a89bf82b 100644
--- a/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr
+++ b/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr
@@ -1,8 +1,8 @@
 error[E0038]: the trait `Bar` cannot be made into an object
-  --> $DIR/object-safety-mentions-Self.rs:22:30
+  --> $DIR/object-safety-mentions-Self.rs:22:31
    |
 LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
-   |                              ^^^^^^^^ `Bar` cannot be made into an object
+   |                               ^^^^^^^ `Bar` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-mentions-Self.rs:11:22
@@ -14,10 +14,10 @@ LL |     fn bar(&self, x: &Self);
    = help: consider moving `bar` to another trait
 
 error[E0038]: the trait `Baz` cannot be made into an object
-  --> $DIR/object-safety-mentions-Self.rs:28:30
+  --> $DIR/object-safety-mentions-Self.rs:28:31
    |
 LL | fn make_baz<T:Baz>(t: &T) -> &dyn Baz {
-   |                              ^^^^^^^^ `Baz` cannot be made into an object
+   |                               ^^^^^^^ `Baz` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-mentions-Self.rs:15:22
diff --git a/src/test/ui/object-safety/object-safety-no-static.curr.stderr b/src/test/ui/object-safety/object-safety-no-static.curr.stderr
index bd8cf4e30f7fa..1b025229e543e 100644
--- a/src/test/ui/object-safety/object-safety-no-static.curr.stderr
+++ b/src/test/ui/object-safety/object-safety-no-static.curr.stderr
@@ -1,8 +1,8 @@
 error[E0038]: the trait `Foo` cannot be made into an object
-  --> $DIR/object-safety-no-static.rs:12:18
+  --> $DIR/object-safety-no-static.rs:12:22
    |
 LL | fn diverges() -> Box<dyn Foo> {
-   |                  ^^^^^^^^^^^^ `Foo` cannot be made into an object
+   |                      ^^^^^^^ `Foo` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-no-static.rs:9:8
diff --git a/src/test/ui/object-safety/object-safety-sized-2.curr.stderr b/src/test/ui/object-safety/object-safety-sized-2.curr.stderr
index 71236c8e38438..b019264128e76 100644
--- a/src/test/ui/object-safety/object-safety-sized-2.curr.stderr
+++ b/src/test/ui/object-safety/object-safety-sized-2.curr.stderr
@@ -1,8 +1,8 @@
 error[E0038]: the trait `Bar` cannot be made into an object
-  --> $DIR/object-safety-sized-2.rs:14:30
+  --> $DIR/object-safety-sized-2.rs:14:31
    |
 LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
-   |                              ^^^^^^^^ `Bar` cannot be made into an object
+   |                               ^^^^^^^ `Bar` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-sized-2.rs:9:18
diff --git a/src/test/ui/object-safety/object-safety-sized.curr.stderr b/src/test/ui/object-safety/object-safety-sized.curr.stderr
index 94b06ee934d44..97481312142fb 100644
--- a/src/test/ui/object-safety/object-safety-sized.curr.stderr
+++ b/src/test/ui/object-safety/object-safety-sized.curr.stderr
@@ -1,8 +1,8 @@
 error[E0038]: the trait `Bar` cannot be made into an object
-  --> $DIR/object-safety-sized.rs:12:30
+  --> $DIR/object-safety-sized.rs:12:31
    |
 LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
-   |                              ^^^^^^^^ `Bar` cannot be made into an object
+   |                               ^^^^^^^ `Bar` cannot be made into an object
    |
 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> $DIR/object-safety-sized.rs:8:13
diff --git a/src/test/ui/type/type-check-defaults.stderr b/src/test/ui/type/type-check-defaults.stderr
index 56a9b5317f76e..cf77c057d46d4 100644
--- a/src/test/ui/type/type-check-defaults.stderr
+++ b/src/test/ui/type/type-check-defaults.stderr
@@ -1,8 +1,8 @@
 error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32`
-  --> $DIR/type-check-defaults.rs:6:19
+  --> $DIR/type-check-defaults.rs:6:23
    |
 LL | struct WellFormed<Z = Foo<i32, i32>>(Z);
-   |                   ^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
+   |                       ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
    |
    = help: the trait `FromIterator<i32>` is not implemented for `i32`
 note: required by a bound in `Foo`
@@ -12,10 +12,10 @@ LL | struct Foo<T, U: FromIterator<T>>(T, U);
    |                  ^^^^^^^^^^^^^^^ required by this bound in `Foo`
 
 error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32`
-  --> $DIR/type-check-defaults.rs:8:27
+  --> $DIR/type-check-defaults.rs:8:38
    |
 LL | struct WellFormedNoBounds<Z:?Sized = Foo<i32, i32>>(Z);
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
+   |                                      ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
    |
    = help: the trait `FromIterator<i32>` is not implemented for `i32`
 note: required by a bound in `Foo`
diff --git a/src/test/ui/wf/wf-trait-fn-ret.stderr b/src/test/ui/wf/wf-trait-fn-ret.stderr
index a59ba3400a4d1..9bd3cc7711b84 100644
--- a/src/test/ui/wf/wf-trait-fn-ret.stderr
+++ b/src/test/ui/wf/wf-trait-fn-ret.stderr
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `Self: Eq` is not satisfied
-  --> $DIR/wf-trait-fn-ret.rs:10:22
+  --> $DIR/wf-trait-fn-ret.rs:10:23
    |
 LL |     fn bar(&self) -> &Bar<Self>;
-   |                      ^^^^^^^^^^ the trait `Eq` is not implemented for `Self`
+   |                       ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
    |
 note: required by a bound in `Bar`
   --> $DIR/wf-trait-fn-ret.rs:7:14

From d9c760db43d8ab701a71f633d820efc72d8cedea Mon Sep 17 00:00:00 2001
From: Thom Chiovoloni <thom@shift.click>
Date: Tue, 30 Aug 2022 00:16:53 -0700
Subject: [PATCH 16/24] Fix UWP and use `AlignedReparseBuf` in
 `symlink_junction_inner`

---
 library/std/src/sys/windows/fs.rs | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 01f914617a073..1545ba0d023fa 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -394,7 +394,7 @@ impl File {
             if attr.file_type().is_reparse_point() {
                 let mut b = AlignedReparseBuf::new([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
-                    attr.reparse_tag = buf.ReparseTag;
+                    attr.reparse_tag = (*buf).ReparseTag;
                 }
             }
             Ok(attr)
@@ -1345,9 +1345,10 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
     let h = f.as_inner().as_raw_handle();
 
     unsafe {
-        let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
-        let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
-        let buf = &mut (*db).ReparseTarget as *mut c::WCHAR;
+        let mut data = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+        let data_ptr = data.value.as_mut_ptr();
+        let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
+        let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
         let mut i = 0;
         // FIXME: this conversion is very hacky
         let v = br"\??\";
@@ -1367,7 +1368,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
         cvt(c::DeviceIoControl(
             h as *mut _,
             c::FSCTL_SET_REPARSE_POINT,
-            data.as_ptr() as *mut _,
+            data_ptr.cast(),
             (*db).ReparseDataLength + 8,
             ptr::null_mut(),
             0,

From 8af7f4208a789ba27a341c99df2de9c018b79828 Mon Sep 17 00:00:00 2001
From: Xiretza <xiretza@xiretza.xyz>
Date: Mon, 22 Aug 2022 13:38:08 +0200
Subject: [PATCH 17/24] Code deduplication in tool_only_multipart_suggestion

---
 compiler/rustc_errors/src/diagnostic.rs | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 506198df4d8ed..f75e2596f361b 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -686,19 +686,12 @@ impl Diagnostic {
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self {
-        assert!(!suggestion.is_empty());
-        self.push_suggestion(CodeSuggestion {
-            substitutions: vec![Substitution {
-                parts: suggestion
-                    .into_iter()
-                    .map(|(span, snippet)| SubstitutionPart { snippet, span })
-                    .collect(),
-            }],
-            msg: self.subdiagnostic_message_to_diagnostic_message(msg),
-            style: SuggestionStyle::CompletelyHidden,
+        self.multipart_suggestion_with_style(
+            msg,
+            suggestion,
             applicability,
-        });
-        self
+            SuggestionStyle::CompletelyHidden,
+        )
     }
 
     /// Prints out a message with a suggested edit of the code.

From 6e8dad5c073555dcbea9a276f76db47e3d7cc693 Mon Sep 17 00:00:00 2001
From: Xiretza <xiretza@xiretza.xyz>
Date: Mon, 22 Aug 2022 18:18:54 +0200
Subject: [PATCH 18/24] Use span_suggestion_with_style in SessionSubdiagnostic
 derive

---
 .../src/diagnostics/subdiagnostic.rs          | 104 +++++++++++-------
 1 file changed, 65 insertions(+), 39 deletions(-)

diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 666dbc23c287c..0db15f0feea72 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -28,6 +28,39 @@ enum SubdiagnosticSuggestionKind {
     Verbose,
 }
 
+impl FromStr for SubdiagnosticSuggestionKind {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "" => Ok(SubdiagnosticSuggestionKind::Normal),
+            "_short" => Ok(SubdiagnosticSuggestionKind::Short),
+            "_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden),
+            "_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose),
+            _ => Err(()),
+        }
+    }
+}
+
+impl SubdiagnosticSuggestionKind {
+    pub fn to_suggestion_style(&self) -> TokenStream {
+        match self {
+            SubdiagnosticSuggestionKind::Normal => {
+                quote! { rustc_errors::SuggestionStyle::ShowCode }
+            }
+            SubdiagnosticSuggestionKind::Short => {
+                quote! { rustc_errors::SuggestionStyle::HideCodeInline }
+            }
+            SubdiagnosticSuggestionKind::Hidden => {
+                quote! { rustc_errors::SuggestionStyle::HideCodeAlways }
+            }
+            SubdiagnosticSuggestionKind::Verbose => {
+                quote! { rustc_errors::SuggestionStyle::ShowAlways }
+            }
+        }
+    }
+}
+
 /// Which kind of subdiagnostic is being created from a variant?
 #[derive(Clone, Copy)]
 enum SubdiagnosticKind {
@@ -52,17 +85,15 @@ impl FromStr for SubdiagnosticKind {
             "note" => Ok(SubdiagnosticKind::Note),
             "help" => Ok(SubdiagnosticKind::Help),
             "warning" => Ok(SubdiagnosticKind::Warn),
-            "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
-            "suggestion_short" => {
-                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
-            }
-            "suggestion_hidden" => {
-                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden))
-            }
-            "suggestion_verbose" => {
-                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose))
+            _ => {
+                if let Some(suggestion_kind) =
+                    s.strip_prefix("suggestion").and_then(|s| s.parse().ok())
+                {
+                    return Ok(SubdiagnosticKind::Suggestion(suggestion_kind));
+                };
+
+                Err(())
             }
-            _ => Err(()),
         }
     }
 }
@@ -74,18 +105,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
             SubdiagnosticKind::Note => write!(f, "note"),
             SubdiagnosticKind::Help => write!(f, "help"),
             SubdiagnosticKind::Warn => write!(f, "warn"),
-            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
-                write!(f, "suggestion")
-            }
-            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => {
-                write!(f, "suggestion_short")
-            }
-            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => {
-                write!(f, "suggestion_hidden")
-            }
-            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => {
-                write!(f, "suggestion_verbose")
-            }
+            SubdiagnosticKind::Suggestion(..) => write!(f, "suggestion_with_style"),
         }
     }
 
@@ -461,25 +481,31 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
         let diag = &self.diag;
         let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
         let message = quote! { rustc_errors::fluent::#slug };
-        let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
-            if let Some(span) = span_field {
-                quote! { #diag.#name(#span, #message, #code, #applicability); }
-            } else {
-                span_err(self.span, "suggestion without `#[primary_span]` field").emit();
-                quote! { unreachable!(); }
+        let call = match kind {
+            SubdiagnosticKind::Suggestion(style) => {
+                if let Some(span) = span_field {
+                    let style = style.to_suggestion_style();
+
+                    quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
+                } else {
+                    span_err(self.span, "suggestion without `#[primary_span]` field").emit();
+                    quote! { unreachable!(); }
+                }
             }
-        } else if matches!(kind, SubdiagnosticKind::Label) {
-            if let Some(span) = span_field {
-                quote! { #diag.#name(#span, #message); }
-            } else {
-                span_err(self.span, "label without `#[primary_span]` field").emit();
-                quote! { unreachable!(); }
+            SubdiagnosticKind::Label => {
+                if let Some(span) = span_field {
+                    quote! { #diag.#name(#span, #message); }
+                } else {
+                    span_err(self.span, "label without `#[primary_span]` field").emit();
+                    quote! { unreachable!(); }
+                }
             }
-        } else {
-            if let Some(span) = span_field {
-                quote! { #diag.#name(#span, #message); }
-            } else {
-                quote! { #diag.#name(#message); }
+            _ => {
+                if let Some(span) = span_field {
+                    quote! { #diag.#name(#span, #message); }
+                } else {
+                    quote! { #diag.#name(#message); }
+                }
             }
         };
 

From 9dc0643744c87e5470e29718b86f54430cc7ba04 Mon Sep 17 00:00:00 2001
From: Xiretza <xiretza@xiretza.xyz>
Date: Fri, 26 Aug 2022 11:09:06 +0200
Subject: [PATCH 19/24] SessionSubdiagnostic: make `#[applicability]` optional

---
 .../src/diagnostics/subdiagnostic.rs          | 12 ++---
 .../subdiagnostic-derive.rs                   |  5 +-
 .../subdiagnostic-derive.stderr               | 51 +++----------------
 3 files changed, 13 insertions(+), 55 deletions(-)

diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 0db15f0feea72..f66565af4c1d8 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -469,14 +469,10 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
         };
 
         let span_field = self.span_field.as_ref().map(|(span, _)| span);
-        let applicability = match self.applicability.clone() {
-            Some((applicability, _)) => Some(applicability),
-            None if is_suggestion => {
-                span_err(self.span, "suggestion without `applicability`").emit();
-                Some(quote! { rustc_errors::Applicability::Unspecified })
-            }
-            None => None,
-        };
+        let applicability = self.applicability.take().map_or_else(
+            || quote! { rustc_errors::Applicability::Unspecified },
+            |(applicability, _)| applicability,
+        );
 
         let diag = &self.diag;
         let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
index ddfc0d3365df0..2505a5a9975c1 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
@@ -401,7 +401,6 @@ struct AK {
 
 #[derive(SessionSubdiagnostic)]
 #[suggestion(parser::add_paren, code = "...")]
-//~^ ERROR suggestion without `applicability`
 struct AL {
     #[primary_span]
     span: Span,
@@ -412,7 +411,6 @@ struct AL {
 
 #[derive(SessionSubdiagnostic)]
 #[suggestion(parser::add_paren, code = "...")]
-//~^ ERROR suggestion without `applicability`
 struct AM {
     #[primary_span]
     span: Span,
@@ -448,8 +446,7 @@ struct AQ;
 
 #[derive(SessionSubdiagnostic)]
 #[suggestion(parser::add_paren, code = "...")]
-//~^ ERROR suggestion without `applicability`
-//~^^ ERROR suggestion without `#[primary_span]` field
+//~^ ERROR suggestion without `#[primary_span]` field
 struct AR {
     var: String,
 }
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
index a289c4fffd936..68d33323db035 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
@@ -249,36 +249,13 @@ LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
 error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
-  --> $DIR/subdiagnostic-derive.rs:408:5
+  --> $DIR/subdiagnostic-derive.rs:407:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
-error: suggestion without `applicability`
-  --> $DIR/subdiagnostic-derive.rs:403:1
-   |
-LL | / #[suggestion(parser::add_paren, code = "...")]
-LL | |
-LL | | struct AL {
-LL | |     #[primary_span]
-...  |
-LL | |     applicability: Span,
-LL | | }
-   | |_^
-
-error: suggestion without `applicability`
-  --> $DIR/subdiagnostic-derive.rs:414:1
-   |
-LL | / #[suggestion(parser::add_paren, code = "...")]
-LL | |
-LL | | struct AM {
-LL | |     #[primary_span]
-LL | |     span: Span,
-LL | | }
-   | |_^
-
 error: suggestion without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:422:1
+  --> $DIR/subdiagnostic-derive.rs:420:1
    |
 LL | / #[suggestion(parser::add_paren)]
 LL | |
@@ -290,47 +267,35 @@ LL | | }
    | |_^
 
 error: invalid applicability
-  --> $DIR/subdiagnostic-derive.rs:432:46
+  --> $DIR/subdiagnostic-derive.rs:430:46
    |
 LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
    |                                              ^^^^^^^^^^^^^^^^^^^^^
 
-error: suggestion without `applicability`
-  --> $DIR/subdiagnostic-derive.rs:450:1
-   |
-LL | / #[suggestion(parser::add_paren, code = "...")]
-LL | |
-LL | |
-LL | | struct AR {
-LL | |     var: String,
-LL | | }
-   | |_^
-
 error: suggestion without `#[primary_span]` field
-  --> $DIR/subdiagnostic-derive.rs:450:1
+  --> $DIR/subdiagnostic-derive.rs:448:1
    |
 LL | / #[suggestion(parser::add_paren, code = "...")]
 LL | |
-LL | |
 LL | | struct AR {
 LL | |     var: String,
 LL | | }
    | |_^
 
 error: unsupported type attribute for subdiagnostic enum
-  --> $DIR/subdiagnostic-derive.rs:465:1
+  --> $DIR/subdiagnostic-derive.rs:462:1
    |
 LL | #[label]
    | ^^^^^^^^
 
 error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:485:39
+  --> $DIR/subdiagnostic-derive.rs:482:39
    |
 LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
    |                                       ^^^^^^^
 
 error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:504:43
+  --> $DIR/subdiagnostic-derive.rs:501:43
    |
 LL |     #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
    |                                           ^^^^^^^
@@ -395,6 +360,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
 LL | #[label(slug)]
    |         ^^^^ not found in `rustc_errors::fluent`
 
-error: aborting due to 52 previous errors
+error: aborting due to 49 previous errors
 
 For more information about this error, try `rustc --explain E0425`.

From 1f69fbc345bafc0839111eefff2501398a2681a2 Mon Sep 17 00:00:00 2001
From: Xiretza <xiretza@xiretza.xyz>
Date: Fri, 26 Aug 2022 11:17:42 +0200
Subject: [PATCH 20/24] Unify indentation in subdiagnostic-derive test

---
 .../subdiagnostic-derive.rs                   | 32 +++++++++----------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
index 2505a5a9975c1..0871ad6b22b39 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
@@ -167,8 +167,8 @@ enum P {
 #[derive(SessionSubdiagnostic)]
 enum Q {
     #[bar]
-//~^ ERROR `#[bar]` is not a valid attribute
-//~^^ ERROR cannot find attribute `bar` in this scope
+    //~^ ERROR `#[bar]` is not a valid attribute
+    //~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -179,8 +179,8 @@ enum Q {
 #[derive(SessionSubdiagnostic)]
 enum R {
     #[bar = "..."]
-//~^ ERROR `#[bar = ...]` is not a valid attribute
-//~^^ ERROR cannot find attribute `bar` in this scope
+    //~^ ERROR `#[bar = ...]` is not a valid attribute
+    //~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -191,8 +191,8 @@ enum R {
 #[derive(SessionSubdiagnostic)]
 enum S {
     #[bar = 4]
-//~^ ERROR `#[bar = ...]` is not a valid attribute
-//~^^ ERROR cannot find attribute `bar` in this scope
+    //~^ ERROR `#[bar = ...]` is not a valid attribute
+    //~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -203,8 +203,8 @@ enum S {
 #[derive(SessionSubdiagnostic)]
 enum T {
     #[bar("...")]
-//~^ ERROR `#[bar("...")]` is not a valid attribute
-//~^^ ERROR cannot find attribute `bar` in this scope
+    //~^ ERROR `#[bar(...)]` is not a valid attribute
+    //~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -215,7 +215,7 @@ enum T {
 #[derive(SessionSubdiagnostic)]
 enum U {
     #[label(code = "...")]
-//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
+    //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
     A {
         #[primary_span]
         span: Span,
@@ -232,7 +232,7 @@ enum V {
         var: String,
     },
     B {
-//~^ ERROR subdiagnostic kind not specified
+    //~^ ERROR subdiagnostic kind not specified
         #[primary_span]
         span: Span,
         var: String,
@@ -331,16 +331,16 @@ struct AE {
 #[label(parser::add_paren)]
 struct AF {
     #[primary_span]
-//~^ NOTE previously specified here
+    //~^ NOTE previously specified here
     span_a: Span,
     #[primary_span]
-//~^ ERROR specified multiple times
+    //~^ ERROR specified multiple times
     span_b: Span,
 }
 
 #[derive(SessionSubdiagnostic)]
 struct AG {
-//~^ ERROR subdiagnostic kind not specified
+    //~^ ERROR subdiagnostic kind not specified
     #[primary_span]
     span: Span,
 }
@@ -392,10 +392,10 @@ struct AK {
     #[primary_span]
     span: Span,
     #[applicability]
-//~^ NOTE previously specified here
+    //~^ NOTE previously specified here
     applicability_a: Applicability,
     #[applicability]
-//~^ ERROR specified multiple times
+    //~^ ERROR specified multiple times
     applicability_b: Applicability,
 }
 
@@ -405,7 +405,7 @@ struct AL {
     #[primary_span]
     span: Span,
     #[applicability]
-//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
+    //~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
     applicability: Span,
 }
 

From 5c3490c90186a1b4d1ac66b57ddede55410409e1 Mon Sep 17 00:00:00 2001
From: Thom Chiovoloni <thom@shift.click>
Date: Tue, 30 Aug 2022 01:15:59 -0700
Subject: [PATCH 21/24] Replace `AlignedAs` with a more specific `Align8` type

---
 library/std/src/sys/windows/fs.rs       | 27 +++++++++++++------------
 library/std/src/sys/windows/mod.rs      | 27 ++++++-------------------
 library/std/src/sys/windows/os/tests.rs | 18 -----------------
 3 files changed, 20 insertions(+), 52 deletions(-)

diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 1545ba0d023fa..c702b8e744c89 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -11,7 +11,7 @@ use crate::slice;
 use crate::sync::Arc;
 use crate::sys::handle::Handle;
 use crate::sys::time::SystemTime;
-use crate::sys::{c, cvt, AlignedAs};
+use crate::sys::{c, cvt, Align8};
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::thread;
 
@@ -47,9 +47,6 @@ pub struct ReadDir {
     first: Option<c::WIN32_FIND_DATAW>,
 }
 
-type AlignedReparseBuf =
-    AlignedAs<c::REPARSE_DATA_BUFFER, [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]>;
-
 struct FindNextFileHandle(c::HANDLE);
 
 unsafe impl Send for FindNextFileHandle {}
@@ -329,7 +326,7 @@ impl File {
             cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
             let mut reparse_tag = 0;
             if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
-                let mut b = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+                let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
                     reparse_tag = (*buf).ReparseTag;
                 }
@@ -392,7 +389,7 @@ impl File {
             attr.file_size = info.AllocationSize as u64;
             attr.number_of_links = Some(info.NumberOfLinks);
             if attr.file_type().is_reparse_point() {
-                let mut b = AlignedReparseBuf::new([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+                let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
                     attr.reparse_tag = (*buf).ReparseTag;
                 }
@@ -466,28 +463,32 @@ impl File {
     // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
     fn reparse_point(
         &self,
-        space: &mut AlignedReparseBuf,
+        space: &mut Align8<[u8]>,
     ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
         unsafe {
             let mut bytes = 0;
             cvt({
+                // Grab this in advance to avoid it invalidating the pointer
+                // we get from `space.0.as_mut_ptr()`.
+                let len = space.0.len();
                 c::DeviceIoControl(
                     self.handle.as_raw_handle(),
                     c::FSCTL_GET_REPARSE_POINT,
                     ptr::null_mut(),
                     0,
-                    space.value.as_mut_ptr() as *mut _,
-                    space.value.len() as c::DWORD,
+                    space.0.as_mut_ptr().cast(),
+                    len as c::DWORD,
                     &mut bytes,
                     ptr::null_mut(),
                 )
             })?;
-            Ok((bytes, space.value.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
+            const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
+            Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
         }
     }
 
     fn readlink(&self) -> io::Result<PathBuf> {
-        let mut space = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+        let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
         let (_bytes, buf) = self.reparse_point(&mut space)?;
         unsafe {
             let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
@@ -1345,8 +1346,8 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
     let h = f.as_inner().as_raw_handle();
 
     unsafe {
-        let mut data = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
-        let data_ptr = data.value.as_mut_ptr();
+        let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+        let data_ptr = data.0.as_mut_ptr();
         let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
         let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
         let mut i = 0;
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index fbbc5cb79fb8b..340cae4066bf4 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -330,25 +330,10 @@ pub fn abort_internal() -> ! {
     crate::intrinsics::abort();
 }
 
-/// Used for some win32 buffers which are stack allocated, for example:
-/// `AlignedAs<c::REPARSE_DATA_BUFFER, [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]>`
-#[repr(C)]
+/// Align the inner value to 8 bytes.
+///
+/// This is enough for almost all of the buffers we're likely to work with in
+/// the Windows APIs we use.
+#[repr(C, align(8))]
 #[derive(Copy, Clone)]
-pub struct AlignedAs<Aligner, Alignee: ?Sized> {
-    /// Use `[Aligner; 0]` as a sort of `PhantomAlignNextField<Aligner>`. This
-    /// is a bit of a hack, and could break (in a way that's caught by tests) if
-    /// #81996 is fixed.
-    aligner: [Aligner; 0],
-    /// The aligned value. Public rather than exposed via accessors so that if
-    /// needed it can be used with `addr_of` and such (also, this is less code).
-    pub value: Alignee,
-}
-
-impl<Aligner, Alignee> AlignedAs<Aligner, Alignee> {
-    // This is frequently used with large stack buffers, so force-inline to
-    // try and avoid using 2x as much stack space in debug builds.
-    #[inline(always)]
-    pub const fn new(value: Alignee) -> Self {
-        Self { aligner: [], value }
-    }
-}
+pub(crate) struct Align8<T: ?Sized>(pub T);
diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs
index 532be0cf083ba..458d6e11c2098 100644
--- a/library/std/src/sys/windows/os/tests.rs
+++ b/library/std/src/sys/windows/os/tests.rs
@@ -11,21 +11,3 @@ fn ntstatus_error() {
             .contains("FormatMessageW() returned error")
     );
 }
-
-#[test]
-fn smoketest_aligned_as() {
-    use crate::{
-        mem::{align_of, size_of},
-        ptr::addr_of,
-        sys::{c, AlignedAs},
-    };
-    type AlignedReparseBuf =
-        AlignedAs<c::REPARSE_DATA_BUFFER, [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]>;
-    assert!(size_of::<AlignedReparseBuf>() >= c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
-    assert_eq!(align_of::<AlignedReparseBuf>(), align_of::<c::REPARSE_DATA_BUFFER>());
-    let a = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
-    // Quick and dirty offsetof check.
-    assert_eq!(addr_of!(a).cast::<u8>(), addr_of!(a.value).cast::<u8>());
-    // Smoke check that it's actually aligned.
-    assert!(addr_of!(a.value).is_aligned_to(align_of::<c::REPARSE_DATA_BUFFER>()));
-}

From 31b939b3152ea70b2a84992905f48e125d85ca18 Mon Sep 17 00:00:00 2001
From: Xiretza <xiretza@xiretza.xyz>
Date: Tue, 23 Aug 2022 07:54:06 +0200
Subject: [PATCH 22/24] Rework SessionSubdiagnostic derive to support
 multipart_suggestion

---
 .../src/diagnostics/subdiagnostic.rs          | 608 +++++++++++-------
 compiler/rustc_macros/src/lib.rs              |   5 +
 .../subdiagnostic-derive.rs                   | 119 +++-
 .../subdiagnostic-derive.stderr               | 207 ++++--
 4 files changed, 672 insertions(+), 267 deletions(-)

diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index f66565af4c1d8..8b40e295bd8a7 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -12,7 +12,7 @@ use quote::{format_ident, quote};
 use std::collections::HashMap;
 use std::fmt;
 use std::str::FromStr;
-use syn::{parse_quote, spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
+use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
 use synstructure::{BindingInfo, Structure, VariantInfo};
 
 /// Which kind of suggestion is being created?
@@ -62,7 +62,7 @@ impl SubdiagnosticSuggestionKind {
 }
 
 /// Which kind of subdiagnostic is being created from a variant?
-#[derive(Clone, Copy)]
+#[derive(Clone)]
 enum SubdiagnosticKind {
     /// `#[label(...)]`
     Label,
@@ -73,29 +73,9 @@ enum SubdiagnosticKind {
     /// `#[warning(...)]`
     Warn,
     /// `#[suggestion{,_short,_hidden,_verbose}]`
-    Suggestion(SubdiagnosticSuggestionKind),
-}
-
-impl FromStr for SubdiagnosticKind {
-    type Err = ();
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "label" => Ok(SubdiagnosticKind::Label),
-            "note" => Ok(SubdiagnosticKind::Note),
-            "help" => Ok(SubdiagnosticKind::Help),
-            "warning" => Ok(SubdiagnosticKind::Warn),
-            _ => {
-                if let Some(suggestion_kind) =
-                    s.strip_prefix("suggestion").and_then(|s| s.parse().ok())
-                {
-                    return Ok(SubdiagnosticKind::Suggestion(suggestion_kind));
-                };
-
-                Err(())
-            }
-        }
-    }
+    Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream },
+    /// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
+    MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind },
 }
 
 impl quote::IdentFragment for SubdiagnosticKind {
@@ -105,7 +85,10 @@ impl quote::IdentFragment for SubdiagnosticKind {
             SubdiagnosticKind::Note => write!(f, "note"),
             SubdiagnosticKind::Help => write!(f, "help"),
             SubdiagnosticKind::Warn => write!(f, "warn"),
-            SubdiagnosticKind::Suggestion(..) => write!(f, "suggestion_with_style"),
+            SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"),
+            SubdiagnosticKind::MultipartSuggestion { .. } => {
+                write!(f, "multipart_suggestion_with_style")
+            }
         }
     }
 
@@ -168,11 +151,9 @@ impl<'a> SessionSubdiagnosticDerive<'a> {
                     variant,
                     span,
                     fields: fields_map,
-                    kind: None,
-                    slug: None,
-                    code: None,
                     span_field: None,
                     applicability: None,
+                    has_suggestion_parts: false,
                 };
                 builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
             });
@@ -213,21 +194,15 @@ struct SessionSubdiagnosticDeriveBuilder<'a> {
     /// derive builder.
     fields: HashMap<String, TokenStream>,
 
-    /// Subdiagnostic kind of the type/variant.
-    kind: Option<(SubdiagnosticKind, proc_macro::Span)>,
-
-    /// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
-    /// `#[kind(slug)]` attribute on the type or variant.
-    slug: Option<(Path, proc_macro::Span)>,
-    /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
-    /// attribute on the type or variant.
-    code: Option<(TokenStream, proc_macro::Span)>,
-
     /// Identifier for the binding to the `#[primary_span]` field.
     span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
     /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
     /// `rustc_errors::Applicability::*` variant directly.
     applicability: Option<(TokenStream, proc_macro::Span)>,
+
+    /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
+    /// during finalization if still `false`.
+    has_suggestion_parts: bool,
 }
 
 impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
@@ -237,7 +212,11 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
 }
 
 impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
-    fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> {
+    fn identify_kind(
+        &mut self,
+    ) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
+        let mut kind_slug = None;
+
         for attr in self.variant.ast().attrs {
             let span = attr.span().unwrap();
 
@@ -245,116 +224,121 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
             let name = name.as_str();
 
             let meta = attr.parse_meta()?;
-            let kind = match meta {
-                Meta::List(MetaList { ref nested, .. }) => {
-                    let mut nested_iter = nested.into_iter();
-                    if let Some(nested_attr) = nested_iter.next() {
-                        match nested_attr {
-                            NestedMeta::Meta(Meta::Path(path)) => {
-                                self.slug.set_once((path.clone(), span));
-                            }
-                            NestedMeta::Meta(meta @ Meta::NameValue(_))
-                                if matches!(
-                                    meta.path().segments.last().unwrap().ident.to_string().as_str(),
-                                    "code" | "applicability"
-                                ) =>
-                            {
-                                // don't error for valid follow-up attributes
-                            }
-                            nested_attr => {
-                                throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                                    diag.help(
-                                        "first argument of the attribute should be the diagnostic \
-                                         slug",
-                                    )
-                                })
-                            }
-                        };
-                    }
+            let Meta::List(MetaList { ref nested, .. }) = meta else {
+                throw_invalid_attr!(attr, &meta);
+            };
 
-                    for nested_attr in nested_iter {
-                        let meta = match nested_attr {
-                            NestedMeta::Meta(ref meta) => meta,
-                            _ => throw_invalid_nested_attr!(attr, &nested_attr),
-                        };
-
-                        let span = meta.span().unwrap();
-                        let nested_name = meta.path().segments.last().unwrap().ident.to_string();
-                        let nested_name = nested_name.as_str();
-
-                        match meta {
-                            Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
-                                match nested_name {
-                                    "code" => {
-                                        let formatted_str = self.build_format(&s.value(), s.span());
-                                        self.code.set_once((formatted_str, span));
-                                    }
-                                    "applicability" => {
-                                        let value = match Applicability::from_str(&s.value()) {
-                                            Ok(v) => v,
-                                            Err(()) => {
-                                                span_err(span, "invalid applicability").emit();
-                                                Applicability::Unspecified
-                                            }
-                                        };
-                                        self.applicability.set_once((quote! { #value }, span));
-                                    }
-                                    _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                                        diag.help(
-                                            "only `code` and `applicability` are valid nested \
-                                             attributes",
-                                        )
-                                    }),
-                                }
-                            }
-                            _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                                if matches!(meta, Meta::Path(_)) {
-                                    diag.help(
-                                        "a diagnostic slug must be the first argument to the \
-                                         attribute",
-                                    )
-                                } else {
-                                    diag
-                                }
-                            }),
-                        }
+            let mut kind = match name {
+                "label" => SubdiagnosticKind::Label,
+                "note" => SubdiagnosticKind::Note,
+                "help" => SubdiagnosticKind::Help,
+                "warning" => SubdiagnosticKind::Warn,
+                _ => {
+                    if let Some(suggestion_kind) =
+                        name.strip_prefix("suggestion").and_then(|s| s.parse().ok())
+                    {
+                        SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() }
+                    } else if let Some(suggestion_kind) =
+                        name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
+                    {
+                        SubdiagnosticKind::MultipartSuggestion { suggestion_kind }
+                    } else {
+                        throw_invalid_attr!(attr, &meta);
                     }
-
-                    let Ok(kind) = SubdiagnosticKind::from_str(name) else {
-                        throw_invalid_attr!(attr, &meta)
-                    };
-
-                    kind
                 }
-                _ => throw_invalid_attr!(attr, &meta),
             };
 
-            if matches!(
-                kind,
-                SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
-            ) && self.code.is_some()
-            {
-                throw_span_err!(
-                    span,
-                    &format!("`code` is not a valid nested attribute of a `{}` attribute", name)
-                );
+            let mut slug = None;
+            let mut code = None;
+
+            let mut nested_iter = nested.into_iter();
+            if let Some(nested_attr) = nested_iter.next() {
+                match nested_attr {
+                    NestedMeta::Meta(Meta::Path(path)) => {
+                        slug.set_once((path.clone(), span));
+                    }
+                    NestedMeta::Meta(meta @ Meta::NameValue(_))
+                        if matches!(
+                            meta.path().segments.last().unwrap().ident.to_string().as_str(),
+                            "code" | "applicability"
+                        ) =>
+                    {
+                        // Don't error for valid follow-up attributes.
+                    }
+                    nested_attr => {
+                        throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                            diag.help(
+                                "first argument of the attribute should be the diagnostic \
+                                 slug",
+                            )
+                        })
+                    }
+                };
             }
 
-            if matches!(
-                kind,
-                SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
-            ) && self.applicability.is_some()
-            {
-                throw_span_err!(
-                    span,
-                    &format!(
-                        "`applicability` is not a valid nested attribute of a `{}` attribute",
-                        name
-                    )
-                );
+            for nested_attr in nested_iter {
+                let meta = match nested_attr {
+                    NestedMeta::Meta(ref meta) => meta,
+                    _ => throw_invalid_nested_attr!(attr, &nested_attr),
+                };
+
+                let span = meta.span().unwrap();
+                let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+                let nested_name = nested_name.as_str();
+
+                let value = match meta {
+                    Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value,
+                    Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                        diag.help("a diagnostic slug must be the first argument to the attribute")
+                    }),
+                    _ => throw_invalid_nested_attr!(attr, &nested_attr),
+                };
+
+                match nested_name {
+                    "code" => {
+                        if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
+                            let formatted_str = self.build_format(&value.value(), value.span());
+                            code.set_once((formatted_str, span));
+                        } else {
+                            span_err(
+                                span,
+                                &format!(
+                                    "`code` is not a valid nested attribute of a `{}` attribute",
+                                    name
+                                ),
+                            )
+                            .emit();
+                        }
+                    }
+                    "applicability" => {
+                        if matches!(
+                            kind,
+                            SubdiagnosticKind::Suggestion { .. }
+                                | SubdiagnosticKind::MultipartSuggestion { .. }
+                        ) {
+                            let value =
+                                Applicability::from_str(&value.value()).unwrap_or_else(|()| {
+                                    span_err(span, "invalid applicability").emit();
+                                    Applicability::Unspecified
+                                });
+                            self.applicability.set_once((quote! { #value }, span));
+                        } else {
+                            span_err(
+                                span,
+                                &format!(
+                                    "`applicability` is not a valid nested attribute of a `{}` attribute",
+                                    name
+                                )
+                            ).emit();
+                        }
+                    }
+                    _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                        diag.help("only `code` and `applicability` are valid nested attributes")
+                    }),
+                }
             }
 
-            if self.slug.is_none() {
+            let Some((slug, _)) = slug else {
                 throw_span_err!(
                     span,
                     &format!(
@@ -362,112 +346,275 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
                         name
                     )
                 );
+            };
+
+            match kind {
+                SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => {
+                    let Some((code, _)) = code else {
+                        throw_span_err!(span, "suggestion without `code = \"...\"`");
+                    };
+                    *code_field = code;
+                }
+                SubdiagnosticKind::Label
+                | SubdiagnosticKind::Note
+                | SubdiagnosticKind::Help
+                | SubdiagnosticKind::Warn
+                | SubdiagnosticKind::MultipartSuggestion { .. } => {}
             }
 
-            self.kind.set_once((kind, span));
+            kind_slug.set_once(((kind, slug), span))
         }
 
-        Ok(())
+        Ok(kind_slug.map(|(kind_slug, _)| kind_slug))
     }
 
-    fn generate_field_code(
+    /// Generates the code for a field with no attributes.
+    fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
+        let ast = binding.ast();
+        assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
+
+        let diag = &self.diag;
+        let ident = ast.ident.as_ref().unwrap();
+        quote! {
+            #diag.set_arg(
+                stringify!(#ident),
+                #binding
+            );
+        }
+    }
+
+    /// Generates the necessary code for all attributes on a field.
+    fn generate_field_attr_code(
         &mut self,
         binding: &BindingInfo<'_>,
-        is_suggestion: bool,
-    ) -> Result<TokenStream, DiagnosticDeriveError> {
+        kind: &SubdiagnosticKind,
+    ) -> TokenStream {
         let ast = binding.ast();
+        assert!(ast.attrs.len() > 0, "field without attributes generating attr code");
 
+        // Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will
+        // apply the generated code on each element in the `Vec` or `Option`.
         let inner_ty = FieldInnerTy::from_type(&ast.ty);
-        let info = FieldInfo {
-            binding: binding,
-            ty: inner_ty.inner_type().unwrap_or(&ast.ty),
-            span: &ast.span(),
-        };
+        ast.attrs
+            .iter()
+            .map(|attr| {
+                let info = FieldInfo {
+                    binding,
+                    ty: inner_ty.inner_type().unwrap_or(&ast.ty),
+                    span: &ast.span(),
+                };
 
-        for attr in &ast.attrs {
-            let name = attr.path.segments.last().unwrap().ident.to_string();
-            let name = name.as_str();
-            let span = attr.span().unwrap();
+                let generated = self
+                    .generate_field_code_inner(kind, attr, info)
+                    .unwrap_or_else(|v| v.to_compile_error());
 
-            let meta = attr.parse_meta()?;
-            match meta {
-                Meta::Path(_) => match name {
-                    "primary_span" => {
-                        report_error_if_not_applied_to_span(attr, &info)?;
-                        self.span_field.set_once((binding.binding.clone(), span));
-                        return Ok(quote! {});
-                    }
-                    "applicability" if is_suggestion => {
-                        report_error_if_not_applied_to_applicability(attr, &info)?;
-                        let binding = binding.binding.clone();
-                        self.applicability.set_once((quote! { #binding }, span));
-                        return Ok(quote! {});
-                    }
-                    "applicability" => {
-                        span_err(span, "`#[applicability]` is only valid on suggestions").emit();
-                        return Ok(quote! {});
-                    }
-                    "skip_arg" => {
-                        return Ok(quote! {});
-                    }
-                    _ => throw_invalid_attr!(attr, &meta, |diag| {
+                inner_ty.with(binding, generated)
+            })
+            .collect()
+    }
+
+    fn generate_field_code_inner(
+        &mut self,
+        kind: &SubdiagnosticKind,
+        attr: &Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<TokenStream, DiagnosticDeriveError> {
+        let meta = attr.parse_meta()?;
+        match meta {
+            Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path),
+            Meta::List(list @ MetaList { .. }) => {
+                self.generate_field_code_inner_list(kind, attr, info, list)
+            }
+            _ => throw_invalid_attr!(attr, &meta),
+        }
+    }
+
+    /// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
+    fn generate_field_code_inner_path(
+        &mut self,
+        kind: &SubdiagnosticKind,
+        attr: &Attribute,
+        info: FieldInfo<'_>,
+        path: Path,
+    ) -> Result<TokenStream, DiagnosticDeriveError> {
+        let span = attr.span().unwrap();
+        let ident = &path.segments.last().unwrap().ident;
+        let name = ident.to_string();
+        let name = name.as_str();
+
+        match name {
+            "skip_arg" => Ok(quote! {}),
+            "primary_span" => {
+                if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
+                    throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
                         diag.help(
-                            "only `primary_span`, `applicability` and `skip_arg` are valid field \
-                             attributes",
+                            "multipart suggestions use one or more `#[suggestion_part]`s rather \
+                            than one `#[primary_span]`",
                         )
-                    }),
-                },
-                _ => throw_invalid_attr!(attr, &meta),
+                    })
+                }
+
+                report_error_if_not_applied_to_span(attr, &info)?;
+
+                let binding = info.binding.binding.clone();
+                self.span_field.set_once((binding, span));
+
+                Ok(quote! {})
+            }
+            "suggestion_part" => {
+                self.has_suggestion_parts = true;
+
+                match kind {
+                    SubdiagnosticKind::MultipartSuggestion { .. } => {
+                        span_err(
+                            span,
+                            "`#[suggestion_part(...)]` attribute without `code = \"...\"`",
+                        )
+                        .emit();
+                        Ok(quote! {})
+                    }
+                    SubdiagnosticKind::Label
+                    | SubdiagnosticKind::Note
+                    | SubdiagnosticKind::Help
+                    | SubdiagnosticKind::Warn
+                    | SubdiagnosticKind::Suggestion { .. } => {
+                        throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
+                            diag.help(
+                                "`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead",
+                            )
+                        });
+                    }
+                }
+            }
+            "applicability" => {
+                if let SubdiagnosticKind::Suggestion { .. }
+                | SubdiagnosticKind::MultipartSuggestion { .. } = kind
+                {
+                    report_error_if_not_applied_to_applicability(attr, &info)?;
+
+                    let binding = info.binding.binding.clone();
+                    self.applicability.set_once((quote! { #binding }, span));
+                } else {
+                    span_err(span, "`#[applicability]` is only valid on suggestions").emit();
+                }
+
+                Ok(quote! {})
             }
+            _ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
+                let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
+                    "suggestion_part"
+                } else {
+                    "primary_span"
+                };
+                diag.help(format!(
+                    "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
+                ))
+            }),
         }
+    }
 
-        let ident = ast.ident.as_ref().unwrap();
+    /// Generates the code for a `[Meta::List]`-like attribute on a field (e.g.
+    /// `#[suggestion_part(code = "...")]`).
+    fn generate_field_code_inner_list(
+        &mut self,
+        kind: &SubdiagnosticKind,
+        attr: &Attribute,
+        info: FieldInfo<'_>,
+        list: MetaList,
+    ) -> Result<TokenStream, DiagnosticDeriveError> {
+        let span = attr.span().unwrap();
+        let ident = &list.path.segments.last().unwrap().ident;
+        let name = ident.to_string();
+        let name = name.as_str();
+
+        match name {
+            "suggestion_part" => {
+                if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
+                    throw_invalid_attr!(attr, &Meta::List(list), |diag| {
+                        diag.help(
+                            "`#[suggestion_part(...)]` is only valid in multipart suggestions",
+                        )
+                    })
+                }
 
-        let diag = &self.diag;
-        let generated = quote! {
-            #diag.set_arg(
-                stringify!(#ident),
-                #binding
-            );
-        };
+                self.has_suggestion_parts = true;
+
+                report_error_if_not_applied_to_span(attr, &info)?;
+
+                let mut code = None;
+                for nested_attr in list.nested.iter() {
+                    let NestedMeta::Meta(ref meta) = nested_attr else {
+                        throw_invalid_nested_attr!(attr, &nested_attr);
+                    };
+
+                    let span = meta.span().unwrap();
+                    let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+                    let nested_name = nested_name.as_str();
+
+                    let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else {
+                        throw_invalid_nested_attr!(attr, &nested_attr);
+                    };
+
+                    match nested_name {
+                        "code" => {
+                            let formatted_str = self.build_format(&value.value(), value.span());
+                            code.set_once((formatted_str, span));
+                        }
+                        _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                            diag.help("`code` is the only valid nested attribute")
+                        }),
+                    }
+                }
 
-        Ok(inner_ty.with(binding, generated))
+                let Some((code, _)) = code else {
+                    span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
+                        .emit();
+                    return Ok(quote! {});
+                };
+                let binding = info.binding;
+
+                Ok(quote! { suggestions.push((#binding, #code)); })
+            }
+            _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
+                let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
+                    "suggestion_part"
+                } else {
+                    "primary_span"
+                };
+                diag.help(format!(
+                    "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
+                ))
+            }),
+        }
     }
 
-    fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
-        self.identify_kind()?;
-        let Some(kind) = self.kind.map(|(kind, _)| kind) else {
+    pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
+        let Some((kind, slug)) = self.identify_kind()? else {
             throw_span_err!(
                 self.variant.ast().ident.span().unwrap(),
                 "subdiagnostic kind not specified"
             );
         };
 
-        let is_suggestion = matches!(kind, SubdiagnosticKind::Suggestion(_));
-
-        let mut args = TokenStream::new();
-        for binding in self.variant.bindings() {
-            let arg = self
-                .generate_field_code(binding, is_suggestion)
-                .unwrap_or_else(|v| v.to_compile_error());
-            args.extend(arg);
-        }
-
-        // Missing slug errors will already have been reported.
-        let slug = self
-            .slug
-            .as_ref()
-            .map(|(slug, _)| slug.clone())
-            .unwrap_or_else(|| parse_quote! { you::need::to::specify::a::slug });
-        let code = match self.code.as_ref() {
-            Some((code, _)) => Some(quote! { #code }),
-            None if is_suggestion => {
-                span_err(self.span, "suggestion without `code = \"...\"`").emit();
-                Some(quote! { /* macro error */ "..." })
+        let init = match &kind {
+            SubdiagnosticKind::Label
+            | SubdiagnosticKind::Note
+            | SubdiagnosticKind::Help
+            | SubdiagnosticKind::Warn
+            | SubdiagnosticKind::Suggestion { .. } => quote! {},
+            SubdiagnosticKind::MultipartSuggestion { .. } => {
+                quote! { let mut suggestions = Vec::new(); }
             }
-            None => None,
         };
 
+        let attr_args: TokenStream = self
+            .variant
+            .bindings()
+            .iter()
+            .filter(|binding| !binding.ast().attrs.is_empty())
+            .map(|binding| self.generate_field_attr_code(binding, &kind))
+            .collect();
+
         let span_field = self.span_field.as_ref().map(|(span, _)| span);
         let applicability = self.applicability.take().map_or_else(
             || quote! { rustc_errors::Applicability::Unspecified },
@@ -478,9 +625,9 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
         let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
         let message = quote! { rustc_errors::fluent::#slug };
         let call = match kind {
-            SubdiagnosticKind::Suggestion(style) => {
+            SubdiagnosticKind::Suggestion { suggestion_kind, code } => {
                 if let Some(span) = span_field {
-                    let style = style.to_suggestion_style();
+                    let style = suggestion_kind.to_suggestion_style();
 
                     quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
                 } else {
@@ -488,6 +635,19 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
                     quote! { unreachable!(); }
                 }
             }
+            SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => {
+                if !self.has_suggestion_parts {
+                    span_err(
+                        self.span,
+                        "multipart suggestion without any `#[suggestion_part(...)]` fields",
+                    )
+                    .emit();
+                }
+
+                let style = suggestion_kind.to_suggestion_style();
+
+                quote! { #diag.#name(#message, suggestions, #applicability, #style); }
+            }
             SubdiagnosticKind::Label => {
                 if let Some(span) = span_field {
                     quote! { #diag.#name(#span, #message); }
@@ -505,9 +665,19 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
             }
         };
 
+        let plain_args: TokenStream = self
+            .variant
+            .bindings()
+            .iter()
+            .filter(|binding| binding.ast().attrs.is_empty())
+            .map(|binding| self.generate_field_set_arg(binding))
+            .collect();
+
         Ok(quote! {
+            #init
+            #attr_args
             #call
-            #args
+            #plain_args
         })
     }
 }
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 8faac8ef36a53..20ee5dfc72798 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -171,8 +171,13 @@ decl_derive!(
         suggestion_short,
         suggestion_hidden,
         suggestion_verbose,
+        multipart_suggestion,
+        multipart_suggestion_short,
+        multipart_suggestion_hidden,
+        multipart_suggestion_verbose,
         // field attributes
         skip_arg,
         primary_span,
+        suggestion_part,
         applicability)] => diagnostics::session_subdiagnostic_derive
 );
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
index 0871ad6b22b39..89eaec78c6f11 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
@@ -310,10 +310,8 @@ union AC {
 #[derive(SessionSubdiagnostic)]
 #[label(parser::add_paren)]
 //~^ NOTE previously specified here
-//~^^ NOTE previously specified here
 #[label(parser::add_paren)]
 //~^ ERROR specified multiple times
-//~^^ ERROR specified multiple times
 struct AD {
     #[primary_span]
     span: Span,
@@ -516,3 +514,120 @@ struct AZ {
     #[primary_span]
     span: Span,
 }
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(parser::add_paren, code = "...")]
+//~^ ERROR suggestion without `#[primary_span]` field
+struct BA {
+    #[suggestion_part]
+    //~^ ERROR `#[suggestion_part]` is not a valid attribute
+    span: Span,
+    #[suggestion_part(code = "...")]
+    //~^ ERROR `#[suggestion_part(...)]` is not a valid attribute
+    span2: Span,
+    #[applicability]
+    applicability: Applicability,
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
+//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
+//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute
+struct BBa {
+    var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
+struct BBb {
+    #[suggestion_part]
+    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
+    span1: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
+struct BBc {
+    #[suggestion_part()]
+    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
+    span1: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren)]
+//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
+struct BC {
+    #[primary_span]
+    //~^ ERROR `#[primary_span]` is not a valid attribute
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren)]
+struct BD {
+    #[suggestion_part]
+    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
+    span1: Span,
+    #[suggestion_part()]
+    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
+    span2: Span,
+    #[suggestion_part(foo = "bar")]
+    //~^ ERROR `#[suggestion_part(foo = ...)]` is not a valid attribute
+    span4: Span,
+    #[suggestion_part(code = "...")]
+    //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
+    s1: String,
+    #[suggestion_part()]
+    //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
+    s2: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
+struct BE {
+    #[suggestion_part(code = "...", code = ",,,")]
+    //~^ ERROR specified multiple times
+    //~| NOTE previously specified here
+    span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
+struct BF {
+    #[suggestion_part(code = "(")]
+    first: Span,
+    #[suggestion_part(code = ")")]
+    second: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren)]
+struct BG {
+    #[applicability]
+    appl: Applicability,
+    #[suggestion_part(code = "(")]
+    first: Span,
+    #[suggestion_part(code = ")")]
+    second: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
+//~^ NOTE previously specified here
+struct BH {
+    #[applicability]
+    //~^ ERROR specified multiple times
+    appl: Applicability,
+    #[suggestion_part(code = "(")]
+    first: Span,
+    #[suggestion_part(code = ")")]
+    second: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
+struct BI {
+    #[suggestion_part(code = "")]
+    spans: Vec<Span>,
+}
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
index 68d33323db035..75a34f44bbe72 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
@@ -65,16 +65,16 @@ LL | #[label()]
    | ^^^^^^^^^^
 
 error: `code` is not a valid nested attribute of a `label` attribute
-  --> $DIR/subdiagnostic-derive.rs:137:1
+  --> $DIR/subdiagnostic-derive.rs:137:28
    |
 LL | #[label(parser::add_paren, code = "...")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                            ^^^^^^^^^^^^
 
 error: `applicability` is not a valid nested attribute of a `label` attribute
-  --> $DIR/subdiagnostic-derive.rs:146:1
+  --> $DIR/subdiagnostic-derive.rs:146:28
    |
 LL | #[label(parser::add_paren, applicability = "machine-applicable")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsupported type attribute for subdiagnostic enum
   --> $DIR/subdiagnostic-derive.rs:155:1
@@ -100,13 +100,11 @@ error: `#[bar = ...]` is not a valid attribute
 LL |     #[bar = 4]
    |     ^^^^^^^^^^
 
-error: `#[bar("...")]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:205:11
+error: `#[bar(...)]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:205:5
    |
 LL |     #[bar("...")]
-   |           ^^^^^
-   |
-   = help: first argument of the attribute should be the diagnostic slug
+   |     ^^^^^^^^^^^^^
 
 error: diagnostic slug must be first argument of a `#[label(...)]` attribute
   --> $DIR/subdiagnostic-derive.rs:217:5
@@ -163,6 +161,8 @@ error: `#[bar(...)]` is not a valid attribute
    |
 LL |     #[bar("...")]
    |     ^^^^^^^^^^^^^
+   |
+   = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
 
 error: unexpected unsupported untagged union
   --> $DIR/subdiagnostic-derive.rs:304:1
@@ -175,19 +175,7 @@ LL | | }
    | |_^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:314:1
-   |
-LL | #[label(parser::add_paren)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:311:1
-   |
-LL | #[label(parser::add_paren)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:314:1
+  --> $DIR/subdiagnostic-derive.rs:313:1
    |
 LL | #[label(parser::add_paren)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -199,7 +187,7 @@ LL | #[label(parser::add_paren)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `#[label(parser::add_paren)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:323:28
+  --> $DIR/subdiagnostic-derive.rs:321:28
    |
 LL | #[label(parser::add_paren, parser::add_paren)]
    |                            ^^^^^^^^^^^^^^^^^
@@ -207,73 +195,67 @@ LL | #[label(parser::add_paren, parser::add_paren)]
    = help: a diagnostic slug must be the first argument to the attribute
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:336:5
+  --> $DIR/subdiagnostic-derive.rs:334:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:333:5
+  --> $DIR/subdiagnostic-derive.rs:331:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
 
 error: subdiagnostic kind not specified
-  --> $DIR/subdiagnostic-derive.rs:342:8
+  --> $DIR/subdiagnostic-derive.rs:340:8
    |
 LL | struct AG {
    |        ^^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:379:47
+  --> $DIR/subdiagnostic-derive.rs:377:47
    |
 LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
    |                                               ^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:379:33
+  --> $DIR/subdiagnostic-derive.rs:377:33
    |
 LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
    |                                 ^^^^^^^^^^^^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:397:5
+  --> $DIR/subdiagnostic-derive.rs:395:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:394:5
+  --> $DIR/subdiagnostic-derive.rs:392:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
 error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
-  --> $DIR/subdiagnostic-derive.rs:407:5
+  --> $DIR/subdiagnostic-derive.rs:405:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
 error: suggestion without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:420:1
+  --> $DIR/subdiagnostic-derive.rs:418:1
    |
-LL | / #[suggestion(parser::add_paren)]
-LL | |
-LL | | struct AN {
-LL | |     #[primary_span]
-...  |
-LL | |     applicability: Applicability,
-LL | | }
-   | |_^
+LL | #[suggestion(parser::add_paren)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: invalid applicability
-  --> $DIR/subdiagnostic-derive.rs:430:46
+  --> $DIR/subdiagnostic-derive.rs:428:46
    |
 LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
    |                                              ^^^^^^^^^^^^^^^^^^^^^
 
 error: suggestion without `#[primary_span]` field
-  --> $DIR/subdiagnostic-derive.rs:448:1
+  --> $DIR/subdiagnostic-derive.rs:446:1
    |
 LL | / #[suggestion(parser::add_paren, code = "...")]
 LL | |
@@ -283,23 +265,156 @@ LL | | }
    | |_^
 
 error: unsupported type attribute for subdiagnostic enum
-  --> $DIR/subdiagnostic-derive.rs:462:1
+  --> $DIR/subdiagnostic-derive.rs:460:1
    |
 LL | #[label]
    | ^^^^^^^^
 
 error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:482:39
+  --> $DIR/subdiagnostic-derive.rs:480:39
    |
 LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
    |                                       ^^^^^^^
 
 error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:501:43
+  --> $DIR/subdiagnostic-derive.rs:499:43
    |
 LL |     #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
    |                                           ^^^^^^^
 
+error: `#[suggestion_part]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:522:5
+   |
+LL |     #[suggestion_part]
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead
+
+error: `#[suggestion_part(...)]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:525:5
+   |
+LL |     #[suggestion_part(code = "...")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: `#[suggestion_part(...)]` is only valid in multipart suggestions
+
+error: suggestion without `#[primary_span]` field
+  --> $DIR/subdiagnostic-derive.rs:519:1
+   |
+LL | / #[suggestion(parser::add_paren, code = "...")]
+LL | |
+LL | | struct BA {
+LL | |     #[suggestion_part]
+...  |
+LL | |     var: String,
+LL | | }
+   | |_^
+
+error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute
+  --> $DIR/subdiagnostic-derive.rs:534:43
+   |
+LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
+   |                                           ^^^^^^^^^^^^
+
+error: multipart suggestion without any `#[suggestion_part(...)]` fields
+  --> $DIR/subdiagnostic-derive.rs:534:1
+   |
+LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
+LL | |
+LL | |
+LL | | struct BBa {
+LL | |     var: String,
+LL | | }
+   | |_^
+
+error: `#[suggestion_part(...)]` attribute without `code = "..."`
+  --> $DIR/subdiagnostic-derive.rs:544:5
+   |
+LL |     #[suggestion_part]
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: `#[suggestion_part(...)]` attribute without `code = "..."`
+  --> $DIR/subdiagnostic-derive.rs:552:5
+   |
+LL |     #[suggestion_part()]
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: `#[primary_span]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:561:5
+   |
+LL |     #[primary_span]
+   |     ^^^^^^^^^^^^^^^
+   |
+   = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]`
+
+error: multipart suggestion without any `#[suggestion_part(...)]` fields
+  --> $DIR/subdiagnostic-derive.rs:558:1
+   |
+LL | / #[multipart_suggestion(parser::add_paren)]
+LL | |
+LL | | struct BC {
+LL | |     #[primary_span]
+LL | |
+LL | |     span: Span,
+LL | | }
+   | |_^
+
+error: `#[suggestion_part(...)]` attribute without `code = "..."`
+  --> $DIR/subdiagnostic-derive.rs:569:5
+   |
+LL |     #[suggestion_part]
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: `#[suggestion_part(...)]` attribute without `code = "..."`
+  --> $DIR/subdiagnostic-derive.rs:572:5
+   |
+LL |     #[suggestion_part()]
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: `#[suggestion_part(foo = ...)]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:575:23
+   |
+LL |     #[suggestion_part(foo = "bar")]
+   |                       ^^^^^^^^^^^
+   |
+   = help: `code` is the only valid nested attribute
+
+error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
+  --> $DIR/subdiagnostic-derive.rs:578:5
+   |
+LL |     #[suggestion_part(code = "...")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
+  --> $DIR/subdiagnostic-derive.rs:581:5
+   |
+LL |     #[suggestion_part()]
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:589:37
+   |
+LL |     #[suggestion_part(code = "...", code = ",,,")]
+   |                                     ^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:589:23
+   |
+LL |     #[suggestion_part(code = "...", code = ",,,")]
+   |                       ^^^^^^^^^^^^
+
+error: specified multiple times
+  --> $DIR/subdiagnostic-derive.rs:619:5
+   |
+LL |     #[applicability]
+   |     ^^^^^^^^^^^^^^^^
+   |
+note: previously specified here
+  --> $DIR/subdiagnostic-derive.rs:616:43
+   |
+LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
+   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: cannot find attribute `foo` in this scope
   --> $DIR/subdiagnostic-derive.rs:63:3
    |
@@ -360,6 +475,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
 LL | #[label(slug)]
    |         ^^^^ not found in `rustc_errors::fluent`
 
-error: aborting due to 49 previous errors
+error: aborting due to 64 previous errors
 
 For more information about this error, try `rustc --explain E0425`.

From c41f21b3e4e992d5c5c11dae880dc81192a12653 Mon Sep 17 00:00:00 2001
From: Thom Chiovoloni <thom@shift.click>
Date: Tue, 30 Aug 2022 01:45:30 -0700
Subject: [PATCH 23/24] Fix UB in Windows `DirBuffIter` (provenance and
 alignment)

---
 library/std/src/sys/windows/fs.rs | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index c702b8e744c89..0aa7c50ded1c7 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -658,18 +658,18 @@ impl File {
 
 /// A buffer for holding directory entries.
 struct DirBuff {
-    buffer: Vec<u8>,
+    buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>,
 }
 impl DirBuff {
+    const BUFFER_SIZE: usize = 1024;
     fn new() -> Self {
-        const BUFFER_SIZE: usize = 1024;
-        Self { buffer: vec![0_u8; BUFFER_SIZE] }
+        Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) }
     }
     fn capacity(&self) -> usize {
-        self.buffer.len()
+        self.buffer.0.len()
     }
     fn as_mut_ptr(&mut self) -> *mut u8 {
-        self.buffer.as_mut_ptr().cast()
+        self.buffer.0.as_mut_ptr().cast()
     }
     /// Returns a `DirBuffIter`.
     fn iter(&self) -> DirBuffIter<'_> {
@@ -678,7 +678,7 @@ impl DirBuff {
 }
 impl AsRef<[u8]> for DirBuff {
     fn as_ref(&self) -> &[u8] {
-        &self.buffer
+        &self.buffer.0
     }
 }
 
@@ -706,9 +706,12 @@ impl<'a> Iterator for DirBuffIter<'a> {
         // used to get the file name slice.
         let (name, is_directory, next_entry) = unsafe {
             let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
+            // Guaranteed to be aligned in documentation for
+            // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
+            assert!(info.is_aligned());
             let next_entry = (*info).NextEntryOffset as usize;
             let name = crate::slice::from_raw_parts(
-                (*info).FileName.as_ptr().cast::<u16>(),
+                ptr::addr_of!((*info).FileName).cast::<u16>(),
                 (*info).FileNameLength as usize / size_of::<u16>(),
             );
             let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;

From 79d4f09942a2fc6f96f56f7ae85196c4aa7df56c Mon Sep 17 00:00:00 2001
From: JeanCASPAR <55629512+JeanCASPAR@users.noreply.github.com>
Date: Tue, 30 Aug 2022 14:26:09 +0200
Subject: [PATCH 24/24] Change fatal diagnostic to an error.

Co-authored-by: Esteban Kuber <estebank@users.noreply.github.com>
---
 compiler/rustc_ast_lowering/src/expr.rs | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 6d8ce62d91327..176047616881f 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1264,7 +1264,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
             (Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
             (None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
             (Some(..), Some(..), Closed) => unreachable!(),
-            (_, None, Closed) => self.tcx.sess.emit_fatal(InclusiveRangeWithNoEnd { span }),
+            (start, None, Closed) => {
+                self.tcx.sess.emit_err(InclusiveRangeWithNoEnd { span });
+                match start {
+                    Some(..) => hir::LangItem::RangeFrom,
+                    None => hir::LangItem::RangeFull,
+                }
+            }
         };
 
         let fields = self.arena.alloc_from_iter(