]> git.lizzy.rs Git - rust.git/commitdiff
Refactor compare_impl_method to use all bounds
authorJared Roesch <roeschinc@gmail.com>
Wed, 14 Jan 2015 21:43:17 +0000 (13:43 -0800)
committerJared Roesch <roeschinc@gmail.com>
Wed, 14 Jan 2015 21:43:17 +0000 (13:43 -0800)
Refactor compare_impl_method into its own file. Modify the
code to stop comparing individual parameter bounds.
Instead we now use the predicates list attached to the trait
and implementation generics. This ensures consistency even
when bounds are declared in different places (i.e on
a parameter vs. in a where clause).

13 files changed:
src/librustc/middle/subst.rs
src/librustc/middle/traits/error_reporting.rs
src/librustc/middle/traits/mod.rs
src/librustc/middle/ty.rs
src/librustc/middle/ty_fold.rs
src/librustc_typeck/check/compare_method.rs [new file with mode: 0644]
src/librustc_typeck/check/mod.rs
src/test/compile-fail/issue-14853.rs
src/test/compile-fail/issue-2611-4.rs
src/test/compile-fail/regions-bound-missing-bound-in-impl.rs
src/test/compile-fail/trait-bounds-impl-comparison-1.rs
src/test/compile-fail/trait-bounds-impl-comparison-2.rs
src/test/run-pass/where-clause-bounds-inconsistency.rs [new file with mode: 0644]

index 9ac6b8a86b68b10585fb191f64ca5552f3cb181a..9ad2dd499cc9deb56b69154a4213847f3167d72d 100644 (file)
@@ -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)
     }
index e9ef214543d6e738a51fde00796c785f1991ec0a..6d0e60ec495a7bee25ea3fe8249befef125adf29 100644 (file)
@@ -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());
-                        }
-                    }
-                }
 
-                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);
@@ -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));
+        }
     }
 }
 
index 3502909251790d3566fa76ae73862e00d0e550d5..425765edf870c5c30cd56a007ca2d2a3d7e639c6 100644 (file)
@@ -121,9 +121,12 @@ pub enum ObligationCauseCode<'tcx> {
     // static items must have `Sync` type
     SharedStatic,
 
+
     BuiltinDerivedObligation(DerivedObligationCause<'tcx>),
 
     ImplDerivedObligation(DerivedObligationCause<'tcx>),
+
+    CompareImplMethodObligation,
 }
 
 #[derive(Clone)]
index 236aa3818c77acc2f1b1c04e98b77118fa0d2bd5..755983c71bba829c96600c914b6068e46d72aa5a 100644 (file)
@@ -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))
+        }
+    }
index cab41d665297879ace454b0d88fa5f5a0ab72e1c..37886b4a1e1f670681be6705676362901d458074 100644 (file)
@@ -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.
 //
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
new file mode 100644 (file)
index 0000000..27d4b20
--- /dev/null
@@ -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;
+    }
+}
index c03cb860dfb9e68b062f8bba4598a4be4794e512..d412ed39c7271b248e6caf4ddd4efb82c6a39518 100644 (file)
@@ -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::*;
 
 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() {
@@ -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,
index 3d8ebc1ecdf19e517bb197f93fe36bbece65ca47..22ba54fea14694f86c9dd2594698c5a395c46cdd 100644 (file)
@@ -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
     }
 }
 
index b141c1f441aeb436f08313a42b7d308d8ab7508a..31796e5e20c4492e683642701852db9bc439bfdf 100644 (file)
@@ -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() {}
index 5028ec8997261c6c2de76cc36ff87e33df9053ab..a3c38dff6b0f62f73275240d21286e2a1fdcc758 100644 (file)
@@ -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() { }
index 5fc80d5660d04cd1c722b30eb20f667fc0061d7b..477bd4f5be9a1a8a942acc1fd2b625b82c1fb5c6 100644 (file)
@@ -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() {}
-
index a970a86408e90ad379aaeb31d8505037dc87691d..8ad19116e7bb13ba2b11af89f5121e45441c9949 100644 (file)
@@ -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}
     }
 }
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 (file)
index 0000000..a1a6112
--- /dev/null
@@ -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() {}