about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-09-16 04:26:18 +0000
committerbors <bors@rust-lang.org>2025-09-16 04:26:18 +0000
commit8a1b39995e5b630c5872f5de5079f1f569bd5ac2 (patch)
treee704a8ae8564ea0a17d60d7033bede8aeb7ded3f /compiler
parent9d82de19dfae60e55c291f5f28e28cfc2c1b9630 (diff)
parentf0e8c2fb6f4cb0bca4f554edf895e2344ef9c74e (diff)
downloadrust-8a1b39995e5b630c5872f5de5079f1f569bd5ac2.tar.gz
rust-8a1b39995e5b630c5872f5de5079f1f569bd5ac2.zip
Auto merge of #146614 - Zalathar:rollup-hcxvdi1, r=Zalathar
Rollup of 9 pull requests

Successful merges:

 - rust-lang/rust#145095 (Migrate `UnsizedConstParamTy`  to unstable impl of `ConstParamTy_`)
 - rust-lang/rust#145960 (Split `FnCtxt::report_args_error` into subfunctions)
 - rust-lang/rust#146402 (interpret: fix overlapping aggregate initialization)
 - rust-lang/rust#146466 (llvm-wrapper: other cleanup)
 - rust-lang/rust#146574 (compiletest: Enable new-output-capture by default)
 - rust-lang/rust#146599 (replace some `#[const_trait]` with `const trait`)
 - rust-lang/rust#146601 (compiletest: Make `./x test --test-args ...` work again)
 - rust-lang/rust#146608 (improve internal bootstrap docs)
 - rust-lang/rust#146609 (bootstrap: lower verbosity of cargo to one less than bootstrap's)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/bounds.rs39
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs11
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs16
-rw-r--r--compiler/rustc_hir/src/lang_items.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs2400
-rw-r--r--compiler/rustc_infer/src/traits/engine.rs17
-rw-r--r--compiler/rustc_llvm/build.rs1
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp124
-rw-r--r--compiler/rustc_llvm/src/lib.rs6
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs38
15 files changed, 1450 insertions, 1242 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
index dd8f0e46a0e..63342880b09 100644
--- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
@@ -51,43 +51,4 @@ pub(crate) fn expand_deriving_const_param_ty(
     };
 
     trait_def.expand(cx, mitem, item, push);
-
-    let trait_def = TraitDef {
-        span,
-        path: path_std!(marker::UnsizedConstParamTy),
-        skip_path_as_bound: false,
-        needs_copy_as_bound_if_packed: false,
-        additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))],
-        supports_unions: false,
-        methods: Vec::new(),
-        associated_types: Vec::new(),
-        is_const,
-        is_staged_api_crate: cx.ecfg.features.staged_api(),
-    };
-
-    trait_def.expand(cx, mitem, item, push);
-}
-
-pub(crate) fn expand_deriving_unsized_const_param_ty(
-    cx: &ExtCtxt<'_>,
-    span: Span,
-    mitem: &MetaItem,
-    item: &Annotatable,
-    push: &mut dyn FnMut(Annotatable),
-    is_const: bool,
-) {
-    let trait_def = TraitDef {
-        span,
-        path: path_std!(marker::UnsizedConstParamTy),
-        skip_path_as_bound: false,
-        needs_copy_as_bound_if_packed: false,
-        additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))],
-        supports_unions: false,
-        methods: Vec::new(),
-        associated_types: Vec::new(),
-        is_const,
-        is_staged_api_crate: cx.ecfg.features.staged_api(),
-    };
-
-    trait_def.expand(cx, mitem, item, push);
 }
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 1bcea95fbb7..4541e2cd3b4 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -129,7 +129,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         Clone: clone::expand_deriving_clone,
         Copy: bounds::expand_deriving_copy,
         ConstParamTy: bounds::expand_deriving_const_param_ty,
-        UnsizedConstParamTy: bounds::expand_deriving_unsized_const_param_ty,
         Debug: debug::expand_deriving_debug,
         Default: default::expand_deriving_default,
         Eq: eq::expand_deriving_eq,
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index f6f2e3f2a3a..f8755874014 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -1,5 +1,5 @@
 use itertools::Itertools as _;
-use rustc_abi::{self as abi, FIRST_VARIANT};
+use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT};
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
@@ -25,6 +25,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         match *rvalue {
             mir::Rvalue::Use(ref operand) => {
                 let cg_operand = self.codegen_operand(bx, operand);
+                // Crucially, we do *not* use `OperandValue::Ref` for types with
+                // `BackendRepr::Scalar | BackendRepr::ScalarPair`. This ensures we match the MIR
+                // semantics regarding when assignment operators allow overlap of LHS and RHS.
+                if matches!(
+                    cg_operand.layout.backend_repr,
+                    BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..),
+                ) {
+                    debug_assert!(!matches!(cg_operand.val, OperandValue::Ref(..)));
+                }
                 // FIXME: consider not copying constants through stack. (Fixable by codegen'ing
                 // constants into `OperandValue::Ref`; why don’t we do that yet if we don’t?)
                 cg_operand.val.store(bx, dest);
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index a86fdf80f60..cd34892f029 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -858,7 +858,7 @@ where
     /// Also, if you use this you are responsible for validating that things get copied at the
     /// right type.
     #[instrument(skip(self), level = "trace")]
-    fn copy_op_no_validate(
+    pub(super) fn copy_op_no_validate(
         &mut self,
         src: &impl Projectable<'tcx, M::Provenance>,
         dest: &impl Writeable<'tcx, M::Provenance>,
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 23d362de308..46950d60f8c 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -310,7 +310,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         operands: &IndexSlice<FieldIdx, mir::Operand<'tcx>>,
         dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
-        self.write_uninit(dest)?; // make sure all the padding ends up as uninit
         let (variant_index, variant_dest, active_field_index) = match *kind {
             mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
                 let variant_dest = self.project_downcast(dest, variant_index)?;
@@ -346,9 +345,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             let field_index = active_field_index.unwrap_or(field_index);
             let field_dest = self.project_field(&variant_dest, field_index)?;
             let op = self.eval_operand(operand, Some(field_dest.layout))?;
-            self.copy_op(&op, &field_dest)?;
+            // We validate manually below so we don't have to do it here.
+            self.copy_op_no_validate(&op, &field_dest, /*allow_transmute*/ false)?;
         }
-        self.write_discriminant(variant_index, dest)
+        self.write_discriminant(variant_index, dest)?;
+        // Validate that the entire thing is valid, and reset padding that might be in between the
+        // fields.
+        if M::enforce_validity(self, dest.layout()) {
+            self.validate_operand(
+                dest,
+                M::enforce_validity_recursively(self, dest.layout()),
+                /*reset_provenance_and_padding*/ true,
+            )?;
+        }
+        interp_ok(())
     }
 
     /// Repeats `operand` into the destination. `dest` must have array type, and that type
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 67d2f15d414..2e099a97b65 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -370,7 +370,6 @@ language_item_table! {
     CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait,     GenericRequirement::Exact(0);
 
     ConstParamTy,            sym::const_param_ty,      const_param_ty_trait,       Target::Trait,          GenericRequirement::Exact(0);
-    UnsizedConstParamTy,     sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
 
     Poll,                    sym::Poll,                poll,                       Target::Enum,           GenericRequirement::None;
     PollReady,               sym::Ready,               poll_ready_variant,         Target::Variant,        GenericRequirement::None;
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index d33f1f3e12a..0a555c7f6e9 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -819,17 +819,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er
             let span = tcx.def_span(param.def_id);
             let def_id = param.def_id.expect_local();
 
-            if tcx.features().unsized_const_params() {
-                enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| {
-                    wfcx.register_bound(
-                        ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)),
-                        wfcx.param_env,
-                        ty,
-                        tcx.require_lang_item(LangItem::UnsizedConstParamTy, span),
-                    );
-                    Ok(())
-                })
-            } else if tcx.features().adt_const_params() {
+            if tcx.features().adt_const_params() {
                 enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| {
                     wfcx.register_bound(
                         ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)),
@@ -880,7 +870,6 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er
                     tcx,
                     tcx.param_env(param.def_id),
                     ty,
-                    LangItem::ConstParamTy,
                     cause,
                 ) {
                     // Can never implement `ConstParamTy`, don't suggest anything.
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 32b175611ce..0b9a01d6042 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -1,7 +1,6 @@
 //! Check properties that are required by built-in traits and set
 //! up data structures required by type-checking/codegen.
 
-use std::assert_matches::assert_matches;
 use std::collections::BTreeMap;
 
 use rustc_data_structures::fx::FxHashSet;
@@ -40,10 +39,7 @@ pub(super) fn check_trait<'tcx>(
     checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?;
     checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?;
     checker.check(lang_items.const_param_ty_trait(), |checker| {
-        visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy)
-    })?;
-    checker.check(lang_items.unsized_const_param_ty_trait(), |checker| {
-        visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy)
+        visit_implementation_of_const_param_ty(checker)
     })?;
     checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?;
     checker
@@ -138,12 +134,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
     }
 }
 
-fn visit_implementation_of_const_param_ty(
-    checker: &Checker<'_>,
-    kind: LangItem,
-) -> Result<(), ErrorGuaranteed> {
-    assert_matches!(kind, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy);
-
+fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
     let tcx = checker.tcx;
     let header = checker.impl_header;
     let impl_did = checker.impl_def_id;
@@ -157,7 +148,7 @@ fn visit_implementation_of_const_param_ty(
     }
 
     let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
-    match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, kind, cause) {
+    match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) {
         Ok(()) => Ok(()),
         Err(ConstParamTyImplementationError::InfrigingFields(fields)) => {
             let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 44a6084ebd5..7ca8580e098 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1,3 +1,4 @@
+use std::ops::Deref;
 use std::{fmt, iter, mem};
 
 use itertools::Itertools;
@@ -7,7 +8,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, lis
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath};
+use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
 use rustc_hir_analysis::check::potentially_plural_count;
 use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants};
 use rustc_index::IndexVec;
