From 809dc73d90d191c75b24840113797bd4f7e138b3 Mon Sep 17 00:00:00 2001
From: Gary Guo <gary@garyguo.net>
Date: Fri, 11 Oct 2024 11:54:16 +0100
Subject: [PATCH 1/8] Make asm label blocks safe context

`asm!()` is forced to be wrapped inside unsafe. If there's no special
treatment, the label blocks would also always be unsafe with no way
of opting out.
---
 .../rustc_mir_build/src/check_unsafety.rs     | 39 ++++++++++++++++++-
 .../src/language-features/asm-goto.md         |  4 +-
 tests/ui/asm/x86_64/goto-block-safe.rs        | 23 +++++++++++
 tests/ui/asm/x86_64/goto-block-safe.stderr    | 14 +++++++
 4 files changed, 78 insertions(+), 2 deletions(-)
 create mode 100644 tests/ui/asm/x86_64/goto-block-safe.rs
 create mode 100644 tests/ui/asm/x86_64/goto-block-safe.stderr

diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 8512763a595..2e3169bbf89 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -541,8 +541,45 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                     self.requires_unsafe(expr.span, DerefOfRawPointer);
                 }
             }
-            ExprKind::InlineAsm { .. } => {
+            ExprKind::InlineAsm(box InlineAsmExpr {
+                asm_macro: _,
+                ref operands,
+                template: _,
+                options: _,
+                line_spans: _,
+            }) => {
                 self.requires_unsafe(expr.span, UseOfInlineAssembly);
+
+                // For inline asm, do not use `walk_expr`, since we want to handle the label block
+                // specially.
+                for op in &**operands {
+                    use rustc_middle::thir::InlineAsmOperand::*;
+                    match op {
+                        In { expr, reg: _ }
+                        | Out { expr: Some(expr), reg: _, late: _ }
+                        | InOut { expr, reg: _, late: _ } => self.visit_expr(&self.thir()[*expr]),
+                        SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
+                            self.visit_expr(&self.thir()[*in_expr]);
+                            if let Some(out_expr) = out_expr {
+                                self.visit_expr(&self.thir()[*out_expr]);
+                            }
+                        }
+                        Out { expr: None, reg: _, late: _ }
+                        | Const { value: _, span: _ }
+                        | SymFn { value: _, span: _ }
+                        | SymStatic { def_id: _ } => {}
+                        Label { block } => {
+                            // Label blocks are safe context.
+                            // `asm!()` is forced to be wrapped inside unsafe. If there's no special
+                            // treatment, the label blocks would also always be unsafe with no way
+                            // of opting out.
+                            self.in_safety_context(SafetyContext::Safe, |this| {
+                                visit::walk_block(this, &this.thir()[*block])
+                            });
+                        }
+                    }
+                }
+                return;
             }
             ExprKind::Adt(box AdtExpr {
                 adt_def,
diff --git a/src/doc/unstable-book/src/language-features/asm-goto.md b/src/doc/unstable-book/src/language-features/asm-goto.md
index d72eb7c0c6e..823118bcae1 100644
--- a/src/doc/unstable-book/src/language-features/asm-goto.md
+++ b/src/doc/unstable-book/src/language-features/asm-goto.md
@@ -21,7 +21,9 @@ unsafe {
 }
 ```
 
-The block must have unit type or diverge.
+The block must have unit type or diverge. The block starts a new safety context,
+so despite outer `unsafe`, you need extra unsafe to perform unsafe operations
+within `label <block>`.
 
 When `label <block>` is used together with `noreturn` option, it means that the
 assembly will not fallthrough. It's allowed to jump to a label within the
diff --git a/tests/ui/asm/x86_64/goto-block-safe.rs b/tests/ui/asm/x86_64/goto-block-safe.rs
new file mode 100644
index 00000000000..ee833a48a4b
--- /dev/null
+++ b/tests/ui/asm/x86_64/goto-block-safe.rs
@@ -0,0 +1,23 @@
+//@ only-x86_64
+//@ needs-asm-support
+
+#![deny(unreachable_code)]
+#![feature(asm_goto)]
+
+use std::arch::asm;
+
+fn goto_fallthough() {
+    unsafe {
+        asm!(
+            "/* {} */",
+            label {
+                core::hint::unreachable_unchecked();
+                //~^ ERROR [E0133]
+            }
+        )
+    }
+}
+
+fn main() {
+    goto_fallthough();
+}
diff --git a/tests/ui/asm/x86_64/goto-block-safe.stderr b/tests/ui/asm/x86_64/goto-block-safe.stderr
new file mode 100644
index 00000000000..49818db7484
--- /dev/null
+++ b/tests/ui/asm/x86_64/goto-block-safe.stderr
@@ -0,0 +1,14 @@
+error[E0133]: call to unsafe function `unreachable_unchecked` is unsafe and requires unsafe function or block
+  --> $DIR/goto-block-safe.rs:14:17
+   |
+LL |     unsafe {
+   |     ------ items do not inherit unsafety from separate enclosing items
+...
+LL |                 core::hint::unreachable_unchecked();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0133`.

From 78bbc648c5b2e6e092167d2ed593cd89f06dd37f Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 2 Nov 2024 03:42:10 +0000
Subject: [PATCH 2/8] Fix closure arg extraction in extract_callable_info

---
 .../src/error_reporting/traits/suggestions.rs |  4 ++--
 .../correct-args-on-call-suggestion.rs        |  7 +++++++
 .../correct-args-on-call-suggestion.stderr    | 20 +++++++++++++++++++
 3 files changed, 29 insertions(+), 2 deletions(-)
 create mode 100644 tests/ui/closures/correct-args-on-call-suggestion.rs
 create mode 100644 tests/ui/closures/correct-args-on-call-suggestion.stderr

diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 553bb61ed04..2ce46ed5a79 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -1091,7 +1091,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         Some((
                             DefIdOrName::DefId(def_id),
                             fn_sig.output(),
-                            fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
+                            fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),
                         ))
                     }
                     ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
