1
Fork 0

Auto merge of #50536 - leodasvacas:report-fullfilment-errors-in-copy-derive, r=estebank

Better error reporting in Copy derive

In Copy derive, report all fulfillment erros when present and do not report errors for types tainted with `TyErr`. Also report all fields which are not Copy rather than just the first.

Also refactored `fn fully_normalize`, removing the not very useful helper function along with a FIXME to the closed issue #26721 that looks out of context now.

Fixes #50480

r? @estebank
This commit is contained in:
bors 2018-05-12 22:48:16 +00:00
commit 6fb34bdfc6
8 changed files with 84 additions and 60 deletions

View file

@ -764,7 +764,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
"unions with `Drop` implementations are unstable"); "unions with `Drop` implementations are unstable");
} else { } else {
let param_env = self.tcx.param_env(def_id); let param_env = self.tcx.param_env(def_id);
if !param_env.can_type_implement_copy(self.tcx, ty, item.span).is_ok() { if !param_env.can_type_implement_copy(self.tcx, ty).is_ok() {
emit_feature_err(&self.tcx.sess.parse_sess, emit_feature_err(&self.tcx.sess.parse_sess,
"untagged_unions", item.span, GateIssue::Language, "untagged_unions", item.span, GateIssue::Language,
"unions with non-`Copy` fields are unstable"); "unions with non-`Copy` fields are unstable");

View file

@ -674,7 +674,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// we move over to lazy normalization *anyway*. // we move over to lazy normalization *anyway*.
let fulfill_cx = FulfillmentContext::new_ignoring_regions(); let fulfill_cx = FulfillmentContext::new_ignoring_regions();
let predicates = match fully_normalize_with_fulfillcx( let predicates = match fully_normalize(
&infcx, &infcx,
fulfill_cx, fulfill_cx,
cause, cause,
@ -734,31 +734,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}) })
} }
pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, pub fn fully_normalize<'a, 'gcx, 'tcx, T>(
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: &T)
-> Result<T, Vec<FulfillmentError<'tcx>>>
where T : TypeFoldable<'tcx>
{
// FIXME (@jroesch) ISSUE 26721
// I'm not sure if this is a bug or not, needs further investigation.
// It appears that by reusing the fulfillment_cx here we incur more
// obligations and later trip an assertion on regionck.rs line 337.
//
// The two possibilities I see is:
// - normalization is not actually fully happening and we
// have a bug else where
// - we are adding a duplicate bound into the list causing
// its size to change.
//
// I think we should probably land this refactor and then come
// back to this is a follow-up patch.
let fulfillcx = FulfillmentContext::new();
fully_normalize_with_fulfillcx(infcx, fulfillcx, cause, param_env, value)
}
pub fn fully_normalize_with_fulfillcx<'a, 'gcx, 'tcx, T>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>, infcx: &InferCtxt<'a, 'gcx, 'tcx>,
mut fulfill_cx: FulfillmentContext<'tcx>, mut fulfill_cx: FulfillmentContext<'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
@ -779,13 +755,7 @@ pub fn fully_normalize_with_fulfillcx<'a, 'gcx, 'tcx, T>(
} }
debug!("fully_normalize: select_all_or_error start"); debug!("fully_normalize: select_all_or_error start");
match fulfill_cx.select_all_or_error(infcx) { fulfill_cx.select_all_or_error(infcx)?;
Ok(()) => { }
Err(e) => {
debug!("fully_normalize: error={:?}", e);
return Err(e);
}
}
debug!("fully_normalize: select_all_or_error complete"); debug!("fully_normalize: select_all_or_error complete");
let resolved_value = infcx.resolve_type_vars_if_possible(&normalized_value); let resolved_value = infcx.resolve_type_vars_if_possible(&normalized_value);
debug!("fully_normalize: resolved_value={:?}", resolved_value); debug!("fully_normalize: resolved_value={:?}", resolved_value);

View file

