Add note about fallback to !: !Trait error

This commit is contained in:
Andrew Cann 2018-03-12 12:15:06 +08:00
parent 5a6b781266
commit 5b32211e62
12 changed files with 107 additions and 26 deletions

View file

@ -151,7 +151,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
// get solved *here*.
match fulfill_cx.select_all_or_error(self) {
Ok(()) => (),
Err(errors) => self.report_fulfillment_errors(&errors, None),
Err(errors) => self.report_fulfillment_errors(&errors, None, false),
}
implied_bounds

View file

@ -47,7 +47,8 @@ use syntax_pos::{DUMMY_SP, Span};
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
pub fn report_fulfillment_errors(&self,
errors: &Vec<FulfillmentError<'tcx>>,
body_id: Option<hir::BodyId>) {
body_id: Option<hir::BodyId>,
fallback_has_occurred: bool) {
#[derive(Debug)]
struct ErrorDescriptor<'tcx> {
predicate: ty::Predicate<'tcx>,
@ -107,7 +108,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
for (error, suppressed) in errors.iter().zip(is_suppressed) {
if !suppressed {
self.report_fulfillment_error(error, body_id);
self.report_fulfillment_error(error, body_id, fallback_has_occurred);
}
}
}
@ -151,11 +152,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>,
body_id: Option<hir::BodyId>) {
body_id: Option<hir::BodyId>,
fallback_has_occurred: bool) {
debug!("report_fulfillment_errors({:?})", error);
match error.code {
FulfillmentErrorCode::CodeSelectionError(ref e) => {
self.report_selection_error(&error.obligation, e);
self.report_selection_error(&error.obligation, e, fallback_has_occurred);
}
FulfillmentErrorCode::CodeProjectionError(ref e) => {
self.report_projection_error(&error.obligation, e);
@ -533,9 +535,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
pub fn report_selection_error(&self,
obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>)
error: &SelectionError<'tcx>,
fallback_has_occurred: bool)
{
let span = obligation.cause.span;
let _ = fallback_has_occurred;
let mut err = match *error {
SelectionError::Unimplemented => {
@ -619,6 +623,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.report_similar_impl_candidates(impl_candidates, &mut err);
}
// If this error is due to `!: !Trait` but `(): Trait` then add a note
// about the fallback behaviour change.
if trait_predicate.skip_binder().self_ty().is_never() {
let predicate = trait_predicate.map_bound(|mut trait_pred| {
{
let trait_ref = &mut trait_pred.trait_ref;
let never_substs = trait_ref.substs;
let mut unit_substs = Vec::with_capacity(never_substs.len());
unit_substs.push(self.tcx.mk_nil().into());
unit_substs.extend(&never_substs[1..]);
trait_ref.substs = self.tcx.intern_substs(&unit_substs);
}
trait_pred
});
let unit_obligation = Obligation {
cause: obligation.cause.clone(),
param_env: obligation.param_env,
recursion_depth: obligation.recursion_depth,
predicate,
};
let mut selcx = SelectionContext::new(self);
if let Ok(Some(..)) = selcx.select(&unit_obligation) {
err.note("the trait is implemented for `()`. \
Possibly this error has been caused by changes to \
Rust's type-inference algorithm \
(see: https://github.com/rust-lang/rust/issues/48950 \
for more info). Consider whether you meant to use the \
type `()` here instead.");
}
}
err
}

View file

@ -580,7 +580,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
) {
Ok(predicates) => predicates,
Err(errors) => {
infcx.report_fulfillment_errors(&errors, None);
infcx.report_fulfillment_errors(&errors, None, false);
// An unnormalized env is better than nothing.
return elaborated_env;
}

View file

@ -1246,7 +1246,7 @@ impl MirPass for QualifyAndPromoteConstants {
tcx.require_lang_item(lang_items::SyncTraitLangItem),
cause);
if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
infcx.report_fulfillment_errors(&err, None);
infcx.report_fulfillment_errors(&err, None, false);
}
});
}

View file

@ -571,7 +571,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// Object safety violations or miscellaneous.
Err(err) => {
self.report_selection_error(&obligation, &err);
self.report_selection_error(&obligation, &err, false);
// Treat this like an obligation and follow through
// with the unsizing - the lack of a coercion should
// be silent, as it causes a type mismatch later.

View file

@ -334,7 +334,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Check that all obligations are satisfied by the implementation's
// version.
if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) {
infcx.report_fulfillment_errors(errors, None);
infcx.report_fulfillment_errors(errors, None, false);
return Err(ErrorReported);
}
@ -839,7 +839,7 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Check that all obligations are satisfied by the implementation's
// version.
if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) {
infcx.report_fulfillment_errors(errors, None);
infcx.report_fulfillment_errors(errors, None, false);
return;
}

View file

