diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 2e2c1591e9b4..0926d5ccf2d5 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -13,7 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; 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::{can_type_implement_copy, CopyImplementationError}; +use rustc_trait_selection::traits::misc::{ + type_allowed_to_implement_copy, CopyImplementationError, +}; use rustc_trait_selection::traits::predicate_for_trait_def; use rustc_trait_selection::traits::{self, ObligationCause}; use std::collections::BTreeMap; @@ -82,7 +84,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { }; let cause = traits::ObligationCause::misc(span, impl_hir_id); - match can_type_implement_copy(tcx, param_env, self_type, cause) { + match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { Ok(()) => {} Err(CopyImplementationError::InfrigingFields(fields)) => { let mut err = struct_span_err!( diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6f445426df70..fe188162cf85 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -72,7 +72,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, InnerSpan, Span}; use rustc_target::abi::{Abi, VariantIdx}; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; -use rustc_trait_selection::traits::{self, misc::can_type_implement_copy, EvaluationResult}; +use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy}; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -709,12 +709,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { // We shouldn't recommend implementing `Copy` on stateful things, // such as iterators. - if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) { - if cx.tcx.infer_ctxt().build().type_implements_trait(iter_trait, [ty], param_env) - == EvaluationResult::EvaluatedToOk - { - return; - } + if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && cx.tcx + .infer_ctxt() + .build() + .type_implements_trait(iter_trait, [ty], param_env) + .must_apply_modulo_regions() + { + return; } // Default value of clippy::trivially_copy_pass_by_ref @@ -726,7 +728,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { } } - if can_type_implement_copy( + if type_allowed_to_implement_copy( cx.tcx, param_env, ty, diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index b6ded4ce5a39..b87412f7de16 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -1,9 +1,9 @@ //! 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_hir as hir; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; @@ -16,14 +16,16 @@ pub enum CopyImplementationError<'tcx> { HasDestructor, } -pub fn can_type_implement_copy<'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,9 +44,14 @@ 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 { + // Do this per-field to get better error messages. + let infcx = tcx.infer_ctxt().build(); + let ocx = traits::ObligationCtxt::new(&infcx); + let ty = field.ty(tcx, substs); if ty.references_error() { continue; @@ -63,21 +70,36 @@ pub fn can_type_implement_copy<'tcx>( } 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); - } - }; + + let ty = ocx.normalize(&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, + // but instead just implement this as a field that isn't WF. + infcx.err_ctxt().report_fulfillment_errors(&normalization_errors, None); + continue; + } + + ocx.register_bound(cause, param_env, ty, copy_def_id); + if !ocx.select_all_or_error().is_empty() { + infringing.push((field, ty)); + } + + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.process_registered_region_obligations( + outlives_env.region_bound_pairs(), + param_env, + ); + if !infcx.resolve_regions(&outlives_env).is_empty() { + infringing.push((field, ty)); + } } } + if !infringing.is_empty() { return Err(CopyImplementationError::InfrigingFields(infringing)); } + if adt.has_dtor(tcx) { return Err(CopyImplementationError::HasDestructor); } 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 new file mode 100644 index 000000000000..1c4d2136677d --- /dev/null +++ b/src/test/ui/traits/copy-is-not-modulo-regions.not_static.stderr @@ -0,0 +1,12 @@ +error[E0204]: the trait `Copy` may not be implemented for this type + --> $DIR/copy-is-not-modulo-regions.rs:13:21 + | +LL | struct Bar<'lt>(Foo<'lt>); + | -------- this field does not implement `Copy` +... +LL | impl<'any> Copy for Bar<'any> {} + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0204`. diff --git a/src/test/ui/traits/copy-is-not-modulo-regions.rs b/src/test/ui/traits/copy-is-not-modulo-regions.rs new file mode 100644 index 000000000000..adb870237697 --- /dev/null +++ b/src/test/ui/traits/copy-is-not-modulo-regions.rs @@ -0,0 +1,19 @@ +// revisions: not_static yes_static +//[yes_static] check-pass + +#[derive(Clone)] +struct Foo<'lt>(&'lt ()); + +impl Copy for Foo<'static> {} + +#[derive(Clone)] +struct Bar<'lt>(Foo<'lt>); + +#[cfg(not_static)] +impl<'any> Copy for Bar<'any> {} +//[not_static]~^ the trait `Copy` may not be implemented for this type + +#[cfg(yes_static)] +impl<'any> Copy for Bar<'static> {} + +fn main() {} diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 1249db5dc479..8c9d4c5cfe66 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -24,7 +24,7 @@ use rustc_span::symbol::kw; use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::misc::can_type_implement_copy; +use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; use std::borrow::Cow; declare_clippy_lint! { @@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { let sugg = |diag: &mut Diagnostic| { if let ty::Adt(def, ..) = ty.kind() { if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { - if can_type_implement_copy( + if type_allowed_to_implement_copy( cx.tcx, cx.param_env, ty,