summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck/src/fn_ctxt
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /compiler/rustc_hir_typeck/src/fn_ctxt
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_typeck/src/fn_ctxt')
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs203
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs136
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs45
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs354
5 files changed, 417 insertions, 331 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 952d27262..6ed8adb47 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1,7 +1,7 @@
use crate::callee::{self, DeferredCallResolution};
use crate::method::{self, MethodCallee, SelfSource};
use crate::rvalue_scopes;
-use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
+use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -10,6 +10,9 @@ use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, GenericArg, Node, QPath};
+use rustc_hir_analysis::astconv::generics::{
+ check_generic_arg_count_for_call, create_substs_for_generic_args,
+};
use rustc_hir_analysis::astconv::{
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
GenericArgCountResult, IsMethodCall, PathSeg,
@@ -22,9 +25,9 @@ use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{
- self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, Ty, UserType,
+ self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, UserType,
};
-use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts};
+use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
@@ -161,47 +164,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
self.write_substs(hir_id, method.substs);
-
- // When the method is confirmed, the `method.substs` includes
- // parameters from not just the method, but also the impl of
- // the method -- in particular, the `Self` type will be fully
- // resolved. However, those are not something that the "user
- // specified" -- i.e., those types come from the inferred type
- // of the receiver, not something the user wrote. So when we
- // create the user-substs, we want to replace those earlier
- // types with just the types that the user actually wrote --
- // that is, those that appear on the *method itself*.
- //
- // As an example, if the user wrote something like
- // `foo.bar::<u32>(...)` -- the `Self` type here will be the
- // type of `foo` (possibly adjusted), but we don't want to
- // include that. We want just the `[_, u32]` part.
- if !method.substs.is_empty() {
- let method_generics = self.tcx.generics_of(method.def_id);
- if !method_generics.params.is_empty() {
- let user_type_annotation = self.probe(|_| {
- let user_substs = UserSubsts {
- substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
- let i = param.index as usize;
- if i < method_generics.parent_count {
- self.var_for_def(DUMMY_SP, param)
- } else {
- method.substs[i]
- }
- }),
- user_self_ty: None, // not relevant here
- };
-
- self.canonicalize_user_type_annotation(UserType::TypeOf(
- method.def_id,
- user_substs,
- ))
- });
-
- debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
- self.write_user_type_annotation(hir_id, user_type_annotation);
- }
- }
}
pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
@@ -333,22 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- /// Basically whenever we are converting from a type scheme into
- /// the fn body space, we always want to normalize associated
- /// types as well. This function combines the two.
- fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: T) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs);
- let value = EarlyBinder(value).subst(self.tcx, substs);
- let result = self.normalize(span, value);
- debug!("instantiate_type_scheme = {:?}", result);
- result
- }
-
- /// As `instantiate_type_scheme`, but for the bounds found in a
- /// generic type scheme.
+ /// Instantiates and normalizes the bounds for a given item
pub(in super::super) fn instantiate_bounds(
&self,
span: Span,
@@ -425,23 +372,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
- let t = <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_t);
+ pub fn handle_raw_ty(&self, span: Span, ty: Ty<'tcx>) -> RawTy<'tcx> {
+ RawTy { raw: ty, normalized: self.normalize(span, ty) }
+ }
+
+ pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> RawTy<'tcx> {
+ let t = self.astconv().ast_ty_to_ty(ast_t);
self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None));
- t
+ self.handle_raw_ty(ast_t.span, t)
}
pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
let ty = self.to_ty(ast_ty);
debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
- if Self::can_contain_user_lifetime_bounds(ty) {
- let c_ty = self.canonicalize_response(UserType::Ty(ty));
+ if Self::can_contain_user_lifetime_bounds(ty.raw) {
+ let c_ty = self.canonicalize_response(UserType::Ty(ty.raw));
debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
}
- ty
+ ty.normalized
+ }
+
+ pub(super) fn user_substs_for_adt(ty: RawTy<'tcx>) -> UserSubsts<'tcx> {
+ match (ty.raw.kind(), ty.normalized.kind()) {
+ (ty::Adt(_, substs), _) => UserSubsts { substs, user_self_ty: None },
+ (_, ty::Adt(adt, substs)) => UserSubsts {
+ substs,
+ user_self_ty: Some(UserSelfTy { impl_def_id: adt.did(), self_ty: ty.raw }),
+ },
+ _ => bug!("non-adt type {:?}", ty),
+ }
}
pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
@@ -711,12 +673,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Note: this check is pessimistic, as the inference type could be matched with something other
// than the opaque type, but then we need a new `TypeRelation` just for this specific case and
// can't re-use `sup` below.
- // See src/test/ui/impl-trait/hidden-type-is-opaque.rs and
- // src/test/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path.
+ // See tests/ui/impl-trait/hidden-type-is-opaque.rs and
+ // tests/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path.
if formal_ret.has_infer_types() {
for ty in ret_ty.walk() {
if let ty::subst::GenericArgKind::Type(ty) = ty.unpack()
- && let ty::Opaque(def_id, _) = *ty.kind()
+ && let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *ty.kind()
&& let Some(def_id) = def_id.as_local()
&& self.opaque_type_origin(def_id, DUMMY_SP).is_some() {
return None;
@@ -795,7 +757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &'tcx QPath<'tcx>,
hir_id: hir::HirId,
span: Span,
- ) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
+ ) -> (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
debug!(
"resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}",
qpath, hir_id, span
@@ -818,7 +780,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// to be object-safe.
// We manually call `register_wf_obligation` in the success path
// below.
- (<dyn AstConv<'_>>::ast_ty_to_ty_in_path(self, qself), qself, segment)
+ let ty = self.astconv().ast_ty_to_ty_in_path(qself);
+ (self.handle_raw_ty(span, ty), qself, segment)
}
QPath::LangItem(..) => {
bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`")
@@ -826,7 +789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
{
- self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
+ self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None));
// Return directly on cache hit. This is useful to avoid doubly reporting
// errors with default match binding modes. See #44614.
let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id));
@@ -834,7 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let item_name = item_segment.ident;
let result = self
- .resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id)
+ .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
.or_else(|error| {
let result = match error {
method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
@@ -845,17 +808,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise,
// register a WF obligation so that we can detect any additional
// errors in the self type.
- if !(matches!(error, method::MethodError::NoMatch(_)) && ty.is_trait()) {
- self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
+ if !(matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait()) {
+ self.register_wf_obligation(
+ ty.raw.into(),
+ qself.span,
+ traits::WellFormed(None),
+ );
}
if item_name.name != kw::Empty {
if let Some(mut e) = self.report_method_error(
span,
- ty,
+ ty.normalized,
item_name,
SelfSource::QPath(qself),
error,
None,
+ Expectation::NoExpectation,
) {
e.emit();
}
@@ -864,7 +832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
if result.is_ok() {
- self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
+ self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None));
}
// Write back the new resolution.
@@ -1001,7 +969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn instantiate_value_path(
&self,
segments: &[hir::PathSegment<'_>],
- self_ty: Option<Ty<'tcx>>,
+ self_ty: Option<RawTy<'tcx>>,
res: Res,
span: Span,
hir_id: hir::HirId,
@@ -1010,8 +978,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let path_segs = match res {
Res::Local(_) | Res::SelfCtor(_) => vec![],
- Res::Def(kind, def_id) => <dyn AstConv<'_>>::def_ids_for_value_path_segments(
- self, segments, self_ty, kind, def_id,
+ Res::Def(kind, def_id) => self.astconv().def_ids_for_value_path_segments(
+ segments,
+ self_ty.map(|ty| ty.raw),
+ kind,
+ def_id,
+ span,
),
_ => bug!("instantiate_value_path on {:?}", res),
};
@@ -1022,8 +994,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Res::Def(DefKind::Ctor(CtorOf::Variant, _), _)
if let Some(self_ty) = self_ty =>
{
- let adt_def = self_ty.ty_adt_def().unwrap();
- user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty });
+ let adt_def = self_ty.normalized.ty_adt_def().unwrap();
+ user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty: self_ty.raw });
is_alias_variant_ctor = true;
}
Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
@@ -1042,7 +1014,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// inherent impl, we need to record the
// `T` for posterity (see `UserSelfTy` for
// details).
- let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
+ let self_ty = self_ty.expect("UFCS sugared assoc missing Self").raw;
user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty });
}
}
@@ -1057,8 +1029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// errors if type parameters are provided in an inappropriate place.
let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
- let generics_has_err = <dyn AstConv<'_>>::prohibit_generics(
- self,
+ let generics_has_err = self.astconv().prohibit_generics(
segments.iter().enumerate().filter_map(|(index, seg)| {
if !generic_segs.contains(&index) || is_alias_variant_ctor {
Some(seg)
@@ -1099,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// parameter internally, but we don't allow users to specify the
// parameter's value explicitly, so we have to do some error-
// checking here.
- let arg_count = <dyn AstConv<'_>>::check_generic_arg_count_for_call(
+ let arg_count = check_generic_arg_count_for_call(
tcx,
span,
def_id,
@@ -1124,19 +1095,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.unwrap_or(false);
let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
- let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
- match *ty.kind() {
- ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
- let variant = adt_def.non_enum_variant();
- let (ctor_kind, ctor_def_id) = variant.ctor.unwrap();
- (Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id), Some(substs))
+ let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id));
+ match ty.normalized.ty_adt_def() {
+ Some(adt_def) if adt_def.has_ctor() => {
+ let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap();
+ let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id);
+ let user_substs = Self::user_substs_for_adt(ty);
+ user_self_ty = user_substs.user_self_ty;
+ (new_res, Some(user_substs.substs))
}
_ => {
let mut err = tcx.sess.struct_span_err(
span,
"the `Self` constructor can only be used with tuple or unit structs",
);
- if let Some(adt_def) = ty.ty_adt_def() {
+ if let Some(adt_def) = ty.normalized.ty_adt_def() {
match adt_def.adt_kind() {
AdtKind::Enum => {
err.help("did you mean to use one of the enum's variants?");
@@ -1160,10 +1133,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let def_id = res.def_id();
- // The things we are substituting into the type should not contain
- // escaping late-bound regions, and nor should the base type scheme.
- let ty = tcx.type_of(def_id);
-
let arg_count = GenericArgCountResult {
explicit_late_bound,
correct: if infer_args_for_err.is_empty() {
@@ -1209,10 +1178,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> ty::GenericArg<'tcx> {
match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
- <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into()
+ self.fcx.astconv().ast_region_to_region(lt, Some(param)).into()
}
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
- self.fcx.to_ty(ty).into()
+ self.fcx.to_ty(ty).raw.into()
}
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
self.fcx.const_arg_to_const(&ct.value, param.def_id).into()
@@ -1244,10 +1213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If we have a default, then we it doesn't matter that we're not
// inferring the type arguments: we provide the default where any
// is missing.
- let default = tcx.bound_type_of(param.def_id);
- self.fcx
- .normalize_ty(self.span, default.subst(tcx, substs.unwrap()))
- .into()
+ tcx.bound_type_of(param.def_id).subst(tcx, substs.unwrap()).into()
} else {
// If no type arguments were provided, we have to infer them.
// This case also occurs as a result of some malformed input, e.g.
@@ -1258,9 +1224,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
GenericParamDefKind::Const { has_default } => {
if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
+ tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
} else {
self.fcx.var_for_def(self.span, param)
}
@@ -1269,13 +1233,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- let substs = self_ctor_substs.unwrap_or_else(|| {
- <dyn AstConv<'_>>::create_substs_for_generic_args(
+ let substs_raw = self_ctor_substs.unwrap_or_else(|| {
+ create_substs_for_generic_args(
tcx,
def_id,
&[],
has_self,
- self_ty,
+ self_ty.map(|s| s.raw),
&arg_count,
&mut CreateCtorSubstsContext {
fcx: self,
@@ -1286,17 +1250,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
)
});
- assert!(!substs.has_escaping_bound_vars());
- assert!(!ty.has_escaping_bound_vars());
// First, store the "user substs" for later.
- self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
+ self.write_user_type_annotation_from_substs(hir_id, def_id, substs_raw, user_self_ty);
+
+ // Normalize only after registering type annotations.
+ let substs = self.normalize(span, substs_raw);
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.
- let ty_substituted = self.instantiate_type_scheme(span, &substs, ty);
+ let ty = tcx.bound_type_of(def_id);
+ assert!(!substs.has_escaping_bound_vars());
+ assert!(!ty.0.has_escaping_bound_vars());
+ let ty_substituted = self.normalize(span, ty.subst(tcx, substs));
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
// In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
@@ -1304,9 +1272,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// type parameters, which we can infer by unifying the provided `Self`
// with the substituted impl type.
// This also occurs for an enum variant on a type alias.
- let ty = tcx.type_of(impl_def_id);
-
- let impl_ty = self.instantiate_type_scheme(span, &substs, ty);
+ let impl_ty = self.normalize(span, tcx.bound_type_of(impl_def_id).subst(tcx, substs));
+ let self_ty = self.normalize(span, self_ty);
match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) {
Ok(ok) => self.register_infer_ok_obligations(ok),
Err(_) => {
@@ -1455,9 +1422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
let mut contained_in_place = false;
- while let hir::Node::Expr(parent_expr) =
- self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
- {
+ while let hir::Node::Expr(parent_expr) = self.tcx.hir().get_parent(expr_id) {
match &parent_expr.kind {
hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
if lhs.hir_id == expr_id {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
index fc83994ca..6f26afcaf 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
@@ -4,15 +4,13 @@ use rustc_index::vec::IndexVec;
use rustc_middle::ty::error::TypeError;
rustc_index::newtype_index! {
- pub(crate) struct ExpectedIdx {
- DEBUG_FORMAT = "ExpectedIdx({})",
- }
+ #[debug_format = "ExpectedIdx({})"]
+ pub(crate) struct ExpectedIdx {}
}
rustc_index::newtype_index! {
- pub(crate) struct ProvidedIdx {
- DEBUG_FORMAT = "ProvidedIdx({})",
- }
+ #[debug_format = "ProvidedIdx({})"]
+ pub(crate) struct ProvidedIdx {}
}
impl ExpectedIdx {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 60fec05d3..2d841d53f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -5,11 +5,11 @@ use crate::method::MethodCallee;
use crate::Expectation::*;
use crate::TupleArgumentsFlag::*;
use crate::{
- struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs,
+ struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
TupleArgumentsFlag,
};
use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -28,7 +28,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor};
use rustc_session::Session;
-use rustc_span::symbol::Ident;
+use rustc_span::symbol::{kw, Ident};
use rustc_span::{self, sym, Span};
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
@@ -214,7 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"cannot use call notation; the first type parameter \
for the function trait is neither a tuple nor unit"
)
- .delay_as_bug();
+ .emit();
(self.err_args(provided_args.len()), None)
}
}
@@ -473,7 +473,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, is_method) = match &call_expr.kind {
+ let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind {
hir::ExprKind::Call(
hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
_,
@@ -481,12 +481,16 @@ 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), false)
+ let name = match of {
+ CtorOf::Struct => "struct",
+ CtorOf::Variant => "enum variant",
+ };
+ (call_span, *span, name, false)
} else {
- (call_span, *span, None, false)
+ (call_span, *span, "function", false)
}
}
- hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false),
+ hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, "function", false),
hir::ExprKind::MethodCall(path_segment, _, _, span) => {
let ident_span = path_segment.ident.span;
let ident_span = if let Some(args) = path_segment.args {
@@ -494,17 +498,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
ident_span
};
- // methods are never ctors
- (*span, ident_span, None, true)
+ (*span, ident_span, "method", true)
}
k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
};
let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span);
- let call_name = match ctor_of {
- Some(CtorOf::Struct) => "struct",
- Some(CtorOf::Variant) => "enum variant",
- None => "function",
- };
// Don't print if it has error types or is just plain `_`
fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
@@ -690,8 +688,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err = tcx.sess.struct_span_err_with_code(
full_call_span,
&format!(
- "this {} takes {}{} but {} {} supplied",
- call_name,
+ "{call_name} takes {}{} but {} {} supplied",
if c_variadic { "at least " } else { "" },
potentially_plural_count(
formal_and_expected_inputs.len(),
@@ -1013,7 +1010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
args_span
};
- labels.push((span, format!("multiple arguments are missing")));
+ labels.push((span, "multiple arguments are missing".to_string()));
suggestion_text = match suggestion_text {
SuggestionText::None | SuggestionText::Provide(_) => {
SuggestionText::Provide(true)
@@ -1141,6 +1138,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"()".to_string()
} else if expected_ty.is_suggestable(tcx, false) {
format!("/* {} */", expected_ty)
+ } else if let Some(fn_def_id) = fn_def_id
+ && self.tcx.def_kind(fn_def_id).is_fn_like()
+ && let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize
+ && let Some(arg) = self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit)
+ && arg.name != kw::SelfLower
+ {
+ format!("/* {} */", arg.name)
} else {
"/* value */".to_string()
}
@@ -1169,7 +1173,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match lit.node {
ast::LitKind::Str(..) => tcx.mk_static_str(),
- ast::LitKind::ByteStr(ref v) => {
+ ast::LitKind::ByteStr(ref v, _) => {
tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64))
}
ast::LitKind::Byte(_) => tcx.types.u8,
@@ -1215,31 +1219,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
return None;
}
- Res::Def(DefKind::Variant, _) => match ty.kind() {
- ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)),
- _ => bug!("unexpected type: {:?}", ty),
+ Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() {
+ Some(adt) => {
+ Some((adt.variant_of_res(def), adt.did(), Self::user_substs_for_adt(ty)))
+ }
+ _ => bug!("unexpected type: {:?}", ty.normalized),
},
Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
| Res::SelfTyParam { .. }
- | Res::SelfTyAlias { .. } => match ty.kind() {
- ty::Adt(adt, substs) if !adt.is_enum() => {
- Some((adt.non_enum_variant(), adt.did(), substs))
+ | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() {
+ Some(adt) if !adt.is_enum() => {
+ Some((adt.non_enum_variant(), adt.did(), Self::user_substs_for_adt(ty)))
}
_ => None,
},
_ => bug!("unexpected definition: {:?}", def),
};
- if let Some((variant, did, substs)) = variant {
+ if let Some((variant, did, ty::UserSubsts { substs, user_self_ty })) = variant {
debug!("check_struct_path: did={:?} substs={:?}", did, substs);
- self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
+
+ // Register type annotation.
+ self.write_user_type_annotation_from_substs(hir_id, did, substs, user_self_ty);
// Check bounds on type arguments used in the path.
self.add_required_obligations_for_hir(path_span, did, substs, hir_id);
- Some((variant, ty))
+ Some((variant, ty.normalized))
} else {
- match ty.kind() {
+ match ty.normalized.kind() {
ty::Error(_) => {
// E0071 might be caused by a spelling error, which will have
// already caused an error message and probably a suggestion
@@ -1252,7 +1260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
path_span,
E0071,
"expected struct, variant or union type, found {}",
- ty.sort_string(self.tcx)
+ ty.normalized.sort_string(self.tcx)
)
.span_label(path_span, "not a struct")
.emit();
@@ -1300,7 +1308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Type check the initializer.
if let Some(ref init) = decl.init {
let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init);
- self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty);
+ self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, init_ty);
}
// Does the expected pattern type originate from an expression and what is the span?
@@ -1315,7 +1323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Type check the pattern. Override if necessary to avoid knock-on errors.
self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
let pat_ty = self.node_ty(decl.pat.hir_id);
- self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty);
+ self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty);
if let Some(blk) = decl.els {
let previous_diverges = self.diverges.get();
@@ -1620,14 +1628,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
hir_id: hir::HirId,
pat: &'tcx hir::Pat<'tcx>,
- decl_ty: Ty<'tcx>,
ty: Ty<'tcx>,
) {
if ty.references_error() {
// Override the types everywhere with `err()` to avoid knock on errors.
- self.write_ty(hir_id, ty);
- self.write_ty(pat.hir_id, ty);
- let local_ty = LocalTy { decl_ty, revealed_ty: ty };
+ let err = self.tcx.ty_error();
+ self.write_ty(hir_id, err);
+ self.write_ty(pat.hir_id, err);
+ let local_ty = LocalTy { decl_ty: err, revealed_ty: err };
self.locals.borrow_mut().insert(hir_id, local_ty);
self.locals.borrow_mut().insert(pat.hir_id, local_ty);
}
@@ -1640,20 +1648,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &QPath<'_>,
path_span: Span,
hir_id: hir::HirId,
- ) -> (Res, Ty<'tcx>) {
+ ) -> (Res, RawTy<'tcx>) {
match *qpath {
QPath::Resolved(ref maybe_qself, ref path) => {
- let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
- let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true);
- (path.res, ty)
+ let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw);
+ let ty = self.astconv().res_to_ty(self_ty, path, true);
+ (path.res, self.handle_raw_ty(path_span, ty))
}
QPath::TypeRelative(ref qself, ref segment) => {
let ty = self.to_ty(qself);
- let result = <dyn AstConv<'_>>::associated_path_to_ty(
- self, hir_id, path_span, ty, qself, segment, true,
- );
+ let result = self
+ .astconv()
+ .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true);
let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
+ let ty = self.handle_raw_ty(path_span, ty);
let result = result.map(|(_, kind, def_id)| (kind, def_id));
// Write back the new resolution.
@@ -1662,7 +1671,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty)
}
QPath::LangItem(lang_item, span, id) => {
- self.resolve_lang_item_path(lang_item, span, hir_id, id)
+ let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id, id);
+ (res, self.handle_raw_ty(path_span, ty))
}
}
}
@@ -1682,7 +1692,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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 remap_cause = FxIndexSet::default();
let mut not_adjusted = vec![];
for error in errors {
@@ -1710,6 +1720,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ // Adjust any other errors that come from other cause codes, when these
+ // errors are of the same predicate as one we successfully adjusted, and
+ // when their spans overlap (suggesting they're due to the same root cause).
+ //
+ // This is because due to normalization, we often register duplicate
+ // obligations with misc obligations that are basically impossible to
+ // line back up with a useful ExprBindingObligation.
for error in not_adjusted {
for (span, predicate, cause) in &remap_cause {
if *predicate == error.obligation.predicate
@@ -1796,7 +1813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir_id: call_hir_id,
span: call_span,
..
- }) = hir.get(hir.get_parent_node(expr.hir_id))
+ }) = hir.get_parent(expr.hir_id)
&& callee.hir_id == expr.hir_id
{
if self.closure_span_overlaps_error(error, *call_span) {
@@ -2111,8 +2128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// FIXME(compiler-errors): This could be problematic if something has two
// fn-like predicates with different args, but callable types really never
// do that, so it's OK.
- for (predicate, span) in
- std::iter::zip(instantiated.predicates, instantiated.spans)
+ for (predicate, span) in instantiated
{
if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder()
&& pred.self_ty().peel_refs() == callee_ty
@@ -2124,7 +2140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
- ty::Opaque(new_def_id, _)
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id: new_def_id, .. })
| ty::Closure(new_def_id, _)
| ty::FnDef(new_def_id, _) => {
def_id = new_def_id;
@@ -2132,19 +2148,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => {
// Look for a user-provided impl of a `Fn` trait, and point to it.
let new_def_id = self.probe(|_| {
- let trait_ref = ty::TraitRef::new(
+ let trait_ref = self.tcx.mk_trait_ref(
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(),
- ),
+ [
+ callee_ty,
+ self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span: rustc_span::DUMMY_SP,
+ }),
+ ],
);
let obligation = traits::Obligation::new(
self.tcx,
@@ -2217,7 +2229,7 @@ fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>)
if arg == param_to_point_at {
return true;
} else if let ty::GenericArgKind::Type(ty) = arg.unpack()
- && let ty::Projection(..) = ty.kind()
+ && let ty::Alias(ty::Projection, ..) = ty.kind()
{
// This logic may seem a bit strange, but typically when
// we have a projection type in a function signature, the
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 30b59da78..428fde642 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -17,11 +17,10 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Const, Ty, TyCtxt};
+use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitable};
use rustc_session::Session;
use rustc_span::symbol::Ident;
-use rustc_span::{self, Span};
+use rustc_span::{self, Span, DUMMY_SP};
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
use std::cell::{Cell, RefCell};
@@ -176,6 +175,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn_sig
})
}),
+ autoderef_steps: Box::new(|ty| {
+ let mut autoderef = self.autoderef(DUMMY_SP, ty).silence_errors();
+ let mut steps = vec![];
+ while let Some((ty, _)) = autoderef.next() {
+ steps.push((ty, autoderef.current_obligations()));
+ }
+ steps
+ }),
}
}
@@ -287,8 +294,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
poly_trait_ref,
);
- let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
- self,
+ let item_substs = self.astconv().create_substs_for_associated_item(
span,
item_def_id,
item_segment,
@@ -298,11 +304,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
self.tcx().mk_projection(item_def_id, item_substs)
}
- fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- if ty.has_escaping_bound_vars() {
- ty // FIXME: normalization and escaping regions
- } else {
- self.normalize(span, ty)
+ fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
+ match ty.kind() {
+ ty::Adt(adt_def, _) => Some(*adt_def),
+ // FIXME(#104767): Should we handle bound regions here?
+ ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => {
+ self.normalize(span, ty).ty_adt_def()
+ }
+ _ => None,
}
}
@@ -310,7 +319,21 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
self.infcx.set_tainted_by_errors(e)
}
- fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
+ fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
+ // FIXME: normalization and escaping regions
+ let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
self.write_ty(hir_id, ty)
}
}
+
+/// Represents a user-provided type in the raw form (never normalized).
+///
+/// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`,
+/// and the API in this module, which expect `Ty` to be fully normalized.
+#[derive(Clone, Copy, Debug)]
+pub struct RawTy<'tcx> {
+ pub raw: Ty<'tcx>,
+
+ /// The normalized form of `raw`, stored here for efficiency.
+ pub normalized: Ty<'tcx>,
+}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 4f92477b5..4d673ac91 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,33 +1,37 @@
use super::FnCtxt;
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
+use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind};
+use rustc_hir::def::{CtorKind, CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
};
use rustc_hir_analysis::astconv::AstConv;
-use rustc_infer::infer;
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty};
+use rustc_middle::ty::{
+ self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
+ TypeVisitable,
+};
use rustc_session::errors::ExprParenthesesNeeded;
-use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::source_map::Spanned;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::NormalizeExt;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
self.typeck_results
.borrow()
.liberated_fn_sigs()
- .get(self.tcx.hir().get_parent_node(self.body_id))
+ .get(self.tcx.hir().parent_id(self.body_id))
.copied()
}
@@ -89,7 +93,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
found: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
) -> bool {
- let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found)
else { return false; };
if can_satisfy(output) {
let (sugg_call, mut applicability) = match inputs.len() {
@@ -158,99 +162,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// 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>,
+ ty: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
- // 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::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- 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(&param, self.tcx).def_id;
- self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- && proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && 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.at(&self.misc(expr.span), self.param_env).normalize(output);
-
- if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
}
pub fn suggest_two_fn_call(
@@ -262,9 +176,9 @@ impl<'a, 'tcx> FnCtxt<'a, '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)
+ let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty)
else { return false; };
- let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+ let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty)
else { return false; };
if can_satisfy(lhs_output_ty, rhs_output_ty) {
@@ -317,11 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- err.multipart_suggestion_verbose(
- format!("use parentheses to call these"),
- sugg,
- applicability,
- );
+ err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability);
true
} else {
@@ -329,6 +239,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ pub fn suggest_remove_last_method_call(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ ) -> bool {
+ if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) = expr.kind &&
+ let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr) &&
+ self.can_coerce(recv_ty, expected) {
+ let span = if let Some(recv_span) = recv_expr.span.find_ancestor_inside(expr.span) {
+ expr.span.with_lo(recv_span.hi())
+ } else {
+ expr.span.with_lo(method.span.lo() - rustc_span::BytePos(1))
+ };
+ err.span_suggestion_verbose(
+ span,
+ "try removing the method call",
+ "",
+ Applicability::MachineApplicable,
+ );
+ return true;
+ }
+ false
+ }
+
pub fn suggest_deref_ref_or_into(
&self,
err: &mut Diagnostic,
@@ -391,10 +326,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
return true;
} 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)
+ && let ty::FnDef(def_id, ..) = *found.kind()
+ && let Some(sp) = self.tcx.hir().span_if_local(def_id)
{
- err.span_label(sp, format!("{found} defined here"));
+ let name = self.tcx.item_name(def_id);
+ let kind = self.tcx.def_kind(def_id);
+ if let DefKind::Ctor(of, CtorKind::Fn) = kind {
+ err.span_label(sp, format!("`{name}` defines {} constructor here, which should be called", match of {
+ CtorOf::Struct => "a struct",
+ CtorOf::Variant => "an enum variant",
+ }));
+ } else {
+ let descr = kind.descr(def_id);
+ err.span_label(sp, format!("{descr} `{name}` defined here"));
+ }
return true;
} else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
return true;
@@ -416,7 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& method_call_list.contains(&conversion_method.name)
// If receiver is `.clone()` and found type has one of those methods,
// we guess that the user wants to convert from a slice type (`&[]` or `&str`)
- // to an owned type (`Vec` or `String`). These conversions clone internally,
+ // to an owned type (`Vec` or `String`). These conversions clone internally,
// so we remove the user's `clone` call.
{
vec![(
@@ -613,10 +558,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
- // Check if the parent expression is a call to Pin::new. If it
+ // Check if the parent expression is a call to Pin::new. If it
// is and we were expecting a Box, ergo Pin<Box<expected>>, we
// can suggest Box::pin.
- let parent = self.tcx.hir().get_parent_node(expr.hir_id);
+ let parent = self.tcx.hir().parent_id(expr.hir_id);
let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else {
return false;
};
@@ -752,12 +697,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return true
}
}
- &hir::FnRetTy::Return(ref ty) => {
+ hir::FnRetTy::Return(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);
let span = ty.span;
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
+ let ty = self.astconv().ast_ty_to_ty(ty);
debug!("suggest_missing_return_type: return type {:?}", ty);
debug!("suggest_missing_return_type: expected type {:?}", ty);
let bound_vars = self.tcx.late_bound_vars(fn_id);
@@ -828,7 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
..
}) => {
// FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below)
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty);
+ let ty = self.astconv().ast_ty_to_ty(bounded_ty);
Some((ty, bounds))
}
_ => None,
@@ -866,7 +811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let all_bounds_str = all_matching_bounds_strs.join(" + ");
let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param);
+ let ty = self.astconv().ast_ty_to_ty( param);
matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
});
@@ -920,7 +865,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if let hir::FnRetTy::Return(ty) = fn_decl.output {
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
+ let ty = self.astconv().ast_ty_to_ty(ty);
let bound_vars = self.tcx.late_bound_vars(fn_id);
let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
let ty = match self.tcx.asyncness(fn_id.owner) {
@@ -948,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
) -> bool {
- let sp = self.tcx.sess.source_map().start_point(expr.span);
+ let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
// `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
@@ -988,6 +933,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ pub(crate) fn suggest_clone_for_ref(
+ &self,
+ diag: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expr_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind()
+ && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait()
+ && expected_ty == *inner_ty
+ && self
+ .infcx
+ .type_implements_trait(
+ clone_trait_def,
+ [self.tcx.erase_regions(expected_ty)],
+ self.param_env
+ )
+ .must_apply_modulo_regions()
+ {
+ diag.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ "consider using clone here",
+ ".clone()",
+ Applicability::MachineApplicable,
+ );
+ return true;
+ }
+ false
+ }
+
pub(crate) fn suggest_copied_or_cloned(
&self,
diag: &mut Diagnostic,
@@ -1126,9 +1101,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let hir = self.tcx.hir();
- let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| {
- matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
- }).next();
+ let cond_parent = hir.parent_iter(expr.hir_id).find(|(_, node)| {
+ !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
+ });
// Don't suggest:
// `let Some(_) = a.is_some() && b`
// ++++++++++
@@ -1234,10 +1209,114 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
true
}
+ ExprKind::Lit(Spanned {
+ node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
+ span,
+ }) => {
+ let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else { return false; };
+ if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
+ return false;
+ }
+ if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
+ return false;
+ }
+ let (_, suffix) = snippet.split_at(snippet.len() - 3);
+ let value = match suffix {
+ "f32" => (lit - 0xf32) / (16 * 16 * 16),
+ "f64" => (lit - 0xf64) / (16 * 16 * 16),
+ _ => return false,
+ };
+ err.span_suggestions(
+ expr.span,
+ "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
+ [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
+ Applicability::MaybeIncorrect,
+ );
+ true
+ }
_ => false,
}
}
+ pub(crate) fn suggest_associated_const(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ let Some((DefKind::AssocFn, old_def_id)) = self.typeck_results.borrow().type_dependent_def(expr.hir_id) else {
+ return false;
+ };
+ let old_item_name = self.tcx.item_name(old_def_id);
+ let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
+ if old_item_name == capitalized_name {
+ return false;
+ }
+ let (item, segment) = match expr.kind {
+ hir::ExprKind::Path(QPath::Resolved(
+ Some(ty),
+ hir::Path { segments: [segment], .. },
+ ))
+ | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
+ let self_ty = self.astconv().ast_ty_to_ty(ty);
+ if let Ok(pick) = self.probe_for_name(
+ Mode::Path,
+ Ident::new(capitalized_name, segment.ident.span),
+ Some(expected_ty),
+ IsSuggestion(true),
+ self_ty,
+ expr.hir_id,
+ ProbeScope::TraitsInScope,
+ ) {
+ (pick.item, segment)
+ } else {
+ return false;
+ }
+ }
+ hir::ExprKind::Path(QPath::Resolved(
+ None,
+ hir::Path { segments: [.., segment], .. },
+ )) => {
+ // we resolved through some path that doesn't end in the item name,
+ // better not do a bad suggestion by accident.
+ if old_item_name != segment.ident.name {
+ return false;
+ }
+ if let Some(item) = self
+ .tcx
+ .associated_items(self.tcx.parent(old_def_id))
+ .filter_by_name_unhygienic(capitalized_name)
+ .next()
+ {
+ (*item, segment)
+ } else {
+ return false;
+ }
+ }
+ _ => return false,
+ };
+ if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
+ // Same item
+ return false;
+ }
+ let item_ty = self.tcx.type_of(item.def_id);
+ // FIXME(compiler-errors): This check is *so* rudimentary
+ if item_ty.needs_subst() {
+ return false;
+ }
+ if self.can_coerce(item_ty, expected_ty) {
+ err.span_suggestion_verbose(
+ segment.ident.span,
+ format!("try referring to the associated const `{capitalized_name}` instead",),
+ capitalized_name,
+ Applicability::MachineApplicable,
+ );
+ true
+ } else {
+ false
+ }
+ }
+
fn is_loop(&self, id: hir::HirId) -> bool {
let node = self.tcx.hir().get(id);
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
@@ -1276,18 +1355,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
// Check that we're in fact trying to clone into the expected type
&& self.can_coerce(*pointee_ty, expected_ty)
+ && let trait_ref = ty::Binder::dummy(self.tcx.mk_trait_ref(clone_trait_did, [expected_ty]))
// And the expected type doesn't implement `Clone`
- && !self.predicate_must_hold_considering_regions(&traits::Obligation {
- cause: traits::ObligationCause::dummy(),
- param_env: self.param_env,
- recursion_depth: 0,
- predicate: ty::Binder::dummy(ty::TraitRef {
- def_id: clone_trait_did,
- substs: self.tcx.mk_substs([expected_ty.into()].iter()),
- })
- .without_const()
- .to_predicate(self.tcx),
- })
+ && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
+ self.tcx,
+ traits::ObligationCause::dummy(),
+ self.param_env,
+ trait_ref,
+ ))
{
diag.span_note(
callee_expr.span,
@@ -1295,6 +1370,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
),
);
+ let owner = self.tcx.hir().enclosing_body_owner(expr.hir_id);
+ if let ty::Param(param) = expected_ty.kind()
+ && let Some(generics) = self.tcx.hir().get_generics(owner)
+ {
+ suggest_constraining_type_params(
+ self.tcx,
+ generics,
+ diag,
+ vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
+ );
+ } else {
+ self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
+ }
}
}