Move all error reporting into rustc_trait_selection

This commit is contained in:
Michael Goulet
2024-07-21 15:20:41 -04:00
parent f49738ba6c
commit ce8a625092
76 changed files with 2541 additions and 2531 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
//! Error Reporting for Anonymous Region Lifetime Errors
//! where both the regions are anonymous.
use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
use crate::error_reporting::infer::nice_region_error::util::AnonymousParamInfo;
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
use crate::errors::AddLifetimeParamsSuggestion;
use crate::errors::LifetimeMismatch;
use crate::errors::LifetimeMismatchLabels;
use crate::infer::RegionResolutionError;
use crate::infer::SubregionOrigin;
use rustc_errors::Subdiagnostic;
use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::Ty;
use rustc_middle::ty::{Region, TyCtxt};
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
///
/// Consider a case where we have
///
/// ```compile_fail
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
/// x.push(y);
/// }
/// ```
///
/// The example gives
///
/// ```text
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
/// --- --- these references are declared with different lifetimes...
/// x.push(y);
/// ^ ...but data from `y` flows into `x` here
/// ```
///
/// It has been extended for the case of structs too.
///
/// Consider the example
///
/// ```no_run
/// struct Ref<'a> { x: &'a u32 }
/// ```
///
/// ```text
/// fn foo(mut x: Vec<Ref>, y: Ref) {
/// --- --- these structs are declared with different lifetimes...
/// x.push(y);
/// ^ ...but data from `y` flows into `x` here
/// }
/// ```
///
/// It will later be extended to trait objects.
pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorGuaranteed> {
let (span, sub, sup) = self.regions()?;
if let Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::ReferenceOutlivesReferent(..),
..,
)) = self.error
{
// This error doesn't make much sense in this case.
return None;
}
// Determine whether the sub and sup consist of both anonymous (elided) regions.
let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?;
let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?;
let scope_def_id_sup = anon_reg_sup.def_id;
let bregion_sup = anon_reg_sup.bound_region;
let scope_def_id_sub = anon_reg_sub.def_id;
let bregion_sub = anon_reg_sub.bound_region;
let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?;
let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?;
debug!(
"try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
ty_sub, sup, bregion_sup
);
debug!(
"try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
ty_sup, sub, bregion_sub
);
let (ty_sup, ty_fndecl_sup) = ty_sup;
let (ty_sub, ty_fndecl_sub) = ty_sub;
let AnonymousParamInfo { param: anon_param_sup, .. } =
self.find_param_with_region(sup, sup)?;
let AnonymousParamInfo { param: anon_param_sub, .. } =
self.find_param_with_region(sub, sub)?;
let sup_is_ret_type =
self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
let sub_is_ret_type =
self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
debug!(
"try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}",
sub_is_ret_type, sup_is_ret_type
);
let labels = match (sup_is_ret_type, sub_is_ret_type) {
(ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => {
let param_span =
if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span };
LifetimeMismatchLabels::InRet {
param_span,
ret_span,
span,
label_var1: anon_param_sup.pat.simple_ident(),
}
}
(None, None) => LifetimeMismatchLabels::Normal {
hir_equal: ty_sup.hir_id == ty_sub.hir_id,
ty_sup: ty_sup.span,
ty_sub: ty_sub.span,
span,
sup: anon_param_sup.pat.simple_ident(),
sub: anon_param_sub.pat.simple_ident(),
},
};
let suggestion = AddLifetimeParamsSuggestion {
tcx: self.tcx(),
sub,
ty_sup,
ty_sub,
add_note: true,
generic_param_scope: self.generic_param_scope,
};
let err = LifetimeMismatch { span, labels, suggestion };
let reported = self.tcx().dcx().emit_err(err);
Some(reported)
}
}
/// Currently only used in rustc_borrowck, probably should be
/// removed in favour of public_errors::AddLifetimeParamsSuggestion
pub fn suggest_adding_lifetime_params<'tcx>(
tcx: TyCtxt<'tcx>,
err: &mut Diag<'_>,
generic_param_scope: LocalDefId,
sub: Region<'tcx>,
ty_sup: &'tcx Ty<'_>,
ty_sub: &'tcx Ty<'_>,
) {
let suggestion = AddLifetimeParamsSuggestion {
tcx,
sub,
ty_sup,
ty_sub,
add_note: false,
generic_param_scope,
};
suggestion.add_to_diag(err);
}

View File

@@ -0,0 +1,236 @@
use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_bound_vars as rbv;
use rustc_middle::ty::{self, Region, TyCtxt};
/// This function calls the `visit_ty` method for the parameters
/// corresponding to the anonymous regions. The `nested_visitor.found_type`
/// contains the anonymous type.
///
/// # Arguments
/// region - the anonymous region corresponding to the anon_anon conflict
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
///
/// # Example
/// ```compile_fail
/// fn foo(x: &mut Vec<&u8>, y: &u8)
/// { x.push(y); }
/// ```
/// The function returns the nested type corresponding to the anonymous region
/// for e.g., `&u8` and `Vec<&u8>`.
pub fn find_anon_type<'tcx>(
tcx: TyCtxt<'tcx>,
generic_param_scope: LocalDefId,
region: Region<'tcx>,
br: &ty::BoundRegionKind,
) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> {
let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?;
let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?;
fn_sig
.decl
.inputs
.iter()
.find_map(|arg| find_component_for_bound_region(tcx, arg, br))
.map(|ty| (ty, fn_sig))
}
// This method creates a FindNestedTypeVisitor which returns the type corresponding
// to the anonymous region.
fn find_component_for_bound_region<'tcx>(
tcx: TyCtxt<'tcx>,
arg: &'tcx hir::Ty<'tcx>,
br: &ty::BoundRegionKind,
) -> Option<&'tcx hir::Ty<'tcx>> {
FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST }
.visit_ty(arg)
.break_value()
}
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
// anonymous region. The example above would lead to a conflict between
// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
// would be invoked twice, once for each lifetime, and would
// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
// where that lifetime appears. This allows us to highlight the
// specific part of the type in the error message.
struct FindNestedTypeVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
// The bound_region corresponding to the Refree(freeregion)
// associated with the anonymous region we are looking for.
bound_region: ty::BoundRegionKind,
current_index: ty::DebruijnIndex,
}
impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
type Result = ControlFlow<&'tcx hir::Ty<'tcx>>;
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
match arg.kind {
hir::TyKind::BareFn(_) => {
self.current_index.shift_in(1);
intravisit::walk_ty(self, arg);
self.current_index.shift_out(1);
return ControlFlow::Continue(());
}
hir::TyKind::TraitObject(bounds, ..) => {
for bound in bounds {
self.current_index.shift_in(1);
self.visit_poly_trait_ref(bound);
self.current_index.shift_out(1);
}
}
hir::TyKind::Ref(lifetime, _) => {
// the lifetime of the Ref
let hir_id = lifetime.hir_id;
match (self.tcx.named_bound_var(hir_id), self.bound_region) {
// Find the index of the named region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
if id == def_id {
return ControlFlow::Break(arg);
}
}
// Find the index of the named region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index
(
Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)),
ty::BrNamed(def_id, _),
) => {
debug!(
"FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
debruijn_index
);
debug!("LateBound id={:?} def_id={:?}", id, def_id);
if debruijn_index == self.current_index && id == def_id {
return ControlFlow::Break(arg);
}
}
(
Some(
rbv::ResolvedArg::StaticLifetime
| rbv::ResolvedArg::Free(_, _)
| rbv::ResolvedArg::EarlyBound(_)
| rbv::ResolvedArg::LateBound(_, _, _)
| rbv::ResolvedArg::Error(_),
)
| None,
_,
) => {
debug!("no arg found");
}
}
}
// Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
hir::TyKind::Path(_) => {
// Prefer using the lifetime in type arguments rather than lifetime arguments.
intravisit::walk_ty(self, arg)?;
// Call `walk_ty` as `visit_ty` is empty.
return if intravisit::walk_ty(
&mut TyPathVisitor {
tcx: self.tcx,
bound_region: self.bound_region,
current_index: self.current_index,
},
arg,
)
.is_break()
{
ControlFlow::Break(arg)
} else {
ControlFlow::Continue(())
};
}
_ => {}
}
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
// go on to visit `&Foo`
intravisit::walk_ty(self, arg)
}
}
// The visitor captures the corresponding `hir::Ty` of the anonymous region
// in the case of structs ie. `hir::TyKind::Path`.
// This visitor would be invoked for each lifetime corresponding to a struct,
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
// where that lifetime appears. This allows us to highlight the
// specific part of the type in the error message.
struct TyPathVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
bound_region: ty::BoundRegionKind,
current_index: ty::DebruijnIndex,
}
impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
type Result = ControlFlow<()>;
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Map<'tcx> {
self.tcx.hir()
}
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result {
match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) {
// the lifetime of the TyPath!
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
if id == def_id {
return ControlFlow::Break(());
}
}
(Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _)) => {
debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,);
debug!("id={:?}", id);
debug!("def_id={:?}", def_id);
if debruijn_index == self.current_index && id == def_id {
return ControlFlow::Break(());
}
}
(
Some(
rbv::ResolvedArg::StaticLifetime
| rbv::ResolvedArg::EarlyBound(_)
| rbv::ResolvedArg::LateBound(_, _, _)
| rbv::ResolvedArg::Free(_, _)
| rbv::ResolvedArg::Error(_),
)
| None,
_,
) => {
debug!("no arg found");
}
}
ControlFlow::Continue(())
}
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
// ignore nested types
//
// If you have a type like `Foo<'a, &Ty>` we
// are only interested in the immediate lifetimes ('a).
//
// Making `visit_ty` empty will ignore the `&Ty` embedded
// inside, it will get reached by the outer visitor.
debug!("`Ty` corresponding to a struct is {:?}", arg);
ControlFlow::Continue(())
}
}

View File

@@ -0,0 +1,127 @@
//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
//! to hold.
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq};
use crate::errors::{
DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, MismatchedStaticLifetime,
};
use crate::infer::RegionResolutionError;
use crate::infer::{SubregionOrigin, TypeTrace};
use crate::traits::ObligationCauseCode;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_middle::bug;
use rustc_middle::ty::TypeVisitor;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorGuaranteed> {
let error = self.error.as_ref()?;
debug!("try_report_mismatched_static_lifetime {:?}", error);
let RegionResolutionError::ConcreteFailure(origin, sub, sup) = error.clone() else {
return None;
};
if !sub.is_static() {
return None;
}
let SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) = origin else {
return None;
};
// If we added a "points at argument expression" obligation, we remove it here, we care
// about the original obligation only.
let code = match cause.code() {
ObligationCauseCode::FunctionArg { parent_code, .. } => &*parent_code,
code => code,
};
let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else {
return None;
};
let (ObligationCauseCode::WhereClause(_, binding_span)
| ObligationCauseCode::WhereClauseInExpr(_, binding_span, ..)) = *parent.code()
else {
return None;
};
if binding_span.is_dummy() {
return None;
}
// FIXME: we should point at the lifetime
let multi_span: MultiSpan = vec![binding_span].into();
let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq {
unmet_requirements: multi_span,
binding_span,
};
let expl = note_and_explain::RegionExplanation::new(
self.tcx(),
self.generic_param_scope,
sup,
Some(binding_span),
note_and_explain::PrefixKind::Empty,
note_and_explain::SuffixKind::Continues,
);
let mut impl_span = None;
let mut implicit_static_lifetimes = Vec::new();
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
// If an impl is local, then maybe this isn't what they want. Try to
// be as helpful as possible with implicit lifetimes.
// First, let's get the hir self type of the impl
let hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, .. }),
..
}) = impl_node
else {
bug!("Node not an impl.");
};
// Next, let's figure out the set of trait objects with implicit static bounds
let ty = self.tcx().type_of(*impl_def_id).instantiate_identity();
let mut v = super::static_impl_trait::TraitObjectVisitor(FxIndexSet::default());
v.visit_ty(ty);
let mut traits = vec![];
for matching_def_id in v.0 {
let mut hir_v =
super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id);
hir_v.visit_ty(impl_self_ty);
}
if traits.is_empty() {
// If there are no trait object traits to point at, either because
// there aren't trait objects or because none are implicit, then just
// write a single note on the impl itself.
impl_span = Some(self.tcx().def_span(*impl_def_id));
} else {
// Otherwise, point at all implicit static lifetimes
for span in &traits {
implicit_static_lifetimes
.push(ImplicitStaticLifetimeSubdiag::Note { span: *span });
// It would be nice to put this immediately under the above note, but they get
// pushed to the end.
implicit_static_lifetimes
.push(ImplicitStaticLifetimeSubdiag::Sugg { span: span.shrink_to_hi() });
}
}
} else {
// Otherwise just point out the impl.
impl_span = Some(self.tcx().def_span(*impl_def_id));
}
let err = MismatchedStaticLifetime {
cause_span: cause.span,
unmet_lifetime_reqs: multispan_subdiag,
expl,
does_not_outlive_static_from_impl: impl_span
.map(|span| DoesNotOutliveStaticFromImpl::Spanned { span })
.unwrap_or(DoesNotOutliveStaticFromImpl::Unspanned),
implicit_static_lifetimes,
};
let reported = self.tcx().dcx().emit_err(err);
Some(reported)
}
}

View File

@@ -0,0 +1,93 @@
use crate::error_reporting::TypeErrCtxt;
use crate::infer::RegionResolutionError;
use crate::infer::RegionResolutionError::*;
use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Span;
mod different_lifetimes;
pub mod find_anon_type;
mod mismatched_static_lifetime;
mod named_anon_conflict;
pub(crate) mod placeholder_error;
mod placeholder_relation;
mod static_impl_trait;
mod trait_impl_difference;
mod util;
pub use different_lifetimes::suggest_adding_lifetime_params;
pub use find_anon_type::find_anon_type;
pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, TraitObjectVisitor};
pub use util::find_param_with_region;
impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> {
pub fn try_report_nice_region_error(
&'cx self,
generic_param_scope: LocalDefId,
error: &RegionResolutionError<'tcx>,
) -> Option<ErrorGuaranteed> {
NiceRegionError::new(self, generic_param_scope, error.clone()).try_report()
}
}
pub struct NiceRegionError<'cx, 'tcx> {
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
/// The innermost definition that introduces generic parameters that may be involved in
/// the region errors we are dealing with.
generic_param_scope: LocalDefId,
error: Option<RegionResolutionError<'tcx>>,
regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>,
}
impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
pub fn new(
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
generic_param_scope: LocalDefId,
error: RegionResolutionError<'tcx>,
) -> Self {
Self { cx, error: Some(error), regions: None, generic_param_scope }
}
pub fn new_from_span(
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
generic_param_scope: LocalDefId,
span: Span,
sub: ty::Region<'tcx>,
sup: ty::Region<'tcx>,
) -> Self {
Self { cx, error: None, regions: Some((span, sub, sup)), generic_param_scope }
}
fn tcx(&self) -> TyCtxt<'tcx> {
self.cx.tcx
}
pub fn try_report_from_nll(&self) -> Option<Diag<'tcx>> {
// Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
// the nice region errors are required when running under the MIR borrow checker.
self.try_report_named_anon_conflict()
.or_else(|| self.try_report_placeholder_conflict())
.or_else(|| self.try_report_placeholder_relation())
}
pub fn try_report(&self) -> Option<ErrorGuaranteed> {
self.try_report_from_nll()
.map(|diag| diag.emit())
.or_else(|| self.try_report_impl_not_conforming_to_trait())
.or_else(|| self.try_report_anon_anon_conflict())
.or_else(|| self.try_report_static_impl_trait())
.or_else(|| self.try_report_mismatched_static_lifetime())
}
pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
match (&self.error, self.regions) {
(Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)),
(Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => {
Some((origin.span(), *sub, *sup))
}
(None, Some((span, sub, sup))) => Some((span, sub, sup)),
_ => None,
}
}
}

View File

