Skip to content

Commit

Permalink
correctly check for unique params
Browse files Browse the repository at this point in the history
  • Loading branch information
aliemjay committed Oct 21, 2023
1 parent 14ce42a commit 20a5163
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 23 deletions.
78 changes: 61 additions & 17 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
Expand Down Expand Up @@ -352,12 +352,7 @@ fn check_opaque_type_parameter_valid<'tcx>(
opaque_type_key: OpaqueTypeKey<'tcx>,
span: Span,
) -> Result<(), ErrorGuaranteed> {
let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id);
let is_ty_alias = match opaque_ty_hir.expect_opaque_ty().origin {
OpaqueTyOrigin::TyAlias { .. } => true,
OpaqueTyOrigin::AsyncFn(..) | OpaqueTyOrigin::FnReturn(..) => false,
};

let canonical_args = get_canonical_key(tcx, opaque_type_key.def_id).args;
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
Expand All @@ -367,20 +362,24 @@ fn check_opaque_type_parameter_valid<'tcx>(

let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
GenericArgKind::Lifetime(lt) => match is_ty_alias {
true => matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)),
// FIXME(#111935, #113916): For RPIT, we currently accept ReStatic as well.
// This is a back-compat hack, see the issue for more.
false => matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic),
},
GenericArgKind::Lifetime(lt) => {
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
|| (lt.is_static() && canonical_args[i].expect_region().is_static())
}
GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
};

if arg_is_param {
// FIXME(#113916): For RPIT, we can't currently check for unique lifetime
// params, see that issue for more. We limit this to TAIT for now.
if is_ty_alias {
seen_params.entry(arg).or_default().push(i);
match seen_params.entry(arg) {
IndexEntry::Vacant(e) => {
e.insert(vec![i]);
}
IndexEntry::Occupied(mut e) => {
let prev_i = e.get()[0];
if canonical_args[i] != canonical_args[prev_i] {
e.get_mut().push(i);
}
}
}
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
Expand Down Expand Up @@ -413,3 +412,48 @@ fn check_opaque_type_parameter_valid<'tcx>(

Ok(())
}

fn get_canonical_key<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> OpaqueTypeKey<'tcx> {
use rustc_hir as hir;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;

let origin = &tcx.opaque_type_origin(def_id);
let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id),
};
let param_env = tcx.param_env(defining_use_anchor);

let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);

let args = match *origin {
hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => {
GenericArgs::identity_for_item(tcx, parent).extend_to(
tcx,
def_id.to_def_id(),
|param, _| tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into(),
)
}
hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id),
};

let wf_tys = ocx.assumed_wf_types(param_env, defining_use_anchor).unwrap_or_default();
let implied_bounds = infcx.implied_bounds_tys(param_env, defining_use_anchor, wf_tys);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let free_regions = outlives_env.free_region_map();

let mut seen = vec![tcx.lifetimes.re_static];
OpaqueTypeKey { def_id, args }.fold_captured_lifetime_args(tcx, |region| {
if let Some(&r2) = seen.iter().find(|&&r2| {
free_regions.sub_free_regions(tcx, region, r2)
&& free_regions.sub_free_regions(tcx, r2, region)
}) {
r2
} else {
seen.push(region);
region
}
})
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/defining-use-captured-non-universal-region.rs:15:18
--> $DIR/defining-use-captured-non-universal-region.rs:14:18
|
LL | fn foo<'a>() -> impl Sized + 'a {
| -- this generic parameter must be used with a generic lifetime parameter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
// This was an ICE. See #110726.
//
// FIXME(#111935) revision `statik` should not pass.

// revisions: statik infer fixed
//[fixed] check-pass
//[statik] check-pass
//[statik] check-fail
#![allow(unconditional_recursion)]

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

#[cfg(infer)]
let i: i32 = foo::<'_>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0792]: expected generic lifetime parameter, found `'static`
--> $DIR/defining-use-captured-non-universal-region.rs:10:18
|
LL | fn foo<'a>() -> impl Sized + 'a {
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
LL | #[cfg(statik)]
LL | let i: i32 = foo::<'static>();
| ^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0792`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ trait Foo {
fn bar<'other: 'a>() -> impl Sized + 'a {}
//~^ ERROR use of undeclared lifetime name `'a`
//~| ERROR use of undeclared lifetime name `'a`
//~| ERROR expected generic lifetime parameter, found `'static`
}

fn main() {}
13 changes: 11 additions & 2 deletions tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ help: consider introducing lifetime `'a` here
LL | trait Foo<'a> {
| ++++

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

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0261`.
Some errors have detailed explanations: E0261, E0792.
For more information about an error, try `rustc --explain E0261`.

0 comments on commit 20a5163

Please sign in to comment.