]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #35877 - KiChjang:issue-35869, r=arielb1
authorbors <bors@rust-lang.org>
Sat, 27 Aug 2016 06:57:17 +0000 (23:57 -0700)
committerGitHub <noreply@github.com>
Sat, 27 Aug 2016 06:57:17 +0000 (23:57 -0700)
Fix ICE when arg types can't be found in impl/trait methods while comparing

Fixes #35869.

src/librustc_typeck/check/compare_method.rs
src/test/compile-fail/issue-35869.rs [new file with mode: 0644]

index 78476e814006fc6ecbfead875a005785997462b1..1604f34d575528209544e2eb2a52aa2890e67d01 100644 (file)
@@ -299,11 +299,10 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                                  impl_m_span,
                                                  impl_m_body_id,
                                                  &impl_sig);
-        let impl_args = impl_sig.inputs.clone();
         let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
             unsafety: impl_m.fty.unsafety,
             abi: impl_m.fty.abi,
-            sig: ty::Binder(impl_sig)
+            sig: ty::Binder(impl_sig.clone())
         }));
         debug!("compare_impl_method: impl_fty={:?}", impl_fty);
 
@@ -318,11 +317,10 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                                  impl_m_span,
                                                  impl_m_body_id,
                                                  &trait_sig);
-        let trait_args = trait_sig.inputs.clone();
         let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
             unsafety: trait_m.fty.unsafety,
             abi: trait_m.fty.abi,
-            sig: ty::Binder(trait_sig)
+            sig: ty::Binder(trait_sig.clone())
         }));
 
         debug!("compare_impl_method: trait_fty={:?}", trait_fty);
@@ -332,65 +330,9 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                    impl_fty,
                    trait_fty);
 
-            let impl_m_iter = match tcx.map.expect_impl_item(impl_m_node_id).node {
-                ImplItemKind::Method(ref impl_m_sig, _) => impl_m_sig.decl.inputs.iter(),
-                _ => bug!("{:?} is not a method", impl_m)
-            };
-
-            let (impl_err_span, trait_err_span) = match terr {
-                TypeError::Mutability => {
-                    if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) {
-                        let trait_m_iter = match tcx.map.expect_trait_item(trait_m_node_id).node {
-                            TraitItem_::MethodTraitItem(ref trait_m_sig, _) =>
-                                trait_m_sig.decl.inputs.iter(),
-                            _ => bug!("{:?} is not a MethodTraitItem", trait_m)
-                        };
-
-                        impl_m_iter.zip(trait_m_iter).find(|&(ref impl_arg, ref trait_arg)| {
-                            match (&impl_arg.ty.node, &trait_arg.ty.node) {
-                                (&Ty_::TyRptr(_, ref impl_mt), &Ty_::TyRptr(_, ref trait_mt)) |
-                                (&Ty_::TyPtr(ref impl_mt), &Ty_::TyPtr(ref trait_mt)) =>
-                                    impl_mt.mutbl != trait_mt.mutbl,
-                                _ => false
-                            }
-                        }).map(|(ref impl_arg, ref trait_arg)| {
-                            match (impl_arg.to_self(), trait_arg.to_self()) {
-                                (Some(impl_self), Some(trait_self)) =>
-                                    (impl_self.span, Some(trait_self.span)),
-                                (None, None) => (impl_arg.ty.span, Some(trait_arg.ty.span)),
-                                _ => bug!("impl and trait fns have different first args, \
-                                           impl: {:?}, trait: {:?}", impl_arg, trait_arg)
-                            }
-                        }).unwrap_or((origin.span(), tcx.map.span_if_local(trait_m.def_id)))
-                    } else {
-                        (origin.span(), tcx.map.span_if_local(trait_m.def_id))
-                    }
-                }
-                TypeError::Sorts(ExpectedFound { expected, found }) => {
-                    if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) {
-                        let trait_m_iter = match tcx.map.expect_trait_item(trait_m_node_id).node {
-                            TraitItem_::MethodTraitItem(ref trait_m_sig, _) =>
-                                trait_m_sig.decl.inputs.iter(),
-                            _ => bug!("{:?} is not a MethodTraitItem", trait_m)
-                        };
-                        let impl_iter = impl_args.iter();
-                        let trait_iter = trait_args.iter();
-                        let arg_idx = impl_iter.zip(trait_iter)
-                                               .position(|(impl_arg_ty, trait_arg_ty)| {
-                                                *impl_arg_ty == found && *trait_arg_ty == expected
-                                               }).unwrap();
-                        impl_m_iter.zip(trait_m_iter)
-                                   .nth(arg_idx)
-                                   .map(|(impl_arg, trait_arg)|
-                                        (impl_arg.ty.span, Some(trait_arg.ty.span)))
-                                   .unwrap_or(
-                                    (origin.span(), tcx.map.span_if_local(trait_m.def_id)))
-                    } else {
-                        (origin.span(), tcx.map.span_if_local(trait_m.def_id))
-                    }
-                }
-                _ => (origin.span(), tcx.map.span_if_local(trait_m.def_id))
-            };
+            let (impl_err_span, trait_err_span) =
+                extract_spans_for_error_reporting(&infcx, &terr, origin, impl_m,
+                    impl_sig, trait_m, trait_sig);
 
             let origin = TypeOrigin::MethodCompatCheck(impl_err_span);
 
@@ -479,6 +421,86 @@ fn check_region_bounds_on_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
         return true;
     }
