mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
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`
This commit is contained in:
commit
7402519c63
@ -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 {
|
||||
@ -178,6 +181,7 @@ impl GenericArgs {
|
||||
match self {
|
||||
AngleBracketed(data) => data.span,
|
||||
Parenthesized(data) => data.span,
|
||||
ReturnTypeNotation(span) => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,6 +481,7 @@ where
|
||||
walk_list!(visitor, visit_ty, &data.inputs);
|
||||
walk_fn_ret_ty(visitor, &data.output);
|
||||
}
|
||||
GenericArgs::ReturnTypeNotation(_span) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
@ -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};
|
||||
@ -482,7 +482,7 @@ enum ParamMode {
|
||||
}
|
||||
|
||||
enum ParenthesizedGenericArgs {
|
||||
Ok,
|
||||
ParenSugar,
|
||||
Err,
|
||||
}
|
||||
|
||||
@ -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)
|
||||
@ -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,
|
||||
});
|
||||
|
||||
@ -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> {
|
||||
|
@ -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))]
|
||||
@ -51,7 +52,7 @@ 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, _)
|
||||
@ -59,10 +60,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
| 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,
|
||||
};
|
||||
@ -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 => {
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
@ -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 {
|
||||
|
@ -1075,6 +1075,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
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 {
|
||||
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 {
|
||||
|
@ -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)
|
||||
}
|
||||
@ -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).
|
||||
|
@ -936,6 +936,10 @@ impl<'a> PrintState<'a> for State<'a> {
|
||||
self.word(")");
|
||||
self.print_fn_ret_ty(&data.output);
|
||||
}
|
||||
|
||||
ast::GenericArgs::ReturnTypeNotation(_span) => {
|
||||
self.word("(..)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ impl<'a> ExtCtxt<'a> {
|
||||
);
|
||||
let args = if !args.is_empty() {
|
||||
let args = args.into_iter().map(ast::AngleBracketedArg::Arg).collect();
|
||||
ast::AngleBracketedArgs { args, span }.into()
|
||||
Some(ast::AngleBracketedArgs { args, span }.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -495,6 +495,8 @@ declare_features! (
|
||||
(active, repr_simd, "1.4.0", Some(27731), None),
|
||||
/// Allows return-position `impl Trait` in traits.
|
||||
(incomplete, return_position_impl_trait_in_trait, "1.65.0", Some(91611), None),
|
||||
/// Allows bounding the return type of AFIT/RPITIT.
|
||||
(incomplete, return_type_notation, "CURRENT_RUSTC_VERSION", Some(109417), None),
|
||||
/// Allows `extern "rust-cold"`.
|
||||
(active, rust_cold_cc, "1.63.0", Some(97544), None),
|
||||
/// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
|
@ -328,7 +328,7 @@ pub struct GenericArgs<'hir> {
|
||||
/// Were arguments written in parenthesized form `Fn(T) -> U`?
|
||||
/// This is required mostly for pretty-printing and diagnostics,
|
||||
/// 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 `()`
|
||||
/// Foo<A, B, AssocTy = D> Fn(T, U, V) -> W
|
||||
/// ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^
|
||||
@ -340,11 +340,16 @@ pub struct GenericArgs<'hir> {
|
||||
|
||||
impl<'hir> GenericArgs<'hir> {
|
||||
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>] {
|
||||
if self.parenthesized {
|
||||
if self.parenthesized == GenericArgsParentheses::ParenSugar {
|
||||
for arg in self.args {
|
||||
match arg {
|
||||
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
|
||||
/// modifier is `Maybe`. Negative bounds should also be handled here.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)]
|
||||
|
@ -178,3 +178,14 @@ hir_analysis_invalid_union_field =
|
||||
|
||||
hir_analysis_invalid_union_field_sugg =
|
||||
wrap the field type in `ManuallyDrop<...>`
|
||||
|
||||
hir_analysis_return_type_notation_on_non_rpitit =
|
||||
return type notation used on function that is not `async` and does not return `impl Trait`
|
||||
.note = function returns `{$ty}`, which is not compatible with associated type return bounds
|
||||
.label = this function must be `async` or return `impl Trait`
|
||||
|
||||
hir_analysis_return_type_notation_equality_bound =
|
||||
return type notation is not allowed to use type equality
|
||||
|
||||
hir_analysis_return_type_notation_missing_method =
|
||||
cannot find associated function `{$assoc_name}` in trait `{$trait_name}`
|
||||
|
@ -55,7 +55,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
|
||||
let trait_def = self.tcx().trait_def(trait_def_id);
|
||||
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.
|
||||
let mut err = feature_err(
|
||||
&self.tcx().sess.parse_sess,
|
||||
@ -71,7 +71,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
|
||||
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.
|
||||
let mut err = feature_err(
|
||||
&sess.parse_sess,
|
||||
@ -607,11 +607,19 @@ pub fn prohibit_assoc_ty_binding(
|
||||
span: Span,
|
||||
segment: Option<(&hir::PathSegment<'_>, Span)>,
|
||||
) {
|
||||
tcx.sess.emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment && segment.args().parenthesized {
|
||||
Some(ParenthesizedFnTraitExpansion { span, expanded_type: fn_trait_to_string(tcx, segment, false) })
|
||||
} else {
|
||||
None
|
||||
}});
|
||||
tcx.sess.emit_err(AssocTypeBindingNotAllowed {
|
||||
span,
|
||||
fn_trait_expansion: if let Some((segment, span)) = segment
|
||||
&& 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(
|
||||
|
@ -854,16 +854,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
)
|
||||
}
|
||||
|
||||
fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
|
||||
fn trait_defines_associated_item_named(
|
||||
&self,
|
||||
trait_def_id: DefId,
|
||||
assoc_kind: ty::AssocKind,
|
||||
assoc_name: Ident,
|
||||
) -> bool {
|
||||
self.tcx()
|
||||
.associated_items(trait_def_id)
|
||||
.find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id)
|
||||
.is_some()
|
||||
}
|
||||
fn trait_defines_associated_const_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
|
||||
self.tcx()
|
||||
.associated_items(trait_def_id)
|
||||
.find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Const, trait_def_id)
|
||||
.find_by_name_and_kind(self.tcx(), assoc_name, assoc_kind, trait_def_id)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
@ -1087,24 +1086,44 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
|
||||
let tcx = self.tcx();
|
||||
|
||||
let candidate =
|
||||
if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
|
||||
// Simple case: X is defined in the current trait.
|
||||
let return_type_notation =
|
||||
binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
|
||||
|
||||
let candidate = if return_type_notation {
|
||||
if self.trait_defines_associated_item_named(
|
||||
trait_ref.def_id(),
|
||||
ty::AssocKind::Fn,
|
||||
binding.item_name,
|
||||
) {
|
||||
trait_ref
|
||||
} else {
|
||||
// Otherwise, we have to walk through the supertraits to find
|
||||
// those that do.
|
||||
self.one_bound_for_assoc_type(
|
||||
|| traits::supertraits(tcx, trait_ref),
|
||||
trait_ref.print_only_trait_path(),
|
||||
binding.item_name,
|
||||
path_span,
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(term) => Some(term),
|
||||
_ => None,
|
||||
},
|
||||
)?
|
||||
};
|
||||
return Err(tcx.sess.emit_err(crate::errors::ReturnTypeNotationMissingMethod {
|
||||
span: binding.span,
|
||||
trait_name: tcx.item_name(trait_ref.def_id()),
|
||||
assoc_name: binding.item_name.name,
|
||||
}));
|
||||
}
|
||||
} else if self.trait_defines_associated_item_named(
|
||||
trait_ref.def_id(),
|
||||
ty::AssocKind::Type,
|
||||
binding.item_name,
|
||||
) {
|
||||
// Simple case: X is defined in the current trait.
|
||||
trait_ref
|
||||
} else {
|
||||
// Otherwise, we have to walk through the supertraits to find
|
||||
// those that do.
|
||||
self.one_bound_for_assoc_type(
|
||||
|| traits::supertraits(tcx, trait_ref),
|
||||
trait_ref.print_only_trait_path(),
|
||||
binding.item_name,
|
||||
path_span,
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(term) => Some(term),
|
||||
_ => None,
|
||||
},
|
||||
)?
|
||||
};
|
||||
|
||||
let (assoc_ident, def_scope) =
|
||||
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
|
||||
@ -1116,9 +1135,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
.filter_by_name_unhygienic(assoc_ident.name)
|
||||
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
|
||||
};
|
||||
let assoc_item = find_item_of_kind(ty::AssocKind::Type)
|
||||
.or_else(|| find_item_of_kind(ty::AssocKind::Const))
|
||||
.expect("missing associated type");
|
||||
let assoc_item = if return_type_notation {
|
||||
find_item_of_kind(ty::AssocKind::Fn)
|
||||
} else {
|
||||
find_item_of_kind(ty::AssocKind::Type)
|
||||
.or_else(|| find_item_of_kind(ty::AssocKind::Const))
|
||||
}
|
||||
.expect("missing associated type");
|
||||
|
||||
if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
|
||||
tcx.sess
|
||||
@ -1135,7 +1158,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
dup_bindings
|
||||
.entry(assoc_item.def_id)
|
||||
.and_modify(|prev_span| {
|
||||
self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
|
||||
tcx.sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
|
||||
span: binding.span,
|
||||
prev_span: *prev_span,
|
||||
item_name: binding.item_name,
|
||||
@ -1145,28 +1168,100 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
.or_insert(binding.span);
|
||||
}
|
||||
|
||||
// Include substitutions for generic parameters of associated types
|
||||
let projection_ty = candidate.map_bound(|trait_ref| {
|
||||
let ident = Ident::new(assoc_item.name, binding.item_name.span);
|
||||
let item_segment = hir::PathSegment {
|
||||
ident,
|
||||
hir_id: binding.hir_id,
|
||||
res: Res::Err,
|
||||
args: Some(binding.gen_args),
|
||||
infer_args: false,
|
||||
let projection_ty = if return_type_notation {
|
||||
// If we have an method return type bound, then we need to substitute
|
||||
// the method's early bound params with suitable late-bound params.
|
||||
let mut num_bound_vars = candidate.bound_vars().len();
|
||||
let substs =
|
||||
candidate.skip_binder().substs.extend_to(tcx, assoc_item.def_id, |param, _| {
|
||||
let subst = match param.kind {
|
||||
GenericParamDefKind::Lifetime => tcx
|
||||
.mk_re_late_bound(
|
||||
ty::INNERMOST,
|
||||
ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(num_bound_vars),
|
||||
kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name),
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
GenericParamDefKind::Type { .. } => tcx
|
||||
.mk_bound(
|
||||
ty::INNERMOST,
|
||||
ty::BoundTy {
|
||||
var: ty::BoundVar::from_usize(num_bound_vars),
|
||||
kind: ty::BoundTyKind::Param(param.def_id, param.name),
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
GenericParamDefKind::Const { .. } => {
|
||||
let ty = tcx
|
||||
.type_of(param.def_id)
|
||||
.no_bound_vars()
|
||||
.expect("ct params cannot have early bound vars");
|
||||
tcx.mk_const(
|
||||
ty::ConstKind::Bound(
|
||||
ty::INNERMOST,
|
||||
ty::BoundVar::from_usize(num_bound_vars),
|
||||
),
|
||||
ty,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
};
|
||||
num_bound_vars += 1;
|
||||
subst
|
||||
});
|
||||
|
||||
// Next, we need to check that the return-type notation is being used on
|
||||
// an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait).
|
||||
let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output();
|
||||
let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind()
|
||||
&& tcx.def_kind(alias_ty.def_id) == DefKind::ImplTraitPlaceholder
|
||||
{
|
||||
alias_ty
|
||||
} else {
|
||||
return Err(self.tcx().sess.emit_err(
|
||||
crate::errors::ReturnTypeNotationOnNonRpitit {
|
||||
span: binding.span,
|
||||
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
|
||||
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
|
||||
note: (),
|
||||
},
|
||||
));
|
||||
};
|
||||
|
||||
let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
|
||||
path_span,
|
||||
assoc_item.def_id,
|
||||
&item_segment,
|
||||
trait_ref.substs,
|
||||
);
|
||||
// Finally, move the fn return type's bound vars over to account for the early bound
|
||||
// params (and trait ref's late bound params). This logic is very similar to
|
||||
// `Predicate::subst_supertrait`, and it's no coincidence why.
|
||||
let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output);
|
||||
let subst_output = ty::EarlyBinder(shifted_output).subst(tcx, substs);
|
||||
|
||||
debug!(?substs_trait_ref_and_assoc_item);
|
||||
let bound_vars = tcx.late_bound_vars(binding.hir_id);
|
||||
ty::Binder::bind_with_vars(subst_output, bound_vars)
|
||||
} else {
|
||||
// Include substitutions for generic parameters of associated types
|
||||
candidate.map_bound(|trait_ref| {
|
||||
let ident = Ident::new(assoc_item.name, binding.item_name.span);
|
||||
let item_segment = hir::PathSegment {
|
||||
ident,
|
||||
hir_id: binding.hir_id,
|
||||
res: Res::Err,
|
||||
args: Some(binding.gen_args),
|
||||
infer_args: false,
|
||||
};
|
||||
|
||||
self.tcx().mk_alias_ty(assoc_item.def_id, substs_trait_ref_and_assoc_item)
|
||||
});
|
||||
let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
|
||||
path_span,
|
||||
assoc_item.def_id,
|
||||
&item_segment,
|
||||
trait_ref.substs,
|
||||
);
|
||||
|
||||
debug!(?substs_trait_ref_and_assoc_item);
|
||||
|
||||
tcx.mk_alias_ty(assoc_item.def_id, substs_trait_ref_and_assoc_item)
|
||||
})
|
||||
};
|
||||
|
||||
if !speculative {
|
||||
// Find any late-bound regions declared in `ty` that are not
|
||||
@ -1206,6 +1301,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}
|
||||
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(..) if return_type_notation => {
|
||||
return Err(self.tcx().sess.emit_err(
|
||||
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
|
||||
));
|
||||
}
|
||||
ConvertedBindingKind::Equality(mut term) => {
|
||||
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
|
||||
// the "projection predicate" for:
|
||||
@ -1267,7 +1367,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
// Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
|
||||
// parameter to have a skipped binder.
|
||||
let param_ty = tcx.mk_alias(ty::Projection, projection_ty.skip_binder());
|
||||
self.add_bounds(param_ty, ast_bounds.iter(), bounds, candidate.bound_vars());
|
||||
self.add_bounds(param_ty, ast_bounds.iter(), bounds, projection_ty.bound_vars());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -1808,10 +1908,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
where
|
||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
{
|
||||
let mut matching_candidates = all_candidates()
|
||||
.filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name));
|
||||
let mut const_candidates = all_candidates()
|
||||
.filter(|r| self.trait_defines_associated_const_named(r.def_id(), assoc_name));
|
||||
let mut matching_candidates = all_candidates().filter(|r| {
|
||||
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Type, assoc_name)
|
||||
});
|
||||
let mut const_candidates = all_candidates().filter(|r| {
|
||||
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Const, assoc_name)
|
||||
});
|
||||
|
||||
let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) {
|
||||
(Some(bound), _) => (bound, matching_candidates.next()),
|
||||
|
@ -1461,7 +1461,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
depth: usize,
|
||||
generic_args: &'tcx hir::GenericArgs<'tcx>,
|
||||
) {
|
||||
if generic_args.parenthesized {
|
||||
if generic_args.parenthesized == hir::GenericArgsParentheses::ParenSugar {
|
||||
self.visit_fn_like_elision(
|
||||
generic_args.inputs(),
|
||||
Some(generic_args.bindings[0].ty()),
|
||||
@ -1640,7 +1640,59 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
},
|
||||
s: self.scope,
|
||||
};
|
||||
if let Some(type_def_id) = type_def_id {
|
||||
// If the binding is parenthesized, then this must be `feature(return_type_notation)`.
|
||||
// In that case, introduce a binder over all of the function's early and late bound vars.
|
||||
//
|
||||
// For example, given
|
||||
// ```
|
||||
// trait Foo {
|
||||
// async fn x<'r, T>();
|
||||
// }
|
||||
// ```
|
||||
// and a bound that looks like:
|
||||
// `for<'a> T::Trait<'a, x(): for<'b> Other<'b>>`
|
||||
// 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>`.
|
||||
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
|
||||
let bound_vars = if let Some(type_def_id) = type_def_id
|
||||
&& self.tcx.def_kind(type_def_id) == DefKind::Trait
|
||||
// FIXME(return_type_notation): We could bound supertrait methods.
|
||||
&& let Some(assoc_fn) = self
|
||||
.tcx
|
||||
.associated_items(type_def_id)
|
||||
.find_by_name_and_kind(self.tcx, binding.ident, ty::AssocKind::Fn, type_def_id)
|
||||
{
|
||||
self.tcx
|
||||
.generics_of(assoc_fn.def_id)
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
|
||||
ty::BoundRegionKind::BrNamed(param.def_id, param.name),
|
||||
),
|
||||
ty::GenericParamDefKind::Type { .. } => ty::BoundVariableKind::Ty(
|
||||
ty::BoundTyKind::Param(param.def_id, param.name),
|
||||
),
|
||||
ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
|
||||
})
|
||||
.chain(self.tcx.fn_sig(assoc_fn.def_id).subst_identity().bound_vars())
|
||||
.collect()
|
||||
} else {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
binding.ident.span,
|
||||
"bad return type notation here",
|
||||
);
|
||||
vec![]
|
||||
};
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::Supertrait { bound_vars, s: this.scope };
|
||||
this.with(scope, |this| {
|
||||
let (bound_vars, _) = this.poly_trait_ref_binder_info();
|
||||
this.record_late_bound_vars(binding.hir_id, bound_vars);
|
||||
this.visit_assoc_type_binding(binding)
|
||||
});
|
||||
});
|
||||
} else if let Some(type_def_id) = type_def_id {
|
||||
let bound_vars =
|
||||
BoundVarContext::supertrait_hrtb_vars(self.tcx, type_def_id, binding.ident);
|
||||
self.with(scope, |this| {
|
||||
|
@ -471,6 +471,18 @@ pub(crate) struct InvalidUnionField {
|
||||
pub note: (),
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_return_type_notation_on_non_rpitit)]
|
||||
pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub ty: Ty<'tcx>,
|
||||
#[label]
|
||||
pub fn_span: Option<Span>,
|
||||
#[note]
|
||||
pub note: (),
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(hir_analysis_invalid_union_field_sugg, applicability = "machine-applicable")]
|
||||
pub(crate) struct InvalidUnionFieldSuggestion {
|
||||
@ -479,3 +491,19 @@ pub(crate) struct InvalidUnionFieldSuggestion {
|
||||
#[suggestion_part(code = ">")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_return_type_notation_equality_bound)]
|
||||
pub(crate) struct ReturnTypeNotationEqualityBound {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_return_type_notation_missing_method)]
|
||||
pub(crate) struct ReturnTypeNotationMissingMethod {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: Symbol,
|
||||
pub assoc_name: Symbol,
|
||||
}
|
||||
|
@ -565,7 +565,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||
/// type Map = HashMap<String>;
|
||||
/// ```
|
||||
fn suggest_adding_args(&self, err: &mut Diagnostic) {
|
||||
if self.gen_args.parenthesized {
|
||||
if self.gen_args.parenthesized != hir::GenericArgsParentheses::No {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -962,7 +962,11 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||
|
||||
let msg = format!(
|
||||
"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);
|
||||
|
@ -1652,61 +1652,65 @@ impl<'a> State<'a> {
|
||||
generic_args: &hir::GenericArgs<'_>,
|
||||
colons_before_params: bool,
|
||||
) {
|
||||
if generic_args.parenthesized {
|
||||
self.word("(");
|
||||
self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(ty));
|
||||
self.word(")");
|
||||
match generic_args.parenthesized {
|
||||
hir::GenericArgsParentheses::No => {
|
||||
let start = if colons_before_params { "::<" } else { "<" };
|
||||
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();
|
||||
self.word_space("->");
|
||||
self.print_type(generic_args.bindings[0].ty());
|
||||
} else {
|
||||
let start = if colons_before_params { "::<" } else { "<" };
|
||||
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(",")
|
||||
}
|
||||
};
|
||||
let mut nonelided_generic_args: bool = false;
|
||||
let elide_lifetimes = generic_args.args.iter().all(|arg| match arg {
|
||||
GenericArg::Lifetime(lt) if lt.is_elided() => true,
|
||||
GenericArg::Lifetime(_) => {
|
||||
nonelided_generic_args = true;
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
nonelided_generic_args = true;
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
let mut nonelided_generic_args: bool = false;
|
||||
let elide_lifetimes = generic_args.args.iter().all(|arg| match arg {
|
||||
GenericArg::Lifetime(lt) if lt.is_elided() => true,
|
||||
GenericArg::Lifetime(_) => {
|
||||
nonelided_generic_args = true;
|
||||
false
|
||||
if nonelided_generic_args {
|
||||
start_or_comma(self);
|
||||
self.commasep(Inconsistent, generic_args.args, |s, generic_arg| {
|
||||
match generic_arg {
|
||||
GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt),
|
||||
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 {
|
||||
start_or_comma(self);
|
||||
self.commasep(
|
||||
Inconsistent,
|
||||
generic_args.args,
|
||||
|s, generic_arg| match generic_arg {
|
||||
GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt),
|
||||
GenericArg::Lifetime(_) => {}
|
||||
GenericArg::Type(ty) => s.print_type(ty),
|
||||
GenericArg::Const(ct) => s.print_anon_const(&ct.value),
|
||||
GenericArg::Infer(_inf) => s.word("_"),
|
||||
},
|
||||
);
|
||||
for binding in generic_args.bindings {
|
||||
start_or_comma(self);
|
||||
self.print_type_binding(binding);
|
||||
}
|
||||
|
||||
if !empty.get() {
|
||||
self.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 {
|
||||
start_or_comma(self);
|
||||
self.print_type_binding(binding);
|
||||
self.space_if_not_bol();
|
||||
self.word_space("->");
|
||||
self.print_type(generic_args.bindings[0].ty());
|
||||
}
|
||||
|
||||
if !empty.get() {
|
||||
self.word(">")
|
||||
hir::GenericArgsParentheses::ReturnTypeNotation => {
|
||||
self.word("(..)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -734,3 +734,7 @@ parse_unknown_start_of_token = unknown start of token: {$escaped}
|
||||
|
||||
parse_box_syntax_removed = `box_syntax` has been removed
|
||||
.suggestion = use `Box::new()` instead
|
||||
|
||||
parse_bad_return_type_notation_output =
|
||||
return type not allowed with return type notation
|
||||
.suggestion = remove the return type
|
||||
|
@ -2316,3 +2316,11 @@ pub struct BoxSyntaxRemoved<'a> {
|
||||
pub span: Span,
|
||||
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,
|
||||
}
|
||||
|
@ -989,8 +989,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
|
||||
// Recover from bad turbofish: `foo.collect::Vec<_>()`.
|
||||
let args = AngleBracketedArgs { args, span }.into();
|
||||
segment.args = args;
|
||||
segment.args = Some(AngleBracketedArgs { args, span }.into());
|
||||
|
||||
self.sess.emit_err(GenericParamsWithoutAngleBrackets {
|
||||
span,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
|
||||
use super::{Parser, Restrictions, TokenType};
|
||||
use crate::maybe_whole;
|
||||
use crate::{errors, maybe_whole};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::{
|
||||
@ -290,6 +290,25 @@ impl<'a> Parser<'a> {
|
||||
})?;
|
||||
let span = lo.to(self.prev_token.span);
|
||||
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 {
|
||||
// `(T, U) -> R`
|
||||
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()
|
||||
};
|
||||
|
||||
PathSegment { ident, args, id: ast::DUMMY_NODE_ID }
|
||||
PathSegment { ident, args: Some(args), id: ast::DUMMY_NODE_ID }
|
||||
} else {
|
||||
// Generic arguments are not found.
|
||||
PathSegment::from_ident(ident)
|
||||
@ -550,7 +569,13 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// Gate associated type bounds, e.g., `Iterator<Item: Ord>`.
|
||||
if let AssocConstraintKind::Bound { .. } = kind {
|
||||
self.sess.gated_spans.gate(sym::associated_type_bounds, span);
|
||||
if gen_args.as_ref().map_or(false, |args| {
|
||||
matches!(args, GenericArgs::ReturnTypeNotation(..))
|
||||
}) {
|
||||
// This is already gated in `parse_path_segment`
|
||||
} else {
|
||||
self.sess.gated_spans.gate(sym::associated_type_bounds, span);
|
||||
}
|
||||
}
|
||||
let constraint =
|
||||
AssocConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span };
|
||||
|
@ -1059,8 +1059,11 @@ impl<'a> Parser<'a> {
|
||||
output,
|
||||
}
|
||||
.into();
|
||||
*fn_path_segment =
|
||||
ast::PathSegment { ident: fn_path_segment.ident, args, id: ast::DUMMY_NODE_ID };
|
||||
*fn_path_segment = ast::PathSegment {
|
||||
ident: fn_path_segment.ident,
|
||||
args: Some(args),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
};
|
||||
|
||||
// Convert parsed `<'a>` in `Fn<'a>` into `for<'a>`.
|
||||
let mut generic_params = lifetimes
|
||||
|
@ -666,7 +666,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
|
||||
fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
|
||||
record_variants!(
|
||||
(self, g, g, Id::None, ast, GenericArgs, GenericArgs),
|
||||
[AngleBracketed, Parenthesized]
|
||||
[AngleBracketed, Parenthesized, ReturnTypeNotation]
|
||||
);
|
||||
ast_visit::walk_generic_args(self, g)
|
||||
}
|
||||
|
@ -1110,6 +1110,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||
}
|
||||
}
|
||||
}
|
||||
GenericArgs::ReturnTypeNotation(_span) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -312,6 +312,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
|
||||
(args.span, found_lifetimes)
|
||||
}
|
||||
GenericArgs::Parenthesized(args) => (args.span, true),
|
||||
GenericArgs::ReturnTypeNotation(span) => (*span, false),
|
||||
}
|
||||
} else {
|
||||
(DUMMY_SP, false)
|
||||
|
@ -1196,6 +1196,7 @@ symbols! {
|
||||
residual,
|
||||
result,
|
||||
return_position_impl_trait_in_trait,
|
||||
return_type_notation,
|
||||
rhs,
|
||||
rintf32,
|
||||
rintf64,
|
||||
|
@ -2002,7 +2002,8 @@ fn clean_generic_args<'tcx>(
|
||||
generic_args: &hir::GenericArgs<'tcx>,
|
||||
cx: &mut DocContext<'tcx>,
|
||||
) -> 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 = if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None };
|
||||
let inputs =
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::last_path_segment;
|
||||
use clippy_utils::source::snippet;
|
||||
use if_chain::if_chain;
|
||||
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_session::{declare_lint_pass, declare_tool_lint};
|
||||
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 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 {
|
||||
GenericArg::Type(inner_ty) => Some(inner_ty),
|
||||
_ => None,
|
||||
|
@ -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 [ref bx] = *path.segments;
|
||||
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 {
|
||||
GenericArg::Type(ty) => Some(ty),
|
||||
_ => None,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::last_path_segment;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{GenericArg, QPath, TyKind};
|
||||
use rustc_hir::{GenericArg, GenericArgsParentheses, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
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);
|
||||
if_chain! {
|
||||
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 {
|
||||
GenericArg::Type(ty) => Some(ty),
|
||||
_ => None,
|
||||
|
@ -10,7 +10,7 @@ use rustc_hir::{
|
||||
def::{CtorOf, DefKind, Res},
|
||||
def_id::LocalDefId,
|
||||
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,
|
||||
};
|
||||
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;
|
||||
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
|
||||
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 !is_from_proc_macro(cx, item); // expensive, should be last check
|
||||
|
@ -401,14 +401,9 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
}
|
||||
|
||||
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.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 {
|
||||
false
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
// edition: 2021
|
||||
|
||||
#![feature(return_type_notation, async_fn_in_trait)]
|
||||
//~^ WARN the feature `return_type_notation` is incomplete
|
||||
//~| WARN the feature `async_fn_in_trait` is incomplete
|
||||
|
||||
trait Trait {
|
||||
async fn method() {}
|
||||
}
|
||||
|
||||
fn foo<T: Trait<method(i32): Send>>() {}
|
||||
//~^ ERROR argument types not allowed with return type notation
|
||||
|
||||
fn bar<T: Trait<method(..) -> (): Send>>() {}
|
||||
//~^ 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() {}
|
@ -0,0 +1,37 @@
|
||||
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
|
||||
--> $DIR/bad-inputs-and-output.rs:3:12
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/bad-inputs-and-output.rs:3:34
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
|
||||
|
||||
error: argument types not allowed with return type notation
|
||||
--> $DIR/bad-inputs-and-output.rs:11:23
|
||||
|
|
||||
LL | fn foo<T: Trait<method(i32): Send>>() {}
|
||||
| ^^^^^ help: remove the input types: `(..)`
|
||||
|
||||
error: return type notation arguments must be elided with `..`
|
||||
--> $DIR/bad-inputs-and-output.rs:17:23
|
||||
|
|
||||
LL | fn baz<T: Trait<method(): Send>>() {}
|
||||
| ^^ help: add `..`: `(..)`
|
||||
|
||||
error: aborting due to 3 previous errors; 2 warnings emitted
|
||||
|
@ -0,0 +1,28 @@
|
||||
// revisions: with without
|
||||
// edition: 2021
|
||||
//[with] check-pass
|
||||
|
||||
#![feature(return_type_notation, async_fn_in_trait)]
|
||||
//~^ WARN the feature `return_type_notation` is incomplete
|
||||
//~| WARN the feature `async_fn_in_trait` is incomplete
|
||||
|
||||
trait Foo {
|
||||
async fn method() -> Result<(), ()>;
|
||||
}
|
||||
|
||||
async fn foo<T: Foo>() -> Result<(), ()> {
|
||||
T::method().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_send(_: impl Send) {}
|
||||
|
||||
fn test<
|
||||
#[cfg(with)] T: Foo<method(..): Send>,
|
||||
#[cfg(without)] T: Foo,
|
||||
>() {
|
||||
is_send(foo::<T>());
|
||||
//[without]~^ ERROR future cannot be sent between threads safely
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,19 @@
|
||||
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/basic.rs:5:12
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/basic.rs:5:34
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
|
||||
|
||||
warning: 2 warnings emitted
|
||||
|
@ -0,0 +1,37 @@
|
||||
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/basic.rs:5:12
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/basic.rs:5:34
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
|
||||
|
||||
error: future cannot be sent between threads safely
|
||||
--> $DIR/basic.rs:24:13
|
||||
|
|
||||
LL | is_send(foo::<T>());
|
||||
| ^^^^^^^^^^ future returned by `foo` is not `Send`
|
||||
|
|
||||
= help: within `impl Future<Output = Result<(), ()>>`, the trait `Send` is not implemented for `impl Future<Output = Result<(), ()>>`
|
||||
note: future is not `Send` as it awaits another future which is not `Send`
|
||||
--> $DIR/basic.rs:14:5
|
||||
|
|
||||
LL | T::method().await?;
|
||||
| ^^^^^^^^^^^ await occurs here on type `impl Future<Output = Result<(), ()>>`, which is not `Send`
|
||||
note: required by a bound in `is_send`
|
||||
--> $DIR/basic.rs:18:20
|
||||
|
|
||||
LL | fn is_send(_: impl Send) {}
|
||||
| ^^^^ required by this bound in `is_send`
|
||||
|
||||
error: aborting due to previous error; 2 warnings emitted
|
||||
|
@ -0,0 +1,16 @@
|
||||
// edition: 2021
|
||||
|
||||
#![feature(return_type_notation, async_fn_in_trait)]
|
||||
//~^ WARN the feature `return_type_notation` is incomplete
|
||||
//~| WARN the feature `async_fn_in_trait` is incomplete
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
trait Trait {
|
||||
async fn method() {}
|
||||
}
|
||||
|
||||
fn test<T: Trait<method(..) = Box<dyn Future<Output = ()>>>>() {}
|
||||
//~^ ERROR return type notation is not allowed to use type equality
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,25 @@
|
||||
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/equality.rs:3:12
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/equality.rs:3:34
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
|
||||
|
||||
error: return type notation is not allowed to use type equality
|
||||
--> $DIR/equality.rs:13:18
|
||||
|
|
||||
LL | fn test<T: Trait<method(..) = Box<dyn Future<Output = ()>>>>() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error; 2 warnings emitted
|
||||
|
@ -0,0 +1,14 @@
|
||||
// edition: 2021
|
||||
|
||||
#![feature(return_type_notation, async_fn_in_trait)]
|
||||
//~^ WARN the feature `return_type_notation` is incomplete
|
||||
//~| WARN the feature `async_fn_in_trait` is incomplete
|
||||
|
||||
trait Trait {
|
||||
async fn method() {}
|
||||
}
|
||||
|
||||
fn bar<T: Trait<methid(..): Send>>() {}
|
||||
//~^ ERROR cannot find associated function `methid` in trait `Trait`
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,25 @@
|
||||
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/missing.rs:3:12
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/missing.rs:3:34
|
||||
|
|
||||
LL | #![feature(return_type_notation, async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
|
||||
|
||||
error: cannot find associated function `methid` in trait `Trait`
|
||||
--> $DIR/missing.rs:11:17
|
||||
|
|
||||
LL | fn bar<T: Trait<methid(..): Send>>() {}
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error; 2 warnings emitted
|
||||
|
@ -0,0 +1,11 @@
|
||||
#![feature(return_type_notation)]
|
||||
//~^ WARN the feature `return_type_notation` is incomplete
|
||||
|
||||
trait Trait {
|
||||
fn method() {}
|
||||
}
|
||||
|
||||
fn test<T: Trait<method(..): Send>>() {}
|
||||
//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait`
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,22 @@
|
||||
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/non-rpitit.rs:1:12
|
||||
|
|
||||
LL | #![feature(return_type_notation)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
error: return type notation used on function that is not `async` and does not return `impl Trait`
|
||||
--> $DIR/non-rpitit.rs:8:18
|
||||
|
|
||||
LL | fn method() {}
|
||||
| ----------- this function must be `async` or return `impl Trait`
|
||||
...
|
||||
LL | fn test<T: Trait<method(..): Send>>() {}
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: function returns `()`, which is not compatible with associated type return bounds
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
@ -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`.
|
@ -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`.
|
15
tests/ui/feature-gates/feature-gate-return_type_notation.rs
Normal file
15
tests/ui/feature-gates/feature-gate-return_type_notation.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// edition: 2021
|
||||
// revisions: cfg no
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
//~^ WARN the feature `async_fn_in_trait` is incomplete
|
||||
|
||||
trait Trait {
|
||||
async fn m();
|
||||
}
|
||||
|
||||
#[cfg(cfg)]
|
||||
fn foo<T: Trait<m(..): Send>>() {}
|
||||
//~^ ERROR return type notation is experimental
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user