1 // Copyright 2012-2013 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.
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.
13 Error Reporting Code for the inference engine
15 Because of the way inference, and in particular region inference,
16 works, it often happens that errors are not detected until far after
17 the relevant line of code has been type-checked. Therefore, there is
18 an elaborate system to track why a particular constraint in the
19 inference graph arose so that we can explain to the user what gave
20 rise to a particular error.
22 The basis of the system are the "origin" types. An "origin" is the
23 reason that a constraint or inference variable arose. There are
24 different "origin" enums for different kinds of constraints/variables
25 (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
26 a span, but also more information so that we can generate a meaningful
29 Having a catalogue of all the different reasons an error can arise is
30 also useful for other reasons, like cross-referencing FAQs etc, though
31 we are not really taking advantage of this yet.
35 Region inference is particularly tricky because it always succeeds "in
36 the moment" and simply registers a constraint. Then, at the end, we
37 can compute the full graph and report errors, so we need to be able to
38 store and later report what gave rise to the conflicting constraints.
42 Determing whether `T1 <: T2` often involves a number of subtypes and
43 subconstraints along the way. A "TypeTrace" is an extended version
44 of an origin that traces the types and other values that were being
45 compared. It is not necessarily comprehensive (in fact, at the time of
46 this writing it only tracks the root values being compared) but I'd
47 like to extend it to include significant "waypoints". For example, if
48 you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
49 <: T4` fails, I'd like the trace to include enough information to say
50 "in the 2nd element of the tuple". Similarly, failures when comparing
51 arguments or return types in fn types should be able to cite the
52 specific position, etc.
56 Of course, there is still a LOT of code in typeck that has yet to be
57 ported to this system, and which relies on string concatenation at the
58 time of error detection.
62 use std::collections::HashSet;
66 use middle::ty::{Region, ReFree};
67 use middle::typeck::infer;
68 use middle::typeck::infer::InferCtxt;
69 use middle::typeck::infer::TypeTrace;
70 use middle::typeck::infer::SubregionOrigin;
71 use middle::typeck::infer::RegionVariableOrigin;
72 use middle::typeck::infer::ValuePairs;
73 use middle::typeck::infer::region_inference::RegionResolutionError;
74 use middle::typeck::infer::region_inference::ConcreteFailure;
75 use middle::typeck::infer::region_inference::SubSupConflict;
76 use middle::typeck::infer::region_inference::SupSupConflict;
77 use middle::typeck::infer::region_inference::ParamBoundFailure;
78 use middle::typeck::infer::region_inference::ProcessedErrors;
79 use middle::typeck::infer::region_inference::SameRegions;
80 use std::cell::{Cell, RefCell};
81 use std::char::from_u32;
83 use std::string::String;
86 use syntax::ast_util::{name_to_dummy_lifetime, PostExpansionMethod};
87 use syntax::owned_slice::OwnedSlice;
89 use syntax::parse::token;
90 use syntax::print::pprust;
92 use util::ppaux::bound_region_to_string;
93 use util::ppaux::note_and_explain_region;
95 // Note: only import UserString, not Repr, since user-facing error
96 // messages shouldn't include debug serializations.
97 use util::ppaux::UserString;
99 pub trait ErrorReporting {
100 fn report_region_errors(&self,
101 errors: &Vec<RegionResolutionError>);
103 fn process_errors(&self, errors: &Vec<RegionResolutionError>)
104 -> Vec<RegionResolutionError>;
106 fn report_type_error(&self, trace: TypeTrace, terr: &ty::type_err);
108 fn report_and_explain_type_error(&self,
110 terr: &ty::type_err);
112 fn values_str(&self, values: &ValuePairs) -> Option<String>;
114 fn expected_found_str<T:UserString+Resolvable>(
116 exp_found: &ty::expected_found<T>)
119 fn report_concrete_failure(&self,
120 origin: SubregionOrigin,
124 fn report_param_bound_failure(&self,
125 origin: SubregionOrigin,
126 param_ty: ty::ParamTy,
130 fn report_sub_sup_conflict(&self,
131 var_origin: RegionVariableOrigin,
132 sub_origin: SubregionOrigin,
134 sup_origin: SubregionOrigin,
137 fn report_sup_sup_conflict(&self,
138 var_origin: RegionVariableOrigin,
139 origin1: SubregionOrigin,
141 origin2: SubregionOrigin,
144 fn report_processed_errors(&self,
145 var_origin: &[RegionVariableOrigin],
146 trace_origin: &[(TypeTrace, ty::type_err)],
147 same_regions: &[SameRegions]);
149 fn give_suggestion(&self, same_regions: &[SameRegions]);
152 trait ErrorReportingHelpers {
153 fn report_inference_failure(&self,
154 var_origin: RegionVariableOrigin);
156 fn note_region_origin(&self,
157 origin: &SubregionOrigin);
159 fn give_expl_lifetime_param(&self,
161 fn_style: ast::FnStyle,
163 opt_explicit_self: Option<&ast::ExplicitSelf_>,
164 generics: &ast::Generics,
165 span: codemap::Span);
168 impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> {
169 fn report_region_errors(&self,
170 errors: &Vec<RegionResolutionError>) {
171 let p_errors = self.process_errors(errors);
172 let errors = if p_errors.is_empty() { errors } else { &p_errors };
173 for error in errors.iter() {
174 match error.clone() {
175 ConcreteFailure(origin, sub, sup) => {
176 self.report_concrete_failure(origin, sub, sup);
179 ParamBoundFailure(origin, param_ty, sub, sups) => {
180 self.report_param_bound_failure(origin, param_ty, sub, sups);
183 SubSupConflict(var_origin,
185 sup_origin, sup_r) => {
186 self.report_sub_sup_conflict(var_origin,
191 SupSupConflict(var_origin,
194 self.report_sup_sup_conflict(var_origin,
199 ProcessedErrors(ref var_origins,
201 ref same_regions) => {
202 if !same_regions.is_empty() {
203 self.report_processed_errors(var_origins.as_slice(),
204 trace_origins.as_slice(),
205 same_regions.as_slice());
212 // This method goes through all the errors and try to group certain types
213 // of error together, for the purpose of suggesting explicit lifetime
214 // parameters to the user. This is done so that we can have a more
215 // complete view of what lifetimes should be the same.
216 // If the return value is an empty vector, it means that processing
217 // failed (so the return value of this method should not be used)
218 fn process_errors(&self, errors: &Vec<RegionResolutionError>)
219 -> Vec<RegionResolutionError> {
220 debug!("process_errors()");
221 let mut var_origins = Vec::new();
222 let mut trace_origins = Vec::new();
223 let mut same_regions = Vec::new();
224 let mut processed_errors = Vec::new();
225 for error in errors.iter() {
226 match error.clone() {
227 ConcreteFailure(origin, sub, sup) => {
228 debug!("processing ConcreteFailure")
229 let trace = match origin {
230 infer::Subtype(trace) => Some(trace),
233 match free_regions_from_same_fn(self.tcx, sub, sup) {
234 Some(ref same_frs) if trace.is_some() => {
235 let trace = trace.unwrap();
236 let terr = ty::terr_regions_does_not_outlive(sup,
238 trace_origins.push((trace, terr));
239 append_to_same_regions(&mut same_regions, same_frs);
241 _ => processed_errors.push((*error).clone()),
244 SubSupConflict(var_origin, _, sub_r, _, sup_r) => {
245 debug!("processing SubSupConflict")
246 match free_regions_from_same_fn(self.tcx, sub_r, sup_r) {
247 Some(ref same_frs) => {
248 var_origins.push(var_origin);
249 append_to_same_regions(&mut same_regions, same_frs);
251 None => processed_errors.push((*error).clone()),
254 SupSupConflict(..) => processed_errors.push((*error).clone()),
255 _ => () // This shouldn't happen
258 if !same_regions.is_empty() {
259 let common_scope_id = same_regions.get(0).scope_id;
260 for sr in same_regions.iter() {
261 // Since ProcessedErrors is used to reconstruct the function
262 // declaration, we want to make sure that they are, in fact,
263 // from the same scope
264 if sr.scope_id != common_scope_id {
265 debug!("returning empty result from process_errors because
266 {} != {}", sr.scope_id, common_scope_id);
270 let pe = ProcessedErrors(var_origins, trace_origins, same_regions);
271 debug!("errors processed: {}", pe);
272 processed_errors.push(pe);
274 return processed_errors;
277 struct FreeRegionsFromSameFn {
278 sub_fr: ty::FreeRegion,
279 sup_fr: ty::FreeRegion,
280 scope_id: ast::NodeId
283 impl FreeRegionsFromSameFn {
284 fn new(sub_fr: ty::FreeRegion,
285 sup_fr: ty::FreeRegion,
286 scope_id: ast::NodeId)
287 -> FreeRegionsFromSameFn {
288 FreeRegionsFromSameFn {
296 fn free_regions_from_same_fn(tcx: &ty::ctxt,
299 -> Option<FreeRegionsFromSameFn> {
300 debug!("free_regions_from_same_fn(sub={}, sup={})", sub, sup);
301 let (scope_id, fr1, fr2) = match (sub, sup) {
302 (ReFree(fr1), ReFree(fr2)) => {
303 if fr1.scope_id != fr2.scope_id {
306 assert!(fr1.scope_id == fr2.scope_id);
307 (fr1.scope_id, fr1, fr2)
311 let parent = tcx.map.get_parent(scope_id);
312 let parent_node = tcx.map.find(parent);
314 Some(node) => match node {
315 ast_map::NodeItem(item) => match item.node {
317 Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
321 ast_map::NodeImplItem(..) |
322 ast_map::NodeTraitItem(..) => {
323 Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
328 debug!("no parent node of scope_id {}", scope_id)
334 fn append_to_same_regions(same_regions: &mut Vec<SameRegions>,
335 same_frs: &FreeRegionsFromSameFn) {
336 let scope_id = same_frs.scope_id;
337 let (sub_fr, sup_fr) = (same_frs.sub_fr, same_frs.sup_fr);
338 for sr in same_regions.iter_mut() {
339 if sr.contains(&sup_fr.bound_region)
340 && scope_id == sr.scope_id {
341 sr.push(sub_fr.bound_region);
345 same_regions.push(SameRegions {
347 regions: vec!(sub_fr.bound_region, sup_fr.bound_region)
352 fn report_type_error(&self, trace: TypeTrace, terr: &ty::type_err) {
353 let expected_found_str = match self.values_str(&trace.values) {
356 return; /* derived error */
360 let message_root_str = match trace.origin {
361 infer::Misc(_) => "mismatched types",
362 infer::MethodCompatCheck(_) => "method not compatible with trait",
363 infer::ExprAssignable(_) => "mismatched types",
364 infer::RelateTraitRefs(_) => "mismatched traits",
365 infer::RelateSelfType(_) => "mismatched types",
366 infer::RelateOutputImplTypes(_) => "mismatched types",
367 infer::MatchExpressionArm(_, _) => "match arms have incompatible types",
368 infer::IfExpression(_) => "if and else have incompatible types",
369 infer::IfExpressionWithNoElse(_) => "if may be missing an else clause",
372 self.tcx.sess.span_err(
374 format!("{}: {} ({})",
377 ty::type_err_to_str(self.tcx, terr)).as_slice());
380 infer::MatchExpressionArm(_, arm_span) =>
381 self.tcx.sess.span_note(arm_span, "match arm with an incompatible type"),
386 fn report_and_explain_type_error(&self,
388 terr: &ty::type_err) {
389 self.report_type_error(trace, terr);
390 ty::note_and_explain_type_err(self.tcx, terr);
393 fn values_str(&self, values: &ValuePairs) -> Option<String> {
395 * Returns a string of the form "expected `{}`, found `{}`",
396 * or None if this is a derived error.
399 infer::Types(ref exp_found) => {
400 self.expected_found_str(exp_found)
402 infer::TraitRefs(ref exp_found) => {
403 self.expected_found_str(exp_found)
408 fn expected_found_str<T:UserString+Resolvable>(
410 exp_found: &ty::expected_found<T>)
413 let expected = exp_found.expected.resolve(self);
414 if expected.contains_error() {
418 let found = exp_found.found.resolve(self);
419 if found.contains_error() {
423 Some(format!("expected `{}`, found `{}`",
424 expected.user_string(self.tcx),
425 found.user_string(self.tcx)))
428 fn report_param_bound_failure(&self,
429 origin: SubregionOrigin,
430 param_ty: ty::ParamTy,
432 _sups: Vec<Region>) {
434 // FIXME: it would be better to report the first error message
435 // with the span of the parameter itself, rather than the span
436 // where the error was detected. But that span is not readily
440 ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => {
441 // Does the required lifetime have a nice name we can print?
442 self.tcx.sess.span_err(
445 "the parameter type `{}` may not live long enough; \
446 consider adding an explicit lifetime bound `{}:{}`...",
447 param_ty.user_string(self.tcx),
448 param_ty.user_string(self.tcx),
449 sub.user_string(self.tcx)).as_slice());
453 // Does the required lifetime have a nice name we can print?
454 self.tcx.sess.span_err(
457 "the parameter type `{}` may not live long enough; \
458 consider adding an explicit lifetime bound `{}:'static`...",
459 param_ty.user_string(self.tcx),
460 param_ty.user_string(self.tcx)).as_slice());
464 // If not, be less specific.
465 self.tcx.sess.span_err(
468 "the parameter type `{}` may not live long enough; \
469 consider adding an explicit lifetime bound to `{}`",
470 param_ty.user_string(self.tcx),
471 param_ty.user_string(self.tcx)).as_slice());
472 note_and_explain_region(
474 format!("the parameter type `{}` must be valid for ",
475 param_ty.user_string(self.tcx)).as_slice(),
481 self.note_region_origin(&origin);
484 fn report_concrete_failure(&self,
485 origin: SubregionOrigin,
489 infer::Subtype(trace) => {
490 let terr = ty::terr_regions_does_not_outlive(sup, sub);
491 self.report_and_explain_type_error(trace, &terr);
493 infer::Reborrow(span) => {
494 self.tcx.sess.span_err(
496 "lifetime of reference outlines \
497 lifetime of borrowed content...");
498 note_and_explain_region(
500 "...the reference is valid for ",
503 note_and_explain_region(
505 "...but the borrowed content is only valid for ",
509 infer::ReborrowUpvar(span, ref upvar_id) => {
510 self.tcx.sess.span_err(
512 format!("lifetime of borrowed pointer outlives \
513 lifetime of captured variable `{}`...",
514 ty::local_var_name_str(self.tcx,
517 .to_string()).as_slice());
518 note_and_explain_region(
520 "...the borrowed pointer is valid for ",
523 note_and_explain_region(
525 format!("...but `{}` is only valid for ",
526 ty::local_var_name_str(self.tcx,
529 .to_string()).as_slice(),
533 infer::InfStackClosure(span) => {
534 self.tcx.sess.span_err(
536 "closure outlives stack frame");
537 note_and_explain_region(
539 "...the closure must be valid for ",
542 note_and_explain_region(
544 "...but the closure's stack frame is only valid for ",
548 infer::InvokeClosure(span) => {
549 self.tcx.sess.span_err(
551 "cannot invoke closure outside of its lifetime");
552 note_and_explain_region(
554 "the closure is only valid for ",
558 infer::DerefPointer(span) => {
559 self.tcx.sess.span_err(
561 "dereference of reference outside its lifetime");
562 note_and_explain_region(
564 "the reference is only valid for ",
568 infer::FreeVariable(span, id) => {
569 self.tcx.sess.span_err(
571 format!("captured variable `{}` does not \
572 outlive the enclosing closure",
573 ty::local_var_name_str(self.tcx,
575 .to_string()).as_slice());
576 note_and_explain_region(
578 "captured variable is valid for ",
581 note_and_explain_region(
583 "closure is valid for ",
587 infer::ProcCapture(span, id) => {
588 self.tcx.sess.span_err(
590 format!("captured variable `{}` must be 'static \
591 to be captured in a proc",
592 ty::local_var_name_str(self.tcx, id).get())
594 note_and_explain_region(
596 "captured variable is only valid for ",
600 infer::IndexSlice(span) => {
601 self.tcx.sess.span_err(span,
602 "index of slice outside its lifetime");
603 note_and_explain_region(
605 "the slice is only valid for ",
609 infer::RelateObjectBound(span) => {
610 self.tcx.sess.span_err(
612 "lifetime of the source pointer does not outlive \
613 lifetime bound of the object type");
614 note_and_explain_region(
616 "object type is valid for ",
619 note_and_explain_region(
621 "source pointer is only valid for ",
625 infer::RelateProcBound(span, var_node_id, ty) => {
626 self.tcx.sess.span_err(
629 "the type `{}` of captured variable `{}` \
630 outlives the `proc()` it \
632 self.ty_to_string(ty),
633 ty::local_var_name_str(self.tcx,
634 var_node_id)).as_slice());
635 note_and_explain_region(
637 "`proc()` is valid for ",
640 note_and_explain_region(
642 format!("the type `{}` is only valid for ",
643 self.ty_to_string(ty)).as_slice(),
647 infer::RelateParamBound(span, param_ty, ty) => {
648 self.tcx.sess.span_err(
650 format!("the type `{}` (provided as the value of \
651 the parameter `{}`) does not fulfill the \
653 self.ty_to_string(ty),
654 param_ty.user_string(self.tcx)).as_slice());
655 note_and_explain_region(self.tcx,
656 "type must outlive ",
660 infer::RelateRegionParamBound(span) => {
661 self.tcx.sess.span_err(
663 "declared lifetime bound not satisfied");
664 note_and_explain_region(
666 "lifetime parameter instantiated with ",
669 note_and_explain_region(
671 "but lifetime parameter must outlive ",
675 infer::RelateDefaultParamBound(span, ty) => {
676 self.tcx.sess.span_err(
678 format!("the type `{}` (provided as the value of \
679 a type parameter) is not valid at this point",
680 self.ty_to_string(ty)).as_slice());
681 note_and_explain_region(self.tcx,
682 "type must outlive ",
686 infer::CallRcvr(span) => {
687 self.tcx.sess.span_err(
689 "lifetime of method receiver does not outlive \
691 note_and_explain_region(
693 "the receiver is only valid for ",
697 infer::CallArg(span) => {
698 self.tcx.sess.span_err(
700 "lifetime of function argument does not outlive \
702 note_and_explain_region(
704 "the function argument is only valid for ",
708 infer::CallReturn(span) => {
709 self.tcx.sess.span_err(
711 "lifetime of return value does not outlive \
713 note_and_explain_region(
715 "the return value is only valid for ",
719 infer::AddrOf(span) => {
720 self.tcx.sess.span_err(
722 "reference is not valid \
723 at the time of borrow");
724 note_and_explain_region(
726 "the borrow is only valid for ",
730 infer::AutoBorrow(span) => {
731 self.tcx.sess.span_err(
733 "automatically reference is not valid \
734 at the time of borrow");
735 note_and_explain_region(
737 "the automatic borrow is only valid for ",
741 infer::ExprTypeIsNotInScope(t, span) => {
742 self.tcx.sess.span_err(
744 format!("type of expression contains references \
745 that are not valid during the expression: `{}`",
746 self.ty_to_string(t)).as_slice());
747 note_and_explain_region(
749 "type is only valid for ",
753 infer::BindingTypeIsNotValidAtDecl(span) => {
754 self.tcx.sess.span_err(
756 "lifetime of variable does not enclose its declaration");
757 note_and_explain_region(
759 "the variable is only valid for ",
763 infer::ReferenceOutlivesReferent(ty, span) => {
764 self.tcx.sess.span_err(
766 format!("in type `{}`, reference has a longer lifetime \
767 than the data it references",
768 self.ty_to_string(ty)).as_slice());
769 note_and_explain_region(
771 "the pointer is valid for ",
774 note_and_explain_region(
776 "but the referenced data is only valid for ",
783 fn report_sub_sup_conflict(&self,
784 var_origin: RegionVariableOrigin,
785 sub_origin: SubregionOrigin,
787 sup_origin: SubregionOrigin,
788 sup_region: Region) {
789 self.report_inference_failure(var_origin);
791 note_and_explain_region(
793 "first, the lifetime cannot outlive ",
797 self.note_region_origin(&sup_origin);
799 note_and_explain_region(
801 "but, the lifetime must be valid for ",
805 self.note_region_origin(&sub_origin);
808 fn report_sup_sup_conflict(&self,
809 var_origin: RegionVariableOrigin,
810 origin1: SubregionOrigin,
812 origin2: SubregionOrigin,
814 self.report_inference_failure(var_origin);
816 note_and_explain_region(
818 "first, the lifetime must be contained by ",
822 self.note_region_origin(&origin1);
824 note_and_explain_region(
826 "but, the lifetime must also be contained by ",
830 self.note_region_origin(&origin2);
833 fn report_processed_errors(&self,
834 var_origins: &[RegionVariableOrigin],
835 trace_origins: &[(TypeTrace, ty::type_err)],
836 same_regions: &[SameRegions]) {
837 for vo in var_origins.iter() {
838 self.report_inference_failure(vo.clone());
840 self.give_suggestion(same_regions);
841 for &(ref trace, terr) in trace_origins.iter() {
842 self.report_type_error(trace.clone(), &terr);
846 fn give_suggestion(&self, same_regions: &[SameRegions]) {
847 let scope_id = same_regions[0].scope_id;
848 let parent = self.tcx.map.get_parent(scope_id);
849 let parent_node = self.tcx.map.find(parent);
850 let node_inner = match parent_node {
851 Some(ref node) => match *node {
852 ast_map::NodeItem(ref item) => {
854 ast::ItemFn(ref fn_decl, pur, _, ref gen, _) => {
855 Some((&**fn_decl, gen, pur, item.ident, None, item.span))
860 ast_map::NodeImplItem(ref item) => {
862 ast::MethodImplItem(ref m) => {
863 Some((m.pe_fn_decl(),
867 Some(&m.pe_explicit_self().node),
870 ast::TypeImplItem(_) => None,
873 ast_map::NodeTraitItem(ref item) => {
875 ast::ProvidedMethod(ref m) => {
876 Some((m.pe_fn_decl(),
880 Some(&m.pe_explicit_self().node),
890 let (fn_decl, generics, fn_style, ident, expl_self, span)
891 = node_inner.expect("expect item fn");
892 let taken = lifetimes_in_scope(self.tcx, scope_id);
893 let life_giver = LifeGiver::with_taken(taken.as_slice());
894 let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self,
895 generics, same_regions, &life_giver);
896 let (fn_decl, expl_self, generics) = rebuilder.rebuild();
897 self.give_expl_lifetime_param(&fn_decl, fn_style, ident,
898 expl_self.as_ref(), &generics, span);
902 struct RebuildPathInfo<'a> {
904 // indexes to insert lifetime on path.lifetimes
906 // number of lifetimes we expect to see on the type referred by `path`
907 // (e.g., expected=1 for struct Foo<'a>)
909 anon_nums: &'a HashSet<uint>,
910 region_names: &'a HashSet<ast::Name>
913 struct Rebuilder<'a, 'tcx: 'a> {
914 tcx: &'a ty::ctxt<'tcx>,
915 fn_decl: &'a ast::FnDecl,
916 expl_self_opt: Option<&'a ast::ExplicitSelf_>,
917 generics: &'a ast::Generics,
918 same_regions: &'a [SameRegions],
919 life_giver: &'a LifeGiver,
920 cur_anon: Cell<uint>,
921 inserted_anons: RefCell<HashSet<uint>>,
929 impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
930 fn new(tcx: &'a ty::ctxt<'tcx>,
931 fn_decl: &'a ast::FnDecl,
932 expl_self_opt: Option<&'a ast::ExplicitSelf_>,
933 generics: &'a ast::Generics,
934 same_regions: &'a [SameRegions],
935 life_giver: &'a LifeGiver)
936 -> Rebuilder<'a, 'tcx> {
940 expl_self_opt: expl_self_opt,
942 same_regions: same_regions,
943 life_giver: life_giver,
944 cur_anon: Cell::new(0),
945 inserted_anons: RefCell::new(HashSet::new()),
950 -> (ast::FnDecl, Option<ast::ExplicitSelf_>, ast::Generics) {
951 let mut expl_self_opt = self.expl_self_opt.map(|x| x.clone());
952 let mut inputs = self.fn_decl.inputs.clone();
953 let mut output = self.fn_decl.output.clone();
954 let mut ty_params = self.generics.ty_params.clone();
955 let where_clause = self.generics.where_clause.clone();
956 let mut kept_lifetimes = HashSet::new();
957 for sr in self.same_regions.iter() {
958 self.cur_anon.set(0);
959 self.offset_cur_anon();
960 let (anon_nums, region_names) =
961 self.extract_anon_nums_and_names(sr);
962 let (lifetime, fresh_or_kept) = self.pick_lifetime(®ion_names);
963 match fresh_or_kept {
964 Kept => { kept_lifetimes.insert(lifetime.name); }
967 expl_self_opt = self.rebuild_expl_self(expl_self_opt, lifetime,
968 &anon_nums, ®ion_names);
969 inputs = self.rebuild_args_ty(inputs.as_slice(), lifetime,
970 &anon_nums, ®ion_names);
971 output = self.rebuild_arg_ty_or_output(&*output, lifetime,
972 &anon_nums, ®ion_names);
973 ty_params = self.rebuild_ty_params(ty_params, lifetime,
976 let fresh_lifetimes = self.life_giver.get_generated_lifetimes();
977 let all_region_names = self.extract_all_region_names();
978 let generics = self.rebuild_generics(self.generics,
984 let new_fn_decl = ast::FnDecl {
988 variadic: self.fn_decl.variadic
990 (new_fn_decl, expl_self_opt, generics)
993 fn pick_lifetime(&self,
994 region_names: &HashSet<ast::Name>)
995 -> (ast::Lifetime, FreshOrKept) {
996 if region_names.len() > 0 {
997 // It's not necessary to convert the set of region names to a
998 // vector of string and then sort them. However, it makes the
999 // choice of lifetime name deterministic and thus easier to test.
1000 let mut names = Vec::new();
1001 for rn in region_names.iter() {
1002 let lt_name = token::get_name(*rn).get().to_string();
1003 names.push(lt_name);
1006 let name = token::str_to_ident(names.get(0).as_slice()).name;
1007 return (name_to_dummy_lifetime(name), Kept);
1009 return (self.life_giver.give_lifetime(), Fresh);
1012 fn extract_anon_nums_and_names(&self, same_regions: &SameRegions)
1013 -> (HashSet<uint>, HashSet<ast::Name>) {
1014 let mut anon_nums = HashSet::new();
1015 let mut region_names = HashSet::new();
1016 for br in same_regions.regions.iter() {
1019 anon_nums.insert(i);
1021 ty::BrNamed(_, name) => {
1022 region_names.insert(name);
1027 (anon_nums, region_names)
1030 fn extract_all_region_names(&self) -> HashSet<ast::Name> {
1031 let mut all_region_names = HashSet::new();
1032 for sr in self.same_regions.iter() {
1033 for br in sr.regions.iter() {
1035 ty::BrNamed(_, name) => {
1036 all_region_names.insert(name);
1045 fn inc_cur_anon(&self, n: uint) {
1046 let anon = self.cur_anon.get();
1047 self.cur_anon.set(anon+n);
1050 fn offset_cur_anon(&self) {
1051 let mut anon = self.cur_anon.get();
1052 while self.inserted_anons.borrow().contains(&anon) {
1055 self.cur_anon.set(anon);
1058 fn inc_and_offset_cur_anon(&self, n: uint) {
1059 self.inc_cur_anon(n);
1060 self.offset_cur_anon();
1063 fn track_anon(&self, anon: uint) {
1064 self.inserted_anons.borrow_mut().insert(anon);
1067 fn rebuild_ty_params(&self,
1068 ty_params: OwnedSlice<ast::TyParam>,
1069 lifetime: ast::Lifetime,
1070 region_names: &HashSet<ast::Name>)
1071 -> OwnedSlice<ast::TyParam> {
1072 ty_params.map(|ty_param| {
1073 let bounds = self.rebuild_ty_param_bounds(ty_param.bounds.clone(),
1077 ident: ty_param.ident,
1080 unbound: ty_param.unbound.clone(),
1081 default: ty_param.default.clone(),
1082 span: ty_param.span,
1087 fn rebuild_ty_param_bounds(&self,
1088 ty_param_bounds: OwnedSlice<ast::TyParamBound>,
1089 lifetime: ast::Lifetime,
1090 region_names: &HashSet<ast::Name>)
1091 -> OwnedSlice<ast::TyParamBound> {
1092 ty_param_bounds.map(|tpb| {
1094 &ast::RegionTyParamBound(lt) => {
1095 // FIXME -- it's unclear whether I'm supposed to
1096 // substitute lifetime here. I suspect we need to
1097 // be passing down a map.
1098 ast::RegionTyParamBound(lt)
1100 &ast::UnboxedFnTyParamBound(ref unboxed_function_type) => {
1101 ast::UnboxedFnTyParamBound((*unboxed_function_type).clone())
1103 &ast::TraitTyParamBound(ref tr) => {
1104 let last_seg = tr.path.segments.last().unwrap();
1105 let mut insert = Vec::new();
1106 for (i, lt) in last_seg.lifetimes.iter().enumerate() {
1107 if region_names.contains(<.name) {
1111 let rebuild_info = RebuildPathInfo {
1114 expected: last_seg.lifetimes.len(),
1115 anon_nums: &HashSet::new(),
1116 region_names: region_names
1118 let new_path = self.rebuild_path(rebuild_info, lifetime);
1119 ast::TraitTyParamBound(ast::TraitRef {
1122 lifetimes: tr.lifetimes.clone(),
1129 fn rebuild_expl_self(&self,
1130 expl_self_opt: Option<ast::ExplicitSelf_>,
1131 lifetime: ast::Lifetime,
1132 anon_nums: &HashSet<uint>,
1133 region_names: &HashSet<ast::Name>)
1134 -> Option<ast::ExplicitSelf_> {
1135 match expl_self_opt {
1136 Some(ref expl_self) => match *expl_self {
1137 ast::SelfRegion(lt_opt, muta, id) => match lt_opt {
1138 Some(lt) => if region_names.contains(<.name) {
1139 return Some(ast::SelfRegion(Some(lifetime), muta, id));
1142 let anon = self.cur_anon.get();
1143 self.inc_and_offset_cur_anon(1);
1144 if anon_nums.contains(&anon) {
1145 self.track_anon(anon);
1146 return Some(ast::SelfRegion(Some(lifetime), muta, id));
1157 fn rebuild_generics(&self,
1158 generics: &ast::Generics,
1159 add: &Vec<ast::Lifetime>,
1160 keep: &HashSet<ast::Name>,
1161 remove: &HashSet<ast::Name>,
1162 ty_params: OwnedSlice<ast::TyParam>,
1163 where_clause: ast::WhereClause)
1165 let mut lifetimes = Vec::new();
1166 for lt in add.iter() {
1167 lifetimes.push(ast::LifetimeDef { lifetime: *lt,
1168 bounds: Vec::new() });
1170 for lt in generics.lifetimes.iter() {
1171 if keep.contains(<.lifetime.name) ||
1172 !remove.contains(<.lifetime.name) {
1173 lifetimes.push((*lt).clone());
1177 lifetimes: lifetimes,
1178 ty_params: ty_params,
1179 where_clause: where_clause,
1183 fn rebuild_args_ty(&self,
1184 inputs: &[ast::Arg],
1185 lifetime: ast::Lifetime,
1186 anon_nums: &HashSet<uint>,
1187 region_names: &HashSet<ast::Name>)
1189 let mut new_inputs = Vec::new();
1190 for arg in inputs.iter() {
1191 let new_ty = self.rebuild_arg_ty_or_output(&*arg.ty, lifetime,
1192 anon_nums, region_names);
1193 let possibly_new_arg = ast::Arg {
1195 pat: arg.pat.clone(),
1198 new_inputs.push(possibly_new_arg);
1203 fn rebuild_arg_ty_or_output(&self,
1205 lifetime: ast::Lifetime,
1206 anon_nums: &HashSet<uint>,
1207 region_names: &HashSet<ast::Name>)
1209 let mut new_ty = P(ty.clone());
1210 let mut ty_queue = vec!(ty);
1211 while !ty_queue.is_empty() {
1212 let cur_ty = ty_queue.shift().unwrap();
1214 ast::TyRptr(lt_opt, ref mut_ty) => {
1215 let rebuild = match lt_opt {
1216 Some(lt) => region_names.contains(<.name),
1218 let anon = self.cur_anon.get();
1219 let rebuild = anon_nums.contains(&anon);
1221 self.track_anon(anon);
1223 self.inc_and_offset_cur_anon(1);
1230 node: ast::TyRptr(Some(lifetime), mut_ty.clone()),
1233 new_ty = self.rebuild_ty(new_ty, P(to));
1235 ty_queue.push(&*mut_ty.ty);
1237 ast::TyPath(ref path, ref bounds, id) => {
1238 let a_def = match self.tcx.def_map.borrow().find(&id) {
1244 pprust::path_to_string(path)).as_slice())
1249 def::DefTy(did, _) | def::DefStruct(did) => {
1250 let generics = ty::lookup_item_type(self.tcx, did).generics;
1253 generics.regions.len(subst::TypeSpace);
1255 &path.segments.last().unwrap().lifetimes;
1256 let mut insert = Vec::new();
1257 if lifetimes.len() == 0 {
1258 let anon = self.cur_anon.get();
1259 for (i, a) in range(anon,
1260 anon+expected).enumerate() {
1261 if anon_nums.contains(&a) {
1266 self.inc_and_offset_cur_anon(expected);
1268 for (i, lt) in lifetimes.iter().enumerate() {
1269 if region_names.contains(<.name) {
1274 let rebuild_info = RebuildPathInfo {
1278 anon_nums: anon_nums,
1279 region_names: region_names
1281 let new_path = self.rebuild_path(rebuild_info, lifetime);
1284 node: ast::TyPath(new_path, bounds.clone(), id),
1287 new_ty = self.rebuild_ty(new_ty, P(to));
1294 ast::TyPtr(ref mut_ty) => {
1295 ty_queue.push(&*mut_ty.ty);
1297 ast::TyVec(ref ty) |
1298 ast::TyUniq(ref ty) |
1299 ast::TyFixedLengthVec(ref ty, _) => {
1300 ty_queue.push(&**ty);
1302 ast::TyTup(ref tys) => ty_queue.extend(tys.iter().map(|ty| &**ty)),
1309 fn rebuild_ty(&self,
1314 fn build_to(from: P<ast::Ty>,
1315 to: &mut Option<P<ast::Ty>>)
1317 if Some(from.id) == to.as_ref().map(|ty| ty.id) {
1318 return to.take().expect("`to` type found more than once during rebuild");
1320 from.map(|ast::Ty {id, node, span}| {
1321 let new_node = match node {
1322 ast::TyRptr(lifetime, mut_ty) => {
1323 ast::TyRptr(lifetime, ast::MutTy {
1324 mutbl: mut_ty.mutbl,
1325 ty: build_to(mut_ty.ty, to),
1328 ast::TyPtr(mut_ty) => {
1329 ast::TyPtr(ast::MutTy {
1330 mutbl: mut_ty.mutbl,
1331 ty: build_to(mut_ty.ty, to),
1334 ast::TyVec(ty) => ast::TyVec(build_to(ty, to)),
1335 ast::TyUniq(ty) => ast::TyUniq(build_to(ty, to)),
1336 ast::TyFixedLengthVec(ty, e) => {
1337 ast::TyFixedLengthVec(build_to(ty, to), e)
1339 ast::TyTup(tys) => {
1340 ast::TyTup(tys.into_iter().map(|ty| build_to(ty, to)).collect())
1342 ast::TyParen(typ) => ast::TyParen(build_to(typ, to)),
1345 ast::Ty { id: id, node: new_node, span: span }
1349 build_to(from, &mut Some(to))
1352 fn rebuild_path(&self,
1353 rebuild_info: RebuildPathInfo,
1354 lifetime: ast::Lifetime)
1356 let RebuildPathInfo {
1360 anon_nums: anon_nums,
1361 region_names: region_names,
1364 let last_seg = path.segments.last().unwrap();
1365 let mut new_lts = Vec::new();
1366 if last_seg.lifetimes.len() == 0 {
1367 // traverse once to see if there's a need to insert lifetime
1368 let need_insert = range(0, expected).any(|i| {
1369 indexes.contains(&i)
1372 for i in range(0, expected) {
1373 if indexes.contains(&i) {
1374 new_lts.push(lifetime);
1376 new_lts.push(self.life_giver.give_lifetime());
1381 for (i, lt) in last_seg.lifetimes.iter().enumerate() {
1382 if indexes.contains(&i) {
1383 new_lts.push(lifetime);
1389 let new_types = last_seg.types.map(|t| {
1390 self.rebuild_arg_ty_or_output(&**t, lifetime, anon_nums, region_names)
1392 let new_seg = ast::PathSegment {
1393 identifier: last_seg.identifier,
1397 let mut new_segs = Vec::new();
1398 new_segs.push_all(path.segments.init());
1399 new_segs.push(new_seg);
1402 global: path.global,
1408 impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
1409 fn give_expl_lifetime_param(&self,
1411 fn_style: ast::FnStyle,
1413 opt_explicit_self: Option<&ast::ExplicitSelf_>,
1414 generics: &ast::Generics,
1415 span: codemap::Span) {
1416 let suggested_fn = pprust::fun_to_string(decl, fn_style, ident,
1417 opt_explicit_self, generics);
1418 let msg = format!("consider using an explicit lifetime \
1419 parameter as shown: {}", suggested_fn);
1420 self.tcx.sess.span_note(span, msg.as_slice());
1423 fn report_inference_failure(&self,
1424 var_origin: RegionVariableOrigin) {
1425 let var_description = match var_origin {
1426 infer::MiscVariable(_) => "".to_string(),
1427 infer::PatternRegion(_) => " for pattern".to_string(),
1428 infer::AddrOfRegion(_) => " for borrow expression".to_string(),
1429 infer::AddrOfSlice(_) => " for slice expression".to_string(),
1430 infer::Autoref(_) => " for autoref".to_string(),
1431 infer::Coercion(_) => " for automatic coercion".to_string(),
1432 infer::LateBoundRegion(_, br) => {
1433 format!(" for {}in function call",
1434 bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
1436 infer::BoundRegionInFnType(_, br) => {
1437 format!(" for {}in function type",
1438 bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
1440 infer::EarlyBoundRegion(_, name) => {
1441 format!(" for lifetime parameter `{}`",
1442 token::get_name(name).get())
1444 infer::BoundRegionInCoherence(name) => {
1445 format!(" for lifetime parameter `{}` in coherence check",
1446 token::get_name(name).get())
1448 infer::UpvarRegion(ref upvar_id, _) => {
1449 format!(" for capture of `{}` by closure",
1450 ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_string())
1454 self.tcx.sess.span_err(
1456 format!("cannot infer an appropriate lifetime{} \
1457 due to conflicting requirements",
1458 var_description).as_slice());
1461 fn note_region_origin(&self, origin: &SubregionOrigin) {
1463 infer::Subtype(ref trace) => {
1464 let desc = match trace.origin {
1466 format!("types are compatible")
1468 infer::MethodCompatCheck(_) => {
1469 format!("method type is compatible with trait")
1471 infer::ExprAssignable(_) => {
1472 format!("expression is assignable")
1474 infer::RelateTraitRefs(_) => {
1475 format!("traits are compatible")
1477 infer::RelateSelfType(_) => {
1478 format!("self type matches impl self type")
1480 infer::RelateOutputImplTypes(_) => {
1481 format!("trait type parameters matches those \
1482 specified on the impl")
1484 infer::MatchExpressionArm(_, _) => {
1485 format!("match arms have compatible types")
1487 infer::IfExpression(_) => {
1488 format!("if and else have compatible types")
1490 infer::IfExpressionWithNoElse(_) => {
1491 format!("if may be missing an else clause")
1495 match self.values_str(&trace.values) {
1496 Some(values_str) => {
1497 self.tcx.sess.span_note(
1498 trace.origin.span(),
1499 format!("...so that {} ({})",
1500 desc, values_str).as_slice());
1503 // Really should avoid printing this error at
1504 // all, since it is derived, but that would
1505 // require more refactoring than I feel like
1506 // doing right now. - nmatsakis
1507 self.tcx.sess.span_note(
1508 trace.origin.span(),
1509 format!("...so that {}", desc).as_slice());
1513 infer::Reborrow(span) => {
1514 self.tcx.sess.span_note(
1516 "...so that reference does not outlive \
1519 infer::ReborrowUpvar(span, ref upvar_id) => {
1520 self.tcx.sess.span_note(
1523 "...so that closure can access `{}`",
1524 ty::local_var_name_str(self.tcx, upvar_id.var_id)
1526 .to_string()).as_slice())
1528 infer::InfStackClosure(span) => {
1529 self.tcx.sess.span_note(
1531 "...so that closure does not outlive its stack frame");
1533 infer::InvokeClosure(span) => {
1534 self.tcx.sess.span_note(
1536 "...so that closure is not invoked outside its lifetime");
1538 infer::DerefPointer(span) => {
1539 self.tcx.sess.span_note(
1541 "...so that pointer is not dereferenced \
1542 outside its lifetime");
1544 infer::FreeVariable(span, id) => {
1545 self.tcx.sess.span_note(
1547 format!("...so that captured variable `{}` \
1548 does not outlive the enclosing closure",
1549 ty::local_var_name_str(
1551 id).get().to_string()).as_slice());
1553 infer::ProcCapture(span, id) => {
1554 self.tcx.sess.span_note(
1556 format!("...so that captured variable `{}` \
1558 ty::local_var_name_str(
1560 id).get()).as_slice());
1562 infer::IndexSlice(span) => {
1563 self.tcx.sess.span_note(
1565 "...so that slice is not indexed outside the lifetime");
1567 infer::RelateObjectBound(span) => {
1568 self.tcx.sess.span_note(
1570 "...so that it can be closed over into an object");
1572 infer::RelateProcBound(span, var_node_id, _ty) => {
1573 self.tcx.sess.span_note(
1576 "...so that the variable `{}` can be captured \
1578 ty::local_var_name_str(self.tcx,
1579 var_node_id)).as_slice());
1581 infer::CallRcvr(span) => {
1582 self.tcx.sess.span_note(
1584 "...so that method receiver is valid for the method call");
1586 infer::CallArg(span) => {
1587 self.tcx.sess.span_note(
1589 "...so that argument is valid for the call");
1591 infer::CallReturn(span) => {
1592 self.tcx.sess.span_note(
1594 "...so that return value is valid for the call");
1596 infer::AddrOf(span) => {
1597 self.tcx.sess.span_note(
1599 "...so that reference is valid \
1600 at the time of borrow");
1602 infer::AutoBorrow(span) => {
1603 self.tcx.sess.span_note(
1605 "...so that auto-reference is valid \
1606 at the time of borrow");
1608 infer::ExprTypeIsNotInScope(t, span) => {
1609 self.tcx.sess.span_note(
1611 format!("...so type `{}` of expression is valid during the \
1613 self.ty_to_string(t)).as_slice());
1615 infer::BindingTypeIsNotValidAtDecl(span) => {
1616 self.tcx.sess.span_note(
1618 "...so that variable is valid at time of its declaration");
1620 infer::ReferenceOutlivesReferent(ty, span) => {
1621 self.tcx.sess.span_note(
1623 format!("...so that the reference type `{}` \
1624 does not outlive the data it points at",
1625 self.ty_to_string(ty)).as_slice());
1627 infer::RelateParamBound(span, param_ty, t) => {
1628 self.tcx.sess.span_note(
1630 format!("...so that the parameter `{}`, \
1631 when instantiated with `{}`, \
1632 will meet its declared lifetime bounds.",
1633 param_ty.user_string(self.tcx),
1634 self.ty_to_string(t)).as_slice());
1636 infer::RelateDefaultParamBound(span, t) => {
1637 self.tcx.sess.span_note(
1639 format!("...so that type parameter \
1640 instantiated with `{}`, \
1641 will meet its declared lifetime bounds.",
1642 self.ty_to_string(t)).as_slice());
1644 infer::RelateRegionParamBound(span) => {
1645 self.tcx.sess.span_note(
1647 format!("...so that the declared lifetime parameter bounds \
1648 are satisfied").as_slice());
1654 pub trait Resolvable {
1655 fn resolve(&self, infcx: &InferCtxt) -> Self;
1656 fn contains_error(&self) -> bool;
1659 impl Resolvable for ty::t {
1660 fn resolve(&self, infcx: &InferCtxt) -> ty::t {
1661 infcx.resolve_type_vars_if_possible(*self)
1663 fn contains_error(&self) -> bool {
1664 ty::type_is_error(*self)
1668 impl Resolvable for Rc<ty::TraitRef> {
1669 fn resolve(&self, infcx: &InferCtxt) -> Rc<ty::TraitRef> {
1670 Rc::new(infcx.resolve_type_vars_in_trait_ref_if_possible(&**self))
1672 fn contains_error(&self) -> bool {
1673 ty::trait_ref_contains_error(&**self)
1677 fn lifetimes_in_scope(tcx: &ty::ctxt,
1678 scope_id: ast::NodeId)
1679 -> Vec<ast::LifetimeDef> {
1680 let mut taken = Vec::new();
1681 let parent = tcx.map.get_parent(scope_id);
1682 let method_id_opt = match tcx.map.find(parent) {
1683 Some(node) => match node {
1684 ast_map::NodeItem(item) => match item.node {
1685 ast::ItemFn(_, _, _, ref gen, _) => {
1686 taken.push_all(gen.lifetimes.as_slice());
1691 ast_map::NodeImplItem(ii) => {
1693 ast::MethodImplItem(ref m) => {
1694 taken.push_all(m.pe_generics().lifetimes.as_slice());
1697 ast::TypeImplItem(_) => None,
1704 if method_id_opt.is_some() {
1705 let method_id = method_id_opt.unwrap();
1706 let parent = tcx.map.get_parent(method_id);
1707 match tcx.map.find(parent) {
1708 Some(node) => match node {
1709 ast_map::NodeItem(item) => match item.node {
1710 ast::ItemImpl(ref gen, _, _, _) => {
1711 taken.push_all(gen.lifetimes.as_slice());
1723 // LifeGiver is responsible for generating fresh lifetime names
1725 taken: HashSet<String>,
1726 counter: Cell<uint>,
1727 generated: RefCell<Vec<ast::Lifetime>>,
1731 fn with_taken(taken: &[ast::LifetimeDef]) -> LifeGiver {
1732 let mut taken_ = HashSet::new();
1733 for lt in taken.iter() {
1734 let lt_name = token::get_name(lt.lifetime.name).get().to_string();
1735 taken_.insert(lt_name);
1739 counter: Cell::new(0),
1740 generated: RefCell::new(Vec::new()),
1744 fn inc_counter(&self) {
1745 let c = self.counter.get();
1746 self.counter.set(c+1);
1749 fn give_lifetime(&self) -> ast::Lifetime {
1752 let mut s = String::from_str("'");
1753 s.push_str(num_to_string(self.counter.get()).as_slice());
1754 if !self.taken.contains(&s) {
1755 lifetime = name_to_dummy_lifetime(
1756 token::str_to_ident(s.as_slice()).name);
1757 self.generated.borrow_mut().push(lifetime);
1765 // 0 .. 25 generates a .. z, 26 .. 51 generates aa .. zz, and so on
1766 fn num_to_string(counter: uint) -> String {
1767 let mut s = String::new();
1768 let (n, r) = (counter/26 + 1, counter % 26);
1769 let letter: char = from_u32((r+97) as u32).unwrap();
1770 for _ in range(0, n) {
1771 s.push_char(letter);
1777 fn get_generated_lifetimes(&self) -> Vec<ast::Lifetime> {
1778 self.generated.borrow().clone()