summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck/src/fn_ctxt
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:31 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:31 +0000
commitdc0db358abe19481e475e10c32149b53370f1a1c (patch)
treeab8ce99c4b255ce46f99ef402c27916055b899ee /compiler/rustc_hir_typeck/src/fn_ctxt
parentReleasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff)
downloadrustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz
rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip
Merging upstream version 1.72.1+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.rs87
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs41
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs133
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs253
6 files changed, 355 insertions, 189 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 557950338..9a80a9c93 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -2,7 +2,7 @@ use crate::callee::{self, DeferredCallResolution};
use crate::errors::CtorIsPrivate;
use crate::method::{self, MethodCallee, SelfSource};
use crate::rvalue_scopes;
-use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy};
+use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, RawTy};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey};
@@ -83,6 +83,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// version (resolve_vars_if_possible), this version will
/// also select obligations if it seems useful, in an effort
/// to get more type information.
+ // FIXME(-Ztrait-solver=next): A lot of the calls to this method should
+ // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
@@ -135,7 +137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
format!("{:p}", self)
}
- pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
+ pub fn local_ty(&self, span: Span, nid: hir::HirId) -> Ty<'tcx> {
self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
})
@@ -451,7 +453,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
match self.typeck_results.borrow().node_types().get(id) {
Some(&t) => t,
- None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e),
+ None if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx,e),
None => {
bug!(
"no type for node {} in fcx {}",
@@ -465,7 +467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> {
match self.typeck_results.borrow().node_types().get(id) {
Some(&t) => Some(t),
- None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error(e)),
+ None if let Some(e) = self.tainted_by_errors() => Some(Ty::new_error(self.tcx,e)),
None => None,
}
}
@@ -483,7 +485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
cause,
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))),
));
}
@@ -556,7 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
);
- let witness = self.tcx.mk_generator_witness_mir(expr_def_id.to_def_id(), substs);
+ let witness = Ty::new_generator_witness_mir(self.tcx, expr_def_id.to_def_id(), substs);
// Unify `interior` with `witness` and collect all the resulting obligations.
let span = self.tcx.hir().body(body_id).value.span;
@@ -647,7 +649,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map(
move |obligation| match &obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(data))
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data))
if self.self_type_matches_expected_vid(
data.projection_ty.self_ty(),
ty_var_root,
@@ -655,23 +657,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
Some(obligation)
}
- ty::PredicateKind::Clause(ty::Clause::Trait(data))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data))
if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) =>
{
Some(obligation)
}
- ty::PredicateKind::Clause(ty::Clause::Trait(..))
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- | ty::PredicateKind::WellFormed(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
@@ -683,7 +685,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// inference variable.
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+ => None,
},
)
}
@@ -692,7 +694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let sized_did = self.tcx.lang_items().sized_trait();
self.obligations_for_self_ty(self_ty).any(|obligation| {
match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
Some(data.def_id()) == sized_did
}
_ => false,
@@ -701,7 +703,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
- let ty_error = self.tcx.ty_error_misc();
+ let ty_error = Ty::new_misc_error(self.tcx);
vec![ty_error; len]
}
@@ -746,7 +748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expect_args = self
.fudge_inference_if_ok(|| {
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
// Attempt to apply a subtyping relationship between the formal
// return type (likely containing type variables if the function
@@ -1152,7 +1154,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
if let Res::Local(hid) = res {
- let ty = self.local_ty(span, hid).decl_ty;
+ let ty = self.local_ty(span, hid);
let ty = self.normalize(span, ty);
self.write_ty(hir_id, ty);
return (ty, res);
@@ -1240,7 +1242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
let reported = err.emit();
- return (tcx.ty_error(reported), res);
+ return (Ty::new_error(tcx, reported), res);
}
}
} else {
@@ -1386,7 +1388,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the referenced item.
let ty = tcx.type_of(def_id);
assert!(!substs.has_escaping_bound_vars());
- assert!(!ty.0.has_escaping_bound_vars());
+ assert!(!ty.skip_binder().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 {
@@ -1465,33 +1467,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- /// Resolves `typ` by a single level if `typ` is a type variable.
+ /// Try to resolve `ty` to a structural type, normalizing aliases.
///
- /// When the new solver is enabled, this will also attempt to normalize
- /// the type if it's a projection (note that it will not deeply normalize
- /// projections within the type, just the outermost layer of the type).
- ///
- /// If no resolution is possible, then an error is reported.
- /// Numeric inference variables may be left unresolved.
- pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- let mut ty = self.resolve_vars_with_obligations(ty);
+ /// In case there is still ambiguity, the returned type may be an inference
+ /// variable. This is different from `structurally_resolve_type` which errors
+ /// in this case.
+ pub fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let ty = self.resolve_vars_with_obligations(ty);
- if self.tcx.trait_solver_next()
+ if self.next_trait_solver()
&& let ty::Alias(ty::Projection, _) = ty.kind()
{
match self
.at(&self.misc(sp), self.param_env)
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut())
{
- Ok(normalized_ty) => {
- ty = normalized_ty;
- },
+ Ok(normalized_ty) => normalized_ty,
Err(errors) => {
let guar = self.err_ctxt().report_fulfillment_errors(&errors);
- return self.tcx.ty_error(guar);
+ return Ty::new_error(self.tcx,guar);
}
}
- }
+ } else {
+ ty
+ }
+ }
+
+ /// Resolves `ty` by a single level if `ty` is a type variable.
+ ///
+ /// When the new solver is enabled, this will also attempt to normalize
+ /// the type if it's a projection (note that it will not deeply normalize
+ /// projections within the type, just the outermost layer of the type).
+ ///
+ /// If no resolution is possible, then an error is reported.
+ /// Numeric inference variables may be left unresolved.
+ pub fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let ty = self.try_structurally_resolve_type(sp, ty);
if !ty.is_ty_var() {
ty
@@ -1501,7 +1512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true)
.emit()
});
- let err = self.tcx.ty_error(e);
+ let err = Ty::new_error(self.tcx, e);
self.demand_suptype(sp, err, ty);
err
}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 3efdab534..ed9bb4945 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -25,14 +25,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let generics = self.tcx.generics_of(def_id);
let predicate_substs = match unsubstituted_pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs.to_vec(),
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
- pred.projection_ty.substs.to_vec()
- }
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(arg, ty)) => {
+ ty::ClauseKind::Trait(pred) => pred.trait_ref.substs.to_vec(),
+ ty::ClauseKind::Projection(pred) => pred.projection_ty.substs.to_vec(),
+ ty::ClauseKind::ConstArgHasType(arg, ty) => {
vec![ty.into(), arg.into()]
}
- ty::PredicateKind::ConstEvaluatable(e) => vec![e.into()],
+ ty::ClauseKind::ConstEvaluatable(e) => vec![e.into()],
_ => return false,
};
@@ -256,7 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
type BreakTy = ty::GenericArg<'tcx>;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
if let Some(origin) = self.0.type_var_origin(ty)
- && let rustc_infer::infer::type_variable::TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) =
+ && let rustc_infer::infer::type_variable::TypeVariableOriginKind::TypeParameterDefinition(_, 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)
@@ -510,11 +508,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This shouldn't happen, but since this is only a diagnostic improvement, avoid breaking things.
return Err(expr);
}
- let relevant_broken_predicate: ty::PredicateKind<'tcx> =
- impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder();
- match relevant_broken_predicate {
- ty::PredicateKind::Clause(ty::Clause::Trait(broken_trait)) => {
+ match impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder() {
+ ty::ClauseKind::Trait(broken_trait) => {
// ...
self.blame_specific_part_of_expr_corresponding_to_generic_param(
broken_trait.trait_ref.self_ty().into(),
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 d45e3d395..7aadb95d9 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
@@ -1,7 +1,7 @@
-use std::cmp;
-
+use core::cmp::Ordering;
use rustc_index::IndexVec;
use rustc_middle::ty::error::TypeError;
+use std::cmp;
rustc_index::newtype_index! {
#[debug_format = "ExpectedIdx({})"]
@@ -34,14 +34,14 @@ enum Issue {
Permutation(Vec<Option<usize>>),
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum Compatibility<'tcx> {
Compatible,
Incompatible(Option<TypeError<'tcx>>),
}
/// Similar to `Issue`, but contains some extra information
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
pub(crate) enum Error<'tcx> {
/// The provided argument is the invalid type for the expected input
Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>),
@@ -55,6 +55,34 @@ pub(crate) enum Error<'tcx> {
Permutation(Vec<(ExpectedIdx, ProvidedIdx)>),
}
+impl Ord for Error<'_> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let key = |error: &Error<'_>| -> usize {
+ match error {
+ Error::Invalid(..) => 0,
+ Error::Extra(_) => 1,
+ Error::Missing(_) => 2,
+ Error::Swap(..) => 3,
+ Error::Permutation(..) => 4,
+ }
+ };
+ match (self, other) {
+ (Error::Invalid(a, _, _), Error::Invalid(b, _, _)) => a.cmp(b),
+ (Error::Extra(a), Error::Extra(b)) => a.cmp(b),
+ (Error::Missing(a), Error::Missing(b)) => a.cmp(b),
+ (Error::Swap(a, b, ..), Error::Swap(c, d, ..)) => a.cmp(c).then(b.cmp(d)),
+ (Error::Permutation(a), Error::Permutation(b)) => a.cmp(b),
+ _ => key(self).cmp(&key(other)),
+ }
+ }
+}
+
+impl PartialOrd for Error<'_> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
pub(crate) struct ArgMatrix<'tcx> {
/// Maps the indices in the `compatibility_matrix` rows to the indices of
/// the *user provided* inputs
@@ -177,7 +205,7 @@ impl<'tcx> ArgMatrix<'tcx> {
// If an argument is unsatisfied, and the input in its position is useless
// then the most likely explanation is that we just got the types wrong
(true, true, true, true) => return Some(Issue::Invalid(i)),
- // Otherwise, if an input is useless, then indicate that this is an extra argument
+ // Otherwise, if an input is useless then indicate that this is an extra input
(true, _, true, _) => return Some(Issue::Extra(i)),
// Otherwise, if an argument is unsatisfiable, indicate that it's missing
(_, true, _, true) => return Some(Issue::Missing(i)),
@@ -376,6 +404,9 @@ impl<'tcx> ArgMatrix<'tcx> {
};
}
+ // sort errors with same type by the order they appear in the source
+ // so that suggestion will be handled properly, see #112507
+ errors.sort();
return (errors, matched_inputs);
}
}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index eba5c829e..41f5fafe7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1,12 +1,12 @@
use crate::coercion::CoerceMany;
+use crate::errors::SuggestPtrNullMut;
use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx};
use crate::gather_locals::Declaration;
use crate::method::MethodCallee;
use crate::TupleArgumentsFlag::*;
use crate::{errors, Expectation::*};
use crate::{
- struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
- TupleArgumentsFlag,
+ struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy, TupleArgumentsFlag,
};
use rustc_ast as ast;
use rustc_data_structures::fx::FxIndexSet;
@@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
let ty = self.resolve_vars_if_possible(ty);
if ty.has_non_region_infer() {
- self.tcx.ty_error_misc()
+ Ty::new_misc_error(self.tcx)
} else {
self.tcx.erase_regions(ty)
}
@@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let err_inputs = match tuple_arguments {
DontTupleArguments => err_inputs,
- TupleArguments => vec![self.tcx.mk_tup(&err_inputs)],
+ TupleArguments => vec![Ty::new_tup(self.tcx, &err_inputs)],
};
self.check_argument_types(
@@ -114,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
tuple_arguments,
method.ok().map(|method| method.def_id),
);
- return self.tcx.ty_error_misc();
+ return Ty::new_misc_error(self.tcx);
}
let method = method.unwrap();
@@ -184,7 +184,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
- let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
+ let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]);
match tuple_type.kind() {
// We expected a tuple and got a tuple
ty::Tuple(arg_types) => {
@@ -412,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// There are a few types which get autopromoted when passed via varargs
// in C but we just error out instead and require explicit casts.
- let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
+ let arg_ty = self.structurally_resolve_type(arg.span, arg_ty);
match arg_ty.kind() {
ty::Float(ty::FloatTy::F32) => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
@@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
}
ty::FnDef(..) => {
- let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
+ let ptr_ty = Ty::new_fn_ptr(self.tcx, arg_ty.fn_sig(self.tcx));
let ptr_ty = self.resolve_vars_if_possible(ptr_ty);
variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
}
@@ -539,7 +539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.typeck_results
.borrow()
.expr_ty_adjusted_opt(*expr)
- .unwrap_or_else(|| tcx.ty_error_misc());
+ .unwrap_or_else(|| Ty::new_misc_error(tcx));
(self.resolve_vars_if_possible(ty), normalize_span(expr.span))
})
.collect();
@@ -648,7 +648,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
{
// Wrap up the N provided arguments starting at this position in a tuple.
- let provided_as_tuple = tcx.mk_tup_from_iter(
+ let provided_as_tuple = Ty::new_tup_from_iter(tcx,
provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
);
@@ -752,20 +752,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
- errors.drain_filter(|error| {
+ errors.retain(|error| {
let Error::Invalid(
provided_idx,
expected_idx,
Compatibility::Incompatible(Some(e)),
- ) = error else { return false };
+ ) = error else { return true };
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
let trace =
mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
- return true;
+ return false;
}
- false
+ true
});
// We're done if we found errors, but we already emitted them.
@@ -814,6 +814,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
+ self.suggest_ptr_null_mut(
+ expected_ty,
+ provided_ty,
+ provided_args[*provided_idx],
+ &mut err,
+ );
+
// Call out where the function is defined
self.label_fn_like(
&mut err,
@@ -947,9 +954,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// - f(0, 1,)
// + f()
if only_extras_so_far
- && errors
+ && !errors
.peek()
- .map_or(true, |next_error| !matches!(next_error, Error::Extra(_)))
+ .is_some_and(|next_error| matches!(next_error, Error::Extra(_)))
{
let next = provided_arg_tys
.get(arg_idx + 1)
@@ -1271,6 +1278,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}
+ fn suggest_ptr_null_mut(
+ &self,
+ expected_ty: Ty<'tcx>,
+ provided_ty: Ty<'tcx>,
+ arg: &hir::Expr<'tcx>,
+ err: &mut rustc_errors::DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ ) {
+ if let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Mut, .. }) = expected_ty.kind()
+ && let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Not, .. }) = provided_ty.kind()
+ && let hir::ExprKind::Call(callee, _) = arg.kind
+ && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = callee.kind
+ && let Res::Def(_, def_id) = path.res
+ && self.tcx.get_diagnostic_item(sym::ptr_null) == Some(def_id)
+ {
+ // The user provided `ptr::null()`, but the function expects
+ // `ptr::null_mut()`.
+ err.subdiagnostic(SuggestPtrNullMut {
+ span: arg.span
+ });
+ }
+ }
+
// AST fragment checking
pub(in super::super) fn check_lit(
&self,
@@ -1280,14 +1309,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
match lit.node {
- ast::LitKind::Str(..) => tcx.mk_static_str(),
- 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::Str(..) => Ty::new_static_str(tcx),
+ ast::LitKind::ByteStr(ref v, _) => Ty::new_imm_ref(
+ tcx,
+ tcx.lifetimes.re_static,
+ Ty::new_array(tcx, tcx.types.u8, v.len() as u64),
+ ),
ast::LitKind::Byte(_) => tcx.types.u8,
ast::LitKind::Char(_) => tcx.types.char,
- ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)),
- ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)),
+ ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, ty::int_ty(t)),
+ ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, ty::uint_ty(t)),
ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
ty::Int(_) | ty::Uint(_) => Some(ty),
@@ -1299,7 +1330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_ty.unwrap_or_else(|| self.next_int_var())
}
ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => {
- tcx.mk_mach_float(ty::float_ty(t))
+ Ty::new_float(tcx, ty::float_ty(t))
}
ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
@@ -1309,12 +1340,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_ty.unwrap_or_else(|| self.next_float_var())
}
ast::LitKind::Bool(_) => tcx.types.bool,
- ast::LitKind::CStr(_, _) => tcx.mk_imm_ref(
+ ast::LitKind::CStr(_, _) => Ty::new_imm_ref(
+ tcx,
tcx.lifetimes.re_static,
tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, Some(lit.span)))
.skip_binder(),
),
- ast::LitKind::Err => tcx.ty_error_misc(),
+ ast::LitKind::Err => Ty::new_misc_error(tcx),
}
}
@@ -1393,7 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// See #44848.
let ref_bindings = pat.contains_explicit_ref_binding();
- let local_ty = self.local_ty(init.span, hir_id).revealed_ty;
+ let local_ty = self.local_ty(init.span, hir_id);
if let Some(m) = ref_bindings {
// Somewhat subtle: if we have a `ref` binding in the pattern,
// we want to avoid introducing coercions for the RHS. This is
@@ -1404,7 +1436,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// type of the place it is referencing, and not some
// supertype thereof.
let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
- self.demand_eqtype(init.span, local_ty, init_ty);
+ if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) {
+ self.emit_type_mismatch_suggestions(
+ &mut diag,
+ init.peel_drop_temps(),
+ init_ty,
+ local_ty,
+ None,
+ None,
+ );
+ diag.emit();
+ }
init_ty
} else {
self.check_expr_coercible_to_type(init, local_ty, None)
@@ -1413,7 +1455,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
// Determine and write the type which we'll check the pattern against.
- let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
+ let decl_ty = self.local_ty(decl.span, decl.hir_id);
self.write_ty(decl.hir_id, decl_ty);
// Type check the initializer.
@@ -1474,7 +1516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::StmtKind::Item(_) => {}
hir::StmtKind::Expr(ref expr) => {
// Check with expected type of `()`.
- self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
+ self.check_expr_has_type_or_error(&expr, Ty::new_unit(self.tcx), |err| {
if expr.can_have_side_effects() {
self.suggest_semicolon_at_end(expr.span, err);
}
@@ -1497,7 +1539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
- let unit = self.tcx.mk_unit();
+ let unit = Ty::new_unit(self.tcx);
let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
// if the block produces a `!` value, that can always be
@@ -1610,7 +1652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
blk.span,
blk.hir_id,
expected_ty,
- self.tcx.mk_unit(),
+ Ty::new_unit(self.tcx),
);
}
if !self.consider_removing_semicolon(blk, expected_ty, err) {
@@ -1640,7 +1682,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::Stmt {
kind:
hir::StmtKind::Expr(hir::Expr {
- kind: hir::ExprKind::Assign(..),
+ kind: hir::ExprKind::Assign(lhs, ..),
..
}),
..
@@ -1650,7 +1692,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} = blk
{
self.comes_from_while_condition(blk.hir_id, |_| {
- err.downgrade_to_delayed_bug();
+ // We cannot suppress the error if the LHS of assignment
+ // is a syntactic place expression because E0070 would
+ // not be emitted by `check_lhs_assignable`.
+ let res = self.typeck_results.borrow().expr_ty_opt(lhs);
+
+ if !lhs.is_syntactic_place_expr()
+ || res.references_error()
+ {
+ err.downgrade_to_delayed_bug();
+ }
})
}
}
@@ -1747,12 +1798,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
if let Err(guar) = ty.error_reported() {
// Override the types everywhere with `err()` to avoid knock on errors.
- let err = self.tcx.ty_error(guar);
+ let err = Ty::new_error(self.tcx, guar);
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);
+ self.locals.borrow_mut().insert(hir_id, err);
+ self.locals.borrow_mut().insert(pat.hir_id, err);
}
}
@@ -1776,8 +1826,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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(|guar| self.tcx().ty_error(guar));
+ let ty = result
+ .map(|(ty, _, _)| ty)
+ .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar));
let ty = self.handle_raw_ty(path_span, ty);
let result = result.map(|(_, kind, def_id)| (kind, def_id));
@@ -1899,7 +1950,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// do that, so it's OK.
for (predicate, span) in instantiated
{
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder()
+ if let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder()
&& pred.self_ty().peel_refs() == callee_ty
&& self.tcx.is_fn_trait(pred.def_id())
{
@@ -1931,7 +1982,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
traits::ObligationCause::dummy(),
self.param_env,
- ty::Binder::dummy(trait_ref),
+ trait_ref,
);
match SelectionContext::new(&self).select(&obligation) {
Ok(Some(traits::ImplSource::UserDefined(impl_source))) => {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 67f45f9aa..20b34df99 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -163,7 +163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return fn_sig;
}
self.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
let normalized_fn_sig =
ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
if ocx.select_all_or_error().is_empty() {
@@ -189,6 +189,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn errors_reported_since_creation(&self) -> bool {
self.tcx.sess.err_count() > self.err_count_on_creation
}
+
+ pub fn next_root_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
+ Ty::new_var(self.tcx, self.next_ty_var_id_in_universe(origin, ty::UniverseIndex::ROOT))
+ }
}
impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
@@ -222,9 +226,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
predicates: tcx.arena.alloc_from_iter(
self.param_env.caller_bounds().iter().filter_map(|predicate| {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data))
- if data.self_ty().is_param(index) =>
- {
+ ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
// HACK(eddyb) should get the original `Span`.
let span = tcx.def_span(def_id);
Some((predicate, span))
@@ -293,7 +295,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
trait_ref.substs,
);
- self.tcx().mk_projection(item_def_id, item_substs)
+ Ty::new_projection(self.tcx(), item_def_id, item_substs)
}
fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index c4add4dbd..79a7c0161 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,6 +1,8 @@
use super::FnCtxt;
-use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing};
+use crate::errors::{
+ AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod,
+};
use crate::fluent_generated as fluent;
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
@@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
) -> bool {
let expr = expr.peel_blocks();
+ let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
+
if let Some((suggestion, msg, applicability, verbose, annotation)) =
self.suggest_deref_or_ref(expr, found, expected)
{
@@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
return true;
- } else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
+ }
+
+ 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))
+ }
+
+ 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)
{
@@ -343,97 +351,156 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(sp, format!("{descr} `{name}` defined here"));
}
return true;
- } else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
+ }
+
+ if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
return true;
- } else {
- let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
- if !methods.is_empty() {
- let mut suggestions = methods.iter()
- .filter_map(|conversion_method| {
- let receiver_method_ident = expr.method_ident();
- if let Some(method_ident) = receiver_method_ident
- && method_ident.name == conversion_method.name
- {
- return None // do not suggest code that is already there (#53348)
- }
+ }
- let method_call_list = [sym::to_vec, sym::to_string];
- let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
- && receiver_method.ident.name == sym::clone
- && 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,
- // so we remove the user's `clone` call.
- {
- vec![(
- receiver_method.ident.span,
- conversion_method.name.to_string()
- )]
- } else if expr.precedence().order()
- < ExprPrecedence::MethodCall.order()
- {
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
- ]
- } else {
- vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
- };
- let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
- if let Some(name) = struct_pat_shorthand_field {
- sugg.insert(
- 0,
- (expr.span.shrink_to_lo(), format!("{}: ", name)),
- );
- }
- Some(sugg)
- })
- .peekable();
- if suggestions.peek().is_some() {
- err.multipart_suggestions(
- "try using a conversion method",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- return true;
- }
- } else if let ty::Adt(found_adt, found_substs) = found.kind()
- && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
- && let ty::Adt(expected_adt, expected_substs) = expected.kind()
- && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
- && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind()
- && inner_ty.is_str()
- {
- let ty = found_substs.type_at(0);
- let mut peeled = ty;
- let mut ref_cnt = 0;
- while let ty::Ref(_, inner, _) = peeled.kind() {
- peeled = *inner;
- ref_cnt += 1;
- }
- if let ty::Adt(adt, _) = peeled.kind()
- && Some(adt.did()) == self.tcx.lang_items().string()
- {
- let sugg = if ref_cnt == 0 {
- ".as_deref()"
+ if !methods.is_empty() {
+ let mut suggestions = methods
+ .iter()
+ .filter_map(|conversion_method| {
+ let receiver_method_ident = expr.method_ident();
+ if let Some(method_ident) = receiver_method_ident
+ && method_ident.name == conversion_method.name
+ {
+ return None // do not suggest code that is already there (#53348)
+ }
+
+ let method_call_list = [sym::to_vec, sym::to_string];
+ let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
+ && receiver_method.ident.name == sym::clone
+ && 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,
+ // so we remove the user's `clone` call.
+ {
+ vec![(
+ receiver_method.ident.span,
+ conversion_method.name.to_string()
+ )]
+ } else if expr.precedence().order()
+ < ExprPrecedence::MethodCall.order()
+ {
+ vec![
+ (expr.span.shrink_to_lo(), "(".to_string()),
+ (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
+ ]
} else {
- ".map(|x| x.as_str())"
+ vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
};
- err.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- fluent::hir_typeck_convert_to_str,
- sugg,
- Applicability::MachineApplicable,
- );
- return true;
- }
+ let struct_pat_shorthand_field =
+ self.maybe_get_struct_pattern_shorthand_field(expr);
+ if let Some(name) = struct_pat_shorthand_field {
+ sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
+ }
+ Some(sugg)
+ })
+ .peekable();
+ if suggestions.peek().is_some() {
+ err.multipart_suggestions(
+ "try using a conversion method",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ return true;
+ }
+ }
+
+ if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
+ self.deconstruct_option_or_result(found, expected)
+ && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
+ {
+ // Suggest removing any stray borrows (unless there's macro shenanigans involved).
+ let inner_expr = expr.peel_borrows();
+ if !inner_expr.span.eq_ctxt(expr.span) {
+ return false;
+ }
+ let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
+ None
+ } else {
+ Some(expr.span.shrink_to_lo().until(inner_expr.span))
+ };
+ // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for
+ // `as_ref` and `as_deref` compatibility.
+ let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| {
+ self.can_eq(self.param_env, Ty::new_imm_ref(self.tcx,self.tcx.lifetimes.re_erased, found), expected)
+ });
+ // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
+ // but those checks need to be a bit more delicate and the benefit is diminishing.
+ if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
+ err.subdiagnostic(SuggestConvertViaMethod {
+ span: expr.span.shrink_to_hi(),
+ sugg: ".as_ref()",
+ expected,
+ found,
+ borrow_removal_span,
+ });
+ return true;
+ } else if let Some((deref_ty, _)) =
+ self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1)
+ && self.can_eq(self.param_env, deref_ty, peeled)
+ && error_tys_equate_as_ref
+ {
+ err.subdiagnostic(SuggestConvertViaMethod {
+ span: expr.span.shrink_to_hi(),
+ sugg: ".as_deref()",
+ expected,
+ found,
+ borrow_removal_span,
+ });
+ return true;
+ } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
+ && Some(adt.did()) == self.tcx.lang_items().string()
+ && peeled.is_str()
+ // `Result::map`, conversely, does not take ref of the error type.
+ && error_tys.map_or(true, |(found, expected)| {
+ self.can_eq(self.param_env, found, expected)
+ })
+ {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ fluent::hir_typeck_convert_to_str,
+ ".map(|x| x.as_str())",
+ Applicability::MachineApplicable,
+ );
+ return true;
}
}
false
}
+ fn deconstruct_option_or_result(
+ &self,
+ found_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
+ let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else {
+ return None;
+ };
+ let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else {
+ return None;
+ };
+ if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
+ && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
+ {
+ Some((found_substs.type_at(0), expected_substs.type_at(0), None))
+ } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
+ && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
+ {
+ Some((
+ found_substs.type_at(0),
+ expected_substs.type_at(0),
+ Some((found_substs.type_at(1), expected_substs.type_at(1))),
+ ))
+ } else {
+ None
+ }
+ }
+
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
/// in the heap by calling `Box::new()`.
pub(in super::super) fn suggest_boxing_when_appropriate(
@@ -448,7 +515,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
return false;
}
- if self.can_coerce(self.tcx.mk_box(found), expected) {
+ if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
let suggest_boxing = match found.kind() {
ty::Tuple(tuple) if tuple.is_empty() => {
SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
@@ -528,9 +595,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
return false;
}
- let box_found = self.tcx.mk_box(found);
- let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
- let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
+ let box_found = Ty::new_box(self.tcx, found);
+ let pin_box_found = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin).unwrap();
+ let pin_found = Ty::new_lang_item(self.tcx, found, LangItem::Pin).unwrap();
match expected.kind() {
ty::Adt(def, _) if Some(def.did()) == pin_did => {
if self.can_coerce(pin_box_found, expected) {
@@ -874,7 +941,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let found = self.resolve_vars_with_obligations(found);
let in_loop = self.is_loop(id)
- || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
+ || self
+ .tcx
+ .hir()
+ .parent_iter(id)
+ .take_while(|(_, node)| {
+ // look at parents until we find the first body owner
+ node.body_id().is_none()
+ })
+ .any(|(parent_id, _)| self.is_loop(parent_id));
let in_local_statement = self.is_local_statement(id)
|| self