From 9835e2ae736235810b4ea1c162ca5e65c547e770 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:50 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_hir_typeck/src/_match.rs | 4 +- compiler/rustc_hir_typeck/src/callee.rs | 4 +- compiler/rustc_hir_typeck/src/cast.rs | 14 +- compiler/rustc_hir_typeck/src/check.rs | 23 +- compiler/rustc_hir_typeck/src/closure.rs | 8 +- compiler/rustc_hir_typeck/src/coercion.rs | 27 +- compiler/rustc_hir_typeck/src/demand.rs | 267 ++++++++++--------- compiler/rustc_hir_typeck/src/errors.rs | 105 +++++++- compiler/rustc_hir_typeck/src/expr.rs | 249 +++++++++++++++-- compiler/rustc_hir_typeck/src/expr_use_visitor.rs | 20 +- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 105 ++++++-- .../src/fn_ctxt/adjust_fulfillment_errors.rs | 43 ++- .../rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 86 +++--- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 29 +- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 74 +++--- .../generator_interior/drop_ranges/cfg_build.rs | 4 +- .../drop_ranges/cfg_propagate.rs | 2 +- .../src/generator_interior/drop_ranges/mod.rs | 7 +- .../drop_ranges/record_consumed_borrow.rs | 4 +- .../rustc_hir_typeck/src/generator_interior/mod.rs | 10 +- compiler/rustc_hir_typeck/src/inherited.rs | 5 +- compiler/rustc_hir_typeck/src/intrinsicck.rs | 16 +- compiler/rustc_hir_typeck/src/lib.rs | 56 +--- .../rustc_hir_typeck/src/mem_categorization.rs | 3 +- compiler/rustc_hir_typeck/src/method/confirm.rs | 27 +- compiler/rustc_hir_typeck/src/method/mod.rs | 12 +- .../rustc_hir_typeck/src/method/prelude2021.rs | 4 +- compiler/rustc_hir_typeck/src/method/probe.rs | 17 +- compiler/rustc_hir_typeck/src/method/suggest.rs | 296 ++++++++++++--------- compiler/rustc_hir_typeck/src/op.rs | 62 +++-- compiler/rustc_hir_typeck/src/pat.rs | 36 ++- compiler/rustc_hir_typeck/src/place_op.rs | 50 ++-- compiler/rustc_hir_typeck/src/upvar.rs | 48 ++-- compiler/rustc_hir_typeck/src/writeback.rs | 113 +++++--- 35 files changed, 1122 insertions(+), 710 deletions(-) (limited to 'compiler/rustc_hir_typeck/src') diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 6c2ce6272..7d2f7e876 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -271,7 +271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, &mut |err| { if let Some((span, msg)) = &ret_reason { - err.span_label(*span, msg); + err.span_label(*span, msg.clone()); } else if let ExprKind::Block(block, _) = &then_expr.kind && let Some(expr) = &block.expr { @@ -530,7 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for ty in [first_ty, second_ty] { for (pred, _) in self .tcx - .bound_explicit_item_bounds(rpit_def_id) + .explicit_item_bounds(rpit_def_id) .subst_iter_copied(self.tcx, substs) { let pred = pred.kind().rebind(match pred.kind().skip_binder() { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 5235710a2..655ab94eb 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -397,7 +397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .sess .struct_span_err( callee_expr.span, - &format!("evaluate({:?}) = {:?}", predicate, result), + format!("evaluate({:?}) = {:?}", predicate, result), ) .span_label(predicate_span, "predicate") .emit(); @@ -630,7 +630,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some((removal_span, kind, path)) = &unit_variant { err.span_suggestion_verbose( *removal_span, - &format!( + format!( "`{path}` is a unit {kind}, and does not take parentheses to be constructed", ), "", diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 1481c038c..98c683f02 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -146,7 +146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let reported = self .tcx .sess - .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t)); + .delay_span_bug(span, format!("`{:?}` should be sized but is not?", t)); return Err(reported); } }) @@ -270,7 +270,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx, ); if self.cast_ty.is_integral() { - err.help(&format!( + err.help(format!( "cast through {} first", match e { CastError::NeedViaPtr => "a raw pointer", @@ -292,7 +292,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.cast_ty, fcx, ) - .help(&format!( + .help(format!( "cast through {} first", match e { CastError::NeedViaInt => "an integer", @@ -465,7 +465,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { .sess .source_map() .span_to_snippet(self.expr_span) - .map_or(false, |snip| snip.starts_with('(')); + .is_ok_and(|snip| snip.starts_with('(')); // Very crude check to see whether the expression must be wrapped // in parentheses for the suggestion to work (issue #89497). @@ -651,13 +651,13 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } Err(_) => { - let msg = &format!("did you mean `&{}{}`?", mtstr, tstr); + let msg = format!("did you mean `&{}{}`?", mtstr, tstr); err.span_help(self.cast_span, msg); } } } else { let msg = - &format!("consider using an implicit coercion to `&{mtstr}{tstr}` instead"); + format!("consider using an implicit coercion to `&{mtstr}{tstr}` instead"); err.span_help(self.span, msg); } } @@ -674,7 +674,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { Err(_) => { err.span_help( self.cast_span, - &format!("you might have meant `Box<{tstr}>`"), + format!("you might have meant `Box<{tstr}>`"), ); } } diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index bf8259ff7..bfabd44bb 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -32,6 +32,7 @@ pub(super) fn check_fn<'a, 'tcx>( fn_def_id: LocalDefId, body: &'tcx hir::Body<'tcx>, can_be_generator: Option, + params_can_be_unsized: bool, ) -> Option> { let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id); @@ -94,7 +95,7 @@ pub(super) fn check_fn<'a, 'tcx>( // The check for a non-trivial pattern is a hack to avoid duplicate warnings // for simple cases like `fn foo(x: Trait)`, // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params { + if param.pat.simple_ident().is_none() && !params_can_be_unsized { fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); } @@ -103,24 +104,8 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - if let ty::Dynamic(_, _, ty::Dyn) = declared_ret_ty.kind() { - // FIXME: We need to verify that the return type is `Sized` after the return expression has - // been evaluated so that we have types available for all the nodes being returned, but that - // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this - // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, - // while keeping the current ordering we will ignore the tail expression's type because we - // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` - // because we will trigger "unreachable expression" lints unconditionally. - // Because of all of this, we perform a crude check to know whether the simplest `!Sized` - // case that a newcomer might make, returning a bare trait, and in that case we populate - // the tail expression's type so that the suggestion will be correct, but ignore all other - // possible cases. - fcx.check_expr(&body.value); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - } else { - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - fcx.check_return_expr(&body.value, false); - } + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + fcx.check_return_expr(&body.value, false); // We insert the deferred_generator_interiors entry after visiting the body. // This ensures that all nested generators appear before the entry of this generator. diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 8c2495e1d..9659a0ec1 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -89,6 +89,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_def_id, body, closure.movability, + // Closure "rust-call" ABI doesn't support unsized params + false, ); let parent_substs = InternalSubsts::identity_for_item( @@ -172,7 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self .deduce_closure_signature_from_predicates( expected_ty, - self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs), + self.tcx.explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs), ), ty::Dynamic(ref object_type, ..) => { let sig = object_type.projection_bounds().find_map(|pb| { @@ -713,13 +715,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self .tcx - .bound_explicit_item_bounds(def_id) + .explicit_item_bounds(def_id) .subst_iter_copied(self.tcx, substs) .find_map(|(p, s)| get_future_output(p, s))?, ty::Error(_) => return None, ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => self .tcx - .bound_explicit_item_bounds(proj.def_id) + .explicit_item_bounds(proj.def_id) .subst_iter_copied(self.tcx, proj.substs) .find_map(|(p, s)| get_future_output(p, s))?, _ => span_bug!( diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 8fa3bcd68..08c4082e8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -601,7 +601,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.tcx, cause, self.fcx.param_env, - self.tcx.mk_trait_ref(coerce_unsized_did, [coerce_source, coerce_target]) + ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]) )]; let mut has_unsized_tuple_coercion = false; @@ -707,9 +707,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { &self.tcx.sess.parse_sess, sym::trait_upcasting, self.cause.span, - &format!("cannot cast `{sub}` to `{sup}`, trait upcasting coercion is experimental"), + format!("cannot cast `{sub}` to `{sup}`, trait upcasting coercion is experimental"), ); - err.note(&format!("required when coercing `{source}` into `{target}`")); + err.note(format!("required when coercing `{source}` into `{target}`")); err.emit(); } @@ -764,8 +764,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.tcx, self.cause.clone(), self.param_env, - ty::Binder::dummy( - self.tcx.at(self.cause.span).mk_trait_ref(hir::LangItem::PointerLike, [a]), + ty::TraitRef::from_lang_item( + self.tcx, + hir::LangItem::PointerLike, + self.cause.span, + [a], ), )); @@ -976,7 +979,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Attempt to coerce an expression to a type, and return the /// adjusted type of the expression, if successful. /// Adjustments are only recorded if the coercion succeeded. - /// The expressions *must not* have any pre-existing adjustments. + /// The expressions *must not* have any preexisting adjustments. pub fn try_coerce( &self, expr: &hir::Expr<'_>, @@ -1340,7 +1343,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } /// As an optimization, you can create a `CoerceMany` with a - /// pre-existing slice of expressions. In this case, you are + /// preexisting slice of expressions. In this case, you are /// expected to pass each element in the slice to `coerce(...)` in /// order. This is used with arrays in particular to avoid /// needlessly cloning the slice. @@ -1657,7 +1660,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { "the function expects a value to always be returned, but loops might run zero times", ); if MAXITER < ret_exprs.len() { - err.note(&format!( + err.note(format!( "if the loop doesn't execute, {} other values would never get returned", ret_exprs.len() - MAXITER )); @@ -1767,7 +1770,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { { err.span_note( sp, - &format!( + format!( "return type inferred to be `{}` here", expected ), @@ -1811,7 +1814,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { .span_to_snippet(return_sp) .unwrap_or_else(|_| "dyn Trait".to_string()); let mut snippet_iter = snippet.split_whitespace(); - let has_impl = snippet_iter.next().map_or(false, |s| s == "impl"); + let has_impl = snippet_iter.next().is_some_and(|s| s == "impl"); // Only suggest `Box` if `Trait` in `impl Trait` is object safe. let mut is_object_safe = false; if let hir::FnRetTy::Return(ty) = fn_output @@ -1831,7 +1834,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { bound .trait_ref() .and_then(|t| t.trait_def_id()) - .map_or(false, |def_id| { + .is_some_and(|def_id| { fcx.tcx.check_is_object_safe(def_id) }) }) @@ -1864,7 +1867,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { Applicability::MaybeIncorrect, ); } else { - err.help(&format!( + err.help(format!( "if the trait `{}` were object safe, you could return a boxed trait object", &snippet[5..] )); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 13442c316..b50630e63 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -17,7 +17,6 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeFoldable}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span, DUMMY_SP}; -use rustc_target::abi::FieldIdx; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::ObligationCause; @@ -52,7 +51,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty) || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) || self.suggest_no_capture_closure(err, expected, expr_ty) - || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty) + || self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) @@ -87,9 +86,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error); self.note_type_is_not_clone(err, expected, expr_ty, expr); self.note_internal_mutation_in_method(err, expr, Some(expected), expr_ty); - self.check_for_range_as_method_call(err, expr, expr_ty, expected); - self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected); - self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty); + self.suggest_method_call_on_range_literal(err, expr, expr_ty, expected); + self.suggest_return_binding_for_missing_tail_expr(err, expr, expr_ty, expected); + self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty); } /// Requires that the two types unify, and prints an error message if @@ -544,13 +543,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We are pointing at the binding's type or initializer value, but it's pattern // is in a different line, so we point at both. err.span_label(secondary_span, "expected due to the type of this binding"); - err.span_label(primary_span, &format!("expected due to this{post_message}")); + err.span_label(primary_span, format!("expected due to this{post_message}")); } else if post_message == "" { // We are pointing at either the assignment lhs or the binding def pattern. err.span_label(primary_span, "expected due to the type of this binding"); } else { // We are pointing at the binding's type or initializer value. - err.span_label(primary_span, &format!("expected due to this{post_message}")); + err.span_label(primary_span, format!("expected due to this{post_message}")); } if !lhs.is_syntactic_place_expr() { @@ -567,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) if rhs.hir_id == expr.hir_id && self.typeck_results.borrow().expr_ty_adjusted_opt(lhs) == Some(expected) => { - err.span_label(lhs.span, &format!("expected because this is `{expected}`")); + err.span_label(lhs.span, format!("expected because this is `{expected}`")); } _ => {} } @@ -705,19 +704,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); err.span_note( path_span, - &format!( + format!( "the `{}` call is resolved to the method in `{container}`, shadowing {tail}", path.ident, ), ); if suggestions.len() > other_methods_in_scope.len() { - err.note(&format!( + err.note(format!( "additionally, there are {} other available methods that aren't in scope", suggestions.len() - other_methods_in_scope.len() )); } err.multipart_suggestions( - &format!( + format!( "you might have meant to call {}; you can use the fully-qualified path to call {} \ explicitly", if suggestions.len() == 1 { @@ -875,7 +874,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant.fields.len() == 1 }) .filter_map(|variant| { - let sole_field = &variant.fields[FieldIdx::from_u32(0)]; + let sole_field = &variant.single_field(); let field_is_local = sole_field.did.is_local(); let field_is_accessible = @@ -942,7 +941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [(variant, ctor_kind, field_name, note)] => { // Just a single matching variant. err.multipart_suggestion_verbose( - &format!( + format!( "try wrapping the expression in `{variant}`{note}", note = note.as_deref().unwrap_or("") ), @@ -954,7 +953,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { // More than one matching variant. err.multipart_suggestions( - &format!( + format!( "try wrapping the expression in a variant of `{}`", self.tcx.def_path_str(expected_adt.did()) ), @@ -1088,7 +1087,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ```ignore (illustrative) /// opt.map(|param| { takes_ref(param) }); /// ``` - fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> { + fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> { let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind else { return None; }; @@ -1134,12 +1133,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => false, }; - match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) { - (true, Ok(src)) => { - let suggestion = format!("as_ref().{}", src); - Some((method_path.ident.span, "consider using `as_ref` instead", suggestion)) - } - _ => None, + if is_as_ref_able { + Some(( + vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())], + "consider using `as_ref` instead", + )) + } else { + None } } @@ -1218,14 +1218,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// In addition of this check, it also checks between references mutability state. If the /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with /// `&mut`!". - pub fn check_ref( + pub fn suggest_deref_or_ref( &self, expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, ) -> Option<( - Span, - String, + Vec<(Span, String)>, String, Applicability, bool, /* verbose */ @@ -1255,30 +1254,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Ok(src) = sm.span_to_snippet(sp) && replace_prefix(&src, "b\"", "\"").is_some() { - let pos = sp.lo() + BytePos(1); - return Some(( - sp.with_hi(pos), - "consider removing the leading `b`".to_string(), - String::new(), - Applicability::MachineApplicable, - true, - false, - )); - } - } + let pos = sp.lo() + BytePos(1); + return Some(( + vec![(sp.with_hi(pos), String::new())], + "consider removing the leading `b`".to_string(), + Applicability::MachineApplicable, + true, + false, + )); + } + } (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind && let Ok(src) = sm.span_to_snippet(sp) && replace_prefix(&src, "\"", "b\"").is_some() { - return Some(( - sp.shrink_to_lo(), - "consider adding a leading `b`".to_string(), - "b".to_string(), - Applicability::MachineApplicable, - true, - false, - )); + return Some(( + vec![(sp.shrink_to_lo(), "b".to_string())], + "consider adding a leading `b`".to_string(), + Applicability::MachineApplicable, + true, + false, + )); } } _ => {} @@ -1321,66 +1318,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind - && let Some(1) = self.deref_steps(expected, checked_ty) { + && let Some(1) = self.deref_steps(expected, checked_ty) + { // We have `*&T`, check if what was expected was `&T`. // If so, we may want to suggest removing a `*`. sugg_sp = sugg_sp.with_hi(inner.span.lo()); return Some(( - sugg_sp, + vec![(sugg_sp, String::new())], "consider removing deref here".to_string(), - "".to_string(), Applicability::MachineApplicable, true, false, )); } - if let Ok(src) = sm.span_to_snippet(sugg_sp) { - let needs_parens = match expr.kind { - // parenthesize if needed (Issue #46756) - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(expr) => true, - _ => false, - }; - - if let Some(sugg) = self.can_use_as_ref(expr) { - return Some(( - sugg.0, - sugg.1.to_string(), - sugg.2, - Applicability::MachineApplicable, - false, - false, - )); - } - - let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!("{ident}: "), - None => String::new(), - }; - - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), - .. - })) = self.tcx.hir().find_parent(expr.hir_id) - { - if mutability.is_mut() { - // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` - return None; - } - } + let needs_parens = match expr.kind { + // parenthesize if needed (Issue #46756) + hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, + // parenthesize borrows of range literals (Issue #54505) + _ if is_range_literal(expr) => true, + _ => false, + }; - let sugg_expr = if needs_parens { format!("({src})") } else { src }; + if let Some((sugg, msg)) = self.can_use_as_ref(expr) { return Some(( - sp, - format!("consider {}borrowing here", mutability.mutably_str()), - format!("{prefix}{}{sugg_expr}", mutability.ref_prefix_str()), + sugg, + msg.to_string(), Applicability::MachineApplicable, - false, + true, false, )); } + + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{ident}: "), + None => String::new(), + }; + + if let Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Assign(..), + .. + })) = self.tcx.hir().find_parent(expr.hir_id) + { + if mutability.is_mut() { + // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` + return None; + } + } + + let sugg = mutability.ref_prefix_str(); + let (sugg, verbose) = if needs_parens { + ( + vec![ + (sp.shrink_to_lo(), format!("{prefix}{sugg}(")), + (sp.shrink_to_hi(), ")".to_string()), + ], + false, + ) + } else { + (vec![(sp.shrink_to_lo(), format!("{prefix}{sugg}"))], true) + }; + return Some(( + sugg, + format!("consider {}borrowing here", mutability.mutably_str()), + Applicability::MachineApplicable, + verbose, + false, + )); } } ( @@ -1402,23 +1406,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && sm.is_span_accessible(call_span) { return Some(( - sp.with_hi(call_span.lo()), + vec![(sp.with_hi(call_span.lo()), String::new())], "consider removing the borrow".to_string(), - String::new(), Applicability::MachineApplicable, true, - true + true, )); } return None; } - if sp.contains(expr.span) - && sm.is_span_accessible(expr.span) - { + if sp.contains(expr.span) && sm.is_span_accessible(expr.span) { return Some(( - sp.with_hi(expr.span.lo()), + vec![(sp.with_hi(expr.span.lo()), String::new())], "consider removing the borrow".to_string(), - String::new(), Applicability::MachineApplicable, true, true, @@ -1442,23 +1442,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| { // skip `&` or `&mut ` if both mutabilities are mutable - let lo = sp.lo() + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _); + let lo = sp.lo() + + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _); // skip `&` or `&mut ` let hi = sp.lo() + BytePos(old_prefix.len() as _); let sp = sp.with_lo(lo).with_hi(hi); ( sp, - format!("{}{derefs}", if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }), - if mutbl_b <= mutbl_a { Applicability::MachineApplicable } else { Applicability::MaybeIncorrect } + format!( + "{}{derefs}", + if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" } + ), + if mutbl_b <= mutbl_a { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }, ) }); if let Some((span, src, applicability)) = suggestion { return Some(( - span, + vec![(span, src)], "consider dereferencing".to_string(), - src, applicability, true, false, @@ -1487,9 +1494,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we've reached our target type with just removing `&`, then just print now. if steps == 0 && !remove.trim().is_empty() { return Some(( - prefix_span, + vec![(prefix_span, String::new())], format!("consider removing the `{}`", remove.trim()), - String::new(), // Do not remove `&&` to get to bool, because it might be something like // { a } && b, which we have a separate fixup suggestion that is more // likely correct... @@ -1509,6 +1515,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(compiler-errors): We can actually do this if the checked_ty is // `steps` layers of boxes, not just one, but this is easier and most likely. || (checked_ty.is_box() && steps == 1) + // We can always deref a binop that takes its arguments by ref. + || matches!( + self.tcx.hir().get_parent(expr.hir_id), + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. }) + if !op.node.is_by_value() + ) { let deref_kind = if checked_ty.is_box() { "unboxing the value" @@ -1549,9 +1561,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } return Some(( - span, + vec![(span, suggestion)], message, - suggestion, Applicability::MachineApplicable, true, false, @@ -1564,7 +1575,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - pub fn check_for_cast( + pub fn suggest_cast( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, @@ -1721,7 +1732,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ]; (msg, suggestion) } else { - let msg = format!("{msg} and panic if the converted value doesn't fit"); + let msg = + format!("{} and panic if the converted value doesn't fit", msg.clone()); let mut suggestion = sugg.clone(); suggestion.push(( expr.span.shrink_to_hi(), @@ -1729,19 +1741,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); (msg, suggestion) }; - err.multipart_suggestion_verbose( - &msg, - suggestion, - Applicability::MachineApplicable, - ); + err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable); }; let suggest_to_change_suffix_or_into = |err: &mut Diagnostic, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| { - let exp_is_lhs = - expected_ty_expr.map(|e| self.tcx.hir().is_lhs(e.hir_id)).unwrap_or(false); + let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir().is_lhs(e.hir_id)); if exp_is_lhs { return; @@ -1750,13 +1757,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let always_fallible = found_to_exp_is_fallible && (exp_to_found_is_fallible || expected_ty_expr.is_none()); let msg = if literal_is_ty_suffixed(expr) { - &lit_msg + lit_msg.clone() } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) { // We now know that converting either the lhs or rhs is fallible. Before we // suggest a fallible conversion, check if the value can never fit in the // expected type. let msg = format!("`{src}` cannot fit into type `{expected_ty}`"); - err.note(&msg); + err.note(msg); return; } else if in_const_context { // Do not recommend `into` or `try_into` in const contexts. @@ -1764,7 +1771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if found_to_exp_is_fallible { return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible); } else { - &msg + msg.clone() }; let suggestion = if literal_is_ty_suffixed(expr) { suffix_suggestion.clone() @@ -1826,14 +1833,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggest_to_change_suffix_or_into(err, false, true); } else if literal_is_ty_suffixed(expr) { err.multipart_suggestion_verbose( - &lit_msg, + lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else if can_cast { // Missing try_into implementation for `f64` to `f32` err.multipart_suggestion_verbose( - &format!("{cast_msg}, producing the closest possible value"), + format!("{cast_msg}, producing the closest possible value"), cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion ); @@ -1843,14 +1850,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { if literal_is_ty_suffixed(expr) { err.multipart_suggestion_verbose( - &lit_msg, + lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else if can_cast { // Missing try_into implementation for `{float}` to `{integer}` err.multipart_suggestion_verbose( - &format!("{msg}, rounding the float towards zero"), + format!("{msg}, rounding the float towards zero"), cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion ); @@ -1861,7 +1868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` if exp.bit_width() > found.bit_width().unwrap_or(256) { err.multipart_suggestion_verbose( - &format!( + format!( "{msg}, producing the floating point representation of the integer", ), into_suggestion, @@ -1869,14 +1876,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } else if literal_is_ty_suffixed(expr) { err.multipart_suggestion_verbose( - &lit_msg, + lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else { // Missing try_into implementation for `{integer}` to `{float}` err.multipart_suggestion_verbose( - &format!( + format!( "{cast_msg}, producing the floating point representation of the integer, \ rounded if necessary", ), @@ -1890,23 +1897,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` if exp.bit_width() > found.bit_width().unwrap_or(256) { err.multipart_suggestion_verbose( - &format!( + format!( "{}, producing the floating point representation of the integer", - &msg, + msg.clone(), ), into_suggestion, Applicability::MachineApplicable, ); } else if literal_is_ty_suffixed(expr) { err.multipart_suggestion_verbose( - &lit_msg, + lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else { // Missing try_into implementation for `{integer}` to `{float}` err.multipart_suggestion_verbose( - &format!( + format!( "{}, producing the floating point representation of the integer, \ rounded if necessary", &msg, @@ -1923,7 +1930,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &ty::Char, ) => { err.multipart_suggestion_verbose( - &format!("{cast_msg}, since a `char` always occupies 4 bytes"), + format!("{cast_msg}, since a `char` always occupies 4 bytes"), cast_suggestion, Applicability::MachineApplicable, ); @@ -1934,7 +1941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Identify when the user has written `foo..bar()` instead of `foo.bar()`. - pub fn check_for_range_as_method_call( + pub fn suggest_method_call_on_range_literal( &self, err: &mut Diagnostic, expr: &hir::Expr<'tcx>, @@ -2003,7 +2010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Identify when the type error is because `()` is found in a binding that was assigned a /// block without a tail expression. - fn check_for_binding_assigned_block_without_tail_expression( + fn suggest_return_binding_for_missing_tail_expr( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, @@ -2045,7 +2052,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_wrong_return_type_due_to_generic_arg( + fn note_wrong_return_ty_due_to_generic_arg( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 3eee2278d..4222205c8 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1,4 +1,6 @@ //! Errors emitted by `rustc_hir_typeck`. +use std::borrow::Cow; + use crate::fluent_generated as fluent; use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -47,8 +49,8 @@ pub struct StructExprNonExhaustive { } #[derive(Diagnostic)] -#[diag(hir_typeck_method_call_on_unknown_type, code = "E0699")] -pub struct MethodCallOnUnknownType { +#[diag(hir_typeck_method_call_on_unknown_raw_pointee, code = "E0699")] +pub struct MethodCallOnUnknownRawPointee { #[primary_span] pub span: Span, } @@ -108,7 +110,7 @@ pub enum ExpectedReturnTypeLabel<'tcx> { #[derive(Diagnostic)] #[diag(hir_typeck_missing_parentheses_in_range, code = "E0689")] -pub struct MissingParentheseInRange { +pub struct MissingParenthesesInRange { #[primary_span] #[label(hir_typeck_missing_parentheses_in_range)] pub span: Span, @@ -228,3 +230,100 @@ impl HelpUseLatestEdition { } } } + +#[derive(Diagnostic)] +#[diag(hir_typeck_const_select_must_be_const)] +#[help] +pub struct ConstSelectMustBeConst { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_const_select_must_be_fn)] +#[note] +#[help] +pub struct ConstSelectMustBeFn<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_union_pat_multiple_fields)] +pub struct UnionPatMultipleFields { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_union_pat_dotdot)] +pub struct UnionPatDotDot { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_arg_mismatch_indeterminate)] +pub struct ArgMismatchIndeterminate { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +pub enum SuggestBoxing { + #[note(hir_typeck_suggest_boxing_note)] + #[multipart_suggestion( + hir_typeck_suggest_boxing_when_appropriate, + applicability = "machine-applicable" + )] + Unit { + #[suggestion_part(code = "Box::new(())")] + start: Span, + #[suggestion_part(code = "")] + end: Span, + }, + #[note(hir_typeck_suggest_boxing_note)] + AsyncBody, + #[note(hir_typeck_suggest_boxing_note)] + #[multipart_suggestion( + hir_typeck_suggest_boxing_when_appropriate, + applicability = "machine-applicable" + )] + Other { + #[suggestion_part(code = "Box::new(")] + start: Span, + #[suggestion_part(code = ")")] + end: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_no_associated_item, code = "E0599")] +pub struct NoAssociatedItem { + #[primary_span] + pub span: Span, + pub item_kind: &'static str, + pub item_name: Ident, + pub ty_prefix: Cow<'static, str>, + pub ty_str: String, + pub trait_missing_method: bool, +} + +#[derive(Subdiagnostic)] +#[note(hir_typeck_candidate_trait_note)] +pub struct CandidateTraitNote { + #[primary_span] + pub span: Span, + pub trait_name: String, + pub item_name: Ident, + pub action_or_ty: String, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_ctor_is_private, code = "E0603")] +pub struct CtorIsPrivate { + #[primary_span] + pub span: Span, + pub def: String, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6ffa0134f..19ff77d83 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -38,6 +38,7 @@ use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::InferOk; +use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; @@ -53,6 +54,8 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -306,6 +309,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id)); self.check_expr_asm(asm) } + ExprKind::OffsetOf(container, ref fields) => { + self.check_offset_of(container, fields, expr) + } ExprKind::Break(destination, ref expr_opt) => { self.check_expr_break(destination, expr_opt.as_deref(), expr) } @@ -491,7 +497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .adjustments() .get(base.hir_id) - .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) + .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) }); if !is_named { self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }); @@ -715,7 +721,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ICE this expression in particular (see #43162). if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind { if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { - fatally_break_rust(self.tcx.sess); + fatally_break_rust(self.tcx); } } } @@ -1239,6 +1245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error, Some((rcvr, args)), expected, + false, ) { err.emit(); } @@ -1420,6 +1427,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_repeat_element_needs_copy_bound(element, count, element_ty); + self.register_wf_obligation( + tcx.mk_array_with_const_len(t, count).into(), + expr.span, + traits::WellFormed(None), + ); + tcx.mk_array_with_const_len(t, count) } @@ -1735,10 +1748,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); - let same_adt = match (adt_ty.kind(), base_ty.kind()) { - (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, - _ => false, - }; + let same_adt = matches!((adt_ty.kind(), base_ty.kind()), + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt); if self.tcx.sess.is_nightly_build() && same_adt { feature_err( &self.tcx.sess.parse_sess, @@ -1940,12 +1951,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 private fields", - ), - ); + let mut err = + self.tcx.sess.struct_span_err( + span, + format!( + "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)>, @@ -2041,7 +2053,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(field.ident.span, "field does not exist"); err.span_suggestion_verbose( expr_span, - &format!( + format!( "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, variant = variant.name, @@ -2059,7 +2071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(field.ident.span, "field does not exist"); err.span_suggestion_verbose( expr_span, - &format!( + format!( "`{adt}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, kind_name = kind_name, @@ -2101,7 +2113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let available_field_names = self.available_field_names(variant, expr_span); if !available_field_names.is_empty() { - err.note(&format!( + err.note(format!( "available fields are: {}", self.name_series_display(available_field_names) )); @@ -2380,7 +2392,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if add_label { - err.span_label(field_ident.span, &format!("field not found in `{ty}`")); + err.span_label(field_ident.span, format!("field not found in `{ty}`")); } } @@ -2449,22 +2461,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base_did: DefId, return_ty: Option>, ) -> ErrorGuaranteed { - let struct_path = self.tcx().def_path_str(base_did); - let kind_name = self.tcx().def_descr(base_did); - let mut err = struct_span_err!( - self.tcx().sess, - field.span, - E0616, - "field `{field}` of {kind_name} `{struct_path}` is private", - ); - err.span_label(field.span, "private field"); + let mut err = self.private_field_err(field, base_did); + // Also check if an accessible method exists, which is often what is meant. if self.method_exists(field, expr_t, expr.hir_id, false, return_ty) && !self.expr_in_place(expr.hir_id) { self.suggest_method_call( &mut err, - &format!("a method `{field}` also exists, call it with parentheses"), + format!("a method `{field}` also exists, call it with parentheses"), field, expr_t, expr, @@ -2568,7 +2573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let param_span = self.tcx.hir().span(param_hir_id); let param_name = self.tcx.hir().ty_param_name(param_def_id.expect_local()); - err.span_label(param_span, &format!("type parameter '{param_name}' declared here")); + err.span_label(param_span, format!("type parameter '{param_name}' declared here")); } fn suggest_fields_on_recordish( @@ -2592,7 +2597,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let struct_variant_def = def.non_enum_variant(); let field_names = self.available_field_names(struct_variant_def, access_span); if !field_names.is_empty() { - err.note(&format!( + err.note(format!( "available fields are: {}", self.name_series_display(field_names), )); @@ -2633,7 +2638,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { let msg = format!("`{base}` is a raw pointer; try dereferencing it"); let suggestion = format!("(*{base}).{field}"); - err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); + err.span_suggestion(expr.span, msg, suggestion, Applicability::MaybeIncorrect); } } @@ -2697,6 +2702,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err } + fn private_field_err( + &self, + field: Ident, + base_did: DefId, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let struct_path = self.tcx().def_path_str(base_did); + let kind_name = self.tcx().def_descr(base_did); + let mut err = struct_span_err!( + self.tcx().sess, + field.span, + E0616, + "field `{field}` of {kind_name} `{struct_path}` is private", + ); + err.span_label(field.span, "private field"); + + err + } + pub(crate) fn get_field_candidates_considering_privacy( &self, span: Span, @@ -2802,6 +2825,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { element_ty } None => { + // Attempt to *shallowly* search for an impl which matches, + // but has nested obligations which are unsatisfied. + for (base_t, _) in self.autoderef(base.span, base_t).silence_errors() { + if let Some((_, index_ty, element_ty)) = + self.find_and_report_unsatisfied_index_impl(base, base_t) + { + self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); + return element_ty; + } + } + let mut err = type_error_struct!( self.tcx.sess, expr.span, @@ -2845,6 +2879,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Try to match an implementation of `Index` against a self type, and report + /// the unsatisfied predicates that result from confirming this impl. + /// + /// Given an index expression, sometimes the `Self` type shallowly but does not + /// deeply satisfy an impl predicate. Instead of simply saying that the type + /// does not support being indexed, we want to point out exactly what nested + /// predicates cause this to be, so that the user can add them to fix their code. + fn find_and_report_unsatisfied_index_impl( + &self, + base_expr: &hir::Expr<'_>, + base_ty: Ty<'tcx>, + ) -> Option<(ErrorGuaranteed, Ty<'tcx>, Ty<'tcx>)> { + let index_trait_def_id = self.tcx.lang_items().index_trait()?; + let index_trait_output_def_id = self.tcx.get_diagnostic_item(sym::IndexOutput)?; + + let mut relevant_impls = vec![]; + self.tcx.for_each_relevant_impl(index_trait_def_id, base_ty, |impl_def_id| { + relevant_impls.push(impl_def_id); + }); + let [impl_def_id] = relevant_impls[..] else { + // Only report unsatisfied impl predicates if there's one impl + return None; + }; + + self.commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(self); + let impl_substs = self.fresh_substs_for_item(base_expr.span, impl_def_id); + let impl_trait_ref = + self.tcx.impl_trait_ref(impl_def_id).unwrap().subst(self.tcx, impl_substs); + let cause = self.misc(base_expr.span); + + // Match the impl self type against the base ty. If this fails, + // we just skip this impl, since it's not particularly useful. + let impl_trait_ref = ocx.normalize(&cause, self.param_env, impl_trait_ref); + ocx.eq(&cause, self.param_env, impl_trait_ref.self_ty(), base_ty)?; + + // Register the impl's predicates. One of these predicates + // must be unsatisfied, or else we wouldn't have gotten here + // in the first place. + ocx.register_obligations(traits::predicates_for_generics( + |idx, span| { + cause.clone().derived_cause( + ty::Binder::dummy(ty::TraitPredicate { + trait_ref: impl_trait_ref, + polarity: ty::ImplPolarity::Positive, + constness: ty::BoundConstness::NotConst, + }), + |derived| { + traits::ImplDerivedObligation(Box::new( + traits::ImplDerivedObligationCause { + derived, + impl_or_alias_def_id: impl_def_id, + impl_def_predicate_index: Some(idx), + span, + }, + )) + }, + ) + }, + self.param_env, + self.tcx.predicates_of(impl_def_id).instantiate(self.tcx, impl_substs), + )); + + // Normalize the output type, which we can use later on as the + // return type of the index expression... + let element_ty = ocx.normalize( + &cause, + self.param_env, + self.tcx.mk_projection(index_trait_output_def_id, impl_trait_ref.substs), + ); + + let errors = ocx.select_where_possible(); + // There should be at least one error reported. If not, we + // will still delay a span bug in `report_fulfillment_errors`. + Ok::<_, NoSolution>(( + self.err_ctxt().report_fulfillment_errors(&errors), + impl_trait_ref.substs.type_at(1), + element_ty, + )) + }) + .ok() + } + fn point_at_index_if_possible( &self, errors: &mut Vec>, @@ -2954,4 +3071,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_unit() } } + + fn check_offset_of( + &self, + container: &'tcx hir::Ty<'tcx>, + fields: &[Ident], + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let container = self.to_ty(container).normalized; + + let mut field_indices = Vec::with_capacity(fields.len()); + let mut current_container = container; + + for &field in fields { + let container = self.structurally_resolved_type(expr.span, current_container); + + match container.kind() { + ty::Adt(container_def, substs) if !container_def.is_enum() => { + let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id); + let (ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block); + + let fields = &container_def.non_enum_variant().fields; + if let Some((index, field)) = fields + .iter_enumerated() + .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == ident) + { + let field_ty = self.field_ty(expr.span, field, substs); + + // FIXME: DSTs with static alignment should be allowed + self.require_type_is_sized(field_ty, expr.span, traits::MiscObligation); + + if field.vis.is_accessible_from(def_scope, self.tcx) { + self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None); + } else { + self.private_field_err(ident, container_def.did()).emit(); + } + + // Save the index of all fields regardless of their visibility in case + // of error recovery. + field_indices.push(index); + current_container = field_ty; + + continue; + } + } + ty::Tuple(tys) => { + let fstr = field.as_str(); + + if let Ok(index) = fstr.parse::() { + if fstr == index.to_string() { + if let Some(&field_ty) = tys.get(index) { + field_indices.push(index.into()); + current_container = field_ty; + + continue; + } + } + } + } + _ => (), + }; + + self.no_such_field_err(field, container, expr.hir_id).emit(); + + break; + } + + self.typeck_results + .borrow_mut() + .offset_of_data_mut() + .insert(expr.hir_id, (container, field_indices)); + + self.tcx.types.usize + } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ee1c6fbfd..e14e8ac2c 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -300,6 +300,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { hir::ExprKind::Continue(..) | hir::ExprKind::Lit(..) | hir::ExprKind::ConstBlock(..) + | hir::ExprKind::OffsetOf(..) | hir::ExprKind::Err(_) => {} hir::ExprKind::Loop(blk, ..) => { @@ -437,12 +438,19 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // to borrow discr. needs_to_be_read = true; } - PatKind::Or(_) - | PatKind::Box(_) - | PatKind::Slice(..) - | PatKind::Ref(..) - | PatKind::Wild => { - // If the PatKind is Or, Box, Slice or Ref, the decision is made later + PatKind::Slice(lhs, wild, rhs) => { + // We don't need to test the length if the pattern is `[..]` + if matches!((lhs, wild, rhs), (&[], Some(_), &[])) + // Arrays have a statically known size, so + // there is no need to read their length + || discr_place.place.base_ty.is_array() + { + } else { + needs_to_be_read = true; + } + } + PatKind::Or(_) | PatKind::Box(_) | PatKind::Ref(..) | PatKind::Wild => { + // If the PatKind is Or, Box, or Ref, the decision is made later // as these patterns contains subpatterns // If the PatKind is Wild, the decision is made based on the other patterns being // examined diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index f736f7a96..557950338 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1,10 +1,11 @@ use crate::callee::{self, DeferredCallResolution}; +use crate::errors::CtorIsPrivate; use crate::method::{self, MethodCallee, SelfSource}; use crate::rvalue_scopes; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -35,7 +36,9 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; +use rustc_trait_selection::traits::{ + self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt, +}; use std::collections::hash_map::Entry; use std::slice; @@ -63,9 +66,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::UNREACHABLE_CODE, id, span, - &msg, + msg.clone(), |lint| { - lint.span_label(span, &msg).span_label( + lint.span_label(span, msg).span_label( orig_span, custom_note .unwrap_or("any code following this expression is unreachable"), @@ -275,7 +278,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { self.tcx.sess.delay_span_bug( expr.span, - &format!( + format!( "while adjusting {:?}, can't compose {:?} and {:?}", expr, entry.get(), @@ -420,9 +423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast_c: &hir::AnonConst, param_def_id: DefId, ) -> ty::Const<'tcx> { - let const_def = - ty::WithOptConstParam { did: ast_c.def_id, const_param_did: Some(param_def_id) }; - let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); + let did = ast_c.def_id; + self.tcx.feed_anon_const_type(did, self.tcx.type_of(param_def_id)); + let c = ty::Const::from_anon_const(self.tcx, did); self.register_wf_obligation( c.into(), self.tcx.hir().span(ast_c.hir_id), @@ -827,7 +830,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } QPath::TypeRelative(ref qself, ref segment) => { // Don't use `self.to_ty`, since this will register a WF obligation. - // If we're trying to call a non-existent method on a trait + // If we're trying to call a nonexistent method on a trait // (e.g. `MyTrait::missing_method`), then resolution will // give us a `QPath::TypeRelative` with a trait object as // `qself`. In that case, we want to avoid registering a WF obligation @@ -853,23 +856,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let item_name = item_segment.ident; let result = self .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) + .and_then(|r| { + // lint bare trait if the method is found in the trait + if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { + diag.emit(); + } + Ok(r) + }) .or_else(|error| { + let guar = self + .tcx + .sess + .delay_span_bug(span, "method resolution should've emitted an error"); let result = match error { method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), - _ => Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()), + _ => Err(guar), }; + let trait_missing_method = + matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait(); // If we have a path like `MyTrait::missing_method`, then don't register // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise, // register a WF obligation so that we can detect any additional // errors in the self type. - if !(matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait()) { + if !trait_missing_method { self.register_wf_obligation( ty.raw.into(), qself.span, traits::WellFormed(None), ); } + + // emit or cancel the diagnostic for bare traits + if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { + if trait_missing_method { + // cancel the diag for bare traits when meeting `MyTrait::missing_method` + diag.cancel(); + } else { + diag.emit(); + } + } + if item_name.name != kw::Empty { if let Some(mut e) = self.report_method_error( span, @@ -879,10 +906,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error, None, Expectation::NoExpectation, + trait_missing_method && span.edition().rust_2021(), // emits missing method for trait only after edition 2021 ) { e.emit(); } } + result }); @@ -989,8 +1018,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .typeck_results .borrow() .expr_ty_adjusted_opt(rcvr) - .and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs())) - .unwrap_or(false); + .zip(expected) + .is_some_and(|(ty, expected_ty)| expected_ty.peel_refs() == ty.peel_refs()); let prev_call_mutates_and_returns_unit = || { self.typeck_results @@ -998,14 +1027,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .type_dependent_def_id(expr.hir_id) .map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder()) .and_then(|sig| sig.inputs_and_output.split_last()) - .map(|(output, inputs)| { + .is_some_and(|(output, inputs)| { output.is_unit() && inputs .get(0) .and_then(|self_ty| self_ty.ref_mutability()) - .map_or(false, rustc_ast::Mutability::is_mut) + .is_some_and(rustc_ast::Mutability::is_mut) }) - .unwrap_or(false) }; if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) { @@ -1034,15 +1062,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr.span, "you probably want to use this value after calling the method...", ); - err.span_note(sp, &modifies_rcvr_note); - err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + err.span_note(sp, modifies_rcvr_note); + err.note(format!("...instead of the `()` output of method `{}`", path_segment.ident)); } else if let ExprKind::MethodCall(..) = rcvr.kind { err.span_note( sp, modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.", ); } else { - err.span_note(sp, &modifies_rcvr_note); + err.span_note(sp, modifies_rcvr_note); } } @@ -1172,16 +1200,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let has_self = path_segs - .last() - .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) - .unwrap_or(false); + let has_self = + path_segs.last().is_some_and(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self); let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id).subst_identity()); match ty.normalized.ty_adt_def() { Some(adt_def) if adt_def.has_ctor() => { let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); + // Check the visibility of the ctor. + let vis = tcx.visibility(ctor_def_id); + if !vis.is_accessible_from(tcx.parent_module(hir_id).to_def_id(), tcx) { + tcx.sess + .emit_err(CtorIsPrivate { span, def: tcx.def_path_str(adt_def.did()) }); + } let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); let user_substs = Self::user_substs_for_adt(ty); user_self_ty = user_substs.user_self_ty; @@ -1374,7 +1406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(_) => { self.tcx.sess.delay_span_bug( span, - &format!( + format!( "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", self_ty, impl_ty, @@ -1434,10 +1466,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Resolves `typ` by a single level if `typ` is a type variable. + /// + /// When the new solver is enabled, this will also attempt to normalize + /// the type if it's a projection (note that it will not deeply normalize + /// projections within the type, just the outermost layer of the type). + /// /// If no resolution is possible, then an error is reported. /// Numeric inference variables may be left unresolved. pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.resolve_vars_with_obligations(ty); + let mut ty = self.resolve_vars_with_obligations(ty); + + if self.tcx.trait_solver_next() + && let ty::Alias(ty::Projection, _) = ty.kind() + { + match self + .at(&self.misc(sp), self.param_env) + .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut()) + { + Ok(normalized_ty) => { + ty = normalized_ty; + }, + Err(errors) => { + let guar = self.err_ctxt().report_fulfillment_errors(&errors); + return self.tcx.ty_error(guar); + } + } + } + if !ty.is_ty_var() { ty } else { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index f879ccbb3..3efdab534 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -164,24 +164,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() { - if let Some(param) = param { - let refined_expr = self.point_at_field_if_possible( - def_id, - param, - variant_def_id, - fields, - ); - - match refined_expr { - None => {} - Some((refined_expr, _)) => { - error.obligation.cause.span = refined_expr - .span - .find_ancestor_in_same_ctxt(error.obligation.cause.span) - .unwrap_or(refined_expr.span); - return true; - } + let refined_expr = + self.point_at_field_if_possible(def_id, param, variant_def_id, fields); + + match refined_expr { + None => {} + Some((refined_expr, _)) => { + error.obligation.cause.span = refined_expr + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(refined_expr.span); + return true; } } } @@ -282,9 +278,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, ) -> bool { if let traits::FulfillmentErrorCode::CodeSelectionError( - traits::SelectionError::OutputTypeParameterMismatch(_, expected, _), + traits::SelectionError::OutputTypeParameterMismatch(box traits::SelectionOutputTypeParameterMismatch{ + expected_trait_ref, .. + }), ) = error.code - && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind() + && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected_trait_ref.skip_binder().self_ty().kind() && span.overlaps(self.tcx.def_span(*def_id)) { true @@ -334,7 +332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// expression mentioned. /// /// `blame_specific_arg_if_possible` will find the most-specific expression anywhere inside - /// the provided function call expression, and mark it as responsible for the fullfillment + /// the provided function call expression, and mark it as responsible for the fulfillment /// error. fn blame_specific_arg_if_possible( &self, @@ -485,7 +483,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // For the purposes of this function, we hope that it is a `struct` type, and that our current `expr` is a literal of // that struct type. let impl_trait_self_ref = if self.tcx.is_trait_alias(obligation.impl_or_alias_def_id) { - self.tcx.mk_trait_ref( + ty::TraitRef::new( + self.tcx, obligation.impl_or_alias_def_id, ty::InternalSubsts::identity_for_item(self.tcx, obligation.impl_or_alias_def_id), ) @@ -846,7 +845,7 @@ fn find_param_in_ty<'tcx>( return true; } if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, ..) = ty.kind() + && let ty::Alias(ty::Projection | ty::Inherent, ..) = ty.kind() { // This logic may seem a bit strange, but typically when // we have a projection type in a function signature, the diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs index 6f26afcaf..d45e3d395 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs @@ -1,6 +1,6 @@ use std::cmp; -use rustc_index::vec::IndexVec; +use rustc_index::IndexVec; use rustc_middle::ty::error::TypeError; rustc_index::newtype_index! { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index ea1b52daa..eba5c829e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2,8 +2,8 @@ use crate::coercion::CoerceMany; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; use crate::gather_locals::Declaration; use crate::method::MethodCallee; -use crate::Expectation::*; use crate::TupleArgumentsFlag::*; +use crate::{errors, Expectation::*}; use crate::{ struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy, TupleArgumentsFlag, @@ -21,7 +21,7 @@ use rustc_hir_analysis::astconv::AstConv; use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::structured_errors::StructuredDiagnostic; -use rustc_index::vec::IndexVec; +use rustc_index::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::TypeTrace; @@ -283,19 +283,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) { self.tcx .sess - .struct_span_err(provided_arg.span, "this argument must be a `const fn`") - .help("consult the documentation on `const_eval_select` for more information") - .emit(); + .emit_err(errors::ConstSelectMustBeConst { span: provided_arg.span }); } } else { - self.tcx - .sess - .struct_span_err(provided_arg.span, "this argument must be a function item") - .note(format!("expected a function item, found {checked_ty}")) - .help( - "consult the documentation on `const_eval_select` for more information", - ) - .emit(); + self.tcx.sess.emit_err(errors::ConstSelectMustBeFn { + span: provided_arg.span, + ty: checked_ty, + }); } } @@ -368,7 +362,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - let is_closure = matches!(arg.kind, ExprKind::Closure { .. }); + // For this check, we do *not* want to treat async generator closures (async blocks) + // as proper closures. Doing so would regress type inference when feeding + // the return value of an argument-position async block to an argument-position + // closure wrapped in a block. + // See . + let is_closure = if let ExprKind::Closure(closure) = arg.kind { + !tcx.generator_is_async(closure.def_id.to_def_id()) + } else { + false + }; if is_closure != check_closures { continue; } @@ -691,7 +694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err = tcx.sess.struct_span_err_with_code( full_call_span, - &format!( + format!( "{call_name} takes {}{} but {} {} supplied", if c_variadic { "at least " } else { "" }, potentially_plural_count( @@ -744,17 +747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if cfg!(debug_assertions) { span_bug!(error_span, "expected errors from argument matrix"); } else { - tcx.sess - .struct_span_err( - error_span, - "argument type mismatch was detected, \ - but rustc had trouble determining where", - ) - .note( - "we would appreciate a bug report: \ - https://github.com/rust-lang/rust/issues/new", - ) - .emit(); + tcx.sess.emit_err(errors::ArgMismatchIndeterminate { span: error_span }); } return; } @@ -844,7 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { tcx.sess.struct_span_err_with_code( full_call_span, - &format!( + format!( "this {} takes {}{} but {} {} supplied", call_name, if c_variadic { "at least " } else { "" }, @@ -892,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut errors = errors.into_iter().peekable(); let mut only_extras_so_far = errors .peek() - .map_or(false, |first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); let mut suggestions = vec![]; while let Some(error) = errors.next() { only_extras_so_far &= matches!(error, Error::Extra(_)); @@ -1219,7 +1212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } SuggestionText::Remove(plural) => { err.multipart_suggestion( - &format!("remove the extra argument{}", if plural { "s" } else { "" }), + format!("remove the extra argument{}", if plural { "s" } else { "" }), suggestions, Applicability::HasPlaceholders, ); @@ -1269,7 +1262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestion += ")"; err.span_suggestion_verbose( suggestion_span, - &suggestion_text, + suggestion_text, suggestion, Applicability::HasPlaceholders, ); @@ -1316,6 +1309,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_ty.unwrap_or_else(|| self.next_float_var()) } ast::LitKind::Bool(_) => tcx.types.bool, + ast::LitKind::CStr(_, _) => tcx.mk_imm_ref( + tcx.lifetimes.re_static, + tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, Some(lit.span))) + .skip_binder(), + ), ast::LitKind::Err => tcx.ty_error_misc(), } } @@ -1530,7 +1528,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // case we can ignore the tail expression (e.g., `'a: { // break 'a 22; }` would not force the type of the block // to be `()`). - let tail_expr = blk.expr.as_ref(); let coerce_to_ty = expected.coercion_target_type(self, blk.span); let coerce = if blk.targeted_by_break { CoerceMany::new(coerce_to_ty) @@ -1548,13 +1545,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // check the tail expression **without** holding the // `enclosing_breakables` lock below. - let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); + let tail_expr_ty = + blk.expr.map(|expr| (expr, self.check_expr_with_expectation(expr, expected))); let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); let ctxt = enclosing_breakables.find_breakable(blk.hir_id); let coerce = ctxt.coerce.as_mut().unwrap(); - if let Some(tail_expr_ty) = tail_expr_ty { - let tail_expr = tail_expr.unwrap(); + if let Some((tail_expr, tail_expr_ty)) = tail_expr_ty { let span = self.get_expr_coercion_span(tail_expr); let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); let ty_for_diagnostic = coerce.merged_ty(); @@ -1607,6 +1604,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self.misc(sp), &mut |err| { if let Some(expected_ty) = expected.only_has_type(self) { + if blk.stmts.is_empty() && blk.expr.is_none() { + self.suggest_boxing_when_appropriate( + err, + blk.span, + blk.hir_id, + expected_ty, + self.tcx.mk_unit(), + ); + } if !self.consider_removing_semicolon(blk, expected_ty, err) { self.err_ctxt().consider_returning_binding( blk, @@ -1619,7 +1625,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // silence this redundant error, as we already emit E0070. // Our block must be a `assign desugar local; assignment` - if let Some(hir::Node::Block(hir::Block { + if let hir::Block { stmts: [ hir::Stmt { @@ -1641,7 +1647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ], .. - })) = self.tcx.hir().find(blk.hir_id) + } = blk { self.comes_from_while_condition(blk.hir_id, |_| { err.downgrade_to_delayed_bug(); @@ -1911,7 +1917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { // Look for a user-provided impl of a `Fn` trait, and point to it. let new_def_id = self.probe(|_| { - let trait_ref = self.tcx.mk_trait_ref( + let trait_ref = ty::TraitRef::new(self.tcx, call_kind.to_def_id(self.tcx), [ callee_ty, @@ -1963,7 +1969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { spans.push_span_label(param.span, ""); } - err.span_note(spans, &format!("{} defined here", self.tcx.def_descr(def_id))); + err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id))); } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id) && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind { @@ -1974,11 +1980,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { ("closure", self.tcx.def_span(def_id)) }; - err.span_note(span, &format!("{} defined here", kind)); + err.span_note(span, format!("{} defined here", kind)); } else { err.span_note( self.tcx.def_span(def_id), - &format!("{} defined here", self.tcx.def_descr(def_id)), + format!("{} defined here", self.tcx.def_descr(def_id)), ); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index c6fd0b610..67f45f9aa 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -17,7 +17,6 @@ use rustc_infer::infer; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; @@ -169,7 +168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig); if ocx.select_all_or_error().is_empty() { let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig); - if !normalized_fn_sig.needs_infer() { + if !normalized_fn_sig.has_infer() { return normalized_fn_sig; } } @@ -250,16 +249,12 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { - return ty; - } - unreachable!() - } else { - self.next_ty_var(TypeVariableOrigin { + match param { + Some(param) => self.var_for_def(span, param).as_type().unwrap(), + None => self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span, - }) + }), } } @@ -269,16 +264,12 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { param: Option<&ty::GenericParamDef>, span: Span, ) -> Const<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { - return ct; - } - unreachable!() - } else { - self.next_const_var( + match param { + Some(param) => self.var_for_def(span, param).as_const().unwrap(), + None => self.next_const_var( ty, ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, - ) + ), } } @@ -309,7 +300,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { match ty.kind() { ty::Adt(adt_def, _) => Some(*adt_def), // FIXME(#104767): Should we handle bound regions here? - ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => { + ty::Alias(ty::Projection | ty::Inherent, _) if !ty.has_escaping_bound_vars() => { self.normalize(span, ty).ty_adt_def() } _ => None, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 5fda4e191..c4add4dbd 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,6 +1,6 @@ use super::FnCtxt; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing}; use crate::fluent_generated as fluent; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; @@ -9,7 +9,8 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, + AsyncGeneratorKind, Expr, ExprKind, GeneratorKind, GenericBound, HirId, Node, Path, QPath, + Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::traits::{self, StatementAsExpression}; @@ -274,13 +275,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let expr = expr.peel_blocks(); - if let Some((sp, msg, suggestion, applicability, verbose, annotation)) = - self.check_ref(expr, found, expected) + if let Some((suggestion, msg, applicability, verbose, annotation)) = + self.suggest_deref_or_ref(expr, found, expected) { if verbose { - err.span_suggestion_verbose(sp, &msg, suggestion, applicability); + err.multipart_suggestion_verbose(msg, suggestion, applicability); } else { - err.span_suggestion(sp, &msg, suggestion, applicability); + err.multipart_suggestion(msg, suggestion, applicability); } if annotation { let suggest_annotation = match expr.peel_drop_temps().kind { @@ -342,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(sp, format!("{descr} `{name}` defined here")); } return true; - } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + } else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { return true; } else { let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); @@ -438,33 +439,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn suggest_boxing_when_appropriate( &self, err: &mut Diagnostic, - expr: &hir::Expr<'_>, + span: Span, + hir_id: HirId, expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return false; - } - if !expected.is_box() || found.is_box() { + // Do not suggest `Box::new` in const context. + if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() { return false; } - let boxed_found = self.tcx.mk_box(found); - if self.can_coerce(boxed_found, expected) { - err.multipart_suggestion( - "store this in the heap by calling `Box::new`", - vec![ - (expr.span.shrink_to_lo(), "Box::new(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - err.note( - "for more on the distinction between the stack and the heap, read \ - https://doc.rust-lang.org/book/ch15-01-box.html, \ - https://doc.rust-lang.org/rust-by-example/std/box.html, and \ - https://doc.rust-lang.org/std/boxed/index.html", - ); + if self.can_coerce(self.tcx.mk_box(found), expected) { + let suggest_boxing = match found.kind() { + ty::Tuple(tuple) if tuple.is_empty() => { + SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span } + } + ty::Generator(def_id, ..) + if matches!( + self.tcx.generator_kind(def_id), + Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) + ) => + { + SuggestBoxing::AsyncBody + } + _ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() }, + }; + err.subdiagnostic(suggest_boxing); + true } else { false @@ -794,7 +794,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; - // get all where BoundPredicates here, because they are used in to cases below + // get all where BoundPredicates here, because they are used in two cases below let where_predicates = predicates .iter() .filter_map(|p| match p { @@ -1096,10 +1096,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, self.misc(expr.span), self.param_env, - ty::Binder::dummy(self.tcx.mk_trait_ref( + ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty] - )), + ), )) { let sugg = if expr.precedence().order() >= PREC_POSTFIX { @@ -1252,7 +1252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed), span, }) => { - let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else { return false; }; + let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(*span) else { return false; }; if !(snippet.starts_with("0x") || snippet.starts_with("0X")) { return false; } @@ -1311,7 +1311,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We have satisfied all requirements to provide a suggestion. Emit it. err.span_suggestion( - span, + *span, format!("if you meant to create a null pointer, use `{null_path_str}()`"), null_path_str + "()", Applicability::MachineApplicable, @@ -1384,7 +1384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let item_ty = self.tcx.type_of(item.def_id).subst_identity(); // FIXME(compiler-errors): This check is *so* rudimentary - if item_ty.needs_subst() { + if item_ty.has_param() { return false; } if self.can_coerce(item_ty, expected_ty) { @@ -1438,7 +1438,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) // Check that we're in fact trying to clone into the expected type && self.can_coerce(*pointee_ty, expected_ty) - && let trait_ref = ty::Binder::dummy(self.tcx.mk_trait_ref(clone_trait_did, [expected_ty])) + && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty]) // And the expected type doesn't implement `Clone` && !self.predicate_must_hold_considering_regions(&traits::Obligation::new( self.tcx, @@ -1449,7 +1449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { diag.span_note( callee_expr.span, - &format!( + format!( "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead" ), ); diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index 3e9a9ce1b..e4a62ec05 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -8,7 +8,7 @@ use hir::{ }; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; -use rustc_index::vec::IndexVec; +use rustc_index::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::{ hir::map::Map, @@ -215,6 +215,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::InlineAsm(..) + | ExprKind::OffsetOf(..) | ExprKind::Struct(..) | ExprKind::Repeat(..) | ExprKind::Yield(..) @@ -485,6 +486,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::InlineAsm(..) + | ExprKind::OffsetOf(..) | ExprKind::Let(..) | ExprKind::Lit(..) | ExprKind::Path(..) diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs index 139d17d2e..633b47889 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs @@ -1,5 +1,5 @@ use super::{DropRangesBuilder, PostOrderId}; -use rustc_index::{bit_set::BitSet, vec::IndexVec}; +use rustc_index::{bit_set::BitSet, IndexVec}; use std::collections::BTreeMap; impl DropRangesBuilder { diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs index f7b493bc2..ecafbd668 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs @@ -20,7 +20,7 @@ use hir::{Body, HirId, HirIdMap, Node}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; +use rustc_index::IndexVec; use rustc_middle::hir::map::Map; use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; use rustc_middle::ty; @@ -193,7 +193,7 @@ impl DropRanges { .get(&TrackedValue::Temporary(hir_id)) .or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id))) .cloned() - .map_or(false, |tracked_value_id| { + .is_some_and(|tracked_value_id| { self.expect_node(location.into()).drop_state.contains(tracked_value_id) }) } @@ -268,8 +268,7 @@ impl DropRangesBuilder { fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo { let size = self.num_values(); - self.nodes.ensure_contains_elem(id, || NodeInfo::new(size)); - &mut self.nodes[id] + self.nodes.ensure_contains_elem(id, || NodeInfo::new(size)) } fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) { diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs index fa3887362..8ab0bd535 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -202,10 +202,10 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { // If the type being assigned needs dropped, then the mutation counts as a borrow // since it is essentially doing `Drop::drop(&mut x); x = new_value;`. let ty = self.tcx.erase_regions(assignee_place.place.base_ty); - if ty.needs_infer() { + if ty.has_infer() { self.tcx.sess.delay_span_bug( self.tcx.hir().span(assignee_place.hir_id), - &format!("inference variables in {ty}"), + format!("inference variables in {ty}"), ); } else if ty.needs_drop(self.tcx, self.param_env) { self.places diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index f39710804..019fb86f5 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -112,7 +112,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { self.fcx .tcx .sess - .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_term)); + .delay_span_bug(span, format!("Encountered var {:?}", unresolved_term)); } else { let note = format!( "the type is part of the {} because of this {}", @@ -460,11 +460,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // Avoid ICEs in needs_drop. let ty = self.fcx.resolve_vars_if_possible(ty); let ty = self.fcx.tcx.erase_regions(ty); - if ty.needs_infer() { + if ty.has_infer() { self.fcx .tcx .sess - .delay_span_bug(expr.span, &format!("inference variables in {ty}")); + .delay_span_bug(expr.span, format!("inference variables in {ty}")); true } else { ty.needs_drop(self.fcx.tcx, self.fcx.param_env) @@ -571,7 +571,7 @@ fn check_must_not_suspend_ty<'tcx>( // FIXME: support adding the attribute to TAITs ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { let mut has_emitted = false; - for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) { + for &(predicate, _) in fcx.tcx.explicit_item_bounds(def).skip_binder() { // We only look at the `DefId`, so it is safe to skip the binder here. if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) = predicate.kind().skip_binder() @@ -650,7 +650,7 @@ fn check_must_not_suspend_ty<'tcx>( }, ) } - // If drop tracking is enabled, we want to look through references, since the referrent + // If drop tracking is enabled, we want to look through references, since the referent // may not be considered live across the await point. ty::Ref(_region, ty, _mutability) if fcx.sess().opts.unstable_opts.drop_tracking => { let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix); diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 4110b176b..294c3bb78 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -4,7 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; -use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefIdMap; @@ -130,7 +131,7 @@ impl<'tcx> Inherited<'tcx> { // (*) binder skipped if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder() && let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t)) - && self.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id) + && self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id) { let new_self_ty = self.tcx.types.unit; diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 106f5bcd7..3c5eafd94 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -1,10 +1,10 @@ use hir::HirId; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_index::vec::Idx; +use rustc_index::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_target::abi::{FieldIdx, Pointer, VariantIdx}; +use rustc_target::abi::{Pointer, VariantIdx}; use super::FnCtxt; @@ -28,7 +28,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { } if def.variant(data_idx).fields.len() == 1 { - return def.variant(data_idx).fields[FieldIdx::from_u32(0)].ty(tcx, substs); + return def.variant(data_idx).single_field().ty(tcx, substs); } } @@ -72,8 +72,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let from = unpack_option_like(tcx, from); if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer(dl.instruction_address_space).size(&tcx) { struct_span_err!(tcx.sess, span, E0591, "can't transmute zero-sized type") - .note(&format!("source type: {from}")) - .note(&format!("target type: {to}")) + .note(format!("source type: {from}")) + .note(format!("target type: {to}")) .help("cast with `as` to a pointer instead") .emit(); return; @@ -109,10 +109,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { or dependently-sized types" ); if from == to { - err.note(&format!("`{from}` does not have a fixed size")); + err.note(format!("`{from}` does not have a fixed size")); } else { - err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) - .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); + err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) + .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); let mut should_delay_as_bug = false; if let Err(LayoutError::Unknown(bad_from)) = sk_from && bad_from.references_error() { should_delay_as_bug = true; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 45890abad..b97b55d8f 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -2,6 +2,7 @@ #![feature(let_chains)] #![feature(try_blocks)] #![feature(never_type)] +#![feature(box_patterns)] #![feature(min_specialization)] #![feature(control_flow_enum)] #![feature(drain_filter)] @@ -59,6 +60,7 @@ use rustc_errors::{ struct_span_err, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, MultiSpan, SubdiagnosticMessage, }; +use rustc_fluent_macro::fluent_messages; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; @@ -66,12 +68,10 @@ use rustc_hir::{HirIdMap, Node}; use rustc_hir_analysis::astconv::AstConv; use rustc_hir_analysis::check::check_abi; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_macros::fluent_messages; +use rustc_middle::query::Providers; use rustc_middle::traits; -use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config; -use rustc_session::Session; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; @@ -152,25 +152,9 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet, (): ()) { - tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); -} - -fn typeck_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(param_did).subst_identity(); - typeck_with_fallback(tcx, did, fallback) -} - fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - if let Some(param_did) = tcx.opt_const_param_of(def_id) { - tcx.typeck_const_arg((def_id, param_did)) - } else { - let fallback = move || tcx.type_of(def_id.to_def_id()).subst_identity(); - typeck_with_fallback(tcx, def_id, fallback) - } + let fallback = move || tcx.type_of(def_id.to_def_id()).subst_identity(); + typeck_with_fallback(tcx, def_id, fallback) } /// Used only to get `TypeckResults` for type inference during error recovery. @@ -228,7 +212,7 @@ fn typeck_with_fallback<'tcx>( let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); let fn_sig = fcx.normalize(body.value.span, fn_sig); - check_fn(&mut fcx, fn_sig, decl, def_id, body, None); + check_fn(&mut fcx, fn_sig, decl, def_id, body, None, tcx.features().unsized_fn_params); } else { let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty { Some(fcx.next_ty_var(TypeVariableOrigin { @@ -237,12 +221,6 @@ fn typeck_with_fallback<'tcx>( })) } else if let Node::AnonConst(_) = node { match tcx.hir().get(tcx.hir().parent_id(id)) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::ConstBlock(ref anon_const), .. - }) if anon_const.hir_id == id => Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - })), Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. }) if anon_const.hir_id == id => { @@ -459,8 +437,8 @@ enum TupleArgumentsFlag { TupleArguments, } -fn fatally_break_rust(sess: &Session) { - let handler = sess.diagnostic(); +fn fatally_break_rust(tcx: TyCtxt<'_>) { + let handler = tcx.sess.diagnostic(); handler.span_bug_no_panic( MultiSpan::new(), "It looks like you're trying to break rust; would you like some ICE?", @@ -470,29 +448,21 @@ fn fatally_break_rust(sess: &Session) { "we would appreciate a joke overview: \ https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", ); - handler.note_without_error(&format!( + handler.note_without_error(format!( "rustc {} running on {}", - option_env!("CFG_VERSION").unwrap_or("unknown_version"), + tcx.sess.cfg_version, config::host_triple(), )); } -fn has_expected_num_generic_args( - tcx: TyCtxt<'_>, - trait_did: Option, - expected: usize, -) -> bool { - trait_did.map_or(true, |trait_did| { - let generics = tcx.generics_of(trait_did); - generics.count() == expected + if generics.has_self { 1 } else { 0 } - }) +fn has_expected_num_generic_args(tcx: TyCtxt<'_>, trait_did: DefId, expected: usize) -> bool { + let generics = tcx.generics_of(trait_did); + generics.count() == expected + if generics.has_self { 1 } else { 0 } } pub fn provide(providers: &mut Providers) { method::provide(providers); *providers = Providers { - typeck_item_bodies, - typeck_const_arg, typeck, diagnostic_only_typeck, has_typeck_results, diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 6c861b593..78171e0b2 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -381,6 +381,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::OffsetOf(..) | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), } } @@ -410,7 +411,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } Res::Local(var_id) => { - if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) { + if self.upvars.is_some_and(|upvars| upvars.contains_key(&var_id)) { self.cat_upvar(hir_id, var_id) } else { Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new())) diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 9155a3d8d..98529b666 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { return self.tcx.ty_error_with_message( rustc_span::DUMMY_SP, - &format!("failed autoderef {}", pick.autoderefs), + format!("failed autoderef {}", pick.autoderefs), ); }; assert_eq!(n, pick.autoderefs); @@ -471,7 +471,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self_ty, method_self_ty, self.span, pick ); let cause = self.cause( - self.span, + self.self_expr.span, ObligationCauseCode::UnifyReceiver(Box::new(UnifyReceiverContext { assoc_item: pick.item, param_env: self.param_env, @@ -482,13 +482,22 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); } - Err(_) => { - span_bug!( - self.span, - "{} was a subtype of {} but now is not?", - self_ty, - method_self_ty - ); + Err(terr) => { + // FIXME(arbitrary_self_types): We probably should limit the + // situations where this can occur by adding additional restrictions + // to the feature, like the self type can't reference method substs. + if self.tcx.features().arbitrary_self_types { + self.err_ctxt() + .report_mismatched_types(&cause, method_self_ty, self_ty, terr) + .emit(); + } else { + span_bug!( + self.span, + "{} was a subtype of {} but now is not?", + self_ty, + method_self_ty + ); + } } } } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 0456dd56c..6f4d674ba 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -13,11 +13,12 @@ pub use self::MethodError::*; use crate::errors::OpMethodGenericParams; use crate::FnCtxt; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, Diagnostic, SubdiagnosticMessage}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; +use rustc_middle::query::Providers; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitableExt}; @@ -28,7 +29,7 @@ use rustc_trait_selection::traits::{self, NormalizeExt}; use self::probe::{IsSuggestion, ProbeScope}; -pub fn provide(providers: &mut ty::query::Providers) { +pub fn provide(providers: &mut Providers) { probe::provide(providers); } @@ -129,7 +130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_method_call( &self, err: &mut Diagnostic, - msg: &str, + msg: impl Into + std::fmt::Debug, method_name: Ident, self_ty: Ty<'tcx>, call_expr: &hir::Expr<'tcx>, @@ -300,8 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trait_def_id: DefId, self_ty: Ty<'tcx>, opt_input_types: Option<&[Ty<'tcx>]>, - ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List>) - { + ) -> (traits::PredicateObligation<'tcx>, &'tcx ty::List>) { // Construct a trait-reference `self_ty : Trait` let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { match param.kind { @@ -317,7 +317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.var_for_def(cause.span, param) }); - let trait_ref = self.tcx.mk_trait_ref(trait_def_id, substs); + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, substs); // Construct an obligation let poly_trait_ref = ty::Binder::dummy(trait_ref); diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 3d6c2119b..ec4e7f7f8 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; lint.span_help( sp, - &format!("disambiguate the method call with `({})`", self_adjusted,), + format!("disambiguate the method call with `({})`", self_adjusted,), ); } @@ -180,7 +180,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { lint.span_help( sp, - &format!( + format!( "disambiguate the associated function with `{}::{}(...)`", trait_name, segment.ident, ), diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4fd778910..9f3d35a77 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,7 +3,7 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::errors::MethodCallOnUnknownType; +use crate::errors::MethodCallOnUnknownRawPointee; use crate::FnCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,6 +16,7 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::middle::stability; +use rustc_middle::query::Providers; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; use rustc_middle::ty::GenericParamDefKind; @@ -437,7 +438,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) if self.tcx.sess.rust_2018() { - self.tcx.sess.emit_err(MethodCallOnUnknownType { span }); + self.tcx.sess.emit_err(MethodCallOnUnknownRawPointee { span }); } else { self.tcx.struct_span_lint_hir( lint::builtin::TYVAR_BEHIND_RAW_POINTER, @@ -495,7 +496,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } -pub fn provide(providers: &mut ty::query::Providers) { +pub fn provide(providers: &mut Providers) { providers.method_autoderef_steps = method_autoderef_steps; } @@ -954,7 +955,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); let trait_substs = self.fresh_item_substs(trait_def_id); - let trait_ref = self.tcx.mk_trait_ref(trait_def_id, trait_substs); + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_substs); if self.tcx.is_trait_alias(trait_def_id) { // For trait aliases, recursively assume all explicitly named traits are relevant @@ -1193,7 +1194,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pick.autoderefs += 1; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { mutbl, - unsize: pick.autoref_or_ptr_adjustment.map_or(false, |a| a.get_unsize()), + unsize: pick.autoref_or_ptr_adjustment.is_some_and(|a| a.get_unsize()), }) } @@ -1396,7 +1397,7 @@ impl<'tcx> Pick<'tcx> { // However `self.span` only // highlights the method name, so we can't use it. Also consider reusing // the code from `report_method_error()`. - lint.help(&format!( + lint.help(format!( "call with fully qualified syntax `{}(...)` to keep using the current \ method", tcx.def_path_str(self.item.def_id), @@ -1420,7 +1421,7 @@ impl<'tcx> Pick<'tcx> { } if tcx.sess.is_nightly_build() { for (candidate, feature) in &self.unstable_candidates { - lint.help(&format!( + lint.help(format!( "add `#![feature({})]` to the crate attributes to enable `{}`", feature, tcx.def_path_str(candidate.item.def_id), @@ -2032,7 +2033,7 @@ impl<'tcx> Candidate<'tcx> { // means they are safe to put into the // `WhereClausePick`. assert!( - !trait_ref.skip_binder().substs.needs_infer() + !trait_ref.skip_binder().substs.has_infer() && !trait_ref.skip_binder().substs.has_placeholders() ); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 900a6fa0d..e04cc44b5 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2,6 +2,8 @@ //! found or is otherwise invalid. use crate::errors; +use crate::errors::CandidateTraitNote; +use crate::errors::NoAssociatedItem; use crate::Expectation; use crate::FnCtxt; use rustc_ast::ast::Mutability; @@ -27,8 +29,8 @@ use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; +use rustc_middle::ty::IsSuggestable; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span}; @@ -38,6 +40,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _ use rustc_trait_selection::traits::{ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, }; +use std::borrow::Cow; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; @@ -72,7 +75,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.autoderef(span, ty).any(|(ty, _)| { info!("check deref {:?} impl FnOnce", ty); self.probe(|_| { - let trait_ref = tcx.mk_trait_ref( + let trait_ref = ty::TraitRef::new( + tcx, fn_once, [ ty, @@ -111,6 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: MethodError<'tcx>, args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, expected: Expectation<'tcx>, + trait_missing_method: bool, ) -> Option> { // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { @@ -135,6 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg_span, &mut no_match_data, expected, + trait_missing_method, ); } @@ -169,13 +175,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind, item_name ); - err.span_label(item_name.span, &format!("private {}", kind)); + err.span_label(item_name.span, format!("private {}", kind)); let sp = self .tcx .hir() .span_if_local(def_id) .unwrap_or_else(|| self.tcx.def_span(def_id)); - err.span_label(sp, &format!("private {} defined here", kind)); + err.span_label(sp, format!("private {} defined here", kind)); self.suggest_valid_traits(&mut err, out_of_scope_traits); err.emit(); } @@ -188,7 +194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { format!("the `{item_name}` method cannot be invoked on a trait object") }; - let mut err = self.sess().struct_span_err(span, &msg); + let mut err = self.sess().struct_span_err(span, msg); if !needs_mut { err.span_label(bound_span, "this has a `Sized` requirement"); } @@ -228,12 +234,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.span_suggestion_verbose( mut_ty.ty.span.shrink_to_lo(), - &msg, + msg, "mut ", Applicability::MachineApplicable, ); } else { - err.help(&msg); + err.help(msg); } } } @@ -277,12 +283,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg_span: Span, no_match_data: &mut NoMatchData<'tcx>, expected: Expectation<'tcx>, + trait_missing_method: bool, ) -> Option> { let mode = no_match_data.mode; let tcx = self.tcx; let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); - let (ty_str, ty_file) = tcx.short_ty_string(rcvr_ty); - let short_ty_str = with_forced_trimmed_paths!(rcvr_ty.to_string()); + let ((mut ty_str, ty_file), short_ty_str) = if trait_missing_method + && let ty::Dynamic(predicates, _, _) = rcvr_ty.kind() { + ((predicates.to_string(), None), with_forced_trimmed_paths!(predicates.to_string())) + } else { + (tcx.short_ty_string(rcvr_ty), with_forced_trimmed_paths!(rcvr_ty.to_string())) + }; let is_method = mode == Mode::MethodCall; let unsatisfied_predicates = &no_match_data.unsatisfied_predicates; let similar_candidate = no_match_data.similar_candidate; @@ -345,7 +356,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.map_or(false, |def_id| { + let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) }) && item_name.name == Symbol::intern("write_fmt"); @@ -354,34 +365,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { self.suggest_missing_writer(rcvr_ty, args) } else { - struct_span_err!( - tcx.sess, + tcx.sess.create_err(NoAssociatedItem { span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", item_kind, item_name, - rcvr_ty.prefix_string(self.tcx), - ty_str_reported, - ) + ty_prefix: if trait_missing_method { + // FIXME(mu001999) E0599 maybe not suitable here because it is for types + Cow::from("trait") + } else { + rcvr_ty.prefix_string(self.tcx) + }, + ty_str: ty_str_reported, + trait_missing_method, + }) }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } - let ty_str = if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { - short_ty_str - } else { - ty_str - }; + + if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { + ty_str = short_ty_str; + } + if let Some(file) = ty_file { - err.note(&format!("the full type name has been written to '{}'", file.display(),)); + err.note(format!("the full type name has been written to '{}'", file.display(),)); } if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); } if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { - err.help(&format!( + err.help(format!( "method `poll` found on `Pin<&mut {ty_str}>`, \ see documentation for `std::pin::Pin`" )); @@ -510,7 +524,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) { let iterator_trait = self.tcx.def_path_str(iterator_trait); - err.note(&format!( + err.note(format!( "`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement" )); } @@ -810,7 +824,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (sp, label) in span_labels { span.push_span_label(sp, label); } - err.span_note(span, &msg); + err.span_note(span, msg); unsatisfied_bounds = true; } @@ -867,7 +881,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligations.sort(); err.span_suggestion_verbose( span, - &format!( + format!( "consider restricting the type parameter{s} to satisfy the \ trait bound{s}", s = pluralize!(obligations.len()) @@ -912,13 +926,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { but its trait bounds were not satisfied" ) }); - err.set_primary_message(&primary_message); + err.set_primary_message(primary_message); if let Some(label) = label { custom_span_label = true; err.span_label(span, label); } if !bound_list.is_empty() { - err.note(&format!( + err.note(format!( "the following trait bounds were not satisfied:\n{bound_list}" )); } @@ -1002,7 +1016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { "".to_string() }; - err.note(&format!( + err.note(format!( "the {item_kind} was found for\n{}{}", type_candidates, additional_types )); @@ -1044,12 +1058,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); + self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name); bound_spans.sort(); bound_spans.dedup(); for (span, msg) in bound_spans.into_iter() { - err.span_label(span, &msg); + err.span_label(span, msg); } if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params { @@ -1066,6 +1080,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &static_candidates, unsatisfied_bounds, expected.only_has_type(self), + trait_missing_method, ); } @@ -1119,7 +1134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion( span, - &format!( + format!( "there is {} {} with a similar name", self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id), self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) @@ -1131,7 +1146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected); + self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected); return Some(err); } @@ -1203,9 +1218,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(note_span) = note_span { // We have a span pointing to the method. Show note with snippet. - err.span_note(note_span, ¬e_str); + err.span_note(note_span, note_str); } else { - err.note(¬e_str); + err.note(note_str); } if let Some(sugg_span) = sugg_span && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { @@ -1243,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(item) = self.associated_value(trait_did, item_name) else { continue }; let item_span = self.tcx.def_span(item.def_id); let idx = if sources.len() > 1 { - let msg = &format!( + let msg = format!( "candidate #{} is defined in the trait `{}`", idx + 1, self.tcx.def_path_str(trait_did) @@ -1251,7 +1266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_note(item_span, msg); Some(idx + 1) } else { - let msg = &format!( + let msg = format!( "the candidate is defined in the trait `{}`", self.tcx.def_path_str(trait_did) ); @@ -1278,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if sources.len() > limit { - err.note(&format!("and {} others", sources.len() - limit)); + err.note(format!("and {} others", sources.len() - limit)); } } @@ -1402,7 +1417,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { applicability, ); } else { - err.help(&format!("try with `{}::{}`", ty_str, item_name,)); + err.help(format!("try with `{}::{}`", ty_str, item_name,)); } } @@ -1436,7 +1451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.is_fn_ty(field_ty, span) { let expr_span = expr.span.to(item_name.span); err.multipart_suggestion( - &format!( + format!( "to call the function stored in `{}`, \ surround the field access with parentheses", item_name, @@ -1507,7 +1522,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span_included = match parent_expr.kind { hir::ExprKind::Struct(_, eps, _) => { - eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + eps.len() > 0 && eps.last().is_some_and(|ep| ep.span.contains(span)) } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. hir::ExprKind::Call(ref func, ..) => func.span.contains(span), @@ -1530,7 +1545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); - tcx.sess.emit_err(errors::MissingParentheseInRange { + tcx.sess.emit_err(errors::MissingParenthesesInRange { span, ty_str: ty_str.to_string(), method_name: item_name.as_str().to_string(), @@ -1612,7 +1627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); err.span_suggestion( lit.span, - &format!( + format!( "you must specify a concrete type for this numeric value, \ like `{}`", concrete_type @@ -1648,7 +1663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // account for `let x: _ = 42;` // ^^^ type_span, - &msg, + msg, format!(": {concrete_type}"), Applicability::MaybeIncorrect, ); @@ -1766,7 +1781,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ProbeScope::TraitsInScope, return_type, ) - .map_or(false, |pick| { + .is_ok_and(|pick| { !never_mention_traits .iter() .flatten() @@ -1804,7 +1819,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_for_inner_self( + fn suggest_unwrapping_inner_self( &self, err: &mut Diagnostic, source: SelfSource<'tcx>, @@ -1861,7 +1876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let self_ty = field.ty(tcx, substs); err.span_note( tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{self_ty}`"), + format!("the method `{item_name}` exists on the type `{self_ty}`"), ); let (article, kind, variant, question) = if tcx.is_diagnostic_item(sym::Result, kind.did()) { @@ -1975,7 +1990,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_note( tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{ty}`"), + format!("the method `{item_name}` exists on the type `{ty}`"), ); } } @@ -2046,7 +2061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pluralize!(preds.len()), ) }; - err.span_note(spans, &msg); + err.span_note(spans, msg); } let preds: Vec<_> = errors @@ -2068,7 +2083,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut derives = Vec::<(String, Span, Symbol)>::new(); let mut traits = Vec::new(); for (pred, _, _) in unsatisfied_predicates { - let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() else { continue }; + let Some(ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))) = + pred.kind().no_bound_vars() + else { + continue + }; let adt = match trait_pred.self_ty().ty_adt_def() { Some(adt) if adt.did().is_local() => adt, _ => continue, @@ -2089,18 +2108,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if can_derive { let self_name = trait_pred.self_ty().to_string(); let self_span = self.tcx.def_span(adt.did()); - if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() { - for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref()) + for super_trait in + supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) + { + if let Some(parent_diagnostic_name) = + self.tcx.get_diagnostic_name(super_trait.def_id()) { - if let Some(parent_diagnostic_name) = - self.tcx.get_diagnostic_name(super_trait.def_id()) - { - derives.push(( - self_name.clone(), - self_span, - parent_diagnostic_name, - )); - } + derives.push((self_name.clone(), self_span, parent_diagnostic_name)); } } derives.push((self_name, self_span, diagnostic_name)); @@ -2147,21 +2161,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } err.span_note( span, - &format!("the trait{} {} must be implemented", pluralize!(len), names), + format!("the trait{} {} must be implemented", pluralize!(len), names), ); } for (self_name, self_span, traits) in &derives_grouped { err.span_suggestion_verbose( self_span.shrink_to_lo(), - &format!("consider annotating `{}` with `#[derive({})]`", self_name, traits), + format!("consider annotating `{}` with `#[derive({})]`", self_name, traits), format!("#[derive({})]\n", traits), Applicability::MaybeIncorrect, ); } } - fn check_for_deref_method( + fn note_derefed_ty_has_method( &self, err: &mut Diagnostic, self_source: SelfSource<'tcx>, @@ -2197,7 +2211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Adt(_, _) | ty::Str - | ty::Alias(ty::Projection, _) + | ty::Alias(ty::Projection | ty::Inherent, _) | ty::Param(_) => format!("{deref_ty}"), // we need to test something like <&[_]>::len or <(&[u32])>::len // and Vec::function(); @@ -2300,7 +2314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestions( span, - &msg, + msg, path_strings.chain(glob_path_strings), Applicability::MaybeIncorrect, ); @@ -2332,7 +2346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_use_candidates(err, msg, candidates); if let Some(did) = edition_fix { - err.note(&format!( + err.note(format!( "'{}' is included in the prelude starting in Edition 2021", with_crate_prefix!(self.tcx.def_path_str(did)) )); @@ -2361,6 +2375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { static_candidates: &[CandidateSource], unsatisfied_bounds: bool, return_type: Option>, + trait_missing_method: bool, ) { let mut alt_rcvr_sugg = false; if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { @@ -2400,7 +2415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if pick.autoderefs == 0 && !skip { err.span_label( pick.item.ident(self.tcx).span, - &format!("the method is available for `{}` here", rcvr_ty), + format!("the method is available for `{}` here", rcvr_ty), ); } break; @@ -2439,14 +2454,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // implement the `AsRef` trait. let skip = skippable.contains(&did) || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)) - || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len); + || inputs_len.is_some_and(|inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box` as a receiver if it only works because of // an autoderef to `&self` if pick.autoderefs == 0 && !skip { err.span_label( pick.item.ident(self.tcx).span, - &format!("the method is available for `{}` here", new_rcvr_t), + format!("the method is available for `{}` here", new_rcvr_t), ); err.multipart_suggestion( "consider wrapping the receiver expression with the \ @@ -2584,11 +2599,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, _ => None, }; - err.help(if param_type.is_some() { - "items from traits can only be used if the type parameter is bounded by the trait" - } else { - "items from traits can only be used if the trait is implemented and in scope" - }); + if !trait_missing_method { + err.help(if param_type.is_some() { + "items from traits can only be used if the type parameter is bounded by the trait" + } else { + "items from traits can only be used if the trait is implemented and in scope" + }); + } + let candidates_len = candidates.len(); let message = |action| { format!( @@ -2619,47 +2637,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Nothing, } let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); - let (sp, mut introducer) = if let Some(span) = - ast_generics.bounds_span_for_suggestions(def_id) - { - (span, Introducer::Plus) - } else if let Some(colon_span) = param.colon_span { - (colon_span.shrink_to_hi(), Introducer::Nothing) - } else { - (param.span.shrink_to_hi(), Introducer::Colon) - }; - if matches!( - param.kind, - hir::GenericParamKind::Type { synthetic: true, .. }, - ) { - introducer = Introducer::Plus - } let trait_def_ids: FxHashSet = ast_generics .bounds_for_param(def_id) .flat_map(|bp| bp.bounds.iter()) .filter_map(|bound| bound.trait_ref()?.trait_def_id()) .collect(); - if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { - err.span_suggestions( - sp, - &message(format!( - "restrict type parameter `{}` with", - param.name.ident(), - )), + if candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { + return; + } + let msg = message(format!( + "restrict type parameter `{}` with", + param.name.ident(), + )); + let bounds_span = ast_generics.bounds_span_for_suggestions(def_id); + if rcvr_ty.is_ref() && param.is_impl_trait() && bounds_span.is_some() { + err.multipart_suggestions( + msg, candidates.iter().map(|t| { - format!( - "{} {}", - match introducer { - Introducer::Plus => " +", - Introducer::Colon => ":", - Introducer::Nothing => "", - }, - self.tcx.def_path_str(t.def_id), - ) + vec![ + (param.span.shrink_to_lo(), "(".to_string()), + ( + bounds_span.unwrap(), + format!(" + {})", self.tcx.def_path_str(t.def_id)), + ), + ] }), Applicability::MaybeIncorrect, ); + return; } + + let (sp, introducer) = if let Some(span) = bounds_span { + (span, Introducer::Plus) + } else if let Some(colon_span) = param.colon_span { + (colon_span.shrink_to_hi(), Introducer::Nothing) + } else if param.is_impl_trait() { + (param.span.shrink_to_hi(), Introducer::Plus) + } else { + (param.span.shrink_to_hi(), Introducer::Colon) + }; + + err.span_suggestions( + sp, + msg, + candidates.iter().map(|t| { + format!( + "{} {}", + match introducer { + Introducer::Plus => " +", + Introducer::Colon => ":", + Introducer::Nothing => "", + }, + self.tcx.def_path_str(t.def_id) + ) + }), + Applicability::MaybeIncorrect, + ); return; } Node::Item(hir::Item { @@ -2674,7 +2707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; err.span_suggestions( sp, - &message(format!("add {} supertrait for", article)), + message(format!("add {} supertrait for", article)), candidates.iter().map(|t| { format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) }), @@ -2708,7 +2741,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity(); let imp_simp = simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup); - imp_simp.map_or(false, |s| s == simp_rcvr_ty) + imp_simp.is_some_and(|s| s == simp_rcvr_ty) }) { explicitly_negative.push(candidate); @@ -2722,27 +2755,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (candidates, Vec::new()) }; - let action = if let Some(param) = param_type { - format!("restrict type parameter `{}` with", param) - } else { - // FIXME: it might only need to be imported into scope, not implemented. - "implement".to_string() - }; match &potential_candidates[..] { [] => {} [trait_info] if trait_info.def_id.is_local() => { - err.span_note( - self.tcx.def_span(trait_info.def_id), - &format!( - "`{}` defines an item `{}`, perhaps you need to {} it", - self.tcx.def_path_str(trait_info.def_id), - item_name, - action - ), - ); + err.subdiagnostic(CandidateTraitNote { + span: self.tcx.def_span(trait_info.def_id), + trait_name: self.tcx.def_path_str(trait_info.def_id), + item_name, + action_or_ty: if trait_missing_method { + "NONE".to_string() + } else { + param_type.map_or_else( + || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. + ToString::to_string, + ) + }, + }); } trait_infos => { - let mut msg = message(action); + let mut msg = message(param_type.map_or_else( + || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. + |param| format!("restrict type parameter `{}` with", param), + )); for (i, trait_info) in trait_infos.iter().enumerate() { msg.push_str(&format!( "\ncandidate #{}: `{}`", @@ -2750,7 +2784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.def_path_str(trait_info.def_id), )); } - err.note(&msg); + err.note(msg); } } match &explicitly_negative[..] { @@ -2761,7 +2795,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.def_path_str(trait_info.def_id), item_name ); - err.note(&msg); + err.note(msg); } trait_infos => { let mut msg = format!( @@ -2771,7 +2805,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for trait_info in trait_infos { msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id))); } - err.note(&msg); + err.note(msg); } } } @@ -2823,7 +2857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_args.len() == args.len() + 1 { err.span_suggestion_verbose( method_name.span.shrink_to_hi(), - &format!("try calling `{}` instead", new_name.name.as_str()), + format!("try calling `{}` instead", new_name.name.as_str()), "_else", Applicability::MaybeIncorrect, ); @@ -2845,7 +2879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match ty.kind() { ty::Adt(def, _) => def.did().is_local(), ty::Foreign(did) => did.is_local(), - ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()), + ty::Dynamic(tr, ..) => tr.principal().is_some_and(|d| d.def_id().is_local()), ty::Param(_) => true, // Everything else (primitive types, etc.) is effectively @@ -2943,7 +2977,7 @@ fn print_disambiguation_help<'tcx>( }; err.span_suggestion_verbose( span, - &format!( + format!( "disambiguate the {} for {}", def_kind_descr, if let Some(candidate) = candidate { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index a52c94cb0..b8bf2b691 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -390,7 +390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .is_ok() { - let msg = &format!( + let msg = format!( "`{}{}` can be used on `{}` if you dereference the left-hand side", op.node.as_str(), match is_assign { @@ -408,7 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - let is_compatible = |lhs_ty, rhs_ty| { + let is_compatible_after_call = |lhs_ty, rhs_ty| { self.lookup_op_method( lhs_ty, Some((rhs_expr, rhs_ty)), @@ -416,6 +416,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, ) .is_ok() + // Suggest calling even if, after calling, the types don't + // implement the operator, since it'll lead to better + // diagnostics later. + || self.can_eq(self.param_env, lhs_ty, rhs_ty) }; // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest @@ -436,16 +440,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggest_deref_binop(*lhs_deref_ty); } } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { - is_compatible(lhs_ty, rhs_ty) + is_compatible_after_call(lhs_ty, rhs_ty) }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| { - is_compatible(lhs_ty, rhs_ty) + is_compatible_after_call(lhs_ty, rhs_ty) }) || self.suggest_two_fn_call( &mut err, rhs_expr, rhs_ty, lhs_expr, lhs_ty, - |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty), + |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty), ) { // Cool } @@ -511,7 +515,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // When we know that a missing bound is responsible, we don't show // this note as it is redundant. - err.note(&format!( + err.note(format!( "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" )); } @@ -545,9 +549,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let to_owned_msg = "create an owned `String` from a string reference"; let string_type = self.tcx.lang_items().string(); - let is_std_string = |ty: Ty<'tcx>| { - ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type) - }; + let is_std_string = + |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|ty_def| Some(ty_def.did()) == string_type); match (lhs_ty.kind(), rhs_ty.kind()) { (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str @@ -686,7 +689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.span_suggestion( ex.span, - &format!( + format!( "you may have meant the maximum value of `{actual}`", ), format!("{actual}::MAX"), @@ -719,7 +722,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Op::Binary(op, _) => op.span, Op::Unary(_, span) => span, }; - let (opname, trait_did) = lang_item_for_op(self.tcx, op, span); + let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else { + // Bail if the operator trait is not defined. + return Err(vec![]); + }; debug!( "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", @@ -753,24 +759,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, traits::BinOp { rhs_span: opt_rhs_expr.map(|expr| expr.span), - is_lit: opt_rhs_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + is_lit: opt_rhs_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), output_ty: expected.only_has_type(self), }, ); - let method = trait_did.and_then(|trait_did| { - self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, Some(input_types)) - }); - - match (method, trait_did) { - (Some(ok), _) => { + let method = self.lookup_method_in_trait( + cause.clone(), + opname, + trait_did, + lhs_ty, + Some(input_types), + ); + match method { + Some(ok) => { let method = self.register_infer_ok_obligations(ok); self.select_obligations_where_possible(|_| {}); Ok(method) } - (None, None) => Err(vec![]), - (None, Some(trait_did)) => { + None => { + // This path may do some inference, so make sure we've really + // doomed compilation so as to not accidentally stabilize new + // inference or something here... + self.tcx.sess.delay_span_bug(span, "this path really should be doomed..."); + // Guide inference for the RHS expression if it's provided -- + // this will allow us to better error reporting, at the expense + // of making some error messages a bit more specific. + if let Some((rhs_expr, rhs_ty)) = opt_rhs + && rhs_ty.is_ty_var() + { + self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None); + } + let (obligation, _) = self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types)); // FIXME: This should potentially just add the obligation to the `FnCtxt` diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index af0bd26de..5af955d31 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,4 +1,4 @@ -use crate::{FnCtxt, RawTy}; +use crate::{errors, FnCtxt, RawTy}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ @@ -517,7 +517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn endpoint_has_type(&self, err: &mut Diagnostic, span: Span, ty: Ty<'_>) { if !ty.references_error() { - err.span_label(span, &format!("this is of type `{}`", ty)); + err.span_label(span, format!("this is of type `{}`", ty)); } } @@ -544,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("this is of type `{}` but it should be `char` or numeric", ty) }; let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| { - err.span_label(first_span, &msg(first_ty)); + err.span_label(first_span, msg(first_ty)); if let Some((_, ty, sp)) = second { let ty = self.resolve_vars_if_possible(ty); self.endpoint_has_type(&mut err, sp, ty); @@ -552,8 +552,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; match (lhs, rhs) { (Some((true, lhs_ty, lhs_sp)), Some((true, rhs_ty, rhs_sp))) => { - err.span_label(lhs_sp, &msg(lhs_ty)); - err.span_label(rhs_sp, &msg(rhs_ty)); + err.span_label(lhs_sp, msg(lhs_ty)); + err.span_label(rhs_sp, msg(rhs_ty)); } (Some((true, lhs_ty, lhs_sp)), rhs) => one_side_err(lhs_sp, lhs_ty, rhs), (lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs), @@ -651,7 +651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); let pre = if in_match { "in the same arm, " } else { "" }; - err.note(&format!("{}a binding must have the same type in all alternatives", pre)); + err.note(format!("{}a binding must have the same type in all alternatives", pre)); self.suggest_adding_missing_ref_or_removing_ref( &mut err, span, @@ -958,11 +958,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let pat_span = pat.span; if let Some(span) = self.tcx.hir().res_span(pat_res) { - e.span_label(span, &format!("{} defined here", res.descr())); + e.span_label(span, format!("{} defined here", res.descr())); if let [hir::PathSegment { ident, .. }] = &*segments { e.span_label( pat_span, - &format!( + format!( "`{}` is interpreted as {} {}, not a new binding", ident, res.article(), @@ -1158,7 +1158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label( last_subpat_span, - &format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len()), + format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len()), ); if self.tcx.sess.source_map().is_multiline(qpath.span().between(last_subpat_span)) { err.span_label(qpath.span(), ""); @@ -1171,7 +1171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } err.span_label( last_field_def_span, - &format!("{} has {} field{}", res.descr(), fields.len(), fields_ending), + format!("{} has {} field{}", res.descr(), fields.len(), fields_ending), ); // Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`. @@ -1410,12 +1410,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Report an error if an incorrect number of fields was specified. if adt.is_union() { if fields.len() != 1 { - tcx.sess - .struct_span_err(pat.span, "union patterns should have exactly one field") - .emit(); + tcx.sess.emit_err(errors::UnionPatMultipleFields { span: pat.span }); } if has_rest_pat { - tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); + tcx.sess.emit_err(errors::UnionPatDotDot { span: pat.span }); } } else if !unmentioned_fields.is_empty() { let accessible_unmentioned_fields: Vec<_> = unmentioned_fields @@ -1643,7 +1641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let unmentioned_field = unmentioned_fields[0].1.name; err.span_suggestion_short( pat_field.ident.span, - &format!( + format!( "`{}` has a field named `{}`", tcx.def_path_str(variant.def_id), unmentioned_field @@ -1659,7 +1657,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if tcx.sess.teach(&err.get_code().unwrap()) { err.note( "This error indicates that a struct pattern attempted to \ - extract a non-existent field from a struct. Struct fields \ + extract a nonexistent field from a struct. Struct fields \ are identified by the name used before the colon : so struct \ patterns should resemble the declaration of the struct type \ being matched.\n\n\ @@ -1833,7 +1831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint.help( "ensure that all fields are mentioned explicitly by adding the suggested fields", ); - lint.note(&format!( + lint.note(format!( "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", ty, )); @@ -1897,7 +1895,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; err.span_suggestion( sp, - &format!( + format!( "include the missing field{} in the pattern{}", pluralize!(len), if have_inaccessible_fields { " and ignore the inaccessible fields" } else { "" } @@ -1924,7 +1922,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_suggestion( sp, - &format!( + format!( "if you don't care about {these} missing field{s}, you can explicitly ignore {them}", these = pluralize!("this", len), s = pluralize!(len), diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 2cca45de5..e2b1dc007 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -73,16 +73,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(ty); let mut err = self.tcx.sess.struct_span_err( span, - &format!("negative integers cannot be used to index on a `{ty}`"), + format!("negative integers cannot be used to index on a `{ty}`"), ); - err.span_label(span, &format!("cannot use a negative integer for indexing on `{ty}`")); + err.span_label(span, format!("cannot use a negative integer for indexing on `{ty}`")); if let (hir::ExprKind::Path(..), Ok(snippet)) = (&base_expr.kind, self.tcx.sess.source_map().span_to_snippet(base_expr.span)) { // `foo[-1]` to `foo[foo.len() - 1]` err.span_suggestion_verbose( span.shrink_to_lo(), - &format!( + format!( "to access an element starting from the end of the `{ty}`, compute the index", ), format!("{snippet}.len() "), @@ -200,9 +200,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option>> { debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); - let (imm_tr, imm_op) = match op { + let (Some(imm_tr), imm_op) = (match op { PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), + }) else { + // Bail if `Deref` or `Index` isn't defined. + return None; }; // If the lang item was declared incorrectly, stop here so that we don't @@ -219,15 +222,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - imm_tr.and_then(|trait_did| { - self.lookup_method_in_trait( - self.misc(span), - Ident::with_dummy_span(imm_op), - trait_did, - base_ty, - Some(arg_tys), - ) - }) + self.lookup_method_in_trait( + self.misc(span), + Ident::with_dummy_span(imm_op), + imm_tr, + base_ty, + Some(arg_tys), + ) } fn try_mutable_overloaded_place_op( @@ -239,9 +240,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option>> { debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); - let (mut_tr, mut_op) = match op { + let (Some(mut_tr), mut_op) = (match op { PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut), + }) else { + // Bail if `DerefMut` or `IndexMut` isn't defined. + return None; }; // If the lang item was declared incorrectly, stop here so that we don't @@ -258,15 +262,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - mut_tr.and_then(|trait_did| { - self.lookup_method_in_trait( - self.misc(span), - Ident::with_dummy_span(mut_op), - trait_did, - base_ty, - Some(arg_tys), - ) - }) + self.lookup_method_in_trait( + self.misc(span), + Ident::with_dummy_span(mut_op), + mut_tr, + base_ty, + Some(arg_tys), + ) } /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` @@ -327,7 +329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). // This helps avoid accidental drops. if inside_union - && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop()) + && source.ty_adt_def().is_some_and(|adt| adt.is_manually_drop()) { let mut err = self.tcx.sess.struct_span_err( expr.span, diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 41a6ad80b..9458099f5 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -223,7 +223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id); if should_do_rust_2021_incompatible_closure_captures_analysis(self.tcx, closure_hir_id) { - self.perform_2229_migration_anaysis(closure_def_id, body_id, capture_clause, span); + self.perform_2229_migration_analysis(closure_def_id, body_id, capture_clause, span); } let after_feature_tys = self.final_upvar_tys(closure_def_id); @@ -713,7 +713,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.sess.delay_span_bug( closure_span, - &format!( + format!( "two identical projections: ({:?}, {:?})", capture1.place.projections, capture2.place.projections ), @@ -731,7 +731,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Perform the migration analysis for RFC 2229, and emit lint /// `disjoint_capture_drop_reorder` if needed. - fn perform_2229_migration_anaysis( + fn perform_2229_migration_analysis( &self, closure_def_id: LocalDefId, body_id: hir::BodyId, @@ -863,7 +863,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(), - &diagnostic_msg, + diagnostic_msg, format!("\n{indent}{migration_string};"), Applicability::MachineApplicable, ); @@ -874,7 +874,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // directly after the `{`. lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(), - &diagnostic_msg, + diagnostic_msg, format!(" {migration_string};"), Applicability::MachineApplicable, ); @@ -882,7 +882,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is a closure without braces around the body. // We add braces to add the `let` before the body. lint.multipart_suggestion( - &diagnostic_msg, + diagnostic_msg, vec![ (closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")), (closure_body_span.shrink_to_hi(), " }".to_string()), @@ -893,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { lint.span_suggestion( closure_span, - &diagnostic_msg, + diagnostic_msg, migration_string, Applicability::HasPlaceholders ); @@ -972,15 +972,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut obligations_should_hold = Vec::new(); // Checks if a root variable implements any of the auto traits for check_trait in auto_traits_def_id.iter() { - obligations_should_hold.push( - check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait(check_trait, [ty], self.param_env) - .must_apply_modulo_regions() - }) - .unwrap_or(false), - ); + obligations_should_hold.push(check_trait.is_some_and(|check_trait| { + self.infcx + .type_implements_trait(check_trait, [ty], self.param_env) + .must_apply_modulo_regions() + })); } let mut problematic_captures = FxHashMap::default(); @@ -996,15 +992,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Checks if a capture implements any of the auto traits let mut obligations_holds_for_capture = Vec::new(); for check_trait in auto_traits_def_id.iter() { - obligations_holds_for_capture.push( - check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait(check_trait, [ty], self.param_env) - .must_apply_modulo_regions() - }) - .unwrap_or(false), - ); + obligations_holds_for_capture.push(check_trait.is_some_and(|check_trait| { + self.infcx + .type_implements_trait(check_trait, [ty], self.param_env) + .must_apply_modulo_regions() + })); } let mut capture_problems = FxHashSet::default(); @@ -1519,7 +1511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); - diag.span_note(span, &output_str); + diag.span_note(span, output_str); } diag.emit(); } @@ -1560,13 +1552,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { multi_span.push_span_label(path_span, path_label); multi_span.push_span_label(capture_kind_span, capture_kind_label); - diag.span_note(multi_span, &output_str); + diag.span_note(multi_span, output_str); } else { let span = capture_info .path_expr_id .map_or(closure_span, |e| self.tcx.hir().span(e)); - diag.span_note(span, &output_str); + diag.span_note(span, output_str); }; } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index e876fa275..0f21fc1e6 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -5,11 +5,10 @@ use crate::FnCtxt; use hir::def_id::LocalDefId; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::Place as HirPlace; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; @@ -70,6 +69,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.visit_user_provided_tys(); wbcx.visit_user_provided_sigs(); wbcx.visit_generator_interior_types(); + wbcx.visit_offset_of_container_types(); wbcx.typeck_results.rvalue_scopes = mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes); @@ -82,10 +82,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.typeck_results.treat_byte_string_as_slice = mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); - if let Some(e) = self.tainted_by_errors() { - wbcx.typeck_results.tainted_by_errors = Some(e); - } - debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results); self.tcx.arena.alloc(wbcx.typeck_results) @@ -118,12 +114,21 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ) -> WritebackCx<'cx, 'tcx> { let owner = body.id().hir_id.owner; - WritebackCx { + let mut wbcx = WritebackCx { fcx, typeck_results: ty::TypeckResults::new(owner), body, rustc_dump_user_substs, + }; + + // HACK: We specifically don't want the (opaque) error from tainting our + // inference context. That'll prevent us from doing opaque type inference + // later on in borrowck, which affects diagnostic spans pretty negatively. + if let Some(e) = fcx.tainted_by_errors() { + wbcx.typeck_results.tainted_by_errors = Some(e); } + + wbcx } fn tcx(&self) -> TyCtxt<'tcx> { @@ -132,7 +137,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn write_ty_to_typeck_results(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) { debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty); - assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions()); + assert!(!ty.has_infer() && !ty.has_placeholders() && !ty.has_free_regions()); self.typeck_results.node_types_mut().insert(hir_id, ty); } @@ -226,7 +231,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // When encountering `return [0][0]` outside of a `fn` body we can encounter a base // that isn't in the type table. We assume more relevant errors have already been // emitted, so we delay an ICE if none have. (#64638) - self.tcx().sess.delay_span_bug(e.span, &format!("bad base: `{:?}`", base)); + self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{:?}`", base)); } if let Some(ty::Ref(_, base_ty, _)) = base_ty { let index_ty = typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| { @@ -236,7 +241,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // error has been emitted. (#64638) self.fcx.tcx.ty_error_with_message( e.span, - &format!("bad index {:?} for base: `{:?}`", index, base), + format!("bad index {:?} for base: `{:?}`", index, base), ) }); let index_ty = self.fcx.resolve_vars_if_possible(index_ty); @@ -295,7 +300,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.visit_field_id(field.hir_id); } } - hir::ExprKind::Field(..) => { + hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => { self.visit_field_id(e.hir_id); } hir::ExprKind::ConstBlock(anon_const) => { @@ -490,7 +495,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let err = self .tcx() .sess - .struct_span_err(span, &format!("user substs: {:?}", user_substs)); + .struct_span_err(span, format!("user substs: {:?}", user_substs)); err.buffer(&mut errors_buffer); } } @@ -507,7 +512,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| { let hir_id = hir::HirId { owner: common_hir_owner, local_id }; - if cfg!(debug_assertions) && c_ty.needs_infer() { + if cfg!(debug_assertions) && c_ty.has_infer() { span_bug!( hir_id.to_span(self.fcx.tcx), "writeback: `{:?}` has inference variables", @@ -526,7 +531,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.user_provided_sigs.extend( fcx_typeck_results.user_provided_sigs.items().map(|(&def_id, c_sig)| { - if cfg!(debug_assertions) && c_sig.needs_infer() { + if cfg!(debug_assertions) && c_sig.has_infer() { span_bug!( self.fcx.tcx.def_span(def_id), "writeback: `{:?}` has inference variables", @@ -578,13 +583,26 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { continue; } - let hidden_type = hidden_type.remap_generic_params_to_declaration_params( - opaque_type_key, - self.fcx.infcx.tcx, - true, - ); - - self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); + let hidden_type = + self.tcx().erase_regions(hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + self.tcx(), + true, + )); + + if let Some(last_opaque_ty) = self + .typeck_results + .concrete_opaque_types + .insert(opaque_type_key.def_id, hidden_type) + && last_opaque_ty.ty != hidden_type.ty + { + hidden_type + .report_mismatch(&last_opaque_ty, opaque_type_key.def_id, self.tcx()) + .stash( + self.tcx().def_span(opaque_type_key.def_id), + StashKey::OpaqueHiddenTypeMismatch, + ); + } } } @@ -617,7 +635,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if let Some(substs) = self.fcx.typeck_results.borrow().node_substs_opt(hir_id) { let substs = self.resolve(substs, &span); debug!("write_substs_to_tcx({:?}, {:?})", hir_id, substs); - assert!(!substs.needs_infer() && !substs.has_placeholders()); + assert!(!substs.has_infer() && !substs.has_placeholders()); self.typeck_results.node_substs_mut().insert(hir_id, substs); } } @@ -682,13 +700,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_offset_of_container_types(&mut self) { + let fcx_typeck_results = self.fcx.typeck_results.borrow(); + assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); + let common_hir_owner = fcx_typeck_results.hir_owner; + + for (local_id, &(container, ref indices)) in + fcx_typeck_results.offset_of_data().items_in_stable_order() + { + let hir_id = hir::HirId { owner: common_hir_owner, local_id }; + let container = self.resolve(container, &hir_id); + self.typeck_results.offset_of_data_mut().insert(hir_id, (container, indices.clone())); + } + } + fn resolve(&mut self, x: T, span: &dyn Locatable) -> T where T: TypeFoldable>, { let mut resolver = Resolver::new(self.fcx, span, self.body); let x = x.fold_with(&mut resolver); - if cfg!(debug_assertions) && x.needs_infer() { + if cfg!(debug_assertions) && x.has_infer() { span_bug!(span.to_span(self.fcx.tcx), "writeback: `{:?}` has inference variables", x); } @@ -722,8 +754,7 @@ impl Locatable for hir::HirId { /// The Resolver. This is the type folding engine that detects /// unresolved types and so forth. struct Resolver<'cx, 'tcx> { - tcx: TyCtxt<'tcx>, - infcx: &'cx InferCtxt<'tcx>, + fcx: &'cx FnCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, @@ -737,18 +768,18 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, ) -> Resolver<'cx, 'tcx> { - Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: None } + Resolver { fcx, span, body, replaced_with_error: None } } fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { - match self.tcx.sess.has_errors() { + match self.fcx.tcx.sess.has_errors() { Some(e) => e, None => self - .infcx + .fcx .err_ctxt() .emit_inference_failure_err( - self.tcx.hir().body_owner_def_id(self.body.id()), - self.span.to_span(self.tcx), + self.fcx.tcx.hir().body_owner_def_id(self.body.id()), + self.span.to_span(self.fcx.tcx), p.into(), E0282, false, @@ -780,40 +811,46 @@ impl<'tcx> TypeFolder> for EraseEarlyRegions<'tcx> { impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { fn interner(&self) -> TyCtxt<'tcx> { - self.tcx + self.fcx.tcx } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match self.infcx.fully_resolve(t) { + match self.fcx.fully_resolve(t) { + Ok(t) if self.fcx.tcx.trait_solver_next() => { + // We must normalize erasing regions here, since later lints + // expect that types that show up in the typeck are fully + // normalized. + self.fcx.tcx.try_normalize_erasing_regions(self.fcx.param_env, t).unwrap_or(t) + } Ok(t) => { // Do not anonymize late-bound regions // (e.g. keep `for<'a>` named `for<'a>`). // This allows NLL to generate error messages that // refer to the higher-ranked lifetime names written by the user. - EraseEarlyRegions { tcx: self.tcx }.fold_ty(t) + EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t) } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); let e = self.report_error(t); self.replaced_with_error = Some(e); - self.interner().ty_error(e) + self.fcx.tcx.ty_error(e) } } } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { debug_assert!(!r.is_late_bound(), "Should not be resolving bound region."); - self.tcx.lifetimes.re_erased + self.fcx.tcx.lifetimes.re_erased } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match self.infcx.fully_resolve(ct) { - Ok(ct) => self.tcx.erase_regions(ct), + match self.fcx.fully_resolve(ct) { + Ok(ct) => self.fcx.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); let e = self.report_error(ct); self.replaced_with_error = Some(e); - self.interner().const_error_with_guaranteed(ct.ty(), e) + self.fcx.tcx.const_error(ct.ty(), e) } } } -- cgit v1.2.3