@@ -1101,7 +1101,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             .iter()
                             .find_map(|pred| {
                                 if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
-                        && self.tcx.is_lang_item(proj.projection_term.def_id,LangItem::FnOnceOutput)
+                        && self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
                         // args tuple will always be args[1]
                         && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
                                 {
diff --git a/tests/ui/closures/correct-args-on-call-suggestion.rs b/tests/ui/closures/correct-args-on-call-suggestion.rs
new file mode 100644
index 00000000000..fa7915a7c03
--- /dev/null
+++ b/tests/ui/closures/correct-args-on-call-suggestion.rs
@@ -0,0 +1,7 @@
+// Ensure we give the right args when we suggest calling a closure.
+
+fn main() {
+    let x = |a: i32, b: i32| a + b;
+    let y: i32 = x;
+    //~^ ERROR mismatched types
+}
diff --git a/tests/ui/closures/correct-args-on-call-suggestion.stderr b/tests/ui/closures/correct-args-on-call-suggestion.stderr
new file mode 100644
index 00000000000..2613c7776b2
--- /dev/null
+++ b/tests/ui/closures/correct-args-on-call-suggestion.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/correct-args-on-call-suggestion.rs:5:18
+   |
+LL |     let x = |a: i32, b: i32| a + b;
+   |             ---------------- the found closure
+LL |     let y: i32 = x;
+   |            ---   ^ expected `i32`, found closure
+   |            |
+   |            expected due to this
+   |
+   = note: expected type `i32`
+           found closure `{closure@$DIR/correct-args-on-call-suggestion.rs:4:13: 4:29}`
+help: use parentheses to call this closure
+   |
+LL |     let y: i32 = x(/* i32 */, /* i32 */);
+   |                   ++++++++++++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.

From cbacb6d93108a7e5156c8b37d1a78f0dca351606 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 2 Nov 2024 03:53:59 +0000
Subject: [PATCH 3/8] Use extract_callable_info, generalize it to async closure

---
 .../rustc_hir_typeck/src/method/suggest.rs    |  55 +-----
 .../src/error_reporting/traits/suggestions.rs | 177 ++++++++++--------
 .../issue-18343.stderr                        |   2 +-
 .../issue-2392.stderr                         |  14 +-
 .../issue-32128.stderr                        |   2 +-
 .../issue-33784.stderr                        |   6 +-
 ...rg-where-it-should-have-been-called.stderr |   4 +
 7 files changed, 121 insertions(+), 139 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index eaf40a193a6..4269e8b9f1e 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -31,6 +31,7 @@ use rustc_span::symbol::{Ident, kw, sym};
 use rustc_span::{
     DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, edit_distance,
 };
+use rustc_trait_selection::error_reporting::traits::DefIdOrName;
 use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -45,50 +46,6 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
 use crate::{Expectation, FnCtxt};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
-        let tcx = self.tcx;
-        match ty.kind() {
-            // Not all of these (e.g., unsafe fns) implement `FnOnce`,
-            // so we look for these beforehand.
-            // FIXME(async_closures): These don't impl `FnOnce` by default.
-            ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(..) => true,
-            // If it's not a simple function, look for things which implement `FnOnce`.
-            _ => {
-                let Some(fn_once) = tcx.lang_items().fn_once_trait() else {
-                    return false;
-                };
-
-                // This conditional prevents us from asking to call errors and unresolved types.
-                // It might seem that we can use `predicate_must_hold_modulo_regions`,
-                // but since a Dummy binder is used to fill in the FnOnce trait's arguments,
-                // type resolution always gives a "maybe" here.
-                if self.autoderef(span, ty).silence_errors().any(|(ty, _)| {
-                    info!("check deref {:?} error", ty);
-                    matches!(ty.kind(), ty::Error(_) | ty::Infer(_))
-                }) {
-                    return false;
-                }
-
-                self.autoderef(span, ty).silence_errors().any(|(ty, _)| {
-                    info!("check deref {:?} impl FnOnce", ty);
-                    self.probe(|_| {
-                        let trait_ref =
-                            ty::TraitRef::new(tcx, fn_once, [ty, self.next_ty_var(span)]);
-                        let poly_trait_ref = ty::Binder::dummy(trait_ref);
-                        let obligation = Obligation::misc(
-                            tcx,
-                            span,
-                            self.body_id,
-                            self.param_env,
-                            poly_trait_ref,
-                        );
-                        self.predicate_may_hold(&obligation)
-                    })
-                })
-            }
-        }
-    }
-
     fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
         self.autoderef(span, ty)
             .silence_errors()
@@ -2365,12 +2322,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let is_accessible = field.vis.is_accessible_from(scope, tcx);
 
             if is_accessible {
-                if self.is_fn_ty(field_ty, span) {
+                if let Some((what, _, _)) = self.extract_callable_info(field_ty) {
+                    let what = match what {
+                        DefIdOrName::DefId(def_id) => self.tcx.def_descr(def_id),
+                        DefIdOrName::Name(what) => what,
+                    };
                     let expr_span = expr.span.to(item_name.span);
                     err.multipart_suggestion(
                         format!(
-                            "to call the function stored in `{item_name}`, \
-                                         surround the field access with parentheses",
+                            "to call the {what} stored in `{item_name}`, \
+                            surround the field access with parentheses",
                         ),
                         vec![
                             (expr_span.shrink_to_lo(), '('.to_string()),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 2ce46ed5a79..d1d6ad04c42 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -1075,93 +1075,110 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     ) -> 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_steps)(found).into_iter().find_map(|(found, _)| {
-                match *found.kind() {
-                    ty::FnPtr(sig_tys, _) => Some((
-                        DefIdOrName::Name("function pointer"),
-                        sig_tys.output(),
-                        sig_tys.inputs(),
-                    )),
-                    ty::FnDef(def_id, _) => {
-                        let fn_sig = found.fn_sig(self.tcx);
-                        Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
-                    }
-                    ty::Closure(def_id, args) => {
-                        let fn_sig = args.as_closure().sig();
-                        Some((
-                            DefIdOrName::DefId(def_id),
-                            fn_sig.output(),
-                            fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),
-                        ))
-                    }
-                    ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
-                        self.tcx
-                            .item_super_predicates(def_id)
-                            .instantiate(self.tcx, args)
-                            .iter()
-                            .find_map(|pred| {
-                                if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
-                        && self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
-                        // args tuple will always be args[1]
-                        && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
-                                {
-                                    Some((
-                                        DefIdOrName::DefId(def_id),
-                                        pred.kind().rebind(proj.term.expect_type()),
-                                        pred.kind().rebind(args.as_slice()),
-                                    ))
-                                } else {
-                                    None
-                                }
-                            })
-                    }
-                    ty::Dynamic(data, _, ty::Dyn) => {
-                        data.iter().find_map(|pred| {
-                            if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+            (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| match *found.kind() {
+                ty::FnPtr(sig_tys, _) => Some((
+                    DefIdOrName::Name("function pointer"),
+                    sig_tys.output(),
+                    sig_tys.inputs(),
+                )),
+                ty::FnDef(def_id, _) => {
+                    let fn_sig = found.fn_sig(self.tcx);
+                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+                }
+                ty::Closure(def_id, args) => {
+                    let fn_sig = args.as_closure().sig();
+                    Some((
+                        DefIdOrName::DefId(def_id),
+                        fn_sig.output(),
+                        fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),
+                    ))
+                }
+                ty::CoroutineClosure(def_id, args) => {
+                    let sig_parts = args.as_coroutine_closure().coroutine_closure_sig();
+                    Some((
+                        DefIdOrName::DefId(def_id),
+                        sig_parts.map_bound(|sig| {
+                            sig.to_coroutine(
+                                self.tcx,
+                                args.as_coroutine_closure().parent_args(),
+                                // Just use infer vars here, since we  don't really care
+                                // what these types are, just that we're returning a coroutine.
+                                self.next_ty_var(DUMMY_SP),
+                                self.tcx.coroutine_for_closure(def_id),
+                                self.next_ty_var(DUMMY_SP),
+                            )
+                        }),
+                        sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()),
+                    ))
+                }
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self
+                    .tcx
+                    .item_super_predicates(def_id)
+                    .instantiate(self.tcx, args)
+                    .iter()
+                    .find_map(|pred| {
+                        if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
+                            && self
+                                .tcx
+                                .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
+                            // args tuple will always be args[1]
+                            && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
+                        {
+                            Some((
+                                DefIdOrName::DefId(def_id),
+                                pred.kind().rebind(proj.term.expect_type()),
+                                pred.kind().rebind(args.as_slice()),
+                            ))
+                        } else {
+                            None
+                        }
+                    }),
+                ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| {
+                    if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
                         && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)
                         // for existential projection, args are shifted over by 1
                         && let ty::Tuple(args) = proj.args.type_at(0).kind()
-                            {
-                                Some((
-                                    DefIdOrName::Name("trait object"),
-                                    pred.rebind(proj.term.expect_type()),
-                                    pred.rebind(args.as_slice()),
-                                ))
-                            } else {
-                                None
-                            }
-                        })
+                    {
+                        Some((
+                            DefIdOrName::Name("trait object"),
+                            pred.rebind(proj.term.expect_type()),
+                            pred.rebind(args.as_slice()),
+                        ))
+                    } else {
+                        None
                     }
-                    ty::Param(param) => {
-                        let generics = self.tcx.generics_of(body_id);
-                        let name = if generics.count() > param.index as usize
-                            && let def = generics.param_at(param.index as usize, self.tcx)
-                            && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
-                            && def.name == param.name
+                }),
+                ty::Param(param) => {
+                    let generics = self.tcx.generics_of(body_id);
+                    let name = if generics.count() > param.index as usize
+                        && let def = generics.param_at(param.index as usize, self.tcx)
+                        && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
+                        && def.name == param.name
+                    {
+                        DefIdOrName::DefId(def.def_id)
+                    } else {
+                        DefIdOrName::Name("type parameter")
+                    };
+                    param_env.caller_bounds().iter().find_map(|pred| {
+                        if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
+                            && self
+                                .tcx
+                                .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
+                            && proj.projection_term.self_ty() == found
+                            // args tuple will always be args[1]
+                            && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
                         {
-                            DefIdOrName::DefId(def.def_id)
+                            Some((
+                                name,
+                                pred.kind().rebind(proj.term.expect_type()),
+                                pred.kind().rebind(args.as_slice()),
+                            ))
                         } else {
-                            DefIdOrName::Name("type parameter")
-                        };
-                        param_env.caller_bounds().iter().find_map(|pred| {
-                            if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
-                        && self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
-                        && proj.projection_term.self_ty() == found
-                        // args tuple will always be args[1]
-                        && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
-                            {
-                                Some((
-                                    name,
-                                    pred.kind().rebind(proj.term.expect_type()),
-                                    pred.kind().rebind(args.as_slice()),
-                                ))
-                            } else {
-                                None
-                            }
-                        })
-                    }
-                    _ => None,
+                            None
+                        }
+                    })
                 }
+                _ => None,
             })
         else {
             return None;
diff --git a/tests/ui/confuse-field-and-method/issue-18343.stderr b/tests/ui/confuse-field-and-method/issue-18343.stderr
index a51fd4f02aa..e50c971d837 100644
--- a/tests/ui/confuse-field-and-method/issue-18343.stderr
+++ b/tests/ui/confuse-field-and-method/issue-18343.stderr
@@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnMut() -> u32 {
 LL |     o.closure();
    |       ^^^^^^^ field, not a method
    |
-help: to call the function stored in `closure`, surround the field access with parentheses
+help: to call the closure stored in `closure`, surround the field access with parentheses
    |
 LL |     (o.closure)();
    |     +         +
diff --git a/tests/ui/confuse-field-and-method/issue-2392.stderr b/tests/ui/confuse-field-and-method/issue-2392.stderr
index 440fbb27c00..77930de44a7 100644
--- a/tests/ui/confuse-field-and-method/issue-2392.stderr
+++ b/tests/ui/confuse-field-and-method/issue-2392.stderr
@@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 {
 LL |     o_closure.closure();
    |               ^^^^^^^ field, not a method
    |
-help: to call the function stored in `closure`, surround the field access with parentheses
+help: to call the closure stored in `closure`, surround the field access with parentheses
    |
 LL |     (o_closure.closure)();
    |     +                 +
@@ -46,7 +46,7 @@ LL | struct BoxedObj {
 LL |     boxed_fn.boxed_closure();
    |              ^^^^^^^^^^^^^ field, not a method
    |
-help: to call the function stored in `boxed_closure`, surround the field access with parentheses
+help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses
    |
 LL |     (boxed_fn.boxed_closure)();
    |     +                      +
@@ -60,7 +60,7 @@ LL | struct BoxedObj {
 LL |     boxed_closure.boxed_closure();
    |                   ^^^^^^^^^^^^^ field, not a method
    |
-help: to call the function stored in `boxed_closure`, surround the field access with parentheses
+help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses
    |
 LL |     (boxed_closure.boxed_closure)();
    |     +                           +
@@ -99,7 +99,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 {
 LL |     check_expression().closure();
    |                        ^^^^^^^ field, not a method
    |
-help: to call the function stored in `closure`, surround the field access with parentheses
+help: to call the trait object stored in `closure`, surround the field access with parentheses
    |
 LL |     (check_expression().closure)();
    |     +                          +
@@ -113,7 +113,7 @@ LL | struct FuncContainer {
 LL |             (*self.container).f1(1);
    |                               ^^ field, not a method
    |
-help: to call the function stored in `f1`, surround the field access with parentheses
+help: to call the function pointer stored in `f1`, surround the field access with parentheses
    |
 LL |             ((*self.container).f1)(1);
    |             +                    +
@@ -127,7 +127,7 @@ LL | struct FuncContainer {
 LL |             (*self.container).f2(1);
    |                               ^^ field, not a method
    |
-help: to call the function stored in `f2`, surround the field access with parentheses
+help: to call the function pointer stored in `f2`, surround the field access with parentheses
    |
 LL |             ((*self.container).f2)(1);
    |             +                    +
@@ -141,7 +141,7 @@ LL | struct FuncContainer {
 LL |             (*self.container).f3(1);
    |                               ^^ field, not a method
    |
-help: to call the function stored in `f3`, surround the field access with parentheses
+help: to call the function pointer stored in `f3`, surround the field access with parentheses
    |
 LL |             ((*self.container).f3)(1);
    |             +                    +
diff --git a/tests/ui/confuse-field-and-method/issue-32128.stderr b/tests/ui/confuse-field-and-method/issue-32128.stderr
index 3d860d8c85a..aaec15a41dc 100644
--- a/tests/ui/confuse-field-and-method/issue-32128.stderr
+++ b/tests/ui/confuse-field-and-method/issue-32128.stderr
@@ -7,7 +7,7 @@ LL | struct Example {
 LL |     demo.example(1);
    |          ^^^^^^^ field, not a method
    |
-help: to call the function stored in `example`, surround the field access with parentheses
+help: to call the trait object stored in `example`, surround the field access with parentheses
    |
 LL |     (demo.example)(1);
    |     +            +
diff --git a/tests/ui/confuse-field-and-method/issue-33784.stderr b/tests/ui/confuse-field-and-method/issue-33784.stderr
index 8acd1f8ff1e..59a6f4fccd8 100644
--- a/tests/ui/confuse-field-and-method/issue-33784.stderr
+++ b/tests/ui/confuse-field-and-method/issue-33784.stderr
@@ -4,7 +4,7 @@ error[E0599]: no method named `closure` found for reference `&Obj<{closure@$DIR/
 LL |     p.closure();
    |       ^^^^^^^ field, not a method
    |
-help: to call the function stored in `closure`, surround the field access with parentheses
+help: to call the closure stored in `closure`, surround the field access with parentheses
    |
 LL |     (p.closure)();
    |     +         +
@@ -19,7 +19,7 @@ error[E0599]: no method named `fn_ptr` found for reference `&&Obj<{closure@$DIR/
 LL |     q.fn_ptr();
    |       ^^^^^^ field, not a method
    |
-help: to call the function stored in `fn_ptr`, surround the field access with parentheses
+help: to call the function pointer stored in `fn_ptr`, surround the field access with parentheses
    |
 LL |     (q.fn_ptr)();
    |     +        +
@@ -30,7 +30,7 @@ error[E0599]: no method named `c_fn_ptr` found for reference `&D` in the current
 LL |     s.c_fn_ptr();
    |       ^^^^^^^^ field, not a method
    |
-help: to call the function stored in `c_fn_ptr`, surround the field access with parentheses
+help: to call the function pointer stored in `c_fn_ptr`, surround the field access with parentheses
    |
 LL |     (s.c_fn_ptr)();
    |     +          +
diff --git a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr
index 05e087fd9f9..a040e71cf3b 100644
--- a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr
+++ b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr
@@ -31,6 +31,10 @@ note: required by a bound in `bar`
    |
 LL | fn bar(f: impl Future<Output=()>) {}
    |                ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
+help: use parentheses to call this closure
+   |
+LL |     bar(async_closure());
+   |                      ++
 
 error: aborting due to 2 previous errors
 

From 7652e349b091531ceb91eae8de23fb86ac7c0b09 Mon Sep 17 00:00:00 2001
From: Taiki Endo <te316e89@gmail.com>
Date: Thu, 14 Nov 2024 22:14:11 +0900
Subject: [PATCH 4/8] Support s390x z13 vector ABI

---
 compiler/rustc_abi/src/layout/ty.rs           |  18 +
 compiler/rustc_target/src/callconv/s390x.rs   |  26 +-
 .../spec/targets/s390x_unknown_linux_gnu.rs   |   3 -
 .../spec/targets/s390x_unknown_linux_musl.rs  |   3 -
 tests/assembly/s390x-vector-abi.rs            | 322 ++++++++++++++++++
 tests/ui/simd-abi-checks-s390x.rs             | 174 ++++++++++
 tests/ui/simd-abi-checks-s390x.z10.stderr     | 111 ++++++
 ...simd-abi-checks-s390x.z13_no_vector.stderr | 111 ++++++
 ...imd-abi-checks-s390x.z13_soft_float.stderr | 111 ++++++
 9 files changed, 865 insertions(+), 14 deletions(-)
 create mode 100644 tests/assembly/s390x-vector-abi.rs
 create mode 100644 tests/ui/simd-abi-checks-s390x.rs
 create mode 100644 tests/ui/simd-abi-checks-s390x.z10.stderr
 create mode 100644 tests/ui/simd-abi-checks-s390x.z13_no_vector.stderr
 create mode 100644 tests/ui/simd-abi-checks-s390x.z13_soft_float.stderr

diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs
index 062447ea03f..d188750bfe1 100644
--- a/compiler/rustc_abi/src/layout/ty.rs
+++ b/compiler/rustc_abi/src/layout/ty.rs
@@ -209,6 +209,24 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         }
     }
 
+    pub fn is_single_vector_element<C>(self, cx: &C, expected_size: Size) -> bool
+    where
+        Ty: TyAbiInterface<'a, C>,
+        C: HasDataLayout,
+    {
+        match self.backend_repr {
+            BackendRepr::Vector { .. } => self.size == expected_size,
+            BackendRepr::Memory { .. } => {
+                if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
+                    self.field(cx, 0).is_single_vector_element(cx, expected_size)
+                } else {
+                    false
+                }
+            }
+            _ => false,
+        }
+    }
+
     pub fn is_adt<C>(self) -> bool
     where
         Ty: TyAbiInterface<'a, C>,
diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs
index 502e7331267..c99eb9226ef 100644
--- a/compiler/rustc_target/src/callconv/s390x.rs
+++ b/compiler/rustc_target/src/callconv/s390x.rs
@@ -1,12 +1,16 @@
-// FIXME: The assumes we're using the non-vector ABI, i.e., compiling
-// for a pre-z13 machine or using -mno-vx.
+// Reference: ELF Application Binary Interface s390x Supplement
+// https://github.com/IBM/s390x-abi
 
-use crate::abi::call::{ArgAbi, FnAbi, Reg};
-use crate::abi::{HasDataLayout, TyAbiInterface};
+use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind};
+use crate::abi::{BackendRepr, HasDataLayout, TyAbiInterface};
 use crate::spec::HasTargetSpec;
 
 fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
-    if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 {
+    let size = ret.layout.size;
+    if size.bits() <= 128 && matches!(ret.layout.backend_repr, BackendRepr::Vector { .. }) {
+        return;
+    }
+    if !ret.layout.is_aggregate() && size.bits() <= 64 {
         ret.extend_integer_width_to(64);
     } else {
         ret.make_indirect();
@@ -32,19 +36,25 @@ where
         }
         return;
     }
-    if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 {
+
+    let size = arg.layout.size;
+    if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) {
+        arg.cast_to(Reg { kind: RegKind::Vector, size });
+        return;
+    }
+    if !arg.layout.is_aggregate() && size.bits() <= 64 {
         arg.extend_integer_width_to(64);
         return;
     }
 
     if arg.layout.is_single_fp_element(cx) {
-        match arg.layout.size.bytes() {
+        match size.bytes() {
             4 => arg.cast_to(Reg::f32()),
             8 => arg.cast_to(Reg::f64()),
             _ => arg.make_indirect(),
         }
     } else {
-        match arg.layout.size.bytes() {
+        match size.bytes() {
             1 => arg.cast_to(Reg::i8()),
             2 => arg.cast_to(Reg::i16()),
             4 => arg.cast_to(Reg::i32()),
diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs
index 3efbb464836..a84a18a433f 100644
--- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs
@@ -6,9 +6,6 @@ pub(crate) fn target() -> Target {
     base.endian = Endian::Big;
     // z10 is the oldest CPU supported by LLVM
     base.cpu = "z10".into();
-    // FIXME: The ABI implementation in abi/call/s390x.rs is for now hard-coded to assume the no-vector
-    // ABI. Pass the -vector feature string to LLVM to respect this assumption.
-    base.features = "-vector".into();
     base.max_atomic_width = Some(128);
     base.min_global_align = Some(16);
     base.stack_probes = StackProbeType::Inline;
diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs
index 65b5c1167bd..4bde0fb729c 100644
--- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs
@@ -6,9 +6,6 @@ pub(crate) fn target() -> Target {
     base.endian = Endian::Big;
     // z10 is the oldest CPU supported by LLVM
     base.cpu = "z10".into();
-    // FIXME: The ABI implementation in abi/call/s390x.rs is for now hard-coded to assume the no-vector
-    // ABI. Pass the -vector feature string to LLVM to respect this assumption.
-    base.features = "-vector".into();
     base.max_atomic_width = Some(128);
     base.min_global_align = Some(16);
     base.static_position_independent_executables = true;
diff --git a/tests/assembly/s390x-vector-abi.rs b/tests/assembly/s390x-vector-abi.rs
new file mode 100644
index 00000000000..c1935582561
--- /dev/null
+++ b/tests/assembly/s390x-vector-abi.rs
@@ -0,0 +1,322 @@
+//@ revisions: z10 z10_vector z13 z13_no_vector
+// ignore-tidy-linelength
+//@ assembly-output: emit-asm
+//@ compile-flags: -O -Z merge-functions=disabled
+//@[z10] compile-flags: --target s390x-unknown-linux-gnu --cfg no_vector
+//@[z10] needs-llvm-components: systemz
+//@[z10_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=+vector
+//@[z10_vector] needs-llvm-components: systemz
+//@[z13] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13
+//@[z13] needs-llvm-components: systemz
+//@[z13_no_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector --cfg no_vector
+//@[z13_no_vector] needs-llvm-components: systemz
+
+#![feature(no_core, lang_items, repr_simd, s390x_target_feature)]
+#![no_core]
+#![crate_type = "lib"]
+#![allow(non_camel_case_types)]
+
+// Cases where vector feature is disabled are rejected.
+// See tests/ui/simd-abi-checks-s390x.rs for test for them.
+
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+#[lang = "freeze"]
+pub trait Freeze {}
+
+impl<T: Copy, const N: usize> Copy for [T; N] {}
+
+#[lang = "phantom_data"]
+pub struct PhantomData<T: ?Sized>;
+impl<T: ?Sized> Copy for PhantomData<T> {}
+
+#[repr(simd)]
+pub struct i8x8([i8; 8]);
+#[repr(simd)]
+pub struct i8x16([i8; 16]);
+#[repr(simd)]
+pub struct i8x32([i8; 32]);
+#[repr(C)]
+pub struct Wrapper<T>(T);
+#[repr(C, align(16))]
+pub struct WrapperAlign16<T>(T);
+#[repr(C)]
+pub struct WrapperWithZst<T>(T, PhantomData<()>);
+#[repr(transparent)]
+pub struct TransparentWrapper<T>(T);
+
+impl Copy for i8 {}
+impl Copy for i64 {}
+impl Copy for i8x8 {}
+impl Copy for i8x16 {}
+impl Copy for i8x32 {}
+impl<T: Copy> Copy for Wrapper<T> {}
+impl<T: Copy> Copy for WrapperAlign16<T> {}
+impl<T: Copy> Copy for WrapperWithZst<T> {}
+impl<T: Copy> Copy for TransparentWrapper<T> {}
+
+// CHECK-LABEL: vector_ret_small:
+// CHECK: vlrepg %v24, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 {
+    *x
+}
+// CHECK-LABEL: vector_ret:
+// CHECK: vl %v24, 0(%r2), 3
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_ret(x: &i8x16) -> i8x16 {
+    *x
+}
+// CHECK-LABEL: vector_ret_large:
+// z10: vl %v0, 16(%r3), 4
+// z10-NEXT: vl %v1, 0(%r3), 4
+// z10-NEXT: vst %v0, 16(%r2), 4
+// z10-NEXT: vst %v1, 0(%r2), 4
+// z10-NEXT: br %r14
+// z13: vl %v0, 0(%r3), 4
+// z13-NEXT: vl %v1, 16(%r3), 4
+// z13-NEXT: vst %v1, 16(%r2), 4
+// z13-NEXT: vst %v0, 0(%r2), 4
+// z13-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_ret_large(x: &i8x32) -> i8x32 {
+    *x
+}
+
+// CHECK-LABEL: vector_wrapper_ret_small:
+// CHECK: mvc 0(8,%r2), 0(%r3)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_ret_small(x: &Wrapper<i8x8>) -> Wrapper<i8x8> {
+    *x
+}
+// CHECK-LABEL: vector_wrapper_ret:
+// CHECK: mvc 0(16,%r2), 0(%r3)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_ret(x: &Wrapper<i8x16>) -> Wrapper<i8x16> {
+    *x
+}
+// CHECK-LABEL: vector_wrapper_ret_large:
+// z10: vl %v0, 16(%r3), 4
+// z10-NEXT: vl %v1, 0(%r3), 4
+// z10-NEXT: vst %v0, 16(%r2), 4
+// z10-NEXT: vst %v1, 0(%r2), 4
+// z10-NEXT: br %r14
+// z13: vl %v0, 16(%r3), 4
+// z13-NEXT: vst %v0, 16(%r2), 4
+// z13-NEXT: vl %v0, 0(%r3), 4
+// z13-NEXT: vst %v0, 0(%r2), 4
+// z13-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_ret_large(x: &Wrapper<i8x32>) -> Wrapper<i8x32> {
+    *x
+}
+
+// CHECK-LABEL: vector_wrapper_padding_ret:
+// CHECK: mvc 0(16,%r2), 0(%r3)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_padding_ret(x: &WrapperAlign16<i8x8>) -> WrapperAlign16<i8x8> {
+    *x
+}
+
+// CHECK-LABEL: vector_wrapper_with_zst_ret_small:
+// CHECK: mvc 0(8,%r2), 0(%r3)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_with_zst_ret_small(
+    x: &WrapperWithZst<i8x8>,
+) -> WrapperWithZst<i8x8> {
+    *x
+}
+// CHECK-LABEL: vector_wrapper_with_zst_ret:
+// CHECK: mvc 0(16,%r2), 0(%r3)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_with_zst_ret(
+    x: &WrapperWithZst<i8x16>,
+) -> WrapperWithZst<i8x16> {
+    *x
+}
+// CHECK-LABEL: vector_wrapper_with_zst_ret_large:
+// z10: vl %v0, 16(%r3), 4
+// z10-NEXT: vl %v1, 0(%r3), 4
+// z10-NEXT: vst %v0, 16(%r2), 4
+// z10-NEXT: vst %v1, 0(%r2), 4
+// z10-NEXT: br %r14
+// z13: vl %v0, 16(%r3), 4
+// z13-NEXT: vst %v0, 16(%r2), 4
+// z13-NEXT: vl %v0, 0(%r3), 4
+// z13-NEXT: vst %v0, 0(%r2), 4
+// z13-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_with_zst_ret_large(
+    x: &WrapperWithZst<i8x32>,
+) -> WrapperWithZst<i8x32> {
+    *x
+}
+
+// CHECK-LABEL: vector_transparent_wrapper_ret_small:
+// CHECK: vlrepg %v24, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_transparent_wrapper_ret_small(
+    x: &TransparentWrapper<i8x8>,
+) -> TransparentWrapper<i8x8> {
+    *x
+}
+// CHECK-LABEL: vector_transparent_wrapper_ret:
+// CHECK: vl %v24, 0(%r2), 3
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_transparent_wrapper_ret(
+    x: &TransparentWrapper<i8x16>,
+) -> TransparentWrapper<i8x16> {
+    *x
+}
+// CHECK-LABEL: vector_transparent_wrapper_ret_large:
+// z10: vl %v0, 16(%r3), 4
+// z10-NEXT: vl %v1, 0(%r3), 4
+// z10-NEXT: vst %v0, 16(%r2), 4
+// z10-NEXT: vst %v1, 0(%r2), 4
+// z10-NEXT: br %r14
+// z13: vl %v0, 0(%r3), 4
+// z13-NEXT: vl %v1, 16(%r3), 4
+// z13-NEXT: vst %v1, 16(%r2), 4
+// z13-NEXT: vst %v0, 0(%r2), 4
+// z13-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_transparent_wrapper_ret_large(
+    x: &TransparentWrapper<i8x32>,
+) -> TransparentWrapper<i8x32> {
+    *x
+}
+
+// CHECK-LABEL: vector_arg_small:
+// CHECK: vlgvg %r2, %v24, 0
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_arg_small(x: i8x8) -> i64 {
+    unsafe { *(&x as *const i8x8 as *const i64) }
+}
+// CHECK-LABEL: vector_arg:
+// CHECK: vlgvg %r2, %v24, 0
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_arg(x: i8x16) -> i64 {
+    unsafe { *(&x as *const i8x16 as *const i64) }
+}
+// CHECK-LABEL: vector_arg_large:
+// CHECK: lg %r2, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_arg_large(x: i8x32) -> i64 {
+    unsafe { *(&x as *const i8x32 as *const i64) }
+}
+
+// CHECK-LABEL: vector_wrapper_arg_small:
+// CHECK: vlgvg %r2, %v24, 0
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 {
+    unsafe { *(&x as *const Wrapper<i8x8> as *const i64) }
+}
+// CHECK-LABEL: vector_wrapper_arg:
+// CHECK: vlgvg %r2, %v24, 0
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 {
+    unsafe { *(&x as *const Wrapper<i8x16> as *const i64) }
+}
+// CHECK-LABEL: vector_wrapper_arg_large:
+// CHECK: lg %r2, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_arg_large(x: Wrapper<i8x32>) -> i64 {
+    unsafe { *(&x as *const Wrapper<i8x32> as *const i64) }
+}
+
+// https://github.com/rust-lang/rust/pull/131586#discussion_r1837071121
+// CHECK-LABEL: vector_wrapper_padding_arg:
+// CHECK: lg %r2, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_padding_arg(x: WrapperAlign16<i8x8>) -> i64 {
+    unsafe { *(&x as *const WrapperAlign16<i8x8> as *const i64) }
+}
+
+// CHECK-LABEL: vector_wrapper_with_zst_arg_small:
+// CHECK: .cfi_startproc
+// CHECK-NOT: vlgvg
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_with_zst_arg_small(x: WrapperWithZst<i8x8>) -> i64 {
+    unsafe { *(&x as *const WrapperWithZst<i8x8> as *const i64) }
+}
+// CHECK-LABEL: vector_wrapper_with_zst_arg:
+// CHECK: lg %r2, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_with_zst_arg(x: WrapperWithZst<i8x16>) -> i64 {
+    unsafe { *(&x as *const WrapperWithZst<i8x16> as *const i64) }
+}
+// CHECK-LABEL: vector_wrapper_with_zst_arg_large:
+// CHECK: lg %r2, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_wrapper_with_zst_arg_large(x: WrapperWithZst<i8x32>) -> i64 {
+    unsafe { *(&x as *const WrapperWithZst<i8x32> as *const i64) }
+}
+
+// CHECK-LABEL: vector_transparent_wrapper_arg_small:
+// CHECK: vlgvg %r2, %v24, 0
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 {
+    unsafe { *(&x as *const TransparentWrapper<i8x8> as *const i64) }
+}
+// CHECK-LABEL: vector_transparent_wrapper_arg:
+// CHECK: vlgvg %r2, %v24, 0
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 {
+    unsafe { *(&x as *const TransparentWrapper<i8x16> as *const i64) }
+}
+// CHECK-LABEL: vector_transparent_wrapper_arg_large:
+// CHECK: lg %r2, 0(%r2)
+// CHECK-NEXT: br %r14
+#[cfg_attr(no_vector, target_feature(enable = "vector"))]
+#[no_mangle]
+unsafe extern "C" fn vector_transparent_wrapper_arg_large(x: TransparentWrapper<i8x32>) -> i64 {
+    unsafe { *(&x as *const TransparentWrapper<i8x32> as *const i64) }
+}
diff --git a/tests/ui/simd-abi-checks-s390x.rs b/tests/ui/simd-abi-checks-s390x.rs
new file mode 100644
index 00000000000..15df66a2ced
--- /dev/null
+++ b/tests/ui/simd-abi-checks-s390x.rs
@@ -0,0 +1,174 @@
+//@ revisions: z10 z13_no_vector z13_soft_float
+//@ build-fail
+//@[z10] compile-flags: --target s390x-unknown-linux-gnu
+//@[z10] needs-llvm-components: systemz
+//@[z13_no_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector
+//@[z13_no_vector] needs-llvm-components: systemz
+// FIXME: +soft-float itself doesn't set -vector
+//@[z13_soft_float] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector,+soft-float
+//@[z13_soft_float] needs-llvm-components: systemz
+
+#![feature(no_core, lang_items, repr_simd, s390x_target_feature)]
+#![no_core]
+#![crate_type = "lib"]
+#![allow(non_camel_case_types, improper_ctypes_definitions)]
+#![deny(abi_unsupported_vector_types)]
+
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+#[lang = "freeze"]
+pub trait Freeze {}
+
+impl<T: Copy, const N: usize> Copy for [T; N] {}
+
+#[repr(simd)]
+pub struct i8x8([i8; 8]);
+#[repr(simd)]
+pub struct i8x16([i8; 16]);
+#[repr(simd)]
+pub struct i8x32([i8; 32]);
+#[repr(C)]
+pub struct Wrapper<T>(T);
+#[repr(transparent)]
+pub struct TransparentWrapper<T>(T);
+
+impl Copy for i8 {}
+impl Copy for i64 {}
+impl Copy for i8x8 {}
+impl Copy for i8x16 {}
+impl Copy for i8x32 {}
+impl<T: Copy> Copy for Wrapper<T> {}
+impl<T: Copy> Copy for TransparentWrapper<T> {}
+
+#[no_mangle]
+extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    *x
+}
+#[no_mangle]
+extern "C" fn vector_ret(x: &i8x16) -> i8x16 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    *x
+}
+#[no_mangle]
+extern "C" fn vector_ret_large(x: &i8x32) -> i8x32 {
+    // Ok
+    *x
+}
+
+#[no_mangle]
+#[target_feature(enable = "vector")]
+unsafe extern "C" fn vector_ret_target_feature_small(x: &i8x8) -> i8x8 {
+    // Ok
+    *x
+}
+#[no_mangle]
+#[target_feature(enable = "vector")]
+unsafe extern "C" fn vector_target_feature_ret(x: &i8x16) -> i8x16 {
+    // Ok
+    *x
+}
+#[no_mangle]
+#[target_feature(enable = "vector")]
+unsafe extern "C" fn vector_ret_target_feature_large(x: &i8x32) -> i8x32 {
+    // Ok
+    *x
+}
+
+#[no_mangle]
+extern "C" fn vector_wrapper_ret_small(x: &Wrapper<i8x8>) -> Wrapper<i8x8> {
+    // Ok
+    *x
+}
+#[no_mangle]
+extern "C" fn vector_wrapper_ret(x: &Wrapper<i8x16>) -> Wrapper<i8x16> {
+    // Ok
+    *x
+}
+#[no_mangle]
+extern "C" fn vector_wrapper_ret_large(x: &Wrapper<i8x32>) -> Wrapper<i8x32> {
+    // Ok
+    *x
+}
+
+#[no_mangle]
+extern "C" fn vector_transparent_wrapper_ret_small(
+    x: &TransparentWrapper<i8x8>,
+) -> TransparentWrapper<i8x8> {
+    //~^^^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^^^ WARN this was previously accepted
+    *x
+}
+#[no_mangle]
+extern "C" fn vector_transparent_wrapper_ret(
+    x: &TransparentWrapper<i8x16>,
+) -> TransparentWrapper<i8x16> {
+    //~^^^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^^^ WARN this was previously accepted
+    *x
+}
+#[no_mangle]
+extern "C" fn vector_transparent_wrapper_ret_large(
+    x: &TransparentWrapper<i8x32>,
+) -> TransparentWrapper<i8x32> {
+    // Ok
+    *x
+}
+
+#[no_mangle]
+extern "C" fn vector_arg_small(x: i8x8) -> i64 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    unsafe { *(&x as *const i8x8 as *const i64) }
+}
+#[no_mangle]
+extern "C" fn vector_arg(x: i8x16) -> i64 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    unsafe { *(&x as *const i8x16 as *const i64) }
+}
+#[no_mangle]
+extern "C" fn vector_arg_large(x: i8x32) -> i64 {
+    // Ok
+    unsafe { *(&x as *const i8x32 as *const i64) }
+}
+
+#[no_mangle]
+extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    unsafe { *(&x as *const Wrapper<i8x8> as *const i64) }
+}
+#[no_mangle]
+extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    unsafe { *(&x as *const Wrapper<i8x16> as *const i64) }
+}
+#[no_mangle]
+extern "C" fn vector_wrapper_arg_large(x: Wrapper<i8x32>) -> i64 {
+    // Ok
+    unsafe { *(&x as *const Wrapper<i8x32> as *const i64) }
+}
+
+#[no_mangle]
+extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    unsafe { *(&x as *const TransparentWrapper<i8x8> as *const i64) }
+}
+#[no_mangle]
+extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 {
+    //~^ ERROR this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+    //~^^ WARN this was previously accepted
+    unsafe { *(&x as *const TransparentWrapper<i8x16> as *const i64) }
+}
+#[no_mangle]
+extern "C" fn vector_transparent_wrapper_arg_large(x: TransparentWrapper<i8x32>) -> i64 {
+    // Ok
+    unsafe { *(&x as *const TransparentWrapper<i8x32> as *const i64) }
+}
diff --git a/tests/ui/simd-abi-checks-s390x.z10.stderr b/tests/ui/simd-abi-checks-s390x.z10.stderr
new file mode 100644
index 00000000000..a91322ec058
--- /dev/null
+++ b/tests/ui/simd-abi-checks-s390x.z10.stderr
@@ -0,0 +1,111 @@
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:46:1
+   |
+LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+note: the lint level is defined here
+  --> $DIR/simd-abi-checks-s390x.rs:15:9
+   |
+LL | #![deny(abi_unsupported_vector_types)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:52:1
+   |
+LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:99:1
+   |
+LL | / extern "C" fn vector_transparent_wrapper_ret_small(
+LL | |     x: &TransparentWrapper<i8x8>,
+LL | | ) -> TransparentWrapper<i8x8> {
+   | |_____________________________^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:107:1
+   |
+LL | / extern "C" fn vector_transparent_wrapper_ret(
+LL | |     x: &TransparentWrapper<i8x16>,
+LL | | ) -> TransparentWrapper<i8x16> {
+   | |______________________________^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:123:1
+   |
+LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:129:1
+   |
+LL | extern "C" fn vector_arg(x: i8x16) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:141:1
+   |
+LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:147:1
+   |
+LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:159:1
+   |
+LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:165:1
+   |
+LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/simd-abi-checks-s390x.z13_no_vector.stderr b/tests/ui/simd-abi-checks-s390x.z13_no_vector.stderr
new file mode 100644
index 00000000000..a91322ec058
--- /dev/null
+++ b/tests/ui/simd-abi-checks-s390x.z13_no_vector.stderr
@@ -0,0 +1,111 @@
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:46:1
+   |
+LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+note: the lint level is defined here
+  --> $DIR/simd-abi-checks-s390x.rs:15:9
+   |
+LL | #![deny(abi_unsupported_vector_types)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:52:1
+   |
+LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:99:1
+   |
+LL | / extern "C" fn vector_transparent_wrapper_ret_small(
+LL | |     x: &TransparentWrapper<i8x8>,
+LL | | ) -> TransparentWrapper<i8x8> {
+   | |_____________________________^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:107:1
+   |
+LL | / extern "C" fn vector_transparent_wrapper_ret(
+LL | |     x: &TransparentWrapper<i8x16>,
+LL | | ) -> TransparentWrapper<i8x16> {
+   | |______________________________^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:123:1
+   |
+LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:129:1
+   |
+LL | extern "C" fn vector_arg(x: i8x16) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:141:1
+   |
+LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:147:1
+   |
+LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:159:1
+   |
+LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:165:1
+   |
+LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/simd-abi-checks-s390x.z13_soft_float.stderr b/tests/ui/simd-abi-checks-s390x.z13_soft_float.stderr
new file mode 100644
index 00000000000..a91322ec058
--- /dev/null
+++ b/tests/ui/simd-abi-checks-s390x.z13_soft_float.stderr
@@ -0,0 +1,111 @@
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:46:1
+   |
+LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+note: the lint level is defined here
+  --> $DIR/simd-abi-checks-s390x.rs:15:9
+   |
+LL | #![deny(abi_unsupported_vector_types)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:52:1
+   |
+LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:99:1
+   |
+LL | / extern "C" fn vector_transparent_wrapper_ret_small(
+LL | |     x: &TransparentWrapper<i8x8>,
+LL | | ) -> TransparentWrapper<i8x8> {
+   | |_____________________________^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:107:1
+   |
+LL | / extern "C" fn vector_transparent_wrapper_ret(
+LL | |     x: &TransparentWrapper<i8x16>,
+LL | | ) -> TransparentWrapper<i8x16> {
+   | |______________________________^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:123:1
+   |
+LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:129:1
+   |
+LL | extern "C" fn vector_arg(x: i8x16) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:141:1
+   |
+LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:147:1
+   |
+LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:159:1
+   |
+LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: this function definition uses a SIMD vector type that (with the chosen ABI) requires the `vector` target feature, which is not enabled
+  --> $DIR/simd-abi-checks-s390x.rs:165:1
+   |
+LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116558 <https://github.com/rust-lang/rust/issues/116558>
+   = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`)
+
+error: aborting due to 10 previous errors
+

From 98ab898d30f0914aa9bc0c3864dc059d45c2c7fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= <matthias.krueger@famsik.de>
Date: Fri, 15 Nov 2024 18:49:43 +0100
Subject: [PATCH 5/8] tests: ui/inline-consts: add issue number to a test,
 rename other tests from a_b_c to a-b-c

---
 ...liveness.rs => const-block-pat-liveness.rs} |  0
 ...w.rs => cross-const-control-flow-125846.rs} |  0
 ... => cross-const-control-flow-125846.stderr} | 18 +++++++++---------
 ...ables.rs => referencing-local-variables.rs} |  0
 ...derr => referencing-local-variables.stderr} |  2 +-
 .../{uninit_local.rs => uninit-local.rs}       |  0
 ...uninit_local.stderr => uninit-local.stderr} |  2 +-
 7 files changed, 11 insertions(+), 11 deletions(-)
 rename tests/ui/inline-const/{const_block_pat_liveness.rs => const-block-pat-liveness.rs} (100%)
 rename tests/ui/inline-const/{cross_const_control_flow.rs => cross-const-control-flow-125846.rs} (100%)
 rename tests/ui/inline-const/{cross_const_control_flow.stderr => cross-const-control-flow-125846.stderr} (82%)
 rename tests/ui/inline-const/{referencing_local_variables.rs => referencing-local-variables.rs} (100%)
 rename tests/ui/inline-const/{referencing_local_variables.stderr => referencing-local-variables.stderr} (87%)
 rename tests/ui/inline-const/{uninit_local.rs => uninit-local.rs} (100%)
 rename tests/ui/inline-const/{uninit_local.stderr => uninit-local.stderr} (93%)

diff --git a/tests/ui/inline-const/const_block_pat_liveness.rs b/tests/ui/inline-const/const-block-pat-liveness.rs
similarity index 100%
rename from tests/ui/inline-const/const_block_pat_liveness.rs
rename to tests/ui/inline-const/const-block-pat-liveness.rs
diff --git a/tests/ui/inline-const/cross_const_control_flow.rs b/tests/ui/inline-const/cross-const-control-flow-125846.rs
similarity index 100%
rename from tests/ui/inline-const/cross_const_control_flow.rs
rename to tests/ui/inline-const/cross-const-control-flow-125846.rs
diff --git a/tests/ui/inline-const/cross_const_control_flow.stderr b/tests/ui/inline-const/cross-const-control-flow-125846.stderr
similarity index 82%
rename from tests/ui/inline-const/cross_const_control_flow.stderr
rename to tests/ui/inline-const/cross-const-control-flow-125846.stderr
index ecfa921edd2..4aa1c273504 100644
--- a/tests/ui/inline-const/cross_const_control_flow.stderr
+++ b/tests/ui/inline-const/cross-const-control-flow-125846.stderr
@@ -1,5 +1,5 @@
 error[E0767]: use of unreachable label `'a`
-  --> $DIR/cross_const_control_flow.rs:9:25
+  --> $DIR/cross-const-control-flow-125846.rs:9:25
    |
 LL |     'a: { const { break 'a } }
    |     --                  ^^ unreachable label `'a`
@@ -9,7 +9,7 @@ LL |     'a: { const { break 'a } }
    = note: labels are unreachable through functions, closures, async blocks and modules
 
 error[E0767]: use of unreachable label `'a`
-  --> $DIR/cross_const_control_flow.rs:22:28
+  --> $DIR/cross-const-control-flow-125846.rs:22:28
    |
 LL |     'a: { const { continue 'a } }
    |     --                     ^^ unreachable label `'a`
