]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_typeck/src/check/expr.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / expr.rs
index 427b22dfb125903118421d730a20af56621c103f..b4476d5c59b2bb0f0baee7eb9fb1aabdcfb1b425 100644 (file)
@@ -26,10 +26,10 @@ use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructEx
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::Diagnostic;
-use rustc_errors::EmissionGuarantee;
-use rustc_errors::ErrorGuaranteed;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
+    EmissionGuarantee, ErrorGuaranteed,
+};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -39,10 +39,10 @@ use rustc_hir::{ExprKind, HirId, QPath};
 use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::InferOk;
+use rustc_infer::traits::ObligationCause;
 use rustc_middle::middle::stability;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
-use rustc_middle::ty::error::ExpectedFound;
-use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts};
+use rustc_middle::ty::error::TypeError::FieldMisMatch;
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeFoldable};
 use rustc_session::parse::feature_err;
@@ -51,6 +51,8 @@ use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Pos};
+use rustc_target::spec::abi::Abi::RustIntrinsic;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{self, ObligationCauseCode};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -63,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
-        extend_err: impl Fn(&mut Diagnostic),
+        extend_err: impl FnMut(&mut Diagnostic),
     ) -> Ty<'tcx> {
         self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err)
     }
@@ -72,7 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &'tcx hir::Expr<'tcx>,
         expected: Expectation<'tcx>,
-        extend_err: impl Fn(&mut Diagnostic),
+        mut extend_err: impl FnMut(&mut Diagnostic),
     ) -> Ty<'tcx> {
         let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
         let mut ty = self.check_expr_with_expectation(expr, expected);
@@ -293,7 +295,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_lang_item_path(lang_item, expr, hir_id)
             }
             ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]),
-            ExprKind::InlineAsm(asm) => self.check_expr_asm(asm),
+            ExprKind::InlineAsm(asm) => {
+                // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars).
+                self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id));
+                self.check_expr_asm(asm)
+            }
             ExprKind::Break(destination, ref expr_opt) => {
                 self.check_expr_break(destination, expr_opt.as_deref(), expr)
             }
@@ -313,8 +319,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ExprKind::Match(discrim, arms, match_src) => {
                 self.check_match(expr, &discrim, arms, expected, match_src)
             }
-            ExprKind::Closure(capture, decl, body_id, _, gen) => {
-                self.check_expr_closure(expr, capture, &decl, body_id, gen, expected)
+            ExprKind::Closure { capture_clause, fn_decl, body, movability, .. } => {
+                self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected)
             }
             ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected),
             ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected),
@@ -529,8 +535,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
         };
 
