Skip to content

Commit 7402519

Browse files
committed
Auto merge of #109010 - compiler-errors:rtn, r=eholk
Initial support for return type notation (RTN) See: https://smallcultfollowing.com/babysteps/blog/2023/02/13/return-type-notation-send-bounds-part-2/ 1. Only supports `T: Trait<method(): Send>` style bounds, not `<T as Trait>::method(): Send`. Checking validity and injecting an implicit binder for all of the late-bound method generics is harder to do for the latter. * I'd add this in a follow-up. 3. ~Doesn't support RTN in general type position, i.e. no `let x: <T as Trait>::method() = ...`~ * I don't think we actually want this. 5. Doesn't add syntax for "eliding" the function args -- i.e. for now, we write `method(): Send` instead of `method(..): Send`. * May be a hazard if we try to add it in the future. I'll probably add it in a follow-up later, with a structured suggestion to change `method()` to `method(..)` once we add it. 7. ~I'm not in love with the feature gate name 😺~ * I renamed it to `return_type_notation` ✔️ Follow-up PRs will probably add support for `where T::method(): Send` bounds. I'm not sure if we ever want to support return-type-notation in arbitrary type positions. I may also make the bounds require `..` in the args list later. r? `@ghost`
2 parents 480068c + 8b592db commit 7402519

File tree

49 files changed

+880
-174
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+880
-174
lines changed

Diff for: compiler/rustc_ast/src/ast.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ pub enum GenericArgs {
167167
AngleBracketed(AngleBracketedArgs),
168168
/// The `(A, B)` and `C` in `Foo(A, B) -> C`.
169169
Parenthesized(ParenthesizedArgs),
170+
/// Associated return type bounds, like `T: Trait<method(..): Send>`
171+
/// which applies the `Send` bound to the return-type of `method`.
172+
ReturnTypeNotation(Span),
170173
}
171174

172175
impl GenericArgs {
@@ -178,6 +181,7 @@ impl GenericArgs {
178181
match self {
179182
AngleBracketed(data) => data.span,
180183
Parenthesized(data) => data.span,
184+
ReturnTypeNotation(span) => *span,
181185
}
182186
}
183187
}
@@ -231,15 +235,15 @@ impl AngleBracketedArg {
231235
}
232236
}
233237

234-
impl Into<Option<P<GenericArgs>>> for AngleBracketedArgs {
235-
fn into(self) -> Option<P<GenericArgs>> {
236-
Some(P(GenericArgs::AngleBracketed(self)))
238+
impl Into<P<GenericArgs>> for AngleBracketedArgs {
239+
fn into(self) -> P<GenericArgs> {
240+
P(GenericArgs::AngleBracketed(self))
237241
}
238242
}
239243

240-
impl Into<Option<P<GenericArgs>>> for ParenthesizedArgs {
241-
fn into(self) -> Option<P<GenericArgs>> {
242-
Some(P(GenericArgs::Parenthesized(self)))
244+
impl Into<P<GenericArgs>> for ParenthesizedArgs {
245+
fn into(self) -> P<GenericArgs> {
246+
P(GenericArgs::Parenthesized(self))
243247
}
244248
}
245249

Diff for: compiler/rustc_ast/src/mut_visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ pub fn noop_visit_generic_args<T: MutVisitor>(generic_args: &mut GenericArgs, vi
561561
match generic_args {
562562
GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data),
563563
GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data),
564+
GenericArgs::ReturnTypeNotation(_span) => {}
564565
}
565566
}
566567

Diff for: compiler/rustc_ast/src/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ where
481481
walk_list!(visitor, visit_ty, &data.inputs);
482482
walk_fn_ret_ty(visitor, &data.output);
483483
}
484+
GenericArgs::ReturnTypeNotation(_span) => {}
484485
}
485486
}
486487

Diff for: compiler/rustc_ast_lowering/messages.ftl

+12
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,15 @@ ast_lowering_trait_fn_async =
139139
.label = `async` because of this
140140
.note = `async` trait functions are not currently supported
141141
.note2 = consider using the `async-trait` crate: https://crates.io/crates/async-trait
142+
143+
ast_lowering_bad_return_type_notation_inputs =
144+
argument types not allowed with return type notation
145+
.suggestion = remove the input types
146+
147+
ast_lowering_bad_return_type_notation_needs_dots =
148+
return type notation arguments must be elided with `..`
149+
.suggestion = add `..`
150+
151+
ast_lowering_bad_return_type_notation_output =
152+
return type not allowed with return type notation
153+
.suggestion = remove the return type

