1
Fork 0

Add (..) syntax for RTN

This commit is contained in:
Michael Goulet 2023-03-16 22:00:08 +00:00
parent 104aacb49f
commit 8b592db27a
44 changed files with 355 additions and 201 deletions

View file

@ -167,6 +167,9 @@ pub enum GenericArgs {
AngleBracketed(AngleBracketedArgs), AngleBracketed(AngleBracketedArgs),
/// The `(A, B)` and `C` in `Foo(A, B) -> C`. /// The `(A, B)` and `C` in `Foo(A, B) -> C`.
Parenthesized(ParenthesizedArgs), 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 { impl GenericArgs {
@ -174,14 +177,11 @@ impl GenericArgs {
matches!(self, AngleBracketed(..)) matches!(self, AngleBracketed(..))
} }
pub fn is_parenthesized(&self) -> bool {
matches!(self, Parenthesized(..))
}
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match self { match self {
AngleBracketed(data) => data.span, AngleBracketed(data) => data.span,
Parenthesized(data) => data.span, Parenthesized(data) => data.span,
ReturnTypeNotation(span) => *span,
} }
} }
} }
@ -235,15 +235,15 @@ impl AngleBracketedArg {
} }
} }
impl Into<Option<P<GenericArgs>>> for AngleBracketedArgs { impl Into<P<GenericArgs>> for AngleBracketedArgs {
fn into(self) -> Option<P<GenericArgs>> { fn into(self) -> P<GenericArgs> {
Some(P(GenericArgs::AngleBracketed(self))) P(GenericArgs::AngleBracketed(self))
} }
} }
impl Into<Option<P<GenericArgs>>> for ParenthesizedArgs { impl Into<P<GenericArgs>> for ParenthesizedArgs {
fn into(self) -> Option<P<GenericArgs>> { fn into(self) -> P<GenericArgs> {
Some(P(GenericArgs::Parenthesized(self))) P(GenericArgs::Parenthesized(self))
} }
} }

View file

@ -561,6 +561,7 @@ pub fn noop_visit_generic_args<T: MutVisitor>(generic_args: &mut GenericArgs, vi
match generic_args { match generic_args {
GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data), GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data),
GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data), GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data),
GenericArgs::ReturnTypeNotation(_span) => {}
} }
} }

View file

@ -481,6 +481,7 @@ where
walk_list!(visitor, visit_ty, &data.inputs); walk_list!(visitor, visit_ty, &data.inputs);
walk_fn_ret_ty(visitor, &data.output); walk_fn_ret_ty(visitor, &data.output);
} }
GenericArgs::ReturnTypeNotation(_span) => {}
} }
} }

View file

@ -144,6 +144,10 @@ ast_lowering_bad_return_type_notation_inputs =
argument types not allowed with return type notation argument types not allowed with return type notation
.suggestion = remove the input types .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 = ast_lowering_bad_return_type_notation_output =
return type not allowed with return type notation return type not allowed with return type notation
.suggestion = remove the return type .suggestion = remove the return type

View file

