From 35dd0c90494909106736093199dfae84f229f747 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:19:06 -0400 Subject: [PATCH] Try to use approximate placeholder regions when outputting an AscribeUserType error in borrowck --- .../src/diagnostics/bound_region_errors.rs | 50 ++++++++++++++----- .../src/diagnostics/region_errors.rs | 6 +++ .../rustc_borrowck/src/region_infer/mod.rs | 9 ++++ compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- .../higher-ranked-lifetime-error.rs | 14 ++++++ .../higher-ranked-lifetime-error.stderr | 12 +++++ 6 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 tests/ui/higher-ranked/higher-ranked-lifetime-error.rs create mode 100644 tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index cfcf31fce32..03172ef34b9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -10,6 +10,8 @@ use rustc_infer::infer::RegionVariableOrigin; 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 @@ trait TypeOpInfo<'tcx> { 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> { - 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) { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 2ea399789b9..c89b276f7f3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -95,6 +95,12 @@ impl<'tcx> RegionErrors<'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`). diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 1049e7a8bbe..b2e86c4efa5 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -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 @@ impl<'tcx> RegionInferenceContext<'tcx> { &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 @@ impl<'tcx> RegionInferenceContext<'tcx> { 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 @@ impl<'tcx> RegionInferenceContext<'tcx> { 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 diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 60e6dcaf0b8..c17bd672a2f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1181,7 +1181,7 @@ impl<'a, 'tcx> TypeChecker<'a, '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); diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs new file mode 100644 index 00000000000..aee5db83669 --- /dev/null +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs @@ -0,0 +1,14 @@ +fn assert_all(_f: F) +where + F: FnMut(&String) -> T, +{ +} + +fn id(x: &String) -> &String { + x +} + +fn main() { + assert_all::<_, &String>(id); + //~^ mismatched types +} diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr new file mode 100644 index 00000000000..890dac16e94 --- /dev/null +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr @@ -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`.