Auto merge of #105102 - compiler-errors:copy-impl-considering-regions, r=lcnr
Check ADT fields for copy implementations considering regions Fixes #88901 r? `@ghost`
This commit is contained in:
commit
94a300b9b8
10 changed files with 217 additions and 101 deletions
|
@ -1,29 +1,36 @@
|
|||
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
|
||||
|
||||
use crate::infer::InferCtxtExt as _;
|
||||
use crate::traits::{self, ObligationCause};
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
|
||||
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
|
||||
|
||||
use crate::traits::error_reporting::TypeErrCtxtExt;
|
||||
use super::outlives_bounds::InferCtxtExt;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CopyImplementationError<'tcx> {
|
||||
InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>)>),
|
||||
InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
|
||||
NotAnAdt,
|
||||
HasDestructor,
|
||||
}
|
||||
|
||||
pub fn can_type_implement_copy<'tcx>(
|
||||
pub enum InfringingFieldsReason<'tcx> {
|
||||
Fulfill(Vec<FulfillmentError<'tcx>>),
|
||||
Regions(Vec<RegionResolutionError<'tcx>>),
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// those violating fields. If it's not an ADT, returns `Err(NotAnAdt)`.
|
||||
pub fn type_allowed_to_implement_copy<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
self_type: Ty<'tcx>,
|
||||
parent_cause: ObligationCause<'tcx>,
|
||||
) -> Result<(), CopyImplementationError<'tcx>> {
|
||||
// FIXME: (@jroesch) float this code up
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let (adt, substs) = match self_type.kind() {
|
||||
// These types used to have a builtin impl.
|
||||
// Now libcore provides that impl.
|
||||
|
@ -42,42 +49,82 @@ pub fn can_type_implement_copy<'tcx>(
|
|||
_ => return Err(CopyImplementationError::NotAnAdt),
|
||||
};
|
||||
|
||||
let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span));
|
||||
|
||||
let mut infringing = Vec::new();
|
||||
for variant in adt.variants() {
|
||||
for field in &variant.fields {
|
||||
let ty = field.ty(tcx, substs);
|
||||
if ty.references_error() {
|
||||
// Do this per-field to get better error messages.
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let ocx = traits::ObligationCtxt::new(&infcx);
|
||||
|
||||
let unnormalized_ty = field.ty(tcx, substs);
|
||||
if unnormalized_ty.references_error() {
|
||||
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
|
||||
// projection types like in issue-50480.
|
||||
// If the ADT has substs, point to the cause we are given.
|
||||
// If it does not, then this field probably doesn't normalize
|
||||
// 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()))
|
||||
.has_non_region_param()
|
||||
{
|
||||
parent_cause.clone()
|
||||
} else {
|
||||
ObligationCause::dummy_with_span(span)
|
||||
};
|
||||
match traits::fully_normalize(&infcx, cause, param_env, ty) {
|
||||
Ok(ty) => {
|
||||
if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
|
||||
infringing.push((field, ty));
|
||||
}
|
||||
}
|
||||
Err(errors) => {
|
||||
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
|
||||
}
|
||||
ObligationCause::dummy_with_span(field_ty_span)
|
||||
};
|
||||
let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
|
||||
let normalization_errors = ocx.select_where_possible();
|
||||
if !normalization_errors.is_empty() {
|
||||
tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking Copy implementation"));
|
||||
continue;
|
||||
}
|
||||
|
||||
ocx.register_bound(
|
||||
ObligationCause::dummy_with_span(field_ty_span),
|
||||
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
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
Some(&infcx),
|
||||
infcx.implied_bounds_tys(
|
||||
param_env,
|
||||
parent_cause.body_id,
|
||||
FxIndexSet::from_iter([self_type]),
|
||||
),
|
||||
);
|
||||
infcx.process_registered_region_obligations(
|
||||
outlives_env.region_bound_pairs(),
|
||||
param_env,
|
||||
);
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
if !errors.is_empty() {
|
||||
infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !infringing.is_empty() {
|
||||
return Err(CopyImplementationError::InfrigingFields(infringing));
|
||||
}
|
||||
|
||||
if adt.has_dtor(tcx) {
|
||||
return Err(CopyImplementationError::HasDestructor);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue