From 4547b622d8d29df964fa2914213088b148c498fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:18:32 +0200 Subject: Merging upstream version 1.67.1+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_hir_typeck/src/_match.rs | 160 +++--- compiler/rustc_hir_typeck/src/autoderef.rs | 19 +- compiler/rustc_hir_typeck/src/callee.rs | 70 +-- compiler/rustc_hir_typeck/src/cast.rs | 52 +- compiler/rustc_hir_typeck/src/check.rs | 139 +----- compiler/rustc_hir_typeck/src/closure.rs | 250 +++++----- compiler/rustc_hir_typeck/src/coercion.rs | 174 +++---- compiler/rustc_hir_typeck/src/demand.rs | 129 ++--- compiler/rustc_hir_typeck/src/errors.rs | 80 ++- compiler/rustc_hir_typeck/src/expectation.rs | 6 +- compiler/rustc_hir_typeck/src/expr.rs | 148 +++--- compiler/rustc_hir_typeck/src/expr_use_visitor.rs | 43 +- compiler/rustc_hir_typeck/src/fallback.rs | 28 +- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 249 ++++----- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 86 ++-- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 66 +-- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 149 ++++-- .../generator_interior/drop_ranges/cfg_build.rs | 63 ++- .../src/generator_interior/drop_ranges/mod.rs | 4 +- .../rustc_hir_typeck/src/generator_interior/mod.rs | 129 +++-- compiler/rustc_hir_typeck/src/inherited.rs | 82 +-- compiler/rustc_hir_typeck/src/intrinsicck.rs | 7 +- compiler/rustc_hir_typeck/src/lib.rs | 100 ++-- .../rustc_hir_typeck/src/mem_categorization.rs | 2 +- compiler/rustc_hir_typeck/src/method/confirm.rs | 26 +- compiler/rustc_hir_typeck/src/method/mod.rs | 169 ++++--- .../rustc_hir_typeck/src/method/prelude2021.rs | 28 +- compiler/rustc_hir_typeck/src/method/probe.rs | 210 ++++---- compiler/rustc_hir_typeck/src/method/suggest.rs | 555 ++++++++++++--------- compiler/rustc_hir_typeck/src/op.rs | 37 +- compiler/rustc_hir_typeck/src/pat.rs | 106 ++-- compiler/rustc_hir_typeck/src/place_op.rs | 7 +- compiler/rustc_hir_typeck/src/upvar.rs | 24 +- compiler/rustc_hir_typeck/src/writeback.rs | 52 +- 34 files changed, 1743 insertions(+), 1706 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 2b15d4dcd..e25a9e903 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,10 +1,10 @@ use crate::coercion::{AsCoercionSite, CoerceMany}; use crate::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_errors::{Applicability, MultiSpan}; +use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; -use rustc_middle::ty::{self, ToPredicate, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -137,55 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&arm.body), arm_ty, Some(&mut |err| { - let Some(ret) = self - .tcx - .hir() - .find_by_def_id(self.body_id.owner.def_id) - .and_then(|owner| owner.fn_decl()) - .map(|decl| decl.output.span()) - else { return; }; - let Expectation::IsLast(stmt) = orig_expected else { - return - }; - let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { - Some(ret_coercion) if self.in_tail_expr => { - let ret_ty = ret_coercion.borrow().expected_ty(); - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - self.can_coerce(arm_ty, ret_ty) - && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty)) - // The match arms need to unify for the case of `impl Trait`. - && !matches!(ret_ty.kind(), ty::Opaque(..)) - } - _ => false, - }; - if !can_coerce_to_return_ty { - return; - } - - let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); - let mut ret_span: MultiSpan = semi_span.into(); - ret_span.push_span_label( - expr.span, - "this could be implicitly returned but it is a statement, not a \ - tail expression", - ); - ret_span - .push_span_label(ret, "the `match` arms can conform to this return type"); - ret_span.push_span_label( - semi_span, - "the `match` is a statement because of this semicolon, consider \ - removing it", - ); - err.span_note( - ret_span, - "you might have meant to return the `match` expression", - ); - err.tool_only_span_suggestion( - semi_span, - "remove this semicolon", - "", - Applicability::MaybeIncorrect, - ); + self.suggest_removing_semicolon_for_coerce( + err, + expr, + orig_expected, + arm_ty, + prior_arm, + ) }), false, ); @@ -219,6 +177,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coercion.complete(self) } + fn suggest_removing_semicolon_for_coerce( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expectation: Expectation<'tcx>, + arm_ty: Ty<'tcx>, + prior_arm: Option<(Option, Ty<'tcx>, Span)>, + ) { + let hir = self.tcx.hir(); + + // First, check that we're actually in the tail of a function. + let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, _), .. }) = + hir.get(self.body_id) else { return; }; + let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. }) + = block.innermost_block().stmts.last() else { return; }; + if last_expr.hir_id != expr.hir_id { + return; + } + + // Next, make sure that we have no type expectation. + let Some(ret) = hir + .find_by_def_id(self.body_id.owner.def_id) + .and_then(|owner| owner.fn_decl()) + .map(|decl| decl.output.span()) else { return; }; + let Expectation::IsLast(stmt) = expectation else { + return; + }; + + let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { + Some(ret_coercion) => { + let ret_ty = ret_coercion.borrow().expected_ty(); + let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); + self.can_coerce(arm_ty, ret_ty) + && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty)) + // The match arms need to unify for the case of `impl Trait`. + && !matches!(ret_ty.kind(), ty::Opaque(..)) + } + _ => false, + }; + if !can_coerce_to_return_ty { + return; + } + + let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); + let mut ret_span: MultiSpan = semi_span.into(); + ret_span.push_span_label( + expr.span, + "this could be implicitly returned but it is a statement, not a \ + tail expression", + ); + ret_span.push_span_label(ret, "the `match` arms can conform to this return type"); + ret_span.push_span_label( + semi_span, + "the `match` is a statement because of this semicolon, consider \ + removing it", + ); + diag.span_note(ret_span, "you might have meant to return the `match` expression"); + diag.tool_only_span_suggestion( + semi_span, + "remove this semicolon", + "", + Applicability::MaybeIncorrect, + ); + } + /// When the previously checked expression (the scrutinee) diverges, /// warn the user about the match arms being unreachable. fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) { @@ -491,11 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. } = self.type_var_origin(expected)? else { return None; }; - let sig = *self - .typeck_results - .borrow() - .liberated_fn_sigs() - .get(hir::HirId::make_owner(self.body_id.owner.def_id))?; + let sig = self.body_fn_sig()?; let substs = sig.output().walk().find_map(|arg| { if let ty::GenericArgKind::Type(ty) = arg.unpack() @@ -519,23 +538,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .bound_explicit_item_bounds(rpit_def_id) .subst_iter_copied(self.tcx, substs) { - let pred = match pred.kind().skip_binder() { - ty::PredicateKind::Trait(mut trait_pred) => { + let pred = pred.kind().rebind(match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => { assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty); - trait_pred.trait_ref.substs = - self.tcx.mk_substs_trait(ty, &trait_pred.trait_ref.substs[1..]); - pred.kind().rebind(trait_pred).to_predicate(self.tcx) + ty::PredicateKind::Clause(ty::Clause::Trait( + trait_pred.with_self_type(self.tcx, ty), + )) } - ty::PredicateKind::Projection(mut proj_pred) => { + ty::PredicateKind::Clause(ty::Clause::Projection(mut proj_pred)) => { assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty); - proj_pred.projection_ty.substs = self - .tcx - .mk_substs_trait(ty, &proj_pred.projection_ty.substs[1..]); - pred.kind().rebind(proj_pred).to_predicate(self.tcx) + proj_pred.projection_ty.substs = self.tcx.mk_substs_trait( + ty, + proj_pred.projection_ty.substs.iter().skip(1), + ); + ty::PredicateKind::Clause(ty::Clause::Projection(proj_pred)) } _ => continue, - }; + }); if !self.predicate_must_hold_modulo_regions(&Obligation::new( + self.tcx, ObligationCause::misc(span, self.body_id), self.param_env, pred, @@ -553,8 +574,5 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn arms_contain_ref_bindings<'tcx>(arms: &'tcx [hir::Arm<'tcx>]) -> Option { - arms.iter().filter_map(|a| a.pat.contains_explicit_ref_binding()).max_by_key(|m| match *m { - hir::Mutability::Mut => 1, - hir::Mutability::Not => 0, - }) + arms.iter().filter_map(|a| a.pat.contains_explicit_ref_binding()).max() } diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 59c366ad7..41b52a4c4 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -12,18 +12,7 @@ use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty, span) - } - - /// Like `autoderef`, but provides a custom `Span` to use for calls to - /// an overloaded `Deref` operator - pub fn autoderef_overloaded_span( - &'a self, - span: Span, - base_ty: Ty<'tcx>, - overloaded_span: Span, - ) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty, overloaded_span) + Autoderef::new(self, self.param_env, self.body_id, span, base_ty) } pub fn try_overloaded_deref( @@ -55,11 +44,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |InferOk { value: method, obligations: o }| { obligations.extend(o); if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { - region, - mutbl, - span: autoderef.overloaded_span(), - }) + Some(OverloadedDeref { region, mutbl, span: autoderef.span() }) } else { None } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 1b33f2f02..b09ddf80e 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -6,7 +6,7 @@ use crate::type_error_struct; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey}; use rustc_hir as hir; -use rustc_hir::def::{self, Namespace, Res}; +use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_infer::{ infer, @@ -30,7 +30,7 @@ use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use std::iter; +use std::{iter, slice}; /// Checks that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific @@ -129,6 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { output } + #[instrument(level = "debug", skip(self, call_expr, callee_expr, arg_exprs, autoderef), ret)] fn try_overloaded_call_step( &self, call_expr: &'tcx hir::Expr<'tcx>, @@ -138,10 +139,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option> { let adjusted_ty = self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - debug!( - "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})", - call_expr, adjusted_ty - ); // If the callee is a bare function or a closure, then we're all set. match *adjusted_ty.kind() { @@ -182,12 +179,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Hack: we know that there are traits implementing Fn for &F // where F:Fn and so forth. In the particular case of types - // like `x: &mut FnMut()`, if there is a call `x()`, we would - // normally translate to `FnMut::call_mut(&mut x, ())`, but - // that winds up requiring `mut x: &mut FnMut()`. A little - // over the top. The simplest fix by far is to just ignore - // this case and deref again, so we wind up with - // `FnMut::call_mut(&mut *x, ())`. + // like `f: &mut FnMut()`, if there is a call `f()`, we would + // normally translate to `FnMut::call_mut(&mut f, ())`, but + // that winds up potentially requiring the user to mark their + // variable as `mut` which feels unnecessary and unexpected. + // + // fn foo(f: &mut impl FnMut()) { f() } + // ^ without this hack `f` would have to be declared as mutable + // + // The simplest fix by far is to just ignore this case and deref again, + // so we wind up with `FnMut::call_mut(&mut *f, ())`. ty::Ref(..) if autoderef.step_count() == 0 => { return None; } @@ -230,22 +231,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ] { let Some(trait_def_id) = opt_trait_def_id else { continue }; - let opt_input_types = opt_arg_exprs.map(|arg_exprs| { - [self.tcx.mk_tup(arg_exprs.iter().map(|e| { + let opt_input_type = opt_arg_exprs.map(|arg_exprs| { + self.tcx.mk_tup(arg_exprs.iter().map(|e| { self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span: e.span, }) - }))] + })) }); - let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref); if let Some(ok) = self.lookup_method_in_trait( call_expr.span, method_name, trait_def_id, adjusted_ty, - opt_input_types, + opt_input_type.as_ref().map(slice::from_ref), ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; @@ -261,15 +261,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded function call ops. + let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No); + autoref = Some(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[0], @@ -383,6 +379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { predicates.predicates.iter().zip(&predicates.spans) { let obligation = Obligation::new( + self.tcx, ObligationCause::dummy_with_span(callee_expr.span), self.param_env, *predicate, @@ -451,7 +448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // previously appeared within a `Binder<>` and hence would not // have been normalized before. let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig); - let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig); + let fn_sig = self.normalize(call_expr.span, fn_sig); // Call the generic checker. let expected_arg_tys = self.expected_inputs_for_expected_output( @@ -471,6 +468,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_id, ); + if fn_sig.abi == abi::Abi::RustCall { + let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); + if let Some(ty) = fn_sig.inputs().last().copied() { + self.register_bound( + ty, + self.tcx.require_lang_item(hir::LangItem::Tuple, Some(sp)), + traits::ObligationCause::new(sp, self.body_id, traits::RustCall), + ); + } else { + self.tcx.sess.span_err( + sp, + "functions with the \"rust-call\" ABI must take a single non-self tuple argument", + ); + } + } + fn_sig.output() } @@ -491,7 +504,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // method lookup. let Ok(pick) = self .probe_for_name( - call_expr.span, Mode::MethodCall, segment.ident, IsSuggestion(true), @@ -582,7 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let mut unit_variant = None; if let hir::ExprKind::Path(qpath) = &callee_expr.kind - && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _) + && let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _) = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) // Only suggest removing parens if there are no arguments && arg_exprs.is_empty() diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index d1dab0540..890a068a7 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -33,6 +33,7 @@ use super::FnCtxt; use crate::type_error_struct; use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; @@ -45,7 +46,6 @@ use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::report_object_safety_error; /// Reifies a cast check to be checked once we have full type information for /// a function context. @@ -67,7 +67,7 @@ pub struct CastCheck<'tcx> { /// The kind of pointer and associated metadata (thin, length or vtable) - we /// only allow casts between fat pointers if their metadata have the same /// kind. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, @@ -76,11 +76,11 @@ enum PointerKind<'tcx> { /// Slice Length, /// The unsize info of this projection - OfProjection(&'tcx ty::ProjectionTy<'tcx>), + OfProjection(ty::ProjectionTy<'tcx>), /// The unsize info of this opaque ty OfOpaque(DefId, SubstsRef<'tcx>), /// The unsize info of this parameter - OfParam(&'tcx ty::ParamTy), + OfParam(ty::ParamTy), } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -94,10 +94,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("pointer_kind({:?}, {:?})", t, span); let t = self.resolve_vars_if_possible(t); - - if let Some(reported) = t.error_reported() { - return Err(reported); - } + t.error_reported()?; if self.type_is_sized_modulo_regions(self.param_env, t, span) { return Ok(Some(PointerKind::Thin)); @@ -121,9 +118,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Pointers to foreign types are thin, despite being unsized ty::Foreign(..) => Some(PointerKind::Thin), // We should really try to normalize here. - ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), + ty::Projection(pi) => Some(PointerKind::OfProjection(pi)), ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), - ty::Param(ref p) => Some(PointerKind::OfParam(p)), + ty::Param(p) => Some(PointerKind::OfParam(p)), // Insufficient type information. ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, @@ -222,8 +219,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // inference is more completely known. match cast_ty.kind() { ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { - let reported = check.report_cast_to_unsized_type(fcx); - Err(reported) + Err(check.report_cast_to_unsized_type(fcx)) } _ => Ok(check), } @@ -501,10 +497,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { let ty = fcx.tcx.erase_regions(ty); let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let expr_ty = fcx.tcx.erase_regions(expr_ty); - let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); if fcx .infcx - .type_implements_trait(from_trait, ty, ty_params, fcx.param_env) + .type_implements_trait(from_trait, [ty, expr_ty], fcx.param_env) .must_apply_modulo_regions() { label = false; @@ -614,10 +609,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn report_cast_to_unsized_type(&self, fcx: &FnCtxt<'a, 'tcx>) -> ErrorGuaranteed { - if let Some(reported) = - self.cast_ty.error_reported().or_else(|| self.expr_ty.error_reported()) - { - return reported; + if let Err(err) = self.cast_ty.error_reported() { + return err; + } + if let Err(err) = self.expr_ty.error_reported() { + return err; } let tstr = fcx.ty_to_string(self.cast_ty); @@ -730,9 +726,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { debug!(" -> CoercionCast"); fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); } - Err(ty::error::TypeError::ObjectUnsafeCoercion(did)) => { - self.report_object_unsafe_cast(&fcx, did); - } Err(_) => { match self.do_check(fcx) { Ok(k) => { @@ -744,14 +737,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; } } - - fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) { - let violations = fcx.tcx.object_safety_violations(did); - let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations); - err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty))); - err.emit(); - } - /// Checks a cast, and report an error if one exists. In some cases, this /// can return Ok and create type errors in the fcx rather than returning /// directly. coercion-cast is handled in check instead of here. @@ -767,10 +752,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { match *self.expr_ty.kind() { ty::FnDef(..) => { // Attempt a coercion to a fn pointer type. - let f = fcx.normalize_associated_types_in( - self.expr_span, - self.expr_ty.fn_sig(fcx.tcx), - ); + let f = fcx.normalize(self.expr_span, self.expr_ty.fn_sig(fcx.tcx)); let res = fcx.try_coerce( self.expr, self.expr_ty, @@ -912,7 +894,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } // vtable kinds must match - if cast_kind == expr_kind { + if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { Ok(CastKind::PtrPtrCast) } else { Err(CastError::DifferingKinds) @@ -954,7 +936,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { m_cast: ty::TypeAndMut<'tcx>, ) -> Result { // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const - if m_expr.mutbl == hir::Mutability::Mut || m_cast.mutbl == hir::Mutability::Not { + if m_expr.mutbl >= m_cast.mutbl { if let ty::Array(ety, _) = m_expr.ty.kind() { // Due to the limitations of LLVM global constants, // region pointers end up pointing at copies of diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 7f76364e1..32f86b804 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -1,18 +1,16 @@ use crate::coercion::CoerceMany; use crate::gather_locals::GatherLocalsVisitor; -use crate::{FnCtxt, Inherited}; -use crate::{GeneratorTypes, UnsafetyState}; +use crate::FnCtxt; +use crate::GeneratorTypes; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ImplicitSelfKind, ItemKind, Node}; use rustc_hir_analysis::check::fn_maybe_err; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; -use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use std::cell::RefCell; @@ -22,22 +20,16 @@ use std::cell::RefCell; /// /// * ... /// * inherited: other fields inherited from the enclosing fn (if any) -#[instrument(skip(inherited, body), level = "debug")] +#[instrument(skip(fcx, body), level = "debug")] pub(super) fn check_fn<'a, 'tcx>( - inherited: &'a Inherited<'tcx>, - param_env: ty::ParamEnv<'tcx>, + fcx: &mut FnCtxt<'a, 'tcx>, fn_sig: ty::FnSig<'tcx>, decl: &'tcx hir::FnDecl<'tcx>, - fn_id: hir::HirId, + fn_def_id: LocalDefId, body: &'tcx hir::Body<'tcx>, can_be_generator: Option, - return_type_pre_known: bool, -) -> (FnCtxt<'a, 'tcx>, Option>) { - // Create the function context. This is either derived from scratch or, - // in the case of closures, based on the outer context. - let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); - fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id)); - fcx.return_type_pre_known = return_type_pre_known; +) -> Option> { + let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id); let tcx = fcx.tcx; let hir = tcx.hir(); @@ -49,11 +41,8 @@ pub(super) fn check_fn<'a, 'tcx>( declared_ret_ty, body.value.hir_id, decl.output.span(), - param_env, + fcx.param_env, )); - // If we replaced declared_ret_ty with infer vars, then we must be inferring - // an opaque type, so set a flag so we can improve diagnostics. - fcx.return_type_has_opaque = ret_ty != declared_ret_ty; fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); @@ -61,45 +50,15 @@ pub(super) fn check_fn<'a, 'tcx>( fn_maybe_err(tcx, span, fn_sig.abi); - if fn_sig.abi == Abi::RustCall { - let expected_args = if let ImplicitSelfKind::None = decl.implicit_self { 1 } else { 2 }; - - let err = || { - let item = match tcx.hir().get(fn_id) { - Node::Item(hir::Item { kind: ItemKind::Fn(header, ..), .. }) => Some(header), - Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(header, ..), .. - }) => Some(header), - Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(header, ..), - .. - }) => Some(header), - // Closures are RustCall, but they tuple their arguments, so shouldn't be checked - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => None, - node => bug!("Item being checked wasn't a function/closure: {:?}", node), - }; - - if let Some(header) = item { - tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple"); - } - }; - - if fn_sig.inputs().len() != expected_args { - err() + if let Some(kind) = body.generator_kind && can_be_generator.is_some() { + let yield_ty = if kind == hir::GeneratorKind::Gen { + let yield_ty = fcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); + fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); + yield_ty } else { - // FIXME(CraftSpider) Add a check on parameter expansion, so we don't just make the ICE happen later on - // This will probably require wide-scale changes to support a TupleKind obligation - // We can't resolve this without knowing the type of the param - if !matches!(fn_sig.inputs()[expected_args - 1].kind(), ty::Tuple(_) | ty::Param(_)) { - err() - } - } - } - - if body.generator_kind.is_some() && can_be_generator.is_some() { - let yield_ty = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); + tcx.mk_unit() + }; // Resume type defaults to `()` if the generator has no argument. let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); @@ -140,10 +99,9 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.write_ty(param.hir_id, param_ty); } - inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); + fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - fcx.in_tail_expr = true; - if let ty::Dynamic(..) = declared_ret_ty.kind() { + 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 @@ -161,7 +119,6 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); fcx.check_return_expr(&body.value, false); } - fcx.in_tail_expr = 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. @@ -211,14 +168,7 @@ pub(super) fn check_fn<'a, 'tcx>( check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); } - // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` - if let Some(alloc_error_handler_did) = tcx.lang_items().oom() - && alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() - { - check_alloc_error_fn(tcx, alloc_error_handler_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - (fcx, gen_ty) + gen_ty } fn check_panic_info_fn( @@ -246,7 +196,7 @@ fn check_panic_info_fn( let arg_is_panic_info = match *inputs[0].kind() { ty::Ref(region, ty, mutbl) => match *ty.kind() { ty::Adt(ref adt, _) => { - adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static() + adt.did() == panic_info_did && mutbl.is_not() && !region.is_static() } _ => false, }, @@ -273,52 +223,3 @@ fn check_panic_info_fn( tcx.sess.span_err(span, "should have no const parameters"); } } - -fn check_alloc_error_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() else { - tcx.sess.err("language item required, but not found: `alloc_layout`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_alloc_layout = match inputs[0].kind() { - ty::Adt(ref adt, _) => adt.did() == alloc_layout_did, - _ => false, - }; - - if !arg_is_alloc_layout { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` function should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess - .span_err(span, "`#[alloc_error_handler]` function should have no const parameters"); - } -} diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index a5a45f75e..429cb60ba 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -4,24 +4,26 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use hir::def::DefKind; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::ArgKind; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use std::cmp; use std::iter; /// What signature do we *expect* the closure to have from context? -#[derive(Debug)] +#[derive(Debug, Clone, TypeFoldable, TypeVisitable)] struct ExpectedSig<'tcx> { /// Span that gave us this expectation, if we know that. cause_span: Option, @@ -33,24 +35,20 @@ struct ClosureSignatures<'tcx> { bound_sig: ty::PolyFnSig<'tcx>, /// The signature within the function body. /// This mostly differs in the sense that lifetimes are now early bound and any - /// opaque types from the signature expectation are overriden in case there are + /// opaque types from the signature expectation are overridden in case there are /// explicit hidden types written by the user in the closure signature. liberated_sig: ty::FnSig<'tcx>, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")] + #[instrument(skip(self, closure), level = "debug")] pub fn check_expr_closure( &self, - expr: &hir::Expr<'_>, - _capture: hir::CaptureBy, - decl: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - gen: Option, + closure: &hir::Closure<'tcx>, + expr_span: Span, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - trace!("expr = {:#?}", expr); + trace!("decl = {:#?}", closure.fn_decl); // It's always helpful for inference if we know the kind of // closure sooner rather than later, so first examine the expected @@ -59,42 +57,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ty) => self.deduce_expectations_from_expected_type(ty), None => (None, None), }; - let body = self.tcx.hir().body(body_id); - self.check_closure(expr, expected_kind, decl, body, gen, expected_sig) + let body = self.tcx.hir().body(closure.body); + self.check_closure(closure, expr_span, expected_kind, body, expected_sig) } - #[instrument(skip(self, expr, body, decl), level = "debug", ret)] + #[instrument(skip(self, closure, body), level = "debug", ret)] fn check_closure( &self, - expr: &hir::Expr<'_>, + closure: &hir::Closure<'tcx>, + expr_span: Span, opt_kind: Option, - decl: &'tcx hir::FnDecl<'tcx>, body: &'tcx hir::Body<'tcx>, - gen: Option, expected_sig: Option>, ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id); + trace!("decl = {:#?}", closure.fn_decl); + let expr_def_id = closure.def_id; debug!(?expr_def_id); let ClosureSignatures { bound_sig, liberated_sig } = - self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig); + self.sig_of_closure(expr_def_id, closure.fn_decl, body, expected_sig); debug!(?bound_sig, ?liberated_sig); - let return_type_pre_known = !liberated_sig.output().is_ty_infer(); - + let mut fcx = FnCtxt::new(self, self.param_env.without_const(), body.value.hir_id); let generator_types = check_fn( - self, - self.param_env.without_const(), + &mut fcx, liberated_sig, - decl, - expr.hir_id, + closure.fn_decl, + expr_def_id, body, - gen, - return_type_pre_known, - ) - .1; + closure.movability, + ); let parent_substs = InternalSubsts::identity_for_item( self.tcx, @@ -103,7 +96,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.hir().span(expr.hir_id), + span: self.tcx.def_span(expr_def_id), }); if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types @@ -149,7 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => self.next_ty_var(TypeVariableOrigin { // FIXME(eddyb) distinguish closure kind inference variables from the rest. kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr.span, + span: expr_span, }), }; @@ -174,34 +167,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Ty<'tcx>, ) -> (Option>, Option) { match *expected_ty.kind() { - ty::Opaque(def_id, substs) => { - let bounds = self.tcx.bound_explicit_item_bounds(def_id); - let sig = - bounds.subst_iter_copied(self.tcx, substs).find_map(|(pred, span)| match pred - .kind() - .skip_binder() - { - ty::PredicateKind::Projection(proj_predicate) => self - .deduce_sig_from_projection( - Some(span), - pred.kind().rebind(proj_predicate), - ), - _ => None, - }); - - let kind = bounds - .0 - .iter() - .filter_map(|(pred, _)| match pred.kind().skip_binder() { - ty::PredicateKind::Trait(tp) => { - self.tcx.fn_trait_kind_from_lang_item(tp.def_id()) - } - _ => None, - }) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); - trace!(?sig, ?kind); - (sig, kind) - } + ty::Opaque(def_id, substs) => self.deduce_signature_from_predicates( + self.tcx.bound_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| { let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); @@ -209,10 +177,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let kind = object_type .principal_def_id() - .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did)); + .and_then(|did| self.tcx.fn_trait_kind_from_def_id(did)); (sig, kind) } - ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid), + ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates( + self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)), + ), ty::FnPtr(sig) => { let expected_sig = ExpectedSig { cause_span: None, sig }; (Some(expected_sig), Some(ty::ClosureKind::Fn)) @@ -221,37 +191,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn deduce_expectations_from_obligations( + fn deduce_signature_from_predicates( &self, - expected_vid: ty::TyVid, + predicates: impl DoubleEndedIterator, Span)>, ) -> (Option>, Option) { - let expected_sig = - self.obligations_for_self_ty(expected_vid).find_map(|(_, obligation)| { - debug!(?obligation.predicate); - - let bound_predicate = obligation.predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = - obligation.predicate.kind().skip_binder() - { - // Given a Projection predicate, we can potentially infer - // the complete signature. + let mut expected_sig = None; + let mut expected_kind = None; + + for obligation in traits::elaborate_predicates_with_span( + self.tcx, + // Reverse the obligations here, since `elaborate_*` uses a stack, + // and we want to keep inference generally in the same order of + // the registered obligations. + predicates.rev(), + ) { + debug!(?obligation.predicate); + let bound_predicate = obligation.predicate.kind(); + + // Given a Projection predicate, we can potentially infer + // the complete signature. + if expected_sig.is_none() + && let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder() + { + expected_sig = self.normalize( + obligation.cause.span, self.deduce_sig_from_projection( - Some(obligation.cause.span), + Some(obligation.cause.span), bound_predicate.rebind(proj_predicate), - ) - } else { - None - } - }); + ), + ); + } - // Even if we can't infer the full signature, we may be able to - // infer the kind. This can occur when we elaborate a predicate - // like `F : Fn`. Note that due to subtyping we could encounter - // many viable options, so pick the most restrictive. - let expected_kind = self - .obligations_for_self_ty(expected_vid) - .filter_map(|(tr, _)| self.tcx.fn_trait_kind_from_lang_item(tr.def_id())) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); + // Even if we can't infer the full signature, we may be able to + // infer the kind. This can occur when we elaborate a predicate + // like `F : Fn`. Note that due to subtyping we could encounter + // many viable options, so pick the most restrictive. + let trait_def_id = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { + Some(data.projection_ty.trait_def_id(self.tcx)) + } + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => Some(data.def_id()), + _ => None, + }; + if let Some(closure_kind) = + trait_def_id.and_then(|def_id| self.tcx.fn_trait_kind_from_def_id(def_id)) + { + expected_kind = Some( + expected_kind + .map_or_else(|| closure_kind, |current| cmp::min(current, closure_kind)), + ); + } + } (expected_sig, expected_kind) } @@ -272,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_def_id = projection.trait_def_id(tcx); - let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some(); + let is_fn = tcx.is_fn_trait(trait_def_id); let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); let is_gen = gen_trait == trait_def_id; if !is_fn && !is_gen { @@ -323,30 +313,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn sig_of_closure( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, expected_sig: Option>, ) -> ClosureSignatures<'tcx> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(hir_id, expr_def_id, decl, body, e) + self.sig_of_closure_with_expectation(expr_def_id, decl, body, e) } else { - self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body) + self.sig_of_closure_no_expectation(expr_def_id, decl, body) } } /// If there is no expected signature, then we will convert the /// types that the user gave into a signature. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] + #[instrument(skip(self, expr_def_id, decl, body), level = "debug")] fn sig_of_closure_no_expectation( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, ) -> ClosureSignatures<'tcx> { - let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); + let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl, body); self.closure_sigs(expr_def_id, body, bound_sig) } @@ -392,17 +380,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// # Arguments /// - /// - `expr_def_id`: the `DefId` of the closure expression + /// - `expr_def_id`: the `LocalDefId` of the closure expression /// - `decl`: the HIR declaration of the closure /// - `body`: the body of the closure /// - `expected_sig`: the expected signature (if any). Note that /// this is missing a binder: that is, there may be late-bound /// regions with depth 1, which are bound then by the closure. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] + #[instrument(skip(self, expr_def_id, decl, body), level = "debug")] fn sig_of_closure_with_expectation( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, expected_sig: ExpectedSig<'tcx>, @@ -411,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // expectation if things don't see to match up with what we // expect. if expected_sig.sig.c_variadic() != decl.c_variadic { - return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body); + return self.sig_of_closure_no_expectation(expr_def_id, decl, body); } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 { return self.sig_of_closure_with_mismatched_number_of_arguments( expr_def_id, @@ -447,27 +434,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Along the way, it also writes out entries for types that the user // wrote into our typeck results, which are then later used by the privacy // check. - match self.merge_supplied_sig_with_expectation( - hir_id, - expr_def_id, - decl, - body, - closure_sigs, - ) { + match self.merge_supplied_sig_with_expectation(expr_def_id, decl, body, closure_sigs) { Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), - Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), + Err(_) => self.sig_of_closure_no_expectation(expr_def_id, decl, body), } } fn sig_of_closure_with_mismatched_number_of_arguments( &self, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, expected_sig: ExpectedSig<'tcx>, ) -> ClosureSignatures<'tcx> { let hir = self.tcx.hir(); - let expr_map_node = hir.get_if_local(expr_def_id).unwrap(); + let expr_map_node = hir.get_by_def_id(expr_def_id); let expected_args: Vec<_> = expected_sig .sig .skip_binder() @@ -475,18 +456,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .map(|ty| ArgKind::from_expected_ty(*ty, None)) .collect(); - let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) { - Some((sp, args)) => (Some(sp), args), - None => (None, Vec::new()), - }; + let (closure_span, closure_arg_span, found_args) = + match self.get_fn_like_arguments(expr_map_node) { + Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args), + None => (None, None, Vec::new()), + }; let expected_span = - expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap()); + expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id)); self.report_arg_count_mismatch( expected_span, closure_span, expected_args, found_args, true, + closure_arg_span, ) .emit(); @@ -498,11 +481,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Enforce the user's types against the expectation. See /// `sig_of_closure_with_expectation` for details on the overall /// strategy. - #[instrument(level = "debug", skip(self, hir_id, expr_def_id, decl, body, expected_sigs))] + #[instrument(level = "debug", skip(self, expr_def_id, decl, body, expected_sigs))] fn merge_supplied_sig_with_expectation( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, mut expected_sigs: ClosureSignatures<'tcx>, @@ -511,7 +493,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); + let supplied_sig = self.supplied_sig_of_closure(expr_def_id, decl, body); debug!(?supplied_sig); @@ -591,8 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, decl, body), level = "debug", ret)] fn supplied_sig_of_closure( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, ) -> ty::PolyFnSig<'tcx> { @@ -601,6 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trace!("decl = {:#?}", decl); debug!(?body.generator_kind); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(expr_def_id); let bound_vars = self.tcx.late_bound_vars(hir_id); // First, convert the types that the user supplied (if any). @@ -642,7 +624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Astconv can't normalize inputs or outputs with escaping bound vars, // so normalize them here, after we've wrapped them in a binder. - let result = self.normalize_associated_types_in(self.tcx.hir().span(hir_id), result); + let result = self.normalize(self.tcx.hir().span(hir_id), result); let c_result = self.inh.infcx.canonicalize_response(result); self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); @@ -659,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug", ret)] fn deduce_future_output_from_obligations( &self, - expr_def_id: DefId, + expr_def_id: LocalDefId, body_id: hir::HirId, ) -> Option> { let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { @@ -677,7 +659,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // where R is the return type we are expecting. This type `T` // will be our output. let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = + bound_predicate.skip_binder() + { self.deduce_future_output_from_projection( span, bound_predicate.rebind(proj_predicate), @@ -689,7 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output_ty = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { + self.obligations_for_self_ty(ret_vid).find_map(|obligation| { get_future_output(obligation.predicate, obligation.cause.span) })? } @@ -808,17 +792,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn closure_sigs( &self, - expr_def_id: DefId, + expr_def_id: LocalDefId, body: &hir::Body<'_>, bound_sig: ty::PolyFnSig<'tcx>, ) -> ClosureSignatures<'tcx> { - let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, bound_sig); - let liberated_sig = self.inh.normalize_associated_types_in( - body.value.span, - body.value.hir_id, - self.param_env, - liberated_sig, - ); + let liberated_sig = + self.tcx().liberate_late_bound_regions(expr_def_id.to_def_id(), bound_sig); + let liberated_sig = self.normalize(body.value.span, liberated_sig); ClosureSignatures { bound_sig, liberated_sig } } } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 86597a703..f0b349f0c 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -46,7 +46,7 @@ use rustc_hir::Expr; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; -use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::Obligation; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, @@ -55,14 +55,16 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeAndMut}; +use rustc_middle::ty::{self, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, BytePos, DesugaringKind, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; +use rustc_trait_selection::traits::{ + self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt, +}; use smallvec::{smallvec, SmallVec}; use std::ops::Deref; @@ -108,11 +110,7 @@ fn coerce_mutbls<'tcx>( from_mutbl: hir::Mutability, to_mutbl: hir::Mutability, ) -> RelateResult<'tcx, ()> { - match (from_mutbl, to_mutbl) { - (hir::Mutability::Mut, hir::Mutability::Mut | hir::Mutability::Not) - | (hir::Mutability::Not, hir::Mutability::Not) => Ok(()), - (hir::Mutability::Not, hir::Mutability::Mut) => Err(TypeError::Mutability), - } + if from_mutbl >= to_mutbl { Ok(()) } else { Err(TypeError::Mutability) } } /// Do not require any adjustments, i.e. coerce `x -> x`. @@ -199,10 +197,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce: unsize successful"); return unsize; } - Err(TypeError::ObjectUnsafeCoercion(did)) => { - debug!("coerce: unsize not object safe"); - return Err(TypeError::ObjectUnsafeCoercion(did)); - } Err(error) => { debug!(?error, "coerce: unsize failed"); } @@ -277,13 +271,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { for &source_ty in &[a, b] { if source_ty != target_ty { obligations.push(Obligation::new( + self.tcx(), self.cause.clone(), self.param_env, ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate { a: source_ty, b: target_ty, - })) - .to_predicate(self.tcx()), + })), )); } } @@ -456,7 +450,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return Err(err); }; - if ty == a && mt_a.mutbl == hir::Mutability::Not && autoderef.step_count() == 1 { + if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { // As a special case, if we would produce `&'a *x`, that's // a total no-op. We end up with the type `&'a T` just as // we started with. In that case, just skip it @@ -468,7 +462,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // `self.x` both have `&mut `type would be a move of // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, // which is a borrow. - assert_eq!(mutbl_b, hir::Mutability::Not); // can only coerce &T -> &U + assert!(mutbl_b.is_not()); // can only coerce &T -> &U return success(vec![], ty, obligations); } @@ -482,12 +476,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let ty::Ref(r_borrow, _, _) = ty.kind() else { span_bug!(span, "expected a ref type, got {:?}", ty); }; - let mutbl = match mutbl_b { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => { - AutoBorrowMutability::Mut { allow_two_phase_borrow: self.allow_two_phase } - } - }; + let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase); adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*r_borrow, mutbl)), target: ty, @@ -507,27 +496,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { target = self.shallow_resolve(target); debug!(?source, ?target); - // These 'if' statements require some explanation. - // The `CoerceUnsized` trait is special - it is only - // possible to write `impl CoerceUnsized for A` where - // A and B have 'matching' fields. This rules out the following - // two types of blanket impls: - // - // `impl CoerceUnsized for SomeType` - // `impl CoerceUnsized for T` - // - // Both of these trigger a special `CoerceUnsized`-related error (E0376) - // - // We can take advantage of this fact to avoid performing unnecessary work. - // If either `source` or `target` is a type variable, then any applicable impl - // would need to be generic over the self-type (`impl CoerceUnsized for T`) - // or generic over the `CoerceUnsized` type parameter (`impl CoerceUnsized for - // SomeType`). - // - // However, these are exactly the kinds of impls which are forbidden by - // the compiler! Therefore, we can be sure that coercion will always fail - // when either the source or target type is a type variable. This allows us - // to skip performing any trait selection, and immediately bail out. + // We don't apply any coercions incase either the source or target + // aren't sufficiently well known but tend to instead just equate + // them both. if source.is_ty_var() { debug!("coerce_unsized: source is a TyVar, bailing out"); return Err(TypeError::Mismatch); @@ -556,15 +527,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let coercion = Coercion(self.cause.span); let r_borrow = self.next_region_var(coercion); - let mutbl = match mutbl_b { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // We don't allow two-phase borrows here, at least for initial - // implementation. If it happens that this coercion is a function argument, - // the reborrow in coerce_borrowed_ptr will pick it up. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; + + // We don't allow two-phase borrows here, at least for initial + // implementation. If it happens that this coercion is a function argument, + // the reborrow in coerce_borrowed_ptr will pick it up. + let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); + Some(( Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { @@ -588,7 +556,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } _ => None, }; - let coerce_source = reborrow.as_ref().map_or(source, |&(_, ref r)| r.target); + let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target); // Setup either a subtyping or a LUB relationship between // the `CoerceUnsized` target type and the expected type. @@ -629,8 +597,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { cause, coerce_unsized_did, 0, - coerce_source, - &[coerce_target.into()] + [coerce_source, coerce_target] )]; let mut has_unsized_tuple_coercion = false; @@ -645,7 +612,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce_unsized resolve step: {:?}", obligation); let bound_predicate = obligation.predicate.kind(); let trait_pred = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_pred) if traits.contains(&trait_pred.def_id()) => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) + if traits.contains(&trait_pred.def_id()) => + { if unsize_did == trait_pred.def_id() { let self_ty = trait_pred.self_ty(); let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty(); @@ -668,7 +637,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { continue; } }; - match selcx.select(&obligation.with(trait_pred)) { + match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { // Uncertain or unimplemented. Ok(None) => { if trait_pred.def_id() == unsize_did { @@ -705,12 +674,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Object safety violations or miscellaneous. Err(err) => { - self.err_ctxt().report_selection_error( - obligation.clone(), - &obligation, - &err, - false, - ); + self.err_ctxt().report_selection_error(obligation.clone(), &obligation, &err); // Treat this like an obligation and follow through // with the unsizing - the lack of a coercion should // be silent, as it causes a type mismatch later. @@ -752,7 +716,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { &self, a: Ty<'tcx>, b: Ty<'tcx>, - predicates: &'tcx ty::List>>, + predicates: &'tcx ty::List>, b_region: ty::Region<'tcx>, ) -> CoerceResult<'tcx> { if !self.tcx.features().dyn_star { @@ -761,25 +725,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if let ty::Dynamic(a_data, _, _) = a.kind() && let ty::Dynamic(b_data, _, _) = b.kind() + && a_data.principal_def_id() == b_data.principal_def_id() { - if a_data.principal_def_id() == b_data.principal_def_id() { - return self.unify_and(a, b, |_| vec![]); - } else if !self.tcx().features().trait_upcasting { - let mut err = feature_err( - &self.tcx.sess.parse_sess, - sym::trait_upcasting, - self.cause.span, - &format!( - "cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental" - ), - ); - err.emit(); - } + return self.unify_and(a, b, |_| vec![]); } // Check the obligations of the cast -- for example, when casting // `usize` to `dyn* Clone + 'static`: - let obligations = predicates + let mut obligations: Vec<_> = predicates .iter() .map(|predicate| { // For each existential predicate (e.g., `?Self: Clone`) substitute @@ -787,18 +740,31 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // and then require that the resulting predicate (e.g., `usize: Clone`) // holds (it does). let predicate = predicate.with_self_ty(self.tcx, a); - Obligation::new(self.cause.clone(), self.param_env, predicate) + Obligation::new(self.tcx, self.cause.clone(), self.param_env, predicate) }) - // Enforce the region bound (e.g., `usize: 'static`, in our example). - .chain([Obligation::new( - self.cause.clone(), - self.param_env, - self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(a, b_region), - ))), - )]) + .chain([ + // Enforce the region bound (e.g., `usize: 'static`, in our example). + Obligation::new( + self.tcx, + self.cause.clone(), + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives( + ty::OutlivesPredicate(a, b_region), + ))), + ), + ]) .collect(); + // Enforce that the type is `usize`/pointer-sized. + obligations.push(Obligation::new( + self.tcx, + self.cause.clone(), + self.param_env, + ty::Binder::dummy( + self.tcx.at(self.cause.span).mk_trait_ref(hir::LangItem::PointerSized, [a]), + ), + )); + Ok(InferOk { value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b), obligations, @@ -868,7 +834,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); let InferOk { value: b, mut obligations } = - self.normalize_associated_types_in_as_infer_ok(self.cause.span, b); + self.at(&self.cause, self.param_env).normalize(b); debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); match b.kind() { @@ -890,7 +856,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } let InferOk { value: a_sig, obligations: o1 } = - self.normalize_associated_types_in_as_infer_ok(self.cause.span, a_sig); + self.at(&self.cause, self.param_env).normalize(a_sig); obligations.extend(o1); let a_fn_pointer = self.tcx.mk_fn_ptr(a_sig); @@ -1043,9 +1009,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Ok(ok) = coerce.coerce(source, target) else { return false; }; - let mut fcx = traits::FulfillmentContext::new_in_snapshot(); - fcx.register_predicate_obligations(self, ok.obligations); - fcx.select_where_possible(&self).is_empty() + let ocx = ObligationCtxt::new_in_snapshot(self); + ocx.register_obligations(ok.obligations); + ocx.select_where_possible().is_empty() }) } @@ -1072,8 +1038,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.infcx .type_implements_trait( self.tcx.lang_items().deref_mut_trait()?, - expr_ty, - ty::List::empty(), + [expr_ty], self.param_env, ) .may_apply() @@ -1116,15 +1081,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Special-case that coercion alone cannot handle: // Function items or non-capturing closures of differing IDs or InternalSubsts. let (a_sig, b_sig) = { - #[allow(rustc::usage_of_ty_tykind)] - let is_capturing_closure = |ty: &ty::TyKind<'tcx>| { - if let &ty::Closure(closure_def_id, _substs) = ty { + let is_capturing_closure = |ty: Ty<'tcx>| { + if let &ty::Closure(closure_def_id, _substs) = ty.kind() { self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some() } else { false } }; - if is_capturing_closure(prev_ty.kind()) || is_capturing_closure(new_ty.kind()) { + if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) { (None, None) } else { match (prev_ty.kind(), new_ty.kind()) { @@ -1179,8 +1143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Err(TypeError::IntrinsicCast); } // The signature must match. - let a_sig = self.normalize_associated_types_in(new.span, a_sig); - let b_sig = self.normalize_associated_types_in(new.span, b_sig); + let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig)); let sig = self .at(cause, self.param_env) .trace(prev_ty, new_ty) @@ -1547,7 +1510,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Mark that we've failed to coerce the types here to suppress // any superfluous errors we might encounter while trying to // emit or provide suggestions on how to fix the initial error. - fcx.set_tainted_by_errors(); + fcx.set_tainted_by_errors( + fcx.tcx.sess.delay_span_bug(cause.span, "coercion error but no error emitted"), + ); let (expected, found) = if label_expression_as_expected { // In the case where this is a "forced unit", like // `break`, we want to call the `()` "expected" @@ -1644,9 +1609,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if visitor.ret_exprs.len() > 0 && let Some(expr) = expression { self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs); } - err.emit_unless(unsized_return); + let reported = err.emit_unless(unsized_return); - self.final_ty = Some(fcx.tcx.ty_error()); + self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported)); } } } @@ -1782,7 +1747,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // may occur at the first return expression we see in the closure // (if it conflicts with the declared return type). Skip adding a // note in this case, since it would be incorrect. - && !fcx.return_type_pre_known + && let Some(fn_sig) = fcx.body_fn_sig() + && fn_sig.output().is_ty_var() { err.span_note( sp, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 16febfc46..24184bdbf 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -2,6 +2,7 @@ use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_hir::def::CtorKind; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; use rustc_infer::infer::InferOk; @@ -18,6 +19,7 @@ use rustc_trait_selection::traits::ObligationCause; use super::method::probe; +use std::cmp::min; use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -30,6 +32,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, error: Option>, ) { + if expr_ty == expected { + return; + } + self.annotate_expected_due_to_let_ty(err, expr, error); // Use `||` to give these suggestions a precedence @@ -42,15 +48,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_boxing_when_appropriate(err, expr, 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_into(err, expr, expr_ty, expected); + || self.suggest_into(err, expr, expr_ty, expected) + || self.suggest_option_to_bool(err, expr, expr_ty, expected) + || self.suggest_floating_point_literal(err, expr, expected); self.note_type_is_not_clone(err, expected, expr_ty, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); } - // Requires that the two types unify, and prints an error message if - // they don't. + /// Requires that the two types unify, and prints an error message if + /// they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) { e.emit(); @@ -148,7 +156,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(e) => e, }; - self.set_tainted_by_errors(); + self.set_tainted_by_errors(self.tcx.sess.delay_span_bug( + expr.span, + "`TypeError` when attempting coercion but no error emitted", + )); let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); let expr_ty = self.resolve_vars_with_obligations(checked_ty); @@ -395,27 +406,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(path) = variant_path.strip_prefix("std::prelude::") && let Some((_, path)) = path.split_once("::") { - return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)); + return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy)); } - Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)) + Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy)) } else { None } }) .collect(); - let suggestions_for = |variant: &_, ctor, field_name| { + let suggestions_for = |variant: &_, ctor_kind, field_name| { let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { Some(ident) => format!("{ident}: "), None => String::new(), }; - let (open, close) = match ctor { - hir::def::CtorKind::Fn => ("(".to_owned(), ")"), - hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"), + let (open, close) = match ctor_kind { + Some(CtorKind::Fn) => ("(".to_owned(), ")"), + None => (format!(" {{ {field_name}: "), " }"), // unit variants don't have fields - hir::def::CtorKind::Const => unreachable!(), + Some(CtorKind::Const) => unreachable!(), }; // Suggest constructor as deep into the block tree as possible. @@ -845,31 +856,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { - if mutability == hir::Mutability::Mut { + if mutability.is_mut() { // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` return None; } } let sugg_expr = if needs_parens { format!("({src})") } else { src }; - return Some(match mutability { - hir::Mutability::Mut => ( - sp, - "consider mutably borrowing here".to_string(), - format!("{prefix}&mut {sugg_expr}"), - Applicability::MachineApplicable, - false, - false, - ), - hir::Mutability::Not => ( - sp, - "consider borrowing here".to_string(), - format!("{prefix}&{sugg_expr}"), - Applicability::MachineApplicable, - false, - false, - ), - }); + return Some(( + sp, + format!("consider {}borrowing here", mutability.mutably_str()), + format!("{prefix}{}{sugg_expr}", mutability.ref_prefix_str()), + Applicability::MachineApplicable, + false, + false, + )); } } } @@ -927,51 +928,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Ok(src) = sm.span_to_snippet(sp) { let derefs = "*".repeat(steps); - if let Some((span, src, applicability)) = match mutbl_b { - hir::Mutability::Mut => { - let new_prefix = "&mut ".to_owned() + &derefs; - match mutbl_a { - hir::Mutability::Mut => { - replace_prefix(&src, "&mut ", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(5); - let sp = sp.with_lo(pos).with_hi(pos); - (sp, derefs, Applicability::MachineApplicable) - }) - } - hir::Mutability::Not => { - replace_prefix(&src, "&", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(1); - let sp = sp.with_lo(pos).with_hi(pos); - ( - sp, - format!("mut {derefs}"), - Applicability::Unspecified, - ) - }) - } - } - } - hir::Mutability::Not => { - let new_prefix = "&".to_owned() + &derefs; - match mutbl_a { - hir::Mutability::Mut => { - replace_prefix(&src, "&mut ", &new_prefix).map(|_| { - let lo = sp.lo() + BytePos(1); - let hi = sp.lo() + BytePos(5); - let sp = sp.with_lo(lo).with_hi(hi); - (sp, derefs, Applicability::MachineApplicable) - }) - } - hir::Mutability::Not => { - replace_prefix(&src, "&", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(1); - let sp = sp.with_lo(pos).with_hi(pos); - (sp, derefs, Applicability::MachineApplicable) - }) - } - } - } - } { + let old_prefix = mutbl_a.ref_prefix_str(); + let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs; + + 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 _); + // 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 } + ) + }); + + if let Some((span, src, applicability)) = suggestion { return Some(( span, "consider dereferencing".to_string(), @@ -995,10 +969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the expression has `&`, removing it would fix the error prefix_span = prefix_span.with_hi(inner.span.lo()); expr = inner; - remove += match mutbl { - hir::Mutability::Not => "&", - hir::Mutability::Mut => "&mut ", - }; + remove.push_str(mutbl.ref_prefix_str()); steps -= 1; } else { break; diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 175037f9b..507272fde 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1,10 +1,11 @@ -//! Errors emitted by `rustc_hir_analysis`. +//! Errors emitted by `rustc_hir_typeck`. +use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span}; #[derive(Diagnostic)] -#[diag(hir_analysis_field_multiply_specified_in_initializer, code = "E0062")] +#[diag(hir_typeck_field_multiply_specified_in_initializer, code = "E0062")] pub struct FieldMultiplySpecifiedInInitializer { #[primary_span] #[label] @@ -15,7 +16,7 @@ pub struct FieldMultiplySpecifiedInInitializer { } #[derive(Diagnostic)] -#[diag(hir_analysis_return_stmt_outside_of_fn_body, code = "E0572")] +#[diag(hir_typeck_return_stmt_outside_of_fn_body, code = "E0572")] pub struct ReturnStmtOutsideOfFnBody { #[primary_span] pub span: Span, @@ -26,14 +27,14 @@ pub struct ReturnStmtOutsideOfFnBody { } #[derive(Diagnostic)] -#[diag(hir_analysis_yield_expr_outside_of_generator, code = "E0627")] +#[diag(hir_typeck_yield_expr_outside_of_generator, code = "E0627")] pub struct YieldExprOutsideOfGenerator { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_struct_expr_non_exhaustive, code = "E0639")] +#[diag(hir_typeck_struct_expr_non_exhaustive, code = "E0639")] pub struct StructExprNonExhaustive { #[primary_span] pub span: Span, @@ -41,21 +42,21 @@ pub struct StructExprNonExhaustive { } #[derive(Diagnostic)] -#[diag(hir_analysis_method_call_on_unknown_type, code = "E0699")] +#[diag(hir_typeck_method_call_on_unknown_type, code = "E0699")] pub struct MethodCallOnUnknownType { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_functional_record_update_on_non_struct, code = "E0436")] +#[diag(hir_typeck_functional_record_update_on_non_struct, code = "E0436")] pub struct FunctionalRecordUpdateOnNonStruct { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_address_of_temporary_taken, code = "E0745")] +#[diag(hir_typeck_address_of_temporary_taken, code = "E0745")] pub struct AddressOfTemporaryTaken { #[primary_span] #[label] @@ -65,7 +66,7 @@ pub struct AddressOfTemporaryTaken { #[derive(Subdiagnostic)] pub enum AddReturnTypeSuggestion { #[suggestion( - hir_analysis_add_return_type_add, + hir_typeck_add_return_type_add, code = "-> {found} ", applicability = "machine-applicable" )] @@ -75,7 +76,7 @@ pub enum AddReturnTypeSuggestion { found: String, }, #[suggestion( - hir_analysis_add_return_type_missing_here, + hir_typeck_add_return_type_missing_here, code = "-> _ ", applicability = "has-placeholders" )] @@ -87,12 +88,12 @@ pub enum AddReturnTypeSuggestion { #[derive(Subdiagnostic)] pub enum ExpectedReturnTypeLabel<'tcx> { - #[label(hir_analysis_expected_default_return_type)] + #[label(hir_typeck_expected_default_return_type)] Unit { #[primary_span] span: Span, }, - #[label(hir_analysis_expected_return_type)] + #[label(hir_typeck_expected_return_type)] Other { #[primary_span] span: Span, @@ -101,10 +102,10 @@ pub enum ExpectedReturnTypeLabel<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_missing_parentheses_in_range, code = "E0689")] +#[diag(hir_typeck_missing_parentheses_in_range, code = "E0689")] pub struct MissingParentheseInRange { #[primary_span] - #[label(hir_analysis_missing_parentheses_in_range)] + #[label(hir_typeck_missing_parentheses_in_range)] pub span: Span, pub ty_str: String, pub method_name: String, @@ -113,8 +114,9 @@ pub struct MissingParentheseInRange { } #[derive(Subdiagnostic)] -#[multipart_suggestion_verbose( - hir_analysis_add_missing_parentheses_in_range, +#[multipart_suggestion( + hir_typeck_add_missing_parentheses_in_range, + style = "verbose", applicability = "maybe-incorrect" )] pub struct AddMissingParenthesesInRange { @@ -124,3 +126,49 @@ pub struct AddMissingParenthesesInRange { #[suggestion_part(code = ")")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_op_trait_generic_params)] +pub struct OpMethodGenericParams { + #[primary_span] + pub span: Span, + pub method_name: String, +} + +pub struct TypeMismatchFruTypo { + /// Span of the LHS of the range + pub expr_span: Span, + /// Span of the `..RHS` part of the range + pub fru_span: Span, + /// Rendered expression of the RHS of the range + pub expr: Option, +} + +impl AddToDiagnostic for TypeMismatchFruTypo { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.set_arg("expr", self.expr.as_deref().unwrap_or("NONE")); + + // Only explain that `a ..b` is a range if it's split up + if self.expr_span.between(self.fru_span).is_empty() { + diag.span_note( + self.expr_span.to(self.fru_span), + rustc_errors::fluent::hir_typeck_fru_note, + ); + } else { + let mut multispan: MultiSpan = vec![self.expr_span, self.fru_span].into(); + multispan.push_span_label(self.expr_span, rustc_errors::fluent::hir_typeck_fru_expr); + multispan.push_span_label(self.fru_span, rustc_errors::fluent::hir_typeck_fru_expr2); + diag.span_note(multispan, rustc_errors::fluent::hir_typeck_fru_note); + } + + diag.span_suggestion( + self.expr_span.shrink_to_hi(), + rustc_errors::fluent::hir_typeck_fru_suggestion, + ", ", + Applicability::MaybeIncorrect, + ); + } +} diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index e9e810344..4f086cf59 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -79,9 +79,9 @@ impl<'a, 'tcx> Expectation<'tcx> { } } - // Resolves `expected` by a single level if it is a variable. If - // there is no expected type or resolution is not possible (e.g., - // no constraints yet present), just returns `self`. + /// Resolves `expected` by a single level if it is a variable. If + /// there is no expected type or resolution is not possible (e.g., + /// no constraints yet present), just returns `self`. fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { match self { NoExpectation => NoExpectation, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 9fde62a81..ed87b94a0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -5,6 +5,7 @@ use crate::cast; use crate::coercion::CoerceMany; use crate::coercion::DynamicCoerceMany; +use crate::errors::TypeMismatchFruTypo; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use crate::errors::{ FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, @@ -30,7 +31,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{Closure, ExprKind, HirId, QPath}; +use rustc_hir::{ExprKind, HirId, QPath}; use rustc_hir_analysis::astconv::AstConv as _; use rustc_hir_analysis::check::ty_kind_suggestion; use rustc_infer::infer; @@ -80,14 +81,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // coercions from ! to `expected`. if ty.is_never() { if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { - self.tcx().sess.delay_span_bug( + let reported = self.tcx().sess.delay_span_bug( expr.span, "expression with never type wound up being adjusted", ); return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { target.to_owned() } else { - self.tcx().ty_error() + self.tcx().ty_error_with_guaranteed(reported) }; } @@ -103,8 +104,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { - let expr = expr.peel_drop_temps(); - self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); + // FIXME(compiler-errors): We probably should fold some of the + // `suggest_` functions from `emit_coerce_suggestions` into here, + // since some of those aren't necessarily just coerce suggestions. + let _ = self.suggest_deref_ref_or_into( + &mut err, + expr.peel_drop_temps(), + expected_ty, + ty, + None, + ) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty); extend_err(&mut err); err.emit(); } @@ -220,7 +229,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Hide the outer diverging and has_errors flags. let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); let ty = ensure_sufficient_stack(|| match &expr.kind { hir::ExprKind::Path( @@ -259,7 +267,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine the diverging and has_error flags. self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id)); debug!("... {:?}, expected is {:?}", ty, expected); @@ -318,9 +325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Match(discrim, arms, match_src) => { self.check_match(expr, &discrim, arms, expected, match_src) } - ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => { - self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected) - } + ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected), ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected), ExprKind::MethodCall(segment, receiver, args, _) => { @@ -398,8 +403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } - err.emit(); - oprnd_t = tcx.ty_error(); + oprnd_t = tcx.ty_error_with_guaranteed(err.emit()); } } hir::UnOp::Not => { @@ -524,12 +528,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); let ty = match res { Res::Err => { - self.set_tainted_by_errors(); - tcx.ty_error() + self.suggest_assoc_method_call(segs); + let e = + self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(e); + tcx.ty_error_with_guaranteed(e) } - Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, qpath, expr.span); - tcx.ty_error() + Res::Def(DefKind::Variant, _) => { + let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, "E0533", "value"); + tcx.ty_error_with_guaranteed(e) } _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, }; @@ -840,10 +847,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_expr_ty, ); - if self.return_type_has_opaque { + if let Some(fn_sig) = self.body_fn_sig() + && fn_sig.output().has_opaque_types() + { // Point any obligations that were registered due to opaque type // inference at the return expression. - self.select_obligations_where_possible(false, |errors| { + self.select_obligations_where_possible(|errors| { self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty); }); } @@ -906,8 +915,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Check if an expression `original_expr_id` comes from the condition of a while loop, - // as opposed from the body of a while loop, which we can naively check by iterating - // parents until we find a loop... + /// as opposed from the body of a while loop, which we can naively check by iterating + /// parents until we find a loop... pub(super) fn comes_from_while_condition( &self, original_expr_id: HirId, @@ -1097,12 +1106,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the assignment expression itself is ill-formed, don't // bother emitting another error - if lhs_ty.references_error() || rhs_ty.references_error() { - err.delay_as_bug() - } else { - err.emit(); - } - return self.tcx.ty_error(); + let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()); + return self.tcx.ty_error_with_guaranteed(reported); } let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); @@ -1114,9 +1119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let lhs_deref_ty_is_sized = self .infcx .type_implements_trait( - self.tcx.lang_items().sized_trait().unwrap(), - lhs_deref_ty, - ty::List::empty(), + self.tcx.require_lang_item(LangItem::Sized, None), + [lhs_deref_ty], self.param_env, ) .may_apply(); @@ -1613,10 +1617,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_coerce_diag(&field.expr, ty, field_type, None, AllowTwoPhase::No); if let Some(mut diag) = diag { - if idx == ast_fields.len() - 1 && remaining_fields.is_empty() { - self.suggest_fru_from_range(field, variant, substs, &mut diag); + if idx == ast_fields.len() - 1 { + if remaining_fields.is_empty() { + self.suggest_fru_from_range(field, variant, substs, &mut diag); + diag.emit(); + } else { + diag.stash(field.span, StashKey::MaybeFruTypo); + } + } else { + diag.emit(); } - diag.emit(); } } @@ -1637,6 +1647,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. if error_happened { + if let Some(base_expr) = base_expr { + self.check_expr(base_expr); + } return; } @@ -1655,7 +1668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .fields .iter() .map(|f| { - let fru_ty = self.normalize_associated_types_in( + let fru_ty = self.normalize( expr_span, self.field_ty(base_expr.span, f, fresh_substs), ); @@ -1739,9 +1752,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(adt, substs) if adt.is_struct() => variant .fields .iter() - .map(|f| { - self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) - }) + .map(|f| self.normalize(expr_span, f.ty(self.tcx, substs))) .collect(), _ => { self.tcx @@ -1874,19 +1885,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|adt| adt.did()) != range_def_id { - let instead = self + // Suppress any range expr type mismatches + if let Some(mut diag) = self + .tcx + .sess + .diagnostic() + .steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo) + { + diag.delay_as_bug(); + } + + // Use a (somewhat arbitrary) filtering heuristic to avoid printing + // expressions that are either too long, or have control character + //such as newlines in them. + let expr = self .tcx .sess .source_map() .span_to_snippet(range_end.expr.span) - .map(|s| format!(" from `{s}`")) - .unwrap_or_default(); - err.span_suggestion( - range_start.span.shrink_to_hi(), - &format!("to set the remaining fields{instead}, separate the last named field with a comma"), - ",", - Applicability::MaybeIncorrect, - ); + .ok() + .filter(|s| s.len() < 25 && !s.contains(|c: char| c.is_control())); + + let fru_span = self + .tcx + .sess + .source_map() + .span_extend_while(range_start.span, |c| c.is_whitespace()) + .unwrap_or(range_start.span).shrink_to_hi().to(range_end.span); + + err.subdiagnostic(TypeMismatchFruTypo { + expr_span: range_start.span, + fru_span, + expr, + }); } } @@ -1961,7 +1992,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span: Span, ) { if variant.is_recovered() { - self.set_tainted_by_errors(); + self.set_tainted_by_errors( + self.tcx + .sess + .delay_span_bug(expr_span, "parser recovered but no error was emitted"), + ); return; } let mut err = self.err_ctxt().type_error_struct_with_diag( @@ -1991,8 +2026,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); - match variant.ctor_kind { - CtorKind::Fn => match ty.kind() { + match variant.ctor_kind() { + Some(CtorKind::Fn) => match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => { err.span_label( variant_ident_span, @@ -2300,12 +2335,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, ty: Ty<'tcx>, ) { - let output_ty = match self.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty), - _ => return, - }; + let Some(output_ty) = self.get_impl_future_output_ty(ty) else { return; }; let mut add_label = true; - if let ty::Adt(def, _) = output_ty.skip_binder().kind() { + if let ty::Adt(def, _) = output_ty.kind() { // no field access on enum type if !def.is_enum() { if def @@ -2738,7 +2770,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((index_ty, element_ty)) => { // two-phase not needed because index_ty is never mutable self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); - self.select_obligations_where_possible(false, |errors| { + self.select_obligations_where_possible(|errors| { self.point_at_index_if_possible(errors, idx.span) }); element_ty @@ -2777,8 +2809,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - err.emit(); - self.tcx.ty_error() + let reported = err.emit(); + self.tcx.ty_error_with_guaranteed(reported) } } } @@ -2791,7 +2823,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { for error in errors { match error.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(predicate) + ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => { } _ => continue, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index fce2a5888..03b174c77 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -252,11 +252,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { hir::ExprKind::Match(ref discr, arms, _) => { let discr_place = return_if_err!(self.mc.cat_expr(discr)); - self.maybe_read_scrutinee( + return_if_err!(self.maybe_read_scrutinee( discr, discr_place.clone(), arms.iter().map(|arm| arm.pat), - ); + )); // treatment of the discriminant is handled while walking the arms. for arm in arms { @@ -352,8 +352,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_expr(base); } - hir::ExprKind::Closure { .. } => { - self.walk_captures(expr); + hir::ExprKind::Closure(closure) => { + self.walk_captures(closure); } hir::ExprKind::Box(ref base) => { @@ -390,7 +390,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { discr: &Expr<'_>, discr_place: PlaceWithHirId<'tcx>, pats: impl Iterator>, - ) { + ) -> Result<(), ()> { // Matching should not always be considered a use of the place, hence // discr does not necessarily need to be borrowed. // We only want to borrow discr if the pattern contain something other @@ -398,7 +398,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; let mut needs_to_be_read = false; for pat in pats { - return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { + mc.cat_pattern(discr_place.clone(), pat, |place, pat| { match &pat.kind { PatKind::Binding(.., opt_sub_pat) => { // If the opt_sub_pat is None, than the binding does not count as @@ -453,7 +453,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // examined } } - })); + })? } if needs_to_be_read { @@ -474,6 +474,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // that the discriminant has been initialized. self.walk_expr(discr); } + Ok(()) } fn walk_local( @@ -490,7 +491,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { f(self); if let Some(els) = els { // borrowing because we need to test the discriminant - self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); + return_if_err!(self.maybe_read_scrutinee( + expr, + expr_place.clone(), + from_ref(pat).iter() + )); self.walk_block(els) } self.walk_irrefutable_pat(&expr_place, &pat); @@ -518,6 +523,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // Consume the expressions supplying values for each field. for field in fields { self.consume_expr(field.expr); + + // The struct path probably didn't resolve + if self.mc.typeck_results.opt_field_index(field.hir_id).is_none() { + self.tcx().sess.delay_span_bug(field.span, "couldn't resolve index for field"); + } } let with_expr = match *opt_with { @@ -535,9 +545,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ty::Adt(adt, substs) if adt.is_struct() => { // Consume those fields of the with expression that are needed. for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { - let is_mentioned = fields.iter().any(|f| { - self.tcx().field_index(f.hir_id, self.mc.typeck_results) == f_index - }); + let is_mentioned = fields + .iter() + .any(|f| self.mc.typeck_results.opt_field_index(f.hir_id) == Some(f_index)); if !is_mentioned { let field_place = self.mc.cat_projection( &*with_expr, @@ -745,7 +755,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing /// closure as the DefId. - fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { + fn walk_captures(&mut self, closure_expr: &hir::Closure<'_>) { fn upvar_is_local_variable<'tcx>( upvars: Option<&'tcx FxIndexMap>, upvar_id: hir::HirId, @@ -757,7 +767,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { debug!("walk_captures({:?})", closure_expr); let tcx = self.tcx(); - let closure_def_id = tcx.hir().local_def_id(closure_expr.hir_id); + let closure_def_id = closure_expr.def_id; let upvars = tcx.upvars_mentioned(self.body_owner); // For purposes of this function, generator and closures are equivalent. @@ -829,10 +839,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // be a local variable PlaceBase::Local(*var_hir_id) }; + let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id); let place_with_id = PlaceWithHirId::new( - capture_info.path_expr_id.unwrap_or( - capture_info.capture_kind_expr_id.unwrap_or(closure_expr.hir_id), - ), + capture_info + .path_expr_id + .unwrap_or(capture_info.capture_kind_expr_id.unwrap_or(closure_hir_id)), place.base_ty, place_base, place.projections.clone(), diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 747ecb036..ac6b0924a 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -7,16 +7,16 @@ use rustc_data_structures::{ use rustc_middle::ty::{self, Ty}; impl<'tcx> FnCtxt<'_, 'tcx> { - /// Performs type inference fallback, returning true if any fallback - /// occurs. - pub(super) fn type_inference_fallback(&self) -> bool { + /// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred` + /// if fallback has occurred. + pub(super) fn type_inference_fallback(&self) { debug!( "type-inference-fallback start obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations() ); // All type checking constraints were added, try to fallback unsolved variables. - self.select_obligations_where_possible(false, |_| {}); + self.select_obligations_where_possible(|_| {}); debug!( "type-inference-fallback post selection obligations: {:#?}", @@ -26,18 +26,17 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // Check if we have any unsolved variables. If not, no need for fallback. let unsolved_variables = self.unsolved_variables(); if unsolved_variables.is_empty() { - return false; + return; } let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables); - let mut fallback_has_occurred = false; // We do fallback in two passes, to try to generate // better error messages. // The first time, we do *not* replace opaque types. for ty in unsolved_variables { debug!("unsolved_variable = {:?}", ty); - fallback_has_occurred |= self.fallback_if_possible(ty, &diverging_fallback); + self.fallback_if_possible(ty, &diverging_fallback); } // We now see if we can make progress. This might cause us to @@ -63,9 +62,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // If we had tried to fallback the opaque inference variable to `MyType`, // we will generate a confusing type-check error that does not explicitly // refer to opaque types. - self.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - fallback_has_occurred + self.select_obligations_where_possible(|_| {}); } // Tries to apply a fallback to `ty` if it is an unsolved variable. @@ -81,12 +78,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // Fallback becomes very dubious if we have encountered // type-checking errors. In that case, fallback to Error. // - // The return value indicates whether fallback has occurred. + // Sets `FnCtxt::fallback_has_occurred` if fallback is performed + // during this call. fn fallback_if_possible( &self, ty: Ty<'tcx>, diverging_fallback: &FxHashMap, Ty<'tcx>>, - ) -> bool { + ) { // Careful: we do NOT shallow-resolve `ty`. We know that `ty` // is an unsolved variable, and we determine its fallback // based solely on how it was created, not what other type @@ -106,12 +104,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // type, `?T` is not considered unsolved, but `?I` is. The // same is true for float variables.) let fallback = match ty.kind() { - _ if self.is_tainted_by_errors() => self.tcx.ty_error(), + _ if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), ty::Infer(ty::IntVar(_)) => self.tcx.types.i32, ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64, _ => match diverging_fallback.get(&ty) { Some(&fallback_ty) => fallback_ty, - None => return false, + None => return, }, }; debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); @@ -122,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .map(|origin| origin.span) .unwrap_or(rustc_span::DUMMY_SP); self.demand_eqtype(span, ty, fallback); - true + self.fallback_has_occurred.set(true); } /// The "diverging fallback" system is rather complicated. This is diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6a1cffe3e..952d27262 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -16,13 +16,13 @@ use rustc_hir_analysis::astconv::{ }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::{InferOk, InferResult}; +use rustc_infer::infer::InferResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, - ToPredicate, Ty, UserType, + self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, Ty, UserType, }; use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; @@ -30,11 +30,8 @@ use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, DUMMY_SP}; -use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; +use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; use std::collections::hash_map::Entry; use std::slice; @@ -106,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // possible. This can help substantially when there are // indirect dependencies that don't seem worth tracking // precisely. - self.select_obligations_where_possible(false, mutate_fulfillment_errors); + self.select_obligations_where_possible(mutate_fulfillment_errors); self.resolve_vars_if_possible(ty) } @@ -142,9 +139,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag()); self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); - if ty.references_error() { - self.has_errors.set(true); - self.set_tainted_by_errors(); + if let Err(e) = ty.error_reported() { + self.set_tainted_by_errors(e); } } @@ -346,7 +342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs); let value = EarlyBinder(value).subst(self.tcx, substs); - let result = self.normalize_associated_types_in(span, value); + let result = self.normalize(span, value); debug!("instantiate_type_scheme = {:?}", result); result } @@ -362,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bounds = self.tcx.predicates_of(def_id); let spans: Vec = bounds.predicates.iter().map(|(_, span)| *span).collect(); let result = bounds.instantiate(self.tcx, substs); - let result = self.normalize_associated_types_in(span, result); + let result = self.normalize(span, result); debug!( "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", bounds, substs, result, spans, @@ -370,50 +366,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (result, spans) } - pub(in super::super) fn normalize_associated_types_in(&self, span: Span, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub(in super::super) fn normalize_associated_types_in_as_infer_ok( - &self, - span: Span, - value: T, - ) -> InferOk<'tcx, T> + pub(in super::super) fn normalize(&self, span: Span, value: T) -> T where T: TypeFoldable<'tcx>, { - self.inh.partially_normalize_associated_types_in( - ObligationCause::misc(span, self.body_id), - self.param_env, - value, - ) - } - - pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok( - &self, - span: Span, - value: T, - opt_input_expr: Option<&hir::Expr<'_>>, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in( - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), - output_ty: None, - }, - ), - self.param_env, - value, + self.register_infer_ok_obligations( + self.at(&self.misc(span), self.param_env).normalize(value), ) } @@ -490,11 +448,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), hir::ArrayLen::Body(anon_const) => { - let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let span = self.tcx.hir().span(anon_const.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); + let span = self.tcx.def_span(anon_const.def_id); + let c = ty::Const::from_anon_const(self.tcx, anon_const.def_id); self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); - self.normalize_associated_types_in(span, c) + self.normalize(span, c) } } } @@ -504,10 +461,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast_c: &hir::AnonConst, param_def_id: DefId, ) -> ty::Const<'tcx> { - let const_def = ty::WithOptConstParam { - did: self.tcx.hir().local_def_id(ast_c.hir_id), - const_param_did: Some(param_def_id), - }; + 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); self.register_wf_obligation( c.into(), @@ -534,7 +489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.ty_error(), + None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), None => { bug!( "no type for node {}: {} in fcx {}", @@ -549,7 +504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty_opt(&self, id: hir::HirId) -> Option> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => Some(t), - None if self.is_tainted_by_errors() => Some(self.tcx.ty_error()), + None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error_with_guaranteed(e)), None => None, } } @@ -564,9 +519,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // WF obligations never themselves fail, so no real need to give a detailed cause: let cause = traits::ObligationCause::new(span, self.body_id, code); self.register_predicate(traits::Obligation::new( + self.tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), )); } @@ -588,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: &'tcx ty::FieldDef, substs: SubstsRef<'tcx>, ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, field.ty(self.tcx, substs)) + self.normalize(span, field.ty(self.tcx, substs)) } pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) { @@ -601,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false, |_| {}); + self.select_obligations_where_possible(|_| {}); crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); } } @@ -612,25 +568,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false); + self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id); } } /// Select as many obligations as we can at present. pub(in super::super) fn select_obligations_where_possible( &self, - fallback_has_occurred: bool, mutate_fulfillment_errors: impl Fn(&mut Vec>), ) { let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self); if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.err_ctxt().report_fulfillment_errors( - &result, - self.inh.body_id, - fallback_has_occurred, - ); + self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id); } } @@ -650,12 +601,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn self_type_matches_expected_vid( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid, - ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); + fn self_type_matches_expected_vid(&self, self_ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { + let self_ty = self.shallow_resolve(self_ty); debug!(?self_ty); match *self_ty.kind() { @@ -674,54 +621,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn obligations_for_self_ty<'b>( &'b self, self_ty: ty::TyVid, - ) -> impl Iterator, traits::PredicateObligation<'tcx>)> - + Captures<'tcx> - + 'b { + ) -> impl DoubleEndedIterator> + Captures<'tcx> + 'b + { // FIXME: consider using `sub_root_var` here so we // can see through subtyping. let ty_var_root = self.root_var(self_ty); trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations()); - self.fulfillment_cx - .borrow() - .pending_obligations() - .into_iter() - .filter_map(move |obligation| { - let bound_predicate = obligation.predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Projection(data) => Some(( - bound_predicate.rebind(data).required_poly_trait_ref(self.tcx), - obligation, - )), - ty::PredicateKind::Trait(data) => { - Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) - } - ty::PredicateKind::Subtype(..) => None, - ty::PredicateKind::Coerce(..) => None, - ty::PredicateKind::RegionOutlives(..) => None, - ty::PredicateKind::TypeOutlives(..) => None, - ty::PredicateKind::WellFormed(..) => None, - ty::PredicateKind::ObjectSafe(..) => None, - ty::PredicateKind::ConstEvaluatable(..) => None, - ty::PredicateKind::ConstEquate(..) => None, - // N.B., this predicate is created by breaking down a - // `ClosureType: FnFoo()` predicate, where - // `ClosureType` represents some `Closure`. It can't - // possibly be referring to the current closure, - // because we haven't produced the `Closure` for - // this closure yet; this is exactly why the other - // code is looking for a self type of an unresolved - // inference variable. - ty::PredicateKind::ClosureKind(..) => None, - ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map( + move |obligation| match &obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(data)) + if self.self_type_matches_expected_vid( + data.projection_ty.self_ty(), + ty_var_root, + ) => + { + Some(obligation) } - }) - .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) + ty::PredicateKind::Clause(ty::Clause::Trait(data)) + if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) => + { + Some(obligation) + } + + ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + // N.B., this predicate is created by breaking down a + // `ClosureType: FnFoo()` predicate, where + // `ClosureType` represents some `Closure`. It can't + // possibly be referring to the current closure, + // because we haven't produced the `Closure` for + // this closure yet; this is exactly why the other + // code is looking for a self type of an unresolved + // inference variable. + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + }, + ) } pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { - self.obligations_for_self_ty(self_ty) - .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) + let sized_did = self.tcx.lang_items().sized_trait(); + self.obligations_for_self_ty(self_ty).any(|obligation| { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + Some(data.def_id()) == sized_did + } + _ => false, + } + }) } pub(in super::super) fn err_args(&self, len: usize) -> Vec> { @@ -769,34 +726,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expect_args = self .fudge_inference_if_ok(|| { + let ocx = ObligationCtxt::new_in_snapshot(self); + // Attempt to apply a subtyping relationship between the formal // return type (likely containing type variables if the function // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // we can. We don't care if some things turn - // out unconstrained or ambiguous, as we're - // just trying to get hints here. - let errors = self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = >::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }); - - if !errors.is_empty() { - return Err(()); - } - } - Err(_) => return Err(()), + ocx.sup(&origin, self.param_env, ret_ty, formal_ret)?; + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); } // Record all the argument types, with the substitutions @@ -1132,7 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Res::Local(hid) = res { let ty = self.local_ty(span, hid).decl_ty; - let ty = self.normalize_associated_types_in(span, ty); + let ty = self.normalize(span, ty); self.write_ty(hir_id, ty); return (ty, res); } @@ -1173,9 +1112,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { explicit_late_bound = ExplicitLateBound::Yes; } - if let Err(GenericArgCountMismatch { reported: Some(_), .. }) = arg_count.correct { + if let Err(GenericArgCountMismatch { reported: Some(e), .. }) = arg_count.correct { infer_args_for_err.insert(index); - self.set_tainted_by_errors(); // See issue #53251. + self.set_tainted_by_errors(e); // See issue #53251. } } @@ -1189,11 +1128,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *ty.kind() { ty::Adt(adt_def, substs) if adt_def.has_ctor() => { let variant = adt_def.non_enum_variant(); - let ctor_def_id = variant.ctor_def_id.unwrap(); - ( - Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), - Some(substs), - ) + let (ctor_kind, ctor_def_id) = variant.ctor.unwrap(); + (Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id), Some(substs)) } _ => { let mut err = tcx.sess.struct_span_err( @@ -1215,9 +1151,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - - return (tcx.ty_error(), res); + let reported = err.emit(); + return (tcx.ty_error_with_guaranteed(reported), res); } } } else { @@ -1466,12 +1401,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ty.is_ty_var() { ty } else { - if !self.is_tainted_by_errors() { + let e = self.tainted_by_errors().unwrap_or_else(|| { self.err_ctxt() .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) - .emit(); - } - let err = self.tcx.ty_error(); + .emit() + }); + let err = self.tcx.ty_error_with_guaranteed(e); self.demand_suptype(sp, err, ty); err } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8e0fcb56c..60fec05d3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -73,7 +73,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); let ty = self.resolve_vars_if_possible(ty); if ty.has_non_region_infer() { - assert!(self.is_tainted_by_errors()); self.tcx.ty_error() } else { self.tcx.erase_regions(ty) @@ -136,6 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, Some(method.def_id), ); + method.sig.output() } @@ -214,7 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "cannot use call notation; the first type parameter \ for the function trait is neither a tuple nor unit" ) - .emit(); + .delay_as_bug(); (self.err_args(provided_args.len()), None) } } @@ -344,7 +344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // an "opportunistic" trait resolution of any trait bounds on // the call. This helps coercions. if check_closures { - self.select_obligations_where_possible(false, |_| {}) + self.select_obligations_where_possible(|_| {}) } // Check each argument, to satisfy the input it was provided for @@ -511,8 +511,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) } - self.set_tainted_by_errors(); let tcx = self.tcx; + // FIXME: taint after emitting errors and pass through an `ErrorGuaranteed` + self.set_tainted_by_errors( + tcx.sess.delay_span_bug(call_span, "no errors reported for args"), + ); // Get the argument span in the context of the call span so that // suggestions and labels are (more) correct when an arg is a @@ -596,6 +599,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), true, mismatched_ty, provided_ty) + }; + // The algorithm here is inspired by levenshtein distance and longest common subsequence. // We'll try to detect 4 different types of mistakes: // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs @@ -660,10 +675,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A tuple wrap suggestion actually occurs within, // so don't do anything special here. err = self.err_ctxt().report_and_explain_type_error( - TypeTrace::types( - &self.misc(*lo), - true, - formal_and_expected_inputs[mismatch_idx.into()].1, + mk_trace( + *lo, + formal_and_expected_inputs[mismatch_idx.into()], provided_arg_tys[mismatch_idx.into()].0, ), terr, @@ -747,9 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors.drain_filter(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; @@ -773,8 +785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; - let cause = &self.misc(provided_arg_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err); self.emit_coerce_suggestions( &mut err, @@ -846,8 +857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; if let Compatibility::Incompatible(error) = compatibility { - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); if let Some(e) = error { self.err_ctxt().note_type_err( &mut err, @@ -1200,7 +1210,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); let variant = match def { Res::Err => { - self.set_tainted_by_errors(); + self.set_tainted_by_errors( + self.tcx.sess.delay_span_bug(path_span, "`Res::Err` but no error emitted"), + ); return None; } Res::Def(DefKind::Variant, _) => match ty.kind() { @@ -1334,7 +1346,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Hide the outer diverging and `has_errors` flags. let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); match stmt.kind { hir::StmtKind::Local(l) => { @@ -1364,7 +1375,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine the diverging and `has_error` flags. self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); } pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { @@ -1383,8 +1393,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { blk: &'tcx hir::Block<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let prev = self.ps.replace(self.ps.get().recurse(blk)); - // In some cases, blocks have just one exit, but other blocks // can be targeted by multiple breaks. This can happen both // with labeled blocks as well as when we desugar @@ -1544,15 +1552,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.diverges.set(prev_diverges); } - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } + let ty = ctxt.coerce.unwrap().complete(self); self.write_ty(blk.hir_id, ty); - self.ps.set(prev); ty } @@ -1728,22 +1731,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir = self.tcx.hir(); let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; }; - // Skip over mentioning async lang item - if Some(def_id) == self.tcx.lang_items().from_generator_fn() - && error.obligation.cause.span.desugaring_kind() - == Some(rustc_span::DesugaringKind::Async) - { - return false; - } - let Some(unsubstituted_pred) = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx) else { return false; }; let generics = self.tcx.generics_of(def_id); let predicate_substs = match unsubstituted_pred.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => pred.trait_ref.substs, - ty::PredicateKind::Projection(pred) => pred.projection_ty.substs, + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs, + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => pred.projection_ty.substs, _ => ty::List::empty(), }; @@ -1920,7 +1915,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { receiver: Option<&'tcx hir::Expr<'tcx>>, args: &'tcx [hir::Expr<'tcx>], ) -> bool { - let sig = self.tcx.fn_sig(def_id).skip_binder(); + let ty = self.tcx.type_of(def_id); + if !ty.is_fn() { + return false; + } + let sig = ty.fn_sig(self.tcx).skip_binder(); let args_referencing_param: Vec<_> = sig .inputs() .iter() @@ -2091,7 +2090,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" - && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id) + && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) && let Some(callee_ty) = callee_ty { let callee_ty = callee_ty.peel_refs(); @@ -2115,9 +2114,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (predicate, span) in std::iter::zip(instantiated.predicates, instantiated.spans) { - if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder() && pred.self_ty().peel_refs() == callee_ty - && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some() + && self.tcx.is_fn_trait(pred.def_id()) { err.span_note(span, "callable defined here"); return; @@ -2148,13 +2147,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); let obligation = traits::Obligation::new( + self.tcx, traits::ObligationCause::dummy(), self.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), + ty::Binder::dummy(trait_ref), ); match SelectionContext::new(&self).select(&obligation) { Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 0c600daf4..30b59da78 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -4,10 +4,11 @@ mod checks; mod suggestions; pub use _impl::*; +use rustc_errors::ErrorGuaranteed; pub use suggestions::*; use crate::coercion::DynamicCoerceMany; -use crate::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; +use crate::{Diverges, EnclosingBreakables, Inherited}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir_analysis::astconv::AstConv; @@ -21,7 +22,7 @@ use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; use std::ops::Deref; @@ -68,17 +69,11 @@ pub struct FnCtxt<'a, 'tcx> { /// any). pub(super) ret_coercion: Option>>, - /// Used exclusively to reduce cost of advanced evaluation used for - /// more helpful diagnostics. - pub(super) in_tail_expr: bool, - /// First span of a return site that we find. Used in error messages. pub(super) ret_coercion_span: Cell>, pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - pub(super) ps: Cell, - /// Whether the last checked node generates a divergence (e.g., /// `return` will set this to `Always`). In general, when entering /// an expression or other node in the tree, the initial value @@ -112,21 +107,11 @@ pub struct FnCtxt<'a, 'tcx> { /// the diverges flag is set to something other than `Maybe`. pub(super) diverges: Cell, - /// Whether any child nodes have any type errors. - pub(super) has_errors: Cell, - pub(super) enclosing_breakables: RefCell>, pub(super) inh: &'a Inherited<'tcx>, - /// True if the function or closure's return type is known before - /// entering the function/closure, i.e. if the return type is - /// either given explicitly or inferred from, say, an `Fn*` trait - /// bound. Used for diagnostic purposes only. - pub(super) return_type_pre_known: bool, - - /// True if the return type has an Opaque type - pub(super) return_type_has_opaque: bool, + pub(super) fallback_has_occurred: Cell, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -140,19 +125,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env, err_count_on_creation: inh.tcx.sess.err_count(), ret_coercion: None, - in_tail_expr: false, ret_coercion_span: Cell::new(None), resume_yield_tys: None, - ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), enclosing_breakables: RefCell::new(EnclosingBreakables { stack: Vec::new(), by_id: Default::default(), }), inh, - return_type_pre_known: true, - return_type_has_opaque: false, + fallback_has_occurred: Cell::new(false), } } @@ -174,7 +155,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { - TypeErrCtxt { infcx: &self.infcx, typeck_results: Some(self.typeck_results.borrow()) } + TypeErrCtxt { + infcx: &self.infcx, + typeck_results: Some(self.typeck_results.borrow()), + fallback_has_occurred: self.fallback_has_occurred.get(), + normalize_fn_sig: Box::new(|fn_sig| { + if fn_sig.has_escaping_bound_vars() { + return fn_sig; + } + self.probe(|_| { + let ocx = ObligationCtxt::new_in_snapshot(self); + let normalized_fn_sig = + 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() { + return normalized_fn_sig; + } + } + fn_sig + }) + }), + } } pub fn errors_reported_since_creation(&self) -> bool { @@ -194,8 +196,8 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { self.tcx } - fn item_def_id(&self) -> Option { - None + fn item_def_id(&self) -> DefId { + self.body_id.owner.to_def_id() } fn get_type_parameter_bounds( @@ -213,7 +215,9 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { predicates: tcx.arena.alloc_from_iter( self.param_env.caller_bounds().iter().filter_map(|predicate| { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(data) if data.self_ty().is_param(index) => { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) + if data.self_ty().is_param(index) => + { // HACK(eddyb) should get the original `Span`. let span = tcx.def_span(def_id); Some((predicate, span)) @@ -298,12 +302,12 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { if ty.has_escaping_bound_vars() { ty // FIXME: normalization and escaping regions } else { - self.normalize_associated_types_in(span, ty) + self.normalize(span, ty) } } - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.infcx.set_tainted_by_errors(e) } fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 4db9c56f9..4f92477b5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,24 +10,35 @@ use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::infer::{self, TyCtxtInferExt}; +use rustc_infer::infer; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; +use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::NormalizeExt; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(crate) fn body_fn_sig(&self) -> Option> { + self.typeck_results + .borrow() + .liberated_fn_sigs() + .get(self.tcx.hir().get_parent_node(self.body_id)) + .copied() + } + pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { + // This suggestion is incorrect for + // fn foo() -> bool { match () { () => true } || match () { () => true } } err.span_suggestion_short( span.shrink_to_hi(), "consider using a semicolon here", ";", - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } @@ -165,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Opaque(def_id, substs) => { self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() // args tuple will always be substs[1] && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() @@ -200,7 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Param(param) => { let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id; self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() && proj.projection_ty.self_ty() == found // args tuple will always be substs[1] @@ -237,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // implied by wf, but also because that would possibly result in // erroneous errors later on. let infer::InferOk { value: output, obligations: _ } = - self.normalize_associated_types_in_as_infer_ok(expr.span, output); + self.at(&self.misc(expr.span), self.param_env).normalize(output); if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } } @@ -337,8 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if annotation { let suggest_annotation = match expr.peel_drop_temps().kind { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&", - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ", + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(), _ => return true, }; let mut tuple_indexes = Vec::new(); @@ -366,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let annotation_span = ty.span; err.span_suggestion( annotation_span.with_hi(annotation_span.lo()), - format!("alternatively, consider changing the type annotation"), + "alternatively, consider changing the type annotation", suggest_annotation, Applicability::MaybeIncorrect, ); @@ -456,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ref_cnt += 1; } if let ty::Adt(adt, _) = peeled.kind() - && self.tcx.is_diagnostic_item(sym::String, adt.did()) + && Some(adt.did()) == self.tcx.lang_items().string() { err.span_suggestion_verbose( expr.span.shrink_to_hi(), @@ -752,7 +762,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("suggest_missing_return_type: expected type {:?}", ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = Binder::bind_with_vars(ty, bound_vars); - let ty = self.normalize_associated_types_in(span, ty); + let ty = self.normalize(span, ty); let ty = self.tcx.erase_late_bound_regions(ty); if self.can_coerce(expected, ty) { err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); @@ -913,22 +923,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = >::ast_ty_to_ty(self, ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); - let ty = self.normalize_associated_types_in(expr.span, ty); let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => { - let infcx = self.tcx.infer_ctxt().build(); - infcx - .get_impl_future_output_ty(ty) - .unwrap_or_else(|| { - span_bug!( - fn_decl.output.span(), - "failed to get output type of async function" - ) - }) - .skip_binder() - } + hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!(fn_decl.output.span(), "failed to get output type of async function") + }), hir::IsAsync::NotAsync => ty, }; + let ty = self.normalize(expr.span, ty); if self.can_coerce(found, ty) { err.multipart_suggestion( "you might have meant to return this value", @@ -1082,14 +1083,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( + self.tcx, self.misc(expr.span), self.param_env, - ty::Binder::dummy(ty::TraitRef { - def_id: into_def_id, - substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]), - }) - .to_poly_trait_predicate() - .to_predicate(self.tcx), + ty::Binder::dummy(self.tcx.mk_trait_ref( + into_def_id, + [expr_ty, expected_ty] + )), )) { let sugg = if expr.precedence().order() >= PREC_POSTFIX { @@ -1108,6 +1108,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()` + pub(crate) fn suggest_option_to_bool( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_bool() { + return false; + } + + let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; }; + if !self.tcx.is_diagnostic_item(sym::Option, def.did()) { + return false; + } + + let hir = self.tcx.hir(); + let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| { + matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And) + }).next(); + // Don't suggest: + // `let Some(_) = a.is_some() && b` + // ++++++++++ + // since the user probably just misunderstood how `let else` + // and `&&` work together. + if let Some((_, hir::Node::Local(local))) = cond_parent + && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind + && let hir::QPath::Resolved(None, path) = qpath + && let Some(did) = path.res.opt_def_id() + .and_then(|did| self.tcx.opt_parent(did)) + .and_then(|did| self.tcx.opt_parent(did)) + && self.tcx.is_diagnostic_item(sym::Option, did) + { + return false; + } + + diag.span_suggestion( + expr.span.shrink_to_hi(), + "use `Option::is_some` to test if the `Option` has a value", + ".is_some()", + Applicability::MachineApplicable, + ); + + true + } + /// Suggest wrapping the block in square brackets instead of curly braces /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. pub(crate) fn suggest_block_to_brackets( @@ -1149,6 +1196,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + #[instrument(skip(self, err))] + pub(crate) fn suggest_floating_point_literal( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_floating_point() { + return false; + } + match expr.kind { + ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { + err.span_suggestion_verbose( + start.span.shrink_to_hi().with_hi(end.span.lo()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { + err.span_suggestion_verbose( + expr.span.with_lo(start.span.hi()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { + err.span_suggestion_verbose( + expr.span.until(end.span), + "remove the unnecessary `.` operator and add an integer part for a floating point literal", + "0.", + Applicability::MaybeIncorrect, + ); + true + } + _ => false, + } + } + fn is_loop(&self, id: hir::HirId) -> bool { let node = self.tcx.hir().get(id); matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) 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 122ad7009..fd8ea1ad7 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 @@ -9,9 +9,10 @@ use hir::{ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_index::vec::IndexVec; +use rustc_infer::infer::InferCtxt; use rustc_middle::{ hir::map::Map, - ty::{TyCtxt, TypeckResults}, + ty::{ParamEnv, TyCtxt, TypeVisitable, TypeckResults}, }; use std::mem::swap; @@ -21,20 +22,29 @@ use std::mem::swap; /// The resulting structure still needs to be iterated to a fixed point, which /// can be done with propagate_to_fixpoint in cfg_propagate. pub(super) fn build_control_flow_graph<'tcx>( - hir: Map<'tcx>, - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, typeck_results: &TypeckResults<'tcx>, + param_env: ParamEnv<'tcx>, consumed_borrowed_places: ConsumedAndBorrowedPlaces, body: &'tcx Body<'tcx>, num_exprs: usize, ) -> (DropRangesBuilder, FxHashSet) { - let mut drop_range_visitor = - DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs); + let mut drop_range_visitor = DropRangeVisitor::new( + infcx, + typeck_results, + param_env, + consumed_borrowed_places, + num_exprs, + ); intravisit::walk_body(&mut drop_range_visitor, body); drop_range_visitor.drop_ranges.process_deferred_edges(); - if let Some(filename) = &tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg { - super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx); + if let Some(filename) = &infcx.tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg { + super::cfg_visualize::write_graph_to_file( + &drop_range_visitor.drop_ranges, + filename, + infcx.tcx, + ); } (drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries) @@ -82,40 +92,44 @@ pub(super) fn build_control_flow_graph<'tcx>( /// ``` struct DropRangeVisitor<'a, 'tcx> { - hir: Map<'tcx>, + typeck_results: &'a TypeckResults<'tcx>, + infcx: &'a InferCtxt<'tcx>, + param_env: ParamEnv<'tcx>, places: ConsumedAndBorrowedPlaces, drop_ranges: DropRangesBuilder, expr_index: PostOrderId, - tcx: TyCtxt<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, label_stack: Vec<(Option, PostOrderId)>, } impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { fn new( - hir: Map<'tcx>, - tcx: TyCtxt<'tcx>, + infcx: &'a InferCtxt<'tcx>, typeck_results: &'a TypeckResults<'tcx>, + param_env: ParamEnv<'tcx>, places: ConsumedAndBorrowedPlaces, num_exprs: usize, ) -> Self { debug!("consumed_places: {:?}", places.consumed); let drop_ranges = DropRangesBuilder::new( places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()), - hir, + infcx.tcx.hir(), num_exprs, ); Self { - hir, + infcx, + typeck_results, + param_env, places, drop_ranges, expr_index: PostOrderId::from_u32(0), - typeck_results, - tcx, label_stack: vec![], } } + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + fn record_drop(&mut self, value: TrackedValue) { if self.places.borrowed.contains(&value) { debug!("not marking {:?} as dropped because it is borrowed at some point", value); @@ -137,7 +151,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { .map_or(vec![], |places| places.iter().cloned().collect()); for place in places { trace!(?place, "consuming place"); - for_each_consumable(self.hir, place, |value| self.record_drop(value)); + for_each_consumable(self.tcx().hir(), place, |value| self.record_drop(value)); } } @@ -214,10 +228,15 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { /// return. fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { let ty = self.typeck_results.expr_ty(expr); - let ty = self.tcx.erase_regions(ty); - let m = self.tcx.parent_module(expr.hir_id).to_def_id(); - let param_env = self.tcx.param_env(m.expect_local()); - if self.tcx.is_ty_uninhabited_from(m, ty, param_env) { + let ty = self.infcx.resolve_vars_if_possible(ty); + if ty.has_non_region_infer() { + self.tcx() + .sess + .delay_span_bug(expr.span, format!("could not resolve infer vars in `{ty}`")); + } + let ty = self.tcx().erase_regions(ty); + let m = self.tcx().parent_module(expr.hir_id).to_def_id(); + if !ty.is_inhabited_from(self.tcx(), m, self.param_env) { // This function will not return. We model this fact as an infinite loop. self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1); } @@ -238,7 +257,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { destination: hir::Destination, ) -> Result { destination.target_id.map(|target| { - let node = self.hir.get(target); + let node = self.tcx().hir().get(target); match node { hir::Node::Expr(_) => target, hir::Node::Block(b) => find_last_block_expression(b), 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 4f3bdfbe7..2abcadcc9 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 @@ -43,9 +43,9 @@ pub fn compute_drop_ranges<'a, 'tcx>( let typeck_results = &fcx.typeck_results.borrow(); let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0); let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph( - fcx.tcx.hir(), - fcx.tcx, + &fcx, typeck_results, + fcx.param_env, consumed_borrowed_places, body, num_exprs, diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index b7dd599cd..3b1518ff7 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -13,10 +13,13 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdSet; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; +use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; -use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::fold::FnMutDelegate; +use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::sym; use rustc_span::Span; +use smallvec::{smallvec, SmallVec}; mod drop_ranges; @@ -95,8 +98,8 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { expr, scope, ty, self.expr_count, yield_data.span ); - if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) + if let Some((unresolved_term, unresolved_type_span)) = + self.fcx.first_unresolved_const_or_ty_var(&ty) { // If unresolved type isn't a ty_var then unresolved_type_span is None let span = self @@ -105,21 +108,22 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { // If we encounter an int/float variable, then inference fallback didn't // finish due to some other error. Don't emit spurious additional errors. - if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = - unresolved_type.kind() + if let Some(unresolved_ty) = unresolved_term.ty() + && let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = unresolved_ty.kind() { self.fcx .tcx .sess - .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type)); + .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_term)); } else { let note = format!( "the type is part of the {} because of this {}", - self.kind, yield_data.source + self.kind.descr(), + yield_data.source ); self.fcx - .need_type_info_err_in_generator(self.kind, span, unresolved_type) + .need_type_info_err_in_generator(self.kind, span, unresolved_term) .span_note(yield_data.span, &*note) .emit(); } @@ -159,7 +163,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { expr.map(|e| e.span) ); if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) + self.fcx.first_unresolved_const_or_ty_var(&ty) { debug!( "remained unresolved_type = {:?}, unresolved_type_span: {:?}", @@ -211,31 +215,57 @@ pub fn resolve_interior<'a, 'tcx>( debug!("types in generator {:?}, span = {:?}", types, body.value.span); - let mut counter = 0; + // We want to deduplicate if the lifetimes are the same modulo some non-informative counter. + // So, we need to actually do two passes: first by type to anonymize (preserving information + // required for diagnostics), then a second pass over all captured types to reassign disjoint + // region indices. let mut captured_tys = FxHashSet::default(); let type_causes: Vec<_> = types .into_iter() .filter_map(|mut cause| { - // Erase regions and canonicalize late-bound regions to deduplicate as many types as we - // can. - let ty = fcx.normalize_associated_types_in(cause.span, cause.ty); - let erased = fcx.tcx.erase_regions(ty); - if captured_tys.insert(erased) { - // Replace all regions inside the generator interior with late bound regions. - // Note that each region slot in the types gets a new fresh late bound region, - // which means that none of the regions inside relate to any other, even if - // typeck had previously found constraints that would cause them to be related. - let folded = fcx.tcx.fold_regions(erased, |_, current_depth| { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(counter), - kind: ty::BrAnon(counter), - }; - let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); - counter += 1; - r - }); - - cause.ty = folded; + // Replace all regions inside the generator interior with late bound regions. + // Note that each region slot in the types gets a new fresh late bound region, + // which means that none of the regions inside relate to any other, even if + // typeck had previously found constraints that would cause them to be related. + + let mut counter = 0; + let mut mk_bound_region = |span| { + let kind = ty::BrAnon(counter, span); + let var = ty::BoundVar::from_u32(counter); + counter += 1; + ty::BoundRegion { var, kind } + }; + let ty = fcx.normalize(cause.span, cause.ty); + let ty = fcx.tcx.fold_regions(ty, |region, current_depth| { + let br = match region.kind() { + ty::ReVar(vid) => { + let origin = fcx.region_var_origin(vid); + match origin { + RegionVariableOrigin::EarlyBoundRegion(span, _) => { + mk_bound_region(Some(span)) + } + _ => mk_bound_region(None), + } + } + // FIXME: these should use `BrNamed` + ty::ReEarlyBound(region) => { + mk_bound_region(Some(fcx.tcx.def_span(region.def_id))) + } + ty::ReLateBound(_, ty::BoundRegion { kind, .. }) + | ty::ReFree(ty::FreeRegion { bound_region: kind, .. }) => match kind { + ty::BoundRegionKind::BrAnon(_, span) => mk_bound_region(span), + ty::BoundRegionKind::BrNamed(def_id, _) => { + mk_bound_region(Some(fcx.tcx.def_span(def_id))) + } + ty::BoundRegionKind::BrEnv => mk_bound_region(None), + }, + _ => mk_bound_region(None), + }; + let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); + r + }); + if captured_tys.insert(ty) { + cause.ty = ty; Some(cause) } else { None @@ -243,11 +273,38 @@ pub fn resolve_interior<'a, 'tcx>( }) .collect(); + let mut bound_vars: SmallVec<[BoundVariableKind; 4]> = smallvec![]; + let mut counter = 0; + // Optimization: If there is only one captured type, then we don't actually + // need to fold and reindex (since the first type doesn't change). + let type_causes = if captured_tys.len() > 0 { + // Optimization: Use `replace_escaping_bound_vars_uncached` instead of + // `fold_regions`, since we only have late bound regions, and it skips + // types without bound regions. + fcx.tcx.replace_escaping_bound_vars_uncached( + type_causes, + FnMutDelegate { + regions: &mut |br| { + let kind = match br.kind { + ty::BrAnon(_, span) => ty::BrAnon(counter, span), + _ => br.kind, + }; + let var = ty::BoundVar::from_usize(bound_vars.len()); + bound_vars.push(ty::BoundVariableKind::Region(kind)); + counter += 1; + fcx.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { var, kind })) + }, + types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"), + consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"), + }, + ) + } else { + type_causes + }; + // Extract type components to build the witness type. let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); - let bound_vars = fcx.tcx.mk_bound_variable_kinds( - (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))), - ); + let bound_vars = fcx.tcx.mk_bound_variable_kinds(bound_vars.iter()); let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); @@ -486,10 +543,10 @@ fn check_must_not_suspend_ty<'tcx>( data: SuspendCheckData<'_, 'tcx>, ) -> bool { if ty.is_unit() - // FIXME: should this check `is_ty_uninhabited_from`. This query is not available in this stage + // FIXME: should this check `Ty::is_inhabited_from`. This query is not available in this stage // of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in // `must_use` - // || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env) + // || !ty.is_inhabited_from(fcx.tcx, fcx.tcx.parent_module(hir_id).to_def_id(), fcx.param_env) { return false; } @@ -510,7 +567,7 @@ fn check_must_not_suspend_ty<'tcx>( let mut has_emitted = false; for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) { // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::PredicateKind::Trait(ref poly_trait_predicate) = + if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) = predicate.kind().skip_binder() { let def_id = poly_trait_predicate.trait_ref.def_id; diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 0fb7651b3..b33e7b8d6 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -1,21 +1,16 @@ use super::callee::DeferredCallResolution; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; use rustc_infer::infer; use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefIdMap; use rustc_span::{self, Span}; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _, -}; +use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _}; use std::cell::RefCell; use std::ops::Deref; @@ -38,19 +33,19 @@ pub struct Inherited<'tcx> { pub(super) fulfillment_cx: RefCell>>, - // Some additional `Sized` obligations badly affect type inference. - // These obligations are added in a later stage of typeck. - // Removing these may also cause additional complications, see #101066. + /// Some additional `Sized` obligations badly affect type inference. + /// These obligations are added in a later stage of typeck. + /// Removing these may also cause additional complications, see #101066. pub(super) deferred_sized_obligations: RefCell, Span, traits::ObligationCauseCode<'tcx>)>>, - // When we process a call like `c()` where `c` is a closure type, - // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or - // `FnOnce` closure. In that case, we defer full resolution of the - // call until upvar inference can kick in and make the - // decision. We keep these deferred resolutions grouped by the - // def-id of the closure, so that once we decide, we can easily go - // back and process them. + /// When we process a call like `c()` where `c` is a closure type, + /// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or + /// `FnOnce` closure. In that case, we defer full resolution of the + /// call until upvar inference can kick in and make the + /// decision. We keep these deferred resolutions grouped by the + /// def-id of the closure, so that once we decide, we can easily go + /// back and process them. pub(super) deferred_call_resolutions: RefCell>>>, pub(super) deferred_cast_checks: RefCell>>, @@ -94,29 +89,7 @@ impl<'tcx> Inherited<'tcx> { infcx: tcx .infer_ctxt() .ignoring_regions() - .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)) - .with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| { - if fn_sig.has_escaping_bound_vars() { - return fn_sig; - } - infcx.probe(|_| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); - let normalized_fn_sig = ocx.normalize( - ObligationCause::dummy(), - // FIXME(compiler-errors): This is probably not the right param-env... - infcx.tcx.param_env(def_id), - fn_sig, - ); - if ocx.select_all_or_error().is_empty() { - let normalized_fn_sig = - infcx.resolve_vars_if_possible(normalized_fn_sig); - if !normalized_fn_sig.needs_infer() { - return normalized_fn_sig; - } - } - fn_sig - }) - })), + .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)), def_id, typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)), } @@ -179,35 +152,4 @@ impl<'tcx> Inherited<'tcx> { self.register_predicates(infer_ok.obligations); infer_ok.value } - - pub(super) fn normalize_associated_types_in( - &self, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - self.normalize_associated_types_in_with_cause( - ObligationCause::misc(span, body_id), - param_env, - value, - ) - } - - pub(super) fn normalize_associated_types_in_with_cause( - &self, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - let ok = self.partially_normalize_associated_types_in(cause, param_env, value); - debug!(?ok); - self.register_infer_ok_obligations(ok) - } } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 9812d96fc..c2dc14024 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -3,7 +3,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use rustc_target::abi::{Pointer, VariantIdx}; use super::FnCtxt; @@ -46,7 +46,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let from = normalize(from); let to = normalize(to); trace!(?from, ?to); - + if from.has_non_region_infer() || to.has_non_region_infer() { + tcx.sess.delay_span_bug(span, "argument to transmute has inference variables"); + return; + } // Transmutes that are only changing lifetimes are always ok. if from == to { return; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 959c54866..5b2352cda 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,9 +53,9 @@ use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{struct_span_err, MultiSpan}; +use rustc_errors::{struct_span_err, DiagnosticId, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_hir::{HirIdMap, Node}; use rustc_hir_analysis::astconv::AstConv; @@ -89,38 +89,6 @@ pub struct LocalTy<'tcx> { revealed_ty: Ty<'tcx>, } -#[derive(Copy, Clone)] -pub struct UnsafetyState { - pub def: hir::HirId, - pub unsafety: hir::Unsafety, - from_fn: bool, -} - -impl UnsafetyState { - pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState { - UnsafetyState { def, unsafety, from_fn: true } - } - - pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState { - use hir::BlockCheckMode; - match self.unsafety { - // If this unsafe, then if the outer function was already marked as - // unsafe we shouldn't attribute the unsafe'ness to the block. This - // way the block can be warned about instead of ignoring this - // extraneous block (functions are never warned about). - hir::Unsafety::Unsafe if self.from_fn => self, - - unsafety => { - let (unsafety, def) = match blk.rules { - BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id), - BlockCheckMode::DefaultBlock => (unsafety, self.def), - }; - UnsafetyState { def, unsafety, from_fn: false } - } - } - } -} - /// If this `DefId` is a "primary tables entry", returns /// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. /// @@ -209,6 +177,7 @@ fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::T typeck_with_fallback(tcx, def_id, fallback) } +#[instrument(level = "debug", skip(tcx, fallback), ret)] fn typeck_with_fallback<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, @@ -232,9 +201,10 @@ fn typeck_with_fallback<'tcx>( let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { let param_env = tcx.param_env(def_id); - let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { + let mut fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); + + if let Some(hir::FnSig { header, decl, .. }) = fn_sig { let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); >::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id) @@ -244,15 +214,10 @@ fn typeck_with_fallback<'tcx>( // Compute the function signature from point of view of inside the fn. let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = inh.normalize_associated_types_in( - body.value.span, - body_id.hir_id, - param_env, - fn_sig, - ); - check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 + let fn_sig = fcx.normalize(body.value.span, fn_sig); + + check_fn(&mut fcx, fn_sig, decl, def_id, body, None); } else { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); let expected_type = body_ty .and_then(|ty| match ty.kind { hir::TyKind::Infer => Some(>::ast_ty_to_ty(&fcx, ty)), @@ -303,7 +268,7 @@ fn typeck_with_fallback<'tcx>( _ => fallback(), }); - let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); + let expected_type = fcx.normalize(body.value.span, expected_type); fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); // Gather locals in statics (because of block expressions). @@ -312,16 +277,14 @@ fn typeck_with_fallback<'tcx>( fcx.check_expr_coercable_to_type(&body.value, expected_type, None); fcx.write_ty(id, expected_type); - - fcx }; - let fallback_has_occurred = fcx.type_inference_fallback(); + fcx.type_inference_fallback(); // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. fcx.check_casts(); - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); + fcx.select_obligations_where_possible(|_| {}); // Closure and generator analysis may run after fallback // because they don't constrain other type variables. @@ -343,7 +306,7 @@ fn typeck_with_fallback<'tcx>( fcx.select_all_obligations_or_error(); - if !fcx.infcx.is_tainted_by_errors() { + if let None = fcx.infcx.tainted_by_errors() { fcx.check_transmutes(); } @@ -427,16 +390,33 @@ impl<'tcx> EnclosingBreakables<'tcx> { } } -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) { - struct_span_err!( - tcx.sess, +fn report_unexpected_variant_res( + tcx: TyCtxt<'_>, + res: Res, + qpath: &hir::QPath<'_>, + span: Span, + err_code: &str, + expected: &str, +) -> ErrorGuaranteed { + let res_descr = match res { + Res::Def(DefKind::Variant, _) => "struct variant", + _ => res.descr(), + }; + let path_str = rustc_hir_pretty::qpath_to_string(qpath); + let mut err = tcx.sess.struct_span_err_with_code( span, - E0533, - "expected unit struct, unit variant or constant, found {} `{}`", - res.descr(), - rustc_hir_pretty::qpath_to_string(qpath), - ) - .emit(); + format!("expected {expected}, found {res_descr} `{path_str}`"), + DiagnosticId::Error(err_code.into()), + ); + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == "E0164" => { + let patterns_url = "https://doc.rust-lang.org/book/ch18-00-patterns.html"; + err.span_label(span, "`fn` calls are not allowed in patterns"); + err.help(format!("for more information, visit {patterns_url}")) + } + _ => err.span_label(span, format!("not a {expected}")), + } + .emit() } /// Controls whether the arguments are tupled. This is used for the call @@ -458,7 +438,7 @@ fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<' /// # fn f(x: (isize, isize)) {} /// f((1, 2)); /// ``` -#[derive(Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] enum TupleArgumentsFlag { DontTupleArguments, TupleArguments, diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 362f1c343..0b5dc946c 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -133,7 +133,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } fn is_tainted_by_errors(&self) -> bool { - self.infcx.is_tainted_by_errors() + self.infcx.tainted_by_errors().is_some() } fn resolve_type_vars_or_error( diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index be4ea9986..03d0e7926 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -106,7 +106,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // traits, no trait system method can be called before this point because they // could alter our Self-type, except for normalizing the receiver from the // signature (which is also done during probing). - let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]); + let method_sig_rcvr = self.normalize(self.span, method_sig.inputs()[0]); debug!( "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}", self_ty, method_sig_rcvr, method_sig, method_predicates @@ -114,7 +114,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs); let (method_sig, method_predicates) = - self.normalize_associated_types_in(self.span, (method_sig, method_predicates)); + self.normalize(self.span, (method_sig, method_predicates)); let method_sig = ty::Binder::dummy(method_sig); // Make sure nobody calls `drop()` explicitly. @@ -151,8 +151,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Ty<'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. - let mut autoderef = - self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); + let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty); let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { return self.tcx.ty_error_with_message( rustc_span::DUMMY_SP, @@ -171,14 +170,11 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let base_ty = target; target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Method call receivers are the primary use case - // for two-phase borrows. - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; + + // Method call receivers are the primary use case + // for two-phase borrows. + let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::Yes); + adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target, @@ -203,7 +199,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { target = match target.kind() { &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { - assert_eq!(mutbl, hir::Mutability::Mut); + assert!(mutbl.is_mut()); self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) } other => panic!("Cannot adjust receiver type {:?} to const ptr", other), @@ -532,7 +528,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) // We don't care about regions here. .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) + if trait_pred.def_id() == sized_def_id => + { let span = iter::zip(&predicates.predicates, &predicates.spans) .find_map( |(p, span)| { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index a1278edef..a2ca5c3b7 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -10,6 +10,7 @@ mod suggest; pub use self::suggest::SelfSource; pub use self::MethodError::*; +use crate::errors::OpMethodGenericParams; use crate::{Expectation, FnCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -19,11 +20,11 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitable}; use rustc_span::symbol::Ident; use rustc_span::Span; -use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{self, NormalizeExt}; use self::probe::{IsSuggestion, ProbeScope}; @@ -55,8 +56,7 @@ pub enum MethodError<'tcx> { // not-in-scope traits which may work. PrivateMatch(DefKind, DefId, Vec), - // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have - // forgotten to import a trait. + // Found a `Self: Sized` bound where `Self` is a trait object. IllegalSizedBound(Vec, bool, Span), // Found a match, but the return type is wrong @@ -93,17 +93,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr_id: hir::HirId, allow_private: bool, ) -> bool { - let mode = probe::Mode::MethodCall; match self.probe_for_name( - method_name.span, - mode, + probe::Mode::MethodCall, method_name, IsSuggestion(false), self_ty, call_expr_id, ProbeScope::TraitsInScope, ) { - Ok(..) => true, + Ok(pick) => { + pick.maybe_emit_unstable_name_collision_hint( + self.tcx, + method_name.span, + call_expr_id, + ); + true + } Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, @@ -125,10 +130,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let params = self .probe_for_name( - method_name.span, probe::Mode::MethodCall, method_name, - IsSuggestion(false), + IsSuggestion(true), self_ty, call_expr.hir_id, ProbeScope::TraitsInScope, @@ -175,7 +179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args: &'tcx [hir::Expr<'tcx>], ) -> Result, MethodError<'tcx>> { let pick = - self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; + self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); @@ -200,13 +204,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }); // We probe again to see if there might be a borrow mutability discrepancy. match self.lookup_probe( - span, segment.ident, trait_type, call_expr, ProbeScope::TraitsInScope, ) { - Ok(ref new_pick) if *new_pick != pick => { + Ok(ref new_pick) if pick.differs_from(new_pick) => { needs_mut = true; } _ => {} @@ -214,29 +217,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // We probe again, taking all traits into account (not only those in scope). - let mut candidates = match self.lookup_probe( - span, - segment.ident, - self_ty, - call_expr, - ProbeScope::AllTraits, - ) { - // If we find a different result the caller probably forgot to import a trait. - Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)], - Err(Ambiguity(ref sources)) => sources - .iter() - .filter_map(|source| { - match *source { - // Note: this cannot come from an inherent impl, - // because the first probing succeeded. - CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), - CandidateSource::Trait(_) => None, - } - }) - .collect(), - _ => Vec::new(), - }; - candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id)); + let candidates = + match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) { + // If we find a different result the caller probably forgot to import a trait. + Ok(ref new_pick) if pick.differs_from(new_pick) => { + vec![new_pick.item.container_id(self.tcx)] + } + Err(Ambiguity(ref sources)) => sources + .iter() + .filter_map(|source| { + match *source { + // Note: this cannot come from an inherent impl, + // because the first probing succeeded. + CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), + CandidateSource::Trait(_) => None, + } + }) + .collect(), + _ => Vec::new(), + }; return Err(IllegalSizedBound(candidates, needs_mut, span)); } @@ -247,23 +246,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, call_expr))] pub fn lookup_probe( &self, - span: Span, method_name: Ident, self_ty: Ty<'tcx>, call_expr: &'tcx hir::Expr<'tcx>, scope: ProbeScope, ) -> probe::PickResult<'tcx> { - let mode = probe::Mode::MethodCall; - let self_ty = self.resolve_vars_if_possible(self_ty); - self.probe_for_name( - span, - mode, + let pick = self.probe_for_name( + probe::Mode::MethodCall, method_name, IsSuggestion(false), self_ty, call_expr.hir_id, scope, - ) + )?; + pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id); + Ok(pick) } pub(super) fn obligation_for_method( @@ -295,10 +292,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let poly_trait_ref = ty::Binder::dummy(trait_ref); ( traits::Obligation::misc( + self.tcx, span, self.body_id, self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), + poly_trait_ref.without_const(), ), substs, ) @@ -337,6 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ( traits::Obligation::new( + self.tcx, traits::ObligationCause::new( span, self.body_id, @@ -348,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ), self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), + poly_trait_ref, ), substs, ) @@ -444,7 +443,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let def_id = method_item.def_id; let generics = tcx.generics_of(def_id); - assert_eq!(generics.params.len(), 0); + + if generics.params.len() != 0 { + tcx.sess.emit_fatal(OpMethodGenericParams { + span: tcx.def_span(method_item.def_id), + method_name: m_name.to_string(), + }); + } debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); let mut obligations = vec![]; @@ -459,11 +464,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = fn_sig.subst(self.tcx, substs); let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig); - let InferOk { value, obligations: o } = if is_op { - self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr) + let cause = if is_op { + ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_ty: None, + }, + ) } else { - self.normalize_associated_types_in_as_infer_ok(span, fn_sig) + traits::ObligationCause::misc(span, self.body_id) }; + + let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(fn_sig); let fn_sig = { obligations.extend(o); value @@ -479,11 +495,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // any late-bound regions appearing in its bounds. let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - let InferOk { value, obligations: o } = if is_op { - self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr) - } else { - self.normalize_associated_types_in_as_infer_ok(span, bounds) - }; + let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(bounds); let bounds = { obligations.extend(o); value @@ -491,20 +503,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(!bounds.has_escaping_bound_vars()); - let cause = if is_op { - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty: None, - }, - ) - } else { - traits::ObligationCause::misc(span, self.body_id) - }; let predicates_cause = cause.clone(); obligations.extend(traits::predicates_for_generics( move |_, _| predicates_cause.clone(), @@ -519,9 +517,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method_ty, obligation ); obligations.push(traits::Obligation::new( + tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())), )); let callee = MethodCallee { def_id, substs, sig: fn_sig }; @@ -559,6 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; // Check if we have an enum variant. + let mut struct_variant = None; if let ty::Adt(adt_def, _) = self_ty.kind() { if adt_def.is_enum() { let variant_def = adt_def @@ -566,29 +566,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did())); if let Some(variant_def) = variant_def { - // Braced variants generate unusable names in value namespace (reserved for - // possible future use), so variants resolved as associated items may refer to - // them as well. It's ok to use the variant's id as a ctor id since an - // error will be reported on any use of such resolution anyway. - let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id); - tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span)); - return Ok(( - DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind), - ctor_def_id, - )); + if let Some((ctor_kind, ctor_def_id)) = variant_def.ctor { + tcx.check_stability( + ctor_def_id, + Some(expr_id), + span, + Some(method_name.span), + ); + return Ok((DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id)); + } else { + struct_variant = Some((DefKind::Variant, variant_def.def_id)); + } } } } let pick = self.probe_for_name( - span, probe::Mode::Path, method_name, IsSuggestion(false), self_ty, expr_id, ProbeScope::TraitsInScope, - )?; + ); + let pick = match (pick, struct_variant) { + // Fall back to a resolution that will produce an error later. + (Err(_), Some(res)) => return Ok(res), + (pick, _) => pick?, + }; + + pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id); self.lint_fully_qualified_call_from_2018( span, diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 3c98a2aa3..dea14dd93 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -5,10 +5,9 @@ use crate::{ use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; -use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_middle::ty::subst::InternalSubsts; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::{Adt, Array, Ref, Ty}; use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; use rustc_span::symbol::kw::{Empty, Underscore}; @@ -88,14 +87,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Mut, - .. - }) => "&mut ", - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Not, - .. - }) => "&", + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => { + mutbl.ref_prefix_str() + } Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", }; if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) @@ -227,14 +221,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we know it does not, we don't need to warn. if method_name.name == sym::from_iter { if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { + let any_type = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + }); if !self .infcx - .type_implements_trait( - trait_def_id, - self_ty, - InternalSubsts::empty(), - self.param_env, - ) + .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env) .may_apply() { return; @@ -387,8 +380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ", - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&", + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(), Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", }; diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 28aa2302f..ae299cc9d 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,7 +9,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -17,8 +16,10 @@ use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; +use rustc_middle::ty::AssocItem; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; use rustc_span::def_id::DefId; @@ -35,6 +36,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefStepsResult, }; use rustc_trait_selection::traits::query::CanonicalTyGoal; +use rustc_trait_selection::traits::NormalizeExt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::cmp::max; use std::iter; @@ -83,8 +85,6 @@ struct ProbeContext<'a, 'tcx> { unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>, Option>)>, - is_suggestion: IsSuggestion, - scope_expr_id: hir::HirId, } @@ -193,7 +193,7 @@ impl AutorefOrPtrAdjustment { } } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone)] pub struct Pick<'tcx> { pub item: ty::AssocItem, pub kind: PickKind<'tcx>, @@ -209,6 +209,9 @@ pub struct Pick<'tcx> { /// `*mut T`, convert it to `*const T`. pub autoref_or_ptr_adjustment: Option, pub self_ty: Ty<'tcx>, + + /// Unstable candidates alongside the stable ones. + unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -298,7 +301,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub fn probe_for_name( &self, - span: Span, mode: Mode, item_name: Ident, is_suggestion: IsSuggestion, @@ -307,7 +309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { scope: ProbeScope, ) -> PickResult<'tcx> { self.probe_op( - span, + item_name.span, mode, Some(item_name), None, @@ -340,10 +342,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut orig_values, ); - let steps = if mode == Mode::MethodCall { - self.tcx.method_autoderef_steps(param_env_and_self_ty) - } else { - self.probe(|_| { + let steps = match mode { + Mode::MethodCall => self.tcx.method_autoderef_steps(param_env_and_self_ty), + Mode::Path => self.probe(|_| { // Mode::Path - the deref steps is "trivial". This turns // our CanonicalQuery into a "trivial" QueryResponse. This // is a bit inefficient, but I don't think that writing @@ -372,7 +373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_bad_ty: None, reached_recursion_limit: false, } - }) + }), }; // If our autoderef loop had reached the recursion limit, @@ -446,7 +447,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type, orig_values, steps.steps, - is_suggestion, scope_expr_id, ); @@ -475,10 +475,9 @@ fn method_autoderef_steps<'tcx>( let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = - Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) - .include_raw_pointers() - .silence_errors(); + let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty) + .include_raw_pointers() + .silence_errors(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef .by_ref() @@ -542,7 +541,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return_type: Option>, orig_steps_var_values: OriginalQueryValues<'tcx>, steps: &'tcx [CandidateStep<'tcx>], - is_suggestion: IsSuggestion, scope_expr_id: hir::HirId, ) -> ProbeContext<'a, 'tcx> { ProbeContext { @@ -560,7 +558,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { allow_similar_names: false, private_candidate: None, unsatisfied_predicates: Vec::new(), - is_suggestion, scope_expr_id, } } @@ -718,9 +715,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized, // see issue #89650 let cause = traits::ObligationCause::misc(self.span, self.body_id); - let selcx = &mut traits::SelectionContext::new(self.fcx); - let traits::Normalized { value: xform_self_ty, obligations } = - traits::normalize(selcx, self.param_env, cause, xform_self_ty); + let InferOk { value: xform_self_ty, obligations } = + self.fcx.at(&cause, self.param_env).normalize(xform_self_ty); + debug!( "assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}", xform_self_ty, xform_ret_ty @@ -787,7 +784,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_predicate) => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { match *trait_predicate.trait_ref.self_ty().kind() { ty::Param(p) if p == param_ty => { Some(bound_predicate.rebind(trait_predicate.trait_ref)) @@ -797,14 +794,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }); @@ -882,7 +880,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - pub fn matches_return_type( + fn matches_return_type( &self, method: &ty::AssocItem, self_ty: Option>, @@ -1019,7 +1017,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let out_of_scope_traits = match self.pick_core() { Some(Ok(p)) => vec![p.item.container_id(self.tcx)], - //Some(Ok(p)) => p.iter().map(|p| p.item.container().id()).collect(), Some(Err(MethodError::Ambiguity(v))) => v .into_iter() .map(|source| match source { @@ -1054,26 +1051,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn pick_core(&mut self) -> Option> { - let mut unstable_candidates = Vec::new(); - let pick = self.pick_all_method(Some(&mut unstable_candidates)); + let pick = self.pick_all_method(Some(&mut vec![])); // In this case unstable picking is done by `pick_method`. if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { return pick; } - match pick { - // Emit a lint if there are unstable candidates alongside the stable ones. - // - // We suppress warning if we're picking the method only because it is a - // suggestion. - Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => { - self.emit_unstable_name_collision_hint(p, &unstable_candidates); - pick - } - Some(_) => pick, - None => self.pick_all_method(None), + if pick.is_none() { + return self.pick_all_method(None); } + pick } fn pick_all_method( @@ -1218,7 +1206,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty)); let mut possibly_unsatisfied_predicates = Vec::new(); - let mut unstable_candidates = Vec::new(); for (kind, candidates) in &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] @@ -1228,26 +1215,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self_ty, candidates.iter(), &mut possibly_unsatisfied_predicates, - Some(&mut unstable_candidates), + Some(&mut vec![]), ); - if let Some(pick) = res { - if !self.is_suggestion.0 && !unstable_candidates.is_empty() { - if let Ok(p) = &pick { - // Emit a lint if there are unstable candidates alongside the stable ones. - // - // We suppress warning if we're picking the method only because it is a - // suggestion. - self.emit_unstable_name_collision_hint(p, &unstable_candidates); - } - } - return Some(pick); + if res.is_some() { + return res; } } debug!("searching unstable candidates"); let res = self.consider_candidates( self_ty, - unstable_candidates.iter().map(|(c, _)| c), + self.inherent_candidates.iter().chain(&self.extension_candidates), &mut possibly_unsatisfied_predicates, None, ); @@ -1302,7 +1280,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Option>, Option>, )>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option> where ProbesIter: Iterator> + Clone, @@ -1326,7 +1304,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - if let Some(uc) = unstable_candidates { + if let Some(uc) = &mut unstable_candidates { applicable_candidates.retain(|&(p, _)| { if let stability::EvalResult::Deny { feature, .. } = self.tcx.eval_stability(p.item.def_id, None, self.span, None) @@ -1345,30 +1323,63 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { applicable_candidates.pop().map(|(probe, status)| { if status == ProbeResult::Match { - Ok(probe.to_unadjusted_pick(self_ty)) + Ok(probe + .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default())) } else { Err(MethodError::BadReturnType) } }) } +} + +impl<'tcx> Pick<'tcx> { + /// In case there were unstable name collisions, emit them as a lint. + /// Checks whether two picks do not refer to the same trait item for the same `Self` type. + /// Only useful for comparisons of picks in order to improve diagnostics. + /// Do not use for type checking. + pub fn differs_from(&self, other: &Self) -> bool { + let Self { + item: + AssocItem { + def_id, + name: _, + kind: _, + container: _, + trait_item_def_id: _, + fn_has_self_parameter: _, + }, + kind: _, + import_ids: _, + autoderefs: _, + autoref_or_ptr_adjustment: _, + self_ty, + unstable_candidates: _, + } = *self; + self_ty != other.self_ty || def_id != other.item.def_id + } - fn emit_unstable_name_collision_hint( + /// In case there were unstable name collisions, emit them as a lint. + pub fn maybe_emit_unstable_name_collision_hint( &self, - stable_pick: &Pick<'_>, - unstable_candidates: &[(Candidate<'tcx>, Symbol)], + tcx: TyCtxt<'tcx>, + span: Span, + scope_expr_id: hir::HirId, ) { - let def_kind = stable_pick.item.kind.as_def_kind(); - self.tcx.struct_span_lint_hir( + if self.unstable_candidates.is_empty() { + return; + } + let def_kind = self.item.kind.as_def_kind(); + tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, - self.scope_expr_id, - self.span, + scope_expr_id, + span, format!( "{} {} with this name may be added to the standard library in the future", def_kind.article(), - def_kind.descr(stable_pick.item.def_id), + def_kind.descr(self.item.def_id), ), |lint| { - match (stable_pick.item.kind, stable_pick.item.container) { + match (self.item.kind, self.item.container) { (ty::AssocKind::Fn, _) => { // FIXME: This should be a `span_suggestion` instead of `help` // However `self.span` only @@ -1377,31 +1388,31 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { lint.help(&format!( "call with fully qualified syntax `{}(...)` to keep using the current \ method", - self.tcx.def_path_str(stable_pick.item.def_id), + tcx.def_path_str(self.item.def_id), )); } (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { - let def_id = stable_pick.item.container_id(self.tcx); + let def_id = self.item.container_id(tcx); lint.span_suggestion( - self.span, + span, "use the fully qualified path to the associated const", format!( "<{} as {}>::{}", - stable_pick.self_ty, - self.tcx.def_path_str(def_id), - stable_pick.item.name + self.self_ty, + tcx.def_path_str(def_id), + self.item.name ), Applicability::MachineApplicable, ); } _ => {} } - if self.tcx.sess.is_nightly_build() { - for (candidate, feature) in unstable_candidates { + if tcx.sess.is_nightly_build() { + for (candidate, feature) in &self.unstable_candidates { lint.help(&format!( "add `#![feature({})]` to the crate attributes to enable `{}`", feature, - self.tcx.def_path_str(candidate.item.def_id), + tcx.def_path_str(candidate.item.def_id), )); } } @@ -1410,14 +1421,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }, ); } +} +impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn select_trait_candidate( &self, trait_ref: ty::TraitRef<'tcx>, ) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> { let cause = traits::ObligationCause::misc(self.span, self.body_id); - let predicate = ty::Binder::dummy(trait_ref).to_poly_trait_predicate(); - let obligation = traits::Obligation::new(cause, self.param_env, predicate); + let predicate = ty::Binder::dummy(trait_ref); + let obligation = traits::Obligation::new(self.tcx, cause, self.param_env, predicate); traits::SelectionContext::new(self).select(&obligation) } @@ -1476,7 +1489,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let mut xform_ret_ty = probe.xform_ret_ty; debug!(?xform_ret_ty); - let selcx = &mut traits::SelectionContext::new(self); let cause = traits::ObligationCause::misc(self.span, self.body_id); let mut parent_pred = None; @@ -1490,10 +1502,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` // for why this is necessary - let traits::Normalized { + let InferOk { value: normalized_xform_ret_ty, obligations: normalization_obligations, - } = traits::normalize(selcx, self.param_env, cause.clone(), probe.xform_ret_ty); + } = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty); xform_ret_ty = normalized_xform_ret_ty; debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); @@ -1501,8 +1513,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let impl_def_id = probe.item.container_id(self.tcx); let impl_bounds = self.tcx.predicates_of(impl_def_id); let impl_bounds = impl_bounds.instantiate(self.tcx, substs); - let traits::Normalized { value: impl_bounds, obligations: norm_obligations } = - traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds); + + let InferOk { value: impl_bounds, obligations: norm_obligations } = + self.fcx.at(&cause, self.param_env).normalize(impl_bounds); // Convert the bounds into obligations. let impl_obligations = traits::predicates_for_generics( @@ -1548,7 +1561,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let predicate = ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx); parent_pred = Some(predicate); - let obligation = traits::Obligation::new(cause, self.param_env, predicate); + let obligation = + traits::Obligation::new(self.tcx, cause, self.param_env, predicate); if !self.predicate_may_hold(&obligation) { result = ProbeResult::NoMatch; if self.probe(|_| { @@ -1669,6 +1683,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { autoderefs: 0, autoref_or_ptr_adjustment: None, self_ty, + unstable_candidates: vec![], }) } @@ -1688,7 +1703,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.return_type, self.orig_steps_var_values.clone(), steps, - IsSuggestion(true), self.scope_expr_id, ); pcx.allow_similar_names = true; @@ -1861,6 +1875,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.tcx.erase_late_bound_regions(value) } + /// Determine if the given associated item type is relevant in the current context. + fn is_relevant_kind_for_mode(&self, kind: ty::AssocKind) -> bool { + match (self.mode, kind) { + (Mode::MethodCall, ty::AssocKind::Fn) => true, + (Mode::Path, ty::AssocKind::Const | ty::AssocKind::Fn) => true, + _ => false, + } + } + /// Finds the method with the appropriate name (or return type, as the case may be). If /// `allow_similar_names` is set, find methods with close-matching names. // The length of the returned iterator is nearly always 0 or 1 and this @@ -1873,7 +1896,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .associated_items(def_id) .in_definition_order() .filter(|x| { - if x.kind.namespace() != Namespace::ValueNS { + if !self.is_relevant_kind_for_mode(x.kind) { return false; } match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist) @@ -1887,16 +1910,26 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } else { self.fcx .associated_value(def_id, name) + .filter(|x| self.is_relevant_kind_for_mode(x.kind)) .map_or_else(SmallVec::new, |x| SmallVec::from_buf([x])) } } else { - self.tcx.associated_items(def_id).in_definition_order().copied().collect() + self.tcx + .associated_items(def_id) + .in_definition_order() + .filter(|x| self.is_relevant_kind_for_mode(x.kind)) + .copied() + .collect() } } } impl<'tcx> Candidate<'tcx> { - fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> { + fn to_unadjusted_pick( + &self, + self_ty: Ty<'tcx>, + unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>, + ) -> Pick<'tcx> { Pick { item: self.item, kind: match self.kind { @@ -1921,6 +1954,7 @@ impl<'tcx> Candidate<'tcx> { autoderefs: 0, autoref_or_ptr_adjustment: None, self_ty, + unstable_candidates, } } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6c21ed902..db93cfab2 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -5,6 +5,7 @@ use crate::errors; use crate::FnCtxt; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::StashKey; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, @@ -13,27 +14,35 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_hir::PatKind::Binding; +use rustc_hir::PathSegment; use rustc_hir::{ExprKind, Node, QPath}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{ + type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, + RegionVariableOrigin, +}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; 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; -use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; +use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ - FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote, + FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, }; -use std::cmp::Ordering; -use std::iter; - use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; +use rustc_hir::intravisit::Visitor; +use std::cmp::Ordering; +use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -62,22 +71,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.autoderef(span, ty).any(|(ty, _)| { info!("check deref {:?} impl FnOnce", ty); self.probe(|_| { - let fn_once_substs = tcx.mk_substs_trait( - ty, - &[self - .next_ty_var(TypeVariableOrigin { + let trait_ref = tcx.mk_trait_ref( + fn_once, + [ + ty, + self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span, - }) - .into()], + }), + ], ); - let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs); let poly_trait_ref = ty::Binder::dummy(trait_ref); let obligation = Obligation::misc( + tcx, span, self.body_id, self.param_env, - poly_trait_ref.without_const().to_predicate(tcx), + poly_trait_ref.without_const(), ); self.predicate_may_hold(&obligation) }) @@ -107,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let report_candidates = |span: Span, err: &mut Diagnostic, sources: &mut Vec, - sugg_span: Span| { + sugg_span: Option| { sources.sort(); sources.dedup(); // Dynamic limit to avoid hiding just one candidate, which is silly. @@ -168,7 +178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.note(¬e_str); } - if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { + if let Some(sugg_span) = sugg_span + && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { let path = self.tcx.def_path_str(trait_ref.def_id); let ty = match item.kind { @@ -217,20 +228,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_note(item_span, msg); None }; - let path = self.tcx.def_path_str(trait_did); - print_disambiguation_help( - item_name, - args, - err, - path, - rcvr_ty, - item.kind, - item.def_id, - sugg_span, - idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ); + if let Some(sugg_span) = sugg_span { + let path = self.tcx.def_path_str(trait_did); + print_disambiguation_help( + item_name, + args, + err, + path, + rcvr_ty, + item.kind, + item.def_id, + sugg_span, + idx, + self.tcx.sess.source_map(), + item.fn_has_self_parameter, + ); + } } } } @@ -248,7 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match error { MethodError::NoMatch(NoMatchData { - static_candidates: mut static_sources, + mut static_candidates, unsatisfied_predicates, out_of_scope_traits, lev_candidate, @@ -256,15 +269,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) => { let tcx = self.tcx; - let actual = self.resolve_vars_if_possible(rcvr_ty); - let ty_str = self.ty_to_string(actual); + let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); + let ty_str = self.ty_to_string(rcvr_ty); let is_method = mode == Mode::MethodCall; let item_kind = if is_method { "method" - } else if actual.is_enum() { + } else if rcvr_ty.is_enum() { "variant or associated item" } else { - match (item_name.as_str().chars().next(), actual.is_fresh_ty()) { + match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { (Some(name), false) if name.is_lowercase() => "function or associated item", (Some(_), false) => "associated item", (Some(_), true) | (None, false) => "variant or associated item", @@ -273,24 +286,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self.suggest_wrapping_range_with_parens( - tcx, actual, source, span, item_name, &ty_str, + tcx, rcvr_ty, source, span, item_name, &ty_str, ) || self.suggest_constraining_numerical_ty( - tcx, actual, source, span, item_kind, item_name, &ty_str, + tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str, ) { return None; } - span = item_name.span; // Don't show generic arguments when the method can't be found in any implementation (#81576). let mut ty_str_reported = ty_str.clone(); - if let ty::Adt(_, generics) = actual.kind() { + if let ty::Adt(_, generics) = rcvr_ty.kind() { if generics.len() > 0 { - let mut autoderef = self.autoderef(span, actual); + let mut autoderef = self.autoderef(span, rcvr_ty); let candidate_found = autoderef.any(|(ty, _)| { - if let ty::Adt(adt_deref, _) = ty.kind() { + if let ty::Adt(adt_def, _) = ty.kind() { self.tcx - .inherent_impls(adt_deref.did()) + .inherent_impls(adt_def.did()) .iter() .filter_map(|def_id| self.associated_value(*def_id, item_name)) .count() @@ -315,16 +327,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "no {} named `{}` found for {} `{}` in the current scope", item_kind, item_name, - actual.prefix_string(self.tcx), + rcvr_ty.prefix_string(self.tcx), ty_str_reported, ); - if actual.references_error() { + if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); } if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { self.suggest_await_before_method( - &mut err, item_name, actual, cal, span, + &mut err, item_name, rcvr_ty, cal, span, ); } if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { @@ -335,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - if let ty::RawPtr(_) = &actual.kind() { + if let ty::RawPtr(_) = &rcvr_ty.kind() { err.note( "try using `<*const T>::as_ref()` to get a reference to the \ type behind the pointer: https://doc.rust-lang.org/std/\ @@ -347,22 +359,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - let ty_span = match actual.kind() { - ty::Param(param_type) => { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); - let type_param = generics.type_param(param_type, self.tcx); - Some(self.tcx.def_span(type_param.def_id)) - } + let ty_span = match rcvr_ty.kind() { + ty::Param(param_type) => Some( + param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()), + ), ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), _ => None, }; - if let Some(span) = ty_span { err.span_label( span, format!( "{item_kind} `{item_name}` not found for this {}", - actual.prefix_string(self.tcx) + rcvr_ty.prefix_string(self.tcx) ), ); } @@ -374,7 +383,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .hir() .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id)); let probe = self.lookup_probe( - span, item_name, output_ty, call_expr, @@ -386,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut custom_span_label = false; - if !static_sources.is_empty() { + if !static_candidates.is_empty() { err.note( "found the following associated functions; to be used as methods, \ functions must have a `self` parameter", @@ -394,43 +402,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, "this is an associated function, not a method"); custom_span_label = true; } - if static_sources.len() == 1 { - let ty_str = - if let Some(CandidateSource::Impl(impl_did)) = static_sources.get(0) { - // When the "method" is resolved through dereferencing, we really want the - // original type that has the associated function for accurate suggestions. - // (#61411) - let ty = tcx.at(span).type_of(*impl_did); - match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) { - (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { - // Use `actual` as it will have more `substs` filled in. - self.ty_to_value_string(actual.peel_refs()) - } - _ => self.ty_to_value_string(ty.peel_refs()), - } - } else { - self.ty_to_value_string(actual.peel_refs()) - }; - if let SelfSource::MethodCall(expr) = source { - err.span_suggestion( - expr.span.to(span), - "use associated function syntax instead", - format!("{}::{}", ty_str, item_name), - Applicability::MachineApplicable, - ); - } else { - err.help(&format!("try with `{}::{}`", ty_str, item_name,)); - } + if static_candidates.len() == 1 { + self.suggest_associated_call_syntax( + &mut err, + &static_candidates, + rcvr_ty, + source, + item_name, + args, + sugg_span, + ); - report_candidates(span, &mut err, &mut static_sources, sugg_span); - } else if static_sources.len() > 1 { - report_candidates(span, &mut err, &mut static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_candidates, None); + } else if static_candidates.len() > 1 { + report_candidates(span, &mut err, &mut static_candidates, Some(sugg_span)); } let mut bound_spans = vec![]; let mut restrict_type_params = false; let mut unsatisfied_bounds = false; - if item_name.name == sym::count && self.is_slice_ty(actual, span) { + if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) { let msg = "consider using `len` instead"; if let SelfSource::MethodCall(_expr) = source { err.span_suggestion_short( @@ -444,7 +435,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!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement")); + err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement")); } } else if !unsatisfied_predicates.is_empty() { let mut type_params = FxHashMap::default(); @@ -454,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut unimplemented_traits = FxHashMap::default(); let mut unimplemented_traits_only = true; for (predicate, _parent_pred, cause) in &unsatisfied_predicates { - if let (ty::PredicateKind::Trait(p), Some(cause)) = + if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) = (predicate.kind().skip_binder(), cause.as_ref()) { if p.trait_ref.self_ty() != rcvr_ty { @@ -481,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // because of some non-Clone item being iterated over. for (predicate, _parent_pred, _cause) in &unsatisfied_predicates { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(p) + ty::PredicateKind::Clause(ty::Clause::Trait(p)) if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} _ => { unimplemented_traits_only = false; @@ -493,27 +484,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut collect_type_param_suggestions = |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { // We don't care about regions here, so it's fine to skip the binder here. - if let (ty::Param(_), ty::PredicateKind::Trait(p)) = + if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) = (self_ty.kind(), parent_pred.kind().skip_binder()) { + let hir = self.tcx.hir(); let node = match p.trait_ref.self_ty().kind() { ty::Param(_) => { // Account for `fn` items like in `issue-35677.rs` to // suggest restricting its type params. - let did = self.tcx.hir().body_owner_def_id(hir::BodyId { - hir_id: self.body_id, - }); - Some( - self.tcx - .hir() - .get(self.tcx.hir().local_def_id_to_hir_id(did)), - ) + let parent_body = + hir.body_owner(hir::BodyId { hir_id: self.body_id }); + Some(hir.get(parent_body)) + } + ty::Adt(def, _) => { + def.did().as_local().map(|def_id| hir.get_by_def_id(def_id)) } - ty::Adt(def, _) => def.did().as_local().map(|def_id| { - self.tcx - .hir() - .get(self.tcx.hir().local_def_id_to_hir_id(def_id)) - }), _ => None, }; if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { @@ -562,7 +547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut format_pred = |pred: ty::Predicate<'tcx>| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Projection(pred) => { + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { let pred = bound_predicate.rebind(pred); // `::Item = String`. let projection_ty = pred.skip_binder().projection_ty; @@ -585,7 +570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_span_label(projection_ty.self_ty(), &obligation, &quiet); Some((obligation, projection_ty.self_ty())) } - ty::PredicateKind::Trait(poly_trait_ref) => { + ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => { let p = poly_trait_ref.trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); @@ -605,7 +590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) .filter_map(|(p, parent, c)| match c.code() { - ObligationCauseCode::ImplDerivedObligation(ref data) => { + ObligationCauseCode::ImplDerivedObligation(data) => { Some((&data.derived, p, parent, data.impl_def_id, data)) } _ => None, @@ -620,22 +605,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match self.tcx.hir().get_if_local(impl_def_id) { // Unmet obligation comes from a `derive` macro, point at it once to // avoid multiple span labels pointing at the same place. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(..), - ident, - .. - })) if matches!( - ident.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) => - { - let span = ident.span.ctxt().outer_expn_data().call_site; - let mut spans: MultiSpan = span.into(); - spans.push_span_label(span, derive_msg); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - Some(Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), .. @@ -659,34 +628,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); } - // Unmet obligation coming from a `trait`. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(..), - ident, - span: item_span, - .. - })) if !matches!( - ident.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) => - { - if let Some(pred) = parent_p { - // Done to add the "doesn't satisfy" `span_label`. - let _ = format_pred(*pred); - } - skip_list.insert(p); - let mut spans = if cause.span != *item_span { - let mut spans: MultiSpan = cause.span.into(); - spans.push_span_label(cause.span, unsatisfied_msg); - spans - } else { - ident.span.into() - }; - spans.push_span_label(ident.span, "in this trait"); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - // Unmet obligation coming from an `impl`. Some(Node::Item(hir::Item { kind: @@ -695,23 +636,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), span: item_span, .. - })) if !matches!( - self_ty.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) && !matches!( - of_trait.as_ref().map(|t| t - .path - .span - .ctxt() - .outer_expn_data() - .kind), - Some(ExpnKind::Macro(MacroKind::Derive, _)) - ) => - { + })) => { let sized_pred = unsatisfied_predicates.iter().any(|(pred, _, _)| { match pred.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { Some(pred.def_id()) == self.tcx.lang_items().sized_trait() && pred.polarity == ty::ImplPolarity::Positive @@ -759,7 +688,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let entry = spanned_predicates.entry(spans); entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); } - _ => {} + Some(_) => unreachable!(), + None => (), } } let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); @@ -844,7 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(_, path)| path) .collect::>() .join("\n"); - let actual_prefix = actual.prefix_string(self.tcx); + let actual_prefix = rcvr_ty.prefix_string(self.tcx); info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); let (primary_message, label) = if unimplemented_traits.len() == 1 && unimplemented_traits_only { @@ -853,7 +783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .next() .map(|(_, (trait_ref, obligation))| { if trait_ref.self_ty().references_error() - || actual.references_error() + || rcvr_ty.references_error() { // Avoid crashing. return (None, None); @@ -863,7 +793,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .on_unimplemented_note(trait_ref, &obligation); (message, label) }) - .unwrap_or((None, None)) + .unwrap() } else { (None, None) }; @@ -889,15 +819,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let label_span_not_found = |err: &mut Diagnostic| { if unsatisfied_predicates.is_empty() { err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); - let is_string_or_ref_str = match actual.kind() { + let is_string_or_ref_str = match rcvr_ty.kind() { ty::Ref(_, ty, _) => { ty.is_str() || matches!( ty.kind(), - ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did()) + ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string() ) } - ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()), + ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(), _ => false, }; if is_string_or_ref_str && item_name.name == sym::iter { @@ -925,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // different from the received one // So we avoid suggestion method with Box // for instance - self.tcx.at(span).type_of(*def_id) != actual + self.tcx.at(span).type_of(*def_id) != rcvr_ty && self.tcx.at(span).type_of(*def_id) != rcvr_ty } (Mode::Path, false, _) => true, @@ -972,7 +902,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { - if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err) + if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) && lev_candidate.is_none() && !custom_span_label { @@ -982,13 +912,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - // Don't suggest (for example) `expr.field.method()` if `expr.method()` - // doesn't exist due to unsatisfied predicates. + // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` + // can't be called due to `typeof(expr): Clone` not holding. if unsatisfied_predicates.is_empty() { - self.check_for_field_method(&mut err, source, span, actual, item_name); + self.suggest_calling_method_on_field( + &mut err, source, span, rcvr_ty, item_name, + ); } - self.check_for_inner_self(&mut err, source, span, actual, item_name); + self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); bound_spans.sort(); bound_spans.dedup(); @@ -996,7 +928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, &msg); } - if actual.is_numeric() && actual.is_fresh() || restrict_type_params { + if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params { } else { self.suggest_traits_to_import( &mut err, @@ -1007,15 +939,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, out_of_scope_traits, &unsatisfied_predicates, - &static_sources, + &static_candidates, unsatisfied_bounds, ); } // Don't emit a suggestion if we found an actual method // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() && actual.is_enum() { - let adt_def = actual.ty_adt_def().expect("enum is not an ADT"); + if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { + let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); if let Some(suggestion) = lev_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::>(), item_name.name, @@ -1030,7 +962,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - if item_name.name == sym::as_str && actual.peel_refs().is_str() { + if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() { let msg = "remove this method call"; let mut fallback_span = true; if let SelfSource::MethodCall(expr) = source { @@ -1089,7 +1021,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - report_candidates(span, &mut err, &mut sources, sugg_span); + report_candidates(span, &mut err, &mut sources, Some(sugg_span)); err.emit(); } @@ -1146,7 +1078,133 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - fn suggest_field_call( + /// Suggest calling `Ty::method` if `.method()` isn't found because the method + /// doesn't take a `self` receiver. + fn suggest_associated_call_syntax( + &self, + err: &mut Diagnostic, + static_candidates: &Vec, + rcvr_ty: Ty<'tcx>, + source: SelfSource<'tcx>, + item_name: Ident, + args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>, + sugg_span: Span, + ) { + let mut has_unsuggestable_args = false; + let ty_str = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) { + // When the "method" is resolved through dereferencing, we really want the + // original type that has the associated function for accurate suggestions. + // (#61411) + let impl_ty = self.tcx.type_of(*impl_did); + let target_ty = self + .autoderef(sugg_span, rcvr_ty) + .find(|(rcvr_ty, _)| { + DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer } + .types_may_unify(*rcvr_ty, impl_ty) + }) + .map_or(impl_ty, |(ty, _)| ty) + .peel_refs(); + if let ty::Adt(def, substs) = target_ty.kind() { + // If there are any inferred arguments, (`{integer}`), we should replace + // them with underscores to allow the compiler to infer them + let infer_substs = self.tcx.mk_substs(substs.into_iter().map(|arg| { + if !arg.is_suggestable(self.tcx, true) { + has_unsuggestable_args = true; + match arg.unpack() { + GenericArgKind::Lifetime(_) => self + .next_region_var(RegionVariableOrigin::MiscVariable( + rustc_span::DUMMY_SP, + )) + .into(), + GenericArgKind::Type(_) => self + .next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }) + .into(), + GenericArgKind::Const(arg) => self + .next_const_var( + arg.ty(), + ConstVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: ConstVariableOriginKind::MiscVariable, + }, + ) + .into(), + } + } else { + arg + } + })); + + self.tcx.value_path_str_with_substs(def.did(), infer_substs) + } else { + self.ty_to_value_string(target_ty) + } + } else { + self.ty_to_value_string(rcvr_ty.peel_refs()) + }; + if let SelfSource::MethodCall(_) = source { + let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) + && let Some(assoc) = self.associated_value(*impl_did, item_name) + && assoc.kind == ty::AssocKind::Fn + { + let sig = self.tcx.fn_sig(assoc.def_id); + sig.inputs().skip_binder().get(0).and_then(|first| if first.peel_refs() == rcvr_ty.peel_refs() { + None + } else { + Some(first.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str())) + }) + } else { + None + }; + let mut applicability = Applicability::MachineApplicable; + let args = if let Some((receiver, args)) = args { + // The first arg is the same kind as the receiver + let explicit_args = if first_arg.is_some() { + std::iter::once(receiver).chain(args.iter()).collect::>() + } else { + // There is no `Self` kind to infer the arguments from + if has_unsuggestable_args { + applicability = Applicability::HasPlaceholders; + } + args.iter().collect() + }; + format!( + "({}{})", + first_arg.unwrap_or(""), + explicit_args + .iter() + .map(|arg| self + .tcx + .sess + .source_map() + .span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::>() + .join(", "), + ) + } else { + applicability = Applicability::HasPlaceholders; + "(...)".to_owned() + }; + err.span_suggestion( + sugg_span, + "use associated function syntax instead", + format!("{}::{}{}", ty_str, item_name, args), + applicability, + ); + } else { + err.help(&format!("try with `{}::{}`", ty_str, item_name,)); + } + } + + /// Suggest calling a field with a type that implements the `Fn*` traits instead of a method with + /// the same name as the field i.e. `(a.my_fn_ptr)(10)` instead of `a.my_fn_ptr(10)`. + fn suggest_calling_field_as_fn( &self, span: Span, rcvr_ty: Ty<'tcx>, @@ -1261,7 +1319,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); let pick = self.probe_for_name( - span, Mode::MethodCall, item_name, IsSuggestion(true), @@ -1408,7 +1465,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn check_for_field_method( + /// For code `rect::area(...)`, + /// if `rect` is a local variable and `area` is a valid assoc method for it, + /// we try to suggest `rect.area()` + pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) { + debug!("suggest_assoc_method_call segs: {:?}", segs); + let [seg1, seg2] = segs else { return; }; + let Some(mut diag) = + self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) + else { return }; + + let map = self.infcx.tcx.hir(); + let body = map.body(rustc_hir::BodyId { hir_id: self.body_id }); + struct LetVisitor<'a> { + result: Option<&'a hir::Expr<'a>>, + ident_name: Symbol, + } + + // FIXME: This really should be taking scoping, etc into account. + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { + if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind + && let Binding(_, _, ident, ..) = pat.kind + && ident.name == self.ident_name + { + self.result = *init; + } else { + hir::intravisit::walk_stmt(self, ex); + } + } + } + + let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; + visitor.visit_body(&body); + + let parent = self.tcx.hir().get_parent_node(seg1.hir_id); + if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) + && let Some(expr) = visitor.result + && let Some(self_ty) = self.node_ty_opt(expr.hir_id) + { + let probe = self.lookup_probe( + seg2.ident, + self_ty, + call_expr, + ProbeScope::TraitsInScope, + ); + if probe.is_ok() { + let sm = self.infcx.tcx.sess.source_map(); + diag.span_suggestion_verbose( + sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(), + "you may have meant to call an instance method", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + diag.emit(); + } + + /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()` + fn suggest_calling_method_on_field( &self, err: &mut Diagnostic, source: SelfSource<'tcx>, @@ -1439,7 +1555,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, &|_, field_ty| { self.lookup_probe( - span, item_name, field_ty, call_expr, @@ -1487,7 +1602,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, source: SelfSource<'tcx>, - span: Span, actual: Ty<'tcx>, item_name: Ident, ) { @@ -1510,15 +1624,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - self.lookup_probe( - span, - item_name, - field_ty, - call_expr, - ProbeScope::TraitsInScope, - ) - .ok() - .map(|pick| (variant, field, pick)) + self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope) + .ok() + .map(|pick| (variant, field, pick)) }) .collect(); @@ -1583,12 +1691,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let [first] = ***substs else { return; }; let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; let Ok(pick) = self.lookup_probe( - span, - item_name, - ty, - call_expr, - ProbeScope::TraitsInScope, - ) else { return; }; + item_name, + ty, + call_expr, + ProbeScope::TraitsInScope, + ) else { return; }; let name = self.ty_to_value_string(actual); let inner_id = kind.did(); @@ -1668,7 +1775,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let all_local_types_needing_impls = errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => match pred.self_ty().kind() { ty::Adt(def, _) => def.did().is_local(), _ => false, }, @@ -1677,7 +1784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut preds: Vec<_> = errors .iter() .filter_map(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => Some(pred), + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => Some(pred), _ => None, }) .collect(); @@ -1748,7 +1855,7 @@ 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::Trait(trait_pred) = pred.kind().skip_binder() else { continue }; + let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() else { continue }; let adt = match trait_pred.self_ty().ty_adt_def() { Some(adt) if adt.did().is_local() => adt, _ => continue, @@ -1838,7 +1945,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let SelfSource::QPath(ty) = self_source else { return; }; for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { if let Ok(pick) = self.probe_for_name( - ty.span, Mode::Path, item_name, IsSuggestion(true), @@ -1865,6 +1971,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Str | ty::Projection(_) | ty::Param(_) => format!("{deref_ty}"), + // we need to test something like <&[_]>::len or <(&[u32])>::len + // and Vec::function(); + // <&[_]>::len or <&[u32]>::len doesn't need an extra "<>" between + // but for Adt type like Vec::function() + // we would suggest <[_]>::function(); + _ if self.tcx.sess.source_map().span_wrapped_by_angle_or_parentheses(ty.span) => format!("{deref_ty}"), _ => format!("<{deref_ty}>"), }; err.span_suggestion_verbose( @@ -1887,7 +1999,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { match ty.kind() { - ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did(), substs)), + ty::Adt(def, substs) => self.tcx.def_path_str_with_substs(def.did(), substs), _ => self.ty_to_string(ty), } } @@ -1901,7 +2013,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, ) { let output_ty = match self.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(), + Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); @@ -2021,7 +2133,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let mut alt_rcvr_sugg = false; if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { - debug!(?span, ?item_name, ?rcvr_ty, ?rcvr); + debug!( + "suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}", + span, item_name, rcvr_ty, rcvr + ); let skippable = [ self.tcx.lang_items().clone_trait(), self.tcx.lang_items().deref_trait(), @@ -2037,7 +2152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { - match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { + match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { Ok(pick) => { // If the method is defined for the receiver we have, it likely wasn't `use`d. // We point at the method, but we just skip the rest of the check for arbitrary @@ -2060,7 +2175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // suggestions are generally misleading (see #94218). break; } - _ => {} + Err(_) => (), } for (rcvr_ty, pre) in &[ @@ -2071,7 +2186,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ] { if let Some(new_rcvr_t) = *rcvr_ty && let Ok(pick) = self.lookup_probe( - span, item_name, new_rcvr_t, rcvr, @@ -2151,8 +2265,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match p.kind().skip_binder() { // Hide traits if they are present in predicates as they can be fixed without // having to implement them. - ty::PredicateKind::Trait(t) => t.def_id() == info.def_id, - ty::PredicateKind::Projection(p) => { + ty::PredicateKind::Clause(ty::Clause::Trait(t)) => { + t.def_id() == info.def_id + } + ty::PredicateKind::Clause(ty::Clause::Projection(p)) => { p.projection_ty.item_def_id == info.def_id } _ => false, @@ -2452,7 +2568,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: method_name.span, }; let probe = self.lookup_probe( - expr.span, new_name, self_ty, self_expr, @@ -2565,11 +2680,7 @@ fn print_disambiguation_help<'tcx>( let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { let args = format!( "({}{})", - if rcvr_ty.is_region_ptr() { - if rcvr_ty.is_mutable_ptr() { "&mut " } else { "&" } - } else { - "" - }, + rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()), std::iter::once(receiver) .chain(args.iter()) .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 895739976..b12d84af4 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -19,7 +19,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; +use rustc_trait_selection::traits::FulfillmentError; use rustc_type_ir::sty::TyKind::*; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -263,14 +263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let by_ref_binop = !op.node.is_by_value(); if is_assign == IsAssign::Yes || by_ref_binop { if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; + let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); let autoref = Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[0], @@ -280,14 +273,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if by_ref_binop { if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; + // Allow two-phase borrows for binops in initial deployment + // since they desugar to methods + let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); + let autoref = Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[1], @@ -529,8 +518,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - self.tcx.ty_error() + let reported = err.emit(); + self.tcx.ty_error_with_guaranteed(reported) } }; @@ -556,9 +545,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; 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| self.tcx.is_diagnostic_item(sym::String, ty_def.did())) + ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type) }; match (lhs_ty.kind(), rhs_ty.kind()) { @@ -772,7 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (method, trait_did) { (Some(ok), _) => { let method = self.register_infer_ok_obligations(ok); - self.select_obligations_where_possible(false, |_| {}); + self.select_obligations_where_possible(|_| {}); Ok(method) } (None, None) => Err(vec![]), @@ -785,9 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { other_ty_expr, expected, ); - let mut fulfill = >::new(self.tcx); - fulfill.register_predicate_obligation(self, obligation); - Err(fulfill.select_where_possible(&self.infcx)) + Err(rustc_trait_selection::traits::fully_solve_obligation(self, obligation)) } } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ea90da4a6..decd317d9 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -19,7 +19,6 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, DUMMY_SP}; -use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use ty::VariantDef; @@ -402,6 +401,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + if self.tcx.features().string_deref_patterns && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind { + let tcx = self.tcx; + let expected = self.resolve_vars_if_possible(expected); + pat_ty = match expected.kind() { + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected, + ty::Str => tcx.mk_static_str(), + _ => pat_ty, + }; + } + // Somewhat surprising: in this case, the subtyping relation goes the // opposite way as the other cases. Actually what we really want is not // a subtyping relation at all but rather that there exists a LUB @@ -693,7 +702,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut_var_suggestion = 'block: { - if !matches!(mutbl, ast::Mutability::Mut) { + if mutbl.is_not() { break 'block None; } @@ -740,7 +749,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("to take parameter `{binding}` by reference, move `&{mutability}` to the type"), vec![ (pat.span.until(inner.span), "".to_owned()), - (ty_span.shrink_to_lo(), format!("&{}", mutbl.prefix_str())), + (ty_span.shrink_to_lo(), mutbl.ref_prefix_str().to_owned()), ], Applicability::MachineApplicable ); @@ -840,12 +849,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (res, opt_ty, segments) = path_resolution; match res { Res::Err => { - self.set_tainted_by_errors(); - return tcx.ty_error(); + let e = tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(e); + return tcx.ty_error_with_guaranteed(e); } - Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { - report_unexpected_variant_res(tcx, res, qpath, pat.span); - return tcx.ty_error(); + Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { + let expected = "unit struct, unit variant or constant"; + let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, "E0533", expected); + return tcx.ty_error_with_guaranteed(e); } Res::SelfCtor(..) | Res::Def( @@ -986,65 +997,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let on_error = || { + let on_error = |e| { for pat in subpats { - self.check_pat(pat, tcx.ty_error(), def_bm, ti); + self.check_pat(pat, tcx.ty_error_with_guaranteed(e), def_bm, ti); } }; let report_unexpected_res = |res: Res| { - let sm = tcx.sess.source_map(); - let path_str = sm - .span_to_snippet(sm.span_until_char(pat.span, '(')) - .map_or_else(|_| String::new(), |s| format!(" `{}`", s.trim_end())); - let msg = format!( - "expected tuple struct or tuple variant, found {}{}", - res.descr(), - path_str - ); - - let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{msg}"); - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, _) => { - err.span_label(pat.span, "`fn` calls are not allowed in patterns"); - err.help( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-00-patterns.html", - ); - } - _ => { - err.span_label(pat.span, "not a tuple variant or struct"); - } - } - err.emit(); - on_error(); + let expected = "tuple struct or tuple variant"; + let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, "E0164", expected); + on_error(e); + e }; // Resolve the path and check the definition for errors. let (res, opt_ty, segments) = self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); if res == Res::Err { - self.set_tainted_by_errors(); - on_error(); - return self.tcx.ty_error(); + let e = tcx.sess.delay_span_bug(pat.span, "`Res:Err` but no error emitted"); + self.set_tainted_by_errors(e); + on_error(e); + return tcx.ty_error_with_guaranteed(e); } // Type-check the path. let (pat_ty, res) = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); if !pat_ty.is_fn() { - report_unexpected_res(res); - return tcx.ty_error(); + let e = report_unexpected_res(res); + return tcx.ty_error_with_guaranteed(e); } let variant = match res { Res::Err => { - self.set_tainted_by_errors(); - on_error(); - return tcx.ty_error(); + let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(e); + on_error(e); + return tcx.ty_error_with_guaranteed(e); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { - report_unexpected_res(res); - return tcx.ty_error(); + let e = report_unexpected_res(res); + return tcx.ty_error_with_guaranteed(e); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), _ => bug!("unexpected pattern resolution: {:?}", res), @@ -1083,9 +1075,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else { // Pattern has wrong number of fields. - self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); - on_error(); - return tcx.ty_error(); + let e = self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); + on_error(e); + return tcx.ty_error_with_guaranteed(e); } pat_ty } @@ -1099,7 +1091,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [ty::FieldDef], expected: Ty<'tcx>, had_err: bool, - ) { + ) -> ErrorGuaranteed { let subpats_ending = pluralize!(subpats.len()); let fields_ending = pluralize!(fields.len()); @@ -1246,7 +1238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - err.emit(); + err.emit() } fn check_pat_tuple( @@ -1278,12 +1270,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_tys = tcx.mk_type_list(element_tys_iter); let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { - err.emit(); + let reported = err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); + let element_tys_iter = (0..max_len).map(|_| tcx.ty_error_with_guaranteed(reported)); for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, tcx.ty_error(), def_bm, ti); + self.check_pat(elem, tcx.ty_error_with_guaranteed(reported), def_bm, ti); } tcx.mk_tup(element_tys_iter) } else { @@ -1468,8 +1460,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // if this is a tuple struct, then all field names will be numbers // so if any fields in a struct pattern use shorthand syntax, they will // be invalid identifiers (for example, Foo { 0, 1 }). - if let (CtorKind::Fn, PatKind::Struct(qpath, field_patterns, ..)) = - (variant.ctor_kind, &pat.kind) + if let (Some(CtorKind::Fn), PatKind::Struct(qpath, field_patterns, ..)) = + (variant.ctor_kind(), &pat.kind) { let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand); if has_shorthand_field_name { @@ -1646,7 +1638,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [hir::PatField<'tcx>], variant: &ty::VariantDef, ) -> Option> { - if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) { + if let (Some(CtorKind::Fn), PatKind::Struct(qpath, ..)) = (variant.ctor_kind(), &pat.kind) { let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { s.print_qpath(qpath, false) }); @@ -2132,7 +2124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let ty::Array(..) | ty::Slice(..) = ty.kind() { err.help("the semantics of slice patterns changed recently; see issue #62254"); - } else if Autoderef::new(&self.infcx, self.param_env, self.body_id, span, expected_ty, span) + } else if self.autoderef(span, expected_ty) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) && let (Some(span), true) = (ti.span, ti.origin_expr) && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba8cf6926..952ea1488 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -90,8 +90,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - err.emit(); - Some((self.tcx.ty_error(), self.tcx.ty_error())) + let reported = err.emit(); + Some(( + self.tcx.ty_error_with_guaranteed(reported), + self.tcx.ty_error_with_guaranteed(reported), + )) } /// To type-check `base_expr[index_expr]`, we progressively autoderef diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 4dea40829..0f4697201 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -970,12 +970,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { check_trait .map(|check_trait| { self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) + .type_implements_trait(check_trait, [ty], self.param_env) .must_apply_modulo_regions() }) .unwrap_or(false), @@ -999,12 +994,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { check_trait .map(|check_trait| { self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) + .type_implements_trait(check_trait, [ty], self.param_env) .must_apply_modulo_regions() }) .unwrap_or(false), @@ -1347,14 +1337,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_drop_defined_for_ty = |ty: Ty<'tcx>| { let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span)); - let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]); self.infcx - .type_implements_trait( - drop_trait, - ty, - ty_params, - self.tcx.param_env(closure_def_id), - ) + .type_implements_trait(drop_trait, [ty], self.tcx.param_env(closure_def_id)) .must_apply_modulo_regions() }; @@ -2184,7 +2168,7 @@ fn determine_place_ancestry_relation<'tcx>( place_a: &Place<'tcx>, place_b: &Place<'tcx>, ) -> PlaceAncestryRelation { - // If Place A and Place B, don't start off from the same root variable, they are divergent. + // If Place A and Place B don't start off from the same root variable, they are divergent. if place_a.base != place_b.base { return PlaceAncestryRelation::Divergent; } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 1e26daa9c..52ffd286e 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -83,10 +83,8 @@ 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 self.is_tainted_by_errors() { - // FIXME(eddyb) keep track of `ErrorGuaranteed` from where the error was emitted. - wbcx.typeck_results.tainted_by_errors = - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + 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); @@ -363,9 +361,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { intravisit::walk_ty(self, hir_ty); - let ty = self.fcx.node_ty(hir_ty.hir_id); - let ty = self.resolve(ty, &hir_ty.span); - self.write_ty_to_typeck_results(hir_ty.hir_id, ty); + // If there are type checking errors, Type privacy pass will stop, + // so we may not get the type from hid_id, see #104513 + if let Some(ty) = self.fcx.node_ty_opt(hir_ty.hir_id) { + let ty = self.resolve(ty, &hir_ty.span); + self.write_ty_to_typeck_results(hir_ty.hir_id, ty); + } } fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { @@ -514,7 +515,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for (&def_id, c_sig) in fcx_typeck_results.user_provided_sigs.iter() { if cfg!(debug_assertions) && c_sig.needs_infer() { span_bug!( - self.fcx.tcx.hir().span_if_local(def_id).unwrap(), + self.fcx.tcx.def_span(def_id), "writeback: `{:?}` has inference variables", c_sig ); @@ -564,7 +565,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { opaque_type_key, self.fcx.infcx.tcx, true, - decl.origin, ); self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); @@ -674,10 +674,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // We may have introduced e.g. `ty::Error`, if inference failed, make sure // to mark the `TypeckResults` as tainted in that case, so that downstream // users of the typeck results don't produce extra errors, or worse, ICEs. - if resolver.replaced_with_error { - // FIXME(eddyb) keep track of `ErrorGuaranteed` from where the error was emitted. - self.typeck_results.tainted_by_errors = - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + if let Some(e) = resolver.replaced_with_error { + self.typeck_results.tainted_by_errors = Some(e); } x @@ -708,8 +706,8 @@ struct Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, - /// Set to `true` if any `Ty` or `ty::Const` had to be replaced with an `Error`. - replaced_with_error: bool, + /// Set to `Some` if any `Ty` or `ty::Const` had to be replaced with an `Error`. + replaced_with_error: Option, } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { @@ -718,12 +716,14 @@ 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: false } + Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: None } } - fn report_error(&self, p: impl Into>) { - if !self.tcx.sess.has_errors().is_some() { - self.infcx + fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { + match self.tcx.sess.has_errors() { + Some(e) => e, + None => self + .infcx .err_ctxt() .emit_inference_failure_err( Some(self.body.id()), @@ -732,7 +732,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { E0282, false, ) - .emit(); + .emit(), } } } @@ -773,9 +773,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_error(t); - self.replaced_with_error = true; - self.tcx().ty_error() + let e = self.report_error(t); + self.replaced_with_error = Some(e); + self.tcx().ty_error_with_guaranteed(e) } } } @@ -790,9 +790,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { Ok(ct) => self.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); - self.report_error(ct); - self.replaced_with_error = true; - self.tcx().const_error(ct.ty()) + let e = self.report_error(ct); + self.replaced_with_error = Some(e); + self.tcx().const_error_with_guaranteed(ct.ty(), e) } } } -- cgit v1.2.3