@@ -0,0 +1,92 @@
//! Error Reporting for Anonymous Region Lifetime Errors
//! where one region is named and the other is anonymous.
use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
use crate::errors::ExplicitLifetimeRequired;
use rustc_errors::Diag;
use rustc_middle::ty;
use rustc_span::symbol::kw;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// When given a `ConcreteFailure` for a function with parameters containing a named region and
/// an anonymous region, emit an descriptive diagnostic error.
pub(super) fn try_report_named_anon_conflict(&self) -> Option<Diag<'tcx>> {
let (span, sub, sup) = self.regions()?;
debug!(
"try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
sub, sup, self.error,
);
// Determine whether the sub and sup consist of one named region ('a)
// and one anonymous (elided) region. If so, find the parameter arg
// where the anonymous region appears (there must always be one; we
// only introduced anonymous regions in parameters) as well as a
// version new_ty of its type where the anonymous region is replaced
// with the named one.
let (named, anon, anon_param_info, region_info) = if sub.has_name()
&& let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sup)
&& let Some(anon_param_info) = self.find_param_with_region(sup, sub)
{
(sub, sup, anon_param_info, region_info)
} else if sup.has_name()
&& let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sub)
&& let Some(anon_param_info) = self.find_param_with_region(sub, sup)
{
(sup, sub, anon_param_info, region_info)
} else {
return None; // inapplicable
};
// Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect,
// and can steer users down the wrong path.
if named.is_static() {
return None;
}
debug!("try_report_named_anon_conflict: named = {:?}", named);
debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info);
debug!("try_report_named_anon_conflict: region_info = {:?}", region_info);
let param = anon_param_info.param;
let new_ty = anon_param_info.param_ty;
let new_ty_span = anon_param_info.param_ty_span;
let br = anon_param_info.bound_region;
let is_first = anon_param_info.is_first;
let scope_def_id = region_info.def_id;
let is_impl_item = region_info.is_impl_item;
match br {
ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon => {}
_ => {
/* not an anonymous region */
debug!("try_report_named_anon_conflict: not an anonymous region");
return None;
}
}
if is_impl_item {
debug!("try_report_named_anon_conflict: impl item, bail out");
return None;
}
if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some()
&& self.is_self_anon(is_first, scope_def_id)
{
return None;
}
let named = named.to_string();
let err = match param.pat.simple_ident() {
Some(simple_ident) => ExplicitLifetimeRequired::WithIdent {
span,
simple_ident,
named,
new_ty_span,
new_ty,
},
None => ExplicitLifetimeRequired::WithParamType { span, named, new_ty_span, new_ty },
};
Some(self.tcx().sess.dcx().create_err(err))
}
}

View File

@@ -0,0 +1,496 @@
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
use crate::errors::{
ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes,
TraitPlaceholderMismatch, TyOrSig,
};
use crate::infer::RegionResolutionError;
use crate::infer::ValuePairs;
use crate::infer::{SubregionOrigin, TypeTrace};
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::intern::Interned;
use rustc_errors::{Diag, IntoDiagArg};
use rustc_hir::def::Namespace;
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_middle::bug;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode};
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, RePlaceholder, Region, TyCtxt};
use std::fmt;
// HACK(eddyb) maybe move this in a more central location.
#[derive(Copy, Clone)]
pub struct Highlighted<'tcx, T> {
tcx: TyCtxt<'tcx>,
highlight: RegionHighlightMode<'tcx>,
value: T,
}
impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T>
where
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
{
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
rustc_errors::DiagArgValue::Str(self.to_string().into())
}
}
impl<'tcx, T> Highlighted<'tcx, T> {
fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
}
}
impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
where
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
printer.region_highlight_mode = self.highlight;
self.value.print(&mut printer)?;
f.write_str(&printer.into_buffer())
}
}
impl<'tcx> NiceRegionError<'_, 'tcx> {
/// When given a `ConcreteFailure` for a function with arguments containing a named region and
/// an anonymous region, emit a descriptive diagnostic error.
pub(super) fn try_report_placeholder_conflict(&self) -> Option<Diag<'tcx>> {
match &self.error {
///////////////////////////////////////////////////////////////////////////
// NB. The ordering of cases in this match is very
// sensitive, because we are often matching against
// specific cases and then using an `_` to match all
// others.
///////////////////////////////////////////////////////////////////////////
// Check for errors from comparing trait failures -- first
// with two placeholders, then with one.
Some(RegionResolutionError::SubSupConflict(
vid,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
Some(*sub_placeholder),
Some(*sup_placeholder),
values,
),
Some(RegionResolutionError::SubSupConflict(
vid,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
_,
_,
)) => self.try_report_trait_placeholder_mismatch(
Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
Some(*sub_placeholder),
None,
values,
),
Some(RegionResolutionError::SubSupConflict(
vid,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
_,
_,
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
None,
Some(*sup_placeholder),
values,
),
Some(RegionResolutionError::SubSupConflict(
vid,
_,
_,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
None,
Some(*sup_placeholder),
values,
),
Some(RegionResolutionError::UpperBoundUniverseConflict(
vid,
_,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
)) => self.try_report_trait_placeholder_mismatch(
Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
None,
Some(*sup_placeholder),
values,
),
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sub_region @ Region(Interned(RePlaceholder(_), _)),
sup_region @ Region(Interned(RePlaceholder(_), _)),
)) => self.try_report_trait_placeholder_mismatch(
None,
cause,
Some(*sub_region),
Some(*sup_region),
values,
),
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sub_region @ Region(Interned(RePlaceholder(_), _)),
sup_region,
)) => self.try_report_trait_placeholder_mismatch(
(!sup_region.has_name()).then_some(*sup_region),
cause,
Some(*sub_region),
None,
values,
),
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sub_region,
sup_region @ Region(Interned(RePlaceholder(_), _)),
)) => self.try_report_trait_placeholder_mismatch(
(!sub_region.has_name()).then_some(*sub_region),
cause,
None,
Some(*sup_region),
values,
),
_ => None,
}
}
fn try_report_trait_placeholder_mismatch(
&self,
vid: Option<Region<'tcx>>,
cause: &ObligationCause<'tcx>,
sub_placeholder: Option<Region<'tcx>>,
sup_placeholder: Option<Region<'tcx>>,
value_pairs: &ValuePairs<'tcx>,
) -> Option<Diag<'tcx>> {
let (expected_args, found_args, trait_def_id) = match value_pairs {
ValuePairs::TraitRefs(ExpectedFound { expected, found })
if expected.def_id == found.def_id =>
{
// It's possible that the placeholders come from a binder
// outside of this value pair. Use `no_bound_vars` as a
// simple heuristic for that.
(expected.args, found.args, expected.def_id)
}
_ => return None,
};
Some(self.report_trait_placeholder_mismatch(
vid,
cause,
sub_placeholder,
sup_placeholder,
trait_def_id,
expected_args,
found_args,
))
}
// error[E0308]: implementation of `Foo` does not apply to enough lifetimes
// --> /home/nmatsakis/tmp/foo.rs:12:5
// |
// 12 | all::<&'static u32>();
// | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
// |
// = note: Due to a where-clause on the function `all`,
// = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
// = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
#[instrument(level = "debug", skip(self))]
fn report_trait_placeholder_mismatch(
&self,
vid: Option<Region<'tcx>>,
cause: &ObligationCause<'tcx>,
sub_placeholder: Option<Region<'tcx>>,
sup_placeholder: Option<Region<'tcx>>,
trait_def_id: DefId,
expected_args: GenericArgsRef<'tcx>,
actual_args: GenericArgsRef<'tcx>,
) -> Diag<'tcx> {
let span = cause.span();
let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =
if let ObligationCauseCode::WhereClause(def_id, span)
| ObligationCauseCode::WhereClauseInExpr(def_id, span, ..) = *cause.code()
&& def_id != CRATE_DEF_ID.to_def_id()
{
(
true,
Some(span),
Some(self.tcx().def_span(def_id)),
None,
self.tcx().def_path_str(def_id),
)
} else {
(false, None, None, Some(span), String::new())
};
let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
self.cx.tcx,
trait_def_id,
expected_args,
));
let actual_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
self.cx.tcx,
trait_def_id,
actual_args,
));
// Search the expected and actual trait references to see (a)
// whether the sub/sup placeholders appear in them (sometimes
// you have a trait ref like `T: Foo<fn(&u8)>`, where the
// placeholder was created as part of an inner type) and (b)
// whether the inference variable appears. In each case,
// assign a counter value in each case if so.
let mut counter = 0;
let mut has_sub = None;
let mut has_sup = None;
let mut actual_has_vid = None;
let mut expected_has_vid = None;
self.tcx().for_each_free_region(&expected_trait_ref, |r| {
if Some(r) == sub_placeholder && has_sub.is_none() {
has_sub = Some(counter);
counter += 1;
} else if Some(r) == sup_placeholder && has_sup.is_none() {
has_sup = Some(counter);
counter += 1;
}
if Some(r) == vid && expected_has_vid.is_none() {
expected_has_vid = Some(counter);
counter += 1;
}
});
self.tcx().for_each_free_region(&actual_trait_ref, |r| {
if Some(r) == vid && actual_has_vid.is_none() {
actual_has_vid = Some(counter);
counter += 1;
}
});
let actual_self_ty_has_vid =
self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
let expected_self_ty_has_vid =
self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
debug!(
?actual_has_vid,
?expected_has_vid,
?has_sub,
?has_sup,
?actual_self_ty_has_vid,
?expected_self_ty_has_vid,
);
let actual_impl_expl_notes = self.explain_actual_impl_that_was_found(
sub_placeholder,
sup_placeholder,
has_sub,
has_sup,
expected_trait_ref,
actual_trait_ref,
vid,
expected_has_vid,
actual_has_vid,
any_self_ty_has_vid,
leading_ellipsis,
);
self.tcx().dcx().create_err(TraitPlaceholderMismatch {
span,
satisfy_span,
where_span,
dup_span,
def_id,
trait_def_id: self.tcx().def_path_str(trait_def_id),
actual_impl_expl_notes,
})
}
/// Add notes with details about the expected and actual trait refs, with attention to cases
/// when placeholder regions are involved: either the trait or the self type containing
/// them needs to be mentioned the closest to the placeholders.
/// This makes the error messages read better, however at the cost of some complexity
/// due to the number of combinations we have to deal with.
fn explain_actual_impl_that_was_found(
&self,
sub_placeholder: Option<Region<'tcx>>,
sup_placeholder: Option<Region<'tcx>>,
has_sub: Option<usize>,
has_sup: Option<usize>,
expected_trait_ref: ty::TraitRef<'tcx>,
actual_trait_ref: ty::TraitRef<'tcx>,
vid: Option<Region<'tcx>>,
expected_has_vid: Option<usize>,
actual_has_vid: Option<usize>,
any_self_ty_has_vid: bool,
leading_ellipsis: bool,
) -> Vec<ActualImplExplNotes<'tcx>> {
// The weird thing here with the `maybe_highlighting_region` calls and the
// the match inside is meant to be like this:
//
// - The match checks whether the given things (placeholders, etc) appear
// in the types are about to print
// - Meanwhile, the `maybe_highlighting_region` calls set up
// highlights so that, if they do appear, we will replace
// them `'0` and whatever. (This replacement takes place
// inside the closure given to `maybe_highlighting_region`.)
//
// There is some duplication between the calls -- i.e., the
// `maybe_highlighting_region` checks if (e.g.) `has_sub` is
// None, an then we check again inside the closure, but this
// setup sort of minimized the number of calls and so form.
let highlight_trait_ref = |trait_ref| Highlighted {
tcx: self.tcx(),
highlight: RegionHighlightMode::default(),
value: trait_ref,
};
let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
let passive_voice = match (has_sub, has_sup) {
(Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
(None, None) => {
expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
match expected_has_vid {
Some(_) => true,
None => any_self_ty_has_vid,
}
}
};
let (kind, ty_or_sig, trait_path) = if same_self_type {
let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
{
let closure_sig = self_ty.map(|closure| {
if let ty::Closure(_, args) = closure.kind() {
self.tcx()
.signature_unclosure(args.as_closure().sig(), rustc_hir::Safety::Safe)
} else {
bug!("type is not longer closure");
}
});
(
ActualImplExpectedKind::Signature,
TyOrSig::ClosureSig(closure_sig),
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
)
} else {
(
ActualImplExpectedKind::Other,
TyOrSig::Ty(self_ty),
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
)
}
} else if passive_voice {
(
ActualImplExpectedKind::Passive,
TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
)
} else {
(
ActualImplExpectedKind::Other,
TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
)
};
let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) {
(Some(n1), Some(n2)) => {
(ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2))
}
(Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0),
(None, None) => {
if let Some(n) = expected_has_vid {
(ActualImplExpectedLifetimeKind::Some, n, 0)
} else {
(ActualImplExpectedLifetimeKind::Nothing, 0, 0)
}
}
};
let note_1 = ActualImplExplNotes::new_expected(
kind,
lt_kind,
leading_ellipsis,
ty_or_sig,
trait_path,
lifetime_1,
lifetime_2,
);
let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
let passive_voice = match actual_has_vid {
Some(_) => any_self_ty_has_vid,
None => true,
};
let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path());
let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string();
let has_lifetime = actual_has_vid.is_some();
let lifetime = actual_has_vid.unwrap_or_default();
let note_2 = if same_self_type {
ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime }
} else if passive_voice {
ActualImplExplNotes::ButActuallyImplementedForTy {
trait_path,
ty,
has_lifetime,
lifetime,
}
} else {
ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime }
};
vec![note_1, note_2]
}
}

View File

@@ -0,0 +1,86 @@
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
use crate::errors::PlaceholderRelationLfNotSatisfied;
use crate::infer::{RegionResolutionError, SubregionOrigin};
use rustc_data_structures::intern::Interned;
use rustc_errors::Diag;
use rustc_middle::ty::{self, RePlaceholder, Region};
impl<'tcx> NiceRegionError<'_, 'tcx> {
/// Emitted wwhen given a `ConcreteFailure` when relating two placeholders.
pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
match &self.error {
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::RelateRegionParamBound(span),
Region(Interned(
RePlaceholder(ty::Placeholder {
bound: ty::BoundRegion { kind: sub_name, .. },
..
}),
_,
)),
Region(Interned(
RePlaceholder(ty::Placeholder {
bound: ty::BoundRegion { kind: sup_name, .. },
..
}),
_,
)),
)) => {
let span = *span;
let (sub_span, sub_symbol) = match sub_name {
ty::BrNamed(def_id, symbol) => {
(Some(self.tcx().def_span(def_id)), Some(symbol))
}
ty::BrAnon | ty::BrEnv => (None, None),
};
let (sup_span, sup_symbol) = match sup_name {
ty::BrNamed(def_id, symbol) => {
(Some(self.tcx().def_span(def_id)), Some(symbol))
}
ty::BrAnon | ty::BrEnv => (None, None),
};
let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) {
(Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => {
PlaceholderRelationLfNotSatisfied::HasBoth {
span,
sub_span,
sup_span,
sub_symbol,
sup_symbol,
note: (),
}
}
(Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => {
PlaceholderRelationLfNotSatisfied::HasSup {
span,
sub_span,
sup_span,
sup_symbol,
note: (),
}
}
(Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => {
PlaceholderRelationLfNotSatisfied::HasSub {
span,
sub_span,
sup_span,
sub_symbol,
note: (),
}
}
(Some(sub_span), Some(sup_span), _, _) => {
PlaceholderRelationLfNotSatisfied::HasNone {
span,
sub_span,
sup_span,
note: (),
}
}
_ => PlaceholderRelationLfNotSatisfied::OnlyPrimarySpan { span, note: () },
};
Some(self.tcx().dcx().create_err(diag))
}
_ => None,
}
}
}

View File

