mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 22:12:15 +00:00
Auto merge of #121679 - lcnr:opaque-wf-check-2, r=oli-obk
stricter hidden type wf-check [based on #115008] Original work by `@aliemjay` in #115008. A huge thanks to them for originally figuring out this approach ❤️ Fixes https://github.com/rust-lang/rust/issues/114728 Fixes https://github.com/rust-lang/rust/issues/114572 Instead of adding the `WellFormed` obligations when relating opaque types, we now always emit such an obligation when defining the hidden type. This causes nested opaque types which aren't wf to error, see the comment below for the described impact. I believe this change to be desirable as it significantly reduces complexity by removing special-cases. It also caused an issue with RPITIT: in defaulted trait methods, we add a `Projection(synthetic_assoc, rpit_of_trait_method)` clause to the `param_env`. This clause is not added to the `ParamEnv` of the nested coroutines. This caused a normalization failure in `fn check_coroutine_obligations` with the new solver. I fixed that by using the env of the typeck root instead. r? `@oli-obk`
This commit is contained in:
commit
09bc67b915
@ -402,39 +402,29 @@ fn check_opaque_meets_bounds<'tcx>(
|
||||
let guar = infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||
return Err(guar);
|
||||
}
|
||||
match origin {
|
||||
// Checked when type checking the function containing them.
|
||||
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
|
||||
// HACK: this should also fall through to the hidden type check below, but the original
|
||||
// implementation had a bug where equivalent lifetimes are not identical. This caused us
|
||||
// to reject existing stable code that is otherwise completely fine. The real fix is to
|
||||
// compare the hidden types via our type equivalence/relation infra instead of doing an
|
||||
// identity check.
|
||||
let _ = infcx.take_opaque_types();
|
||||
return Ok(());
|
||||
}
|
||||
// Nested opaque types occur only in associated types:
|
||||
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
|
||||
// They can only be referenced as `<Opaque<T> as Trait<&'static T>>::AssocTy`.
|
||||
// We don't have to check them here because their well-formedness follows from the WF of
|
||||
// the projection input types in the defining- and use-sites.
|
||||
hir::OpaqueTyOrigin::TyAlias { .. }
|
||||
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
|
||||
// Can have different predicates to their defining use
|
||||
hir::OpaqueTyOrigin::TyAlias { .. } => {
|
||||
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
|
||||
|
||||
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?;
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
|
||||
|
||||
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
|
||||
// HACK: this should also fall through to the hidden type check below, but the original
|
||||
// implementation had a bug where equivalent lifetimes are not identical. This caused us
|
||||
// to reject existing stable code that is otherwise completely fine. The real fix is to
|
||||
// compare the hidden types via our type equivalence/relation infra instead of doing an
|
||||
// identity check.
|
||||
let _ = infcx.take_opaque_types();
|
||||
Ok(())
|
||||
} else {
|
||||
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
|
||||
for (mut key, mut ty) in infcx.take_opaque_types() {
|
||||
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
|
||||
key = infcx.resolve_vars_if_possible(key);
|
||||
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
|
||||
for (mut key, mut ty) in infcx.take_opaque_types() {
|
||||
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
|
||||
key = infcx.resolve_vars_if_possible(key);
|
||||
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sanity_check_found_hidden_type<'tcx>(
|
||||
@ -1549,6 +1539,8 @@ fn opaque_type_cycle_error(
|
||||
err.emit()
|
||||
}
|
||||
|
||||
// FIXME(@lcnr): This should not be computed per coroutine, but instead once for
|
||||
// each typeck root.
|
||||
pub(super) fn check_coroutine_obligations(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: LocalDefId,
|
||||
@ -1556,7 +1548,7 @@ pub(super) fn check_coroutine_obligations(
|
||||
debug_assert!(tcx.is_coroutine(def_id.to_def_id()));
|
||||
|
||||
let typeck = tcx.typeck(def_id);
|
||||
let param_env = tcx.param_env(def_id);
|
||||
let param_env = tcx.param_env(typeck.hir_owner.def_id);
|
||||
|
||||
let coroutine_interior_predicates = &typeck.coroutine_interior_predicates[&def_id];
|
||||
debug!(?coroutine_interior_predicates);
|
||||
|
@ -592,8 +592,25 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let item_bounds = tcx.explicit_item_bounds(def_id);
|
||||
// Require that the hidden type is well-formed. We have to
|
||||
// make sure we wf-check the hidden type to fix #114728.
|
||||
//
|
||||
// However, we don't check that all types are well-formed.
|
||||
// We only do so for types provided by the user or if they are
|
||||
// "used", e.g. for method selection.
|
||||
//
|
||||
// This means we never check the wf requirements of the hidden
|
||||
// type during MIR borrowck, causing us to infer the wrong
|
||||
// lifetime for its member constraints which then results in
|
||||
// unexpected region errors.
|
||||
obligations.push(traits::Obligation::new(
|
||||
tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
ty::ClauseKind::WellFormed(hidden_ty.into()),
|
||||
));
|
||||
|
||||
let item_bounds = tcx.explicit_item_bounds(def_id);
|
||||
for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) {
|
||||
let predicate = predicate.fold_with(&mut BottomUpFolder {
|
||||
tcx,
|
||||
|
@ -121,18 +121,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
|
||||
}
|
||||
}
|
||||
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
|
||||
DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
|
||||
DefKind::TyAlias => ty::List::empty(),
|
||||
DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
|
||||
// Nested opaque types only occur in associated types:
|
||||
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
|
||||
// assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
|
||||
// and `&'static T`.
|
||||
DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"),
|
||||
def_kind => {
|
||||
bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
|
||||
}
|
||||
},
|
||||
DefKind::OpaqueTy => bug!("implied bounds are not defined for opaques"),
|
||||
DefKind::Mod
|
||||
| DefKind::Struct
|
||||
| DefKind::Union
|
||||
|
@ -1,7 +1,7 @@
|
||||
fn server() -> impl {
|
||||
//~^ ERROR at least one trait must be specified
|
||||
//~^ ERROR at least one trait must be specified
|
||||
//~| ERROR type annotations needed
|
||||
().map2(|| "")
|
||||
//~^ ERROR type annotations needed
|
||||
}
|
||||
|
||||
trait FilterBase2 {
|
||||
|
@ -23,10 +23,10 @@ LL | struct Map2<Segment2, F> {
|
||||
| +++
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/issue-78720.rs:3:5
|
||||
--> $DIR/issue-78720.rs:1:16
|
||||
|
|
||||
LL | ().map2(|| "")
|
||||
| ^^^^^^^^^^^^^^ cannot infer type
|
||||
LL | fn server() -> impl {
|
||||
| ^^^^ cannot infer type
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-78720.rs:8:39
|
||||
|
@ -1,12 +1,6 @@
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
//@ edition:2021
|
||||
//@ compile-flags:-Z treat-err-as-bug=2
|
||||
//@ error-pattern: due to `-Z treat-err-as-bug=2
|
||||
//@ failure-status:101
|
||||
//@ normalize-stderr-test ".*note: .*\n\n" -> ""
|
||||
//@ normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> ""
|
||||
//@ rustc-env:RUST_BACKTRACE=0
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
@ -29,6 +23,7 @@ struct Context {
|
||||
type TransactionResult<O> = Result<O, ()>;
|
||||
|
||||
type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
|
||||
//~^ ERROR unconstrained opaque type
|
||||
|
||||
fn execute_transaction_fut<'f, F, O>(
|
||||
f: F,
|
||||
@ -37,6 +32,7 @@ where
|
||||
F: FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> + 'f
|
||||
{
|
||||
f
|
||||
//~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
}
|
||||
|
||||
impl Context {
|
||||
@ -44,6 +40,7 @@ impl Context {
|
||||
&self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>
|
||||
) -> TransactionResult<O>
|
||||
{
|
||||
//~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
let mut conn = Connection {};
|
||||
let mut transaction = TestTransaction { conn: &mut conn };
|
||||
f(&mut transaction).await
|
||||
|
@ -1,11 +1,13 @@
|
||||
error: unconstrained opaque type
|
||||
--> $DIR/issue-86800.rs:31:34
|
||||
--> $DIR/issue-86800.rs:25:34
|
||||
|
|
||||
LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
error: internal compiler error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
--> $DIR/issue-86800.rs:39:5
|
||||
= note: `TransactionFuture` must be used in combination with a concrete type within the same module
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
--> $DIR/issue-86800.rs:34:5
|
||||
|
|
||||
LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
|
||||
| --- this generic parameter must be used with a generic lifetime parameter
|
||||
@ -13,9 +15,20 @@ LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResu
|
||||
LL | f
|
||||
| ^
|
||||
|
||||
error: the compiler unexpectedly panicked. this is a bug.
|
||||
error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
--> $DIR/issue-86800.rs:42:5
|
||||
|
|
||||
LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
|
||||
| --- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | / {
|
||||
LL | |
|
||||
LL | | let mut conn = Connection {};
|
||||
LL | | let mut transaction = TestTransaction { conn: &mut conn };
|
||||
LL | | f(&mut transaction).await
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
||||
query stack during panic:
|
||||
#0 [mir_borrowck] borrow-checking `execute_transaction_fut`
|
||||
#1 [type_of_opaque] computing type of opaque `execute_transaction_fut::{opaque#0}`
|
||||
end of query stack
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
@ -1,9 +1,9 @@
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/recursive-coroutine-boxed.rs:10:23
|
||||
--> $DIR/recursive-coroutine-boxed.rs:11:23
|
||||
|
|
||||
LL | let mut gen = Box::pin(foo());
|
||||
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
|
||||
...
|
||||
LL |
|
||||
LL | let mut r = gen.as_mut().resume(());
|
||||
| ------ type must be known at this point
|
||||
|
|
||||
@ -13,10 +13,10 @@ LL | let mut gen = Box::<T>::pin(foo());
|
||||
| +++++
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/recursive-coroutine-boxed.rs:10:32
|
||||
--> $DIR/recursive-coroutine-boxed.rs:8:13
|
||||
|
|
||||
LL | let mut gen = Box::pin(foo());
|
||||
| ^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
|
||||
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -6,10 +6,10 @@
|
||||
use std::ops::{Coroutine, CoroutineState};
|
||||
|
||||
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
||||
//[next]~^ ERROR type annotations needed
|
||||
|| {
|
||||
let mut gen = Box::pin(foo());
|
||||
//[next]~^ ERROR type annotations needed
|
||||
//[next]~| ERROR type annotations needed
|
||||
let mut r = gen.as_mut().resume(());
|
||||
while let CoroutineState::Yielded(v) = r {
|
||||
yield v;
|
||||
|
20
tests/ui/impl-trait/wf-check-hidden-type.rs
Normal file
20
tests/ui/impl-trait/wf-check-hidden-type.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Regression test for #114728.
|
||||
|
||||
trait Extend<'a, 'b> {
|
||||
fn extend(self, _: &'a str) -> &'b str;
|
||||
}
|
||||
|
||||
impl<'a, 'b> Extend<'a, 'b> for Option<&'b &'a ()> {
|
||||
fn extend(self, s: &'a str) -> &'b str {
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
|
||||
None::<&'_ &'_ ()> //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let y = boom().extend(&String::from("temporary"));
|
||||
println!("{}", y);
|
||||
}
|
14
tests/ui/impl-trait/wf-check-hidden-type.stderr
Normal file
14
tests/ui/impl-trait/wf-check-hidden-type.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/wf-check-hidden-type.rs:14:5
|
||||
|
|
||||
LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
|
||||
| -- -- lifetime `'b` defined here
|
||||
| |
|
||||
| lifetime `'a` defined here
|
||||
LL | None::<&'_ &'_ ()>
|
||||
| ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
||||
|
|
||||
= help: consider adding the following bound: `'a: 'b`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -6,6 +6,7 @@ use std::future::Future;
|
||||
async fn wrapper<F>(f: F)
|
||||
//~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
|
||||
//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
|
||||
//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
|
||||
where
|
||||
F:,
|
||||
for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
|
||||
|
@ -4,6 +4,7 @@ error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
|
||||
LL | / async fn wrapper<F>(f: F)
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | where
|
||||
LL | | F:,
|
||||
LL | | for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
|
||||
@ -20,7 +21,21 @@ LL | async fn wrapper<F>(f: F)
|
||||
= help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
|
||||
|
||||
error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
|
||||
--> $DIR/issue-76168-hr-outlives-3.rs:12:1
|
||||
--> $DIR/issue-76168-hr-outlives-3.rs:6:1
|
||||
|
|
||||
LL | / async fn wrapper<F>(f: F)
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | where
|
||||
LL | | F:,
|
||||
LL | | for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
|
||||
| |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32`
|
||||
|
|
||||
= help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
|
||||
|
||||
error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
|
||||
--> $DIR/issue-76168-hr-outlives-3.rs:13:1
|
||||
|
|
||||
LL | / {
|
||||
LL | |
|
||||
@ -31,6 +46,6 @@ LL | | }
|
||||
|
|
||||
= help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
@ -21,6 +21,20 @@ help: consider adding an explicit lifetime bound
|
||||
LL | for<F> F: 'a, !1_"F": 'a
|
||||
| ~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 1 previous error; 1 warning emitted
|
||||
error[E0309]: the placeholder type `!2_"F"` may not live long enough
|
||||
--> $DIR/type-match-with-late-bound.rs:11:1
|
||||
|
|
||||
LL | async fn walk2<'a, T: 'a>(_: T)
|
||||
| -- the placeholder type `!2_"F"` must be valid for the lifetime `'a` as defined here...
|
||||
...
|
||||
LL | {}
|
||||
| ^^ ...so that the type `F` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | for<F> F: 'a, !2_"F": 'a
|
||||
| ~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 2 previous errors; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0309`.
|
||||
|
@ -2,15 +2,13 @@ error[E0277]: the trait bound `B: Bar` is not satisfied
|
||||
--> $DIR/issue-90400-2.rs:25:9
|
||||
|
|
||||
LL | MyBaz(bar)
|
||||
| ^^^^^^^^^^ the trait `Bar` is not implemented for `B`, which is required by `MyBaz<B>: Baz`
|
||||
| ^^^^^^^^^^ the trait `Bar` is not implemented for `B`
|
||||
|
|
||||
note: required for `MyBaz<B>` to implement `Baz`
|
||||
--> $DIR/issue-90400-2.rs:30:14
|
||||
note: required by a bound in `MyBaz`
|
||||
--> $DIR/issue-90400-2.rs:29:17
|
||||
|
|
||||
LL | impl<B: Bar> Baz for MyBaz<B> {
|
||||
| --- ^^^ ^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
LL | struct MyBaz<B: Bar>(B);
|
||||
| ^^^ required by this bound in `MyBaz`
|
||||
help: consider restricting type parameter `B`
|
||||
|
|
||||
LL | type FooFn<B: Bar> = impl Baz;
|
||||
|
40
tests/ui/type-alias-impl-trait/wf-check-definition-site.rs
Normal file
40
tests/ui/type-alias-impl-trait/wf-check-definition-site.rs
Normal file
@ -0,0 +1,40 @@
|
||||
//@ check-pass
|
||||
|
||||
// Regression test for #114572, We were inferring an ill-formed type:
|
||||
//
|
||||
// `Opaque<'a> = Static<&'a str>`, vs
|
||||
// `Opaque<'a> = Static<&'static str>`.
|
||||
//
|
||||
// The hidden type of the opaque ends up as `Static<'?0 str>`. When
|
||||
// computing member constraints we end up choosing `'a` for `?0` unless
|
||||
// `?0` is already required to outlive `'a`. We achieve this by checking
|
||||
// that `Static<'?0 str>` is well-formed.
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
struct Static<T: 'static>(T);
|
||||
|
||||
type OpaqueRet<'a> = impl Sized + 'a;
|
||||
fn test_return<'a>(msg: Static<&'static u8>) -> OpaqueRet<'a> {
|
||||
msg
|
||||
}
|
||||
|
||||
fn test_rpit<'a>(msg: Static<&'static u8>) -> impl Sized + 'a {
|
||||
msg
|
||||
}
|
||||
|
||||
type OpaqueAssign<'a> = impl Sized + 'a;
|
||||
fn test_assign<'a>(msg: Static<&'static u8>) -> Option<OpaqueAssign<'a>> {
|
||||
let _: OpaqueAssign<'a> = msg;
|
||||
None
|
||||
}
|
||||
|
||||
// `OpaqueRef<'a, T> = Ref<'a, T>`, vs
|
||||
// `OpaqueRef<'a, T> = Ref<'static, T>`.
|
||||
trait RefAt<'a>: 'a {}
|
||||
struct Ref<'a, T: RefAt<'a>>(&'a T);
|
||||
type OpaqueRef<'a, T: RefAt<'static>> = impl Sized + 'a;
|
||||
fn test_trait<'a, T: RefAt<'static>>(msg: Ref<'static, T>) -> OpaqueRef<'a, T> {
|
||||
msg
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,21 +1,16 @@
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:57:27
|
||||
--> $DIR/wf-nested.rs:64:38
|
||||
|
|
||||
LL | type InnerOpaque<T> = impl Sized;
|
||||
| ^^^^^^^^^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds...
|
||||
LL | fn define<T>() -> OuterOpaque<T> {}
|
||||
| ^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
note: ...that is required by this bound
|
||||
--> $DIR/wf-nested.rs:12:20
|
||||
|
|
||||
LL | struct IsStatic<T: 'static>(T);
|
||||
| ^^^^^^^
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | type InnerOpaque<T: 'static> = impl Sized;
|
||||
| +++++++++
|
||||
LL | fn define<T: 'static>() -> OuterOpaque<T> {}
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
31
tests/ui/type-alias-impl-trait/wf-nested.pass.stderr
Normal file
31
tests/ui/type-alias-impl-trait/wf-nested.pass.stderr
Normal file
@ -0,0 +1,31 @@
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:34:38
|
||||
|
|
||||
LL | fn define<T>() -> OuterOpaque<T> {}
|
||||
| ^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn define<T: 'static>() -> OuterOpaque<T> {}
|
||||
| +++++++++
|
||||
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:37:69
|
||||
|
|
||||
LL | fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
|
||||
| ^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn define_rpit<T: 'static>() -> impl Trait<&'static T, Out = impl Sized> {}
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
@ -1,5 +1,19 @@
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:46:17
|
||||
--> $DIR/wf-nested.rs:46:38
|
||||
|
|
||||
LL | fn define<T>() -> OuterOpaque<T> {}
|
||||
| ^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn define<T: 'static>() -> OuterOpaque<T> {}
|
||||
| +++++++++
|
||||
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:51:17
|
||||
|
|
||||
LL | let _ = outer.get();
|
||||
| ^^^^^^^^^^^
|
||||
@ -13,7 +27,7 @@ LL | fn test<T: 'static>() {
|
||||
| +++++++++
|
||||
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:46:17
|
||||
--> $DIR/wf-nested.rs:51:17
|
||||
|
|
||||
LL | let _ = outer.get();
|
||||
| ^^^^^^^^^^^
|
||||
@ -27,6 +41,6 @@ help: consider adding an explicit lifetime bound
|
||||
LL | fn test<T: 'static>() {
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
||||
|
@ -1,12 +1,8 @@
|
||||
// Well-formedness of nested opaque types, i.e. `impl Sized` in
|
||||
// `type Outer = impl Trait<Assoc = impl Sized>`.
|
||||
// See the comments below.
|
||||
//
|
||||
//@ revisions: pass pass_sound fail
|
||||
//@ [pass] check-pass
|
||||
//@ [pass_sound] check-fail
|
||||
//@ [fail] check-fail
|
||||
|
||||
// `type Outer = impl Trait<Assoc = impl Sized>`. We check that
|
||||
// the nested type is well-formed, even though this would also
|
||||
// be implied by the item bounds of the opaque being
|
||||
// well-formed. See the comments below.
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
struct IsStatic<T: 'static>(T);
|
||||
@ -23,40 +19,26 @@ impl<T> Trait<&'static T> for () {
|
||||
type Out = IsStatic<T>;
|
||||
}
|
||||
|
||||
// The hidden type for `impl Sized` is `IsStatic<T>`, which requires `T: 'static`.
|
||||
// We know it is well-formed because it can *only* be referenced as a projection:
|
||||
// <OuterOpaque<T> as Trait<&'static T>>::Out`.
|
||||
// So any instantiation of the type already requires proving `T: 'static`.
|
||||
#[cfg(pass)]
|
||||
mod pass {
|
||||
use super::*;
|
||||
type OuterOpaque<T> = impl Trait<&'static T, Out = impl Sized>;
|
||||
fn define<T>() -> OuterOpaque<T> {}
|
||||
}
|
||||
|
||||
// Test the soundness of `pass` - We should require `T: 'static` at the use site.
|
||||
#[cfg(pass_sound)]
|
||||
mod pass_sound {
|
||||
use super::*;
|
||||
type OuterOpaque<T> = impl Trait<&'static T, Out = impl Sized>;
|
||||
fn define<T>() -> OuterOpaque<T> {}
|
||||
// We could theoretically allow this (and previously did), as even
|
||||
// though the nested opaque is not well-formed, it can only be
|
||||
// used by normalizing the projection
|
||||
// <OuterOpaque1<T> as Trait<&'static T>>::Out
|
||||
// Assuming that we check that this projection is well-formed, the wf
|
||||
// of the nested opaque is implied.
|
||||
type OuterOpaque1<T> = impl Trait<&'static T, Out = impl Sized>;
|
||||
fn define<T>() -> OuterOpaque1<T> {}
|
||||
//~^ ERROR `T` may not live long enough
|
||||
|
||||
fn test<T>() {
|
||||
let outer = define::<T>();
|
||||
let _ = outer.get();
|
||||
//[pass_sound]~^ ERROR `T` may not live long enough
|
||||
//[pass_sound]~| ERROR `T` may not live long enough
|
||||
}
|
||||
}
|
||||
fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
|
||||
// Similar to `pass` but here `impl Sized` can be referenced directly as
|
||||
// InnerOpaque<T>, so we require an explicit bound `T: 'static`.
|
||||
#[cfg(fail)]
|
||||
mod fail {
|
||||
use super::*;
|
||||
type InnerOpaque<T> = impl Sized; //[fail]~ ERROR `T` may not live long enough
|
||||
type OuterOpaque<T> = impl Trait<&'static T, Out = InnerOpaque<T>>;
|
||||
fn define<T>() -> OuterOpaque<T> {}
|
||||
}
|
||||
// Similar to `define` but here `impl Sized` can be referenced directly as
|
||||
// InnerOpaque<T>, so the `'static` bound is definitely required for
|
||||
// soundness.
|
||||
type InnerOpaque<T> = impl Sized;
|
||||
type OuterOpaque2<T> = impl Trait<&'static T, Out = InnerOpaque<T>>;
|
||||
fn define_nested_rpit<T>() -> OuterOpaque2<T> {}
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
|
||||
fn main() {}
|
||||
|
45
tests/ui/type-alias-impl-trait/wf-nested.stderr
Normal file
45
tests/ui/type-alias-impl-trait/wf-nested.stderr
Normal file
@ -0,0 +1,45 @@
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:30:35
|
||||
|
|
||||
LL | fn define<T>() -> OuterOpaque1<T> {}
|
||||
| ^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn define<T: 'static>() -> OuterOpaque1<T> {}
|
||||
| +++++++++
|
||||
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:33:65
|
||||
|
|
||||
LL | fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
|
||||
| ^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn define_rpit<T: 'static>() -> impl Trait<&'static T, Out = impl Sized> {}
|
||||
| +++++++++
|
||||
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:41:47
|
||||
|
|
||||
LL | fn define_nested_rpit<T>() -> OuterOpaque2<T> {}
|
||||
| ^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn define_nested_rpit<T: 'static>() -> OuterOpaque2<T> {}
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
Loading…
Reference in New Issue
Block a user