@@ -565,358 +566,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         tuple_arguments: TupleArgumentsFlag,
     ) -> ErrorGuaranteed {
         // Next, let's construct the error
-        let (error_span, call_ident, full_call_span, call_name, is_method) = match &call_expr.kind {
-            hir::ExprKind::Call(
-                hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
-                _,
-            ) => {
-                if let Res::Def(DefKind::Ctor(of, _), _) =
-                    self.typeck_results.borrow().qpath_res(qpath, *hir_id)
-                {
-                    let name = match of {
-                        CtorOf::Struct => "struct",
-                        CtorOf::Variant => "enum variant",
-                    };
-                    (call_span, None, *span, name, false)
-                } else {
-                    (call_span, None, *span, "function", false)
-                }
-            }
-            hir::ExprKind::Call(hir::Expr { span, .. }, _) => {
-                (call_span, None, *span, "function", false)
-            }
-            hir::ExprKind::MethodCall(path_segment, _, _, span) => {
-                let ident_span = path_segment.ident.span;
-                let ident_span = if let Some(args) = path_segment.args {
-                    ident_span.with_hi(args.span_ext.hi())
-                } else {
-                    ident_span
-                };
-                (*span, Some(path_segment.ident), ident_span, "method", true)
-            }
-            k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
-        };
-        let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span);
-
-        // Don't print if it has error types or is just plain `_`
-        fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
-            tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var())
-        }
-
-        let tcx = self.tcx;
-
-        // Get the argument span in the context of the call span so that
-        // suggestions and labels are (more) correct when an arg is a
-        // macro invocation.
-        let normalize_span = |span: Span| -> Span {
-            let normalized_span = span.find_ancestor_inside_same_ctxt(error_span).unwrap_or(span);
-            // Sometimes macros mess up the spans, so do not normalize the
-            // arg span to equal the error span, because that's less useful
-            // than pointing out the arg expr in the wrong context.
-            if normalized_span.source_equal(error_span) { span } else { normalized_span }
-        };
-
-        // Precompute the provided types and spans, since that's all we typically need for below
-        let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
-            .iter()
-            .map(|expr| {
-                let ty = self
-                    .typeck_results
-                    .borrow()
-                    .expr_ty_adjusted_opt(*expr)
-                    .unwrap_or_else(|| Ty::new_misc_error(tcx));
-                (self.resolve_vars_if_possible(ty), normalize_span(expr.span))
-            })
-            .collect();
-        let callee_expr = match &call_expr.peel_blocks().kind {
-            hir::ExprKind::Call(callee, _) => Some(*callee),
-            hir::ExprKind::MethodCall(_, receiver, ..) => {
-                if let Some((DefKind::AssocFn, def_id)) =
-                    self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
-                    && let Some(assoc) = tcx.opt_associated_item(def_id)
-                    && assoc.is_method()
-                {
-                    Some(*receiver)
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        };
-        let callee_ty = callee_expr
-            .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
-
-        // Obtain another method on `Self` that have similar name.
-        let similar_assoc = |call_name: Ident| -> Option<(ty::AssocItem, ty::FnSig<'_>)> {
-            if let Some(callee_ty) = callee_ty
-                && let Ok(Some(assoc)) = self.probe_op(
-                    call_name.span,
-                    MethodCall,
-                    Some(call_name),
-                    None,
-                    IsSuggestion(true),
-                    callee_ty.peel_refs(),
-                    callee_expr.unwrap().hir_id,
-                    TraitsInScope,
-                    |mut ctxt| ctxt.probe_for_similar_candidate(),
-                )
-                && assoc.is_method()
-            {
-                let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id);
-                let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args);
-
-                self.instantiate_binder_with_fresh_vars(
-                    call_name.span,
-                    BoundRegionConversionTime::FnCall,
-                    fn_sig,
-                );
-            }
-            None
-        };
-
-        let suggest_confusable = |err: &mut Diag<'_>| {
-            let Some(call_name) = call_ident else {
-                return;
-            };
-            let Some(callee_ty) = callee_ty else {
-                return;
-            };
-            let input_types: Vec<Ty<'_>> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect();
-            // Check for other methods in the following order
-            //  - methods marked as `rustc_confusables` with the provided arguments
-            //  - methods with the same argument type/count and short levenshtein distance
-            //  - methods marked as `rustc_confusables` (done)
-            //  - methods with short levenshtein distance
-
-            // Look for commonly confusable method names considering arguments.
-            if let Some(_name) = self.confusable_method_name(
-                err,
-                callee_ty.peel_refs(),
-                call_name,
-                Some(input_types.clone()),
-            ) {
-                return;
-            }
-            // Look for method names with short levenshtein distance, considering arguments.
-            if let Some((assoc, fn_sig)) = similar_assoc(call_name)
-                && fn_sig.inputs()[1..]
-                    .iter()
-                    .zip(input_types.iter())
-                    .all(|(expected, found)| self.may_coerce(*expected, *found))
-                && fn_sig.inputs()[1..].len() == input_types.len()
-            {
-                let assoc_name = assoc.name();
-                err.span_suggestion_verbose(
-                    call_name.span,
-                    format!("you might have meant to use `{}`", assoc_name),
-                    assoc_name,
-                    Applicability::MaybeIncorrect,
-                );
-                return;
-            }
-            // Look for commonly confusable method names disregarding arguments.
-            if let Some(_name) =
-                self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None)
-            {
-                return;
-            }
-            // Look for similarly named methods with levenshtein distance with the right
-            // number of arguments.
-            if let Some((assoc, fn_sig)) = similar_assoc(call_name)
-                && fn_sig.inputs()[1..].len() == input_types.len()
-            {
-                err.span_note(
-                    tcx.def_span(assoc.def_id),
-                    format!(
-                        "there's is a method with similar name `{}`, but the arguments don't match",
-                        assoc.name(),
-                    ),
-                );
-                return;
-            }
-            // Fallthrough: look for similarly named methods with levenshtein distance.
-            if let Some((assoc, _)) = similar_assoc(call_name) {
-                err.span_note(
-                    tcx.def_span(assoc.def_id),
-                    format!(
-                        "there's is a method with similar name `{}`, but their argument count \
-                         doesn't match",
-                        assoc.name(),
-                    ),
-                );
-                return;
-            }
-        };
-        // A "softer" version of the `demand_compatible`, which checks types without persisting them,
-        // and treats error types differently
-        // This will allow us to "probe" for other argument orders that would likely have been correct
-        let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| {
-            if provided_idx.as_usize() == expected_idx.as_usize() {
-                return compatibility_diagonal[provided_idx].clone();
-            }
-
-            let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx];
-            // If either is an error type, we defy the usual convention and consider them to *not* be
-            // coercible. This prevents our error message heuristic from trying to pass errors into
-            // every argument.
-            if (formal_input_ty, expected_input_ty).references_error() {
-                return Compatibility::Incompatible(None);
-            }
 
-            let (arg_ty, arg_span) = provided_arg_tys[provided_idx];
-
-            let expectation = Expectation::rvalue_hint(self, expected_input_ty);
-            let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
-            let can_coerce = self.may_coerce(arg_ty, coerced_ty);
-            if !can_coerce {
-                return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts(
-                    ty::error::ExpectedFound::new(coerced_ty, arg_ty),
-                )));
-            }
-
-            // Using probe here, since we don't want this subtyping to affect inference.
-            let subtyping_error = self.probe(|_| {
-                self.at(&self.misc(arg_span), self.param_env)
-                    .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty)
-                    .err()
-            });
-
-            // Same as above: if either the coerce type or the checked type is an error type,
-            // consider them *not* compatible.
-            let references_error = (coerced_ty, arg_ty).references_error();
-            match (references_error, subtyping_error) {
-                (false, None) => Compatibility::Compatible,
-                (_, subtyping_error) => Compatibility::Incompatible(subtyping_error),
-            }
-        };
-
-        let mk_trace = |span, (formal_ty, expected_ty), provided_ty| {
-            let mismatched_ty = if expected_ty == provided_ty {
-                // If expected == provided, then we must have failed to sup
-                // the formal type. Avoid printing out "expected Ty, found Ty"
-                // in that case.
-                formal_ty
-            } else {
-                expected_ty
-            };
-            TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty)
-        };
-
-        // The algorithm here is inspired by levenshtein distance and longest common subsequence.
-        // We'll try to detect 4 different types of mistakes:
-        // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs
-        // - An input is missing, which isn't satisfied by *any* of the other arguments
-        // - Some number of arguments have been provided in the wrong order
-        // - A type is straight up invalid
-
-        // First, let's find the errors
-        let (mut errors, matched_inputs) =
-            ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible)
-                .find_errors();
+        let mut fn_call_diag_ctxt = FnCallDiagCtxt::new(
+            self,
+            compatibility_diagonal,
+            formal_and_expected_inputs,
+            provided_args,
+            c_variadic,
+            err_code,
+            fn_def_id,
+            call_span,
+            call_expr,
+            tuple_arguments,
+        );
 
         // First, check if we just need to wrap some arguments in a tuple.
-        if let Some((mismatch_idx, terr)) =
-            compatibility_diagonal.iter_enumerated().find_map(|(i, c)| {
-                if let Compatibility::Incompatible(Some(terr)) = c {
-                    Some((i, *terr))
-                } else {
-                    None
-                }
-            })
-        {
-            // Is the first bad expected argument a tuple?
-            // Do we have as many extra provided arguments as the tuple's length?
-            // If so, we might have just forgotten to wrap some args in a tuple.
-            if let Some(ty::Tuple(tys)) =
-                formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind())
-                // If the tuple is unit, we're not actually wrapping any arguments.
-                && !tys.is_empty()
-                && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
-            {
-                // Wrap up the N provided arguments starting at this position in a tuple.
-                let provided_args_to_tuple = &provided_arg_tys[mismatch_idx..];
-                let (provided_args_to_tuple, provided_args_after_tuple) =
-                    provided_args_to_tuple.split_at(tys.len());
-                let provided_as_tuple =
-                    Ty::new_tup_from_iter(tcx, provided_args_to_tuple.iter().map(|&(ty, _)| ty));
-
-                let mut satisfied = true;
-                // Check if the newly wrapped tuple + rest of the arguments are compatible.
-                for ((_, expected_ty), provided_ty) in std::iter::zip(
-                    formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(),
-                    [provided_as_tuple]
-                        .into_iter()
-                        .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)),
-                ) {
-                    if !self.may_coerce(provided_ty, *expected_ty) {
-                        satisfied = false;
-                        break;
-                    }
-                }
+        if let Some(err) = fn_call_diag_ctxt.check_wrap_args_in_tuple() {
+            return err;
+        }
 
-                // If they're compatible, suggest wrapping in an arg, and we're done!
-                // Take some care with spans, so we don't suggest wrapping a macro's
-                // innards in parenthesis, for example.
-                if satisfied
-                    && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple
-                {
-                    let mut err;
-                    if tys.len() == 1 {
-                        // A tuple wrap suggestion actually occurs within,
-                        // so don't do anything special here.
-                        err = self.err_ctxt().report_and_explain_type_error(
-                            mk_trace(
-                                lo,
-                                formal_and_expected_inputs[mismatch_idx.to_expected_idx()],
-                                provided_arg_tys[mismatch_idx].0,
-                            ),
-                            self.param_env,
-                            terr,
-                        );
-                        err.span_label(
-                            full_call_span,
-                            format!("arguments to this {call_name} are incorrect"),
-                        );
-                    } else {
-                        err = self.dcx().struct_span_err(
-                            full_call_span,
-                            format!(
-                                "{call_name} takes {}{} but {} {} supplied",
-                                if c_variadic { "at least " } else { "" },
-                                potentially_plural_count(
-                                    formal_and_expected_inputs.len(),
-                                    "argument"
-                                ),
-                                potentially_plural_count(provided_args.len(), "argument"),
-                                pluralize!("was", provided_args.len())
-                            ),
-                        );
-                        err.code(err_code.to_owned());
-                        err.multipart_suggestion_verbose(
-                            "wrap these arguments in parentheses to construct a tuple",
-                            vec![
-                                (lo.shrink_to_lo(), "(".to_string()),
-                                (hi.shrink_to_hi(), ")".to_string()),
-                            ],
-                            Applicability::MachineApplicable,
-                        );
-                    };
-                    self.label_fn_like(
-                        &mut err,
-                        fn_def_id,
-                        callee_ty,
-                        call_expr,
-                        None,
-                        Some(mismatch_idx.as_usize()),
-                        &matched_inputs,
-                        &formal_and_expected_inputs,
-                        is_method,
-                        tuple_arguments,
-                    );
-                    suggest_confusable(&mut err);
-                    return err.emit();
-                }
-            }
+        if let Some(fallback_error) = fn_call_diag_ctxt.ensure_has_errors() {
+            return fallback_error;
         }
 
         // Okay, so here's where it gets complicated in regards to what errors
@@ -926,608 +596,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         //   2) Valid but incorrect arguments
         //   3) Invalid arguments
         //      - Currently I think this only comes up with `CyclicTy`
-        //
+
         // We first need to go through, remove those from (3) and emit those
         // as their own error, particularly since they're error code and
         // message is special. From what I can tell, we *must* emit these
         // here (vs somewhere prior to this function) since the arguments
         // become invalid *because* of how they get used in the function.
         // It is what it is.
-
-        if errors.is_empty() {
-            if cfg!(debug_assertions) {
-                span_bug!(error_span, "expected errors from argument matrix");
-            } else {
-                let mut err =
-                    self.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span });
-                suggest_confusable(&mut err);
-                return err.emit();
-            }
+        if let Some(err) = fn_call_diag_ctxt.filter_out_invalid_arguments()
+            && fn_call_diag_ctxt.errors.is_empty()
+        {
+            // We're done if we found errors, but we already emitted them.
+            return err;
         }
 
-        let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| {
-            if let ty::Adt(adt, _) = ty.kind()
-                && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull)
-                && let hir::ExprKind::Struct(
-                    hir::QPath::LangItem(hir::LangItem::RangeFull, _),
-                    [],
-                    _,
-                ) = expr.kind
-            {
-                // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax
-                // from default field values, which is not supported on tuples.
-                let explanation = if self.tcx.features().default_field_values() {
-                    "this is only supported on non-tuple struct literals"
-                } else if self.tcx.sess.is_nightly_build() {
-                    "this is only supported on non-tuple struct literals when \
-                     `#![feature(default_field_values)]` is enabled"
-                } else {
-                    "this is not supported"
-                };
-                let msg = format!(
-                    "you might have meant to use `..` to skip providing a value for \
-                     expected fields, but {explanation}; it is instead interpreted as a \
-                     `std::ops::RangeFull` literal",
-                );
-                err.span_help(expr.span, msg);
-            }
-        };
+        assert!(!fn_call_diag_ctxt.errors.is_empty());
 
-        let mut reported = None;
-        errors.retain(|error| {
-            let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) =
-                error
-            else {
-                return true;
-            };
-            let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
-            let trace =
-                mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
-            if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
-                let mut err =
-                    self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *e);
-                suggest_confusable(&mut err);
-                reported = Some(err.emit());
-                return false;
-            }
-            true
-        });
-
-        // We're done if we found errors, but we already emitted them.
-        if let Some(reported) = reported
-            && errors.is_empty()
-        {
-            return reported;
+        // Last special case: if there is only one "Incompatible" error, just emit that
+        if let Some(err) = fn_call_diag_ctxt.check_single_incompatible() {
+            return err;
         }
-        assert!(!errors.is_empty());
 
         // Okay, now that we've emitted the special errors separately, we
         // are only left missing/extra/swapped and mismatched arguments, both
         // can be collated pretty easily if needed.
 
-        // Next special case: if there is only one "Incompatible" error, just emit that
-        if let &[
-            Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))),
-        ] = &errors[..]
-        {
-            let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
-            let (provided_ty, provided_arg_span) = provided_arg_tys[provided_idx];
-            let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty);
-            let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err);
-            self.emit_coerce_suggestions(
-                &mut err,
-                provided_args[provided_idx],
-                provided_ty,
-                Expectation::rvalue_hint(self, expected_ty)
-                    .only_has_type(self)
-                    .unwrap_or(formal_ty),
-                None,
-                None,
-            );
-            err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect"));
-
-            self.label_generic_mismatches(
-                &mut err,
-                fn_def_id,
-                &matched_inputs,
-                &provided_arg_tys,
-                &formal_and_expected_inputs,
-                is_method,
-            );
-
-            if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind
-                && provided_idx.as_usize() == expected_idx.as_usize()
-            {
-                self.note_source_of_type_mismatch_constraint(
-                    &mut err,
-                    rcvr,
-                    crate::demand::TypeMismatchSource::Arg {
-                        call_expr,
-                        incompatible_arg: provided_idx.as_usize(),
-                    },
-                );
-            }
-
-            self.suggest_ptr_null_mut(
-                expected_ty,
-                provided_ty,
-                provided_args[provided_idx],
-                &mut err,
-            );
-
-            self.suggest_deref_unwrap_or(
-                &mut err,
-                callee_ty,
-                call_ident,
-                expected_ty,
-                provided_ty,
-                provided_args[provided_idx],
-                is_method,
-            );
-
-            // Call out where the function is defined
-            self.label_fn_like(
-                &mut err,
-                fn_def_id,
-                callee_ty,
-                call_expr,
-                Some(expected_ty),
-                Some(expected_idx.as_usize()),
-                &matched_inputs,
-                &formal_and_expected_inputs,
-                is_method,
-                tuple_arguments,
-            );
-            suggest_confusable(&mut err);
-            detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]);
-            return err.emit();
-        }
-
         // Special case, we found an extra argument is provided, which is very common in practice.
         // but there is a obviously better removing suggestion compared to the current one,
         // try to find the argument with Error type, if we removed it all the types will become good,
         // then we will replace the current suggestion.
