Skip to content

Commit 1f0a968

Browse files
committed
Auto merge of rust-lang#92306 - Aaron1011:opaque-type-op, r=oli-obk
Improve opaque type higher-ranked region error message under NLL Currently, any higher-ranked region errors involving opaque types fall back to a generic "higher-ranked subtype error" message when run under NLL. This PR adds better error message handling for this case, giving us the same kinds of error messages that we currently get without NLL: ``` error: implementation of `MyTrait` is not general enough --> $DIR/opaque-hrtb.rs:12:13 | LL | fn foo() -> impl for<'a> MyTrait<&'a str> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `MyTrait` is not general enough | = note: `impl MyTrait<&'2 str>` must implement `MyTrait<&'1 str>`, for any lifetime `'1`... = note: ...but it actually implements `MyTrait<&'2 str>`, for some specific lifetime `'2` error: aborting due to previous error ``` To accomplish this, several different refactoring needed to be made: * We now have a dedicated `InstantiateOpaqueType` struct which implements `TypeOp`. This is used to invoke `instantiate_opaque_types` during MIR type checking. * `TypeOp` is refactored to pass around a `MirBorrowckCtxt`, which is needed to report opaque type region errors. * We no longer assume that all `TypeOp`s correspond to canonicalized queries. This allows us to properly handle opaque type instantiation (which does not occur in a query) as a `TypeOp`. A new `ErrorInfo` associated type is used to determine what additional information is used during higher-ranked region error handling. * The body of `try_extract_error_from_fulfill_cx` has been moved out to a new function `try_extract_error_from_region_constraints`. This allows us to re-use the same error reporting code between canonicalized queries (which can extract region constraints directly from a fresh `InferCtxt`) and opaque type handling (which needs to take region constraints from the pre-existing `InferCtxt` that we use throughout MIR borrow checking).
2 parents bf242bb + 48a48fd commit 1f0a968

File tree

14 files changed

+231
-71
lines changed

14 files changed

+231
-71
lines changed

compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs

+87-21
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ use rustc_errors::DiagnosticBuilder;
22
use rustc_infer::infer::canonical::Canonical;
33
use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError;
44
use rustc_infer::infer::region_constraints::Constraint;
5+
use rustc_infer::infer::region_constraints::RegionConstraintData;
6+
use rustc_infer::infer::RegionVariableOrigin;
57
use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _};
68
use rustc_infer::traits::{Normalized, ObligationCause, TraitEngine, TraitEngineExt};
79
use rustc_middle::ty::error::TypeError;
10+
use rustc_middle::ty::RegionVid;
11+
use rustc_middle::ty::UniverseIndex;
812
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
913
use rustc_span::Span;
1014
use rustc_trait_selection::traits::query::type_op;
@@ -78,6 +82,15 @@ crate trait ToUniverseInfo<'tcx> {
7882
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx>;
7983
}
8084

85+
impl<'tcx> ToUniverseInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
86+
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
87+
UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(crate::type_check::InstantiateOpaqueType {
88+
base_universe: Some(base_universe),
89+
..self
90+
})))
91+
}
92+
}
93+
8194
impl<'tcx> ToUniverseInfo<'tcx>
8295
for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>
8396
{
@@ -118,6 +131,12 @@ impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::Custo
118131
}
119132
}
120133

134+
impl<'tcx> ToUniverseInfo<'tcx> for ! {
135+
fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
136+
self
137+
}
138+
}
139+
121140
#[allow(unused_lifetimes)]
122141
trait TypeOpInfo<'tcx> {
123142
/// Returns an error to be reported if rerunning the type op fails to
@@ -128,7 +147,7 @@ trait TypeOpInfo<'tcx> {
128147

129148
fn nice_error(
130149
&self,
131-
tcx: TyCtxt<'tcx>,
150+
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
132151
cause: ObligationCause<'tcx>,
133152
placeholder_region: ty::Region<'tcx>,
134153
error_region: Option<ty::Region<'tcx>>,
@@ -175,7 +194,7 @@ trait TypeOpInfo<'tcx> {
175194
debug!(?placeholder_region);
176195

177196
let span = cause.span;
178-
let nice_error = self.nice_error(tcx, cause, placeholder_region, error_region);
197+
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
179198

180199
if let Some(nice_error) = nice_error {
181200
nice_error.buffer(&mut mbcx.errors_buffer);
@@ -204,16 +223,16 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
204223

205224
fn nice_error(
206225
&self,
207-
tcx: TyCtxt<'tcx>,
226+
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
208227
cause: ObligationCause<'tcx>,
209228
placeholder_region: ty::Region<'tcx>,
210229
error_region: Option<ty::Region<'tcx>>,
211230
) -> Option<DiagnosticBuilder<'tcx>> {
212-
tcx.infer_ctxt().enter_with_canonical(
231+
mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
213232
cause.span,
214233
&self.canonical_query,
215234
|ref infcx, key, _| {
216-
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
235+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
217236
type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause);
218237
try_extract_error_from_fulfill_cx(
219238
fulfill_cx,
@@ -247,16 +266,16 @@ where
247266

248267
fn nice_error(
249268
&self,
250-
tcx: TyCtxt<'tcx>,
269+
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
251270
cause: ObligationCause<'tcx>,
252271
placeholder_region: ty::Region<'tcx>,
253272
error_region: Option<ty::Region<'tcx>>,
254273
) -> Option<DiagnosticBuilder<'tcx>> {
255-
tcx.infer_ctxt().enter_with_canonical(
274+
mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
256275
cause.span,
257276
&self.canonical_query,
258277
|ref infcx, key, _| {
259-
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
278+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
260279

261280
let mut selcx = SelectionContext::new(infcx);
262281

@@ -304,16 +323,16 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
304323

305324
fn nice_error(
306325
&self,
307-
tcx: TyCtxt<'tcx>,
326+
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
308327
cause: ObligationCause<'tcx>,
309328
placeholder_region: ty::Region<'tcx>,
310329
error_region: Option<ty::Region<'tcx>>,
311330
) -> Option<DiagnosticBuilder<'tcx>> {
312-
tcx.infer_ctxt().enter_with_canonical(
331+
mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
313332
cause.span,
314333
&self.canonical_query,
315334
|ref infcx, key, _| {
316-
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
335+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
317336
type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span))
318337
.ok()?;
319338
try_extract_error_from_fulfill_cx(
@@ -327,43 +346,90 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
327346
}
328347
}
329348

349+
impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
350+
fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
351+
// FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
352+
// and is only the fallback when the nice error fails. Consider improving this some more.
353+
tcx.sess.struct_span_err(span, "higher-ranked lifetime error for opaque type!")
354+
}
355+
356+
fn base_universe(&self) -> ty::UniverseIndex {
357+
self.base_universe.unwrap()
358+
}
359+
360+
fn nice_error(
361+
&self,
362+
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
363+
_cause: ObligationCause<'tcx>,
364+
placeholder_region: ty::Region<'tcx>,
365+
error_region: Option<ty::Region<'tcx>>,
366+
) -> Option<DiagnosticBuilder<'tcx>> {
367+
try_extract_error_from_region_constraints(
368+
mbcx.infcx,
369+
placeholder_region,
370+
error_region,
371+
self.region_constraints.as_ref().unwrap(),
372+
// We're using the original `InferCtxt` that we
373+
// started MIR borrowchecking with, so the region
374+
// constraints have already been taken. Use the data from
375+
// our `mbcx` instead.
376+
|vid| mbcx.regioncx.var_infos[vid].origin,
377+
|vid| mbcx.regioncx.var_infos[vid].universe,
378+
)
379+
}
380+
}
381+
330382
#[instrument(skip(fulfill_cx, infcx), level = "debug")]
331383
fn try_extract_error_from_fulfill_cx<'tcx>(
332384
mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>,
333385
infcx: &InferCtxt<'_, 'tcx>,
334386
placeholder_region: ty::Region<'tcx>,
335387
error_region: Option<ty::Region<'tcx>>,
336388
) -> Option<DiagnosticBuilder<'tcx>> {
337-
let tcx = infcx.tcx;
338-
339389
// We generally shouldn't have errors here because the query was
340390
// already run, but there's no point using `delay_span_bug`
341391
// when we're going to emit an error here anyway.
342392
let _errors = fulfill_cx.select_all_or_error(infcx);
393+
let region_constraints = infcx.with_region_constraints(|r| r.clone());
394+
try_extract_error_from_region_constraints(
395+
infcx,
396+
placeholder_region,
397+
error_region,
398+
&region_constraints,
399+
|vid| infcx.region_var_origin(vid),
400+
|vid| infcx.universe_of_region(infcx.tcx.mk_region(ty::ReVar(vid))),
401+
)
402+
}
343403

