auto merge of #20290 : jroesch/rust/generalize-impl-bounds, r=nikomatsakis
This should fix both #20020 and #20107. This moves out the code into its own file. I have a couple concerns that can either be addressed in this PR or in a future one. - The error reporting for the fulfillment context should be span aware because currently it is attached to the top of the file which is less then desirable. - There is a failure in the test file: run-pass/issue-2611-3.rs, this seems like it should be a failure to me, but I am not sure. As a nit I'm not enthused about the file name, and am open to better suggestions. r? @nikomatsakis
This commit is contained in:
commit
451e134c18
@ -19,7 +19,7 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::slice::Iter;
|
||||
use std::vec::Vec;
|
||||
use std::vec::{Vec, IntoIter};
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -397,6 +397,10 @@ pub fn iter<'a>(&'a self) -> Iter<'a,T> {
|
||||
self.content.iter()
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> IntoIter<T> {
|
||||
self.content.into_iter()
|
||||
}
|
||||
|
||||
pub fn iter_enumerated<'a>(&'a self) -> EnumeratedItems<'a,T> {
|
||||
EnumeratedItems::new(self)
|
||||
}
|
||||
|
@ -161,66 +161,80 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
|
||||
SelectionError::Unimplemented => {
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(ref trait_predicate) => {
|
||||
let trait_predicate =
|
||||
infcx.resolve_type_vars_if_possible(trait_predicate);
|
||||
if !trait_predicate.references_error() {
|
||||
let trait_ref = trait_predicate.to_poly_trait_ref();
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the trait `{}` is not implemented for the type `{}`",
|
||||
trait_ref.user_string(infcx.tcx),
|
||||
trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
|
||||
// Check if it has a custom "#[rustc_on_unimplemented]" error message,
|
||||
// report with that message if it does
|
||||
let custom_note = report_on_unimplemented(infcx, &*trait_ref.0,
|
||||
obligation.cause.span);
|
||||
if let Some(s) = custom_note {
|
||||
infcx.tcx.sess.span_note(obligation.cause.span,
|
||||
s.as_slice());
|
||||
match &obligation.cause.code {
|
||||
&ObligationCauseCode::CompareImplMethodObligation => {
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the requirement `{}` appears on the impl \
|
||||
method but not on the corresponding trait method",
|
||||
obligation.predicate.user_string(infcx.tcx)).as_slice());
|
||||
}
|
||||
_ => {
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(ref trait_predicate) => {
|
||||
let trait_predicate =
|
||||
infcx.resolve_type_vars_if_possible(trait_predicate);
|
||||
|
||||
if !trait_predicate.references_error() {
|
||||
let trait_ref = trait_predicate.to_poly_trait_ref();
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the trait `{}` is not implemented for the type `{}`",
|
||||
trait_ref.user_string(infcx.tcx),
|
||||
trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
|
||||
// Check if it has a custom "#[rustc_on_unimplemented]"
|
||||
// error message, report with that message if it does
|
||||
let custom_note = report_on_unimplemented(infcx, &*trait_ref.0,
|
||||
obligation.cause.span);
|
||||
if let Some(s) = custom_note {
|
||||
infcx.tcx.sess.span_note(obligation.cause.span,
|
||||
s.as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::Equate(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.equality_predicate(obligation.cause.span,
|
||||
&predicate).unwrap_err();
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate.user_string(infcx.tcx),
|
||||
ty::type_err_to_str(infcx.tcx, &err)).as_slice());
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.region_outlives_predicate(obligation.cause.span,
|
||||
&predicate).unwrap_err();
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate.user_string(infcx.tcx),
|
||||
ty::type_err_to_str(infcx.tcx, &err)).as_slice());
|
||||
}
|
||||
|
||||
ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
|
||||
let predicate =
|
||||
infcx.resolve_type_vars_if_possible(&obligation.predicate);
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the requirement `{}` is not satisfied",
|
||||
predicate.user_string(infcx.tcx)).as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::Equate(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.equality_predicate(obligation.cause.span,
|
||||
&predicate).unwrap_err();
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate.user_string(infcx.tcx),
|
||||
ty::type_err_to_str(infcx.tcx, &err)).as_slice());
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.region_outlives_predicate(obligation.cause.span,
|
||||
&predicate).unwrap_err();
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate.user_string(infcx.tcx),
|
||||
ty::type_err_to_str(infcx.tcx, &err)).as_slice());
|
||||
}
|
||||
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::TypeOutlives(..) => {
|
||||
let predicate =
|
||||
infcx.resolve_type_vars_if_possible(&obligation.predicate);
|
||||
infcx.tcx.sess.span_err(
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"the requirement `{}` is not satisfied",
|
||||
predicate.user_string(infcx.tcx)).as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, ref e) => {
|
||||
let expected_trait_ref = infcx.resolve_type_vars_if_possible(&*expected_trait_ref);
|
||||
let actual_trait_ref = infcx.resolve_type_vars_if_possible(&*actual_trait_ref);
|
||||
@ -229,12 +243,12 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
obligation.cause.span,
|
||||
format!(
|
||||
"type mismatch: the type `{}` implements the trait `{}`, \
|
||||
but the trait `{}` is required ({})",
|
||||
but the trait `{}` is required ({})",
|
||||
expected_trait_ref.self_ty().user_string(infcx.tcx),
|
||||
expected_trait_ref.user_string(infcx.tcx),
|
||||
actual_trait_ref.user_string(infcx.tcx),
|
||||
ty::type_err_to_str(infcx.tcx, e)).as_slice());
|
||||
note_obligation_cause(infcx, obligation);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -330,7 +344,7 @@ fn note_obligation_cause<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
}
|
||||
|
||||
fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
_predicate: &ty::Predicate<'tcx>,
|
||||
predicate: &ty::Predicate<'tcx>,
|
||||
cause_span: Span,
|
||||
cause_code: &ObligationCauseCode<'tcx>)
|
||||
{
|
||||
@ -417,6 +431,12 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
let parent_predicate = parent_trait_ref.as_predicate();
|
||||
note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code);
|
||||
}
|
||||
ObligationCauseCode::CompareImplMethodObligation => {
|
||||
span_note!(tcx.sess, cause_span,
|
||||
"the requirement `{}` appears on the impl method\
|
||||
but not on the corresponding trait method",
|
||||
predicate.user_string(infcx.tcx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,9 +121,12 @@ pub enum ObligationCauseCode<'tcx> {
|
||||
// static items must have `Sync` type
|
||||
SharedStatic,
|
||||
|
||||
|
||||
BuiltinDerivedObligation(DerivedObligationCause<'tcx>),
|
||||
|
||||
ImplDerivedObligation(DerivedObligationCause<'tcx>),
|
||||
|
||||
CompareImplMethodObligation,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -7341,3 +7341,15 @@ fn repr(&self, tcx: &ctxt<'tcx>) -> String {
|
||||
self.mt.repr(tcx))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Repr<'tcx> for ParameterEnvironment<'a, 'tcx> {
|
||||
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
|
||||
format!("ParameterEnvironment(\
|
||||
free_substs={}, \
|
||||
implicit_region_bound={}, \
|
||||
caller_bounds={})",
|
||||
self.free_substs.repr(tcx),
|
||||
self.implicit_region_bound.repr(tcx),
|
||||
self.caller_bounds.repr(tcx))
|
||||
}
|
||||
}
|
||||
|
@ -564,6 +564,18 @@ fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::UnboxedClosureUpv
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where 'tcx: 'a {
|
||||
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ParameterEnvironment<'a, 'tcx> {
|
||||
ty::ParameterEnvironment {
|
||||
tcx: self.tcx,
|
||||
free_substs: self.free_substs.fold_with(folder),
|
||||
implicit_region_bound: self.implicit_region_bound.fold_with(folder),
|
||||
caller_bounds: self.caller_bounds.fold_with(folder),
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// "super" routines: these are the default implementations for TypeFolder.
|
||||
//
|
||||
|
413
src/librustc_typeck/check/compare_method.rs
Normal file
413
src/librustc_typeck/check/compare_method.rs
Normal file
@ -0,0 +1,413 @@
|
||||
// Copyright 2012-2014 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.
|
||||
|
||||
use middle::infer;
|
||||
use middle::traits;
|
||||
use middle::ty::{self};
|
||||
use middle::subst::{self, Subst, Substs, VecPerParamSpace};
|
||||
use util::ppaux::{self, Repr};
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{Span};
|
||||
use syntax::parse::token;
|
||||
|
||||
use super::assoc;
|
||||
|
||||
/// Checks that a method from an impl conforms to the signature of
|
||||
/// the same method as declared in the trait.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - impl_m: type of the method we are checking
|
||||
/// - impl_m_span: span to use for reporting errors
|
||||
/// - impl_m_body_id: id of the method body
|
||||
/// - trait_m: the method in the trait
|
||||
/// - impl_trait_ref: the TraitRef corresponding to the trait implementation
|
||||
|
||||
pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
impl_m: &ty::Method<'tcx>,
|
||||
impl_m_span: Span,
|
||||
impl_m_body_id: ast::NodeId,
|
||||
trait_m: &ty::Method<'tcx>,
|
||||
impl_trait_ref: &ty::TraitRef<'tcx>) {
|
||||
debug!("compare_impl_method(impl_trait_ref={})",
|
||||
impl_trait_ref.repr(tcx));
|
||||
|
||||
debug!("compare_impl_method: impl_trait_ref (liberated) = {}",
|
||||
impl_trait_ref.repr(tcx));
|
||||
|
||||
let infcx = infer::new_infer_ctxt(tcx);
|
||||
let mut fulfillment_cx = traits::FulfillmentContext::new();
|
||||
|
||||
let trait_to_impl_substs = &impl_trait_ref.substs;
|
||||
|
||||
// Try to give more informative error messages about self typing
|
||||
// mismatches. Note that any mismatch will also be detected
|
||||
// below, where we construct a canonical function type that
|
||||
// includes the self parameter as a normal parameter. It's just
|
||||
// that the error messages you get out of this code are a bit more
|
||||
// inscrutable, particularly for cases where one method has no
|
||||
// self.
|
||||
match (&trait_m.explicit_self, &impl_m.explicit_self) {
|
||||
(&ty::StaticExplicitSelfCategory,
|
||||
&ty::StaticExplicitSelfCategory) => {}
|
||||
(&ty::StaticExplicitSelfCategory, _) => {
|
||||
tcx.sess.span_err(
|
||||
impl_m_span,
|
||||
format!("method `{}` has a `{}` declaration in the impl, \
|
||||
but not in the trait",
|
||||
token::get_name(trait_m.name),
|
||||
ppaux::explicit_self_category_to_str(
|
||||
&impl_m.explicit_self)).as_slice());
|
||||
return;
|
||||
}
|
||||
(_, &ty::StaticExplicitSelfCategory) => {
|
||||
tcx.sess.span_err(
|
||||
impl_m_span,
|
||||
format!("method `{}` has a `{}` declaration in the trait, \
|
||||
but not in the impl",
|
||||
token::get_name(trait_m.name),
|
||||
ppaux::explicit_self_category_to_str(
|
||||
&trait_m.explicit_self)).as_slice());
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
// Let the type checker catch other errors below
|
||||
}
|
||||
}
|
||||
|
||||
let num_impl_m_type_params = impl_m.generics.types.len(subst::FnSpace);
|
||||
let num_trait_m_type_params = trait_m.generics.types.len(subst::FnSpace);
|
||||
if num_impl_m_type_params != num_trait_m_type_params {
|
||||
span_err!(tcx.sess, impl_m_span, E0049,
|
||||
"method `{}` has {} type parameter{} \
|
||||
but its trait declaration has {} type parameter{}",
|
||||
token::get_name(trait_m.name),
|
||||
num_impl_m_type_params,
|
||||
if num_impl_m_type_params == 1 {""} else {"s"},
|
||||
num_trait_m_type_params,
|
||||
if num_trait_m_type_params == 1 {""} else {"s"});
|
||||
return;
|
||||
}
|
||||
|
||||
if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() {
|
||||
span_err!(tcx.sess, impl_m_span, E0050,
|
||||
"method `{}` has {} parameter{} \
|
||||
but the declaration in trait `{}` has {}",
|
||||
token::get_name(trait_m.name),
|
||||
impl_m.fty.sig.0.inputs.len(),
|
||||
if impl_m.fty.sig.0.inputs.len() == 1 {""} else {"s"},
|
||||
ty::item_path_str(tcx, trait_m.def_id),
|
||||
trait_m.fty.sig.0.inputs.len());
|
||||
return;
|
||||
}
|
||||
|
||||
// This code is best explained by example. Consider a trait:
|
||||
//
|
||||
// trait Trait<'t,T> {
|
||||
// fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
|
||||
// }
|
||||
//
|
||||
// And an impl:
|
||||
//
|
||||
// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
|
||||
// fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
|
||||
// }
|
||||
//
|
||||
// We wish to decide if those two method types are compatible.
|
||||
//
|
||||
// We start out with trait_to_impl_substs, that maps the trait
|
||||
// type parameters to impl type parameters. This is taken from the
|
||||
// impl trait reference:
|
||||
//
|
||||
// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
|
||||
//
|
||||
// We create a mapping `dummy_substs` that maps from the impl type
|
||||
// parameters to fresh types and regions. For type parameters,
|
||||
// this is the identity transform, but we could as well use any
|
||||
// skolemized types. For regions, we convert from bound to free
|
||||
// regions (Note: but only early-bound regions, i.e., those
|
||||
// declared on the impl or used in type parameter bounds).
|
||||
//
|
||||
// impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
|
||||
//
|
||||
// Now we can apply skol_substs to the type of the impl method
|
||||
// to yield a new function type in terms of our fresh, skolemized
|
||||
// types:
|
||||
//
|
||||
// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
|
||||
//
|
||||
// We now want to extract and substitute the type of the *trait*
|
||||
// method and compare it. To do so, we must create a compound
|
||||
// substitution by combining trait_to_impl_substs and
|
||||
// impl_to_skol_substs, and also adding a mapping for the method
|
||||
// type parameters. We extend the mapping to also include
|
||||
// the method parameters.
|
||||
//
|
||||
// trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
|
||||
//
|
||||
// Applying this to the trait method type yields:
|
||||
//
|
||||
// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
|
||||
//
|
||||
// This type is also the same but the name of the bound region ('a
|
||||
// vs 'b). However, the normal subtyping rules on fn types handle
|
||||
// this kind of equivalency just fine.
|
||||
//
|
||||
// We now use these subsititions to ensure that all declared bounds are
|
||||
// satisfied by the implementation's method.
|
||||
//
|
||||
// We do this by creating a parameter environment which contains a
|
||||
// substition corresponding to impl_to_skol_substs. We then build
|
||||
// trait_to_skol_substs and use it to convert the predicates contained
|
||||
// in the trait_m.generics to the skolemized form.
|
||||
//
|
||||
// Finally we register each of these predicates as an obligation in
|
||||
// a fresh FulfillmentCtxt, and invoke select_all_or_error.
|
||||
|
||||
// Create a parameter environment that represents the implementation's
|
||||
// method.
|
||||
let impl_param_env =
|
||||
ty::ParameterEnvironment::for_item(tcx, impl_m.def_id.node);
|
||||
|
||||
// Create mapping from impl to skolemized.
|
||||
let impl_to_skol_substs = &impl_param_env.free_substs;
|
||||
|
||||
// Create mapping from trait to skolemized.
|
||||
let trait_to_skol_substs =
|
||||
trait_to_impl_substs
|
||||
.subst(tcx, impl_to_skol_substs)
|
||||
.with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(),
|
||||
impl_to_skol_substs.regions().get_slice(subst::FnSpace).to_vec());
|
||||
debug!("compare_impl_method: trait_to_skol_substs={}",
|
||||
trait_to_skol_substs.repr(tcx));
|
||||
|
||||
// Check region bounds. FIXME(@jroesch) refactor this away when removing
|
||||
// ParamBounds.
|
||||
if !check_region_bounds_on_impl_method(tcx,
|
||||
impl_m_span,
|
||||
impl_m,
|
||||
&trait_m.generics,
|
||||
&impl_m.generics,
|
||||
&trait_to_skol_substs,
|
||||
impl_to_skol_substs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create obligations for each predicate declared by the impl
|
||||
// definition in the context of the trait's parameter
|
||||
// environment. We can't just use `impl_env.caller_bounds`,
|
||||
// however, because we want to replace all late-bound regions with
|
||||
// region variables.
|
||||
let impl_bounds =
|
||||
impl_m.generics.to_bounds(tcx, impl_to_skol_substs);
|
||||
|
||||
let (impl_bounds, _) =
|
||||
infcx.replace_late_bound_regions_with_fresh_var(
|
||||
impl_m_span,
|
||||
infer::HigherRankedType,
|
||||
&ty::Binder(impl_bounds));
|
||||
debug!("compare_impl_method: impl_bounds={}",
|
||||
impl_bounds.repr(tcx));
|
||||
|
||||
// // Normalize the associated types in the impl_bounds.
|
||||
// let traits::Normalized { value: impl_bounds, .. } =
|
||||
// traits::normalize(&mut selcx, normalize_cause.clone(), &impl_bounds);
|
||||
|
||||
// Normalize the associated types in the trait_bounds.
|
||||
let trait_bounds = trait_m.generics.to_bounds(tcx, &trait_to_skol_substs);
|
||||
// let traits::Normalized { value: trait_bounds, .. } =
|
||||
// traits::normalize(&mut selcx, normalize_cause, &trait_bounds);
|
||||
|
||||
// Obtain the predicate split predicate sets for each.
|
||||
let trait_pred = trait_bounds.predicates.split();
|
||||
let impl_pred = impl_bounds.predicates.split();
|
||||
|
||||
// This is the only tricky bit of the new way we check implementation methods
|
||||
// We need to build a set of predicates where only the FnSpace bounds
|
||||
// are from the trait and we assume all other bounds from the implementation
|
||||
// to be previously satisfied.
|
||||
//
|
||||
// We then register the obligations from the impl_m and check to see
|
||||
// if all constraints hold.
|
||||
let hybrid_preds = VecPerParamSpace::new(
|
||||
impl_pred.types,
|
||||
impl_pred.selfs,
|
||||
trait_pred.fns
|
||||
);
|
||||
|
||||
// Construct trait parameter environment and then shift it into the skolemized viewpoint.
|
||||
let mut trait_param_env = impl_param_env.clone();
|
||||
// The key step here is to update the caller_bounds's predicates to be
|
||||
// the new hybrid bounds we computed.
|
||||
trait_param_env.caller_bounds.predicates = hybrid_preds;
|
||||
|
||||
debug!("compare_impl_method: trait_bounds={}",
|
||||
trait_param_env.caller_bounds.repr(tcx));
|
||||
|
||||
let mut selcx = traits::SelectionContext::new(&infcx, &trait_param_env);
|
||||
|
||||
let normalize_cause =
|
||||
traits::ObligationCause::misc(impl_m_span, impl_m_body_id);
|
||||
|
||||
for predicate in impl_pred.fns.into_iter() {
|
||||
let traits::Normalized { value: predicate, .. } =
|
||||
traits::normalize(&mut selcx, normalize_cause.clone(), &predicate);
|
||||
|
||||
let cause = traits::ObligationCause {
|
||||
span: impl_m_span,
|
||||
body_id: impl_m_body_id,
|
||||
code: traits::ObligationCauseCode::CompareImplMethodObligation
|
||||
};
|
||||
|
||||
fulfillment_cx.register_predicate_obligation(
|
||||
&infcx,
|
||||
traits::Obligation::new(cause, predicate));
|
||||
}
|
||||
|
||||
// We now need to check that the signature of the impl method is
|
||||
// compatible with that of the trait method. We do this by
|
||||
// checking that `impl_fty <: trait_fty`.
|
||||
//
|
||||
// FIXME. Unfortunately, this doesn't quite work right now because
|
||||
// associated type normalization is not integrated into subtype
|
||||
// checks. For the comparison to be valid, we need to
|
||||
// normalize the associated types in the impl/trait methods
|
||||
// first. However, because function types bind regions, just
|
||||
// calling `normalize_associated_types_in` would have no effect on
|
||||
// any associated types appearing in the fn arguments or return
|
||||
// type.
|
||||
|
||||
// Compute skolemized form of impl and trait method tys.
|
||||
let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
|
||||
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
|
||||
let trait_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(trait_m.fty.clone()));
|
||||
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
|
||||
|
||||
let err = infcx.try(|snapshot| {
|
||||
let origin = infer::MethodCompatCheck(impl_m_span);
|
||||
|
||||
let (impl_sig, _) =
|
||||
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
|
||||
infer::HigherRankedType,
|
||||
&impl_m.fty.sig);
|
||||
let impl_sig =
|
||||
impl_sig.subst(tcx, impl_to_skol_substs);
|
||||
let impl_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&impl_param_env,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&impl_sig);
|
||||
let impl_fty =
|
||||
ty::mk_bare_fn(tcx,
|
||||
None,
|
||||
tcx.mk_bare_fn(ty::BareFnTy { unsafety: impl_m.fty.unsafety,
|
||||
abi: impl_m.fty.abi,
|
||||
sig: ty::Binder(impl_sig) }));
|
||||
debug!("compare_impl_method: impl_fty={}",
|
||||
impl_fty.repr(tcx));
|
||||
|
||||
let (trait_sig, skol_map) =
|
||||
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
|
||||
let trait_sig =
|
||||
trait_sig.subst(tcx, &trait_to_skol_substs);
|
||||
let trait_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&impl_param_env,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&trait_sig);
|
||||
let trait_fty =
|
||||
ty::mk_bare_fn(tcx,
|
||||
None,
|
||||
tcx.mk_bare_fn(ty::BareFnTy { unsafety: trait_m.fty.unsafety,
|
||||
abi: trait_m.fty.abi,
|
||||
sig: ty::Binder(trait_sig) }));
|
||||
|
||||
debug!("compare_impl_method: trait_fty={}",
|
||||
trait_fty.repr(tcx));
|
||||
|
||||
try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
|
||||
|
||||
infcx.leak_check(&skol_map, snapshot)
|
||||
});
|
||||
|
||||
match err {
|
||||
Ok(()) => { }
|
||||
Err(terr) => {
|
||||
debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
|
||||
impl_fty.repr(tcx),
|
||||
trait_fty.repr(tcx));
|
||||
span_err!(tcx.sess, impl_m_span, E0053,
|
||||
"method `{}` has an incompatible type for trait: {}",
|
||||
token::get_name(trait_m.name),
|
||||
ty::type_err_to_str(tcx, &terr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all obligations are satisfied by the implementation's
|
||||
// version.
|
||||
match fulfillment_cx.select_all_or_error(&infcx, &trait_param_env) {
|
||||
Err(ref errors) => { traits::report_fulfillment_errors(&infcx, errors) }
|
||||
Ok(_) => {}
|
||||
}
|
||||
|
||||
// Finally, resolve all regions. This catches wily misuses of lifetime
|
||||
// parameters.
|
||||
infcx.resolve_regions_and_report_errors(impl_m_body_id);
|
||||
|
||||
fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
span: Span,
|
||||
impl_m: &ty::Method<'tcx>,
|
||||
trait_generics: &ty::Generics<'tcx>,
|
||||
impl_generics: &ty::Generics<'tcx>,
|
||||
trait_to_skol_substs: &Substs<'tcx>,
|
||||
impl_to_skol_substs: &Substs<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
|
||||
let trait_params = trait_generics.regions.get_slice(subst::FnSpace);
|
||||
let impl_params = impl_generics.regions.get_slice(subst::FnSpace);
|
||||
|
||||
debug!("check_region_bounds_on_impl_method: \
|
||||
trait_generics={} \
|
||||
impl_generics={} \
|
||||
trait_to_skol_substs={} \
|
||||
impl_to_skol_substs={}",
|
||||
trait_generics.repr(tcx),
|
||||
impl_generics.repr(tcx),
|
||||
trait_to_skol_substs.repr(tcx),
|
||||
impl_to_skol_substs.repr(tcx));
|
||||
|
||||
// Must have same number of early-bound lifetime parameters.
|
||||
// Unfortunately, if the user screws up the bounds, then this
|
||||
// will change classification between early and late. E.g.,
|
||||
// if in trait we have `<'a,'b:'a>`, and in impl we just have
|
||||
// `<'a,'b>`, then we have 2 early-bound lifetime parameters
|
||||
// in trait but 0 in the impl. But if we report "expected 2
|
||||
// but found 0" it's confusing, because it looks like there
|
||||
// are zero. Since I don't quite know how to phrase things at
|
||||
// the moment, give a kind of vague error message.
|
||||
if trait_params.len() != impl_params.len() {
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
&format!("lifetime parameters or bounds on method `{}` do \
|
||||
not match the trait declaration",
|
||||
token::get_name(impl_m.name))[]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -78,6 +78,7 @@
|
||||
|
||||
pub use self::LvaluePreference::*;
|
||||
pub use self::Expectation::*;
|
||||
pub use self::compare_method::compare_impl_method;
|
||||
use self::IsBinopAssignment::*;
|
||||
use self::TupleArgumentsFlag::*;
|
||||
|
||||
@ -106,7 +107,7 @@
|
||||
use middle::lang_items::TypeIdLangItem;
|
||||
use lint;
|
||||
use util::common::{block_query, indenter, loop_query};
|
||||
use util::ppaux::{self, UserString, Repr};
|
||||
use util::ppaux::{self, Repr};
|
||||
use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
|
||||
|
||||
use std::cell::{Cell, Ref, RefCell};
|
||||
@ -137,8 +138,8 @@
|
||||
pub mod wf;
|
||||
mod closure;
|
||||
mod callee;
|
||||
mod compare_method;
|
||||
|
||||
/// Fields that are part of a `FnCtxt` which are inherited by
|
||||
/// closures defined within the function. For example:
|
||||
///
|
||||
/// fn foo() {
|
||||
@ -993,503 +994,6 @@ trait `{}`",
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that a method from an impl conforms to the signature of
|
||||
/// the same method as declared in the trait.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - impl_generics: the generics declared on the impl itself (not the method!)
|
||||
/// - impl_m: type of the method we are checking
|
||||
/// - impl_m_span: span to use for reporting errors
|
||||
/// - impl_m_body_id: id of the method body
|
||||
/// - trait_m: the method in the trait
|
||||
/// - trait_to_impl_substs: the substitutions used on the type of the trait
|
||||
fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
impl_m: &ty::Method<'tcx>,
|
||||
impl_m_span: Span,
|
||||
impl_m_body_id: ast::NodeId,
|
||||
trait_m: &ty::Method<'tcx>,
|
||||
impl_trait_ref: &ty::TraitRef<'tcx>) {
|
||||
debug!("compare_impl_method(impl_trait_ref={})",
|
||||
impl_trait_ref.repr(tcx));
|
||||
|
||||
debug!("impl_trait_ref (liberated) = {}",
|
||||
impl_trait_ref.repr(tcx));
|
||||
|
||||
let infcx = infer::new_infer_ctxt(tcx);
|
||||
let mut fulfillment_cx = traits::FulfillmentContext::new();
|
||||
|
||||
let trait_to_impl_substs = &impl_trait_ref.substs;
|
||||
|
||||
// Try to give more informative error messages about self typing
|
||||
// mismatches. Note that any mismatch will also be detected
|
||||
// below, where we construct a canonical function type that
|
||||
// includes the self parameter as a normal parameter. It's just
|
||||
// that the error messages you get out of this code are a bit more
|
||||
// inscrutable, particularly for cases where one method has no
|
||||
// self.
|
||||
match (&trait_m.explicit_self, &impl_m.explicit_self) {
|
||||
(&ty::StaticExplicitSelfCategory,
|
||||
&ty::StaticExplicitSelfCategory) => {}
|
||||
(&ty::StaticExplicitSelfCategory, _) => {
|
||||
tcx.sess.span_err(
|
||||
impl_m_span,
|
||||
&format!("method `{}` has a `{}` declaration in the impl, \
|
||||
but not in the trait",
|
||||
token::get_name(trait_m.name),
|
||||
ppaux::explicit_self_category_to_str(
|
||||
&impl_m.explicit_self))[]);
|
||||
return;
|
||||
}
|
||||
(_, &ty::StaticExplicitSelfCategory) => {
|
||||
tcx.sess.span_err(
|
||||
impl_m_span,
|
||||
&format!("method `{}` has a `{}` declaration in the trait, \
|
||||
but not in the impl",
|
||||
token::get_name(trait_m.name),
|
||||
ppaux::explicit_self_category_to_str(
|
||||
&trait_m.explicit_self))[]);
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
// Let the type checker catch other errors below
|
||||
}
|
||||
}
|
||||
|
||||
let num_impl_m_type_params = impl_m.generics.types.len(subst::FnSpace);
|
||||
let num_trait_m_type_params = trait_m.generics.types.len(subst::FnSpace);
|
||||
if num_impl_m_type_params != num_trait_m_type_params {
|
||||
span_err!(tcx.sess, impl_m_span, E0049,
|
||||
"method `{}` has {} type parameter{} \
|
||||
but its trait declaration has {} type parameter{}",
|
||||
token::get_name(trait_m.name),
|
||||
num_impl_m_type_params,
|
||||
if num_impl_m_type_params == 1 {""} else {"s"},
|
||||
num_trait_m_type_params,
|
||||
if num_trait_m_type_params == 1 {""} else {"s"});
|
||||
return;
|
||||
}
|
||||
|
||||
if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() {
|
||||
span_err!(tcx.sess, impl_m_span, E0050,
|
||||
"method `{}` has {} parameter{} \
|
||||
but the declaration in trait `{}` has {}",
|
||||
token::get_name(trait_m.name),
|
||||
impl_m.fty.sig.0.inputs.len(),
|
||||
if impl_m.fty.sig.0.inputs.len() == 1 {""} else {"s"},
|
||||
ty::item_path_str(tcx, trait_m.def_id),
|
||||
trait_m.fty.sig.0.inputs.len());
|
||||
return;
|
||||
}
|
||||
|
||||
// This code is best explained by example. Consider a trait:
|
||||
//
|
||||
// trait Trait<'t,T> {
|
||||
// fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
|
||||
// }
|
||||
//
|
||||
// And an impl:
|
||||
//
|
||||
// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
|
||||
// fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
|
||||
// }
|
||||
//
|
||||
// We wish to decide if those two method types are compatible.
|
||||
//
|
||||
// We start out with trait_to_impl_substs, that maps the trait
|
||||
// type parameters to impl type parameters. This is taken from the
|
||||
// impl trait reference:
|
||||
//
|
||||
// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
|
||||
//
|
||||
// We create a mapping `dummy_substs` that maps from the impl type
|
||||
// parameters to fresh types and regions. For type parameters,
|
||||
// this is the identity transform, but we could as well use any
|
||||
// skolemized types. For regions, we convert from bound to free
|
||||
// regions (Note: but only early-bound regions, i.e., those
|
||||
// declared on the impl or used in type parameter bounds).
|
||||
//
|
||||
// impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
|
||||
//
|
||||
// Now we can apply skol_substs to the type of the impl method
|
||||
// to yield a new function type in terms of our fresh, skolemized
|
||||
// types:
|
||||
//
|
||||
// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
|
||||
//
|
||||
// We now want to extract and substitute the type of the *trait*
|
||||
// method and compare it. To do so, we must create a compound
|
||||
// substitution by combining trait_to_impl_substs and
|
||||
// impl_to_skol_substs, and also adding a mapping for the method
|
||||
// type parameters. We extend the mapping to also include
|
||||
// the method parameters.
|
||||
//
|
||||
// trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
|
||||
//
|
||||
// Applying this to the trait method type yields:
|
||||
//
|
||||
// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
|
||||
//
|
||||
// This type is also the same but the name of the bound region ('a
|
||||
// vs 'b). However, the normal subtyping rules on fn types handle
|
||||
// this kind of equivalency just fine.
|
||||
|
||||
// Create mapping from impl to skolemized.
|
||||
let impl_param_env = ty::construct_parameter_environment(tcx, &impl_m.generics, impl_m_body_id);
|
||||
let impl_to_skol_substs = &impl_param_env.free_substs;
|
||||
|
||||
// Create mapping from trait to skolemized.
|
||||
let trait_to_skol_substs =
|
||||
trait_to_impl_substs
|
||||
.subst(tcx, impl_to_skol_substs)
|
||||
.with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(),
|
||||
impl_to_skol_substs.regions().get_slice(subst::FnSpace).to_vec());
|
||||
|
||||
// Check region bounds.
|
||||
if !check_region_bounds_on_impl_method(tcx,
|
||||
impl_m_span,
|
||||
impl_m,
|
||||
&trait_m.generics,
|
||||
&impl_m.generics,
|
||||
&trait_to_skol_substs,
|
||||
impl_to_skol_substs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check bounds. Note that the bounds from the impl may reference
|
||||
// late-bound regions declared on the impl, so liberate those.
|
||||
// This requires two artificial binding scopes -- one for the impl,
|
||||
// and one for the method.
|
||||
//
|
||||
// An example would be:
|
||||
//
|
||||
// trait Foo<T> { fn method<U:Bound<T>>() { ... } }
|
||||
//
|
||||
// impl<'a> Foo<&'a T> for &'a U {
|
||||
// fn method<U:Bound<&'a T>>() { ... }
|
||||
// }
|
||||
//
|
||||
// Here, the region parameter `'a` is late-bound, so in the bound
|
||||
// `Bound<&'a T>`, the lifetime `'a` will be late-bound with a
|
||||
// depth of 3 (it is nested within 3 binders: the impl, method,
|
||||
// and trait-ref itself). So when we do the liberation, we have
|
||||
// two introduce two `ty::Binder` scopes, one for the impl and one
|
||||
// the method.
|
||||
//
|
||||
// The only late-bounded regions that can possibly appear here are
|
||||
// from the impl, not the method. This is because region
|
||||
// parameters declared on the method which appear in a type bound
|
||||
// would be early bound. On the trait side, there can be no
|
||||
// late-bound lifetimes because trait definitions do not introduce
|
||||
// a late region binder.
|
||||
let trait_bounds =
|
||||
trait_m.generics.types.get_slice(subst::FnSpace).iter()
|
||||
.map(|trait_param_def| &trait_param_def.bounds);
|
||||
let impl_bounds =
|
||||
impl_m.generics.types.get_slice(subst::FnSpace).iter()
|
||||
.map(|impl_param_def| &impl_param_def.bounds);
|
||||
for (i, (trait_param_bounds, impl_param_bounds)) in
|
||||
trait_bounds.zip(impl_bounds).enumerate()
|
||||
{
|
||||
// Check that the impl does not require any builtin-bounds
|
||||
// that the trait does not guarantee:
|
||||
let extra_bounds =
|
||||
impl_param_bounds.builtin_bounds -
|
||||
trait_param_bounds.builtin_bounds;
|
||||
if !extra_bounds.is_empty() {
|
||||
span_err!(tcx.sess, impl_m_span, E0051,
|
||||
"in method `{}`, type parameter {} requires `{}`, \
|
||||
which is not required by the corresponding type parameter \
|
||||
in the trait declaration",
|
||||
token::get_name(trait_m.name),
|
||||
i,
|
||||
extra_bounds.user_string(tcx));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that the trait bounds of the trait imply the bounds of its
|
||||
// implementation.
|
||||
//
|
||||
// FIXME(pcwalton): We could be laxer here regarding sub- and super-
|
||||
// traits, but I doubt that'll be wanted often, so meh.
|
||||
for impl_trait_bound in impl_param_bounds.trait_bounds.iter() {
|
||||
debug!("compare_impl_method(): impl-trait-bound subst");
|
||||
let impl_trait_bound =
|
||||
impl_trait_bound.subst(tcx, impl_to_skol_substs);
|
||||
|
||||
// There may be late-bound regions from the impl in the
|
||||
// impl's bound, so "liberate" those. Note that the
|
||||
// trait_to_skol_substs is derived from the impl's
|
||||
// trait-ref, and the late-bound regions appearing there
|
||||
// have already been liberated, so the result should match
|
||||
// up.
|
||||
|
||||
let found_match_in_trait =
|
||||
trait_param_bounds.trait_bounds.iter().any(|trait_bound| {
|
||||
debug!("compare_impl_method(): trait-bound subst");
|
||||
let trait_bound =
|
||||
trait_bound.subst(tcx, &trait_to_skol_substs);
|
||||
infer::mk_sub_poly_trait_refs(&infcx,
|
||||
true,
|
||||
infer::Misc(impl_m_span),
|
||||
trait_bound,
|
||||
impl_trait_bound.clone()).is_ok()
|
||||
});
|
||||
|
||||
if !found_match_in_trait {
|
||||
span_err!(tcx.sess, impl_m_span, E0052,
|
||||
"in method `{}`, type parameter {} requires bound `{}`, which is not \
|
||||
required by the corresponding type parameter in the trait declaration",
|
||||
token::get_name(trait_m.name),
|
||||
i,
|
||||
impl_trait_bound.user_string(tcx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We now need to check that the signature of the impl method is
|
||||
// compatible with that of the trait method. We do this by
|
||||
// checking that `impl_fty <: trait_fty`.
|
||||
//
|
||||
// FIXME. Unfortunately, this doesn't quite work right now because
|
||||
// associated type normalization is not integrated into subtype
|
||||
// checks. For the comparison to be valid, we need to
|
||||
// normalize the associated types in the impl/trait methods
|
||||
// first. However, because function types bind regions, just
|
||||
// calling `normalize_associated_types_in` would have no effect on
|
||||
// any associated types appearing in the fn arguments or return
|
||||
// type.
|
||||
|
||||
|
||||
// Compute skolemized form of impl and trait method tys.
|
||||
let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
|
||||
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
|
||||
let trait_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(trait_m.fty.clone()));
|
||||
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
|
||||
|
||||
let err = infcx.try(|snapshot| {
|
||||
let origin = infer::MethodCompatCheck(impl_m_span);
|
||||
|
||||
let (impl_sig, _) =
|
||||
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
|
||||
infer::HigherRankedType,
|
||||
&impl_m.fty.sig);
|
||||
let impl_sig =
|
||||
impl_sig.subst(tcx, impl_to_skol_substs);
|
||||
let impl_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&impl_param_env,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&impl_sig);
|
||||
let impl_fty =
|
||||
ty::mk_bare_fn(tcx,
|
||||
None,
|
||||
tcx.mk_bare_fn(ty::BareFnTy { unsafety: impl_m.fty.unsafety,
|
||||
abi: impl_m.fty.abi,
|
||||
sig: ty::Binder(impl_sig) }));
|
||||
debug!("compare_impl_method: impl_fty={}",
|
||||
impl_fty.repr(tcx));
|
||||
|
||||
let (trait_sig, skol_map) =
|
||||
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
|
||||
let trait_sig =
|
||||
trait_sig.subst(tcx, &trait_to_skol_substs);
|
||||
let trait_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&impl_param_env,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&trait_sig);
|
||||
let trait_fty =
|
||||
ty::mk_bare_fn(tcx,
|
||||
None,
|
||||
tcx.mk_bare_fn(ty::BareFnTy { unsafety: trait_m.fty.unsafety,
|
||||
abi: trait_m.fty.abi,
|
||||
sig: ty::Binder(trait_sig) }));
|
||||
|
||||
debug!("compare_impl_method: trait_fty={}",
|
||||
trait_fty.repr(tcx));
|
||||
|
||||
try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
|
||||
|
||||
infcx.leak_check(&skol_map, snapshot)
|
||||
});
|
||||
|
||||
match err {
|
||||
Ok(()) => { }
|
||||
Err(terr) => {
|
||||
debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
|
||||
impl_fty.repr(tcx),
|
||||
trait_fty.repr(tcx));
|
||||
span_err!(tcx.sess, impl_m_span, E0053,
|
||||
"method `{}` has an incompatible type for trait: {}",
|
||||
token::get_name(trait_m.name),
|
||||
ty::type_err_to_str(tcx, &terr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the fulfillment context to completion to accommodate any
|
||||
// associated type normalizations that may have occurred.
|
||||
match fulfillment_cx.select_all_or_error(&infcx, &impl_param_env) {
|
||||
Ok(()) => { }
|
||||
Err(errors) => {
|
||||
traits::report_fulfillment_errors(&infcx, &errors);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, resolve all regions. This catches wily misuses of lifetime
|
||||
// parameters.
|
||||
infcx.resolve_regions_and_report_errors(impl_m_body_id);
|
||||
|
||||
/// Check that region bounds on impl method are the same as those on the trait. In principle,
|
||||
/// it could be ok for there to be fewer region bounds on the impl method, but this leads to an
|
||||
/// annoying corner case that is painful to handle (described below), so for now we can just
|
||||
/// forbid it.
|
||||
///
|
||||
/// Example (see `src/test/compile-fail/regions-bound-missing-bound-in-impl.rs`):
|
||||
///
|
||||
/// ```
|
||||
/// trait Foo<'a> {
|
||||
/// fn method1<'b>();
|
||||
/// fn method2<'b:'a>();
|
||||
/// }
|
||||
///
|
||||
/// impl<'a> Foo<'a> for ... {
|
||||
/// fn method1<'b:'a>() { .. case 1, definitely bad .. }
|
||||
/// fn method2<'b>() { .. case 2, could be ok .. }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The "definitely bad" case is case #1. Here, the impl adds an extra constraint not present
|
||||
/// in the trait.
|
||||
///
|
||||
/// The "maybe bad" case is case #2. Here, the impl adds an extra constraint not present in the
|
||||
/// trait. We could in principle allow this, but it interacts in a complex way with early/late
|
||||
/// bound resolution of lifetimes. Basically the presence or absence of a lifetime bound
|
||||
/// affects whether the lifetime is early/late bound, and right now the code breaks if the
|
||||
/// trait has an early bound lifetime parameter and the method does not.
|
||||
fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
span: Span,
|
||||
impl_m: &ty::Method<'tcx>,
|
||||
trait_generics: &ty::Generics<'tcx>,
|
||||
impl_generics: &ty::Generics<'tcx>,
|
||||
trait_to_skol_substs: &Substs<'tcx>,
|
||||
impl_to_skol_substs: &Substs<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
|
||||
let trait_params = trait_generics.regions.get_slice(subst::FnSpace);
|
||||
let impl_params = impl_generics.regions.get_slice(subst::FnSpace);
|
||||
|
||||
debug!("check_region_bounds_on_impl_method: \
|
||||
trait_generics={} \
|
||||
impl_generics={} \
|
||||
trait_to_skol_substs={} \
|
||||
impl_to_skol_substs={}",
|
||||
trait_generics.repr(tcx),
|
||||
impl_generics.repr(tcx),
|
||||
trait_to_skol_substs.repr(tcx),
|
||||
impl_to_skol_substs.repr(tcx));
|
||||
|
||||
// Must have same number of early-bound lifetime parameters.
|
||||
// Unfortunately, if the user screws up the bounds, then this
|
||||
// will change classification between early and late. E.g.,
|
||||
// if in trait we have `<'a,'b:'a>`, and in impl we just have
|
||||
// `<'a,'b>`, then we have 2 early-bound lifetime parameters
|
||||
// in trait but 0 in the impl. But if we report "expected 2
|
||||
// but found 0" it's confusing, because it looks like there
|
||||
// are zero. Since I don't quite know how to phrase things at
|
||||
// the moment, give a kind of vague error message.
|
||||
if trait_params.len() != impl_params.len() {
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
&format!("lifetime parameters or bounds on method `{}` do \
|
||||
not match the trait declaration",
|
||||
token::get_name(impl_m.name))[]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Each parameter `'a:'b+'c+'d` in trait should have the same
|
||||
// set of bounds in the impl, after subst.
|
||||
for (trait_param, impl_param) in
|
||||
trait_params.iter().zip(
|
||||
impl_params.iter())
|
||||
{
|
||||
let trait_bounds =
|
||||
trait_param.bounds.subst(tcx, trait_to_skol_substs);
|
||||
let impl_bounds =
|
||||
impl_param.bounds.subst(tcx, impl_to_skol_substs);
|
||||
|
||||
debug!("check_region_bounds_on_impl_method: \
|
||||
trait_param={} \
|
||||
impl_param={} \
|
||||
trait_bounds={} \
|
||||
impl_bounds={}",
|
||||
trait_param.repr(tcx),
|
||||
impl_param.repr(tcx),
|
||||
trait_bounds.repr(tcx),
|
||||
impl_bounds.repr(tcx));
|
||||
|
||||
// Collect the set of bounds present in trait but not in
|
||||
// impl.
|
||||
let missing: Vec<ty::Region> =
|
||||
trait_bounds.iter()
|
||||
.filter(|&b| !impl_bounds.contains(b))
|
||||
.map(|&b| b)
|
||||
.collect();
|
||||
|
||||
// Collect set present in impl but not in trait.
|
||||
let extra: Vec<ty::Region> =
|
||||
impl_bounds.iter()
|
||||
.filter(|&b| !trait_bounds.contains(b))
|
||||
.map(|&b| b)
|
||||
.collect();
|
||||
|
||||
debug!("missing={} extra={}",
|
||||
missing.repr(tcx), extra.repr(tcx));
|
||||
|
||||
let err = if missing.len() != 0 || extra.len() != 0 {
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
&format!(
|
||||
"the lifetime parameter `{}` declared in the impl \
|
||||
has a distinct set of bounds \
|
||||
from its counterpart `{}` \
|
||||
declared in the trait",
|
||||
impl_param.name.user_string(tcx),
|
||||
trait_param.name.user_string(tcx))[]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if missing.len() != 0 {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
&format!("the impl is missing the following bounds: `{}`",
|
||||
missing.user_string(tcx))[]);
|
||||
}
|
||||
|
||||
if extra.len() != 0 {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
&format!("the impl has the following extra bounds: `{}`",
|
||||
extra.user_string(tcx))[]);
|
||||
}
|
||||
|
||||
if err {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_cast(fcx: &FnCtxt,
|
||||
cast_expr: &ast::Expr,
|
||||
e: &ast::Expr,
|
||||
|
@ -20,8 +20,7 @@ struct X { data: u32 }
|
||||
|
||||
impl Something for X {
|
||||
fn yay<T: Str>(_:Option<X>, thing: &[T]) {
|
||||
//~^ ERROR in method `yay`, type parameter 0 requires bound `Str`, which is not required
|
||||
|
||||
//~^ ERROR the requirement `T : Str` appears on the impl method
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,8 @@ struct E {
|
||||
}
|
||||
|
||||
impl A for E {
|
||||
fn b<F: Sync, G>(_x: F) -> F { panic!() } //~ ERROR type parameter 0 requires `Sync`
|
||||
fn b<F: Sync, G>(_x: F) -> F { panic!() }
|
||||
//~^ ERROR `F : core::marker::Sync` appears on the impl method
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -16,15 +16,16 @@ struct Inv<'a> { // invariant w/r/t 'a
|
||||
x: &'a mut &'a isize
|
||||
}
|
||||
|
||||
pub trait Foo<'a> {
|
||||
pub trait Foo<'a, 't> {
|
||||
fn no_bound<'b>(self, b: Inv<'b>);
|
||||
fn has_bound<'b:'a>(self, b: Inv<'b>);
|
||||
fn wrong_bound1<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
|
||||
fn wrong_bound2<'b,'c,'d:'a+'b+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
|
||||
fn okay_bound<'b,'c,'d:'a+'b+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
|
||||
fn another_bound<'x: 'a>(self, x: Inv<'x>);
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Foo<'a> for &'a isize {
|
||||
impl<'a, 't> Foo<'a, 't> for &'a isize {
|
||||
fn no_bound<'b:'a>(self, b: Inv<'b>) {
|
||||
//~^ ERROR lifetime parameters or bounds on method `no_bound` do not match
|
||||
}
|
||||
@ -47,9 +48,10 @@ fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
|
||||
// cases.
|
||||
}
|
||||
|
||||
fn wrong_bound2<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {
|
||||
//~^ ERROR distinct set of bounds from its counterpart
|
||||
fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {
|
||||
}
|
||||
|
||||
fn another_bound<'x: 't>(self, x: Inv<'x>) {}
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
@ -32,15 +32,15 @@ trait Foo {
|
||||
impl Foo for isize {
|
||||
// invalid bound for T, was defined as Eq in trait
|
||||
fn test_error1_fn<T: Ord>(&self) {}
|
||||
//~^ ERROR in method `test_error1_fn`, type parameter 0 requires bound `core::cmp::Ord`
|
||||
//~^ ERROR the requirement `T : core::cmp::Ord` appears on the impl
|
||||
|
||||
// invalid bound for T, was defined as Eq + Ord in trait
|
||||
fn test_error2_fn<T: Eq + B>(&self) {}
|
||||
//~^ ERROR in method `test_error2_fn`, type parameter 0 requires bound `B`
|
||||
//~^ ERROR the requirement `T : B` appears on the impl
|
||||
|
||||
// invalid bound for T, was defined as Eq + Ord in trait
|
||||
fn test_error3_fn<T: B + Eq>(&self) {}
|
||||
//~^ ERROR in method `test_error3_fn`, type parameter 0 requires bound `B`
|
||||
//~^ ERROR the requirement `T : B` appears on the impl
|
||||
|
||||
// multiple bounds, same order as in trait
|
||||
fn test3_fn<T: Ord + Eq>(&self) {}
|
||||
@ -50,16 +50,16 @@ fn test4_fn<T: Eq + Ord>(&self) {}
|
||||
|
||||
// parameters in impls must be equal or more general than in the defining trait
|
||||
fn test_error5_fn<T: B>(&self) {}
|
||||
//~^ ERROR in method `test_error5_fn`, type parameter 0 requires bound `B`
|
||||
//~^ ERROR the requirement `T : B` appears on the impl
|
||||
|
||||
// bound `std::cmp::Eq` not enforced by this implementation, but this is OK
|
||||
fn test6_fn<T: A>(&self) {}
|
||||
|
||||
fn test_error7_fn<T: A + Eq>(&self) {}
|
||||
//~^ ERROR in method `test_error7_fn`, type parameter 0 requires bound `core::cmp::Eq`
|
||||
//~^ ERROR the requirement `T : core::cmp::Eq` appears on the impl
|
||||
|
||||
fn test_error8_fn<T: C>(&self) {}
|
||||
//~^ ERROR in method `test_error8_fn`, type parameter 0 requires bound `C`
|
||||
//~^ ERROR the requirement `T : C` appears on the impl
|
||||
}
|
||||
|
||||
|
||||
@ -71,8 +71,7 @@ trait Trait {
|
||||
|
||||
impl Trait for usize {
|
||||
fn method<G: Getter<usize>>() {}
|
||||
//~^ ERROR in method `method`, type parameter 0 requires bound `Getter<usize>`
|
||||
//~^ G : Getter<usize>` appears on the impl method but not on the corresponding trait method
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
|
@ -20,7 +20,7 @@ trait IteratorUtil<A> {
|
||||
|
||||
impl<A, T: Iterator<A>> IteratorUtil<A> for T {
|
||||
fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> {
|
||||
//~^ ERROR in method `zip`, type parameter 1 requires bound `Iterator<B>`
|
||||
//~^ ERROR the requirement `U : Iterator<B>` appears on the impl method
|
||||
ZipIterator{a: self, b: other}
|
||||
}
|
||||
}
|
||||
|
28
src/test/run-pass/where-clause-bounds-inconsistency.rs
Normal file
28
src/test/run-pass/where-clause-bounds-inconsistency.rs
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
trait Bound {}
|
||||
|
||||
trait Trait {
|
||||
fn a<T>(&self, T) where T: Bound;
|
||||
fn b<T>(&self, T) where T: Bound;
|
||||
fn c<T: Bound>(&self, T);
|
||||
fn d<T: Bound>(&self, T);
|
||||
}
|
||||
|
||||
impl Trait for bool {
|
||||
fn a<T: Bound>(&self, _: T) {}
|
||||
//^~ This gets rejected but should be accepted
|
||||
fn b<T>(&self, _: T) where T: Bound {}
|
||||
fn c<T: Bound>(&self, _: T) {}
|
||||
fn d<T>(&self, _: T) where T: Bound {}
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user