Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f3b9d47

Browse files
committedFeb 6, 2024
Auto merge of #120392 - compiler-errors:async-bound-modifier, r=davidtwco,fmease
Introduce support for `async` bound modifier on `Fn*` traits Adds `async` to the list of `TraitBoundModifiers`, which instructs AST lowering to map the trait to an async flavor of the trait. For now, this is only supported for `Fn*` to `AsyncFn*`, and I expect that this manual mapping via lang items will be replaced with a better system in the future. The motivation for adding these bounds is to separate the users of async closures from the exact trait desugaring of their callable bounds. Instead of users needing to be concerned with the `AsyncFn` trait, they should be able to write `async Fn()` and it will desugar to whatever underlying trait we decide is best for the lowering of async closures. Note: rustfmt support can be done in the rustfmt repo after a subtree sync.
2 parents f067fd6 + 3913c9a commit f3b9d47

30 files changed

+516
-62
lines changed
 

‎compiler/rustc_ast/src/ast.rs

+26-3
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,16 @@ pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID};
291291
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
292292
pub struct TraitBoundModifiers {
293293
pub constness: BoundConstness,
294+
pub asyncness: BoundAsyncness,
294295
pub polarity: BoundPolarity,
295296
}
296297

297298
impl TraitBoundModifiers {
298-
pub const NONE: Self =
299-
Self { constness: BoundConstness::Never, polarity: BoundPolarity::Positive };
299+
pub const NONE: Self = Self {
300+
constness: BoundConstness::Never,
301+
asyncness: BoundAsyncness::Normal,
302+
polarity: BoundPolarity::Positive,
303+
};
300304
}
301305

302306
/// The AST represents all type param bounds as types.
@@ -2562,6 +2566,25 @@ impl BoundConstness {
25622566
}
25632567
}
25642568

