1
Fork 0

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

View file

@ -1,163 +0,0 @@
//! 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

@ -1,236 +0,0 @@
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

@ -1,127 +0,0 @@
//! 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

@ -1,93 +0,0 @@
use crate::error_reporting::infer::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

@ -1,92 +0,0 @@
//! 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

@ -1,496 +0,0 @@
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

@ -1,86 +0,0 @@
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

@ -1,618 +0,0 @@
//! 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

@ -1,162 +0,0 @@
//! 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

@ -1,166 +0,0 @@
//! 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

@ -1,421 +0,0 @@
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

@ -1,940 +0,0 @@
use super::TypeErrCtxt;
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};
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

@ -1,81 +0,0 @@
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

@ -1,899 +0,0 @@
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::errors::{
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
};
use super::TypeErrCtxt;
#[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 +0,0 @@
pub mod infer;

File diff suppressed because it is too large Load diff

View file

@ -1,183 +0,0 @@
use crate::error_reporting::infer::nice_region_error::find_anon_type;
use crate::fluent_generated as fluent;
use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::bug;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{symbol::kw, Span};
struct DescriptionCtx<'a> {
span: Option<Span>,
kind: &'a str,
arg: String,
}
impl<'a> DescriptionCtx<'a> {
fn new<'tcx>(
tcx: TyCtxt<'tcx>,
generic_param_scope: LocalDefId,
region: ty::Region<'tcx>,
alt_span: Option<Span>,
) -> Option<Self> {
let (span, kind, arg) = match *region {
ty::ReEarlyParam(br) => {
let scope = tcx
.parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
.expect_local();
let span = if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
{
param.span
} else {
tcx.def_span(scope)
};
if br.has_name() {
(Some(span), "as_defined", br.name.to_string())
} else {
(Some(span), "as_defined_anon", String::new())
}
}
ty::ReLateParam(ref fr) => {
if !fr.bound_region.is_named()
&& let Some((ty, _)) =
find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
{
(Some(ty.span), "defined_here", String::new())
} else {
let scope = fr.scope.expect_local();
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
let span = if let Some(param) = tcx
.hir()
.get_generics(scope)
.and_then(|generics| generics.get_named(name))
{
param.span
} else {
tcx.def_span(scope)
};
if name == kw::UnderscoreLifetime {
(Some(span), "as_defined_anon", String::new())
} else {
(Some(span), "as_defined", name.to_string())
}
}
ty::BrAnon => {
let span = Some(tcx.def_span(scope));
(span, "defined_here", String::new())
}
_ => (Some(tcx.def_span(scope)), "defined_here_reg", region.to_string()),
}
}
}
ty::ReStatic => (alt_span, "restatic", String::new()),
ty::RePlaceholder(_) | ty::ReError(_) => return None,
ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => {
bug!("unexpected region for DescriptionCtx: {:?}", region);
}
};
Some(DescriptionCtx { span, kind, arg })
}
}
pub enum PrefixKind {
Empty,
RefValidFor,
ContentValidFor,
TypeObjValidFor,
SourcePointerValidFor,
TypeSatisfy,
TypeOutlive,
LfParamInstantiatedWith,
LfParamMustOutlive,
LfInstantiatedWith,
LfMustOutlive,
PointerValidFor,
DataValidFor,
}
pub enum SuffixKind {
Empty,
Continues,
ReqByBinding,
}
impl IntoDiagArg for PrefixKind {
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
let kind = match self {
Self::Empty => "empty",
Self::RefValidFor => "ref_valid_for",
Self::ContentValidFor => "content_valid_for",
Self::TypeObjValidFor => "type_obj_valid_for",
Self::SourcePointerValidFor => "source_pointer_valid_for",
Self::TypeSatisfy => "type_satisfy",
Self::TypeOutlive => "type_outlive",
Self::LfParamInstantiatedWith => "lf_param_instantiated_with",
Self::LfParamMustOutlive => "lf_param_must_outlive",
Self::LfInstantiatedWith => "lf_instantiated_with",
Self::LfMustOutlive => "lf_must_outlive",
Self::PointerValidFor => "pointer_valid_for",
Self::DataValidFor => "data_valid_for",
}
.into();
rustc_errors::DiagArgValue::Str(kind)
}
}
impl IntoDiagArg for SuffixKind {
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
let kind = match self {
Self::Empty => "empty",
Self::Continues => "continues",
Self::ReqByBinding => "req_by_binding",
}
.into();
rustc_errors::DiagArgValue::Str(kind)
}
}
pub struct RegionExplanation<'a> {
desc: DescriptionCtx<'a>,
prefix: PrefixKind,
suffix: SuffixKind,
}
impl RegionExplanation<'_> {
pub fn new<'tcx>(
tcx: TyCtxt<'tcx>,
generic_param_scope: LocalDefId,
region: ty::Region<'tcx>,
alt_span: Option<Span>,
prefix: PrefixKind,
suffix: SuffixKind,
) -> Option<Self> {
Some(Self {
desc: DescriptionCtx::new(tcx, generic_param_scope, region, alt_span)?,
prefix,
suffix,
})
}
}
impl Subdiagnostic for RegionExplanation<'_> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
f: &F,
) {
diag.arg("pref_kind", self.prefix);
diag.arg("suff_kind", self.suffix);
diag.arg("desc_kind", self.desc.kind);
diag.arg("desc_arg", self.desc.arg);
let msg = f(diag, fluent::infer_region_explanation.into());
if let Some(span) = self.desc.span {
diag.span_note(span, msg);
} else {
diag.note(msg);
}
}
}

