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).
This commit is contained in:
parent
e7cc3bddbe
commit
48a48fd1b8
14 changed files with 231 additions and 71 deletions
|
@ -4,6 +4,7 @@ use crate::traits::engine::TraitEngineExt as _;
|
|||
use crate::traits::query::type_op::TypeOpOutput;
|
||||
use crate::traits::query::Fallible;
|
||||
use crate::traits::TraitEngine;
|
||||
use rustc_infer::infer::region_constraints::RegionConstraintData;
|
||||
use rustc_infer::traits::TraitEngineExt as _;
|
||||
use rustc_span::source_map::DUMMY_SP;
|
||||
|
||||
|
@ -31,6 +32,9 @@ where
|
|||
G: Fn() -> String,
|
||||
{
|
||||
type Output = R;
|
||||
/// We can't do any custom error reporting for `CustomTypeOp`, so
|
||||
/// we can use `!` to enforce that the implementation never provides it.
|
||||
type ErrorInfo = !;
|
||||
|
||||
/// Processes the operation and all resulting obligations,
|
||||
/// returning the final result along with any region constraints
|
||||
|
@ -40,7 +44,7 @@ where
|
|||
info!("fully_perform({:?})", self);
|
||||
}
|
||||
|
||||
scrape_region_constraints(infcx, || (self.closure)(infcx))
|
||||
Ok(scrape_region_constraints(infcx, || (self.closure)(infcx))?.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,10 +59,10 @@ where
|
|||
|
||||
/// Executes `op` and then scrapes out all the "old style" region
|
||||
/// constraints that result, creating query-region-constraints.
|
||||
fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
|
||||
pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
|
||||
infcx: &InferCtxt<'_, 'tcx>,
|
||||
op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
|
||||
) -> Fallible<TypeOpOutput<'tcx, Op>> {
|
||||
) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> {
|
||||
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
|
||||
|
||||
// During NLL, we expect that nobody will register region
|
||||
|
@ -97,12 +101,18 @@ fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
|
|||
);
|
||||
|
||||
if region_constraints.is_empty() {
|
||||
Ok(TypeOpOutput { output: value, constraints: None, canonicalized_query: None })
|
||||
Ok((
|
||||
TypeOpOutput { output: value, constraints: None, error_info: None },
|
||||
region_constraint_data,
|
||||
))
|
||||
} else {
|
||||
Ok(TypeOpOutput {
|
||||
output: value,
|
||||
constraints: Some(Rc::new(region_constraints)),
|
||||
canonicalized_query: None,
|
||||
})
|
||||
Ok((
|
||||
TypeOpOutput {
|
||||
output: value,
|
||||
constraints: Some(Rc::new(region_constraints)),
|
||||
error_info: None,
|
||||
},
|
||||
region_constraint_data,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ pub use rustc_middle::traits::query::type_op::*;
|
|||
/// cannot be completed).
|
||||
pub trait TypeOp<'tcx>: Sized + fmt::Debug {
|
||||
type Output;
|
||||
type ErrorInfo;
|
||||
|
||||
/// Processes the operation and all resulting obligations,
|
||||
/// returning the final result along with any region constraints
|
||||
|
@ -41,9 +42,8 @@ pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> {
|
|||
pub output: Op::Output,
|
||||
/// Any region constraints from performing the type op.
|
||||
pub constraints: Option<Rc<QueryRegionConstraints<'tcx>>>,
|
||||
/// The canonicalized form of the query.
|
||||
/// This for error reporting to be able to rerun the query.
|
||||
pub canonicalized_query: Option<Canonical<'tcx, Op>>,
|
||||
/// Used for error reporting to be able to rerun the query
|
||||
pub error_info: Option<Op::ErrorInfo>,
|
||||
}
|
||||
|
||||
/// "Query type ops" are type ops that are implemented using a
|
||||
|
@ -119,10 +119,11 @@ where
|
|||
Q: QueryTypeOp<'tcx>,
|
||||
{
|
||||
type Output = Q::QueryResponse;
|
||||
type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>;
|
||||
|
||||
fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
|
||||
let mut region_constraints = QueryRegionConstraints::default();
|
||||
let (output, canonicalized_query, mut obligations, _) =
|
||||
let (output, error_info, mut obligations, _) =
|
||||
Q::fully_perform_into(self, infcx, &mut region_constraints)?;
|
||||
|
||||
// Typically, instantiating NLL query results does not
|
||||
|
@ -160,6 +161,6 @@ where
|
|||
let region_constraints =
|
||||
if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) };
|
||||
|
||||
Ok(TypeOpOutput { output, constraints: region_constraints, canonicalized_query })
|
||||
Ok(TypeOpOutput { output, constraints: region_constraints, error_info })
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue