1
Fork 0

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:
Aaron Hill 2021-12-18 19:06:53 -05:00
parent e7cc3bddbe
commit 48a48fd1b8
No known key found for this signature in database
GPG key ID: B4087E510E98B164
14 changed files with 231 additions and 71 deletions

View file

@ -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,
))
}
}

View file

@ -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 })
}
}