From 17d40c6057c88f4c432b0d7bac88e1b84cb7e67f Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:03:36 +0200 Subject: Adding upstream version 1.65.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_typeck/src/check/_match.rs | 122 ++-- compiler/rustc_typeck/src/check/callee.rs | 25 +- compiler/rustc_typeck/src/check/cast.rs | 121 +++- compiler/rustc_typeck/src/check/check.rs | 195 +++--- compiler/rustc_typeck/src/check/closure.rs | 90 ++- compiler/rustc_typeck/src/check/coercion.rs | 89 ++- compiler/rustc_typeck/src/check/compare_method.rs | 449 +++++++++--- compiler/rustc_typeck/src/check/demand.rs | 87 +-- compiler/rustc_typeck/src/check/dropck.rs | 2 + compiler/rustc_typeck/src/check/expr.rs | 454 +++++++------ compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs | 90 +-- .../rustc_typeck/src/check/fn_ctxt/arg_matrix.rs | 25 +- compiler/rustc_typeck/src/check/fn_ctxt/checks.rs | 752 +++++++++++++++------ compiler/rustc_typeck/src/check/fn_ctxt/mod.rs | 14 +- .../rustc_typeck/src/check/fn_ctxt/suggestions.rs | 383 ++++++++--- .../rustc_typeck/src/check/generator_interior.rs | 26 +- .../generator_interior/drop_ranges/cfg_build.rs | 5 +- .../drop_ranges/record_consumed_borrow.rs | 4 +- compiler/rustc_typeck/src/check/inherited.rs | 30 +- compiler/rustc_typeck/src/check/intrinsic.rs | 28 +- compiler/rustc_typeck/src/check/intrinsicck.rs | 71 +- compiler/rustc_typeck/src/check/method/confirm.rs | 14 +- compiler/rustc_typeck/src/check/method/mod.rs | 62 +- .../rustc_typeck/src/check/method/prelude2021.rs | 1 - compiler/rustc_typeck/src/check/method/probe.rs | 19 +- compiler/rustc_typeck/src/check/method/suggest.rs | 277 +++++--- compiler/rustc_typeck/src/check/mod.rs | 81 ++- compiler/rustc_typeck/src/check/op.rs | 505 ++++++-------- compiler/rustc_typeck/src/check/pat.rs | 67 +- compiler/rustc_typeck/src/check/region.rs | 43 +- compiler/rustc_typeck/src/check/regionck.rs | 47 -- compiler/rustc_typeck/src/check/upvar.rs | 6 +- compiler/rustc_typeck/src/check/wfcheck.rs | 196 +++--- compiler/rustc_typeck/src/check/writeback.rs | 39 +- 34 files changed, 2815 insertions(+), 1604 deletions(-) delete mode 100644 compiler/rustc_typeck/src/check/regionck.rs (limited to 'compiler/rustc_typeck/src/check') diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 1b13c98e4..20332e75c 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -4,7 +4,7 @@ use rustc_errors::{Applicability, 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, TypeVisitable}; +use rustc_middle::ty::{self, Subst, ToPredicate, Ty}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -12,7 +12,7 @@ use rustc_trait_selection::traits::{ }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] pub fn check_match( &self, expr: &'tcx hir::Expr<'tcx>, @@ -94,7 +94,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arm_ty = self.check_expr_with_expectation(&arm.body, expected); all_arms_diverge &= self.diverges.get(); - let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected); + let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| { + self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected) + }); let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind { (Some(blk.hir_id), self.find_block_span(blk)) @@ -135,9 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&arm.body), arm_ty, Some(&mut |err| { - let Some(ret) = self.ret_type_span else { - return; - }; + let Some(ret) = self + .tcx + .hir() + .find_by_def_id(self.body_id.owner) + .and_then(|owner| owner.fn_decl()) + .map(|decl| decl.output.span()) + else { return; }; let Expectation::IsLast(stmt) = orig_expected else { return }; @@ -210,9 +216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We won't diverge unless the scrutinee or all arms diverge. self.diverges.set(scrut_diverges | all_arms_diverge); - let match_ty = coercion.complete(self); - debug!(?match_ty); - match_ty + coercion.complete(self) } /// When the previously checked expression (the scrutinee) diverges, @@ -468,53 +472,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` - // we check if the different arms would work with boxed trait objects instead and - // provide a structured suggestion in that case. + /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` + /// we check if the different arms would work with boxed trait objects instead and + /// provide a structured suggestion in that case. pub(crate) fn opt_suggest_box_span( &self, - outer_ty: Ty<'tcx>, + first_ty: Ty<'tcx>, + second_ty: Ty<'tcx>, orig_expected: Expectation<'tcx>, ) -> Option { + // FIXME(compiler-errors): This really shouldn't need to be done during the + // "good" path of typeck, but here we are. match orig_expected { - Expectation::ExpectHasType(expected) - if self.in_tail_expr - && self.ret_coercion.as_ref()?.borrow().merged_ty().has_opaque_types() - && self.can_coerce(outer_ty, expected) => - { - let obligations = self.fulfillment_cx.borrow().pending_obligations(); - let mut suggest_box = !obligations.is_empty(); - for o in obligations { - match o.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(t) => { - let pred = - ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: t.def_id(), - substs: self.tcx.mk_substs_trait(outer_ty, &[]), - }, - constness: t.constness, - polarity: t.polarity, - })); - let obl = Obligation::new( - o.cause.clone(), - self.param_env, - pred.to_predicate(self.tcx), - ); - suggest_box &= self.predicate_must_hold_modulo_regions(&obl); - if !suggest_box { - // We've encountered some obligation that didn't hold, so the - // return expression can't just be boxed. We don't need to - // evaluate the rest of the obligations. - break; + Expectation::ExpectHasType(expected) => { + let TypeVariableOrigin { + span, + kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id), + .. + } = 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))?; + + let substs = sig.output().walk().find_map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Opaque(def_id, substs) = *ty.kind() + && def_id == rpit_def_id + { + Some(substs) + } else { + None + } + })?; + let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs); + + if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) { + return None; + } + + for ty in [first_ty, second_ty] { + for pred in self.tcx.bound_explicit_item_bounds(rpit_def_id).transpose_iter() { + let pred = pred.map_bound(|(pred, _)| *pred).subst(self.tcx, substs); + let pred = match pred.kind().skip_binder() { + ty::PredicateKind::Trait(mut 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::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) + } + _ => continue, + }; + if !self.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::misc(span, self.body_id), + self.param_env, + pred, + )) { + return None; } - _ => {} } } - // If all the obligations hold (or there are no obligations) the tail expression - // we can suggest to return a boxed trait object instead of an opaque type. - if suggest_box { self.ret_type_span } else { None } + + Some(span) } _ => None, } diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 75f5aced8..0d35c2479 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -1,5 +1,5 @@ use super::method::MethodCallee; -use super::{Expectation, FnCtxt, TupleArgumentsFlag}; +use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag}; use crate::type_error_struct; use rustc_errors::{struct_span_err, Applicability, Diagnostic}; @@ -24,7 +24,8 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; use rustc_trait_selection::autoderef::Autoderef; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::iter; @@ -471,7 +472,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { - err.span_label(call_expr.span, "call expression requires function"); + if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_expr, callee_ty) + && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span) + { + let descr = match maybe_def { + DefIdOrName::DefId(def_id) => self.tcx.def_kind(def_id).descr(def_id), + DefIdOrName::Name(name) => name, + }; + err.span_label( + callee_expr.span, + format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called") + ); + if let DefIdOrName::DefId(def_id) = maybe_def + && let Some(def_span) = self.tcx.hir().span_if_local(def_id) + { + err.span_label(def_span, "the callable type is defined here"); + } + } else { + err.span_label(call_expr.span, "call expression requires function"); + } } if let Some(span) = self.tcx.hir().res_span(def) { diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 7aaddc2bd..81a979865 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -32,30 +32,33 @@ use super::FnCtxt; use crate::hir::def_id::DefId; use crate::type_error_struct; +use hir::def_id::LOCAL_CRATE; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable}; +use rustc_middle::ty::{self, Binder, Ty, TypeAndMut, TypeVisitable, VariantDef}; use rustc_session::lint; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits; 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. #[derive(Debug)] pub struct CastCheck<'tcx> { + /// The expression whose value is being casted expr: &'tcx hir::Expr<'tcx>, + /// The source type for the cast expression expr_ty: Ty<'tcx>, expr_span: Span, + /// The target type. That is, the type we are casting to. cast_ty: Ty<'tcx>, cast_span: Span, span: Span, @@ -96,13 +99,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Err(reported); } - if self.type_is_known_to_be_sized_modulo_regions(t, span) { + if self.type_is_sized_modulo_regions(self.param_env, t, span) { return Ok(Some(PointerKind::Thin)); } Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())), + ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { None => Some(PointerKind::Thin), Some(f) => { @@ -139,6 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Generator(..) | ty::Adt(..) | ty::Never + | ty::Dynamic(_, _, ty::DynStar) | ty::Error(_) => { let reported = self .tcx @@ -173,6 +177,7 @@ pub enum CastError { /// or "a length". If this argument is None, then the metadata is unknown, for example, /// when we're typechecking a type parameter with a ?Sized bound. IntToFatCast(Option<&'static str>), + ForeignNonExhaustiveAdt, } impl From for CastError { @@ -199,8 +204,76 @@ fn make_invalid_casting_error<'a, 'tcx>( ) } +pub enum CastCheckResult<'tcx> { + Ok, + Deferred(CastCheck<'tcx>), + Err(ErrorGuaranteed), +} + +pub fn check_cast<'tcx>( + fcx: &FnCtxt<'_, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + cast_span: Span, + span: Span, +) -> CastCheckResult<'tcx> { + if cast_ty.is_dyn_star() { + check_dyn_star_cast(fcx, expr, expr_ty, cast_ty) + } else { + match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) { + Ok(check) => CastCheckResult::Deferred(check), + Err(e) => CastCheckResult::Err(e), + } + } +} + +fn check_dyn_star_cast<'tcx>( + fcx: &FnCtxt<'_, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, +) -> CastCheckResult<'tcx> { + // Find the bounds in the dyn*. For eaxmple, if we have + // + // let x = 22_usize as dyn* (Clone + Debug + 'static) + // + // this would return `existential_predicates = [?Self: Clone, ?Self: Debug]` and `region = 'static`. + let (existential_predicates, region) = match cast_ty.kind() { + ty::Dynamic(predicates, region, ty::DynStar) => (predicates, region), + _ => panic!("Invalid dyn* cast_ty"), + }; + + let cause = ObligationCause::new( + expr.span, + fcx.body_id, + // FIXME(dyn-star): Use a better obligation cause code + ObligationCauseCode::MiscObligation, + ); + + // For each existential predicate (e.g., `?Self: Clone`) substitute + // the type of the expression (e.g., `usize` in our example above) + // and then require that the resulting predicate (e.g., `usize: Clone`) + // holds (it does). + for existential_predicate in existential_predicates.iter() { + let predicate = existential_predicate.with_self_ty(fcx.tcx, expr_ty); + fcx.register_predicate(Obligation::new(cause.clone(), fcx.param_env, predicate)); + } + + // Enforce the region bound `'static` (e.g., `usize: 'static`, in our example). + fcx.register_predicate(Obligation::new( + cause, + fcx.param_env, + fcx.tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives( + ty::OutlivesPredicate(expr_ty, *region), + ))), + )); + + CastCheckResult::Ok +} + impl<'a, 'tcx> CastCheck<'tcx> { - pub fn new( + fn new( fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, @@ -215,7 +288,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // cases now. We do a more thorough check at the end, once // inference is more completely known. match cast_ty.kind() { - ty::Dynamic(..) | ty::Slice(..) => { + ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { let reported = check.report_cast_to_unsized_type(fcx); Err(reported) } @@ -591,6 +664,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { } err.emit(); } + CastError::ForeignNonExhaustiveAdt => { + make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ) + .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") + .emit(); + } } } @@ -692,7 +776,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); - if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span) + if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty, self.span) && !self.cast_ty.has_infer_types() { self.report_cast_to_unsized_type(fcx); @@ -789,6 +873,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { _ => return Err(CastError::NonScalar), }; + if let ty::Adt(adt_def, _) = *self.expr_ty.kind() { + if adt_def.did().krate != LOCAL_CRATE { + if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) { + return Err(CastError::ForeignNonExhaustiveAdt); + } + } + } + match (t_from, t_cast) { // These types have invariants! can't cast into them. (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), @@ -835,6 +927,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), + + // FIXME(dyn-star): this needs more conditions... + (_, DynStar) => Ok(CastKind::DynStarCast), + + // FIXME(dyn-star): do we want to allow dyn* upcasting or other casts? + (DynStar, _) => Err(CastError::IllegalCast), } } @@ -1063,10 +1161,3 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } } - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); - traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span) - } -} diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 9c1fd9b30..d6fa74c87 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -18,6 +18,7 @@ use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; @@ -32,7 +33,6 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt}; use rustc_ty_utils::representability::{self, Representability}; -use std::iter; use std::ops::ControlFlow; pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { @@ -101,12 +101,11 @@ pub(super) fn check_fn<'a, 'tcx>( decl.output.span(), param_env, )); - // If we replaced declared_ret_ty with infer vars, then we must be infering + // 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))); - fcx.ret_type_span = Some(decl.output.span()); let span = body.value.span; @@ -610,12 +609,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { match arg.kind { hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { - [ - PathSegment { - res: Some(Res::SelfTy { trait_: _, alias_to: impl_ref }), - .. - }, - ] => { + [PathSegment { res: Res::SelfTy { trait_: _, alias_to: impl_ref }, .. }] => { let impl_ty_name = impl_ref.map(|(def_id, _)| self.tcx.def_path_str(def_id)); self.selftys.push((path.span, impl_ty_name)); @@ -1104,12 +1098,28 @@ fn check_impl_items_against_trait<'tcx>( missing_items.push(tcx.associated_item(trait_item_id)); } - if let Some(required_items) = &must_implement_one_of { - // true if this item is specifically implemented in this impl - let is_implemented_here = ancestors - .leaf_def(tcx, trait_item_id) - .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); + // true if this item is specifically implemented in this impl + let is_implemented_here = ancestors + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); + + if !is_implemented_here { + match tcx.eval_default_body_stability(trait_item_id, full_impl_span) { + EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable( + tcx, + full_impl_span, + trait_item_id, + feature, + reason, + issue, + ), + // Unmarked default bodies are considered stable (at least for now). + EvalResult::Allow | EvalResult::Unmarked => {} + } + } + + if let Some(required_items) = &must_implement_one_of { if is_implemented_here { let trait_item = tcx.associated_item(trait_item_id); if required_items.contains(&trait_item.ident(tcx)) { @@ -1448,7 +1458,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L def.destructor(tcx); // force the destructor to be evaluated if vs.is_empty() { - if let Some(attr) = tcx.get_attr(def_id.to_def_id(), sym::repr) { + if let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() { struct_span_err!( tcx.sess, attr.span, @@ -1494,76 +1504,107 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L } } - let mut disr_vals: Vec> = Vec::with_capacity(vs.len()); - // This tracks the previous variant span (in the loop) incase we need it for diagnostics - let mut prev_variant_span: Span = DUMMY_SP; - for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) { - // Check for duplicate discriminant values - if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { - let variant_did = def.variant(VariantIdx::new(i)).def_id; - let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local()); - let variant_i = tcx.hir().expect_variant(variant_i_hir_id); - let i_span = match variant_i.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => tcx.def_span(variant_did), - }; - let span = match v.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => v.span, - }; - let display_discr = format_discriminant_overflow(tcx, v, discr); - let display_discr_i = format_discriminant_overflow(tcx, variant_i, disr_vals[i]); - let no_disr = v.disr_expr.is_none(); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0081, - "discriminant value `{}` assigned more than once", - discr, - ); - - err.span_label(i_span, format!("first assignment of {display_discr_i}")); - err.span_label(span, format!("second assignment of {display_discr}")); - - if no_disr { - err.span_label( - prev_variant_span, - format!( - "assigned discriminant for `{}` was incremented from this discriminant", - v.ident - ), - ); - } - err.emit(); - } - - disr_vals.push(discr); - prev_variant_span = v.span; - } + detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp); check_representable(tcx, sp, def_id); check_transparent(tcx, sp, def); } -/// In the case that a discriminant is both a duplicate and an overflowing literal, -/// we insert both the assigned discriminant and the literal it overflowed from into the formatted -/// output. Otherwise we format the discriminant normally. -fn format_discriminant_overflow<'tcx>( +/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal +fn detect_discriminant_duplicate<'tcx>( tcx: TyCtxt<'tcx>, - variant: &hir::Variant<'_>, - dis: Discr<'tcx>, -) -> String { - if let Some(expr) = &variant.disr_expr { - let body = &tcx.hir().body(expr.body).value; - if let hir::ExprKind::Lit(lit) = &body.kind - && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node - && dis.val != *lit_value - { - return format!("`{dis}` (overflowed from `{lit_value}`)"); + mut discrs: Vec<(VariantIdx, Discr<'tcx>)>, + vs: &'tcx [hir::Variant<'tcx>], + self_span: Span, +) { + // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate. + // Here `idx` refers to the order of which the discriminant appears, and its index in `vs` + let report = |dis: Discr<'tcx>, idx: usize, err: &mut Diagnostic| { + let var = &vs[idx]; // HIR for the duplicate discriminant + let (span, display_discr) = match var.disr_expr { + Some(ref expr) => { + // In the case the discriminant is both a duplicate and overflowed, let the user know + if let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind + && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node + && *lit_value != dis.val + { + (tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)")) + // Otherwise, format the value as-is + } else { + (tcx.hir().span(expr.hir_id), format!("`{dis}`")) + } + } + None => { + // At this point we know this discriminant is a duplicate, and was not explicitly + // assigned by the user. Here we iterate backwards to fetch the HIR for the last + // explicitly assigned discriminant, and letting the user know that this was the + // increment startpoint, and how many steps from there leading to the duplicate + if let Some((n, hir::Variant { span, ident, .. })) = + vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some()) + { + let ve_ident = var.ident; + let n = n + 1; + let sp = if n > 1 { "variants" } else { "variant" }; + + err.span_label( + *span, + format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"), + ); + } + + (vs[idx].span, format!("`{dis}`")) + } + }; + + err.span_label(span, format!("{display_discr} assigned here")); + }; + + // Here we loop through the discriminants, comparing each discriminant to another. + // When a duplicate is detected, we instantiate an error and point to both + // initial and duplicate value. The duplicate discriminant is then discarded by swapping + // it with the last element and decrementing the `vec.len` (which is why we have to evaluate + // `discrs.len()` anew every iteration, and why this could be tricky to do in a functional + // style as we are mutating `discrs` on the fly). + let mut i = 0; + while i < discrs.len() { + let hir_var_i_idx = discrs[i].0.index(); + let mut error: Option> = None; + + let mut o = i + 1; + while o < discrs.len() { + let hir_var_o_idx = discrs[o].0.index(); + + if discrs[i].1.val == discrs[o].1.val { + let err = error.get_or_insert_with(|| { + let mut ret = struct_span_err!( + tcx.sess, + self_span, + E0081, + "discriminant value `{}` assigned more than once", + discrs[i].1, + ); + + report(discrs[i].1, hir_var_i_idx, &mut ret); + + ret + }); + + report(discrs[o].1, hir_var_o_idx, err); + + // Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty + discrs[o] = *discrs.last().unwrap(); + discrs.pop(); + } else { + o += 1; + } } - } - format!("`{dis}`") + if let Some(mut e) = error { + e.emit(); + } + + i += 1; + } } pub(super) fn check_type_params_are_used<'tcx>( diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index fee872155..9b943b160 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -4,6 +4,7 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use crate::astconv::AstConv; use crate::rustc_middle::ty::subst::Subst; +use hir::def::DefKind; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; @@ -29,7 +30,12 @@ struct ExpectedSig<'tcx> { } struct ClosureSignatures<'tcx> { + /// The signature users of the closure see. 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 + /// explicit hidden types written by the user in the closure signature. liberated_sig: ty::FnSig<'tcx>, } @@ -58,7 +64,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_closure(expr, expected_kind, decl, body, gen, expected_sig) } - #[instrument(skip(self, expr, body, decl), level = "debug")] + #[instrument(skip(self, expr, body, decl), level = "debug", ret)] fn check_closure( &self, expr: &hir::Expr<'_>, @@ -158,11 +164,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs); - - debug!(?expr.hir_id, ?closure_type); - - closure_type + self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs) } /// Given the expected type, figures out what it can about this closure we @@ -262,7 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// The `cause_span` should be the span that caused us to /// have this expected signature, or `None` if we can't readily /// know that. - #[instrument(level = "debug", skip(self, cause_span))] + #[instrument(level = "debug", skip(self, cause_span), ret)] fn deduce_sig_from_projection( &self, cause_span: Option, @@ -317,7 +319,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Unsafety::Normal, Abi::Rust, )); - debug!(?sig); Some(ExpectedSig { cause_span, sig }) } @@ -448,18 +449,16 @@ 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.check_supplied_sig_against_expectation( + match self.merge_supplied_sig_with_expectation( hir_id, expr_def_id, decl, body, - &closure_sigs, + closure_sigs, ) { Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), - Err(_) => return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), + Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), } - - closure_sigs } fn sig_of_closure_with_mismatched_number_of_arguments( @@ -501,21 +500,22 @@ 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. - fn check_supplied_sig_against_expectation( + #[instrument(level = "debug", skip(self, hir_id, expr_def_id, decl, body, expected_sigs))] + fn merge_supplied_sig_with_expectation( &self, hir_id: hir::HirId, expr_def_id: DefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, - expected_sigs: &ClosureSignatures<'tcx>, - ) -> InferResult<'tcx, ()> { + mut expected_sigs: ClosureSignatures<'tcx>, + ) -> InferResult<'tcx, ClosureSignatures<'tcx>> { // Get the signature S that the user gave. // // (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); - debug!("check_supplied_sig_against_expectation: supplied_sig={:?}", supplied_sig); + debug!(?supplied_sig); // FIXME(#45727): As discussed in [this comment][c1], naively // forcing equality here actually results in suboptimal error @@ -533,23 +533,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 self.commit_if_ok(|_| { let mut all_obligations = vec![]; + let inputs: Vec<_> = iter::zip( + decl.inputs, + supplied_sig.inputs().skip_binder(), // binder moved to (*) below + ) + .map(|(hir_ty, &supplied_ty)| { + // Instantiate (this part of..) S to S', i.e., with fresh variables. + self.replace_bound_vars_with_fresh_vars( + hir_ty.span, + LateBoundRegionConversionTime::FnCall, + // (*) binder moved to here + supplied_sig.inputs().rebind(supplied_ty), + ) + }) + .collect(); // The liberated version of this signature should be a subtype // of the liberated form of the expectation. for ((hir_ty, &supplied_ty), expected_ty) in iter::zip( - iter::zip( - decl.inputs, - supplied_sig.inputs().skip_binder(), // binder moved to (*) below - ), + iter::zip(decl.inputs, &inputs), expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. ) { - // Instantiate (this part of..) S to S', i.e., with fresh variables. - let supplied_ty = self.replace_bound_vars_with_fresh_vars( - hir_ty.span, - LateBoundRegionConversionTime::FnCall, - supplied_sig.inputs().rebind(supplied_ty), - ); // recreated from (*) above - // Check that E' = S'. let cause = self.misc(hir_ty.span); let InferOk { value: (), obligations } = @@ -568,7 +572,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; all_obligations.extend(obligations); - Ok(InferOk { value: (), obligations: all_obligations }) + let inputs = inputs.into_iter().map(|ty| self.resolve_vars_if_possible(ty)); + + expected_sigs.liberated_sig = self.tcx.mk_fn_sig( + inputs, + supplied_output_ty, + expected_sigs.liberated_sig.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + ); + + Ok(InferOk { value: expected_sigs, obligations: all_obligations }) }) } @@ -576,7 +590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// types that the user gave into a signature. /// /// Also, record this closure signature for later. - #[instrument(skip(self, decl, body), level = "debug")] + #[instrument(skip(self, decl, body), level = "debug", ret)] fn supplied_sig_of_closure( &self, hir_id: hir::HirId, @@ -629,8 +643,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_vars, ); - debug!(?result); - let c_result = self.inh.infcx.canonicalize_response(result); self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); @@ -643,7 +655,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// user specified. The "desugared" return type is an `impl /// Future`, so we do this by searching through the /// obligations to extract the `T`. - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn deduce_future_output_from_obligations( &self, expr_def_id: DefId, @@ -687,9 +699,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|e| e.map_bound(|e| *e).transpose_tuple2()) .find_map(|(p, s)| get_future_output(p.subst(self.tcx, substs), s.0))?, ty::Error(_) => return None, + ty::Projection(proj) + if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder => + { + self.tcx + .bound_explicit_item_bounds(proj.item_def_id) + .transpose_iter() + .map(|e| e.map_bound(|e| *e).transpose_tuple2()) + .find_map(|(p, s)| get_future_output(p.subst(self.tcx, proj.substs), s.0))? + } _ => span_bug!( self.tcx.def_span(expr_def_id), - "async fn generator return type not an inference variable" + "async fn generator return type not an inference variable: {ret_ty}" ), }; @@ -704,7 +725,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.register_predicates(obligations); - debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty); Some(output_ty) } diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 2ed5f569b..def592c46 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -38,10 +38,12 @@ use crate::astconv::AstConv; use crate::check::FnCtxt; use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::Expr; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt}; @@ -87,6 +89,19 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { type CoerceResult<'tcx> = InferResult<'tcx, (Vec>, Ty<'tcx>)>; +struct CollectRetsVisitor<'tcx> { + ret_exprs: Vec<&'tcx hir::Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Ret(_) = expr.kind { + self.ret_exprs.push(expr); + } + intravisit::walk_expr(self, expr); + } +} + /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. fn coerce_mutbls<'tcx>( @@ -1464,23 +1479,29 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } Err(coercion_error) => { + // 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(); 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" // since it is implied by the syntax. // (Note: not all force-units work this way.)" - (expression_ty, self.final_ty.unwrap_or(self.expected_ty)) + (expression_ty, self.merged_ty()) } else { // Otherwise, the "expected" type for error // reporting is the current unification type, // which is basically the LUB of the expressions // we've seen so far (combined with the expected // type) - (self.final_ty.unwrap_or(self.expected_ty), expression_ty) + (self.merged_ty(), expression_ty) }; + let (expected, found) = fcx.resolve_vars_if_possible((expected, found)); let mut err; let mut unsized_return = false; + let mut visitor = CollectRetsVisitor { ret_exprs: vec![] }; match *cause.code() { ObligationCauseCode::ReturnNoExpression => { err = struct_span_err!( @@ -1506,6 +1527,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_unsized(fcx, blk_id); } + if let Some(expression) = expression + && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { + intravisit::walk_block(& mut visitor, loop_blk); + } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1551,12 +1576,47 @@ 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); self.final_ty = Some(fcx.tcx.ty_error()); } } } + fn note_unreachable_loop_return( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, + ) { + let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;}; + let mut span: MultiSpan = vec![loop_span].into(); + span.push_span_label(loop_span, "this might have zero elements to iterate on"); + const MAXITER: usize = 3; + let iter = ret_exprs.iter().take(MAXITER); + for ret_expr in iter { + span.push_span_label( + ret_expr.span, + "if the loop doesn't execute, this value would never get returned", + ); + } + err.span_note( + span, + "the function expects a value to always be returned, but loops might run zero times", + ); + if MAXITER < ret_exprs.len() { + err.note(&format!( + "if the loop doesn't execute, {} other values would never get returned", + ret_exprs.len() - MAXITER + )); + } + err.help( + "return a value for the case when the loop has zero elements to iterate on, or \ + consider changing the return type to account for that possibility", + ); + } fn report_return_mismatched_types<'a>( &self, @@ -1648,9 +1708,30 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ); } - if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) { + let ret_coercion_span = fcx.ret_coercion_span.get(); + + if let Some(sp) = ret_coercion_span + // If the closure has an explicit return type annotation, or if + // the closure's return type has been inferred from outside + // requirements (such as an Fn* trait bound), then a type error + // 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 + { + err.span_note( + sp, + &format!( + "return type inferred to be `{}` here", + expected + ), + ); + } + + if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) { self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); } + err } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 666498403..59d591acd 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,23 +1,26 @@ use super::potentially_plural_count; -use crate::check::regionck::OutlivesEnvironmentExt; -use crate::check::wfcheck; use crate::errors::LifetimesOrBoundsMismatchOnTrait; -use rustc_data_structures::fx::FxHashSet; +use hir::def_id::DefId; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::util::ExplicitSelf; -use rustc_middle::ty::{self, DefIdTree}; +use rustc_middle::ty::{ + self, AssocItem, DefIdTree, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, +}; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, }; @@ -71,11 +74,78 @@ pub(crate) fn compare_impl_method<'tcx>( } } +/// This function is best explained by example. Consider a trait: +/// +/// trait Trait<'t, T> { +/// // `trait_m` +/// fn method<'a, M>(t: &'t T, m: &'a M) -> Self; +/// } +/// +/// And an impl: +/// +/// impl<'i, 'j, U> Trait<'j, &'i U> for Foo { +/// // `impl_m` +/// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo; +/// } +/// +/// We wish to decide if those two method types are compatible. +/// For this we have to show that, assuming the bounds of the impl hold, the +/// bounds of `trait_m` imply the bounds of `impl_m`. +/// +/// We start out with `trait_to_impl_substs`, that maps the trait +/// type parameters to impl type parameters. This is taken from the +/// impl trait reference: +/// +/// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo} +/// +/// We create a mapping `dummy_substs` that maps from the impl type +/// parameters to fresh types and regions. For type parameters, +/// this is the identity transform, but we could as well use any +/// placeholder types. For regions, we convert from bound to free +/// regions (Note: but only early-bound regions, i.e., those +/// declared on the impl or used in type parameter bounds). +/// +/// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 } +/// +/// Now we can apply `placeholder_substs` to the type of the impl method +/// to yield a new function type in terms of our fresh, placeholder +/// types: +/// +/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo +/// +/// We now want to extract and substitute the type of the *trait* +/// method and compare it. To do so, we must create a compound +/// substitution by combining `trait_to_impl_substs` and +/// `impl_to_placeholder_substs`, and also adding a mapping for the method +/// type parameters. We extend the mapping to also include +/// the method parameters. +/// +/// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 } +/// +/// Applying this to the trait method type yields: +/// +/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo +/// +/// This type is also the same but the name of the bound region (`'a` +/// vs `'b`). However, the normal subtyping rules on fn types handle +/// this kind of equivalency just fine. +/// +/// We now use these substitutions to ensure that all declared bounds are +/// satisfied by the implementation's method. +/// +/// We do this by creating a parameter environment which contains a +/// substitution corresponding to `impl_to_placeholder_substs`. We then build +/// `trait_to_placeholder_substs` and use it to convert the predicates contained +/// in the `trait_m` generics to the placeholder form. +/// +/// Finally we register each of these predicates as an obligation and check that +/// they hold. +#[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))] fn compare_predicate_entailment<'tcx>( tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, + impl_m: &AssocItem, impl_m_span: Span, - trait_m: &ty::AssocItem, + trait_m: &AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { let trait_to_impl_substs = impl_trait_ref.substs; @@ -97,69 +167,6 @@ fn compare_predicate_entailment<'tcx>( }, ); - // This code is best explained by example. Consider a trait: - // - // trait Trait<'t, T> { - // fn method<'a, M>(t: &'t T, m: &'a M) -> Self; - // } - // - // And an impl: - // - // impl<'i, 'j, U> Trait<'j, &'i U> for Foo { - // fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo; - // } - // - // We wish to decide if those two method types are compatible. - // - // We start out with trait_to_impl_substs, that maps the trait - // type parameters to impl type parameters. This is taken from the - // impl trait reference: - // - // trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo} - // - // We create a mapping `dummy_substs` that maps from the impl type - // parameters to fresh types and regions. For type parameters, - // this is the identity transform, but we could as well use any - // placeholder types. For regions, we convert from bound to free - // regions (Note: but only early-bound regions, i.e., those - // declared on the impl or used in type parameter bounds). - // - // impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 } - // - // Now we can apply placeholder_substs to the type of the impl method - // to yield a new function type in terms of our fresh, placeholder - // types: - // - // <'b> fn(t: &'i0 U0, m: &'b) -> Foo - // - // We now want to extract and substitute the type of the *trait* - // method and compare it. To do so, we must create a compound - // substitution by combining trait_to_impl_substs and - // impl_to_placeholder_substs, and also adding a mapping for the method - // type parameters. We extend the mapping to also include - // the method parameters. - // - // trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 } - // - // Applying this to the trait method type yields: - // - // <'a> fn(t: &'i0 U0, m: &'a) -> Foo - // - // This type is also the same but the name of the bound region ('a - // vs 'b). However, the normal subtyping rules on fn types handle - // this kind of equivalency just fine. - // - // We now use these substitutions to ensure that all declared bounds are - // satisfied by the implementation's method. - // - // We do this by creating a parameter environment which contains a - // substitution corresponding to impl_to_placeholder_substs. We then build - // trait_to_placeholder_substs and use it to convert the predicates contained - // in the trait_m.generics to the placeholder form. - // - // Finally we register each of these predicates as an obligation in - // a fresh FulfillmentCtxt, and invoke select_all_or_error. - // Create mapping from impl to placeholder. let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id); @@ -264,8 +271,14 @@ fn compare_predicate_entailment<'tcx>( let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); + + // Next, add all inputs and output as well-formed tys. Importantly, + // we have to do this before normalization, since the normalized ty may + // not contain the input parameters. See issue #87748. + wf_tys.extend(trait_sig.inputs_and_output.iter()); let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig); - // Add the resulting inputs and output as well-formed. + // We also have to add the normalized trait signature + // as we don't normalize during implied bounds computation. wf_tys.extend(trait_sig.inputs_and_output.iter()); let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); @@ -277,16 +290,30 @@ fn compare_predicate_entailment<'tcx>( // type would be more appropriate. In other places we have a `Vec` // corresponding to their `Vec`, but we don't have that here. // Fixing this would improve the output of test `issue-83765.rs`. - let sub_result = infcx + let mut result = infcx .at(&cause, param_env) .sup(trait_fty, impl_fty) .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)); - if let Err(terr) = sub_result { + // HACK(RPITIT): #101614. When we are trying to infer the hidden types for + // RPITITs, we need to equate the output tys instead of just subtyping. If + // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes + // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets + // fixed up to `ReEmpty`, and which is certainly not what we want. + if trait_fty.has_infer_types() { + result = result.and_then(|()| { + infcx + .at(&cause, param_env) + .eq(trait_sig.output(), impl_sig.output()) + .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)) + }); + } + + if let Err(terr) = result { debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); let (impl_err_span, trait_err_span) = - extract_spans_for_error_reporting(&infcx, &terr, &cause, impl_m, trait_m); + extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); cause.span = impl_err_span; @@ -376,7 +403,7 @@ fn compare_predicate_entailment<'tcx>( expected: trait_fty.into(), found: impl_fty.into(), })), - &terr, + terr, false, false, ); @@ -394,8 +421,11 @@ fn compare_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, wf_tys, impl_m_hir_id); + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(infcx), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + ); infcx.check_region_obligations_and_report_errors( impl_m.def_id.expect_local(), &outlives_environment, @@ -405,6 +435,227 @@ fn compare_predicate_entailment<'tcx>( }) } +pub fn collect_trait_impl_trait_tys<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> Result<&'tcx FxHashMap>, ErrorGuaranteed> { + let impl_m = tcx.opt_associated_item(def_id).unwrap(); + let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap(); + let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap(); + let param_env = tcx.param_env(def_id); + + let trait_to_impl_substs = impl_trait_ref.substs; + + let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); + let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span(); + let cause = ObligationCause::new( + return_span, + impl_m_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_m.def_id.expect_local(), + trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, + }, + ); + + // Create mapping from impl to placeholder. + let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id); + + // Create mapping from trait to placeholder. + let trait_to_placeholder_substs = + impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs); + + tcx.infer_ctxt().enter(|ref infcx| { + let ocx = ObligationCtxt::new(infcx); + + let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id); + let impl_return_ty = ocx.normalize( + norm_cause.clone(), + param_env, + infcx + .replace_bound_vars_with_fresh_vars( + return_span, + infer::HigherRankedType, + tcx.fn_sig(impl_m.def_id), + ) + .output(), + ); + + let mut collector = + ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id); + let unnormalized_trait_return_ty = tcx + .liberate_late_bound_regions( + impl_m.def_id, + tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs), + ) + .output() + .fold_with(&mut collector); + let trait_return_ty = + ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_return_ty); + + let wf_tys = FxHashSet::from_iter([unnormalized_trait_return_ty, trait_return_ty]); + + match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) { + Ok(infer::InferOk { value: (), obligations }) => { + ocx.register_obligations(obligations); + } + Err(terr) => { + let mut diag = struct_span_err!( + tcx.sess, + cause.span(), + E0053, + "method `{}` has an incompatible return type for trait", + trait_m.name + ); + let hir = tcx.hir(); + infcx.note_type_err( + &mut diag, + &cause, + hir.get_if_local(impl_m.def_id) + .and_then(|node| node.fn_decl()) + .map(|decl| (decl.output.span(), "return type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_return_ty.into(), + found: impl_return_ty.into(), + })), + terr, + false, + false, + ); + return Err(diag.emit()); + } + } + + // Check that all obligations are satisfied by the implementation's + // RPITs. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let reported = infcx.report_fulfillment_errors(&errors, None, false); + return Err(reported); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(infcx), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + ); + infcx.check_region_obligations_and_report_errors( + impl_m.def_id.expect_local(), + &outlives_environment, + ); + + let mut collected_tys = FxHashMap::default(); + for (def_id, (ty, substs)) in collector.types { + match infcx.fully_resolve(ty) { + Ok(ty) => { + // `ty` contains free regions that we created earlier while liberating the + // trait fn signature. However, projection normalization expects `ty` to + // contains `def_id`'s early-bound regions. + let id_substs = InternalSubsts::identity_for_item(tcx, def_id); + debug!(?id_substs, ?substs); + let map: FxHashMap, ty::GenericArg<'tcx>> = substs + .iter() + .enumerate() + .map(|(index, arg)| (arg, id_substs[index])) + .collect(); + debug!(?map); + + let ty = tcx.fold_regions(ty, |region, _| { + if let ty::ReFree(_) = region.kind() { + map[®ion.into()].expect_region() + } else { + region + } + }); + debug!(%ty); + collected_tys.insert(def_id, ty); + } + Err(err) => { + tcx.sess.delay_span_bug( + return_span, + format!("could not fully resolve: {ty} => {err:?}"), + ); + collected_tys.insert(def_id, tcx.ty_error()); + } + } + } + + Ok(&*tcx.arena.alloc(collected_tys)) + }) +} + +struct ImplTraitInTraitCollector<'a, 'tcx> { + ocx: &'a ObligationCtxt<'a, 'tcx>, + types: FxHashMap, ty::SubstsRef<'tcx>)>, + span: Span, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, +} + +impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> { + fn new( + ocx: &'a ObligationCtxt<'a, 'tcx>, + span: Span, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ) -> Self { + ImplTraitInTraitCollector { ocx, types: FxHashMap::default(), span, param_env, body_id } + } +} + +impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.ocx.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Projection(proj) = ty.kind() + && self.tcx().def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder + { + if let Some((ty, _)) = self.types.get(&proj.item_def_id) { + return *ty; + } + //FIXME(RPITIT): Deny nested RPITIT in substs too + if proj.substs.has_escaping_bound_vars() { + bug!("FIXME(RPITIT): error here"); + } + // Replace with infer var + let infer_ty = self.ocx.infcx.next_ty_var(TypeVariableOrigin { + span: self.span, + kind: TypeVariableOriginKind::MiscVariable, + }); + self.types.insert(proj.item_def_id, (infer_ty, proj.substs)); + // Recurse into bounds + for pred in self.tcx().bound_explicit_item_bounds(proj.item_def_id).transpose_iter() { + let pred_span = pred.0.1; + + let pred = pred.map_bound(|(pred, _)| *pred).subst(self.tcx(), proj.substs); + let pred = pred.fold_with(self); + let pred = self.ocx.normalize( + ObligationCause::misc(self.span, self.body_id), + self.param_env, + pred, + ); + + self.ocx.register_obligation(traits::Obligation::new( + ObligationCause::new( + self.span, + self.body_id, + ObligationCauseCode::BindingObligation(proj.item_def_id, pred_span), + ), + self.param_env, + pred, + )); + } + infer_ty + } else { + ty.super_fold_with(self) + } + } +} + fn check_region_bounds_on_impl_item<'tcx>( tcx: TyCtxt<'tcx>, impl_m: &ty::AssocItem, @@ -463,7 +714,7 @@ fn check_region_bounds_on_impl_item<'tcx>( #[instrument(level = "debug", skip(infcx))] fn extract_spans_for_error_reporting<'a, 'tcx>( infcx: &infer::InferCtxt<'a, 'tcx>, - terr: &TypeError<'_>, + terr: TypeError<'_>, cause: &ObligationCause<'tcx>, impl_m: &ty::AssocItem, trait_m: &ty::AssocItem, @@ -483,7 +734,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), }); - match *terr { + match terr { TypeError::ArgumentMutability(i) => { (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) } @@ -848,8 +1099,7 @@ fn compare_synthetic_generics<'tcx>( { if impl_synthetic != trait_synthetic { let impl_def_id = impl_def_id.expect_local(); - let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id); - let impl_span = tcx.hir().span(impl_hir_id); + let impl_span = tcx.def_span(impl_def_id); let trait_span = tcx.def_span(trait_def_id); let mut err = struct_span_err!( tcx.sess, @@ -868,17 +1118,16 @@ fn compare_synthetic_generics<'tcx>( // try taking the name from the trait impl // FIXME: this is obviously suboptimal since the name can already be used // as another generic argument - let new_name = tcx.sess.source_map().span_to_snippet(trait_span).ok()?; + let new_name = tcx.opt_item_name(trait_def_id)?; let trait_m = trait_m.def_id.as_local()?; - let trait_m = tcx.hir().trait_item(hir::TraitItemId { def_id: trait_m }); + let trait_m = tcx.hir().expect_trait_item(trait_m); let impl_m = impl_m.def_id.as_local()?; - let impl_m = tcx.hir().impl_item(hir::ImplItemId { def_id: impl_m }); + let impl_m = tcx.hir().expect_impl_item(impl_m); // in case there are no generics, take the spot between the function name // and the opening paren of the argument list - let new_generics_span = - tcx.sess.source_map().generate_fn_name_span(impl_span)?.shrink_to_hi(); + let new_generics_span = tcx.def_ident_span(impl_def_id)?.shrink_to_hi(); // in case there are generics, just replace them let generics_span = impl_m.generics.span.substitute_dummy(new_generics_span); @@ -890,7 +1139,7 @@ fn compare_synthetic_generics<'tcx>( "try changing the `impl Trait` argument to a generic parameter", vec![ // replace `impl Trait` with `T` - (impl_span, new_name), + (impl_span, new_name.to_string()), // replace impl method generics with trait method generics // This isn't quite right, as users might have changed the names // of the generics, but it works for the common case @@ -907,7 +1156,7 @@ fn compare_synthetic_generics<'tcx>( err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); (|| { let impl_m = impl_m.def_id.as_local()?; - let impl_m = tcx.hir().impl_item(hir::ImplItemId { def_id: impl_m }); + let impl_m = tcx.hir().expect_impl_item(impl_m); let input_tys = match impl_m.kind { hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, _ => unreachable!(), @@ -1138,7 +1387,7 @@ pub(crate) fn compare_const_impl<'tcx>( expected: trait_ty.into(), found: impl_ty.into(), })), - &terr, + terr, false, false, ); @@ -1300,7 +1549,7 @@ fn compare_type_predicate_entailment<'tcx>( /// For default associated types the normalization is not possible (the value /// from the impl could be overridden). We also can't normalize generic /// associated types (yet) because they contain bound parameters. -#[tracing::instrument(level = "debug", skip(tcx))] +#[instrument(level = "debug", skip(tcx))] pub fn check_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, trait_ty: &ty::AssocItem, @@ -1440,14 +1689,17 @@ pub fn check_type_bounds<'tcx>( }; debug!(?normalize_param_env); + let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id); let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); tcx.infer_ctxt().enter(move |infcx| { let ocx = ObligationCtxt::new(&infcx); + let assumed_wf_types = + ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local()); + let mut selcx = traits::SelectionContext::new(&infcx); - let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); let normalize_cause = ObligationCause::new( impl_ty_span, impl_ty_hir_id, @@ -1458,7 +1710,7 @@ pub fn check_type_bounds<'tcx>( ); let mk_cause = |span: Span| { let code = if span.is_dummy() { - traits::MiscObligation + traits::ItemObligation(trait_ty.def_id) } else { traits::BindingObligation(trait_ty.def_id, span) }; @@ -1503,17 +1755,10 @@ pub fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let implied_bounds = match impl_ty.container { - ty::TraitContainer => FxHashSet::default(), - ty::ImplContainer => wfcheck::impl_implied_bounds( - tcx, - param_env, - container_id.expect_local(), - impl_ty_span, - ), - }; - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, implied_bounds, impl_ty_hir_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds); + infcx.check_region_obligations_and_report_errors( impl_ty.def_id.expect_local(), &outlives_environment, diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 4de48dc5b..e1d55ff82 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -42,10 +42,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); self.suggest_missing_parentheses(err, expr); self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected); + self.suggest_copied_or_cloned(err, expr, expr_ty, 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); - self.report_closure_inferred_return_type(err, expected); } // Requires that the two types unify, and prints an error message if @@ -131,7 +131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!` /// will be permitted if the diverges flag is currently "always". - #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] + #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, expr: &hir::Expr<'tcx>, @@ -375,7 +375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_is_local = sole_field.did.is_local(); let field_is_accessible = - sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx) + sole_field.vis.is_accessible_from(expr.hir_id.owner, self.tcx) // Skip suggestions for unstable public fields (for example `Pin::pointer`) && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked); @@ -590,7 +590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let closure_params_len = closure_fn_decl.inputs.len(); let ( Some(Node::Expr(hir::Expr { - kind: hir::ExprKind::MethodCall(method_path, method_expr, _), + kind: hir::ExprKind::MethodCall(method_path, receiver, ..), .. })), 1, @@ -598,14 +598,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let self_ty = self.typeck_results.borrow().expr_ty(&method_expr[0]); - let self_ty = format!("{:?}", self_ty); + let self_ty = self.typeck_results.borrow().expr_ty(receiver); let name = method_path.ident.name; - let is_as_ref_able = (self_ty.starts_with("&std::option::Option") - || self_ty.starts_with("&std::result::Result") - || self_ty.starts_with("std::option::Option") - || self_ty.starts_with("std::result::Result")) - && (name == sym::map || name == sym::and_then); + let is_as_ref_able = match self_ty.peel_refs().kind() { + ty::Adt(def, _) => { + (self.tcx.is_diagnostic_item(sym::Option, def.did()) + || self.tcx.is_diagnostic_item(sym::Result, def.did())) + && (name == sym::map || name == sym::and_then) + } + _ => false, + }; match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) { (true, Ok(src)) => { let suggestion = format!("as_ref().{}", src); @@ -637,11 +639,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }?; match hir.find(hir.get_parent_node(expr.hir_id))? { - Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => { - for field in *fields { - if field.ident.name == local.name && field.is_shorthand { - return Some(local.name); - } + Node::ExprField(field) => { + if field.ident.name == local.name && field.is_shorthand { + return Some(local.name); } } _ => {} @@ -767,22 +767,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self.can_coerce(ref_ty, expected) { let mut sugg_sp = sp; - if let hir::ExprKind::MethodCall(ref segment, ref args, _) = expr.kind { + if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind { let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span)); - if let ([arg], Some(true), sym::clone) = ( - &args[..], - self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( + if args.is_empty() + && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( |did| { let ai = self.tcx.associated_item(did); ai.trait_container(self.tcx) == Some(clone_trait) }, - ), - segment.ident.name, - ) { + ) == Some(true) + && segment.ident.name == sym::clone + { // If this expression had a clone call when suggesting borrowing // we want to suggest removing it because it'd now be unnecessary. - sugg_sp = arg.span; + sugg_sp = receiver.span; } } if let Ok(src) = sm.span_to_snippet(sugg_sp) { @@ -793,7 +792,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ if is_range_literal(expr) => true, _ => false, }; - let sugg_expr = if needs_parens { format!("({src})") } else { src }; if let Some(sugg) = self.can_use_as_ref(expr) { return Some(( @@ -821,6 +819,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let sugg_expr = if needs_parens { format!("({src})") } else { src }; return Some(match mutability { hir::Mutability::Mut => ( sp, @@ -1072,21 +1071,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut sugg = vec![]; - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Struct(_, fields, _), .. - })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) + if let Some(hir::Node::ExprField(field)) = + self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { // `expr` is a literal field for a struct, only suggest if appropriate - match (*fields) - .iter() - .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand) - { + if field.is_shorthand { // This is a field literal - Some(field) => { - sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); - } + sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); + } else { // Likely a field was meant, but this field wasn't found. Do not suggest anything. - None => return false, + return false; } }; @@ -1418,25 +1412,4 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => false, } } - - // Report the type inferred by the return statement. - fn report_closure_inferred_return_type(&self, err: &mut Diagnostic, expected: Ty<'tcx>) { - if let Some(sp) = self.ret_coercion_span.get() - // If the closure has an explicit return type annotation, or if - // the closure's return type has been inferred from outside - // requirements (such as an Fn* trait bound), then a type error - // 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. - && !self.return_type_pre_known - { - err.span_note( - sp, - &format!( - "return type inferred to be `{}` here", - self.resolve_vars_if_possible(expected) - ), - ); - } - } } diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 321064ec0..ab143c059 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -144,6 +144,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); let assumptions_in_impl_context = assumptions_in_impl_context.predicates; + debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates); + let self_param_env = tcx.param_env(self_type_did); // An earlier version of this code attempted to do this checking diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 6e97b0bf2..93b008500 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -3,32 +3,28 @@ //! See `mod.rs` for more context on type checking in general. use crate::astconv::AstConv as _; -use crate::check::cast; +use crate::check::cast::{self, CastCheckResult}; use crate::check::coercion::CoerceMany; use crate::check::fatally_break_rust; use crate::check::method::SelfSource; -use crate::check::report_unexpected_variant_res; -use crate::check::BreakableCtxt; -use crate::check::Diverges; -use crate::check::DynamicCoerceMany; use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::check::FnCtxt; -use crate::check::Needs; -use crate::check::TupleArgumentsFlag::DontTupleArguments; +use crate::check::{ + report_unexpected_variant_res, BreakableCtxt, Diverges, DynamicCoerceMany, FnCtxt, Needs, + TupleArgumentsFlag::DontTupleArguments, +}; use crate::errors::{ FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, YieldExprOutsideOfGenerator, }; use crate::type_error_struct; -use super::suggest_call_constructor; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, - EmissionGuarantee, ErrorGuaranteed, + ErrorGuaranteed, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -44,13 +40,12 @@ use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::TypeError::FieldMisMatch; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable}; +use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable}; use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Pos}; use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; @@ -326,8 +321,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected), - ExprKind::MethodCall(segment, args, _) => { - self.check_method_call(expr, segment, args, expected) + ExprKind::MethodCall(segment, receiver, args, _) => { + self.check_method_call(expr, segment, receiver, args, expected) } ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), ExprKind::Type(e, t) => { @@ -880,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, op_span: Span, - adjust_err: impl FnOnce(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>), + adjust_err: impl FnOnce(&mut Diagnostic), ) { if lhs.is_syntactic_place_expr() { return; @@ -1002,7 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); - let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected); + let opt_suggest_box_span = self.opt_suggest_box_span(then_ty, else_ty, orig_expected); let if_cause = self.if_cause( sp, cond_expr.span, @@ -1090,8 +1085,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - let suggest_deref_binop = |err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, - rhs_ty: Ty<'tcx>| { + let suggest_deref_binop = |err: &mut Diagnostic, rhs_ty: Ty<'tcx>| { if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { // Can only assign if the type is sized, so if `DerefMut` yields a type that is // unsized, do not suggest dereferencing it. @@ -1198,13 +1192,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &'tcx hir::Expr<'tcx>, segment: &hir::PathSegment<'_>, + rcvr: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let rcvr = &args[0]; let rcvr_t = self.check_expr(&rcvr); // no need to check for bot/err -- callee does that - let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); + let rcvr_t = self.structurally_resolved_type(rcvr.span, rcvr_t); let span = segment.ident.span; let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) { @@ -1221,9 +1215,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_t, segment.ident, - SelfSource::MethodCall(&args[0]), + SelfSource::MethodCall(rcvr), error, - Some(args), + Some((rcvr, args)), ) { err.emit(); } @@ -1233,14 +1227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Call the generic checker. - self.check_method_argument_types( - span, - expr, - method, - &args[1..], - DontTupleArguments, - expected, - ) + self.check_method_argument_types(span, expr, method, &args, DontTupleArguments, expected) } fn check_expr_cast( @@ -1262,8 +1249,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { - Ok(cast_check) => { + match cast::check_cast(self, e, t_expr, t_cast, t.span, expr.span) { + CastCheckResult::Ok => t_cast, + CastCheckResult::Deferred(cast_check) => { debug!( "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", t_cast, t_expr, cast_check, @@ -1271,7 +1259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deferred_cast_checks.push(cast_check); t_cast } - Err(_) => self.tcx.ty_error(), + CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(), } } } @@ -1309,7 +1297,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: expr.span, }) }; - self.tcx.mk_array(element_ty, args.len() as u64) + let array_len = args.len() as u64; + self.suggest_array_len(expr, array_len); + self.tcx.mk_array(element_ty, array_len) + } + + fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) { + let parent_node = self.tcx.hir().parent_iter(expr.hir_id).find(|(_, node)| { + !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. })) + }); + let Some((_, + hir::Node::Local(hir::Local { ty: Some(ty), .. }) + | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) + ) = parent_node else { + return + }; + if let hir::TyKind::Array(_, length) = ty.peel_refs().kind + && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length + && let Some(span) = self.tcx.hir().opt_span(hir_id) + { + match self.tcx.sess.diagnostic().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) { + Some(mut err) => { + err.span_suggestion( + span, + "consider specifying the array length", + array_len, + Applicability::MaybeIncorrect, + ); + err.emit(); + } + None => () + } + } } fn check_expr_const_block( @@ -1335,10 +1354,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { element: &'tcx hir::Expr<'tcx>, count: &'tcx hir::ArrayLen, expected: Expectation<'tcx>, - _expr: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let count = self.array_length_to_const(count); + if let Some(count) = count.try_eval_usize(tcx, self.param_env) { + self.suggest_array_len(expr, count); + } let uty = match expected { ExpectHasType(uty) => match *uty.kind() { @@ -1518,7 +1540,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut error_happened = false; // Type-check each field. - for field in ast_fields { + for (idx, field) in ast_fields.iter().enumerate() { let ident = tcx.adjust_ident(field.ident, variant.def_id); let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { seen_fields.insert(ident, field.span); @@ -1556,7 +1578,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. - self.check_expr_coercable_to_type(&field.expr, field_type, None); + let ty = self.check_expr_with_hint(&field.expr, field_type); + let (_, diag) = + 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); + } + diag.emit(); + } } // Make sure the programmer specified correct number of fields. @@ -1695,9 +1726,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let private_fields: Vec<&ty::FieldDef> = variant .fields .iter() - .filter(|field| { - !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) - }) + .filter(|field| !field.vis.is_accessible_from(tcx.parent_module(expr_id), tcx)) .collect(); if !private_fields.is_empty() { @@ -1784,25 +1813,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); - // If the last field is a range literal, but it isn't supposed to be, then they probably - // meant to use functional update syntax. - // + if let Some(last) = ast_fields.last() { + self.suggest_fru_from_range(last, variant, substs, &mut err); + } + + err.emit(); + } + + /// If the last field is a range literal, but it isn't supposed to be, then they probably + /// meant to use functional update syntax. + fn suggest_fru_from_range( + &self, + last_expr_field: &hir::ExprField<'tcx>, + variant: &ty::VariantDef, + substs: SubstsRef<'tcx>, + err: &mut Diagnostic, + ) { // I don't use 'is_range_literal' because only double-sided, half-open ranges count. - if let Some(( - last, - ExprKind::Struct( + if let ExprKind::Struct( QPath::LangItem(LangItem::Range, ..), &[ref range_start, ref range_end], _, - ), - )) = ast_fields.last().map(|last| (last, &last.expr.kind)) && - let variant_field = - variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) && - let range_def_id = self.tcx.lang_items().range_struct() && - variant_field - .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) - .map(|adt| adt.did()) - != range_def_id + ) = last_expr_field.expr.kind + && let variant_field = + variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident) + && let range_def_id = self.tcx.lang_items().range_struct() + && variant_field + .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) + .map(|adt| adt.did()) + != range_def_id { let instead = self .tcx @@ -1818,8 +1857,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - - err.emit(); } /// Report an error for a struct field expression when there are invisible fields. @@ -2086,15 +2123,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: Ident, ) -> Ty<'tcx> { debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); - let expr_t = self.check_expr(base); - let expr_t = self.structurally_resolved_type(base.span, expr_t); + let base_ty = self.check_expr(base); + let base_ty = self.structurally_resolved_type(base.span, base_ty); let mut private_candidate = None; - let mut autoderef = self.autoderef(expr.span, expr_t); - while let Some((base_t, _)) = autoderef.next() { - debug!("base_t: {:?}", base_t); - match base_t.kind() { + let mut autoderef = self.autoderef(expr.span, base_ty); + while let Some((deref_base_ty, _)) = autoderef.next() { + debug!("deref_base_ty: {:?}", deref_base_ty); + match deref_base_ty.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { - debug!("struct named {:?}", base_t); + debug!("struct named {:?}", deref_base_ty); let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); let fields = &base_def.non_enum_variant().fields; @@ -2142,25 +2179,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (#90483) apply adjustments to avoid ExprUseVisitor from // creating erroneous projection. self.apply_adjustments(base, adjustments); - self.ban_private_field_access(expr, expr_t, field, did); + self.ban_private_field_access(expr, base_ty, field, did); return field_ty; } if field.name == kw::Empty { - } else if self.method_exists(field, expr_t, expr.hir_id, true) { - self.ban_take_value_of_method(expr, expr_t, field); - } else if !expr_t.is_primitive_ty() { - self.ban_nonexisting_field(field, base, expr, expr_t); + } else if self.method_exists(field, base_ty, expr.hir_id, true) { + self.ban_take_value_of_method(expr, base_ty, field); + } else if !base_ty.is_primitive_ty() { + self.ban_nonexisting_field(field, base, expr, base_ty); } else { let field_name = field.to_string(); let mut err = type_error_struct!( self.tcx().sess, field.span, - expr_t, + base_ty, E0610, - "`{expr_t}` is a primitive type and therefore doesn't have fields", + "`{base_ty}` is a primitive type and therefore doesn't have fields", ); - let is_valid_suffix = |field: String| { + let is_valid_suffix = |field: &str| { if field == "f32" || field == "f64" { return true; } @@ -2185,20 +2222,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suffix = chars.collect::(); suffix.is_empty() || suffix == "f32" || suffix == "f64" }; - if let ty::Infer(ty::IntVar(_)) = expr_t.kind() + let maybe_partial_suffix = |field: &str| -> Option<&str> { + let first_chars = ['f', 'l']; + if field.len() >= 1 + && field.to_lowercase().starts_with(first_chars) + && field[1..].chars().all(|c| c.is_ascii_digit()) + { + if field.to_lowercase().starts_with(['f']) { Some("f32") } else { Some("f64") } + } else { + None + } + }; + if let ty::Infer(ty::IntVar(_)) = base_ty.kind() && let ExprKind::Lit(Spanned { node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed), .. }) = base.kind && !base.span.from_expansion() - && is_valid_suffix(field_name) { - err.span_suggestion_verbose( - field.span.shrink_to_lo(), - "If the number is meant to be a floating point number, consider adding a `0` after the period", - '0', - Applicability::MaybeIncorrect, - ); + if is_valid_suffix(&field_name) { + err.span_suggestion_verbose( + field.span.shrink_to_lo(), + "if intended to be a floating point literal, consider adding a `0` after the period", + '0', + Applicability::MaybeIncorrect, + ); + } else if let Some(correct_suffix) = maybe_partial_suffix(&field_name) { + err.span_suggestion_verbose( + field.span, + format!("if intended to be a floating point literal, consider adding a `0` after the period and a `{correct_suffix}` suffix"), + format!("0{correct_suffix}"), + Applicability::MaybeIncorrect, + ); + } } err.emit(); } @@ -2206,35 +2262,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx().ty_error() } - fn check_call_constructor( - &self, - err: &mut DiagnosticBuilder<'_, G>, - base: &'tcx hir::Expr<'tcx>, - def_id: DefId, - ) { - if let Some(local_id) = def_id.as_local() { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id); - let node = self.tcx.hir().get(hir_id); - - if let Some(fields) = node.tuple_fields() { - let kind = match self.tcx.opt_def_kind(local_id) { - Some(DefKind::Ctor(of, _)) => of, - _ => return, - }; - - suggest_call_constructor(base.span, kind, fields.len(), err); - } - } else { - // The logic here isn't smart but `associated_item_def_ids` - // doesn't work nicely on local. - if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) { - let parent_def_id = self.tcx.parent(def_id); - let fields = self.tcx.associated_item_def_ids(parent_def_id); - suggest_call_constructor(base.span, of, fields.len(), err); - } - } - } - fn suggest_await_on_field_access( &self, err: &mut Diagnostic, @@ -2277,40 +2304,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn ban_nonexisting_field( &self, - field: Ident, + ident: Ident, base: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, - expr_t: Ty<'tcx>, + base_ty: Ty<'tcx>, ) { debug!( - "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}", - field, base, expr, expr_t + "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", + ident, base, expr, base_ty ); - let mut err = self.no_such_field_err(field, expr_t, base.hir_id); + let mut err = self.no_such_field_err(ident, base_ty, base.hir_id); - match *expr_t.peel_refs().kind() { + match *base_ty.peel_refs().kind() { ty::Array(_, len) => { - self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); + self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len); } ty::RawPtr(..) => { - self.suggest_first_deref_field(&mut err, expr, base, field); + self.suggest_first_deref_field(&mut err, expr, base, ident); } ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, field, expr.span); + self.suggest_fields_on_recordish(&mut err, def, ident, expr.span); } ty::Param(param_ty) => { self.point_at_param_definition(&mut err, param_ty); } ty::Opaque(_, _) => { - self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs()); - } - ty::FnDef(def_id, _) => { - self.check_call_constructor(&mut err, base, def_id); + self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs()); } _ => {} } - if field.name == kw::Await { + self.suggest_fn_call(&mut err, base, base_ty, |output_ty| { + if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() { + def.non_enum_variant().fields.iter().any(|field| { + field.ident(self.tcx) == ident + && field.vis.is_accessible_from(expr.hir_id.owner, self.tcx) + }) + } else if let ty::Tuple(tys) = output_ty.kind() + && let Ok(idx) = ident.as_str().parse::() + { + idx < tys.len() + } else { + false + } + }); + + if ident.name == kw::Await { // We know by construction that `.await` is either on Rust 2015 // or results in `ExprKind::Await`. Suggest switching the edition to 2018. err.note("to `.await` a `Future`, switch to Rust 2018 or later"); @@ -2398,37 +2437,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr, Some(span), ); + } else if let ty::RawPtr(ty_and_mut) = expr_t.kind() + && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() + && let ExprKind::Field(base_expr, _) = expr.kind + && adt_def.variants().len() == 1 + && adt_def + .variants() + .iter() + .next() + .unwrap() + .fields + .iter() + .any(|f| f.ident(self.tcx) == field) + { + err.multipart_suggestion( + "to access the field, dereference first", + vec![ + (base_expr.span.shrink_to_lo(), "(*".to_string()), + (base_expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); } else { - let mut found = false; - - if let ty::RawPtr(ty_and_mut) = expr_t.kind() - && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() - { - if adt_def.variants().len() == 1 - && adt_def - .variants() - .iter() - .next() - .unwrap() - .fields - .iter() - .any(|f| f.ident(self.tcx) == field) - { - if let Some(dot_loc) = expr_snippet.rfind('.') { - found = true; - err.span_suggestion( - expr.span.with_hi(expr.span.lo() + BytePos::from_usize(dot_loc)), - "to access the field, dereference first", - format!("(*{})", &expr_snippet[0..dot_loc]), - Applicability::MaybeIncorrect, - ); - } - } - } - - if !found { - err.help("methods are immutable and cannot be assigned to"); - } + err.help("methods are immutable and cannot be assigned to"); } err.emit(); @@ -2535,54 +2566,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // try to add a suggestion in case the field is a nested field of a field of the Adt - if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) { - for candidate_field in fields.iter() { - if let Some(mut field_path) = self.check_for_nested_field_satisfying( - span, - &|candidate_field, _| candidate_field.ident(self.tcx()) == field, - candidate_field, - substs, - vec![], - self.tcx.parent_module(id).to_def_id(), - ) { - // field_path includes `field` that we're looking for, so pop it. + let mod_id = self.tcx.parent_module(id).to_def_id(); + if let Some((fields, substs)) = + self.get_field_candidates_considering_privacy(span, expr_t, mod_id) + { + let candidate_fields: Vec<_> = fields + .filter_map(|candidate_field| { + self.check_for_nested_field_satisfying( + span, + &|candidate_field, _| candidate_field.ident(self.tcx()) == field, + candidate_field, + substs, + vec![], + mod_id, + ) + }) + .map(|mut field_path| { field_path.pop(); - - let field_path_str = field_path + field_path .iter() .map(|id| id.name.to_ident_string()) .collect::>() - .join("."); - debug!("field_path_str: {:?}", field_path_str); + .join(".") + }) + .collect::>(); - err.span_suggestion_verbose( - field.span.shrink_to_lo(), - "one of the expressions' fields has a field of the same name", - format!("{field_path_str}."), - Applicability::MaybeIncorrect, - ); - } + let len = candidate_fields.len(); + if len > 0 { + err.span_suggestions( + field.span.shrink_to_lo(), + format!( + "{} of the expressions' fields {} a field of the same name", + if len > 1 { "some" } else { "one" }, + if len > 1 { "have" } else { "has" }, + ), + candidate_fields.iter().map(|path| format!("{path}.")), + Applicability::MaybeIncorrect, + ); } } err } - pub(crate) fn get_field_candidates( + pub(crate) fn get_field_candidates_considering_privacy( &self, span: Span, - base_t: Ty<'tcx>, - ) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> { - debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t); + base_ty: Ty<'tcx>, + mod_id: DefId, + ) -> Option<(impl Iterator + 'tcx, SubstsRef<'tcx>)> { + debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); - for (base_t, _) in self.autoderef(span, base_t) { + for (base_t, _) in self.autoderef(span, base_ty) { match base_t.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { + let tcx = self.tcx; let fields = &base_def.non_enum_variant().fields; - // For compile-time reasons put a limit on number of fields we search - if fields.len() > 100 { - return None; + // Some struct, e.g. some that impl `Deref`, have all private fields + // because you're expected to deref them to access the _real_ fields. + // This, for example, will help us suggest accessing a field through a `Box`. + if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) { + continue; } - return Some((fields, substs)); + return Some(( + fields + .iter() + .filter(move |field| field.vis.is_accessible_from(mod_id, tcx)) + // For compile-time reasons put a limit on number of fields we search + .take(100), + substs, + )); } _ => {} } @@ -2599,7 +2651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidate_field: &ty::FieldDef, subst: SubstsRef<'tcx>, mut field_path: Vec, - id: DefId, + mod_id: DefId, ) -> Option> { debug!( "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}", @@ -2611,24 +2663,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // up to a depth of three None } else { - // recursively search fields of `candidate_field` if it's a ty::Adt field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0()); let field_ty = candidate_field.ty(self.tcx, subst); - if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) { - for field in nested_fields.iter() { - if field.vis.is_accessible_from(id, self.tcx) { - if matches(candidate_field, field_ty) { - return Some(field_path); - } else if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - matches, - field, - subst, - field_path.clone(), - id, - ) { - return Some(field_path); - } + if matches(candidate_field, field_ty) { + return Some(field_path); + } else if let Some((nested_fields, subst)) = + self.get_field_candidates_considering_privacy(span, field_ty, mod_id) + { + // recursively search fields of `candidate_field` if it's a ty::Adt + for field in nested_fields { + if let Some(field_path) = self.check_for_nested_field_satisfying( + span, + matches, + field, + subst, + field_path.clone(), + mod_id, + ) { + return Some(field_path); } } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 3a8093345..a40478db9 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {}) } - #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")] + #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)] pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment( &self, mut ty: Ty<'tcx>, @@ -107,10 +107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // indirect dependencies that don't seem worth tracking // precisely. self.select_obligations_where_possible(false, mutate_fulfillment_errors); - ty = self.resolve_vars_if_possible(ty); - - debug!(?ty); - ty + self.resolve_vars_if_possible(ty) } pub(in super::super) fn record_deferred_call_resolution( @@ -412,7 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), - output_pred: None, + output_ty: None, }, ), self.param_env, @@ -498,13 +495,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> { let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); + let span = self.tcx.hir().span(ast_c.hir_id); let c = ty::Const::from_anon_const(self.tcx, const_def_id); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::WellFormed(None), - ); - c + self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); + self.normalize_associated_types_in(span, c) } pub fn const_arg_to_const( @@ -618,9 +612,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(in super::super) fn select_all_obligations_or_error(&self) { - let errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); + let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); if !errors.is_empty() { + self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); self.report_fulfillment_errors(&errors, self.inh.body_id, false); } } @@ -634,6 +629,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred); } } @@ -831,23 +827,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = item_ty.subst(self.tcx, substs); self.write_resolution(hir_id, Ok((def_kind, def_id))); - self.add_required_obligations_with_code( - span, - def_id, - &substs, - match lang_item { - hir::LangItem::IntoFutureIntoFuture => { - ObligationCauseCode::AwaitableExpr(expr_hir_id) - } - hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => { - ObligationCauseCode::ForLoopIterator - } - hir::LangItem::TryTraitFromOutput - | hir::LangItem::TryTraitFromResidual - | hir::LangItem::TryTraitBranch => ObligationCauseCode::QuestionMark, - _ => traits::ItemObligation(def_id), - }, - ); + + let code = match lang_item { + hir::LangItem::IntoFutureIntoFuture => { + Some(ObligationCauseCode::AwaitableExpr(expr_hir_id)) + } + hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => { + Some(ObligationCauseCode::ForLoopIterator) + } + hir::LangItem::TryTraitFromOutput + | hir::LangItem::TryTraitFromResidual + | hir::LangItem::TryTraitBranch => Some(ObligationCauseCode::QuestionMark), + _ => None, + }; + if let Some(code) = code { + self.add_required_obligations_with_code(span, def_id, substs, move |_, _| code.clone()); + } else { + self.add_required_obligations_for_hir(span, def_id, substs, hir_id); + } + (Res::Def(def_kind, def_id), ty) } @@ -986,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if found != self.tcx.types.unit { return; } - if let ExprKind::MethodCall(path_segment, [rcvr, ..], _) = expr.kind { + if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind { if self .typeck_results .borrow() @@ -1359,7 +1357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // First, store the "user substs" for later. self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); - self.add_required_obligations(span, def_id, &substs); + self.add_required_obligations_for_hir(span, def_id, &substs, hir_id); // Substitute the values for the type parameters into the type of // the referenced item. @@ -1396,32 +1394,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Add all the obligations that are required, substituting and normalized appropriately. - pub(crate) fn add_required_obligations( + pub(crate) fn add_required_obligations_for_hir( &self, span: Span, def_id: DefId, - substs: &SubstsRef<'tcx>, + substs: SubstsRef<'tcx>, + hir_id: hir::HirId, ) { - self.add_required_obligations_with_code( - span, - def_id, - substs, - traits::ItemObligation(def_id), - ) + self.add_required_obligations_with_code(span, def_id, substs, |idx, span| { + if span.is_dummy() { + ObligationCauseCode::ExprItemObligation(def_id, hir_id, idx) + } else { + ObligationCauseCode::ExprBindingObligation(def_id, span, hir_id, idx) + } + }) } - #[tracing::instrument(level = "debug", skip(self, span, def_id, substs))] + #[instrument(level = "debug", skip(self, code, span, def_id, substs))] fn add_required_obligations_with_code( &self, span: Span, def_id: DefId, - substs: &SubstsRef<'tcx>, - code: ObligationCauseCode<'tcx>, + substs: SubstsRef<'tcx>, + code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>, ) { let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); for obligation in traits::predicates_for_generics( - traits::ObligationCause::new(span, self.body_id, code), + |idx, predicate_span| { + traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span)) + }, self.param_env, bounds, ) { diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs index 7602f2550..fc83994ca 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs @@ -130,14 +130,17 @@ impl<'tcx> ArgMatrix<'tcx> { let ai = &self.expected_indices; let ii = &self.provided_indices; + // Issue: 100478, when we end the iteration, + // `next_unmatched_idx` will point to the index of the first unmatched + let mut next_unmatched_idx = 0; for i in 0..cmp::max(ai.len(), ii.len()) { - // If we eliminate the last row, any left-over inputs are considered missing + // If we eliminate the last row, any left-over arguments are considered missing if i >= mat.len() { - return Some(Issue::Missing(i)); + return Some(Issue::Missing(next_unmatched_idx)); } - // If we eliminate the last column, any left-over arguments are extra + // If we eliminate the last column, any left-over inputs are extra if mat[i].len() == 0 { - return Some(Issue::Extra(i)); + return Some(Issue::Extra(next_unmatched_idx)); } // Make sure we don't pass the bounds of our matrix @@ -145,6 +148,7 @@ impl<'tcx> ArgMatrix<'tcx> { let is_input = i < ii.len(); if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) { // This is a satisfied input, so move along + next_unmatched_idx += 1; continue; } @@ -163,7 +167,7 @@ impl<'tcx> ArgMatrix<'tcx> { if is_input { for j in 0..ai.len() { // If we find at least one argument that could satisfy this input - // this argument isn't useless + // this input isn't useless if matches!(mat[i][j], Compatibility::Compatible) { useless = false; break; @@ -232,8 +236,8 @@ impl<'tcx> ArgMatrix<'tcx> { if matches!(c, Compatibility::Compatible) { Some(i) } else { None } }) .collect(); - if compat.len() != 1 { - // this could go into multiple slots, don't bother exploring both + if compat.len() < 1 { + // try to find a cycle even when this could go into multiple slots, see #101097 is_cycle = false; break; } @@ -309,7 +313,8 @@ impl<'tcx> ArgMatrix<'tcx> { } while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { - match self.find_issue() { + let res = self.find_issue(); + match res { Some(Issue::Invalid(idx)) => { let compatibility = self.compatibility_matrix[idx][idx].clone(); let input_idx = self.provided_indices[idx]; @@ -364,7 +369,9 @@ impl<'tcx> ArgMatrix<'tcx> { None => { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs - for (inp, arg) in self.eliminate_satisfied() { + let eliminated = self.eliminate_satisfied(); + assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); + for (inp, arg) in eliminated { matched_inputs[arg] = Some(inp); } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 660e7e4e3..311fcaada 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -15,6 +15,7 @@ use crate::check::{ use crate::structured_errors::StructuredDiagnostic; use rustc_ast as ast; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -27,13 +28,14 @@ use rustc_infer::infer::InferOk; use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; +use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; +use rustc_span::{self, sym, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; +use std::ops::ControlFlow; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -58,7 +60,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); for (asm, hir_id) in deferred_asm_checks.drain(..) { let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id); - InlineAsmCtxt::new_in_fn(self) + let get_operand_ty = |expr| { + let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); + let ty = self.resolve_vars_if_possible(ty); + if ty.has_infer_types_or_consts() { + assert!(self.is_tainted_by_errors()); + self.tcx.ty_error() + } else { + self.tcx.erase_regions(ty) + } + }; + InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty) .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id)); } } @@ -141,7 +153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let tcx = self.tcx; - // Conceptually, we've got some number of expected inputs, and some number of provided aguments + // Conceptually, we've got some number of expected inputs, and some number of provided arguments // and we can form a grid of whether each argument could satisfy a given input: // in1 | in2 | in3 | ... // arg1 ? | | | @@ -212,6 +224,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let minimum_input_count = expected_input_tys.len(); let provided_arg_count = provided_args.len(); + let is_const_eval_select = matches!(fn_def_id, Some(def_id) if + self.tcx.def_kind(def_id) == hir::def::DefKind::Fn + && self.tcx.is_intrinsic(def_id) + && self.tcx.item_name(def_id) == sym::const_eval_select); + // We introduce a helper function to demand that a given argument satisfy a given input // This is more complicated than just checking type equality, as arguments could be coerced // This version writes those types back so further type checking uses the narrowed types @@ -237,17 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Cause selection errors caused by resolving a single argument to point at the // argument and not the call. This lets us customize the span pointed to in the // fulfillment error to be more accurate. - let coerced_ty = - self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); - self.point_at_arg_instead_of_call_if_possible( - errors, - call_expr, - call_span, - provided_args, - &expected_input_tys, - ); - }); + let coerced_ty = self.resolve_vars_with_obligations(coerced_ty); let coerce_error = self .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None) @@ -257,6 +264,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Compatibility::Incompatible(coerce_error); } + // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that + // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed + // in the function signature (`F: FnOnce`), so I did not bother to add another check here. + // + // This check is here because there is currently no way to express a trait bound for `FnDef` types only. + if is_const_eval_select && (1..=2).contains(&idx) { + if let ty::FnDef(def_id, _) = checked_ty.kind() { + if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a `const fn`") + .help("consult the documentation on `const_eval_select` for more information") + .emit(); + } + } else { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a function item") + .note(format!("expected a function item, found {checked_ty}")) + .help( + "consult the documentation on `const_eval_select` for more information", + ) + .emit(); + } + } + // 3. Check if the formal type is a supertype of the checked one // and register any such obligations for future type checks let supertype_error = self @@ -302,16 +335,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, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); - self.point_at_arg_instead_of_call_if_possible( - errors, - call_expr, - call_span, - &provided_args, - &expected_input_tys, - ); - }) + self.select_obligations_where_possible(false, |_| {}) } // Check each argument, to satisfy the input it was provided for @@ -440,7 +464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &hir::Expr<'tcx>, ) { // Next, let's construct the error - let (error_span, full_call_span, ctor_of) = match &call_expr.kind { + let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind { hir::ExprKind::Call( hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, _, @@ -448,22 +472,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Res::Def(DefKind::Ctor(of, _), _) = self.typeck_results.borrow().qpath_res(qpath, *hir_id) { - (call_span, *span, Some(of)) + (call_span, *span, Some(of), false) } else { - (call_span, *span, None) + (call_span, *span, None, false) } } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None), - hir::ExprKind::MethodCall(path_segment, _, span) => { + hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false), + hir::ExprKind::MethodCall(path_segment, _, _, span) => { let ident_span = path_segment.ident.span; let ident_span = if let Some(args) = path_segment.args { ident_span.with_hi(args.span_ext.hi()) } else { ident_span }; - ( - *span, ident_span, None, // methods are never ctors - ) + // methods are never ctors + (*span, ident_span, None, true) } k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), }; @@ -507,13 +530,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); let callee_expr = match &call_expr.peel_blocks().kind { hir::ExprKind::Call(callee, _) => Some(*callee), - hir::ExprKind::MethodCall(_, callee, _) => { + hir::ExprKind::MethodCall(_, receiver, ..) => { if let Some((DefKind::AssocFn, def_id)) = self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) && let Some(assoc) = tcx.opt_associated_item(def_id) && assoc.fn_has_self_parameter { - Some(&callee[0]) + Some(*receiver) } else { None } @@ -545,7 +568,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); let can_coerce = self.can_coerce(arg_ty, coerced_ty); if !can_coerce { - return Compatibility::Incompatible(None); + return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( + ty::error::ExpectedFound::new(true, coerced_ty, arg_ty), + ))); } // Using probe here, since we don't want this subtyping to affect inference. @@ -577,7 +602,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // First, check if we just need to wrap some arguments in a tuple. if let Some((mismatch_idx, terr)) = compatibility_diagonal.iter().enumerate().find_map(|(i, c)| { - if let Compatibility::Incompatible(Some(terr)) = c { Some((i, terr)) } else { None } + if let Compatibility::Incompatible(Some(terr)) = c { + Some((i, *terr)) + } else { + None + } }) { // Is the first bad expected argument a tuple? @@ -659,7 +688,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); }; - self.label_fn_like(&mut err, fn_def_id, callee_ty); + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + Some(mismatch_idx), + is_method, + ); err.emit(); return; } @@ -701,16 +736,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } errors.drain_filter(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(error)) = error else { return false }; + 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); - if let Some(e) = error { - if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { - self.report_and_explain_type_error(trace, e).emit(); - return true; - } + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { + self.report_and_explain_type_error(trace, *e).emit(); + return true; } false }); @@ -733,7 +766,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 mut err = self.report_and_explain_type_error(trace, err); + let mut err = self.report_and_explain_type_error(trace, *err); self.emit_coerce_suggestions( &mut err, &provided_args[*provided_idx], @@ -749,7 +782,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty); + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + Some(expected_idx.as_usize()), + is_method, + ); err.emit(); return; } @@ -797,7 +836,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Error::Invalid(provided_idx, expected_idx, compatibility) => { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; - if let Compatibility::Incompatible(error) = &compatibility { + if let Compatibility::Incompatible(error) = compatibility { let cause = &self.misc(provided_span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); if let Some(e) = error { @@ -1031,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty); + self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -1048,11 +1087,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(suggestion_text) = suggestion_text { let source_map = self.sess().source_map(); - let mut suggestion = format!( - "{}(", - source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| fn_def_id - .map_or("".to_string(), |fn_def_id| tcx.item_name(fn_def_id).to_string())) - ); + let (mut suggestion, suggestion_span) = + if let Some(call_span) = full_call_span.find_ancestor_inside(error_span) { + ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi())) + } else { + ( + format!( + "{}(", + source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| { + fn_def_id.map_or("".to_string(), |fn_def_id| { + tcx.item_name(fn_def_id).to_string() + }) + }) + ), + error_span, + ) + }; let mut needs_comma = false; for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { if needs_comma { @@ -1062,8 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let suggestion_text = if let Some(provided_idx) = provided_idx && let (_, provided_span) = provided_arg_tys[*provided_idx] - && let Ok(arg_text) = - source_map.span_to_snippet(provided_span) + && let Ok(arg_text) = source_map.span_to_snippet(provided_span) { arg_text } else { @@ -1081,7 +1130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } suggestion += ")"; err.span_suggestion_verbose( - error_span, + suggestion_span, &suggestion_text, suggestion, Applicability::HasPlaceholders, @@ -1129,7 +1178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_ty.unwrap_or_else(|| self.next_float_var()) } ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err(_) => tcx.ty_error(), + ast::LitKind::Err => tcx.ty_error(), } } @@ -1164,7 +1213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_user_type_annotation_from_substs(hir_id, did, substs, None); // Check bounds on type arguments used in the path. - self.add_required_obligations(path_span, did, substs); + self.add_required_obligations_for_hir(path_span, did, substs, hir_id); Some((variant, ty)) } else { @@ -1601,186 +1650,420 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk - /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s - /// reference a type argument. The reason to walk also the checked type is that the coerced type - /// can be not easily comparable with predicate type (because of coercion). If the types match - /// for either checked or coerced type, and there's only *one* argument that does, we point at - /// the corresponding argument's expression span instead of the `fn` call path span. - fn point_at_arg_instead_of_call_if_possible( + /// Given a vector of fulfillment errors, try to adjust the spans of the + /// errors to more accurately point at the cause of the failure. + /// + /// This applies to calls, methods, and struct expressions. This will also + /// try to deduplicate errors that are due to the same cause but might + /// have been created with different [`ObligationCause`][traits::ObligationCause]s. + pub(super) fn adjust_fulfillment_errors_for_expr_obligation( &self, errors: &mut Vec>, - expr: &'tcx hir::Expr<'tcx>, - call_sp: Span, - args: &'tcx [hir::Expr<'tcx>], - expected_tys: &[Ty<'tcx>], ) { - // We *do not* do this for desugared call spans to keep good diagnostics when involving - // the `?` operator. - if call_sp.desugaring_kind().is_some() { - return; - } - - 'outer: for error in errors { - // Only if the cause is somewhere inside the expression we want try to point at arg. - // Otherwise, it means that the cause is somewhere else and we should not change - // anything because we can break the correct span. - if !call_sp.contains(error.obligation.cause.span) { - continue; + // Store a mapping from `(Span, Predicate) -> ObligationCause`, so that + // other errors that have the same span and predicate can also get fixed, + // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind. + // This is important since if we adjust one span but not the other, then + // we will have "duplicated" the error on the UI side. + let mut remap_cause = FxHashSet::default(); + let mut not_adjusted = vec![]; + + for error in errors { + let before_span = error.obligation.cause.span; + if self.adjust_fulfillment_error_for_expr_obligation(error) + || before_span != error.obligation.cause.span + { + // Store both the predicate and the predicate *without constness* + // since sometimes we instantiate and check both of these in a + // method call, for example. + remap_cause.insert(( + before_span, + error.obligation.predicate, + error.obligation.cause.clone(), + )); + remap_cause.insert(( + before_span, + error.obligation.predicate.without_const(self.tcx), + error.obligation.cause.clone(), + )); + } else { + // If it failed to be adjusted once around, it may be adjusted + // via the "remap cause" mapping the second time... + not_adjusted.push(error); } + } - // Peel derived obligation, because it's the type that originally - // started this inference chain that matters, not the one we wound - // up with at the end. - fn unpeel_to_top<'a, 'tcx>( - mut code: &'a ObligationCauseCode<'tcx>, - ) -> &'a ObligationCauseCode<'tcx> { - let mut result_code = code; - loop { - let parent = match code { - ObligationCauseCode::ImplDerivedObligation(c) => &c.derived.parent_code, - ObligationCauseCode::BuiltinDerivedObligation(c) - | ObligationCauseCode::DerivedObligation(c) => &c.parent_code, - _ => break result_code, - }; - (result_code, code) = (code, parent); + for error in not_adjusted { + for (span, predicate, cause) in &remap_cause { + if *predicate == error.obligation.predicate + && span.contains(error.obligation.cause.span) + { + error.obligation.cause = cause.clone(); + continue; } } - let self_: ty::subst::GenericArg<'_> = - match unpeel_to_top(error.obligation.cause.code()) { - ObligationCauseCode::BuiltinDerivedObligation(code) - | ObligationCauseCode::DerivedObligation(code) => { - code.parent_trait_pred.self_ty().skip_binder().into() + } + } + + fn adjust_fulfillment_error_for_expr_obligation( + &self, + error: &mut traits::FulfillmentError<'tcx>, + ) -> bool { + let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx)) + = *error.obligation.cause.code().peel_derives() else { return false; }; + 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::List::empty(), + }; + + let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| { + predicate_substs.types().find_map(|ty| { + ty.walk().find_map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(param_ty) = ty.kind() + && matches(param_ty) + { + Some(arg) + } else { + None } - ObligationCauseCode::ImplDerivedObligation(code) => { - code.derived.parent_trait_pred.self_ty().skip_binder().into() + }) + }) + }; + + // Prefer generics that are local to the fn item, since these are likely + // to be the cause of the unsatisfied predicate. + let mut param_to_point_at = find_param_matching(&|param_ty| { + self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id + }); + // Fall back to generic that isn't local to the fn item. This will come + // from a trait or impl, for example. + let mut fallback_param_to_point_at = find_param_matching(&|param_ty| { + self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id + && param_ty.name != rustc_span::symbol::kw::SelfUpper + }); + // Finally, the `Self` parameter is possibly the reason that the predicate + // is unsatisfied. This is less likely to be true for methods, because + // method probe means that we already kinda check that the predicates due + // to the `Self` type are true. + let mut self_param_to_point_at = + find_param_matching(&|param_ty| param_ty.name == rustc_span::symbol::kw::SelfUpper); + + // Finally, for ambiguity-related errors, we actually want to look + // for a parameter that is the source of the inference type left + // over in this predicate. + if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code { + fallback_param_to_point_at = None; + self_param_to_point_at = None; + param_to_point_at = + self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); + } + + if self.closure_span_overlaps_error(error, expr.span) { + return false; + } + + match &expr.kind { + hir::ExprKind::Path(qpath) => { + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(callee, args), + hir_id: call_hir_id, + span: call_span, + .. + }) = hir.get(hir.get_parent_node(expr.hir_id)) + && callee.hir_id == expr.hir_id + { + if self.closure_span_overlaps_error(error, *call_span) { + return false; } - _ if let ty::PredicateKind::Trait(predicate) = - error.obligation.predicate.kind().skip_binder() => + + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() { - predicate.self_ty().into() + if self.point_at_arg_if_possible( + error, + def_id, + param, + *call_hir_id, + callee.span, + None, + args, + ) + { + return true; + } } - _ => continue, - }; - let self_ = self.resolve_vars_if_possible(self_); - let ty_matches_self = |ty: Ty<'tcx>| ty.walk().any(|arg| arg == self_); - - let typeck_results = self.typeck_results.borrow(); - - for (idx, arg) in args.iter().enumerate() { - // Don't adjust the span if we already have a more precise span - // within one of the args. - if arg.span.contains(error.obligation.cause.span) { - let references_arg = - typeck_results.expr_ty_opt(arg).map_or(false, &ty_matches_self) - || expected_tys.get(idx).copied().map_or(false, &ty_matches_self); - if references_arg && !arg.span.from_expansion() { - error.obligation.cause.map_code(|parent_code| { - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: args[idx].hir_id, - call_hir_id: expr.hir_id, - parent_code, - } - }) + } + // Notably, we only point to params that are local to the + // item we're checking, since those are the ones we are able + // to look in the final `hir::PathSegment` for. Everything else + // would require a deeper search into the `qpath` than I think + // is worthwhile. + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + { + return true; + } + } + hir::ExprKind::MethodCall(segment, receiver, args, ..) => { + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.point_at_arg_if_possible( + error, + def_id, + param, + hir_id, + segment.ident.span, + Some(receiver), + args, + ) { + return true; + } + } + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment) + { + return true; + } + } + hir::ExprKind::Struct(qpath, fields, ..) => { + if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = + self.typeck_results.borrow().qpath_res(qpath, hir_id) + { + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + { + if let Some(param) = param + && self.point_at_field_if_possible( + error, + def_id, + param, + variant_def_id, + fields, + ) + { + return true; + } } - continue 'outer; + } + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + { + return true; } } + _ => {} + } - // Collect the argument position for all arguments that could have caused this - // `FulfillmentError`. - let mut referenced_in: Vec<_> = std::iter::zip(expected_tys, args) - .enumerate() - .flat_map(|(idx, (expected_ty, arg))| { - if let Some(arg_ty) = typeck_results.expr_ty_opt(arg) { - vec![(idx, arg_ty), (idx, *expected_ty)] - } else { - vec![] - } - }) - .filter_map(|(i, ty)| { - let ty = self.resolve_vars_if_possible(ty); - // We walk the argument type because the argument's type could have - // been `Option`, but the `FulfillmentError` references `T`. - if ty_matches_self(ty) { Some(i) } else { None } - }) - .collect(); + false + } + + fn closure_span_overlaps_error( + &self, + error: &traits::FulfillmentError<'tcx>, + span: Span, + ) -> bool { + if let traits::FulfillmentErrorCode::CodeSelectionError( + traits::SelectionError::OutputTypeParameterMismatch(_, expected, _), + ) = error.code + && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind() + && span.overlaps(self.tcx.def_span(*def_id)) + { + true + } else { + false + } + } + + fn point_at_arg_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + call_hir_id: hir::HirId, + callee_span: Span, + receiver: Option<&'tcx hir::Expr<'tcx>>, + args: &'tcx [hir::Expr<'tcx>], + ) -> bool { + let sig = self.tcx.fn_sig(def_id).skip_binder(); + let args_referencing_param: Vec<_> = sig + .inputs() + .iter() + .enumerate() + .filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at)) + .collect(); + // If there's one field that references the given generic, great! + if let [(idx, _)] = args_referencing_param.as_slice() + && let Some(arg) = receiver + .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) { + error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span); + error.obligation.cause.map_code(|parent_code| { + ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id: arg.hir_id, + call_hir_id, + parent_code, + } + }); + return true; + } else if args_referencing_param.len() > 0 { + // If more than one argument applies, then point to the callee span at least... + // We have chance to fix this up further in `point_at_generics_if_possible` + error.obligation.cause.span = callee_span; + } - // Both checked and coerced types could have matched, thus we need to remove - // duplicates. + false + } - // We sort primitive type usize here and can use unstable sort - referenced_in.sort_unstable(); - referenced_in.dedup(); + fn point_at_field_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + variant_def_id: DefId, + expr_fields: &[hir::ExprField<'tcx>], + ) -> bool { + let def = self.tcx.adt_def(def_id); + + let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id); + let fields_referencing_param: Vec<_> = def + .variant_with_id(variant_def_id) + .fields + .iter() + .filter(|field| { + let field_ty = field.ty(self.tcx, identity_substs); + find_param_in_ty(field_ty, param_to_point_at) + }) + .collect(); - if let &[idx] = &referenced_in[..] { - // Do not point at the inside of a macro. - // That would often result in poor error messages. - if args[idx].span.from_expansion() { - continue; + if let [field] = fields_referencing_param.as_slice() { + for expr_field in expr_fields { + // Look for the ExprField that matches the field, using the + // same rules that check_expr_struct uses for macro hygiene. + if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx) + { + error.obligation.cause.span = expr_field + .expr + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(expr_field.span); + return true; } - // We make sure that only *one* argument matches the obligation failure - // and we assign the obligation's span to its expression's. - error.obligation.cause.span = args[idx].span; - error.obligation.cause.map_code(|parent_code| { - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: args[idx].hir_id, - call_hir_id: expr.hir_id, - parent_code, - } - }); - } else if error.obligation.cause.span == call_sp { - // Make function calls point at the callee, not the whole thing. - if let hir::ExprKind::Call(callee, _) = expr.kind { - error.obligation.cause.span = callee.span; + } + } + + false + } + + fn point_at_path_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param: ty::GenericArg<'tcx>, + qpath: &QPath<'tcx>, + ) -> bool { + match qpath { + hir::QPath::Resolved(_, path) => { + if let Some(segment) = path.segments.last() + && self.point_at_generic_if_possible(error, def_id, param, segment) + { + return true; } } + hir::QPath::TypeRelative(_, segment) => { + if self.point_at_generic_if_possible(error, def_id, param, segment) { + return true; + } + } + _ => {} } + + false } - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the - /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s - /// were caused by them. If they were, we point at the corresponding type argument's span - /// instead of the `fn` call path span. - fn point_at_type_arg_instead_of_call_if_possible( + fn point_at_generic_if_possible( &self, - errors: &mut Vec>, - call_expr: &'tcx hir::Expr<'tcx>, - ) { - if let hir::ExprKind::Call(path, _) = &call_expr.kind { - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &path.kind { - for error in errors { - if let ty::PredicateKind::Trait(predicate) = - error.obligation.predicate.kind().skip_binder() - { - // If any of the type arguments in this path segment caused the - // `FulfillmentError`, point at its span (#61860). - for arg in path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref()) - .flat_map(|a| a.args.iter()) - { - if let hir::GenericArg::Type(hir_ty) = &arg - && let Some(ty) = - self.typeck_results.borrow().node_type_opt(hir_ty.hir_id) - && self.resolve_vars_if_possible(ty) == predicate.self_ty() - { - error.obligation.cause.span = hir_ty.span; - break; - } - } - } + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + segment: &hir::PathSegment<'tcx>, + ) -> bool { + let own_substs = self + .tcx + .generics_of(def_id) + .own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id)); + let Some((index, _)) = own_substs + .iter() + .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) + .enumerate() + .find(|(_, arg)| **arg == param_to_point_at) else { return false }; + let Some(arg) = segment + .args() + .args + .iter() + .filter(|arg| matches!(arg, hir::GenericArg::Type(_))) + .nth(index) else { return false; }; + error.obligation.cause.span = arg + .span() + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(arg.span()); + true + } + + fn find_ambiguous_parameter_in>( + &self, + item_def_id: DefId, + t: T, + ) -> Option> { + struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId); + impl<'tcx> TypeVisitor<'tcx> for FindAmbiguousParameter<'_, 'tcx> { + type BreakTy = ty::GenericArg<'tcx>; + fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow { + if let Some(origin) = self.0.type_var_origin(ty) + && let TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) = + origin.kind + && let generics = self.0.tcx.generics_of(self.1) + && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id) + && let Some(subst) = ty::InternalSubsts::identity_for_item(self.0.tcx, self.1) + .get(index as usize) + { + ControlFlow::Break(*subst) + } else { + ty.super_visit_with(self) } } } + t.visit_with(&mut FindAmbiguousParameter(self, item_def_id)).break_value() } fn label_fn_like( &self, - err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, + err: &mut Diagnostic, callable_def_id: Option, callee_ty: Option>, + // A specific argument should be labeled, instead of all of them + expected_idx: Option, + is_method: bool, ) { let Some(mut def_id) = callable_def_id else { return; @@ -1838,14 +2121,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let new_def_id = self.probe(|_| { let trait_ref = ty::TraitRef::new( call_kind.to_def_id(self.tcx), - self.tcx.mk_substs([ - ty::GenericArg::from(callee_ty), - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: rustc_span::DUMMY_SP, - }) - .into(), - ].into_iter()), + self.tcx.mk_substs( + [ + ty::GenericArg::from(callee_ty), + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: rustc_span::DUMMY_SP, + }) + .into(), + ] + .into_iter(), + ), ); let obligation = traits::Obligation::new( traits::ObligationCause::dummy(), @@ -1860,7 +2146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { Some(impl_source.impl_def_id) } - _ => None + _ => None, } }); if let Some(new_def_id) = new_def_id { @@ -1881,14 +2167,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .get_if_local(def_id) .and_then(|node| node.body_id()) .into_iter() - .flat_map(|id| self.tcx.hir().body(id).params); + .flat_map(|id| self.tcx.hir().body(id).params) + .skip(if is_method { 1 } else { 0 }); - for param in params { + for (_, param) in params + .into_iter() + .enumerate() + .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)) + { spans.push_span_label(param.span, ""); } let def_kind = self.tcx.def_kind(def_id); err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id) + && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind + { + let param = expected_idx + .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx)); + let (kind, span) = if let Some(param) = param { + ("closure parameter", param.span) + } else { + ("closure", self.tcx.def_span(def_id)) + }; + err.span_note(span, &format!("{} defined here", kind)); } else { let def_kind = self.tcx.def_kind(def_id); err.span_note( @@ -1898,3 +2200,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + +fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool { + let mut walk = ty.walk(); + while let Some(arg) = walk.next() { + if arg == param_to_point_at { + return true; + } else if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Projection(..) = ty.kind() + { + // This logic may seem a bit strange, but typically when + // we have a projection type in a function signature, the + // argument that's being passed into that signature is + // not actually constraining that projection's substs in + // a meaningful way. So we skip it, and see improvements + // in some UI tests. + walk.skip_current_subtree(); + } + } + false +} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs index 05bcc710e..0e22971d3 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs @@ -26,6 +26,17 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use std::cell::{Cell, RefCell}; use std::ops::Deref; +/// The `FnCtxt` stores type-checking context needed to type-check bodies of +/// functions, closures, and `const`s, including performing type inference +/// with [`InferCtxt`]. +/// +/// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures* +/// and thus does not perform type inference. +/// +/// See [`ItemCtxt`]'s docs for more. +/// +/// [`ItemCtxt`]: crate::collect::ItemCtxt +/// [`InferCtxt`]: infer::InferCtxt pub struct FnCtxt<'a, 'tcx> { pub(super) body_id: hir::HirId, @@ -57,8 +68,6 @@ pub struct FnCtxt<'a, 'tcx> { /// any). pub(super) ret_coercion: Option>>, - pub(super) ret_type_span: Option, - /// Used exclusively to reduce cost of advanced evaluation used for /// more helpful diagnostics. pub(super) in_tail_expr: bool, @@ -131,7 +140,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env, err_count_on_creation: inh.tcx.sess.err_count(), ret_coercion: None, - ret_type_span: None, in_tail_expr: false, ret_coercion_span: Cell::new(None), resume_yield_tys: None, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 57771e096..ee0ad7b5d 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -2,6 +2,7 @@ use super::FnCtxt; use crate::astconv::AstConv; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use hir::def_id::DefId; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; @@ -16,6 +17,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -61,70 +63,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pointing_at_return_type } - /// When encountering an fn-like ctor that needs to unify with a value, check whether calling - /// the ctor would successfully solve the type mismatch and if so, suggest it: + /// When encountering an fn-like type, try accessing the output of the type + /// // and suggesting calling it if it satisfies a predicate (i.e. if the + /// output has a method or a field): /// ```compile_fail,E0308 /// fn foo(x: usize) -> usize { x } /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` /// ``` - fn suggest_fn_call( + pub(crate) fn suggest_fn_call( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, - expected: Ty<'tcx>, found: Ty<'tcx>, + can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, ) -> bool { - let (def_id, output, inputs) = match *found.kind() { - ty::FnDef(def_id, _) => { - let fn_sig = found.fn_sig(self.tcx); - (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len()) - } - ty::Closure(def_id, substs) => { - let fn_sig = substs.as_closure().sig(); - (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1) - } - ty::Opaque(def_id, substs) => { - let sig = 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() - && 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() - { - Some(( - pred.kind().rebind(proj.term.ty().unwrap()), - args.len(), - )) - } else { - None - } - }); - if let Some((output, inputs)) = sig { - (def_id, output, inputs) - } else { - return false; - } - } - _ => return false, - }; - - let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); - let output = self.normalize_associated_types_in(expr.span, output); - if !output.is_ty_var() && self.can_coerce(output, expected) { - let (sugg_call, mut applicability) = match inputs { + let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found) + else { return false; }; + if can_satisfy(output) { + let (sugg_call, mut applicability) = match inputs.len() { 0 => ("".to_string(), Applicability::MachineApplicable), 1..=4 => ( - (0..inputs).map(|_| "_").collect::>().join(", "), - Applicability::MachineApplicable, + inputs + .iter() + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "".to_string() + } + }) + .collect::>() + .join(", "), + Applicability::HasPlaceholders, ), - _ => ("...".to_string(), Applicability::HasPlaceholders), + _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), }; - let msg = match self.tcx.def_kind(def_id) { - DefKind::Fn => "call this function", - DefKind::Closure | DefKind::OpaqueTy => "call this closure", - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct", - DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant", - _ => "call this function", + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => { + "instantiate this tuple variant".to_string() + } + kind => format!("call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("call this {name}"), }; let sugg = match expr.kind { @@ -161,6 +144,182 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// Extracts information about a callable type for diagnostics. This is a + /// heuristic -- it doesn't necessarily mean that a type is always callable, + /// because the callable type must also be well-formed to be called. + pub(in super::super) fn extract_callable_info( + &self, + expr: &Expr<'_>, + found: Ty<'tcx>, + ) -> Option<(DefIdOrName, Ty<'tcx>, Vec>)> { + // Autoderef is useful here because sometimes we box callables, etc. + let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { + match *found.kind() { + ty::FnPtr(fn_sig) => + Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())), + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) + } + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..]))) + } + 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() + && 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() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Dynamic(data, _, ty::Dyn) => { + data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() + // for existential projection, substs are shifted over by 1 + && let ty::Tuple(args) = proj.substs.type_at(0).kind() + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + pred.rebind(args.as_slice()), + )) + } else { + None + } + }) + } + 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() + && 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] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + _ => None, + } + }) else { return None; }; + + let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); + let inputs = inputs + .skip_binder() + .iter() + .map(|ty| { + self.replace_bound_vars_with_fresh_vars( + expr.span, + infer::FnCall, + inputs.rebind(*ty), + ) + }) + .collect(); + + // We don't want to register any extra obligations, which should be + // 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); + + if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } + } + + pub fn suggest_two_fn_call( + &self, + err: &mut Diagnostic, + lhs_expr: &'tcx hir::Expr<'tcx>, + lhs_ty: Ty<'tcx>, + rhs_expr: &'tcx hir::Expr<'tcx>, + rhs_ty: Ty<'tcx>, + can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, + ) -> bool { + let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty) + else { return false; }; + let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty) + else { return false; }; + + if can_satisfy(lhs_output_ty, rhs_output_ty) { + let mut sugg = vec![]; + let mut applicability = Applicability::MachineApplicable; + + for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] { + let (sugg_call, this_applicability) = match inputs.len() { + 0 => ("".to_string(), Applicability::MachineApplicable), + 1..=4 => ( + inputs + .iter() + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::>() + .join(", "), + Applicability::HasPlaceholders, + ), + _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), + }; + + applicability = applicability.max(this_applicability); + + match expr.kind { + hir::ExprKind::Call(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Lit(..) => { + sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]); + } + hir::ExprKind::Closure { .. } => { + // Might be `{ expr } || { bool }` + applicability = Applicability::MaybeIncorrect; + sugg.extend([ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ]); + } + _ => { + sugg.extend([ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ]); + } + } + } + + err.multipart_suggestion_verbose( + format!("use parentheses to call these"), + sugg, + applicability, + ); + + true + } else { + false + } + } + pub fn suggest_deref_ref_or_into( &self, err: &mut Diagnostic, @@ -178,12 +337,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion(sp, &msg, suggestion, applicability); } - } else if let (ty::FnDef(def_id, ..), true) = - (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) + } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) + && let ty::FnDef(def_id, ..) = &found.kind() + && let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - err.span_label(sp, format!("{found} defined here")); - } + err.span_label(sp, format!("{found} defined here")); } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { @@ -506,30 +664,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); // Only suggest changing the return type for methods that // haven't set a return type at all (and aren't `fn main()` or an impl). - match ( - &fn_decl.output, - found.is_suggestable(self.tcx, false), - can_suggest, - expected.is_unit(), - ) { - (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { - err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found }); - true - } - (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { - // FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest - // that. - err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); - true - } - (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { + match &fn_decl.output { + &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => { // `fn main()` must return `()`, do not suggest changing return type err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span }); - true + return true; + } + &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { + if found.is_suggestable(self.tcx, false) { + err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); + return true; + } else if let ty::Closure(_, substs) = found.kind() + // FIXME(compiler-errors): Get better at printing binders... + && let closure = substs.as_closure() + && closure.sig().is_suggestable(self.tcx, false) + { + err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() }); + return true; + } else { + // FIXME: if `found` could be `impl Iterator` we should suggest that. + err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); + return true + } } - // expectation was caused by something else, not the default return - (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, - (&hir::FnRetTy::Return(ref ty), _, _, _) => { + &hir::FnRetTy::Return(ref ty) => { // Only point to return type if the expected type is the return type, as if they // are not, the expectation must have been caused by something else. debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); @@ -546,9 +704,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.try_suggest_return_impl_trait(err, expected, ty, fn_id); return true; } - false } + _ => {} } + false } /// check whether the return type is a generic type with a trait bound @@ -770,6 +929,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub(crate) fn suggest_copied_or_cloned( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) { + let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; }; + let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; }; + if adt_def != expected_adt_def { + return; + } + + let mut suggest_copied_or_cloned = || { + let expr_inner_ty = substs.type_at(0); + let expected_inner_ty = expected_substs.type_at(0); + if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() + && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok() + { + let def_path = self.tcx.def_path_str(adt_def.did()); + if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) { + diag.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{def_path}::copied` to copy the value inside the `{def_path}`" + ), + ".copied()", + Applicability::MachineApplicable, + ); + } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() + && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( + self, + self.param_env, + *ty, + clone_did, + expr.span + ) + { + diag.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{def_path}::cloned` to clone the value inside the `{def_path}`" + ), + ".cloned()", + Applicability::MachineApplicable, + ); + } + } + }; + + if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) + && adt_def.did() == result_did + // Check that the error types are equal + && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() + { + suggest_copied_or_cloned(); + } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) + && adt_def.did() == option_did + { + suggest_copied_or_cloned(); + } + } + /// 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( @@ -830,7 +1052,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found_ty: Ty<'tcx>, expr: &hir::Expr<'_>, ) { - let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; }; + let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; }; let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return }; let results = self.typeck_results.borrow(); @@ -910,3 +1132,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + +pub enum DefIdOrName { + DefId(DefId), + Name(&'static str), +} diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index d4f800149..7ab6d9e2b 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -17,7 +17,6 @@ use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::sym; use rustc_span::Span; -use tracing::debug; mod drop_ranges; @@ -409,8 +408,15 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { }) { self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) } else { - debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id)); - match self.fcx.tcx.hir().find_parent_node(expr.hir_id) { + let parent_expr = self + .fcx + .tcx + .hir() + .parent_iter(expr.hir_id) + .find(|(_, node)| matches!(node, hir::Node::Expr(_))) + .map(|(id, _)| id); + debug!("parent_expr: {:?}", parent_expr); + match parent_expr { Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }), None => { self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) @@ -457,7 +463,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { } #[derive(Default)] -pub struct SuspendCheckData<'a, 'tcx> { +struct SuspendCheckData<'a, 'tcx> { expr: Option<&'tcx Expr<'tcx>>, source_span: Span, yield_span: Span, @@ -472,7 +478,7 @@ pub struct SuspendCheckData<'a, 'tcx> { // // Note that this technique was chosen over things like a `Suspend` marker trait // as it is simpler and has precedent in the compiler -pub fn check_must_not_suspend_ty<'tcx>( +fn check_must_not_suspend_ty<'tcx>( fcx: &FnCtxt<'_, 'tcx>, ty: Ty<'tcx>, hir_id: HirId, @@ -489,6 +495,8 @@ pub fn check_must_not_suspend_ty<'tcx>( let plural_suffix = pluralize!(data.plural_len); + debug!("Checking must_not_suspend for {}", ty); + match *ty.kind() { ty::Adt(..) if ty.is_box() => { let boxed_ty = ty.boxed_ty(); @@ -519,7 +527,7 @@ pub fn check_must_not_suspend_ty<'tcx>( } has_emitted } - ty::Dynamic(binder, _) => { + ty::Dynamic(binder, _, _) => { let mut has_emitted = false; for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { @@ -580,6 +588,12 @@ pub fn check_must_not_suspend_ty<'tcx>( }, ) } + // If drop tracking is enabled, we want to look through references, since the referrent + // may not be considered live across the await point. + ty::Ref(_region, ty, _mutability) if fcx.sess().opts.unstable_opts.drop_tracking => { + let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix); + check_must_not_suspend_ty(fcx, ty, hir_id, SuspendCheckData { descr_pre, ..data }) + } _ => false, } } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index a2c23db16..016f4056b 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -256,6 +256,8 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | hir::Node::TypeBinding(..) | hir::Node::TraitRef(..) | hir::Node::Pat(..) + | hir::Node::PatField(..) + | hir::Node::ExprField(..) | hir::Node::Arm(..) | hir::Node::Local(..) | hir::Node::Ctor(..) @@ -432,7 +434,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { self.handle_uninhabited_return(expr); } - ExprKind::MethodCall(_, exprs, _) => { + ExprKind::MethodCall(_, receiver, exprs, _) => { + self.visit_expr(receiver); for expr in exprs { self.visit_expr(expr); } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs index ded0888c3..e22675e9d 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -159,8 +159,8 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { bk: rustc_middle::ty::BorrowKind, ) { debug!( - "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \ - borrow_kind={bk:?}" + "borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \ + borrow_kind={bk:#?}" ); self.borrow_place(place_with_id); diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs index cd152eb97..37c830d4e 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -1,6 +1,7 @@ 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; @@ -12,7 +13,9 @@ 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, TraitEngine, TraitEngineExt}; +use rustc_trait_selection::traits::{ + self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _, +}; use std::cell::RefCell; use std::ops::Deref; @@ -37,6 +40,7 @@ pub struct Inherited<'a, 'tcx> { // 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>)>>, @@ -89,7 +93,29 @@ impl<'tcx> Inherited<'_, 'tcx> { infcx: tcx .infer_ctxt() .ignoring_regions() - .with_fresh_in_progress_typeck_results(hir_owner), + .with_fresh_in_progress_typeck_results(hir_owner) + .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 + }) + })), def_id, } } diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 3f2a0da8d..c7425ff78 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -69,6 +69,9 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. sym::abort + | sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_uninit_valid | sym::size_of | sym::min_align_of | sym::needs_drop @@ -92,8 +95,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::type_id | sym::likely | sym::unlikely - | sym::ptr_guaranteed_eq - | sym::ptr_guaranteed_ne + | sym::ptr_guaranteed_cmp | sym::minnumf32 | sym::minnumf64 | sym::maxnumf32 @@ -102,7 +104,8 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::type_name | sym::forget | sym::black_box - | sym::variant_count => hir::Unsafety::Normal, + | sym::variant_count + | sym::ptr_mask => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, } } @@ -200,6 +203,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ], tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), ), + sym::ptr_mask => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + tcx.types.usize, + ], + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + ), + sym::copy | sym::copy_nonoverlapping => ( 1, vec![ @@ -289,8 +301,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { (1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool])) } - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool) + sym::ptr_guaranteed_cmp => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8) } sym::const_allocate => { @@ -465,7 +477,11 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), - sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)), + sym::simd_cast + | sym::simd_as + | sym::simd_cast_ptr + | sym::simd_expose_addr + | sym::simd_from_exposed_addr => (2, vec![param(0)], param(1)), sym::simd_bitmask => (2, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, vec![param(0), param(1), param(1)], param(1)) diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs index df94abbaf..d8fe63dbf 100644 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ b/compiler/rustc_typeck/src/check/intrinsicck.rs @@ -9,7 +9,6 @@ use rustc_session::lint; use rustc_span::{Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Pointer, VariantIdx}; use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; -use rustc_trait_selection::infer::InferCtxtExt; use super::FnCtxt; @@ -98,12 +97,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } err.emit(); } +} + +pub struct InlineAsmCtxt<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + get_operand_ty: Box) -> Ty<'tcx> + 'a>, +} + +impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { + pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { + InlineAsmCtxt { + tcx, + param_env: ty::ParamEnv::empty(), + get_operand_ty: Box::new(|e| bug!("asm operand in global asm: {e:?}")), + } + } + + pub fn new_in_fn( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + get_operand_ty: impl Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a, + ) -> Self { + InlineAsmCtxt { tcx, param_env, get_operand_ty: Box::new(get_operand_ty) } + } // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { // Type still may have region variables, but `Sized` does not depend // on those, so just erase them before querying. - if self.tcx.erase_regions(ty).is_sized(self.tcx.at(DUMMY_SP), self.param_env) { + if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) { return true; } if let ty::Foreign(..) = ty.kind() { @@ -111,36 +134,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } false } -} - -pub struct InlineAsmCtxt<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - fcx: Option<&'a FnCtxt<'a, 'tcx>>, -} - -impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { - pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { - InlineAsmCtxt { tcx, fcx: None } - } - - pub fn new_in_fn(fcx: &'a FnCtxt<'a, 'tcx>) -> Self { - InlineAsmCtxt { tcx: fcx.tcx, fcx: Some(fcx) } - } fn check_asm_operand_type( &self, idx: usize, reg: InlineAsmRegOrRegClass, - expr: &hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, template: &[InlineAsmTemplatePiece], is_input: bool, - tied_input: Option<(&hir::Expr<'tcx>, Option)>, + tied_input: Option<(&'tcx hir::Expr<'tcx>, Option)>, target_features: &FxHashSet, ) -> Option { - let fcx = self.fcx.unwrap_or_else(|| span_bug!(expr.span, "asm operand for global asm")); - // Check the type against the allowed types for inline asm. - let ty = fcx.typeck_results.borrow().expr_ty_adjusted(expr); - let ty = fcx.resolve_vars_if_possible(ty); + let ty = (self.get_operand_ty)(expr); + if ty.has_infer_types_or_consts() { + bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty); + } let asm_ty_isize = match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, @@ -148,12 +156,6 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { _ => unreachable!(), }; - // Expect types to be fully resolved, no const or type variables. - if ty.has_infer_types_or_consts() { - assert!(fcx.is_tainted_by_errors()); - return None; - } - let asm_ty = match *ty.kind() { // `!` is allowed for input but not for output (issue #87802) ty::Never if is_input => return None, @@ -167,7 +169,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), ty::FnPtr(_) => Some(asm_ty_isize), - ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if fcx.is_thin_ptr_ty(ty) => { + ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => { Some(asm_ty_isize) } ty::Adt(adt, substs) if adt.repr().simd() => { @@ -219,7 +221,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, ty, DUMMY_SP) { + if !ty.is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env) { let msg = "arguments for inline assembly must be copyable"; let mut err = self.tcx.sess.struct_span_err(expr.span, msg); err.note(&format!("`{ty}` does not implement the Copy trait")); @@ -240,8 +242,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let msg = "incompatible types for asm inout argument"; let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg); - let in_expr_ty = fcx.typeck_results.borrow().expr_ty_adjusted(in_expr); - let in_expr_ty = fcx.resolve_vars_if_possible(in_expr_ty); + let in_expr_ty = (self.get_operand_ty)(in_expr); err.span_label(in_expr.span, &format!("type `{in_expr_ty}`")); err.span_label(expr.span, &format!("type `{ty}`")); err.note( @@ -332,10 +333,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let mut err = lint.build(msg); err.span_label(expr.span, "for this argument"); err.help(&format!( - "use the `{suggested_modifier}` modifier to have the register formatted as `{suggested_result}`", + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", )); err.help(&format!( - "or use the `{default_modifier}` modifier to keep the default formatting of `{default_result}`", + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", )); err.emit(); }, diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index 2c89b63ae..59fd5c315 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -491,7 +491,19 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // so we just call `predicates_for_generics` directly to avoid redoing work. // `self.add_required_obligations(self.span, def_id, &all_substs);` for obligation in traits::predicates_for_generics( - traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)), + |idx, span| { + let code = if span.is_dummy() { + ObligationCauseCode::ExprItemObligation(def_id, self.call_expr.hir_id, idx) + } else { + ObligationCauseCode::ExprBindingObligation( + def_id, + span, + self.call_expr.hir_id, + idx, + ) + }; + traits::ObligationCause::new(self.span, self.body_id, code) + }, self.param_env, method_predicates, ) { diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 0e678c41f..249e9c66b 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -20,10 +20,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{ - self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy, Term, - ToPredicate, Ty, TypeVisitable, -}; +use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable}; use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits; @@ -168,7 +165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `call_expr`: the complete method call: (`foo.bar::(...)`) /// * `self_expr`: the self expression (`foo`) /// * `args`: the expressions of the arguments (`a, b + 1, ...`) - #[instrument(level = "debug", skip(self, call_expr, self_expr))] + #[instrument(level = "debug", skip(self))] pub fn lookup_method( &self, self_ty: Ty<'tcx>, @@ -178,11 +175,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_expr: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) -> Result, MethodError<'tcx>> { - debug!( - "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", - segment.ident, self_ty, call_expr, self_expr - ); - let pick = self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; @@ -342,22 +334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Construct an obligation let poly_trait_ref = ty::Binder::dummy(trait_ref); - let opt_output_ty = - expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); - let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind( - self.tcx, - Ident::from_str("Output"), - AssocKind::Type, - trait_def_id, - ); - let output_pred = - opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| { - ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate { - projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id }, - term: Term::Ty(output_ty), - })) - .to_predicate(self.tcx) - }); + let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); ( traits::Obligation::new( @@ -368,7 +345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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_pred, + output_ty, }, ), self.param_env, @@ -383,7 +360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// In particular, it doesn't really do any probing: it simply constructs /// an obligation for a particular trait with the given self type and checks /// whether that trait is implemented. - #[instrument(level = "debug", skip(self, span, opt_input_types))] + #[instrument(level = "debug", skip(self, span))] pub(super) fn lookup_method_in_trait( &self, span: Span, @@ -392,11 +369,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, opt_input_types: Option<&[Ty<'tcx>]>, ) -> Option>> { - debug!( - "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})", - self_ty, m_name, trait_def_id, opt_input_types - ); - let (obligation, substs) = self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); self.construct_obligation_for_trait( @@ -528,13 +500,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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_pred: None, + output_ty: None, }, ) } else { traits::ObligationCause::misc(span, self.body_id) }; - obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); + let predicates_cause = cause.clone(); + obligations.extend(traits::predicates_for_generics( + move |_, _| predicates_cause.clone(), + self.param_env, + bounds, + )); // Also add an obligation for the method type being well-formed. let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig)); @@ -571,7 +548,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `self_ty`: the type to search within (`Foo`) /// * `self_ty_span` the span for the type being searched within (span of `Foo`) /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub fn resolve_fully_qualified_call( &self, span: Span, @@ -580,11 +557,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty_span: Span, expr_id: hir::HirId, ) -> Result<(DefKind, DefId), MethodError<'tcx>> { - debug!( - "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}", - method_name, self_ty, expr_id, - ); - let tcx = self.tcx; // Check if we have an enum variant. @@ -628,21 +600,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &pick, ); - debug!("resolve_fully_qualified_call: pick={:?}", pick); + debug!(?pick); { let mut typeck_results = self.typeck_results.borrow_mut(); let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); for import_id in pick.import_ids { - debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id); + debug!(used_trait_import=?import_id); used_trait_imports.insert(import_id); } } let def_kind = pick.item.kind.as_def_kind(); - debug!( - "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}", - def_kind, pick.item.def_id - ); tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span)); Ok((def_kind, pick.item.def_id)) } diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index 7c68d9304..392695cca 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -160,7 +160,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if precise { let args = args .iter() - .skip(1) .map(|arg| { let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); format!( diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index efe15fec7..e9f55ab34 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -253,7 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self))] pub fn probe_for_return_type( &self, span: Span, @@ -262,10 +262,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, ) -> Vec { - debug!( - "probe(self_ty={:?}, return_type={}, scope_expr_id={})", - self_ty, return_type, scope_expr_id - ); let method_names = self .probe_op( span, @@ -299,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self))] pub fn probe_for_name( &self, span: Span, @@ -310,10 +306,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { scope_expr_id: hir::HirId, scope: ProbeScope, ) -> PickResult<'tcx> { - debug!( - "probe(self_ty={:?}, item_name={}, scope_expr_id={})", - self_ty, item_name, scope_expr_id - ); self.probe_op( span, mode, @@ -1514,8 +1506,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds); // Convert the bounds into obligations. - let impl_obligations = - traits::predicates_for_generics(cause, self.param_env, impl_bounds); + let impl_obligations = traits::predicates_for_generics( + move |_, _| cause.clone(), + self.param_env, + impl_bounds, + ); let candidate_obligations = impl_obligations .chain(norm_obligations.into_iter()) diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index c92b93cbc..2d459b2cc 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -16,8 +16,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::with_crate_prefix; -use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, 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}; @@ -30,8 +30,8 @@ use rustc_trait_selection::traits::{ use std::cmp::Ordering; use std::iter; -use super::probe::{Mode, ProbeScope}; -use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData}; +use super::probe::{IsSuggestion, Mode, ProbeScope}; +use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -95,7 +95,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, source: SelfSource<'tcx>, error: MethodError<'tcx>, - args: Option<&'tcx [hir::Expr<'tcx>]>, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, ) -> Option> { // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { @@ -363,44 +363,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if self.is_fn_ty(rcvr_ty, span) { - if let SelfSource::MethodCall(expr) = source { - let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { - if let Some(local_id) = def_id.as_local() { - let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); - let node = tcx.hir().get(hir_id); - let fields = node.tuple_fields(); - if let Some(fields) = fields - && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { - Some((fields.len(), of)) - } else { - None - } - } else { - // The logic here isn't smart but `associated_item_def_ids` - // doesn't work nicely on local. - if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) { - let parent_def_id = tcx.parent(*def_id); - Some((tcx.associated_item_def_ids(parent_def_id).len(), of)) - } else { - None - } - } - } else { - None - }; - - // If the function is a tuple constructor, we recommend that they call it - if let Some((fields, kind)) = suggest { - suggest_call_constructor(expr.span, kind, fields, &mut err); - } else { - // General case - err.span_label( - expr.span, - "this is a function, perhaps you wish to call it", - ); - } - } + if let SelfSource::MethodCall(rcvr_expr) = source { + self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { + let call_expr = self + .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, + ProbeScope::AllTraits, + ); + probe.is_ok() + }); } let mut custom_span_label = false; @@ -560,7 +537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans.push((self.tcx.def_span(def.did()), msg)) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _) => { + ty::Dynamic(preds, _, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => bound_spans @@ -904,7 +881,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| { + 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() { @@ -1000,7 +977,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - self.check_for_field_method(&mut err, source, span, actual, item_name); + // Don't suggest (for example) `expr.field.method()` if `expr.method()` + // doesn't exist due to unsatisfied predicates. + if unsatisfied_predicates.is_empty() { + self.check_for_field_method(&mut err, source, span, actual, item_name); + } self.check_for_unwrap_self(&mut err, source, span, actual, item_name); @@ -1017,7 +998,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_ty, item_name, - args.map(|args| args.len()), + args.map(|(_, args)| args.len() + 1), source, out_of_scope_traits, &unsatisfied_predicates, @@ -1062,19 +1043,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() { let def_kind = lev_candidate.kind.as_def_kind(); - err.span_suggestion( - span, - &format!( - "there is {} {} with a similar name", - def_kind.article(), - def_kind.descr(lev_candidate.def_id), - ), - lev_candidate.name, - Applicability::MaybeIncorrect, - ); + // Methods are defined within the context of a struct and their first parameter is always self, + // which represents the instance of the struct the method is being called on + // Associated functions don’t take self as a parameter and + // they are not methods because they don’t have an instance of the struct to work with. + if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter { + err.span_suggestion( + span, + &format!("there is a method with a similar name",), + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + span, + &format!( + "there is {} {} with a similar name", + def_kind.article(), + def_kind.descr(lev_candidate.def_id), + ), + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } } } + self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); + return Some(err); } @@ -1150,7 +1146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, expr: &hir::Expr<'_>, item_name: Ident, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, ) -> bool { let tcx = self.tcx; let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { @@ -1165,7 +1161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }); if let Some((field, field_ty)) = field_receiver { - let scope = tcx.parent_module(self.body_id).to_def_id(); + let scope = tcx.parent_module(self.body_id); let is_accessible = field.vis.is_accessible_from(scope, tcx); if is_accessible { @@ -1282,7 +1278,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // local binding if let hir::def::Res::Local(hir_id) = path.res { let span = tcx.hir().span(hir_id); - let snippet = tcx.sess.source_map().span_to_snippet(span); let filename = tcx.sess.source_map().span_to_filename(span); let parent_node = @@ -1292,7 +1287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { concrete_type, ); - match (filename, parent_node, snippet) { + match (filename, parent_node) { ( FileName::Real(_), Node::Local(hir::Local { @@ -1300,14 +1295,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty, .. }), - Ok(ref snippet), ) => { + let type_span = ty.map(|ty| ty.span.with_lo(span.hi())).unwrap_or(span.shrink_to_hi()); err.span_suggestion( // account for `let x: _ = 42;` - // ^^^^ - span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), + // ^^^ + type_span, &msg, - format!("{}: {}", snippet, concrete_type), + format!(": {concrete_type}"), Applicability::MaybeIncorrect, ); } @@ -1327,55 +1322,82 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_for_field_method( &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, source: SelfSource<'tcx>, span: Span, actual: Ty<'tcx>, item_name: Ident, ) { if let SelfSource::MethodCall(expr) = source - && let Some((fields, substs)) = self.get_field_candidates(span, actual) + && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() + && let Some((fields, substs)) = + self.get_field_candidates_considering_privacy(span, actual, mod_id) { let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - for candidate_field in fields.iter() { - if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - &|_, field_ty| { - self.lookup_probe( - span, - item_name, - field_ty, - call_expr, - ProbeScope::AllTraits, - ) - .is_ok() - }, - candidate_field, - substs, - vec![], - self.tcx.parent_module(expr.hir_id).to_def_id(), - ) { - let field_path_str = field_path + + let lang_items = self.tcx.lang_items(); + let never_mention_traits = [ + lang_items.clone_trait(), + lang_items.deref_trait(), + lang_items.deref_mut_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), + self.tcx.get_diagnostic_item(sym::AsMut), + self.tcx.get_diagnostic_item(sym::Borrow), + self.tcx.get_diagnostic_item(sym::BorrowMut), + ]; + let candidate_fields: Vec<_> = fields + .filter_map(|candidate_field| { + self.check_for_nested_field_satisfying( + span, + &|_, field_ty| { + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + ) + .map_or(false, |pick| { + !never_mention_traits + .iter() + .flatten() + .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) + }) + }, + candidate_field, + substs, + vec![], + mod_id, + ) + }) + .map(|field_path| { + field_path .iter() .map(|id| id.name.to_ident_string()) .collect::>() - .join("."); - debug!("field_path_str: {:?}", field_path_str); - - err.span_suggestion_verbose( - item_name.span.shrink_to_lo(), - "one of the expressions' fields has a method of the same name", - format!("{field_path_str}."), - Applicability::MaybeIncorrect, - ); - } + .join(".") + }) + .collect(); + + let len = candidate_fields.len(); + if len > 0 { + err.span_suggestions( + item_name.span.shrink_to_lo(), + format!( + "{} of the expressions' fields {} a method of the same name", + if len > 1 { "some" } else { "one" }, + if len > 1 { "have" } else { "has" }, + ), + candidate_fields.iter().map(|path| format!("{path}.")), + Applicability::MaybeIncorrect, + ); } } } fn check_for_unwrap_self( &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, source: SelfSource<'tcx>, span: Span, actual: Ty<'tcx>, @@ -1631,6 +1653,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_for_deref_method( + &self, + err: &mut Diagnostic, + self_source: SelfSource<'tcx>, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + ) { + 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), + deref_ty, + ty.hir_id, + ProbeScope::TraitsInScope, + ) { + if deref_ty.is_suggestable(self.tcx, true) + // If this method receives `&self`, then the provided + // argument _should_ coerce, so it's valid to suggest + // just changing the path. + && pick.item.fn_has_self_parameter + && let Some(self_ty) = + self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0) + && self_ty.is_ref() + { + let suggested_path = match deref_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Str + | ty::Projection(_) + | ty::Param(_) => format!("{deref_ty}"), + _ => format!("<{deref_ty}>"), + }; + err.span_suggestion_verbose( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + suggested_path, + Applicability::MaybeIncorrect, + ); + } else { + err.span_note( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + ); + } + return; + } + } + } + /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { match ty.kind() { @@ -2232,7 +2310,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec { fn print_disambiguation_help<'tcx>( item_name: Ident, - args: Option<&'tcx [hir::Expr<'tcx>]>, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, err: &mut Diagnostic, trait_name: String, rcvr_ty: Ty<'_>, @@ -2244,7 +2322,7 @@ fn print_disambiguation_help<'tcx>( fn_has_self_parameter: bool, ) { let mut applicability = Applicability::MachineApplicable; - let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) { + let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { let args = format!( "({}{})", if rcvr_ty.is_region_ptr() { @@ -2252,7 +2330,8 @@ fn print_disambiguation_help<'tcx>( } else { "" }, - args.iter() + std::iter::once(receiver) + .chain(args.iter()) .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { applicability = Applicability::HasPlaceholders; "_".to_owned() diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 17c2e4868..cfae63e4a 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -87,7 +87,6 @@ mod op; mod pat; mod place_op; mod region; -pub mod regionck; pub mod rvalue_scopes; mod upvar; pub mod wfcheck; @@ -97,14 +96,13 @@ use check::{check_abi, check_fn, check_mod_item_types}; pub use diverges::Diverges; pub use expectation::Expectation; pub use fn_ctxt::*; -use hir::def::CtorOf; pub use inherited::{Inherited, InheritedBuilder}; use crate::astconv::AstConv; use crate::check::gather_locals::GatherLocalsVisitor; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ - pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan, + pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::Res; @@ -112,7 +110,6 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{HirIdMap, ImplicitSelfKind, Node}; use rustc_index::bit_set::BitSet; -use rustc_index::vec::Idx; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; @@ -122,18 +119,20 @@ use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, BytePos, Span}; +use rustc_span::{self, BytePos, Span, Symbol}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; use std::cell::RefCell; +use std::num::NonZeroU32; use crate::require_c_abi_if_c_variadic; use crate::util::common::indenter; use self::coercion::DynamicCoerceMany; +use self::compare_method::collect_trait_impl_trait_tys; use self::region::region_scope_tree; pub use self::Expectation::*; @@ -251,6 +250,7 @@ pub fn provide(providers: &mut Providers) { used_trait_imports, check_mod_item_types, region_scope_tree, + collect_trait_impl_trait_tys, ..*providers }; } @@ -343,7 +343,6 @@ fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::T typeck_with_fallback(tcx, def_id, fallback) } -#[instrument(skip(tcx, fallback))] fn typeck_with_fallback<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, @@ -548,13 +547,13 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { // For the wasm32 target statics with `#[link_section]` are placed into custom // sections of the final output file, but this isn't link custom sections of // other executable formats. Namely we can only embed a list of bytes, - // nothing with pointers to anything else or relocations. If any relocation - // show up, reject them here. + // nothing with provenance (pointers to anything else). If any provenance + // show up, reject it here. // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) - && alloc.inner().relocations().len() != 0 + && alloc.inner().provenance().len() != 0 { let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ @@ -662,6 +661,37 @@ fn missing_items_must_implement_one_of_err( err.emit(); } +fn default_body_is_unstable( + tcx: TyCtxt<'_>, + impl_span: Span, + item_did: DefId, + feature: Symbol, + reason: Option, + issue: Option, +) { + let missing_item_name = &tcx.associated_item(item_did).name; + let use_of_unstable_library_feature_note = match reason { + Some(r) => format!("use of unstable library feature '{feature}': {r}"), + None => format!("use of unstable library feature '{feature}'"), + }; + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing: `{missing_item_name}`", + ); + err.note(format!("default implementation of `{missing_item_name}` is unstable")); + err.note(use_of_unstable_library_feature_note); + rustc_session::parse::add_feature_diagnostics_for_issue( + &mut err, + &tcx.sess.parse_sess, + feature, + rustc_feature::GateIssue::Library(issue), + ); + err.emit(); +} + /// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. fn bounds_from_generic_predicates<'tcx>( tcx: TyCtxt<'tcx>, @@ -935,36 +965,3 @@ fn has_expected_num_generic_args<'tcx>( generics.count() == expected + if generics.has_self { 1 } else { 0 } }) } - -/// Suggests calling the constructor of a tuple struct or enum variant -/// -/// * `snippet` - The snippet of code that references the constructor -/// * `span` - The span of the snippet -/// * `params` - The number of parameters the constructor accepts -/// * `err` - A mutable diagnostic builder to add the suggestion to -fn suggest_call_constructor( - span: Span, - kind: CtorOf, - params: usize, - err: &mut DiagnosticBuilder<'_, G>, -) { - // Note: tuple-structs don't have named fields, so just use placeholders - let args = vec!["_"; params].join(", "); - let applicable = if params > 0 { - Applicability::HasPlaceholders - } else { - // When n = 0, it's an empty-tuple struct/enum variant - // so we trivially know how to construct it - Applicability::MachineApplicable - }; - let kind = match kind { - CtorOf::Struct => "a struct", - CtorOf::Variant => "an enum variant", - }; - err.span_label(span, &format!("this is the constructor of {kind}")); - err.multipart_suggestion( - "call the constructor", - vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))], - applicable, - ); -} diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 920b3e688..4754717c2 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -11,9 +11,8 @@ use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; -use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, -}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -22,8 +21,6 @@ use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; use rustc_type_ir::sty::TyKind::*; -use std::ops::ControlFlow; - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` pub fn check_binop_assign( @@ -57,9 +54,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .is_ok() { - // Suppress this error, since we already emitted - // a deref suggestion in check_overloaded_binop - err.delay_as_bug(); + // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have + // emitted a better suggestion during error handling in check_overloaded_binop. + if self + .lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs), + Op::Binary(op, IsAssign::Yes), + expected, + ) + .is_err() + { + err.downgrade_to_delayed_bug(); + } else { + // Otherwise, it's valid to suggest dereferencing the LHS here. + err.span_suggestion_verbose( + lhs.span.shrink_to_lo(), + "consider dereferencing the left-hand side of this operation", + "*", + Applicability::MaybeIncorrect, + ); + } } } }); @@ -294,8 +310,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // error types are considered "builtin" Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), Err(errors) => { - let source_map = self.tcx.sess.source_map(); - let (mut err, missing_trait, use_output) = match is_assign { + let (_, trait_def_id) = + lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); + let missing_trait = trait_def_id + .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id))); + let (mut err, output_def_id) = match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( self.tcx.sess, @@ -309,130 +328,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr.span, format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), ); - let missing_trait = match op.node { - hir::BinOpKind::Add => Some("std::ops::AddAssign"), - hir::BinOpKind::Sub => Some("std::ops::SubAssign"), - hir::BinOpKind::Mul => Some("std::ops::MulAssign"), - hir::BinOpKind::Div => Some("std::ops::DivAssign"), - hir::BinOpKind::Rem => Some("std::ops::RemAssign"), - hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"), - hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"), - hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"), - hir::BinOpKind::Shl => Some("std::ops::ShlAssign"), - hir::BinOpKind::Shr => Some("std::ops::ShrAssign"), - _ => None, - }; self.note_unmet_impls_on_type(&mut err, errors); - (err, missing_trait, false) + (err, None) } IsAssign::No => { - let (message, missing_trait, use_output) = match op.node { - hir::BinOpKind::Add => ( - format!("cannot add `{rhs_ty}` to `{lhs_ty}`"), - Some("std::ops::Add"), - true, - ), - hir::BinOpKind::Sub => ( - format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`"), - Some("std::ops::Sub"), - true, - ), - hir::BinOpKind::Mul => ( - format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`"), - Some("std::ops::Mul"), - true, - ), - hir::BinOpKind::Div => ( - format!("cannot divide `{lhs_ty}` by `{rhs_ty}`"), - Some("std::ops::Div"), - true, - ), - hir::BinOpKind::Rem => ( - format!("cannot mod `{lhs_ty}` by `{rhs_ty}`"), - Some("std::ops::Rem"), - true, - ), - hir::BinOpKind::BitAnd => ( - format!("no implementation for `{lhs_ty} & {rhs_ty}`"), - Some("std::ops::BitAnd"), - true, - ), - hir::BinOpKind::BitXor => ( - format!("no implementation for `{lhs_ty} ^ {rhs_ty}`"), - Some("std::ops::BitXor"), - true, - ), - hir::BinOpKind::BitOr => ( - format!("no implementation for `{lhs_ty} | {rhs_ty}`"), - Some("std::ops::BitOr"), - true, - ), - hir::BinOpKind::Shl => ( - format!("no implementation for `{lhs_ty} << {rhs_ty}`"), - Some("std::ops::Shl"), - true, - ), - hir::BinOpKind::Shr => ( - format!("no implementation for `{lhs_ty} >> {rhs_ty}`"), - Some("std::ops::Shr"), - true, - ), - hir::BinOpKind::Eq | hir::BinOpKind::Ne => ( - format!( - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty - ), - Some("std::cmp::PartialEq"), - false, - ), - hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Gt - | hir::BinOpKind::Ge => ( - format!( - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty - ), - Some("std::cmp::PartialOrd"), - false, - ), - _ => ( - format!( - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty - ), - None, - false, + let message = match op.node { + hir::BinOpKind::Add => { + format!("cannot add `{rhs_ty}` to `{lhs_ty}`") + } + hir::BinOpKind::Sub => { + format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`") + } + hir::BinOpKind::Mul => { + format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`") + } + hir::BinOpKind::Div => { + format!("cannot divide `{lhs_ty}` by `{rhs_ty}`") + } + hir::BinOpKind::Rem => { + format!("cannot mod `{lhs_ty}` by `{rhs_ty}`") + } + hir::BinOpKind::BitAnd => { + format!("no implementation for `{lhs_ty} & {rhs_ty}`") + } + hir::BinOpKind::BitXor => { + format!("no implementation for `{lhs_ty} ^ {rhs_ty}`") + } + hir::BinOpKind::BitOr => { + format!("no implementation for `{lhs_ty} | {rhs_ty}`") + } + hir::BinOpKind::Shl => { + format!("no implementation for `{lhs_ty} << {rhs_ty}`") + } + hir::BinOpKind::Shr => { + format!("no implementation for `{lhs_ty} >> {rhs_ty}`") + } + _ => format!( + "binary operation `{}` cannot be applied to type `{}`", + op.node.as_str(), + lhs_ty ), }; + let output_def_id = trait_def_id.and_then(|def_id| { + self.tcx + .associated_item_def_ids(def_id) + .iter() + .find(|item_def_id| { + self.tcx.associated_item(*item_def_id).name == sym::Output + }) + .cloned() + }); let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}"); if !lhs_expr.span.eq(&rhs_expr.span) { - self.add_type_neq_err_label( - &mut err, - lhs_expr.span, - lhs_ty, - rhs_ty, - rhs_expr, - op, - is_assign, - expected, - ); - self.add_type_neq_err_label( - &mut err, - rhs_expr.span, - rhs_ty, - lhs_ty, - lhs_expr, - op, - is_assign, - expected, - ); + err.span_label(lhs_expr.span, lhs_ty.to_string()); + err.span_label(rhs_expr.span, rhs_ty.to_string()); } self.note_unmet_impls_on_type(&mut err, errors); - (err, missing_trait, use_output) + (err, output_def_id) } }; @@ -447,42 +399,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .is_ok() { - if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { - let msg = &format!( - "`{}{}` can be used on `{}`, you can dereference `{}`", - op.node.as_str(), - match is_assign { - IsAssign::Yes => "=", - IsAssign::No => "", - }, - lhs_deref_ty.peel_refs(), - lstring, - ); - err.span_suggestion_verbose( - lhs_expr.span.shrink_to_lo(), - msg, - "*", - rustc_errors::Applicability::MachineApplicable, - ); - } + let msg = &format!( + "`{}{}` can be used on `{}` if you dereference the left-hand side", + op.node.as_str(), + match is_assign { + IsAssign::Yes => "=", + IsAssign::No => "", + }, + lhs_deref_ty, + ); + err.span_suggestion_verbose( + lhs_expr.span.shrink_to_lo(), + msg, + "*", + rustc_errors::Applicability::MachineApplicable, + ); } }; + let is_compatible = |lhs_ty, rhs_ty| { + self.lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + }; + // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest // `a += b` => `*a += b` if a is a mut ref. - if is_assign == IsAssign::Yes - && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - suggest_deref_binop(lhs_deref_ty); + if !op.span.can_be_used_for_suggestions() { + // Suppress suggestions when lhs and rhs are not in the same span as the error + } else if is_assign == IsAssign::Yes + && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) + { + suggest_deref_binop(lhs_deref_ty); } else if is_assign == IsAssign::No - && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() { - if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) { + && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() + { + if self.type_is_copy_modulo_regions( + self.param_env, + *lhs_deref_ty, + lhs_expr.span, + ) { suggest_deref_binop(*lhs_deref_ty); } + } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { + is_compatible(lhs_ty, rhs_ty) + }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| { + is_compatible(lhs_ty, rhs_ty) + }) || self.suggest_two_fn_call( + &mut err, + rhs_expr, + rhs_ty, + lhs_expr, + lhs_ty, + |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty), + ) { + // Cool } - if let Some(missing_trait) = missing_trait { - let mut visitor = TypeParamVisitor(vec![]); - visitor.visit_ty(lhs_ty); + if let Some(missing_trait) = missing_trait { if op.node == hir::BinOpKind::Add && self.check_str_addition( lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op, @@ -491,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted - } else if let [ty] = &visitor.0[..] { + } else if lhs_ty.has_param_types_or_consts() { // Look for a TraitPredicate in the Fulfillment errors, // and use it to generate a suggestion. // @@ -513,12 +492,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(trait_pred) = error.obligation.predicate.to_opt_poly_trait_pred() { - let proj_pred = match error.obligation.cause.code() { + let output_associated_item = match error.obligation.cause.code() + { ObligationCauseCode::BinOp { - output_pred: Some(output_pred), + output_ty: Some(output_ty), .. - } if use_output => { - output_pred.to_opt_poly_projection_pred() + } => { + // Make sure that we're attaching `Output = ..` to the right trait predicate + if let Some(output_def_id) = output_def_id + && let Some(trait_def_id) = trait_def_id + && self.tcx.parent(output_def_id) == trait_def_id + { + Some(("Output", *output_ty)) + } else { + None + } } _ => None, }; @@ -526,12 +514,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_restricting_param_bound( &mut err, trait_pred, - proj_pred, + output_associated_item, self.body_id, ); } } - } else if *ty != lhs_ty { + } else { // When we know that a missing bound is responsible, we don't show // this note as it is redundant. err.note(&format!( @@ -548,69 +536,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (lhs_ty, rhs_ty, return_ty) } - /// If one of the types is an uncalled function and calling it would yield the other type, - /// suggest calling the function. Returns `true` if suggestion would apply (even if not given). - fn add_type_neq_err_label( - &self, - err: &mut Diagnostic, - span: Span, - ty: Ty<'tcx>, - other_ty: Ty<'tcx>, - other_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - is_assign: IsAssign, - expected: Expectation<'tcx>, - ) -> bool /* did we suggest to call a function because of missing parentheses? */ { - err.span_label(span, ty.to_string()); - if let FnDef(def_id, _) = *ty.kind() { - if !self.tcx.has_typeck_results(def_id) { - return false; - } - // FIXME: Instead of exiting early when encountering bound vars in - // the function signature, consider keeping the binder here and - // propagating it downwards. - let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else { - return false; - }; - - let other_ty = if let FnDef(def_id, _) = *other_ty.kind() { - if !self.tcx.has_typeck_results(def_id) { - return false; - } - // We're emitting a suggestion, so we can just ignore regions - self.tcx.fn_sig(def_id).skip_binder().output() - } else { - other_ty - }; - - if self - .lookup_op_method( - fn_sig.output(), - Some(other_ty), - Some(other_expr), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - { - let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { - ("( /* arguments */ )", Applicability::HasPlaceholders) - } else { - ("()", Applicability::MaybeIncorrect) - }; - - err.span_suggestion_verbose( - span.shrink_to_hi(), - "you might have forgotten to call this function", - variable_snippet, - applicability, - ); - return true; - } - } - false - } - /// Provide actionable suggestions when trying to add two strings with incorrect types, /// like `&str + &str`, `String + String` and `&str + &String`. /// @@ -731,14 +656,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("cannot apply unary operator `{}`", op.as_str()), ); - let mut visitor = TypeParamVisitor(vec![]); - visitor.visit_ty(operand_ty); - if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() { - let predicates = errors - .iter() - .filter_map(|error| { - error.obligation.predicate.to_opt_poly_trait_pred() - }); + if operand_ty.has_param_types_or_consts() { + let predicates = errors.iter().filter_map(|error| { + error.obligation.predicate.to_opt_poly_trait_pred() + }); for pred in predicates { self.suggest_restricting_param_bound( &mut err, @@ -806,64 +727,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: Op, expected: Expectation<'tcx>, ) -> Result, Vec>> { - let lang = self.tcx.lang_items(); - let span = match op { Op::Binary(op, _) => op.span, Op::Unary(_, span) => span, }; - let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op { - match op.node { - hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), - hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), - hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), - hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), - hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), - hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), - hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), - hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), - hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), - hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), - hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt - | hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::And - | hir::BinOpKind::Or => { - span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) - } - } - } else if let Op::Binary(op, IsAssign::No) = op { - match op.node { - hir::BinOpKind::Add => (sym::add, lang.add_trait()), - hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), - hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), - hir::BinOpKind::Div => (sym::div, lang.div_trait()), - hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()), - hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()), - hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()), - hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()), - hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()), - hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()), - hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()), - hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()), - hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()), - hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()), - hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), - hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), - hir::BinOpKind::And | hir::BinOpKind::Or => { - span_bug!(span, "&& and || are not overloadable") - } - } - } else if let Op::Unary(hir::UnOp::Not, _) = op { - (sym::not, lang.not_trait()) - } else if let Op::Unary(hir::UnOp::Neg, _) = op { - (sym::neg, lang.neg_trait()) - } else { - bug!("lookup_op_method: op not supported: {:?}", op) - }; + let (opname, trait_did) = lang_item_for_op(self.tcx, op, span); debug!( "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", @@ -924,6 +792,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +fn lang_item_for_op( + tcx: TyCtxt<'_>, + op: Op, + span: Span, +) -> (rustc_span::Symbol, Option) { + let lang = tcx.lang_items(); + if let Op::Binary(op, IsAssign::Yes) = op { + match op.node { + hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), + hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), + hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), + hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), + hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), + hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), + hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), + hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), + hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), + hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), + hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt + | hir::BinOpKind::Eq + | hir::BinOpKind::Ne + | hir::BinOpKind::And + | hir::BinOpKind::Or => { + span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) + } + } + } else if let Op::Binary(op, IsAssign::No) = op { + match op.node { + hir::BinOpKind::Add => (sym::add, lang.add_trait()), + hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), + hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), + hir::BinOpKind::Div => (sym::div, lang.div_trait()), + hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()), + hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()), + hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()), + hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()), + hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()), + hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()), + hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()), + hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()), + hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()), + hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()), + hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), + hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), + hir::BinOpKind::And | hir::BinOpKind::Or => { + span_bug!(span, "&& and || are not overloadable") + } + } + } else if let Op::Unary(hir::UnOp::Not, _) = op { + (sym::not, lang.not_trait()) + } else if let Op::Unary(hir::UnOp::Neg, _) = op { + (sym::neg, lang.neg_trait()) + } else { + bug!("lookup_op_method: op not supported: {:?}", op) + } +} + // Binary operator categories. These categories summarize the behavior // with respect to the builtin operations supported. enum BinOpCategory { @@ -1046,17 +974,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool } } -struct TypeParamVisitor<'tcx>(Vec>); - -impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - if let ty::Param(_) = ty.kind() { - self.0.push(ty); - } - ty.super_visit_with(self) - } -} - struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span); impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> { diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 837c32355..8906b622b 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -569,7 +569,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { // Determine the binding mode... let bm = match ba { - hir::BindingAnnotation::Unannotated => def_bm, + hir::BindingAnnotation::NONE => def_bm, _ => BindingMode::convert(ba), }; // ...and store it in a side table: @@ -600,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If there are multiple arms, make sure they all agree on // what the type of the binding `x` ought to be. if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti); + self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti); } if let Some(p) = sub { @@ -610,7 +610,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { local_ty } - fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) { + fn check_binding_alt_eq_ty( + &self, + ba: hir::BindingAnnotation, + span: Span, + var_id: HirId, + ty: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) { let var_ty = self.local_ty(span, var_id).decl_ty; if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { let hir = self.tcx.hir(); @@ -628,12 +635,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let pre = if in_match { "in the same arm, " } else { "" }; err.note(&format!("{}a binding must have the same type in all alternatives", pre)); - // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing - // `ref` or `&` to the pattern. + self.suggest_adding_missing_ref_or_removing_ref( + &mut err, + span, + var_ty, + self.resolve_vars_with_obligations(ty), + ba, + ); err.emit(); } } + fn suggest_adding_missing_ref_or_removing_ref( + &self, + err: &mut Diagnostic, + span: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + ba: hir::BindingAnnotation, + ) { + match (expected.kind(), actual.kind(), ba) { + (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) + if self.can_eq(self.param_env, *inner_ty, actual).is_ok() => + { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider adding `ref`", + "ref ", + Applicability::MaybeIncorrect, + ); + } + (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) + if self.can_eq(self.param_env, expected, *inner_ty).is_ok() => + { + err.span_suggestion_verbose( + span.with_hi(span.lo() + BytePos(4)), + "consider removing `ref`", + "", + Applicability::MaybeIncorrect, + ); + } + _ => (), + } + } + // Precondition: pat is a Ref(_) pattern fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) { let tcx = self.tcx; @@ -882,7 +927,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) { - hir::Node::Pat(Pat { kind: hir::PatKind::Struct(..), .. }) => { + hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), "bind the struct field to a different name instead", @@ -936,7 +981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, qpath: &'tcx hir::QPath<'tcx>, subpats: &'tcx [Pat<'tcx>], - ddpos: Option, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, @@ -1021,7 +1066,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check subpatterns. if subpats.len() == variant.fields.len() - || subpats.len() < variant.fields.len() && ddpos.is_some() + || subpats.len() < variant.fields.len() && ddpos.as_opt_usize().is_some() { let ty::Adt(_, substs) = pat_ty.kind() else { bug!("unexpected pattern type {:?}", pat_ty); @@ -1209,14 +1254,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, elements: &'tcx [Pat<'tcx>], - ddpos: Option, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); - if ddpos.is_some() { + if ddpos.as_opt_usize().is_some() { // Require known type only when `..` is present. if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() { expected_len = tys.len(); @@ -1352,7 +1397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .copied() .filter(|(field, _)| { - field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) + field.vis.is_accessible_from(tcx.parent_module(pat.hir_id), tcx) && !matches!( tcx.eval_stability(field.did, None, DUMMY_SP, None), EvalResult::Deny { .. } diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index 0081e9049..b89db79be 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -126,6 +126,29 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h for (i, statement) in blk.stmts.iter().enumerate() { match statement.kind { + hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => { + // Let-else has a special lexical structure for variables. + // First we take a checkpoint of the current scope context here. + let mut prev_cx = visitor.cx; + + visitor.enter_scope(Scope { + id: blk.hir_id.local_id, + data: ScopeData::Remainder(FirstStatementIndex::new(i)), + }); + visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement); + // We need to back out temporarily to the last enclosing scope + // for the `else` block, so that even the temporaries receiving + // extended lifetime will be dropped inside this block. + // We are visiting the `else` block in this order so that + // the sequence of visits agree with the order in the default + // `hir::intravisit` visitor. + mem::swap(&mut prev_cx, &mut visitor.cx); + visitor.terminating_scopes.insert(els.hir_id.local_id); + visitor.visit_block(els); + // From now on, we continue normally. + visitor.cx = prev_cx; + } hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => { // Each declaration introduces a subscope for bindings // introduced by the declaration; this subscope covers a @@ -138,10 +161,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement) } - hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), } - visitor.visit_stmt(statement) } walk_list!(visitor, visit_expr, &blk.expr); } @@ -460,7 +483,6 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, - els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -547,9 +569,6 @@ fn resolve_local<'tcx>( if let Some(pat) = pat { visitor.visit_pat(pat); } - if let Some(els) = els { - visitor.visit_block(els); - } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -587,8 +606,7 @@ fn resolve_local<'tcx>( // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). match pat.kind { - PatKind::Binding(hir::BindingAnnotation::Ref, ..) - | PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true, + PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true, PatKind::Struct(_, ref field_pats, _) => { field_pats.iter().any(|fp| is_binding_pat(&fp.pat)) @@ -607,10 +625,7 @@ fn resolve_local<'tcx>( PatKind::Box(ref subpat) => is_binding_pat(&subpat), PatKind::Ref(_, _) - | PatKind::Binding( - hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, - .., - ) + | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Path(_) | PatKind::Lit(_) @@ -770,7 +785,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value), None); + resolve_local(self, None, Some(&body.value)); } if body.generator_kind.is_some() { @@ -797,7 +812,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init, l.els) + resolve_local(self, Some(&l.pat), l.init) } } diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs deleted file mode 100644 index d49a6138f..000000000 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::outlives::outlives_bounds::InferCtxtExt as _; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::Ty; - -pub(crate) trait OutlivesEnvironmentExt<'tcx> { - fn add_implied_bounds( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - fn_sig_tys: FxHashSet>, - body_id: hir::HirId, - ); -} - -impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> { - /// This method adds "implied bounds" into the outlives environment. - /// Implied bounds are outlives relationships that we can deduce - /// on the basis that certain types must be well-formed -- these are - /// either the types that appear in the function signature or else - /// the input types to an impl. For example, if you have a function - /// like - /// - /// ``` - /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { } - /// ``` - /// - /// we can assume in the caller's body that `'b: 'a` and that `T: - /// 'b` (and hence, transitively, that `T: 'a`). This method would - /// add those assumptions into the outlives-environment. - /// - /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs` - #[instrument(level = "debug", skip(self, infcx))] - fn add_implied_bounds<'a>( - &mut self, - infcx: &InferCtxt<'a, 'tcx>, - fn_sig_tys: FxHashSet>, - body_id: hir::HirId, - ) { - for ty in fn_sig_tys { - let ty = infcx.resolve_vars_if_possible(ty); - let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty); - self.add_outlives_bounds(Some(infcx), implied_bounds) - } - } -} diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index dd8f943b9..0b207a6c0 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -1217,7 +1217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine all the reasons of why the root variable should be captured as a result of // auto trait implementation issues - auto_trait_migration_reasons.extend(capture_trait_reasons.clone()); + auto_trait_migration_reasons.extend(capture_trait_reasons.iter().copied()); diagnostics_info.push(MigrationLintNote { captures_info, @@ -2024,6 +2024,10 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( tcx: TyCtxt<'_>, closure_id: hir::HirId, ) -> bool { + if tcx.sess.rust_2021() { + return false; + } + let (level, _) = tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index d0334cd0d..27b3da8ab 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1,5 +1,5 @@ -use crate::check::regionck::OutlivesEnvironmentExt; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; +use hir::def::DefKind; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; @@ -10,7 +10,7 @@ use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs}; use rustc_infer::infer::outlives::obligations::TypeOutlives; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::Normalized; +use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -23,9 +23,8 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::query::normalize::AtExt; -use rustc_trait_selection::traits::query::NoSolution; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, }; @@ -72,9 +71,11 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ) { let cause = traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)); + // for a type to be WF, we do not need to check if const trait predicates satisfy. + let param_env = self.param_env.without_const(); self.ocx.register_obligation(traits::Obligation::new( cause, - self.param_env, + param_env, ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()), )); } @@ -86,26 +87,31 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( body_def_id: LocalDefId, f: F, ) where - F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet>, + F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>), { let param_env = tcx.param_env(body_def_id); let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id); tcx.infer_ctxt().enter(|ref infcx| { let ocx = ObligationCtxt::new(infcx); + + let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id); + let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; if !tcx.features().trivial_bounds { wfcx.check_false_global_bounds() } - let wf_tys = f(&mut wfcx); + f(&mut wfcx); let errors = wfcx.select_all_or_error(); if !errors.is_empty() { infcx.report_fulfillment_errors(&errors, None, false); return; } - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, wf_tys, body_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); + infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); }) } @@ -383,7 +389,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe tcx, param_env, item_hir_id, - sig.output(), + sig.inputs_and_output, // We also assume that all of the function signature's parameter types // are well formed. &sig.inputs().iter().copied().collect(), @@ -658,7 +664,7 @@ fn ty_known_to_outlive<'tcx>( resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| { let origin = infer::RelateParamBound(DUMMY_SP, ty, None); let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env); - outlives.type_must_outlive(origin, ty, region); + outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation); }) } @@ -676,7 +682,12 @@ fn region_known_to_outlive<'tcx>( use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; let origin = infer::RelateRegionParamBound(DUMMY_SP); // `region_a: region_b` -> `region_b <= region_a` - infcx.push_sub_region_constraint(origin, region_b, region_a); + infcx.push_sub_region_constraint( + origin, + region_b, + region_a, + ConstraintCategory::BoringNoLocation, + ); }) } @@ -694,8 +705,11 @@ fn resolve_regions_with_wf_tys<'tcx>( // region constraints get added and solved there and we need to test each // call individually. tcx.infer_ctxt().enter(|infcx| { - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id); + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(&infcx), + infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), + ); let region_bound_pairs = outlives_environment.region_bound_pairs(); add_constraints(&infcx, region_bound_pairs); @@ -761,7 +775,7 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { match ty.kind { hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments { - [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()), + [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()), _ => false, }, _ => false, @@ -965,7 +979,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { } } -#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))] +#[instrument(level = "debug", skip(tcx, span, sig_if_method))] fn check_associated_item( tcx: TyCtxt<'_>, item_id: LocalDefId, @@ -976,15 +990,9 @@ fn check_associated_item( enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| { let item = tcx.associated_item(item_id); - let (mut implied_bounds, self_ty) = match item.container { - ty::TraitContainer => (FxHashSet::default(), tcx.types.self_param), - ty::ImplContainer => { - let def_id = item.container_id(tcx); - ( - impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span), - tcx.type_of(def_id), - ) - } + let self_ty = match item.container { + ty::TraitContainer => tcx.types.self_param, + ty::ImplContainer => tcx.type_of(item.container_id(tcx)), }; match item.kind { @@ -1002,7 +1010,6 @@ fn check_associated_item( sig, hir_sig.decl, item.def_id.expect_local(), - &mut implied_bounds, ); check_method_receiver(wfcx, hir_sig, item, self_ty); } @@ -1017,8 +1024,6 @@ fn check_associated_item( } } } - - implied_bounds }) } @@ -1118,9 +1123,6 @@ fn check_type_defn<'tcx, F>( } check_where_clauses(wfcx, item.span, item.def_id); - - // No implied bounds in a struct definition. - FxHashSet::default() }); } @@ -1144,9 +1146,7 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { } enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { - check_where_clauses(wfcx, item.span, item.def_id); - - FxHashSet::default() + check_where_clauses(wfcx, item.span, item.def_id) }); // Only check traits, don't check trait aliases @@ -1186,9 +1186,7 @@ fn check_item_fn( ) { enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| { let sig = tcx.fn_sig(def_id); - let mut implied_bounds = FxHashSet::default(); - check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds); - implied_bounds + check_fn_or_method(wfcx, ident.span, sig, decl, def_id); }) } @@ -1231,13 +1229,10 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo tcx.require_lang_item(LangItem::Sync, Some(ty_span)), ); } - - // No implied bounds in a const, etc. - FxHashSet::default() }); } -#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] +#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] fn check_impl<'tcx>( tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>, @@ -1274,7 +1269,11 @@ fn check_impl<'tcx>( } None => { let self_ty = tcx.type_of(item.def_id); - let self_ty = wfcx.normalize(item.span, None, self_ty); + let self_ty = wfcx.normalize( + item.span, + Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), + self_ty, + ); wfcx.register_wf_obligation( ast_self_ty.span, Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), @@ -1284,8 +1283,6 @@ fn check_impl<'tcx>( } check_where_clauses(wfcx, item.span, item.def_id); - - impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span) }); } @@ -1321,7 +1318,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id // parameter includes another (e.g., ``). In those cases, we can't // be sure if it will error or not as user might always specify the other. if !ty.needs_subst() { - wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into()); + wfcx.register_wf_obligation( + tcx.def_span(param.def_id), + Some(WellFormedLoc::Ty(param.def_id.expect_local())), + ty.into(), + ); } } } @@ -1465,21 +1466,26 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| { - traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp) + traits::wf::predicate_obligations( + infcx, + wfcx.param_env.without_const(), + wfcx.body_id, + p, + sp, + ) }); let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect(); wfcx.register_obligations(obligations); } -#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))] +#[instrument(level = "debug", skip(wfcx, span, hir_decl))] fn check_fn_or_method<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, sig: ty::PolyFnSig<'tcx>, hir_decl: &hir::FnDecl<'_>, def_id: LocalDefId, - implied_bounds: &mut FxHashSet>, ) { let tcx = wfcx.tcx(); let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); @@ -1521,23 +1527,66 @@ fn check_fn_or_method<'tcx>( ); } - implied_bounds.extend(sig.inputs()); - - wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into()); + wfcx.register_wf_obligation( + hir_decl.output.span(), + Some(WellFormedLoc::Param { + function: def_id, + param_idx: sig.inputs().len().try_into().unwrap(), + }), + sig.output().into(), + ); - // FIXME(#27579) return types should not be implied bounds - implied_bounds.insert(sig.output()); + check_where_clauses(wfcx, span, def_id); - debug!(?implied_bounds); + check_return_position_impl_trait_in_trait_bounds( + tcx, + wfcx, + def_id, + sig.output(), + hir_decl.output.span(), + ); +} - check_where_clauses(wfcx, span, def_id); +/// Basically `check_associated_type_bounds`, but separated for now and should be +/// deduplicated when RPITITs get lowered into real associated items. +fn check_return_position_impl_trait_in_trait_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + wfcx: &WfCheckingCtxt<'_, 'tcx>, + fn_def_id: LocalDefId, + fn_output: Ty<'tcx>, + span: Span, +) { + if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) + && assoc_item.container == ty::AssocItemContainer::TraitContainer + { + for arg in fn_output.walk() { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Projection(proj) = ty.kind() + && tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder + && tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id() + { + let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id); + let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { + let normalized_bound = wfcx.normalize(span, None, bound); + traits::wf::predicate_obligations( + wfcx.infcx, + wfcx.param_env, + wfcx.body_id, + normalized_bound, + bound_span, + ) + }); + wfcx.register_obligations(wf_obligations); + } + } + } } const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box`, \ `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one \ of the previous types except `Self`)"; -#[tracing::instrument(level = "debug", skip(wfcx))] +#[instrument(level = "debug", skip(wfcx))] fn check_method_receiver<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, fn_sig: &hir::FnSig<'_>, @@ -1817,6 +1866,7 @@ fn report_bivariance( impl<'tcx> WfCheckingCtxt<'_, 'tcx> { /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that /// aren't true. + #[instrument(level = "debug", skip(self))] fn check_false_global_bounds(&mut self) { let tcx = self.ocx.infcx.tcx; let mut span = self.span; @@ -1924,40 +1974,6 @@ impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> { } } -pub fn impl_implied_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: LocalDefId, - span: Span, -) -> FxHashSet> { - // We completely ignore any obligations caused by normalizing the types - // we assume to be well formed. Considering that the user of the implied - // bounds will also normalize them, we leave it to them to emit errors - // which should result in better causes and spans. - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id)); - match tcx.impl_trait_ref(impl_def_id) { - Some(trait_ref) => { - // Trait impl: take implied bounds from all types that - // appear in the trait reference. - match infcx.at(&cause, param_env).normalize(trait_ref) { - Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(), - Err(NoSolution) => FxHashSet::default(), - } - } - - None => { - // Inherent impl: take implied bounds from the `self` type. - let self_ty = tcx.type_of(impl_def_id); - match infcx.at(&cause, param_env).normalize(self_ty) { - Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]), - Err(NoSolution) => FxHashSet::default(), - } - } - } - }) -} - fn error_392( tcx: TyCtxt<'_>, span: Span, diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index f549807c3..9ecf34e9a 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -3,7 +3,6 @@ // substitutions. use crate::check::FnCtxt; - use hir::def_id::LocalDefId; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; @@ -16,6 +15,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::TypeckResults; use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -192,6 +192,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + // (ouz-a 1005988): Normally `[T] : std::ops::Index` should be normalized + // into [T] but currently `Where` clause stops the normalization process for it, + // here we compare types of expr and base in a code without `Where` clause they would be equal + // if they are not we don't modify the expr, hence we bypass the ICE + fn is_builtin_index( + &mut self, + typeck_results: &TypeckResults<'tcx>, + e: &hir::Expr<'_>, + base_ty: Ty<'tcx>, + index_ty: Ty<'tcx>, + ) -> bool { + if let Some(elem_ty) = base_ty.builtin_index() { + let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;}; + let resolved_exp_ty = self.resolve(exp_ty, &e.span); + + elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize + } else { + false + } + } + // Similar to operators, indexing is always assumed to be overloaded // Here, correct cases where an indexing expression can be simplified // to use builtin indexing because the index type is known to be @@ -222,8 +243,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ) }); let index_ty = self.fcx.resolve_vars_if_possible(index_ty); + let resolved_base_ty = self.resolve(*base_ty, &base.span); - if base_ty.builtin_index().is_some() && index_ty == self.fcx.tcx.types.usize { + if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) { // Remove the method call record typeck_results.type_dependent_defs_mut().remove(e.hir_id); typeck_results.node_substs_mut().remove(e.hir_id); @@ -292,6 +314,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_expr(self, e); } + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + match &p.kind { + hir::GenericParamKind::Lifetime { .. } => { + // Nothing to write back here + } + hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { + self.tcx().sess.delay_span_bug(p.span, format!("unexpected generic param: {p:?}")); + } + } + } + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { self.visit_node_id(b.span, b.hir_id); intravisit::walk_block(self, b); @@ -468,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if !errors_buffer.is_empty() { errors_buffer.sort_by_key(|diag| diag.span.primary_span()); - for mut diag in errors_buffer.drain(..) { + for mut diag in errors_buffer { self.tcx().sess.diagnostic().emit_diagnostic(&mut diag); } } -- cgit v1.2.3