Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 33 additions & 23 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,23 +525,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;
};

// 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;
}
if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() {

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, def_id, substs, span, origin);
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))]
Expand Down Expand Up @@ -858,17 +868,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 => {
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) },
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_privacy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 45 additions & 3 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,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> {
Expand Down Expand Up @@ -1318,6 +1326,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));
Expand All @@ -1329,7 +1350,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) => {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2212,6 +2244,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);

Expand Down
3 changes: 0 additions & 3 deletions src/test/ui/async-await/async-trait-fn.rs
Original file line number Diff line number Diff line change
@@ -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() {}
}
Expand Down
56 changes: 4 additions & 52 deletions src/test/ui/async-await/async-trait-fn.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
| -----^^^^^^^^^^^^^^
Expand All @@ -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() {
| -----^^^^^^^^^
Expand All @@ -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[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
error: aborting due to 3 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`.
1 change: 0 additions & 1 deletion src/test/ui/async-await/edition-deny-async-fns-2015.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
26 changes: 6 additions & 20 deletions src/test/ui/async-await/edition-deny-async-fns-2015.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 11 previous errors
error: aborting due to 10 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`.
21 changes: 21 additions & 0 deletions src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs
Original file line number Diff line number Diff line change
@@ -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();
}
12 changes: 12 additions & 0 deletions src/test/ui/impl-trait/in-trait/default-body-with-rpit.stderr
Original file line number Diff line number Diff line change
@@ -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`.
Loading