Skip to content

Commit 350b33e

Browse files
committed
correctly check for unique params
1 parent 14ce42a commit 350b33e

9 files changed

+231
-22
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

+86-16
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,7 @@ fn check_opaque_type_parameter_valid<'tcx>(
352352
opaque_type_key: OpaqueTypeKey<'tcx>,
353353
span: Span,
354354
) -> Result<(), ErrorGuaranteed> {
355-
let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id);
356-
let is_ty_alias = match opaque_ty_hir.expect_opaque_ty().origin {
357-
OpaqueTyOrigin::TyAlias { .. } => true,
358-
OpaqueTyOrigin::AsyncFn(..) | OpaqueTyOrigin::FnReturn(..) => false,
359-
};
360-
355+
let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
361356
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
362357
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
363358
for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
@@ -367,20 +362,17 @@ fn check_opaque_type_parameter_valid<'tcx>(
367362

368363
let arg_is_param = match arg.unpack() {
369364
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
370-
GenericArgKind::Lifetime(lt) => match is_ty_alias {
371-
true => matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)),
372-
// FIXME(#111935, #113916): For RPIT, we currently accept ReStatic as well.
373-
// This is a back-compat hack, see the issue for more.
374-
false => matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic),
375-
},
365+
GenericArgKind::Lifetime(lt) => {
366+
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
367+
|| (lt.is_static() && opaque_env.param_equal_static(i))
368+
}
376369
GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
377370
};
378371

379372
if arg_is_param {
380-
// FIXME(#113916): For RPIT, we can't currently check for unique lifetime
381-
// params, see that issue for more. We limit this to TAIT for now.
382-
if is_ty_alias {
383-
seen_params.entry(arg).or_default().push(i);
373+
let seen_where = seen_params.entry(arg).or_default();
374+
if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
375+
seen_where.push(i);
384376
}
385377
} else {
386378
// Prevent `fn foo() -> Foo<u32>` from being defining.
@@ -413,3 +405,81 @@ fn check_opaque_type_parameter_valid<'tcx>(
413405

414406
Ok(())
415407
}
408+
409+
struct LazyOpaqueTyEnv<'tcx> {
410+
tcx: TyCtxt<'tcx>,
411+
def_id: LocalDefId,
412+
canonical_args: std::cell::Cell<Option<ty::GenericArgsRef<'tcx>>>,
413+
}
414+
415+
impl<'tcx> LazyOpaqueTyEnv<'tcx> {
416+
pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
417+
Self { tcx, def_id, canonical_args: std::cell::Cell::new(None) }
418+
}
419+
420+
pub fn param_equal_static(&self, param_index: usize) -> bool {
421+
self.get_canonical_args()[param_index].expect_region().is_static()
422+
}
423+
424+
pub fn params_equal(&self, param1: usize, param2: usize) -> bool {
425+
let canonical_args = self.get_canonical_args();
426+
canonical_args[param1] == canonical_args[param2]
427+
}
428+
429+
fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
430+
use rustc_hir as hir;
431+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
432+
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
433+
434+
if let Some(canonical_args) = self.canonical_args.get() {
435+
return canonical_args;
436+
}
437+
438+
let &Self { tcx, def_id, .. } = self;
439+
let origin = tcx.opaque_type_origin(def_id);
440+
let defining_use_anchor = match origin {
441+
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
442+
hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id),
443+
};
444+
let param_env = tcx.param_env(defining_use_anchor);
445+
446+
let infcx = tcx.infer_ctxt().build();
447+
let ocx = ObligationCtxt::new(&infcx);
448+
449+
let args = match origin {
450+
hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => {
451+
GenericArgs::identity_for_item(tcx, parent).extend_to(
452+
tcx,
453+
def_id.to_def_id(),
454+
|param, _| {
455+
tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into()
456+
},
457+
)
458+
}
459+
hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id),
460+
};
461+
462+
let wf_tys = ocx.assumed_wf_types(param_env, defining_use_anchor).unwrap_or_else(|_| {
463+
tcx.sess.delay_span_bug(tcx.def_span(def_id), "error getting implied bounds");
464+
Default::default()
465+
});
466+
let implied_bounds = infcx.implied_bounds_tys(param_env, defining_use_anchor, wf_tys);
467+
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
468+
469+
let mut seen = vec![tcx.lifetimes.re_static];
470+
let canonical_args = tcx.fold_regions(args, |r1, _| {
471+
if let Some(&r2) = seen.iter().find(|&&r2| {
472+
let free_regions = outlives_env.free_region_map();
473+
free_regions.sub_free_regions(tcx, r1, r2)
474+
&& free_regions.sub_free_regions(tcx, r2, r1)
475+
}) {
476+
r2
477+
} else {
478+
seen.push(r1);
479+
r1
480+
}
481+
});
482+
self.canonical_args.set(Some(canonical_args));
483+
canonical_args
484+
}
485+
}