-        if let [Error::Extra(provided_idx)] = &errors[..] {
-            let remove_idx_is_perfect = |idx: usize| -> bool {
-                let removed_arg_tys = provided_arg_tys
-                    .iter()
-                    .enumerate()
-                    .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) })
-                    .collect::<IndexVec<ProvidedIdx, _>>();
-                std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all(
-                    |((expected_ty, _), (provided_ty, _))| {
-                        !provided_ty.references_error()
-                            && self.may_coerce(*provided_ty, *expected_ty)
-                    },
-                )
-            };
+        fn_call_diag_ctxt.maybe_optimize_extra_arg_suggestion();
 
-            if !remove_idx_is_perfect(provided_idx.as_usize()) {
-                if let Some(i) = (0..provided_args.len()).find(|&i| remove_idx_is_perfect(i)) {
-                    errors = vec![Error::Extra(ProvidedIdx::from_usize(i))];
-                }
-            }
-        }
+        let mut err = fn_call_diag_ctxt.initial_final_diagnostic();
+        fn_call_diag_ctxt.suggest_confusable(&mut err);
 
-        let mut err = if formal_and_expected_inputs.len() == provided_args.len() {
-            struct_span_code_err!(
-                self.dcx(),
-                full_call_span,
-                E0308,
-                "arguments to this {} are incorrect",
-                call_name,
-            )
-        } else {
-            self.dcx()
-                .struct_span_err(
-                    full_call_span,
-                    format!(
-                        "this {} takes {}{} but {} {} supplied",
-                        call_name,
-                        if c_variadic { "at least " } else { "" },
-                        potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
-                        potentially_plural_count(provided_args.len(), "argument"),
-                        pluralize!("was", provided_args.len())
-                    ),
-                )
-                .with_code(err_code.to_owned())
-        };
+        // As we encounter issues, keep track of what we want to provide for the suggestion.
 
-        suggest_confusable(&mut err);
-        // As we encounter issues, keep track of what we want to provide for the suggestion
-        let mut labels = vec![];
-        // If there is a single error, we give a specific suggestion; otherwise, we change to
-        // "did you mean" with the suggested function call
-        enum SuggestionText {
-            None,
-            Provide(bool),
-            Remove(bool),
-            Swap,
-            Reorder,
-            DidYouMean,
-        }
-        let mut suggestion_text = SuggestionText::None;
-
-        let ty_to_snippet = |ty: Ty<'tcx>, expected_idx: ExpectedIdx| {
-            if ty.is_unit() {
-                "()".to_string()
-            } else if ty.is_suggestable(tcx, false) {
-                format!("/* {ty} */")
-            } else if let Some(fn_def_id) = fn_def_id
-                && self.tcx.def_kind(fn_def_id).is_fn_like()
-                && let self_implicit =
-                    matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize
-                && let Some(Some(arg)) =
-                    self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit)
-                && arg.name != kw::SelfLower
-            {
-                format!("/* {} */", arg.name)
-            } else {
-                "/* value */".to_string()
-            }
-        };
-
-        let mut errors = errors.into_iter().peekable();
-        let mut only_extras_so_far = errors
-            .peek()
-            .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0));
-        let mut prev_extra_idx = None;
-        let mut suggestions = vec![];
-        while let Some(error) = errors.next() {
-            only_extras_so_far &= matches!(error, Error::Extra(_));
-
-            match error {
-                Error::Invalid(provided_idx, expected_idx, compatibility) => {
-                    let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
-                    let (provided_ty, provided_span) = provided_arg_tys[provided_idx];
-                    if let Compatibility::Incompatible(error) = compatibility {
-                        let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty);
-                        if let Some(e) = error {
-                            self.err_ctxt().note_type_err(
-                                &mut err,
-                                &trace.cause,
-                                None,
-                                Some(self.param_env.and(trace.values)),
-                                e,
-                                true,
-                                None,
-                            );
-                        }
-                    }
+        let (mut suggestions, labels, suggestion_text) =
+            fn_call_diag_ctxt.labels_and_suggestion_text(&mut err);
 
-                    self.emit_coerce_suggestions(
-                        &mut err,
-                        provided_args[provided_idx],
-                        provided_ty,
-                        Expectation::rvalue_hint(self, expected_ty)
-                            .only_has_type(self)
-                            .unwrap_or(formal_ty),
-                        None,
-                        None,
-                    );
-                    detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]);
-                }
-                Error::Extra(arg_idx) => {
-                    let (provided_ty, provided_span) = provided_arg_tys[arg_idx];
-                    let provided_ty_name = if !has_error_or_infer([provided_ty]) {
-                        // FIXME: not suggestable, use something else
-                        format!(" of type `{provided_ty}`")
-                    } else {
-                        "".to_string()
-                    };
-                    let idx = if provided_arg_tys.len() == 1 {
-                        "".to_string()
-                    } else {
-                        format!(" #{}", arg_idx.as_usize() + 1)
-                    };
-                    labels.push((
-                        provided_span,
-                        format!("unexpected argument{idx}{provided_ty_name}"),
-                    ));
-                    let mut span = provided_span;
-                    if span.can_be_used_for_suggestions()
-                        && error_span.can_be_used_for_suggestions()
-                    {
-                        if arg_idx.index() > 0
-                            && let Some((_, prev)) =
-                                provided_arg_tys.get(ProvidedIdx::from_usize(arg_idx.index() - 1))
-                        {
-                            // Include previous comma
-                            span = prev.shrink_to_hi().to(span);
-                        }
-
-                        // Is last argument for deletion in a row starting from the 0-th argument?
-                        // Then delete the next comma, so we are not left with `f(, ...)`
-                        //
-                        //     fn f() {}
-                        //   - f(0, 1,)
-                        //   + f()
-                        let trim_next_comma = match errors.peek() {
-                            Some(Error::Extra(provided_idx))
-                                if only_extras_so_far
-                                    && provided_idx.index() > arg_idx.index() + 1 =>
-                            // If the next Error::Extra ("next") doesn't next to current ("current"),
-                            // fn foo(_: (), _: u32) {}
-                            // - foo("current", (), 1u32, "next")
-                            // + foo((), 1u32)
-                            // If the previous error is not a `Error::Extra`, then do not trim the next comma
-                            // - foo((), "current", 42u32, "next")
-                            // + foo((), 42u32)
-                            {
-                                prev_extra_idx.is_none_or(|prev_extra_idx| {
-                                    prev_extra_idx + 1 == arg_idx.index()
-                                })
-                            }
-                            // If no error left, we need to delete the next comma
-                            None if only_extras_so_far => true,
-                            // Not sure if other error type need to be handled as well
-                            _ => false,
-                        };
-
-                        if trim_next_comma {
-                            let next = provided_arg_tys
-                                .get(arg_idx + 1)
-                                .map(|&(_, sp)| sp)
-                                .unwrap_or_else(|| {
-                                    // Try to move before `)`. Note that `)` here is not necessarily
-                                    // the latin right paren, it could be a Unicode-confusable that
-                                    // looks like a `)`, so we must not use `- BytePos(1)`
-                                    // manipulations here.
-                                    self.tcx().sess.source_map().end_point(call_expr.span)
-                                });
-
-                            // Include next comma
-                            span = span.until(next);
-                        }
-
-                        suggestions.push((span, String::new()));
-
-                        suggestion_text = match suggestion_text {
-                            SuggestionText::None => SuggestionText::Remove(false),
-                            SuggestionText::Remove(_) => SuggestionText::Remove(true),
-                            _ => SuggestionText::DidYouMean,
-                        };
-                        prev_extra_idx = Some(arg_idx.index())
-                    }
-                    detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]);
-                }
-                Error::Missing(expected_idx) => {
-                    // If there are multiple missing arguments adjacent to each other,
-                    // then we can provide a single error.
-
-                    let mut missing_idxs = vec![expected_idx];
-                    while let Some(e) = errors.next_if(|e| {
-                        matches!(e, Error::Missing(next_expected_idx)
-                            if *next_expected_idx == *missing_idxs.last().unwrap() + 1)
-                    }) {
-                        match e {
-                            Error::Missing(expected_idx) => missing_idxs.push(expected_idx),
-                            _ => unreachable!(
-                                "control flow ensures that we should always get an `Error::Missing`"
-                            ),
-                        }
-                    }
-
-                    // NOTE: Because we might be re-arranging arguments, might have extra
-                    // arguments, etc. it's hard to *really* know where we should provide
-                    // this error label, so as a heuristic, we point to the provided arg, or
-                    // to the call if the missing inputs pass the provided args.
-                    match &missing_idxs[..] {
-                        &[expected_idx] => {
-                            let (_, input_ty) = formal_and_expected_inputs[expected_idx];
-                            let span = if let Some((_, arg_span)) =
-                                provided_arg_tys.get(expected_idx.to_provided_idx())
-                            {
-                                *arg_span
-                            } else {
-                                args_span
-                            };
-                            let rendered = if !has_error_or_infer([input_ty]) {
-                                format!(" of type `{input_ty}`")
-                            } else {
-                                "".to_string()
-                            };
-                            labels.push((
-                                span,
-                                format!(
-                                    "argument #{}{rendered} is missing",
-                                    expected_idx.as_usize() + 1
-                                ),
-                            ));
-
-                            suggestion_text = match suggestion_text {
-                                SuggestionText::None => SuggestionText::Provide(false),
-                                SuggestionText::Provide(_) => SuggestionText::Provide(true),
-                                _ => SuggestionText::DidYouMean,
-                            };
-                        }
-                        &[first_idx, second_idx] => {
-                            let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
-                            let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
-                            let span = if let (Some((_, first_span)), Some((_, second_span))) = (
-                                provided_arg_tys.get(first_idx.to_provided_idx()),
-                                provided_arg_tys.get(second_idx.to_provided_idx()),
-                            ) {
-                                first_span.to(*second_span)
-                            } else {
-                                args_span
-                            };
-                            let rendered =
-                                if !has_error_or_infer([first_expected_ty, second_expected_ty]) {
-                                    format!(
-                                        " of type `{first_expected_ty}` and `{second_expected_ty}`"
-                                    )
-                                } else {
-                                    "".to_string()
-                                };
-                            labels.push((span, format!("two arguments{rendered} are missing")));
-                            suggestion_text = match suggestion_text {
-                                SuggestionText::None | SuggestionText::Provide(_) => {
-                                    SuggestionText::Provide(true)
-                                }
-                                _ => SuggestionText::DidYouMean,
-                            };
-                        }
-                        &[first_idx, second_idx, third_idx] => {
-                            let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
-                            let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
-                            let (_, third_expected_ty) = formal_and_expected_inputs[third_idx];
-                            let span = if let (Some((_, first_span)), Some((_, third_span))) = (
-                                provided_arg_tys.get(first_idx.to_provided_idx()),
-                                provided_arg_tys.get(third_idx.to_provided_idx()),
-                            ) {
-                                first_span.to(*third_span)
-                            } else {
-                                args_span
-                            };
-                            let rendered = if !has_error_or_infer([
-                                first_expected_ty,
-                                second_expected_ty,
-                                third_expected_ty,
-                            ]) {
-                                format!(
-                                    " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`"
-                                )
-                            } else {
-                                "".to_string()
-                            };
-                            labels.push((span, format!("three arguments{rendered} are missing")));
-                            suggestion_text = match suggestion_text {
-                                SuggestionText::None | SuggestionText::Provide(_) => {
-                                    SuggestionText::Provide(true)
-                                }
-                                _ => SuggestionText::DidYouMean,
-                            };
-                        }
-                        missing_idxs => {
-                            let first_idx = *missing_idxs.first().unwrap();
-                            let last_idx = *missing_idxs.last().unwrap();
-                            // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
-                            // It's hard to *really* know where we should provide this error label, so this is a
-                            // decent heuristic
-                            let span = if let (Some((_, first_span)), Some((_, last_span))) = (
-                                provided_arg_tys.get(first_idx.to_provided_idx()),
-                                provided_arg_tys.get(last_idx.to_provided_idx()),
-                            ) {
-                                first_span.to(*last_span)
-                            } else {
-                                args_span
-                            };
-                            labels.push((span, "multiple arguments are missing".to_string()));
-                            suggestion_text = match suggestion_text {
-                                SuggestionText::None | SuggestionText::Provide(_) => {
-                                    SuggestionText::Provide(true)
-                                }
-                                _ => SuggestionText::DidYouMean,
-                            };
-                        }
-                    }
-                }
-                Error::Swap(
-                    first_provided_idx,
-                    second_provided_idx,
-                    first_expected_idx,
-                    second_expected_idx,
-                ) => {
-                    let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx];
-                    let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx];
-                    let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
-                        format!(", found `{first_provided_ty}`")
-                    } else {
-                        String::new()
-                    };
-                    labels.push((
-                        first_span,
-                        format!("expected `{first_expected_ty}`{first_provided_ty_name}"),
-                    ));
-
-                    let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx];
-                    let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx];
-                    let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
-                        format!(", found `{second_provided_ty}`")
-                    } else {
-                        String::new()
-                    };
-                    labels.push((
-                        second_span,
-                        format!("expected `{second_expected_ty}`{second_provided_ty_name}"),
-                    ));
-
-                    suggestion_text = match suggestion_text {
-                        SuggestionText::None => SuggestionText::Swap,
-                        _ => SuggestionText::DidYouMean,
-                    };
-                }
-                Error::Permutation(args) => {
-                    for (dst_arg, dest_input) in args {
-                        let (_, expected_ty) = formal_and_expected_inputs[dst_arg];
-                        let (provided_ty, provided_span) = provided_arg_tys[dest_input];
-                        let provided_ty_name = if !has_error_or_infer([provided_ty]) {
-                            format!(", found `{provided_ty}`")
-                        } else {
-                            String::new()
-                        };
-                        labels.push((
-                            provided_span,
-                            format!("expected `{expected_ty}`{provided_ty_name}"),
-                        ));
-                    }
-
-                    suggestion_text = match suggestion_text {
-                        SuggestionText::None => SuggestionText::Reorder,
-                        _ => SuggestionText::DidYouMean,
-                    };
-                }
-            }
-        }
-
-        self.label_generic_mismatches(
-            &mut err,
-            fn_def_id,
-            &matched_inputs,
-            &provided_arg_tys,
-            &formal_and_expected_inputs,
-            is_method,
-        );
-
-        // Incorporate the argument changes in the removal suggestion.
-        // When a type is *missing*, and the rest are additional, we want to suggest these with a
-        // multipart suggestion, but in order to do so we need to figure out *where* the arg that
-        // was provided but had the wrong type should go, because when looking at `expected_idx`
-        // that is the position in the argument list in the definition, while `provided_idx` will
-        // not be present. So we have to look at what the *last* provided position was, and point
-        // one after to suggest the replacement. FIXME(estebank): This is hacky, and there's
-        // probably a better more involved change we can make to make this work.
-        // For example, if we have
-        // ```
-        // fn foo(i32, &'static str) {}
-        // foo((), (), ());
-        // ```
-        // what should be suggested is
-        // ```
-        // foo(/* i32 */, /* &str */);
-        // ```
-        // which includes the replacement of the first two `()` for the correct type, and the
-        // removal of the last `()`.
-        let mut prev = -1;
-        for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
-            // We want to point not at the *current* argument expression index, but rather at the
-            // index position where it *should have been*, which is *after* the previous one.
-            if let Some(provided_idx) = provided_idx {
-                prev = provided_idx.index() as i64;
-                continue;
-            }
-            let idx = ProvidedIdx::from_usize((prev + 1) as usize);
-            if let Some((_, arg_span)) = provided_arg_tys.get(idx) {
-                prev += 1;
-                // There is a type that was *not* found anywhere, so it isn't a move, but a
-                // replacement and we look at what type it should have been. This will allow us
-                // To suggest a multipart suggestion when encountering `foo(1, "")` where the def
-                // was `fn foo(())`.
-                let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
-                suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx)));
-            }
-        }
+        fn_call_diag_ctxt.label_generic_mismatches(&mut err);
+        fn_call_diag_ctxt.append_arguments_changes(&mut suggestions);
 
         // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
         if labels.len() <= 5 {
@@ -1537,113 +646,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // Call out where the function is defined
-        self.label_fn_like(
+        fn_call_diag_ctxt.label_fn_like(
             &mut err,
             fn_def_id,
-            callee_ty,
+            fn_call_diag_ctxt.callee_ty,
             call_expr,
             None,
             None,
-            &matched_inputs,
-            &formal_and_expected_inputs,
-            is_method,
+            &fn_call_diag_ctxt.matched_inputs,
+            &fn_call_diag_ctxt.formal_and_expected_inputs,
+            fn_call_diag_ctxt.call_metadata.is_method,
             tuple_arguments,
         );
 
         // And add a suggestion block for all of the parameters
-        let suggestion_text = match suggestion_text {
-            SuggestionText::None => None,
-            SuggestionText::Provide(plural) => {
-                Some(format!("provide the argument{}", if plural { "s" } else { "" }))
-            }
-            SuggestionText::Remove(plural) => {
-                err.multipart_suggestion_verbose(
-                    format!("remove the extra argument{}", if plural { "s" } else { "" }),
-                    suggestions,
-                    Applicability::HasPlaceholders,
-                );
-                None
-            }
-            SuggestionText::Swap => Some("swap these arguments".to_string()),
-            SuggestionText::Reorder => Some("reorder these arguments".to_string()),
-            SuggestionText::DidYouMean => Some("did you mean".to_string()),
-        };
-        if let Some(suggestion_text) = suggestion_text
-            && !full_call_span.in_external_macro(self.sess().source_map())
+        if let Some(suggestion_message) =
+            FnCallDiagCtxt::format_suggestion_text(&mut err, suggestions, suggestion_text)
+            && !fn_call_diag_ctxt.call_is_in_macro()
         {
-            let source_map = self.sess().source_map();
-            let suggestion_span = if let Some(args_span) = error_span.trim_start(full_call_span) {
-                // Span of the braces, e.g. `(a, b, c)`.
-                args_span
-            } else {
-                // The arg span of a function call that wasn't even given braces
-                // like what might happen with delegation reuse.
-                // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`.
-                full_call_span.shrink_to_hi()
-            };
-
-            // Controls how the arguments should be listed in the suggestion.
-            enum ArgumentsFormatting {
-                SingleLine,
-                Multiline { fallback_indent: String, brace_indent: String },
-            }
-            let arguments_formatting = {
-                let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a);
-                if let Some(brace_indent) = source_map.indentation_before(suggestion_span)
-                    && let Some(first_idx) = provided_inputs.by_ref().next()
-                    && let Some(last_idx) = provided_inputs.by_ref().next()
-                    && let (_, first_span) = provided_arg_tys[first_idx]
-                    && let (_, last_span) = provided_arg_tys[last_idx]
-                    && source_map.is_multiline(first_span.to(last_span))
-                    && let Some(fallback_indent) = source_map.indentation_before(first_span)
-                {
-                    ArgumentsFormatting::Multiline { fallback_indent, brace_indent }
-                } else {
-                    ArgumentsFormatting::SingleLine
-                }
-            };
+            let (suggestion_span, suggestion_code) = fn_call_diag_ctxt.suggestion_code();
 
-            let mut suggestion = "(".to_owned();
-            let mut needs_comma = false;
-            for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
-                if needs_comma {
-                    suggestion += ",";
-                }
-                match &arguments_formatting {
-                    ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ",
-                    ArgumentsFormatting::SingleLine => {}
-                    ArgumentsFormatting::Multiline { .. } => suggestion += "\n",
-                }
-                needs_comma = true;
-                let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx
-                    && let (_, provided_span) = provided_arg_tys[*provided_idx]
-                    && let Ok(arg_text) = source_map.span_to_snippet(provided_span)
-                {
-                    (Some(provided_span), arg_text)
-                } else {
-                    // Propose a placeholder of the correct type
-                    let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
-                    (None, ty_to_snippet(expected_ty, expected_idx))
-                };
-                if let ArgumentsFormatting::Multiline { fallback_indent, .. } =
-                    &arguments_formatting
-                {
-                    let indent = suggestion_span
-                        .and_then(|span| source_map.indentation_before(span))
-                        .unwrap_or_else(|| fallback_indent.clone());
-                    suggestion += &indent;
-                }
-                suggestion += &suggestion_text;
-            }
-            if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting {
-                suggestion += ",\n";
-                suggestion += &brace_indent;
-            }
-            suggestion += ")";
             err.span_suggestion_verbose(
                 suggestion_span,
-                suggestion_text,
-                suggestion,
+                suggestion_message,
+                suggestion_code,
                 Applicability::HasPlaceholders,
             );
         }