-        if let ty::FnDef(..) = ty.kind() {
+        if let ty::FnDef(did, ..) = *ty.kind() {
             let fn_sig = ty.fn_sig(tcx);
+            if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute {
+                let from = fn_sig.inputs().skip_binder()[0];
+                let to = fn_sig.output().skip_binder();
+                // We defer the transmute to the end of typeck, once all inference vars have
+                // been resolved or we errored. This is important as we can only check transmute
+                // on concrete types, but the output type may not be known yet (it would only
+                // be known if explicitly specified via turbofish).
+                self.deferred_transmute_checks.borrow_mut().push((from, to, expr.span));
+            }
             if !tcx.features().unsized_fn_params {
                 // We want to remove some Sized bounds from std functions,
                 // but don't want to expose the removal to stable Rust.
@@ -546,13 +561,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // placeholder lifetimes with probing, we just replace higher lifetimes
                     // with fresh vars.
                     let span = args.get(i).map(|a| a.span).unwrap_or(expr.span);
-                    let input = self
-                        .replace_bound_vars_with_fresh_vars(
-                            span,
-                            infer::LateBoundRegionConversionTime::FnCall,
-                            fn_sig.input(i),
-                        )
-                        .0;
+                    let input = self.replace_bound_vars_with_fresh_vars(
+                        span,
+                        infer::LateBoundRegionConversionTime::FnCall,
+                        fn_sig.input(i),
+                    );
                     self.require_type_is_sized_deferred(
                         input,
                         span,
@@ -566,13 +579,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Also, as we just want to check sizedness, instead of introducing
             // placeholder lifetimes with probing, we just replace higher lifetimes
             // with fresh vars.
-            let output = self
-                .replace_bound_vars_with_fresh_vars(
-                    expr.span,
-                    infer::LateBoundRegionConversionTime::FnCall,
-                    fn_sig.output(),
-                )
-                .0;
+            let output = self.replace_bound_vars_with_fresh_vars(
+                expr.span,
+                infer::LateBoundRegionConversionTime::FnCall,
+                fn_sig.output(),
+            );
             self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType);
         }
 
@@ -829,6 +840,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return_expr,
             return_expr_ty,
         );
+
+        if self.return_type_has_opaque {
+            // Point any obligations that were registered due to opaque type
+            // inference at the return expression.
+            self.select_obligations_where_possible(false, |errors| {
+                self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty);
+            });
+        }
+    }
+
+    fn point_at_return_for_opaque_ty_error(
+        &self,
+        errors: &mut Vec<traits::FulfillmentError<'tcx>>,
+        span: Span,
+        return_expr_ty: Ty<'tcx>,
+    ) {
+        // Don't point at the whole block if it's empty
+        if span == self.tcx.hir().span(self.body_id) {
+            return;
+        }
+        for err in errors {
+            let cause = &mut err.obligation.cause;
+            if let ObligationCauseCode::OpaqueReturnType(None) = cause.code() {
+                let new_cause = ObligationCause::new(
+                    cause.span,
+                    cause.body_id,
+                    ObligationCauseCode::OpaqueReturnType(Some((return_expr_ty, span))),
+                );
+                *cause = new_cause;
+            }
+        }
     }
 
     pub(crate) fn check_lhs_assignable(
@@ -836,6 +878,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs: &'tcx hir::Expr<'tcx>,
         err_code: &'static str,
         op_span: Span,
+        adjust_err: impl FnOnce(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>),
     ) {
         if lhs.is_syntactic_place_expr() {
             return;
@@ -853,11 +896,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.span_suggestion_verbose(
                 expr.span.shrink_to_lo(),
                 "you might have meant to use pattern destructuring",
-                "let ".to_string(),
+                "let ",
                 Applicability::MachineApplicable,
             );
         });
 
+        adjust_err(&mut err);
+
         err.emit();
     }
 
@@ -1017,7 +1062,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 (Applicability::MaybeIncorrect, false)
             };
-            if !lhs.is_syntactic_place_expr() && !matches!(lhs.kind, hir::ExprKind::Lit(_)) {
+            if !lhs.is_syntactic_place_expr()
+                && lhs.is_approximately_pattern()
+                && !matches!(lhs.kind, hir::ExprKind::Lit(_))
+            {
                 // Do not suggest `if let x = y` as `==` is way more likely to be the intention.
                 let hir = self.tcx.hir();
                 if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
@@ -1026,7 +1074,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     err.span_suggestion_verbose(
                         expr.span.shrink_to_lo(),
                         "you might have meant to use pattern matching",
-                        "let ".to_string(),
+                        "let ",
                         applicability,
                     );
                 };
@@ -1035,7 +1083,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 err.span_suggestion_verbose(
                     span,
                     "you might have meant to compare for equality",
-                    "==".to_string(),
+                    "==",
                     applicability,
                 );
             }
@@ -1050,10 +1098,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return self.tcx.ty_error();
         }
 
-        self.check_lhs_assignable(lhs, "E0070", span);
-
         let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
