Skip to content

Commit

Permalink
Auto merge of #109010 - compiler-errors:rtn, r=eholk
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
bors committed Mar 31, 2023
2 parents 480068c + 8b592db commit 7402519
Show file tree
Hide file tree
Showing 49 changed files with 880 additions and 174 deletions.
16 changes: 10 additions & 6 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ pub enum GenericArgs {
AngleBracketed(AngleBracketedArgs),
/// The `(A, B)` and `C` in `Foo(A, B) -> C`.
Parenthesized(ParenthesizedArgs),
/// Associated return type bounds, like `T: Trait<method(..): Send>`
/// which applies the `Send` bound to the return-type of `method`.
ReturnTypeNotation(Span),
}

impl GenericArgs {
Expand All @@ -178,6 +181,7 @@ impl GenericArgs {
match self {
AngleBracketed(data) => data.span,
Parenthesized(data) => data.span,
ReturnTypeNotation(span) => *span,
}
}
}
Expand Down Expand Up @@ -231,15 +235,15 @@ impl AngleBracketedArg {
}
}

impl Into<Option<P<GenericArgs>>> for AngleBracketedArgs {
fn into(self) -> Option<P<GenericArgs>> {
Some(P(GenericArgs::AngleBracketed(self)))
impl Into<P<GenericArgs>> for AngleBracketedArgs {
fn into(self) -> P<GenericArgs> {
P(GenericArgs::AngleBracketed(self))
}
}

impl Into<Option<P<GenericArgs>>> for ParenthesizedArgs {
fn into(self) -> Option<P<GenericArgs>> {
Some(P(GenericArgs::Parenthesized(self)))
impl Into<P<GenericArgs>> for ParenthesizedArgs {
fn into(self) -> P<GenericArgs> {
P(GenericArgs::Parenthesized(self))
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ pub fn noop_visit_generic_args<T: MutVisitor>(generic_args: &mut GenericArgs, vi
match generic_args {
GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data),
GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data),
GenericArgs::ReturnTypeNotation(_span) => {}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ where
walk_list!(visitor, visit_ty, &data.inputs);
walk_fn_ret_ty(visitor, &data.output);
}
GenericArgs::ReturnTypeNotation(_span) => {}
}
}

Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,15 @@ ast_lowering_trait_fn_async =
.label = `async` because of this
.note = `async` trait functions are not currently supported
.note2 = consider using the `async-trait` crate: https://crates.io/crates/async-trait
ast_lowering_bad_return_type_notation_inputs =
argument types not allowed with return type notation
.suggestion = remove the input types
ast_lowering_bad_return_type_notation_needs_dots =
return type notation arguments must be elided with `..`
.suggestion = add `..`
ast_lowering_bad_return_type_notation_output =
return type not allowed with return type notation
.suggestion = remove the return type
22 changes: 22 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,25 @@ pub struct TraitFnAsync {
#[label]
pub span: Span,
}

#[derive(Diagnostic)]
pub enum BadReturnTypeNotation {
#[diag(ast_lowering_bad_return_type_notation_inputs)]
Inputs {
#[primary_span]
#[suggestion(code = "(..)", applicability = "maybe-incorrect")]
span: Span,
},
#[diag(ast_lowering_bad_return_type_notation_needs_dots)]
NeedsDots {
#[primary_span]
#[suggestion(code = "(..)", applicability = "maybe-incorrect")]
span: Span,
},
#[diag(ast_lowering_bad_return_type_notation_output)]
Output {
#[primary_span]
#[suggestion(code = "", applicability = "maybe-incorrect")]
span: Span,
},
}
68 changes: 56 additions & 12 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use rustc_middle::{
span_bug,
ty::{ResolverAstLowering, TyCtxt},
};
use rustc_session::parse::feature_err;
use rustc_session::parse::{add_feature_diagnostics, feature_err};
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
Expand Down Expand Up @@ -482,7 +482,7 @@ enum ParamMode {
}

enum ParenthesizedGenericArgs {
Ok,
ParenSugar,
Err,
}

Expand Down Expand Up @@ -987,14 +987,56 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
GenericArgs::AngleBracketed(data) => {
self.lower_angle_bracketed_parameter_data(data, ParamMode::Explicit, itctx).0
}
&GenericArgs::ReturnTypeNotation(span) => GenericArgsCtor {
args: Default::default(),
bindings: &[],
parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
span,
},
GenericArgs::Parenthesized(data) => {
self.emit_bad_parenthesized_trait_in_assoc_ty(data);
self.lower_angle_bracketed_parameter_data(
&data.as_angle_bracketed_args(),
ParamMode::Explicit,
itctx,
)
.0
if let Some(start_char) = constraint.ident.as_str().chars().next()
&& start_char.is_ascii_lowercase()
{
let mut err = if !data.inputs.is_empty() {
self.tcx.sess.create_err(errors::BadReturnTypeNotation::Inputs {
span: data.inputs_span,
})
} else if let FnRetTy::Ty(ty) = &data.output {
self.tcx.sess.create_err(errors::BadReturnTypeNotation::Output {
span: data.inputs_span.shrink_to_hi().to(ty.span),
})
} else {
self.tcx.sess.create_err(errors::BadReturnTypeNotation::NeedsDots {
span: data.inputs_span,
})
};
if !self.tcx.features().return_type_notation
&& self.tcx.sess.is_nightly_build()
{
add_feature_diagnostics(
&mut err,
&self.tcx.sess.parse_sess,
sym::return_type_notation,
);
}
err.emit();
GenericArgsCtor {
args: Default::default(),
bindings: &[],
parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
span: data.span,
}
} else {
self.emit_bad_parenthesized_trait_in_assoc_ty(data);
// FIXME(return_type_notation): we could issue a feature error
// if the parens are empty and there's no return type.
self.lower_angle_bracketed_parameter_data(
&data.as_angle_bracketed_args(),
ParamMode::Explicit,
itctx,
)
.0
}
}
};
gen_args_ctor.into_generic_args(self)
Expand Down Expand Up @@ -2075,7 +2117,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let future_args = self.arena.alloc(hir::GenericArgs {
args: &[],
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
parenthesized: false,
parenthesized: hir::GenericArgsParentheses::No,
span_ext: DUMMY_SP,
});

Expand Down Expand Up @@ -2595,13 +2637,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
struct GenericArgsCtor<'hir> {
args: SmallVec<[hir::GenericArg<'hir>; 4]>,
bindings: &'hir [hir::TypeBinding<'hir>],
parenthesized: bool,
parenthesized: hir::GenericArgsParentheses,
span: Span,
}

impl<'hir> GenericArgsCtor<'hir> {
fn is_empty(&self) -> bool {
self.args.is_empty() && self.bindings.is_empty() && !self.parenthesized
self.args.is_empty()
&& self.bindings.is_empty()
&& self.parenthesized == hir::GenericArgsParentheses::No
}

fn into_generic_args(self, this: &LoweringContext<'_, 'hir>) -> &'hir hir::GenericArgs<'hir> {
Expand Down
38 changes: 29 additions & 9 deletions compiler/rustc_ast_lowering/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, DUMMY_SP};

use smallvec::{smallvec, SmallVec};
use thin_vec::ThinVec;

impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[instrument(level = "trace", skip(self))]
Expand Down Expand Up @@ -51,18 +52,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let parenthesized_generic_args = match base_res {
// `a::b::Trait(Args)`
Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
ParenthesizedGenericArgs::Ok
ParenthesizedGenericArgs::ParenSugar
}
// `a::b::Trait(Args)::TraitItem`
Res::Def(DefKind::AssocFn, _)
| Res::Def(DefKind::AssocConst, _)
| Res::Def(DefKind::AssocTy, _)
if i + 2 == proj_start =>
{
ParenthesizedGenericArgs::Ok
ParenthesizedGenericArgs::ParenSugar
}
// Avoid duplicated errors.
Res::Err => ParenthesizedGenericArgs::Ok,
Res::Err => ParenthesizedGenericArgs::ParenSugar,
// An error
_ => ParenthesizedGenericArgs::Err,
};
Expand Down Expand Up @@ -180,7 +181,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
}
GenericArgs::Parenthesized(data) => match parenthesized_generic_args {
ParenthesizedGenericArgs::Ok => {
ParenthesizedGenericArgs::ParenSugar => {
self.lower_parenthesized_parameter_data(data, itctx)
}
ParenthesizedGenericArgs::Err => {
Expand Down Expand Up @@ -218,13 +219,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
)
}
},
&GenericArgs::ReturnTypeNotation(span) => {
self.tcx.sess.emit_err(GenericTypeWithParentheses { span, sub: None });
(
self.lower_angle_bracketed_parameter_data(
&AngleBracketedArgs { span, args: ThinVec::default() },
param_mode,
itctx,
)
.0,
false,
)
}
}
} else {
(
GenericArgsCtor {
args: Default::default(),
bindings: &[],
parenthesized: false,
parenthesized: hir::GenericArgsParentheses::No,
span: path_span.shrink_to_hi(),
},
param_mode == ParamMode::Optional,
Expand All @@ -233,7 +246,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

let has_lifetimes =
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
if !generic_args.parenthesized && !has_lifetimes {

// FIXME(return_type_notation): Is this correct? I think so.
if generic_args.parenthesized != hir::GenericArgsParentheses::ParenSugar && !has_lifetimes {
self.maybe_insert_elided_lifetimes_in_path(
path_span,
segment.id,
Expand Down Expand Up @@ -328,7 +343,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
AngleBracketedArg::Constraint(c) => Some(self.lower_assoc_ty_constraint(c, itctx)),
AngleBracketedArg::Arg(_) => None,
}));
let ctor = GenericArgsCtor { args, bindings, parenthesized: false, span: data.span };
let ctor = GenericArgsCtor {
args,
bindings,
parenthesized: hir::GenericArgsParentheses::No,
span: data.span,
};
(ctor, !has_non_lt_args && param_mode == ParamMode::Optional)
}

Expand Down Expand Up @@ -376,7 +396,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
GenericArgsCtor {
args,
bindings: arena_vec![self; binding],
parenthesized: true,
parenthesized: hir::GenericArgsParentheses::ParenSugar,
span: data.inputs_span,
},
false,
Expand All @@ -396,7 +416,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let gen_args = self.arena.alloc(hir::GenericArgs {
args,
bindings,
parenthesized: false,
parenthesized: hir::GenericArgsParentheses::No,
span_ext: DUMMY_SP,
});
hir::TypeBinding {
Expand Down
14 changes: 9 additions & 5 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.with_impl_trait(None, |this| this.visit_ty(ty));
}
}
GenericArgs::ReturnTypeNotation(_span) => {}
}
}