@@ -2836,3 +1862,1333 @@ impl FnParam<'_> {
         D(*self, idx)
     }
 }
+
+struct FnCallDiagCtxt<'a, 'b, 'tcx> {
+    arg_matching_ctxt: ArgMatchingCtxt<'a, 'b, 'tcx>,
+    errors: Vec<Error<'tcx>>,
+    matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>>,
+}
+
+impl<'a, 'b, 'tcx> Deref for FnCallDiagCtxt<'a, 'b, 'tcx> {
+    type Target = ArgMatchingCtxt<'a, 'b, 'tcx>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.arg_matching_ctxt
+    }
+}
+
+// Controls how the arguments should be listed in the suggestion.
+enum ArgumentsFormatting {
+    SingleLine,
+    Multiline { fallback_indent: String, brace_indent: String },
+}
+
+impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> {
+    fn new(
+        arg: &'a FnCtxt<'b, 'tcx>,
+        compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
+        formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+        provided_args: IndexVec<ProvidedIdx, &'tcx Expr<'tcx>>,
+        c_variadic: bool,
+        err_code: ErrCode,
+        fn_def_id: Option<DefId>,
+        call_span: Span,
+        call_expr: &'tcx Expr<'tcx>,
+        tuple_arguments: TupleArgumentsFlag,
+    ) -> Self {
+        let arg_matching_ctxt = ArgMatchingCtxt::new(
+            arg,
+            compatibility_diagonal,
+            formal_and_expected_inputs,
+            provided_args,
+            c_variadic,
+            err_code,
+            fn_def_id,
+            call_span,
+            call_expr,
+            tuple_arguments,
+        );
+
+        // The algorithm here is inspired by levenshtein distance and longest common subsequence.
+        // We'll try to detect 4 different types of mistakes:
+        // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs
+        // - An input is missing, which isn't satisfied by *any* of the other arguments
+        // - Some number of arguments have been provided in the wrong order
+        // - A type is straight up invalid
+        let (errors, matched_inputs) = ArgMatrix::new(
+            arg_matching_ctxt.provided_args.len(),
+            arg_matching_ctxt.formal_and_expected_inputs.len(),
+            |provided, expected| arg_matching_ctxt.check_compatible(provided, expected),
+        )
+        .find_errors();
+
+        FnCallDiagCtxt { arg_matching_ctxt, errors, matched_inputs }
+    }
+
+    fn check_wrap_args_in_tuple(&self) -> Option<ErrorGuaranteed> {
+        if let Some((mismatch_idx, terr)) = self.first_incompatible_error() {
+            // Is the first bad expected argument a tuple?
+            // Do we have as many extra provided arguments as the tuple's length?
+            // If so, we might have just forgotten to wrap some args in a tuple.
+            if let Some(ty::Tuple(tys)) =
+               self.formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind())
+                // If the tuple is unit, we're not actually wrapping any arguments.
+                && !tys.is_empty()
+                && self.provided_arg_tys.len() == self.formal_and_expected_inputs.len() - 1 + tys.len()
+            {
+                // Wrap up the N provided arguments starting at this position in a tuple.
+                let provided_args_to_tuple = &self.provided_arg_tys[mismatch_idx..];
+                let (provided_args_to_tuple, provided_args_after_tuple) =
+                    provided_args_to_tuple.split_at(tys.len());
+                let provided_as_tuple = Ty::new_tup_from_iter(
+                    self.tcx,
+                    provided_args_to_tuple.iter().map(|&(ty, _)| ty),
+                );
+
+                let mut satisfied = true;
+                // Check if the newly wrapped tuple + rest of the arguments are compatible.
+                for ((_, expected_ty), provided_ty) in std::iter::zip(
+                    self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(),
+                    [provided_as_tuple]
+                        .into_iter()
+                        .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)),
+                ) {
+                    if !self.may_coerce(provided_ty, *expected_ty) {
+                        satisfied = false;
+                        break;
+                    }
+                }
+
+                // If they're compatible, suggest wrapping in an arg, and we're done!
+                // Take some care with spans, so we don't suggest wrapping a macro's
+                // innards in parenthesis, for example.
+                if satisfied
+                    && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple
+                {
+                    let mut err;
+                    if tys.len() == 1 {
+                        // A tuple wrap suggestion actually occurs within,
+                        // so don't do anything special here.
+                        err = self.err_ctxt().report_and_explain_type_error(
+                            self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace(
+                                lo,
+                                self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()],
+                                self.provided_arg_tys[mismatch_idx].0,
+                            ),
+                            self.param_env,
+                            terr,
+                        );
+                        let call_name = self.call_metadata.call_name;
+                        err.span_label(
+                            self.call_metadata.full_call_span,
+                            format!("arguments to this {call_name} are incorrect"),
+                        );
+                    } else {
+                        let call_name = self.call_metadata.call_name;
+                        err = self.dcx().struct_span_err(
+                            self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span,
+                            format!(
+                                "{call_name} takes {}{} but {} {} supplied",
+                                if self.c_variadic { "at least " } else { "" },
+                                potentially_plural_count(
+                                    self.formal_and_expected_inputs.len(),
+                                    "argument"
+                                ),
+                                potentially_plural_count(self.provided_args.len(), "argument"),
+                                pluralize!("was", self.provided_args.len())
+                            ),
+                        );
+                        err.code(self.err_code.to_owned());
+                        err.multipart_suggestion_verbose(
+                            "wrap these arguments in parentheses to construct a tuple",
+                            vec![
+                                (lo.shrink_to_lo(), "(".to_string()),
+                                (hi.shrink_to_hi(), ")".to_string()),
+                            ],
+                            Applicability::MachineApplicable,
+                        );
+                    };
+                    self.arg_matching_ctxt.args_ctxt.call_ctxt.fn_ctxt.label_fn_like(
+                        &mut err,
+                        self.fn_def_id,
+                        self.callee_ty,
+                        self.call_expr,
+                        None,
+                        Some(mismatch_idx.as_usize()),
+                        &self.matched_inputs,
+                        &self.formal_and_expected_inputs,
+                        self.call_metadata.is_method,
+                        self.tuple_arguments,
+                    );
+                    self.suggest_confusable(&mut err);
+                    Some(err.emit())
+                } else {
+                    None
+                }
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    }
+
+    fn ensure_has_errors(&self) -> Option<ErrorGuaranteed> {
+        if self.errors.is_empty() {
+            if cfg!(debug_assertions) {
+                span_bug!(self.call_metadata.error_span, "expected errors from argument matrix");
+            } else {
+                let mut err = self.dcx().create_err(errors::ArgMismatchIndeterminate {
+                    span: self.call_metadata.error_span,
+                });
+                self.arg_matching_ctxt.suggest_confusable(&mut err);
+                return Some(err.emit());
+            }
+        }
+
+        None
+    }
+
+    fn detect_dotdot(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'tcx>) {
+        if let ty::Adt(adt, _) = ty.kind()
+            && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull)
+            && let hir::ExprKind::Struct(hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], _) =
+                expr.kind
+        {
+            // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax
+            // from default field values, which is not supported on tuples.
+            let explanation = if self.tcx.features().default_field_values() {
+                "this is only supported on non-tuple struct literals"
+            } else if self.tcx.sess.is_nightly_build() {
+                "this is only supported on non-tuple struct literals when \
+                 `#![feature(default_field_values)]` is enabled"
+            } else {
+                "this is not supported"
+            };
+            let msg = format!(
+                "you might have meant to use `..` to skip providing a value for \
+                 expected fields, but {explanation}; it is instead interpreted as a \
+                 `std::ops::RangeFull` literal",
+            );
+            err.span_help(expr.span, msg);
+        }
+    }
+
+    fn filter_out_invalid_arguments(&mut self) -> Option<ErrorGuaranteed> {
+        let mut reported = None;
+
+        self.errors.retain(|error| {
+            let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) =
+                error
+            else {
+                return true;
+            };
+            let (provided_ty, provided_span) =
+                self.arg_matching_ctxt.provided_arg_tys[*provided_idx];
+            let trace = self.arg_matching_ctxt.mk_trace(
+                provided_span,
+                self.arg_matching_ctxt.formal_and_expected_inputs[*expected_idx],
+                provided_ty,
+            );
+            if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
+                let mut err = self.arg_matching_ctxt.err_ctxt().report_and_explain_type_error(
+                    trace,
+                    self.arg_matching_ctxt.param_env,
+                    *e,
+                );
+                self.arg_matching_ctxt.suggest_confusable(&mut err);
+                reported = Some(err.emit());
+                return false;
+            }
+            true
+        });
+
+        reported
+    }
+
+    fn check_single_incompatible(&self) -> Option<ErrorGuaranteed> {
+        if let &[
+            Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))),
+        ] = &self.errors[..]
+        {
+            let (formal_ty, expected_ty) = self.formal_and_expected_inputs[expected_idx];
+            let (provided_ty, provided_arg_span) = self.provided_arg_tys[provided_idx];
+            let trace = self.mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty);
+            let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err);
+            self.emit_coerce_suggestions(
+                &mut err,
+                self.provided_args[provided_idx],
+                provided_ty,
+                Expectation::rvalue_hint(self.fn_ctxt, expected_ty)
+                    .only_has_type(self.fn_ctxt)
+                    .unwrap_or(formal_ty),
+                None,
+                None,
+            );
+            let call_name = self.call_metadata.call_name;
+            err.span_label(
+                self.call_metadata.full_call_span,
+                format!("arguments to this {call_name} are incorrect"),
+            );
+
+            self.fn_ctxt.label_generic_mismatches(
+                &mut err,
+                self.fn_def_id,
+                &self.matched_inputs,
+                &self.provided_arg_tys,
+                &self.formal_and_expected_inputs,
+                self.call_metadata.is_method,
+            );
+
+            if let hir::ExprKind::MethodCall(_, rcvr, _, _) =
+                self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind
+                && provided_idx.as_usize() == expected_idx.as_usize()
+            {
+                self.note_source_of_type_mismatch_constraint(
+                    &mut err,
+                    rcvr,
+                    crate::demand::TypeMismatchSource::Arg {
+                        call_expr: self.call_expr,
+                        incompatible_arg: provided_idx.as_usize(),
+                    },
+                );
+            }
+
+            self.suggest_ptr_null_mut(
+                expected_ty,
+                provided_ty,
+                self.provided_args[provided_idx],
+                &mut err,
+            );
+
+            self.suggest_deref_unwrap_or(
+                &mut err,
+                self.callee_ty,
+                self.call_metadata.call_ident,
+                expected_ty,
+                provided_ty,
+                self.provided_args[provided_idx],
+                self.call_metadata.is_method,
+            );
+
+            // Call out where the function is defined
+            self.label_fn_like(
+                &mut err,
+                self.fn_def_id,
+                self.callee_ty,
+                self.call_expr,
+                Some(expected_ty),
+                Some(expected_idx.as_usize()),
+                &self.matched_inputs,
+                &self.formal_and_expected_inputs,
+                self.call_metadata.is_method,
+                self.tuple_arguments,
+            );
+            self.arg_matching_ctxt.suggest_confusable(&mut err);
+            self.detect_dotdot(&mut err, provided_ty, self.provided_args[provided_idx]);
+            return Some(err.emit());
+        }
+
+        None
+    }
+
+    fn maybe_optimize_extra_arg_suggestion(&mut self) {
+        if let [Error::Extra(provided_idx)] = &self.errors[..] {
+            if !self.remove_idx_is_perfect(provided_idx.as_usize()) {
+                if let Some(i) = (0..self.args_ctxt.call_ctxt.provided_args.len())
+                    .find(|&i| self.remove_idx_is_perfect(i))
+                {
+                    self.errors = vec![Error::Extra(ProvidedIdx::from_usize(i))];
+                }
+            }
+        }
+    }
+
+    fn initial_final_diagnostic(&self) -> Diag<'_> {
+        if self.formal_and_expected_inputs.len() == self.provided_args.len() {
+            struct_span_code_err!(
+                self.dcx(),
+                self.call_metadata.full_call_span,
+                E0308,
+                "arguments to this {} are incorrect",
+                self.call_metadata.call_name,
+            )
+        } else {
+            self.arg_matching_ctxt
+                .dcx()
+                .struct_span_err(
+                    self.call_metadata.full_call_span,
+                    format!(
+                        "this {} takes {}{} but {} {} supplied",
+                        self.call_metadata.call_name,
+                        if self.c_variadic { "at least " } else { "" },
+                        potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"),
+                        potentially_plural_count(self.provided_args.len(), "argument"),
+                        pluralize!("was", self.provided_args.len())
+                    ),
+                )
+                .with_code(self.err_code.to_owned())
+        }
+    }
+
+    fn labels_and_suggestion_text(
+        &self,
+        err: &mut Diag<'_>,
+    ) -> (Vec<(Span, String)>, Vec<(Span, String)>, SuggestionText) {
+        // Don't print if it has error types or is just plain `_`
+        fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
+            tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var())
+        }
+
+        let mut labels = Vec::new();
+        let mut suggestion_text = SuggestionText::None;
+
+        let mut errors = self.errors.iter().peekable();
+        let mut only_extras_so_far = errors
+            .peek()
+            .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0));
+        let mut prev_extra_idx = None;
+        let mut suggestions = vec![];
+        while let Some(error) = errors.next() {
+            only_extras_so_far &= matches!(error, Error::Extra(_));
+
+            match error {
+                Error::Invalid(provided_idx, expected_idx, compatibility) => {
+                    let (formal_ty, expected_ty) =
+                        self.arg_matching_ctxt.args_ctxt.call_ctxt.formal_and_expected_inputs
+                            [*expected_idx];
+                    let (provided_ty, provided_span) =
+                        self.arg_matching_ctxt.provided_arg_tys[*provided_idx];
+                    if let Compatibility::Incompatible(error) = compatibility {
+                        let trace = self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace(
+                            provided_span,
+                            (formal_ty, expected_ty),
+                            provided_ty,
+                        );
+                        if let Some(e) = error {
+                            self.err_ctxt().note_type_err(
+                                err,
+                                &trace.cause,
+                                None,
+                                Some(self.param_env.and(trace.values)),
+                                *e,
+                                true,
+                                None,
+                            );
+                        }
+                    }
+
+                    self.emit_coerce_suggestions(
+                        err,
+                        self.provided_args[*provided_idx],
+                        provided_ty,
+                        Expectation::rvalue_hint(self.fn_ctxt, expected_ty)
+                            .only_has_type(self.fn_ctxt)
+                            .unwrap_or(formal_ty),
+                        None,
+                        None,
+                    );
+                    self.detect_dotdot(err, provided_ty, self.provided_args[*provided_idx]);
+                }
+                Error::Extra(arg_idx) => {
+                    let (provided_ty, provided_span) = self.provided_arg_tys[*arg_idx];
+                    let provided_ty_name = if !has_error_or_infer([provided_ty]) {
+                        // FIXME: not suggestable, use something else
+                        format!(" of type `{provided_ty}`")
+                    } else {
+                        "".to_string()
+                    };
+                    let idx = if self.provided_arg_tys.len() == 1 {
+                        "".to_string()
+                    } else {
+                        format!(" #{}", arg_idx.as_usize() + 1)
+                    };
+                    labels.push((
+                        provided_span,
+                        format!("unexpected argument{idx}{provided_ty_name}"),
+                    ));
+                    let mut span = provided_span;
+                    if span.can_be_used_for_suggestions()
+                        && self.call_metadata.error_span.can_be_used_for_suggestions()
+                    {
+                        if arg_idx.index() > 0
+                            && let Some((_, prev)) = self
+                                .provided_arg_tys
+                                .get(ProvidedIdx::from_usize(arg_idx.index() - 1))
+                        {
+                            // Include previous comma
+                            span = prev.shrink_to_hi().to(span);
+                        }
+
+                        // Is last argument for deletion in a row starting from the 0-th argument?
+                        // Then delete the next comma, so we are not left with `f(, ...)`
+                        //
+                        //     fn f() {}
+                        //   - f(0, 1,)
+                        //   + f()
+                        let trim_next_comma = match errors.peek() {
+                            Some(Error::Extra(provided_idx))
+                                if only_extras_so_far
+                                    && provided_idx.index() > arg_idx.index() + 1 =>
+                            // If the next Error::Extra ("next") doesn't next to current ("current"),
+                            // fn foo(_: (), _: u32) {}
+                            // - foo("current", (), 1u32, "next")
+                            // + foo((), 1u32)
+                            // If the previous error is not a `Error::Extra`, then do not trim the next comma
+                            // - foo((), "current", 42u32, "next")
+                            // + foo((), 42u32)
+                            {
+                                prev_extra_idx.is_none_or(|prev_extra_idx| {
+                                    prev_extra_idx + 1 == arg_idx.index()
+                                })
+                            }
+                            // If no error left, we need to delete the next comma
+                            None if only_extras_so_far => true,
+                            // Not sure if other error type need to be handled as well
+                            _ => false,
+                        };
+
+                        if trim_next_comma {
+                            let next = self
+                                .provided_arg_tys
+                                .get(*arg_idx + 1)
+                                .map(|&(_, sp)| sp)
+                                .unwrap_or_else(|| {
+                                    // Try to move before `)`. Note that `)` here is not necessarily
+                                    // the latin right paren, it could be a Unicode-confusable that
+                                    // looks like a `)`, so we must not use `- BytePos(1)`
+                                    // manipulations here.
+                                    self.arg_matching_ctxt
+                                        .tcx()
+                                        .sess
+                                        .source_map()
+                                        .end_point(self.call_expr.span)
+                                });
+
+                            // Include next comma
+                            span = span.until(next);
+                        }
+
+                        suggestions.push((span, String::new()));
+
+                        suggestion_text = match suggestion_text {
+                            SuggestionText::None => SuggestionText::Remove(false),
+                            SuggestionText::Remove(_) => SuggestionText::Remove(true),
+                            _ => SuggestionText::DidYouMean,
+                        };
+                        prev_extra_idx = Some(arg_idx.index())
+                    }
+                    self.detect_dotdot(err, provided_ty, self.provided_args[*arg_idx]);
+                }
+                Error::Missing(expected_idx) => {
+                    // If there are multiple missing arguments adjacent to each other,
+                    // then we can provide a single error.
+
+                    let mut missing_idxs = vec![*expected_idx];
+                    while let Some(e) = errors.next_if(|e| {
+                        matches!(e, Error::Missing(next_expected_idx)
+                            if *next_expected_idx == *missing_idxs.last().unwrap() + 1)
+                    }) {
+                        match e {
+                            Error::Missing(expected_idx) => missing_idxs.push(*expected_idx),
+                            _ => unreachable!(
+                                "control flow ensures that we should always get an `Error::Missing`"
+                            ),
+                        }
+                    }
+
+                    // NOTE: Because we might be re-arranging arguments, might have extra
+                    // arguments, etc. it's hard to *really* know where we should provide
+                    // this error label, so as a heuristic, we point to the provided arg, or
+                    // to the call if the missing inputs pass the provided args.
+                    match &missing_idxs[..] {
+                        &[expected_idx] => {
+                            let (_, input_ty) = self.formal_and_expected_inputs[expected_idx];
+                            let span = if let Some((_, arg_span)) =
+                                self.provided_arg_tys.get(expected_idx.to_provided_idx())
+                            {
+                                *arg_span
+                            } else {
+                                self.args_span
+                            };
+                            let rendered = if !has_error_or_infer([input_ty]) {
+                                format!(" of type `{input_ty}`")
+                            } else {
+                                "".to_string()
+                            };
+                            labels.push((
+                                span,
+                                format!(
+                                    "argument #{}{rendered} is missing",
+                                    expected_idx.as_usize() + 1
+                                ),
+                            ));
+
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None => SuggestionText::Provide(false),
+                                SuggestionText::Provide(_) => SuggestionText::Provide(true),
+                                _ => SuggestionText::DidYouMean,
+                            };
+                        }
+                        &[first_idx, second_idx] => {
+                            let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx];
+                            let (_, second_expected_ty) =
+                                self.formal_and_expected_inputs[second_idx];
+                            let span = if let (Some((_, first_span)), Some((_, second_span))) = (
+                                self.provided_arg_tys.get(first_idx.to_provided_idx()),
+                                self.provided_arg_tys.get(second_idx.to_provided_idx()),
+                            ) {
+                                first_span.to(*second_span)
+                            } else {
+                                self.args_span
+                            };
+                            let rendered =
+                                if !has_error_or_infer([first_expected_ty, second_expected_ty]) {
+                                    format!(
+                                        " of type `{first_expected_ty}` and `{second_expected_ty}`"
+                                    )
+                                } else {
+                                    "".to_string()
+                                };
+                            labels.push((span, format!("two arguments{rendered} are missing")));
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None | SuggestionText::Provide(_) => {
+                                    SuggestionText::Provide(true)
+                                }
+                                _ => SuggestionText::DidYouMean,
+                            };
+                        }
+                        &[first_idx, second_idx, third_idx] => {
+                            let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx];
+                            let (_, second_expected_ty) =
+                                self.formal_and_expected_inputs[second_idx];
+                            let (_, third_expected_ty) = self.formal_and_expected_inputs[third_idx];
+                            let span = if let (Some((_, first_span)), Some((_, third_span))) = (
+                                self.provided_arg_tys.get(first_idx.to_provided_idx()),
+                                self.provided_arg_tys.get(third_idx.to_provided_idx()),
+                            ) {
+                                first_span.to(*third_span)
+                            } else {
+                                self.args_span
+                            };
+                            let rendered = if !has_error_or_infer([
+                                first_expected_ty,
+                                second_expected_ty,
+                                third_expected_ty,
+                            ]) {
+                                format!(
+                                    " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`"
+                                )
+                            } else {
+                                "".to_string()
+                            };
+                            labels.push((span, format!("three arguments{rendered} are missing")));
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None | SuggestionText::Provide(_) => {
+                                    SuggestionText::Provide(true)
+                                }
+                                _ => SuggestionText::DidYouMean,
+                            };
+                        }
+                        missing_idxs => {
+                            let first_idx = *missing_idxs.first().unwrap();
+                            let last_idx = *missing_idxs.last().unwrap();
+                            // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
+                            // It's hard to *really* know where we should provide this error label, so this is a
+                            // decent heuristic
+                            let span = if let (Some((_, first_span)), Some((_, last_span))) = (
+                                self.provided_arg_tys.get(first_idx.to_provided_idx()),
+                                self.provided_arg_tys.get(last_idx.to_provided_idx()),
+                            ) {
+                                first_span.to(*last_span)
+                            } else {
+                                self.args_span
+                            };
+                            labels.push((span, "multiple arguments are missing".to_string()));
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None | SuggestionText::Provide(_) => {
+                                    SuggestionText::Provide(true)
+                                }
+                                _ => SuggestionText::DidYouMean,
+                            };
+                        }
+                    }
+                }
+                Error::Swap(
+                    first_provided_idx,
+                    second_provided_idx,
+                    first_expected_idx,
+                    second_expected_idx,
+                ) => {
+                    let (first_provided_ty, first_span) =
+                        self.provided_arg_tys[*first_provided_idx];
+                    let (_, first_expected_ty) =
+                        self.formal_and_expected_inputs[*first_expected_idx];
+                    let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
+                        format!(", found `{first_provided_ty}`")
+                    } else {
+                        String::new()
+                    };
+                    labels.push((
+                        first_span,
+                        format!("expected `{first_expected_ty}`{first_provided_ty_name}"),
+                    ));
+
+                    let (second_provided_ty, second_span) =
+                        self.provided_arg_tys[*second_provided_idx];
+                    let (_, second_expected_ty) =
+                        self.formal_and_expected_inputs[*second_expected_idx];
+                    let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
+                        format!(", found `{second_provided_ty}`")
+                    } else {
+                        String::new()
+                    };
+                    labels.push((
+                        second_span,
+                        format!("expected `{second_expected_ty}`{second_provided_ty_name}"),
+                    ));
+
+                    suggestion_text = match suggestion_text {
+                        SuggestionText::None => SuggestionText::Swap,
+                        _ => SuggestionText::DidYouMean,
+                    };
+                }
+                Error::Permutation(args) => {
+                    for (dst_arg, dest_input) in args {
+                        let (_, expected_ty) = self.formal_and_expected_inputs[*dst_arg];
+                        let (provided_ty, provided_span) = self.provided_arg_tys[*dest_input];
+                        let provided_ty_name = if !has_error_or_infer([provided_ty]) {
+                            format!(", found `{provided_ty}`")
+                        } else {
+                            String::new()
+                        };
+                        labels.push((
+                            provided_span,
+                            format!("expected `{expected_ty}`{provided_ty_name}"),
+                        ));
+                    }
+
+                    suggestion_text = match suggestion_text {
+                        SuggestionText::None => SuggestionText::Reorder,
+                        _ => SuggestionText::DidYouMean,
+                    };
+                }
+            }
+        }
+
+        (suggestions, labels, suggestion_text)
+    }
+
+    fn label_generic_mismatches(&self, err: &mut Diag<'b>) {
+        self.fn_ctxt.label_generic_mismatches(
+            err,
+            self.fn_def_id,
+            &self.matched_inputs,
+            &self.provided_arg_tys,
+            &self.formal_and_expected_inputs,
+            self.call_metadata.is_method,
+        );
+    }
+
+    /// Incorporate the argument changes in the removal suggestion.
+    ///
+    /// When a type is *missing*, and the rest are additional, we want to suggest these with a
+    /// multipart suggestion, but in order to do so we need to figure out *where* the arg that
+    /// was provided but had the wrong type should go, because when looking at `expected_idx`
+    /// that is the position in the argument list in the definition, while `provided_idx` will
+    /// not be present. So we have to look at what the *last* provided position was, and point
+    /// one after to suggest the replacement.
+    fn append_arguments_changes(&self, suggestions: &mut Vec<(Span, String)>) {
+        // FIXME(estebank): This is hacky, and there's
+        // probably a better more involved change we can make to make this work.
+        // For example, if we have
+        // ```
+        // fn foo(i32, &'static str) {}
+        // foo((), (), ());
+        // ```
+        // what should be suggested is
+        // ```
+        // foo(/* i32 */, /* &str */);
+        // ```
+        // which includes the replacement of the first two `()` for the correct type, and the
+        // removal of the last `()`.
+
+        let mut prev = -1;
+        for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() {
+            // We want to point not at the *current* argument expression index, but rather at the
+            // index position where it *should have been*, which is *after* the previous one.
+            if let Some(provided_idx) = provided_idx {
+                prev = provided_idx.index() as i64;
+                continue;
+            }
+            let idx = ProvidedIdx::from_usize((prev + 1) as usize);
+            if let Some((_, arg_span)) = self.provided_arg_tys.get(idx) {
+                prev += 1;
+                // There is a type that was *not* found anywhere, so it isn't a move, but a
+                // replacement and we look at what type it should have been. This will allow us
+                // To suggest a multipart suggestion when encountering `foo(1, "")` where the def
+                // was `fn foo(())`.
+                let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx];
+                suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx)));
+            }
+        }
+    }
+
+    fn format_suggestion_text(
+        err: &mut Diag<'_>,
+        suggestions: Vec<(Span, String)>,
+        suggestion_text: SuggestionText,
+    ) -> Option<String> {
+        let suggestion_text = match suggestion_text {
+            SuggestionText::None => None,
+            SuggestionText::Provide(plural) => {
+                Some(format!("provide the argument{}", if plural { "s" } else { "" }))
+            }
+            SuggestionText::Remove(plural) => {
+                err.multipart_suggestion_verbose(
+                    format!("remove the extra argument{}", if plural { "s" } else { "" }),
+                    suggestions,
+                    Applicability::HasPlaceholders,
+                );
+                None
+            }
+            SuggestionText::Swap => Some("swap these arguments".to_string()),
+            SuggestionText::Reorder => Some("reorder these arguments".to_string()),
+            SuggestionText::DidYouMean => Some("did you mean".to_string()),
+        };
+        suggestion_text
+    }
+
+    fn arguments_formatting(&self, suggestion_span: Span) -> ArgumentsFormatting {
+        let source_map = self.sess().source_map();
+        let mut provided_inputs = self.matched_inputs.iter().filter_map(|a| *a);
+        if let Some(brace_indent) = source_map.indentation_before(suggestion_span)
+            && let Some(first_idx) = provided_inputs.by_ref().next()
+            && let Some(last_idx) = provided_inputs.by_ref().next()
+            && let (_, first_span) = self.provided_arg_tys[first_idx]
+            && let (_, last_span) = self.provided_arg_tys[last_idx]
+            && source_map.is_multiline(first_span.to(last_span))
+            && let Some(fallback_indent) = source_map.indentation_before(first_span)
+        {
+            ArgumentsFormatting::Multiline { fallback_indent, brace_indent }
+        } else {
+            ArgumentsFormatting::SingleLine
+        }
+    }
+
+    fn suggestion_code(&self) -> (Span, String) {
+        let source_map = self.sess().source_map();
+        let suggestion_span = if let Some(args_span) =
+            self.call_metadata.error_span.trim_start(self.call_metadata.full_call_span)
+        {
+            // Span of the braces, e.g. `(a, b, c)`.
+            args_span
+        } else {
+            // The arg span of a function call that wasn't even given braces
+            // like what might happen with delegation reuse.
+            // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`.
+            self.call_metadata.full_call_span.shrink_to_hi()
+        };
+
+        let arguments_formatting = self.arguments_formatting(suggestion_span);
+
+        let mut suggestion = "(".to_owned();
+        let mut needs_comma = false;
+        for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() {
+            if needs_comma {
+                suggestion += ",";
+            }
+            match &arguments_formatting {
+                ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ",
+                ArgumentsFormatting::SingleLine => {}
+                ArgumentsFormatting::Multiline { .. } => suggestion += "\n",
+            }
+            needs_comma = true;
+            let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx
+                && let (_, provided_span) = self.provided_arg_tys[*provided_idx]
+                && let Ok(arg_text) = source_map.span_to_snippet(provided_span)
+            {
+                (Some(provided_span), arg_text)
+            } else {
+                // Propose a placeholder of the correct type
+                let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx];
+                (None, self.ty_to_snippet(expected_ty, expected_idx))
+            };
+            if let ArgumentsFormatting::Multiline { fallback_indent, .. } = &arguments_formatting {
+                let indent = suggestion_span
+                    .and_then(|span| source_map.indentation_before(span))
+                    .unwrap_or_else(|| fallback_indent.clone());
+                suggestion += &indent;
+            }
+            suggestion += &suggestion_text;
+        }
+        if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting {
+            suggestion += ",\n";
+            suggestion += &brace_indent;
+        }
+        suggestion += ")";
+
+        (suggestion_span, suggestion)
+    }
+}
+
+struct ArgMatchingCtxt<'a, 'b, 'tcx> {
+    args_ctxt: ArgsCtxt<'a, 'b, 'tcx>,
+    provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)>,
+}
+
+impl<'a, 'b, 'tcx> Deref for ArgMatchingCtxt<'a, 'b, 'tcx> {
+    type Target = ArgsCtxt<'a, 'b, 'tcx>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.args_ctxt
+    }
+}
+
+impl<'a, 'b, 'tcx> ArgMatchingCtxt<'a, 'b, 'tcx> {
+    fn new(
+        arg: &'a FnCtxt<'b, 'tcx>,
+        compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
+        formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+        provided_args: IndexVec<ProvidedIdx, &'tcx Expr<'tcx>>,
+        c_variadic: bool,
+        err_code: ErrCode,
+        fn_def_id: Option<DefId>,
+        call_span: Span,
+        call_expr: &'tcx Expr<'tcx>,
+        tuple_arguments: TupleArgumentsFlag,
+    ) -> Self {
+        let args_ctxt = ArgsCtxt::new(
+            arg,
+            compatibility_diagonal,
+            formal_and_expected_inputs,
+            provided_args,
+            c_variadic,
+            err_code,
+            fn_def_id,
+            call_span,
+            call_expr,
+            tuple_arguments,
+        );
+        let provided_arg_tys = args_ctxt.provided_arg_tys();
+
+        ArgMatchingCtxt { args_ctxt, provided_arg_tys }
+    }
+
+    fn suggest_confusable(&self, err: &mut Diag<'_>) {
+        let Some(call_name) = self.call_metadata.call_ident else {
+            return;
+        };
+        let Some(callee_ty) = self.callee_ty else {
+            return;
+        };
+        let input_types: Vec<Ty<'_>> = self.provided_arg_tys.iter().map(|(ty, _)| *ty).collect();
+
+        // Check for other methods in the following order
+        //  - methods marked as `rustc_confusables` with the provided arguments
+        //  - methods with the same argument type/count and short levenshtein distance
+        //  - methods marked as `rustc_confusables` (done)
+        //  - methods with short levenshtein distance
+
+        // Look for commonly confusable method names considering arguments.
+        if let Some(_name) = self.confusable_method_name(
+            err,
+            callee_ty.peel_refs(),
+            call_name,
+            Some(input_types.clone()),
+        ) {
+            return;
+        }
+        // Look for method names with short levenshtein distance, considering arguments.
+        if let Some((assoc, fn_sig)) = self.similar_assoc(call_name)
+            && fn_sig.inputs()[1..]
+                .iter()
+                .zip(input_types.iter())
+                .all(|(expected, found)| self.may_coerce(*expected, *found))
+            && fn_sig.inputs()[1..].len() == input_types.len()
+        {
+            let assoc_name = assoc.name();
+            err.span_suggestion_verbose(
+                call_name.span,
+                format!("you might have meant to use `{}`", assoc_name),
+                assoc_name,
+                Applicability::MaybeIncorrect,
+            );
+            return;
+        }
+    }
+
+    /// A "softer" version of the `demand_compatible`, which checks types without persisting them,
+    /// and treats error types differently
+    /// This will allow us to "probe" for other argument orders that would likely have been correct
+    fn check_compatible(
+        &self,
+        provided_idx: ProvidedIdx,
+        expected_idx: ExpectedIdx,
+    ) -> Compatibility<'tcx> {
+        if provided_idx.as_usize() == expected_idx.as_usize() {
+            return self.compatibility_diagonal[provided_idx].clone();
+        }
+
+        let (formal_input_ty, expected_input_ty) = self.formal_and_expected_inputs[expected_idx];
+        // If either is an error type, we defy the usual convention and consider them to *not* be
+        // coercible. This prevents our error message heuristic from trying to pass errors into
+        // every argument.
+        if (formal_input_ty, expected_input_ty).references_error() {
+            return Compatibility::Incompatible(None);
+        }
+
+        let (arg_ty, arg_span) = self.provided_arg_tys[provided_idx];
+
+        let expectation = Expectation::rvalue_hint(self.fn_ctxt, expected_input_ty);
+        let coerced_ty = expectation.only_has_type(self.fn_ctxt).unwrap_or(formal_input_ty);
+        let can_coerce = self.may_coerce(arg_ty, coerced_ty);
+        if !can_coerce {
+            return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts(
+                ty::error::ExpectedFound::new(coerced_ty, arg_ty),
+            )));
+        }
+
+        // Using probe here, since we don't want this subtyping to affect inference.
+        let subtyping_error = self.probe(|_| {
+            self.at(&self.misc(arg_span), self.param_env)
+                .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty)
+                .err()
+        });
+
+        // Same as above: if either the coerce type or the checked type is an error type,
+        // consider them *not* compatible.
+        let references_error = (coerced_ty, arg_ty).references_error();
+        match (references_error, subtyping_error) {
+            (false, None) => Compatibility::Compatible,
+            (_, subtyping_error) => Compatibility::Incompatible(subtyping_error),
+        }
+    }
+
+    fn remove_idx_is_perfect(&self, idx: usize) -> bool {
+        let removed_arg_tys = self
+            .provided_arg_tys
+            .iter()
+            .enumerate()
+            .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) })
+            .collect::<IndexVec<ProvidedIdx, _>>();
+        std::iter::zip(self.formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all(
+            |((expected_ty, _), (provided_ty, _))| {
+                !provided_ty.references_error() && self.may_coerce(*provided_ty, *expected_ty)
+            },
+        )
+    }
+}
+
+struct ArgsCtxt<'a, 'b, 'tcx> {
+    call_ctxt: CallCtxt<'a, 'b, 'tcx>,
+    call_metadata: CallMetadata,
+    args_span: Span,
+}
+
+impl<'a, 'b, 'tcx> Deref for ArgsCtxt<'a, 'b, 'tcx> {
+    type Target = CallCtxt<'a, 'b, 'tcx>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.call_ctxt
+    }
+}
+
+impl<'a, 'b, 'tcx> ArgsCtxt<'a, 'b, 'tcx> {
+    fn new(
+        arg: &'a FnCtxt<'b, 'tcx>,
+        compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
+        formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+        provided_args: IndexVec<ProvidedIdx, &'tcx Expr<'tcx>>,
+        c_variadic: bool,
+        err_code: ErrCode,
+        fn_def_id: Option<DefId>,
+        call_span: Span,
+        call_expr: &'tcx Expr<'tcx>,
+        tuple_arguments: TupleArgumentsFlag,
+    ) -> Self {
+        let call_ctxt: CallCtxt<'_, '_, '_> = CallCtxt::new(
+            arg,
+            compatibility_diagonal,
+            formal_and_expected_inputs,
+            provided_args,
+            c_variadic,
+            err_code,
+            fn_def_id,
+            call_span,
+            call_expr,
+            tuple_arguments,
+        );
+
+        let call_metadata = call_ctxt.call_metadata();
+        let args_span = call_metadata
+            .error_span
+            .trim_start(call_metadata.full_call_span)
+            .unwrap_or(call_metadata.error_span);
+
+        ArgsCtxt { args_span, call_metadata, call_ctxt }
+    }
+
+    /// Get the argument span in the context of the call span so that
+    /// suggestions and labels are (more) correct when an arg is a
+    /// macro invocation.
+    fn normalize_span(&self, span: Span) -> Span {
+        let normalized_span =
+            span.find_ancestor_inside_same_ctxt(self.call_metadata.error_span).unwrap_or(span);
+        // Sometimes macros mess up the spans, so do not normalize the
+        // arg span to equal the error span, because that's less useful
+        // than pointing out the arg expr in the wrong context.
+        if normalized_span.source_equal(self.call_metadata.error_span) {
+            span
+        } else {
+            normalized_span
+        }
+    }
+
+    /// Computes the provided types and spans.
+    fn provided_arg_tys(&self) -> IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> {
+        self.call_ctxt
+            .provided_args
+            .iter()
+            .map(|expr| {
+                let ty = self
+                    .call_ctxt
+                    .fn_ctxt
+                    .typeck_results
+                    .borrow()
+                    .expr_ty_adjusted_opt(*expr)
+                    .unwrap_or_else(|| Ty::new_misc_error(self.call_ctxt.fn_ctxt.tcx));
+                (
+                    self.call_ctxt.fn_ctxt.resolve_vars_if_possible(ty),
+                    self.normalize_span(expr.span),
+                )
+            })
+            .collect()
+    }
+
+    // Obtain another method on `Self` that have similar name.
+    fn similar_assoc(&self, call_name: Ident) -> Option<(ty::AssocItem, ty::FnSig<'tcx>)> {
+        if let Some(callee_ty) = self.call_ctxt.callee_ty
+            && let Ok(Some(assoc)) = self.call_ctxt.fn_ctxt.probe_op(
+                call_name.span,
+                MethodCall,
+                Some(call_name),
+                None,
+                IsSuggestion(true),
+                callee_ty.peel_refs(),
+                self.call_ctxt.callee_expr.unwrap().hir_id,
+                TraitsInScope,
+                |mut ctxt| ctxt.probe_for_similar_candidate(),
+            )
+            && assoc.is_method()
+        {
+            let args =
+                self.call_ctxt.fn_ctxt.infcx.fresh_args_for_item(call_name.span, assoc.def_id);
+            let fn_sig = self
+                .call_ctxt
+                .fn_ctxt
+                .tcx
+                .fn_sig(assoc.def_id)
+                .instantiate(self.call_ctxt.fn_ctxt.tcx, args);
+
+            self.call_ctxt.fn_ctxt.instantiate_binder_with_fresh_vars(
+                call_name.span,
+                BoundRegionConversionTime::FnCall,
+                fn_sig,
+            );
+        }
+        None
+    }
+
+    fn call_is_in_macro(&self) -> bool {
+        self.call_metadata.full_call_span.in_external_macro(self.sess().source_map())
+    }
+}
+
+struct CallMetadata {
+    error_span: Span,
+    call_ident: Option<Ident>,
+    full_call_span: Span,
+    call_name: &'static str,
+    is_method: bool,
+}
+
+struct CallCtxt<'a, 'b, 'tcx> {
+    fn_ctxt: &'a FnCtxt<'b, 'tcx>,
+    compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
+    formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+    provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>,
+    c_variadic: bool,
+    err_code: ErrCode,
+    fn_def_id: Option<DefId>,
+    call_span: Span,
+    call_expr: &'tcx hir::Expr<'tcx>,
+    tuple_arguments: TupleArgumentsFlag,
+    callee_expr: Option<&'tcx Expr<'tcx>>,
+    callee_ty: Option<Ty<'tcx>>,
+}
+
+impl<'a, 'b, 'tcx> Deref for CallCtxt<'a, 'b, 'tcx> {
+    type Target = &'a FnCtxt<'b, 'tcx>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.fn_ctxt
+    }
+}
+
+impl<'a, 'b, 'tcx> CallCtxt<'a, 'b, 'tcx> {
+    fn new(
+        fn_ctxt: &'a FnCtxt<'b, 'tcx>,
+        compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
+        formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+        provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>,
+        c_variadic: bool,
+        err_code: ErrCode,
+        fn_def_id: Option<DefId>,
+        call_span: Span,
+        call_expr: &'tcx hir::Expr<'tcx>,
+        tuple_arguments: TupleArgumentsFlag,
+    ) -> CallCtxt<'a, 'b, 'tcx> {
+        let callee_expr = match &call_expr.peel_blocks().kind {
+            hir::ExprKind::Call(callee, _) => Some(*callee),
+            hir::ExprKind::MethodCall(_, receiver, ..) => {
+                if let Some((DefKind::AssocFn, def_id)) =
+                    fn_ctxt.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
+                    && let Some(assoc) = fn_ctxt.tcx.opt_associated_item(def_id)
+                    && assoc.is_method()
+                {
+                    Some(*receiver)
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        };
+
+        let callee_ty = callee_expr.and_then(|callee_expr| {
+            fn_ctxt.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)
+        });
+
+        CallCtxt {
+            fn_ctxt,
+            compatibility_diagonal,
+            formal_and_expected_inputs,
+            provided_args,
+            c_variadic,
+            err_code,
+            fn_def_id,
+            call_span,
+            call_expr,
+            tuple_arguments,
+            callee_expr,
+            callee_ty,
+        }
+    }
+
+    fn call_metadata(&self) -> CallMetadata {
+        match &self.call_expr.kind {
+            hir::ExprKind::Call(
+                hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
+                _,
+            ) => {
+                if let Res::Def(DefKind::Ctor(of, _), _) =
+                    self.typeck_results.borrow().qpath_res(qpath, *hir_id)
+                {
+                    let name = match of {
+                        CtorOf::Struct => "struct",
+                        CtorOf::Variant => "enum variant",
+                    };
+                    CallMetadata {
+                        error_span: self.call_span,
+                        call_ident: None,
+                        full_call_span: *span,
+                        call_name: name,
+                        is_method: false,
+                    }
+                } else {
+                    CallMetadata {
+                        error_span: self.call_span,
+                        call_ident: None,
+                        full_call_span: *span,
+                        call_name: "function",
+                        is_method: false,
+                    }
+                }
+            }
+            hir::ExprKind::Call(hir::Expr { span, .. }, _) => CallMetadata {
+                error_span: self.call_span,
+                call_ident: None,
+                full_call_span: *span,
+                call_name: "function",
+                is_method: false,
+            },
+            hir::ExprKind::MethodCall(path_segment, _, _, span) => {
+                let ident_span = path_segment.ident.span;
+                let ident_span = if let Some(args) = path_segment.args {
+                    ident_span.with_hi(args.span_ext.hi())
+                } else {
+                    ident_span
+                };
+                CallMetadata {
+                    error_span: *span,
+                    call_ident: Some(path_segment.ident),
+                    full_call_span: ident_span,
+                    call_name: "method",
+                    is_method: true,
+                }
+            }
+            k => span_bug!(self.call_span, "checking argument types on a non-call: `{:?}`", k),
+        }
+    }
+
+    fn mk_trace(
+        &self,
+        span: Span,
+        (formal_ty, expected_ty): (Ty<'tcx>, Ty<'tcx>),
+        provided_ty: Ty<'tcx>,
+    ) -> TypeTrace<'tcx> {
+        let mismatched_ty = if expected_ty == provided_ty {
+            // If expected == provided, then we must have failed to sup
+            // the formal type. Avoid printing out "expected Ty, found Ty"
+            // in that case.
+            formal_ty
+        } else {
+            expected_ty
+        };
+        TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty)
+    }
+
+    fn ty_to_snippet(&self, ty: Ty<'tcx>, expected_idx: ExpectedIdx) -> String {
+        if ty.is_unit() {
+            "()".to_string()
+        } else if ty.is_suggestable(self.tcx, false) {
+            format!("/* {ty} */")
+        } else if let Some(fn_def_id) = self.fn_def_id
+            && self.tcx.def_kind(fn_def_id).is_fn_like()
+            && let self_implicit =
+                matches!(self.call_expr.kind, hir::ExprKind::MethodCall(..)) as usize
+            && let Some(Some(arg)) =
+                self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit)
+            && arg.name != kw::SelfLower
+        {
+            format!("/* {} */", arg.name)
+        } else {
+            "/* value */".to_string()
+        }
+    }
+
+    fn first_incompatible_error(&self) -> Option<(ProvidedIdx, TypeError<'tcx>)> {
+        self.compatibility_diagonal.iter_enumerated().find_map(|(i, c)| {
+            if let Compatibility::Incompatible(Some(terr)) = c { Some((i, *terr)) } else { None }
+        })
+    }
+}
+
+enum SuggestionText {
+    None,
+    Provide(bool),
+    Remove(bool),
+    Swap,
+    Reorder,
+    DidYouMean,
+}
diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs
index 9a66bd0574c..4a252719694 100644
--- a/compiler/rustc_infer/src/traits/engine.rs
+++ b/compiler/rustc_infer/src/traits/engine.rs
@@ -72,12 +72,27 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
             self.register_predicate_obligation(infcx, obligation);
         }
     }
