diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 9ac6b8a86b6..9ad2dd499cc 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -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 { + self.content.into_iter() + } + pub fn iter_enumerated<'a>(&'a self) -> EnumeratedItems<'a,T> { EnumeratedItems::new(self) } diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index e9ef214543d..6d0e60ec495 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -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)); + } } } diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 35029092517..425765edf87 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -121,9 +121,12 @@ pub enum ObligationCauseCode<'tcx> { // static items must have `Sync` type SharedStatic, + BuiltinDerivedObligation(DerivedObligationCause<'tcx>), ImplDerivedObligation(DerivedObligationCause<'tcx>), + + CompareImplMethodObligation, } #[derive(Clone)] diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 236aa3818c7..755983c71bb 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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)) + } + } diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index cab41d66529..37886b4a1e1 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -564,6 +564,18 @@ fn fold_with>(&self, folder: &mut F) -> ty::UnboxedClosureUpv } } +impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where 'tcx: 'a { + fn fold_with>(&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. // diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs new file mode 100644 index 00000000000..27d4b2055d4 --- /dev/null +++ b/src/librustc_typeck/check/compare_method.rs @@ -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 or the MIT license +// , 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; + } +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c03cb860dfb..d412ed39c72 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 { fn method>() { ... } } - // - // impl<'a> Foo<&'a T> for &'a U { - // fn method>() { ... } - // } - // - // 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 = - 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 = - 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, diff --git a/src/test/compile-fail/issue-14853.rs b/src/test/compile-fail/issue-14853.rs index 3d8ebc1ecdf..22ba54fea14 100644 --- a/src/test/compile-fail/issue-14853.rs +++ b/src/test/compile-fail/issue-14853.rs @@ -20,8 +20,7 @@ struct X { data: u32 } impl Something for X { fn yay(_:Option, 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 } } diff --git a/src/test/compile-fail/issue-2611-4.rs b/src/test/compile-fail/issue-2611-4.rs index b141c1f441a..31796e5e20c 100644 --- a/src/test/compile-fail/issue-2611-4.rs +++ b/src/test/compile-fail/issue-2611-4.rs @@ -20,7 +20,8 @@ struct E { } impl A for E { - fn b(_x: F) -> F { panic!() } //~ ERROR type parameter 0 requires `Sync` + fn b(_x: F) -> F { panic!() } + //~^ ERROR `F : core::marker::Sync` appears on the impl method } fn main() {} diff --git a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs index 5028ec89972..a3c38dff6b0 100644 --- a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs +++ b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs @@ -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() { } diff --git a/src/test/compile-fail/trait-bounds-impl-comparison-1.rs b/src/test/compile-fail/trait-bounds-impl-comparison-1.rs index 5fc80d5660d..477bd4f5be9 100644 --- a/src/test/compile-fail/trait-bounds-impl-comparison-1.rs +++ b/src/test/compile-fail/trait-bounds-impl-comparison-1.rs @@ -32,15 +32,15 @@ trait Foo { impl Foo for isize { // invalid bound for T, was defined as Eq in trait fn test_error1_fn(&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(&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(&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(&self) {} @@ -50,16 +50,16 @@ fn test4_fn(&self) {} // parameters in impls must be equal or more general than in the defining trait fn test_error5_fn(&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(&self) {} fn test_error7_fn(&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(&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>() {} - //~^ ERROR in method `method`, type parameter 0 requires bound `Getter` + //~^ G : Getter` appears on the impl method but not on the corresponding trait method } fn main() {} - diff --git a/src/test/compile-fail/trait-bounds-impl-comparison-2.rs b/src/test/compile-fail/trait-bounds-impl-comparison-2.rs index a970a86408e..8ad19116e7b 100644 --- a/src/test/compile-fail/trait-bounds-impl-comparison-2.rs +++ b/src/test/compile-fail/trait-bounds-impl-comparison-2.rs @@ -20,7 +20,7 @@ trait IteratorUtil { impl> IteratorUtil for T { fn zip>(self, other: U) -> ZipIterator { - //~^ ERROR in method `zip`, type parameter 1 requires bound `Iterator` + //~^ ERROR the requirement `U : Iterator` appears on the impl method ZipIterator{a: self, b: other} } } diff --git a/src/test/run-pass/where-clause-bounds-inconsistency.rs b/src/test/run-pass/where-clause-bounds-inconsistency.rs new file mode 100644 index 00000000000..a1a61127f68 --- /dev/null +++ b/src/test/run-pass/where-clause-bounds-inconsistency.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Bound {} + +trait Trait { + fn a(&self, T) where T: Bound; + fn b(&self, T) where T: Bound; + fn c(&self, T); + fn d(&self, T); +} + +impl Trait for bool { + fn a(&self, _: T) {} + //^~ This gets rejected but should be accepted + fn b(&self, _: T) where T: Bound {} + fn c(&self, _: T) {} + fn d(&self, _: T) where T: Bound {} +} + +fn main() {}