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:20:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
commit631cd5845e8de329d0e227aaa707d7ea228b8f8f (patch)
treea1b87c8f8cad01cf18f7c5f57a08f102771ed303 /compiler/rustc_hir_typeck/src/fn_ctxt
parentAdding debian version 1.69.0+dfsg1-1. (diff)
downloadrustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.tar.xz
rustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.zip
Merging upstream version 1.70.0+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.rs216
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs121
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs91
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs91
5 files changed, 295 insertions, 230 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 60e55c7b0..f736f7a96 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -19,13 +19,13 @@ use rustc_hir_analysis::astconv::{
};
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
-use rustc_infer::infer::InferResult;
+use rustc_infer::infer::{DefineOpaqueTypes, InferResult};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{
- self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, TyCtxt, UserType,
+ self, AdtKind, CanonicalUserType, GenericParamDefKind, Ty, TyCtxt, UserType,
};
use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts};
use rustc_session::lint;
@@ -33,6 +33,7 @@ use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
+use rustc_target::abi::FieldIdx;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt};
@@ -147,7 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
+ pub fn write_field_index(&self, hir_id: hir::HirId, index: FieldIdx) {
self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
}
@@ -301,16 +302,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
def_id: DefId,
substs: SubstsRef<'tcx>,
- ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
+ ) -> ty::InstantiatedPredicates<'tcx> {
let bounds = self.tcx.predicates_of(def_id);
- let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
let result = bounds.instantiate(self.tcx, substs);
let result = self.normalize(span, result);
- debug!(
- "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
- bounds, substs, result, spans,
- );
- (result, spans)
+ debug!("instantiate_bounds(bounds={:?}, substs={:?}) = {:?}", bounds, substs, result);
+ result
}
pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T
@@ -562,7 +559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let span = self.tcx.hir().body(body_id).value.span;
let ok = self
.at(&self.misc(span), self.param_env)
- .eq(interior, witness)
+ .eq(DefineOpaqueTypes::No, interior, witness)
.expect("Failed to unify generator interior type");
let mut obligations = ok.obligations;
@@ -581,11 +578,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
pub(in super::super) fn report_ambiguity_errors(&self) {
- let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors();
+ let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(self);
if !errors.is_empty() {
self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
- self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id);
+ self.err_ctxt().report_fulfillment_errors(&errors);
}
}
@@ -598,7 +595,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !result.is_empty() {
mutate_fulfillment_errors(&mut result);
self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
- self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id);
+ self.err_ctxt().report_fulfillment_errors(&result);
}
}
@@ -670,7 +667,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::AliasEq(..)
+ | ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
// N.B., this predicate is created by breaking down a
@@ -902,56 +899,74 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}
- /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
+ /// Given a function `Node`, return its `HirId` and `FnDecl` if it exists. Given a closure
+ /// that is the child of a function, return that function's `HirId` and `FnDecl` instead.
+ /// This may seem confusing at first, but this is used in diagnostics for `async fn`,
+ /// for example, where most of the type checking actually happens within a nested closure,
+ /// but we often want access to the parent function's signature.
+ ///
+ /// Otherwise, return false.
pub(in super::super) fn get_node_fn_decl(
&self,
node: Node<'tcx>,
- ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
+ ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> {
match node {
- Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
+ Node::Item(&hir::Item {
+ ident,
+ kind: hir::ItemKind::Fn(ref sig, ..),
+ owner_id,
+ ..
+ }) => {
// This is less than ideal, it will not suggest a return type span on any
// method called `main`, regardless of whether it is actually the entry point,
// but it will still present it as the reason for the expected type.
- Some((&sig.decl, ident, ident.name != sym::main))
+ Some((
+ hir::HirId::make_owner(owner_id.def_id),
+ &sig.decl,
+ ident,
+ ident.name != sym::main,
+ ))
}
Node::TraitItem(&hir::TraitItem {
ident,
kind: hir::TraitItemKind::Fn(ref sig, ..),
+ owner_id,
..
- }) => Some((&sig.decl, ident, true)),
+ }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, true)),
Node::ImplItem(&hir::ImplItem {
ident,
kind: hir::ImplItemKind::Fn(ref sig, ..),
+ owner_id,
..
- }) => Some((&sig.decl, ident, false)),
- Node::Expr(&hir::Expr {
- hir_id,
- kind: hir::ExprKind::Closure(..),
- ..
- }) if let Some(Node::Expr(&hir::Expr {
- hir_id,
- kind: hir::ExprKind::Call(..),
- ..
- })) = self.tcx.hir().find_parent(hir_id) &&
- let Some(Node::Item(&hir::Item {
+ }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)),
+ Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. })
+ if let Some(Node::Item(&hir::Item {
+ ident,
+ kind: hir::ItemKind::Fn(ref sig, ..),
+ owner_id,
+ ..
+ })) = self.tcx.hir().find_parent(hir_id) => Some((
+ hir::HirId::make_owner(owner_id.def_id),
+ &sig.decl,
ident,
- kind: hir::ItemKind::Fn(ref sig, ..),
- ..
- })) = self.tcx.hir().find_parent(hir_id) => {
- Some((&sig.decl, ident, ident.name != sym::main))
- },
+ ident.name != sym::main,
+ )),
_ => None,
}
}
- /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
+ /// Given a `HirId`, return the `HirId` of the enclosing function, its `FnDecl`, and whether a
/// suggestion can be made, `None` otherwise.
- pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
+ pub fn get_fn_decl(
+ &self,
+ blk_id: hir::HirId,
+ ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, bool)> {
// Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
// `while` before reaching it, as block tail returns are not available in them.
self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
let parent = self.tcx.hir().get(blk_id);
- self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
+ self.get_node_fn_decl(parent)
+ .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main))
})
}
@@ -959,44 +974,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
+ expected: Option<Ty<'tcx>>,
found: Ty<'tcx>,
) {
if found != self.tcx.types.unit {
return;
}
- if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind {
- if self
- .typeck_results
+
+ let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind else {
+ return;
+ };
+
+ let rcvr_has_the_expected_type = self
+ .typeck_results
+ .borrow()
+ .expr_ty_adjusted_opt(rcvr)
+ .and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs()))
+ .unwrap_or(false);
+
+ let prev_call_mutates_and_returns_unit = || {
+ self.typeck_results
.borrow()
- .expr_ty_adjusted_opt(rcvr)
- .map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
- {
- return;
- }
- let mut sp = MultiSpan::from_span(path_segment.ident.span);
- sp.push_span_label(
- path_segment.ident.span,
- format!(
- "this call modifies {} in-place",
- match rcvr.kind {
- ExprKind::Path(QPath::Resolved(
- None,
- hir::Path { segments: [segment], .. },
- )) => format!("`{}`", segment.ident),
- _ => "its receiver".to_string(),
- }
- ),
- );
+ .type_dependent_def_id(expr.hir_id)
+ .map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder())
+ .and_then(|sig| sig.inputs_and_output.split_last())
+ .map(|(output, inputs)| {
+ output.is_unit()
+ && inputs
+ .get(0)
+ .and_then(|self_ty| self_ty.ref_mutability())
+ .map_or(false, rustc_ast::Mutability::is_mut)
+ })
+ .unwrap_or(false)
+ };
+
+ if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) {
+ return;
+ }
+
+ let mut sp = MultiSpan::from_span(path_segment.ident.span);
+ sp.push_span_label(
+ path_segment.ident.span,
+ format!(
+ "this call modifies {} in-place",
+ match rcvr.kind {
+ ExprKind::Path(QPath::Resolved(
+ None,
+ hir::Path { segments: [segment], .. },
+ )) => format!("`{}`", segment.ident),
+ _ => "its receiver".to_string(),
+ }
+ ),
+ );
+
+ let modifies_rcvr_note =
+ format!("method `{}` modifies its receiver in-place", path_segment.ident);
+ if rcvr_has_the_expected_type {
sp.push_span_label(
rcvr.span,
"you probably want to use this value after calling the method...",
);
+ err.span_note(sp, &modifies_rcvr_note);
+ err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
+ } else if let ExprKind::MethodCall(..) = rcvr.kind {
err.span_note(
sp,
- &format!("method `{}` modifies its receiver in-place", path_segment.ident),
+ modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.",
);
- err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
+ } else {
+ err.span_note(sp, &modifies_rcvr_note);
}
}
@@ -1319,7 +1365,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This also occurs for an enum variant on a type alias.
let impl_ty = self.normalize(span, tcx.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) {
+ match self.at(&self.misc(span), self.param_env).eq(
+ DefineOpaqueTypes::No,
+ impl_ty,
+ self_ty,
+ ) {
Ok(ok) => self.register_infer_ok_obligations(ok),
Err(_) => {
self.tcx.sess.delay_span_bug(
@@ -1367,41 +1417,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
let param_env = self.param_env;
- let remap = match self.tcx.def_kind(def_id) {
- // Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
- // `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
- // Therefore we have to remap the param env here to be non-const.
- hir::def::DefKind::AssocConst => true,
- hir::def::DefKind::AssocFn
- if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait =>
- {
- // N.B.: All callsites to this function involve checking a path expression.
- //
- // When instantiating a trait method as a function item, it does not actually matter whether
- // the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as
- // `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
- // check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
- // `const fn` pointer.
- //
- // FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
- // `~const FnOnce` or can be coerced to `const fn` pointer.
- true
- }
- _ => false,
- };
- let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
+ let bounds = self.instantiate_bounds(span, def_id, &substs);
- for mut obligation in traits::predicates_for_generics(
+ for obligation in traits::predicates_for_generics(
|idx, predicate_span| {
traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span))
},
param_env,
bounds,
) {
- if remap {
- obligation = obligation.without_const(self.tcx);
- }
- self.register_predicate(obligation);
+ // N.B. We are remapping all predicates to non-const since we don't know if we just
+ // want them as function pointers or we are calling them from a const-context. The
+ // actual checking will occur in `rustc_const_eval::transform::check_consts`.
+ self.register_predicate(obligation.without_const(self.tcx));
}
}
@@ -1415,7 +1443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
let e = self.tainted_by_errors().unwrap_or_else(|| {
self.err_ctxt()
- .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
+ .emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true)
.emit()
});
let err = self.tcx.ty_error(e);
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 b09886fe3..f879ccbb3 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
@@ -3,10 +3,8 @@ use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_infer::traits::ObligationCauseCode;
-use rustc_middle::ty::{
- self, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
-};
-use rustc_span::{self, Span};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
+use rustc_span::{self, symbol::kw, Span};
use rustc_trait_selection::traits;
use std::ops::ControlFlow;
@@ -27,17 +25,28 @@ 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,
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => pred.projection_ty.substs,
- _ => ty::List::empty(),
+ 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)) => {
+ vec![ty.into(), arg.into()]
+ }
+ ty::PredicateKind::ConstEvaluatable(e) => vec![e.into()],
+ _ => return false,
};
- let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| {
- predicate_substs.types().find_map(|ty| {
- ty.walk().find_map(|arg| {
+ let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| {
+ predicate_substs.iter().find_map(|arg| {
+ arg.walk().find_map(|arg| {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
- && let ty::Param(param_ty) = ty.kind()
- && matches(param_ty)
+ && let ty::Param(param_ty) = *ty.kind()
+ && matches(ty::ParamTerm::Ty(param_ty))
+ {
+ Some(arg)
+ } else if let ty::GenericArgKind::Const(ct) = arg.unpack()
+ && let ty::ConstKind::Param(param_ct) = ct.kind()
+ && matches(ty::ParamTerm::Const(param_ct))
{
Some(arg)
} else {
@@ -49,26 +58,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Prefer generics that are local to the fn item, since these are likely
// to be the cause of the unsatisfied predicate.
- let mut param_to_point_at = find_param_matching(&|param_ty| {
- self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id
+ let mut param_to_point_at = find_param_matching(&|param_term| {
+ self.tcx.parent(generics.param_at(param_term.index(), self.tcx).def_id) == def_id
});
// Fall back to generic that isn't local to the fn item. This will come
// from a trait or impl, for example.
- let mut fallback_param_to_point_at = find_param_matching(&|param_ty| {
- self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id
- && param_ty.name != rustc_span::symbol::kw::SelfUpper
+ let mut fallback_param_to_point_at = find_param_matching(&|param_term| {
+ self.tcx.parent(generics.param_at(param_term.index(), self.tcx).def_id) != def_id
+ && !matches!(param_term, ty::ParamTerm::Ty(ty) if ty.name == kw::SelfUpper)
});
// Finally, the `Self` parameter is possibly the reason that the predicate
// is unsatisfied. This is less likely to be true for methods, because
// method probe means that we already kinda check that the predicates due
// to the `Self` type are true.
- let mut self_param_to_point_at =
- find_param_matching(&|param_ty| param_ty.name == rustc_span::symbol::kw::SelfUpper);
+ let mut self_param_to_point_at = find_param_matching(
+ &|param_term| matches!(param_term, ty::ParamTerm::Ty(ty) if ty.name == kw::SelfUpper),
+ );
// Finally, for ambiguity-related errors, we actually want to look
// for a parameter that is the source of the inference type left
// over in this predicate.
- if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code {
+ if let traits::FulfillmentErrorCode::CodeAmbiguity { .. } = error.code {
fallback_param_to_point_at = None;
self_param_to_point_at = None;
param_to_point_at =
@@ -227,15 +237,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id));
let Some((index, _)) = own_substs
.iter()
- .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_)))
.enumerate()
.find(|(_, arg)| **arg == param_to_point_at) else { return false };
let Some(arg) = segment
.args()
.args
- .iter()
- .filter(|arg| matches!(arg, hir::GenericArg::Type(_)))
- .nth(index) else { return false; };
+ .get(index) else { return false; };
error.obligation.cause.span = arg
.span()
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
@@ -302,7 +309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.iter()
.filter(|field| {
let field_ty = field.ty(self.tcx, identity_substs);
- Self::find_param_in_ty(field_ty.into(), param_to_point_at)
+ find_param_in_ty(field_ty.into(), param_to_point_at)
})
.collect();
@@ -348,7 +355,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.inputs()
.iter()
.enumerate()
- .filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), param_to_point_at))
+ .filter(|(_, ty)| find_param_in_ty((**ty).into(), param_to_point_at))
.collect();
// If there's one field that references the given generic, great!
if let [(idx, _)] = args_referencing_param.as_slice()
@@ -459,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize.
///
/// This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be
- /// reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be
+ /// reported as an error. If it is `Ok`, then it means it refined successful. If it is `Err`, then it may be
/// only a partial success - but it cannot be refined even further.
fn blame_specific_expr_if_possible_for_derived_predicate_obligation(
&self,
@@ -527,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// - in_ty: `(Option<Vec<T>, bool)`
/// we would drill until we arrive at `vec![1, 2, 3]`.
///
- /// If successful, we return `Ok(refined_expr)`. If unsuccesful, we return `Err(partially_refined_expr`),
+ /// If successful, we return `Ok(refined_expr)`. If unsuccessful, we return `Err(partially_refined_expr`),
/// which will go as far as possible. For example, given `(foo(), false)` instead, we would drill to
/// `foo()` and then return `Err("foo()")`.
///
@@ -571,8 +578,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Find out which of `in_ty_elements` refer to `param`.
// FIXME: It may be better to take the first if there are multiple,
// just so that the error points to a smaller expression.
- let Some((drill_expr, drill_ty)) = Self::is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| {
- Self::find_param_in_ty((*in_ty_elem).into(), param)
+ let Some((drill_expr, drill_ty)) = is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| {
+ find_param_in_ty((*in_ty_elem).into(), param)
})) else {
// The param is not mentioned, or it is mentioned in multiple indexes.
return Err(expr);
@@ -620,10 +627,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We need to know which of the generic parameters mentions our target param.
// We expect that at least one of them does, since it is expected to be mentioned.
let Some((drill_generic_index, generic_argument_type)) =
- Self::is_iterator_singleton(
+ is_iterator_singleton(
in_ty_adt_generic_args.iter().enumerate().filter(
|(_index, in_ty_generic)| {
- Self::find_param_in_ty(*in_ty_generic, param)
+ find_param_in_ty(*in_ty_generic, param)
},
),
) else {
@@ -743,10 +750,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We need to know which of the generic parameters mentions our target param.
// We expect that at least one of them does, since it is expected to be mentioned.
let Some((drill_generic_index, generic_argument_type)) =
- Self::is_iterator_singleton(
+ is_iterator_singleton(
in_ty_adt_generic_args.iter().enumerate().filter(
|(_index, in_ty_generic)| {
- Self::find_param_in_ty(*in_ty_generic, param)
+ find_param_in_ty(*in_ty_generic, param)
},
),
) else {
@@ -785,14 +792,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// outer contextual information.
// (1) Find the (unique) field index which mentions the type in our constraint:
- let Some((field_index, field_type)) = Self::is_iterator_singleton(
+ let Some((field_index, field_type)) = is_iterator_singleton(
in_ty_adt
.variant_with_id(variant_def_id)
.fields
.iter()
.map(|field| field.ty(self.tcx, *in_ty_adt_generic_args))
.enumerate()
- .filter(|(_index, field_type)| Self::find_param_in_ty((*field_type).into(), param))
+ .filter(|(_index, field_type)| find_param_in_ty((*field_type).into(), param))
) else {
return Err(expr);
};
@@ -825,20 +832,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(expr)
}
+}
- // FIXME: This can be made into a private, non-impl function later.
- /// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references
- /// to the given `param_to_point_at`. Returns `true` if it finds any use of the param.
- pub fn find_param_in_ty(
- ty: ty::GenericArg<'tcx>,
- param_to_point_at: ty::GenericArg<'tcx>,
- ) -> bool {
- let mut walk = ty.walk();
- while let Some(arg) = walk.next() {
- if arg == param_to_point_at {
- return true;
- }
- if let ty::GenericArgKind::Type(ty) = arg.unpack()
+/// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references
+/// to the given `param_to_point_at`. Returns `true` if it finds any use of the param.
+fn find_param_in_ty<'tcx>(
+ ty: ty::GenericArg<'tcx>,
+ param_to_point_at: ty::GenericArg<'tcx>,
+) -> bool {
+ let mut walk = ty.walk();
+ while let Some(arg) = walk.next() {
+ if arg == param_to_point_at {
+ return true;
+ }
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
&& let ty::Alias(ty::Projection, ..) = ty.kind()
{
// This logic may seem a bit strange, but typically when
@@ -849,16 +856,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// in some UI tests.
walk.skip_current_subtree();
}
- }
- false
}
+ false
+}
- // FIXME: This can be made into a private, non-impl function later.
- /// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise.
- pub fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> {
- match (iterator.next(), iterator.next()) {
- (_, Some(_)) => None,
- (first, _) => first,
- }
+/// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise.
+fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> {
+ match (iterator.next(), iterator.next()) {
+ (_, Some(_)) => None,
+ (first, _) => first,
}
}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index a46bdeb41..ea1b52daa 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -24,14 +24,14 @@ use rustc_hir_analysis::structured_errors::StructuredDiagnostic;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::InferOk;
use rustc_infer::infer::TypeTrace;
+use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::visit::TypeVisitableExt;
-use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty};
+use rustc_middle::ty::{self, IsSuggestable, Ty};
use rustc_session::Session;
use rustc_span::symbol::{kw, Ident};
-use rustc_span::{self, sym, Span};
+use rustc_span::{self, sym, BytePos, Span};
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
use std::iter;
@@ -301,9 +301,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 3. Check if the formal type is a supertype of the checked one
// and register any such obligations for future type checks
- let supertype_error = self
- .at(&self.misc(provided_arg.span), self.param_env)
- .sup(formal_input_ty, coerced_ty);
+ let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup(
+ DefineOpaqueTypes::No,
+ formal_input_ty,
+ coerced_ty,
+ );
let subtyping_error = match supertype_error {
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
@@ -470,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err_code: &str,
fn_def_id: Option<DefId>,
call_span: Span,
- call_expr: &hir::Expr<'tcx>,
+ call_expr: &'tcx hir::Expr<'tcx>,
) {
// Next, let's construct the error
let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind {
@@ -585,7 +587,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Using probe here, since we don't want this subtyping to affect inference.
let subtyping_error = self.probe(|_| {
- self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err()
+ self.at(&self.misc(arg_span), self.param_env)
+ .sup(DefineOpaqueTypes::No, formal_input_ty, coerced_ty)
+ .err()
});
// Same as above: if either the coerce type or the checked type is an error type,
@@ -764,7 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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(_)) {
+ if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
return true;
}
@@ -803,24 +807,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
full_call_span,
format!("arguments to this {} are incorrect", call_name),
);
- if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) =
- (callee_ty, &call_expr.kind)
+
+ if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind
+ && provided_idx.as_usize() == expected_idx.as_usize()
{
- // Type that would have accepted this argument if it hadn't been inferred earlier.
- // FIXME: We leave an inference variable for now, but it'd be nice to get a more
- // specific type to increase the accuracy of the diagnostic.
- let expected = self.infcx.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: full_call_span,
- });
- self.point_at_expr_source_of_inferred_type(
+ self.note_source_of_type_mismatch_constraint(
&mut err,
rcvr,
- expected,
- callee_ty,
- provided_arg_span,
+ crate::demand::TypeMismatchSource::Arg {
+ call_expr,
+ incompatible_arg: provided_idx.as_usize(),
+ },
);
}
+
// Call out where the function is defined
self.label_fn_like(
&mut err,
@@ -890,8 +890,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let mut errors = errors.into_iter().peekable();
+ let mut only_extras_so_far = errors
+ .peek()
+ .map_or(false, |first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0));
let mut suggestions = vec![];
while let Some(error) = errors.next() {
+ only_extras_so_far &= matches!(error, Error::Extra(_));
+
match error {
Error::Invalid(provided_idx, expected_idx, compatibility) => {
let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
@@ -937,10 +942,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if arg_idx.index() > 0
&& let Some((_, prev)) = provided_arg_tys
.get(ProvidedIdx::from_usize(arg_idx.index() - 1)
- ) {
- // Include previous comma
- span = prev.shrink_to_hi().to(span);
- }
+ ) {
+ // Include previous comma
+ span = prev.shrink_to_hi().to(span);
+ }
+
+ // Is last argument for deletion in a row starting from the 0-th argument?
+ // Then delete the next comma, so we are not left with `f(, ...)`
+ //
+ // fn f() {}
+ // - f(0, 1,)
+ // + f()
+ if only_extras_so_far
+ && errors
+ .peek()
+ .map_or(true, |next_error| !matches!(next_error, Error::Extra(_)))
+ {
+ let next = provided_arg_tys
+ .get(arg_idx + 1)
+ .map(|&(_, sp)| sp)
+ .unwrap_or_else(|| {
+ // Subtract one to move before `)`
+ call_expr.span.with_lo(call_expr.span.hi() - BytePos(1))
+ });
+
+ // Include next comma
+ span = span.until(next);
+ }
+
suggestions.push((span, String::new()));
suggestion_text = match suggestion_text {
@@ -1158,11 +1187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// index position where it *should have been*, which is *after* the previous one.
if let Some(provided_idx) = provided_idx {
prev = provided_idx.index() as i64;
+ continue;
}
let idx = ProvidedIdx::from_usize((prev + 1) as usize);
- if let None = provided_idx
- && let Some((_, arg_span)) = provided_arg_tys.get(idx)
- {
+ if let Some((_, arg_span)) = provided_arg_tys.get(idx) {
+ prev += 1;
// There is a type that was *not* found anywhere, so it isn't a move, but a
// replacement and we look at what type it should have been. This will allow us
// To suggest a multipart suggestion when encountering `foo(1, "")` where the def
@@ -1380,7 +1409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.demand_eqtype(init.span, local_ty, init_ty);
init_ty
} else {
- self.check_expr_coercable_to_type(init, local_ty, None)
+ self.check_expr_coercible_to_type(init, local_ty, None)
}
}
@@ -1665,7 +1694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id);
- self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
+ self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident))
}
/// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 1dea3e6f9..c6fd0b610 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -211,13 +211,13 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
fn get_type_parameter_bounds(
&self,
_: Span,
- def_id: DefId,
+ def_id: LocalDefId,
_: Ident,
) -> ty::GenericPredicates<'tcx> {
let tcx = self.tcx;
- let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
+ let item_def_id = tcx.hir().ty_param_owner(def_id);
let generics = tcx.generics_of(item_def_id);
- let index = generics.param_def_id_to_index[&def_id];
+ let index = generics.param_def_id_to_index[&def_id.to_def_id()];
ty::GenericPredicates {
parent: None,
predicates: tcx.arena.alloc_from_iter(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index c49621b7c..5fda4e191 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -16,7 +16,7 @@ use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
- self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
+ self, suggest_constraining_type_params, Binder, IsSuggestable, ToPredicate, Ty,
TypeVisitableExt,
};
use rustc_session::errors::ExprParenthesesNeeded;
@@ -64,8 +64,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expr = expr.peel_drop_temps();
self.suggest_missing_semicolon(err, expr, expected, false);
let mut pointing_at_return_type = false;
- if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
- let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
+ if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
pointing_at_return_type = self.suggest_missing_return_type(
err,
&fn_decl,
@@ -166,8 +165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
ty: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
- let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
- self.err_ctxt().extract_callable_info(body_hir_id, self.param_env, ty)
+ self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
}
pub fn suggest_two_fn_call(
@@ -669,6 +667,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// This routine checks if the return type is left as default, the method is not part of an
/// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
/// type.
+ #[instrument(level = "trace", skip(self, err))]
pub(in super::super) fn suggest_missing_return_type(
&self,
err: &mut Diagnostic,
@@ -705,28 +704,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return true
}
}
- hir::FnRetTy::Return(ty) => {
- let span = ty.span;
-
- if let hir::TyKind::OpaqueDef(item_id, ..) = ty.kind
- && let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::OpaqueTy(op_ty),
- ..
- }) = self.tcx.hir().get(item_id.hir_id())
- && let hir::OpaqueTy {
- bounds: [bound], ..
- } = op_ty
- && let hir::GenericBound::LangItemTrait(
- hir::LangItem::Future, _, _, generic_args) = bound
- && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args
- && let hir::TypeBinding { kind, .. } = ty_binding
- && let hir::TypeBindingKind::Equality { term } = kind
- && let hir::Term::Ty(term_ty) = term {
+ hir::FnRetTy::Return(hir_ty) => {
+ let span = hir_ty.span;
+
+ if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind
+ && let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::OpaqueTy(op_ty),
+ ..
+ }) = self.tcx.hir().get(item_id.hir_id())
+ && let [hir::GenericBound::LangItemTrait(
+ hir::LangItem::Future, _, _, generic_args)] = op_ty.bounds
+ && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args
+ && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(term) } = ty_binding.kind
+ {
// Check if async function's return type was omitted.
// Don't emit suggestions if the found type is `impl Future<...>`.
- debug!("suggest_missing_return_type: found = {:?}", found);
+ debug!(?found);
if found.is_suggestable(self.tcx, false) {
- if term_ty.span.is_empty() {
+ if term.span.is_empty() {
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
return true;
} else {
@@ -737,11 +732,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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 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);
+ debug!("return type {:?}", hir_ty);
+ let ty = self.astconv().ast_ty_to_ty(hir_ty);
+ debug!("return type {:?}", ty);
+ debug!("expected type {:?}", expected);
+ let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into());
let ty = Binder::bind_with_vars(ty, bound_vars);
let ty = self.normalize(span, ty);
let ty = self.tcx.erase_late_bound_regions(ty);
@@ -988,13 +983,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
.must_apply_modulo_regions()
{
- diag.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- "consider using clone here",
- ".clone()",
- Applicability::MachineApplicable,
- );
- return true;
+ let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) {
+ Some(ident) => format!(": {}.clone()", ident),
+ None => ".clone()".to_string()
+ };
+
+ diag.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ "consider using clone here",
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+ return true;
}
false
}
@@ -1015,11 +1015,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut suggest_copied_or_cloned = || {
let expr_inner_ty = substs.type_at(0);
let expected_inner_ty = expected_substs.type_at(0);
- if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
- && self.can_eq(self.param_env, *ty, expected_inner_ty)
+ if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
+ && self.can_eq(self.param_env, ty, expected_inner_ty)
{
let def_path = self.tcx.def_path_str(adt_def.did());
- if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) {
+ if self.type_is_copy_modulo_regions(self.param_env, ty) {
diag.span_suggestion_verbose(
expr.span.shrink_to_hi(),
format!(
@@ -1033,9 +1033,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
self,
self.param_env,
- *ty,
+ ty,
clone_did,
- expr.span
)
{
diag.span_suggestion_verbose(
@@ -1156,13 +1155,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return false;
}
- diag.span_suggestion(
+ let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) {
+ Some(ident) => format!(": {}.is_some()", ident),
+ None => ".is_some()".to_string(),
+ };
+
+ diag.span_suggestion_verbose(
expr.span.shrink_to_hi(),
"use `Option::is_some` to test if the `Option` has a value",
- ".is_some()",
+ suggestion,
Applicability::MachineApplicable,
);
-
true
}