Try to use approximate placeholder regions when outputting an AscribeUserType error in borrowck

This commit is contained in:
Jack Huey 2023-09-19 16:19:06 -04:00
parent 42ca6e4e57
commit 35dd0c9049
6 changed files with 80 additions and 13 deletions

View file

@ -10,6 +10,8 @@
use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _};
use rustc_infer::traits::ObligationCause;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::RePlaceholder;
use rustc_middle::ty::Region;
use rustc_middle::ty::RegionVid;
use rustc_middle::ty::UniverseIndex;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
@ -205,6 +207,8 @@ fn report_error(
let span = cause.span;
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
debug!(?nice_error);
if let Some(nice_error) = nice_error {
mbcx.buffer_error(nice_error);
} else {
@ -404,19 +408,41 @@ fn try_extract_error_from_region_constraints<'tcx>(
mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
let (sub_region, cause) =
region_constraints.constraints.iter().find_map(|(constraint, cause)| {
match *constraint {
Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
Some((sub, cause.clone()))
}
// FIXME: Should this check the universe of the var?
Constraint::VarSubReg(vid, sup) if sup == placeholder_region => {
Some((ty::Region::new_var(infcx.tcx, vid), cause.clone()))
}
_ => None,
let matches =
|a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) {
(RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound,
_ => a_region == b_region,
};
let check = |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| {
match *constraint {
Constraint::RegSubReg(sub, sup)
if ((exact && sup == placeholder_region)
|| (!exact && matches(sup, placeholder_region)))
&& sup != sub =>
{
Some((sub, cause.clone()))
}
})?;
// FIXME: Should this check the universe of the var?
Constraint::VarSubReg(vid, sup)
if ((exact && sup == placeholder_region)
|| (!exact && matches(sup, placeholder_region))) =>
{
Some((ty::Region::new_var(infcx.tcx, vid), cause.clone()))
}
_ => None,
}
};
let mut info = region_constraints
.constraints
.iter()
.find_map(|(constraint, cause)| check(constraint, cause, true));
if info.is_none() {
info = region_constraints
.constraints
.iter()
.find_map(|(constraint, cause)| check(constraint, cause, false));
}
let (sub_region, cause) = info?;
debug!(?sub_region, "cause = {:#?}", cause);
let error = match (error_region, *sub_region) {

View file

@ -95,6 +95,12 @@ pub fn into_iter(self) -> impl Iterator<Item = RegionErrorKind<'tcx>> {
}
}
impl std::fmt::Debug for RegionErrors<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("RegionErrors").field(&self.0).finish()
}
}
#[derive(Clone, Debug)]
pub(crate) enum RegionErrorKind<'tcx> {
/// A generic bound failure for a type test (`T: 'a`).

View file

@ -147,6 +147,7 @@ pub(crate) struct AppliedMemberConstraint {
pub(crate) member_constraint_index: NllMemberConstraintIndex,
}
#[derive(Debug)]
pub(crate) struct RegionDefinition<'tcx> {
/// What kind of variable is this -- a free region? existential
/// variable? etc. (See the `NllRegionVariableOrigin` for more
@ -680,6 +681,9 @@ pub(super) fn solve(
&mut errors_buffer,
);
debug!(?errors_buffer);
debug!(?outlives_requirements);
// In Polonius mode, the errors about missing universal region relations are in the output
// and need to be emitted or propagated. Otherwise, we need to check whether the
// constraints were too strong, and if so, emit or propagate those errors.
@ -693,10 +697,14 @@ pub(super) fn solve(
self.check_universal_regions(outlives_requirements.as_mut(), &mut errors_buffer);
}
debug!(?errors_buffer);
if errors_buffer.is_empty() {
self.check_member_constraints(infcx, &mut errors_buffer);
}
debug!(?errors_buffer);
let outlives_requirements = outlives_requirements.unwrap_or_default();
if outlives_requirements.is_empty() {
@ -1450,6 +1458,7 @@ fn check_universal_regions(
errors_buffer: &mut RegionErrors<'tcx>,
) {
for (fr, fr_definition) in self.definitions.iter_enumerated() {
debug!(?fr, ?fr_definition);
match fr_definition.origin {
NllRegionVariableOrigin::FreeRegion => {
// Go through each of the universal regions `fr` and check that

View file

@ -1181,7 +1181,7 @@ fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
#[instrument(skip(self, body, location), level = "debug")]
#[instrument(skip(self, body), level = "debug")]
fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
let tcx = self.tcx();
debug!("stmt kind: {:?}", stmt.kind);

View file

@ -0,0 +1,14 @@
fn assert_all<F, T>(_f: F)
where
F: FnMut(&String) -> T,
{
}
fn id(x: &String) -> &String {
x
}
fn main() {
assert_all::<_, &String>(id);
//~^ mismatched types
}

View file

@ -0,0 +1,12 @@
error[E0308]: mismatched types
--> $DIR/higher-ranked-lifetime-error.rs:12:5
|
LL | assert_all::<_, &String>(id);
| ^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected reference `&String`
found reference `&String`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.