about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-19 18:40:33 +0000
committerbors <bors@rust-lang.org>2024-07-19 18:40:33 +0000
commitff4b39867e3033864315bf3cada039e92a6b751e (patch)
treea9fb8f61fccf3ab188bea07474e2e40da8bf5d67
parent0cd01aac6a80735cc936f75b45e3545a5273e2ad (diff)
parente28be0d16892825141044350d39766be3fa228ac (diff)
downloadrust-ff4b39867e3033864315bf3cada039e92a6b751e.tar.gz
rust-ff4b39867e3033864315bf3cada039e92a6b751e.zip
Auto merge of #127982 - matthiaskrgr:rollup-nzyvphj, r=matthiaskrgr
Rollup of 6 pull requests

Successful merges:

 - #127295 (CFI: Support provided methods on traits)
 - #127814 (`C-cmse-nonsecure-call`: improved error messages)
 - #127949 (fix: explain E0120 better cover cases when its raised)
 - #127966 (Use structured suggestions for unconstrained generic parameters on impl blocks)
 - #127976 (Lazy type aliases: Diagostics: Detect bivariant ty params that are only used recursively)
 - #127978 (Avoid ref when using format! for perf)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0120.md24
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0798.md39
-rw-r--r--compiler/rustc_error_codes/src/lib.rs1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl22
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs73
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs41
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs156
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs76
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs95
-rw-r--r--library/core/src/fmt/builders.rs2
-rw-r--r--library/core/tests/future.rs2
-rw-r--r--library/proc_macro/src/lib.rs4
-rw-r--r--library/std/src/fs/tests.rs2
-rw-r--r--library/std/src/io/error.rs2
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs21
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr47
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs24
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs27
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr4
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs23
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr43
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs54
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr84
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs53
-rw-r--r--tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr23
-rw-r--r--tests/ui/lazy-type-alias/inherent-impls-overflow.rs4
-rw-r--r--tests/ui/sanitizer/cfi-supertraits.rs6
28 files changed, 744 insertions, 212 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0120.md b/compiler/rustc_error_codes/src/error_codes/E0120.md
index dc7258d8731..aa701df5774 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0120.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0120.md
@@ -1,7 +1,7 @@
-Drop was implemented on a trait, which is not allowed: only structs and
-enums can implement Drop.
+`Drop` was implemented on a trait object or reference, which is not allowed;
+only structs, enums, and unions can implement Drop.
 
-Erroneous code example:
+Erroneous code examples:
 
 ```compile_fail,E0120
 trait MyTrait {}
@@ -11,8 +11,16 @@ impl Drop for MyTrait {
 }
 ```
 
-A workaround for this problem is to wrap the trait up in a struct, and implement
-Drop on that:
+```compile_fail,E0120
+struct Concrete {}
+
+impl Drop for &'_ mut Concrete  {
+    fn drop(&mut self) {}
+}
+```
+
+A workaround for traits is to create a wrapper struct with a generic type,
+add a trait bound to the type, and implement `Drop` on the wrapper:
 
 ```
 trait MyTrait {}
@@ -24,13 +32,13 @@ impl <T: MyTrait> Drop for MyWrapper<T> {
 
 ```
 
-Alternatively, wrapping trait objects requires something:
+Alternatively, the `Drop` wrapper can contain the trait object:
 
 ```
 trait MyTrait {}
 
-//or Box<MyTrait>, if you wanted an owned trait object
-struct MyWrapper<'a> { foo: &'a MyTrait }
+// or Box<dyn MyTrait>, if you wanted an owned trait object
+struct MyWrapper<'a> { foo: &'a dyn MyTrait }
 
 impl <'a> Drop for MyWrapper<'a> {
     fn drop(&mut self) {}
diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md
new file mode 100644
index 00000000000..da08cde3010
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0798.md
@@ -0,0 +1,39 @@
+Functions marked as `C-cmse-nonsecure-call` place restrictions on their
+inputs and outputs.
+
+- inputs must fit in the 4 available 32-bit argument registers. Alignment
+is relevant.
+- outputs must either fit in 4 bytes, or be a foundational type of
+size 8 (`i64`, `u64`, `f64`).
+- no generics can be used in the signature
+
+For more information,
+see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases).
+
+Erroneous code example:
+
+```ignore (only fails on supported targets)
+#![feature(abi_c_cmse_nonsecure_call)]
+
+#[no_mangle]
+pub fn test(
+    f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32,
+) -> u32 {
+    f(1, 2, 3, 4, 5)
+}
+```
+
+Arguments' alignment is respected. In the example below, padding is inserted
+so that the `u64` argument is passed in registers r2 and r3. There is then no
+room left for the final `f32` argument
+
+```ignore (only fails on supported targets)
+#![feature(abi_c_cmse_nonsecure_call)]
+
+#[no_mangle]
+pub fn test(
+    f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32,
+) -> u32 {
+    f(1, 2, 3.0)
+}
+```
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index d13d5e1bca2..2a7bc2501c0 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -536,6 +536,7 @@ E0794: 0794,
 E0795: 0795,
 E0796: 0796,
 E0797: 0797,
+E0798: 0798,
         );
     )
 }
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index b43559f4225..f08a0f8c8fc 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -58,6 +58,23 @@ hir_analysis_cannot_capture_late_bound_ty =
 hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
     .label = `for<...>` is here
 
+hir_analysis_cmse_call_generic =
+    function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
+
+hir_analysis_cmse_call_inputs_stack_spill =
+    arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+    .label = {$plural ->
+        [false] this argument doesn't
+        *[true] these arguments don't
+    } fit in the available registers
+    .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+hir_analysis_cmse_call_output_stack_spill =
+    return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+    .label = this type doesn't fit in the available registers
+    .note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+    .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
 hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
 
 hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
@@ -519,6 +536,11 @@ hir_analysis_typeof_reserved_keyword_used =
     .suggestion = consider replacing `typeof(...)` with an actual type
     .label = reserved keyword
 
+hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates
+    .label = unconstrained {$param_def_kind}
+    .const_param_note = expressions using a const parameter must map each value to a distinct output value
+    .const_param_note2 = proving the result of expressions other than the parameter are unique is not supported
+
 hir_analysis_unconstrained_opaque_type = unconstrained opaque type
     .note = `{$name}` must be used in combination with a concrete type within the same {$what}
 
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 809427f86ee..71a7b0b1638 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1922,31 +1922,24 @@ fn report_bivariance<'tcx>(
     );
 
     if !usage_spans.is_empty() {
-        // First, check if the ADT is (probably) cyclical. We say probably here, since
-        // we're not actually looking into substitutions, just walking through fields.
-        // And we only recurse into the fields of ADTs, and not the hidden types of
-        // opaques or anything else fancy.
+        // First, check if the ADT/LTA is (probably) cyclical. We say probably here, since we're
+        // not actually looking into substitutions, just walking through fields / the "RHS".
+        // We don't recurse into the hidden types of opaques or anything else fancy.
         let item_def_id = item.owner_id.to_def_id();
-        let is_probably_cyclical = if matches!(
-            tcx.def_kind(item_def_id),
-            DefKind::Struct | DefKind::Union | DefKind::Enum
-        ) {
-            IsProbablyCyclical { tcx, adt_def_id: item_def_id, seen: Default::default() }
-                .visit_all_fields(tcx.adt_def(item_def_id))
-                .is_break()
-        } else {
-            false
-        };
-        // If the ADT is cyclical, then if at least one usage of the type parameter or
-        // the `Self` alias is present in the, then it's probably a cyclical struct, and
-        // we should call those parameter usages recursive rather than just saying they're
-        // unused...
+        let is_probably_cyclical =
+            IsProbablyCyclical { tcx, item_def_id, seen: Default::default() }
+                .visit_def(item_def_id)
+                .is_break();
+        // If the ADT/LTA is cyclical, then if at least one usage of the type parameter or
+        // the `Self` alias is present in the, then it's probably a cyclical struct/ type
+        // alias, and we should call those parameter usages recursive rather than just saying
+        // they're unused...
         //
         // We currently report *all* of the parameter usages, since computing the exact
         // subset is very involved, and the fact we're mentioning recursion at all is
         // likely to guide the user in the right direction.
         if is_probably_cyclical {
-            let diag = tcx.dcx().create_err(errors::RecursiveGenericParameter {
+            return tcx.dcx().emit_err(errors::RecursiveGenericParameter {
                 spans: usage_spans,
                 param_span: param.span,
                 param_name,
@@ -1954,7 +1947,6 @@ fn report_bivariance<'tcx>(
                 help,
                 note: (),
             });
-            return diag.emit();
         }
     }
 
@@ -1974,42 +1966,51 @@ fn report_bivariance<'tcx>(
     diag.emit()
 }
 
-/// Detects cases where an ADT is trivially cyclical -- we want to detect this so
-/// /we only mention that its parameters are used cyclically if the ADT is truly
+/// Detects cases where an ADT/LTA is trivially cyclical -- we want to detect this so
+/// we only mention that its parameters are used cyclically if the ADT/LTA is truly
 /// cyclical.
 ///
 /// Notably, we don't consider substitutions here, so this may have false positives.
 struct IsProbablyCyclical<'tcx> {
     tcx: TyCtxt<'tcx>,
-    adt_def_id: DefId,
+    item_def_id: DefId,
     seen: FxHashSet<DefId>,
 }
 
 impl<'tcx> IsProbablyCyclical<'tcx> {
-    fn visit_all_fields(&mut self, adt_def: ty::AdtDef<'tcx>) -> ControlFlow<(), ()> {
-        for field in adt_def.all_fields() {
-            self.tcx.type_of(field.did).instantiate_identity().visit_with(self)?;
+    fn visit_def(&mut self, def_id: DefId) -> ControlFlow<(), ()> {
+        match self.tcx.def_kind(def_id) {
+            DefKind::Struct | DefKind::Enum | DefKind::Union => {
+                self.tcx.adt_def(def_id).all_fields().try_for_each(|field| {
+                    self.tcx.type_of(field.did).instantiate_identity().visit_with(self)
+                })
+            }
+            DefKind::TyAlias if self.tcx.type_alias_is_lazy(def_id) => {
+                self.tcx.type_of(def_id).instantiate_identity().visit_with(self)
+            }
+            _ => ControlFlow::Continue(()),
         }
-
-        ControlFlow::Continue(())
     }
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsProbablyCyclical<'tcx> {
     type Result = ControlFlow<(), ()>;
 
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<(), ()> {
-        if let Some(adt_def) = t.ty_adt_def() {
-            if adt_def.did() == self.adt_def_id {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<(), ()> {
+        let def_id = match ty.kind() {
+            ty::Adt(adt_def, _) => Some(adt_def.did()),
+            ty::Alias(ty::Weak, alias_ty) => Some(alias_ty.def_id),
+            _ => None,
+        };
+        if let Some(def_id) = def_id {
+            if def_id == self.item_def_id {
                 return ControlFlow::Break(());
             }
-
-            if self.seen.insert(adt_def.did()) {
-                self.visit_all_fields(adt_def)?;
+            if self.seen.insert(def_id) {
+                self.visit_def(def_id)?;
             }
         }
-
-        t.super_visit_with(self)
+        ty.super_visit_with(self)
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 03311fed396..2eca64c27d0 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1605,6 +1605,20 @@ pub(crate) enum UnusedGenericParameterHelp {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_analysis_unconstrained_generic_parameter)]
+pub(crate) struct UnconstrainedGenericParameter {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub param_name: Symbol,
+    pub param_def_kind: &'static str,
+    #[note(hir_analysis_const_param_note)]
+    pub const_param_note: Option<()>,
+    #[note(hir_analysis_const_param_note2)]
+    pub const_param_note2: Option<()>,
+}
+
+#[derive(Diagnostic)]
 pub enum UnnamedFieldsRepr<'a> {
     #[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)]
     MissingReprC {
@@ -1673,3 +1687,30 @@ pub struct InvalidReceiverTy<'tcx> {
 #[note]
 #[help]
 pub struct EffectsWithoutNextSolver;
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)]
+#[note]
+pub struct CmseCallInputsStackSpill {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub plural: bool,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)]
+#[note(hir_analysis_note1)]
+#[note(hir_analysis_note2)]
+pub struct CmseCallOutputStackSpill {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_cmse_call_generic, code = E0798)]
+pub struct CmseCallGeneric {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
new file mode 100644
index 00000000000..e99717ce00f
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
@@ -0,0 +1,156 @@
+use rustc_errors::DiagCtxtHandle;
+use rustc_hir as hir;
+use rustc_hir::HirId;
+use rustc_middle::ty::layout::LayoutError;
+use rustc_middle::ty::{self, ParamEnv, TyCtxt};
+use rustc_span::Span;
+use rustc_target::spec::abi;
+
+use crate::errors;
+
+/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be
+/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these
+/// conditions, but by checking them here rustc can emit nicer error messages.
+pub fn validate_cmse_abi<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    dcx: DiagCtxtHandle<'_>,
+    hir_id: HirId,
+    abi: abi::Abi,
+    fn_sig: ty::PolyFnSig<'tcx>,
+) {
+    if let abi::Abi::CCmseNonSecureCall = abi {
+        let hir_node = tcx.hir_node(hir_id);
+        let hir::Node::Ty(hir::Ty {
+            span: bare_fn_span,
+            kind: hir::TyKind::BareFn(bare_fn_ty),
+            ..
+        }) = hir_node
+        else {
+            // might happen when this ABI is used incorrectly. That will be handled elsewhere
+            return;
+        };
+
+        match is_valid_cmse_inputs(tcx, fn_sig) {
+            Ok(Ok(())) => {}
+            Ok(Err(index)) => {
+                // fn(x: u32, u32, u32, u16, y: u16) -> u32,
+                //                           ^^^^^^
+                let span = bare_fn_ty.param_names[index]
+                    .span
+                    .to(bare_fn_ty.decl.inputs[index].span)
+                    .to(bare_fn_ty.decl.inputs.last().unwrap().span);
+                let plural = bare_fn_ty.param_names.len() - index != 1;
+                dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural });
+            }
+            Err(layout_err) => {
+                if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
+                    dcx.emit_err(err);
+                }
+            }
+        }
+
+        match is_valid_cmse_output(tcx, fn_sig) {
+            Ok(true) => {}
+            Ok(false) => {
+                let span = bare_fn_ty.decl.output.span();
+                dcx.emit_err(errors::CmseCallOutputStackSpill { span });
+            }
+            Err(layout_err) => {
+                if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
+                    dcx.emit_err(err);
+                }
+            }
+        };
+    }
+}
+
+/// Returns whether the inputs will fit into the available registers
+fn is_valid_cmse_inputs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_sig: ty::PolyFnSig<'tcx>,
+) -> Result<Result<(), usize>, &'tcx LayoutError<'tcx>> {
+    let mut span = None;
+    let mut accum = 0u64;
+
+    for (index, arg_def) in fn_sig.inputs().iter().enumerate() {
+        let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?;
+
+        let align = layout.layout.align().abi.bytes();
+        let size = layout.layout.size().bytes();
+
+        accum += size;
+        accum = accum.next_multiple_of(Ord::max(4, align));
+
+        // i.e. exceeds 4 32-bit registers
+        if accum > 16 {
+            span = span.or(Some(index));
+        }
+    }
+
+    match span {
+        None => Ok(Ok(())),
+        Some(span) => Ok(Err(span)),
+    }
+}
+
+/// Returns whether the output will fit into the available registers
+fn is_valid_cmse_output<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_sig: ty::PolyFnSig<'tcx>,
+) -> Result<bool, &'tcx LayoutError<'tcx>> {
+    let mut ret_ty = fn_sig.output().skip_binder();
+    let layout = tcx.layout_of(ParamEnv::reveal_all().and(ret_ty))?;
+    let size = layout.layout.size().bytes();
+
+    if size <= 4 {
+        return Ok(true);
+    } else if size > 8 {
+        return Ok(false);
+    }
+
+    // next we need to peel any repr(transparent) layers off
+    'outer: loop {
+        let ty::Adt(adt_def, args) = ret_ty.kind() else {
+            break;
+        };
+
+        if !adt_def.repr().transparent() {
+            break;
+        }
+
+        // the first field with non-trivial size and alignment must be the data
+        for variant_def in adt_def.variants() {
+            for field_def in variant_def.fields.iter() {
+                let ty = field_def.ty(tcx, args);
+                let layout = tcx.layout_of(ParamEnv::reveal_all().and(ty))?;
+
+                if !layout.layout.is_1zst() {
+                    ret_ty = ty;
+                    continue 'outer;
+                }
+            }
+        }
+    }
+
+    Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
+}
+
+fn cmse_layout_err<'tcx>(
+    layout_err: &'tcx LayoutError<'tcx>,
+    span: Span,
+) -> Option<crate::errors::CmseCallGeneric> {
+    use LayoutError::*;
+
+    match layout_err {
+        Unknown(ty) => {
+            if ty.is_impl_trait() {
+                None // prevent double reporting of this error
+            } else {
+                Some(errors::CmseCallGeneric { span })
+            }
+        }
+        SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => {
+            None // not our job to report these
+        }
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 7d1ab232c71..d6eb1a66902 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -14,6 +14,7 @@
 //! trait references and bounds.
 
 mod bounds;
+mod cmse;
 pub mod errors;
 pub mod generics;
 mod lint;
@@ -2378,6 +2379,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi);
         let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
 
+        // reject function types that violate cmse ABI requirements
+        cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty);
+
         // Find any late-bound regions declared in return type that do
         // not appear in the arguments. These are not well-formed.
         //
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index 5cc1ec71757..f0fcbd5528f 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -8,15 +8,15 @@
 //! specialization errors. These things can (and probably should) be
 //! fixed, but for the moment it's easier to do these checks early.
 
-use crate::constrained_generic_params as cgp;
+use crate::{constrained_generic_params as cgp, errors::UnconstrainedGenericParameter};
 use min_specialization::check_min_specialization;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{codes::*, struct_span_code_err};
+use rustc_errors::codes::*;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
-use rustc_span::{ErrorGuaranteed, Span, Symbol};
+use rustc_span::ErrorGuaranteed;
 
 mod min_specialization;
 
@@ -117,43 +117,34 @@ fn enforce_impl_params_are_constrained(
 
     let mut res = Ok(());
     for param in &impl_generics.own_params {
-        match param.kind {
+        let err = match param.kind {
             // Disallow ANY unconstrained type parameters.
             ty::GenericParamDefKind::Type { .. } => {
                 let param_ty = ty::ParamTy::for_def(param);
-                if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
-                    res = Err(report_unused_parameter(
-                        tcx,
-                        tcx.def_span(param.def_id),
-                        "type",
-                        param_ty.name,
-                    ));
-                }
+                !input_parameters.contains(&cgp::Parameter::from(param_ty))
             }
             ty::GenericParamDefKind::Lifetime => {
                 let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
-                if lifetimes_in_associated_types.contains(&param_lt) && // (*)
+                lifetimes_in_associated_types.contains(&param_lt) && // (*)
                     !input_parameters.contains(&param_lt)
-                {
-                    res = Err(report_unused_parameter(
-                        tcx,
-                        tcx.def_span(param.def_id),
-                        "lifetime",
-                        param.name,
-                    ));
-                }
             }
             ty::GenericParamDefKind::Const { .. } => {
                 let param_ct = ty::ParamConst::for_def(param);
-                if !input_parameters.contains(&cgp::Parameter::from(param_ct)) {
-                    res = Err(report_unused_parameter(
-                        tcx,
-                        tcx.def_span(param.def_id),
-                        "const",
-                        param_ct.name,
-                    ));
-                }
+                !input_parameters.contains(&cgp::Parameter::from(param_ct))
             }
+        };
+        if err {
+            let const_param_note =
+                matches!(param.kind, ty::GenericParamDefKind::Const { .. }).then_some(());
+            let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
+                span: tcx.def_span(param.def_id),
+                param_name: param.name,
+                param_def_kind: tcx.def_descr(param.def_id),
+                const_param_note,
+                const_param_note2: const_param_note,
+            });
+            diag.code(E0207);
+            res = Err(diag.emit());
         }
     }
     res
@@ -177,30 +168,3 @@ fn enforce_impl_params_are_constrained(
     // associated types. I believe this is sound, because lifetimes
     // used elsewhere are not projected back out.
 }
-
-fn report_unused_parameter(
-    tcx: TyCtxt<'_>,
-    span: Span,
-    kind: &str,
-    name: Symbol,
-) -> ErrorGuaranteed {
-    let mut err = struct_span_code_err!(
-        tcx.dcx(),
-        span,
-        E0207,
-        "the {} parameter `{}` is not constrained by the \
-        impl trait, self type, or predicates",
-        kind,
-        name
-    );
-    err.span_label(span, format!("unconstrained {kind} parameter"));
-    if kind == "const" {
-        err.note(
-            "expressions using a const parameter must map each value to a distinct output value",
-        );
-        err.note(
-            "proving the result of expressions other than the parameter are unique is not supported",
-        );
-    }
-    err.emit()
-}
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index f0f2d1fefd2..9b05576d721 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -9,10 +9,10 @@ use rustc_hir::LangItem;
 use rustc_middle::bug;
 use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::{
-    self, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable,
-    TypeVisitableExt, UintTy,
+    self, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy, List, TraitRef, Ty,
+    TyCtxt, TypeFoldable, TypeVisitableExt, UintTy,
 };
-use rustc_span::sym;
+use rustc_span::{def_id::DefId, sym};
 use rustc_trait_selection::traits;
 use std::iter;
 use tracing::{debug, instrument};
@@ -360,41 +360,29 @@ pub fn transform_instance<'tcx>(
     if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
         // Perform type erasure for calls on trait objects by transforming self into a trait object
         // of the trait that defines the method.
-        if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
-            && let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
-        {
-            let impl_method = tcx.associated_item(instance.def_id());
-            let method_id = impl_method
-                .trait_item_def_id
-                .expect("Part of a trait implementation, but not linked to the def_id?");
-            let trait_method = tcx.associated_item(method_id);
-            let trait_id = trait_ref.skip_binder().def_id;
-            if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
-                && tcx.is_object_safe(trait_id)
-            {
-                // Trait methods will have a Self polymorphic parameter, where the concreteized
-                // implementatation will not. We need to walk back to the more general trait method
-                let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
-                    instance.args,
-                    ty::ParamEnv::reveal_all(),
-                    trait_ref,
-                );
-                let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
+        if let Some((trait_ref, method_id, ancestor)) = implemented_method(tcx, instance) {
+            // Trait methods will have a Self polymorphic parameter, where the concreteized
+            // implementatation will not. We need to walk back to the more general trait method
+            let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
+                instance.args,
+                ty::ParamEnv::reveal_all(),
+                trait_ref,
+            );
+            let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
 
-                // At the call site, any call to this concrete function through a vtable will be
-                // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
-                // original method id, and we've recovered the trait arguments, we can make the callee
-                // instance we're computing the alias set for match the caller instance.
-                //
-                // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
-                // If we ever *do* start encoding the vtable index, we will need to generate an alias set
-                // based on which vtables we are putting this method into, as there will be more than one
-                // index value when supertraits are involved.
-                instance.def = ty::InstanceKind::Virtual(method_id, 0);
-                let abstract_trait_args =
-                    tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
-                instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
-            }
+            // At the call site, any call to this concrete function through a vtable will be
+            // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
+            // original method id, and we've recovered the trait arguments, we can make the callee
+            // instance we're computing the alias set for match the caller instance.
+            //
+            // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
+            // If we ever *do* start encoding the vtable index, we will need to generate an alias set
+            // based on which vtables we are putting this method into, as there will be more than one
+            // index value when supertraits are involved.
+            instance.def = ty::InstanceKind::Virtual(method_id, 0);
+            let abstract_trait_args =
+                tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
+            instance.args = instance.args.rebase_onto(tcx, ancestor, abstract_trait_args);
         } else if tcx.is_closure_like(instance.def_id()) {
             // We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
             // instantiate it, and take the type of its only method as our own.
@@ -452,3 +440,36 @@ pub fn transform_instance<'tcx>(
 
     instance
 }
+
+fn implemented_method<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+) -> Option<(ty::EarlyBinder<'tcx, TraitRef<'tcx>>, DefId, DefId)> {
+    let trait_ref;
+    let method_id;
+    let trait_id;
+    let trait_method;
+    let ancestor = if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) {
+        // Implementation in an `impl` block
+        trait_ref = tcx.impl_trait_ref(impl_id)?;
+        let impl_method = tcx.associated_item(instance.def_id());
+        method_id = impl_method.trait_item_def_id?;
+        trait_method = tcx.associated_item(method_id);
+        trait_id = trait_ref.skip_binder().def_id;
+        impl_id
+    } else if let InstanceKind::Item(def_id) = instance.def
+        && let Some(trait_method_bound) = tcx.opt_associated_item(def_id)
+    {
+        // Provided method in a `trait` block
+        trait_method = trait_method_bound;
+        method_id = instance.def_id();
+        trait_id = tcx.trait_of_item(method_id)?;
+        trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args));
+        trait_id
+    } else {
+        return None;
+    };
+    let vtable_possible =
+        traits::is_vtable_safe_method(tcx, trait_id, trait_method) && tcx.is_object_safe(trait_id);
+    vtable_possible.then_some((trait_ref, method_id, ancestor))
+}
diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs
index 4ccb585862c..36fae1c1596 100644
--- a/library/core/src/fmt/builders.rs
+++ b/library/core/src/fmt/builders.rs
@@ -1026,7 +1026,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
 /// assert_eq!(format!("{}", value), "a");
 /// assert_eq!(format!("{:?}", value), "'a'");
 ///
-/// let wrapped = fmt::FormatterFn(|f| write!(f, "{:?}", &value));
+/// let wrapped = fmt::FormatterFn(|f| write!(f, "{value:?}"));
 /// assert_eq!(format!("{}", wrapped), "'a'");
 /// assert_eq!(format!("{:?}", wrapped), "'a'");
 /// ```
diff --git a/library/core/tests/future.rs b/library/core/tests/future.rs
index db417256dd0..93aca72d590 100644
--- a/library/core/tests/future.rs
+++ b/library/core/tests/future.rs
@@ -56,7 +56,7 @@ fn test_join() {
 
         let y = String::new();
         let x = join!(async {
-            println!("{}", &y);
+            println!("{y}");
             1
         })
         .await;
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 581d7e3efe3..57247359fbf 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -1544,10 +1544,10 @@ impl fmt::Debug for Literal {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("Literal")
             // format the kind on one line even in {:#?} mode
-            .field("kind", &format_args!("{:?}", &self.0.kind))
+            .field("kind", &format_args!("{:?}", self.0.kind))
             .field("symbol", &self.0.symbol)
             // format `Some("...")` on one line even in {:#?} mode
-            .field("suffix", &format_args!("{:?}", &self.0.suffix))
+            .field("suffix", &format_args!("{:?}", self.0.suffix))
             .field("span", &self.0.span)
             .finish()
     }
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 5ca631399aa..c1fc2e5488d 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -683,7 +683,7 @@ fn recursive_rmdir_toctou() {
     let drop_canary_arc = Arc::new(());
     let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
 
-    eprintln!("x: {:?}", &victim_del_path);
+    eprintln!("x: {victim_del_path:?}");
 
     // victim just continuously removes `victim_del`
     thread::spawn(move || {
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index f366cb8f42b..8de27367a3f 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -775,7 +775,7 @@ impl Error {
     ///
     /// impl Display for MyError {
     ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    ///         write!(f, "MyError: {}", &self.v)
+    ///         write!(f, "MyError: {}", self.v)
     ///     }
     /// }
     ///
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
new file mode 100644
index 00000000000..e6b0bf3e686
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
@@ -0,0 +1,21 @@
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
+#![no_core]
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+impl Copy for u32 {}
+
+#[repr(C)]
+struct Wrapper<T>(T);
+
+struct Test<T: Copy> {
+    f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64, //~ ERROR cannot find type `U` in this scope
+    //~^ ERROR function pointer types may not have generic parameters
+    f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
+    //~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters
+    f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798]
+    f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, //~ ERROR [E0798]
+}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
new file mode 100644
index 00000000000..fa68d95218c
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
@@ -0,0 +1,47 @@
+error: function pointer types may not have generic parameters
+  --> $DIR/generics.rs:15:42
+   |
+LL |     f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
+   |                                          ^^^^^^^^^
+
+error[E0412]: cannot find type `U` in this scope
+  --> $DIR/generics.rs:15:52
+   |
+LL | struct Test<T: Copy> {
+   |             - similarly named type parameter `T` defined here
+LL |     f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
+   |                                                    ^
+   |
+help: a type parameter with a similar name exists
+   |
+LL |     f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(T, u32, u32, u32) -> u64,
+   |                                                    ~
+help: you might be missing a type parameter
+   |
+LL | struct Test<T: Copy, U> {
+   |                    +++
+
+error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters
+  --> $DIR/generics.rs:17:43
+   |
+LL |     f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
+   |                                           ^^^^^^^^^
+   |
+   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
+
+error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
+  --> $DIR/generics.rs:19:9
+   |
+LL |     f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
+  --> $DIR/generics.rs:20:9
+   |
+LL |     f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0412, E0562, E0798.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs
deleted file mode 100644
index 364d0858afb..00000000000
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-//@ build-pass
-//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
-//@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
-#![no_core]
-#[lang="sized"]
-pub trait Sized { }
-#[lang="copy"]
-pub trait Copy { }
-impl Copy for u32 {}
-
-extern "rust-intrinsic" {
-    pub fn transmute<T, U>(e: T) -> U;
-}
-
-#[no_mangle]
-pub fn test(a: u32, b: u32, c: u32, d: u32) -> u32 {
-    let non_secure_function = unsafe {
-        transmute::<usize, extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u32>(
-            0x10000004,
-        )
-    };
-    non_secure_function(a, b, c, d)
-}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs
deleted file mode 100644
index c225a26c065..00000000000
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-//@ build-fail
-//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
-//@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
-#![no_core]
-#[lang="sized"]
-pub trait Sized { }
-#[lang="copy"]
-pub trait Copy { }
-impl Copy for u32 {}
-
-extern "rust-intrinsic" {
-    pub fn transmute<T, U>(e: T) -> U;
-}
-
-#[no_mangle]
-pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 {
-    let non_secure_function = unsafe {
-        transmute::<
-            usize,
-            extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32>
-        (
-            0x10000004,
-        )
-    };
-    non_secure_function(a, b, c, d, e)
-}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr
deleted file mode 100644
index a8aced2483e..00000000000
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr
+++ /dev/null
@@ -1,4 +0,0 @@
-error: <unknown>:0:0: in function test i32 (i32, i32, i32, i32, i32): call to non-secure function would require passing arguments on stack
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs
new file mode 100644
index 00000000000..083a563bd7b
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs
@@ -0,0 +1,23 @@
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
+#![no_core]
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+impl Copy for u32 {}
+
+#[repr(C, align(16))]
+#[allow(unused)]
+pub struct AlignRelevant(u32);
+
+#[no_mangle]
+pub fn test(
+    f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), //~ ERROR [E0798]
+    f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16),            //~ ERROR [E0798]
+    f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32),                      //~ ERROR [E0798]
+    f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32),                 //~ ERROR [E0798]
+    f5: extern "C-cmse-nonsecure-call" fn([u32; 5]),                           //~ ERROR [E0798]
+) {
+}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr
new file mode 100644
index 00000000000..a5f5e1c3151
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr
@@ -0,0 +1,43 @@
+error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:17:63
+   |
+LL |     f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32),
+   |                                                               ^^^^^^^^^^^^^^ these arguments don't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:18:63
+   |
+LL |     f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16),
+   |                                                               ^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:19:53
+   |
+LL |     f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32),
+   |                                                     ^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:20:58
+   |
+LL |     f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32),
+   |                                                          ^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:21:43
+   |
+LL |     f5: extern "C-cmse-nonsecure-call" fn([u32; 5]),
+   |                                           ^^^^^^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0798`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs
new file mode 100644
index 00000000000..e6af1d60e77
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs
@@ -0,0 +1,54 @@
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
+#![no_core]
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+impl Copy for u32 {}
+
+#[repr(C)]
+pub struct ReprCU64(u64);
+
+#[repr(C)]
+pub struct ReprCBytes(u8, u8, u8, u8, u8);
+
+#[repr(C)]
+pub struct U64Compound(u32, u32);
+
+#[repr(C, align(16))]
+pub struct ReprCAlign16(u16);
+
+#[no_mangle]
+pub fn test(
+    f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, //~ ERROR [E0798]
+    f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, //~ ERROR [E0798]
+    f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, //~ ERROR [E0798]
+    f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, //~ ERROR [E0798]
+    f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5],  //~ ERROR [E0798]
+) {
+}
+
+#[allow(improper_ctypes_definitions)]
+struct Test {
+    u128: extern "C-cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798]
+    i128: extern "C-cmse-nonsecure-call" fn() -> i128, //~ ERROR [E0798]
+}
+
+#[repr(C)]
+pub union ReprCUnionU64 {
+    _unused: u64,
+}
+
+#[repr(Rust)]
+pub union ReprRustUnionU64 {
+    _unused: u64,
+}
+
+#[no_mangle]
+pub fn test_union(
+    f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ ERROR [E0798]
+    f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64,    //~ ERROR [E0798]
+) {
+}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr
new file mode 100644
index 00000000000..89f7f159d6e
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr
@@ -0,0 +1,84 @@
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:35:50
+   |
+LL |     u128: extern "C-cmse-nonsecure-call" fn() -> u128,
+   |                                                  ^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:36:50
+   |
+LL |     i128: extern "C-cmse-nonsecure-call" fn() -> i128,
+   |                                                  ^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:25:48
+   |
+LL |     f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64,
+   |                                                ^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:26:48
+   |
+LL |     f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes,
+   |                                                ^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:27:48
+   |
+LL |     f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound,
+   |                                                ^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:28:48
+   |
+LL |     f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16,
+   |                                                ^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:29:48
+   |
+LL |     f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5],
+   |                                                ^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:51:48
+   |
+LL |     f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64,
+   |                                                ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:52:48
+   |
+LL |     f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64,
+   |                                                ^^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0798`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs
new file mode 100644
index 00000000000..9fda55c2a48
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs
@@ -0,0 +1,53 @@
+//@ build-pass
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
+#![no_core]
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+impl Copy for u32 {}
+
+#[repr(transparent)]
+pub struct ReprTransparentStruct<T> {
+    _marker1: (),
+    _marker2: (),
+    field: T,
+    _marker3: (),
+}
+
+#[repr(transparent)]
+pub enum ReprTransparentEnumU64 {
+    A(u64),
+}
+
+#[repr(C)]
+pub struct U32Compound(u16, u16);
+
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub fn params(
+    f1: extern "C-cmse-nonsecure-call" fn(),
+    f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32),
+    f3: extern "C-cmse-nonsecure-call" fn(u64, u64),
+    f4: extern "C-cmse-nonsecure-call" fn(u128),
+    f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32),
+    f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStruct<u64>, U32Compound),
+    f7: extern "C-cmse-nonsecure-call" fn([u32; 4]),
+) {
+}
+
+#[no_mangle]
+pub fn returns(
+    f1: extern "C-cmse-nonsecure-call" fn() -> u32,
+    f2: extern "C-cmse-nonsecure-call" fn() -> u64,
+    f3: extern "C-cmse-nonsecure-call" fn() -> i64,
+    f4: extern "C-cmse-nonsecure-call" fn() -> f64,
+    f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4],
+    f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct<u64>,
+    f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct<ReprTransparentStruct<u64>>,
+    f8: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64,
+    f9: extern "C-cmse-nonsecure-call" fn() -> U32Compound,
+) {
+}
diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
index 48e7da96612..192b5eebdaa 100644
--- a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
+++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
@@ -4,27 +4,27 @@ error[E0275]: overflow evaluating the requirement `Loop == _`
 LL | impl Loop {}
    |      ^^^^
 
