Add note about fallback to !: !Trait
error
This commit is contained in:
parent
5a6b781266
commit
5b32211e62
12 changed files with 107 additions and 26 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
41
src/test/compile-fail/defaulted-never-note.rs
Normal file
41
src/test/compile-fail/defaulted-never-note.rs
Normal 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();
|
||||
}
|
||||
|
Loading…
Reference in a new issue