@@ -0,0 +1,618 @@
//! Error Reporting for static impl Traits.
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
use crate::errors::{
ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted,
ReqIntroducedLocations,
};
use crate::infer::RegionResolutionError;
use crate::infer::{SubregionOrigin, TypeTrace};
use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{
self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind,
};
use rustc_middle::ty::{
self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
};
use rustc_span::symbol::Ident;
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when the return type is a static `impl Trait`,
/// `dyn Trait` or if a method call on a trait object introduces a static requirement.
pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
debug!("try_report_static_impl_trait(error={:?})", self.error);
let tcx = self.tcx();
let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
RegionResolutionError::SubSupConflict(
_,
var_origin,
sub_origin,
sub_r,
sup_origin,
sup_r,
spans,
) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
sub_r,
sup_r,
) if sub_r.is_static() => {
// This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
// This may have a closure and it would cause ICE
// through `find_param_with_region` (#78262).
let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
if fn_returns.is_empty() {
return None;
}
let param = self.find_param_with_region(*sup_r, *sub_r)?;
let simple_ident = param.param.pat.simple_ident();
let (has_impl_path, impl_path) = match ctxt.assoc_item.container {
AssocItemContainer::TraitContainer => {
let id = ctxt.assoc_item.container_id(tcx);
(true, tcx.def_path_str(id))
}
AssocItemContainer::ImplContainer => (false, String::new()),
};
let mut err = self.tcx().dcx().create_err(ButCallingIntroduces {
param_ty_span: param.param_ty_span,
cause_span: cause.span,
has_param_name: simple_ident.is_some(),
param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
has_lifetime: sup_r.has_name(),
lifetime: sup_r.to_string(),
assoc_item: ctxt.assoc_item.name,
has_impl_path,
impl_path,
});
if self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt) {
let reported = err.emit();
return Some(reported);
} else {
err.cancel()
}
}
return None;
}
_ => return None,
};
debug!(
"try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
var_origin, sub_origin, sub_r, sup_origin, sup_r
);
let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
let sp = var_origin.span();
let return_sp = sub_origin.span();
let param = self.find_param_with_region(*sup_r, *sub_r)?;
let simple_ident = param.param.pat.simple_ident();
let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
let (mention_influencer, influencer_point) =
if sup_origin.span().overlaps(param.param_ty_span) {
// Account for `async fn` like in `async-await/issues/issue-62097.rs`.
// The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same
// place (but with different `ctxt`, hence `overlaps` instead of `==` above).
//
// This avoids the following:
//
// LL | pub async fn run_dummy_fn(&self) {
// | ^^^^^
// | |
// | this data with an anonymous lifetime `'_`...
// | ...is captured here...
(false, sup_origin.span())
} else {
(!sup_origin.span().overlaps(return_sp), param.param_ty_span)
};
debug!("try_report_static_impl_trait: param_info={:?}", param);
let mut spans = spans.clone();
if mention_influencer {
spans.push(sup_origin.span());
}
// We dedup the spans *ignoring* expansion context.
spans.sort();
spans.dedup_by_key(|span| (span.lo(), span.hi()));
// We try to make the output have fewer overlapping spans if possible.
let require_span =
if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
let spans_empty = spans.is_empty();
let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp);
let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
Some(*bound)
} else {
None
};
let mut subdiag = None;
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
if let ObligationCauseCode::ReturnValue(hir_id)
| ObligationCauseCode::BlockTailExpression(hir_id, ..) = cause.code()
{
let parent_id = tcx.hir().get_parent_item(*hir_id);
if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) {
let mut span: MultiSpan = fn_decl.output.span().into();
let mut spans = Vec::new();
let mut add_label = true;
if let hir::FnRetTy::Return(ty) = fn_decl.output {
let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
v.visit_ty(ty);
if !v.0.is_empty() {
span = v.0.clone().into();
spans = v.0;
add_label = false;
}
}
let fn_decl_span = fn_decl.output.span();
subdiag = Some(ReqIntroducedLocations {
span,
spans,
fn_decl_span,
cause_span: cause.span,
add_label,
});
}
}
}
let diag = ButNeedsToSatisfy {
sp,
influencer_point,
spans: spans.clone(),
// If any of the "captured here" labels appears on the same line or after
// `require_span`, we put it on a note to ensure the text flows by appearing
// always at the end.
require_span_as_note: require_as_note.then_some(require_span),
// We don't need a note, it's already at the end, it can be shown as a `span_label`.
require_span_as_label: (!require_as_note).then_some(require_span),
req_introduces_loc: subdiag,
has_lifetime: sup_r.has_name(),
lifetime: lifetime_name.clone(),
has_param_name: simple_ident.is_some(),
param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
spans_empty,
bound,
};
let mut err = self.tcx().dcx().create_err(diag);
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
let mut override_error_code = None;
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin
&& let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code()
// Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
// `'static` lifetime when called as a method on a binding: `bar.qux()`.
&& self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt)
{
override_error_code = Some(ctxt.assoc_item.name);
}
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin
&& let code = match cause.code() {
ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
_ => cause.code(),
}
&& let (
&ObligationCauseCode::WhereClause(item_def_id, _)
| &ObligationCauseCode::WhereClauseInExpr(item_def_id, ..),
None,
) = (code, override_error_code)
{
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
// lifetime as above, but called using a fully-qualified path to the method:
// `Foo::qux(bar)`.
let mut v = TraitObjectVisitor(FxIndexSet::default());
v.visit_ty(param.param_ty);
if let Some((ident, self_ty)) =
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0)
&& self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty)
{
override_error_code = Some(ident.name);
}
}
if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
// Provide a more targeted error code and description.
let retarget_subdiag = MoreTargeted { ident };
retarget_subdiag.add_to_diag(&mut err);
}
let arg = match param.param.pat.simple_ident() {
Some(simple_ident) => format!("argument `{simple_ident}`"),
None => "the argument".to_string(),
};
let captures = format!("captures data from {arg}");
suggest_new_region_bound(
tcx,
&mut err,
fn_returns,
lifetime_name,
Some(arg),
captures,
Some((param.param_ty_span, param.param_ty.to_string())),
Some(anon_reg_sup.def_id),
);
let reported = err.emit();
Some(reported)
}
}
pub fn suggest_new_region_bound(
tcx: TyCtxt<'_>,
err: &mut Diag<'_>,
fn_returns: Vec<&rustc_hir::Ty<'_>>,
lifetime_name: String,
arg: Option<String>,
captures: String,
param: Option<(Span, String)>,
scope_def_id: Option<LocalDefId>,
) {
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
let consider = "consider changing";
let declare = "to declare that";
let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound");
let explicit_static =
arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}"));
let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
let plus_lt = format!(" + {lifetime_name}");
for fn_return in fn_returns {
if fn_return.span.desugaring_kind().is_some() {
// Skip `async` desugaring `impl Future`.
continue;
}
match fn_return.kind {
// FIXME(precise_captures): Suggest adding to `use<...>` list instead.
TyKind::OpaqueDef(item_id, _, _) => {
let item = tcx.hir().item(item_id);
let ItemKind::OpaqueTy(opaque) = &item.kind else {
return;
};
// Get the identity type for this RPIT
let did = item_id.owner_id.to_def_id();
let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did));
if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
GenericBound::Outlives(Lifetime {
res: LifetimeName::Static, ident, ..
}) => Some(ident.span),
_ => None,
}) {
if let Some(explicit_static) = &explicit_static {
err.span_suggestion_verbose(
span,
format!("{consider} `{ty}`'s {explicit_static}"),
&lifetime_name,
Applicability::MaybeIncorrect,
);
}
if let Some((param_span, ref param_ty)) = param {
err.span_suggestion_verbose(
param_span,
add_static_bound,
param_ty,
Applicability::MaybeIncorrect,
);
}
} else if opaque.bounds.iter().any(|arg| {
matches!(arg,
GenericBound::Outlives(Lifetime { ident, .. })
if ident.name.to_string() == lifetime_name )
}) {
} else {
// get a lifetime name of existing named lifetimes if any
let existing_lt_name = if let Some(id) = scope_def_id
&& let Some(generics) = tcx.hir().get_generics(id)
&& let named_lifetimes = generics
.params
.iter()
.filter(|p| {
matches!(
p.kind,
GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit
}
)
})
.map(|p| {
if let hir::ParamName::Plain(name) = p.name {
Some(name.to_string())
} else {
None
}
})
.filter(|n| !matches!(n, None))
.collect::<Vec<_>>()
&& named_lifetimes.len() > 0
{
named_lifetimes[0].clone()
} else {
None
};
let name = if let Some(name) = &existing_lt_name { name } else { "'a" };
// if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
// introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
if let Some(id) = scope_def_id
&& let Some(generics) = tcx.hir().get_generics(id)
&& let mut spans_suggs =
make_elided_region_spans_suggs(name, generics.params.iter())
&& spans_suggs.len() > 1
{
let use_lt = if existing_lt_name == None {
spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
format!("you can introduce a named lifetime parameter `{name}`")
} else {
// make use the existing named lifetime
format!("you can use the named lifetime parameter `{name}`")
};
spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
err.multipart_suggestion_verbose(
format!("{declare} `{ty}` {captures}, {use_lt}",),
spans_suggs,
Applicability::MaybeIncorrect,
);
} else {
err.span_suggestion_verbose(
fn_return.span.shrink_to_hi(),
format!("{declare} `{ty}` {captures}, {explicit}",),
&plus_lt,
Applicability::MaybeIncorrect,
);
}
}
}
TyKind::TraitObject(_, lt, _) => {
if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
err.span_suggestion_verbose(
fn_return.span.shrink_to_hi(),
format!("{declare} the trait object {captures}, {explicit}",),
&plus_lt,
Applicability::MaybeIncorrect,
);
} else if lt.ident.name.to_string() != lifetime_name {
// With this check we avoid suggesting redundant bounds. This
// would happen if there are nested impl/dyn traits and only
// one of them has the bound we'd suggest already there, like
// in `impl Foo<X = dyn Bar> + '_`.
if let Some(explicit_static) = &explicit_static {
err.span_suggestion_verbose(
lt.ident.span,
format!("{consider} the trait object's {explicit_static}"),
&lifetime_name,
Applicability::MaybeIncorrect,
);
}
if let Some((param_span, param_ty)) = param.clone() {
err.span_suggestion_verbose(
param_span,
add_static_bound,
param_ty,
Applicability::MaybeIncorrect,
);
}
}
}
_ => {}
}
}
}
fn make_elided_region_spans_suggs<'a>(
name: &str,
generic_params: impl Iterator<Item = &'a GenericParam<'a>>,
) -> Vec<(Span, String)> {
let mut spans_suggs = Vec::new();
let mut bracket_span = None;
let mut consecutive_brackets = 0;
let mut process_consecutive_brackets =
|span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| {
if span
.is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span))
{
consecutive_brackets += 1;
} else if let Some(bracket_span) = bracket_span.take() {
let sugg = std::iter::once("<")
.chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", "))
.chain([">"])
.collect();
spans_suggs.push((bracket_span.shrink_to_hi(), sugg));
consecutive_brackets = 0;
}
bracket_span = span;
};
for p in generic_params {
if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind {
match kind {
MissingLifetimeKind::Underscore => {
process_consecutive_brackets(None, &mut spans_suggs);
spans_suggs.push((p.span, name.to_string()))
}
MissingLifetimeKind::Ampersand => {
process_consecutive_brackets(None, &mut spans_suggs);
spans_suggs.push((p.span.shrink_to_hi(), format!("{name} ")));
}
MissingLifetimeKind::Comma => {
process_consecutive_brackets(None, &mut spans_suggs);
spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, ")));
}
MissingLifetimeKind::Brackets => {
process_consecutive_brackets(Some(p.span), &mut spans_suggs);
}
}
}
}
process_consecutive_brackets(None, &mut spans_suggs);
spans_suggs
}
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
pub fn get_impl_ident_and_self_ty_from_trait(
tcx: TyCtxt<'tcx>,
def_id: DefId,
trait_objects: &FxIndexSet<DefId>,
) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
match tcx.hir().get_if_local(def_id)? {
Node::ImplItem(impl_item) => {
let impl_did = tcx.hir().get_parent_item(impl_item.hir_id());
if let hir::OwnerNode::Item(Item {
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
..
}) = tcx.hir_owner_node(impl_did)
{
Some((impl_item.ident, self_ty))
} else {
None
}
}
Node::TraitItem(trait_item) => {
let trait_id = tcx.hir().get_parent_item(trait_item.hir_id());
debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
// The method being called is defined in the `trait`, but the `'static`
// obligation comes from the `impl`. Find that `impl` so that we can point
// at it in the suggestion.
let trait_did = trait_id.to_def_id();
tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| {
if let Node::Item(Item {
kind: ItemKind::Impl(hir::Impl { self_ty, .. }), ..
}) = tcx.hir_node_by_def_id(impl_did)
&& trait_objects.iter().all(|did| {
// FIXME: we should check `self_ty` against the receiver
// type in the `UnifyReceiver` context, but for now, use
// this imperfect proxy. This will fail if there are
// multiple `impl`s for the same trait like
// `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
// In that case, only the first one will get suggestions.
let mut traits = vec![];
let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
hir_v.visit_ty(self_ty);
!traits.is_empty()
})
{
Some((trait_item.ident, *self_ty))
} else {
None
}
})
}
_ => None,
}
}
/// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
/// `'static` obligation. Suggest relaxing that implicit bound.
fn find_impl_on_dyn_trait(
&self,
err: &mut Diag<'_>,
ty: Ty<'_>,
ctxt: &UnifyReceiverContext<'tcx>,
) -> bool {
let tcx = self.tcx();
// Find the method being called.
let Ok(Some(instance)) = ty::Instance::try_resolve(
tcx,
ctxt.param_env,
ctxt.assoc_item.def_id,
self.cx.resolve_vars_if_possible(ctxt.args),
) else {
return false;
};
let mut v = TraitObjectVisitor(FxIndexSet::default());
v.visit_ty(ty);
// Get the `Ident` of the method being called and the corresponding `impl` (to point at
// `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
let Some((ident, self_ty)) =
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0)
else {
return false;
};
// Find the trait object types in the argument, so we point at *only* the trait object.
self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
}
fn suggest_constrain_dyn_trait_in_impl(
&self,
err: &mut Diag<'_>,
found_dids: &FxIndexSet<DefId>,
ident: Ident,
self_ty: &hir::Ty<'_>,
) -> bool {
let mut suggested = false;
for found_did in found_dids {
let mut traits = vec![];
let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
hir_v.visit_ty(self_ty);
for &span in &traits {
let subdiag = DynTraitConstraintSuggestion { span, ident };
subdiag.add_to_diag(err);
suggested = true;
}
}
suggested
}
}
/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor {
fn visit_ty(&mut self, t: Ty<'tcx>) {
match t.kind() {
ty::Dynamic(preds, re, _) if re.is_static() => {
if let Some(def_id) = preds.principal_def_id() {
self.0.insert(def_id);
}
}
_ => t.super_visit_with(self),
}
}
}
/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
if let TyKind::TraitObject(
poly_trait_refs,
Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
_,
) = t.kind
{
for ptr in poly_trait_refs {
if Some(self.1) == ptr.trait_ref.trait_def_id() {
self.0.push(ptr.span);
}
}
}
walk_ty(self, t);
}
}

View File

@@ -0,0 +1,162 @@
//! Error Reporting for `impl` items that do not match the obligations from their `trait`.
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff};
use crate::infer::RegionResolutionError;
use crate::infer::{Subtype, ValuePairs};
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::RegionHighlightMode;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
use rustc_span::Span;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> {
let error = self.error.as_ref()?;
debug!("try_report_impl_not_conforming_to_trait {:?}", error);
if let RegionResolutionError::SubSupConflict(
_,
var_origin,
sub_origin,
_sub,
sup_origin,
_sup,
_,
) = error.clone()
&& let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
&& let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } =
sub_trace.cause.code()
&& sub_trace.values == sup_trace.values
&& let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
{
// FIXME(compiler-errors): Don't like that this needs `Ty`s, but
// all of the region highlighting machinery only deals with those.
let guar = self.emit_err(
var_origin.span(),
Ty::new_fn_ptr(self.cx.tcx, expected),
Ty::new_fn_ptr(self.cx.tcx, found),
*trait_item_def_id,
);
return Some(guar);
}
None
}
fn emit_err(
&self,
sp: Span,
expected: Ty<'tcx>,
found: Ty<'tcx>,
trait_def_id: DefId,
) -> ErrorGuaranteed {
let trait_sp = self.tcx().def_span(trait_def_id);
// Mark all unnamed regions in the type with a number.
// This diagnostic is called in response to lifetime errors, so be informative.
struct HighlightBuilder<'tcx> {
highlight: RegionHighlightMode<'tcx>,
counter: usize,
}
impl<'tcx> HighlightBuilder<'tcx> {
fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
let mut builder =
HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
builder.visit_ty(ty);
builder.highlight
}
}
impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
fn visit_region(&mut self, r: ty::Region<'tcx>) {
if !r.has_name() && self.counter <= 3 {
self.highlight.highlighting_region(r, self.counter);
self.counter += 1;
}
}
}
let expected_highlight = HighlightBuilder::build(expected);
let expected = self
.cx
.extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
.name;
let found_highlight = HighlightBuilder::build(found);
let found =
self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
// Get the span of all the used type parameters in the method.
let assoc_item = self.tcx().associated_item(trait_def_id);
let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
match assoc_item.kind {
ty::AssocKind::Fn => {
let hir = self.tcx().hir();
if let Some(hir_id) =
assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id))
{
if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
visitor.visit_fn_decl(decl);
}
}
}
_ => {}
}
let diag = TraitImplDiff {
sp,
trait_sp,
note: (),
param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() },
rel_help: visitor.types.is_empty().then_some(RelationshipHelp),
expected,
found,
};
self.tcx().dcx().emit_err(diag)
}
}
struct TypeParamSpanVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
types: Vec<Span>,
}
impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
match arg.kind {
hir::TyKind::Ref(_, ref mut_ty) => {
// We don't want to suggest looking into borrowing `&T` or `&Self`.
hir::intravisit::walk_ty(self, mut_ty.ty);
return;
}
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
[segment]
if matches!(
segment.res,
Res::SelfTyParam { .. }
| Res::SelfTyAlias { .. }
| Res::Def(hir::def::DefKind::TyParam, _)
) =>
{
self.types.push(path.span);
}
_ => {}
},
_ => {}
}
hir::intravisit::walk_ty(self, arg);
}
}

View File

@@ -0,0 +1,166 @@
//! Helper functions corresponding to lifetime errors due to
//! anonymous regions.
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable};
use rustc_span::Span;
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
/// Information about the anonymous region we are searching for.
#[derive(Debug)]
pub struct AnonymousParamInfo<'tcx> {
/// The parameter corresponding to the anonymous region.
pub param: &'tcx hir::Param<'tcx>,
/// The type corresponding to the anonymous region parameter.
pub param_ty: Ty<'tcx>,
/// The ty::BoundRegionKind corresponding to the anonymous region.
pub bound_region: ty::BoundRegionKind,
/// The `Span` of the parameter type.
pub param_ty_span: Span,
/// Signals that the argument is the first parameter in the declaration.
pub is_first: bool,
}
// This method walks the Type of the function body parameters using
// `fold_regions()` function and returns the
// &hir::Param of the function parameter corresponding to the anonymous
// region and the Ty corresponding to the named region.
// Currently only the case where the function declaration consists of
// one named region and one anonymous region is handled.
// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
// Here, we would return the hir::Param for y, we return the type &'a
// i32, which is the type of y but with the anonymous region replaced
// with 'a, the corresponding bound region and is_first which is true if
// the hir::Param is the first parameter in the function declaration.
#[instrument(skip(tcx), level = "debug")]
pub fn find_param_with_region<'tcx>(
tcx: TyCtxt<'tcx>,
generic_param_scope: LocalDefId,
anon_region: Region<'tcx>,
replace_region: Region<'tcx>,
) -> Option<AnonymousParamInfo<'tcx>> {
let (id, bound_region) = match *anon_region {
ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region),
ty::ReEarlyParam(ebr) => {
let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id;
(tcx.parent(region_def), ty::BoundRegionKind::BrNamed(region_def, ebr.name))
}
_ => return None, // not a free region
};
let hir = &tcx.hir();
let def_id = id.as_local()?;
// FIXME: use def_kind
// Don't perform this on closures
match tcx.hir_node_by_def_id(generic_param_scope) {
hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
return None;
}
_ => {}
}
let body = hir.maybe_body_owned_by(def_id)?;
let owner_id = hir.body_owner(body.id());
let fn_decl = hir.fn_decl_by_hir_id(owner_id)?;
let poly_fn_sig = tcx.fn_sig(id).instantiate_identity();
let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
body.params
.iter()
.take(if fn_sig.c_variadic {
fn_sig.inputs().len()
} else {
assert_eq!(fn_sig.inputs().len(), body.params.len());
body.params.len()
})
.enumerate()
.find_map(|(index, param)| {
// May return None; sometimes the tables are not yet populated.
let ty = fn_sig.inputs()[index];
let mut found_anon_region = false;
let new_param_ty = tcx.fold_regions(ty, |r, _| {
if r == anon_region {
found_anon_region = true;
replace_region
} else {
r
}
});
found_anon_region.then(|| {
let ty_hir_id = fn_decl.inputs[index].hir_id;
let param_ty_span = hir.span(ty_hir_id);
let is_first = index == 0;
AnonymousParamInfo {
param,
param_ty: new_param_ty,
param_ty_span,
bound_region,
is_first,
}
})
})
}
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
pub(super) fn find_param_with_region(
&self,
anon_region: Region<'tcx>,
replace_region: Region<'tcx>,
) -> Option<AnonymousParamInfo<'tcx>> {
find_param_with_region(self.tcx(), self.generic_param_scope, anon_region, replace_region)
}
// Here, we check for the case where the anonymous region
// is in the return type as written by the user.
// FIXME(#42703) - Need to handle certain cases here.
pub(super) fn is_return_type_anon(
&self,
scope_def_id: LocalDefId,
br: ty::BoundRegionKind,
hir_sig: &hir::FnSig<'_>,
) -> Option<Span> {
let fn_ty = self.tcx().type_of(scope_def_id).instantiate_identity();
if let ty::FnDef(_, _) = fn_ty.kind() {
let ret_ty = fn_ty.fn_sig(self.tcx()).output();
let span = hir_sig.decl.output.span();
let future_output = if hir_sig.header.is_async() {
ret_ty.map_bound(|ty| self.cx.get_impl_future_output_ty(ty)).transpose()
} else {
None
};
return match future_output {
Some(output) if self.includes_region(output, br) => Some(span),
None if self.includes_region(ret_ty, br) => Some(span),
_ => None,
};
}
None
}
fn includes_region(
&self,
ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
region: ty::BoundRegionKind,
) -> bool {
let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
// We are only checking is any region meets the condition so order doesn't matter
#[allow(rustc::potential_query_instability)]
late_bound_regions.iter().any(|r| *r == region)
}
// Here we check for the case where anonymous region
// corresponds to self and if yes, we display E0312.
// FIXME(#42700) - Need to format self properly to
// enable E0621 for it.
pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: LocalDefId) -> bool {
is_first
&& self
.tcx()
.opt_associated_item(scope_def_id.to_def_id())
.is_some_and(|i| i.fn_has_self_parameter)
}
}

View File

@@ -0,0 +1,421 @@
use crate::error_reporting::infer::{note_and_explain_region, TypeErrCtxt};
use crate::errors::{
note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
};
use crate::fluent_generated as fluent;
use crate::infer::{self, SubregionOrigin};
use rustc_errors::{Diag, Subdiagnostic};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
use rustc_span::symbol::kw;
use super::ObligationCauseAsDiagArg;
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
match *origin {
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
span: trace.cause.span,
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
}
.add_to_diag(err),
infer::Reborrow(span) => {
RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
}
infer::RelateObjectBound(span) => {
RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
.add_to_diag(err);
}
infer::ReferenceOutlivesReferent(ty, span) => {
RegionOriginNote::WithName {
span,
msg: fluent::infer_reference_outlives_referent,
name: &self.ty_to_string(ty),
continues: false,
}
.add_to_diag(err);
}
infer::RelateParamBound(span, ty, opt_span) => {
RegionOriginNote::WithName {
span,
msg: fluent::infer_relate_param_bound,
name: &self.ty_to_string(ty),
continues: opt_span.is_some(),
}
.add_to_diag(err);
if let Some(span) = opt_span {
RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
.add_to_diag(err);
}
}
infer::RelateRegionParamBound(span) => {
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
.add_to_diag(err);
}
infer::CompareImplItemObligation { span, .. } => {
RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
.add_to_diag(err);
}
infer::CheckAssociatedTypeBounds { ref parent, .. } => {
self.note_region_origin(err, parent);
}
infer::AscribeUserTypeProvePredicate(span) => {
RegionOriginNote::Plain {
span,
msg: fluent::infer_ascribe_user_type_prove_predicate,
}
.add_to_diag(err);
}
}
}
pub(super) fn report_concrete_failure(
&self,
generic_param_scope: LocalDefId,
origin: SubregionOrigin<'tcx>,
sub: Region<'tcx>,
sup: Region<'tcx>,
) -> Diag<'a> {
let mut err = match origin {
infer::Subtype(box trace) => {
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
let mut err = self.report_and_explain_type_error(trace, terr);
match (*sub, *sup) {
(ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
(ty::RePlaceholder(_), _) => {
note_and_explain_region(
self.tcx,
&mut err,
generic_param_scope,
"",
sup,
" doesn't meet the lifetime requirements",
None,
);
}
(_, ty::RePlaceholder(_)) => {
note_and_explain_region(
self.tcx,
&mut err,
generic_param_scope,
"the required lifetime does not necessarily outlive ",
sub,
"",
None,
);
}
_ => {
note_and_explain_region(
self.tcx,
&mut err,
generic_param_scope,
"",
sup,
"...",
None,
);
note_and_explain_region(
self.tcx,
&mut err,
generic_param_scope,
"...does not necessarily outlive ",
sub,
"",
None,
);
}
}
err
}
infer::Reborrow(span) => {
let reference_valid = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sub,
None,
note_and_explain::PrefixKind::RefValidFor,
note_and_explain::SuffixKind::Continues,
);
let content_valid = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sup,
None,
note_and_explain::PrefixKind::ContentValidFor,
note_and_explain::SuffixKind::Empty,
);
self.dcx().create_err(OutlivesContent {
span,
notes: reference_valid.into_iter().chain(content_valid).collect(),
})
}
infer::RelateObjectBound(span) => {
let object_valid = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sub,
None,
note_and_explain::PrefixKind::TypeObjValidFor,
note_and_explain::SuffixKind::Empty,
);
let pointer_valid = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sup,
None,
note_and_explain::PrefixKind::SourcePointerValidFor,
note_and_explain::SuffixKind::Empty,
);
self.dcx().create_err(OutlivesBound {
span,
notes: object_valid.into_iter().chain(pointer_valid).collect(),
})
}
infer::RelateParamBound(span, ty, opt_span) => {
let prefix = match *sub {
ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
_ => note_and_explain::PrefixKind::TypeOutlive,
};
let suffix = if opt_span.is_some() {
note_and_explain::SuffixKind::ReqByBinding
} else {
note_and_explain::SuffixKind::Empty
};
let note = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sub,
opt_span,
prefix,
suffix,
);
self.dcx().create_err(FulfillReqLifetime {
span,
ty: self.resolve_vars_if_possible(ty),
note,
})
}
infer::RelateRegionParamBound(span) => {
let param_instantiated = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sup,
None,
note_and_explain::PrefixKind::LfParamInstantiatedWith,
note_and_explain::SuffixKind::Empty,
);
let param_must_outlive = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sub,
None,
note_and_explain::PrefixKind::LfParamMustOutlive,
note_and_explain::SuffixKind::Empty,
);
self.dcx().create_err(LfBoundNotSatisfied {
span,
notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
})
}
infer::ReferenceOutlivesReferent(ty, span) => {
let pointer_valid = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sub,
None,
note_and_explain::PrefixKind::PointerValidFor,
note_and_explain::SuffixKind::Empty,
);
let data_valid = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sup,
None,
note_and_explain::PrefixKind::DataValidFor,
note_and_explain::SuffixKind::Empty,
);
self.dcx().create_err(RefLongerThanData {
span,
ty: self.resolve_vars_if_possible(ty),
notes: pointer_valid.into_iter().chain(data_valid).collect(),
})
}
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
let mut err = self.infcx.report_extra_impl_obligation(
span,
impl_item_def_id,
trait_item_def_id,
&format!("`{sup}: {sub}`"),
);
// We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
&& generics.where_clause_span.contains(span)
{
self.suggest_copy_trait_method_bounds(
trait_item_def_id,
impl_item_def_id,
&mut err,
);
}
err
}
infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
// Don't mention the item name if it's an RPITIT, since that'll just confuse
// folks.
if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
let trait_item_span = self.tcx.def_span(trait_item_def_id);
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
err.span_label(
trait_item_span,
format!("definition of `{item_name}` from trait"),
);
}
self.suggest_copy_trait_method_bounds(
trait_item_def_id,
impl_item_def_id,
&mut err,
);
err
}
infer::AscribeUserTypeProvePredicate(span) => {
let instantiated = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sup,
None,
note_and_explain::PrefixKind::LfInstantiatedWith,
note_and_explain::SuffixKind::Empty,
);
let must_outlive = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sub,
None,
note_and_explain::PrefixKind::LfMustOutlive,
note_and_explain::SuffixKind::Empty,
);
self.dcx().create_err(LfBoundNotSatisfied {
span,
notes: instantiated.into_iter().chain(must_outlive).collect(),
})
}
};
if sub.is_error() || sup.is_error() {
err.downgrade_to_delayed_bug();
}
err
}
pub fn suggest_copy_trait_method_bounds(
&self,
trait_item_def_id: DefId,
impl_item_def_id: LocalDefId,
err: &mut Diag<'_>,
) {
// FIXME(compiler-errors): Right now this is only being used for region
// predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
// but right now it's not really very smart when it comes to implicit `Sized`
// predicates and bounds on the trait itself.
let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
else {
return;
};
let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
return;
};
let trait_args = trait_ref
.instantiate_identity()
// Replace the explicit self type with `Self` for better suggestion rendering
.with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
.args;
let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
.rebase_onto(self.tcx, impl_def_id, trait_args);
let Ok(trait_predicates) =
self.tcx
.explicit_predicates_of(trait_item_def_id)
.instantiate_own(self.tcx, trait_item_args)
.map(|(pred, _)| {
if pred.is_suggestable(self.tcx, false) {
Ok(pred.to_string())
} else {
Err(())
}
})
.collect::<Result<Vec<_>, ()>>()
else {
return;
};
let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
return;
};
let suggestion = if trait_predicates.is_empty() {
WhereClauseSuggestions::Remove { span: generics.where_clause_span }
} else {
let space = if generics.where_clause_span.is_empty() { " " } else { "" };
WhereClauseSuggestions::CopyPredicates {
span: generics.where_clause_span,
space,
trait_predicates: trait_predicates.join(", "),
}
};
err.subdiagnostic(suggestion);
}
pub(super) fn report_placeholder_failure(
&self,
generic_param_scope: LocalDefId,
placeholder_origin: SubregionOrigin<'tcx>,
sub: Region<'tcx>,
sup: Region<'tcx>,
) -> Diag<'a> {
// I can't think how to do better than this right now. -nikomatsakis
debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
match placeholder_origin {
infer::Subtype(box ref trace)
if matches!(
&trace.cause.code().peel_derives(),
ObligationCauseCode::WhereClause(..)
| ObligationCauseCode::WhereClauseInExpr(..)
) =>
{
// Hack to get around the borrow checker because trace.cause has an `Rc`.
if let ObligationCauseCode::WhereClause(_, span)
| ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
&trace.cause.code().peel_derives()
&& !span.is_dummy()
{
let span = *span;
self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
.with_span_note(span, "the lifetime requirement is introduced here")
} else {
unreachable!(
"control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
)
}
}
infer::Subtype(box trace) => {
let terr = TypeError::RegionsPlaceholderMismatch;
return self.report_and_explain_type_error(trace, terr);
}
_ => {
return self.report_concrete_failure(
generic_param_scope,
placeholder_origin,
sub,
sup,
);
}
}
}
}

View File