2569+
/// The asyncness of a trait bound.
2570+
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
2571+
#[derive(HashStable_Generic)]
2572+
pub enum BoundAsyncness {
2573+
/// `Type: Trait`
2574+
Normal,
2575+
/// `Type: async Trait`
2576+
Async(Span),
2577+
}
2578+
2579+
impl BoundAsyncness {
2580+
pub fn as_str(self) -> &'static str {
2581+
match self {
2582+
Self::Normal => "",
2583+
Self::Async(_) => "async",
2584+
}
2585+
}
2586+
}
2587+
25652588
#[derive(Clone, Encodable, Decodable, Debug)]
25662589
pub enum FnRetTy {
25672590
/// Returns type is not specified.
@@ -3300,7 +3323,7 @@ mod size_asserts {
33003323
static_assert_size!(ForeignItem, 96);
33013324
static_assert_size!(ForeignItemKind, 24);
33023325
static_assert_size!(GenericArg, 24);
3303-
static_assert_size!(GenericBound, 72);
3326+
static_assert_size!(GenericBound, 88);
33043327
static_assert_size!(Generics, 40);
33053328
static_assert_size!(Impl, 136);
33063329
static_assert_size!(Item, 136);

‎compiler/rustc_ast_lowering/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ ast_lowering_argument = argument
1111
ast_lowering_assoc_ty_parentheses =
1212
parenthesized generic arguments cannot be used in associated type constraints
1313
14+
ast_lowering_async_bound_not_on_trait =
15+
`async` bound modifier only allowed on trait, not `{$descr}`
16+
17+
ast_lowering_async_bound_only_for_fn_traits =
18+
`async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits
19+
1420
ast_lowering_async_coroutines_not_supported =
1521
`async` coroutines are not yet supported
1622

‎compiler/rustc_ast_lowering/src/errors.rs

+15
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,18 @@ pub(crate) struct GenericParamDefaultInBinder {
395395
#[primary_span]
396396
pub span: Span,
397397
}
398+
399+
#[derive(Diagnostic)]
400+
#[diag(ast_lowering_async_bound_not_on_trait)]
401+
pub(crate) struct AsyncBoundNotOnTrait {
402+
#[primary_span]
403+
pub span: Span,
404+
pub descr: &'static str,
405+
}
406+
407+
#[derive(Diagnostic)]
408+
#[diag(ast_lowering_async_bound_only_for_fn_traits)]
409+
pub(crate) struct AsyncBoundOnlyForFnTraits {
410+
#[primary_span]
411+
pub span: Span,
412+
}

‎compiler/rustc_ast_lowering/src/expr.rs

+2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
100100
ParenthesizedGenericArgs::Err,
101101
&ImplTraitContext::Disallowed(ImplTraitPosition::Path),
102102
None,
103+
// Method calls can't have bound modifiers
104+
None,
103105
));
104106
let receiver = self.lower_expr(receiver);
105107
let args =

‎compiler/rustc_ast_lowering/src/item.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -340,14 +340,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
340340
let itctx = ImplTraitContext::Universal;
341341
let (generics, (trait_ref, lowered_ty)) =
342342
self.lower_generics(ast_generics, *constness, id, &itctx, |this| {
343-
let constness = match *constness {
344-
Const::Yes(span) => BoundConstness::Maybe(span),
345-
Const::No => BoundConstness::Never,
343+
let modifiers = TraitBoundModifiers {
344+
constness: match *constness {
345+
Const::Yes(span) => BoundConstness::Maybe(span),
346+
Const::No => BoundConstness::Never,
347+
},
348+
asyncness: BoundAsyncness::Normal,
349+
// we don't use this in bound lowering
350+
polarity: BoundPolarity::Positive,
346351
};
347352

348353
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
349354
this.lower_trait_ref(
350-
constness,
355+
modifiers,
351356
trait_ref,
352357
&ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
353358
)

‎compiler/rustc_ast_lowering/src/lib.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ struct LoweringContext<'a, 'hir> {
131131
allow_gen_future: Lrc<[Symbol]>,
132132
allow_async_iterator: Lrc<[Symbol]>,
133133
allow_for_await: Lrc<[Symbol]>,
134+
allow_async_fn_traits: Lrc<[Symbol]>,
134135

135136
/// Mapping from generics `def_id`s to TAIT generics `def_id`s.
136137
/// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
@@ -176,6 +177,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
176177
[sym::gen_future].into()
177178
},
178179
allow_for_await: [sym::async_iterator].into(),
180+
allow_async_fn_traits: [sym::async_fn_traits].into(),
179181
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
180182
// interact with `gen`/`async gen` blocks
181183
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
@@ -1311,7 +1313,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13111313
span: t.span,
13121314
},
13131315
itctx,
1314-
ast::BoundConstness::Never,
1316+
TraitBoundModifiers::NONE,
13151317
);
13161318
let bounds = this.arena.alloc_from_iter([bound]);
13171319
let lifetime_bound = this.elided_dyn_bound(t.span);
@@ -1426,7 +1428,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
14261428
itctx,
14271429
// Still, don't pass along the constness here; we don't want to
14281430
// synthesize any host effect args, it'd only cause problems.
1429-
ast::BoundConstness::Never,
1431+
TraitBoundModifiers {
1432+
constness: BoundConstness::Never,
1433+
..*modifiers
1434+
},
14301435
))
14311436
}
14321437
BoundPolarity::Maybe(_) => None,
@@ -2019,7 +2024,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
20192024
) -> hir::GenericBound<'hir> {
20202025
match tpb {
20212026
GenericBound::Trait(p, modifiers) => hir::GenericBound::Trait(
2022-
self.lower_poly_trait_ref(p, itctx, modifiers.constness.into()),
2027+
self.lower_poly_trait_ref(p, itctx, *modifiers),
20232028
self.lower_trait_bound_modifiers(*modifiers),
20242029
),
20252030
GenericBound::Outlives(lifetime) => {
@@ -2192,7 +2197,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
21922197

21932198
fn lower_trait_ref(
21942199
&mut self,
2195-
constness: ast::BoundConstness,
2200+
modifiers: ast::TraitBoundModifiers,
21962201
p: &TraitRef,
21972202
itctx: &ImplTraitContext,
21982203
) -> hir::TraitRef<'hir> {
@@ -2202,7 +2207,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22022207
&p.path,
22032208
ParamMode::Explicit,
22042209
itctx,
2205-
Some(constness),
2210+
Some(modifiers),
22062211
) {
22072212
hir::QPath::Resolved(None, path) => path,
22082213
qpath => panic!("lower_trait_ref: unexpected QPath `{qpath:?}`"),
@@ -2215,11 +2220,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22152220
&mut self,
22162221
p: &PolyTraitRef,
22172222
itctx: &ImplTraitContext,
2218-
constness: ast::BoundConstness,
2223+
modifiers: ast::TraitBoundModifiers,
22192224
) -> hir::PolyTraitRef<'hir> {
22202225
let bound_generic_params =
22212226
self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params);
2222-
let trait_ref = self.lower_trait_ref(constness, &p.trait_ref, itctx);
2227+
let trait_ref = self.lower_trait_ref(modifiers, &p.trait_ref, itctx);
22232228
hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
22242229
}
22252230

‎compiler/rustc_ast_lowering/src/path.rs

+90-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
use crate::ImplTraitPosition;
22

3-
use super::errors::{GenericTypeWithParentheses, UseAngleBrackets};
3+
use super::errors::{
4+
AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, GenericTypeWithParentheses, UseAngleBrackets,
5+
};
46
use super::ResolverAstLoweringExt;
57
use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
68
use super::{ImplTraitContext, LoweringContext, ParamMode};
79

810
use rustc_ast::{self as ast, *};
11+
use rustc_data_structures::sync::Lrc;
912
use rustc_hir as hir;
1013
use rustc_hir::def::{DefKind, PartialRes, Res};
14+
use rustc_hir::def_id::DefId;
1115
use rustc_hir::GenericArg;
1216
use rustc_middle::span_bug;
1317
use rustc_span::symbol::{kw, sym, Ident};
14-
use rustc_span::{BytePos, Span, DUMMY_SP};
18+
use rustc_span::{BytePos, DesugaringKind, Span, Symbol, DUMMY_SP};
1519

1620
use smallvec::{smallvec, SmallVec};
1721

@@ -24,8 +28,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
2428
p: &Path,
2529
param_mode: ParamMode,
2630
itctx: &ImplTraitContext,
27-
// constness of the impl/bound if this is a trait path
28-
constness: Option<ast::BoundConstness>,
31+
// modifiers of the impl/bound if this is a trait path
32+
modifiers: Option<ast::TraitBoundModifiers>,
2933
) -> hir::QPath<'hir> {
3034
let qself_position = qself.as_ref().map(|q| q.position);
3135
let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
@@ -35,10 +39,36 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
3539
let base_res = partial_res.base_res();
3640
let unresolved_segments = partial_res.unresolved_segments();
3741

42+
let mut res = self.lower_res(base_res);
43+
44+
// When we have an `async` kw on a bound, map the trait it resolves to.
45+
let mut bound_modifier_allowed_features = None;
46+
if let Some(TraitBoundModifiers { asyncness: BoundAsyncness::Async(_), .. }) = modifiers {
47+
match res {
48+
Res::Def(DefKind::Trait, def_id) => {
49+
if let Some((async_def_id, features)) = self.map_trait_to_async_trait(def_id) {
50+
res = Res::Def(DefKind::Trait, async_def_id);
51+
bound_modifier_allowed_features = Some(features);
52+
} else {
53+
self.dcx().emit_err(AsyncBoundOnlyForFnTraits { span: p.span });
54+
}
55+
}
56+
Res::Err => {
57+
// No additional error.
58+
}
59+
_ => {
60+
// This error isn't actually emitted AFAICT, but it's best to keep
61+
// it around in case the resolver doesn't always check the defkind
62+
// of an item or something.
63+
self.dcx().emit_err(AsyncBoundNotOnTrait { span: p.span, descr: res.descr() });
64+
}
65+
}
66+
}
67+
3868
let path_span_lo = p.span.shrink_to_lo();
3969
let proj_start = p.segments.len() - unresolved_segments;
4070
let path = self.arena.alloc(hir::Path {
41-
res: self.lower_res(base_res),
71+
res,
4272
segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(
4373
|(i, segment)| {
4474
let param_mode = match (qself_position, param_mode) {
@@ -77,7 +107,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
77107
parenthesized_generic_args,
78108
itctx,
79109
// if this is the last segment, add constness to the trait path
80-
if i == proj_start - 1 { constness } else { None },
110+
if i == proj_start - 1 { modifiers.map(|m| m.constness) } else { None },
111+
bound_modifier_allowed_features.clone(),
81112
)
82113
},
83114
)),
@@ -88,6 +119,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
88119
),
89120
});
90121

122+
if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {
123+
path.span = self.mark_span_with_reason(
124+
DesugaringKind::BoundModifier,
125+
path.span,
126+
Some(bound_modifier_allowed_features),
127+
);
128+
}
129+
91130
// Simple case, either no projections, or only fully-qualified.
92131
// E.g., `std::mem::size_of` or `<I as Iterator>::Item`.
93132
if unresolved_segments == 0 {
@@ -125,6 +164,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
125164
ParenthesizedGenericArgs::Err,
126165
itctx,
127166
None,
167+
None,
128168
));
129169
let qpath = hir::QPath::TypeRelative(ty, hir_segment);
130170

@@ -166,6 +206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
166206
ParenthesizedGenericArgs::Err,
167207
&ImplTraitContext::Disallowed(ImplTraitPosition::Path),
168208
None,
209+
None,
169210
)
170211
})),
171212
span: self.lower_span(p.span),
@@ -180,6 +221,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
180221
parenthesized_generic_args: ParenthesizedGenericArgs,
181222
itctx: &ImplTraitContext,
182223
constness: Option<ast::BoundConstness>,
224+
// Additional features ungated with a bound modifier like `async`.
225+
// This is passed down to the implicit associated type binding in
226+
// parenthesized bounds.
227+
bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
183228
) -> hir::PathSegment<'hir> {
184229
debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment);
185230
let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() {
@@ -188,9 +233,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
188233
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
189234
}
190235
GenericArgs::Parenthesized(data) => match parenthesized_generic_args {
191-
ParenthesizedGenericArgs::ParenSugar => {
192-
self.lower_parenthesized_parameter_data(data, itctx)
193-
}
236+
ParenthesizedGenericArgs::ParenSugar => self
237+
.lower_parenthesized_parameter_data(
238+
data,
239+
itctx,
240+
bound_modifier_allowed_features,
241+
),
194242
ParenthesizedGenericArgs::Err => {
195243
// Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
196244
let sub = if !data.inputs.is_empty() {
@@ -357,6 +405,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
357405
&mut self,
358406
data: &ParenthesizedArgs,
359407
itctx: &ImplTraitContext,
408+
bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
360409
) -> (GenericArgsCtor<'hir>, bool) {
361410
// Switch to `PassThrough` mode for anonymous lifetimes; this
362411
// means that we permit things like `&Ref<T>`, where `Ref` has
@@ -392,7 +441,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
392441
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
393442
};
394443
let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
395-
let binding = self.assoc_ty_binding(sym::Output, output_ty.span, output_ty);
444+
445+
// If we have a bound like `async Fn() -> T`, make sure that we mark the
446+
// `Output = T` associated type bound with the right feature gates.
447+
let mut output_span = output_ty.span;
448+
if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {
449+
output_span = self.mark_span_with_reason(
450+
DesugaringKind::BoundModifier,
451+
output_span,
452+
Some(bound_modifier_allowed_features),
453+
);
454+
}
455+
let binding = self.assoc_ty_binding(sym::Output, output_span, output_ty);
456+
396457
(
397458
GenericArgsCtor {
398459
args,
@@ -429,4 +490,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
429490
kind,
430491
}
431492
}
493+
494+
/// When a bound is annotated with `async`, it signals to lowering that the trait
495+
/// that the bound refers to should be mapped to the "async" flavor of the trait.
496+
///
497+
/// This only needs to be done until we unify `AsyncFn` and `Fn` traits into one
498+
/// that is generic over `async`ness, if that's ever possible, or modify the
499+
/// lowering of `async Fn()` bounds to desugar to another trait like `LendingFn`.
500+
fn map_trait_to_async_trait(&self, def_id: DefId) -> Option<(DefId, Lrc<[Symbol]>)> {
501+
let lang_items = self.tcx.lang_items();
502+
if Some(def_id) == lang_items.fn_trait() {
503+
Some((lang_items.async_fn_trait()?, self.allow_async_fn_traits.clone()))
504+
} else if Some(def_id) == lang_items.fn_mut_trait() {
505+
Some((lang_items.async_fn_mut_trait()?, self.allow_async_fn_traits.clone()))
506+
} else if Some(def_id) == lang_items.fn_once_trait() {
507+
Some((lang_items.async_fn_once_trait()?, self.allow_async_fn_traits.clone()))
508+
} else {
509+
None
510+
}
511+
}
432512
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.