diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 78476e81400..1604f34d575 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -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 @@ pub fn compare_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) { + 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 index 00000000000..8b7fc80bdb6 --- /dev/null +++ b/src/test/compile-fail/issue-35869.rs @@ -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 or the MIT license +// , 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); //~ 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) {} + //~^ 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() {}