about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-11-30 20:41:02 +0000
committerMichael Goulet <michael@errs.io>2023-01-13 23:06:29 +0000
commit8cf7f40a895f476ecd3216b11ff673389135b652 (patch)
tree5d563f3db175e808bc4ca5679e22ee11d4086743
parent0b90256ada21c6a81b4c18f2c7a23151ab5fc232 (diff)
downloadrust-8cf7f40a895f476ecd3216b11ff673389135b652.tar.gz
rust-8cf7f40a895f476ecd3216b11ff673389135b652.zip
Check ADT fields for copy implementations considering regions
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs6
-rw-r--r--compiler/rustc_lint/src/builtin.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs50
-rw-r--r--src/test/ui/traits/copy-is-not-modulo-regions.not_static.stderr12
-rw-r--r--src/test/ui/traits/copy-is-not-modulo-regions.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs4
6 files changed, 83 insertions, 26 deletions
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 2e2c1591e9b..0926d5ccf2d 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 6f445426df7..fe188162cf8 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 b6ded4ce5a3..b87412f7de1 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 00000000000..1c4d2136677
--- /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 00000000000..adb87023769
--- /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 1249db5dc47..8c9d4c5cfe6 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,