@@ -0,0 +1,941 @@
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, Diag, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::Printer;
use rustc_middle::{
traits::ObligationCause,
ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty},
};
use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
use crate::error_reporting::TypeErrCtxt;
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
pub fn note_and_explain_type_err(
&self,
diag: &mut Diag<'_>,
err: TypeError<'tcx>,
cause: &ObligationCause<'tcx>,
sp: Span,
body_owner_def_id: DefId,
) {
debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
let tcx = self.tcx;
match err {
TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
match (*values.expected.kind(), *values.found.kind()) {
(ty::Closure(..), ty::Closure(..)) => {
diag.note("no two closures, even if identical, have the same type");
diag.help("consider boxing your closure and/or using it as a trait object");
}
(ty::Coroutine(def_id1, ..), ty::Coroutine(def_id2, ..))
if self.tcx.coroutine_is_async(def_id1)
&& self.tcx.coroutine_is_async(def_id2) =>
{
diag.note("no two async blocks, even if identical, have the same type");
diag.help(
"consider pinning your async block and casting it to a trait object",
);
}
(ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
// Issue #63167
diag.note("distinct uses of `impl Trait` result in different opaque types");
}
(ty::Float(_), ty::Infer(ty::IntVar(_)))
if let Ok(
// Issue #53280
snippet,
) = tcx.sess.source_map().span_to_snippet(sp) =>
{
if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
diag.span_suggestion_verbose(
sp.shrink_to_hi(),
"use a float literal",
".0",
MachineApplicable,
);
}
}
(ty::Param(expected), ty::Param(found)) => {
let generics = tcx.generics_of(body_owner_def_id);
let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
if !sp.contains(e_span) {
diag.span_label(e_span, "expected type parameter");
}
let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
if !sp.contains(f_span) {
diag.span_label(f_span, "found type parameter");
}
diag.note(
"a type parameter was expected, but a different one was found; \
you might be missing a type parameter or trait bound",
);
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch10-02-traits.html\
#traits-as-parameters",
);
}
(
ty::Alias(ty::Projection | ty::Inherent, _),
ty::Alias(ty::Projection | ty::Inherent, _),
) => {
diag.note("an associated type was expected, but a different one was found");
}
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
(ty::Param(p), ty::Alias(ty::Projection, proj))
| (ty::Alias(ty::Projection, proj), ty::Param(p))
if !tcx.is_impl_trait_in_trait(proj.def_id) =>
{
let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx);
let p_def_id = param.def_id;
let p_span = tcx.def_span(p_def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
}
let parent = p_def_id.as_local().and_then(|id| {
let local_id = tcx.local_def_id_to_hir_id(id);
let generics = tcx.parent_hir_node(local_id).generics()?;
Some((id, generics))
});
let mut note = true;
if let Some((local_id, generics)) = parent {
// Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics.
let (trait_ref, assoc_args) = proj.trait_ref_and_own_args(tcx);
let item_name = tcx.item_name(proj.def_id);
let item_args = self.format_generic_args(assoc_args);
// Here, we try to see if there's an existing
// trait implementation that matches the one that
// we're suggesting to restrict. If so, find the
// "end", whether it be at the end of the trait
// or the end of the generic arguments.
let mut matching_span = None;
let mut matched_end_of_args = false;
for bound in generics.bounds_for_param(local_id) {
let potential_spans = bound.bounds.iter().find_map(|bound| {
let bound_trait_path = bound.trait_ref()?.path;
let def_id = bound_trait_path.res.opt_def_id()?;
let generic_args = bound_trait_path
.segments
.iter()
.last()
.map(|path| path.args());
(def_id == trait_ref.def_id)
.then_some((bound_trait_path.span, generic_args))
});
if let Some((end_of_trait, end_of_args)) = potential_spans {
let args_span = end_of_args.and_then(|args| args.span());
matched_end_of_args = args_span.is_some();
matching_span = args_span
.or_else(|| Some(end_of_trait))
.map(|span| span.shrink_to_hi());
break;
}
}
if matched_end_of_args {
// Append suggestion to the end of our args
let path = format!(", {item_name}{item_args} = {p}");
note = !suggest_constraining_type_param(
tcx,
generics,
diag,
&proj.self_ty().to_string(),
&path,
None,
matching_span,
);
} else {
// Suggest adding a bound to an existing trait
// or if the trait doesn't exist, add the trait
// and the suggested bounds.
let path = format!("<{item_name}{item_args} = {p}>");
note = !suggest_constraining_type_param(
tcx,
generics,
diag,
&proj.self_ty().to_string(),
&path,
None,
matching_span,
);
}
}
if note {
diag.note("you might be missing a type parameter or trait bound");
}
}
(ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
}
diag.help("type parameters must be constrained to match other types");
if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
diag.help(
"given a type parameter `T` and a method `foo`:
```
trait Trait<T> { fn foo(&self) -> T; }
```
the only ways to implement method `foo` are:
- constrain `T` with an explicit type:
```
impl Trait<String> for X {
fn foo(&self) -> String { String::new() }
}
```
- add a trait bound to `T` and call a method on that trait that returns `Self`:
```
impl<T: std::default::Default> Trait<T> for X {
fn foo(&self) -> T { <T as std::default::Default>::default() }
}
```
- change `foo` to return an argument of type `T`:
```
impl<T> Trait<T> for X {
fn foo(&self, x: T) -> T { x }
}
```",
);
}
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch10-02-traits.html\
#traits-as-parameters",
);
}
(
ty::Param(p),
ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "expected this type parameter");
}
diag.help(format!(
"every closure has a distinct type and so could not always match the \
caller-chosen type of parameter `{p}`"
));
}
(ty::Param(p), _) | (_, ty::Param(p)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
}
}
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
{
self.expected_projection(
diag,
proj_ty,
values,
body_owner_def_id,
cause.code(),
);
}
(_, ty::Alias(ty::Projection | ty::Inherent, proj_ty))
if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
{
let msg = || {
format!(
"consider constraining the associated type `{}` to `{}`",
values.found, values.expected,
)
};
if !(self.suggest_constraining_opaque_associated_type(
diag,
msg,
proj_ty,
values.expected,
) || self.suggest_constraint(
diag,
&msg,
body_owner_def_id,
proj_ty,
values.expected,
)) {
diag.help(msg());
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
);
}
}
(ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias))
if let Some(def_id) = t.principal_def_id()
&& tcx
.explicit_item_super_predicates(alias.def_id)
.skip_binder()
.iter()
.any(|(pred, _span)| match pred.kind().skip_binder() {
ty::ClauseKind::Trait(trait_predicate)
if trait_predicate.polarity
== ty::PredicatePolarity::Positive =>
{
trait_predicate.def_id() == def_id
}
_ => false,
}) =>
{
diag.help(format!(
"you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \
change the expected type as well",
values.found, values.expected,
));
}
(ty::Dynamic(t, _, ty::DynKind::Dyn), _)
if let Some(def_id) = t.principal_def_id() =>
{
let mut impl_def_ids = vec![];
tcx.for_each_relevant_impl(def_id, values.found, |did| {
impl_def_ids.push(did)
});
if let [_] = &impl_def_ids[..] {
let trait_name = tcx.item_name(def_id);
diag.help(format!(
"`{}` implements `{trait_name}` so you could box the found value \
and coerce it to the trait object `Box<dyn {trait_name}>`, you \
will have to change the expected type as well",
values.found,
));
}
}
(_, ty::Dynamic(t, _, ty::DynKind::Dyn))
if let Some(def_id) = t.principal_def_id() =>
{
let mut impl_def_ids = vec![];
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
impl_def_ids.push(did)
});
if let [_] = &impl_def_ids[..] {
let trait_name = tcx.item_name(def_id);
diag.help(format!(
"`{}` implements `{trait_name}` so you could change the expected \
type to `Box<dyn {trait_name}>`",
values.expected,
));
}
}
(ty::Dynamic(t, _, ty::DynKind::DynStar), _)
if let Some(def_id) = t.principal_def_id() =>
{
let mut impl_def_ids = vec![];
tcx.for_each_relevant_impl(def_id, values.found, |did| {
impl_def_ids.push(did)
});
if let [_] = &impl_def_ids[..] {
let trait_name = tcx.item_name(def_id);
diag.help(format!(
"`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \
not enabled; that feature it is currently incomplete",
values.found,
));
}
}
(_, ty::Alias(ty::Opaque, opaque_ty))
| (ty::Alias(ty::Opaque, opaque_ty), _) => {
if opaque_ty.def_id.is_local()
&& matches!(
tcx.def_kind(body_owner_def_id),
DefKind::Fn
| DefKind::Static { .. }
| DefKind::Const
| DefKind::AssocFn
| DefKind::AssocConst
)
&& tcx.is_type_alias_impl_trait(opaque_ty.def_id)
&& !tcx
.opaque_types_defined_by(body_owner_def_id.expect_local())
.contains(&opaque_ty.def_id.expect_local())
{
let sp = tcx
.def_ident_span(body_owner_def_id)
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
diag.span_note(
sp,
"this item must have the opaque type in its signature in order to \
be able to register hidden types",
);
}
// If two if arms can be coerced to a trait object, provide a structured
// suggestion.
let ObligationCauseCode::IfExpression(cause) = cause.code() else {
return;
};
let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else {
return;
};
let Some(then) = blk.expr else {
return;
};
let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else {
return;
};
let Some(else_) = blk.expr else {
return;
};
let expected = match values.found.kind() {
ty::Alias(..) => values.expected,
_ => values.found,
};
let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id);
for (pred, _span) in preds.skip_binder() {
let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
else {
continue;
};
if trait_predicate.polarity != ty::PredicatePolarity::Positive {
continue;
}
let def_id = trait_predicate.def_id();
let mut impl_def_ids = vec![];
tcx.for_each_relevant_impl(def_id, expected, |did| {
impl_def_ids.push(did)
});
if let [_] = &impl_def_ids[..] {
let trait_name = tcx.item_name(def_id);
diag.multipart_suggestion(
format!(
"`{expected}` implements `{trait_name}` so you can box \
both arms and coerce to the trait object \
`Box<dyn {trait_name}>`",
),
vec![
(then.span.shrink_to_lo(), "Box::new(".to_string()),
(
then.span.shrink_to_hi(),
format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
),
(else_.span.shrink_to_lo(), "Box::new(".to_string()),
(else_.span.shrink_to_hi(), ")".to_string()),
],
MachineApplicable,
);
}
}
}
(ty::FnPtr(sig), ty::FnDef(def_id, _))
| (ty::FnDef(def_id, _), ty::FnPtr(sig)) => {
if tcx.fn_sig(def_id).skip_binder().safety() < sig.safety() {
diag.note(
"unsafe functions cannot be coerced into safe function pointers",
);
}
}
(ty::Adt(_, _), ty::Adt(def, args))
if let ObligationCauseCode::IfExpression(cause) = cause.code()
&& let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
&& let Some(then) = blk.expr
&& def.is_box()
&& let boxed_ty = args.type_at(0)
&& let ty::Dynamic(t, _, _) = boxed_ty.kind()
&& let Some(def_id) = t.principal_def_id()
&& let mut impl_def_ids = vec![]
&& let _ =
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
impl_def_ids.push(did)
})
&& let [_] = &impl_def_ids[..] =>
{
// We have divergent if/else arms where the expected value is a type that
// implements the trait of the found boxed trait object.
diag.multipart_suggestion(
format!(
"`{}` implements `{}` so you can box it to coerce to the trait \
object `{}`",
values.expected,
tcx.item_name(def_id),
values.found,
),
vec![
(then.span.shrink_to_lo(), "Box::new(".to_string()),
(then.span.shrink_to_hi(), ")".to_string()),
],
MachineApplicable,
);
}
_ => {}
}
debug!(
"note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
values.expected,
values.expected.kind(),
values.found,
values.found.kind(),
);
}
TypeError::CyclicTy(ty) => {
// Watch out for various cases of cyclic types and try to explain.
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
diag.note(
"closures cannot capture themselves or take themselves as argument;\n\
this error may be the result of a recent compiler bug-fix,\n\
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
for more information",
);
}
}
TypeError::TargetFeatureCast(def_id) => {
let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
diag.note(
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
);
diag.span_labels(target_spans, "`#[target_feature]` added here");
}
_ => {}
}
}
fn suggest_constraint(
&self,
diag: &mut Diag<'_>,
msg: impl Fn() -> String,
body_owner_def_id: DefId,
proj_ty: ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let assoc = tcx.associated_item(proj_ty.def_id);
let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
let Some(item) = tcx.hir().get_if_local(body_owner_def_id) else {
return false;
};
let Some(hir_generics) = item.generics() else {
return false;
};
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
// This will also work for `impl Trait`.
let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
return false;
};
let generics = tcx.generics_of(body_owner_def_id);
let def_id = generics.type_param(param_ty, tcx).def_id;
let Some(def_id) = def_id.as_local() else {
return false;
};
// First look in the `where` clause, as this might be
// `fn foo<T>(x: T) where T: Trait`.
for pred in hir_generics.bounds_for_param(def_id) {
if self.constrain_generic_bound_associated_type_structured_suggestion(
diag,
trait_ref,
pred.bounds,
assoc,
assoc_args,
ty,
&msg,
false,
) {
return true;
}
}
if (param_ty.index as usize) >= generics.parent_count {
// The param comes from the current item, do not look at the parent. (#117209)
return false;
}
// If associated item, look to constrain the params of the trait/impl.
let hir_id = match item {
hir::Node::ImplItem(item) => item.hir_id(),
hir::Node::TraitItem(item) => item.hir_id(),
_ => return false,
};
let parent = tcx.hir().get_parent_item(hir_id).def_id;
self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty)
}
/// An associated type was expected and a different type was found.
///
/// We perform a few different checks to see what we can suggest:
///
/// - In the current item, look for associated functions that return the expected type and
/// suggest calling them. (Not a structured suggestion.)
/// - If any of the item's generic bounds can be constrained, we suggest constraining the
/// associated type to the found type.
/// - If the associated type has a default type and was expected inside of a `trait`, we
/// mention that this is disallowed.
/// - If all other things fail, and the error is not because of a mismatch between the `trait`
/// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
/// fn that returns the type.
fn expected_projection(
&self,
diag: &mut Diag<'_>,
proj_ty: ty::AliasTy<'tcx>,
values: ExpectedFound<Ty<'tcx>>,
body_owner_def_id: DefId,
cause_code: &ObligationCauseCode<'_>,
) {
let tcx = self.tcx;
// Don't suggest constraining a projection to something containing itself
if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) {
return;
}
let msg = || {
format!(
"consider constraining the associated type `{}` to `{}`",
values.expected, values.found
)
};
let body_owner = tcx.hir().get_if_local(body_owner_def_id);
let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
// We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
let callable_scope = matches!(
body_owner,
Some(
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
| hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
)
);
let impl_comparison = matches!(cause_code, ObligationCauseCode::CompareImplItem { .. });
let assoc = tcx.associated_item(proj_ty.def_id);
if impl_comparison {
// We do not want to suggest calling functions when the reason of the
// type error is a comparison of an `impl` with its `trait`.
} else {
let point_at_assoc_fn = if callable_scope
&& self.point_at_methods_that_satisfy_associated_type(
diag,
assoc.container_id(tcx),
current_method_ident,
proj_ty.def_id,
values.expected,
) {
// If we find a suitable associated function that returns the expected type, we
// don't want the more general suggestion later in this method about "consider
// constraining the associated type or calling a method that returns the associated
// type".
true
} else {
false
};
// Possibly suggest constraining the associated type to conform to the
// found type.
if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
|| point_at_assoc_fn
{
return;
}
}
self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
return;
}
if !impl_comparison {
// Generic suggestion when we can't be more specific.
if callable_scope {
diag.help(format!(
"{} or calling a method that returns `{}`",
msg(),
values.expected
));
} else {
diag.help(msg());
}
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
);
}
if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
diag.help(
"given an associated type `T` and a method `foo`:
```
trait Trait {
type T;
fn foo(&self) -> Self::T;
}
```
the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
```
impl Trait for X {
type T = String;
fn foo(&self) -> Self::T { String::new() }
}
```",
);
}
}
/// When the expected `impl Trait` is not defined in the current item, it will come from
/// a return type. This can occur when dealing with `TryStream` (#71035).
fn suggest_constraining_opaque_associated_type(
&self,
diag: &mut Diag<'_>,
msg: impl Fn() -> String,
proj_ty: ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let assoc = tcx.associated_item(proj_ty.def_id);
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
let opaque_local_def_id = def_id.as_local();
let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
tcx.hir().expect_item(opaque_local_def_id).expect_opaque_ty()
} else {
return false;
};
let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
self.constrain_generic_bound_associated_type_structured_suggestion(
diag,
trait_ref,
opaque_hir_ty.bounds,
assoc,
assoc_args,
ty,
msg,
true,
)
} else {
false
}
}
fn point_at_methods_that_satisfy_associated_type(
&self,
diag: &mut Diag<'_>,
assoc_container_id: DefId,
current_method_ident: Option<Symbol>,
proj_ty_item_def_id: DefId,
expected: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let items = tcx.associated_items(assoc_container_id);
// Find all the methods in the trait that could be called to construct the
// expected associated type.
// FIXME: consider suggesting the use of associated `const`s.
let methods: Vec<(Span, String)> = items
.in_definition_order()
.filter(|item| {
ty::AssocKind::Fn == item.kind
&& Some(item.name) != current_method_ident
&& !tcx.is_doc_hidden(item.def_id)
})
.filter_map(|item| {
let method = tcx.fn_sig(item.def_id).instantiate_identity();
match *method.output().skip_binder().kind() {
ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
if item_def_id == proj_ty_item_def_id =>
{
Some((
tcx.def_span(item.def_id),
format!("consider calling `{}`", tcx.def_path_str(item.def_id)),
))
}
_ => None,
}
})
.collect();
if !methods.is_empty() {
// Use a single `help:` to show all the methods in the trait that can
// be used to construct the expected associated type.
let mut span: MultiSpan =
methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
let msg = format!(
"{some} method{s} {are} available that return{r} `{ty}`",
some = if methods.len() == 1 { "a" } else { "some" },
s = pluralize!(methods.len()),
are = pluralize!("is", methods.len()),
r = if methods.len() == 1 { "s" } else { "" },
ty = expected
);
for (sp, label) in methods.into_iter() {
span.push_span_label(sp, label);
}
diag.span_help(span, msg);
return true;
}
false
}
fn point_at_associated_type(
&self,
diag: &mut Diag<'_>,
body_owner_def_id: DefId,
found: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let Some(def_id) = body_owner_def_id.as_local() else {
return false;
};
// When `body_owner` is an `impl` or `trait` item, look in its associated types for
// `expected` and point at it.
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let parent_id = tcx.hir().get_parent_item(hir_id);
let item = tcx.hir_node_by_def_id(parent_id.def_id);
debug!("expected_projection parent item {:?}", item);
let param_env = tcx.param_env(body_owner_def_id);
match item {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. }) => {
// FIXME: account for `#![feature(specialization)]`
for item in &items[..] {
match item.kind {
hir::AssocItemKind::Type => {
// FIXME: account for returning some type in a trait fn impl that has
// an assoc type as a return type (#72076).
if let hir::Defaultness::Default { has_value: true } =
tcx.defaultness(item.id.owner_id)
{
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
if self.infcx.can_eq_shallow(param_env, assoc_ty, found) {
diag.span_label(
item.span,
"associated type defaults can't be assumed inside the \
trait defining them",
);
return true;
}
}
}
_ => {}
}
}
}
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
..
}) => {
for item in &items[..] {
if let hir::AssocItemKind::Type = item.kind {
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
if let hir::Defaultness::Default { has_value: true } =
tcx.defaultness(item.id.owner_id)
&& self.infcx.can_eq_shallow(param_env, assoc_ty, found)
{
diag.span_label(
item.span,
"associated type is `default` and may be overridden",
);
return true;
}
}
}
}
_ => {}
}
false
}
/// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
/// requirement, provide a structured suggestion to constrain it to a given type `ty`.
///
/// `is_bound_surely_present` indicates whether we know the bound we're looking for is
/// inside `bounds`. If that's the case then we can consider `bounds` containing only one
/// trait bound as the one we're looking for. This can help in cases where the associated
/// type is defined on a supertrait of the one present in the bounds.
fn constrain_generic_bound_associated_type_structured_suggestion(
&self,
diag: &mut Diag<'_>,
trait_ref: ty::TraitRef<'tcx>,
bounds: hir::GenericBounds<'_>,
assoc: ty::AssocItem,
assoc_args: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: impl Fn() -> String,
is_bound_surely_present: bool,
) -> bool {
// FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
let trait_bounds = bounds.iter().filter_map(|bound| match bound {
hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
_ => None,
});
let matching_trait_bounds = trait_bounds
.clone()
.filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
.collect::<Vec<_>>();
let span = match &matching_trait_bounds[..] {
&[ptr] => ptr.span,
&[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
&[ptr] => ptr.span,
_ => return false,
},
_ => return false,
};
self.constrain_associated_type_structured_suggestion(diag, span, assoc, assoc_args, ty, msg)
}
/// Given a span corresponding to a bound, provide a structured suggestion to set an
/// associated type to a given type `ty`.
fn constrain_associated_type_structured_suggestion(
&self,
diag: &mut Diag<'_>,
span: Span,
assoc: ty::AssocItem,
assoc_args: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: impl Fn() -> String,
) -> bool {
let tcx = self.tcx;
if let Ok(has_params) =
tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
{
let (span, sugg) = if has_params {
let pos = span.hi() - BytePos(1);
let span = Span::new(pos, pos, span.ctxt(), span.parent());
(span, format!(", {} = {}", assoc.ident(tcx), ty))
} else {
let item_args = self.format_generic_args(assoc_args);
(span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
};
diag.span_suggestion_verbose(span, msg(), sugg, MaybeIncorrect);
return true;
}
false
}
pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| {
cx.path_generic_args(|_| Ok(()), args)
})
.expect("could not write to `String`.")
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::undo_log::NoUndo;
use rustc_data_structures::unify as ut;
use rustc_middle::ty;
use crate::infer::InferCtxt;
#[derive(Debug, Copy, Clone, PartialEq)]
struct SubId(u32);
impl ut::UnifyKey for SubId {
type Value = ();
#[inline]
fn index(&self) -> u32 {
self.0
}
#[inline]
fn from_index(i: u32) -> SubId {
SubId(i)
}
fn tag() -> &'static str {
"SubId"
}
}
/// When reporting ambiguity errors, we sometimes want to
/// treat all inference vars which are subtypes of each
/// others as if they are equal. For this case we compute
/// the transitive closure of our subtype obligations here.
///
/// E.g. when encountering ambiguity errors, we want to suggest
/// specifying some method argument or to add a type annotation
/// to a local variable. Because subtyping cannot change the
/// shape of a type, it's fine if the cause of the ambiguity error
/// is only related to the suggested variable via subtyping.
///
/// Even for something like `let x = returns_arg(); x.method();` the
/// type of `x` is only a supertype of the argument of `returns_arg`. We
/// still want to suggest specifying the type of the argument.
#[derive(Default)]
pub struct SubRelations {
map: FxHashMap<ty::TyVid, SubId>,
table: ut::UnificationTableStorage<SubId>,
}
impl SubRelations {
fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId {
let root_vid = infcx.root_var(vid);
*self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(()))
}
pub fn add_constraints<'tcx>(
&mut self,
infcx: &InferCtxt<'tcx>,
obls: impl IntoIterator<Item = ty::Predicate<'tcx>>,
) {
for p in obls {
let (a, b) = match p.kind().skip_binder() {
ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
(a, b)
}
ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
_ => continue,
};
match (a.kind(), b.kind()) {
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
let a = self.get_id(infcx, a_vid);
let b = self.get_id(infcx, b_vid);
self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap();
}
_ => continue,
}
}
}
pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool {
let a = self.get_id(infcx, a);
let b = self.get_id(infcx, b);
self.table.with_log(&mut NoUndo).unioned(a, b)
}
}

