Skip to content

Commit 4e1999d

Browse files
committed
ignore uncaptured lifetimes when checking opaques
1 parent 92f40b8 commit 4e1999d

11 files changed

+183
-17
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
179179

180180
// Next, insert universal regions from args, so we can translate regions that appear
181181
// in them but are not subject to member constraints, for instance closure args.
182-
let universal_args = infcx.tcx.fold_regions(args, |region, _| {
182+
let universal_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
183183
if let ty::RePlaceholder(..) = region.kind() {
184184
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the args.
185185
return region;
186186
}
187187
let vid = self.to_region_vid(region);
188188
to_universal_region(vid, &mut arg_regions)
189189
});
190+
let universal_args = universal_key.args;
190191
debug!(?universal_args);
191192
debug!(?arg_regions);
192193

@@ -431,23 +432,21 @@ fn check_opaque_type_well_formed<'tcx>(
431432
}
432433
}
433434

434-
fn check_opaque_type_parameter_valid(
435-
tcx: TyCtxt<'_>,
436-
opaque_type_key: OpaqueTypeKey<'_>,
435+
fn check_opaque_type_parameter_valid<'tcx>(
436+
tcx: TyCtxt<'tcx>,
437+
opaque_type_key: OpaqueTypeKey<'tcx>,
437438
span: Span,
438439
) -> Result<(), ErrorGuaranteed> {
439440
let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id);
440-
let (parent, is_ty_alias) = match opaque_ty_hir.expect_opaque_ty().origin {
441+
let (_parent, is_ty_alias) = match opaque_ty_hir.expect_opaque_ty().origin {
441442
OpaqueTyOrigin::TyAlias { parent, .. } => (parent, true),
442443
OpaqueTyOrigin::AsyncFn(parent) | OpaqueTyOrigin::FnReturn(parent) => (parent, false),
443444
};
444445

445-
let parent_generics = tcx.generics_of(parent);
446+
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
446447
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
447448

448-
// Only check the parent generics, which will ignore any of the
449-
// duplicated lifetime args that come from reifying late-bounds.
450-
for (i, arg) in opaque_type_key.args.iter().take(parent_generics.count()).enumerate() {
449+
for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
451450
let arg_is_param = match arg.unpack() {
452451
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
453452
GenericArgKind::Lifetime(lt) if is_ty_alias => {
@@ -464,7 +463,7 @@ fn check_opaque_type_parameter_valid(
464463
seen_params.entry(arg).or_default().push(i);
465464
} else {
466465
// Prevent `fn foo() -> Foo<u32>` from being defining.
467-
let opaque_param = parent_generics.param_at(i, tcx);
466+
let opaque_param = opaque_generics.param_at(i, tcx);
468467
let kind = opaque_param.kind.descr();
469468

470469
return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam {
@@ -478,10 +477,10 @@ fn check_opaque_type_parameter_valid(
478477

479478
for (_, indices) in seen_params {
480479
if indices.len() > 1 {
481-
let descr = parent_generics.param_at(indices[0], tcx).kind.descr();
480+
let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
482481
let spans: Vec<_> = indices
483482
.into_iter()
484-
.map(|i| tcx.def_span(parent_generics.param_at(i, tcx).def_id))
483+
.map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
485484
.collect();
486485
#[allow(rustc::diagnostic_outside_of_impl)]
487486
#[allow(rustc::untranslatable_diagnostic)]

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,38 @@ pub struct OpaqueTypeKey<'tcx> {
832832
pub args: GenericArgsRef<'tcx>,
833833
}
834834

835+
impl<'tcx> OpaqueTypeKey<'tcx> {
836+
pub fn iter_captured_args(
837+
self,
838+
tcx: TyCtxt<'tcx>,
839+
) -> impl Iterator<Item = (usize, GenericArg<'tcx>)> {
840+
std::iter::zip(self.args, tcx.variances_of(self.def_id)).enumerate().filter_map(
841+
|(i, (arg, v))| match (arg.unpack(), v) {
842+
(_, ty::Invariant) => Some((i, arg)),
843+
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None,
844+
_ => bug!("unexpected opaque type arg variance"),
845+
},
846+
)
847+
}
848+
849+
pub fn fold_captured_lifetime_args(
850+
self,
851+
tcx: TyCtxt<'tcx>,
852+
mut f: impl FnMut(Region<'tcx>) -> Region<'tcx>,
853+
) -> Self {
854+
let Self { def_id, args } = self;
855+
let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| {
856+
match (arg.unpack(), v) {
857+
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg,
858+
(ty::GenericArgKind::Lifetime(lt), _) => f(lt).into(),
859+
_ => arg,
860+
}
861+
});
862+
let args = tcx.mk_args_from_iter(args);
863+
Self { def_id, args }
864+
}
865+
}
866+
835867
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
836868
pub struct OpaqueHiddenType<'tcx> {
837869
/// The span of this particular definition of the opaque type. So
Lines changed: 74 additions & 0 deletions
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() {}
Lines changed: 13 additions & 0 deletions
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() {}
Lines changed: 16 additions & 0 deletions
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() {}

tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: {foo<DefId(..)_'a/#0>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()}
1+
error: {foo<'{erased}>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()}
22
--> $DIR/erased-regions-in-hidden-ty.rs:12:36
33
|
44
LL | fn foo<'a: 'a>(x: &'a Vec<i32>) -> impl Fn() + 'static {

tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: {foo<DefId(..)_'a/#0>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()}
1+
error: {foo<'{erased}>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()}
22
--> $DIR/erased-regions-in-hidden-ty.rs:12:36
33
|
44
LL | fn foo<'a: 'a>(x: &'a Vec<i32>) -> impl Fn() + 'static {

tests/ui/impl-trait/erased-regions-in-hidden-ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// Make sure that the compiler can handle `ReErased` in the hidden type of an opaque.
1111

1212
fn foo<'a: 'a>(x: &'a Vec<i32>) -> impl Fn() + 'static {
13-
//~^ ERROR 'a/#0>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()}
13+
//~^ ERROR '{erased}>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()}
1414
// Can't write whole type because of lack of path sanitization
1515
|| ()
1616
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// The defining use below has an unconstrained lifetime argument.
2+
// Opaque<'{empty}, 'a> := ();
3+
// Make sure we accept it because the lifetime parameter in such position is
4+
// irrelevant - it is an artifact of how we internally represent opaque
5+
// generics.
6+
// See issue #122307 for details.
7+
8+
//@ check-pass
9+
#![feature(type_alias_impl_trait)]
10+
#![allow(unconditional_recursion)]
11+
12+
type Opaque<'a> = impl Sized + 'a;
13+
14+
fn test<'a>() -> Opaque<'a> {
15+
let _: () = test::<'a>();
16+
}
17+
18+
fn main() {}

tests/ui/type-alias-impl-trait/escaping-bound-var.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ impl Test<'_> for () {}
1717

1818
fn constrain() -> Foo {
1919
()
20+
//~^ ERROR expected generic lifetime parameter, found `'static`
21+
// FIXME(aliemjay): Undesirable error message appears because error regions
22+
// are converterted internally into `'?0` which corresponds to `'static`
23+
// This should be fixed in a later commit.
2024
}
2125

2226
fn main() {}

tests/ui/type-alias-impl-trait/escaping-bound-var.stderr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ note: lifetime declared here
1010
LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
1111
| ^^
1212

13-
error: aborting due to 1 previous error
13+
error[E0792]: expected generic lifetime parameter, found `'static`
14+
--> $DIR/escaping-bound-var.rs:19:5
15+
|
16+
LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
17+
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
18+
...
19+
LL | ()
20+
| ^^
21+
22+
error: aborting due to 2 previous errors
1423

15-
For more information about this error, try `rustc --explain E0657`.
24+
Some errors have detailed explanations: E0657, E0792.
25+
For more information about an error, try `rustc --explain E0657`.

0 commit comments

Comments
 (0)