344-
let (sub_region, cause) = infcx.with_region_constraints(|region_constraints| {
345-
debug!("{:#?}", region_constraints);
404+
fn try_extract_error_from_region_constraints<'tcx>(
405+
infcx: &InferCtxt<'_, 'tcx>,
406+
placeholder_region: ty::Region<'tcx>,
407+
error_region: Option<ty::Region<'tcx>>,
408+
region_constraints: &RegionConstraintData<'tcx>,
409+
mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
410+
mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
411+
) -> Option<DiagnosticBuilder<'tcx>> {
412+
let (sub_region, cause) =
346413
region_constraints.constraints.iter().find_map(|(constraint, cause)| {
347414
match *constraint {
348415
Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
349416
Some((sub, cause.clone()))
350417
}
351418
// FIXME: Should this check the universe of the var?
352419
Constraint::VarSubReg(vid, sup) if sup == placeholder_region => {
353-
Some((tcx.mk_region(ty::ReVar(vid)), cause.clone()))
420+
Some((infcx.tcx.mk_region(ty::ReVar(vid)), cause.clone()))
354421
}
355422
_ => None,
356423
}
357-
})
358-
})?;
424+
})?;
359425

360426
debug!(?sub_region, "cause = {:#?}", cause);
361427
let nice_error = match (error_region, sub_region) {
362428
(Some(error_region), &ty::ReVar(vid)) => NiceRegionError::new(
363429
infcx,
364430
RegionResolutionError::SubSupConflict(
365431
vid,
366-
infcx.region_var_origin(vid),
432+
region_var_origin(vid),
367433
cause.clone(),
368434
error_region,
369435
cause.clone(),
@@ -380,8 +446,8 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
380446
infcx,
381447
RegionResolutionError::UpperBoundUniverseConflict(
382448
vid,
383-
infcx.region_var_origin(vid),
384-
infcx.universe_of_region(sub_region),
449+
region_var_origin(vid),
450+
universe_of_region(vid),
385451
cause.clone(),
386452
placeholder_region,
387453
),

compiler/rustc_borrowck/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![feature(crate_visibility_modifier)]
66
#![feature(let_else)]
77
#![feature(min_specialization)]
8+
#![feature(never_type)]
89
#![feature(stmt_expr_attributes)]
910
#![feature(trusted_step)]
1011
#![feature(try_blocks)]

compiler/rustc_borrowck/src/region_infer/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mod reverse_sccs;
4444
pub mod values;
4545

4646
pub struct RegionInferenceContext<'tcx> {
47+
pub var_infos: VarInfos,
4748
/// Contains the definition for every region variable. Region
4849
/// variables are identified by their index (`RegionVid`). The
4950
/// definition contains information about where the region came
@@ -266,7 +267,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
266267
) -> Self {
267268
// Create a RegionDefinition for each inference variable.
268269
let definitions: IndexVec<_, _> = var_infos
269-
.into_iter()
270+
.iter()
270271
.map(|info| RegionDefinition::new(info.universe, info.origin))
271272
.collect();
272273

@@ -291,6 +292,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
291292
Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
292293

293294
let mut result = Self {
295+
var_infos,
294296
definitions,
295297
liveness_constraints,
296298
constraints,

compiler/rustc_borrowck/src/type_check/canonical.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
3333
) -> Fallible<R>
3434
where
3535
Op: type_op::TypeOp<'tcx, Output = R>,
36-
Canonical<'tcx, Op>: ToUniverseInfo<'tcx>,
36+
Op::ErrorInfo: ToUniverseInfo<'tcx>,
3737
{
3838
let old_universe = self.infcx.universe();
3939

40-
let TypeOpOutput { output, constraints, canonicalized_query } =
41-
op.fully_perform(self.infcx)?;
40+
let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?;
4241

4342
if let Some(data) = &constraints {
4443
self.push_region_constraints(locations, category, data);
@@ -47,8 +46,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
4746
let universe = self.infcx.universe();
4847

4948
if old_universe != universe {
50-
let universe_info = match canonicalized_query {
51-
Some(canonicalized_query) => canonicalized_query.to_universe_info(old_universe),
49+
let universe_info = match error_info {
50+
Some(error_info) => error_info.to_universe_info(old_universe),
5251
None => UniverseInfo::other(),
5352
};
5453
for u in old_universe..universe {

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
268268
TypeOpOutput {
269269
output: self.infcx.tcx.ty_error(),
270270
constraints: None,
271-
canonicalized_query: None,
271+
error_info: None,
272272
}
273273
});
274274
// Note: we need this in examples like

compiler/rustc_borrowck/src/type_check/mod.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_hir::lang_items::LangItem;
1717
use rustc_index::vec::{Idx, IndexVec};
1818
use rustc_infer::infer::canonical::QueryRegionConstraints;
1919
use rustc_infer::infer::outlives::env::RegionBoundPairs;
20+
use rustc_infer::infer::region_constraints::RegionConstraintData;
2021
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2122
use rustc_infer::infer::{
2223
InferCtxt, InferOk, LateBoundRegionConversionTime, NllRegionVariableOrigin,
@@ -39,9 +40,11 @@ use rustc_target::abi::VariantIdx;
3940
use rustc_trait_selection::infer::InferCtxtExt as _;
4041
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
4142
use rustc_trait_selection::traits::query::type_op;
43+
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
4244
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
45+
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
4346
use rustc_trait_selection::traits::query::Fallible;
44-
use rustc_trait_selection::traits::{self, ObligationCause};
47+
use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
4548

4649
use rustc_const_eval::transform::{
4750
check_consts::ConstCx, promote_consts::is_const_fn_in_array_repeat_expression,
@@ -2637,3 +2640,32 @@ impl NormalizeLocation for Location {
26372640
Locations::Single(self)
26382641
}
26392642
}
2643+
2644+
/// Runs `infcx.instantiate_opaque_types`. Unlike other `TypeOp`s,
2645+
/// this is not canonicalized - it directly affects the main `InferCtxt`
2646+
/// that we use during MIR borrowchecking.
2647+
#[derive(Debug)]
2648+
pub(super) struct InstantiateOpaqueType<'tcx> {
2649+
pub base_universe: Option<ty::UniverseIndex>,
2650+
pub region_constraints: Option<RegionConstraintData<'tcx>>,
2651+
pub obligation: PredicateObligation<'tcx>,
2652+
}
2653+
2654+
impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
2655+
type Output = ();
2656+
/// We use this type itself to store the information used
2657+
/// when reporting errors. Since this is not a query, we don't
2658+
/// re-run anything during error reporting - we just use the information
2659+
/// we saved to help extract an error from the already-existing region
2660+
/// constraints in our `InferCtxt`
2661+
type ErrorInfo = InstantiateOpaqueType<'tcx>;
2662+
2663+
fn fully_perform(mut self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
2664+
let (mut output, region_constraints) = scrape_region_constraints(infcx, || {
2665+
Ok(InferOk { value: (), obligations: vec![self.obligation.clone()] })
2666+
})?;
2667+
self.region_constraints = Some(region_constraints);
2668+
output.error_info = Some(self);
2669+
Ok(output)
2670+
}
2671+
}

0 commit comments

Comments
 (0)