Skip to content

Commit a70037d

Browse files
committed
rework opaque types region inference
See the ui tests for user-visible changes.
1 parent f9886de commit a70037d

9 files changed

+236
-73
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

+31-68
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_middle::traits::DefiningAnchor;
1010
use rustc_middle::ty::visit::TypeVisitableExt;
@@ -66,85 +66,48 @@ impl<'tcx> RegionInferenceContext<'tcx> {
6666
) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
6767
let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
6868

69-
let member_constraints: FxIndexMap<_, _> = self
70-
.member_constraints
71-
.all_indices()
72-
.map(|ci| (self.member_constraints[ci].key, ci))
73-
.collect();
74-
debug!(?member_constraints);
75-
7669
for (opaque_type_key, concrete_type) in opaque_ty_decls {
77-
let args = opaque_type_key.args;
78-
debug!(?concrete_type, ?args);
70+
debug!(?opaque_type_key, ?concrete_type);
7971

80-
let mut subst_regions = vec![self.universal_regions.fr_static];
72+
let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
73+
vec![(self.universal_regions.fr_static, infcx.tcx.lifetimes.re_static)];
8174

82-
let to_universal_region = |vid, subst_regions: &mut Vec<_>| {
83-
trace!(?vid);
84-
let scc = self.constraint_sccs.scc(vid);
85-
trace!(?scc);
86-
match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| {
87-
self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?)
88-
}) {
89-
Some(region) => {
90-
let vid = self.universal_regions.to_region_vid(region);
91-
subst_regions.push(vid);
92-
region
75+
let opaque_type_key =
76+
opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
77+
let scc = self.constraint_sccs.scc(self.to_region_vid(region));
78+
let vid = self.scc_representatives[scc];
79+
let named = match self.definitions[vid].origin {
80+
NllRegionVariableOrigin::FreeRegion => self.definitions[vid].external_name,
81+
NllRegionVariableOrigin::Placeholder(placeholder) => {
82+
Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
83+
}
84+
NllRegionVariableOrigin::Existential { .. } => None,
9385
}
94-
None => {
95-
subst_regions.push(vid);
86+
.unwrap_or_else(|| {
9687
ty::Region::new_error_with_message(
9788
infcx.tcx,
9889
concrete_type.span,
9990
"opaque type with non-universal region args",
10091
)
101-
}
102-
}
103-
};
92+
});
10493

105-
// Start by inserting universal regions from the member_constraint choice regions.
106-
// This will ensure they get precedence when folding the regions in the concrete type.
107-
if let Some(&ci) = member_constraints.get(&opaque_type_key) {
108-
for &vid in self.member_constraints.choice_regions(ci) {
109-
to_universal_region(vid, &mut subst_regions);
110-
}
111-
}
112-
debug!(?subst_regions);
113-
114-
// Next, insert universal regions from args, so we can translate regions that appear
115-
// in them but are not subject to member constraints, for instance closure args.
116-
let universal_args = infcx.tcx.fold_regions(args, |region, _| {
117-
if let ty::RePlaceholder(..) = region.kind() {
118-
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the args.
119-
return region;
120-
}
94+
arg_regions.push((vid, named));
95+
named
96+
});
97+
debug!(?opaque_type_key, ?arg_regions);
98+
99+
let concrete_type = infcx.tcx.fold_regions(concrete_type, |region, _| {
121100
let vid = self.to_region_vid(region);
122-
to_universal_region(vid, &mut subst_regions)
101+
arg_regions
102+
.iter()
103+
.find(|&&(ur_vid, _)| self.eval_equal(vid, ur_vid))
104+
.map(|&(_, ur_name)| ur_name)
105+
.unwrap_or(infcx.tcx.lifetimes.re_erased)
123106
});
124-
debug!(?universal_args);
125-
debug!(?subst_regions);
126-
127-
// Deduplicate the set of regions while keeping the chosen order.
128-
let subst_regions = subst_regions.into_iter().collect::<FxIndexSet<_>>();
129-
debug!(?subst_regions);
130-
131-
let universal_concrete_type =
132-
infcx.tcx.fold_regions(concrete_type, |region, _| match *region {
133-
ty::ReVar(vid) => subst_regions
134-
.iter()
135-
.find(|ur_vid| self.eval_equal(vid, **ur_vid))
136-
.and_then(|ur_vid| self.definitions[*ur_vid].external_name)
137-
.unwrap_or(infcx.tcx.lifetimes.re_erased),
138-
_ => region,
139-
});
140-
debug!(?universal_concrete_type);
107+
debug!(?concrete_type);
141108

142-
let opaque_type_key =
143-
OpaqueTypeKey { def_id: opaque_type_key.def_id, args: universal_args };
144-
let ty = infcx.infer_opaque_definition_from_instantiation(
145-
opaque_type_key,
146-
universal_concrete_type,
147-
);
109+
let ty =
110+
infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
148111
// Sometimes two opaque types are the same only after we remap the generic parameters
149112
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)`
150113
// and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we only know that

compiler/rustc_middle/src/ty/mod.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,25 @@ pub struct OpaqueTypeKey<'tcx> {
15041504
pub args: GenericArgsRef<'tcx>,
15051505
}
15061506

1507+
impl<'tcx> OpaqueTypeKey<'tcx> {
1508+
pub fn fold_captured_lifetime_args(
1509+
self,
1510+
tcx: TyCtxt<'tcx>,
1511+
mut f: impl FnMut(Region<'tcx>) -> Region<'tcx>,
1512+
) -> Self {
1513+
let Self { def_id, args } = self;
1514+
let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| {
1515+
match (arg.unpack(), v) {
1516+
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg,
1517+
(ty::GenericArgKind::Lifetime(lt), _) => f(lt).into(),
1518+
_ => arg,
1519+
}
1520+
});
1521+
let args = tcx.mk_args_from_iter(args);
1522+
Self { def_id, args }
1523+
}
1524+
}
1525+
15071526
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
15081527
pub struct OpaqueHiddenType<'tcx> {
15091528
/// The span of this particular definition of the opaque type. So
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// issue: #110623
2+
// check-pass
3+
4+
use std::{collections::BTreeMap, num::ParseIntError, str::FromStr};
5+
6+
enum FileSystem {
7+
File(usize),
8+
Directory(BTreeMap<String, FileSystem>),
9+
}
10+
11+
impl FromStr for FileSystem {
12+
type Err = ParseIntError;
13+
14+
fn from_str(s: &str) -> Result<Self, Self::Err> {
15+
if s.starts_with("dir") {
16+
Ok(Self::new_dir())
17+
} else {
18+
Ok(Self::File(s.split_whitespace().next().unwrap().parse()?))
19+
}
20+
}
21+
}
22+
23+
impl FileSystem {
24+
fn new_dir() -> FileSystem {
25+
FileSystem::Directory(BTreeMap::new())
26+
}
27+
28+
fn insert(&mut self, name: String, other: FileSystem) -> Option<FileSystem> {
29+
match self {
30+
FileSystem::File(_) => panic!("can only insert into directory!"),
31+
FileSystem::Directory(tree) => tree.insert(name, other),
32+
}
33+
}
34+
35+
// Recursively build a tree from commands. This uses (abuses?)
36+
// the fact that `cd /` only appears at the start and that
37+
// subsequent `cd`s can only move ONE level to use the recursion
38+
// stack as the filesystem stack
39+
fn build<'a>(
40+
&mut self,
41+
mut commands: impl Iterator<Item = &'a str> + 'a,
42+
) -> Option<impl Iterator<Item = &'a str> + 'a> {
43+
let cmd = commands.next()?;
44+
let mut elements = cmd.lines();
45+
match elements.next().map(str::trim) {
46+
Some("cd /") | None => self.build(commands),
47+
Some("cd ..") => {
48+
// return to higher scope
49+
Some(commands)
50+
}
51+
Some("ls") => {
52+
for item in elements {
53+
let name = item.split_whitespace().last().unwrap();
54+
let prior = self.insert(name.to_string(), item.parse().unwrap());
55+
debug_assert!(prior.is_none());
56+
}
57+
// continue on
58+
self.build(commands)
59+
}
60+
Some(other_cd) => {
61+
let name = other_cd
62+
.trim()
63+
.strip_prefix("cd ")
64+
.expect("expected a cd command");
65+
let mut directory = FileSystem::new_dir();
66+
let further_commands = directory.build(commands);
67+
self.insert(name.to_string(), directory);
68+
self.build(further_commands?) // THIS LINE FAILS TO COMPILE
69+
}
70+
}
71+
}
72+
}
73+
74+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// check-pass
2+
3+
#![feature(adt_const_params)]
4+
#![allow(incomplete_features)]
5+
6+
trait Bar<const FOO: &'static str> {}
7+
impl Bar<"asdf"> for () {}
8+
9+
fn foo<const FOO: &'static str>() -> impl Bar<"asdf"> {
10+
()
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// issue: #111906
2+
// check-pass
3+
4+
#![allow(unconditional_recursion)]
5+
6+
fn foo<'a: 'a>() -> impl Sized {
7+
let _: () = foo::<'a>();
8+
loop {}
9+
}
10+
11+
fn bar<'a: 'a>() -> impl Sized + 'a {
12+
let _: *mut &'a () = bar::<'a>();
13+
loop {}
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// User type annotation in fn bodies is a a valid defining site for opaque types.
22
// check-pass
3+
34
#![feature(type_alias_impl_trait)]
45

56
trait Equate { type Proj; }
@@ -8,12 +9,24 @@ impl<T> Equate for T { type Proj = T; }
89
trait Indirect { type Ty; }
910
impl<A, B: Equate<Proj = A>> Indirect for (A, B) { type Ty = (); }
1011

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

1932
fn main() {}
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 previous error
11+
12+
For more information about this error, try `rustc --explain E0792`.
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 previous error
14+
15+
For more information about this error, try `rustc --explain E0700`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// `Opaque<'?1> := u8` is not a valid defining use here.
2+
//
3+
// Due to fundamental limitations of the member constraints algorithm,
4+
// we require '?1 to be *equal* to some universal region.
5+
//
6+
// While '?1 is eventually inferred to be equal to 'x because of the constraint '?1: 'x,
7+
// we don't consider them equal in the strict sense because they lack the bidirectional outlives
8+
// constraints ['?1: 'x, 'x: '?1]. In NLL terms, they are not part of the same SCC.
9+
//
10+
// See #113971 for details.
11+
12+
// revisions: basic member_constraints
13+
#![feature(type_alias_impl_trait)]
14+
15+
trait Captures<'a> {}
16+
impl<T> Captures<'_> for T {}
17+
18+
fn ensure_outlives<'a, X: 'a>(_: X) {}
19+
fn relate<X>(_: X, _: X) {}
20+
21+
type Opaque<'a> = impl Copy + Captures<'a>;
22+
23+
fn test<'x>(_: Opaque<'x>) {
24+
let opaque = None::<Opaque<'_>>; // let's call this lifetime '?1
25+
26+
#[cfg(basic)]
27+
let hidden = None::<u8>;
28+
29+
#[cfg(member_constraints)]
30+
let hidden = None::<&'x u8>;
31+
32+
ensure_outlives::<'x>(opaque); // outlives constraint: '?1: 'x
33+
relate(opaque, hidden); // defining use: Opaque<'?1> := u8
34+
//[basic]~^ ERROR expected generic lifetime parameter, found `'_`
35+
//[member_constraints]~^^ ERROR captures lifetime that does not appear in bounds
36+
}
37+
38+
fn main() {}

0 commit comments

Comments
 (0)