From 09524bfd5ace53bfecddd669a5d90e5495eaa3de Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 3 Mar 2023 03:56:42 +0300 Subject: [PATCH] promote subject even if it has unnamed regions Don't require a region to have an `external_name` in order to be promoted. --- .../rustc_borrowck/src/region_infer/mod.rs | 35 +++++------- .../src/type_check/constraint_conversion.rs | 4 +- compiler/rustc_middle/src/mir/query.rs | 57 +++++++++++++++---- .../type-test-subject-opaque-1.rs | 4 +- .../type-test-subject-opaque-1.stderr | 8 --- .../type-test-subject-unnamed-region.rs | 4 +- .../type-test-subject-unnamed-region.stderr | 21 ------- 7 files changed, 66 insertions(+), 67 deletions(-) delete mode 100644 tests/ui/nll/closure-requirements/type-test-subject-opaque-1.stderr delete mode 100644 tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.stderr diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 941da2dd3b5..eadd9bd7dfe 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -12,8 +12,9 @@ use rustc_infer::infer::outlives::test_type_match; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::mir::{ - Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - ConstraintCategory, Local, Location, ReturnConstraint, TerminatorKind, + Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy, + ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint, + TerminatorKind, }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; @@ -1084,18 +1085,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { true } - /// When we promote a type test `T: 'r`, we have to convert the - /// type `T` into something we can store in a query result (so - /// something allocated for `'tcx`). This is problematic if `ty` - /// contains regions. During the course of NLL region checking, we - /// will have replaced all of those regions with fresh inference - /// variables. To create a test subject, we want to replace those - /// inference variables with some region from the closure - /// signature -- this is not always possible, so this is a - /// fallible process. Presuming we do find a suitable region, we - /// will use it's *external name*, which will be a `RegionKind` - /// variant that can be used in query responses such as - /// `ReEarlyBound`. + /// When we promote a type test `T: 'r`, we have to replace all region + /// variables in the type `T` with an equal universal region from the + /// closure signature. + /// This is not always possible, so this is a fallible process. #[instrument(level = "debug", skip(self, infcx))] fn try_promote_type_test_subject( &self, @@ -1144,22 +1137,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { // find an equivalent. let upper_bound = self.non_local_universal_upper_bound(region_vid); if self.region_contains(region_vid, upper_bound) { - self.definitions[upper_bound].external_name.unwrap_or(r) + tcx.mk_re_var(upper_bound) } else { - // In the case of a failure, use a `ReVar` result. This will - // cause the `needs_infer` later on to return `None`. - r + // In the case of a failure, use `ReErased`. We will eventually + // return `None` in this case. + tcx.lifetimes.re_erased } }); debug!("try_promote_type_test_subject: folded ty = {:?}", ty); - // `needs_infer` will only be true if we failed to promote some region. - if ty.needs_infer() { + // This will be true if we failed to promote some region. + if ty.has_erased_regions() { return None; } - Some(ClosureOutlivesSubject::Ty(ty)) + Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::new(tcx, ty))) } /// Given some universal or existential region `r`, finds a diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 504633c6a5c..a9356135006 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -116,7 +116,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { let outlived_region = closure_mapping[outlives_requirement.outlived_free_region]; let subject = match outlives_requirement.subject { ClosureOutlivesSubject::Region(re) => closure_mapping[re].into(), - ClosureOutlivesSubject::Ty(ty) => ty.into(), + ClosureOutlivesSubject::Ty(subject_ty) => { + subject_ty.instantiate(self.tcx, |vid| closure_mapping[vid]).into() + } }; self.category = outlives_requirement.category; diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index e2ab3fd35b3..87a2b9ec73e 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -8,7 +8,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::bit_set::BitMatrix; -use rustc_index::vec::IndexVec; +use rustc_index::vec::{Idx, IndexVec}; use rustc_span::Span; use rustc_target::abi::VariantIdx; use smallvec::SmallVec; @@ -289,13 +289,6 @@ pub struct ConstQualifs { /// instance of the closure is created, the corresponding free regions /// can be extracted from its type and constrained to have the given /// outlives relationship. -/// -/// In some cases, we have to record outlives requirements between types and -/// regions as well. In that case, if those types include any regions, those -/// regions are recorded using their external names (`ReStatic`, -/// `ReEarlyBound`, `ReFree`). We use these because in a query response we -/// cannot use `ReVar` (which is what we use internally within the rest of the -/// NLL code). #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct ClosureRegionRequirements<'tcx> { /// The number of external regions defined on the closure. In our @@ -392,16 +385,56 @@ pub enum ClosureOutlivesSubject<'tcx> { /// Subject is a type, typically a type parameter, but could also /// be a projection. Indicates a requirement like `T: 'a` being /// passed to the caller, where the type here is `T`. - /// - /// The type here is guaranteed not to contain any free regions at - /// present. - Ty(Ty<'tcx>), + Ty(ClosureOutlivesSubjectTy<'tcx>), /// Subject is a free region from the closure. Indicates a requirement /// like `'a: 'b` being passed to the caller; the region here is `'a`. Region(ty::RegionVid), } +/// Represents a `ty::Ty` for use in [`ClosureOutlivesSubject`]. +/// +/// This indirection is necessary because the type may include `ReVar` regions, +/// which is what we use internally within NLL code, +/// and we can't use `ReVar`s in a query response. +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct ClosureOutlivesSubjectTy<'tcx> { + inner: Ty<'tcx>, +} + +impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { + // All regions of `ty` must be of kind `ReVar` + // and must point to an early-bound region in the closure's signature. + pub fn new(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { + let inner = tcx.fold_regions(ty, |r, depth| match r.kind() { + ty::ReVar(vid) => { + let br = ty::BoundRegion { + var: ty::BoundVar::new(vid.index()), + kind: ty::BrAnon(0u32, None), + }; + tcx.mk_re_late_bound(depth, br) + } + _ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"), + }); + + Self { inner } + } + + pub fn instantiate( + self, + tcx: TyCtxt<'tcx>, + mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>, + ) -> Ty<'tcx> { + tcx.fold_regions(self.inner, |r, depth| match r.kind() { + ty::ReLateBound(debruijn, br) => { + debug_assert_eq!(debruijn, depth); + map(ty::RegionVid::new(br.var.index())) + } + _ => bug!("unexpected region {r:?}"), + }) + } +} + /// The constituent parts of a mir constant of kind ADT or array. #[derive(Copy, Clone, Debug, HashStable)] pub struct DestructuredConstant<'tcx> { diff --git a/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.rs b/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.rs index 4e015833669..fce6f2fee7f 100644 --- a/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.rs +++ b/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.rs @@ -1,5 +1,5 @@ -// check-fail -// known-bug: #107426 +// Regression test for #107426. +// check-pass use std::marker::PhantomData; #[derive(Clone, Copy)] diff --git a/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.stderr b/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.stderr deleted file mode 100644 index e54bc8406de..00000000000 --- a/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `make_fn::{opaque#0}<'_>` does not live long enough - --> $DIR/type-test-subject-opaque-1.rs:15:8 - | -LL | || event(cx, open_toggle); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.rs b/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.rs index 25ce0891a8a..b5a95c17009 100644 --- a/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.rs +++ b/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.rs @@ -1,5 +1,5 @@ -// check-fail -// known-bug: #108635 +// See #108635 for description. +// check-pass trait Trait { type Item<'a>: 'a; diff --git a/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.stderr b/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.stderr deleted file mode 100644 index 201329bb005..00000000000 --- a/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0310]: the associated type `::Item<'_>` may not live long enough - --> $DIR/type-test-subject-unnamed-region.rs:11:27 - | -LL | let closure = |a, _b| assert_static(a); - | ^^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `::Item<'_>: 'static`... - = note: ...so that the type `::Item<'_>` will meet its required lifetime bounds - -error[E0310]: the associated type `::Item<'_>` may not live long enough - --> $DIR/type-test-subject-unnamed-region.rs:20:9 - | -LL | assert_static(a); - | ^^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `::Item<'_>: 'static`... - = note: ...so that the type `::Item<'_>` will meet its required lifetime bounds - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0310`.