Add feature gate, not working yet
This commit is contained in:
parent
33c245b9e9
commit
3b05779626
5 changed files with 116 additions and 14 deletions
|
@ -390,6 +390,8 @@ declare_features! (
|
|||
(unstable, associated_type_defaults, "1.2.0", Some(29661)),
|
||||
/// Allows `async || body` closures.
|
||||
(unstable, async_closure, "1.37.0", Some(62290)),
|
||||
/// Allows async functions to be called from `dyn Trait`.
|
||||
(incomplete, async_fn_in_dyn_trait, "CURRENT_RUSTC_VERSION", Some(133119)),
|
||||
/// Allows `#[track_caller]` on async functions.
|
||||
(unstable, async_fn_track_caller, "1.73.0", Some(110011)),
|
||||
/// Allows `for await` loops.
|
||||
|
|
|
@ -461,6 +461,7 @@ symbols! {
|
|||
async_drop_slice,
|
||||
async_drop_surface_drop_in_place,
|
||||
async_fn,
|
||||
async_fn_in_dyn_trait,
|
||||
async_fn_in_trait,
|
||||
async_fn_kind_helper,
|
||||
async_fn_kind_upvars,
|
||||
|
|
|
@ -11,6 +11,7 @@ use rustc_abi::BackendRepr;
|
|||
use rustc_errors::FatalError;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{
|
||||
self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt,
|
||||
|
@ -901,23 +902,59 @@ fn contains_illegal_impl_trait_in_trait<'tcx>(
|
|||
fn_def_id: DefId,
|
||||
ty: ty::Binder<'tcx, Ty<'tcx>>,
|
||||
) -> Option<MethodViolationCode> {
|
||||
// This would be caught below, but rendering the error as a separate
|
||||
// `async-specific` message is better.
|
||||
let ty = tcx.liberate_late_bound_regions(fn_def_id, ty);
|
||||
|
||||
if tcx.asyncness(fn_def_id).is_async() {
|
||||
return Some(MethodViolationCode::AsyncFn);
|
||||
// FIXME(async_fn_in_dyn_trait): Think of a better way to unify these code paths
|
||||
// to issue an appropriate feature suggestion when users try to use AFIDT.
|
||||
// Obviously we must only do this once AFIDT is finished enough to actually be usable.
|
||||
if tcx.features().async_fn_in_dyn_trait() {
|
||||
let ty::Alias(ty::Projection, proj) = *ty.kind() else {
|
||||
bug!("expected async fn in trait to return an RPITIT");
|
||||
};
|
||||
assert!(tcx.is_impl_trait_in_trait(proj.def_id));
|
||||
|
||||
// FIXME(async_fn_in_dyn_trait): We should check that this bound is legal too,
|
||||
// and stop relying on `async fn` in the definition.
|
||||
for bound in tcx.item_bounds(proj.def_id).instantiate(tcx, proj.args) {
|
||||
if let Some(violation) = bound
|
||||
.visit_with(&mut IllegalRpititVisitor { tcx, allowed: Some(proj) })
|
||||
.break_value()
|
||||
{
|
||||
return Some(violation);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(RPITIT): Perhaps we should use a visitor here?
|
||||
ty.skip_binder().walk().find_map(|arg| {
|
||||
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Alias(ty::Projection, proj) = ty.kind()
|
||||
&& tcx.is_impl_trait_in_trait(proj.def_id)
|
||||
{
|
||||
Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.def_id)))
|
||||
} else {
|
||||
None
|
||||
} else {
|
||||
// Rendering the error as a separate `async-specific` message is better.
|
||||
Some(MethodViolationCode::AsyncFn)
|
||||
}
|
||||
} else {
|
||||
ty.visit_with(&mut IllegalRpititVisitor { tcx, allowed: None }).break_value()
|
||||
}
|
||||
}
|
||||
|
||||
struct IllegalRpititVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
allowed: Option<ty::AliasTy<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalRpititVisitor<'tcx> {
|
||||
type Result = ControlFlow<MethodViolationCode>;
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
if let ty::Alias(ty::Projection, proj) = *ty.kind()
|
||||
&& Some(proj) != self.allowed
|
||||
&& self.tcx.is_impl_trait_in_trait(proj.def_id)
|
||||
{
|
||||
ControlFlow::Break(MethodViolationCode::ReferencesImplTraitInTrait(
|
||||
self.tcx.def_span(proj.def_id),
|
||||
))
|
||||
} else {
|
||||
ty.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
|
|
14
tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs
Normal file
14
tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
//@ edition: 2021
|
||||
|
||||
trait Foo {
|
||||
async fn bar(&self);
|
||||
}
|
||||
|
||||
async fn takes_dyn_trait(x: &dyn Foo) {
|
||||
//~^ ERROR the trait `Foo` cannot be made into an object
|
||||
x.bar().await;
|
||||
//~^ ERROR the trait `Foo` cannot be made into an object
|
||||
//~| ERROR the trait `Foo` cannot be made into an object
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,48 @@
|
|||
error[E0038]: the trait `Foo` cannot be made into an object
|
||||
--> $DIR/feature-gate-async-fn-in-dyn-trait.rs:7:30
|
||||
|
|
||||
LL | async fn takes_dyn_trait(x: &dyn Foo) {
|
||||
| ^^^^^^^ `Foo` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14
|
||||
|
|
||||
LL | trait Foo {
|
||||
| --- this trait cannot be made into an object...
|
||||
LL | async fn bar(&self);
|
||||
| ^^^ ...because method `bar` is `async`
|
||||
= help: consider moving `bar` to another trait
|
||||
|
||||
error[E0038]: the trait `Foo` cannot be made into an object
|
||||
--> $DIR/feature-gate-async-fn-in-dyn-trait.rs:9:7
|
||||
|
|
||||
LL | x.bar().await;
|
||||
| ^^^ `Foo` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14
|
||||
|
|
||||
LL | trait Foo {
|
||||
| --- this trait cannot be made into an object...
|
||||
LL | async fn bar(&self);
|
||||
| ^^^ ...because method `bar` is `async`
|
||||
= help: consider moving `bar` to another trait
|
||||
|
||||
error[E0038]: the trait `Foo` cannot be made into an object
|
||||
--> $DIR/feature-gate-async-fn-in-dyn-trait.rs:9:5
|
||||
|
|
||||
LL | x.bar().await;
|
||||
| ^^^^^^^ `Foo` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14
|
||||
|
|
||||
LL | trait Foo {
|
||||
| --- this trait cannot be made into an object...
|
||||
LL | async fn bar(&self);
|
||||
| ^^^ ...because method `bar` is `async`
|
||||
= help: consider moving `bar` to another trait
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0038`.
|
Loading…
Add table
Add a link
Reference in a new issue