@@ -19,7 +19,7 @@ LL |     'a: { const { continue 'a } }
    = note: labels are unreachable through functions, closures, async blocks and modules
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/cross_const_control_flow.rs:41:14
+  --> $DIR/cross-const-control-flow-125846.rs:41:14
    |
 LL |     const { &x };
    |              ^ non-constant value
@@ -30,7 +30,7 @@ LL |     const x: /* Type */ = 1;
    |     ~~~~~  ++++++++++++
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/cross_const_control_flow.rs:35:22
+  --> $DIR/cross-const-control-flow-125846.rs:35:22
    |
 LL |     const { async {}.await }
    |           -----------^^^^^--
@@ -39,31 +39,31 @@ LL |     const { async {}.await }
    |           this is not `async`
 
 error[E0268]: `break` outside of a loop or labeled block
-  --> $DIR/cross_const_control_flow.rs:9:19
+  --> $DIR/cross-const-control-flow-125846.rs:9:19
    |
 LL |     'a: { const { break 'a } }
    |                   ^^^^^^^^ cannot `break` outside of a loop or labeled block
 
 error[E0268]: `break` outside of a loop or labeled block
-  --> $DIR/cross_const_control_flow.rs:16:17
+  --> $DIR/cross-const-control-flow-125846.rs:16:17
    |
 LL |         const { break }
    |                 ^^^^^ cannot `break` outside of a loop or labeled block
 
 error[E0268]: `continue` outside of a loop