Diff for: compiler/rustc_ast_lowering/src/errors.rs

+22
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,25 @@ pub struct TraitFnAsync {
347347
#[label]
348348
pub span: Span,
349349
}
350+
351+
#[derive(Diagnostic)]
352+
pub enum BadReturnTypeNotation {
353+
#[diag(ast_lowering_bad_return_type_notation_inputs)]
354+
Inputs {
355+
#[primary_span]
356+
#[suggestion(code = "(..)", applicability = "maybe-incorrect")]
357+
span: Span,
358+
},
359+
#[diag(ast_lowering_bad_return_type_notation_needs_dots)]
360+
NeedsDots {
361+
#[primary_span]
362+
#[suggestion(code = "(..)", applicability = "maybe-incorrect")]
363+
span: Span,
364+
},
365+
#[diag(ast_lowering_bad_return_type_notation_output)]
366+
Output {
367+
#[primary_span]
368+
#[suggestion(code = "", applicability = "maybe-incorrect")]
369+
span: Span,
370+
},
371+
}

Diff for: compiler/rustc_ast_lowering/src/lib.rs

+56-12
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use rustc_middle::{
6666
span_bug,
6767
ty::{ResolverAstLowering, TyCtxt},
6868
};
69-
use rustc_session::parse::feature_err;
69+
use rustc_session::parse::{add_feature_diagnostics, feature_err};
7070
use rustc_span::hygiene::MacroKind;
7171
use rustc_span::source_map::DesugaringKind;
7272
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -482,7 +482,7 @@ enum ParamMode {
482482
}
483483

484484
enum ParenthesizedGenericArgs {
485-
Ok,
485+
ParenSugar,
486486
Err,
487487
}
488488

@@ -987,14 +987,56 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
987987
GenericArgs::AngleBracketed(data) => {
988988
self.lower_angle_bracketed_parameter_data(data, ParamMode::Explicit, itctx).0
989989
}
990+
&GenericArgs::ReturnTypeNotation(span) => GenericArgsCtor {
991+
args: Default::default(),
992+
bindings: &[],
993+
parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
994+
span,
995+
},
990996
GenericArgs::Parenthesized(data) => {
991-
self.emit_bad_parenthesized_trait_in_assoc_ty(data);
992-
self.lower_angle_bracketed_parameter_data(
993-
&data.as_angle_bracketed_args(),
994-
ParamMode::Explicit,
995-
itctx,
996-
)
997-
.0
997+
if let Some(start_char) = constraint.ident.as_str().chars().next()
998+
&& start_char.is_ascii_lowercase()
999+
{
1000+
let mut err = if !data.inputs.is_empty() {
1001+
self.tcx.sess.create_err(errors::BadReturnTypeNotation::Inputs {
1002+
span: data.inputs_span,
1003+
})
1004+
} else if let FnRetTy::Ty(ty) = &data.output {
1005+
self.tcx.sess.create_err(errors::BadReturnTypeNotation::Output {
1006+
span: data.inputs_span.shrink_to_hi().to(ty.span),
1007+
})
1008+
} else {
1009+
self.tcx.sess.create_err(errors::BadReturnTypeNotation::NeedsDots {
1010+
span: data.inputs_span,
1011+
})
1012+
};
1013+
if !self.tcx.features().return_type_notation
1014+
&& self.tcx.sess.is_nightly_build()
1015+
{
1016+
add_feature_diagnostics(
1017+
&mut err,
1018+
&self.tcx.sess.parse_sess,
1019+
sym::return_type_notation,
1020+
);
1021+
}
1022+
err.emit();
1023+
GenericArgsCtor {
1024+
args: Default::default(),
1025+
bindings: &[],
1026+
parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
1027+
span: data.span,
1028+
}
1029+
} else {
1030+
self.emit_bad_parenthesized_trait_in_assoc_ty(data);
1031+
// FIXME(return_type_notation): we could issue a feature error
1032+
// if the parens are empty and there's no return type.
1033+
self.lower_angle_bracketed_parameter_data(
1034+
&data.as_angle_bracketed_args(),
1035+
ParamMode::Explicit,
1036+
itctx,
1037+
)
1038+
.0
1039+
}
9981040
}
9991041
};
10001042
gen_args_ctor.into_generic_args(self)
@@ -2075,7 +2117,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
20752117
let future_args = self.arena.alloc(hir::GenericArgs {
20762118
args: &[],
20772119
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
2078-
parenthesized: false,
2120+
parenthesized: hir::GenericArgsParentheses::No,
20792121
span_ext: DUMMY_SP,
20802122
});
20812123

@@ -2595,13 +2637,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
25952637
struct GenericArgsCtor<'hir> {
25962638
args: SmallVec<[hir::GenericArg<'hir>; 4]>,
25972639
bindings: &'hir [hir::TypeBinding<'hir>],
2598-
parenthesized: bool,
2640+
parenthesized: hir::GenericArgsParentheses,
25992641
span: Span,
26002642
}
26012643

26022644
impl<'hir> GenericArgsCtor<'hir> {
26032645
fn is_empty(&self) -> bool {
2604-
self.args.is_empty() && self.bindings.is_empty() && !self.parenthesized
2646+
self.args.is_empty()
2647+
&& self.bindings.is_empty()
2648+
&& self.parenthesized == hir::GenericArgsParentheses::No
26052649
}
26062650

26072651
fn into_generic_args(self, this: &LoweringContext<'_, 'hir>) -> &'hir hir::GenericArgs<'hir> {

Diff for: compiler/rustc_ast_lowering/src/path.rs

+29-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_span::symbol::{kw, sym, Ident};
1313
use rustc_span::{BytePos, Span, DUMMY_SP};
1414

1515
use smallvec::{smallvec, SmallVec};
16+
use thin_vec::ThinVec;
1617

1718
impl<'a, 'hir> LoweringContext<'a, 'hir> {
1819
#[instrument(level = "trace", skip(self))]
@@ -51,18 +52,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
5152
let parenthesized_generic_args = match base_res {
5253
// `a::b::Trait(Args)`
5354
Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
54-
ParenthesizedGenericArgs::Ok
55+
ParenthesizedGenericArgs::ParenSugar
5556
}
5657
// `a::b::Trait(Args)::TraitItem`
5758
Res::Def(DefKind::AssocFn, _)
5859
| Res::Def(DefKind::AssocConst, _)
5960
| Res::Def(DefKind::AssocTy, _)
6061
if i + 2 == proj_start =>
6162
{
62-
ParenthesizedGenericArgs::Ok
63+
ParenthesizedGenericArgs::ParenSugar
6364
}
6465
// Avoid duplicated errors.
65-
Res::Err => ParenthesizedGenericArgs::Ok,
66+
Res::Err => ParenthesizedGenericArgs::ParenSugar,
6667
// An error
6768
_ => ParenthesizedGenericArgs::Err,
6869
};
@@ -180,7 +181,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
180181
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
181182
}
182183
GenericArgs::Parenthesized(data) => match parenthesized_generic_args {
183-
ParenthesizedGenericArgs::Ok => {
184+
ParenthesizedGenericArgs::ParenSugar => {
184185
self.lower_parenthesized_parameter_data(data, itctx)
185186
}
186187
ParenthesizedGenericArgs::Err => {
@@ -218,13 +219,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
218219
)
219220
}
220221
},
222+
&GenericArgs::ReturnTypeNotation(span) => {
223+
self.tcx.sess.emit_err(GenericTypeWithParentheses { span, sub: None });
224+
(
225+
self.lower_angle_bracketed_parameter_data(
226+
&AngleBracketedArgs { span, args: ThinVec::default() },
227+
param_mode,
228+
itctx,
229+
)
230+
.0,
231+
false,
232+
)
233+
}
221234
}
222235
} else {
223236
(
224237
GenericArgsCtor {
225238
args: Default::default(),
226239
bindings: &[],
227-
parenthesized: false,
240+
parenthesized: hir::GenericArgsParentheses::No,
228241
span: path_span.shrink_to_hi(),
229242
},
230243
param_mode == ParamMode::Optional,
@@ -233,7 +246,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
233246

234247
let has_lifetimes =
235248
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
236-
if !generic_args.parenthesized && !has_lifetimes {
249+
250+
// FIXME(return_type_notation): Is this correct? I think so.
251+
if generic_args.parenthesized != hir::GenericArgsParentheses::ParenSugar && !has_lifetimes {
237252
self.maybe_insert_elided_lifetimes_in_path(
238253
path_span,
239254
segment.id,
@@ -328,7 +343,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
328343
AngleBracketedArg::Constraint(c) => Some(self.lower_assoc_ty_constraint(c, itctx)),
329344
AngleBracketedArg::Arg(_) => None,
330345
}));
331-
let ctor = GenericArgsCtor { args, bindings, parenthesized: false, span: data.span };
346+
let ctor = GenericArgsCtor {
347+
args,
348+
bindings,
349+
parenthesized: hir::GenericArgsParentheses::No,
350+
span: data.span,
351+
};
332352
(ctor, !has_non_lt_args && param_mode == ParamMode::Optional)
333353
}
334354

@@ -376,7 +396,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
376396
GenericArgsCtor {
377397
args,
378398
bindings: arena_vec![self; binding],
379-
parenthesized: true,
399+
parenthesized: hir::GenericArgsParentheses::ParenSugar,
380400
span: data.inputs_span,
381401
},
382402
false,
@@ -396,7 +416,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
396416
let gen_args = self.arena.alloc(hir::GenericArgs {
397417
args,
398418
bindings,
399-
parenthesized: false,
419+
parenthesized: hir::GenericArgsParentheses::No,
400420
span_ext: DUMMY_SP,
401421
});
402422
hir::TypeBinding {

Diff for: compiler/rustc_ast_passes/src/ast_validation.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10751075
self.with_impl_trait(None, |this| this.visit_ty(ty));
10761076
}
10771077
}
1078+
GenericArgs::ReturnTypeNotation(_span) => {}
10781079
}
10791080
}
10801081

