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 23, 2023
1 parent 14ce42a commit 350b33e
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 22 deletions.
102 changes: 86 additions & 16 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
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 opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
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,17 @@ 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() && opaque_env.param_equal_static(i))
}
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);
let seen_where = seen_params.entry(arg).or_default();
if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
seen_where.push(i);
}
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
Expand Down Expand Up @@ -413,3 +405,81 @@ fn check_opaque_type_parameter_valid<'tcx>(

Ok(())
}

struct LazyOpaqueTyEnv<'tcx> {
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
canonical_args: std::cell::Cell<Option<ty::GenericArgsRef<'tcx>>>,
}

impl<'tcx> LazyOpaqueTyEnv<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
Self { tcx, def_id, canonical_args: std::cell::Cell::new(None) }
}

pub fn param_equal_static(&self, param_index: usize) -> bool {
self.get_canonical_args()[param_index].expect_region().is_static()
}

pub fn params_equal(&self, param1: usize, param2: usize) -> bool {
let canonical_args = self.get_canonical_args();
canonical_args[param1] == canonical_args[param2]
}

fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
use rustc_hir as hir;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;

if let Some(canonical_args) = self.canonical_args.get() {
return canonical_args;
}

let &Self { tcx, def_id, .. } = self;
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_else(|_| {
tcx.sess.delay_span_bug(tcx.def_span(def_id), "error getting implied bounds");
Default::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 mut seen = vec![tcx.lifetimes.re_static];
let canonical_args = tcx.fold_regions(args, |r1, _| {
if let Some(&r2) = seen.iter().find(|&&r2| {
let free_regions = outlives_env.free_region_map();
free_regions.sub_free_regions(tcx, r1, r2)
&& free_regions.sub_free_regions(tcx, r2, r1)
}) {
r2
} else {
seen.push(r1);
r1
}
});
self.canonical_args.set(Some(canonical_args));
canonical_args
}
}
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`.
37 changes: 37 additions & 0 deletions tests/ui/impl-trait/rpit/non-defining-use-lifetimes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// issue: #111935

#![allow(unconditional_recursion)]

// Lt indirection is necessary to make the lifetime of the function late-bound,
// in order to bypass some other bugs.
type Lt<'lt> = Option<*mut &'lt ()>;

mod statik {
use super::*;
// invalid defining use: Opaque<'static> := ()
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
let _: () = foo(Lt::<'static>::None);
//~^ ERROR expected generic lifetime parameter, found `'static`
}
}

mod infer {
use super::*;
// invalid defining use: Opaque<'_> := ()
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
let _: () = foo(Lt::<'_>::None);
//~^ ERROR expected generic lifetime parameter, found `'_`
}
}

mod equal {
use super::*;
// invalid defining use: Opaque<'a, 'a> := ()
// because of the use of equal lifetimes in args
fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
//~^ ERROR non-defining opaque type use in defining scope
}
}

fn main() {}
31 changes: 31 additions & 0 deletions tests/ui/impl-trait/rpit/non-defining-use-lifetimes.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error[E0792]: expected generic lifetime parameter, found `'static`
--> $DIR/non-defining-use-lifetimes.rs:13:16
|
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
LL | let _: () = foo(Lt::<'static>::None);
| ^^

error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/non-defining-use-lifetimes.rs:22:16
|
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
| -- this generic parameter must be used with a generic lifetime parameter
LL | let _: () = foo(Lt::<'_>::None);
| ^^

error: non-defining opaque type use in defining scope
--> $DIR/non-defining-use-lifetimes.rs:32:16
|
LL | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
| ^^
|
note: lifetime used multiple times
--> $DIR/non-defining-use-lifetimes.rs:31:58
|
LL | fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
| ^^ ^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0792`.
50 changes: 50 additions & 0 deletions tests/ui/type-alias-impl-trait/equal-lifetime-params-ok.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// FIXME: description
// issue: #113916
// check-pass

#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]

trait Trait<'a, 'b> {}
impl<T> Trait<'_, '_> for T {}

mod equal_params {
type Opaque<'a: 'b, 'b: 'a> = impl super::Trait<'a, 'b>;
fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {
let _ = None::<&'a &'b &'a ()>;
0u8
}
}

mod equal_static {
type Opaque<'a: 'static> = impl Sized + 'a;
fn test<'a: 'static>() -> Opaque<'a> {
let _ = None::<&'static &'a ()>;
0u8
}
}

mod implied_bounds {
trait Traitor {
type Assoc;
fn define(self) -> Self::Assoc;
}

impl<'a> Traitor for &'static &'a () {
type Assoc = impl Sized + 'a;
fn define(self) -> Self::Assoc {
let _ = None::<&'static &'a ()>;
0u8
}
}

impl<'a, 'b> Traitor for (&'a &'b (), &'b &'a ()) {
type Assoc = impl Sized + 'a + 'b;
fn define(self) -> Self::Assoc {
let _ = None::<(&'a &'b (), &'b &'a ())>;
0u8
}
}
}

fn main() {}

0 comments on commit 350b33e

Please sign in to comment.