-
+    /// Go over the list of pending obligations and try to evaluate them.
+    ///
+    /// For each result:
+    /// Ok: remove the obligation from the list
+    /// Ambiguous: leave the obligation in the list to be evaluated later
+    /// Err: remove the obligation from the list and return an error
+    ///
+    /// Returns a list of errors from obligations that evaluated to Err.
     #[must_use]
     fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>;
 
     fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>;
 
+    /// Evaluate all pending obligations, return error if they can't be evaluated.
+    ///
+    /// For each result:
+    /// Ok: remove the obligation from the list
+    /// Ambiguous: remove the obligation from the list and return an error
+    /// Err: remove the obligation from the list and return an error
+    ///
+    /// Returns a list of errors from obligations that evaluated to Ambiguous or Err.
     #[must_use]
     fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
         let errors = self.select_where_possible(infcx);
diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs
index 9d21d0d22e3..2cfda7a5fb4 100644
--- a/compiler/rustc_llvm/build.rs
+++ b/compiler/rustc_llvm/build.rs
@@ -16,7 +16,6 @@ const OPTIONAL_COMPONENTS: &[&str] = &[
     "mips",
     "powerpc",
     "systemz",
-    "jsbackend",
     "webassembly",
     "msp430",
     "sparc",
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 3bb1533c2fe..91d11ba317a 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -79,122 +79,6 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) {
   timeTraceProfilerCleanup();
 }
 
