Skip to content

Commit e6b62a0

Browse files
committed
support higher-ranked regions in opaque type inference
1 parent fcbd6a3 commit e6b62a0

File tree

8 files changed

+257
-12
lines changed

8 files changed

+257
-12
lines changed

compiler/rustc_borrowck/src/region_infer/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,25 @@ pub(crate) struct RegionDefinition<'tcx> {
167167

168168
/// If this is 'static or an early-bound region, then this is
169169
/// `Some(X)` where `X` is the name of the region.
170+
///
171+
/// Use the method `Self::external_name` for more flexibility.
170172
pub(crate) external_name: Option<ty::Region<'tcx>>,
171173
}
172174

175+
impl<'tcx> RegionDefinition<'tcx> {
176+
/// Gets the name of a universal region (including placeholders).
177+
/// Returns `None` if this is an existential variable.
178+
pub fn external_name(&self, tcx: TyCtxt<'tcx>) -> Option<ty::Region<'tcx>> {
179+
match self.origin {
180+
NllRegionVariableOrigin::FreeRegion => self.external_name,
181+
NllRegionVariableOrigin::Placeholder(placeholder) => {
182+
Some(tcx.mk_region(ty::RePlaceholder(placeholder)))
183+
}
184+
NllRegionVariableOrigin::Existential { .. } => None,
185+
}
186+
}
187+
}
188+
173189
/// N.B., the variants in `Cause` are intentionally ordered. Lower
174190
/// values are preferred when it comes to error messages. Do not
175191
/// reorder willy nilly.

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

+42-12
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
7272

7373
let mut subst_regions = vec![self.universal_regions.fr_static];
7474
let universal_substs = infcx.tcx.fold_regions(substs, |region, _| {
75-
if let ty::RePlaceholder(..) = region.kind() {
76-
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the substs.
77-
return region;
75+
let (vid, scc) = match region.kind() {
76+
ty::ReVar(vid) => (vid, self.constraint_sccs.scc(vid)),
77+
_ => bug!("expected nll var"),
78+
};
79+
trace!(?vid, ?scc);
80+
81+
// Special handling of higher-ranked regions. These appear in the substs of the
82+
// inner opaque type `impl Sized` in:
83+
// `fn test() -> impl for<'a> Trait<'a, Ty = impl Sized + 'a>`
84+
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
85+
subst_regions.push(vid);
86+
return self.definitions[vid]
87+
.external_name(infcx.tcx)
88+
.expect("higher-ranked existential region found in opaque type substs");
7889
}
79-
let vid = self.to_region_vid(region);
80-
trace!(?vid);
81-
let scc = self.constraint_sccs.scc(vid);
82-
trace!(?scc);
90+
8391
match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| {
8492
self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?)
8593
}) {
@@ -107,9 +115,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
107115
ty::ReVar(vid) => subst_regions
108116
.iter()
109117
.find(|ur_vid| self.eval_equal(vid, **ur_vid))
110-
.and_then(|ur_vid| self.definitions[*ur_vid].external_name)
118+
.and_then(|ur_vid| self.definitions[*ur_vid].external_name(infcx.tcx))
111119
.unwrap_or(infcx.tcx.lifetimes.re_root_empty),
112-
_ => region,
120+
_ => bug!("expected nll var"),
113121
});
114122

115123
debug!(?universal_concrete_type, ?universal_substs);
@@ -160,6 +168,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
160168
{
161169
tcx.fold_regions(ty, |region, _| match *region {
162170
ty::ReVar(vid) => {
171+
let scc = self.constraint_sccs.scc(vid);
172+
173+
// Special handling of higher-ranked regions.
174+
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
175+
// If the region contains a single placeholder then they're equal.
176+
if let Some((0, placeholder)) =
177+
self.scc_values.placeholders_contained_in(scc).enumerate().last()
178+
{
179+
// HACK: we convert named placeholders to free regions for better errors.
180+
// Otherwise, this is incorrect.
181+
if let bound_region @ ty::BrNamed(scope, _) = placeholder.name {
182+
return tcx
183+
.mk_region(ty::ReFree(ty::FreeRegion { scope, bound_region }));
184+
}
185+
}
186+
// Fallback: this will produce a cryptic error message.
187+
return region;
188+
}
189+
163190
// Find something that we can name
164191
let upper_bound = self.approx_universal_upper_bound(vid);
165192
let upper_bound = &self.definitions[upper_bound];
@@ -385,7 +412,7 @@ fn check_opaque_type_parameter_valid(
385412
return false;
386413
}
387414
GenericArgKind::Lifetime(lt) => {
388-
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
415+
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_) | ty::RePlaceholder(_))
389416
}
390417
GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
391418
};
@@ -495,9 +522,12 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
495522
ty::ReErased => return r,
496523

497524
// The regions that we expect from borrow checking.
498-
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {}
525+
ty::ReEarlyBound(_)
526+
| ty::ReFree(_)
527+
| ty::RePlaceholder(_)
528+
| ty::ReEmpty(ty::UniverseIndex::ROOT) => {}
499529

500-
ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => {
530+
ty::ReEmpty(_) | ty::ReVar(_) => {
501531
// All of the regions in the type should either have been
502532
// erased by writeback, or mapped back to named regions by
503533
// borrow checking.

compiler/rustc_borrowck/src/type_check/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -2682,6 +2682,21 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
26822682
hidden_type.ty = self.infcx.tcx.ty_error();
26832683
}
26842684

2685+
// Convert all regions to nll vars.
2686+
let (opaque_type_key, hidden_type) =
2687+
self.infcx.tcx.fold_regions((opaque_type_key, hidden_type), |region, _| {
2688+
match region.kind() {
2689+
ty::ReVar(_) => region,
2690+
ty::RePlaceholder(placeholder) => self
2691+
.borrowck_context
2692+
.constraints
2693+
.placeholder_region(self.infcx, placeholder),
2694+
_ => self.infcx.tcx.mk_region(ty::ReVar(
2695+
self.borrowck_context.universal_regions.to_region_vid(region),
2696+
)),
2697+
}
2698+
});
2699+
26852700
(opaque_type_key, OpaqueTypeDecl { hidden_type, ..decl })
26862701
})
26872702
.collect()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Regression test for #97099.
2+
// This was an ICE because `impl Sized` captures the lifetime 'a.
3+
4+
// check-fail
5+
6+
trait Trait<E> {
7+
type Assoc;
8+
}
9+
10+
struct Foo;
11+
12+
impl<'a> Trait<&'a ()> for Foo {
13+
type Assoc = ();
14+
}
15+
16+
fn foo() -> impl for<'a> Trait<&'a ()> {
17+
Foo
18+
}
19+
20+
fn bar() -> impl for<'a> Trait<&'a (), Assoc = impl Sized> {
21+
foo()
22+
//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds
23+
}
24+
25+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
2+
--> $DIR/higher-ranked-regions-diag.rs:21:5
3+
|
4+
LL | fn bar() -> impl for<'a> Trait<&'a (), Assoc = impl Sized> {
5+
| -- hidden type `<impl for<'a> Trait<&'a ()> as Trait<&'a ()>>::Assoc` captures the lifetime `'a` as defined here
6+
LL | foo()
7+
| ^^^^^
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0700`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Basic tests for opaque type inference under for<_> binders.
2+
3+
// check-fail
4+
5+
#![feature(type_alias_impl_trait)]
6+
7+
trait Trait<'a> {
8+
type Ty;
9+
}
10+
impl<'a, T> Trait<'a> for T {
11+
type Ty = &'a ();
12+
}
13+
14+
mod basic_pass {
15+
use super::*;
16+
type Opq<'a> = impl Sized + 'a;
17+
fn test() -> impl for<'a> Trait<'a, Ty = Opq<'a>> {}
18+
}
19+
20+
mod capture_rpit {
21+
use super::*;
22+
fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {}
23+
//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds
24+
}
25+
26+
mod capture_tait {
27+
use super::*;
28+
type Opq0 = impl Sized;
29+
type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>;
30+
type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
31+
fn test() -> Opq2 {}
32+
//~^ ERROR hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds
33+
}
34+
35+
mod capture_tait_complex_pass {
36+
use super::*;
37+
type Opq0<'a> = impl Sized;
38+
type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b
39+
type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
40+
fn test() -> Opq2 {}
41+
}
42+
43+
// Same as the above, but make sure that different placeholder regions are not equal.
44+
mod capture_tait_complex_fail {
45+
use super::*;
46+
type Opq0<'a> = impl Sized;
47+
type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a
48+
type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
49+
fn test() -> Opq2 {}
50+
//~^ ERROR hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds
51+
}
52+
53+
// non-defining use because 'static is used.
54+
mod constrain_fail0 {
55+
use super::*;
56+
type Opq0<'a, 'b> = impl Sized;
57+
fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {}
58+
//~^ ERROR non-defining opaque type use in defining scope
59+
}
60+
61+
// non-defining use because generic lifetime is used multiple times.
62+
mod constrain_fail {
63+
use super::*;
64+
type Opq0<'a, 'b> = impl Sized;
65+
fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {}
66+
//~^ ERROR non-defining opaque type use in defining scope
67+
}
68+
69+
mod constrain_pass {
70+
use super::*;
71+
type Opq0<'a, 'b> = impl Sized;
72+
type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>;
73+
type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
74+
fn test() -> Opq2 {}
75+
}
76+
77+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
2+
--> $DIR/higher-ranked-regions-basic.rs:22:58
3+
|
4+
LL | fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {}
5+
| -- ^^
6+
| |
7+
| hidden type `&'a ()` captures the lifetime `'a` as defined here
8+
9+
error[E0700]: hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds
10+
--> $DIR/higher-ranked-regions-basic.rs:31:23
11+
|
12+
LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>;
13+
| -- hidden type `&'b ()` captures the lifetime `'b` as defined here
14+
LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
15+
LL | fn test() -> Opq2 {}
16+
| ^^
17+
18+
error[E0700]: hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds
19+
--> $DIR/higher-ranked-regions-basic.rs:49:23
20+
|
21+
LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a
22+
| -- hidden type `&'b ()` captures the lifetime `'b` as defined here
23+
LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
24+
LL | fn test() -> Opq2 {}
25+
| ^^
26+
27+
error: non-defining opaque type use in defining scope
28+
--> $DIR/higher-ranked-regions-basic.rs:57:65
29+
|
30+
LL | type Opq0<'a, 'b> = impl Sized;
31+
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
32+
LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {}
33+
| ^^
34+
35+
error: non-defining opaque type use in defining scope
36+
--> $DIR/higher-ranked-regions-basic.rs:65:60
37+
|
38+
LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {}
39+
| ^^
40+
|
41+
note: lifetime used multiple times
42+
--> $DIR/higher-ranked-regions-basic.rs:64:15
43+
|
44+
LL | type Opq0<'a, 'b> = impl Sized;
45+
| ^^ ^^
46+
47+
error: aborting due to 5 previous errors
48+
49+
For more information about this error, try `rustc --explain E0700`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Regression test for #97098.
2+
3+
// check-pass
4+
5+
#![feature(generic_associated_types)]
6+
#![feature(type_alias_impl_trait)]
7+
8+
pub trait Trait {
9+
type Assoc<'a>;
10+
}
11+
12+
pub type Foo = impl for<'a> Trait<Assoc<'a> = FooAssoc<'a>>;
13+
pub type FooAssoc<'a> = impl Sized;
14+
15+
struct Struct;
16+
impl Trait for Struct {
17+
type Assoc<'a> = &'a u32;
18+
}
19+
20+
const FOO: Foo = Struct;
21+
22+
fn main() {}

0 commit comments

Comments
 (0)