Skip to content

Commit 20a5163

Browse files
committed
correctly check for unique params
1 parent 14ce42a commit 20a5163

6 files changed

+88
-23
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

+61-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_data_structures::fx::FxIndexMap;
1+
use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
22
use rustc_errors::ErrorGuaranteed;
33
use rustc_hir::def::DefKind;
44
use rustc_hir::def_id::LocalDefId;
@@ -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 canonical_args = get_canonical_key(tcx, opaque_type_key.def_id).args;
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,24 @@ 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() && canonical_args[i].expect_region().is_static())
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+
match seen_params.entry(arg) {
374+
IndexEntry::Vacant(e) => {
375+
e.insert(vec![i]);
376+
}
377+
IndexEntry::Occupied(mut e) => {
378+
let prev_i = e.get()[0];
379+
if canonical_args[i] != canonical_args[prev_i] {
380+
e.get_mut().push(i);
381+
}
382+
}
384383
}
385384
} else {
386385
// Prevent `fn foo() -> Foo<u32>` from being defining.
@@ -413,3 +412,48 @@ fn check_opaque_type_parameter_valid<'tcx>(
413412

414413
Ok(())
415414
}
415+
416+
fn get_canonical_key<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> OpaqueTypeKey<'tcx> {
417+
use rustc_hir as hir;
418+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
419+
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
420+
421+
let origin = &tcx.opaque_type_origin(def_id);
422+
let defining_use_anchor = match *origin {
423+
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
424+
hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id),
425+
};
426+
let param_env = tcx.param_env(defining_use_anchor);
427+
428+
let infcx = tcx.infer_ctxt().build();
429+
let ocx = ObligationCtxt::new(&infcx);
430+
431+
let args = match *origin {
432+
hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => {
433+
GenericArgs::identity_for_item(tcx, parent).extend_to(
434+
tcx,
435+
def_id.to_def_id(),
436+
|param, _| tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into(),
437+
)
438+
}
439+
hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id),
440+
};
441+
442+
let wf_tys = ocx.assumed_wf_types(param_env, defining_use_anchor).unwrap_or_default();
443+
let implied_bounds = infcx.implied_bounds_tys(param_env, defining_use_anchor, wf_tys);
444+
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
445+
let free_regions = outlives_env.free_region_map();
446+
447+
let mut seen = vec![tcx.lifetimes.re_static];
448+
OpaqueTypeKey { def_id, args }.fold_captured_lifetime_args(tcx, |region| {
449+
if let Some(&r2) = seen.iter().find(|&&r2| {
450+
free_regions.sub_free_regions(tcx, region, r2)
451+
&& free_regions.sub_free_regions(tcx, r2, region)
452+
}) {
453+
r2
454+
} else {
455+
seen.push(region);
456+
region
457+
}
458+
})
459+
}

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`.

0 commit comments

Comments
 (0)