diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 0926d5ccf2d..e642572e3da 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -7,14 +7,14 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; -use rustc_infer::infer; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{self, RegionResolutionError}; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; 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::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::{self, ObligationCause}; @@ -99,50 +99,70 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { let mut errors: BTreeMap<_, Vec<_>> = Default::default(); 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_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`"); - // Spin up a new FulfillmentContext, so we can get the _precise_ reason - // why this field does not implement Copy. This is useful because sometimes - // it is not immediately clear why Copy is not implemented for a field, since - // all we point at is the field itself. - 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; - // 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). - // FIXME: This error could be more descriptive, especially if the error_predicate - // contains a foreign type or if it's a deeply nested type... - if error_predicate != error.root_obligation.predicate { - errors - .entry((ty.to_string(), error_predicate.to_string())) - .or_default() - .push(error.obligation.cause.span); + match reason { + InfringingFieldsReason::Fulfill(fulfillment_errors) => { + for error in fulfillment_errors { + let error_predicate = error.obligation.predicate; + // 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). + + // FIXME: This error could be more descriptive, especially if the error_predicate + // contains a foreign type or if it's a deeply nested type... + if error_predicate != error.root_obligation.predicate { + errors + .entry((ty.to_string(), error_predicate.to_string())) + .or_default() + .push(error.obligation.cause.span); + } + if let ty::PredicateKind::Clause(ty::Clause::Trait( + ty::TraitPredicate { + trait_ref, + polarity: ty::ImplPolarity::Positive, + .. + }, + )) = error_predicate.kind().skip_binder() + { + let ty = trait_ref.self_ty(); + if let ty::Param(_) = ty.kind() { + bounds.push(( + format!("{ty}"), + trait_ref.print_only_trait_path().to_string(), + Some(trait_ref.def_id), + )); + } + } + } } - if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { - trait_ref, - polarity: ty::ImplPolarity::Positive, - .. - })) = error_predicate.kind().skip_binder() - { - let ty = trait_ref.self_ty(); - if let ty::Param(_) = ty.kind() { - bounds.push(( - format!("{ty}"), - trait_ref.print_only_trait_path().to_string(), - Some(trait_ref.def_id), - )); + 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, + } } } } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 46eea628a34..0de44dba0dd 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -4,21 +4,25 @@ use crate::traits::{self, ObligationCause}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -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 enum InfringingFieldsReason<'tcx> { + Fulfill(Vec>), + Regions(Vec>), +} + /// 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 @@ -60,22 +64,27 @@ pub fn type_allowed_to_implement_copy<'tcx>( if 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) + ObligationCause::dummy_with_span(field_ty_span) }; - - let ty = ocx.normalize(&cause, param_env, ty); + let ty = ocx.normalize(&normalization_cause, param_env, ty); let normalization_errors = ocx.select_where_possible(); if !normalization_errors.is_empty() { // 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; } - ocx.register_bound(cause, param_env, ty, copy_def_id); - if !ocx.select_all_or_error().is_empty() { - infringing.push((field, ty)); + 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 @@ -103,8 +118,9 @@ pub fn type_allowed_to_implement_copy<'tcx>( outlives_env.region_bound_pairs(), param_env, ); - if !infcx.resolve_regions(&outlives_env).is_empty() { - infringing.push((field, ty)); + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); } } } diff --git a/src/test/ui/traits/copy-is-not-modulo-regions.not_static.stderr b/src/test/ui/traits/copy-is-not-modulo-regions.not_static.stderr index 1c4d2136677..edd94d2010b 100644 --- a/src/test/ui/traits/copy-is-not-modulo-regions.not_static.stderr +++ b/src/test/ui/traits/copy-is-not-modulo-regions.not_static.stderr @@ -6,6 +6,16 @@ LL | struct Bar<'lt>(Foo<'lt>); ... 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