Skip to content

Commit 4ecdf5f

Browse files
committed
except equal parameters from the uniqueness check
1 parent 4e1999d commit 4ecdf5f

File tree

6 files changed

+246
-5
lines changed

6 files changed

+246
-5
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,10 @@ fn check_opaque_type_well_formed<'tcx>(
432432
}
433433
}
434434

435+
/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
436+
///
437+
/// [rustc-dev-guide chapter]:
438+
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
435439
fn check_opaque_type_parameter_valid<'tcx>(
436440
tcx: TyCtxt<'tcx>,
437441
opaque_type_key: OpaqueTypeKey<'tcx>,
@@ -444,13 +448,15 @@ fn check_opaque_type_parameter_valid<'tcx>(
444448
};
445449

446450
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
451+
let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
447452
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
448453

449454
for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
450455
let arg_is_param = match arg.unpack() {
451456
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
452457
GenericArgKind::Lifetime(lt) if is_ty_alias => {
453458
matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
459+
|| (lt.is_static() && opaque_env.param_equal_static(i))
454460
}
455461
// FIXME(#113916): we can't currently check for unique lifetime params,
456462
// see that issue for more. We will also have to ignore unused lifetime
@@ -460,7 +466,13 @@ fn check_opaque_type_parameter_valid<'tcx>(
460466
};
461467

462468
if arg_is_param {
463-
seen_params.entry(arg).or_default().push(i);
469+
// Register if the same lifetime appears multiple times in the generic args.
470+
// There is an exception when the opaque type *requires* the lifetimes to be equal.
471+
// See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
472+
let seen_where = seen_params.entry(arg).or_default();
473+
if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
474+
seen_where.push(i);
475+
}
464476
} else {
465477
// Prevent `fn foo() -> Foo<u32>` from being defining.
466478
let opaque_param = opaque_generics.param_at(i, tcx);
@@ -494,3 +506,87 @@ fn check_opaque_type_parameter_valid<'tcx>(
494506

495507
Ok(())
496508
}
509+
510+
/// Computes if an opaque type requires a lifetime parameter to be equal to
511+
/// another one or to the `'static` lifetime.
512+
/// These requirements are derived from the explicit and implied bounds.
513+
struct LazyOpaqueTyEnv<'tcx> {
514+
tcx: TyCtxt<'tcx>,
515+
def_id: LocalDefId,
516+
517+
/// Equal parameters will have the same name. Computed Lazily.
518+
/// Example:
519+
/// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
520+
/// Identity args: `['a, 'b, 'c]`
521+
/// Canonical args: `['static, 'b, 'b]`
522+
canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
523+
}
524+
525+
impl<'tcx> LazyOpaqueTyEnv<'tcx> {
526+
pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
527+
Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
528+
}
529+
530+
pub fn param_equal_static(&self, param_index: usize) -> bool {
531+
self.get_canonical_args()[param_index].expect_region().is_static()
532+
}
533+
534+
pub fn params_equal(&self, param1: usize, param2: usize) -> bool {
535+
let canonical_args = self.get_canonical_args();
536+
canonical_args[param1] == canonical_args[param2]
537+
}
538+
539+
fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
540+
use rustc_hir as hir;
541+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
542+
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
543+
544+
if let Some(&canonical_args) = self.canonical_args.get() {
545+
return canonical_args;
546+
}
547+
548+
let &Self { tcx, def_id, .. } = self;
549+
let origin = tcx.opaque_type_origin(def_id);
550+
let parent = match origin {
551+
hir::OpaqueTyOrigin::FnReturn(parent)
552+
| hir::OpaqueTyOrigin::AsyncFn(parent)
553+
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent,
554+
};
555+
let param_env = tcx.param_env(parent);
556+
let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
557+
tcx,
558+
def_id.to_def_id(),
559+
|param, _| {
560+
tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
561+
},
562+
);
563+
564+
let infcx = tcx.infer_ctxt().build();
565+
let ocx = ObligationCtxt::new(&infcx);
566+
567+
let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
568+
tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
569+
Default::default()
570+
});
571+
let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &wf_tys);
572+
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
573+
574+
let mut seen = vec![tcx.lifetimes.re_static];
575+
let canonical_args = tcx.fold_regions(args, |r1, _| {
576+
if r1.is_error() {
577+
r1
578+
} else if let Some(&r2) = seen.iter().find(|&&r2| {
579+
let free_regions = outlives_env.free_region_map();
580+
free_regions.sub_free_regions(tcx, r1, r2)
581+
&& free_regions.sub_free_regions(tcx, r2, r1)
582+
}) {
583+
r2
584+
} else {
585+
seen.push(r1);
586+
r1
587+
}
588+
});
589+
self.canonical_args.set(canonical_args).unwrap();
590+
canonical_args
591+
}
592+
}

tests/ui/error-codes/E0657.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ fn free_fn_capture_hrtb_in_impl_trait()
1111
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
1212
{
1313
Box::new(())
14+
//~^ ERROR expected generic lifetime parameter, found `'static`
1415
}
1516

1617
struct Foo;
@@ -20,6 +21,7 @@ impl Foo {
2021
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
2122
{
2223
Box::new(())
24+
//~^ ERROR expected generic lifetime parameter, found `'static`
2325
}
2426
}
2527

tests/ui/error-codes/E0657.stderr

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,37 @@ note: lifetime declared here
1010
LL | -> Box<for<'a> Id<impl Lt<'a>>>
1111
| ^^
1212