View File

@@ -0,0 +1,898 @@
use crate::error_reporting::infer::hir::Path;
use core::ops::ControlFlow;
use hir::def::CtorKind;
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
use hir::{LetStmt, QPath};
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::MatchSource;
use rustc_hir::Node;
use rustc_middle::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
StatementAsExpression,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
use rustc_span::{sym, Span};
use crate::error_reporting::TypeErrCtxt;
use crate::errors::{
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
};
#[derive(Clone, Copy)]
pub enum SuggestAsRefKind {
Option,
Result,
}
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
pub(super) fn suggest_remove_semi_or_return_binding(
&self,
first_id: Option<hir::HirId>,
first_ty: Ty<'tcx>,
first_span: Span,
second_id: Option<hir::HirId>,
second_ty: Ty<'tcx>,
second_span: Span,
) -> Option<SuggestRemoveSemiOrReturnBinding> {
let remove_semicolon = [
(first_id, self.resolve_vars_if_possible(second_ty)),
(second_id, self.resolve_vars_if_possible(first_ty)),
]
.into_iter()
.find_map(|(id, ty)| {
let hir::Node::Block(blk) = self.tcx.hir_node(id?) else { return None };
self.could_remove_semicolon(blk, ty)
});
match remove_semicolon {
Some((sp, StatementAsExpression::NeedsBoxing)) => {
Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
first_lo: first_span.shrink_to_lo(),
first_hi: first_span.shrink_to_hi(),
second_lo: second_span.shrink_to_lo(),
second_hi: second_span.shrink_to_hi(),
sp,
})
}
Some((sp, StatementAsExpression::CorrectType)) => {
Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
}
None => {
let mut ret = None;
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
if let Some(id) = id
&& let hir::Node::Block(blk) = self.tcx.hir_node(id)
&& let Some(diag) = self.consider_returning_binding_diag(blk, ty)
{
ret = Some(diag);
break;
}
}
ret
}
}
}
pub(super) fn suggest_tuple_pattern(
&self,
cause: &ObligationCause<'tcx>,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diag<'_>,
) {
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
// some modifications due to that being in typeck and this being in infer.
if let ObligationCauseCode::Pattern { .. } = cause.code() {
if let ty::Adt(expected_adt, args) = exp_found.expected.kind() {
let compatible_variants: Vec<_> = expected_adt
.variants()
.iter()
.filter(|variant| {
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
})
.filter_map(|variant| {
let sole_field = &variant.single_field();
let sole_field_ty = sole_field.ty(self.tcx, args);
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
let variant_path =
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
// FIXME #56861: DRYer prelude filtering
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
if let Some((_, path)) = path.split_once("::") {
return Some(path.to_string());
}
}
Some(variant_path)
} else {
None
}
})
.collect();
match &compatible_variants[..] {
[] => {}
[variant] => {
let sugg = SuggestTuplePatternOne {
variant: variant.to_owned(),
span_low: cause.span.shrink_to_lo(),
span_high: cause.span.shrink_to_hi(),
};
diag.subdiagnostic(sugg);
}
_ => {
// More than one matching variant.
let sugg = SuggestTuplePatternMany {
path: self.tcx.def_path_str(expected_adt.did()),
cause_span: cause.span,
compatible_variants,
};
diag.subdiagnostic(sugg);
}
}
}
}
}
/// A possible error is to forget to add `.await` when using futures:
///
/// ```compile_fail,E0308
/// async fn make_u32() -> u32 {
/// 22
/// }
///
/// fn take_u32(x: u32) {}
///
/// async fn foo() {
/// let x = make_u32();
/// take_u32(x);
/// }
/// ```
///
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
/// `.await` to the tail of the expression.
pub(super) fn suggest_await_on_expect_found(
&self,
cause: &ObligationCause<'tcx>,
exp_span: Span,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diag<'_>,
) {
debug!(
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
exp_span, exp_found.expected, exp_found.found,
);
if let ObligationCauseCode::CompareImplItem { .. } = cause.code() {
return;
}
let subdiag = match (
self.get_impl_future_output_ty(exp_found.expected),
self.get_impl_future_output_ty(exp_found.found),
) {
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
.code()
{
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
let then_span = self.find_block_span_from_hir_id(*then_id);
Some(ConsiderAddingAwait::BothFuturesSugg {
first: then_span.shrink_to_hi(),
second: exp_span.shrink_to_hi(),
})
}
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
prior_non_diverging_arms,
..
}) => {
if let [.., arm_span] = &prior_non_diverging_arms[..] {
Some(ConsiderAddingAwait::BothFuturesSugg {
first: arm_span.shrink_to_hi(),
second: exp_span.shrink_to_hi(),
})
} else {
Some(ConsiderAddingAwait::BothFuturesHelp)
}
}
_ => Some(ConsiderAddingAwait::BothFuturesHelp),
},
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
// FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
span: exp_span.shrink_to_hi(),
});
Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
}
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
{
ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
origin_expr.then_some(ConsiderAddingAwait::FutureSugg {
span: then_span.shrink_to_hi(),
})
}
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
let then_span = self.find_block_span_from_hir_id(*then_id);
Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
}
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
ref prior_non_diverging_arms,
..
}) => Some({
ConsiderAddingAwait::FutureSuggMultiple {
spans: prior_non_diverging_arms
.iter()
.map(|arm| arm.shrink_to_hi())
.collect(),
}
}),
_ => None,
},
_ => None,
};
if let Some(subdiag) = subdiag {
diag.subdiagnostic(subdiag);
}
}
pub(super) fn suggest_accessing_field_where_appropriate(
&self,
cause: &ObligationCause<'tcx>,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diag<'_>,
) {
debug!(
"suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
cause, exp_found
);
if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() {
if expected_def.is_enum() {
return;
}
if let Some((name, ty)) = expected_def
.non_enum_variant()
.fields
.iter()
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
.map(|field| (field.name, field.ty(self.tcx, expected_args)))
.find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
{
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let suggestion = if expected_def.is_struct() {
SuggestAccessingField::Safe { span, snippet, name, ty }
} else if expected_def.is_union() {
SuggestAccessingField::Unsafe { span, snippet, name, ty }
} else {
return;
};
diag.subdiagnostic(suggestion);
}
}
}
}
}
pub(super) fn suggest_turning_stmt_into_expr(
&self,
cause: &ObligationCause<'tcx>,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diag<'_>,
) {
let ty::error::ExpectedFound { expected, found } = exp_found;
if !found.peel_refs().is_unit() {
return;
}
let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
else {
return;
};
let node = self.tcx.hir_node(*hir_id);
let mut blocks = vec![];
if let hir::Node::Block(block) = node
&& let Some(expr) = block.expr
&& let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
&& let Res::Local(local) = res
&& let Node::LetStmt(LetStmt { init: Some(init), .. }) =
self.tcx.parent_hir_node(*local)
{
fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
match expr.kind {
// `blk1` and `blk2` must be have the same types, it will be reported before reaching here
hir::ExprKind::If(_, blk1, Some(blk2)) => {
collect_blocks(blk1, blocks);
collect_blocks(blk2, blocks);
}
hir::ExprKind::Match(_, arms, _) => {
// all arms must have same types
for arm in arms.iter() {
collect_blocks(arm.body, blocks);
}
}
hir::ExprKind::Block(blk, _) => {
blocks.push(blk);
}
_ => {}
}
}
collect_blocks(init, &mut blocks);
}
let expected_inner: Ty<'_> = expected.peel_refs();
for block in blocks.iter() {
self.consider_removing_semicolon(block, expected_inner, diag);
}
}
/// A common error is to add an extra semicolon:
///
/// ```compile_fail,E0308
/// fn foo() -> usize {
/// 22;
/// }
/// ```
///
/// This routine checks if the final statement in a block is an
/// expression with an explicit semicolon whose type is compatible
/// with `expected_ty`. If so, it suggests removing the semicolon.
pub fn consider_removing_semicolon(
&self,
blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>,
diag: &mut Diag<'_>,
) -> bool {
if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
if let StatementAsExpression::NeedsBoxing = boxed {
diag.span_suggestion_verbose(
span_semi,
"consider removing this semicolon and boxing the expression",
"",
Applicability::HasPlaceholders,
);
} else {
diag.span_suggestion_short(
span_semi,
"remove this semicolon to return this value",
"",
Applicability::MachineApplicable,
);
}
true
} else {
false
}
}
pub(super) fn suggest_function_pointers(
&self,
cause: &ObligationCause<'tcx>,
span: Span,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diag<'_>,
) {
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
let ty::error::ExpectedFound { expected, found } = exp_found;
let expected_inner = expected.peel_refs();
let found_inner = found.peel_refs();
if !expected_inner.is_fn() || !found_inner.is_fn() {
return;
}
match (&expected_inner.kind(), &found_inner.kind()) {
(ty::FnPtr(sig), ty::FnDef(did, args)) => {
let expected_sig = &(self.normalize_fn_sig)(*sig);
let found_sig =
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
let fn_name = self.tcx.def_path_str_with_args(*did, args);
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|| !sig.is_suggestable(self.tcx, true)
|| self.tcx.intrinsic(*did).is_some()
{
return;
}
let sugg = match (expected.is_ref(), found.is_ref()) {
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
(true, true) => {
diag.subdiagnostic(FnItemsAreDistinct);
FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
}
(false, false) => {
diag.subdiagnostic(FnItemsAreDistinct);
FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
}
};
diag.subdiagnostic(sugg);
}
(ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
let expected_sig =
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
let found_sig =
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
diag.subdiagnostic(FnUniqTypes);
}
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|| !found_sig.is_suggestable(self.tcx, true)
|| !expected_sig.is_suggestable(self.tcx, true)
|| self.tcx.intrinsic(*did1).is_some()
|| self.tcx.intrinsic(*did2).is_some()
{
return;
}
let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
let sug = if found.is_ref() {
FunctionPointerSuggestion::CastBothRef {
span,
fn_name,
found_sig: *found_sig,
expected_sig: *expected_sig,
}
} else {
FunctionPointerSuggestion::CastBoth {
span,
fn_name,
found_sig: *found_sig,
expected_sig: *expected_sig,
}
};
diag.subdiagnostic(sug);
}
(ty::FnDef(did, args), ty::FnPtr(sig)) => {
let expected_sig =
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
let found_sig = &(self.normalize_fn_sig)(*sig);
if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
return;
}
let fn_name = self.tcx.def_path_str_with_args(*did, args);
let casting = if expected.is_ref() {
format!("&({fn_name} as {found_sig})")
} else {
format!("{fn_name} as {found_sig}")
};
diag.subdiagnostic(FnConsiderCasting { casting });
}
_ => {
return;
}
};
}
pub fn should_suggest_as_ref_kind(
&self,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) -> Option<SuggestAsRefKind> {
if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
(expected.kind(), found.kind())
{
if let ty::Adt(found_def, found_args) = *found_ty.kind() {
if exp_def == &found_def {
let have_as_ref = &[
(sym::Option, SuggestAsRefKind::Option),
(sym::Result, SuggestAsRefKind::Result),
];
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
}) {
let mut show_suggestion = true;
for (exp_ty, found_ty) in
std::iter::zip(exp_args.types(), found_args.types())
{
match *exp_ty.kind() {
ty::Ref(_, exp_ty, _) => {
match (exp_ty.kind(), found_ty.kind()) {
(_, ty::Param(_))
| (_, ty::Infer(_))
| (ty::Param(_), _)
| (ty::Infer(_), _) => {}
_ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
_ => show_suggestion = false,
};
}
ty::Param(_) | ty::Infer(_) => {}
_ => show_suggestion = false,
}
}
if show_suggestion {
return Some(*msg);
}
}
}
}
}
None
}
// FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
match self.should_suggest_as_ref_kind(expected, found) {
Some(SuggestAsRefKind::Option) => Some(
"you can convert from `&Option<T>` to `Option<&T>` using \
`.as_ref()`",
),
Some(SuggestAsRefKind::Result) => Some(
"you can convert from `&Result<T, E>` to \
`Result<&T, &E>` using `.as_ref()`",
),
None => None,
}
}
/// Try to find code with pattern `if Some(..) = expr`
/// use a `visitor` to mark the `if` which its span contains given error span,
/// and then try to find a assignment in the `cond` part, which span is equal with error span
pub(super) fn suggest_let_for_letchains(
&self,
cause: &ObligationCause<'_>,
span: Span,
) -> Option<TypeErrorAdditionalDiags> {
/// Find the if expression with given span
struct IfVisitor {
pub found_if: bool,
pub err_span: Span,
}
impl<'v> Visitor<'v> for IfVisitor {
type Result = ControlFlow<()>;
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
match ex.kind {
hir::ExprKind::If(cond, _, _) => {
self.found_if = true;
walk_expr(self, cond)?;
self.found_if = false;
ControlFlow::Continue(())
}
_ => walk_expr(self, ex),
}
}
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
if let hir::StmtKind::Let(LetStmt {
span,
pat: hir::Pat { .. },
ty: None,
init: Some(_),
..
}) = &ex.kind
&& self.found_if
&& span.eq(&self.err_span)
{
ControlFlow::Break(())
} else {
walk_stmt(self, ex)
}
}
}
self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body| {
IfVisitor { err_span: span, found_if: false }
.visit_body(&body)
.is_break()
.then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
})
}
/// For "one type is more general than the other" errors on closures, suggest changing the lifetime
/// of the parameters to accept all lifetimes.
pub(super) fn suggest_for_all_lifetime_closure(
&self,
span: Span,
hir: hir::Node<'_>,
exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
diag: &mut Diag<'_>,
) {
// 0. Extract fn_decl from hir
let hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
..
}) = hir
else {
return;
};
let hir::Body { params, .. } = self.tcx.hir().body(*body);
// 1. Get the args of the closure.
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
let Some(expected) = exp_found.expected.args.get(1) else {
return;
};
let Some(found) = exp_found.found.args.get(1) else {
return;
};
let expected = expected.unpack();
let found = found.unpack();
// 3. Extract the tuple type from Fn trait and suggest the change.
if let GenericArgKind::Type(expected) = expected
&& let GenericArgKind::Type(found) = found
&& let ty::Tuple(expected) = expected.kind()
&& let ty::Tuple(found) = found.kind()
&& expected.len() == found.len()
{
let mut suggestion = "|".to_string();
let mut is_first = true;
let mut has_suggestion = false;
for (((expected, found), param_hir), arg_hir) in
expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
{
if is_first {
is_first = false;
} else {
suggestion += ", ";
}
if let ty::Ref(expected_region, _, _) = expected.kind()
&& let ty::Ref(found_region, _, _) = found.kind()
&& expected_region.is_bound()
&& !found_region.is_bound()
&& let hir::TyKind::Infer = arg_hir.kind
{
// If the expected region is late bound, the found region is not, and users are asking compiler
// to infer the type, we can suggest adding `: &_`.
if param_hir.pat.span == param_hir.ty_span {
// for `|x|`, `|_|`, `|x: impl Foo|`
let Ok(pat) =
self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
else {
return;
};
suggestion += &format!("{pat}: &_");
} else {
// for `|x: ty|`, `|_: ty|`
let Ok(pat) =
self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
else {
return;
};
let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
else {
return;
};
suggestion += &format!("{pat}: &{ty}");
}
has_suggestion = true;
} else {
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
return;
};
// Otherwise, keep it as-is.
suggestion += &arg;
}
}
suggestion += "|";
if has_suggestion {
diag.span_suggestion_verbose(
span,
"consider specifying the type of the closure parameters",
suggestion,
Applicability::MaybeIncorrect,
);
}
}
}
}
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
/// Be helpful when the user wrote `{... expr; }` and taking the `;` off
/// is enough to fix the error.
pub fn could_remove_semicolon(
&self,
blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>,
) -> Option<(Span, StatementAsExpression)> {
let blk = blk.innermost_block();
// Do not suggest if we have a tail expr.
if blk.expr.is_some() {
return None;
}
let last_stmt = blk.stmts.last()?;
let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
return None;
};
let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
_ if last_expr_ty.references_error() => return None,
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
StatementAsExpression::CorrectType
}
(
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
(
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, args: last_bounds, .. }),
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, args: exp_bounds, .. }),
) => {
debug!(
"both opaque, likely future {:?} {:?} {:?} {:?}",
last_def_id, last_bounds, exp_def_id, exp_bounds
);
let last_local_id = last_def_id.as_local()?;
let exp_local_id = exp_def_id.as_local()?;
match (
&self.tcx.hir().expect_item(last_local_id).kind,
&self.tcx.hir().expect_item(exp_local_id).kind,
) {
(
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
left, right,
) {
(hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr))
if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
&& ml == mr =>
{
true
}
_ => false,
}) =>
{
StatementAsExpression::NeedsBoxing
}
_ => StatementAsExpression::CorrectType,
}
}
_ => return None,
};
let span = if last_stmt.span.from_expansion() {
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
} else {
self.tcx
.sess
.source_map()
.span_extend_while_whitespace(last_expr.span)
.shrink_to_hi()
.with_hi(last_stmt.span.hi())
};
Some((span, needs_box))
}
/// Suggest returning a local binding with a compatible type if the block
/// has no return expression.
pub fn consider_returning_binding_diag(
&self,
blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>,
) -> Option<SuggestRemoveSemiOrReturnBinding> {
let blk = blk.innermost_block();
// Do not suggest if we have a tail expr.
if blk.expr.is_some() {
return None;
}
let mut shadowed = FxIndexSet::default();
let mut candidate_idents = vec![];
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
&& let Some(pat_ty) = self
.typeck_results
.as_ref()
.and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
{
let pat_ty = self.resolve_vars_if_possible(pat_ty);
if self.same_type_modulo_infer(pat_ty, expected_ty)
&& !(pat_ty, expected_ty).references_error()
&& shadowed.insert(ident.name)
{
candidate_idents.push((*ident, pat_ty));
}
}
true
};
let hir = self.tcx.hir();
for stmt in blk.stmts.iter().rev() {
let hir::StmtKind::Let(local) = &stmt.kind else {
continue;
};
local.pat.walk(&mut find_compatible_candidates);
}
match self.tcx.parent_hir_node(blk.hir_id) {
hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
hir::Node::Arm(hir::Arm { pat, .. }) => {
pat.walk(&mut find_compatible_candidates);
}
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
| hir::Node::ImplItem(hir::ImplItem {
kind: hir::ImplItemKind::Fn(_, body), ..
})
| hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
..
})
| hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
..
}) => {
for param in hir.body(*body).params {
param.pat.walk(&mut find_compatible_candidates);
}
}
hir::Node::Expr(hir::Expr {
kind:
hir::ExprKind::If(
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
then_block,
_,
),
..
}) if then_block.hir_id == *hir_id => {
let_.pat.walk(&mut find_compatible_candidates);
}
_ => {}
},
_ => {}
}
match &candidate_idents[..] {
[(ident, _ty)] => {
let sm = self.tcx.sess.source_map();
let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
let stmt_span = sm.stmt_span(stmt.span, blk.span);
let sugg = if sm.is_multiline(blk.span)
&& let Some(spacing) = sm.indentation_before(stmt_span)
{
format!("\n{spacing}{ident}")
} else {
format!(" {ident}")
};
(stmt_span.shrink_to_hi(), sugg)
} else {
let sugg = if sm.is_multiline(blk.span)
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
{
format!("\n{spacing} {ident}\n{spacing}")
} else {
format!(" {ident} ")
};
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
(sm.span_extend_while_whitespace(left_span), sugg)
};
Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
}
values if (1..3).contains(&values.len()) => {
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
}
_ => None,
}
}
pub fn consider_returning_binding(
&self,
blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>,
err: &mut Diag<'_>,
) -> bool {
let diag = self.consider_returning_binding_diag(blk, expected_ty);
match diag {
Some(diag) => {
err.subdiagnostic(diag);
true
}
None => false,
}
}
}