+
+    fn extract_spans_for_error_reporting<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
+                                                         terr: &TypeError,
+                                                         origin: TypeOrigin,
+                                                         impl_m: &ty::Method,
+                                                         impl_sig: ty::FnSig<'tcx>,
+                                                         trait_m: &ty::Method,
+                                                         trait_sig: ty::FnSig<'tcx>)
+                                                        -> (Span, Option<Span>) {
+        let tcx = infcx.tcx;
+        let impl_m_node_id = tcx.map.as_local_node_id(impl_m.def_id).unwrap();
+        let (impl_m_output, impl_m_iter) = match tcx.map.expect_impl_item(impl_m_node_id).node {
+            ImplItemKind::Method(ref impl_m_sig, _) =>
+                (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()),
+            _ => bug!("{:?} is not a method", impl_m)
+        };
+
+        match *terr {
+            TypeError::Mutability => {
+                if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) {
+                    let trait_m_iter = match tcx.map.expect_trait_item(trait_m_node_id).node {
+                        TraitItem_::MethodTraitItem(ref trait_m_sig, _) =>
+                            trait_m_sig.decl.inputs.iter(),
+                        _ => bug!("{:?} is not a MethodTraitItem", trait_m)
+                    };
+
+                    impl_m_iter.zip(trait_m_iter).find(|&(ref impl_arg, ref trait_arg)| {
+                        match (&impl_arg.ty.node, &trait_arg.ty.node) {
+                            (&Ty_::TyRptr(_, ref impl_mt), &Ty_::TyRptr(_, ref trait_mt)) |
+                            (&Ty_::TyPtr(ref impl_mt), &Ty_::TyPtr(ref trait_mt)) =>
+                                impl_mt.mutbl != trait_mt.mutbl,
+                            _ => false
+                        }
+                    }).map(|(ref impl_arg, ref trait_arg)| {
+                        match (impl_arg.to_self(), trait_arg.to_self()) {
+                            (Some(impl_self), Some(trait_self)) =>
+                                (impl_self.span, Some(trait_self.span)),
+                            (None, None) => (impl_arg.ty.span, Some(trait_arg.ty.span)),
+                            _ => bug!("impl and trait fns have different first args, \
+                                       impl: {:?}, trait: {:?}", impl_arg, trait_arg)
+                        }
+                    }).unwrap_or((origin.span(), tcx.map.span_if_local(trait_m.def_id)))
+                } else {
+                    (origin.span(), tcx.map.span_if_local(trait_m.def_id))
+                }
+            }
+            TypeError::Sorts(ExpectedFound { .. }) => {
+                if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) {
+                    let (trait_m_output, trait_m_iter) =
+                    match tcx.map.expect_trait_item(trait_m_node_id).node {
+                        TraitItem_::MethodTraitItem(ref trait_m_sig, _) =>
+                            (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter()),
+                        _ => bug!("{:?} is not a MethodTraitItem", trait_m)
+                    };
+
+                    let impl_iter = impl_sig.inputs.iter();
+                    let trait_iter = trait_sig.inputs.iter();
+                    impl_iter.zip(trait_iter).zip(impl_m_iter).zip(trait_m_iter)
+                        .filter_map(|(((impl_arg_ty, trait_arg_ty), impl_arg), trait_arg)| {
+                            match infcx.sub_types(true, origin, trait_arg_ty, impl_arg_ty) {
+                                Ok(_) => None,
+                                Err(_) => Some((impl_arg.ty.span, Some(trait_arg.ty.span)))
+                            }
+                        })
+                        .next()
+                        .unwrap_or_else(|| {
+                            if infcx.sub_types(false, origin, impl_sig.output,
+                                               trait_sig.output).is_err() {
+                                (impl_m_output.span(), Some(trait_m_output.span()))
+                            } else {
+                                (origin.span(), tcx.map.span_if_local(trait_m.def_id))
+                            }
+                        })
+                } else {
+                    (origin.span(), tcx.map.span_if_local(trait_m.def_id))
+                }
+            }
+            _ => (origin.span(), tcx.map.span_if_local(trait_m.def_id))
+        }
+    }
 }
 
 pub fn compare_const_impl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
diff --git a/src/test/compile-fail/issue-35869.rs b/src/test/compile-fail/issue-35869.rs
new file mode 100644 (file)
index 0000000..8b7fc80
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(conservative_impl_trait)]
+
+trait Foo {
+    fn foo(fn(u8) -> ()); //~ NOTE type in trait
+    fn bar(Option<u8>); //~ NOTE type in trait
+    fn baz((u8, u16)); //~ NOTE type in trait
+    fn qux() -> u8; //~ NOTE type in trait
+}
+
+struct Bar;
+
+impl Foo for Bar {
+    fn foo(_: fn(u16) -> ()) {}
+    //~^ ERROR method `foo` has an incompatible type for trait
+    //~| NOTE expected u8
+    fn bar(_: Option<u16>) {}
+    //~^ ERROR method `bar` has an incompatible type for trait
+    //~| NOTE expected u8
+    fn baz(_: (u16, u16)) {}
+    //~^ ERROR method `baz` has an incompatible type for trait
+    //~| NOTE expected u8
+    fn qux() -> u16 { 5u16 }
+    //~^ ERROR method `qux` has an incompatible type for trait
+    //~| NOTE expected u8
+}
+
+fn main() {}