From 20b20b23eac9809172eeaab71a3952ec53d37ce6 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 3 Mar 2023 06:46:23 +0300 Subject: [PATCH 1/6] add known-bug tests --- .../type-test-subject-non-trivial-region.rs | 19 +++++++++++++++ ...ype-test-subject-non-trivial-region.stderr | 12 ++++++++++ .../type-test-subject-opaque-1.rs | 18 ++++++++++++++ .../type-test-subject-opaque-1.stderr | 8 +++++++ .../type-test-subject-opaque-2.rs | 17 +++++++++++++ .../type-test-subject-opaque-2.stderr | 18 ++++++++++++++ .../type-test-subject-unnamed-region.rs | 24 +++++++++++++++++++ .../type-test-subject-unnamed-region.stderr | 21 ++++++++++++++++ 8 files changed, 137 insertions(+) create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-opaque-1.rs create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-opaque-1.stderr create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-opaque-2.rs create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.rs create mode 100644 tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.stderr diff --git a/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs b/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs new file mode 100644 index 0000000000000..623e5e1a71fdc --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs @@ -0,0 +1,19 @@ +// chek-fail +// known-bug: #108639 + +trait Trait { + type Item<'a>: 'a; +} + +fn assert_static(_: T) {} +fn relate(_: T, _: T) {} + +fn test_args() { + let closure = |a, b| { + relate(&a, b); + assert_static(a); + }; + closure(None::>, &None::>); +} + +fn main() {} diff --git a/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr b/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr new file mode 100644 index 0000000000000..83f0b64d5a737 --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr @@ -0,0 +1,12 @@ +error[E0310]: the associated type `::Item<'_>` may not live long enough + --> $DIR/type-test-subject-non-trivial-region.rs:14: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 previous error + +For more information about this error, try `rustc --explain E0310`. 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 new file mode 100644 index 0000000000000..4e01583366952 --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.rs @@ -0,0 +1,18 @@ +// check-fail +// known-bug: #107426 + +use std::marker::PhantomData; +#[derive(Clone, Copy)] +pub struct Scope<'a>(&'a PhantomData<&'a mut &'a ()>); +fn event<'a, F: FnMut() + 'a>(_: Scope<'a>, _: F) {} +fn make_fn<'a>(_: Scope<'a>) -> impl Fn() + Copy + 'a { + || {} +} + +fn foo(cx: Scope) { + let open_toggle = make_fn(cx); + + || event(cx, open_toggle); +} + +fn main() {} 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 new file mode 100644 index 0000000000000..e54bc8406de48 --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-opaque-1.stderr @@ -0,0 +1,8 @@ +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-opaque-2.rs b/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.rs new file mode 100644 index 0000000000000..fc1923bb76f1f --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.rs @@ -0,0 +1,17 @@ +// check-fail +// known-bug: #107516 + +fn iter1<'a: 'a>() -> impl Iterator { + None.into_iter() +} + +fn iter2<'a>() -> impl Iterator { + None.into_iter() +} + +struct Bivar<'a, I: Iterator + 'a>(I); + +fn main() { + let _ = || Bivar(iter1()); + let _ = || Bivar(iter2()); +} diff --git a/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr b/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr new file mode 100644 index 0000000000000..f6266dd4eb5dc --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr @@ -0,0 +1,18 @@ +error[E0310]: the opaque type `iter1<'_>::{opaque#0}` may not live long enough + --> $DIR/type-test-subject-opaque-2.rs:15:16 + | +LL | let _ = || Bivar(iter1()); + | ^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `iter1<'_>::{opaque#0}: 'static`... + = note: ...so that the type `impl Iterator` will meet its required lifetime bounds + +error: `iter2<'_>::{opaque#0}<'_>` does not live long enough + --> $DIR/type-test-subject-opaque-2.rs:16:16 + | +LL | let _ = || Bivar(iter2()); + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. 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 new file mode 100644 index 0000000000000..25ce0891a8a66 --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.rs @@ -0,0 +1,24 @@ +// check-fail +// known-bug: #108635 + +trait Trait { + type Item<'a>: 'a; +} + +fn assert_static(_: T) {} + +fn test_args() { + let closure = |a, _b| assert_static(a); + + closure(None::>, &None::>); +} + +fn test_upvars() { + let upvars = (None::>, &None::>); + let _closure = || { + let (a, _b) = upvars; + assert_static(a); + }; +} + +fn main() {} 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 new file mode 100644 index 0000000000000..201329bb005ca --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-subject-unnamed-region.stderr @@ -0,0 +1,21 @@ +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`. 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 2/6] 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 941da2dd3b5fc..eadd9bd7dfe2f 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 504633c6a5c39..a93561350062c 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 e2ab3fd35b331..87a2b9ec73e41 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 4e01583366952..fce6f2fee7fda 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 e54bc8406de48..0000000000000 --- 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 25ce0891a8a66..b5a95c170099d 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 201329bb005ca..0000000000000 --- 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`. From 9d74bff829174c4a707ee2ab17ac6e01490d9a6a Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 3 Mar 2023 06:25:52 +0300 Subject: [PATCH 3/6] smarter algorithm for finding an equal region Smarter and simpler! --- .../rustc_borrowck/src/region_infer/mod.rs | 76 +++---------------- .../src/type_check/free_region_relations.rs | 25 ------ .../type-test-subject-non-trivial-region.rs | 4 +- ...ype-test-subject-non-trivial-region.stderr | 12 --- 4 files changed, 11 insertions(+), 106 deletions(-) delete mode 100644 tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index eadd9bd7dfe2f..e526ccd71ca9e 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1098,51 +1098,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { let tcx = infcx.tcx; let ty = tcx.fold_regions(ty, |r, _depth| { - let region_vid = self.to_region_vid(r); + let r_vid = self.to_region_vid(r); + let r_scc = self.constraint_sccs.scc(r_vid); // The challenge if this. We have some region variable `r` // whose value is a set of CFG points and universal // regions. We want to find if that set is *equivalent* to // any of the named regions found in the closure. - // - // To do so, we compute the - // `non_local_universal_upper_bound`. This will be a - // non-local, universal region that is greater than `r`. - // However, it might not be *contained* within `r`, so - // then we further check whether this bound is contained - // in `r`. If so, we can say that `r` is equivalent to the - // bound. - // - // Let's work through a few examples. For these, imagine - // that we have 3 non-local regions (I'll denote them as - // `'static`, `'a`, and `'b`, though of course in the code - // they would be represented with indices) where: - // - // - `'static: 'a` - // - `'static: 'b` - // - // First, let's assume that `r` is some existential - // variable with an inferred value `{'a, 'static}` (plus - // some CFG nodes). In this case, the non-local upper - // bound is `'static`, since that outlives `'a`. `'static` - // is also a member of `r` and hence we consider `r` - // equivalent to `'static` (and replace it with - // `'static`). - // - // Now let's consider the inferred value `{'a, 'b}`. This - // means `r` is effectively `'a | 'b`. I'm not sure if - // this can come about, actually, but assuming it did, we - // would get a non-local upper bound of `'static`. Since - // `'static` is not contained in `r`, we would fail to - // find an equivalent. - let upper_bound = self.non_local_universal_upper_bound(region_vid); - if self.region_contains(region_vid, upper_bound) { - tcx.mk_re_var(upper_bound) - } else { + // To do so, we simply check every candidate `u_r` for equality. + self.scc_values + .universal_regions_outlived_by(r_scc) + .filter(|&u_r| !self.universal_regions.is_local_free_region(u_r)) + .find(|&u_r| self.eval_equal(u_r, r_vid)) + .map(|u_r| tcx.mk_re_var(u_r)) // In the case of a failure, use `ReErased`. We will eventually // return `None` in this case. - tcx.lifetimes.re_erased - } + .unwrap_or(tcx.lifetimes.re_erased) }); debug!("try_promote_type_test_subject: folded ty = {:?}", ty); @@ -1155,35 +1126,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::new(tcx, ty))) } - /// Given some universal or existential region `r`, finds a - /// non-local, universal region `r+` that outlives `r` at entry to (and - /// exit from) the closure. In the worst case, this will be - /// `'static`. - /// - /// This is used for two purposes. First, if we are propagated - /// some requirement `T: r`, we can use this method to enlarge `r` - /// to something we can encode for our creator (which only knows - /// about non-local, universal regions). It is also used when - /// encoding `T` as part of `try_promote_type_test_subject` (see - /// that fn for details). - /// - /// This is based on the result `'y` of `universal_upper_bound`, - /// except that it converts further takes the non-local upper - /// bound of `'y`, so that the final result is non-local. - fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!("non_local_universal_upper_bound(r={:?}={})", r, self.region_value_str(r)); - - let lub = self.universal_upper_bound(r); - - // Grow further to get smallest universal region known to - // creator. - let non_local_lub = self.universal_region_relations.non_local_upper_bound(lub); - - debug!("non_local_universal_upper_bound: non_local_lub={:?}", non_local_lub); - - non_local_lub - } - /// Returns a universally quantified region that outlives the /// value of `r` (`r` may be existentially or universally /// quantified). diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index e2f897a89e82e..4004966c40a77 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -93,31 +93,6 @@ impl UniversalRegionRelations<'_> { res } - /// Returns the "postdominating" bound of the set of - /// `non_local_upper_bounds` for the given region. - pub(crate) fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid { - let upper_bounds = self.non_local_upper_bounds(fr); - - // In case we find more than one, reduce to one for - // convenience. This is to prevent us from generating more - // complex constraints, but it will cause spurious errors. - let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds); - - debug!("non_local_bound: post_dom={:?}", post_dom); - - post_dom - .and_then(|post_dom| { - // If the mutual immediate postdom is not local, then - // there is no non-local result we can return. - if !self.universal_regions.is_local_free_region(post_dom) { - Some(post_dom) - } else { - None - } - }) - .unwrap_or(self.universal_regions.fr_static) - } - /// Finds a "lower bound" for `fr` that is not local. In other /// words, returns the largest (*) known region `fr1` that (a) is /// outlived by `fr` and (b) is not local. diff --git a/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs b/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs index 623e5e1a71fdc..d8772e86894df 100644 --- a/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs +++ b/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.rs @@ -1,5 +1,5 @@ -// chek-fail -// known-bug: #108639 +// See #108639 for description. +// check-pass trait Trait { type Item<'a>: 'a; diff --git a/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr b/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr deleted file mode 100644 index 83f0b64d5a737..0000000000000 --- a/tests/ui/nll/closure-requirements/type-test-subject-non-trivial-region.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0310]: the associated type `::Item<'_>` may not live long enough - --> $DIR/type-test-subject-non-trivial-region.rs:14: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 previous error - -For more information about this error, try `rustc --explain E0310`. From 10da7710cdd5d76261bcfcf31dd163c966b3ebb3 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 3 Mar 2023 06:26:01 +0300 Subject: [PATCH 4/6] ignore bivariant regions in opaque types --- .../rustc_borrowck/src/region_infer/mod.rs | 30 +++++++++++++++++++ .../type-test-subject-opaque-2.rs | 4 +-- .../type-test-subject-opaque-2.stderr | 18 ----------- 3 files changed, 32 insertions(+), 20 deletions(-) delete mode 100644 tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index e526ccd71ca9e..3137e71781ca4 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1097,6 +1097,36 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> Option> { let tcx = infcx.tcx; + // Opaque types' substs may include useless lifetimes. + // We will replace them with ReStatic. + struct OpaqueFolder<'tcx> { + tcx: TyCtxt<'tcx>, + } + impl<'tcx> ty::TypeFolder> for OpaqueFolder<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + use ty::TypeSuperFoldable as _; + let tcx = self.tcx; + let &ty::Alias(ty::Opaque, ty::AliasTy { substs, def_id, .. }) = t.kind() else { + return t.super_fold_with(self); + }; + let substs = + std::iter::zip(substs, tcx.variances_of(def_id)).map(|(arg, v)| { + match (arg.unpack(), v) { + (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => { + tcx.lifetimes.re_static.into() + } + _ => arg.fold_with(self), + } + }); + tcx.mk_opaque(def_id, tcx.mk_substs_from_iter(substs)) + } + } + + let ty = ty.fold_with(&mut OpaqueFolder { tcx }); + let ty = tcx.fold_regions(ty, |r, _depth| { let r_vid = self.to_region_vid(r); let r_scc = self.constraint_sccs.scc(r_vid); diff --git a/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.rs b/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.rs index fc1923bb76f1f..55905850f0c97 100644 --- a/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.rs +++ b/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.rs @@ -1,5 +1,5 @@ -// check-fail -// known-bug: #107516 +// Resgression test for #107516. +// check-pass fn iter1<'a: 'a>() -> impl Iterator { None.into_iter() diff --git a/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr b/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr deleted file mode 100644 index f6266dd4eb5dc..0000000000000 --- a/tests/ui/nll/closure-requirements/type-test-subject-opaque-2.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0310]: the opaque type `iter1<'_>::{opaque#0}` may not live long enough - --> $DIR/type-test-subject-opaque-2.rs:15:16 - | -LL | let _ = || Bivar(iter1()); - | ^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `iter1<'_>::{opaque#0}: 'static`... - = note: ...so that the type `impl Iterator` will meet its required lifetime bounds - -error: `iter2<'_>::{opaque#0}<'_>` does not live long enough - --> $DIR/type-test-subject-opaque-2.rs:16:16 - | -LL | let _ = || Bivar(iter2()); - | ^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0310`. From 97381d2f1ef6481e201eb8fb8bbc1c5ddeffb61d Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sat, 4 Mar 2023 11:19:56 +0300 Subject: [PATCH 5/6] tweak ClosureOutlivesSubjectTy --- compiler/rustc_borrowck/src/region_infer/mod.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 3137e71781ca4..74ea2451348c4 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1153,7 +1153,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { return None; } - Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::new(tcx, ty))) + Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::bind(tcx, ty))) } /// Returns a universally quantified region that outlives the diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 87a2b9ec73e41..b964c1852d251 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -394,23 +394,26 @@ pub enum ClosureOutlivesSubject<'tcx> { /// 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. +/// This abstraction is necessary because the type may include `ReVar` regions, +/// which is what we use internally within NLL code, and they can't be used in +/// a query response. +/// +/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this +/// type is not recognized as a binder for late-bound region. #[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 { + /// All regions of `ty` must be of kind `ReVar` and must represent + /// universal regions *external* to the closure. + pub fn bind(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), + kind: ty::BrAnon(vid.as_u32(), None), }; tcx.mk_re_late_bound(depth, br) } From 427dc18c25009adb75919df918f23556205ec859 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sat, 4 Mar 2023 12:10:42 +0300 Subject: [PATCH 6/6] tweak debug output and bless tests --- compiler/rustc_borrowck/src/nll.rs | 32 +++++++++++-------- .../projection-one-region-closure.stderr | 2 +- ...tion-one-region-trait-bound-closure.stderr | 2 +- ...tion-two-region-trait-bound-closure.stderr | 28 ++++++++-------- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index e5dbb83dd0728..96228338a4c22 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -10,10 +10,9 @@ use rustc_middle::mir::{ BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted, }; -use rustc_middle::ty::{self, OpaqueHiddenType, Region, RegionVid}; +use rustc_middle::ty::{self, OpaqueHiddenType, Region, RegionVid, TyCtxt}; use rustc_span::symbol::sym; use std::env; -use std::fmt::Debug; use std::io; use std::path::PathBuf; use std::rc::Rc; @@ -325,7 +324,7 @@ pub(super) fn dump_mir_results<'tcx>( infcx: &BorrowckInferCtxt<'_, 'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, - closure_region_requirements: &Option>, + closure_region_requirements: &Option>, ) { if !dump_enabled(infcx.tcx, "nll", body.source.def_id()) { return; @@ -340,9 +339,11 @@ pub(super) fn dump_mir_results<'tcx>( if let Some(closure_region_requirements) = closure_region_requirements { writeln!(out, "| Free Region Constraints")?; - for_each_region_constraint(closure_region_requirements, &mut |msg| { - writeln!(out, "| {}", msg) - })?; + for_each_region_constraint( + infcx.tcx, + closure_region_requirements, + &mut |msg| writeln!(out, "| {}", msg), + )?; writeln!(out, "|")?; } } @@ -375,7 +376,7 @@ pub(super) fn dump_annotation<'tcx>( infcx: &BorrowckInferCtxt<'_, 'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, - closure_region_requirements: &Option>, + closure_region_requirements: &Option>, opaque_type_values: &VecMap>, errors: &mut crate::error::BorrowckErrors<'tcx>, ) { @@ -405,7 +406,7 @@ pub(super) fn dump_annotation<'tcx>( // Dump the region constraints we are imposing *between* those // newly created variables. - for_each_region_constraint(closure_region_requirements, &mut |msg| { + for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| { err.note(msg); Ok(()) }) @@ -426,16 +427,19 @@ pub(super) fn dump_annotation<'tcx>( errors.buffer_non_error_diag(err); } -fn for_each_region_constraint( - closure_region_requirements: &ClosureRegionRequirements<'_>, +fn for_each_region_constraint<'tcx>( + tcx: TyCtxt<'tcx>, + closure_region_requirements: &ClosureRegionRequirements<'tcx>, with_msg: &mut dyn FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for req in &closure_region_requirements.outlives_requirements { - let subject: &dyn Debug = match &req.subject { - ClosureOutlivesSubject::Region(subject) => subject, - ClosureOutlivesSubject::Ty(ty) => ty, + let subject = match req.subject { + ClosureOutlivesSubject::Region(subject) => format!("{:?}", subject), + ClosureOutlivesSubject::Ty(ty) => { + format!("{:?}", ty.instantiate(tcx, |vid| tcx.mk_re_var(vid))) + } }; - with_msg(&format!("where {:?}: {:?}", subject, req.outlived_free_region,))?; + with_msg(&format!("where {}: {:?}", subject, req.outlived_free_region,))?; } Ok(()) } diff --git a/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr b/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr index dbda04c42c5c7..11ada59c06639 100644 --- a/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -110,7 +110,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 4 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-one-region-closure.rs:62:1 diff --git a/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index 250c796e2c71e..47d4f2e46c62b 100644 --- a/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -86,7 +86,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 4 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-one-region-trait-bound-closure.rs:52:1 diff --git a/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index 90f04914286c1..530dd86819dc3 100644 --- a/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -11,7 +11,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); ] = note: late-bound region is '_#4r = note: number of external vids: 5 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:34:1 @@ -23,14 +23,14 @@ LL | | T: Anything<'b, 'c>, | = note: defining type: no_relationships_late::<'_#1r, '_#2r, T> -error[E0309]: the associated type `>::AssocType` may not live long enough +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:38:39 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... - = note: ...so that the type `>::AssocType` will meet its required lifetime bounds + = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... + = note: ...so that the type `>::AssocType` will meet its required lifetime bounds note: external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:48:29 @@ -44,7 +44,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:43:1 @@ -57,14 +57,14 @@ LL | | 'a: 'a, | = note: defining type: no_relationships_early::<'_#1r, '_#2r, '_#3r, T> -error[E0309]: the associated type `>::AssocType` may not live long enough +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:48:39 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... - = note: ...so that the type `>::AssocType` will meet its required lifetime bounds + = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... + = note: ...so that the type `>::AssocType` will meet its required lifetime bounds note: external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:61:29 @@ -78,7 +78,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:53:1 @@ -103,7 +103,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:65:1 @@ -128,7 +128,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:74:1 @@ -154,7 +154,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); ] = note: late-bound region is '_#3r = note: number of external vids: 4 - = note: where >::AssocType: '_#2r + = note: where >::AssocType: '_#2r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:83:1 @@ -194,7 +194,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 4 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:92:1 @@ -219,7 +219,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); (), ] = note: number of external vids: 3 - = note: where >::AssocType: '_#2r + = note: where >::AssocType: '_#2r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:101:1