@ -112,7 +112,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) {
// this could be reached when we get lazy normalization
infcx.report_fulfillment_errors(errors, None);
infcx.report_fulfillment_errors(errors, None, false);
return Err(ErrorReported);
}

View file

@ -873,11 +873,12 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
};
// All type checking constraints were added, try to fallback unsolved variables.
fcx.select_obligations_where_possible();
fcx.select_obligations_where_possible(false);
let mut fallback_has_occurred = false;
for ty in &fcx.unsolved_variables() {
fcx.fallback_if_possible(ty);
fallback_has_occurred |= fcx.fallback_if_possible(ty);
}
fcx.select_obligations_where_possible();
fcx.select_obligations_where_possible(fallback_has_occurred);
// Even though coercion casts provide type hints, we check casts after fallback for
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
@ -1837,7 +1838,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// possible. This can help substantially when there are
// indirect dependencies that don't seem worth tracking
// precisely.
self.select_obligations_where_possible();
self.select_obligations_where_possible(false);
ty = self.resolve_type_vars_if_possible(&ty);
debug!("resolve_type_vars_with_obligations: ty={:?}", ty);
@ -2154,7 +2155,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn resolve_generator_interiors(&self, def_id: DefId) {
let mut generators = self.deferred_generator_interiors.borrow_mut();
for (body_id, interior) in generators.drain(..) {
self.select_obligations_where_possible();
self.select_obligations_where_possible(false);
generator_interior::resolve_interior(self, def_id, body_id, interior);
}
}
@ -2164,7 +2165,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// unconstrained floats with f64.
// Fallback becomes very dubious if we have encountered type-checking errors.
// In that case, fallback to TyError.
fn fallback_if_possible(&self, ty: Ty<'tcx>) {
// The return value indicates whether fallback has occured.
fn fallback_if_possible(&self, ty: Ty<'tcx>) -> bool {
use rustc::ty::error::UnconstrainedNumeric::Neither;
use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
@ -2174,24 +2176,27 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
UnconstrainedInt => self.tcx.types.i32,
UnconstrainedFloat => self.tcx.types.f64,
Neither if self.type_var_diverges(ty) => self.tcx.types.never,
Neither => return
Neither => return false,
};
debug!("default_type_parameters: defaulting `{:?}` to `{:?}`", ty, fallback);
self.demand_eqtype(syntax_pos::DUMMY_SP, ty, fallback);
true
}
fn select_all_obligations_or_error(&self) {
debug!("select_all_obligations_or_error");
if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
self.report_fulfillment_errors(&errors, self.inh.body_id);
self.report_fulfillment_errors(&errors, self.inh.body_id, false);
}
}
/// Select as many obligations as we can at present.
fn select_obligations_where_possible(&self) {
fn select_obligations_where_possible(&self, fallback_has_occurred: bool) {
match self.fulfillment_cx.borrow_mut().select_where_possible(self) {
Ok(()) => { }
Err(errors) => { self.report_fulfillment_errors(&errors, self.inh.body_id); }
Err(errors) => {
self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
},
}
}
@ -2595,7 +2600,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// an "opportunistic" vtable resolution of any trait bounds on
// the call. This helps coercions.
if check_closures {
self.select_obligations_where_possible();
self.select_obligations_where_possible(false);
}
// For variadic functions, we don't have a declared type for all of

View file

@ -479,7 +479,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
match method {
Some(ok) => {
let method = self.register_infer_ok_obligations(ok);
self.select_obligations_where_possible();
self.select_obligations_where_possible(false);
Ok(method)
}

View file

@ -386,7 +386,7 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Check that all transitive obligations are satisfied.
if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
infcx.report_fulfillment_errors(&errors, None);
infcx.report_fulfillment_errors(&errors, None, false);
}
// Finally, resolve all regions.

View file

@ -174,7 +174,7 @@ fn require_same_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
match fulfill_cx.select_all_or_error(infcx) {
Ok(()) => true,
Err(errors) => {
infcx.report_fulfillment_errors(&errors, None);
infcx.report_fulfillment_errors(&errors, None, false);
false
}
}

View file

@ -0,0 +1,41 @@
// 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(unused)]
trait Deserialize: Sized {
fn deserialize() -> Result<Self, String>;
}
impl Deserialize for () {
fn deserialize() -> Result<(), String> {
Ok(())
}
}
trait ImplementedForUnitButNotNever {}
impl ImplementedForUnitButNotNever for () {}
fn foo<T: ImplementedForUnitButNotNever>(_t: T) {}
//~^ NOTE required by `foo`
fn smeg() {
let _x = return;
foo(_x);
//~^ ERROR the trait bound
//~| NOTE the trait `ImplementedForUnitButNotNever` is not implemented
//~| NOTE the trait is implemented for `()`
}
fn main() {
smeg();
}