-error[E0392]: type parameter `T` is never used
-  --> $DIR/inherent-impls-overflow.rs:14:12
+error: type parameter `T` is only used recursively
+  --> $DIR/inherent-impls-overflow.rs:14:24
    |
 LL | type Poly0<T> = Poly1<(T,)>;
-   |            ^           - `T` is named here, but is likely unused in the containing type
+   |            -           ^
    |            |
-   |            unused type parameter
+   |            type parameter must be used non-recursively in the definition
    |
    = help: consider removing `T` or referring to it in the body of the type alias
-   = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
+   = note: all type parameters must be used in a non-recursive way in order to constrain their variance
 
-error[E0392]: type parameter `T` is never used
-  --> $DIR/inherent-impls-overflow.rs:17:12
+error: type parameter `T` is only used recursively
+  --> $DIR/inherent-impls-overflow.rs:17:24
    |
 LL | type Poly1<T> = Poly0<(T,)>;
-   |            ^           - `T` is named here, but is likely unused in the containing type
+   |            -           ^
    |            |
-   |            unused type parameter
+   |            type parameter must be used non-recursively in the definition
    |
    = help: consider removing `T` or referring to it in the body of the type alias
-   = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
+   = note: all type parameters must be used in a non-recursive way in order to constrain their variance
 
 error[E0275]: overflow evaluating the requirement `Poly0<()> == _`
   --> $DIR/inherent-impls-overflow.rs:21:6
