Skip to content

Commit d548747

Browse files
committed
use implied bounds when checking opaque types
1 parent 4a18324 commit d548747

File tree

7 files changed

+189
-4
lines changed

7 files changed

+189
-4
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use rustc_target::abi::FieldIdx;
3131
use rustc_target::spec::abi::Abi;
3232
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
3333
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
34+
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
3435
use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _};
3536

3637
use std::ops::ControlFlow;
@@ -222,7 +223,7 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
222223
if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() {
223224
return;
224225
}
225-
check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin);
226+
check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
226227
}
227228

228229
/// 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>(
391392
fn check_opaque_meets_bounds<'tcx>(
392393
tcx: TyCtxt<'tcx>,
393394
def_id: LocalDefId,
394-
substs: SubstsRef<'tcx>,
395395
span: Span,
396396
origin: &hir::OpaqueTyOrigin,
397397
) {
@@ -406,6 +406,8 @@ fn check_opaque_meets_bounds<'tcx>(
406406
.with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor))
407407
.build();
408408
let ocx = ObligationCtxt::new(&infcx);
409+
410+
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
409411
let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
410412

411413
// `ReErased` regions appear in the "parent_substs" of closures/generators.
@@ -448,9 +450,18 @@ fn check_opaque_meets_bounds<'tcx>(
448450
match origin {
449451
// Checked when type checking the function containing them.
450452
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
453+
// Nested opaque types occur only in associated types:
454+
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
455+
// They can only be referenced as `<Opaque<T> as Trait<&'static T>>::AssocTy`.
456+
// We don't have to check them here because their well-formedness follows from the WF of
457+
// the projection input types in the defining- and use-sites.
458+
hir::OpaqueTyOrigin::TyAlias
459+
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
451460
// Can have different predicates to their defining use
452461
hir::OpaqueTyOrigin::TyAlias => {
453-
let outlives_env = OutlivesEnvironment::new(param_env);
462+
let wf_tys = ocx.assumed_wf_types(param_env, span, def_id);
463+
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
464+
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
454465
let _ = ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env);
455466
}
456467
}