View File

@@ -1 +1,73 @@
use std::ops::Deref;
use rustc_errors::DiagCtxtHandle;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::PredicateObligation;
use rustc_macros::extension;
use rustc_middle::bug;
use rustc_middle::ty::{self, Ty};
use crate::error_reporting::infer::sub_relations;
pub mod infer;
pub mod traits;
/// A helper for building type related errors. The `typeck_results`
/// field is only populated during an in-progress typeck.
/// Get an instance by calling `InferCtxt::err_ctxt` or `FnCtxt::err_ctxt`.
///
/// You must only create this if you intend to actually emit an error (or
/// perhaps a warning, though preferably not.) It provides a lot of utility
/// methods which should not be used during the happy path.
pub struct TypeErrCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>,
pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>,
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
pub fallback_has_occurred: bool,
pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>,
pub autoderef_steps:
Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>,
}
#[extension(pub trait InferCtxtErrorExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Creates a `TypeErrCtxt` for emitting various inference errors.
/// During typeck, use `FnCtxt::err_ctxt` instead.
fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
TypeErrCtxt {
infcx: self,
sub_relations: Default::default(),
typeck_results: None,
fallback_has_occurred: false,
normalize_fn_sig: Box::new(|fn_sig| fn_sig),
autoderef_steps: Box::new(|ty| {
debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck");
vec![(ty, vec![])]
}),
}
}
}
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
pub fn dcx(&self) -> DiagCtxtHandle<'a> {
self.infcx.dcx()
}
/// This is just to avoid a potential footgun of accidentally
/// dropping `typeck_results` by calling `InferCtxt::err_ctxt`
#[deprecated(note = "you already have a `TypeErrCtxt`")]
#[allow(unused)]
pub fn err_ctxt(&self) -> ! {
bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call");
}
}
impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> {
type Target = InferCtxt<'tcx>;
fn deref(&self) -> &InferCtxt<'tcx> {
self.infcx
}
}

View File

