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

Remove support for defaulted trait methods with async or return-position impl Trait #108142

Closed
Closed
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
7 changes: 7 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,10 @@ hir_analysis_linkage_type =
hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}`
.label = deref recursion limit reached
.help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)

hir_analysis_default_async_fn_not_allowed = default body not allowed on async trait method
.remove_label = remove this body, or move it to an impl instead

hir_analysis_default_rpitit_fn_not_allowed = default body not allowed on trait method with `impl Trait`
.label = this `impl Trait`
.remove_label = remove this body, or move it to an impl instead
58 changes: 58 additions & 0 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::autoderef::Autoderef;
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
use crate::errors;

use hir::def::DefKind;
use rustc_ast as ast;
Expand All @@ -15,6 +16,7 @@ use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{
self, ir::TypeVisitor, AdtKind, DefIdTree, GenericParamDefKind, Ty, TyCtxt, TypeFoldable,
TypeSuperVisitable,
Expand Down Expand Up @@ -278,6 +280,62 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
check_object_unsafe_self_trait_by_name(tcx, trait_item);
check_associated_item(tcx, def_id, span, method_sig);

struct FindImplTraitInTrait<'tcx>(TyCtxt<'tcx>);
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindImplTraitInTrait<'tcx> {
type BreakTy = DefId;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::Alias(ty::Projection, alias_ty) = *ty.kind()
&& self.0.def_kind(alias_ty.def_id) == DefKind::ImplTraitPlaceholder
{
return ControlFlow::Break(alias_ty.def_id);
} else if ty.has_projections() {
ty.super_visit_with(self)
} else {
ControlFlow::Continue(())
}
}
}
if let hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) = trait_item.kind
&& let ControlFlow::Break(def_id) = tcx
.fn_sig(def_id)
.skip_binder()
.output()
.visit_with(&mut FindImplTraitInTrait(tcx))
{
let hir::ItemKind::OpaqueTy(opaque) = &tcx.hir().expect_item(def_id.expect_local()).kind else {
bug!();
};
match opaque.origin {
hir::OpaqueTyOrigin::FnReturn(_) => {
let mut diag = tcx.sess.create_err(
errors::DefaultRpititMethodNotAllowed::ReturnPositionImplTrait {
body_span: tcx.hir().span(body_id.hir_id).shrink_to_lo(),
rpitit_span: tcx.def_span(def_id),
},
);
if tcx.features().return_position_impl_trait_in_trait {
diag.emit();
} else {
diag.delay_as_bug();
}
}
hir::OpaqueTyOrigin::AsyncFn(_) => {
let mut diag =
tcx.sess.create_err(errors::DefaultRpititMethodNotAllowed::AsyncFn {
body_span: tcx.hir().span(body_id.hir_id).shrink_to_lo(),
});
if tcx.features().async_fn_in_trait {
diag.emit();
} else {
diag.delay_as_bug();
}
}
hir::OpaqueTyOrigin::TyAlias => {
// TAIT comes from alias expansion, so it's fine.
}
}
}

let encl_trait_def_id = tcx.local_parent(def_id);
let encl_trait = tcx.hir().expect_item(encl_trait_def_id);
let encl_trait_def_id = encl_trait.owner_id.to_def_id();
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,21 @@ pub struct AutoDerefReachedRecursionLimit<'a> {
pub suggested_limit: rustc_session::Limit,
pub crate_name: Symbol,
}

#[derive(Diagnostic)]
pub enum DefaultRpititMethodNotAllowed {
#[diag(hir_analysis_default_async_fn_not_allowed)]
AsyncFn {
#[primary_span]
#[label(remove_label)]
body_span: Span,
},
#[diag(hir_analysis_default_rpitit_fn_not_allowed)]
ReturnPositionImplTrait {
#[primary_span]
#[label(remove_label)]
body_span: Span,
#[label]
rpitit_span: Span,
},
}
36 changes: 3 additions & 33 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,7 @@ enum ProjectionCandidate<'tcx> {
/// From an "impl" (or a "pseudo-impl" returned by select)
Select(Selection<'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>>),
ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
}

enum ProjectionCandidateSet<'tcx> {
Expand Down Expand Up @@ -1292,17 +1284,6 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
let tcx = selcx.tcx();
if tcx.def_kind(obligation.predicate.def_id) == DefKind::ImplTraitPlaceholder {
let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.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.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 =
Expand All @@ -1313,9 +1294,7 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
let _ = selcx.infcx.commit_if_ok(|_| {
match selcx.select(&obligation.with(tcx, trait_predicate)) {
Ok(Some(super::ImplSource::UserDefined(data))) => {
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
ImplTraitInTraitCandidate::Impl(data),
));
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
Ok(())
}
Ok(None) => {
Expand Down Expand Up @@ -1777,18 +1756,9 @@ fn confirm_candidate<'cx, 'tcx>(
ProjectionCandidate::Select(impl_source) => {
confirm_select_candidate(selcx, obligation, impl_source)
}
ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => {
ProjectionCandidate::ImplTraitInTrait(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.def_id, obligation.predicate.substs)
.into(),
obligations: vec![],
},
};

// When checking for cycle during evaluation, we compare predicates with
Expand Down
3 changes: 3 additions & 0 deletions tests/ui/async-await/async-trait-fn.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// 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: 52 additions & 4 deletions tests/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:4:5
--> $DIR/async-trait-fn.rs:5: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:5:5
--> $DIR/async-trait-fn.rs:7:5
|
LL | async fn baz() {
| -----^^^^^^^^^
Expand All @@ -37,6 +37,54 @@ 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: aborting due to 3 previous errors
error[E0308]: mismatched types
--> $DIR/async-trait-fn.rs:3:20
|
LL | async fn foo() {}
| ^^
| |
| expected associated type, found `async fn` body
| arguments to this function are incorrect
|
= note: expected associated type `impl Future<Output = ()>`
found `async fn` body `[async fn body@$DIR/async-trait-fn.rs:3:20: 3:22]`
note: function defined here
--> $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 `async fn` body
| arguments to this function are incorrect
|
= note: expected associated type `impl Future<Output = ()>`
found `async fn` body `[async fn body@$DIR/async-trait-fn.rs:5:25: 5:27]`
note: function defined here
--> $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 `async fn` body
| arguments to this function are incorrect
|
= note: expected associated type `impl Future<Output = ()>`
found `async fn` body `[async fn body@$DIR/async-trait-fn.rs:7:20: 11:6]`
note: function defined here
--> $SRC_DIR/core/src/future/mod.rs:LL:COL

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0706`.
Some errors have detailed explanations: E0308, E0706.
For more information about an error, try `rustc --explain E0308`.
1 change: 1 addition & 0 deletions tests/ui/async-await/edition-deny-async-fns-2015.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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: 20 additions & 6 deletions tests/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:36:9
--> $DIR/edition-deny-async-fns-2015.rs:37: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:26:9
--> $DIR/edition-deny-async-fns-2015.rs:27: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:31:13
--> $DIR/edition-deny-async-fns-2015.rs:32:13
|
LL | async fn bar() {}
| ^^^^^ to use `async fn`, switch to Rust 2018 or later
Expand All @@ -92,7 +92,21 @@ 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: aborting due to 10 previous errors
error[E0308]: mismatched types
--> $DIR/edition-deny-async-fns-2015.rs:18:20
|
LL | async fn foo() {}
| ^^
| |
| expected associated type, found `async fn` body
| arguments to this function are incorrect
|
= note: expected associated type `impl Future<Output = ()>`
found `async fn` body `[async fn body@$DIR/edition-deny-async-fns-2015.rs:18:20: 18:22]`
note: function defined here
--> $SRC_DIR/core/src/future/mod.rs:LL:COL

error: aborting due to 11 previous errors

Some errors have detailed explanations: E0670, E0706.
For more information about an error, try `rustc --explain E0670`.
Some errors have detailed explanations: E0308, E0670, E0706.
For more information about an error, try `rustc --explain E0308`.
4 changes: 1 addition & 3 deletions tests/ui/async-await/in-trait/return-type-suggestion.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// edition: 2021
// known-bug: #108142

#![feature(async_fn_in_trait)]
//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes

trait A {
async fn e() {
Ok(())
//~^ ERROR mismatched types
//~| HELP consider using a semicolon here
}
}

Expand Down
16 changes: 5 additions & 11 deletions tests/ui/async-await/in-trait/return-type-suggestion.stderr
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/return-type-suggestion.rs:3:12
--> $DIR/return-type-suggestion.rs:4:12
|
LL | #![feature(async_fn_in_trait)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0308]: mismatched types
--> $DIR/return-type-suggestion.rs:8:9
error: default body not allowed on async trait method
--> $DIR/return-type-suggestion.rs:7:18
|
LL | Ok(())
| ^^^^^^- help: consider using a semicolon here: `;`
| |
| expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
LL | async fn e() {
| ^ remove this body, or move it to an impl instead

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0308`.
3 changes: 1 addition & 2 deletions tests/ui/impl-trait/in-trait/box-coerce-span-in-default.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// check-pass
// known-bug: #108142

#![feature(return_position_impl_trait_in_trait)]
//~^ WARN the feature `return_position_impl_trait_in_trait` is incomplete

struct TestA {}
struct TestB {}
Expand Down
10 changes: 9 additions & 1 deletion tests/ui/impl-trait/in-trait/box-coerce-span-in-default.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,13 @@ LL | #![feature(return_position_impl_trait_in_trait)]
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted
error: default body not allowed on trait method with `impl Trait`
--> $DIR/box-coerce-span-in-default.rs:37:68
|
LL | fn test_func(&self, func: &str) -> impl TestTrait<Output = ()> {
| --------------------------- ^ remove this body, or move it to an impl instead
| |
| this `impl Trait`

error: aborting due to previous error; 1 warning emitted

Loading