use implied bounds when checking opaque types
This commit is contained in:
parent
4a18324a4d
commit
d548747c85
@ -31,6 +31,7 @@ use rustc_target::abi::FieldIdx;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _};
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
@ -222,7 +223,7 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
|
||||
if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() {
|
||||
return;
|
||||
}
|
||||
check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin);
|
||||
check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
|
||||
}
|
||||
|
||||
/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
|
||||
@ -391,7 +392,6 @@ pub(super) fn check_opaque_for_cycles<'tcx>(
|
||||
fn check_opaque_meets_bounds<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
substs: SubstsRef<'tcx>,
|
||||
span: Span,
|
||||
origin: &hir::OpaqueTyOrigin,
|
||||
) {
|
||||
@ -406,6 +406,8 @@ fn check_opaque_meets_bounds<'tcx>(
|
||||
.with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor))
|
||||
.build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
|
||||
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
|
||||
let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
|
||||
|
||||
// `ReErased` regions appear in the "parent_substs" of closures/generators.
|
||||
@ -448,9 +450,18 @@ fn check_opaque_meets_bounds<'tcx>(
|
||||
match origin {
|
||||
// Checked when type checking the function containing them.
|
||||
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
|
||||
// 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 outlives_env = OutlivesEnvironment::new(param_env);
|
||||
let wf_tys = ocx.assumed_wf_types(param_env, span, 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);
|
||||
let _ = ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,18 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
|
||||
}
|
||||
}
|
||||
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
|
||||
DefKind::OpaqueTy => match tcx.def_kind(tcx.parent(def_id)) {
|
||||
DefKind::TyAlias => ty::List::empty(),
|
||||
DefKind::AssocTy => tcx.assumed_wf_types(tcx.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 neseted opaque types"),
|
||||
def_kind @ _ => {
|
||||
bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
|
||||
}
|
||||
},
|
||||
DefKind::Mod
|
||||
| DefKind::Struct
|
||||
| DefKind::Union
|
||||
@ -51,7 +63,6 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
|
||||
| DefKind::ForeignMod
|
||||
| DefKind::AnonConst
|
||||
| DefKind::InlineConst
|
||||
| DefKind::OpaqueTy
|
||||
| DefKind::ImplTraitPlaceholder
|
||||
| DefKind::Field
|
||||
| DefKind::LifetimeParam
|
||||
|
@ -0,0 +1,25 @@
|
||||
error[E0309]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-in-associated-type.rs:36:23
|
||||
|
|
||||
LL | type Opaque = impl Sized + 'a;
|
||||
| ^^^^^^^^^^^^^^^ ...so that the type `&'a T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
LL | impl<'a, T: 'a> Trait<'a, T> for () {
|
||||
| ++++
|
||||
|
||||
error[E0309]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-in-associated-type.rs:36:23
|
||||
|
|
||||
LL | type Opaque = impl Sized + 'a;
|
||||
| ^^^^^^^^^^^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at
|
||||
|
|
||||
help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
LL | impl<'a, T: 'a> Trait<'a, T> for () {
|
||||
| ++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0309`.
|
45
tests/ui/type-alias-impl-trait/wf-in-associated-type.rs
Normal file
45
tests/ui/type-alias-impl-trait/wf-in-associated-type.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// WF check for impl Trait in associated type position.
|
||||
//
|
||||
// revisions: pass fail
|
||||
// [pass] check-pass
|
||||
// [fail] check-fail
|
||||
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
|
||||
// The hidden type here (`&'a T`) requires proving `T: 'a`.
|
||||
// We know it holds because of implied bounds from the impl header.
|
||||
#[cfg(pass)]
|
||||
mod pass {
|
||||
trait Trait<Req> {
|
||||
type Opaque1;
|
||||
fn constrain_opaque1(req: Req) -> Self::Opaque1;
|
||||
}
|
||||
|
||||
impl<'a, T> Trait<&'a T> for () {
|
||||
type Opaque1 = impl IntoIterator<Item = impl Sized + 'a>;
|
||||
fn constrain_opaque1(req: &'a T) -> Self::Opaque1 {
|
||||
[req]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The hidden type here (`&'a T`) requires proving `T: 'a`,
|
||||
// but that is not known to hold in the impl.
|
||||
#[cfg(fail)]
|
||||
mod fail {
|
||||
trait Trait<'a, T> {
|
||||
type Opaque;
|
||||
fn constrain_opaque(req: &'a T) -> Self::Opaque;
|
||||
}
|
||||
|
||||
impl<'a, T> Trait<'a, T> for () {
|
||||
type Opaque = impl Sized + 'a;
|
||||
//[fail]~^ ERROR the parameter type `T` may not live long enough
|
||||
//[fail]~| ERROR the parameter type `T` may not live long enough
|
||||
fn constrain_opaque(req: &'a T) -> Self::Opaque {
|
||||
req
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
19
tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
Normal file
19
tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:55:27
|
||||
|
|
||||
LL | type InnerOpaque<T> = impl Sized;
|
||||
| ^^^^^^^^^^ ...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;
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
14
tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
Normal file
14
tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/wf-nested.rs:46:17
|
||||
|
|
||||
LL | let _ = outer.get();
|
||||
| ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
LL | fn test<T: 'static>() {
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
60
tests/ui/type-alias-impl-trait/wf-nested.rs
Normal file
60
tests/ui/type-alias-impl-trait/wf-nested.rs
Normal file
@ -0,0 +1,60 @@
|
||||
// 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
|
||||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
struct IsStatic<T: 'static>(T);
|
||||
|
||||
trait Trait<In> {
|
||||
type Out;
|
||||
|
||||
fn get(&self) -> Result<Self::Out, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
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> {}
|
||||
|
||||
fn test<T>() {
|
||||
let outer = define::<T>();
|
||||
let _ = outer.get(); //[pass_sound]~ ERROR `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> {}
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user