Check ADT fields for copy implementations considering regions
This commit is contained in:
parent
0b90256ada
commit
8cf7f40a89
6 changed files with 83 additions and 26 deletions
|
@ -13,7 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
|
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
|
||||||
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
|
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::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::predicate_for_trait_def;
|
||||||
use rustc_trait_selection::traits::{self, ObligationCause};
|
use rustc_trait_selection::traits::{self, ObligationCause};
|
||||||
use std::collections::BTreeMap;
|
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);
|
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(()) => {}
|
Ok(()) => {}
|
||||||
Err(CopyImplementationError::InfrigingFields(fields)) => {
|
Err(CopyImplementationError::InfrigingFields(fields)) => {
|
||||||
let mut err = struct_span_err!(
|
let mut err = struct_span_err!(
|
||||||
|
|
|
@ -72,7 +72,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::{BytePos, InnerSpan, Span};
|
use rustc_span::{BytePos, InnerSpan, Span};
|
||||||
use rustc_target::abi::{Abi, VariantIdx};
|
use rustc_target::abi::{Abi, VariantIdx};
|
||||||
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
|
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};
|
use crate::nonstandard_style::{method_context, MethodLateContext};
|
||||||
|
|
||||||
|
@ -709,13 +709,15 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||||
|
|
||||||
// We shouldn't recommend implementing `Copy` on stateful things,
|
// We shouldn't recommend implementing `Copy` on stateful things,
|
||||||
// such as iterators.
|
// such as iterators.
|
||||||
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) {
|
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)
|
&& cx.tcx
|
||||||
== EvaluationResult::EvaluatedToOk
|
.infer_ctxt()
|
||||||
|
.build()
|
||||||
|
.type_implements_trait(iter_trait, [ty], param_env)
|
||||||
|
.must_apply_modulo_regions()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Default value of clippy::trivially_copy_pass_by_ref
|
// Default value of clippy::trivially_copy_pass_by_ref
|
||||||
const MAX_SIZE: u64 = 256;
|
const MAX_SIZE: u64 = 256;
|
||||||
|
@ -726,7 +728,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_type_implement_copy(
|
if type_allowed_to_implement_copy(
|
||||||
cx.tcx,
|
cx.tcx,
|
||||||
param_env,
|
param_env,
|
||||||
ty,
|
ty,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
|
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
|
||||||
|
|
||||||
use crate::infer::InferCtxtExt as _;
|
|
||||||
use crate::traits::{self, ObligationCause};
|
use crate::traits::{self, ObligationCause};
|
||||||
|
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
|
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
|
||||||
|
|
||||||
|
@ -16,14 +16,16 @@ pub enum CopyImplementationError<'tcx> {
|
||||||
HasDestructor,
|
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>,
|
tcx: TyCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
self_type: Ty<'tcx>,
|
self_type: Ty<'tcx>,
|
||||||
parent_cause: ObligationCause<'tcx>,
|
parent_cause: ObligationCause<'tcx>,
|
||||||
) -> Result<(), CopyImplementationError<'tcx>> {
|
) -> Result<(), CopyImplementationError<'tcx>> {
|
||||||
// FIXME: (@jroesch) float this code up
|
|
||||||
let infcx = tcx.infer_ctxt().build();
|
|
||||||
let (adt, substs) = match self_type.kind() {
|
let (adt, substs) = match self_type.kind() {
|
||||||
// These types used to have a builtin impl.
|
// These types used to have a builtin impl.
|
||||||
// Now libcore provides that impl.
|
// Now libcore provides that impl.
|
||||||
|
@ -42,9 +44,14 @@ pub fn can_type_implement_copy<'tcx>(
|
||||||
_ => return Err(CopyImplementationError::NotAnAdt),
|
_ => return Err(CopyImplementationError::NotAnAdt),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span));
|
||||||
let mut infringing = Vec::new();
|
let mut infringing = Vec::new();
|
||||||
for variant in adt.variants() {
|
for variant in adt.variants() {
|
||||||
for field in &variant.fields {
|
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);
|
let ty = field.ty(tcx, substs);
|
||||||
if ty.references_error() {
|
if ty.references_error() {
|
||||||
continue;
|
continue;
|
||||||
|
@ -63,21 +70,36 @@ pub fn can_type_implement_copy<'tcx>(
|
||||||
} else {
|
} else {
|
||||||
ObligationCause::dummy_with_span(span)
|
ObligationCause::dummy_with_span(span)
|
||||||
};
|
};
|
||||||
match traits::fully_normalize(&infcx, cause, param_env, ty) {
|
|
||||||
Ok(ty) => {
|
let ty = ocx.normalize(&cause, param_env, ty);
|
||||||
if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
|
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));
|
infringing.push((field, ty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(errors) => {
|
|
||||||
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !infringing.is_empty() {
|
if !infringing.is_empty() {
|
||||||
return Err(CopyImplementationError::InfrigingFields(infringing));
|
return Err(CopyImplementationError::InfrigingFields(infringing));
|
||||||
}
|
}
|
||||||
|
|
||||||
if adt.has_dtor(tcx) {
|
if adt.has_dtor(tcx) {
|
||||||
return Err(CopyImplementationError::HasDestructor);
|
return Err(CopyImplementationError::HasDestructor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`.
|
19
src/test/ui/traits/copy-is-not-modulo-regions.rs
Normal file
19
src/test/ui/traits/copy-is-not-modulo-regions.rs
Normal file
|
@ -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() {}
|
|
@ -24,7 +24,7 @@ use rustc_span::symbol::kw;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use rustc_trait_selection::traits;
|
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;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||||
let sugg = |diag: &mut Diagnostic| {
|
let sugg = |diag: &mut Diagnostic| {
|
||||||
if let ty::Adt(def, ..) = ty.kind() {
|
if let ty::Adt(def, ..) = ty.kind() {
|
||||||
if let Some(span) = cx.tcx.hir().span_if_local(def.did()) {
|
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.tcx,
|
||||||
cx.param_env,
|
cx.param_env,
|
||||||
ty,
|
ty,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue