mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
cleanup error reporting and add ui
tests
This commit is contained in:
parent
e77cc9c983
commit
bd5fa7532d
@ -245,6 +245,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
debug!("report_region_errors: {} errors after preprocessing", errors.len());
|
||||
|
||||
for error in errors {
|
||||
debug!("report_region_errors: error = {:?}", error);
|
||||
match error.clone() {
|
||||
ConcreteFailure(origin, sub, sup) => {
|
||||
self.report_concrete_failure(origin, sub, sup).emit();
|
||||
@ -299,44 +300,58 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
let mut bound_failures = Vec::new();
|
||||
|
||||
for error in errors {
|
||||
// Check whether we can process this error into some other
|
||||
// form; if not, fall through.
|
||||
match *error {
|
||||
ConcreteFailure(ref origin, sub, sup) => {
|
||||
debug!("processing ConcreteFailure");
|
||||
match free_regions_from_same_fn(self.tcx, sub, sup) {
|
||||
Some(ref same_frs) => {
|
||||
if let SubregionOrigin::CompareImplMethodObligation { .. } = *origin {
|
||||
// When comparing an impl method against a
|
||||
// trait method, it is not helpful to suggest
|
||||
// changes to the impl method. This is
|
||||
// because the impl method signature is being
|
||||
// checked using the trait's environment, so
|
||||
// usually the changes we suggest would
|
||||
// actually have to be applied to the *trait*
|
||||
// method (and it's not clear that the trait
|
||||
// method is even under the user's control).
|
||||
} else if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
|
||||
origins.push(
|
||||
ProcessedErrorOrigin::ConcreteFailure(
|
||||
origin.clone(),
|
||||
sub,
|
||||
sup));
|
||||
append_to_same_regions(&mut same_regions, same_frs);
|
||||
}
|
||||
_ => {
|
||||
other_errors.push(error.clone());
|
||||
append_to_same_regions(&mut same_regions, &same_frs);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
SubSupConflict(ref var_origin, _, sub_r, _, sup_r) => {
|
||||
debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub_r, sup_r);
|
||||
match free_regions_from_same_fn(self.tcx, sub_r, sup_r) {
|
||||
Some(ref same_frs) => {
|
||||
SubSupConflict(ref var_origin, ref sub_origin, sub, ref sup_origin, sup) => {
|
||||
debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub, sup);
|
||||
if let SubregionOrigin::CompareImplMethodObligation { .. } = *sub_origin {
|
||||
// As above, when comparing an impl method
|
||||
// against a trait method, it is not helpful
|
||||
// to suggest changes to the impl method.
|
||||
} else if let SubregionOrigin::CompareImplMethodObligation { .. } = *sup_origin {
|
||||
// See above.
|
||||
} else if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
|
||||
origins.push(
|
||||
ProcessedErrorOrigin::VariableFailure(
|
||||
var_origin.clone()));
|
||||
append_to_same_regions(&mut same_regions, same_frs);
|
||||
}
|
||||
None => {
|
||||
other_errors.push(error.clone());
|
||||
}
|
||||
append_to_same_regions(&mut same_regions, &same_frs);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
GenericBoundFailure(ref origin, ref kind, region) => {
|
||||
bound_failures.push((origin.clone(), kind.clone(), region));
|
||||
continue;
|
||||
}
|
||||
ProcessedErrors(..) => {
|
||||
bug!("should not encounter a `ProcessedErrors` yet: {:?}", error)
|
||||
}
|
||||
}
|
||||
|
||||
// No changes to this error.
|
||||
other_errors.push(error.clone());
|
||||
}
|
||||
|
||||
// ok, let's pull together the errors, sorted in an order that
|
||||
@ -630,6 +645,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
format!("the associated type `{}`", p),
|
||||
};
|
||||
|
||||
if let SubregionOrigin::CompareImplMethodObligation {
|
||||
span, item_name, impl_item_def_id, trait_item_def_id
|
||||
} = origin {
|
||||
self.report_extra_impl_obligation(span,
|
||||
item_name,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id,
|
||||
&format!("`{}: {}`", bound_kind, sub))
|
||||
.emit();
|
||||
return;
|
||||
}
|
||||
|
||||
let mut err = match *sub {
|
||||
ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => {
|
||||
// Does the required lifetime have a nice name we can print?
|
||||
@ -947,6 +974,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
"");
|
||||
err
|
||||
}
|
||||
infer::CompareImplMethodObligation { span,
|
||||
item_name,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id } => {
|
||||
self.report_extra_impl_obligation(span,
|
||||
item_name,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id,
|
||||
&format!("`{}: {}`", sup, sub))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1792,6 +1829,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
"...so that references are valid when the destructor \
|
||||
runs");
|
||||
}
|
||||
infer::CompareImplMethodObligation { span, .. } => {
|
||||
err.span_note(
|
||||
span,
|
||||
"...so that the definition in impl matches the definition from the trait");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -360,6 +360,15 @@ pub enum SubregionOrigin<'tcx> {
|
||||
|
||||
// Region constraint arriving from destructor safety
|
||||
SafeDestructor(Span),
|
||||
|
||||
// Comparing the signature and requirements of an impl method against
|
||||
// the containing trait.
|
||||
CompareImplMethodObligation {
|
||||
span: Span,
|
||||
item_name: ast::Name,
|
||||
impl_item_def_id: DefId,
|
||||
trait_item_def_id: DefId,
|
||||
},
|
||||
}
|
||||
|
||||
/// Places that type/region parameters can appear.
|
||||
@ -1152,16 +1161,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
pub fn region_outlives_predicate(&self,
|
||||
span: Span,
|
||||
cause: &traits::ObligationCause<'tcx>,
|
||||
predicate: &ty::PolyRegionOutlivesPredicate<'tcx>)
|
||||
-> UnitResult<'tcx>
|
||||
{
|
||||
self.commit_if_ok(|snapshot| {
|
||||
let (ty::OutlivesPredicate(r_a, r_b), skol_map) =
|
||||
self.skolemize_late_bound_regions(predicate, snapshot);
|
||||
let origin = RelateRegionParamBound(span);
|
||||
let origin = SubregionOrigin::from_cause(cause, || RelateRegionParamBound(cause.span));
|
||||
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
|
||||
self.leak_check(false, span, &skol_map, snapshot)?;
|
||||
self.leak_check(false, cause.span, &skol_map, snapshot)?;
|
||||
Ok(self.pop_skolemized(skol_map, snapshot))
|
||||
})
|
||||
}
|
||||
@ -1792,6 +1801,30 @@ impl<'tcx> SubregionOrigin<'tcx> {
|
||||
AddrOf(a) => a,
|
||||
AutoBorrow(a) => a,
|
||||
SafeDestructor(a) => a,
|
||||
CompareImplMethodObligation { span, .. } => span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_cause<F>(cause: &traits::ObligationCause<'tcx>,
|
||||
default: F)
|
||||
-> Self
|
||||
where F: FnOnce() -> Self
|
||||
{
|
||||
match cause.code {
|
||||
traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) =>
|
||||
SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span),
|
||||
|
||||
traits::ObligationCauseCode::CompareImplMethodObligation { item_name,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id } =>
|
||||
SubregionOrigin::CompareImplMethodObligation {
|
||||
span: cause.span,
|
||||
item_name: item_name,
|
||||
impl_item_def_id: impl_item_def_id,
|
||||
trait_item_def_id: trait_item_def_id,
|
||||
},
|
||||
|
||||
_ => default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ use util::nodemap::{FnvHashMap, FnvHashSet};
|
||||
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use syntax::ast;
|
||||
use syntax_pos::Span;
|
||||
use errors::DiagnosticBuilder;
|
||||
|
||||
@ -417,6 +418,32 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
self.report_overflow_error(&cycle[0], false);
|
||||
}
|
||||
|
||||
pub fn report_extra_impl_obligation(&self,
|
||||
error_span: Span,
|
||||
item_name: ast::Name,
|
||||
_impl_item_def_id: DefId,
|
||||
trait_item_def_id: DefId,
|
||||
requirement: &fmt::Display)
|
||||
-> DiagnosticBuilder<'tcx>
|
||||
{
|
||||
let mut err =
|
||||
struct_span_err!(self.tcx.sess,
|
||||
error_span,
|
||||
E0276,
|
||||
"impl has stricter requirements than trait");
|
||||
|
||||
if let Some(trait_item_span) = self.tcx.map.span_if_local(trait_item_def_id) {
|
||||
err.span_label(trait_item_span,
|
||||
&format!("definition of `{}` from trait", item_name));
|
||||
}
|
||||
|
||||
err.span_label(
|
||||
error_span,
|
||||
&format!("impl has extra requirement {}", requirement));
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
pub fn report_selection_error(&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
error: &SelectionError<'tcx>)
|
||||
@ -424,12 +451,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
let span = obligation.cause.span;
|
||||
let mut err = match *error {
|
||||
SelectionError::Unimplemented => {
|
||||
if let ObligationCauseCode::CompareImplMethodObligation = obligation.cause.code {
|
||||
span_err!(
|
||||
self.tcx.sess, span, E0276,
|
||||
"the requirement `{}` appears on the impl \
|
||||
method but not on the corresponding trait method",
|
||||
obligation.predicate);
|
||||
if let ObligationCauseCode::CompareImplMethodObligation {
|
||||
item_name, impl_item_def_id, trait_item_def_id
|
||||
} = obligation.cause.code {
|
||||
self.report_extra_impl_obligation(
|
||||
span,
|
||||
item_name,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id,
|
||||
&format!("`{}`", obligation.predicate))
|
||||
.emit();
|
||||
return;
|
||||
} else {
|
||||
match obligation.predicate {
|
||||
@ -492,7 +523,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
|
||||
ty::Predicate::RegionOutlives(ref predicate) => {
|
||||
let predicate = self.resolve_type_vars_if_possible(predicate);
|
||||
let err = self.region_outlives_predicate(span,
|
||||
let err = self.region_outlives_predicate(&obligation.cause,
|
||||
&predicate).err().unwrap();
|
||||
struct_span_err!(self.tcx.sess, span, E0279,
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
@ -886,7 +917,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
&parent_predicate,
|
||||
&data.parent_code);
|
||||
}
|
||||
ObligationCauseCode::CompareImplMethodObligation => {
|
||||
ObligationCauseCode::CompareImplMethodObligation { .. } => {
|
||||
err.note(
|
||||
&format!("the requirement `{}` appears on the impl method \
|
||||
but not on the corresponding trait method",
|
||||
|
@ -526,7 +526,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref binder) => {
|
||||
match selcx.infcx().region_outlives_predicate(obligation.cause.span, binder) {
|
||||
match selcx.infcx().region_outlives_predicate(&obligation.cause, binder) {
|
||||
Ok(()) => Ok(Some(Vec::new())),
|
||||
Err(_) => Err(CodeSelectionError(Unimplemented)),
|
||||
}
|
||||
|
@ -138,7 +138,11 @@ pub enum ObligationCauseCode<'tcx> {
|
||||
|
||||
ImplDerivedObligation(DerivedObligationCause<'tcx>),
|
||||
|
||||
CompareImplMethodObligation,
|
||||
CompareImplMethodObligation {
|
||||
item_name: ast::Name,
|
||||
impl_item_def_id: DefId,
|
||||
trait_item_def_id: DefId
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -195,8 +195,14 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
|
||||
super::ImplDerivedObligation(ref cause) => {
|
||||
tcx.lift(cause).map(super::ImplDerivedObligation)
|
||||
}
|
||||
super::CompareImplMethodObligation => {
|
||||
Some(super::CompareImplMethodObligation)
|
||||
super::CompareImplMethodObligation { item_name,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id } => {
|
||||
Some(super::CompareImplMethodObligation {
|
||||
item_name: item_name,
|
||||
impl_item_def_id: impl_item_def_id,
|
||||
trait_item_def_id: trait_item_def_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -459,7 +465,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
|
||||
super::FieldSized |
|
||||
super::ConstSized |
|
||||
super::SharedStatic |
|
||||
super::CompareImplMethodObligation => self.clone(),
|
||||
super::CompareImplMethodObligation { .. } => self.clone(),
|
||||
|
||||
super::ProjectionWf(proj) => super::ProjectionWf(proj.fold_with(folder)),
|
||||
super::ReferenceOutlivesReferent(ty) => {
|
||||
@ -492,7 +498,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
|
||||
super::FieldSized |
|
||||
super::ConstSized |
|
||||
super::SharedStatic |
|
||||
super::CompareImplMethodObligation => false,
|
||||
super::CompareImplMethodObligation { .. } => false,
|
||||
|
||||
super::ProjectionWf(proj) => proj.visit_with(visitor),
|
||||
super::ReferenceOutlivesReferent(ty) => ty.visit_with(visitor),
|
||||
|
@ -363,7 +363,11 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
let cause = traits::ObligationCause {
|
||||
span: impl_m_span,
|
||||
body_id: impl_m_body_id,
|
||||
code: traits::ObligationCauseCode::CompareImplMethodObligation,
|
||||
code: traits::ObligationCauseCode::CompareImplMethodObligation {
|
||||
item_name: impl_m.name,
|
||||
impl_item_def_id: impl_m.def_id,
|
||||
trait_item_def_id: trait_m.def_id,
|
||||
},
|
||||
};
|
||||
|
||||
fulfillment_cx.borrow_mut().register_predicate_obligation(
|
||||
|
@ -356,7 +356,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
debug!("visit_region_obligations: r_o={:?} cause={:?}",
|
||||
r_o, r_o.cause);
|
||||
let sup_type = self.resolve_type(r_o.sup_type);
|
||||
let origin = self.code_to_origin(r_o.cause.span, sup_type, &r_o.cause.code);
|
||||
let origin = self.code_to_origin(&r_o.cause, sup_type);
|
||||
self.type_must_outlive(origin, sup_type, r_o.sub_region);
|
||||
}
|
||||
|
||||
@ -366,16 +366,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
fn code_to_origin(&self,
|
||||
span: Span,
|
||||
sup_type: Ty<'tcx>,
|
||||
code: &traits::ObligationCauseCode<'tcx>)
|
||||
cause: &traits::ObligationCause<'tcx>,
|
||||
sup_type: Ty<'tcx>)
|
||||
-> SubregionOrigin<'tcx> {
|
||||
match *code {
|
||||
traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) =>
|
||||
infer::ReferenceOutlivesReferent(ref_type, span),
|
||||
_ =>
|
||||
infer::RelateParamBound(span, sup_type),
|
||||
}
|
||||
SubregionOrigin::from_cause(cause, || infer::RelateParamBound(cause.span, sup_type))
|
||||
}
|
||||
|
||||
/// This method populates the region map's `free_region_map`. It walks over the transformed
|
||||
|
@ -18,8 +18,7 @@ trait Master<'a, T: ?Sized, U> {
|
||||
|
||||
// `U::Item: 'a` does not imply that `U: 'a`
|
||||
impl<'a, U: Iterator> Master<'a, U::Item, U> for () {
|
||||
fn foo() where U: 'a { }
|
||||
//~^ ERROR parameter type `V` may not live long enough
|
||||
fn foo() where U: 'a { } //~ ERROR E0276
|
||||
}
|
||||
|
||||
fn main() {
|
11
src/test/ui/compare-method/proj-outlives-region.stderr
Normal file
11
src/test/ui/compare-method/proj-outlives-region.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0276]: impl has stricter requirements than trait
|
||||
--> $DIR/proj-outlives-region.rs:21:5
|
||||
|
|
||||
16 | fn foo() where T: 'a;
|
||||
| --------------------- definition of `foo` from trait
|
||||
...
|
||||
21 | fn foo() where U: 'a { } //~ ERROR E0276
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
11
src/test/ui/compare-method/region-unrelated.stderr
Normal file
11
src/test/ui/compare-method/region-unrelated.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0276]: impl has stricter requirements than trait
|
||||
--> $DIR/region-unrelated.rs:21:5
|
||||
|
|
||||
16 | fn foo() where T: 'a;
|
||||
| --------------------- definition of `foo` from trait
|
||||
...
|
||||
21 | fn foo() where V: 'a { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `V: 'a`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
27
src/test/ui/compare-method/region.rs
Normal file
27
src/test/ui/compare-method/region.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
// Test that we elaborate `Type: 'region` constraints and infer various important things.
|
||||
|
||||
trait Master<'a, 'b> {
|
||||
fn foo();
|
||||
}
|
||||
|
||||
// `U: 'a` does not imply `V: 'a`
|
||||
impl<'a, 'b> Master<'a, 'b> for () {
|
||||
fn foo() where 'a: 'b { }
|
||||
//~^ ERROR parameter type `V` may not live long enough
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
11
src/test/ui/compare-method/region.stderr
Normal file
11
src/test/ui/compare-method/region.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0276]: impl has stricter requirements than trait
|
||||
--> $DIR/region.rs:21:5
|
||||
|
|
||||
16 | fn foo();
|
||||
| --------- definition of `foo` from trait
|
||||
...
|
||||
21 | fn foo() where 'a: 'b { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'a: 'b`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -36,12 +36,12 @@ while [[ "$1" != "" ]]; do
|
||||
STDOUT_NAME="${1/%.rs/.stdout}"
|
||||
shift
|
||||
if [ -f $BUILD_DIR/$STDOUT_NAME ] && \
|
||||
! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME > /dev/null); then
|
||||
! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then
|
||||
echo updating $MYDIR/$STDOUT_NAME
|
||||
cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME
|
||||
fi
|
||||
if [ -f $BUILD_DIR/$STDERR_NAME ] && \
|
||||
! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME > /dev/null); then
|
||||
! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then
|
||||
echo updating $MYDIR/$STDERR_NAME
|
||||
cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user