tests/ui/impl-trait/defining-use-captured-non-universal-region.infer.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0792]: expected generic lifetime parameter, found `'_`
2-
--> $DIR/defining-use-captured-non-universal-region.rs:15:18
2+
--> $DIR/defining-use-captured-non-universal-region.rs:14:18
33
|
44
LL | fn foo<'a>() -> impl Sized + 'a {
55
| -- this generic parameter must be used with a generic lifetime parameter

tests/ui/impl-trait/defining-use-captured-non-universal-region.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// This was an ICE. See #110726.
2-
//
3-
// FIXME(#111935) revision `statik` should not pass.
42

53
// revisions: statik infer fixed
64
//[fixed] check-pass
7-
//[statik] check-pass
5+
//[statik] check-fail
86
#![allow(unconditional_recursion)]
97

108
fn foo<'a>() -> impl Sized + 'a {
119
#[cfg(statik)]
1210
let i: i32 = foo::<'static>();
11+
//[statik]~^ ERROR expected generic lifetime parameter, found `'static`
1312

1413
#[cfg(infer)]
1514
let i: i32 = foo::<'_>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0792]: expected generic lifetime parameter, found `'static`
2+
--> $DIR/defining-use-captured-non-universal-region.rs:10:18
3+
|
4+
LL | fn foo<'a>() -> impl Sized + 'a {
5+
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
6+
LL | #[cfg(statik)]
7+
LL | let i: i32 = foo::<'static>();
8+
| ^^^^^^^^^^^^^^^^
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0792`.

tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ trait Foo {
55
fn bar<'other: 'a>() -> impl Sized + 'a {}
66
//~^ ERROR use of undeclared lifetime name `'a`
77
//~| ERROR use of undeclared lifetime name `'a`
8+
//~| ERROR expected generic lifetime parameter, found `'static`
89
}
910

1011
fn main() {}

tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.stderr

+11-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ help: consider introducing lifetime `'a` here
2828
LL | trait Foo<'a> {
2929
| ++++
3030

31-
error: aborting due to 2 previous errors
31+
error[E0792]: expected generic lifetime parameter, found `'static`
32+
--> $DIR/bad-item-bound-within-rpitit-2.rs:5:45
33+
|
34+
LL | fn bar<'other: 'a>() -> impl Sized + 'a {}
35+
| ------ ^^
36+
| |
37+
| cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
38+
39+
error: aborting due to 3 previous errors
3240

33-
For more information about this error, try `rustc --explain E0261`.
41+
Some errors have detailed explanations: E0261, E0792.
42+
For more information about an error, try `rustc --explain E0261`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// issue: #111935
2+
3+
#![allow(unconditional_recursion)]
4+
5+
// Lt indirection is necessary to make the lifetime of the function late-bound,
6+
// in order to bypass some other bugs.
7+
type Lt<'lt> = Option<*mut &'lt ()>;
8+
9+
mod statik {
10+
use super::*;
11+
// invalid defining use: Opaque<'static> := ()
12+
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
13+
let _: () = foo(Lt::<'static>::None);
14+
//~^ ERROR expected generic lifetime parameter, found `'static`
15+
}
16+
}
17+
18+
mod infer {
19+
use super::*;
20+
// invalid defining use: Opaque<'_> := ()
21+
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
22+
let _: () = foo(Lt::<'_>::None);
23+
//~^ ERROR expected generic lifetime parameter, found `'_`
24+
}
25+
}
26+
27+
mod equal {
28+
use super::*;
29+
// invalid defining use: Opaque<'a, 'a> := ()
30+
// because of the use of equal lifetimes in args
31+
fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
32+
let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
33+
//~^ ERROR non-defining opaque type use in defining scope
34+
}
35+
}
36+
37+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0792]: expected generic lifetime parameter, found `'static`
2+
--> $DIR/non-defining-use-lifetimes.rs:13:16
3+
|
4+
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
5+
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
6+
LL | let _: () = foo(Lt::<'static>::None);
7+
| ^^
8+
9+
error[E0792]: expected generic lifetime parameter, found `'_`
10+
--> $DIR/non-defining-use-lifetimes.rs:22:16
11+
|
12+
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
13+
| -- this generic parameter must be used with a generic lifetime parameter
14+
LL | let _: () = foo(Lt::<'_>::None);
15+
| ^^
16+
17+
error: non-defining opaque type use in defining scope
18+
--> $DIR/non-defining-use-lifetimes.rs:32:16
19+
|
20+
LL | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
21+
| ^^
22+
|
23+
note: lifetime used multiple times
24+
--> $DIR/non-defining-use-lifetimes.rs:31:58
25+
|
26+
LL | fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
27+
| ^^ ^^
28+
29+
error: aborting due to 3 previous errors
30+
31+
For more information about this error, try `rustc --explain E0792`.
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)