-        let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs));
+
+        let suggest_deref_binop = |err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+                                   rhs_ty: Ty<'tcx>| {
+            if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
+                // Can only assign if the type is sized, so if `DerefMut` yields a type that is
+                // unsized, do not suggest dereferencing it.
+                let lhs_deref_ty_is_sized = self
+                    .infcx
+                    .type_implements_trait(
+                        self.tcx.lang_items().sized_trait().unwrap(),
+                        lhs_deref_ty,
+                        ty::List::empty(),
+                        self.param_env,
+                    )
+                    .may_apply();
+                if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) {
+                    err.span_suggestion_verbose(
+                        lhs.span.shrink_to_lo(),
+                        "consider dereferencing here to assign to the mutably borrowed value",
+                        "*",
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+        };
+
+        self.check_lhs_assignable(lhs, "E0070", span, |err| {
+            let rhs_ty = self.check_expr(&rhs);
+            suggest_deref_binop(err, rhs_ty);
+        });
+
+        // This is (basically) inlined `check_expr_coercable_to_type`, but we want
+        // to suggest an additional fixup here in `suggest_deref_binop`.
+        let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty);
+        if let (_, Some(mut diag)) =
+            self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No)
+        {
+            suggest_deref_binop(&mut diag, rhs_ty);
+            diag.emit();
+        }
 
         self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
 
@@ -1064,7 +1149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> {
+    pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> {
         // for let statements, this is done in check_stmt
         let init = let_expr.init;
         self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression");
@@ -1416,18 +1501,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         let tcx = self.tcx;
 
-        let adt_ty_hint = self
-            .expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty])
-            .get(0)
-            .cloned()
-            .unwrap_or(adt_ty);
+        let expected_inputs =
+            self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]);
+        let adt_ty_hint = if let Some(expected_inputs) = expected_inputs {
+            expected_inputs.get(0).cloned().unwrap_or(adt_ty)
+        } else {
+            adt_ty
+        };
         // re-link the regions that EIfEO can erase.
         self.demand_eqtype(span, adt_ty_hint, adt_ty);
 
-        let (substs, adt_kind, kind_name) = match adt_ty.kind() {
-            ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()),
-            _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"),
+        let ty::Adt(adt, substs) = adt_ty.kind() else {
+            span_bug!(span, "non-ADT passed to check_expr_struct_fields");
         };
+        let adt_kind = adt.adt_kind();
 
         let mut remaining_fields = variant
             .fields
@@ -1465,7 +1552,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     });
                 } else {
                     self.report_unknown_field(
-                        adt_ty, variant, field, ast_fields, kind_name, expr_span,
+                        adt_ty,
+                        variant,
+                        field,
+                        ast_fields,
+                        adt.variant_descr(),
+                        expr_span,
                     );
                 }
 