@ -196,6 +196,7 @@ pub(super) fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// that this always succeeds. // that this always succeeds.
let impl1_trait_ref = let impl1_trait_ref =
match traits::fully_normalize(&infcx, match traits::fully_normalize(&infcx,
FulfillmentContext::new(),
ObligationCause::dummy(), ObligationCause::dummy(),
penv, penv,
&impl1_trait_ref) { &impl1_trait_ref) {

View file

@ -16,7 +16,7 @@ use hir::map::{DefPathData, Node};
use hir; use hir;
use ich::NodeIdHashingMode; use ich::NodeIdHashingMode;
use middle::const_val::ConstVal; use middle::const_val::ConstVal;
use traits; use traits::{self, ObligationCause};
use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::{self, Ty, TyCtxt, TypeFoldable};
use ty::fold::TypeVisitor; use ty::fold::TypeVisitor;
use ty::subst::UnpackedKind; use ty::subst::UnpackedKind;
@ -166,9 +166,9 @@ impl IntTypeExt for attr::IntType {
} }
#[derive(Copy, Clone)] #[derive(Clone)]
pub enum CopyImplementationError<'tcx> { pub enum CopyImplementationError<'tcx> {
InfrigingField(&'tcx ty::FieldDef), InfrigingFields(Vec<&'tcx ty::FieldDef>),
NotAnAdt, NotAnAdt,
HasDestructor, HasDestructor,
} }
@ -191,7 +191,7 @@ pub enum Representability {
impl<'tcx> ty::ParamEnv<'tcx> { impl<'tcx> ty::ParamEnv<'tcx> {
pub fn can_type_implement_copy<'a>(self, pub fn can_type_implement_copy<'a>(self,
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
self_type: Ty<'tcx>, span: Span) self_type: Ty<'tcx>)
-> Result<(), CopyImplementationError<'tcx>> { -> Result<(), CopyImplementationError<'tcx>> {
// FIXME: (@jroesch) float this code up // FIXME: (@jroesch) float this code up
tcx.infer_ctxt().enter(|infcx| { tcx.infer_ctxt().enter(|infcx| {
@ -207,22 +207,29 @@ impl<'tcx> ty::ParamEnv<'tcx> {
_ => return Err(CopyImplementationError::NotAnAdt), _ => return Err(CopyImplementationError::NotAnAdt),
}; };
let field_implements_copy = |field: &ty::FieldDef| { let mut infringing = Vec::new();
let cause = traits::ObligationCause::dummy();
match traits::fully_normalize(&infcx, cause, self, &field.ty(tcx, substs)) {
Ok(ty) => !infcx.type_moves_by_default(self, ty, span),
Err(..) => false,
}
};
for variant in &adt.variants { for variant in &adt.variants {
for field in &variant.fields { for field in &variant.fields {
if !field_implements_copy(field) { let span = tcx.def_span(field.did);
return Err(CopyImplementationError::InfrigingField(field)); let ty = field.ty(tcx, substs);
if ty.references_error() {
continue;
} }
let cause = ObligationCause { span, ..ObligationCause::dummy() };
let ctx = traits::FulfillmentContext::new();
match traits::fully_normalize(&infcx, ctx, cause, self, &ty) {
Ok(ty) => if infcx.type_moves_by_default(self, ty, span) {
infringing.push(field);
}
Err(errors) => {
infcx.report_fulfillment_errors(&errors, None, false);
}
};
} }
} }
if !infringing.is_empty() {
return Err(CopyImplementationError::InfrigingFields(infringing));
}
if adt.has_dtor(tcx) { if adt.has_dtor(tcx) {
return Err(CopyImplementationError::HasDestructor); return Err(CopyImplementationError::HasDestructor);
} }

View file

@ -541,7 +541,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
if !ty.moves_by_default(cx.tcx, param_env, item.span) { if !ty.moves_by_default(cx.tcx, param_env, item.span) {
return; return;
} }
if param_env.can_type_implement_copy(cx.tcx, ty, item.span).is_ok() { if param_env.can_type_implement_copy(cx.tcx, ty).is_ok() {
cx.span_lint(MISSING_COPY_IMPLEMENTATIONS, cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
item.span, item.span,
"type could implement `Copy`; consider adding `impl \ "type could implement `Copy`; consider adding `impl \

View file

@ -111,9 +111,9 @@ fn visit_implementation_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
debug!("visit_implementation_of_copy: self_type={:?} (free)", debug!("visit_implementation_of_copy: self_type={:?} (free)",
self_type); self_type);
match param_env.can_type_implement_copy(tcx, self_type, span) { match param_env.can_type_implement_copy(tcx, self_type) {
Ok(()) => {} Ok(()) => {}
Err(CopyImplementationError::InfrigingField(field)) => { Err(CopyImplementationError::InfrigingFields(fields)) => {
let item = tcx.hir.expect_item(impl_node_id); let item = tcx.hir.expect_item(impl_node_id);
let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node { let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
tr.path.span tr.path.span
@ -121,14 +121,14 @@ fn visit_implementation_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
span span
}; };
struct_span_err!(tcx.sess, let mut err = struct_span_err!(tcx.sess,
span, span,
E0204, E0204,
"the trait `Copy` may not be implemented for this type") "the trait `Copy` may not be implemented for this type");
.span_label( for span in fields.iter().map(|f| tcx.def_span(f.did)) {
tcx.def_span(field.did), err.span_label(span, "this field does not implement `Copy`");
"this field does not implement `Copy`") }
.emit() err.emit()
} }
Err(CopyImplementationError::NotAnAdt) => { Err(CopyImplementationError::NotAnAdt) => {
let item = tcx.hir.expect_item(impl_node_id); let item = tcx.hir.expect_item(impl_node_id);

View file

@ -0,0 +1,17 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[derive(Clone, Copy)]
//~^ ERROR the trait `Copy` may not be implemented for this type
struct Foo(NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
//~^ ERROR cannot find type `NotDefined` in this scope
//~| ERROR the trait bound `i32: std::iter::Iterator` is not satisfied
fn main() {}

View file

@ -0,0 +1,29 @@
error[E0412]: cannot find type `NotDefined` in this scope
--> $DIR/issue-50480.rs:13:12
|
LL | struct Foo(NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| ^^^^^^^^^^ not found in this scope
error[E0277]: the trait bound `i32: std::iter::Iterator` is not satisfied
--> $DIR/issue-50480.rs:13:24
|
LL | struct Foo(NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| ^^^^^^^^^^^^^^^^^^^^^^^ `i32` is not an iterator; maybe try calling `.iter()` or a similar method
|
= help: the trait `std::iter::Iterator` is not implemented for `i32`
error[E0204]: the trait `Copy` may not be implemented for this type
--> $DIR/issue-50480.rs:11:17
|
LL | #[derive(Clone, Copy)]
| ^^^^
LL | //~^ ERROR the trait `Copy` may not be implemented for this type
LL | struct Foo(NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| -------- ------ this field does not implement `Copy`
| |
| this field does not implement `Copy`
error: aborting due to 3 previous errors
Some errors occurred: E0204, E0277, E0412.
For more information about an error, try `rustc --explain E0204`.