@ -353,7 +353,13 @@ pub enum BadReturnTypeNotation {
#[diag(ast_lowering_bad_return_type_notation_inputs)] #[diag(ast_lowering_bad_return_type_notation_inputs)]
Inputs { Inputs {
#[primary_span] #[primary_span]
#[suggestion(code = "()", applicability = "maybe-incorrect")] #[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, span: Span,
}, },
#[diag(ast_lowering_bad_return_type_notation_output)] #[diag(ast_lowering_bad_return_type_notation_output)]

View file

@ -66,7 +66,7 @@ use rustc_middle::{
span_bug, span_bug,
ty::{ResolverAstLowering, TyCtxt}, 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::hygiene::MacroKind;
use rustc_span::source_map::DesugaringKind; use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
@ -987,33 +987,56 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
GenericArgs::AngleBracketed(data) => { GenericArgs::AngleBracketed(data) => {
self.lower_angle_bracketed_parameter_data(data, ParamMode::Explicit, itctx).0 self.lower_angle_bracketed_parameter_data(data, ParamMode::Explicit, itctx).0
} }
GenericArgs::Parenthesized(data) if self.tcx.features().return_type_notation => { &GenericArgs::ReturnTypeNotation(span) => GenericArgsCtor {
if !data.inputs.is_empty() { args: Default::default(),
self.tcx.sess.emit_err(errors::BadReturnTypeNotation::Inputs { bindings: &[],
span: data.inputs_span, parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
}); span,
} else if let FnRetTy::Ty(ty) = &data.output { },
self.tcx.sess.emit_err(errors::BadReturnTypeNotation::Output {
span: data.inputs_span.shrink_to_hi().to(ty.span),
});
}
GenericArgsCtor {
args: Default::default(),
bindings: &[],
parenthesized: true,
span: data.span,
}
}
GenericArgs::Parenthesized(data) => { GenericArgs::Parenthesized(data) => {
self.emit_bad_parenthesized_trait_in_assoc_ty(data); if let Some(start_char) = constraint.ident.as_str().chars().next()
// FIXME(return_type_notation): we could issue a feature error && start_char.is_ascii_lowercase()
// if the parens are empty and there's no return type. {
self.lower_angle_bracketed_parameter_data( let mut err = if !data.inputs.is_empty() {
&data.as_angle_bracketed_args(), self.tcx.sess.create_err(errors::BadReturnTypeNotation::Inputs {
ParamMode::Explicit, span: data.inputs_span,
itctx, })
) } else if let FnRetTy::Ty(ty) = &data.output {
.0 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) gen_args_ctor.into_generic_args(self)
@ -2094,7 +2117,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let future_args = self.arena.alloc(hir::GenericArgs { let future_args = self.arena.alloc(hir::GenericArgs {
args: &[], args: &[],
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)], bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
parenthesized: false, parenthesized: hir::GenericArgsParentheses::No,
span_ext: DUMMY_SP, span_ext: DUMMY_SP,
}); });
@ -2614,13 +2637,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
struct GenericArgsCtor<'hir> { struct GenericArgsCtor<'hir> {
args: SmallVec<[hir::GenericArg<'hir>; 4]>, args: SmallVec<[hir::GenericArg<'hir>; 4]>,
bindings: &'hir [hir::TypeBinding<'hir>], bindings: &'hir [hir::TypeBinding<'hir>],
parenthesized: bool, parenthesized: hir::GenericArgsParentheses,
span: Span, span: Span,
} }
impl<'hir> GenericArgsCtor<'hir> { impl<'hir> GenericArgsCtor<'hir> {
fn is_empty(&self) -> bool { 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> { fn into_generic_args(self, this: &LoweringContext<'_, 'hir>) -> &'hir hir::GenericArgs<'hir> {

View file

@ -13,6 +13,7 @@ use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_span::{BytePos, Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use thin_vec::ThinVec;
impl<'a, 'hir> LoweringContext<'a, 'hir> { impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[instrument(level = "trace", skip(self))] #[instrument(level = "trace", skip(self))]
@ -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 { } else {
( (
GenericArgsCtor { GenericArgsCtor {
args: Default::default(), args: Default::default(),
bindings: &[], bindings: &[],
parenthesized: false, parenthesized: hir::GenericArgsParentheses::No,
span: path_span.shrink_to_hi(), span: path_span.shrink_to_hi(),
}, },
param_mode == ParamMode::Optional, param_mode == ParamMode::Optional,
@ -233,7 +246,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let has_lifetimes = let has_lifetimes =
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); 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( self.maybe_insert_elided_lifetimes_in_path(
path_span, path_span,
segment.id, segment.id,
@ -328,7 +343,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
AngleBracketedArg::Constraint(c) => Some(self.lower_assoc_ty_constraint(c, itctx)), AngleBracketedArg::Constraint(c) => Some(self.lower_assoc_ty_constraint(c, itctx)),
AngleBracketedArg::Arg(_) => None, 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) (ctor, !has_non_lt_args && param_mode == ParamMode::Optional)
} }
@ -376,7 +396,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
GenericArgsCtor { GenericArgsCtor {
args, args,
bindings: arena_vec![self; binding], bindings: arena_vec![self; binding],
parenthesized: true, parenthesized: hir::GenericArgsParentheses::ParenSugar,
span: data.inputs_span, span: data.inputs_span,
}, },
false, false,
@ -396,7 +416,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let gen_args = self.arena.alloc(hir::GenericArgs { let gen_args = self.arena.alloc(hir::GenericArgs {
args, args,
bindings, bindings,
parenthesized: false, parenthesized: hir::GenericArgsParentheses::No,
span_ext: DUMMY_SP, span_ext: DUMMY_SP,
}); });
hir::TypeBinding { hir::TypeBinding {

View file

@ -1075,6 +1075,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.with_impl_trait(None, |this| this.visit_ty(ty)); self.with_impl_trait(None, |this| this.visit_ty(ty));
} }
} }
GenericArgs::ReturnTypeNotation(_span) => {}
} }
} }
@ -1387,16 +1388,19 @@ fn deny_equality_constraints(
match &mut assoc_path.segments[len].args { match &mut assoc_path.segments[len].args {
Some(args) => match args.deref_mut() { Some(args) => match args.deref_mut() {
GenericArgs::Parenthesized(_) => continue, GenericArgs::Parenthesized(_) => continue,
GenericArgs::ReturnTypeNotation(_span) => continue,
GenericArgs::AngleBracketed(args) => { GenericArgs::AngleBracketed(args) => {
args.args.push(arg); args.args.push(arg);
} }
}, },
empty_args => { empty_args => {
*empty_args = AngleBracketedArgs { *empty_args = Some(
span: ident.span, AngleBracketedArgs {
args: thin_vec![arg], span: ident.span,
} args: thin_vec![arg],
.into(); }
.into(),
);
} }
} }
err.assoc = Some(errors::AssociatedSuggestion { err.assoc = Some(errors::AssociatedSuggestion {

View file

@ -482,13 +482,20 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) { fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) {
if let AssocConstraintKind::Bound { .. } = constraint.kind { if let AssocConstraintKind::Bound { .. } = constraint.kind {
if constraint.gen_args.as_ref().map_or(false, |args| args.is_parenthesized()) { if let Some(args) = constraint.gen_args.as_ref()
gate_feature_post!( && matches!(
&self, args,
return_type_notation, ast::GenericArgs::ReturnTypeNotation(..) | ast::GenericArgs::Parenthesized(..)
constraint.span,
"return type notation is unstable"
) )
{
// 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 { } else {
gate_feature_post!( gate_feature_post!(
&self, &self,
@ -586,6 +593,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(yeet_expr, "`do yeet` expression is experimental"); gate_all!(yeet_expr, "`do yeet` expression is experimental");
gate_all!(dyn_star, "`dyn*` trait objects are experimental"); gate_all!(dyn_star, "`dyn*` trait objects are experimental");
gate_all!(const_closures, "const closures 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, // All uses of `gate_all!` below this point were added in #65742,
// and subsequently disabled (with the non-early gating readded). // and subsequently disabled (with the non-early gating readded).

View file

@ -936,6 +936,10 @@ impl<'a> PrintState<'a> for State<'a> {
self.word(")"); self.word(")");
self.print_fn_ret_ty(&data.output); self.print_fn_ret_ty(&data.output);
} }
ast::GenericArgs::ReturnTypeNotation(_span) => {
self.word("(..)");
}
} }
} }
} }

View file

@ -36,7 +36,7 @@ impl<'a> ExtCtxt<'a> {
); );
let args = if !args.is_empty() { let args = if !args.is_empty() {
let args = args.into_iter().map(ast::AngleBracketedArg::Arg).collect(); let args = args.into_iter().map(ast::AngleBracketedArg::Arg).collect();
ast::AngleBracketedArgs { args, span }.into() Some(ast::AngleBracketedArgs { args, span }.into())
} else { } else {
None None
}; };

View file

@ -328,7 +328,7 @@ pub struct GenericArgs<'hir> {
/// Were arguments written in parenthesized form `Fn(T) -> U`? /// Were arguments written in parenthesized form `Fn(T) -> U`?
/// This is required mostly for pretty-printing and diagnostics, /// This is required mostly for pretty-printing and diagnostics,
/// but also for changing lifetime elision rules to be "function-like". /// but also for changing lifetime elision rules to be "function-like".
pub parenthesized: bool, pub parenthesized: GenericArgsParentheses,
/// The span encompassing arguments and the surrounding brackets `<>` or `()` /// The span encompassing arguments and the surrounding brackets `<>` or `()`
/// Foo<A, B, AssocTy = D> Fn(T, U, V) -> W /// Foo<A, B, AssocTy = D> Fn(T, U, V) -> W
/// ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ /// ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^
@ -340,11 +340,16 @@ pub struct GenericArgs<'hir> {
impl<'hir> GenericArgs<'hir> { impl<'hir> GenericArgs<'hir> {
pub const fn none() -> Self { pub const fn none() -> Self {
Self { args: &[], bindings: &[], parenthesized: false, span_ext: DUMMY_SP } Self {
args: &[],
bindings: &[],
parenthesized: GenericArgsParentheses::No,
span_ext: DUMMY_SP,
}
} }
pub fn inputs(&self) -> &[Ty<'hir>] { pub fn inputs(&self) -> &[Ty<'hir>] {
if self.parenthesized { if self.parenthesized == GenericArgsParentheses::ParenSugar {
for arg in self.args { for arg in self.args {
match arg { match arg {
GenericArg::Lifetime(_) => {} GenericArg::Lifetime(_) => {}
@ -417,6 +422,17 @@ impl<'hir> GenericArgs<'hir> {
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum GenericArgsParentheses {
No,
/// Bounds for `feature(return_type_notation)`, like `T: Trait<method(..): Send>`,
/// where the args are explicitly elided with `..`
ReturnTypeNotation,
/// parenthesized function-family traits, like `T: Fn(u32) -> i32`
ParenSugar,
}
/// A modifier on a bound, currently this is only used for `?Sized`, where the /// A modifier on a bound, currently this is only used for `?Sized`, where the
/// modifier is `Maybe`. Negative bounds should also be handled here. /// modifier is `Maybe`. Negative bounds should also be handled here.
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)]

View file

@ -55,7 +55,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let trait_def = self.tcx().trait_def(trait_def_id); let trait_def = self.tcx().trait_def(trait_def_id);
if !trait_def.paren_sugar { if !trait_def.paren_sugar {
if trait_segment.args().parenthesized { if trait_segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar {
// For now, require that parenthetical notation be used only with `Fn()` etc. // For now, require that parenthetical notation be used only with `Fn()` etc.
let mut err = feature_err( let mut err = feature_err(
&self.tcx().sess.parse_sess, &self.tcx().sess.parse_sess,
@ -71,7 +71,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let sess = self.tcx().sess; let sess = self.tcx().sess;
if !trait_segment.args().parenthesized { if trait_segment.args().parenthesized != hir::GenericArgsParentheses::ParenSugar {
// For now, require that parenthetical notation be used only with `Fn()` etc. // For now, require that parenthetical notation be used only with `Fn()` etc.
let mut err = feature_err( let mut err = feature_err(
&sess.parse_sess, &sess.parse_sess,
@ -607,11 +607,19 @@ pub fn prohibit_assoc_ty_binding(
span: Span, span: Span,
segment: Option<(&hir::PathSegment<'_>, Span)>, segment: Option<(&hir::PathSegment<'_>, Span)>,
) { ) {
tcx.sess.emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment && segment.args().parenthesized { tcx.sess.emit_err(AssocTypeBindingNotAllowed {
Some(ParenthesizedFnTraitExpansion { span, expanded_type: fn_trait_to_string(tcx, segment, false) }) span,
} else { fn_trait_expansion: if let Some((segment, span)) = segment
None && segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar
}}); {
Some(ParenthesizedFnTraitExpansion {
span,
expanded_type: fn_trait_to_string(tcx, segment, false),
})
} else {
None
},
});
} }
pub(crate) fn fn_trait_to_string( pub(crate) fn fn_trait_to_string(

View file

@ -1087,7 +1087,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let tcx = self.tcx(); let tcx = self.tcx();
let return_type_notation = let return_type_notation =
binding.gen_args.parenthesized && tcx.features().return_type_notation; binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
let candidate = if return_type_notation { let candidate = if return_type_notation {
if self.trait_defines_associated_item_named( if self.trait_defines_associated_item_named(

View file

@ -1461,7 +1461,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
depth: usize, depth: usize,
generic_args: &'tcx hir::GenericArgs<'tcx>, generic_args: &'tcx hir::GenericArgs<'tcx>,
) { ) {
if generic_args.parenthesized { if generic_args.parenthesized == hir::GenericArgsParentheses::ParenSugar {
self.visit_fn_like_elision( self.visit_fn_like_elision(
generic_args.inputs(), generic_args.inputs(),
Some(generic_args.bindings[0].ty()), Some(generic_args.bindings[0].ty()),
@ -1653,7 +1653,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
// `for<'a> T::Trait<'a, x(): for<'b> Other<'b>>` // `for<'a> T::Trait<'a, x(): for<'b> Other<'b>>`
// this is going to expand to something like: // this is going to expand to something like:
// `for<'a> for<'r, T> <T as Trait<'a>>::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`. // `for<'a> for<'r, T> <T as Trait<'a>>::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`.
if binding.gen_args.parenthesized { if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
let bound_vars = if let Some(type_def_id) = type_def_id let bound_vars = if let Some(type_def_id) = type_def_id
&& self.tcx.def_kind(type_def_id) == DefKind::Trait && self.tcx.def_kind(type_def_id) == DefKind::Trait
// FIXME(return_type_notation): We could bound supertrait methods. // FIXME(return_type_notation): We could bound supertrait methods.

View file

@ -565,7 +565,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
/// type Map = HashMap<String>; /// type Map = HashMap<String>;
/// ``` /// ```
fn suggest_adding_args(&self, err: &mut Diagnostic) { fn suggest_adding_args(&self, err: &mut Diagnostic) {
if self.gen_args.parenthesized { if self.gen_args.parenthesized != hir::GenericArgsParentheses::No {
return; return;
} }
@ -962,7 +962,11 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
let msg = format!( let msg = format!(
"remove these {}generics", "remove these {}generics",
if self.gen_args.parenthesized { "parenthetical " } else { "" }, if self.gen_args.parenthesized == hir::GenericArgsParentheses::ParenSugar {
"parenthetical "
} else {
""
},
); );
err.span_suggestion(span, &msg, "", Applicability::MaybeIncorrect); err.span_suggestion(span, &msg, "", Applicability::MaybeIncorrect);

View file

@ -1652,61 +1652,65 @@ impl<'a> State<'a> {
generic_args: &hir::GenericArgs<'_>, generic_args: &hir::GenericArgs<'_>,
colons_before_params: bool, colons_before_params: bool,
) { ) {
if generic_args.parenthesized { match generic_args.parenthesized {
self.word("("); hir::GenericArgsParentheses::No => {
self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(ty)); let start = if colons_before_params { "::<" } else { "<" };
self.word(")"); let empty = Cell::new(true);
let start_or_comma = |this: &mut Self| {
if empty.get() {
empty.set(false);
this.word(start)
} else {
this.word_space(",")
}
};
self.space_if_not_bol(); let mut nonelided_generic_args: bool = false;
self.word_space("->"); let elide_lifetimes = generic_args.args.iter().all(|arg| match arg {
self.print_type(generic_args.bindings[0].ty()); GenericArg::Lifetime(lt) if lt.is_elided() => true,
} else { GenericArg::Lifetime(_) => {
let start = if colons_before_params { "::<" } else { "<" }; nonelided_generic_args = true;
let empty = Cell::new(true); false
let start_or_comma = |this: &mut Self| { }
if empty.get() { _ => {
empty.set(false); nonelided_generic_args = true;
this.word(start) true
} else { }
this.word_space(",") });
}
};
let mut nonelided_generic_args: bool = false; if nonelided_generic_args {
let elide_lifetimes = generic_args.args.iter().all(|arg| match arg { start_or_comma(self);
GenericArg::Lifetime(lt) if lt.is_elided() => true, self.commasep(Inconsistent, generic_args.args, |s, generic_arg| {
GenericArg::Lifetime(_) => { match generic_arg {
nonelided_generic_args = true; GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt),
false GenericArg::Lifetime(_) => {}
GenericArg::Type(ty) => s.print_type(ty),
GenericArg::Const(ct) => s.print_anon_const(&ct.value),
GenericArg::Infer(_inf) => s.word("_"),
}
});
} }
_ => {
nonelided_generic_args = true;
true
}
});
if nonelided_generic_args { for binding in generic_args.bindings {
start_or_comma(self); start_or_comma(self);
self.commasep( self.print_type_binding(binding);
Inconsistent, }
generic_args.args,
|s, generic_arg| match generic_arg { if !empty.get() {
GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), self.word(">")
GenericArg::Lifetime(_) => {} }
GenericArg::Type(ty) => s.print_type(ty),
GenericArg::Const(ct) => s.print_anon_const(&ct.value),
GenericArg::Infer(_inf) => s.word("_"),
},
);
} }
hir::GenericArgsParentheses::ParenSugar => {
self.word("(");
self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(ty));
self.word(")");
for binding in generic_args.bindings { self.space_if_not_bol();
start_or_comma(self); self.word_space("->");
self.print_type_binding(binding); self.print_type(generic_args.bindings[0].ty());
} }
hir::GenericArgsParentheses::ReturnTypeNotation => {
if !empty.get() { self.word("(..)");
self.word(">")
} }
} }
} }

View file

@ -734,3 +734,7 @@ parse_unknown_start_of_token = unknown start of token: {$escaped}
parse_box_syntax_removed = `box_syntax` has been removed parse_box_syntax_removed = `box_syntax` has been removed
.suggestion = use `Box::new()` instead .suggestion = use `Box::new()` instead
parse_bad_return_type_notation_output =
return type not allowed with return type notation
.suggestion = remove the return type

View file

@ -2316,3 +2316,11 @@ pub struct BoxSyntaxRemoved<'a> {
pub span: Span, pub span: Span,
pub code: &'a str, pub code: &'a str,
} }
#[derive(Diagnostic)]
#[diag(parse_bad_return_type_notation_output)]
pub(crate) struct BadReturnTypeNotationOutput {
#[primary_span]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}

View file

@ -989,8 +989,7 @@ impl<'a> Parser<'a> {
} }
if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) { if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
// Recover from bad turbofish: `foo.collect::Vec<_>()`. // Recover from bad turbofish: `foo.collect::Vec<_>()`.
let args = AngleBracketedArgs { args, span }.into(); segment.args = Some(AngleBracketedArgs { args, span }.into());
segment.args = args;
self.sess.emit_err(GenericParamsWithoutAngleBrackets { self.sess.emit_err(GenericParamsWithoutAngleBrackets {
span, span,

View file

@ -1,6 +1,6 @@
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{Parser, Restrictions, TokenType}; use super::{Parser, Restrictions, TokenType};
use crate::maybe_whole; use crate::{errors, maybe_whole};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::{ use rustc_ast::{
@ -290,6 +290,25 @@ impl<'a> Parser<'a> {
})?; })?;
let span = lo.to(self.prev_token.span); let span = lo.to(self.prev_token.span);
AngleBracketedArgs { args, span }.into() AngleBracketedArgs { args, span }.into()
} else if self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
// FIXME(return_type_notation): Could also recover `...` here.
&& self.look_ahead(1, |tok| tok.kind == token::DotDot)
{
let lo = self.token.span;
self.bump();
self.bump();
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
let span = lo.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::return_type_notation, span);
if self.eat_noexpect(&token::RArrow) {
let lo = self.prev_token.span;
let ty = self.parse_ty()?;
self.sess
.emit_err(errors::BadReturnTypeNotationOutput { span: lo.to(ty.span) });
}
P(GenericArgs::ReturnTypeNotation(span))
} else { } else {
// `(T, U) -> R` // `(T, U) -> R`
let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?;
@ -300,7 +319,7 @@ impl<'a> Parser<'a> {
ParenthesizedArgs { span, inputs, inputs_span, output }.into() ParenthesizedArgs { span, inputs, inputs_span, output }.into()
}; };
PathSegment { ident, args, id: ast::DUMMY_NODE_ID } PathSegment { ident, args: Some(args), id: ast::DUMMY_NODE_ID }
} else { } else {
// Generic arguments are not found. // Generic arguments are not found.
PathSegment::from_ident(ident) PathSegment::from_ident(ident)
@ -550,8 +569,10 @@ impl<'a> Parser<'a> {
// Gate associated type bounds, e.g., `Iterator<Item: Ord>`. // Gate associated type bounds, e.g., `Iterator<Item: Ord>`.
if let AssocConstraintKind::Bound { .. } = kind { if let AssocConstraintKind::Bound { .. } = kind {
if gen_args.as_ref().map_or(false, |args| args.is_parenthesized()) { if gen_args.as_ref().map_or(false, |args| {
self.sess.gated_spans.gate(sym::return_type_notation, span); matches!(args, GenericArgs::ReturnTypeNotation(..))
}) {
// This is already gated in `parse_path_segment`
} else { } else {
self.sess.gated_spans.gate(sym::associated_type_bounds, span); self.sess.gated_spans.gate(sym::associated_type_bounds, span);
} }

View file

@ -1059,8 +1059,11 @@ impl<'a> Parser<'a> {
output, output,
} }
.into(); .into();
*fn_path_segment = *fn_path_segment = ast::PathSegment {
ast::PathSegment { ident: fn_path_segment.ident, args, id: ast::DUMMY_NODE_ID }; ident: fn_path_segment.ident,
args: Some(args),
id: ast::DUMMY_NODE_ID,
};
// Convert parsed `<'a>` in `Fn<'a>` into `for<'a>`. // Convert parsed `<'a>` in `Fn<'a>` into `for<'a>`.
let mut generic_params = lifetimes let mut generic_params = lifetimes

View file

@ -666,7 +666,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) { fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
record_variants!( record_variants!(
(self, g, g, Id::None, ast, GenericArgs, GenericArgs), (self, g, g, Id::None, ast, GenericArgs, GenericArgs),
[AngleBracketed, Parenthesized] [AngleBracketed, Parenthesized, ReturnTypeNotation]
); );
ast_visit::walk_generic_args(self, g) ast_visit::walk_generic_args(self, g)
} }

View file

@ -1110,6 +1110,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
} }
} }
} }
GenericArgs::ReturnTypeNotation(_span) => {}
} }
} }
} }

View file

@ -312,6 +312,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
(args.span, found_lifetimes) (args.span, found_lifetimes)
} }
GenericArgs::Parenthesized(args) => (args.span, true), GenericArgs::Parenthesized(args) => (args.span, true),
GenericArgs::ReturnTypeNotation(span) => (*span, false),
} }
} else { } else {
(DUMMY_SP, false) (DUMMY_SP, false)

View file

@ -2013,7 +2013,8 @@ fn clean_generic_args<'tcx>(
generic_args: &hir::GenericArgs<'tcx>, generic_args: &hir::GenericArgs<'tcx>,
cx: &mut DocContext<'tcx>, cx: &mut DocContext<'tcx>,
) -> GenericArgs { ) -> GenericArgs {
if generic_args.parenthesized { // FIXME(return_type_notation): Fix RTN parens rendering
if generic_args.parenthesized == hir::GenericArgsParentheses::ParenSugar {
let output = clean_ty(generic_args.bindings[0].ty(), cx); let output = clean_ty(generic_args.bindings[0].ty(), cx);
let output = if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None }; let output = if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None };
let inputs = let inputs =

View file

@ -3,7 +3,7 @@ use clippy_utils::last_path_segment;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_hir::{GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
if cx.tcx.is_diagnostic_item(sym::Option, def_id); if cx.tcx.is_diagnostic_item(sym::Option, def_id);
if let Some(params) = last_path_segment(qpath).args ; if let Some(params) = last_path_segment(qpath).args ;
if !params.parenthesized; if params.parenthesized == GenericArgsParentheses::No;
if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
GenericArg::Type(inner_ty) => Some(inner_ty), GenericArg::Type(inner_ty) => Some(inner_ty),
_ => None, _ => None,

View file

@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
if let QPath::Resolved(None, path) = *qpath; if let QPath::Resolved(None, path) = *qpath;
if let [ref bx] = *path.segments; if let [ref bx] = *path.segments;
if let Some(params) = bx.args; if let Some(params) = bx.args;
if !params.parenthesized; if params.parenthesized == hir::GenericArgsParentheses::No;
if let Some(inner) = params.args.iter().find_map(|arg| match arg { if let Some(inner) = params.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty), GenericArg::Type(ty) => Some(ty),
_ => None, _ => None,

View file

@ -1,6 +1,6 @@
use clippy_utils::last_path_segment; use clippy_utils::last_path_segment;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir::{GenericArg, QPath, TyKind}; use rustc_hir::{GenericArg, GenericArgsParentheses, QPath, TyKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
@ -8,7 +8,7 @@ pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>)
let last = last_path_segment(qpath); let last = last_path_segment(qpath);
if_chain! { if_chain! {
if let Some(params) = last.args; if let Some(params) = last.args;
if !params.parenthesized; if params.parenthesized == GenericArgsParentheses::No;
if let Some(ty) = params.args.iter().find_map(|arg| match arg { if let Some(ty) = params.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty), GenericArg::Type(ty) => Some(ty),
_ => None, _ => None,

View file

@ -10,7 +10,7 @@ use rustc_hir::{
def::{CtorOf, DefKind, Res}, def::{CtorOf, DefKind, Res},
def_id::LocalDefId, def_id::LocalDefId,
intravisit::{walk_inf, walk_ty, Visitor}, intravisit::{walk_inf, walk_ty, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item, Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
}; };
use rustc_hir_analysis::hir_ty_to_ty; use rustc_hir_analysis::hir_ty_to_ty;
@ -100,7 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
if parameters.as_ref().map_or(true, |params| { if parameters.as_ref().map_or(true, |params| {
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) params.parenthesized == GenericArgsParentheses::No
&& !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
}); });
if !item.span.from_expansion(); if !item.span.from_expansion();
if !is_from_proc_macro(cx, item); // expensive, should be last check if !is_from_proc_macro(cx, item); // expensive, should be last check

View file

@ -401,14 +401,9 @@ impl HirEqInterExpr<'_, '_, '_> {
} }
fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
if !(left.parenthesized || right.parenthesized) { if left.parenthesized == right.parenthesized {
over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
&& over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r)) && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r))
} else if left.parenthesized && right.parenthesized {
over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
&& both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
self.eq_ty(l, r)
})
} else { } else {
false false
} }

View file

@ -11,7 +11,10 @@ trait Trait {
fn foo<T: Trait<method(i32): Send>>() {} fn foo<T: Trait<method(i32): Send>>() {}
//~^ ERROR argument types not allowed with return type notation //~^ ERROR argument types not allowed with return type notation
fn bar<T: Trait<method() -> (): Send>>() {} fn bar<T: Trait<method(..) -> (): Send>>() {}
//~^ ERROR return type not allowed with return type notation //~^ ERROR return type not allowed with return type notation
fn baz<T: Trait<method(): Send>>() {}
//~^ ERROR return type notation arguments must be elided with `..`
fn main() {} fn main() {}

View file

@ -1,3 +1,9 @@
error: return type not allowed with return type notation
--> $DIR/bad-inputs-and-output.rs:14:28
|
LL | fn bar<T: Trait<method(..) -> (): Send>>() {}
| ^^^^^ help: remove the return type
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/bad-inputs-and-output.rs:3:12 --> $DIR/bad-inputs-and-output.rs:3:12
| |
@ -19,13 +25,13 @@ error: argument types not allowed with return type notation
--> $DIR/bad-inputs-and-output.rs:11:23 --> $DIR/bad-inputs-and-output.rs:11:23
| |
LL | fn foo<T: Trait<method(i32): Send>>() {} LL | fn foo<T: Trait<method(i32): Send>>() {}
| ^^^^^ help: remove the input types: `()` | ^^^^^ help: remove the input types: `(..)`
error: return type not allowed with return type notation error: return type notation arguments must be elided with `..`
--> $DIR/bad-inputs-and-output.rs:14:25 --> $DIR/bad-inputs-and-output.rs:17:23
| |
LL | fn bar<T: Trait<method() -> (): Send>>() {} LL | fn baz<T: Trait<method(): Send>>() {}
| ^^^^^^ help: remove the return type | ^^ help: add `..`: `(..)`
error: aborting due to 2 previous errors; 2 warnings emitted error: aborting due to 3 previous errors; 2 warnings emitted

View file

@ -18,7 +18,7 @@ async fn foo<T: Foo>() -> Result<(), ()> {
fn is_send(_: impl Send) {} fn is_send(_: impl Send) {}
fn test< fn test<
#[cfg(with)] T: Foo<method(): Send>, #[cfg(with)] T: Foo<method(..): Send>,
#[cfg(without)] T: Foo, #[cfg(without)] T: Foo,
>() { >() {
is_send(foo::<T>()); is_send(foo::<T>());

View file

@ -10,7 +10,7 @@ trait Trait {
async fn method() {} async fn method() {}
} }
fn test<T: Trait<method() = Box<dyn Future<Output = ()>>>>() {} fn test<T: Trait<method(..) = Box<dyn Future<Output = ()>>>>() {}
//~^ ERROR return type notation is not allowed to use type equality //~^ ERROR return type notation is not allowed to use type equality
fn main() {} fn main() {}

View file

@ -18,8 +18,8 @@ LL | #![feature(return_type_notation, async_fn_in_trait)]
error: return type notation is not allowed to use type equality error: return type notation is not allowed to use type equality
--> $DIR/equality.rs:13:18 --> $DIR/equality.rs:13:18
| |
LL | fn test<T: Trait<method() = Box<dyn Future<Output = ()>>>>() {} LL | fn test<T: Trait<method(..) = Box<dyn Future<Output = ()>>>>() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error; 2 warnings emitted error: aborting due to previous error; 2 warnings emitted

View file

@ -8,7 +8,7 @@ trait Trait {
async fn method() {} async fn method() {}
} }
fn bar<T: Trait<methid(): Send>>() {} fn bar<T: Trait<methid(..): Send>>() {}
//~^ ERROR cannot find associated function `methid` in trait `Trait` //~^ ERROR cannot find associated function `methid` in trait `Trait`
fn main() {} fn main() {}

View file

@ -18,8 +18,8 @@ LL | #![feature(return_type_notation, async_fn_in_trait)]
error: cannot find associated function `methid` in trait `Trait` error: cannot find associated function `methid` in trait `Trait`
--> $DIR/missing.rs:11:17 --> $DIR/missing.rs:11:17
| |
LL | fn bar<T: Trait<methid(): Send>>() {} LL | fn bar<T: Trait<methid(..): Send>>() {}
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: aborting due to previous error; 2 warnings emitted error: aborting due to previous error; 2 warnings emitted

View file

@ -5,7 +5,7 @@ trait Trait {
fn method() {} fn method() {}
} }
fn test<T: Trait<method(): Send>>() {} fn test<T: Trait<method(..): Send>>() {}
//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait` //~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait`
fn main() {} fn main() {}

View file

@ -13,8 +13,8 @@ error: return type notation used on function that is not `async` and does not re
LL | fn method() {} LL | fn method() {}
| ----------- this function must be `async` or return `impl Trait` | ----------- this function must be `async` or return `impl Trait`
... ...
LL | fn test<T: Trait<method(): Send>>() {} LL | fn test<T: Trait<method(..): Send>>() {}
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
| |
= note: function returns `()`, which is not compatible with associated type return bounds = note: function returns `()`, which is not compatible with associated type return bounds

View file

@ -0,0 +1,21 @@
error[E0658]: return type notation is experimental
--> $DIR/feature-gate-return_type_notation.rs:12:18
|
LL | fn foo<T: Trait<m(..): Send>>() {}
| ^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= help: add `#![feature(return_type_notation)]` to the crate attributes to enable
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/feature-gate-return_type_notation.rs:4:12
|
LL | #![feature(async_fn_in_trait)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= note: `#[warn(incomplete_features)]` on by default
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,21 @@
error[E0658]: return type notation is experimental
--> $DIR/feature-gate-return_type_notation.rs:12:18
|
LL | fn foo<T: Trait<m(..): Send>>() {}
| ^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= help: add `#![feature(return_type_notation)]` to the crate attributes to enable
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/feature-gate-return_type_notation.rs:4:12
|
LL | #![feature(async_fn_in_trait)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= note: `#[warn(incomplete_features)]` on by default
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View file

@ -1,4 +1,5 @@
// edition: 2021 // edition: 2021
// revisions: cfg no
#![feature(async_fn_in_trait)] #![feature(async_fn_in_trait)]
//~^ WARN the feature `async_fn_in_trait` is incomplete //~^ WARN the feature `async_fn_in_trait` is incomplete
@ -7,9 +8,8 @@ trait Trait {
async fn m(); async fn m();
} }
fn foo<T: Trait<m(): Send>>() {} #[cfg(cfg)]
//~^ ERROR parenthesized generic arguments cannot be used in associated type constraints fn foo<T: Trait<m(..): Send>>() {}
//~| ERROR associated type `m` not found for `Trait` //~^ ERROR return type notation is experimental
//~| ERROR return type notation is unstable
fn main() {} fn main() {}

View file

@ -1,36 +0,0 @@
error[E0658]: return type notation is unstable
--> $DIR/feature-gate-return_type_notation.rs:10:17
|
LL | fn foo<T: Trait<m(): Send>>() {}
| ^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= help: add `#![feature(return_type_notation)]` to the crate attributes to enable
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/feature-gate-return_type_notation.rs:3:12
|
LL | #![feature(async_fn_in_trait)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= note: `#[warn(incomplete_features)]` on by default
error: parenthesized generic arguments cannot be used in associated type constraints
--> $DIR/feature-gate-return_type_notation.rs:10:17
|
LL | fn foo<T: Trait<m(): Send>>() {}
| ^--
| |
| help: remove these parentheses
error[E0220]: associated type `m` not found for `Trait`
--> $DIR/feature-gate-return_type_notation.rs:10:17
|
LL | fn foo<T: Trait<m(): Send>>() {}
| ^ associated type `m` not found
error: aborting due to 3 previous errors; 1 warning emitted
Some errors have detailed explanations: E0220, E0658.
For more information about an error, try `rustc --explain E0220`.