Skip to content

Commit f57a28b

Browse files
committed
Improve error reporting for region inference failures to make use of
all the information it has at its disposal. Unfortunately this also reveals that we need to improve the reporting heuristics further, as sometimes the errors it chooses to emit seem somewhat mystifying and are not related to the actual problem.
1 parent 168ac52 commit f57a28b

File tree

1 file changed

+174
-20
lines changed

1 file changed

+174
-20
lines changed

src/librustc/middle/typeck/infer/error_reporting.rs

Lines changed: 174 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ use middle::typeck::infer::region_inference::SubSupConflict;
7373
use middle::typeck::infer::region_inference::SupSupConflict;
7474
use syntax::opt_vec::OptVec;
7575
use util::ppaux::UserString;
76+
use util::ppaux::bound_region_to_str;
7677
use util::ppaux::note_and_explain_region;
7778

7879
pub trait ErrorReporting {
@@ -110,6 +111,13 @@ pub trait ErrorReporting {
110111
region2: Region);
111112
}
112113

114+
trait ErrorReportingHelpers {
115+
fn report_inference_failure(@mut self,
116+
var_origin: RegionVariableOrigin);
117+
118+
fn note_region_origin(@mut self,
119+
origin: SubregionOrigin);
120+
}
113121

114122
impl ErrorReporting for InferCtxt {
115123
fn report_region_errors(@mut self,
@@ -398,30 +406,23 @@ impl ErrorReporting for InferCtxt {
398406
sub_region: Region,
399407
sup_origin: SubregionOrigin,
400408
sup_region: Region) {
401-
self.tcx.sess.span_err(
402-
var_origin.span(),
403-
format!("cannot infer an appropriate lifetime \
404-
due to conflicting requirements"));
409+
self.report_inference_failure(var_origin);
405410

406411
note_and_explain_region(
407412
self.tcx,
408413
"first, the lifetime cannot outlive ",
409414
sup_region,
410415
"...");
411416

412-
self.tcx.sess.span_note(
413-
sup_origin.span(),
414-
format!("...due to the following expression"));
417+
self.note_region_origin(sup_origin);
415418

416419
note_and_explain_region(
417420
self.tcx,
418421
"but, the lifetime must be valid for ",
419422
sub_region,
420423
"...");
421424

422-
self.tcx.sess.span_note(
423-
sub_origin.span(),
424-
format!("...due to the following expression"));
425+
self.note_region_origin(sub_origin);
425426
}
426427

427428
fn report_sup_sup_conflict(@mut self,
@@ -430,30 +431,183 @@ impl ErrorReporting for InferCtxt {
430431
region1: Region,
431432
origin2: SubregionOrigin,
432433
region2: Region) {
433-
self.tcx.sess.span_err(
434-
var_origin.span(),
435-
format!("cannot infer an appropriate lifetime \
436-
due to conflicting requirements"));
434+
self.report_inference_failure(var_origin);
437435

438436
note_and_explain_region(
439437
self.tcx,
440438
"first, the lifetime must be contained by ",
441439
region1,
442440
"...");
443441

444-
self.tcx.sess.span_note(
445-
origin1.span(),
446-
format!("...due to the following expression"));
442+
self.note_region_origin(origin1);
447443

448444
note_and_explain_region(
449445
self.tcx,
450446
"but, the lifetime must also be contained by ",
451447
region2,
452448
"...");
453449

454-
self.tcx.sess.span_note(
455-
origin2.span(),
456-
format!("...due to the following expression"));
450+
self.note_region_origin(origin2);
451+
}
452+
}
453+
454+
impl ErrorReportingHelpers for InferCtxt {
455+
fn report_inference_failure(@mut self,
456+
var_origin: RegionVariableOrigin) {
457+
let var_description = match var_origin {
458+
infer::MiscVariable(_) => ~"",
459+
infer::PatternRegion(_) => ~" for pattern",
460+
infer::AddrOfRegion(_) => ~" for borrow expression",
461+
infer::AddrOfSlice(_) => ~" for slice expression",
462+
infer::Autoref(_) => ~" for autoref",
463+
infer::Coercion(_) => ~" for automatic coercion",
464+
infer::BoundRegionInFnCall(_, br) => {
465+
format!(" for {}in function call",
466+
bound_region_to_str(self.tcx, "region ", true, br))
467+
}
468+
infer::BoundRegionInFnType(_, br) => {
469+
format!(" for {}in function type",
470+
bound_region_to_str(self.tcx, "region ", true, br))
471+
}
472+
infer::BoundRegionInTypeOrImpl(_) => {
473+
format!(" for region in type/impl")
474+
}
475+
infer::BoundRegionInCoherence(*) => {
476+
format!(" for coherence check")
477+
}
478+
};
479+
480+
self.tcx.sess.span_err(
481+
var_origin.span(),
482+
format!("cannot infer an appropriate lifetime{} \
483+
due to conflicting requirements",
484+
var_description));
485+
}
486+
487+
fn note_region_origin(@mut self,
488+
origin: SubregionOrigin) {
489+
match origin {
490+
infer::Subtype(ref trace) => {
491+
let desc = match trace.origin {
492+
infer::Misc(_) => {
493+
format!("types are compatible")
494+
}
495+
infer::MethodCompatCheck(_) => {
496+
format!("method type is compatible with trait")
497+
}
498+
infer::ExprAssignable(_) => {
499+
format!("expression is assignable")
500+
}
501+
infer::RelateTraitRefs(_) => {
502+
format!("traits are compatible")
503+
}
504+
infer::RelateSelfType(_) => {
505+
format!("type matches impl")
506+
}
507+
infer::MatchExpression(_) => {
508+
format!("match arms have compatible types")
509+
}
510+
infer::IfExpression(_) => {
511+
format!("if and else have compatible types")
512+
}
513+
};
514+
515+
match self.values_str(&trace.values) {
516+
Some(values_str) => {
517+
self.tcx.sess.span_note(
518+
trace.origin.span(),
519+
format!("...so that {} ({})",
520+
desc, values_str));
521+
}
522+
None => {
523+
// Really should avoid printing this error at
524+
// all, since it is derived, but that would
525+
// require more refactoring than I feel like
526+
// doing right now. - nmatsakis
527+
self.tcx.sess.span_note(
528+
trace.origin.span(),
529+
format!("...so that {}", desc));
530+
}
531+
}
532+
}
533+
infer::Reborrow(span) => {
534+
self.tcx.sess.span_note(
535+
span,
536+
"...so that borrowed pointer does not outlive \
537+
borrowed content");
538+
}
539+
infer::InfStackClosure(span) => {
540+
self.tcx.sess.span_note(
541+
span,
542+
"...so that closure does not outlive its stack frame");
543+
}
544+
infer::InvokeClosure(span) => {
545+
self.tcx.sess.span_note(
546+
span,
547+
"...so that closure is not invoked outside its lifetime");
548+
}
549+
infer::DerefPointer(span) => {
550+
self.tcx.sess.span_note(
551+
span,
552+
"...so that pointer is not dereferenced \
553+
outside its lifetime");
554+
}
555+
infer::FreeVariable(span) => {
556+
self.tcx.sess.span_note(
557+
span,
558+
"...so that captured variable does not outlive the \
559+
enclosing closure");
560+
}
561+
infer::IndexSlice(span) => {
562+
self.tcx.sess.span_note(
563+
span,
564+
"...so that slice is not indexed outside the lifetime");
565+
}
566+
infer::RelateObjectBound(span) => {
567+
self.tcx.sess.span_note(
568+
span,
569+
"...so that source pointer does not outlive \
570+
lifetime bound of the object type");
571+
}
572+
infer::CallRcvr(span) => {
573+
self.tcx.sess.span_note(
574+
span,
575+
"...so that method receiver is valid for the method call");
576+
}
577+
infer::CallArg(span) => {
578+
self.tcx.sess.span_note(
579+
span,
580+
"...so that argument is valid for the call");
581+
}
582+
infer::CallReturn(span) => {
583+
self.tcx.sess.span_note(
584+
span,
585+
"...so that return value is valid for the call");
586+
}
587+
infer::AddrOf(span) => {
588+
self.tcx.sess.span_note(
589+
span,
590+
"...so that borrowed pointer is valid \
591+
at the time of borrow");
592+
}
593+
infer::AutoBorrow(span) => {
594+
self.tcx.sess.span_note(
595+
span,
596+
"...so that automatically borrowed pointer is valid \
597+
at the time of borrow");
598+
}
599+
infer::BindingTypeIsNotValidAtDecl(span) => {
600+
self.tcx.sess.span_note(
601+
span,
602+
"...so that variable is valid at time of its declaration");
603+
}
604+
infer::ReferenceOutlivesReferent(_, span) => {
605+
self.tcx.sess.span_note(
606+
span,
607+
"...so that the pointer does not outlive the \
608+
data it points at");
609+
}
610+
}
457611
}
458612
}
459613

0 commit comments

Comments
 (0)