compiler/rustc_ty_utils/src/implied_bounds.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
3131
}
3232
}
3333
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
34+
DefKind::OpaqueTy => match tcx.def_kind(tcx.parent(def_id)) {
35+
DefKind::TyAlias => ty::List::empty(),
36+
DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
37+
// Nested opaque types only occur in associated types:
38+
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
39+
// assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
40+
// and `&'static T`.
41+
DefKind::OpaqueTy => bug!("unimplemented implied bounds for neseted opaque types"),
42+
def_kind @ _ => {
43+
bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
44+
}
45+
},
3446
DefKind::Mod
3547
| DefKind::Struct
3648
| DefKind::Union
@@ -51,7 +63,6 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
5163
| DefKind::ForeignMod
5264
| DefKind::AnonConst
5365
| DefKind::InlineConst
54-
| DefKind::OpaqueTy
5566
| DefKind::ImplTraitPlaceholder
5667
| DefKind::Field
5768
| DefKind::LifetimeParam
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0309]: the parameter type `T` may not live long enough
2+
--> $DIR/wf-in-associated-type.rs:36:23
3+
|
4+
LL | type Opaque = impl Sized + 'a;
5+
| ^^^^^^^^^^^^^^^ ...so that the type `&'a T` will meet its required lifetime bounds
6+
|
7+
help: consider adding an explicit lifetime bound...
8+
|
9+
LL | impl<'a, T: 'a> Trait<'a, T> for () {
10+
| ++++
11+
12+
error[E0309]: the parameter type `T` may not live long enough
13+
--> $DIR/wf-in-associated-type.rs:36:23
14+
|
15+
LL | type Opaque = impl Sized + 'a;
16+
| ^^^^^^^^^^^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at
17+
|
18+
help: consider adding an explicit lifetime bound...
19+
|
20+
LL | impl<'a, T: 'a> Trait<'a, T> for () {
21+
| ++++
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0309`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// WF check for impl Trait in associated type position.
2+
//
3+
// revisions: pass fail
4+
// [pass] check-pass
5+
// [fail] check-fail
6+
7+
#![feature(impl_trait_in_assoc_type)]
8+
9+
// The hidden type here (`&'a T`) requires proving `T: 'a`.
10+
// We know it holds because of implied bounds from the impl header.
11+
#[cfg(pass)]
12+
mod pass {
13+
trait Trait<Req> {
14+
type Opaque1;
15+
fn constrain_opaque1(req: Req) -> Self::Opaque1;
16+
}
17+
18+
impl<'a, T> Trait<&'a T> for () {
19+
type Opaque1 = impl IntoIterator<Item = impl Sized + 'a>;
20+
fn constrain_opaque1(req: &'a T) -> Self::Opaque1 {
21+
[req]
22+
}
23+
}
24+
}
25+
26+
// The hidden type here (`&'a T`) requires proving `T: 'a`,
27+
// but that is not known to hold in the impl.
28+
#[cfg(fail)]
29+
mod fail {
30+
trait Trait<'a, T> {
31+
type Opaque;
32+
fn constrain_opaque(req: &'a T) -> Self::Opaque;
33+
}
34+
35+
impl<'a, T> Trait<'a, T> for () {
36+
type Opaque = impl Sized + 'a;
37+
//[fail]~^ ERROR the parameter type `T` may not live long enough
38+
//[fail]~| ERROR the parameter type `T` may not live long enough
39+
fn constrain_opaque(req: &'a T) -> Self::Opaque {
40+
req
41+
}
42+
}
43+
}
44+
45+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0310]: the parameter type `T` may not live long enough
2+
--> $DIR/wf-nested.rs:55:27
3+
|
4+
LL | type InnerOpaque<T> = impl Sized;
5+
| ^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
6+
|
7+
note: ...that is required by this bound
8+
--> $DIR/wf-nested.rs:12:20
9+
|
10+
LL | struct IsStatic<T: 'static>(T);
11+
| ^^^^^^^
12+
help: consider adding an explicit lifetime bound...
13+
|
14+
LL | type InnerOpaque<T: 'static> = impl Sized;
15+
| +++++++++
16+
17+
error: aborting due to previous error
18+
19+
For more information about this error, try `rustc --explain E0310`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0310]: the parameter type `T` may not live long enough
2+
--> $DIR/wf-nested.rs:46:17
3+
|
4+
LL | let _ = outer.get();
5+
| ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
6+
|
7+
help: consider adding an explicit lifetime bound...
8+
|
9+
LL | fn test<T: 'static>() {
10+
| +++++++++
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0310`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Well-formedness of nested opaque types, i.e. `impl Sized` in
2+
// `type Outer = impl Trait<Assoc = impl Sized>`.
3+
// See the comments below.
4+
//
5+
// revisions: pass pass_sound fail
6+
// [pass] check-pass
7+
// [pass_sound] check-fail
8+
// [fail] check-fail
9+
10+
#![feature(type_alias_impl_trait)]
11+
12+
struct IsStatic<T: 'static>(T);
13+
14+
trait Trait<In> {
15+
type Out;
16+
17+
fn get(&self) -> Result<Self::Out, ()> {
18+
Err(())
19+
}
20+
}
21+
22+
impl<T> Trait<&'static T> for () {
23+
type Out = IsStatic<T>;
24+
}
25+
26+
// The hidden type for `impl Sized` is `IsStatic<T>`, which requires `T: 'static`.
27+
// We know it is well-formed because it can *only* be referenced as a projection:
28+
// <OuterOpaque<T> as Trait<&'static T>>::Out`.
29+
// So any instantiation of the type already requires proving `T: 'static`.
30+
#[cfg(pass)]
31+
mod pass {
32+
use super::*;
33+
type OuterOpaque<T> = impl Trait<&'static T, Out = impl Sized>;
34+
fn define<T>() -> OuterOpaque<T> {}
35+
}
36+
37+
// Test the soundness of `pass` - We should require `T: 'static` at the use site.
38+
#[cfg(pass_sound)]
39+
mod pass_sound {
40+
use super::*;
41+
type OuterOpaque<T> = impl Trait<&'static T, Out = impl Sized>;
42+
fn define<T>() -> OuterOpaque<T> {}
43+
44+
fn test<T>() {
45+
let outer = define::<T>();
46+
let _ = outer.get(); //[pass_sound]~ ERROR `T` may not live long enough
47+
}
48+
}
49+
50+
// Similar to `pass` but here `impl Sized` can be referenced directly as
51+
// InnerOpaque<T>, so we require an explicit bound `T: 'static`.
52+
#[cfg(fail)]
53+
mod fail {
54+
use super::*;
55+
type InnerOpaque<T> = impl Sized; //[fail]~ ERROR `T` may not live long enough
56+
type OuterOpaque<T> = impl Trait<&'static T, Out = InnerOpaque<T>>;
57+
fn define<T>() -> OuterOpaque<T> {}
58+
}
59+
60+
fn main() {}

0 commit comments

Comments
 (0)