Auto merge of #101679 - compiler-errors:rpitit-default-body, r=nikomatsakis

Support default-body trait functions with return-position `impl Trait` in traits

Introduce a new `Trait` candidate kind for the `ImplTraitInTrait` projection candidate, which just projects an RPITIT down to its opaque type form.

This is a hack until we lower RPITITs to regular associated types, after which we will need to rework how these default bodies are type-checked, so comments are left in a few places for us to clean up later.

Fixes #101665
This commit is contained in:
bors 2022-10-12 21:03:47 +00:00
commit 0938e1680d
14 changed files with 166 additions and 129 deletions

View File

@ -522,23 +522,33 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
/// projections that would result in "inheriting lifetimes".
pub(super) fn check_opaque<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
substs: SubstsRef<'tcx>,
origin: &hir::OpaqueTyOrigin,
) {
let span = tcx.def_span(def_id);
check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
if tcx.type_of(def_id).references_error() {
fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
let item = tcx.hir().item(id);
let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item");
return;
}
if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() {
return;
}
check_opaque_meets_bounds(tcx, def_id, substs, span, origin);
}
};
// HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
// `async-std` (and `pub async fn` in general).
// Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
// See https://github.com/rust-lang/rust/issues/75100
if tcx.sess.opts.actually_rustdoc {
return;
}
let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id());
let span = tcx.def_span(item.def_id.def_id);
check_opaque_for_inheriting_lifetimes(tcx, item.def_id.def_id, span);
if tcx.type_of(item.def_id.def_id).references_error() {
return;
}
if check_opaque_for_cycles(tcx, item.def_id.def_id, substs, span, &origin).is_err() {
return;
}
check_opaque_meets_bounds(tcx, item.def_id.def_id, substs, span, &origin);
}
/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
/// in "inheriting lifetimes".
#[instrument(level = "debug", skip(tcx, span))]
@ -857,17 +867,17 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
check_union(tcx, id.def_id.def_id);
}
DefKind::OpaqueTy => {
let item = tcx.hir().item(id);
let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
return;
};
// HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
// `async-std` (and `pub async fn` in general).
// Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
// See https://github.com/rust-lang/rust/issues/75100
if !tcx.sess.opts.actually_rustdoc {
let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id());
check_opaque(tcx, item.def_id.def_id, substs, &origin);
check_opaque(tcx, id);
}
DefKind::ImplTraitPlaceholder => {
let parent = tcx.impl_trait_in_trait_parent(id.def_id.to_def_id());
// Only check the validity of this opaque type if the function has a default body
if let hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
..
}) = tcx.hir().get_by_def_id(parent.expect_local())
{
check_opaque(tcx, id);
}
}
DefKind::TyAlias => {

View File

@ -340,10 +340,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
..
}) => {
if in_trait {
span_bug!(item.span, "impl-trait in trait has no default")
} else {
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
assert!(tcx.impl_defaultness(owner).has_value());
}
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
}
ItemKind::Trait(..)
| ItemKind::TraitAlias(..)

View File

@ -1161,6 +1161,7 @@ impl<'tcx> ProjectionTy<'tcx> {
tcx: TyCtxt<'tcx>,
) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
let def_id = tcx.parent(self.item_def_id);
assert_eq!(tcx.def_kind(def_id), DefKind::Trait);
let trait_generics = tcx.generics_of(def_id);
(
ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, trait_generics) },

View File