13+
error[E0792]: expected generic lifetime parameter, found `'static`
14+
--> $DIR/E0657.rs:13:5
15+
|
16+
LL | -> Box<for<'a> Id<impl Lt<'a>>>
17+
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
18+
...
19+
LL | Box::new(())
20+
| ^^^^^^^^^^^^
21+
1322
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
14-
--> $DIR/E0657.rs:19:35
23+
--> $DIR/E0657.rs:20:35
1524
|
1625
LL | -> Box<for<'a> Id<impl Lt<'a>>>
1726
| ^^
1827
|
1928
note: lifetime declared here
20-
--> $DIR/E0657.rs:19:20
29+
--> $DIR/E0657.rs:20:20
2130
|
2231
LL | -> Box<for<'a> Id<impl Lt<'a>>>
2332
| ^^
2433

25-
error: aborting due to 2 previous errors
34+
error[E0792]: expected generic lifetime parameter, found `'static`
35+
--> $DIR/E0657.rs:23:9
36+
|
37+
LL | -> Box<for<'a> Id<impl Lt<'a>>>
38+
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
39+
...
40+
LL | Box::new(())
41+
| ^^^^^^^^^^^^
42+
43+
error: aborting due to 4 previous errors
2644

27-
For more information about this error, try `rustc --explain E0657`.
45+
Some errors have detailed explanations: E0657, E0792.
46+
For more information about an error, try `rustc --explain E0657`.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// issue: #111935
2+
// FIXME(aliemjay): outdated due to "once modulo regions" restriction.
3+
// FIXME(aliemjay): mod `infer` should fail.
4+
5+
#![allow(unconditional_recursion)]
6+
7+
// Lt indirection is necessary to make the lifetime of the function late-bound,
8+
// in order to bypass some other bugs.
9+
type Lt<'lt> = Option<*mut &'lt ()>;
10+
11+
mod statik {
12+
use super::*;
13+
// invalid defining use: Opaque<'static> := ()
14+
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
15+
let _: () = foo(Lt::<'static>::None);
16+
//~^ ERROR opaque type used twice with different lifetimes
17+
}
18+
}
19+
20+
mod infer {
21+
use super::*;
22+
// invalid defining use: Opaque<'_> := ()
23+
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
24+
let _: () = foo(Lt::<'_>::None);
25+
}
26+
}
27+
28+
mod equal {
29+
use super::*;
30+
// invalid defining use: Opaque<'a, 'a> := ()
31+
// because of the use of equal lifetimes in args
32+
fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
33+
let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
34+
//~^ ERROR opaque type used twice with different lifetimes
35+
}
36+
}
37+
38+
fn main() {}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error: opaque type used twice with different lifetimes
2+
--> $DIR/non-defining-use-lifetimes.rs:15:16
3+
|
4+
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
5+
| ______________________________________________-
6+
LL | | let _: () = foo(Lt::<'static>::None);
7+
| | ^^ lifetime `'static` used here
8+
LL | |
9+
LL | | }
10+
| |_____- lifetime `'a` previously used here
11+
|
12+
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
13+
--> $DIR/non-defining-use-lifetimes.rs:15:16
14+
|
15+
LL | let _: () = foo(Lt::<'static>::None);
16+
| ^^
17+
18+
error: opaque type used twice with different lifetimes
19+
--> $DIR/non-defining-use-lifetimes.rs:33:16
20+
|
21+
LL | fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
22+
| __________________________________________________________________-
23+
LL | | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
24+
| | ^^ lifetime `'a` used here
25+
LL | |
26+
LL | | }
27+
| |_____- lifetime `'b` previously used here
28+
|
29+
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
30+
--> $DIR/non-defining-use-lifetimes.rs:33:16
31+
|
32+
LL | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
33+
| ^^
34+
35+
error: aborting due to 2 previous errors
36+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// FIXME: description
2+
// issue: #113916
3+
//@ check-pass
4+
5+
#![feature(type_alias_impl_trait)]
6+
#![feature(impl_trait_in_assoc_type)]
7+
8+
trait Trait<'a, 'b> {}
9+
impl<T> Trait<'_, '_> for T {}
10+
11+
mod equal_params {
12+
type Opaque<'a: 'b, 'b: 'a> = impl super::Trait<'a, 'b>;
13+
fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {
14+
let _ = None::<&'a &'b &'a ()>;
15+
0u8
16+
}
17+
}
18+
19+
mod equal_static {
20+
type Opaque<'a: 'static> = impl Sized + 'a;
21+
fn test<'a: 'static>() -> Opaque<'a> {
22+
let _ = None::<&'static &'a ()>;
23+
0u8
24+
}
25+
}
26+
27+
mod implied_bounds {
28+
trait Traitor {
29+
type Assoc;
30+
fn define(self) -> Self::Assoc;
31+
}
32+
33+
impl<'a> Traitor for &'static &'a () {
34+
type Assoc = impl Sized + 'a;
35+
fn define(self) -> Self::Assoc {
36+
let _ = None::<&'static &'a ()>;
37+
0u8
38+
}
39+
}
40+
41+
impl<'a, 'b> Traitor for (&'a &'b (), &'b &'a ()) {
42+
type Assoc = impl Sized + 'a + 'b;
43+
fn define(self) -> Self::Assoc {
44+
let _ = None::<(&'a &'b (), &'b &'a ())>;
45+
0u8
46+
}
47+
}
48+
}
49+
50+
fn main() {}

0 commit comments

Comments
 (0)