View file

@ -11,7 +11,6 @@ pub use BoundRegionConversionTime::*;
pub use RegionVariableOrigin::*;
pub use SubregionOrigin::*;
use crate::error_reporting::infer::TypeErrCtxt;
use crate::infer::relate::RelateResult;
use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine};
use free_regions::RegionRelations;
@ -24,7 +23,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::undo_log::Rollback;
use rustc_data_structures::unify as ut;
use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::extension;
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
@ -696,22 +696,6 @@ impl<'tcx> InferCtxt<'tcx> {
self.next_trait_solver
}
/// Creates a `TypeErrCtxt` for emitting various inference errors.
/// During typeck, use `FnCtxt::err_ctxt` instead.
pub 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![])]
}),
}
}
pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
t.fold_with(&mut self.freshener())
}
@ -1591,60 +1575,6 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// [Note-Type-error-reporting]
// An invariant is that anytime the expected or actual type is Error (the special
// error type, meaning that an error occurred when typechecking this expression),
// this is a derived error. The error cascaded from another error (that was already
// reported), so it's not useful to display it to the user.
// The following methods implement this logic.
// They check if either the actual or expected type is Error, and don't print the error
// in this case. The typechecker should only ever report type errors involving mismatched
// types using one of these methods, and should not call span_err directly for such
// errors.
pub fn type_error_struct_with_diag<M>(
&self,
sp: Span,
mk_diag: M,
actual_ty: Ty<'tcx>,
) -> Diag<'a>
where
M: FnOnce(String) -> Diag<'a>,
{
let actual_ty = self.resolve_vars_if_possible(actual_ty);
debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);
let mut err = mk_diag(self.ty_to_string(actual_ty));
// Don't report an error if actual type is `Error`.
if actual_ty.references_error() {
err.downgrade_to_delayed_bug();
}
err
}
pub fn report_mismatched_types(
&self,
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
err: TypeError<'tcx>,
) -> Diag<'a> {
self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err)
}
pub fn report_mismatched_consts(
&self,
cause: &ObligationCause<'tcx>,
expected: ty::Const<'tcx>,
actual: ty::Const<'tcx>,
err: TypeError<'tcx>,
) -> Diag<'a> {
self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err)
}
}
/// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently
/// used only for `traits::fulfill`'s list of `stalled_on` inference variables.
#[derive(Copy, Clone, Debug)]
@ -1890,3 +1820,32 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 })
}
impl<'tcx> InferCtxt<'tcx> {
/// Given a [`hir::Block`], get the span of its last expression or
/// statement, peeling off any inner blocks.
pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
let block = block.innermost_block();
if let Some(expr) = &block.expr {
expr.span
} else if let Some(stmt) = block.stmts.last() {
// possibly incorrect trailing `;` in the else arm
stmt.span
} else {
// empty block; point at its entirety
block.span
}
}
/// Given a [`hir::HirId`] for a block, get the span of its last expression
/// or statement, peeling off any inner blocks.
pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span {
match self.tcx.hir_node(hir_id) {
hir::Node::Block(blk) => self.find_block_span(blk),
// The parser was in a weird state if either of these happen, but
// it's better not to panic.
hir::Node::Expr(e) => e.span,
_ => rustc_span::DUMMY_SP,
}
}
}

View file

@ -34,7 +34,6 @@
#[macro_use]
extern crate tracing;
pub mod error_reporting;
mod errors;
pub mod infer;
pub mod traits;

View file

@ -1,203 +0,0 @@
use super::ObjectSafetyViolation;
use crate::error_reporting::infer::TypeErrCtxt;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Span;
use std::fmt;
use std::iter;
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

@ -3,7 +3,6 @@
//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
mod engine;
pub mod error_reporting;
mod project;
mod structural_impls;
pub mod util;