@ -122,8 +122,20 @@ where
&mut self,
projection: ty::ProjectionTy<'tcx>,
) -> ControlFlow<V::BreakTy> {
let (trait_ref, assoc_substs) =
projection.trait_ref_and_own_substs(self.def_id_visitor.tcx());
let tcx = self.def_id_visitor.tcx();
let (trait_ref, assoc_substs) = if tcx.def_kind(projection.item_def_id)
!= DefKind::ImplTraitPlaceholder
{
projection.trait_ref_and_own_substs(tcx)
} else {
// HACK(RPITIT): Remove this when RPITITs are lowered to regular assoc tys
let def_id = tcx.impl_trait_in_trait_parent(projection.item_def_id);
let trait_generics = tcx.generics_of(def_id);
(
ty::TraitRef { def_id, substs: projection.substs.truncate_to(tcx, trait_generics) },
&projection.substs[trait_generics.count()..],
)
};
self.visit_trait(trait_ref)?;
if self.def_id_visitor.shallow() {
ControlFlow::CONTINUE

View File

@ -72,7 +72,15 @@ enum ProjectionCandidate<'tcx> {
/// From an "impl" (or a "pseudo-impl" returned by select)
Select(Selection<'tcx>),
ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
ImplTraitInTrait(ImplTraitInTraitCandidate<'tcx>),
}
#[derive(PartialEq, Eq, Debug)]
enum ImplTraitInTraitCandidate<'tcx> {
// The `impl Trait` from a trait function's default body
Trait,
// A concrete type provided from a trait's `impl Trait` from an impl
Impl(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
}
enum ProjectionCandidateSet<'tcx> {
@ -1319,6 +1327,19 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
let tcx = selcx.tcx();
if tcx.def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder {
let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id);
// If we are trying to project an RPITIT with trait's default `Self` parameter,
// then we must be within a default trait body.
if obligation.predicate.self_ty()
== ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.item_def_id)
.type_at(0)
&& tcx.associated_item(trait_fn_def_id).defaultness(tcx).has_value()
{
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
ImplTraitInTraitCandidate::Trait,
));
return;
}
let trait_def_id = tcx.parent(trait_fn_def_id);
let trait_substs =
obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id));
@ -1330,7 +1351,9 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
let _ =
selcx.infcx().commit_if_ok(|_| match selcx.select(&obligation.with(trait_predicate)) {
Ok(Some(super::ImplSource::UserDefined(data))) => {
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
ImplTraitInTraitCandidate::Impl(data),
));
Ok(())
}
Ok(None) => {
@ -1792,9 +1815,18 @@ fn confirm_candidate<'cx, 'tcx>(
ProjectionCandidate::Select(impl_source) => {
confirm_select_candidate(selcx, obligation, impl_source)
}
ProjectionCandidate::ImplTraitInTrait(data) => {
ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => {
confirm_impl_trait_in_trait_candidate(selcx, obligation, data)
}
// If we're projecting an RPITIT for a default trait body, that's just
// the same def-id, but as an opaque type (with regular RPIT semantics).
ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Trait) => Progress {
term: selcx
.tcx()
.mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs)
.into(),
obligations: vec![],
},
};
// When checking for cycle during evaluation, we compare predicates with
@ -2211,6 +2243,16 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
return Progress { term: tcx.ty_error().into(), obligations };
}
// Use the default `impl Trait` for the trait, e.g., for a default trait body
if leaf_def.item.container == ty::AssocItemContainer::TraitContainer {
return Progress {
term: tcx
.mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs)
.into(),
obligations,
};
}
let impl_fn_def_id = leaf_def.item.def_id;
let impl_fn_substs = obligation.predicate.substs.rebase_onto(tcx, trait_fn_def_id, data.substs);

View File

@ -1,11 +1,8 @@
// edition:2018
trait T {
async fn foo() {} //~ ERROR functions in traits cannot be declared `async`
//~^ ERROR mismatched types
async fn bar(&self) {} //~ ERROR functions in traits cannot be declared `async`
//~^ ERROR mismatched types
async fn baz() { //~ ERROR functions in traits cannot be declared `async`
//~^ ERROR mismatched types
// Nested item must not ICE.
fn a() {}
}

View File

@ -12,7 +12,7 @@ LL | async fn foo() {}
= help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable
error[E0706]: functions in traits cannot be declared `async`
--> $DIR/async-trait-fn.rs:5:5
--> $DIR/async-trait-fn.rs:4:5
|
LL | async fn bar(&self) {}
| -----^^^^^^^^^^^^^^
@ -25,7 +25,7 @@ LL | async fn bar(&self) {}
= help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable
error[E0706]: functions in traits cannot be declared `async`
--> $DIR/async-trait-fn.rs:7:5
--> $DIR/async-trait-fn.rs:5:5
|
LL | async fn baz() {
| -----^^^^^^^^^
@ -37,54 +37,6 @@ LL | async fn baz() {
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable
error[E0308]: mismatched types
--> $DIR/async-trait-fn.rs:3:20
|
LL | async fn foo() {}
| ^^ expected associated type, found opaque type
|
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected associated type `impl Future<Output = ()>` (trait associated opaque type at <$DIR/async-trait-fn.rs:3:20>)
found opaque type `impl Future<Output = ()>` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>)
error: aborting due to 3 previous errors
error[E0308]: mismatched types
--> $DIR/async-trait-fn.rs:5:25
|
LL | async fn bar(&self) {}
| ^^ expected associated type, found opaque type
|
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected associated type `impl Future<Output = ()>` (trait associated opaque type at <$DIR/async-trait-fn.rs:5:25>)
found opaque type `impl Future<Output = ()>` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>)
error[E0308]: mismatched types
--> $DIR/async-trait-fn.rs:7:20
|
LL | async fn baz() {
| ____________________^
LL | |
LL | | // Nested item must not ICE.
LL | | fn a() {}
LL | | }
| |_____^ expected associated type, found opaque type
|
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected associated type `impl Future<Output = ()>` (trait associated opaque type at <$DIR/async-trait-fn.rs:7:20>)
found opaque type `impl Future<Output = ()>` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>)
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0308, E0706.
For more information about an error, try `rustc --explain E0308`.
For more information about this error, try `rustc --explain E0706`.

View File