-#ifdef LLVM_COMPONENT_X86
-#define SUBTARGET_X86 SUBTARGET(X86)
-#else
-#define SUBTARGET_X86
-#endif
-
-#ifdef LLVM_COMPONENT_ARM
-#define SUBTARGET_ARM SUBTARGET(ARM)
-#else
-#define SUBTARGET_ARM
-#endif
-
-#ifdef LLVM_COMPONENT_AARCH64
-#define SUBTARGET_AARCH64 SUBTARGET(AArch64)
-#else
-#define SUBTARGET_AARCH64
-#endif
-
-#ifdef LLVM_COMPONENT_AVR
-#define SUBTARGET_AVR SUBTARGET(AVR)
-#else
-#define SUBTARGET_AVR
-#endif
-
-#ifdef LLVM_COMPONENT_M68k
-#define SUBTARGET_M68K SUBTARGET(M68k)
-#else
-#define SUBTARGET_M68K
-#endif
-
-#ifdef LLVM_COMPONENT_CSKY
-#define SUBTARGET_CSKY SUBTARGET(CSKY)
-#else
-#define SUBTARGET_CSKY
-#endif
-
-#ifdef LLVM_COMPONENT_MIPS
-#define SUBTARGET_MIPS SUBTARGET(Mips)
-#else
-#define SUBTARGET_MIPS
-#endif
-
-#ifdef LLVM_COMPONENT_POWERPC
-#define SUBTARGET_PPC SUBTARGET(PPC)
-#else
-#define SUBTARGET_PPC
-#endif
-
-#ifdef LLVM_COMPONENT_SYSTEMZ
-#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ)
-#else
-#define SUBTARGET_SYSTEMZ
-#endif
-
-#ifdef LLVM_COMPONENT_MSP430
-#define SUBTARGET_MSP430 SUBTARGET(MSP430)
-#else
-#define SUBTARGET_MSP430
-#endif
-
-#ifdef LLVM_COMPONENT_RISCV
-#define SUBTARGET_RISCV SUBTARGET(RISCV)
-#else
-#define SUBTARGET_RISCV
-#endif
-
-#ifdef LLVM_COMPONENT_SPARC
-#define SUBTARGET_SPARC SUBTARGET(Sparc)
-#else
-#define SUBTARGET_SPARC
-#endif
-
-#ifdef LLVM_COMPONENT_XTENSA
-#define SUBTARGET_XTENSA SUBTARGET(XTENSA)
-#else
-#define SUBTARGET_XTENSA
-#endif
-
-#ifdef LLVM_COMPONENT_HEXAGON
-#define SUBTARGET_HEXAGON SUBTARGET(Hexagon)
-#else
-#define SUBTARGET_HEXAGON
-#endif
-
-#ifdef LLVM_COMPONENT_LOONGARCH
-#define SUBTARGET_LOONGARCH SUBTARGET(LoongArch)
-#else
-#define SUBTARGET_LOONGARCH
-#endif
-
-#define GEN_SUBTARGETS                                                         \
-  SUBTARGET_X86                                                                \
-  SUBTARGET_ARM                                                                \
-  SUBTARGET_AARCH64                                                            \
-  SUBTARGET_AVR                                                                \
-  SUBTARGET_M68K                                                               \
-  SUBTARGET_CSKY                                                               \
-  SUBTARGET_MIPS                                                               \
-  SUBTARGET_PPC                                                                \
-  SUBTARGET_SYSTEMZ                                                            \
-  SUBTARGET_MSP430                                                             \
-  SUBTARGET_SPARC                                                              \
-  SUBTARGET_HEXAGON                                                            \
-  SUBTARGET_XTENSA                                                             \
-  SUBTARGET_RISCV                                                              \
-  SUBTARGET_LOONGARCH
-
-#define SUBTARGET(x)                                                           \
-  namespace llvm {                                                             \
-  extern const SubtargetFeatureKV x##FeatureKV[];                              \
-  extern const SubtargetFeatureKV x##SubTypeKV[];                              \
-  }
-
-GEN_SUBTARGETS
-#undef SUBTARGET
-
 // This struct and various functions are sort of a hack right now, but the
 // problem is that we've got in-memory LLVM modules after we generate and
 // optimize all codegen-units for one compilation in rustc. To be compatible