-  --> $DIR/cross_const_control_flow.rs:22:19
+  --> $DIR/cross-const-control-flow-125846.rs:22:19
    |
 LL |     'a: { const { continue 'a } }
    |                   ^^^^^^^^^^^ cannot `continue` outside of a loop
 
 error[E0268]: `continue` outside of a loop
-  --> $DIR/cross_const_control_flow.rs:29:17
+  --> $DIR/cross-const-control-flow-125846.rs:29:17
    |
 LL |         const { continue }
    |                 ^^^^^^^^ cannot `continue` outside of a loop
 
 error[E0572]: return statement outside of function body
-  --> $DIR/cross_const_control_flow.rs:4:13
+  --> $DIR/cross-const-control-flow-125846.rs:4:13
    |
 LL | / fn foo() {
 LL | |     const { return }
diff --git a/tests/ui/inline-const/referencing_local_variables.rs b/tests/ui/inline-const/referencing-local-variables.rs
similarity index 100%
rename from tests/ui/inline-const/referencing_local_variables.rs
rename to tests/ui/inline-const/referencing-local-variables.rs
diff --git a/tests/ui/inline-const/referencing_local_variables.stderr b/tests/ui/inline-const/referencing-local-variables.stderr
similarity index 87%
rename from tests/ui/inline-const/referencing_local_variables.stderr
rename to tests/ui/inline-const/referencing-local-variables.stderr
index 4a0a5406602..7e1cecdddcb 100644
--- a/tests/ui/inline-const/referencing_local_variables.stderr
+++ b/tests/ui/inline-const/referencing-local-variables.stderr
@@ -1,5 +1,5 @@
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/referencing_local_variables.rs:2:13
+  --> $DIR/referencing-local-variables.rs:2:13
    |
 LL | const fn test_me<T>(a: usize) -> usize {
    |                     - this would need to be a `const`
diff --git a/tests/ui/inline-const/uninit_local.rs b/tests/ui/inline-const/uninit-local.rs
similarity index 100%
rename from tests/ui/inline-const/uninit_local.rs
rename to tests/ui/inline-const/uninit-local.rs
diff --git a/tests/ui/inline-const/uninit_local.stderr b/tests/ui/inline-const/uninit-local.stderr
similarity index 93%
rename from tests/ui/inline-const/uninit_local.stderr
rename to tests/ui/inline-const/uninit-local.stderr
index 37b78e337e7..fab65ffb5fa 100644
--- a/tests/ui/inline-const/uninit_local.stderr
+++ b/tests/ui/inline-const/uninit-local.stderr
@@ -1,5 +1,5 @@
 error[E0381]: used binding `x` isn't initialized
-  --> $DIR/uninit_local.rs:4:15
+  --> $DIR/uninit-local.rs:4:15
    |
 LL |         let x: bool;
    |             - binding declared here but left uninitialized

From dc97db105ac3ebefe0ab51b88a26f87ef4fec207 Mon Sep 17 00:00:00 2001
From: Jane Losare-Lusby <jlusby@yaah.dev>
Date: Fri, 6 Sep 2024 13:05:01 -0700
Subject: [PATCH 6/8] unstable feature usage metrics

---
 Cargo.lock                                    |  2 +
 compiler/rustc_driver_impl/messages.ftl       |  2 +
 compiler/rustc_driver_impl/src/lib.rs         | 24 ++++-
 .../src/session_diagnostics.rs                |  8 ++
 compiler/rustc_feature/Cargo.toml             |  2 +
 compiler/rustc_feature/src/unstable.rs        | 50 +++++++++++
 compiler/rustc_session/src/config.rs          |  2 +-
 compiler/rustc_session/src/options.rs         |  2 +-
 .../unstable-feature-usage-metrics/lib.rs     |  9 ++
 .../unstable-feature-usage-metrics/rmake.rs   | 87 +++++++++++++++++++
 10 files changed, 185 insertions(+), 3 deletions(-)
 create mode 100644 tests/run-make/unstable-feature-usage-metrics/lib.rs
 create mode 100644 tests/run-make/unstable-feature-usage-metrics/rmake.rs

diff --git a/Cargo.lock b/Cargo.lock
index b98c4fd0642..ed624af2476 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3684,6 +3684,8 @@ version = "0.0.0"
 dependencies = [
  "rustc_data_structures",
  "rustc_span",
+ "serde",
+ "serde_json",
 ]
 
 [[package]]
diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl
index 31837e01764..05e11c4527f 100644
--- a/compiler/rustc_driver_impl/messages.ftl
+++ b/compiler/rustc_driver_impl/messages.ftl
@@ -23,3 +23,5 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver
 driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}`
 
 driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file
+
+driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error}
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index b6f7abed6f3..6c3c81d9f7a 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -50,6 +50,7 @@ use rustc_interface::{Linker, Queries, interface, passes};
 use rustc_lint::unerased_lint_store;
 use rustc_metadata::creader::MetadataLoader;
 use rustc_metadata::locator;
+use rustc_middle::ty::TyCtxt;
 use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
 use rustc_session::config::{
     CG_OPTIONS, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, Z_OPTIONS,
@@ -102,7 +103,7 @@ mod signal_handler {
 
 use crate::session_diagnostics::{
     RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
-    RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead,
+    RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage,
 };
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
@@ -430,6 +431,10 @@ fn run_compiler(
             // Make sure name resolution and macro expansion is run.
             queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());
 
+            if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
+                queries.global_ctxt()?.enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir));
+            }
+
             if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
                 return early_exit();
             }
@@ -474,6 +479,23 @@ fn run_compiler(
     })
 }
 
+fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &PathBuf) {
+    let output_filenames = tcxt.output_filenames(());
+    let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-");
+    let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json");
+    let metrics_file_stem =
+        metrics_path.file_name().expect("there should be a valid default output filename");
+    metrics_file_name.push(metrics_file_stem);
+    metrics_path.pop();
+    metrics_path.push(metrics_file_name);
+    if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
+        // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
+        // default metrics" to only produce a warning when metrics are enabled by default and emit
+        // an error only when the user manually enables metrics
+        tcxt.dcx().emit_err(UnstableFeatureUsage { error });
+    }
+}
+
 // Extract output directory and file from matches.
 fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileName>) {
     let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs
index 449878f28c4..e06c56539d1 100644
--- a/compiler/rustc_driver_impl/src/session_diagnostics.rs
+++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs
@@ -1,3 +1,5 @@
+use std::error::Error;
+
 use rustc_macros::{Diagnostic, Subdiagnostic};
 
 #[derive(Diagnostic)]
@@ -93,3 +95,9 @@ pub(crate) struct IceFlags {
 #[derive(Diagnostic)]
 #[diag(driver_impl_ice_exclude_cargo_defaults)]
 pub(crate) struct IceExcludeCargoDefaults;
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_unstable_feature_usage)]
+pub(crate) struct UnstableFeatureUsage {
+    pub error: Box<dyn Error>,
+}
diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml
index 9df320e1279..77de7fabd4f 100644
--- a/compiler/rustc_feature/Cargo.toml
+++ b/compiler/rustc_feature/Cargo.toml
@@ -7,4 +7,6 @@ edition = "2021"
 # tidy-alphabetical-start
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_span = { path = "../rustc_span" }
+serde = { version = "1.0.125", features = [ "derive" ] }
+serde_json = "1.0.59"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 8326d0031ea..afd793d71aa 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -1,5 +1,7 @@
 //! List of the unstable feature gates.
 
+use std::path::PathBuf;
+
 use rustc_data_structures::fx::FxHashSet;
 use rustc_span::Span;
 use rustc_span::symbol::{Symbol, sym};
@@ -649,6 +651,54 @@ declare_features! (
     // -------------------------------------------------------------------------
 );
 
+impl Features {
+    pub fn dump_feature_usage_metrics(
+        &self,
+        metrics_path: PathBuf,
+    ) -> Result<(), Box<dyn std::error::Error>> {
+        #[derive(serde::Serialize)]
+        struct LibFeature {
+            symbol: String,
+        }
+
+        #[derive(serde::Serialize)]
+        struct LangFeature {
+            symbol: String,
+            since: Option<String>,
+        }
+
+        #[derive(serde::Serialize)]
+        struct FeatureUsage {
+            lib_features: Vec<LibFeature>,
+            lang_features: Vec<LangFeature>,
+        }
+
+        let metrics_file = std::fs::File::create(metrics_path)?;
+        let metrics_file = std::io::BufWriter::new(metrics_file);
+
+        let lib_features = self
+            .enabled_lib_features
+            .iter()
+            .map(|EnabledLibFeature { gate_name, .. }| LibFeature { symbol: gate_name.to_string() })
+            .collect();
+
+        let lang_features = self
+            .enabled_lang_features
+            .iter()
+            .map(|EnabledLangFeature { gate_name, stable_since, .. }| LangFeature {
+                symbol: gate_name.to_string(),
+                since: stable_since.map(|since| since.to_string()),
+            })
+            .collect();
+
+        let feature_usage = FeatureUsage { lib_features, lang_features };
+
+        serde_json::to_writer(metrics_file, &feature_usage)?;
+
+        Ok(())
+    }
+}
+
 /// Some features are not allowed to be used together at the same time, if
 /// the two are present, produce an error.
 ///
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 44721bd889a..bfd38728edb 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1068,7 +1068,7 @@ impl OutputFilenames {
         self.with_directory_and_extension(&self.out_directory, extension)
     }
 
-    fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
+    pub fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
         let mut path = directory.join(&self.filestem);
         path.set_extension(extension);
         path
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index f485e8cace5..5d237575991 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1889,7 +1889,7 @@ options! {
     meta_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather metadata statistics (default: no)"),
     metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
-        "stores metrics about the errors being emitted by rustc to disk"),
+        "the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
     mir_emit_retag: bool = (false, parse_bool, [TRACKED],
         "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
         (default: no)"),
diff --git a/tests/run-make/unstable-feature-usage-metrics/lib.rs b/tests/run-make/unstable-feature-usage-metrics/lib.rs
new file mode 100644
index 00000000000..2202d722c49
--- /dev/null
+++ b/tests/run-make/unstable-feature-usage-metrics/lib.rs
@@ -0,0 +1,9 @@
+#![feature(ascii_char)] // random lib feature
+#![feature(box_patterns)] // random lang feature
+
+// picked arbitrary unstable features, just need a random lib and lang feature, ideally ones that
+// won't be stabilized any time soon so we don't have to update this test
+
+fn main() {
+    println!("foobar");
+}
diff --git a/tests/run-make/unstable-feature-usage-metrics/rmake.rs b/tests/run-make/unstable-feature-usage-metrics/rmake.rs
new file mode 100644
index 00000000000..7e122def6b7
--- /dev/null
+++ b/tests/run-make/unstable-feature-usage-metrics/rmake.rs
@@ -0,0 +1,87 @@
+//! This test checks if unstable feature usage metric dump files `unstable-feature-usage*.json` work
+//! as expected.
+//!
+//! - Basic sanity checks on a default ICE dump.
+//!
+//! See <https://github.com/rust-lang/rust/issues/129485>.
+//!
+//! # Test history
+//!
+//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming
+//! those will be present in this test as well on the same platform
+
+//@ ignore-windows
+//FIXME(#128911): still flakey on i686-mingw.
+
+use std::path::{Path, PathBuf};
+
+use run_make_support::rfs::create_dir_all;
+use run_make_support::{
+    cwd, filename_contains, has_extension, rfs, run_in_tmpdir, rustc, serde_json,
+    shallow_find_files,
+};
+
+fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
+    shallow_find_files(dir, |path| {
+        if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") {
+            true
+        } else {
+            dbg!(path);
+            false
+        }
+    })
+}
+
+fn main() {
+    test_metrics_dump();
+    test_metrics_errors();
+}
+
+#[track_caller]
+fn test_metrics_dump() {
+    run_in_tmpdir(|| {
+        let metrics_dir = cwd().join("metrics");
+        create_dir_all(&metrics_dir);
+        rustc()
+            .input("lib.rs")
+            .env("RUST_BACKTRACE", "short")
+            .arg(format!("-Zmetrics-dir={}", metrics_dir.display()))
+            .run();
+        let mut metrics = find_feature_usage_metrics(&metrics_dir);
+        let json_path =
+            metrics.pop().expect("there should be one metrics file in the output directory");
+
+        assert_eq!(
+            0,
+            metrics.len(),
+            "there should be no more than one metrics file in the output directory"
+        );
+
+        let message = rfs::read_to_string(json_path);
+        let parsed: serde_json::Value =
+            serde_json::from_str(&message).expect("metrics should be dumped as json");
+        let expected = serde_json::json!(
+            {
+                "lib_features":[{"symbol":"ascii_char"}],
+                "lang_features":[{"symbol":"box_patterns","since":null}]
+            }
+        );
+
+        assert_eq!(expected, parsed);
+    });
+}
+
+#[track_caller]
+fn test_metrics_errors() {
+    run_in_tmpdir(|| {
+        rustc()
+            .input("lib.rs")
+            .env("RUST_BACKTRACE", "short")
+            .arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist")
+            .run_fail()
+            .assert_stderr_contains(
+                "error: cannot dump feature usage metrics: No such file or directory",
+            )
+            .assert_stdout_not_contains("internal compiler error");
+    });
+}

From 0a14f712d7a2cf42e2e5705b2f5a8f1b8e67c7c2 Mon Sep 17 00:00:00 2001
From: Jane Losare-Lusby <jlusby42@gmail.com>
Date: Wed, 20 Nov 2024 12:11:40 -0800
Subject: [PATCH 7/8] Update
 tests/run-make/unstable-feature-usage-metrics/rmake.rs

Co-authored-by: Esteban Kuber <estebank@users.noreply.github.com>
---
 tests/run-make/unstable-feature-usage-metrics/rmake.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/run-make/unstable-feature-usage-metrics/rmake.rs b/tests/run-make/unstable-feature-usage-metrics/rmake.rs
index 7e122def6b7..1397548a6fc 100644
--- a/tests/run-make/unstable-feature-usage-metrics/rmake.rs
+++ b/tests/run-make/unstable-feature-usage-metrics/rmake.rs
@@ -51,9 +51,9 @@ fn test_metrics_dump() {
         let json_path =
             metrics.pop().expect("there should be one metrics file in the output directory");
 
-        assert_eq!(
-            0,
-            metrics.len(),
+        // After the `pop` above, there should be no files left.
+        assert!(
+            metrics.is_empty(),
             "there should be no more than one metrics file in the output directory"
         );
 

From 61a03fa98f8d9c28a08e80dcc4832728e5eb3241 Mon Sep 17 00:00:00 2001
From: Jieyou Xu <jieyouxu@outlook.com>
Date: Thu, 21 Nov 2024 17:20:37 +0800
Subject: [PATCH 8/8] Don't exclude relnotes from `needs-triage` label

---
 triagebot.toml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/triagebot.toml b/triagebot.toml
index 2483bfc4a41..b7a9b794c8b 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -451,7 +451,6 @@ new_issue = true
 exclude_labels = [
     "C-tracking-issue",
     "A-diagnostics",
-    "relnotes-tracking-issue",
 ]
 
 [autolabel."WG-trait-system-refactor"]