mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-15 05:26:47 +00:00
except equal parameters from the uniqueness check
This commit is contained in:
parent
4e1999d387
commit
4ecdf5ff00
@ -432,6 +432,10 @@ fn check_opaque_type_well_formed<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
|
||||
///
|
||||
/// [rustc-dev-guide chapter]:
|
||||
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
|
||||
fn check_opaque_type_parameter_valid<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||
@ -444,6 +448,7 @@ fn check_opaque_type_parameter_valid<'tcx>(
|
||||
};
|
||||
|
||||
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
|
||||
let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
|
||||
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
|
||||
|
||||
for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
|
||||
@ -451,6 +456,7 @@ fn check_opaque_type_parameter_valid<'tcx>(
|
||||
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
|
||||
GenericArgKind::Lifetime(lt) if is_ty_alias => {
|
||||
matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
|
||||
|| (lt.is_static() && opaque_env.param_equal_static(i))
|
||||
}
|
||||
// FIXME(#113916): we can't currently check for unique lifetime params,
|
||||
// see that issue for more. We will also have to ignore unused lifetime
|
||||
@ -460,7 +466,13 @@ fn check_opaque_type_parameter_valid<'tcx>(
|
||||
};
|
||||
|
||||
if arg_is_param {
|
||||
seen_params.entry(arg).or_default().push(i);
|
||||
// Register if the same lifetime appears multiple times in the generic args.
|
||||
// There is an exception when the opaque type *requires* the lifetimes to be equal.
|
||||
// See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
|
||||
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.
|
||||
let opaque_param = opaque_generics.param_at(i, tcx);
|
||||
@ -494,3 +506,87 @@ fn check_opaque_type_parameter_valid<'tcx>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes if an opaque type requires a lifetime parameter to be equal to
|
||||
/// another one or to the `'static` lifetime.
|
||||
/// These requirements are derived from the explicit and implied bounds.
|
||||
struct LazyOpaqueTyEnv<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
|
||||
/// Equal parameters will have the same name. Computed Lazily.
|
||||
/// Example:
|
||||
/// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
|
||||
/// Identity args: `['a, 'b, 'c]`
|
||||
/// Canonical args: `['static, 'b, 'b]`
|
||||
canonical_args: std::cell::OnceCell<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::OnceCell::new() }
|
||||
}
|
||||
|
||||
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 parent = match origin {
|
||||
hir::OpaqueTyOrigin::FnReturn(parent)
|
||||
| hir::OpaqueTyOrigin::AsyncFn(parent)
|
||||
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent,
|
||||
};
|
||||
let param_env = tcx.param_env(parent);
|
||||
let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
|
||||
tcx,
|
||||
def_id.to_def_id(),
|
||||
|param, _| {
|
||||
tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
|
||||
},
|
||||
);
|
||||
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
|
||||
let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
|
||||
tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
|
||||
Default::default()
|
||||
});
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &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 r1.is_error() {
|
||||
r1
|
||||
} else 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(canonical_args).unwrap();
|
||||
canonical_args
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ fn free_fn_capture_hrtb_in_impl_trait()
|
||||
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
|
||||
{
|
||||
Box::new(())
|
||||
//~^ ERROR expected generic lifetime parameter, found `'static`
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
@ -20,6 +21,7 @@ impl Foo {
|
||||
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
|
||||
{
|
||||
Box::new(())
|
||||
//~^ ERROR expected generic lifetime parameter, found `'static`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,18 +10,37 @@ note: lifetime declared here
|
||||
LL | -> Box<for<'a> Id<impl Lt<'a>>>
|
||||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'static`
|
||||
--> $DIR/E0657.rs:13:5
|
||||
|
|
||||
LL | -> Box<for<'a> Id<impl Lt<'a>>>
|
||||
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
|
||||
...
|
||||
LL | Box::new(())
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
|
||||
--> $DIR/E0657.rs:19:35
|
||||
--> $DIR/E0657.rs:20:35
|
||||
|
|
||||
LL | -> Box<for<'a> Id<impl Lt<'a>>>
|
||||
| ^^
|
||||
|
|
||||
note: lifetime declared here
|
||||
--> $DIR/E0657.rs:19:20
|
||||
--> $DIR/E0657.rs:20:20
|
||||
|
|
||||
LL | -> Box<for<'a> Id<impl Lt<'a>>>
|
||||
| ^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error[E0792]: expected generic lifetime parameter, found `'static`
|
||||
--> $DIR/E0657.rs:23:9
|
||||
|
|
||||
LL | -> Box<for<'a> Id<impl Lt<'a>>>
|
||||
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
|
||||
...
|
||||
LL | Box::new(())
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
For more information about this error, try `rustc --explain E0657`.
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0657, E0792.
|
||||
For more information about an error, try `rustc --explain E0657`.
|
||||
|
38
tests/ui/impl-trait/rpit/non-defining-use-lifetimes.rs
Normal file
38
tests/ui/impl-trait/rpit/non-defining-use-lifetimes.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// issue: #111935
|
||||
// FIXME(aliemjay): outdated due to "once modulo regions" restriction.
|
||||
// FIXME(aliemjay): mod `infer` should fail.
|
||||
|
||||
#![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 opaque type used twice with different lifetimes
|
||||
}
|
||||
}
|
||||
|
||||
mod infer {
|
||||
use super::*;
|
||||
// invalid defining use: Opaque<'_> := ()
|
||||
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
|
||||
let _: () = foo(Lt::<'_>::None);
|
||||
}
|
||||
}
|
||||
|
||||
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 opaque type used twice with different lifetimes
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
36
tests/ui/impl-trait/rpit/non-defining-use-lifetimes.stderr
Normal file
36
tests/ui/impl-trait/rpit/non-defining-use-lifetimes.stderr
Normal file
@ -0,0 +1,36 @@
|
||||
error: opaque type used twice with different lifetimes
|
||||
--> $DIR/non-defining-use-lifetimes.rs:15:16
|
||||
|
|
||||
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
|
||||
| ______________________________________________-
|
||||
LL | | let _: () = foo(Lt::<'static>::None);
|
||||
| | ^^ lifetime `'static` used here
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____- lifetime `'a` previously used here
|
||||
|
|
||||
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
|
||||
--> $DIR/non-defining-use-lifetimes.rs:15:16
|
||||
|
|
||||
LL | let _: () = foo(Lt::<'static>::None);
|
||||
| ^^
|
||||
|
||||
error: opaque type used twice with different lifetimes
|
||||
--> $DIR/non-defining-use-lifetimes.rs:33:16
|
||||
|
|
||||
LL | fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
|
||||
| __________________________________________________________________-
|
||||
LL | | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
|
||||
| | ^^ lifetime `'a` used here
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____- lifetime `'b` previously used here
|
||||
|
|
||||
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
|
||||
--> $DIR/non-defining-use-lifetimes.rs:33:16
|
||||
|
|
||||
LL | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
|
||||
| ^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
50
tests/ui/type-alias-impl-trait/equal-lifetime-params-ok.rs
Normal file
50
tests/ui/type-alias-impl-trait/equal-lifetime-params-ok.rs
Normal file
@ -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() {}
|
Loading…
Reference in New Issue
Block a user