Skip to content

Commit

Permalink
Add note about fallback to !: !Trait error
Browse files Browse the repository at this point in the history
  • Loading branch information
canndrew committed Mar 14, 2018
1 parent 5a6b781 commit 5b32211
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/librustc/infer/outlives/bounds.rs
Expand Up @@ -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
Expand Down
45 changes: 40 additions & 5 deletions src/librustc/traits/error_reporting.rs
Expand Up @@ -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>,
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/mod.rs
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/qualify_consts.rs
Expand Up @@ -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);
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/coercion.rs
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/compare_method.rs
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/dropck.rs
Expand Up @@ -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);
}

Expand Down
27 changes: 16 additions & 11 deletions src/librustc_typeck/check/mod.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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};

Expand All @@ -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);
},
}
}

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/op.rs
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/coherence/builtin.rs
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/lib.rs
Expand Up @@ -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
}
}
Expand Down
41 changes: 41 additions & 0 deletions src/test/compile-fail/defaulted-never-note.rs
@@ -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();
}

0 comments on commit 5b32211

Please sign in to comment.