@@ -8,21 +8,17 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor as _;
use rustc_hir::LangItem;
use rustc_infer::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
use rustc_infer::error_reporting::infer::TypeErrCtxt;
use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
use rustc_infer::traits::util::elaborate;
use rustc_infer::traits::{
Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
};
use rustc_macros::extension;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
use crate::error_reporting::traits::{
to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _,
};
use crate::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
use crate::error_reporting::traits::{to_pretty_impl_header, FindExprBySpan};
use crate::error_reporting::TypeErrCtxt;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::ObligationCtxt;
@@ -153,10 +149,12 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>(
ambiguities
}
#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
pub(super) fn maybe_report_ambiguity(
&self,
obligation: &PredicateObligation<'tcx>,
) -> ErrorGuaranteed {
// Unable to successfully determine, probably means
// insufficient type information, but could mean
// ambiguous impls. The latter *ought* to be a

View File

@@ -1,6 +1,9 @@
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
use super::suggestions::get_explanation_based_on_obligation;
use crate::error_reporting::infer::TyCategory;
use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
use crate::error_reporting::traits::report_object_safety_error;
use crate::error_reporting::TypeErrCtxt;
use crate::errors::{
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
};
@@ -24,10 +27,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::Node;
use rustc_hir::{self as hir, LangItem};
use rustc_infer::error_reporting::infer::TyCategory;
use rustc_infer::error_reporting::infer::TypeErrCtxt;
use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_macros::extension;
use rustc_middle::traits::select::OverflowError;
use rustc_middle::traits::SignatureMismatchData;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -49,14 +49,11 @@ use super::{
ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst,
};
pub use rustc_infer::traits::error_reporting::*;
#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// The `root_obligation` parameter should be the `root_obligation` field
/// from a `FulfillmentError`. If no `FulfillmentError` is available,
/// then it should be the same as `obligation`.
fn report_selection_error(
pub fn report_selection_error(
&self,
mut obligation: PredicateObligation<'tcx>,
root_obligation: &PredicateObligation<'tcx>,
@@ -682,9 +679,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool {
pub(super) fn apply_do_not_recommend(
&self,
obligation: &mut PredicateObligation<'tcx>,
) -> bool {
let mut base_cause = obligation.cause.code().clone();
let mut applied_do_not_recommend = false;
loop {
@@ -1142,7 +1141,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
#[extension(pub(super) trait InferCtxtPrivExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn can_match_trait(
&self,
@@ -1182,7 +1180,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// returns if `cond` not occurring implies that `error` does not occur - i.e., that
// `error` occurring implies that `cond` occurs.
#[instrument(level = "debug", skip(self), ret)]
fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
pub(super) fn error_implies(
&self,
cond: ty::Predicate<'tcx>,
error: ty::Predicate<'tcx>,
) -> bool {
if cond == error {
return true;
}
@@ -1205,7 +1207,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
#[instrument(level = "debug", skip_all)]
fn report_projection_error(
pub(super) fn report_projection_error(
&self,
obligation: &PredicateObligation<'tcx>,
error: &MismatchedProjectionTypes<'tcx>,
@@ -1455,7 +1457,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn fuzzy_match_tys(
pub fn fuzzy_match_tys(
&self,
mut a: Ty<'tcx>,
mut b: Ty<'tcx>,
@@ -1535,7 +1537,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
pub(super) fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
match kind {
hir::ClosureKind::Closure => "a closure",
hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) => "a coroutine",
@@ -1585,7 +1587,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn find_similar_impl_candidates(
pub(super) fn find_similar_impl_candidates(
&self,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> Vec<ImplCandidate<'tcx>> {
@@ -1615,7 +1617,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
candidates
}
fn report_similar_impl_candidates(
pub(super) fn report_similar_impl_candidates(
&self,
impl_candidates: &[ImplCandidate<'tcx>],
trait_ref: ty::PolyTraitRef<'tcx>,
@@ -1989,7 +1991,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// `trait_ref`.
///
/// For this to work, `new_self_ty` must have no escaping bound variables.
fn mk_trait_obligation_with_new_self_ty(
pub(super) fn mk_trait_obligation_with_new_self_ty(
&self,
param_env: ty::ParamEnv<'tcx>,
trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
@@ -2041,7 +2043,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
}
fn note_obligation_cause(&self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>) {
pub fn note_obligation_cause(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
) {
// First, attempt to add note to this error with an async-await-specific
// message, and fall back to regular note otherwise.
if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
@@ -2067,7 +2073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn is_recursive_obligation(
pub(super) fn is_recursive_obligation(
&self,
obligated_types: &mut Vec<Ty<'tcx>>,
cause_code: &ObligationCauseCode<'tcx>,

View File

@@ -5,28 +5,24 @@ pub mod on_unimplemented;
mod overflow;
pub mod suggestions;
use std::iter;
use std::{fmt, iter};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_hir::def_id::DefId;
use rustc_errors::{struct_span_code_err, Applicability, Diag, MultiSpan, E0038, E0276};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, LangItem};
use rustc_infer::error_reporting::infer::TypeErrCtxt;
use rustc_infer::traits::{
Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError,
ObjectSafetyViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
SelectionError,
};
use rustc_macros::extension;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
use ambiguity::TypeErrCtxtAmbiguityExt as _;
use fulfillment_errors::TypeErrCtxtExt as _;
use suggestions::TypeErrCtxtExt as _;
use crate::error_reporting::TypeErrCtxt;
use crate::traits::{FulfillmentError, FulfillmentErrorCode};
pub use self::fulfillment_errors::*;
pub use self::infer_ctxt_ext::*;
pub use self::overflow::*;
@@ -137,9 +133,8 @@ pub enum DefIdOrName {
Name(&'static str),
}
#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn report_fulfillment_errors(
pub fn report_fulfillment_errors(
&self,
mut errors: Vec<FulfillmentError<'tcx>>,
) -> ErrorGuaranteed {
@@ -383,3 +378,194 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
w.push(';');
Some(w)
}
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
pub fn report_extra_impl_obligation(
&self,
error_span: Span,
impl_item_def_id: LocalDefId,
trait_item_def_id: DefId,
requirement: &dyn fmt::Display,
) -> Diag<'a> {
let mut err = struct_span_code_err!(
self.dcx(),
error_span,
E0276,
"impl has stricter requirements than trait"
);
if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
err.span_label(span, format!("definition of `{item_name}` from trait"));
}
}
err.span_label(error_span, format!("impl has extra requirement {requirement}"));
err
}
}
pub fn report_object_safety_error<'tcx>(
tcx: TyCtxt<'tcx>,
span: Span,
hir_id: Option<hir::HirId>,
trait_def_id: DefId,
violations: &[ObjectSafetyViolation],
) -> Diag<'tcx> {
let trait_str = tcx.def_path_str(trait_def_id);
let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
hir::Node::Item(item) => Some(item.ident.span),
_ => None,
});
let mut err = struct_span_code_err!(
tcx.dcx(),
span,
E0038,
"the trait `{}` cannot be made into an object",
trait_str
);
err.span_label(span, format!("`{trait_str}` cannot be made into an object"));
if let Some(hir_id) = hir_id
&& let hir::Node::Ty(ty) = tcx.hir_node(hir_id)
&& let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
{
let mut hir_id = hir_id;
while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) {
hir_id = ty.hir_id;
}
if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
// Do not suggest `impl Trait` when dealing with things like super-traits.
err.span_suggestion_verbose(
ty.span.until(trait_ref.span),
"consider using an opaque type instead",
"impl ",
Applicability::MaybeIncorrect,
);
}
}
let mut reported_violations = FxIndexSet::default();
let mut multi_span = vec![];
let mut messages = vec![];
for violation in violations {
if let ObjectSafetyViolation::SizedSelf(sp) = &violation
&& !sp.is_empty()
{
// Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations
// with a `Span`.
reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into()));
}
if reported_violations.insert(violation.clone()) {
let spans = violation.spans();
let msg = if trait_span.is_none() || spans.is_empty() {
format!("the trait cannot be made into an object because {}", violation.error_msg())
} else {
format!("...because {}", violation.error_msg())
};
if spans.is_empty() {
err.note(msg);
} else {
for span in spans {
multi_span.push(span);
messages.push(msg.clone());
}
}
}
}
let has_multi_span = !multi_span.is_empty();
let mut note_span = MultiSpan::from_spans(multi_span.clone());
if let (Some(trait_span), true) = (trait_span, has_multi_span) {
note_span.push_span_label(trait_span, "this trait cannot be made into an object...");
}
for (span, msg) in iter::zip(multi_span, messages) {
note_span.push_span_label(span, msg);
}
err.span_note(
note_span,
"for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \
to be resolvable dynamically; for more information visit \
<https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
);
// Only provide the help if its a local trait, otherwise it's not actionable.
if trait_span.is_some() {
let mut reported_violations: Vec<_> = reported_violations.into_iter().collect();
reported_violations.sort();
let mut potential_solutions: Vec<_> =
reported_violations.into_iter().map(|violation| violation.solution()).collect();
potential_solutions.sort();
// Allows us to skip suggesting that the same item should be moved to another trait multiple times.
potential_solutions.dedup();
for solution in potential_solutions {
solution.add_to(&mut err);
}
}
let impls_of = tcx.trait_impls_of(trait_def_id);
let impls = if impls_of.blanket_impls().is_empty() {
impls_of
.non_blanket_impls()
.values()
.flatten()
.filter(|def_id| {
!matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..))
})
.collect::<Vec<_>>()
} else {
vec![]
};
let externally_visible = if !impls.is_empty()
&& let Some(def_id) = trait_def_id.as_local()
// We may be executing this during typeck, which would result in cycle
// if we used effective_visibilities query, which looks into opaque types
// (and therefore calls typeck).
&& tcx.resolutions(()).effective_visibilities.is_exported(def_id)
{
true
} else {
false
};
match &impls[..] {
[] => {}
_ if impls.len() > 9 => {}
[only] if externally_visible => {
err.help(with_no_trimmed_paths!(format!(
"only type `{}` is seen to implement the trait in this crate, consider using it \
directly instead",
tcx.type_of(*only).instantiate_identity(),
)));
}
[only] => {
err.help(with_no_trimmed_paths!(format!(
"only type `{}` implements the trait, consider using it directly instead",
tcx.type_of(*only).instantiate_identity(),
)));
}
impls => {
let types = impls
.iter()
.map(|t| {
with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),))
})
.collect::<Vec<_>>();
err.help(format!(
"the following types implement the trait, consider defining an enum where each \
variant holds one of these types, implementing `{}` for this new enum and using \
it instead:\n{}",
trait_str,
types.join("\n"),
));
}
}
if externally_visible {
err.note(format!(
"`{trait_str}` can be implemented in other crates; if you want to support your users \
passing their own types here, you can't refer to a specific type",
));
}
err
}

View File

@@ -1,5 +1,5 @@
use super::{ObligationCauseCode, PredicateObligation};
use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
use crate::error_reporting::TypeErrCtxt;
use crate::errors::{
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
};
@@ -13,8 +13,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::error_reporting::infer::TypeErrCtxt;
use rustc_macros::{extension, LintDiagnostic};
use rustc_macros::LintDiagnostic;
use rustc_middle::bug;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::GenericArgsRef;
@@ -41,7 +40,6 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
sym::Trait,
];
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn impl_similar_to(
&self,
@@ -109,7 +107,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
fn on_unimplemented_note(
pub fn on_unimplemented_note(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,

View File

@@ -5,17 +5,14 @@ use rustc_errors::{
};
use rustc_hir::def::Namespace;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_infer::error_reporting::infer::TypeErrCtxt;
use rustc_infer::traits::{Obligation, PredicateObligation};
use rustc_macros::extension;
use rustc_middle::ty::print::{FmtPrinter, Print};
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::Limit;
use rustc_span::Span;
use rustc_type_ir::Upcast;
use super::InferCtxtPrivExt;
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt;
use crate::error_reporting::TypeErrCtxt;
pub enum OverflowCause<'tcx> {
DeeplyNormalize(ty::AliasTerm<'tcx>),
@@ -38,7 +35,6 @@ pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
));
}
#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// Reports that an overflow has occurred and halts compilation. We
/// halt compilation unconditionally because it is important that
@@ -46,7 +42,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// whose result could not be truly determined and thus we can't say
/// if the program type checks or not -- and they are unusual
/// occurrences in any case.
fn report_overflow_error(
pub fn report_overflow_error(
&self,
cause: OverflowCause<'tcx>,
span: Span,
@@ -59,7 +55,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
FatalError.raise();
}
fn build_overflow_error(
pub fn build_overflow_error(
&self,
cause: OverflowCause<'tcx>,
span: Span,
@@ -132,7 +128,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// whose result could not be truly determined and thus we can't say
/// if the program type checks or not -- and they are unusual
/// occurrences in any case.
fn report_overflow_obligation<T>(
pub fn report_overflow_obligation<T>(
&self,
obligation: &Obligation<'tcx, T>,
suggest_increasing_limit: bool,
@@ -165,7 +161,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// that we can give a more helpful error message (and, in particular,
/// we do not suggest increasing the overflow limit, which is not
/// going to help).
fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
pub fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
let cycle = self.resolve_vars_if_possible(cycle.to_owned());
assert!(!cycle.is_empty());
@@ -179,7 +175,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}
fn report_overflow_no_abort(
pub fn report_overflow_no_abort(
&self,
obligation: PredicateObligation<'tcx>,
suggest_increasing_limit: bool,

View File

@@ -5,11 +5,10 @@ use super::{
PredicateObligation,
};
use crate::error_reporting::TypeErrCtxt;
use crate::errors;
use crate::infer::InferCtxt;
use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt};
use hir::def::CtorOf;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
@@ -17,15 +16,15 @@ use rustc_errors::{
Style, SuggestionStyle,
};
use rustc_hir as hir;
use rustc_hir::def::CtorOf;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::is_range_literal;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
use rustc_infer::error_reporting::infer::TypeErrCtxt;
use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
use rustc_macros::extension;
use rustc_middle::hir::map;
use rustc_middle::traits::IsConstable;
use rustc_middle::ty::error::TypeError;
@@ -44,7 +43,6 @@ use std::assert_matches::debug_assert_matches;
use std::borrow::Cow;
use std::iter;
use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
use crate::infer::InferCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_middle::ty::print::{
@@ -241,9 +239,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
}
}
#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn suggest_restricting_param_bound(
pub fn suggest_restricting_param_bound(
&self,
err: &mut Diag<'_>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -453,7 +450,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// When after several dereferencing, the reference satisfies the trait
/// bound. This function provides dereference suggestion for this
/// specific situation.
fn suggest_dereferences(
pub(super) fn suggest_dereferences(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -782,7 +779,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// We tried to apply the bound to an `fn` or closure. Check whether calling it would
/// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling
/// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.
fn suggest_fn_call(
pub(super) fn suggest_fn_call(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -898,7 +895,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
true
}
fn check_for_binding_assigned_block_without_tail_expression(
pub(super) fn check_for_binding_assigned_block_without_tail_expression(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -974,7 +971,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn suggest_add_clone_to_arg(
pub(super) fn suggest_add_clone_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -1074,7 +1071,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// Extracts information about a callable type for diagnostics. This is a
/// heuristic -- it doesn't necessarily mean that a type is always callable,
/// because the callable type must also be well-formed to be called.
fn extract_callable_info(
pub fn extract_callable_info(
&self,
body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
@@ -1200,7 +1197,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
}
fn suggest_add_reference_to_arg(
pub(super) fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -1422,7 +1419,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
// Suggest borrowing the type
fn suggest_borrowing_for_object_cast(
pub(super) fn suggest_borrowing_for_object_cast(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
@@ -1457,7 +1454,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
/// suggest removing these references until we reach a type that implements the trait.
fn suggest_remove_reference(
pub(super) fn suggest_remove_reference(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -1578,7 +1575,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
false
}
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>) {
pub(super) fn suggest_remove_await(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
) {
let hir = self.tcx.hir();
if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives()
&& let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
@@ -1644,7 +1645,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// Check if the trait bound is implemented for a different mutability and note it in the
/// final error.
fn suggest_change_mut(
pub(super) fn suggest_change_mut(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -1720,7 +1721,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn suggest_semicolon_removal(
pub(super) fn suggest_semicolon_removal(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -1762,7 +1763,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
false
}
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
pub(super) fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) =
self.tcx.hir_node_by_def_id(obligation.cause.body_id)
else {
@@ -1775,7 +1776,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if
/// applicable and signal that the error has been expanded appropriately and needs to be
/// emitted.
fn suggest_impl_trait(
pub(super) fn suggest_impl_trait(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
@@ -1865,7 +1866,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
true
}
fn point_at_returns_when_relevant(
pub(super) fn point_at_returns_when_relevant(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
@@ -1897,7 +1898,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn report_closure_arg_mismatch(
pub(super) fn report_closure_arg_mismatch(
&self,
span: Span,
found_span: Option<Span>,
@@ -2176,7 +2177,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn suggest_fully_qualified_path(
pub(super) fn suggest_fully_qualified_path(
&self,
err: &mut Diag<'_>,
item_def_id: DefId,
@@ -2243,7 +2244,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
///
/// Returns `true` if an async-await specific note was added to the diagnostic.
#[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))]
fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
pub(super) fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
&self,
err: &mut Diag<'_, G>,
obligation: &PredicateObligation<'tcx>,
@@ -2712,7 +2713,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}
fn note_obligation_cause_code<G: EmissionGuarantee, T>(
pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>(
&self,
body_id: LocalDefId,
err: &mut Diag<'_, G>,
@@ -3554,7 +3555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
#[instrument(
level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty())
)]
fn suggest_await_before_try(
pub(super) fn suggest_await_before_try(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
@@ -3611,7 +3612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn suggest_floating_point_literal(
pub(super) fn suggest_floating_point_literal(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -3635,7 +3636,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn suggest_derive(
pub fn suggest_derive(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -3701,7 +3702,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn suggest_dereferencing_index(
pub(super) fn suggest_dereferencing_index(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -4323,7 +4324,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// If the type that failed selection is an array or a reference to an array,
/// but the trait is implemented for slices, suggest that the user converts
/// the array into a slice.
fn suggest_convert_to_slice(
pub(super) fn suggest_convert_to_slice(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
@@ -4395,7 +4396,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn explain_hrtb_projection(
pub(super) fn explain_hrtb_projection(
&self,
diag: &mut Diag<'_>,
pred: ty::PolyTraitPredicate<'tcx>,
@@ -4461,7 +4462,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn suggest_desugaring_async_fn_in_trait(
pub(super) fn suggest_desugaring_async_fn_in_trait(
&self,
err: &mut Diag<'_>,
trait_ref: ty::PolyTraitRef<'tcx>,
@@ -4545,7 +4546,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}
fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option<String> {
pub fn ty_kind_suggestion(
&self,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<String> {
let tcx = self.infcx.tcx;
let implements_default = |ty| {
let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
@@ -4607,7 +4612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
}
fn suggest_add_result_as_return_type(
pub(super) fn suggest_add_result_as_return_type(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
@@ -4648,7 +4653,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
#[instrument(level = "debug", skip_all)]
fn suggest_unsized_bound_if_applicable(
pub(super) fn suggest_unsized_bound_if_applicable(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,