]> git.proxmox.com Git - rustc.git/blame - src/librustc_typeck/check/compare_method.rs
New upstream version 1.12.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
bd371182 11use middle::free_region::FreeRegionMap;
54a0048b 12use rustc::infer::{self, InferOk, TypeOrigin};
a7813a04 13use rustc::ty;
5bcae85e
SL
14use rustc::traits::{self, Reveal};
15use rustc::ty::error::ExpectedFound;
54a0048b 16use rustc::ty::subst::{self, Subst, Substs, VecPerParamSpace};
5bcae85e
SL
17use rustc::hir::map::Node;
18use rustc::hir::{ImplItemKind, TraitItem_};
85aaf69f
SL
19
20use syntax::ast;
3157f602 21use syntax_pos::Span;
85aaf69f 22
a7813a04 23use CrateCtxt;
85aaf69f
SL
24use super::assoc;
25
26/// Checks that a method from an impl conforms to the signature of
27/// the same method as declared in the trait.
28///
29/// # Parameters
30///
31/// - impl_m: type of the method we are checking
32/// - impl_m_span: span to use for reporting errors
33/// - impl_m_body_id: id of the method body
34/// - trait_m: the method in the trait
35/// - impl_trait_ref: the TraitRef corresponding to the trait implementation
36
a7813a04
XL
37pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
38 impl_m: &ty::Method<'tcx>,
39 impl_m_span: Span,
40 impl_m_body_id: ast::NodeId,
41 trait_m: &ty::Method<'tcx>,
42 impl_trait_ref: &ty::TraitRef<'tcx>) {
62682a34
SL
43 debug!("compare_impl_method(impl_trait_ref={:?})",
44 impl_trait_ref);
85aaf69f 45
62682a34
SL
46 debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}",
47 impl_trait_ref);
85aaf69f 48
a7813a04 49 let tcx = ccx.tcx;
85aaf69f
SL
50
51 let trait_to_impl_substs = &impl_trait_ref.substs;
52
53 // Try to give more informative error messages about self typing
54 // mismatches. Note that any mismatch will also be detected
55 // below, where we construct a canonical function type that
56 // includes the self parameter as a normal parameter. It's just
57 // that the error messages you get out of this code are a bit more
58 // inscrutable, particularly for cases where one method has no
59 // self.
60 match (&trait_m.explicit_self, &impl_m.explicit_self) {
9cc50fc6
SL
61 (&ty::ExplicitSelfCategory::Static,
62 &ty::ExplicitSelfCategory::Static) => {}
63 (&ty::ExplicitSelfCategory::Static, _) => {
5bcae85e 64 let mut err = struct_span_err!(tcx.sess, impl_m_span, E0185,
85aaf69f
SL
65 "method `{}` has a `{}` declaration in the impl, \
66 but not in the trait",
62682a34
SL
67 trait_m.name,
68 impl_m.explicit_self);
5bcae85e
SL
69 err.span_label(impl_m_span, &format!("`{}` used in impl",
70 impl_m.explicit_self));
71 if let Some(span) = tcx.map.span_if_local(trait_m.def_id) {
72 err.span_label(span, &format!("trait declared without `{}`",
73 impl_m.explicit_self));
74 }
75 err.emit();
85aaf69f
SL
76 return;
77 }
9cc50fc6 78 (_, &ty::ExplicitSelfCategory::Static) => {
5bcae85e 79 let mut err = struct_span_err!(tcx.sess, impl_m_span, E0186,
85aaf69f
SL
80 "method `{}` has a `{}` declaration in the trait, \
81 but not in the impl",
62682a34
SL
82 trait_m.name,
83 trait_m.explicit_self);
5bcae85e
SL
84 err.span_label(impl_m_span, &format!("expected `{}` in impl",
85 trait_m.explicit_self));
86 if let Some(span) = tcx.map.span_if_local(trait_m.def_id) {
87 err.span_label(span, & format!("`{}` used in trait",
88 trait_m.explicit_self));
89 }
90 err.emit();
85aaf69f
SL
91 return;
92 }
93 _ => {
94 // Let the type checker catch other errors below
95 }
96 }
97
98 let num_impl_m_type_params = impl_m.generics.types.len(subst::FnSpace);
99 let num_trait_m_type_params = trait_m.generics.types.len(subst::FnSpace);
100 if num_impl_m_type_params != num_trait_m_type_params {
101 span_err!(tcx.sess, impl_m_span, E0049,
102 "method `{}` has {} type parameter{} \
103 but its trait declaration has {} type parameter{}",
c1a9b12d 104 trait_m.name,
85aaf69f
SL
105 num_impl_m_type_params,
106 if num_impl_m_type_params == 1 {""} else {"s"},
107 num_trait_m_type_params,
108 if num_trait_m_type_params == 1 {""} else {"s"});
109 return;
110 }
111
112 if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() {
113 span_err!(tcx.sess, impl_m_span, E0050,
114 "method `{}` has {} parameter{} \
115 but the declaration in trait `{}` has {}",
c1a9b12d 116 trait_m.name,
85aaf69f
SL
117 impl_m.fty.sig.0.inputs.len(),
118 if impl_m.fty.sig.0.inputs.len() == 1 {""} else {"s"},
c1a9b12d 119 tcx.item_path_str(trait_m.def_id),
85aaf69f
SL
120 trait_m.fty.sig.0.inputs.len());
121 return;
122 }
123
124 // This code is best explained by example. Consider a trait:
125 //
126 // trait Trait<'t,T> {
127 // fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
128 // }
129 //
130 // And an impl:
131 //
132 // impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
133 // fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
134 // }
135 //
136 // We wish to decide if those two method types are compatible.
137 //
138 // We start out with trait_to_impl_substs, that maps the trait
139 // type parameters to impl type parameters. This is taken from the
140 // impl trait reference:
141 //
142 // trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
143 //
144 // We create a mapping `dummy_substs` that maps from the impl type
145 // parameters to fresh types and regions. For type parameters,
146 // this is the identity transform, but we could as well use any
147 // skolemized types. For regions, we convert from bound to free
148 // regions (Note: but only early-bound regions, i.e., those
149 // declared on the impl or used in type parameter bounds).
150 //
151 // impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
152 //
153 // Now we can apply skol_substs to the type of the impl method
154 // to yield a new function type in terms of our fresh, skolemized
155 // types:
156 //
157 // <'b> fn(t: &'i0 U0, m: &'b) -> Foo
158 //
159 // We now want to extract and substitute the type of the *trait*
160 // method and compare it. To do so, we must create a compound
161 // substitution by combining trait_to_impl_substs and
162 // impl_to_skol_substs, and also adding a mapping for the method
163 // type parameters. We extend the mapping to also include
164 // the method parameters.
165 //
166 // trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
167 //
168 // Applying this to the trait method type yields:
169 //
170 // <'a> fn(t: &'i0 U0, m: &'a) -> Foo
171 //
172 // This type is also the same but the name of the bound region ('a
173 // vs 'b). However, the normal subtyping rules on fn types handle
174 // this kind of equivalency just fine.
175 //
176 // We now use these substitutions to ensure that all declared bounds are
177 // satisfied by the implementation's method.
178 //
179 // We do this by creating a parameter environment which contains a
180 // substitution corresponding to impl_to_skol_substs. We then build
181 // trait_to_skol_substs and use it to convert the predicates contained
182 // in the trait_m.generics to the skolemized form.
183 //
184 // Finally we register each of these predicates as an obligation in
185 // a fresh FulfillmentCtxt, and invoke select_all_or_error.
186
187 // Create a parameter environment that represents the implementation's
188 // method.
b039eaaf
SL
189 let impl_m_node_id = tcx.map.as_local_node_id(impl_m.def_id).unwrap();
190 let impl_param_env = ty::ParameterEnvironment::for_item(tcx, impl_m_node_id);
85aaf69f
SL
191
192 // Create mapping from impl to skolemized.
193 let impl_to_skol_substs = &impl_param_env.free_substs;
194
195 // Create mapping from trait to skolemized.
196 let trait_to_skol_substs =
197 trait_to_impl_substs
a7813a04 198 .subst(tcx, impl_to_skol_substs).clone()
85aaf69f 199 .with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(),
54a0048b 200 impl_to_skol_substs.regions.get_slice(subst::FnSpace).to_vec());
62682a34
SL
201 debug!("compare_impl_method: trait_to_skol_substs={:?}",
202 trait_to_skol_substs);
85aaf69f
SL
203
204 // Check region bounds. FIXME(@jroesch) refactor this away when removing
205 // ParamBounds.
a7813a04 206 if !check_region_bounds_on_impl_method(ccx,
85aaf69f
SL
207 impl_m_span,
208 impl_m,
209 &trait_m.generics,
210 &impl_m.generics,
211 &trait_to_skol_substs,
212 impl_to_skol_substs) {
213 return;
214 }
215
5bcae85e 216 tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|mut infcx| {
a7813a04
XL
217 let mut fulfillment_cx = traits::FulfillmentContext::new();
218
219 // Normalize the associated types in the trait_bounds.
220 let trait_bounds = trait_m.predicates.instantiate(tcx, &trait_to_skol_substs);
221
222 // Create obligations for each predicate declared by the impl
223 // definition in the context of the trait's parameter
224 // environment. We can't just use `impl_env.caller_bounds`,
225 // however, because we want to replace all late-bound regions with
226 // region variables.
227 let impl_bounds =
228 impl_m.predicates.instantiate(tcx, impl_to_skol_substs);
229
230 debug!("compare_impl_method: impl_bounds={:?}", impl_bounds);
231
232 // Obtain the predicate split predicate sets for each.
233 let trait_pred = trait_bounds.predicates.split();
234 let impl_pred = impl_bounds.predicates.split();
235
236 // This is the only tricky bit of the new way we check implementation methods
237 // We need to build a set of predicates where only the FnSpace bounds
238 // are from the trait and we assume all other bounds from the implementation
239 // to be previously satisfied.
240 //
241 // We then register the obligations from the impl_m and check to see
242 // if all constraints hold.
243 let hybrid_preds = VecPerParamSpace::new(
244 impl_pred.types,
245 impl_pred.selfs,
246 trait_pred.fns
247 );
248
249 // Construct trait parameter environment and then shift it into the skolemized viewpoint.
250 // The key step here is to update the caller_bounds's predicates to be
251 // the new hybrid bounds we computed.
252 let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_body_id);
253 let trait_param_env = impl_param_env.with_caller_bounds(hybrid_preds.into_vec());
254 let trait_param_env = traits::normalize_param_env_or_error(tcx,
255 trait_param_env,
256 normalize_cause.clone());
257 // FIXME(@jroesch) this seems ugly, but is a temporary change
258 infcx.parameter_environment = trait_param_env;
259
260 debug!("compare_impl_method: trait_bounds={:?}",
261 infcx.parameter_environment.caller_bounds);
262
263 let mut selcx = traits::SelectionContext::new(&infcx);
264
265 let (impl_pred_fns, _) =
266 infcx.replace_late_bound_regions_with_fresh_var(
267 impl_m_span,
268 infer::HigherRankedType,
269 &ty::Binder(impl_pred.fns));
270 for predicate in impl_pred_fns {
271 let traits::Normalized { value: predicate, .. } =
272 traits::normalize(&mut selcx, normalize_cause.clone(), &predicate);
273
274 let cause = traits::ObligationCause {
275 span: impl_m_span,
276 body_id: impl_m_body_id,
277 code: traits::ObligationCauseCode::CompareImplMethodObligation
278 };
279
280 fulfillment_cx.register_predicate_obligation(
281 &infcx,
282 traits::Obligation::new(cause, predicate));
283 }
85aaf69f 284
a7813a04
XL
285 // We now need to check that the signature of the impl method is
286 // compatible with that of the trait method. We do this by
287 // checking that `impl_fty <: trait_fty`.
288 //
289 // FIXME. Unfortunately, this doesn't quite work right now because
290 // associated type normalization is not integrated into subtype
291 // checks. For the comparison to be valid, we need to
292 // normalize the associated types in the impl/trait methods
293 // first. However, because function types bind regions, just
294 // calling `normalize_associated_types_in` would have no effect on
295 // any associated types appearing in the fn arguments or return
296 // type.
297
298 // Compute skolemized form of impl and trait method tys.
3157f602
XL
299 let tcx = infcx.tcx;
300 let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
301
302 let (impl_sig, _) =
303 infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
304 infer::HigherRankedType,
305 &impl_m.fty.sig);
306 let impl_sig =
307 impl_sig.subst(tcx, impl_to_skol_substs);
308 let impl_sig =
309 assoc::normalize_associated_types_in(&infcx,
310 &mut fulfillment_cx,
311 impl_m_span,
312 impl_m_body_id,
313 &impl_sig);
314 let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
315 unsafety: impl_m.fty.unsafety,
316 abi: impl_m.fty.abi,
317 sig: ty::Binder(impl_sig)
318 }));
319 debug!("compare_impl_method: impl_fty={:?}", impl_fty);
320
321 let trait_sig = tcx.liberate_late_bound_regions(
322 infcx.parameter_environment.free_id_outlive,
323 &trait_m.fty.sig);
324 let trait_sig =
325 trait_sig.subst(tcx, &trait_to_skol_substs);
326 let trait_sig =
327 assoc::normalize_associated_types_in(&infcx,
328 &mut fulfillment_cx,
329 impl_m_span,
330 impl_m_body_id,
331 &trait_sig);
332 let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
333 unsafety: trait_m.fty.unsafety,
334 abi: trait_m.fty.abi,
335 sig: ty::Binder(trait_sig)
336 }));
337
338 debug!("compare_impl_method: trait_fty={:?}", trait_fty);
339
340 if let Err(terr) = infcx.sub_types(false, origin, impl_fty, trait_fty) {
341 debug!("sub_types failed: impl ty {:?}, trait ty {:?}",
342 impl_fty,
a7813a04 343 trait_fty);
5bcae85e
SL
344
345 let mut diag = struct_span_err!(
346 tcx.sess, origin.span(), E0053,
347 "method `{}` has an incompatible type for trait", trait_m.name
348 );
349 infcx.note_type_err(
350 &mut diag, origin,
351 Some(infer::ValuePairs::Types(ExpectedFound {
352 expected: trait_fty,
353 found: impl_fty
354 })), &terr
355 );
356 diag.emit();
3157f602 357 return
a7813a04 358 }
85aaf69f 359
a7813a04
XL
360 // Check that all obligations are satisfied by the implementation's
361 // version.
3157f602
XL
362 if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) {
363 infcx.report_fulfillment_errors(errors);
364 return
85aaf69f 365 }
85aaf69f 366
a7813a04
XL
367 // Finally, resolve all regions. This catches wily misuses of
368 // lifetime parameters. We have to build up a plausible lifetime
369 // environment based on what we find in the trait. We could also
370 // include the obligations derived from the method argument types,
371 // but I don't think it's necessary -- after all, those are still
372 // in effect when type-checking the body, and all the
373 // where-clauses in the header etc should be implied by the trait
374 // anyway, so it shouldn't be needed there either. Anyway, we can
375 // always add more relations later (it's backwards compat).
376 let mut free_regions = FreeRegionMap::new();
377 free_regions.relate_free_regions_from_predicates(
378 &infcx.parameter_environment.caller_bounds);
379
380 infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id);
381 });
85aaf69f 382
a7813a04
XL
383 fn check_region_bounds_on_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
384 span: Span,
385 impl_m: &ty::Method<'tcx>,
386 trait_generics: &ty::Generics<'tcx>,
387 impl_generics: &ty::Generics<'tcx>,
388 trait_to_skol_substs: &Substs<'tcx>,
389 impl_to_skol_substs: &Substs<'tcx>)
390 -> bool
85aaf69f
SL
391 {
392
393 let trait_params = trait_generics.regions.get_slice(subst::FnSpace);
394 let impl_params = impl_generics.regions.get_slice(subst::FnSpace);
395
396 debug!("check_region_bounds_on_impl_method: \
62682a34
SL
397 trait_generics={:?} \
398 impl_generics={:?} \
399 trait_to_skol_substs={:?} \
400 impl_to_skol_substs={:?}",
401 trait_generics,
402 impl_generics,
403 trait_to_skol_substs,
404 impl_to_skol_substs);
85aaf69f
SL
405
406 // Must have same number of early-bound lifetime parameters.
407 // Unfortunately, if the user screws up the bounds, then this
408 // will change classification between early and late. E.g.,
409 // if in trait we have `<'a,'b:'a>`, and in impl we just have
410 // `<'a,'b>`, then we have 2 early-bound lifetime parameters
411 // in trait but 0 in the impl. But if we report "expected 2
412 // but found 0" it's confusing, because it looks like there
413 // are zero. Since I don't quite know how to phrase things at
414 // the moment, give a kind of vague error message.
415 if trait_params.len() != impl_params.len() {
a7813a04 416 span_err!(ccx.tcx.sess, span, E0195,
85aaf69f
SL
417 "lifetime parameters or bounds on method `{}` do \
418 not match the trait declaration",
c1a9b12d 419 impl_m.name);
85aaf69f
SL
420 return false;
421 }
422
423 return true;
424 }
425}
d9579d0f 426
a7813a04
XL
427pub fn compare_const_impl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
428 impl_c: &ty::AssociatedConst<'tcx>,
429 impl_c_span: Span,
430 trait_c: &ty::AssociatedConst<'tcx>,
431 impl_trait_ref: &ty::TraitRef<'tcx>) {
62682a34
SL
432 debug!("compare_const_impl(impl_trait_ref={:?})",
433 impl_trait_ref);
d9579d0f 434
a7813a04 435 let tcx = ccx.tcx;
5bcae85e 436 tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
a7813a04
XL
437 let mut fulfillment_cx = traits::FulfillmentContext::new();
438
439 // The below is for the most part highly similar to the procedure
440 // for methods above. It is simpler in many respects, especially
441 // because we shouldn't really have to deal with lifetimes or
442 // predicates. In fact some of this should probably be put into
443 // shared functions because of DRY violations...
444 let trait_to_impl_substs = &impl_trait_ref.substs;
445
446 // Create a parameter environment that represents the implementation's
447 // method.
448 let impl_c_node_id = tcx.map.as_local_node_id(impl_c.def_id).unwrap();
449 let impl_param_env = ty::ParameterEnvironment::for_item(tcx, impl_c_node_id);
450
451 // Create mapping from impl to skolemized.
452 let impl_to_skol_substs = &impl_param_env.free_substs;
453
454 // Create mapping from trait to skolemized.
455 let trait_to_skol_substs =
456 trait_to_impl_substs
457 .subst(tcx, impl_to_skol_substs).clone()
458 .with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(),
459 impl_to_skol_substs.regions.get_slice(subst::FnSpace).to_vec());
460 debug!("compare_const_impl: trait_to_skol_substs={:?}",
461 trait_to_skol_substs);
462
463 // Compute skolemized form of impl and trait const tys.
464 let impl_ty = impl_c.ty.subst(tcx, impl_to_skol_substs);
465 let trait_ty = trait_c.ty.subst(tcx, &trait_to_skol_substs);
5bcae85e 466 let mut origin = TypeOrigin::Misc(impl_c_span);
a7813a04
XL
467
468 let err = infcx.commit_if_ok(|_| {
a7813a04
XL
469 // There is no "body" here, so just pass dummy id.
470 let impl_ty =
471 assoc::normalize_associated_types_in(&infcx,
472 &mut fulfillment_cx,
473 impl_c_span,
474 0,
475 &impl_ty);
476
477 debug!("compare_const_impl: impl_ty={:?}",
478 impl_ty);
479
480 let trait_ty =
481 assoc::normalize_associated_types_in(&infcx,
482 &mut fulfillment_cx,
483 impl_c_span,
484 0,
485 &trait_ty);
486
487 debug!("compare_const_impl: trait_ty={:?}",
488 trait_ty);
489
490 infcx.sub_types(false, origin, impl_ty, trait_ty)
491 .map(|InferOk { obligations, .. }| {
492 // FIXME(#32730) propagate obligations
493 assert!(obligations.is_empty())
494 })
495 });
d9579d0f 496
a7813a04 497 if let Err(terr) = err {
62682a34
SL
498 debug!("checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
499 impl_ty,
500 trait_ty);
5bcae85e
SL
501
502 // Locate the Span containing just the type of the offending impl
503 if let Some(impl_trait_node) = tcx.map.get_if_local(impl_c.def_id) {
504 if let Node::NodeImplItem(impl_trait_item) = impl_trait_node {
505 if let ImplItemKind::Const(ref ty, _) = impl_trait_item.node {
506 origin = TypeOrigin::Misc(ty.span);
507 }
508 }
509 }
510
511 let mut diag = struct_span_err!(
512 tcx.sess, origin.span(), E0326,
513 "implemented const `{}` has an incompatible type for trait",
514 trait_c.name
515 );
516
517 // Add a label to the Span containing just the type of the item
518 if let Some(orig_trait_node) = tcx.map.get_if_local(trait_c.def_id) {
519 if let Node::NodeTraitItem(orig_trait_item) = orig_trait_node {
520 if let TraitItem_::ConstTraitItem(ref ty, _) = orig_trait_item.node {
521 diag.span_label(ty.span, &format!("original trait requirement"));
522 }
523 }
524 }
525
526 infcx.note_type_err(
527 &mut diag, origin,
528 Some(infer::ValuePairs::Types(ExpectedFound {
529 expected: trait_ty,
530 found: impl_ty
531 })), &terr
532 );
533 diag.emit();
d9579d0f 534 }
a7813a04 535 });
d9579d0f 536}