diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index cc0fc7b8358fb..9b6bfaadef05a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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` + /// 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>> for AngleBracketedArgs { - fn into(self) -> Option> { - Some(P(GenericArgs::AngleBracketed(self))) +impl Into> for AngleBracketedArgs { + fn into(self) -> P { + P(GenericArgs::AngleBracketed(self)) } } -impl Into>> for ParenthesizedArgs { - fn into(self) -> Option> { - Some(P(GenericArgs::Parenthesized(self))) +impl Into> for ParenthesizedArgs { + fn into(self) -> P { + P(GenericArgs::Parenthesized(self)) } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 46e46ab575ef7..514978f5569a7 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -561,6 +561,7 @@ pub fn noop_visit_generic_args(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) => {} } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 608f87ab6ebf4..e5a0ad1f1e481 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -481,6 +481,7 @@ where walk_list!(visitor, visit_ty, &data.inputs); walk_fn_ret_ty(visitor, &data.output); } + GenericArgs::ReturnTypeNotation(_span) => {} } } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 3ccd84398ec27..21b2a3c22fa10 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -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 diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 5e6b6050bc0ea..f4e55619ebb18 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -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, + }, +} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0b6b02ba00fb5..c5d39634c44b7 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -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> { diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 592fc5aa6456f..1c47a969696d7 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -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 { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 93c854cc80942..44b6c77fc4195 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -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 { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 344a1e7f5e795..de94c1bc47794 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -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). diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 694d688bf1fe0..80c451d675355 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -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("(..)"); + } } } } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 8a78c3296f9cf..8a16143311b8f 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -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 }; diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index b7d280b8751c9..9c0dc938635a3 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -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. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f4b46b9a131fb..35a72f868fbcc 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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 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`, + /// 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)] diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 0105cbf36dee2..a57f398784916 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -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}` diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 156334fe785b9..672e7176fde09 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -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( diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 4ab6bb5908bcd..e25b07d93922e 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -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 { - 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 { + 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::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); + + 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, + }; - debug!(?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, + ); - self.tcx().mk_alias_ty(assoc_item.def_id, substs_trait_ref_and_assoc_item) - }); + 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` 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>, { - 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()), diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index f1769415797c1..5e4f377a1e7ea 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -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> >::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| { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index f57197edeb74d..c71ce9a0bc7cd 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -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, + #[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, +} diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index cae884ae8fb73..8f4d81ec3a935 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -565,7 +565,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// type Map = HashMap; /// ``` 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); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 63ea6c904775f..4f27c01fad2c9 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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(",") + } + }; + + 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 + } + }); - 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(",") + 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("_"), + } + }); } - }; - 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 + for binding in generic_args.bindings { + start_or_comma(self); + self.print_type_binding(binding); } - _ => { - 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(")"); - if !empty.get() { - self.word(">") + self.space_if_not_bol(); + self.word_space("->"); + self.print_type(generic_args.bindings[0].ty()); + } + hir::GenericArgsParentheses::ReturnTypeNotation => { + self.word("(..)"); } } } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 96765c296e79b..e21bbd0217bd5 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -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 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index a9d116012ae5b..aead216b61cd6 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -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, +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9544afd3d6df9..5210b8fe69d6e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -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, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index b50d2984a4ec0..f1c9f0109f82f 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -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`. 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 }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 3d9d2cc62e385..400c8dbe9bc6b 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -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 diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 47e032758f23d..ce44f709f3b7c 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -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) } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4ca54bab31a68..f66bad1d429a9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1110,6 +1110,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, } } } + GenericArgs::ReturnTypeNotation(_span) => {} } } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0e84432a5b4bf..99fad22d4a12d 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -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) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d5dc1d0b315ec..1bccc7d627ada 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1196,6 +1196,7 @@ symbols! { residual, result, return_position_impl_trait_in_trait, + return_type_notation, rhs, rintf32, rintf64, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2d247bd537bdf..1c57cd7a9461c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -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 = diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index 448a32b77c036..c984a8286eb88 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -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, diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 65dfe7637ea99..acdf54710691e 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -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, diff --git a/src/tools/clippy/clippy_lints/src/types/utils.rs b/src/tools/clippy/clippy_lints/src/types/utils.rs index 7f43b7841ff33..a30748db88fc9 100644 --- a/src/tools/clippy/clippy_lints/src/types/utils.rs +++ b/src/tools/clippy/clippy_lints/src/types/utils.rs @@ -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, diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 8ea5475fb2527..7dfb0956077e4 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -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 diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 3a6d23ca5c102..3ee7147828bd5 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -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 } diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs new file mode 100644 index 0000000000000..75aa25906aa0f --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs @@ -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>() {} +//~^ ERROR argument types not allowed with return type notation + +fn bar (): Send>>() {} +//~^ ERROR return type not allowed with return type notation + +fn baz>() {} +//~^ ERROR return type notation arguments must be elided with `..` + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr new file mode 100644 index 0000000000000..5b075a0fa2921 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr @@ -0,0 +1,37 @@ +error: return type not allowed with return type notation + --> $DIR/bad-inputs-and-output.rs:14:28 + | +LL | fn bar (): 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 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 for more information + +error: argument types not allowed with return type notation + --> $DIR/bad-inputs-and-output.rs:11:23 + | +LL | fn foo>() {} + | ^^^^^ 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>() {} + | ^^ help: add `..`: `(..)` + +error: aborting due to 3 previous errors; 2 warnings emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/basic.rs b/tests/ui/associated-type-bounds/return-type-notation/basic.rs new file mode 100644 index 0000000000000..75d1dc745d1d5 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/basic.rs @@ -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() -> Result<(), ()> { + T::method().await?; + Ok(()) +} + +fn is_send(_: impl Send) {} + +fn test< + #[cfg(with)] T: Foo, + #[cfg(without)] T: Foo, +>() { + is_send(foo::()); + //[without]~^ ERROR future cannot be sent between threads safely +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/basic.with.stderr b/tests/ui/associated-type-bounds/return-type-notation/basic.with.stderr new file mode 100644 index 0000000000000..722c774cb3394 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/basic.with.stderr @@ -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 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 for more information + +warning: 2 warnings emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr new file mode 100644 index 0000000000000..1645d8c26502a --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr @@ -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 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 for more information + +error: future cannot be sent between threads safely + --> $DIR/basic.rs:24:13 + | +LL | is_send(foo::()); + | ^^^^^^^^^^ future returned by `foo` is not `Send` + | + = help: within `impl Future>`, the trait `Send` is not implemented for `impl Future>` +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>`, 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 + diff --git a/tests/ui/associated-type-bounds/return-type-notation/equality.rs b/tests/ui/associated-type-bounds/return-type-notation/equality.rs new file mode 100644 index 0000000000000..c8fc980974ed7 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/equality.rs @@ -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>>>() {} +//~^ ERROR return type notation is not allowed to use type equality + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/equality.stderr b/tests/ui/associated-type-bounds/return-type-notation/equality.stderr new file mode 100644 index 0000000000000..cd50ff3869460 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/equality.stderr @@ -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 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 for more information + +error: return type notation is not allowed to use type equality + --> $DIR/equality.rs:13:18 + | +LL | fn test>>>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/missing.rs b/tests/ui/associated-type-bounds/return-type-notation/missing.rs new file mode 100644 index 0000000000000..1263cae44775e --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/missing.rs @@ -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>() {} +//~^ ERROR cannot find associated function `methid` in trait `Trait` + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/missing.stderr b/tests/ui/associated-type-bounds/return-type-notation/missing.stderr new file mode 100644 index 0000000000000..93111b5c36b13 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/missing.stderr @@ -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 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 for more information + +error: cannot find associated function `methid` in trait `Trait` + --> $DIR/missing.rs:11:17 + | +LL | fn bar>() {} + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.rs b/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.rs new file mode 100644 index 0000000000000..d283c6eab370d --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.rs @@ -0,0 +1,11 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Trait { + fn method() {} +} + +fn test>() {} +//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait` + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.stderr b/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.stderr new file mode 100644 index 0000000000000..38c498bc2fbf9 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.stderr @@ -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 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>() {} + | ^^^^^^^^^^^^^^^^ + | + = note: function returns `()`, which is not compatible with associated type return bounds + +error: aborting due to previous error; 1 warning emitted + diff --git a/tests/ui/feature-gates/feature-gate-return_type_notation.cfg.stderr b/tests/ui/feature-gates/feature-gate-return_type_notation.cfg.stderr new file mode 100644 index 0000000000000..85728f8e1ad4d --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-return_type_notation.cfg.stderr @@ -0,0 +1,21 @@ +error[E0658]: return type notation is experimental + --> $DIR/feature-gate-return_type_notation.rs:12:18 + | +LL | fn foo>() {} + | ^^^^ + | + = note: see issue #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 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`. diff --git a/tests/ui/feature-gates/feature-gate-return_type_notation.no.stderr b/tests/ui/feature-gates/feature-gate-return_type_notation.no.stderr new file mode 100644 index 0000000000000..85728f8e1ad4d --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-return_type_notation.no.stderr @@ -0,0 +1,21 @@ +error[E0658]: return type notation is experimental + --> $DIR/feature-gate-return_type_notation.rs:12:18 + | +LL | fn foo>() {} + | ^^^^ + | + = note: see issue #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 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`. diff --git a/tests/ui/feature-gates/feature-gate-return_type_notation.rs b/tests/ui/feature-gates/feature-gate-return_type_notation.rs new file mode 100644 index 0000000000000..b75feb130a63e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-return_type_notation.rs @@ -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>() {} +//~^ ERROR return type notation is experimental + +fn main() {}