@@ -36,5 +36,4 @@ LL | impl Poly0<()> {}
 
 error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0275, E0392.
-For more information about an error, try `rustc --explain E0275`.
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs
index 98f0d811a47..1397695a3fe 100644
--- a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs
+++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs
@@ -13,10 +13,10 @@ impl Loop {}
 
 type Poly0<T> = Poly1<(T,)>;
 //[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
-//[next]~^^ ERROR type parameter `T` is never used
+//[next]~^^ ERROR type parameter `T` is only used recursively
 type Poly1<T> = Poly0<(T,)>;
 //[current]~^ ERROR  overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
-//[next]~^^ ERROR type parameter `T` is never used
+//[next]~^^ ERROR type parameter `T` is only used recursively
 
 impl Poly0<()> {}
 //[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
diff --git a/tests/ui/sanitizer/cfi-supertraits.rs b/tests/ui/sanitizer/cfi-supertraits.rs
index ed3d722ebb7..4bb6177577f 100644
--- a/tests/ui/sanitizer/cfi-supertraits.rs
+++ b/tests/ui/sanitizer/cfi-supertraits.rs
@@ -16,6 +16,9 @@
 trait Parent1 {
     type P1;
     fn p1(&self) -> Self::P1;
+    fn d(&self) -> i32 {
+        42
+    }
 }
 
 trait Parent2 {
@@ -60,14 +63,17 @@ fn main() {
     x.c();
     x.p1();
     x.p2();
+    x.d();
     // Parents can be created and access their methods.
     let y = &Foo as &dyn Parent1<P1=u16>;
     y.p1();
+    y.d();
     let z = &Foo as &dyn Parent2<P2=u32>;
     z.p2();
     // Trait upcasting works
     let x1 = x as &dyn Parent1<P1=u16>;
     x1.p1();
+    x1.d();
     let x2 = x as &dyn Parent2<P2=u32>;
     x2.p2();
 }