@@ -340,14 +224,6 @@ static FloatABI::ABIType fromRust(LLVMRustFloatABI RustFloatAbi) {
   report_fatal_error("Bad FloatABI.");
 }
 
-/// getLongestEntryLength - Return the length of the longest entry in the table.
-template <typename KV> static size_t getLongestEntryLength(ArrayRef<KV> Table) {
-  size_t MaxLen = 0;
-  for (auto &I : Table)
-    MaxLen = std::max(MaxLen, std::strlen(I.Key));
-  return MaxLen;
-}
-
 extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
                                         RustStringRef OutStr) {
   ArrayRef<SubtargetSubTypeKV> CPUTable =
diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs
index ed5edeef161..14e94121d1c 100644
--- a/compiler/rustc_llvm/src/lib.rs
+++ b/compiler/rustc_llvm/src/lib.rs
@@ -180,12 +180,6 @@ pub fn initialize_available_targets() {
         LLVMInitializeSystemZAsmParser
     );
     init_target!(
-        llvm_component = "jsbackend",
-        LLVMInitializeJSBackendTargetInfo,
-        LLVMInitializeJSBackendTarget,
-        LLVMInitializeJSBackendTargetMC
-    );
-    init_target!(
         llvm_component = "msp430",
         LLVMInitializeMSP430TargetInfo,
         LLVMInitializeMSP430Target,
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 3b8def67f92..d402ea4b04f 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -327,9 +327,11 @@ pub enum StatementKind<'tcx> {
     /// interesting for optimizations? Do we want to allow such optimizations?
     ///
     /// **Needs clarification**: We currently require that the LHS place not overlap with any place
-    /// read as part of computation of the RHS for some rvalues (generally those not producing
-    /// primitives). This requirement is under discussion in [#68364]. As a part of this discussion,
-    /// it is also unclear in what order the components are evaluated.
+    /// read as part of computation of the RHS for some rvalues. This requirement is under
+    /// discussion in [#68364]. Specifically, overlap is permitted only for assignments of a type
+    /// with `BackendRepr::Scalar | BackendRepr::ScalarPair` where all the scalar fields are
+    /// [`Scalar::Initialized`][rustc_abi::Scalar::Initialized]. As a part of this discussion, it is
+    /// also unclear in what order the components are evaluated.
     ///
     /// [#68364]: https://github.com/rust-lang/rust/issues/68364
     ///
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index 393f458bea2..4c25882daa9 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -1,15 +1,14 @@
 //! Miscellaneous type-system utilities that are too small to deserve their own modules.
 
-use std::assert_matches::assert_matches;
-
 use hir::LangItem;
 use rustc_ast::Mutability;
 use rustc_hir as hir;
 use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
+use rustc_span::sym;
 
 use crate::regions::InferCtxtRegionExt;
-use crate::traits::{self, FulfillmentError, ObligationCause};
+use crate::traits::{self, FulfillmentError, Obligation, ObligationCause};
 
 pub enum CopyImplementationError<'tcx> {
     InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
@@ -98,10 +97,9 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     self_type: Ty<'tcx>,
-    lang_item: LangItem,
     parent_cause: ObligationCause<'tcx>,
 ) -> Result<(), ConstParamTyImplementationError<'tcx>> {
-    assert_matches!(lang_item, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy);
+    let mut need_unstable_feature_bound = false;
 
     let inner_tys: Vec<_> = match *self_type.kind() {
         // Trivially okay as these types are all:
@@ -112,18 +110,14 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
 
         // Handle types gated under `feature(unsized_const_params)`
         // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references
-        ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not)
-            if lang_item == LangItem::UnsizedConstParamTy =>
-        {
+        ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) => {
+            need_unstable_feature_bound = true;
             vec![inner_ty]
         }
-        ty::Str if lang_item == LangItem::UnsizedConstParamTy => {
+        ty::Str => {
+            need_unstable_feature_bound = true;
             vec![Ty::new_slice(tcx, tcx.types.u8)]
         }
-        ty::Str | ty::Slice(..) | ty::Ref(_, _, Mutability::Not) => {
-            return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired);
-        }
-
         ty::Array(inner_ty, _) => vec![inner_ty],
 
         // `str` morally acts like a newtype around `[u8]`
@@ -137,7 +131,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
                 adt,
                 args,
                 parent_cause.clone(),
-                lang_item,
+                LangItem::ConstParamTy,
             )
             .map_err(ConstParamTyImplementationError::InfrigingFields)?;
 
@@ -153,11 +147,25 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
         let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
         let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
 
+        // Make sure impls certain types are gated with #[unstable_feature_bound(unsized_const_params)]
+        if need_unstable_feature_bound {
+            ocx.register_obligation(Obligation::new(
+                tcx,
+                parent_cause.clone(),
+                param_env,
+                ty::ClauseKind::UnstableFeature(sym::unsized_const_params),
+            ));
+
+            if !ocx.select_all_or_error().is_empty() {
+                return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired);
+            }
+        }
+
         ocx.register_bound(
             parent_cause.clone(),
             param_env,
             inner_ty,
-            tcx.require_lang_item(lang_item, parent_cause.span),
+            tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span),
         );
 
         let errors = ocx.select_all_or_error();