Skip to content

Commit 8cf7f40

Browse files
Check ADT fields for copy implementations considering regions
1 parent 0b90256 commit 8cf7f40

File tree

6 files changed

+83
-26
lines changed

6 files changed

+83
-26
lines changed

compiler/rustc_hir_analysis/src/coherence/builtin.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt;
1313
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
1414
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
1515
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
16-
use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
16+
use rustc_trait_selection::traits::misc::{
17+
type_allowed_to_implement_copy, CopyImplementationError,
18+
};
1719
use rustc_trait_selection::traits::predicate_for_trait_def;
1820
use rustc_trait_selection::traits::{self, ObligationCause};
1921
use std::collections::BTreeMap;
@@ -82,7 +84,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
8284
};
8385

8486
let cause = traits::ObligationCause::misc(span, impl_hir_id);
85-
match can_type_implement_copy(tcx, param_env, self_type, cause) {
87+
match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
8688
Ok(()) => {}
8789
Err(CopyImplementationError::InfrigingFields(fields)) => {
8890
let mut err = struct_span_err!(

compiler/rustc_lint/src/builtin.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
7272
use rustc_span::{BytePos, InnerSpan, Span};
7373
use rustc_target::abi::{Abi, VariantIdx};
7474
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
75-
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy, EvaluationResult};
75+
use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy};
7676

7777
use crate::nonstandard_style::{method_context, MethodLateContext};
7878

@@ -709,12 +709,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
709709

710710
// We shouldn't recommend implementing `Copy` on stateful things,
711711
// such as iterators.
712-
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) {
713-
if cx.tcx.infer_ctxt().build().type_implements_trait(iter_trait, [ty], param_env)
714-
== EvaluationResult::EvaluatedToOk
715-
{
716-
return;
717-
}
712+
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
713+
&& cx.tcx
714+
.infer_ctxt()
715+
.build()
716+
.type_implements_trait(iter_trait, [ty], param_env)
717+
.must_apply_modulo_regions()
718+
{
719+
return;
718720
}
719721

720722
// Default value of clippy::trivially_copy_pass_by_ref
@@ -726,7 +728,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
726728
}
727729
}
728730

729-
if can_type_implement_copy(
731+
if type_allowed_to_implement_copy(
730732
cx.tcx,
731733
param_env,
732734
ty,

compiler/rustc_trait_selection/src/traits/misc.rs

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
22
3-
use crate::infer::InferCtxtExt as _;
43
use crate::traits::{self, ObligationCause};
54

65
use rustc_hir as hir;
6+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
77
use rustc_infer::infer::TyCtxtInferExt;
88
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
99

@@ -16,14 +16,16 @@ pub enum CopyImplementationError<'tcx> {
1616
HasDestructor,
1717
}
1818

19-
pub fn can_type_implement_copy<'tcx>(
19+
/// Checks that the fields of the type (an ADT) all implement copy.
20+
///
21+
/// If fields don't implement copy, return an error containing a list of
22+
/// those violating fields. If it's not an ADT, returns `Err(NotAnAdt)`.
23+
pub fn type_allowed_to_implement_copy<'tcx>(
2024
tcx: TyCtxt<'tcx>,
2125
param_env: ty::ParamEnv<'tcx>,
2226
self_type: Ty<'tcx>,
2327
parent_cause: ObligationCause<'tcx>,
2428
) -> Result<(), CopyImplementationError<'tcx>> {
25-
// FIXME: (@jroesch) float this code up
26-
let infcx = tcx.infer_ctxt().build();
2729
let (adt, substs) = match self_type.kind() {
2830
// These types used to have a builtin impl.
2931
// Now libcore provides that impl.
@@ -42,9 +44,14 @@ pub fn can_type_implement_copy<'tcx>(
4244
_ => return Err(CopyImplementationError::NotAnAdt),
4345
};
4446

47+
let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span));
4548
let mut infringing = Vec::new();
4649
for variant in adt.variants() {
4750
for field in &variant.fields {
51+
// Do this per-field to get better error messages.
52+
let infcx = tcx.infer_ctxt().build();
53+
let ocx = traits::ObligationCtxt::new(&infcx);
54+
4855
let ty = field.ty(tcx, substs);
4956
if ty.references_error() {
5057
continue;
@@ -63,21 +70,36 @@ pub fn can_type_implement_copy<'tcx>(
6370
} else {
6471
ObligationCause::dummy_with_span(span)
6572
};
66-
match traits::fully_normalize(&infcx, cause, param_env, ty) {
67-
Ok(ty) => {
68-
if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
69-
infringing.push((field, ty));
70-
}
71-
}
72-
Err(errors) => {
73-
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
74-
}
75-
};
73+
74+
let ty = ocx.normalize(&cause, param_env, ty);
75+
let normalization_errors = ocx.select_where_possible();
76+
if !normalization_errors.is_empty() {
77+
// Don't report this as a field that doesn't implement Copy,
78+
// but instead just implement this as a field that isn't WF.
79+
infcx.err_ctxt().report_fulfillment_errors(&normalization_errors, None);
80+
continue;
81+
}
82+
83+
ocx.register_bound(cause, param_env, ty, copy_def_id);
84+
if !ocx.select_all_or_error().is_empty() {
85+
infringing.push((field, ty));
86+
}
87+
88+
let outlives_env = OutlivesEnvironment::new(param_env);
89+
infcx.process_registered_region_obligations(
90+
outlives_env.region_bound_pairs(),
91+
param_env,
92+
);
93+
if !infcx.resolve_regions(&outlives_env).is_empty() {
94+
infringing.push((field, ty));
95+
}
7696
}
7797
}
98+
7899
if !infringing.is_empty() {
79100
return Err(CopyImplementationError::InfrigingFields(infringing));
80101
}
102+
81103
if adt.has_dtor(tcx) {
82104
return Err(CopyImplementationError::HasDestructor);
83105
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0204]: the trait `Copy` may not be implemented for this type
2+
--> $DIR/copy-is-not-modulo-regions.rs:13:21
3+
|
4+
LL | struct Bar<'lt>(Foo<'lt>);
5+
| -------- this field does not implement `Copy`
6+
...
7+
LL | impl<'any> Copy for Bar<'any> {}
8+
| ^^^^^^^^^
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0204`.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// revisions: not_static yes_static
2+
//[yes_static] check-pass
3+
4+
#[derive(Clone)]
5+
struct Foo<'lt>(&'lt ());
6+
7+
impl Copy for Foo<'static> {}
8+
9+
#[derive(Clone)]
10+
struct Bar<'lt>(Foo<'lt>);
11+
12+
#[cfg(not_static)]
13+
impl<'any> Copy for Bar<'any> {}
14+
//[not_static]~^ the trait `Copy` may not be implemented for this type
15+
16+
#[cfg(yes_static)]
17+
impl<'any> Copy for Bar<'static> {}
18+
19+
fn main() {}

src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_span::symbol::kw;
2424
use rustc_span::{sym, Span};
2525
use rustc_target::spec::abi::Abi;
2626
use rustc_trait_selection::traits;
27-
use rustc_trait_selection::traits::misc::can_type_implement_copy;
27+
use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;
2828
use std::borrow::Cow;
2929

3030
declare_clippy_lint! {
@@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
200200
let sugg = |diag: &mut Diagnostic| {
201201
if let ty::Adt(def, ..) = ty.kind() {
202202
if let Some(span) = cx.tcx.hir().span_if_local(def.did()) {
203-
if can_type_implement_copy(
203+
if type_allowed_to_implement_copy(
204204
cx.tcx,
205205
cx.param_env,
206206
ty,

0 commit comments

Comments
 (0)