Skip to content

Commit f4940e4

Browse files
committed
rework opaque types region inference
1 parent 08c8caa commit f4940e4

8 files changed

+250
-92
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

Lines changed: 65 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
1+
use rustc_data_structures::fx::FxIndexMap;
22
use rustc_errors::ErrorGuaranteed;
33
use rustc_hir::def::DefKind;
44
use rustc_hir::def_id::LocalDefId;
55
use rustc_hir::OpaqueTyOrigin;
6-
use rustc_infer::infer::InferCtxt;
76
use rustc_infer::infer::TyCtxtInferExt as _;
7+
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
88
use rustc_infer::traits::{Obligation, ObligationCause};
99
use rustc_macros::extension;
1010
use rustc_middle::traits::DefiningAnchor;
@@ -95,39 +95,43 @@ impl<'tcx> RegionInferenceContext<'tcx> {
9595
/// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
9696
/// This is lowered to give HIR something like
9797
///
98-
/// type f<'a>::_Return<'_a> = impl Sized + '_a;
99-
/// fn f<'a>(x: &'a i32) -> f<'static>::_Return<'a> { x }
98+
/// type f<'a>::_Return<'_x> = impl Sized + '_x;
99+
/// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
100100
///
101101
/// When checking the return type record the type from the return and the
102102
/// type used in the return value. In this case they might be `_Return<'1>`
103103
/// and `&'2 i32` respectively.
104104
///
105105
/// Once we to this method, we have completed region inference and want to
106106
/// call `infer_opaque_definition_from_instantiation` to get the inferred
107-
/// type of `_Return<'_a>`. `infer_opaque_definition_from_instantiation`
107+
/// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
108108
/// compares lifetimes directly, so we need to map the inference variables
109109
/// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
110110
///
111-
/// First we map all the lifetimes in the concrete type to an equal
112-
/// universal region that occurs in the concrete type's args, in this case
113-
/// this would result in `&'1 i32`. We only consider regions in the args
111+
/// First we map the regions in the the generic parameters `_Return<'1>` to
112+
/// their `external_name` giving `_Return<'a>`. This step is a bit involved.
113+
/// See the [rustc-dev-guide chapter] for more info.
114+
///
115+
/// Then we map all the lifetimes in the concrete type to an equal
116+
/// universal region that occurs in the opaque type's args, in this case
117+
/// this would result in `&'a i32`. We only consider regions in the args
114118
/// in case there is an equal region that does not. For example, this should
115119
/// be allowed:
116120
/// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
117121
///
118-
/// Then we map the regions in both the type and the generic parameters to their
119-
/// `external_name` giving `concrete_type = &'a i32`,
120-
/// `args = ['static, 'a]`. This will then allow
121-
/// `infer_opaque_definition_from_instantiation` to determine that
122-
/// `_Return<'_a> = &'_a i32`.
122+
/// This will then allow `infer_opaque_definition_from_instantiation` to
123+
/// determine that `_Return<'_x> = &'_x i32`.
123124
///
124125
/// There's a slight complication around closures. Given
125126
/// `fn f<'a: 'a>() { || {} }` the closure's type is something like
126127
/// `f::<'a>::{{closure}}`. The region parameter from f is essentially
127128
/// ignored by type checking so ends up being inferred to an empty region.
128129
/// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
129-
/// which has no `external_name` in which case we use `'empty` as the
130+
/// which has no `external_name` in which case we use `'{erased}` as the
130131
/// region to pass to `infer_opaque_definition_from_instantiation`.
132+
///
133+
/// [rustc-dev-guide chapter]:
134+
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
131135
#[instrument(level = "debug", skip(self, infcx), ret)]
132136
pub(crate) fn infer_opaque_types(
133137
&self,
@@ -138,85 +142,59 @@ impl<'tcx> RegionInferenceContext<'tcx> {
138142

139143
let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
140144

141-
let member_constraints: FxIndexMap<_, _> = self
142-
.member_constraints
143-
.all_indices()
144-
.map(|ci| (self.member_constraints[ci].key, ci))
145-
.collect();
146-
debug!(?member_constraints);
147-
148145
for (opaque_type_key, concrete_type) in opaque_ty_decls {
149-
let args = opaque_type_key.args;
150-
debug!(?concrete_type, ?args);
146+
debug!(?opaque_type_key, ?concrete_type);
151147

152-
let mut arg_regions = vec![self.universal_regions.fr_static];
148+
let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
149+
vec![(self.universal_regions.fr_static, infcx.tcx.lifetimes.re_static)];
153150

154-
let to_universal_region = |vid, arg_regions: &mut Vec<_>| match self.universal_name(vid)
155-
{
156-
Some(region) => {
157-
let vid = self.universal_regions.to_region_vid(region);
158-
arg_regions.push(vid);
159-
region
160-
}
161-
None => {
162-
arg_regions.push(vid);
163-
ty::Region::new_error_with_message(
164-
infcx.tcx,
165-
concrete_type.span,
166-
"opaque type with non-universal region args",
167-
)
168-
}
169-
};
151+
let opaque_type_key =
152+
opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
153+
// Use the SCC representative instead of directly using `region`.
154+
// See [rustc-dev-guide chapter] § "Strict lifetime equality".
155+
let scc = self.constraint_sccs.scc(region.as_var());
156+
let vid = self.scc_representatives[scc];
157+
let named = match self.definitions[vid].origin {
158+
// Iterate over all universal regions in a consistent order and find the
159+
// *first* equal region. This makes sure that equal lifetimes will have
160+
// the same name and simplifies subsequent handling.
161+
// See [rustc-dev-guide chapter] § "Semantic lifetime equality".
162+
NllRegionVariableOrigin::FreeRegion => self
163+
.universal_regions
164+
.universal_regions()
165+
.filter(|&ur| self.universal_region_relations.equal(vid, ur))
166+
// FIXME(aliemjay): universal regions with no `external_name`
167+
// are extenal closure regions, which should be rejected eventually.
168+
.find_map(|ur| self.definitions[ur].external_name),
169+
NllRegionVariableOrigin::Placeholder(placeholder) => {
170+
Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
171+
}
172+
NllRegionVariableOrigin::Existential { .. } => None,
173+
}
174+
.unwrap_or_else(|| {
175+
ty::Region::new_error_with_message(
176+
infcx.tcx,
177+
concrete_type.span,
178+
"opaque type with non-universal region args",
179+
)
180+
});
170181

171-
// Start by inserting universal regions from the member_constraint choice regions.
172-
// This will ensure they get precedence when folding the regions in the concrete type.
173-
if let Some(&ci) = member_constraints.get(&opaque_type_key) {
174-
for &vid in self.member_constraints.choice_regions(ci) {
175-
to_universal_region(vid, &mut arg_regions);
176-
}
177-
}
178-
debug!(?arg_regions);
179-
180-
// Next, insert universal regions from args, so we can translate regions that appear
181-
// in them but are not subject to member constraints, for instance closure args.
182-
let universal_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
183-
if let ty::RePlaceholder(..) = region.kind() {
184-
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the args.
185-
return region;
186-
}
187-
let vid = self.to_region_vid(region);
188-
to_universal_region(vid, &mut arg_regions)
189-
});
190-
let universal_args = universal_key.args;
191-
debug!(?universal_args);
192-
debug!(?arg_regions);
193-
194-
// Deduplicate the set of regions while keeping the chosen order.
195-
let arg_regions = arg_regions.into_iter().collect::<FxIndexSet<_>>();
196-
debug!(?arg_regions);
197-
198-
let universal_concrete_type =
199-
infcx.tcx.fold_regions(concrete_type, |region, _| match *region {
200-
ty::ReVar(vid) => arg_regions
201-
.iter()
202-
.find(|ur_vid| self.eval_equal(vid, **ur_vid))
203-
.and_then(|ur_vid| self.definitions[*ur_vid].external_name)
204-
.unwrap_or(infcx.tcx.lifetimes.re_erased),
205-
ty::RePlaceholder(_) => ty::Region::new_error_with_message(
206-
infcx.tcx,
207-
concrete_type.span,
208-
"hidden type contains placeholders, we don't support higher kinded opaques yet",
209-
),
210-
_ => region,
182+
arg_regions.push((vid, named));
183+
named
211184
});
212-
debug!(?universal_concrete_type);
185+
debug!(?opaque_type_key, ?arg_regions);
186+
187+
let concrete_type = infcx.tcx.fold_regions(concrete_type, |region, _| {
188+
arg_regions
189+
.iter()
190+
.find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
191+
.map(|&(_, arg_named)| arg_named)
192+
.unwrap_or(infcx.tcx.lifetimes.re_erased)
193+
});
194+
debug!(?concrete_type);
213195

214-
let opaque_type_key =
215-
OpaqueTypeKey { def_id: opaque_type_key.def_id, args: universal_args };
216-
let ty = infcx.infer_opaque_definition_from_instantiation(
217-
opaque_type_key,
218-
universal_concrete_type,
219-
);
196+
let ty =
197+
infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
220198
// Sometimes two opaque types are the same only after we remap the generic parameters
221199
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)`
222200
// and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we only know that

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ impl UniversalRegionRelations<'_> {
164164
self.outlives.contains(fr1, fr2)
165165
}
166166

167+
/// Returns `true` if fr1 is known to equal fr2.
168+
///
169+
/// This will only ever be true for universally quantified regions.
170+
pub(crate) fn equal(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
171+
self.outlives.contains(fr1, fr2) && self.outlives.contains(fr2, fr1)
172+
}
173+
167174
/// Returns a vector of free regions `x` such that `fr1: x` is
168175
/// known to hold.
169176
pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<RegionVid> {

tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,24 @@ impl<T> Equate for T { type Proj = T; }
88
trait Indirect { type Ty; }
99
impl<A, B: Equate<Proj = A>> Indirect for (A, B) { type Ty = (); }
1010

11-
type Opq = impl Sized;
12-
fn define_1(_: Opq) {
13-
let _ = None::<<(Opq, u8) as Indirect>::Ty>;
11+
mod basic {
12+
use super::*;
13+
type Opq = impl Sized;
14+
fn define_1(_: Opq) {
15+
let _ = None::<<(Opq, u8) as Indirect>::Ty>;
16+
}
17+
fn define_2() -> Opq {
18+
0u8
19+
}
1420
}
15-
fn define_2() -> Opq {
16-
0u8
21+
22+
// `Opq<'a> == &'b u8` shouldn't be an error because `'a == 'b`.
23+
mod lifetime {
24+
use super::*;
25+
type Opq<'a> = impl Sized + 'a;
26+
fn define<'a: 'b, 'b: 'a>(_: Opq<'a>) {
27+
let _ = None::<<(Opq<'a>, &'b u8) as Indirect>::Ty>;
28+
}
1729
}
1830

1931
fn main() {}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// issue: #112841
2+
3+
#![feature(type_alias_impl_trait)]
4+
5+
trait Trait<'a, 'b> {}
6+
impl<T> Trait<'_, '_> for T {}
7+
8+
mod mod1 {
9+
type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
10+
fn test<'a>() -> Opaque<'a, 'a> {}
11+
//~^ ERROR non-defining opaque type use in defining scope
12+
//~| ERROR non-defining opaque type use in defining scope
13+
}
14+
15+
mod mod2 {
16+
type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
17+
fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {}
18+
//~^ ERROR non-defining opaque type use in defining scope
19+
}
20+
21+
mod mod3 {
22+
type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
23+
fn test<'a: 'b, 'b: 'a>(a: &'a str) -> Opaque<'a, 'b> { a }
24+
//~^ ERROR non-defining opaque type use in defining scope
25+
}
26+
27+
// This is similar to the previous cases in that 'a is equal to 'static,
28+
// which is is some sense an implicit parameter to `Opaque`.
29+
// For example, given a defining use `Opaque<'a> := &'a ()`,
30+
// it is ambiguous whether `Opaque<'a> := &'a ()` or `Opaque<'a> := &'static ()`
31+
mod mod4 {
32+
type Opaque<'a> = impl super::Trait<'a, 'a>;
33+
fn test<'a: 'static>() -> Opaque<'a> {}
34+
//~^ ERROR expected generic lifetime parameter, found `'static`
35+
}
36+
37+
fn main() {}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error: non-defining opaque type use in defining scope
2+
--> $DIR/equal-lifetime-params-not-ok.rs:10:22
3+
|
4+
LL | fn test<'a>() -> Opaque<'a, 'a> {}
5+
| ^^^^^^^^^^^^^^ generic argument `'a` used twice
6+
|
7+
note: for this opaque type
8+
--> $DIR/equal-lifetime-params-not-ok.rs:9:27
9+
|
10+
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: non-defining opaque type use in defining scope
14+
--> $DIR/equal-lifetime-params-not-ok.rs:10:37
15+
|
16+
LL | fn test<'a>() -> Opaque<'a, 'a> {}
17+
| ^^
18+
|
19+
note: lifetime used multiple times
20+
--> $DIR/equal-lifetime-params-not-ok.rs:9:17
21+
|
22+
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
23+
| ^^ ^^
24+
25+
error: non-defining opaque type use in defining scope
26+
--> $DIR/equal-lifetime-params-not-ok.rs:17:49
27+
|
28+
LL | fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {}
29+
| ^^
30+
|
31+
note: lifetime used multiple times
32+
--> $DIR/equal-lifetime-params-not-ok.rs:16:17
33+
|
34+
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
35+
| ^^ ^^
36+
37+
error: non-defining opaque type use in defining scope
38+
--> $DIR/equal-lifetime-params-not-ok.rs:23:61
39+
|
40+
LL | fn test<'a: 'b, 'b: 'a>(a: &'a str) -> Opaque<'a, 'b> { a }
41+
| ^
42+
|
43+
note: lifetime used multiple times
44+
--> $DIR/equal-lifetime-params-not-ok.rs:22:17
45+
|
46+
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
47+
| ^^ ^^
48+
49+
error[E0792]: expected generic lifetime parameter, found `'static`
50+
--> $DIR/equal-lifetime-params-not-ok.rs:33:42
51+
|
52+
LL | type Opaque<'a> = impl super::Trait<'a, 'a>;
53+
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
54+
LL | fn test<'a: 'static>() -> Opaque<'a> {}
55+
| ^^
56+
57+
error: aborting due to 5 previous errors
58+
59+
For more information about this error, try `rustc --explain E0792`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0792]: expected generic lifetime parameter, found `'_`
2+
--> $DIR/generic-not-strictly-equal.rs:33:5
3+
|
4+
LL | type Opaque<'a> = impl Copy + Captures<'a>;
5+
| -- this generic parameter must be used with a generic lifetime parameter
6+
...
7+
LL | relate(opaque, hidden); // defining use: Opaque<'?1> := u8
8+
| ^^^^^^^^^^^^^^^^^^^^^^
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0792`.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0700]: hidden type for `Opaque<'x>` captures lifetime that does not appear in bounds
2+
--> $DIR/generic-not-strictly-equal.rs:33:5
3+
|
4+
LL | type Opaque<'a> = impl Copy + Captures<'a>;
5+
| ------------------------ opaque type defined here
6+
LL |
7+
LL | fn test<'x>(_: Opaque<'x>) {
8+
| -- hidden type `&'x u8` captures the lifetime `'x` as defined here
9+
...
10+
LL | relate(opaque, hidden); // defining use: Opaque<'?1> := u8
11+
| ^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0700`.

0 commit comments

Comments
 (0)