@@ -1387,16 +1388,19 @@ fn deny_equality_constraints(
13871388
match &mut assoc_path.segments[len].args {
13881389
Some(args) => match args.deref_mut() {
13891390
GenericArgs::Parenthesized(_) => continue,
1391+
GenericArgs::ReturnTypeNotation(_span) => continue,
13901392
GenericArgs::AngleBracketed(args) => {
13911393
args.args.push(arg);
13921394
}
13931395
},
13941396
empty_args => {
1395-
*empty_args = AngleBracketedArgs {
1396-
span: ident.span,
1397-
args: thin_vec![arg],
1398-
}
1399-
.into();
1397+
*empty_args = Some(
1398+
AngleBracketedArgs {
1399+
span: ident.span,
1400+
args: thin_vec![arg],
1401+
}
1402+
.into(),
1403+
);
14001404
}
14011405
}
14021406
err.assoc = Some(errors::AssociatedSuggestion {

Diff for: compiler/rustc_ast_passes/src/feature_gate.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -482,12 +482,28 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
482482

483483
fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) {
484484
if let AssocConstraintKind::Bound { .. } = constraint.kind {
485-
gate_feature_post!(
486-
&self,
487-
associated_type_bounds,
488-
constraint.span,
489-
"associated type bounds are unstable"
490-
)
485+
if let Some(args) = constraint.gen_args.as_ref()
486+
&& matches!(
487+
args,
488+
ast::GenericArgs::ReturnTypeNotation(..) | ast::GenericArgs::Parenthesized(..)
489+
)
490+
{
491+
// RTN is gated elsewhere, and parenthesized args will turn into
492+
// another error.
493+
if matches!(args, ast::GenericArgs::Parenthesized(..)) {
494+
self.sess.delay_span_bug(
495+
constraint.span,
496+
"should have emitted a parenthesized generics error",
497+
);
498+
}
499+
} else {
500+
gate_feature_post!(
501+
&self,
502+
associated_type_bounds,
503+
constraint.span,
504+
"associated type bounds are unstable"
505+
)
506+
}
491507
}
492508
visit::walk_assoc_constraint(self, constraint)
493509
}
@@ -577,6 +593,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
577593
gate_all!(yeet_expr, "`do yeet` expression is experimental");
578594
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
579595
gate_all!(const_closures, "const closures are experimental");
596+
gate_all!(return_type_notation, "return type notation is experimental");
580597

581598
// All uses of `gate_all!` below this point were added in #65742,
582599
// and subsequently disabled (with the non-early gating readded).

0 commit comments

Comments
 (0)