@ -17,7 +17,6 @@ impl Foo {
trait Bar {
async fn foo() {} //~ ERROR `async fn` is not permitted in Rust 2015
//~^ ERROR functions in traits cannot be declared `async`
//~| ERROR mismatched types
}
fn main() {

View File

@ -53,7 +53,7 @@ LL | async fn foo() {}
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
error[E0670]: `async fn` is not permitted in Rust 2015
--> $DIR/edition-deny-async-fns-2015.rs:37:9
--> $DIR/edition-deny-async-fns-2015.rs:36:9
|
LL | async fn bar() {}
| ^^^^^ to use `async fn`, switch to Rust 2018 or later
@ -62,7 +62,7 @@ LL | async fn bar() {}
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
error[E0670]: `async fn` is not permitted in Rust 2015
--> $DIR/edition-deny-async-fns-2015.rs:27:9
--> $DIR/edition-deny-async-fns-2015.rs:26:9
|
LL | async fn foo() {}
| ^^^^^ to use `async fn`, switch to Rust 2018 or later
@ -71,7 +71,7 @@ LL | async fn foo() {}
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
error[E0670]: `async fn` is not permitted in Rust 2015
--> $DIR/edition-deny-async-fns-2015.rs:32:13
--> $DIR/edition-deny-async-fns-2015.rs:31:13
|
LL | async fn bar() {}
| ^^^^^ to use `async fn`, switch to Rust 2018 or later
@ -92,21 +92,7 @@ LL | async fn foo() {}
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable
error[E0308]: mismatched types
--> $DIR/edition-deny-async-fns-2015.rs:18:20
|
LL | async fn foo() {}
| ^^ expected associated type, found opaque type
|
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected associated type `impl Future<Output = ()>` (trait associated opaque type at <$DIR/edition-deny-async-fns-2015.rs:18:20>)
found opaque type `impl Future<Output = ()>` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>)
error: aborting due to 10 previous errors
error: aborting due to 11 previous errors
Some errors have detailed explanations: E0308, E0670, E0706.
For more information about an error, try `rustc --explain E0308`.
Some errors have detailed explanations: E0670, E0706.
For more information about an error, try `rustc --explain E0670`.

View File

@ -0,0 +1,21 @@
// known-bug: #102688
// edition:2021
#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)]
#![allow(incomplete_features)]
use std::fmt::Debug;
trait Foo {
async fn baz(&self) -> impl Debug {
""
}
}
struct Bar;
impl Foo for Bar {}
fn main() {
let _ = Bar.baz();
}

View File

@ -0,0 +1,12 @@
error[E0720]: cannot resolve opaque type
--> $DIR/default-body-with-rpit.rs:10:28
|
LL | async fn baz(&self) -> impl Debug {
| ^^^^^^^^^^ cannot resolve opaque type
|
= note: these returned values have a concrete "never" type
= help: this error will resolve once the item's body returns a concrete type
error: aborting due to previous error
For more information about this error, try `rustc --explain E0720`.

View File

@ -0,0 +1,21 @@
// check-pass
// edition:2021
#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)]
#![allow(incomplete_features)]
use std::fmt::Debug;
trait Foo {
async fn baz(&self) -> &str {
""
}
}
struct Bar;
impl Foo for Bar {}
fn main() {
let _ = Bar.baz();
}

View File

@ -12,7 +12,6 @@ impl A {
trait C{async fn new(val: T) {} //~ ERROR `async fn` is not permitted in Rust 2015
//~^ ERROR functions in traits cannot be declared `async`
//~| ERROR mismatched types
//~| ERROR cannot find type `T` in this scope
//~| WARN changes to closure capture in Rust 2021 will affect drop order [rust_2021_incompatible_closure_captures]

View File

@ -1,5 +1,5 @@
error: this file contains an unclosed delimiter
--> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:19:53
--> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:18:53
|
LL | trait C{async fn new(val: T) {}
| - unclosed delimiter
@ -74,20 +74,6 @@ help: add a dummy let to cause `path` to be fully captured
LL | async fn create(path: impl AsRef<std::path::Path>) { let _ = &path;
| ++++++++++++++
error[E0308]: mismatched types
--> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:30
|
LL | trait C{async fn new(val: T) {}
| ^^ expected associated type, found opaque type
|
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected associated type `impl Future<Output = ()>` (trait associated opaque type at <$DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:30>)
found opaque type `impl Future<Output = ()>` (opaque type at <$SRC_DIR/core/src/future/mod.rs:LL:COL>)
warning: changes to closure capture in Rust 2021 will affect drop order
--> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:30
|
@ -103,7 +89,7 @@ help: add a dummy let to cause `val` to be fully captured
LL | trait C{async fn new(val: T) { let _ = &val;}
| +++++++++++++
error: aborting due to 7 previous errors; 2 warnings emitted
error: aborting due to 6 previous errors; 2 warnings emitted
Some errors have detailed explanations: E0308, E0412, E0423, E0670, E0706.
For more information about an error, try `rustc --explain E0308`.
Some errors have detailed explanations: E0412, E0423, E0670, E0706.
For more information about an error, try `rustc --explain E0412`.