use std::fmt;
use std::slice::Iter;
-use std::vec::Vec;
+use std::vec::{Vec, IntoIter};
use syntax::codemap::{Span, DUMMY_SP};
///////////////////////////////////////////////////////////////////////////
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)
}
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());
- }
- }
- }
- 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();
+ SelectionError::Unimplemented => {
+ match &obligation.cause.code {
+ &ObligationCauseCode::CompareImplMethodObligation => {
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());
+ "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);
- 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());
- }
+ 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::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);
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);
}
}
}
}
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>)
{
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));
+ }
}
}
// static items must have `Sync` type
SharedStatic,
+
BuiltinDerivedObligation(DerivedObligationCause<'tcx>),
ImplDerivedObligation(DerivedObligationCause<'tcx>),
+
+ CompareImplMethodObligation,
}
#[derive(Clone)]
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))
+ }
+ }
}
}
+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.
//
--- /dev/null
+// 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;
+ }
+}
pub use self::LvaluePreference::*;
pub use self::Expectation::*;
+pub use self::compare_method::compare_impl_method;
use self::IsBinopAssignment::*;
use self::TupleArgumentsFlag::*;
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};
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() {
}
}
-/// 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,
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
}
}
}
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() {}
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
}
// 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() { }
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) {}
// 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
}
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() {}
-
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}
}
}
--- /dev/null
+// 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() {}