Expand Down Expand Up @@ -1387,16 +1388,19 @@ fn deny_equality_constraints(
match &mut assoc_path.segments[len].args {
Some(args) => match args.deref_mut() {
GenericArgs::Parenthesized(_) => continue,
GenericArgs::ReturnTypeNotation(_span) => continue,
GenericArgs::AngleBracketed(args) => {
args.args.push(arg);
}
},
empty_args => {
*empty_args = AngleBracketedArgs {
span: ident.span,
args: thin_vec![arg],
}
.into();
*empty_args = Some(
AngleBracketedArgs {
span: ident.span,
args: thin_vec![arg],
}
.into(),
);
}
}
err.assoc = Some(errors::AssociatedSuggestion {
Expand Down
29 changes: 23 additions & 6 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,12 +482,28 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {

fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) {
if let AssocConstraintKind::Bound { .. } = constraint.kind {
gate_feature_post!(
&self,
associated_type_bounds,
constraint.span,
"associated type bounds are unstable"
)
if let Some(args) = constraint.gen_args.as_ref()
&& matches!(
args,
ast::GenericArgs::ReturnTypeNotation(..) | ast::GenericArgs::Parenthesized(..)
)
{
// RTN is gated elsewhere, and parenthesized args will turn into
// another error.
if matches!(args, ast::GenericArgs::Parenthesized(..)) {
self.sess.delay_span_bug(
constraint.span,
"should have emitted a parenthesized generics error",
);
}
} else {
gate_feature_post!(
&self,
associated_type_bounds,
constraint.span,
"associated type bounds are unstable"
)
}
}
visit::walk_assoc_constraint(self, constraint)
}
Expand Down Expand Up @@ -577,6 +593,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(yeet_expr, "`do yeet` expression is experimental");
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(return_type_notation, "return type notation is experimental");

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

0 comments on commit 7402519

Please sign in to comment.