@@ -1478,7 +1570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // Make sure the programmer specified correct number of fields.
-        if kind_name == "union" {
+        if adt_kind == AdtKind::Union {
             if ast_fields.len() != 1 {
                 struct_span_err!(
                     tcx.sess,
@@ -1501,67 +1593,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // FIXME: We are currently creating two branches here in order to maintain
             // consistency. But they should be merged as much as possible.
             let fru_tys = if self.tcx.features().type_changing_struct_update {
-                let base_ty = self.check_expr(base_expr);
-                match adt_ty.kind() {
-                    ty::Adt(adt, substs) if adt.is_struct() => {
-                        match base_ty.kind() {
-                            ty::Adt(base_adt, base_subs) if adt == base_adt => {
-                                variant
-                                    .fields
-                                    .iter()
-                                    .map(|f| {
-                                        let fru_ty = self.normalize_associated_types_in(
-                                            expr_span,
-                                            self.field_ty(base_expr.span, f, base_subs),
-                                        );
-                                        let ident = self
-                                            .tcx
-                                            .adjust_ident(f.ident(self.tcx), variant.def_id);
-                                        if let Some(_) = remaining_fields.remove(&ident) {
-                                            let target_ty =
-                                                self.field_ty(base_expr.span, f, substs);
-                                            let cause = self.misc(base_expr.span);
-                                            match self
-                                                .at(&cause, self.param_env)
-                                                .sup(target_ty, fru_ty)
-                                            {
-                                                Ok(InferOk { obligations, value: () }) => {
-                                                    self.register_predicates(obligations)
-                                                }
-                                                // FIXME: Need better diagnostics for `FieldMisMatch` error
-                                                Err(_) => {
-                                                    self.report_mismatched_types(
-                                                        &cause,
-                                                        target_ty,
-                                                        fru_ty,
-                                                        FieldMisMatch(variant.name, ident.name),
-                                                    )
-                                                    .emit();
-                                                }
-                                            }
-                                        }
-                                        fru_ty
-                                    })
-                                    .collect()
-                            }
-                            _ => {
-                                self.report_mismatched_types(
-                                    &self.misc(base_expr.span),
-                                    adt_ty,
-                                    base_ty,
-                                    Sorts(ExpectedFound::new(true, adt_ty, base_ty)),
-                                )
-                                .emit();
-                                return;
+                if adt.is_struct() {
+                    // Make some fresh substitutions for our ADT type.
+                    let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did());
+                    // We do subtyping on the FRU fields first, so we can
+                    // learn exactly what types we expect the base expr
+                    // needs constrained to be compatible with the struct
+                    // type we expect from the expectation value.
+                    let fru_tys = variant
+                        .fields
+                        .iter()
+                        .map(|f| {
+                            let fru_ty = self.normalize_associated_types_in(
+                                expr_span,
+                                self.field_ty(base_expr.span, f, fresh_substs),
+                            );
+                            let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
+                            if let Some(_) = remaining_fields.remove(&ident) {
+                                let target_ty = self.field_ty(base_expr.span, f, substs);
+                                let cause = self.misc(base_expr.span);
+                                match self.at(&cause, self.param_env).sup(target_ty, fru_ty) {
+                                    Ok(InferOk { obligations, value: () }) => {
+                                        self.register_predicates(obligations)
+                                    }
+                                    Err(_) => {
+                                        // This should never happen, since we're just subtyping the
+                                        // remaining_fields, but it's fine to emit this, I guess.
+                                        self.report_mismatched_types(
+                                            &cause,
+                                            target_ty,
+                                            fru_ty,
+                                            FieldMisMatch(variant.name, ident.name),
+                                        )
+                                        .emit();
+                                    }
+                                }
                             }
-                        }
-                    }
-                    _ => {
-                        self.tcx
-                            .sess
-                            .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
-                        return;
-                    }
+                            self.resolve_vars_if_possible(fru_ty)
+                        })
+                        .collect();
+                    // The use of fresh substs that we have subtyped against
+                    // our base ADT type's fields allows us to guide inference
+                    // along so that, e.g.
+                    // ```
+                    // MyStruct<'a, F1, F2, const C: usize> {
+                    //     f: F1,
+                    //     // Other fields that reference `'a`, `F2`, and `C`
+                    // }
+                    //
+                    // let x = MyStruct {
+                    //    f: 1usize,
+                    //    ..other_struct
+                    // };
+                    // ```
+                    // will have the `other_struct` expression constrained to
+                    // `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
+                    // This is important to allow coercions to happen in
+                    // `other_struct` itself. See `coerce-in-base-expr.rs`.
+                    let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
+                    self.check_expr_has_type_or_error(
+                        base_expr,
+                        self.resolve_vars_if_possible(fresh_base_ty),
+                        |_| {},
+                    );
+                    fru_tys
+                } else {
+                    // Check the base_expr, regardless of a bad expected adt_ty, so we can get
+                    // type errors on that expression, too.
+                    self.check_expr(base_expr);
+                    self.tcx
+                        .sess
+                        .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
+                    return;
                 }
             } else {
                 self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {
@@ -1597,13 +1700,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             };
             self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
-        } else if kind_name != "union" && !remaining_fields.is_empty() {
-            let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
-                !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
-            });
+        } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
+            debug!(?remaining_fields);
+            let private_fields: Vec<&ty::FieldDef> = variant
+                .fields
+                .iter()
+                .filter(|field| {
+                    !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
+                })
+                .collect();
 
-            if inaccessible_remaining_fields {
-                self.report_inaccessible_fields(adt_ty, span);
+            if !private_fields.is_empty() {
+                self.report_private_fields(adt_ty, span, private_fields, ast_fields);
             } else {
                 self.report_missing_fields(
                     adt_ty,
@@ -1716,7 +1824,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.span_suggestion(
                 range_start.span.shrink_to_hi(),
                 &format!("to set the remaining fields{instead}, separate the last named field with a comma"),
-                ",".to_string(),
+                ",",
                 Applicability::MaybeIncorrect,
             );
         }
@@ -1727,7 +1835,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Report an error for a struct field expression when there are invisible fields.
     ///
     /// ```text
-    /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
+    /// error: cannot construct `Foo` with struct literal syntax due to private fields
     ///  --> src/main.rs:8:5
     ///   |
     /// 8 |     foo::Foo {};
@@ -1735,13 +1843,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// error: aborting due to previous error
     /// ```
-    fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
-        self.tcx.sess.span_err(
+    fn report_private_fields(
+        &self,
+        adt_ty: Ty<'tcx>,
+        span: Span,
+        private_fields: Vec<&ty::FieldDef>,
+        used_fields: &'tcx [hir::ExprField<'tcx>],
+    ) {
+        let mut err = self.tcx.sess.struct_span_err(
             span,
             &format!(
-                "cannot construct `{adt_ty}` with struct literal syntax due to inaccessible fields",
+                "cannot construct `{adt_ty}` with struct literal syntax due to private fields",
             ),
         );
+        let (used_private_fields, remaining_private_fields): (
+            Vec<(Symbol, Span, bool)>,
+            Vec<(Symbol, Span, bool)>,
+        ) = private_fields
+            .iter()
+            .map(|field| {
+                match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
+                    Some(used_field) => (field.name, used_field.span, true),
+                    None => (field.name, self.tcx.def_span(field.did), false),
+                }
+            })
+            .partition(|field| field.2);
+        err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field");
+        if !remaining_private_fields.is_empty() {
+            let remaining_private_fields_len = remaining_private_fields.len();
+            let names = match &remaining_private_fields
+                .iter()
+                .map(|(name, _, _)| name.to_string())
+                .collect::<Vec<_>>()[..]
+            {
+                _ if remaining_private_fields_len > 6 => String::new(),
+                [name] => format!("`{name}` "),
+                [names @ .., last] => {
+                    let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
+                    format!("{} and `{last}` ", names.join(", "))
+                }
+                [] => unreachable!(),
+            };
+            err.note(format!(
+                "... and other private field{s} {names}that {were} not provided",
+                s = pluralize!(remaining_private_fields_len),
+                were = pluralize!("was", remaining_private_fields_len),
+            ));
+        }
+        err.emit();
     }
 
     fn report_unknown_field(
@@ -1838,7 +1987,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     err.span_suggestion(
                         field.ident.span,
                         "a field with a similar name exists",
-                        field_name.to_string(),
+                        field_name,
                         Applicability::MaybeIncorrect,
                     );
                 } else {
@@ -2084,7 +2233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     err.span_suggestion_verbose(
                         base.span.shrink_to_hi(),
                         "consider `await`ing on the `Future` and access the field of its `Output`",
-                        ".await".to_string(),
+                        ".await",
                         Applicability::MaybeIncorrect,
                     );
                 }
@@ -2284,7 +2433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.span_suggestion(
                 field.span,
                 "a field with a similar name exists",
-                suggested_field_name.to_string(),
+                suggested_field_name,
                 Applicability::MaybeIncorrect,
             );
         } else {
@@ -2387,11 +2536,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err
     }
 
-    crate fn get_field_candidates(
+    pub(crate) fn get_field_candidates(
         &self,
         span: Span,
         base_t: Ty<'tcx>,
-    ) -> Option<(&Vec<ty::FieldDef>, SubstsRef<'tcx>)> {
+    ) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> {
         debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
 
         for (base_t, _) in self.autoderef(span, base_t) {
@@ -2412,7 +2561,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// This method is called after we have encountered a missing field error to recursively
     /// search for the field
-    crate fn check_for_nested_field_satisfying(
+    pub(crate) fn check_for_nested_field_satisfying(
         &self,
         span: Span,
         matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
@@ -2598,10 +2747,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self.check_expr_asm_operand(out_expr, false);
                     }
                 }
-                hir::InlineAsmOperand::Const { anon_const }
-                | hir::InlineAsmOperand::SymFn { anon_const } => {
-                    self.to_const(anon_const);
-                }
+                // `AnonConst`s have their own body and is type-checked separately.
+                // As they don't flow into the type system we don't need them to
+                // be well-formed.
+                hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {}
                 hir::InlineAsmOperand::SymStatic { .. } => {}
             }
         }