]> git.proxmox.com Git - rustc.git/blame - src/librustc_typeck/check/compare_method.rs
New upstream version 1.17.0+dfsg1
[rustc.git] / src / librustc_typeck / check / compare_method.rs
CommitLineData
85aaf69f
SL
1// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
32a655c1 11use rustc::hir::{self, ImplItemKind, TraitItemKind};
476ff2be 12use rustc::infer::{self, InferOk};
c30ab7b3 13use rustc::middle::free_region::FreeRegionMap;
8bb4bdeb 14use rustc::ty::{self, TyCtxt};
476ff2be 15use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
9e0c209e
SL
16use rustc::ty::error::{ExpectedFound, TypeError};
17use rustc::ty::subst::{Subst, Substs};
c30ab7b3 18use rustc::util::common::ErrorReported;
85aaf69f
SL
19
20use syntax::ast;
3157f602 21use syntax_pos::Span;
85aaf69f
SL
22
23use super::assoc;
c30ab7b3 24use super::{Inherited, FnCtxt};
476ff2be 25use astconv::ExplicitSelf;
85aaf69f
SL
26
27/// Checks that a method from an impl conforms to the signature of
28/// the same method as declared in the trait.
29///
30/// # Parameters
31///
32/// - impl_m: type of the method we are checking
33/// - impl_m_span: span to use for reporting errors
34/// - impl_m_body_id: id of the method body
35/// - trait_m: the method in the trait
36/// - impl_trait_ref: the TraitRef corresponding to the trait implementation
37
8bb4bdeb 38pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
476ff2be 39 impl_m: &ty::AssociatedItem,
a7813a04
XL
40 impl_m_span: Span,
41 impl_m_body_id: ast::NodeId,
476ff2be
SL
42 trait_m: &ty::AssociatedItem,
43 impl_trait_ref: ty::TraitRef<'tcx>,
c30ab7b3
SL
44 trait_item_span: Option<Span>,
45 old_broken_mode: bool) {
62682a34
SL
46 debug!("compare_impl_method(impl_trait_ref={:?})",
47 impl_trait_ref);
85aaf69f 48
8bb4bdeb 49 if let Err(ErrorReported) = compare_self_type(tcx,
c30ab7b3
SL
50 impl_m,
51 impl_m_span,
476ff2be
SL
52 trait_m,
53 impl_trait_ref) {
c30ab7b3 54 return;
85aaf69f
SL
55 }
56
8bb4bdeb 57 if let Err(ErrorReported) = compare_number_of_generics(tcx,
c30ab7b3
SL
58 impl_m,
59 impl_m_span,
60 trait_m,
61 trait_item_span) {
62 return;
63 }
9e0c209e 64
8bb4bdeb 65 if let Err(ErrorReported) = compare_number_of_method_arguments(tcx,
c30ab7b3
SL
66 impl_m,
67 impl_m_span,
68 trait_m,
69 trait_item_span) {
85aaf69f
SL
70 return;
71 }
72
8bb4bdeb 73 if let Err(ErrorReported) = compare_predicate_entailment(tcx,
c30ab7b3
SL
74 impl_m,
75 impl_m_span,
76 impl_m_body_id,
77 trait_m,
78 impl_trait_ref,
79 old_broken_mode) {
85aaf69f
SL
80 return;
81 }
c30ab7b3
SL
82}
83
8bb4bdeb 84fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
476ff2be 85 impl_m: &ty::AssociatedItem,
c30ab7b3
SL
86 impl_m_span: Span,
87 impl_m_body_id: ast::NodeId,
476ff2be
SL
88 trait_m: &ty::AssociatedItem,
89 impl_trait_ref: ty::TraitRef<'tcx>,
c30ab7b3
SL
90 old_broken_mode: bool)
91 -> Result<(), ErrorReported> {
476ff2be
SL
92 let trait_to_impl_substs = impl_trait_ref.substs;
93
94 let cause = ObligationCause {
95 span: impl_m_span,
96 body_id: impl_m_body_id,
97 code: ObligationCauseCode::CompareImplMethodObligation {
98 item_name: impl_m.name,
99 impl_item_def_id: impl_m.def_id,
100 trait_item_def_id: trait_m.def_id,
101 lint_id: if !old_broken_mode { Some(impl_m_body_id) } else { None },
102 },
103 };
85aaf69f
SL
104
105 // This code is best explained by example. Consider a trait:
106 //
107 // trait Trait<'t,T> {
108 // fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
109 // }
110 //
111 // And an impl:
112 //
113 // impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
114 // fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
115 // }
116 //
117 // We wish to decide if those two method types are compatible.
118 //
119 // We start out with trait_to_impl_substs, that maps the trait
120 // type parameters to impl type parameters. This is taken from the
121 // impl trait reference:
122 //
123 // trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
124 //
125 // We create a mapping `dummy_substs` that maps from the impl type
126 // parameters to fresh types and regions. For type parameters,
127 // this is the identity transform, but we could as well use any
128 // skolemized types. For regions, we convert from bound to free
129 // regions (Note: but only early-bound regions, i.e., those
130 // declared on the impl or used in type parameter bounds).
131 //
132 // impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
133 //
134 // Now we can apply skol_substs to the type of the impl method
135 // to yield a new function type in terms of our fresh, skolemized
136 // types:
137 //
138 // <'b> fn(t: &'i0 U0, m: &'b) -> Foo
139 //
140 // We now want to extract and substitute the type of the *trait*
141 // method and compare it. To do so, we must create a compound
142 // substitution by combining trait_to_impl_substs and
143 // impl_to_skol_substs, and also adding a mapping for the method
144 // type parameters. We extend the mapping to also include
145 // the method parameters.
146 //
147 // trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
148 //
149 // Applying this to the trait method type yields:
150 //
151 // <'a> fn(t: &'i0 U0, m: &'a) -> Foo
152 //
153 // This type is also the same but the name of the bound region ('a
154 // vs 'b). However, the normal subtyping rules on fn types handle
155 // this kind of equivalency just fine.
156 //
157 // We now use these substitutions to ensure that all declared bounds are
158 // satisfied by the implementation's method.
159 //
160 // We do this by creating a parameter environment which contains a
161 // substitution corresponding to impl_to_skol_substs. We then build
162 // trait_to_skol_substs and use it to convert the predicates contained
163 // in the trait_m.generics to the skolemized form.
164 //
165 // Finally we register each of these predicates as an obligation in
166 // a fresh FulfillmentCtxt, and invoke select_all_or_error.
167
168 // Create a parameter environment that represents the implementation's
169 // method.
32a655c1 170 let impl_m_node_id = tcx.hir.as_local_node_id(impl_m.def_id).unwrap();
b039eaaf 171 let impl_param_env = ty::ParameterEnvironment::for_item(tcx, impl_m_node_id);
85aaf69f
SL
172
173 // Create mapping from impl to skolemized.
174 let impl_to_skol_substs = &impl_param_env.free_substs;
175
176 // Create mapping from trait to skolemized.
c30ab7b3 177 let trait_to_skol_substs = impl_to_skol_substs.rebase_onto(tcx,
476ff2be 178 impl_m.container.id(),
c30ab7b3
SL
179 trait_to_impl_substs.subst(tcx,
180 impl_to_skol_substs));
62682a34
SL
181 debug!("compare_impl_method: trait_to_skol_substs={:?}",
182 trait_to_skol_substs);
85aaf69f 183
476ff2be
SL
184 let impl_m_generics = tcx.item_generics(impl_m.def_id);
185 let trait_m_generics = tcx.item_generics(trait_m.def_id);
186 let impl_m_predicates = tcx.item_predicates(impl_m.def_id);
187 let trait_m_predicates = tcx.item_predicates(trait_m.def_id);
188
c30ab7b3 189 // Check region bounds.
8bb4bdeb 190 check_region_bounds_on_impl_method(tcx,
c30ab7b3
SL
191 impl_m_span,
192 impl_m,
476ff2be
SL
193 &trait_m_generics,
194 &impl_m_generics,
c30ab7b3
SL
195 trait_to_skol_substs,
196 impl_to_skol_substs)?;
197
198 // Create obligations for each predicate declared by the impl
199 // definition in the context of the trait's parameter
200 // environment. We can't just use `impl_env.caller_bounds`,
201 // however, because we want to replace all late-bound regions with
202 // region variables.
476ff2be 203 let impl_predicates = tcx.item_predicates(impl_m_predicates.parent.unwrap());
c30ab7b3
SL
204 let mut hybrid_preds = impl_predicates.instantiate(tcx, impl_to_skol_substs);
205
206 debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds);
207
208 // This is the only tricky bit of the new way we check implementation methods
209 // We need to build a set of predicates where only the method-level bounds
210 // are from the trait and we assume all other bounds from the implementation
211 // to be previously satisfied.
212 //
213 // We then register the obligations from the impl_m and check to see
214 // if all constraints hold.
215 hybrid_preds.predicates
476ff2be 216 .extend(trait_m_predicates.instantiate_own(tcx, trait_to_skol_substs).predicates);
c30ab7b3
SL
217
218 // Construct trait parameter environment and then shift it into the skolemized viewpoint.
219 // The key step here is to update the caller_bounds's predicates to be
220 // the new hybrid bounds we computed.
221 let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_body_id);
222 let trait_param_env = impl_param_env.with_caller_bounds(hybrid_preds.predicates);
223 let trait_param_env = traits::normalize_param_env_or_error(tcx,
224 trait_param_env,
225 normalize_cause.clone());
226
8bb4bdeb
XL
227 tcx.infer_ctxt(trait_param_env, Reveal::UserFacing).enter(|infcx| {
228 let inh = Inherited::new(infcx);
c30ab7b3
SL
229 let infcx = &inh.infcx;
230 let fulfillment_cx = &inh.fulfillment_cx;
a7813a04 231
9e0c209e 232 debug!("compare_impl_method: caller_bounds={:?}",
c30ab7b3 233 infcx.parameter_environment.caller_bounds);
a7813a04
XL
234
235 let mut selcx = traits::SelectionContext::new(&infcx);
236
476ff2be 237 let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_skol_substs);
c30ab7b3
SL
238 let (impl_m_own_bounds, _) = infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
239 infer::HigherRankedType,
240 &ty::Binder(impl_m_own_bounds.predicates));
9e0c209e 241 for predicate in impl_m_own_bounds {
a7813a04
XL
242 let traits::Normalized { value: predicate, .. } =
243 traits::normalize(&mut selcx, normalize_cause.clone(), &predicate);
244
c30ab7b3 245 fulfillment_cx.borrow_mut().register_predicate_obligation(
a7813a04 246 &infcx,
476ff2be 247 traits::Obligation::new(cause.clone(), predicate));
a7813a04 248 }
85aaf69f 249
a7813a04
XL
250 // We now need to check that the signature of the impl method is
251 // compatible with that of the trait method. We do this by
252 // checking that `impl_fty <: trait_fty`.
253 //
254 // FIXME. Unfortunately, this doesn't quite work right now because
255 // associated type normalization is not integrated into subtype
256 // checks. For the comparison to be valid, we need to
257 // normalize the associated types in the impl/trait methods
258 // first. However, because function types bind regions, just
259 // calling `normalize_associated_types_in` would have no effect on
260 // any associated types appearing in the fn arguments or return
261 // type.
262
263 // Compute skolemized form of impl and trait method tys.
3157f602 264 let tcx = infcx.tcx;
476ff2be 265
8bb4bdeb 266 let m_sig = |method: &ty::AssociatedItem| {
476ff2be
SL
267 match tcx.item_type(method.def_id).sty {
268 ty::TyFnDef(_, _, f) => f,
269 _ => bug!()
270 }
271 };
3157f602
XL
272
273 let (impl_sig, _) =
274 infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
275 infer::HigherRankedType,
8bb4bdeb 276 &m_sig(impl_m));
3157f602
XL
277 let impl_sig =
278 impl_sig.subst(tcx, impl_to_skol_substs);
279 let impl_sig =
280 assoc::normalize_associated_types_in(&infcx,
c30ab7b3 281 &mut fulfillment_cx.borrow_mut(),
3157f602
XL
282 impl_m_span,
283 impl_m_body_id,
284 &impl_sig);
8bb4bdeb 285 let impl_fty = tcx.mk_fn_ptr(ty::Binder(impl_sig));
3157f602
XL
286 debug!("compare_impl_method: impl_fty={:?}", impl_fty);
287
288 let trait_sig = tcx.liberate_late_bound_regions(
289 infcx.parameter_environment.free_id_outlive,
8bb4bdeb 290 &m_sig(trait_m));
3157f602 291 let trait_sig =
9e0c209e 292 trait_sig.subst(tcx, trait_to_skol_substs);
3157f602
XL
293 let trait_sig =
294 assoc::normalize_associated_types_in(&infcx,
c30ab7b3 295 &mut fulfillment_cx.borrow_mut(),
3157f602
XL
296 impl_m_span,
297 impl_m_body_id,
298 &trait_sig);
8bb4bdeb 299 let trait_fty = tcx.mk_fn_ptr(ty::Binder(trait_sig));
3157f602
XL
300
301 debug!("compare_impl_method: trait_fty={:?}", trait_fty);
302
476ff2be 303 let sub_result = infcx.sub_types(false, &cause, impl_fty, trait_fty)
c30ab7b3
SL
304 .map(|InferOk { obligations, .. }| {
305 // FIXME(#32730) propagate obligations
306 assert!(obligations.is_empty());
307 });
308
309 if let Err(terr) = sub_result {
3157f602
XL
310 debug!("sub_types failed: impl ty {:?}, trait ty {:?}",
311 impl_fty,
a7813a04 312 trait_fty);
5bcae85e 313
c30ab7b3
SL
314 let (impl_err_span, trait_err_span) = extract_spans_for_error_reporting(&infcx,
315 &terr,
476ff2be 316 &cause,
c30ab7b3
SL
317 impl_m,
318 impl_sig,
319 trait_m,
320 trait_sig);
9e0c209e 321
476ff2be
SL
322 let cause = ObligationCause {
323 span: impl_err_span,
324 ..cause.clone()
325 };
9e0c209e 326
c30ab7b3 327 let mut diag = struct_span_err!(tcx.sess,
476ff2be 328 cause.span,
c30ab7b3
SL
329 E0053,
330 "method `{}` has an incompatible type for trait",
331 trait_m.name);
332
333 infcx.note_type_err(&mut diag,
476ff2be 334 &cause,
c30ab7b3
SL
335 trait_err_span.map(|sp| (sp, format!("type in trait"))),
336 Some(infer::ValuePairs::Types(ExpectedFound {
337 expected: trait_fty,
338 found: impl_fty,
339 })),
340 &terr);
5bcae85e 341 diag.emit();
c30ab7b3 342 return Err(ErrorReported);
a7813a04 343 }
85aaf69f 344
a7813a04
XL
345 // Check that all obligations are satisfied by the implementation's
346 // version.
c30ab7b3 347 if let Err(ref errors) = fulfillment_cx.borrow_mut().select_all_or_error(&infcx) {
3157f602 348 infcx.report_fulfillment_errors(errors);
c30ab7b3 349 return Err(ErrorReported);
85aaf69f 350 }
85aaf69f 351
a7813a04 352 // Finally, resolve all regions. This catches wily misuses of
c30ab7b3
SL
353 // lifetime parameters.
354 if old_broken_mode {
355 // FIXME(#18937) -- this is how the code used to
356 // work. This is buggy because the fulfillment cx creates
357 // region obligations that get overlooked. The right
358 // thing to do is the code below. But we keep this old
359 // pass around temporarily.
360 let mut free_regions = FreeRegionMap::new();
361 free_regions.relate_free_regions_from_predicates(
362 &infcx.parameter_environment.caller_bounds);
363 infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id);
364 } else {
476ff2be 365 let fcx = FnCtxt::new(&inh, Some(tcx.types.err), impl_m_body_id);
c30ab7b3 366 fcx.regionck_item(impl_m_body_id, impl_m_span, &[]);
85aaf69f
SL
367 }
368
c30ab7b3
SL
369 Ok(())
370 })
371}
372
8bb4bdeb 373fn check_region_bounds_on_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
c30ab7b3 374 span: Span,
476ff2be 375 impl_m: &ty::AssociatedItem,
8bb4bdeb
XL
376 trait_generics: &ty::Generics,
377 impl_generics: &ty::Generics,
c30ab7b3
SL
378 trait_to_skol_substs: &Substs<'tcx>,
379 impl_to_skol_substs: &Substs<'tcx>)
380 -> Result<(), ErrorReported> {
381 let trait_params = &trait_generics.regions[..];
382 let impl_params = &impl_generics.regions[..];
383
384 debug!("check_region_bounds_on_impl_method: \
385 trait_generics={:?} \
386 impl_generics={:?} \
387 trait_to_skol_substs={:?} \
388 impl_to_skol_substs={:?}",
389 trait_generics,
390 impl_generics,
391 trait_to_skol_substs,
392 impl_to_skol_substs);
393
394 // Must have same number of early-bound lifetime parameters.
395 // Unfortunately, if the user screws up the bounds, then this
396 // will change classification between early and late. E.g.,
397 // if in trait we have `<'a,'b:'a>`, and in impl we just have
398 // `<'a,'b>`, then we have 2 early-bound lifetime parameters
399 // in trait but 0 in the impl. But if we report "expected 2
400 // but found 0" it's confusing, because it looks like there
401 // are zero. Since I don't quite know how to phrase things at
402 // the moment, give a kind of vague error message.
403 if trait_params.len() != impl_params.len() {
8bb4bdeb 404 struct_span_err!(tcx.sess,
c30ab7b3
SL
405 span,
406 E0195,
407 "lifetime parameters or bounds on method `{}` do not match the \
408 trait declaration",
409 impl_m.name)
410 .span_label(span, &format!("lifetimes do not match trait"))
411 .emit();
412 return Err(ErrorReported);
85aaf69f 413 }
9e0c209e 414
c30ab7b3
SL
415 return Ok(());
416}
9e0c209e 417
c30ab7b3
SL
418fn extract_spans_for_error_reporting<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
419 terr: &TypeError,
476ff2be
SL
420 cause: &ObligationCause<'tcx>,
421 impl_m: &ty::AssociatedItem,
c30ab7b3 422 impl_sig: ty::FnSig<'tcx>,
476ff2be 423 trait_m: &ty::AssociatedItem,
c30ab7b3
SL
424 trait_sig: ty::FnSig<'tcx>)
425 -> (Span, Option<Span>) {
426 let tcx = infcx.tcx;
32a655c1
SL
427 let impl_m_node_id = tcx.hir.as_local_node_id(impl_m.def_id).unwrap();
428 let (impl_m_output, impl_m_iter) = match tcx.hir.expect_impl_item(impl_m_node_id).node {
c30ab7b3
SL
429 ImplItemKind::Method(ref impl_m_sig, _) => {
430 (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter())
431 }
432 _ => bug!("{:?} is not a method", impl_m),
433 };
434
435 match *terr {
436 TypeError::Mutability => {
32a655c1
SL
437 if let Some(trait_m_node_id) = tcx.hir.as_local_node_id(trait_m.def_id) {
438 let trait_m_iter = match tcx.hir.expect_trait_item(trait_m_node_id).node {
439 TraitItemKind::Method(ref trait_m_sig, _) => {
c30ab7b3
SL
440 trait_m_sig.decl.inputs.iter()
441 }
32a655c1 442 _ => bug!("{:?} is not a TraitItemKind::Method", trait_m),
c30ab7b3
SL
443 };
444
32a655c1
SL
445 impl_m_iter.zip(trait_m_iter).find(|&(ref impl_arg, ref trait_arg)| {
446 match (&impl_arg.node, &trait_arg.node) {
447 (&hir::TyRptr(_, ref impl_mt), &hir::TyRptr(_, ref trait_mt)) |
448 (&hir::TyPtr(ref impl_mt), &hir::TyPtr(ref trait_mt)) => {
449 impl_mt.mutbl != trait_mt.mutbl
450 }
451 _ => false,
452 }
453 }).map(|(ref impl_arg, ref trait_arg)| {
454 (impl_arg.span, Some(trait_arg.span))
455 })
456 .unwrap_or_else(|| (cause.span, tcx.hir.span_if_local(trait_m.def_id)))
c30ab7b3 457 } else {
32a655c1 458 (cause.span, tcx.hir.span_if_local(trait_m.def_id))
c30ab7b3
SL
459 }
460 }
461 TypeError::Sorts(ExpectedFound { .. }) => {
32a655c1 462 if let Some(trait_m_node_id) = tcx.hir.as_local_node_id(trait_m.def_id) {
c30ab7b3 463 let (trait_m_output, trait_m_iter) =
32a655c1
SL
464 match tcx.hir.expect_trait_item(trait_m_node_id).node {
465 TraitItemKind::Method(ref trait_m_sig, _) => {
c30ab7b3
SL
466 (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter())
467 }
32a655c1 468 _ => bug!("{:?} is not a TraitItemKind::Method", trait_m),
9e0c209e
SL
469 };
470
476ff2be
SL
471 let impl_iter = impl_sig.inputs().iter();
472 let trait_iter = trait_sig.inputs().iter();
c30ab7b3
SL
473 impl_iter.zip(trait_iter)
474 .zip(impl_m_iter)
475 .zip(trait_m_iter)
476 .filter_map(|(((impl_arg_ty, trait_arg_ty), impl_arg), trait_arg)| {
476ff2be 477 match infcx.sub_types(true, &cause, trait_arg_ty, impl_arg_ty) {
c30ab7b3 478 Ok(_) => None,
32a655c1 479 Err(_) => Some((impl_arg.span, Some(trait_arg.span))),
c30ab7b3
SL
480 }
481 })
482 .next()
483 .unwrap_or_else(|| {
476ff2be
SL
484 if infcx.sub_types(false, &cause, impl_sig.output(),
485 trait_sig.output())
c30ab7b3
SL
486 .is_err() {
487 (impl_m_output.span(), Some(trait_m_output.span()))
488 } else {
32a655c1 489 (cause.span, tcx.hir.span_if_local(trait_m.def_id))
c30ab7b3
SL
490 }
491 })
492 } else {
32a655c1 493 (cause.span, tcx.hir.span_if_local(trait_m.def_id))
c30ab7b3
SL
494 }
495 }
32a655c1 496 _ => (cause.span, tcx.hir.span_if_local(trait_m.def_id)),
c30ab7b3
SL
497 }
498}
499
8bb4bdeb 500fn compare_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
476ff2be 501 impl_m: &ty::AssociatedItem,
c30ab7b3 502 impl_m_span: Span,
476ff2be
SL
503 trait_m: &ty::AssociatedItem,
504 impl_trait_ref: ty::TraitRef<'tcx>)
c30ab7b3
SL
505 -> Result<(), ErrorReported>
506{
c30ab7b3
SL
507 // Try to give more informative error messages about self typing
508 // mismatches. Note that any mismatch will also be detected
509 // below, where we construct a canonical function type that
510 // includes the self parameter as a normal parameter. It's just
511 // that the error messages you get out of this code are a bit more
512 // inscrutable, particularly for cases where one method has no
513 // self.
476ff2be
SL
514
515 let self_string = |method: &ty::AssociatedItem| {
516 let untransformed_self_ty = match method.container {
517 ty::ImplContainer(_) => impl_trait_ref.self_ty(),
518 ty::TraitContainer(_) => tcx.mk_self_type()
519 };
520 let method_ty = tcx.item_type(method.def_id);
521 let self_arg_ty = *method_ty.fn_sig().input(0).skip_binder();
522 match ExplicitSelf::determine(untransformed_self_ty, self_arg_ty) {
523 ExplicitSelf::ByValue => "self".to_string(),
524 ExplicitSelf::ByReference(_, hir::MutImmutable) => "&self".to_string(),
525 ExplicitSelf::ByReference(_, hir::MutMutable) => "&mut self".to_string(),
526 _ => format!("self: {}", self_arg_ty)
527 }
528 };
529
530 match (trait_m.method_has_self_argument, impl_m.method_has_self_argument) {
531 (false, false) | (true, true) => {}
532
533 (false, true) => {
534 let self_descr = self_string(impl_m);
c30ab7b3
SL
535 let mut err = struct_span_err!(tcx.sess,
536 impl_m_span,
537 E0185,
538 "method `{}` has a `{}` declaration in the impl, but \
539 not in the trait",
540 trait_m.name,
476ff2be
SL
541 self_descr);
542 err.span_label(impl_m_span, &format!("`{}` used in impl", self_descr));
32a655c1 543 if let Some(span) = tcx.hir.span_if_local(trait_m.def_id) {
476ff2be 544 err.span_label(span, &format!("trait declared without `{}`", self_descr));
c30ab7b3
SL
545 }
546 err.emit();
547 return Err(ErrorReported);
548 }
476ff2be
SL
549
550 (true, false) => {
551 let self_descr = self_string(trait_m);
c30ab7b3
SL
552 let mut err = struct_span_err!(tcx.sess,
553 impl_m_span,
554 E0186,
555 "method `{}` has a `{}` declaration in the trait, but \
556 not in the impl",
557 trait_m.name,
476ff2be 558 self_descr);
c30ab7b3 559 err.span_label(impl_m_span,
476ff2be 560 &format!("expected `{}` in impl", self_descr));
32a655c1 561 if let Some(span) = tcx.hir.span_if_local(trait_m.def_id) {
476ff2be 562 err.span_label(span, &format!("`{}` used in trait", self_descr));
c30ab7b3
SL
563 }
564 err.emit();
565 return Err(ErrorReported);
566 }
c30ab7b3
SL
567 }
568
569 Ok(())
570}
571
8bb4bdeb 572fn compare_number_of_generics<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
476ff2be 573 impl_m: &ty::AssociatedItem,
c30ab7b3 574 impl_m_span: Span,
476ff2be 575 trait_m: &ty::AssociatedItem,
c30ab7b3
SL
576 trait_item_span: Option<Span>)
577 -> Result<(), ErrorReported> {
476ff2be
SL
578 let impl_m_generics = tcx.item_generics(impl_m.def_id);
579 let trait_m_generics = tcx.item_generics(trait_m.def_id);
580 let num_impl_m_type_params = impl_m_generics.types.len();
581 let num_trait_m_type_params = trait_m_generics.types.len();
c30ab7b3 582 if num_impl_m_type_params != num_trait_m_type_params {
32a655c1
SL
583 let impl_m_node_id = tcx.hir.as_local_node_id(impl_m.def_id).unwrap();
584 let span = match tcx.hir.expect_impl_item(impl_m_node_id).node {
c30ab7b3
SL
585 ImplItemKind::Method(ref impl_m_sig, _) => {
586 if impl_m_sig.generics.is_parameterized() {
587 impl_m_sig.generics.span
9e0c209e 588 } else {
c30ab7b3 589 impl_m_span
9e0c209e
SL
590 }
591 }
c30ab7b3
SL
592 _ => bug!("{:?} is not a method", impl_m),
593 };
594
595 let mut err = struct_span_err!(tcx.sess,
596 span,
597 E0049,
598 "method `{}` has {} type parameter{} but its trait \
599 declaration has {} type parameter{}",
600 trait_m.name,
601 num_impl_m_type_params,
602 if num_impl_m_type_params == 1 { "" } else { "s" },
603 num_trait_m_type_params,
604 if num_trait_m_type_params == 1 {
605 ""
606 } else {
607 "s"
608 });
9e0c209e 609
c30ab7b3
SL
610 let mut suffix = None;
611
612 if let Some(span) = trait_item_span {
613 err.span_label(span,
614 &format!("expected {}",
615 &if num_trait_m_type_params != 1 {
616 format!("{} type parameters", num_trait_m_type_params)
617 } else {
618 format!("{} type parameter", num_trait_m_type_params)
619 }));
620 } else {
621 suffix = Some(format!(", expected {}", num_trait_m_type_params));
622 }
623
624 err.span_label(span,
625 &format!("found {}{}",
626 &if num_impl_m_type_params != 1 {
627 format!("{} type parameters", num_impl_m_type_params)
628 } else {
629 format!("1 type parameter")
630 },
631 suffix.as_ref().map(|s| &s[..]).unwrap_or("")));
632
633 err.emit();
634
635 return Err(ErrorReported);
636 }
637
638 Ok(())
639}
640
8bb4bdeb 641fn compare_number_of_method_arguments<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
476ff2be 642 impl_m: &ty::AssociatedItem,
c30ab7b3 643 impl_m_span: Span,
476ff2be 644 trait_m: &ty::AssociatedItem,
c30ab7b3
SL
645 trait_item_span: Option<Span>)
646 -> Result<(), ErrorReported> {
476ff2be
SL
647 let m_fty = |method: &ty::AssociatedItem| {
648 match tcx.item_type(method.def_id).sty {
649 ty::TyFnDef(_, _, f) => f,
650 _ => bug!()
651 }
652 };
653 let impl_m_fty = m_fty(impl_m);
654 let trait_m_fty = m_fty(trait_m);
8bb4bdeb
XL
655 let trait_number_args = trait_m_fty.inputs().skip_binder().len();
656 let impl_number_args = impl_m_fty.inputs().skip_binder().len();
476ff2be 657 if trait_number_args != impl_number_args {
32a655c1 658 let trait_m_node_id = tcx.hir.as_local_node_id(trait_m.def_id);
c30ab7b3 659 let trait_span = if let Some(trait_id) = trait_m_node_id {
32a655c1
SL
660 match tcx.hir.expect_trait_item(trait_id).node {
661 TraitItemKind::Method(ref trait_m_sig, _) => {
c30ab7b3
SL
662 if let Some(arg) = trait_m_sig.decl.inputs.get(if trait_number_args > 0 {
663 trait_number_args - 1
664 } else {
665 0
666 }) {
32a655c1 667 Some(arg.span)
c30ab7b3
SL
668 } else {
669 trait_item_span
670 }
671 }
672 _ => bug!("{:?} is not a method", impl_m),
673 }
674 } else {
675 trait_item_span
676 };
32a655c1
SL
677 let impl_m_node_id = tcx.hir.as_local_node_id(impl_m.def_id).unwrap();
678 let impl_span = match tcx.hir.expect_impl_item(impl_m_node_id).node {
c30ab7b3
SL
679 ImplItemKind::Method(ref impl_m_sig, _) => {
680 if let Some(arg) = impl_m_sig.decl.inputs.get(if impl_number_args > 0 {
681 impl_number_args - 1
682 } else {
683 0
684 }) {
32a655c1 685 arg.span
9e0c209e 686 } else {
c30ab7b3 687 impl_m_span
9e0c209e
SL
688 }
689 }
c30ab7b3
SL
690 _ => bug!("{:?} is not a method", impl_m),
691 };
692 let mut err = struct_span_err!(tcx.sess,
693 impl_span,
694 E0050,
695 "method `{}` has {} parameter{} but the declaration in \
696 trait `{}` has {}",
697 trait_m.name,
698 impl_number_args,
699 if impl_number_args == 1 { "" } else { "s" },
700 tcx.item_path_str(trait_m.def_id),
701 trait_number_args);
702 if let Some(trait_span) = trait_span {
703 err.span_label(trait_span,
704 &format!("trait requires {}",
705 &if trait_number_args != 1 {
706 format!("{} parameters", trait_number_args)
707 } else {
708 format!("{} parameter", trait_number_args)
709 }));
9e0c209e 710 }
c30ab7b3
SL
711 err.span_label(impl_span,
712 &format!("expected {}, found {}",
713 &if trait_number_args != 1 {
714 format!("{} parameters", trait_number_args)
715 } else {
716 format!("{} parameter", trait_number_args)
717 },
718 impl_number_args));
719 err.emit();
720 return Err(ErrorReported);
9e0c209e 721 }
c30ab7b3
SL
722
723 Ok(())
85aaf69f 724}
d9579d0f 725
8bb4bdeb 726pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
476ff2be 727 impl_c: &ty::AssociatedItem,
a7813a04 728 impl_c_span: Span,
476ff2be
SL
729 trait_c: &ty::AssociatedItem,
730 impl_trait_ref: ty::TraitRef<'tcx>) {
c30ab7b3 731 debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
d9579d0f 732
8bb4bdeb 733 tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
a7813a04
XL
734 let mut fulfillment_cx = traits::FulfillmentContext::new();
735
736 // The below is for the most part highly similar to the procedure
737 // for methods above. It is simpler in many respects, especially
738 // because we shouldn't really have to deal with lifetimes or
739 // predicates. In fact some of this should probably be put into
740 // shared functions because of DRY violations...
476ff2be 741 let trait_to_impl_substs = impl_trait_ref.substs;
a7813a04
XL
742
743 // Create a parameter environment that represents the implementation's
744 // method.
32a655c1 745 let impl_c_node_id = tcx.hir.as_local_node_id(impl_c.def_id).unwrap();
a7813a04
XL
746 let impl_param_env = ty::ParameterEnvironment::for_item(tcx, impl_c_node_id);
747
748 // Create mapping from impl to skolemized.
749 let impl_to_skol_substs = &impl_param_env.free_substs;
750
751 // Create mapping from trait to skolemized.
c30ab7b3
SL
752 let trait_to_skol_substs = impl_to_skol_substs.rebase_onto(tcx,
753 impl_c.container.id(),
754 trait_to_impl_substs.subst(tcx,
755 impl_to_skol_substs));
a7813a04 756 debug!("compare_const_impl: trait_to_skol_substs={:?}",
c30ab7b3 757 trait_to_skol_substs);
a7813a04
XL
758
759 // Compute skolemized form of impl and trait const tys.
476ff2be
SL
760 let impl_ty = tcx.item_type(impl_c.def_id).subst(tcx, impl_to_skol_substs);
761 let trait_ty = tcx.item_type(trait_c.def_id).subst(tcx, trait_to_skol_substs);
762 let mut cause = ObligationCause::misc(impl_c_span, impl_c_node_id);
a7813a04
XL
763
764 let err = infcx.commit_if_ok(|_| {
a7813a04 765 // There is no "body" here, so just pass dummy id.
c30ab7b3
SL
766 let impl_ty = assoc::normalize_associated_types_in(&infcx,
767 &mut fulfillment_cx,
768 impl_c_span,
769 ast::CRATE_NODE_ID,
770 &impl_ty);
771
772 debug!("compare_const_impl: impl_ty={:?}", impl_ty);
773
774 let trait_ty = assoc::normalize_associated_types_in(&infcx,
775 &mut fulfillment_cx,
776 impl_c_span,
777 ast::CRATE_NODE_ID,
778 &trait_ty);
779
780 debug!("compare_const_impl: trait_ty={:?}", trait_ty);
a7813a04 781
476ff2be
SL
782 infcx.sub_types(false, &cause, impl_ty, trait_ty)
783 .map(|InferOk { obligations, value: () }| {
784 for obligation in obligations {
785 fulfillment_cx.register_predicate_obligation(&infcx, obligation);
786 }
787 })
a7813a04 788 });
d9579d0f 789
a7813a04 790 if let Err(terr) = err {
62682a34
SL
791 debug!("checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
792 impl_ty,
793 trait_ty);
5bcae85e
SL
794
795 // Locate the Span containing just the type of the offending impl
32a655c1 796 match tcx.hir.expect_impl_item(impl_c_node_id).node {
476ff2be 797 ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
c30ab7b3 798 _ => bug!("{:?} is not a impl const", impl_c),
5bcae85e
SL
799 }
800
c30ab7b3 801 let mut diag = struct_span_err!(tcx.sess,
476ff2be 802 cause.span,
c30ab7b3
SL
803 E0326,
804 "implemented const `{}` has an incompatible type for \
805 trait",
806 trait_c.name);
5bcae85e
SL
807
808 // Add a label to the Span containing just the type of the item
32a655c1
SL
809 let trait_c_node_id = tcx.hir.as_local_node_id(trait_c.def_id).unwrap();
810 let trait_c_span = match tcx.hir.expect_trait_item(trait_c_node_id).node {
811 TraitItemKind::Const(ref ty, _) => ty.span,
c30ab7b3 812 _ => bug!("{:?} is not a trait const", trait_c),
9e0c209e 813 };
5bcae85e 814
c30ab7b3 815 infcx.note_type_err(&mut diag,
476ff2be 816 &cause,
c30ab7b3
SL
817 Some((trait_c_span, format!("type in trait"))),
818 Some(infer::ValuePairs::Types(ExpectedFound {
819 expected: trait_ty,
820 found: impl_ty,
821 })),
822 &terr);
5bcae85e 823 diag.emit();
d9579d0f 824 }
a7813a04 825 });
d9579d0f 826}