Skip to content

Commit 8918ceb

Browse files
authored
Rollup merge of rust-lang#39913 - nikomatsakis:inference-error, r=pnkfelix
Report full details of inference errors When the old suggestion machinery was removed by @brson in rust-lang#37057, it was not completely removed. There was a bit of code that had the job of going through errors and finding those for which suggestions were applicable, and it remained, causing us not to emit the full details of such errors. This PR removes that. I've also added various lifetime tests to the UI test suite (so you can also see the before/after there). I have some concrete thoughts on how to improve these cases and am planning on writing those up in some mentoring issues (@cengizio has expressed interest in working on those changes, so I plan to work with him on it, at least to start). cc @jonathandturner
2 parents 5b7c556 + 75da4b6 commit 8918ceb

18 files changed

+362
-260
lines changed

src/librustc/infer/error_reporting.rs

+24-221
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@ use super::region_inference::ConcreteFailure;
6565
use super::region_inference::SubSupConflict;
6666
use super::region_inference::GenericBoundFailure;
6767
use super::region_inference::GenericKind;
68-
use super::region_inference::ProcessedErrors;
69-
use super::region_inference::ProcessedErrorOrigin;
70-
use super::region_inference::SameRegions;
7168

7269
use hir::map as hir_map;
7370
use hir;
@@ -77,11 +74,10 @@ use infer;
7774
use middle::region;
7875
use traits::{ObligationCause, ObligationCauseCode};
7976
use ty::{self, TyCtxt, TypeFoldable};
80-
use ty::{Region, ReFree, Issue32330};
77+
use ty::{Region, Issue32330};
8178
use ty::error::TypeError;
8279

8380
use std::fmt;
84-
use syntax::ast;
8581
use syntax_pos::{Pos, Span};
8682
use errors::DiagnosticBuilder;
8783

@@ -255,8 +251,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
255251

256252
// try to pre-process the errors, which will group some of them
257253
// together into a `ProcessedErrors` group:
258-
let processed_errors = self.process_errors(errors);
259-
let errors = processed_errors.as_ref().unwrap_or(errors);
254+
let errors = self.process_errors(errors);
260255

261256
debug!("report_region_errors: {} errors after preprocessing", errors.len());
262257

@@ -278,13 +273,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
278273
sub_origin, sub_r,
279274
sup_origin, sup_r);
280275
}
281-
282-
ProcessedErrors(ref origins,
283-
ref same_regions) => {
284-
if !same_regions.is_empty() {
285-
self.report_processed_errors(origins);
286-
}
287-
}
288276
}
289277
}
290278
}
@@ -300,202 +288,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
300288
// duplicates that will be unhelpful to the end-user. But
301289
// obviously it never weeds out ALL errors.
302290
fn process_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>)
303-
-> Option<Vec<RegionResolutionError<'tcx>>> {
291+
-> Vec<RegionResolutionError<'tcx>> {
304292
debug!("process_errors()");
305-
let mut origins = Vec::new();
306-
307-
// we collect up ConcreteFailures and SubSupConflicts that are
308-
// relating free-regions bound on the fn-header and group them
309-
// together into this vector
310-
let mut same_regions = Vec::new();
311-
312-
// here we put errors that we will not be able to process nicely
313-
let mut other_errors = Vec::new();
314-
315-
// we collect up GenericBoundFailures in here.
316-
let mut bound_failures = Vec::new();
317-
318-
for error in errors {
319-
// Check whether we can process this error into some other
320-
// form; if not, fall through.
321-
match *error {
322-
ConcreteFailure(ref origin, sub, sup) => {
323-
debug!("processing ConcreteFailure");
324-
if let SubregionOrigin::CompareImplMethodObligation { .. } = *origin {
325-
// When comparing an impl method against a
326-
// trait method, it is not helpful to suggest
327-
// changes to the impl method. This is
328-
// because the impl method signature is being
329-
// checked using the trait's environment, so
330-
// usually the changes we suggest would
331-
// actually have to be applied to the *trait*
332-
// method (and it's not clear that the trait
333-
// method is even under the user's control).
334-
} else if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
335-
origins.push(
336-
ProcessedErrorOrigin::ConcreteFailure(
337-
origin.clone(),
338-
sub,
339-
sup));
340-
append_to_same_regions(&mut same_regions, &same_frs);
341-
continue;
342-
}
343-
}
344-
SubSupConflict(ref var_origin, ref sub_origin, sub, ref sup_origin, sup) => {
345-
debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub, sup);
346-
match (sub_origin, sup_origin) {
347-
(&SubregionOrigin::CompareImplMethodObligation { .. }, _) => {
348-
// As above, when comparing an impl method
349-
// against a trait method, it is not helpful
350-
// to suggest changes to the impl method.
351-
}
352-
(_, &SubregionOrigin::CompareImplMethodObligation { .. }) => {
353-
// See above.
354-
}
355-
_ => {
356-
if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
357-
origins.push(
358-
ProcessedErrorOrigin::VariableFailure(
359-
var_origin.clone()));
360-
append_to_same_regions(&mut same_regions, &same_frs);
361-
continue;
362-
}
363-
}
364-
}
365-
}
366-
GenericBoundFailure(ref origin, ref kind, region) => {
367-
bound_failures.push((origin.clone(), kind.clone(), region));
368-
continue;
369-
}
370-
ProcessedErrors(..) => {
371-
bug!("should not encounter a `ProcessedErrors` yet: {:?}", error)
372-
}
373-
}
374-
375-
// No changes to this error.
376-
other_errors.push(error.clone());
377-
}
378-
379-
// ok, let's pull together the errors, sorted in an order that
380-
// we think will help user the best
381-
let mut processed_errors = vec![];
382-
383-
// first, put the processed errors, if any
384-
if !same_regions.is_empty() {
385-
let common_scope_id = same_regions[0].scope_id;
386-
for sr in &same_regions {
387-
// Since ProcessedErrors is used to reconstruct the function
388-
// declaration, we want to make sure that they are, in fact,
389-
// from the same scope
390-
if sr.scope_id != common_scope_id {
391-
debug!("returning empty result from process_errors because
392-
{} != {}", sr.scope_id, common_scope_id);
393-
return None;
394-
}
395-
}
396-
assert!(origins.len() > 0);
397-
let pe = ProcessedErrors(origins, same_regions);
398-
debug!("errors processed: {:?}", pe);
399-
processed_errors.push(pe);
400-
}
401-
402-
// next, put the other misc errors
403-
processed_errors.extend(other_errors);
404-
405-
// finally, put the `T: 'a` errors, but only if there were no
406-
// other errors. otherwise, these have a very high rate of
407-
// being unhelpful in practice. This is because they are
408-
// basically secondary checks that test the state of the
409-
// region graph after the rest of inference is done, and the
410-
// other kinds of errors indicate that the region constraint
411-
// graph is internally inconsistent, so these test results are
412-
// likely to be meaningless.
413-
if processed_errors.is_empty() {
414-
for (origin, kind, region) in bound_failures {
415-
processed_errors.push(GenericBoundFailure(origin, kind, region));
416-
}
417-
}
418293

419-
// we should always wind up with SOME errors, unless there were no
420-
// errors to start
421-
assert!(if errors.len() > 0 {processed_errors.len() > 0} else {true});
422-
423-
return Some(processed_errors);
424-
425-
#[derive(Debug)]
426-
struct FreeRegionsFromSameFn {
427-
sub_fr: ty::FreeRegion,
428-
sup_fr: ty::FreeRegion,
429-
scope_id: ast::NodeId
430-
}
431-
432-
impl FreeRegionsFromSameFn {
433-
fn new(sub_fr: ty::FreeRegion,
434-
sup_fr: ty::FreeRegion,
435-
scope_id: ast::NodeId)
436-
-> FreeRegionsFromSameFn {
437-
FreeRegionsFromSameFn {
438-
sub_fr: sub_fr,
439-
sup_fr: sup_fr,
440-
scope_id: scope_id
441-
}
442-
}
443-
}
444-
445-
fn free_regions_from_same_fn<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
446-
sub: &'tcx Region,
447-
sup: &'tcx Region)
448-
-> Option<FreeRegionsFromSameFn> {
449-
debug!("free_regions_from_same_fn(sub={:?}, sup={:?})", sub, sup);
450-
let (scope_id, fr1, fr2) = match (sub, sup) {
451-
(&ReFree(fr1), &ReFree(fr2)) => {
452-
if fr1.scope != fr2.scope {
453-
return None
454-
}
455-
assert!(fr1.scope == fr2.scope);
456-
(fr1.scope.node_id(&tcx.region_maps), fr1, fr2)
457-
},
458-
_ => return None
459-
};
460-
let parent = tcx.hir.get_parent(scope_id);
461-
let parent_node = tcx.hir.find(parent);
462-
match parent_node {
463-
Some(node) => match node {
464-
hir_map::NodeItem(item) => match item.node {
465-
hir::ItemFn(..) => {
466-
Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
467-
},
468-
_ => None
469-
},
470-
hir_map::NodeImplItem(..) |
471-
hir_map::NodeTraitItem(..) => {
472-
Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
473-
},
474-
_ => None
475-
},
476-
None => {
477-
debug!("no parent node of scope_id {}", scope_id);
478-
None
479-
}
480-
}
481-
}
294+
// We want to avoid reporting generic-bound failures if we can
295+
// avoid it: these have a very high rate of being unhelpful in
296+
// practice. This is because they are basically secondary
297+
// checks that test the state of the region graph after the
298+
// rest of inference is done, and the other kinds of errors
299+
// indicate that the region constraint graph is internally
300+
// inconsistent, so these test results are likely to be
301+
// meaningless.
302+
//
303+
// Therefore, we filter them out of the list unless they are
304+
// the only thing in the list.
305+
306+
let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
307+
ConcreteFailure(..) => false,
308+
SubSupConflict(..) => false,
309+
GenericBoundFailure(..) => true,
310+
};
482311

483-
fn append_to_same_regions(same_regions: &mut Vec<SameRegions>,
484-
same_frs: &FreeRegionsFromSameFn) {
485-
debug!("append_to_same_regions(same_regions={:?}, same_frs={:?})",
486-
same_regions, same_frs);
487-
let scope_id = same_frs.scope_id;
488-
let (sub_fr, sup_fr) = (same_frs.sub_fr, same_frs.sup_fr);
489-
for sr in same_regions.iter_mut() {
490-
if sr.contains(&sup_fr.bound_region) && scope_id == sr.scope_id {
491-
sr.push(sub_fr.bound_region);
492-
return
493-
}
494-
}
495-
same_regions.push(SameRegions {
496-
scope_id: scope_id,
497-
regions: vec![sub_fr.bound_region, sup_fr.bound_region]
498-
})
312+
if errors.iter().all(|e| is_bound_failure(e)) {
313+
errors.clone()
314+
} else {
315+
errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
499316
}
500317
}
501318

@@ -1072,20 +889,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
1072889
self.note_region_origin(&mut err, &sub_origin);
1073890
err.emit();
1074891
}
1075-
1076-
fn report_processed_errors(&self,
1077-
origins: &[ProcessedErrorOrigin<'tcx>]) {
1078-
for origin in origins.iter() {
1079-
let mut err = match *origin {
1080-
ProcessedErrorOrigin::VariableFailure(ref var_origin) =>
1081-
self.report_inference_failure(var_origin.clone()),
1082-
ProcessedErrorOrigin::ConcreteFailure(ref sr_origin, sub, sup) =>
1083-
self.report_concrete_failure(sr_origin.clone(), sub, sup),
1084-
};
1085-
1086-
err.emit();
1087-
}
1088-
}
1089892
}
1090893

1091894
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

src/librustc/infer/region_inference/mod.rs

+1-35
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING};
2424
use rustc_data_structures::unify::{self, UnificationTable};
2525
use middle::free_region::FreeRegionMap;
2626
use ty::{self, Ty, TyCtxt};
27-
use ty::{BoundRegion, Region, RegionVid};
27+
use ty::{Region, RegionVid};
2828
use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased};
2929
use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
3030

@@ -171,13 +171,6 @@ pub enum RegionResolutionError<'tcx> {
171171
&'tcx Region,
172172
SubregionOrigin<'tcx>,
173173
&'tcx Region),
174-
175-
/// For subsets of `ConcreteFailure` and `SubSupConflict`, we can derive
176-
/// more specific errors message by suggesting to the user where they
177-
/// should put a lifetime. In those cases we process and put those errors
178-
/// into `ProcessedErrors` before we do any reporting.
179-
ProcessedErrors(Vec<ProcessedErrorOrigin<'tcx>>,
180-
Vec<SameRegions>),
181174
}
182175

183176
#[derive(Clone, Debug)]
@@ -186,33 +179,6 @@ pub enum ProcessedErrorOrigin<'tcx> {
186179
VariableFailure(RegionVariableOrigin),
187180
}
188181

189-
/// SameRegions is used to group regions that we think are the same and would
190-
/// like to indicate so to the user.
191-
/// For example, the following function
192-
/// ```
193-
/// struct Foo { bar: i32 }
194-
/// fn foo2<'a, 'b>(x: &'a Foo) -> &'b i32 {
195-
/// &x.bar
196-
/// }
197-
/// ```
198-
/// would report an error because we expect 'a and 'b to match, and so we group
199-
/// 'a and 'b together inside a SameRegions struct
200-
#[derive(Clone, Debug)]
201-
pub struct SameRegions {
202-
pub scope_id: ast::NodeId,
203-
pub regions: Vec<BoundRegion>,
204-
}
205-
206-
impl SameRegions {
207-
pub fn contains(&self, other: &BoundRegion) -> bool {
208-
self.regions.contains(other)
209-
}
210-
211-
pub fn push(&mut self, other: BoundRegion) {
212-
self.regions.push(other);
213-
}
214-
}
215-
216182
pub type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
217183

218184
pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {

src/test/compile-fail/issue-17728.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,6 @@ impl Debug for Player {
108108

109109
fn str_to_direction(to_parse: &str) -> RoomDirection {
110110
match to_parse { //~ ERROR match arms have incompatible types
111-
//~^ expected enum `RoomDirection`, found enum `std::option::Option`
112-
//~| expected type `RoomDirection`
113-
//~| found type `std::option::Option<_>`
114111
"w" | "west" => RoomDirection::West,
115112
"e" | "east" => RoomDirection::East,
116113
"n" | "north" => RoomDirection::North,
@@ -119,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
119116
"out" => RoomDirection::Out,
120117
"up" => RoomDirection::Up,
121118
"down" => RoomDirection::Down,
122-
_ => None //~ NOTE match arm with an incompatible type
119+
_ => None
123120
}
124121
}
125122

src/test/run-pass/const-enum-vec-index.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#[derive(Copy, Clone)]
1112
enum E { V1(isize), V0 }
13+
1214
const C: &'static [E] = &[E::V0, E::V1(0xDEADBEE)];
1315
static C0: E = C[0];
1416
static C1: E = C[1];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2016 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+
11+
fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
12+
if x > y { x } else { y }
13+
}
14+
15+
fn main() { }

0 commit comments

Comments
 (0)