diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 5d2589cb2b2f7..9bdf56535ae5a 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,3 +1,5 @@ +use crate::AsyncReturn; + use super::errors::{InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound}; use super::ResolverAstLoweringExt; use super::{AstOwner, ImplTraitContext, ImplTraitPosition}; @@ -261,7 +263,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let itctx = ImplTraitContext::Universal; let (generics, decl) = this.lower_generics(generics, id, &itctx, |this| { - let ret_id = asyncness.opt_return_id(); + let ret_id = + AsyncReturn::new_opt(asyncness.opt_return_id(), attrs.unwrap_or(&[])); this.lower_fn_decl(&decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id) }); let sig = hir::FnSig { @@ -721,7 +724,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, FnDeclKind::Trait, - asyncness.opt_return_id(), + AsyncReturn::new_opt(asyncness.opt_return_id(), &i.attrs), ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) } @@ -734,7 +737,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, FnDeclKind::Trait, - asyncness.opt_return_id(), + AsyncReturn::new_opt(asyncness.opt_return_id(), &i.attrs), ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) } @@ -827,7 +830,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, - asyncness.opt_return_id(), + AsyncReturn::new_opt(asyncness.opt_return_id(), &i.attrs), ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -1181,7 +1184,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: &FnSig, id: NodeId, kind: FnDeclKind, - is_async: Option<(NodeId, Span)>, + is_async: Option, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); let itctx = ImplTraitContext::Universal; diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bc6d2cf12c78a..dfb5e239dfe67 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -462,6 +462,30 @@ enum ParenthesizedGenericArgs { Err, } +/// Specifies options for generating an async return type +#[derive(Debug)] +struct AsyncReturn { + /// The `NodeId` of the return `impl Trait` item + node_id: NodeId, + /// Points to the `async` keyword + span: Span, + /// Whether to add a `+ Send` bound to the `-> impl Future` return type + is_send: bool, +} + +impl AsyncReturn { + /// Creates a new `Option` from an `Option<(NodeId, Span)>` that is typically + /// returned from `Async::opt_node_id()` and a list of attributes to determine whether the + /// resulting type should be Send. + fn new_opt(opt_return_id: Option<(NodeId, Span)>, attrs: &[Attribute]) -> Option { + opt_return_id.map(|(node_id, span)| AsyncReturn { + node_id, + span, + is_send: attrs.iter().any(|attr| attr.has_name(sym::async_send)), + }) + } +} + impl<'a, 'hir> LoweringContext<'a, 'hir> { fn create_def( &mut self, @@ -1666,7 +1690,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn_node_id: NodeId, fn_span: Span, kind: FnDeclKind, - make_ret_async: Option<(NodeId, Span)>, + make_ret_async: Option, ) -> &'hir hir::FnDecl<'hir> { let c_variadic = decl.c_variadic(); @@ -1695,20 +1719,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_ty_direct(¶m.ty, &itctx) })); - let output = if let Some((ret_id, span)) = make_ret_async { + let output = if let Some(async_return) = make_ret_async { if !kind.async_fn_allowed(self.tcx) { match kind { FnDeclKind::Trait | FnDeclKind::Impl => { self.tcx .sess .create_feature_err( - TraitFnAsync { fn_span, span }, + TraitFnAsync { fn_span, span: async_return.span }, sym::async_fn_in_trait, ) .emit(); } _ => { - self.tcx.sess.emit_err(TraitFnAsync { fn_span, span }); + self.tcx.sess.emit_err(TraitFnAsync { fn_span, span: async_return.span }); } } } @@ -1716,7 +1740,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_async_fn_ret_ty( &decl.output, fn_node_id, - ret_id, + async_return, matches!(kind, FnDeclKind::Trait), ) } else { @@ -1793,11 +1817,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, output: &FnRetTy, fn_node_id: NodeId, - opaque_ty_node_id: NodeId, + async_return: AsyncReturn, in_trait: bool, ) -> hir::FnRetTy<'hir> { let span = output.span(); + let opaque_ty_node_id = async_return.node_id; + let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); let fn_def_id = self.local_def_id(fn_node_id); @@ -1967,6 +1993,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { )); debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); + // Add Send bound if `#[async_send]` attribute is present + let bounds = if async_return.is_send { + let send_bound = hir::GenericBound::LangItemTrait( + hir::LangItem::Send, + this.lower_span(span), + this.next_id(), + this.arena.alloc(hir::GenericArgs { + args: &[], + bindings: &[], + parenthesized: false, + span_ext: DUMMY_SP, + }), + ); + + arena_vec![this; future_bound, send_bound] + } else { + arena_vec![this; future_bound] + }; + let opaque_ty_item = hir::OpaqueTy { generics: this.arena.alloc(hir::Generics { params: generic_params, @@ -1975,7 +2020,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { where_clause_span: this.lower_span(span), span: this.lower_span(span), }), - bounds: arena_vec![this; future_bound], + bounds, origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id), in_trait, }; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index af56a0b245987..df017b095773f 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -490,6 +490,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // RFC 2397 gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)), + gated!(async_send, Normal, template!(Word), ErrorFollowing, async_fn_in_trait, + "`async_send` is a temporary placeholder for marking async methods as returning a future \ + that is also Send and may be removed or renamed in the future."), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 3474fab34f00b..332c3d2aaf8aa 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -216,6 +216,7 @@ language_item_table! { FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); + Send, sym::send, send_trait, Target::Trait, GenericRequirement::Exact(0); GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None; Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 706002f79b1fb..d266337cd2a1a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -393,6 +393,7 @@ symbols! { async_await, async_closure, async_fn_in_trait, + async_send, atomic, atomic_mod, atomics, @@ -1297,6 +1298,7 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + send, shadow_call_stack, shl, shl_assign, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 1326fc9ab096f..8d09136ec3d0f 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -31,6 +31,7 @@ use crate::hash::Hasher; /// [ub]: ../../reference/behavior-considered-undefined.html #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Send")] +#[cfg_attr(not(bootstrap), lang = "send")] #[rustc_on_unimplemented( message = "`{Self}` cannot be sent between threads safely", label = "`{Self}` cannot be sent between threads safely" diff --git a/tests/ui/async-await/in-trait/send-bound-gate.rs b/tests/ui/async-await/in-trait/send-bound-gate.rs new file mode 100644 index 0000000000000..8fed247cf7480 --- /dev/null +++ b/tests/ui/async-await/in-trait/send-bound-gate.rs @@ -0,0 +1,14 @@ +// Make sure the #[async_send] attribute requires the async_fn_in_trait feature + +// edition: 2021 + +trait MyTrait { + #[async_send] //~ `async_send` is a temporary placeholder + async fn foo(&self) -> usize; + //~^ functions in traits cannot be declared `async` +} + +#[async_send] //~ `async_send` is a temporary placeholder +async fn bar() {} + +fn main() {} diff --git a/tests/ui/async-await/in-trait/send-bound-gate.stderr b/tests/ui/async-await/in-trait/send-bound-gate.stderr new file mode 100644 index 0000000000000..b81956cbaa5e4 --- /dev/null +++ b/tests/ui/async-await/in-trait/send-bound-gate.stderr @@ -0,0 +1,35 @@ +error[E0658]: `async_send` is a temporary placeholder for marking async methods as returning a future that is also Send and may be removed or renamed in the future. + --> $DIR/send-bound-gate.rs:6:5 + | +LL | #[async_send] + | ^^^^^^^^^^^^^ + | + = note: see issue #91611 for more information + = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable + +error[E0658]: `async_send` is a temporary placeholder for marking async methods as returning a future that is also Send and may be removed or renamed in the future. + --> $DIR/send-bound-gate.rs:11:1 + | +LL | #[async_send] + | ^^^^^^^^^^^^^ + | + = note: see issue #91611 for more information + = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable + +error[E0706]: functions in traits cannot be declared `async` + --> $DIR/send-bound-gate.rs:7:5 + | +LL | async fn foo(&self) -> usize; + | -----^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + = note: see issue #91611 for more information + = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0658, E0706. +For more information about an error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/in-trait/send-bound.rs b/tests/ui/async-await/in-trait/send-bound.rs new file mode 100644 index 0000000000000..ac473b001bf55 --- /dev/null +++ b/tests/ui/async-await/in-trait/send-bound.rs @@ -0,0 +1,18 @@ +// check-pass +// edition: 2021 + +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +trait MyTrait { + #[async_send] + async fn foo(&self) -> usize; +} + +fn assert_send(_: T) {} + +fn use_trait(x: T) { + assert_send(x.foo()) +} + +fn main() {}