Skip to content

Commit d563a63

Browse files
matthewjasperlqd
authored andcommitted
Report nicer errors for HRTB NLL errors from queries
1 parent 0c388b0 commit d563a63

29 files changed

+473
-101
lines changed

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1111,7 +1111,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11111111
/// etc) this is the root universe U0. For inference variables or
11121112
/// placeholders, however, it will return the universe which which
11131113
/// they are associated.
1114-
fn universe_of_region(&self, r: ty::Region<'tcx>) -> ty::UniverseIndex {
1114+
pub fn universe_of_region(&self, r: ty::Region<'tcx>) -> ty::UniverseIndex {
11151115
self.inner.borrow_mut().unwrap_region_constraints().universe(r)
11161116
}
11171117

@@ -1289,6 +1289,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
12891289
op(inner.unwrap_region_constraints().data())
12901290
}
12911291

1292+
pub fn region_var_origin(&self, vid: ty::RegionVid) -> RegionVariableOrigin {
1293+
let mut inner = self.inner.borrow_mut();
1294+
let inner = &mut *inner;
1295+
inner
1296+
.region_constraint_storage
1297+
.as_mut()
1298+
.expect("regions already resolved")
1299+
.with_log(&mut inner.undo_log)
1300+
.var_origin(vid)
1301+
}
1302+
12921303
/// Takes ownership of the list of variable regions. This implies
12931304
/// that all the region constraints have already been taken, and
12941305
/// hence that `resolve_regions_and_report_errors` can never be

compiler/rustc_infer/src/infer/region_constraints/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
445445
self.var_infos[vid].universe
446446
}
447447

448+
/// Returns the origin for the given variable.
449+
pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
450+
self.var_infos[vid].origin
451+
}
452+
448453
fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) {
449454
// cannot add constraints once regions are resolved
450455
debug!("RegionConstraintCollector: add_constraint({:?})", constraint);

compiler/rustc_mir/src/borrow_check/diagnostics/bound_region_errors.rs

Lines changed: 234 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
use rustc_errors::DiagnosticBuilder;
12
use rustc_infer::infer::canonical::Canonical;
2-
use rustc_infer::traits::ObligationCause;
3+
use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError;
4+
use rustc_infer::infer::region_constraints::Constraint;
5+
use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _};
6+
use rustc_infer::traits::{Normalized, Obligation, ObligationCause, TraitEngine, TraitEngineExt};
37
use rustc_middle::ty::error::TypeError;
4-
use rustc_middle::ty::{self, Ty, TypeFoldable};
8+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
59
use rustc_span::Span;
610
use rustc_trait_selection::traits::query::type_op;
11+
use rustc_trait_selection::traits::{SelectionContext, TraitEngineExt as _};
712

813
use std::fmt;
914
use std::rc::Rc;
@@ -37,11 +42,10 @@ impl UniverseInfo<'tcx> {
3742
crate fn report_error(
3843
&self,
3944
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
40-
_placeholder: ty::PlaceholderRegion,
41-
_error_element: RegionElement,
45+
placeholder: ty::PlaceholderRegion,
46+
error_element: RegionElement,
4247
span: Span,
4348
) {
44-
// FIXME: improve this error message
4549
match self.0 {
4650
UniverseInfoInner::RelateTys { expected, found } => {
4751
let body_id = mbcx.infcx.tcx.hir().local_def_id_to_hir_id(mbcx.mir_def_id());
@@ -53,7 +57,13 @@ impl UniverseInfo<'tcx> {
5357
);
5458
err.buffer(&mut mbcx.errors_buffer);
5559
}
56-
UniverseInfoInner::TypeOp(_) | UniverseInfoInner::Other => {
60+
UniverseInfoInner::TypeOp(ref type_op_info) => {
61+
type_op_info.report_error(mbcx, placeholder, error_element, span);
62+
}
63+
UniverseInfoInner::Other => {
64+
// FIXME: This error message isn't great, but it doesn't show
65+
// up in the existing UI tests. Consider investigating this
66+
// some more.
5767
mbcx.infcx
5868
.tcx
5969
.sess
@@ -73,8 +83,8 @@ impl<'tcx> ToUniverseInfo<'tcx>
7383
{
7484
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
7585
UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(PredicateQuery {
76-
_canonical_query: self,
77-
_base_universe: base_universe,
86+
canonical_query: self,
87+
base_universe,
7888
})))
7989
}
8090
}
@@ -84,8 +94,8 @@ impl<'tcx, T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx> ToUniverseInfo<'t
8494
{
8595
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
8696
UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(NormalizeQuery {
87-
_canonical_query: self,
88-
_base_universe: base_universe,
97+
canonical_query: self,
98+
base_universe,
8999
})))
90100
}
91101
}
@@ -109,23 +119,229 @@ impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::Custo
109119

110120
#[allow(unused_lifetimes)]
111121
trait TypeOpInfo<'tcx> {
112-
// TODO: Methods for rerunning type op and reporting an error
122+
/// Returns an rrror to be reported if rerunning the type op fails to
123+
/// recover the error's cause.
124+
fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
125+
126+
fn base_universe(&self) -> ty::UniverseIndex;
127+
128+
fn nice_error(
129+
&self,
130+
tcx: TyCtxt<'tcx>,
131+
span: Span,
132+
placeholder_region: ty::Region<'tcx>,
133+
error_region: Option<ty::Region<'tcx>>,
134+
) -> Option<DiagnosticBuilder<'tcx>>;
135+
136+
fn report_error(
137+
&self,
138+
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
139+
placeholder: ty::PlaceholderRegion,
140+
error_element: RegionElement,
141+
span: Span,
142+
) {
143+
let tcx = mbcx.infcx.tcx;
144+
let base_universe = self.base_universe();
145+
146+
let adjusted_universe = if let Some(adjusted) =
147+
placeholder.universe.as_u32().checked_sub(base_universe.as_u32())
148+
{
149+
adjusted
150+
} else {
151+
self.fallback_error(tcx, span).buffer(&mut mbcx.errors_buffer);
152+
return;
153+
};
154+
155+
let placeholder_region = tcx.mk_region(ty::RePlaceholder(ty::Placeholder {
156+
name: placeholder.name,
157+
universe: adjusted_universe.into(),
158+
}));
159+
160+
let error_region =
161+
if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
162+
let adjusted_universe =
163+
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
164+
adjusted_universe.map(|adjusted| {
165+
tcx.mk_region(ty::RePlaceholder(ty::Placeholder {
166+
name: error_placeholder.name,
167+
universe: adjusted.into(),
168+
}))
169+
})
170+
} else {
171+
None
172+
};
173+
174+
debug!(?placeholder_region);
175+
176+
let nice_error = self.nice_error(tcx, span, placeholder_region, error_region);
177+
178+
if let Some(nice_error) = nice_error {
179+
nice_error.buffer(&mut mbcx.errors_buffer);
180+
} else {
181+
self.fallback_error(tcx, span).buffer(&mut mbcx.errors_buffer);
182+
}
183+
}
113184
}
114185

115186
struct PredicateQuery<'tcx> {
116-
_canonical_query:
187+
canonical_query:
117188
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>,
118-
_base_universe: ty::UniverseIndex,
189+
base_universe: ty::UniverseIndex,
119190
}
120191

121-
impl TypeOpInfo<'tcx> for PredicateQuery<'tcx> {}
192+
impl TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
193+
fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
194+
let mut err = tcx.sess.struct_span_err(span, "higher-ranked lifetime error");
195+
err.note(&format!("could not prove {}", self.canonical_query.value.value.predicate));
196+
err
197+
}
198+
199+
fn base_universe(&self) -> ty::UniverseIndex {
200+
self.base_universe
201+
}
202+
203+
fn nice_error(
204+
&self,
205+
tcx: TyCtxt<'tcx>,
206+
span: Span,
207+
placeholder_region: ty::Region<'tcx>,
208+
error_region: Option<ty::Region<'tcx>>,
209+
) -> Option<DiagnosticBuilder<'tcx>> {
210+
tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
211+
let mut fulfill_cx = TraitEngine::new(tcx);
212+
213+
let (param_env, prove_predicate) = key.into_parts();
214+
fulfill_cx.register_predicate_obligation(
215+
infcx,
216+
Obligation::new(
217+
ObligationCause::dummy_with_span(span),
218+
param_env,
219+
prove_predicate.predicate,
220+
),
221+
);
222+
223+
try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
224+
})
225+
}
226+
}
122227

123228
struct NormalizeQuery<'tcx, T> {
124-
_canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>,
125-
_base_universe: ty::UniverseIndex,
229+
canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>,
230+
base_universe: ty::UniverseIndex,
126231
}
127232

128-
impl<T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T> where
129-
T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx
233+
impl<T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T>
234+
where
235+
T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx,
130236
{
237+
fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
238+
let mut err = tcx.sess.struct_span_err(span, "higher-ranked lifetime error");
239+
err.note(&format!("could not normalize `{}`", self.canonical_query.value.value.value));
240+
err
241+
}
242+
243+
fn base_universe(&self) -> ty::UniverseIndex {
244+
self.base_universe
245+
}
246+
247+
fn nice_error(
248+
&self,
249+
tcx: TyCtxt<'tcx>,
250+
span: Span,
251+
placeholder_region: ty::Region<'tcx>,
252+
error_region: Option<ty::Region<'tcx>>,
253+
) -> Option<DiagnosticBuilder<'tcx>> {
254+
tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
255+
let mut fulfill_cx = TraitEngine::new(tcx);
256+
257+
let mut selcx = SelectionContext::new(infcx);
258+
let (param_env, value) = key.into_parts();
259+
260+
let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize(
261+
&mut selcx,
262+
param_env,
263+
ObligationCause::dummy_with_span(span),
264+
value.value,
265+
);
266+
fulfill_cx.register_predicate_obligations(infcx, obligations);
267+
268+
try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
269+
})
270+
}
271+
}
272+
273+
fn try_extract_error_from_fulfill_cx<'tcx>(
274+
mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>,
275+
infcx: &InferCtxt<'_, 'tcx>,
276+
placeholder_region: ty::Region<'tcx>,
277+
error_region: Option<ty::Region<'tcx>>,
278+
) -> Option<DiagnosticBuilder<'tcx>> {
279+
let tcx = infcx.tcx;
280+
281+
// We generally shouldn't have here because the query was
282+
// already run, but there's no point using `delay_span_bug`
283+
// when we're going to emit an error here anyway.
284+
let _errors = fulfill_cx.select_all_or_error(infcx).err().unwrap_or_else(Vec::new);
285+
286+
let region_obligations = infcx.take_registered_region_obligations();
287+
debug!(?region_obligations);
288+
289+
let (sub_region, cause) = infcx.with_region_constraints(|region_constraints| {
290+
debug!(?region_constraints);
291+
region_constraints.constraints.iter().find_map(|(constraint, cause)| {
292+
match *constraint {
293+
Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
294+
Some((sub, cause.clone()))
295+
}
296+
// FIXME: Should this check the universe of the var?
297+
Constraint::VarSubReg(vid, sup) if sup == placeholder_region => {
298+
Some((tcx.mk_region(ty::ReVar(vid)), cause.clone()))
299+
}
300+
_ => None,
301+
}
302+
})
303+
})?;
304+
305+
debug!(?sub_region, ?cause);
306+
let nice_error = match (error_region, sub_region) {
307+
(Some(error_region), &ty::ReVar(vid)) => NiceRegionError::new(
308+
infcx,
309+
RegionResolutionError::SubSupConflict(
310+
vid,
311+
infcx.region_var_origin(vid),
312+
cause.clone(),
313+
error_region,
314+
cause.clone(),
315+
placeholder_region,
316+
),
317+
),
318+
(Some(error_region), _) => NiceRegionError::new(
319+
infcx,
320+
RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region),
321+
),
322+
// Note universe here is wrong...
323+
(None, &ty::ReVar(vid)) => NiceRegionError::new(
324+
infcx,
325+
RegionResolutionError::UpperBoundUniverseConflict(
326+
vid,
327+
infcx.region_var_origin(vid),
328+
infcx.universe_of_region(sub_region),
329+
cause.clone(),
330+
placeholder_region,
331+
),
332+
),
333+
(None, _) => NiceRegionError::new(
334+
infcx,
335+
RegionResolutionError::ConcreteFailure(cause.clone(), sub_region, placeholder_region),
336+
),
337+
};
338+
nice_error.try_report_from_nll().or_else(|| {
339+
if let SubregionOrigin::Subtype(trace) = cause {
340+
Some(
341+
infcx.report_and_explain_type_error(*trace, &TypeError::RegionsPlaceholderMismatch),
342+
)
343+
} else {
344+
None
345+
}
346+
})
131347
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
error: higher-ranked subtype error
1+
error[E0308]: mismatched types
22
--> $DIR/higher-ranked-projection.rs:25:5
33
|
44
LL | foo(());
5-
| ^^^^^^^
5+
| ^^^^^^^ one type is more general than the other
6+
|
7+
= note: expected type `&'a ()`
8+
found reference `&()`
69

710
error: aborting due to previous error
811

12+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/generator/auto-trait-regions.nll.stderr

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,23 @@ LL | assert_foo(a);
2424
|
2525
= note: consider using a `let` binding to create a longer lived value
2626

27-
error: higher-ranked subtype error
27+
error: implementation of `Foo` is not general enough
2828
--> $DIR/auto-trait-regions.rs:31:5
2929
|
3030
LL | assert_foo(gen);
31-
| ^^^^^^^^^^^^^^^
31+
| ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
32+
|
33+
= note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`...
34+
= note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef`
3235

33-
error: higher-ranked subtype error
36+
error: implementation of `Foo` is not general enough
3437
--> $DIR/auto-trait-regions.rs:50:5
3538
|
3639
LL | assert_foo(gen);
37-
| ^^^^^^^^^^^^^^^
40+
| ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
41+
|
42+
= note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`...
43+
= note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2`
3844

3945
error: aborting due to 4 previous errors
4046

0 commit comments

Comments
 (0)