Suggest lifetime bound in illegal Copy impl
This commit is contained in:
parent
333c6bf523
commit
16cfadbfe8
3 changed files with 102 additions and 56 deletions
|
@ -7,14 +7,14 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_hir::ItemKind;
|
use rustc_hir::ItemKind;
|
||||||
use rustc_infer::infer;
|
|
||||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_infer::infer::{self, RegionResolutionError};
|
||||||
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
|
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
|
||||||
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
|
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
|
||||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
||||||
use rustc_trait_selection::traits::misc::{
|
use rustc_trait_selection::traits::misc::{
|
||||||
type_allowed_to_implement_copy, CopyImplementationError,
|
type_allowed_to_implement_copy, CopyImplementationError, InfringingFieldsReason,
|
||||||
};
|
};
|
||||||
use rustc_trait_selection::traits::predicate_for_trait_def;
|
use rustc_trait_selection::traits::predicate_for_trait_def;
|
||||||
use rustc_trait_selection::traits::{self, ObligationCause};
|
use rustc_trait_selection::traits::{self, ObligationCause};
|
||||||
|
@ -99,25 +99,13 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||||
let mut errors: BTreeMap<_, Vec<_>> = Default::default();
|
let mut errors: BTreeMap<_, Vec<_>> = Default::default();
|
||||||
let mut bounds = vec![];
|
let mut bounds = vec![];
|
||||||
|
|
||||||
for (field, ty) in fields {
|
for (field, ty, reason) in fields {
|
||||||
let field_span = tcx.def_span(field.did);
|
let field_span = tcx.def_span(field.did);
|
||||||
let field_ty_span = match tcx.hir().get_if_local(field.did) {
|
|
||||||
Some(hir::Node::Field(field_def)) => field_def.ty.span,
|
|
||||||
_ => field_span,
|
|
||||||
};
|
|
||||||
err.span_label(field_span, "this field does not implement `Copy`");
|
err.span_label(field_span, "this field does not implement `Copy`");
|
||||||
// Spin up a new FulfillmentContext, so we can get the _precise_ reason
|
|
||||||
// why this field does not implement Copy. This is useful because sometimes
|
match reason {
|
||||||
// it is not immediately clear why Copy is not implemented for a field, since
|
InfringingFieldsReason::Fulfill(fulfillment_errors) => {
|
||||||
// all we point at is the field itself.
|
for error in fulfillment_errors {
|
||||||
let infcx = tcx.infer_ctxt().ignoring_regions().build();
|
|
||||||
for error in traits::fully_solve_bound(
|
|
||||||
&infcx,
|
|
||||||
traits::ObligationCause::dummy_with_span(field_ty_span),
|
|
||||||
param_env,
|
|
||||||
ty,
|
|
||||||
tcx.require_lang_item(LangItem::Copy, Some(span)),
|
|
||||||
) {
|
|
||||||
let error_predicate = error.obligation.predicate;
|
let error_predicate = error.obligation.predicate;
|
||||||
// Only note if it's not the root obligation, otherwise it's trivial and
|
// Only note if it's not the root obligation, otherwise it's trivial and
|
||||||
// should be self-explanatory (i.e. a field literally doesn't implement Copy).
|
// should be self-explanatory (i.e. a field literally doesn't implement Copy).
|
||||||
|
@ -130,11 +118,13 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(error.obligation.cause.span);
|
.push(error.obligation.cause.span);
|
||||||
}
|
}
|
||||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
|
if let ty::PredicateKind::Clause(ty::Clause::Trait(
|
||||||
|
ty::TraitPredicate {
|
||||||
trait_ref,
|
trait_ref,
|
||||||
polarity: ty::ImplPolarity::Positive,
|
polarity: ty::ImplPolarity::Positive,
|
||||||
..
|
..
|
||||||
})) = error_predicate.kind().skip_binder()
|
},
|
||||||
|
)) = error_predicate.kind().skip_binder()
|
||||||
{
|
{
|
||||||
let ty = trait_ref.self_ty();
|
let ty = trait_ref.self_ty();
|
||||||
if let ty::Param(_) = ty.kind() {
|
if let ty::Param(_) = ty.kind() {
|
||||||
|
@ -147,6 +137,36 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
InfringingFieldsReason::Regions(region_errors) => {
|
||||||
|
for error in region_errors {
|
||||||
|
let ty = ty.to_string();
|
||||||
|
match error {
|
||||||
|
RegionResolutionError::ConcreteFailure(origin, a, b) => {
|
||||||
|
let predicate = format!("{b}: {a}");
|
||||||
|
errors
|
||||||
|
.entry((ty.clone(), predicate.clone()))
|
||||||
|
.or_default()
|
||||||
|
.push(origin.span());
|
||||||
|
if let ty::RegionKind::ReEarlyBound(ebr) = *b && ebr.has_name() {
|
||||||
|
bounds.push((b.to_string(), a.to_string(), None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegionResolutionError::GenericBoundFailure(origin, a, b) => {
|
||||||
|
let predicate = format!("{a}: {b}");
|
||||||
|
errors
|
||||||
|
.entry((ty.clone(), predicate.clone()))
|
||||||
|
.or_default()
|
||||||
|
.push(origin.span());
|
||||||
|
if let infer::region_constraints::GenericKind::Param(_) = a {
|
||||||
|
bounds.push((a.to_string(), b.to_string(), None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for ((ty, error_predicate), spans) in errors {
|
for ((ty, error_predicate), spans) in errors {
|
||||||
let span: MultiSpan = spans.into();
|
let span: MultiSpan = spans.into();
|
||||||
err.span_note(
|
err.span_note(
|
||||||
|
|
|
@ -4,21 +4,25 @@ use crate::traits::{self, ObligationCause};
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxIndexSet;
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
|
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
|
||||||
|
|
||||||
use crate::traits::error_reporting::TypeErrCtxtExt;
|
use crate::traits::error_reporting::TypeErrCtxtExt;
|
||||||
|
|
||||||
use super::outlives_bounds::InferCtxtExt;
|
use super::outlives_bounds::InferCtxtExt;
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum CopyImplementationError<'tcx> {
|
pub enum CopyImplementationError<'tcx> {
|
||||||
InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>)>),
|
InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
|
||||||
NotAnAdt,
|
NotAnAdt,
|
||||||
HasDestructor,
|
HasDestructor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum InfringingFieldsReason<'tcx> {
|
||||||
|
Fulfill(Vec<FulfillmentError<'tcx>>),
|
||||||
|
Regions(Vec<RegionResolutionError<'tcx>>),
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks that the fields of the type (an ADT) all implement copy.
|
/// Checks that the fields of the type (an ADT) all implement copy.
|
||||||
///
|
///
|
||||||
/// If fields don't implement copy, return an error containing a list of
|
/// If fields don't implement copy, return an error containing a list of
|
||||||
|
@ -60,22 +64,27 @@ pub fn type_allowed_to_implement_copy<'tcx>(
|
||||||
if ty.references_error() {
|
if ty.references_error() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let span = tcx.def_span(field.did);
|
|
||||||
|
let field_span = tcx.def_span(field.did);
|
||||||
|
let field_ty_span = match tcx.hir().get_if_local(field.did) {
|
||||||
|
Some(hir::Node::Field(field_def)) => field_def.ty.span,
|
||||||
|
_ => field_span,
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME(compiler-errors): This gives us better spans for bad
|
// FIXME(compiler-errors): This gives us better spans for bad
|
||||||
// projection types like in issue-50480.
|
// projection types like in issue-50480.
|
||||||
// If the ADT has substs, point to the cause we are given.
|
// If the ADT has substs, point to the cause we are given.
|
||||||
// If it does not, then this field probably doesn't normalize
|
// If it does not, then this field probably doesn't normalize
|
||||||
// to begin with, and point to the bad field's span instead.
|
// to begin with, and point to the bad field's span instead.
|
||||||
let cause = if field
|
let normalization_cause = if field
|
||||||
.ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
|
.ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
|
||||||
.has_non_region_param()
|
.has_non_region_param()
|
||||||
{
|
{
|
||||||
parent_cause.clone()
|
parent_cause.clone()
|
||||||
} else {
|
} else {
|
||||||
ObligationCause::dummy_with_span(span)
|
ObligationCause::dummy_with_span(field_ty_span)
|
||||||
};
|
};
|
||||||
|
let ty = ocx.normalize(&normalization_cause, param_env, ty);
|
||||||
let ty = ocx.normalize(&cause, param_env, ty);
|
|
||||||
let normalization_errors = ocx.select_where_possible();
|
let normalization_errors = ocx.select_where_possible();
|
||||||
if !normalization_errors.is_empty() {
|
if !normalization_errors.is_empty() {
|
||||||
// Don't report this as a field that doesn't implement Copy,
|
// Don't report this as a field that doesn't implement Copy,
|
||||||
|
@ -84,9 +93,15 @@ pub fn type_allowed_to_implement_copy<'tcx>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ocx.register_bound(cause, param_env, ty, copy_def_id);
|
ocx.register_bound(
|
||||||
if !ocx.select_all_or_error().is_empty() {
|
ObligationCause::dummy_with_span(field_ty_span),
|
||||||
infringing.push((field, ty));
|
param_env,
|
||||||
|
ty,
|
||||||
|
copy_def_id,
|
||||||
|
);
|
||||||
|
let errors = ocx.select_all_or_error();
|
||||||
|
if !errors.is_empty() {
|
||||||
|
infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check regions assuming the self type of the impl is WF
|
// Check regions assuming the self type of the impl is WF
|
||||||
|
@ -103,8 +118,9 @@ pub fn type_allowed_to_implement_copy<'tcx>(
|
||||||
outlives_env.region_bound_pairs(),
|
outlives_env.region_bound_pairs(),
|
||||||
param_env,
|
param_env,
|
||||||
);
|
);
|
||||||
if !infcx.resolve_regions(&outlives_env).is_empty() {
|
let errors = infcx.resolve_regions(&outlives_env);
|
||||||
infringing.push((field, ty));
|
if !errors.is_empty() {
|
||||||
|
infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,16 @@ LL | struct Bar<'lt>(Foo<'lt>);
|
||||||
...
|
...
|
||||||
LL | impl<'any> Copy for Bar<'any> {}
|
LL | impl<'any> Copy for Bar<'any> {}
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: the `Copy` impl for `Foo<'any>` requires that `'any: 'static`
|
||||||
|
--> $DIR/copy-is-not-modulo-regions.rs:10:17
|
||||||
|
|
|
||||||
|
LL | struct Bar<'lt>(Foo<'lt>);
|
||||||
|
| ^^^^^^^^
|
||||||
|
help: consider restricting type parameter `'any`
|
||||||
|
|
|
||||||
|
LL | impl<'any: 'static> Copy for Bar<'any> {}
|
||||||
|
| +++++++++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue