From 6d788a18c5ce42071f7a6fb46e7f280b2ed648d8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 22 Aug 2024 21:51:00 -0400 Subject: [PATCH 1/7] Resolve RTN for TyKind::Path ending in (..) --- compiler/rustc_resolve/src/late.rs | 33 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ac03a3ac42c66..2d97834d05a3c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -404,6 +404,8 @@ pub(crate) enum PathSource<'a> { Delegation, /// An arg in a `use<'a, N>` precise-capturing bound. PreciseCapturingArg(Namespace), + // Paths that end with `(..)`, for return type notation. + ReturnTypeNotation, } impl<'a> PathSource<'a> { @@ -413,7 +415,8 @@ impl<'a> PathSource<'a> { PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) - | PathSource::Delegation => ValueNS, + | PathSource::Delegation + | PathSource::ReturnTypeNotation => ValueNS, PathSource::TraitItem(ns) => ns, PathSource::PreciseCapturingArg(ns) => ns, } @@ -425,7 +428,8 @@ impl<'a> PathSource<'a> { | PathSource::Expr(..) | PathSource::Pat | PathSource::Struct - | PathSource::TupleStruct(..) => true, + | PathSource::TupleStruct(..) + | PathSource::ReturnTypeNotation => true, PathSource::Trait(_) | PathSource::TraitItem(..) | PathSource::Delegation @@ -471,7 +475,7 @@ impl<'a> PathSource<'a> { }, _ => "value", }, - PathSource::Delegation => "function", + PathSource::ReturnTypeNotation | PathSource::Delegation => "function", PathSource::PreciseCapturingArg(..) => "type or const parameter", } } @@ -540,6 +544,10 @@ impl<'a> PathSource<'a> { Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, _ => false, }, + PathSource::ReturnTypeNotation => match res { + Res::Def(DefKind::AssocFn, _) => true, + _ => false, + }, PathSource::Delegation => matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn, _)), PathSource::PreciseCapturingArg(ValueNS) => { matches!(res, Res::Def(DefKind::ConstParam, _)) @@ -565,8 +573,8 @@ impl<'a> PathSource<'a> { (PathSource::Expr(..), false) | (PathSource::Delegation, false) => E0425, (PathSource::Pat | PathSource::TupleStruct(..), true) => E0532, (PathSource::Pat | PathSource::TupleStruct(..), false) => E0531, - (PathSource::TraitItem(..), true) => E0575, - (PathSource::TraitItem(..), false) => E0576, + (PathSource::TraitItem(..), true) | (PathSource::ReturnTypeNotation, true) => E0575, + (PathSource::TraitItem(..), false) | (PathSource::ReturnTypeNotation, false) => E0576, (PathSource::PreciseCapturingArg(..), true) => E0799, (PathSource::PreciseCapturingArg(..), false) => E0800, } @@ -781,7 +789,17 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r } TyKind::Path(qself, path) => { self.diag_metadata.current_type_path = Some(ty); - self.smart_resolve_path(ty.id, qself, path, PathSource::Type); + + let source = if let Some(seg) = path.segments.last() + && let Some(args) = &seg.args + && matches!(**args, GenericArgs::ParenthesizedElided(..)) + { + PathSource::ReturnTypeNotation + } else { + PathSource::Type + }; + + self.smart_resolve_path(ty.id, qself, path, source); // Check whether we should interpret this as a bare trait object. if qself.is_none() @@ -1920,7 +1938,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { PathSource::Trait(..) | PathSource::TraitItem(..) | PathSource::Type - | PathSource::PreciseCapturingArg(..) => false, + | PathSource::PreciseCapturingArg(..) + | PathSource::ReturnTypeNotation => false, PathSource::Expr(..) | PathSource::Pat | PathSource::Struct From 19881b5a5a17b4fbc91529f867ca4e577af6438a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 14:51:26 -0400 Subject: [PATCH 2/7] Conditionally allow lowering RTN (..) in paths --- compiler/rustc_ast_lowering/src/asm.rs | 5 +- compiler/rustc_ast_lowering/src/delegation.rs | 3 +- compiler/rustc_ast_lowering/src/expr.rs | 7 +- compiler/rustc_ast_lowering/src/lib.rs | 22 ++++++- compiler/rustc_ast_lowering/src/pat.rs | 5 +- compiler/rustc_ast_lowering/src/path.rs | 66 +++++++++++++++++-- compiler/rustc_hir_analysis/messages.ftl | 2 + compiler/rustc_hir_analysis/src/errors.rs | 7 ++ .../src/hir_ty_lowering/mod.rs | 15 ++++- .../return-type-notation/bare-path.rs | 7 +- .../return-type-notation/bare-path.stderr | 37 +++-------- .../let-binding-init-expr-as-ty.rs | 2 +- .../let-binding-init-expr-as-ty.stderr | 17 +++-- 13 files changed, 141 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index a9d1ee5c9c13a..6c966c9f73ac2 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -20,8 +20,8 @@ use super::errors::{ }; use super::LoweringContext; use crate::{ - fluent_generated as fluent, ImplTraitContext, ImplTraitPosition, ParamMode, - ResolverAstLoweringExt, + fluent_generated as fluent, AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, + ParamMode, ResolverAstLoweringExt, }; impl<'a, 'hir> LoweringContext<'a, 'hir> { @@ -201,6 +201,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &sym.qself, &sym.path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index ac527df474aff..97483e85f77dd 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -52,7 +52,7 @@ use rustc_target::spec::abi; use {rustc_ast as ast, rustc_hir as hir}; use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode}; -use crate::{ImplTraitPosition, ResolverAstLoweringExt}; +use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt}; pub(crate) struct DelegationResults<'hir> { pub body_id: hir::BodyId, @@ -340,6 +340,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &delegation.qself, &delegation.path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index e105026ebd19d..974144cc8d99b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -23,7 +23,7 @@ use super::{ GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, }; use crate::errors::YieldInClosure; -use crate::{fluent_generated, FnDeclKind, ImplTraitPosition}; +use crate::{fluent_generated, AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition}; impl<'hir> LoweringContext<'_, 'hir> { fn lower_exprs(&mut self, exprs: &[AstP]) -> &'hir [hir::Expr<'hir>] { @@ -281,6 +281,7 @@ impl<'hir> LoweringContext<'_, 'hir> { qself, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); @@ -328,6 +329,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &se.qself, &se.path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, )), @@ -1291,6 +1293,7 @@ impl<'hir> LoweringContext<'_, 'hir> { qself, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); @@ -1311,6 +1314,7 @@ impl<'hir> LoweringContext<'_, 'hir> { qself, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); @@ -1336,6 +1340,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &se.qself, &se.path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index efd3ae336afb8..d59197b396a9b 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -485,8 +485,16 @@ enum ParamMode { Optional, } +#[derive(Copy, Clone, Debug)] +enum AllowReturnTypeNotation { + Yes, + No, +} + +#[derive(Copy, Clone, Debug)] enum GenericArgsMode { ParenSugar, + ReturnTypeNotation, Err, } @@ -1226,7 +1234,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } let id = self.lower_node_id(t.id); - let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx, None); + let qpath = self.lower_qpath( + t.id, + qself, + path, + param_mode, + // We deny these after the fact in HIR->middle type lowering. + AllowReturnTypeNotation::Yes, + itctx, + None, + ); self.ty_path(id, t.span, qpath) } @@ -2203,6 +2220,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &None, &p.path, ParamMode::Explicit, + AllowReturnTypeNotation::No, itctx, Some(modifiers), ) { @@ -2341,6 +2359,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &None, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); @@ -2419,6 +2438,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index d82bdd526b707..584d94ebe2df1 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -11,7 +11,7 @@ use super::errors::{ ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, }; use super::{ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt}; -use crate::ImplTraitPosition; +use crate::{AllowReturnTypeNotation, ImplTraitPosition}; impl<'a, 'hir> LoweringContext<'a, 'hir> { pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> { @@ -38,6 +38,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); @@ -55,6 +56,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); @@ -66,6 +68,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, ParamMode::Optional, + AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 2ab30eff6d864..491c0dfc42e18 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -5,6 +5,7 @@ use rustc_hir::def::{DefKind, PartialRes, Res}; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; use rustc_middle::span_bug; +use rustc_session::parse::add_feature_diagnostics; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, DesugaringKind, Span, Symbol, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; @@ -15,8 +16,8 @@ use super::errors::{ GenericTypeWithParentheses, UseAngleBrackets, }; use super::{ - GenericArgsCtor, GenericArgsMode, ImplTraitContext, LifetimeRes, LoweringContext, ParamMode, - ResolverAstLoweringExt, + AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, LifetimeRes, + LoweringContext, ParamMode, ResolverAstLoweringExt, }; use crate::ImplTraitPosition; @@ -28,6 +29,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself: &Option>, p: &Path, param_mode: ParamMode, + allow_return_type_notation: AllowReturnTypeNotation, itctx: ImplTraitContext, // modifiers of the impl/bound if this is a trait path modifiers: Option, @@ -103,6 +105,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { { GenericArgsMode::ParenSugar } + Res::Def(DefKind::AssocFn, _) if i + 1 == proj_start => { + match allow_return_type_notation { + AllowReturnTypeNotation::Yes => GenericArgsMode::ReturnTypeNotation, + AllowReturnTypeNotation::No => GenericArgsMode::Err, + } + } // Avoid duplicated errors. Res::Err => GenericArgsMode::ParenSugar, // An error @@ -164,11 +172,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // 3. `<>::IntoIter>::Item` // * final path is `<<>::IntoIter>::Item>::clone` for (i, segment) in p.segments.iter().enumerate().skip(proj_start) { + // If this is a type-dependent `T::method(..)`. + let generic_args_mode = if i + 1 == p.segments.len() + && matches!(allow_return_type_notation, AllowReturnTypeNotation::Yes) + { + GenericArgsMode::ReturnTypeNotation + } else { + GenericArgsMode::Err + }; + let hir_segment = self.arena.alloc(self.lower_path_segment( p.span, segment, param_mode, - GenericArgsMode::Err, + generic_args_mode, itctx, None, )); @@ -238,6 +255,40 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_angle_bracketed_parameter_data(data, param_mode, itctx) } GenericArgs::Parenthesized(data) => match generic_args_mode { + GenericArgsMode::ReturnTypeNotation => { + let mut err = if !data.inputs.is_empty() { + self.dcx().create_err(BadReturnTypeNotation::Inputs { + span: data.inputs_span, + }) + } else if let FnRetTy::Ty(ty) = &data.output { + self.dcx().create_err(BadReturnTypeNotation::Output { + span: data.inputs_span.shrink_to_hi().to(ty.span), + }) + } else { + self.dcx().create_err(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, + sym::return_type_notation, + ); + } + err.emit(); + ( + GenericArgsCtor { + args: Default::default(), + constraints: &[], + parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation, + span: path_span, + }, + false, + ) + } GenericArgsMode::ParenSugar => self.lower_parenthesized_parameter_data( data, itctx, @@ -279,7 +330,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } }, GenericArgs::ParenthesizedElided(span) => { - self.dcx().emit_err(BadReturnTypeNotation::Position { span: *span }); + match generic_args_mode { + GenericArgsMode::ReturnTypeNotation => { + // Ok + } + GenericArgsMode::ParenSugar | GenericArgsMode::Err => { + self.dcx().emit_err(BadReturnTypeNotation::Position { span: *span }); + } + } ( GenericArgsCtor { args: Default::default(), diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 633ccacedfcba..f5e8119cdca61 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -48,6 +48,8 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found} +hir_analysis_bad_return_type_notation_position = return type notation not allowed in this position yet + hir_analysis_cannot_capture_late_bound_const = cannot capture late-bound const parameter in {$what} .label = parameter defined here diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index e4e5f76ae322e..11b6fedf1f935 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1693,3 +1693,10 @@ pub(crate) struct CmseCallGeneric { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_bad_return_type_notation_position)] +pub(crate) struct BadReturnTypeNotation { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 7163352e8a439..507446193f749 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -53,7 +53,7 @@ use rustc_trait_selection::traits::{self, ObligationCtxt}; use tracing::{debug, debug_span, instrument}; use crate::bounds::Bounds; -use crate::errors::{AmbiguousLifetimeBound, WildPatTy}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, WildPatTy}; use crate::hir_ty_lowering::errors::{prohibit_assoc_item_constraint, GenericsArgsErrExtend}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -1910,6 +1910,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::BoundConstness::NotConst, ) } + // Deny any qpath types that were successfully lowered in AST lowering. + Res::Def(DefKind::AssocFn, _) + if let [.., _trait_segment, item_segment] = &path.segments[..] + && item_segment.args.is_some_and(|args| { + matches!( + args.parenthesized, + hir::GenericArgsParentheses::ReturnTypeNotation + ) + }) => + { + let guar = self.dcx().emit_err(BadReturnTypeNotation { span: path.span }); + Ty::new_error(tcx, guar) + } Res::PrimTy(prim_ty) => { assert_eq!(opt_self_ty, None); let _ = self.prohibit_generic_args( diff --git a/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs b/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs index f507d82afec2f..baaa432067bd9 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs +++ b/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs @@ -10,17 +10,14 @@ trait Tr { fn foo() where T::method(..): Send, - //~^ ERROR return type notation not allowed in this position yet - //~| ERROR expected type, found function + //~^ ERROR expected type, found function ::method(..): Send, //~^ ERROR return type notation not allowed in this position yet - //~| ERROR expected associated type, found associated function `Tr::method` { let _ = T::CONST::(..); //~^ ERROR return type notation not allowed in this position yet let _: T::method(..); - //~^ ERROR return type notation not allowed in this position yet - //~| ERROR expected type, found function + //~^ ERROR expected type, found function } fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr b/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr index cb45de59c7e23..a4305fdf30f4d 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr @@ -1,9 +1,3 @@ -error[E0575]: expected associated type, found associated function `Tr::method` - --> $DIR/bare-path.rs:15:5 - | -LL | ::method(..): Send, - | ^^^^^^^^^^^^^^^^^^^^^ not a associated type - warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/bare-path.rs:1:12 | @@ -14,29 +8,11 @@ LL | #![feature(return_type_notation)] = note: `#[warn(incomplete_features)]` on by default error: return type notation not allowed in this position yet - --> $DIR/bare-path.rs:19:23 + --> $DIR/bare-path.rs:17:23 | LL | let _ = T::CONST::(..); | ^^^^ -error: return type notation not allowed in this position yet - --> $DIR/bare-path.rs:21:21 - | -LL | let _: T::method(..); - | ^^^^ - -error: return type notation not allowed in this position yet - --> $DIR/bare-path.rs:12:14 - | -LL | T::method(..): Send, - | ^^^^ - -error: return type notation not allowed in this position yet - --> $DIR/bare-path.rs:15:22 - | -LL | ::method(..): Send, - | ^^^^ - error: expected type, found function --> $DIR/bare-path.rs:12:8 | @@ -49,8 +25,14 @@ note: the associated function is defined here LL | fn method() -> impl Sized; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: return type notation not allowed in this position yet + --> $DIR/bare-path.rs:14:5 + | +LL | ::method(..): Send, + | ^^^^^^^^^^^^^^^^^^^^^ + error: expected type, found function - --> $DIR/bare-path.rs:21:15 + --> $DIR/bare-path.rs:19:15 | LL | let _: T::method(..); | ^^^^^^ unexpected function @@ -61,6 +43,5 @@ note: the associated function is defined here LL | fn method() -> impl Sized; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors; 1 warning emitted +error: aborting due to 4 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0575`. diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs index 06ee421fc3279..fa62f104b8844 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs @@ -1,7 +1,7 @@ pub fn foo(num: i32) -> i32 { let foo: i32::from_be(num); //~^ ERROR expected type, found local variable `num` - //~| ERROR parenthesized type parameters may only be used with a `Fn` trait + //~| ERROR argument types not allowed with return type notation //~| ERROR ambiguous associated type foo } diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr index b90ae051fb776..03e4882d71f2b 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr @@ -6,16 +6,15 @@ LL | let foo: i32::from_be(num); | | | help: use `=` if you meant to assign -error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/let-binding-init-expr-as-ty.rs:2:19 +error: argument types not allowed with return type notation + --> $DIR/let-binding-init-expr-as-ty.rs:2:26 | LL | let foo: i32::from_be(num); - | ^^^^^^^^^^^^ only `Fn` traits may use parentheses + | ^^^^^ help: remove the input types: `()` | -help: use angle brackets instead - | -LL | let foo: i32::from_be; - | ~ ~ + = note: see issue #109417 for more information + = help: add `#![feature(return_type_notation)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0223]: ambiguous associated type --> $DIR/let-binding-init-expr-as-ty.rs:2:14 @@ -30,5 +29,5 @@ LL | let foo: ::from_be; error: aborting due to 3 previous errors -Some errors have detailed explanations: E0214, E0223, E0573. -For more information about an error, try `rustc --explain E0214`. +Some errors have detailed explanations: E0223, E0573. +For more information about an error, try `rustc --explain E0223`. From 51b51bb570e076d981e12a8e221ed3a74b3025c6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 15:03:30 -0400 Subject: [PATCH 3/7] Implement RTN in resolve_bound_vars and HIR ty lowering --- compiler/rustc_ast_lowering/src/lib.rs | 4 +- compiler/rustc_ast_lowering/src/path.rs | 5 +- .../src/collect/predicates_of.rs | 13 +- .../src/collect/resolve_bound_vars.rs | 109 +++++++ .../src/hir_ty_lowering/bounds.rs | 286 ++++++++++++++---- .../src/hir_ty_lowering/mod.rs | 122 ++++---- compiler/rustc_resolve/src/late.rs | 1 + .../return-type-notation/bare-path.rs | 4 +- .../return-type-notation/bare-path.stderr | 32 +- .../let-binding-init-expr-as-ty.rs | 2 +- .../let-binding-init-expr-as-ty.stderr | 10 +- 11 files changed, 411 insertions(+), 177 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d59197b396a9b..5266eb0e91f3a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -487,11 +487,12 @@ enum ParamMode { #[derive(Copy, Clone, Debug)] enum AllowReturnTypeNotation { + /// Only in types, since RTN is denied later during HIR lowering. Yes, + /// All other positions (path expr, method, use tree). No, } -#[derive(Copy, Clone, Debug)] enum GenericArgsMode { ParenSugar, ReturnTypeNotation, @@ -1239,7 +1240,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, param_mode, - // We deny these after the fact in HIR->middle type lowering. AllowReturnTypeNotation::Yes, itctx, None, diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 491c0dfc42e18..6f0641dbc403d 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -16,10 +16,9 @@ use super::errors::{ GenericTypeWithParentheses, UseAngleBrackets, }; use super::{ - AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, LifetimeRes, - LoweringContext, ParamMode, ResolverAstLoweringExt, + AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition, + LifetimeRes, LoweringContext, ParamMode, ResolverAstLoweringExt, }; -use crate::ImplTraitPosition; impl<'a, 'hir> LoweringContext<'a, 'hir> { #[instrument(level = "trace", skip(self))] diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 7243e85ce98d2..f9539ad0efb5d 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -240,7 +240,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen for predicate in hir_generics.predicates { match predicate { hir::WherePredicate::BoundPredicate(bound_pred) => { - let ty = icx.lower_ty(bound_pred.bounded_ty); + let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty); + let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); // Keep the type around in a dummy predicate, in case of no bounds. // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` @@ -770,6 +771,10 @@ impl<'tcx> ItemCtxt<'tcx> { continue; }; + // Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we + // want to only consider predicates with `Self: ...`, but we don't want + // `OnlySelfBounds(true)` since we want to collect the nested associated + // type bound as well. let (only_self_bounds, assoc_name) = match filter { PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { (OnlySelfBounds(false), None) @@ -780,14 +785,10 @@ impl<'tcx> ItemCtxt<'tcx> { } }; - // Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we - // want to only consider predicates with `Self: ...`, but we don't want - // `OnlySelfBounds(true)` since we want to collect the nested associated - // type bound as well. let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) { ty } else if matches!(filter, PredicateFilter::All) { - self.lower_ty(predicate.bounded_ty) + self.lowerer().lower_ty_maybe_return_type_notation(predicate.bounded_ty) } else { continue; }; 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 53dcede91c36a..7d8fb39710f46 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -889,7 +889,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { (pair, r) }) .unzip(); + self.record_late_bound_vars(hir_id, binders); + + // If this is an RTN type in the self type, then append those to the binder. + self.try_append_return_type_notation_params(hir_id, bounded_ty); + // Even if there are no lifetimes defined here, we still wrap it in a binder // scope. If there happens to be a nested poly trait ref (an error), that // will be `Concatenating` anyways, so we don't have to worry about the depth @@ -1839,6 +1844,110 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id); assert_eq!(old_value, Some(bad_def)); } + + // TODO: + fn try_append_return_type_notation_params( + &mut self, + hir_id: HirId, + hir_ty: &'tcx hir::Ty<'tcx>, + ) { + let hir::TyKind::Path(qpath) = hir_ty.kind else { + // TODO: + return; + }; + + let (mut bound_vars, item_def_id, item_segment) = match qpath { + // TODO: + hir::QPath::Resolved(_, path) + if let [.., item_segment] = &path.segments[..] + && item_segment.args.is_some_and(|args| { + matches!( + args.parenthesized, + hir::GenericArgsParentheses::ReturnTypeNotation + ) + }) => + { + let Res::Def(DefKind::AssocFn, item_def_id) = path.res else { + bug!(); + }; + (vec![], item_def_id, item_segment) + } + + // TODO: + hir::QPath::TypeRelative(qself, item_segment) + if item_segment.args.is_some_and(|args| { + matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation) + }) => + { + let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind else { + return; + }; + + match path.res { + Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => { + let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics() + else { + return; + }; + + let one_bound = generics.predicates.iter().find_map(|predicate| { + let hir::WherePredicate::BoundPredicate(predicate) = predicate else { + return None; + }; + let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) = + predicate.bounded_ty.kind + else { + return None; + }; + if bounded_path.res != path.res { + return None; + } + predicate.bounds.iter().find_map(|bound| { + let hir::GenericBound::Trait(trait_, _) = bound else { + return None; + }; + BoundVarContext::supertrait_hrtb_vars( + self.tcx, + trait_.trait_ref.trait_def_id()?, + item_segment.ident, + ty::AssocKind::Fn, + ) + }) + }); + let Some((bound_vars, assoc_item)) = one_bound else { + return; + }; + (bound_vars, assoc_item.def_id, item_segment) + } + Res::SelfTyAlias { is_trait_impl: true, .. } => todo!(), + _ => return, + } + } + + _ => return, + }; + + // TODO: + bound_vars.extend(self.tcx.generics_of(item_def_id).own_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, + } + })); + bound_vars.extend(self.tcx.fn_sig(item_def_id).instantiate_identity().bound_vars()); + + // TODO: + let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id).unwrap(); + let existing_bound_vars_saved = existing_bound_vars.clone(); + existing_bound_vars.extend(bound_vars); + // TODO: subtle + self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved); + } } /// Detects late-bound lifetimes and inserts them into diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index bffe68f9b745f..1aaacf8db3249 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -6,6 +6,7 @@ use rustc_errors::struct_span_code_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::HirId; use rustc_middle::bug; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; @@ -15,6 +16,7 @@ use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, use smallvec::SmallVec; use tracing::{debug, instrument}; +use super::errors::GenericsArgsErrExtend; use crate::bounds::Bounds; use crate::errors; use crate::hir_ty_lowering::{ @@ -332,74 +334,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .or_insert(constraint.span); let projection_term = if let ty::AssocKind::Fn = assoc_kind { - let mut emitted_bad_param_err = None; - // If we have an method return type bound, then we need to instantiate - // the method's early bound params with suitable late-bound params. - let mut num_bound_vars = candidate.bound_vars().len(); - let args = - candidate.skip_binder().args.extend_to(tcx, assoc_item.def_id, |param, _| { - let arg = match param.kind { - ty::GenericParamDefKind::Lifetime => ty::Region::new_bound( - tcx, - ty::INNERMOST, - ty::BoundRegion { - var: ty::BoundVar::from_usize(num_bound_vars), - kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name), - }, - ) - .into(), - ty::GenericParamDefKind::Type { .. } => { - let guar = *emitted_bad_param_err.get_or_insert_with(|| { - self.dcx().emit_err( - crate::errors::ReturnTypeNotationIllegalParam::Type { - span: path_span, - param_span: tcx.def_span(param.def_id), - }, - ) - }); - Ty::new_error(tcx, guar).into() - } - ty::GenericParamDefKind::Const { .. } => { - let guar = *emitted_bad_param_err.get_or_insert_with(|| { - self.dcx().emit_err( - crate::errors::ReturnTypeNotationIllegalParam::Const { - span: path_span, - param_span: tcx.def_span(param.def_id), - }, - ) - }); - ty::Const::new_error(tcx, guar).into() - } - }; - num_bound_vars += 1; - arg - }); - - // 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.is_impl_trait_in_trait(alias_ty.def_id) - { - alias_ty.into() - } else { - return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { - span: constraint.span, - ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output), - fn_span: tcx.hir().span_if_local(assoc_item.def_id), - note: (), - })); - }; - - // 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 - // `rustc_middle::ty::predicate::Clause::instantiate_supertrait` - // and it's no coincidence why. - let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output); - let instantiation_output = ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args); - let bound_vars = tcx.late_bound_vars(constraint.hir_id); - ty::Binder::bind_with_vars(instantiation_output, bound_vars) + ty::Binder::bind_with_vars( + self.lower_return_type_notation_ty(candidate, assoc_item.def_id, path_span)?.into(), + bound_vars, + ) } else { // Create the generic arguments for the associated type or constant by joining the // parent arguments (the arguments of the trait) and the own arguments (the ones of @@ -525,6 +464,219 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Ok(()) } + + // TODO: + pub fn lower_ty_maybe_return_type_notation(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + let hir::TyKind::Path(qpath) = hir_ty.kind else { + return self.lower_ty(hir_ty); + }; + + let tcx = self.tcx(); + match qpath { + hir::QPath::Resolved(opt_self_ty, path) + if let [mod_segments @ .., trait_segment, item_segment] = &path.segments[..] + && item_segment.args.is_some_and(|args| { + matches!( + args.parenthesized, + hir::GenericArgsParentheses::ReturnTypeNotation + ) + }) => + { + let _ = + self.prohibit_generic_args(mod_segments.iter(), GenericsArgsErrExtend::None); + + let Res::Def(DefKind::AssocFn, item_def_id) = path.res else { + bug!(); + }; + let trait_def_id = tcx.parent(item_def_id); + + let Some(self_ty) = opt_self_ty else { + return self.error_missing_qpath_self_ty( + trait_def_id, + hir_ty.span, + item_segment, + ); + }; + let self_ty = self.lower_ty(self_ty); + + let trait_ref = self.lower_mono_trait_ref( + hir_ty.span, + trait_def_id, + self_ty, + trait_segment, + false, + ty::BoundConstness::NotConst, + ); + + let candidate = + ty::Binder::bind_with_vars(trait_ref, tcx.late_bound_vars(item_segment.hir_id)); + + match self.lower_return_type_notation_ty(candidate, item_def_id, hir_ty.span) { + Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty), + Err(guar) => Ty::new_error(tcx, guar), + } + } + hir::QPath::TypeRelative(qself, item_segment) + if item_segment.args.is_some_and(|args| { + matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation) + }) => + { + match self + .resolve_type_relative_return_type_notation( + qself, + item_segment, + hir_ty.hir_id, + hir_ty.span, + ) + .and_then(|(candidate, item_def_id)| { + self.lower_return_type_notation_ty(candidate, item_def_id, hir_ty.span) + }) { + Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty), + Err(guar) => Ty::new_error(tcx, guar), + } + } + _ => self.lower_ty(hir_ty), + } + } + + // TODO: + fn resolve_type_relative_return_type_notation( + &self, + qself: &'tcx hir::Ty<'tcx>, + item_segment: &'tcx hir::PathSegment<'tcx>, + qpath_hir_id: HirId, + span: Span, + ) -> Result<(ty::PolyTraitRef<'tcx>, DefId), ErrorGuaranteed> { + let tcx = self.tcx(); + let qself_ty = self.lower_ty(qself); + let assoc_ident = item_segment.ident; + let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + path.res + } else { + Res::Err + }; + + let bound = match (qself_ty.kind(), qself_res) { + (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => { + // `Self` in an impl of a trait -- we have a concrete self type and a + // trait reference. + let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { + // A cycle error occurred, most likely. + self.dcx().span_bug(span, "expected cycle error"); + }; + + self.probe_single_bound_for_assoc_item( + || { + traits::supertraits( + tcx, + ty::Binder::dummy(trait_ref.instantiate_identity()), + ) + }, + AssocItemQSelf::SelfTyAlias, + ty::AssocKind::Fn, + assoc_ident, + span, + None, + )? + } + ( + &ty::Param(_), + Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), + ) => self.probe_single_ty_param_bound_for_assoc_item( + param_did.expect_local(), + qself.span, + ty::AssocKind::Fn, + assoc_ident, + span, + )?, + _ => todo!(), + }; + + // Don't let `T::method` resolve to some `for<'a> >::method`. + // This is the same restrictions as associated types; even though we could + // support it, it just makes things a lot more difficult to support in + // `resolve_bound_vars`. + if bound.has_bound_vars() { + todo!(); + } + + let trait_def_id = bound.def_id(); + let assoc_ty = self + .probe_assoc_item(assoc_ident, ty::AssocKind::Fn, qpath_hir_id, span, trait_def_id) + .expect("failed to find associated type"); + + Ok((bound, assoc_ty.def_id)) + } + + // TODO: + fn lower_return_type_notation_ty( + &self, + candidate: ty::PolyTraitRef<'tcx>, + item_def_id: DefId, + path_span: Span, + ) -> Result, ErrorGuaranteed> { + let tcx = self.tcx(); + let mut emitted_bad_param_err = None; + // If we have an method return type bound, then we need to instantiate + // the method's early bound params with suitable late-bound params. + let mut num_bound_vars = candidate.bound_vars().len(); + let args = candidate.skip_binder().args.extend_to(tcx, item_def_id, |param, _| { + let arg = match param.kind { + ty::GenericParamDefKind::Lifetime => ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_usize(num_bound_vars), + kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name), + }, + ) + .into(), + ty::GenericParamDefKind::Type { .. } => { + let guar = *emitted_bad_param_err.get_or_insert_with(|| { + self.dcx().emit_err(crate::errors::ReturnTypeNotationIllegalParam::Type { + span: path_span, + param_span: tcx.def_span(param.def_id), + }) + }); + Ty::new_error(tcx, guar).into() + } + ty::GenericParamDefKind::Const { .. } => { + let guar = *emitted_bad_param_err.get_or_insert_with(|| { + self.dcx().emit_err(crate::errors::ReturnTypeNotationIllegalParam::Const { + span: path_span, + param_span: tcx.def_span(param.def_id), + }) + }); + ty::Const::new_error(tcx, guar).into() + } + }; + num_bound_vars += 1; + arg + }); + + // 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(item_def_id).skip_binder().output(); + let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind() + && tcx.is_impl_trait_in_trait(alias_ty.def_id) + { + alias_ty + } else { + return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { + span: path_span, + ty: tcx.liberate_late_bound_regions(item_def_id, output), + fn_span: tcx.hir().span_if_local(item_def_id), + note: (), + })); + }; + + // 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 + // `rustc_middle::ty::predicate::Clause::instantiate_supertrait` + // and it's no coincidence why. + let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output); + Ok(ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args)) + } } /// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 507446193f749..d42a70308d624 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -820,10 +820,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// `ty_param_def_id` is the `LocalDefId` of the type parameter. #[instrument(level = "debug", skip_all, ret)] - fn probe_single_ty_param_bound_for_assoc_ty( + fn probe_single_ty_param_bound_for_assoc_item( &self, ty_param_def_id: LocalDefId, ty_param_span: Span, + kind: ty::AssocKind, assoc_name: Ident, span: Span, ) -> Result, ErrorGuaranteed> { @@ -841,7 +842,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) }, AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), - ty::AssocKind::Type, + kind, assoc_name, span, None, @@ -1081,9 +1082,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ( &ty::Param(_), Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), - ) => self.probe_single_ty_param_bound_for_assoc_ty( + ) => self.probe_single_ty_param_bound_for_assoc_item( param_did.expect_local(), qself.span, + ty::AssocKind::Type, assoc_ident, span, )?, @@ -1545,48 +1547,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?trait_def_id); let Some(self_ty) = opt_self_ty else { - let path_str = tcx.def_path_str(trait_def_id); - - let def_id = self.item_def_id(); - debug!(item_def_id = ?def_id); - - // FIXME: document why/how this is different from `tcx.local_parent(def_id)` - let parent_def_id = - tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id(); - debug!(?parent_def_id); - - // If the trait in segment is the same as the trait defining the item, - // use the `` syntax in the error. - let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id; - let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id; - - let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { - vec!["Self".to_string()] - } else { - // Find all the types that have an `impl` for the trait. - tcx.all_impls(trait_def_id) - .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) - .filter(|header| { - // Consider only accessible traits - tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) - && header.polarity != ty::ImplPolarity::Negative - }) - .map(|header| header.trait_ref.instantiate_identity().self_ty()) - // We don't care about blanket impls. - .filter(|self_ty| !self_ty.has_non_region_param()) - .map(|self_ty| tcx.erase_regions(self_ty).to_string()) - .collect() - }; - // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that - // references the trait. Relevant for the first case in - // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` - let reported = self.report_ambiguous_assoc_ty( - span, - &type_names, - &[path_str], - item_segment.ident.name, - ); - return Ty::new_error(tcx, reported); + return self.error_missing_qpath_self_ty(trait_def_id, span, item_segment); }; debug!(?self_ty); @@ -1600,6 +1561,53 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_projection_from_args(tcx, item_def_id, item_args) } + fn error_missing_qpath_self_ty( + &self, + trait_def_id: DefId, + span: Span, + item_segment: &hir::PathSegment<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + let path_str = tcx.def_path_str(trait_def_id); + + let def_id = self.item_def_id(); + debug!(item_def_id = ?def_id); + + // FIXME: document why/how this is different from `tcx.local_parent(def_id)` + let parent_def_id = + tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id(); + debug!(?parent_def_id); + + // If the trait in segment is the same as the trait defining the item, + // use the `` syntax in the error. + let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id; + let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id; + + let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { + vec!["Self".to_string()] + } else { + // Find all the types that have an `impl` for the trait. + tcx.all_impls(trait_def_id) + .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) + .filter(|header| { + // Consider only accessible traits + tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) + && header.polarity != ty::ImplPolarity::Negative + }) + .map(|header| header.trait_ref.instantiate_identity().self_ty()) + // We don't care about blanket impls. + .filter(|self_ty| !self_ty.has_non_region_param()) + .map(|self_ty| tcx.erase_regions(self_ty).to_string()) + .collect() + }; + // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that + // references the trait. Relevant for the first case in + // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` + let reported = + self.report_ambiguous_assoc_ty(span, &type_names, &[path_str], item_segment.ident.name); + Ty::new_error(tcx, reported) + } + pub fn prohibit_generic_args<'a>( &self, segments: impl Iterator> + Clone, @@ -1910,19 +1918,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::BoundConstness::NotConst, ) } - // Deny any qpath types that were successfully lowered in AST lowering. - Res::Def(DefKind::AssocFn, _) - if let [.., _trait_segment, item_segment] = &path.segments[..] - && item_segment.args.is_some_and(|args| { - matches!( - args.parenthesized, - hir::GenericArgsParentheses::ReturnTypeNotation - ) - }) => - { - let guar = self.dcx().emit_err(BadReturnTypeNotation { span: path.span }); - Ty::new_error(tcx, guar) - } Res::PrimTy(prim_ty) => { assert_eq!(opt_self_ty, None); let _ = self.prohibit_generic_args( @@ -1943,7 +1938,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .tcx() .dcx() .span_delayed_bug(path.span, "path with `Res::Err` but no error emitted"); - Ty::new_error(self.tcx(), e) + Ty::new_error(tcx, e) } Res::Def(..) => { assert_eq!( @@ -2098,6 +2093,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), } } + // TODO: + hir::TyKind::Path(hir::QPath::TypeRelative(_, segment)) + if segment.args.is_some_and(|args| { + matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation) + }) => + { + let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span }); + Ty::new_error(tcx, guar) + } hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => { debug!(?qself, ?segment); let ty = self.lower_ty(qself); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2d97834d05a3c..813d569710a15 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -790,6 +790,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r TyKind::Path(qself, path) => { self.diag_metadata.current_type_path = Some(ty); + // TODO: let source = if let Some(seg) = path.segments.last() && let Some(args) = &seg.args && matches!(**args, GenericArgs::ParenthesizedElided(..)) diff --git a/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs b/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs index baaa432067bd9..185c05236332b 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs +++ b/tests/ui/associated-type-bounds/return-type-notation/bare-path.rs @@ -10,14 +10,12 @@ trait Tr { fn foo() where T::method(..): Send, - //~^ ERROR expected type, found function ::method(..): Send, - //~^ ERROR return type notation not allowed in this position yet { let _ = T::CONST::(..); //~^ ERROR return type notation not allowed in this position yet let _: T::method(..); - //~^ ERROR expected type, found function + //~^ ERROR return type notation not allowed in this position yet } fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr b/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr index a4305fdf30f4d..dca2bdeab0a7d 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/bare-path.stderr @@ -8,40 +8,16 @@ LL | #![feature(return_type_notation)] = note: `#[warn(incomplete_features)]` on by default error: return type notation not allowed in this position yet - --> $DIR/bare-path.rs:17:23 + --> $DIR/bare-path.rs:15:23 | LL | let _ = T::CONST::(..); | ^^^^ -error: expected type, found function - --> $DIR/bare-path.rs:12:8 - | -LL | T::method(..): Send, - | ^^^^^^ unexpected function - | -note: the associated function is defined here - --> $DIR/bare-path.rs:7:5 - | -LL | fn method() -> impl Sized; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: return type notation not allowed in this position yet - --> $DIR/bare-path.rs:14:5 - | -LL | ::method(..): Send, - | ^^^^^^^^^^^^^^^^^^^^^ - -error: expected type, found function - --> $DIR/bare-path.rs:19:15 + --> $DIR/bare-path.rs:17:12 | LL | let _: T::method(..); - | ^^^^^^ unexpected function - | -note: the associated function is defined here - --> $DIR/bare-path.rs:7:5 - | -LL | fn method() -> impl Sized; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs index fa62f104b8844..71e5a0c728d6b 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs @@ -2,7 +2,7 @@ pub fn foo(num: i32) -> i32 { let foo: i32::from_be(num); //~^ ERROR expected type, found local variable `num` //~| ERROR argument types not allowed with return type notation - //~| ERROR ambiguous associated type + //~| ERROR return type notation not allowed in this position yet foo } diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr index 03e4882d71f2b..83a5441e3c0c6 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr @@ -16,18 +16,12 @@ LL | let foo: i32::from_be(num); = help: add `#![feature(return_type_notation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0223]: ambiguous associated type +error: return type notation not allowed in this position yet --> $DIR/let-binding-init-expr-as-ty.rs:2:14 | LL | let foo: i32::from_be(num); | ^^^^^^^^^^^^^^^^^ - | -help: if there were a trait named `Example` with associated type `from_be` implemented for `i32`, you could use the fully-qualified path - | -LL | let foo: ::from_be; - | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 3 previous errors -Some errors have detailed explanations: E0223, E0573. -For more information about an error, try `rustc --explain E0223`. +For more information about this error, try `rustc --explain E0573`. From 7c8e281f735d02cddfd5c5ff350482a19a3d62c5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 15:53:39 -0400 Subject: [PATCH 4/7] Flesh out some TODOs --- compiler/rustc_hir_analysis/messages.ftl | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 1 + .../src/collect/resolve_bound_vars.rs | 48 +++++++++++++++---- compiler/rustc_hir_analysis/src/errors.rs | 3 +- .../src/hir_ty_lowering/bounds.rs | 43 ++++++++++++++--- .../src/hir_ty_lowering/mod.rs | 15 +++++- compiler/rustc_resolve/src/late.rs | 4 +- 7 files changed, 97 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index f5e8119cdca61..262277aaa2a47 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -37,7 +37,7 @@ hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here -hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated type of a trait with uninferred generic parameters +hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated {$what} of a trait with uninferred generic parameters .suggestion = use a fully qualified path with inferred lifetimes hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index ac9976148e25c..435a0e31b56c2 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -507,6 +507,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { inferred_sugg, bound, mpart_sugg, + what: "type", }), ) } 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 7d8fb39710f46..b241f8a5317c4 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1845,19 +1845,38 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { assert_eq!(old_value, Some(bad_def)); } - // TODO: + // When we have a return type notation type in a where clause, like + // `where ::method(..): Send`, we need to introduce new bound + // vars to the existing where clause's binder, to represent the lifetimes + // elided by the return-type-notation syntax. + // + // For example, given + // ``` + // trait Foo { + // async fn x<'r, T>(); + // } + // ``` + // and a bound that looks like: + // `for<'a, 'b> >::x(): Other<'b>` + // this is going to expand to something like: + // `for<'a, 'b, 'r, T> >::x::<'r, T>::{opaque#0}: Other<'b>`. + // + // We handle this similarly for associated-type-bound style return-type-notation + // in `visit_segment_args`. fn try_append_return_type_notation_params( &mut self, hir_id: HirId, hir_ty: &'tcx hir::Ty<'tcx>, ) { let hir::TyKind::Path(qpath) = hir_ty.kind else { - // TODO: + // We only care about path types here. All other self types + // (including nesting the RTN type in another type) don't do + // anything. return; }; let (mut bound_vars, item_def_id, item_segment) = match qpath { - // TODO: + // If we have a fully qualified method, then we don't need to do any special lookup. hir::QPath::Resolved(_, path) if let [.., item_segment] = &path.segments[..] && item_segment.args.is_some_and(|args| { @@ -1873,23 +1892,32 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { (vec![], item_def_id, item_segment) } - // TODO: + // If we have a type-dependent path, then we do need to do some lookup. hir::QPath::TypeRelative(qself, item_segment) if item_segment.args.is_some_and(|args| { matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation) }) => { + // First, ignore a qself that isn't a type or `Self` param. Those are the + // only ones that support `T::Assoc` anyways in HIR lowering. let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind else { return; }; - match path.res { Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => { + // Get the generics of this type's hir owner. This is *different* + // from the generics of the parameter's definition, since we want + // to be able to resolve an RTN path on a nested body (e.g. method + // inside an impl) using the where clauses on the method. let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics() else { return; }; + // Look for the first bound that contains an associated type that + // matches the segment that we're looking for. We ignore any subsequent + // bounds since we'll be emitting a hard error in HIR lowering, so this + // is purely speculative. let one_bound = generics.predicates.iter().find_map(|predicate| { let hir::WherePredicate::BoundPredicate(predicate) = predicate else { return None; @@ -1927,7 +1955,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { _ => return, }; - // TODO: + // Append the early-bound vars on the function, and then the late-bound ones. + // We actually turn type parameters into higher-ranked types here, but we + // deny them later in HIR lowering. bound_vars.extend(self.tcx.generics_of(item_def_id).own_params.iter().map(|param| { match param.kind { ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region( @@ -1941,11 +1971,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { })); bound_vars.extend(self.tcx.fn_sig(item_def_id).instantiate_identity().bound_vars()); - // TODO: + // SUBTLE: Stash the old bound vars onto the *item segment* before appending + // the new bound vars. We do this because we need to know how many bound vars + // are present on the binder explicitly (i.e. not return-type-notation vars) + // to do bound var shifting correctly in HIR lowering. let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id).unwrap(); let existing_bound_vars_saved = existing_bound_vars.clone(); existing_bound_vars.extend(bound_vars); - // TODO: subtle self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved); } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 11b6fedf1f935..f332080a71d22 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -787,7 +787,8 @@ pub(crate) struct AssociatedTypeTraitUninferredGenericParams { pub inferred_sugg: Option, pub bound: String, #[subdiagnostic] - pub mpart_sugg: Option, + pub mpart_sugg: Option, + pub what: &'static str, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 1aaacf8db3249..033334bec8b60 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -465,7 +465,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ok(()) } - // TODO: + /// Lower a type, possibly specially handling the type if it's a return type notation + /// which we otherwise deny in other positions. pub fn lower_ty_maybe_return_type_notation(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { let hir::TyKind::Path(qpath) = hir_ty.kind else { return self.lower_ty(hir_ty); @@ -482,14 +483,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) }) => { + // We don't allow generics on the module segments. let _ = self.prohibit_generic_args(mod_segments.iter(), GenericsArgsErrExtend::None); let Res::Def(DefKind::AssocFn, item_def_id) = path.res else { - bug!(); + bug!("expected RTN to resolve to associated fn"); }; let trait_def_id = tcx.parent(item_def_id); + // Good error for `where Trait::method(..): Send`. let Some(self_ty) = opt_self_ty else { return self.error_missing_qpath_self_ty( trait_def_id, @@ -508,6 +511,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::BoundConstness::NotConst, ); + // SUBTLE: As noted at the end of `try_append_return_type_notation_params` + // in `resolve_bound_vars`, we stash the explicit bound vars of the where + // clause onto the item segment of the RTN type. This allows us to know + // how many bound vars are *not* coming from the signature of the function + // from lowering RTN itself. + // + // For example, in `where for<'a> >::method(..): Other`, + // the `late_bound_vars` of the where clause predicate (i.e. this HIR ty's + // parent) will include `'a` AND all the early- and late-bound vars of the + // method. But when lowering the RTN type, we just want the list of vars + // we used to resolve the trait ref. We explicitly stored those back onto + // the item segment, since there's no other good place to put them. let candidate = ty::Binder::bind_with_vars(trait_ref, tcx.late_bound_vars(item_segment.hir_id)); @@ -539,7 +554,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - // TODO: + /// Perform type-dependent lookup for a *method* for return type notation. + /// This generally mirrors `::lower_assoc_path`. fn resolve_type_relative_return_type_notation( &self, qself: &'tcx hir::Ty<'tcx>, @@ -592,12 +608,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { _ => todo!(), }; - // Don't let `T::method` resolve to some `for<'a> >::method`. + // Don't let `T::method` resolve to some `for<'a> >::method`, + // which may happen via a higher-ranked where clause or supertrait. // This is the same restrictions as associated types; even though we could // support it, it just makes things a lot more difficult to support in - // `resolve_bound_vars`. + // `resolve_bound_vars`, since we'd need to introduce those as elided + // bound vars on the where clause too. if bound.has_bound_vars() { - todo!(); + return Err(self.tcx().dcx().emit_err( + errors::AssociatedItemTraitUninferredGenericParams { + span, + inferred_sugg: Some(span.with_hi(item_segment.ident.span.lo())), + bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder(),), + mpart_sugg: None, + what: "function", + }, + )); } let trait_def_id = bound.def_id(); @@ -608,7 +634,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ok((bound, assoc_ty.def_id)) } - // TODO: + /// Do the common parts of lowering an RTN type. This involves extending the + /// candidate binder to include all of the early- and late-bound vars that are + /// defined on the function itself, and constructing a projection to the RPITIT + /// return type of that function. fn lower_return_type_notation_ty( &self, candidate: ty::PolyTraitRef<'tcx>, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index d42a70308d624..6998f580d808f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2069,6 +2069,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr) } + // If we encounter a fully qualified path with RTN generics, then it must have + // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore + // it's certainly in an illegal position. + hir::TyKind::Path(hir::QPath::Resolved(_, path)) + if path.segments.last().and_then(|segment| segment.args).is_some_and(|args| { + matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation) + }) => + { + let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span }); + Ty::new_error(tcx, guar) + } hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => { debug!(?maybe_qself, ?path); let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); @@ -2093,7 +2104,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), } } - // TODO: + // If we encounter a type relative path with RTN generics, then it must have + // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore + // it's certainly in an illegal position. hir::TyKind::Path(hir::QPath::TypeRelative(_, segment)) if segment.args.is_some_and(|args| { matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 813d569710a15..f1d9028f88c10 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -790,7 +790,9 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r TyKind::Path(qself, path) => { self.diag_metadata.current_type_path = Some(ty); - // TODO: + // If we have a path that ends with `(..)`, then it must be + // return type notation. Resolve that path in the *value* + // namespace. let source = if let Some(seg) = path.segments.last() && let Some(args) = &seg.args && matches!(**args, GenericArgs::ParenthesizedElided(..)) From 174c3f95192068b3d11c00ae77d06808f45c3ef3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 16:31:06 -0400 Subject: [PATCH 5/7] Add missing diagnostics and flesh out tests --- compiler/rustc_ast_lowering/src/lib.rs | 5 ++ compiler/rustc_ast_lowering/src/path.rs | 15 +++--- compiler/rustc_hir_analysis/src/collect.rs | 4 +- .../src/collect/resolve_bound_vars.rs | 9 ++-- compiler/rustc_hir_analysis/src/errors.rs | 4 +- .../src/hir_ty_lowering/bounds.rs | 27 ++++++++-- .../src/hir_ty_lowering/mod.rs | 3 +- .../return-type-notation/path-ambiguous.rs | 27 ++++++++++ .../path-ambiguous.stderr | 54 +++++++++++++++++++ .../path-higher-ranked.rs | 25 +++++++++ .../path-higher-ranked.stderr | 34 ++++++++++++ .../return-type-notation/path-missing.rs | 25 +++++++++ .../return-type-notation/path-missing.stderr | 33 ++++++++++++ .../return-type-notation/path-no-qself.rs | 15 ++++++ .../return-type-notation/path-no-qself.stderr | 23 ++++++++ .../path-non-param-qself.rs | 21 ++++++++ .../path-non-param-qself.stderr | 30 +++++++++++ .../return-type-notation/path-self-qself.rs | 16 ++++++ .../path-self-qself.stderr | 11 ++++ .../return-type-notation/path-type-param.rs | 22 ++++++++ .../path-type-param.stderr | 29 ++++++++++ .../return-type-notation/path-unsatisfied.rs | 25 +++++++++ .../path-unsatisfied.stderr | 36 +++++++++++++ .../return-type-notation/path-works.rs | 23 ++++++++ .../return-type-notation/path-works.stderr | 11 ++++ 25 files changed, 508 insertions(+), 19 deletions(-) create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-missing.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-no-qself.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-self-qself.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-type-param.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-type-param.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-works.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-works.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5266eb0e91f3a..d13e26d251006 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -494,9 +494,14 @@ enum AllowReturnTypeNotation { } enum GenericArgsMode { + /// Allow paren sugar, don't allow RTN. ParenSugar, + /// Allow RTN, don't allow paren sugar. ReturnTypeNotation, + // Error if parenthesized generics or RTN are encountered. Err, + /// Silence errors when lowering generics. Only used with `Res::Err`. + Silence, } impl<'a, 'hir> LoweringContext<'a, 'hir> { diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 6f0641dbc403d..03c8097e4c351 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -111,7 +111,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } // Avoid duplicated errors. - Res::Err => GenericArgsMode::ParenSugar, + Res::Err => GenericArgsMode::Silence, // An error _ => GenericArgsMode::Err, }; @@ -288,11 +288,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { false, ) } - GenericArgsMode::ParenSugar => self.lower_parenthesized_parameter_data( - data, - itctx, - bound_modifier_allowed_features, - ), + GenericArgsMode::ParenSugar | GenericArgsMode::Silence => self + .lower_parenthesized_parameter_data( + data, + itctx, + bound_modifier_allowed_features, + ), GenericArgsMode::Err => { // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait` let sub = if !data.inputs.is_empty() { @@ -330,7 +331,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }, GenericArgs::ParenthesizedElided(span) => { match generic_args_mode { - GenericArgsMode::ReturnTypeNotation => { + GenericArgsMode::ReturnTypeNotation | GenericArgsMode::Silence => { // Ok } GenericArgsMode::ParenSugar | GenericArgsMode::Err => { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 435a0e31b56c2..4c59f7540eebe 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -460,7 +460,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { [] => (generics.span, format!("<{lt_name}>")), [bound, ..] => (bound.span.shrink_to_lo(), format!("{lt_name}, ")), }; - mpart_sugg = Some(errors::AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion { + mpart_sugg = Some(errors::AssociatedItemTraitUninferredGenericParamsMultipartSuggestion { fspan: lt_sp, first: sugg, sspan: span.with_hi(item_segment.ident.span.lo()), @@ -502,7 +502,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { } Ty::new_error( self.tcx(), - self.tcx().dcx().emit_err(errors::AssociatedTypeTraitUninferredGenericParams { + self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams { span, inferred_sugg, bound, 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 b241f8a5317c4..3be908fbaea01 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1886,10 +1886,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { ) }) => { - let Res::Def(DefKind::AssocFn, item_def_id) = path.res else { - bug!(); - }; - (vec![], item_def_id, item_segment) + match path.res { + Res::Err => return, + Res::Def(DefKind::AssocFn, item_def_id) => (vec![], item_def_id, item_segment), + _ => bug!("only expected method resolution for fully qualified RTN"), + } } // If we have a type-dependent path, then we do need to do some lookup. diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index f332080a71d22..4edb68e61999e 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -780,7 +780,7 @@ pub(crate) struct PlaceholderNotAllowedItemSignatures { #[derive(Diagnostic)] #[diag(hir_analysis_associated_type_trait_uninferred_generic_params, code = E0212)] -pub(crate) struct AssociatedTypeTraitUninferredGenericParams { +pub(crate) struct AssociatedItemTraitUninferredGenericParams { #[primary_span] pub span: Span, #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "{bound}")] @@ -796,7 +796,7 @@ pub(crate) struct AssociatedTypeTraitUninferredGenericParams { hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion, applicability = "maybe-incorrect" )] -pub(crate) struct AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion { +pub(crate) struct AssociatedItemTraitUninferredGenericParamsMultipartSuggestion { #[suggestion_part(code = "{first}")] pub fspan: Span, pub first: String, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 033334bec8b60..6f7f312834729 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -487,8 +487,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let _ = self.prohibit_generic_args(mod_segments.iter(), GenericsArgsErrExtend::None); - let Res::Def(DefKind::AssocFn, item_def_id) = path.res else { - bug!("expected RTN to resolve to associated fn"); + let item_def_id = match path.res { + Res::Def(DefKind::AssocFn, item_def_id) => item_def_id, + Res::Err => { + return Ty::new_error_with_message( + tcx, + hir_ty.span, + "failed to resolve RTN", + ); + } + _ => bug!("only expected method resolution for fully qualified RTN"), }; let trait_def_id = tcx.parent(item_def_id); @@ -605,7 +613,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_ident, span, )?, - _ => todo!(), + _ => { + if let Err(reported) = qself_ty.error_reported() { + return Err(reported); + } else { + // FIXME(return_type_notation): Provide some structured suggestion here. + let err = struct_span_code_err!( + self.dcx(), + span, + E0223, + "ambiguous associated function" + ); + return Err(err.emit()); + } + } }; // Don't let `T::method` resolve to some `for<'a> >::method`, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 6998f580d808f..4416caa71ff36 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -813,7 +813,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Search for a trait bound on a type parameter whose trait defines the associated type given by `assoc_name`. + /// Search for a trait bound on a type parameter whose trait defines the associated item + /// given by `assoc_name` and `kind`. /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.rs b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.rs new file mode 100644 index 0000000000000..cb42c33e364da --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.rs @@ -0,0 +1,27 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait A { + fn method() -> impl Sized; +} +trait B { + fn method() -> impl Sized; +} + +fn ambiguous() +where + T::method(..): Send, + //~^ ERROR ambiguous associated function `method` in bounds of `T` +{ +} + +trait Sub: A + B {} + +fn ambiguous_via_supertrait() +where + T::method(..): Send, + //~^ ERROR ambiguous associated function `method` in bounds of `T` +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr new file mode 100644 index 0000000000000..e841049ac66bd --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr @@ -0,0 +1,54 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-ambiguous.rs:1:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0221]: ambiguous associated function `method` in bounds of `T` + --> $DIR/path-ambiguous.rs:13:5 + | +LL | fn method() -> impl Sized; + | -------------------------- ambiguous `method` from `A` +... +LL | fn method() -> impl Sized; + | -------------------------- ambiguous `method` from `B` +... +LL | T::method(..): Send, + | ^^^^^^^^^^^^^ ambiguous associated function `method` + | +help: use fully-qualified syntax to disambiguate + | +LL | ::method(..): Send, + | ~~~~~~~~~~ +help: use fully-qualified syntax to disambiguate + | +LL | ::method(..): Send, + | ~~~~~~~~~~ + +error[E0221]: ambiguous associated function `method` in bounds of `T` + --> $DIR/path-ambiguous.rs:22:5 + | +LL | fn method() -> impl Sized; + | -------------------------- ambiguous `method` from `A` +... +LL | fn method() -> impl Sized; + | -------------------------- ambiguous `method` from `B` +... +LL | T::method(..): Send, + | ^^^^^^^^^^^^^ ambiguous associated function `method` + | +help: use fully-qualified syntax to disambiguate + | +LL | ::method(..): Send, + | ~~~~~~~~~~ +help: use fully-qualified syntax to disambiguate + | +LL | ::method(..): Send, + | ~~~~~~~~~~ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0221`. diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.rs b/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.rs new file mode 100644 index 0000000000000..a4d8f00537105 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.rs @@ -0,0 +1,25 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait A<'a> { + fn method() -> impl Sized; +} +trait B: for<'a> A<'a> {} + +fn higher_ranked() +where + T: for<'a> A<'a>, + T::method(..): Send, + //~^ ERROR cannot use the associated function of a trait with uninferred generic parameters +{ +} + +fn higher_ranked_via_supertrait() +where + T: B, + T::method(..): Send, + //~^ ERROR cannot use the associated function of a trait with uninferred generic parameters +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr new file mode 100644 index 0000000000000..22de6165503a1 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr @@ -0,0 +1,34 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-higher-ranked.rs:1:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0212]: cannot use the associated function of a trait with uninferred generic parameters + --> $DIR/path-higher-ranked.rs:12:5 + | +LL | T::method(..): Send, + | ^^^^^^^^^^^^^ + | +help: use a fully qualified path with inferred lifetimes + | +LL | >::method(..): Send, + | ~~~~~~~~~~~~~~ + +error[E0212]: cannot use the associated function of a trait with uninferred generic parameters + --> $DIR/path-higher-ranked.rs:20:5 + | +LL | T::method(..): Send, + | ^^^^^^^^^^^^^ + | +help: use a fully qualified path with inferred lifetimes + | +LL | >::method(..): Send, + | ~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0212`. diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-missing.rs b/tests/ui/associated-type-bounds/return-type-notation/path-missing.rs new file mode 100644 index 0000000000000..c1a7b95ca2daa --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-missing.rs @@ -0,0 +1,25 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait A { + #[allow(non_camel_case_types)] + type bad; +} + +fn fully_qualified() +where + ::method(..): Send, + //~^ ERROR cannot find method or associated constant `method` in trait `A` + ::bad(..): Send, + //~^ ERROR expected method or associated constant, found associated type `A::bad` +{ +} + +fn type_dependent() +where + T::method(..): Send, + //~^ associated function `method` not found for `T` +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr new file mode 100644 index 0000000000000..0130c3bc61460 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr @@ -0,0 +1,33 @@ +error[E0576]: cannot find method or associated constant `method` in trait `A` + --> $DIR/path-missing.rs:11:15 + | +LL | ::method(..): Send, + | ^^^^^^ not found in `A` + +error[E0575]: expected method or associated constant, found associated type `A::bad` + --> $DIR/path-missing.rs:13:5 + | +LL | ::bad(..): Send, + | ^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as a constructor + +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-missing.rs:1:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0220]: associated function `method` not found for `T` + --> $DIR/path-missing.rs:20:8 + | +LL | T::method(..): Send, + | ^^^^^^ associated function `method` not found + +error: aborting due to 3 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0220, E0575, E0576. +For more information about an error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.rs b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.rs new file mode 100644 index 0000000000000..d2636789c10ab --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.rs @@ -0,0 +1,15 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Trait { + fn method() -> impl Sized; +} + +fn test() +where + Trait::method(..): Send, + //~^ ERROR ambiguous associated type +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr new file mode 100644 index 0000000000000..d66b0a109fc17 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr @@ -0,0 +1,23 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-no-qself.rs:1:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0223]: ambiguous associated type + --> $DIR/path-no-qself.rs:10:5 + | +LL | Trait::method(..): Send, + | ^^^^^^^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL | ::method: Send, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.rs b/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.rs new file mode 100644 index 0000000000000..b0e6ea852b04d --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.rs @@ -0,0 +1,21 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Trait { + fn method() -> impl Sized; +} + +struct Adt; + +fn non_param_qself() +where + <()>::method(..): Send, + //~^ ERROR ambiguous associated function + i32::method(..): Send, + //~^ ERROR ambiguous associated function + Adt::method(..): Send, + //~^ ERROR ambiguous associated function +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr new file mode 100644 index 0000000000000..cd1aa9813e351 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr @@ -0,0 +1,30 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-non-param-qself.rs:1:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0223]: ambiguous associated function + --> $DIR/path-non-param-qself.rs:12:5 + | +LL | <()>::method(..): Send, + | ^^^^^^^^^^^^^^^^ + +error[E0223]: ambiguous associated function + --> $DIR/path-non-param-qself.rs:14:5 + | +LL | i32::method(..): Send, + | ^^^^^^^^^^^^^^^ + +error[E0223]: ambiguous associated function + --> $DIR/path-non-param-qself.rs:16:5 + | +LL | Adt::method(..): Send, + | ^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs b/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs new file mode 100644 index 0000000000000..1173101ae453a --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs @@ -0,0 +1,16 @@ +//@ check-pass + +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Foo { + fn method() -> impl Sized; +} + +trait Bar: Foo { + fn other() + where + Self::method(..): Send; +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.stderr new file mode 100644 index 0000000000000..ab33647583ca3 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.stderr @@ -0,0 +1,11 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-self-qself.rs:3:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-type-param.rs b/tests/ui/associated-type-bounds/return-type-notation/path-type-param.rs new file mode 100644 index 0000000000000..693a300eb1d98 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-type-param.rs @@ -0,0 +1,22 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Foo { + fn method() -> impl Sized; +} + +fn test() +where + ::method(..): Send, + //~^ ERROR return type notation is not allowed for functions that have type parameters +{ +} + +fn test_type_dependent() +where + ::method(..): Send, + //~^ ERROR return type notation is not allowed for functions that have type parameters +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-type-param.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-type-param.stderr new file mode 100644 index 0000000000000..0d33d4f97d7ac --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-type-param.stderr @@ -0,0 +1,29 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-type-param.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 is not allowed for functions that have type parameters + --> $DIR/path-type-param.rs:10:5 + | +LL | fn method() -> impl Sized; + | - type parameter declared here +... +LL | ::method(..): Send, + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: return type notation is not allowed for functions that have type parameters + --> $DIR/path-type-param.rs:17:5 + | +LL | fn method() -> impl Sized; + | - type parameter declared here +... +LL | ::method(..): Send, + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.rs b/tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.rs new file mode 100644 index 0000000000000..a5b0b0e4e251c --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.rs @@ -0,0 +1,25 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Trait { + fn method() -> impl Sized; +} + +struct DoesntWork; +impl Trait for DoesntWork { + fn method() -> impl Sized { + std::ptr::null_mut::<()>() + // This isn't `Send`. + } +} + +fn test() +where + T::method(..): Send, +{ +} + +fn main() { + test::(); + //~^ ERROR `*mut ()` cannot be sent between threads safely +} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.stderr new file mode 100644 index 0000000000000..7d32a428555e8 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-unsatisfied.stderr @@ -0,0 +1,36 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-unsatisfied.rs:1:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: `*mut ()` cannot be sent between threads safely + --> $DIR/path-unsatisfied.rs:23:12 + | +LL | fn method() -> impl Sized { + | ---------- within this `impl Sized` +... +LL | test::(); + | ^^^^^^^^^^ `*mut ()` cannot be sent between threads safely + | + = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()`, which is required by `impl Sized: Send` +note: required because it appears within the type `impl Sized` + --> $DIR/path-unsatisfied.rs:10:20 + | +LL | fn method() -> impl Sized { + | ^^^^^^^^^^ +note: required by a bound in `test` + --> $DIR/path-unsatisfied.rs:18:20 + | +LL | fn test() + | ---- required by a bound in this function +LL | where +LL | T::method(..): Send, + | ^^^^ required by this bound in `test` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-works.rs b/tests/ui/associated-type-bounds/return-type-notation/path-works.rs new file mode 100644 index 0000000000000..027bc89f13e08 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-works.rs @@ -0,0 +1,23 @@ +//@ check-pass + +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Trait { + fn method() -> impl Sized; +} + +struct Works; +impl Trait for Works { + fn method() -> impl Sized {} +} + +fn test() +where + T::method(..): Send, +{ +} + +fn main() { + test::(); +} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-works.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-works.stderr new file mode 100644 index 0000000000000..b1ec8069ba0d4 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-works.stderr @@ -0,0 +1,11 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-works.rs:3:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + From af24d0b66081bebc881769145385f5baeeed390d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 16:39:44 -0400 Subject: [PATCH 6/7] Resolve self type alias in impl for RTN --- .../src/collect/resolve_bound_vars.rs | 26 ++++++++++++++++++- .../path-constrained-in-method.rs | 24 +++++++++++++++++ .../path-constrained-in-method.stderr | 11 ++++++++ .../return-type-notation/path-self-qself.rs | 11 ++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.stderr 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 3be908fbaea01..71d9703bb0edc 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1910,6 +1910,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // from the generics of the parameter's definition, since we want // to be able to resolve an RTN path on a nested body (e.g. method // inside an impl) using the where clauses on the method. + // FIXME(return_type_notation): Think of some better way of doing this. let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics() else { return; @@ -1948,7 +1949,30 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { }; (bound_vars, assoc_item.def_id, item_segment) } - Res::SelfTyAlias { is_trait_impl: true, .. } => todo!(), + // If we have a self type alias (in an impl), try to resolve an + // associated item from one of the supertraits of the impl's trait. + Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. } => { + let hir::ItemKind::Impl(hir::Impl { of_trait: Some(trait_ref), .. }) = self + .tcx + .hir_node_by_def_id(impl_def_id.expect_local()) + .expect_item() + .kind + else { + return; + }; + let Some(trait_def_id) = trait_ref.trait_def_id() else { + return; + }; + let Some((bound_vars, assoc_item)) = BoundVarContext::supertrait_hrtb_vars( + self.tcx, + trait_def_id, + item_segment.ident, + ty::AssocKind::Fn, + ) else { + return; + }; + (bound_vars, assoc_item.def_id, item_segment) + } _ => return, } } diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.rs b/tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.rs new file mode 100644 index 0000000000000..56abd167fb6b7 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.rs @@ -0,0 +1,24 @@ +//@ check-pass + +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Trait { + fn method() -> impl Sized; +} + +fn is_send(_: impl Send) {} + +struct W(T); + +impl W { + fn test() + where + T: Trait, + T::method(..): Send, + { + is_send(T::method()); + } +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.stderr new file mode 100644 index 0000000000000..3db033d8cf5ed --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-constrained-in-method.stderr @@ -0,0 +1,11 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-constrained-in-method.rs:3:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs b/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs index 1173101ae453a..0cf84457ba73f 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs +++ b/tests/ui/associated-type-bounds/return-type-notation/path-self-qself.rs @@ -13,4 +13,15 @@ trait Bar: Foo { Self::method(..): Send; } +fn is_send(_: impl Send) {} + +impl Bar for T { + fn other() + where + Self::method(..): Send, + { + is_send(Self::method()); + } +} + fn main() {} From 1de894f0c1882aed5b6fba6b665de7664626bd59 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 20 Sep 2024 22:59:03 -0400 Subject: [PATCH 7/7] More tests and tweak comments --- .../src/collect/resolve_bound_vars.rs | 19 +++++-- .../bad-inputs-and-output.rs | 18 +++++++ .../bad-inputs-and-output.stderr | 39 +++++++++++++- .../higher-ranked-bound-works.rs | 52 +++++++++++++++++++ .../higher-ranked-bound-works.stderr | 11 ++++ .../namespace-conflict.rs | 42 +++++++++++++++ .../namespace-conflict.stderr | 11 ++++ .../return-type-notation/non-rpitit.rs | 7 ++- .../return-type-notation/non-rpitit.stderr | 19 +++++-- .../return-type-notation/not-a-method.rs | 42 +++++++++++++++ .../return-type-notation/not-a-method.stderr | 49 +++++++++++++++++ 11 files changed, 298 insertions(+), 11 deletions(-) create mode 100644 tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.stderr create mode 100644 tests/ui/associated-type-bounds/return-type-notation/not-a-method.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/not-a-method.stderr 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 71d9703bb0edc..d1601446b6f4c 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1640,9 +1640,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // } // ``` // and a bound that looks like: - // `for<'a> T::Trait<'a, x(): for<'b> Other<'b>>` + // `for<'a> T::Trait<'a, x(..): for<'b> Other<'b>>` // this is going to expand to something like: - // `for<'a> for<'r, T> >::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`. + // `for<'a> for<'r> >::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`. if constraint.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { let bound_vars = if let Some(type_def_id) = type_def_id @@ -1853,13 +1853,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // For example, given // ``` // trait Foo { - // async fn x<'r, T>(); + // async fn x<'r>(); // } // ``` // and a bound that looks like: // `for<'a, 'b> >::x(): Other<'b>` // this is going to expand to something like: - // `for<'a, 'b, 'r, T> >::x::<'r, T>::{opaque#0}: Other<'b>`. + // `for<'a, 'b, 'r> >::x::<'r, T>::{opaque#0}: Other<'b>`. // // We handle this similarly for associated-type-bound style return-type-notation // in `visit_segment_args`. @@ -2000,6 +2000,17 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // the new bound vars. We do this because we need to know how many bound vars // are present on the binder explicitly (i.e. not return-type-notation vars) // to do bound var shifting correctly in HIR lowering. + // + // For example, in `where for<'a> >::method(..): Other`, + // the `late_bound_vars` of the where clause predicate (i.e. this HIR ty's + // parent) will include `'a` AND all the early- and late-bound vars of the + // method. But when lowering the RTN type, we just want the list of vars + // we used to resolve the trait ref. We explicitly stored those back onto + // the item segment, since there's no other good place to put them. + // + // See where these vars are used in `HirTyLowerer::lower_ty_maybe_return_type_notation`. + // And this is exercised in: + // `tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs`. let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id).unwrap(); let existing_bound_vars_saved = existing_bound_vars.clone(); existing_bound_vars.extend(bound_vars); 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 index a8c8a85c5aa68..af64901ace022 100644 --- 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 @@ -16,4 +16,22 @@ fn bar (): Send>>() {} fn baz>() {} //~^ ERROR return type notation arguments must be elided with `..` +fn foo_path() where T::method(i32): Send {} +//~^ ERROR argument types not allowed with return type notation + +fn bar_path() where T::method() -> (): Send {} +//~^ ERROR return type not allowed with return type notation + +fn baz_path() where T::method(): Send {} +//~^ ERROR return type notation arguments must be elided with `..` + +fn foo_qualified() where ::method(i32): Send {} +//~^ ERROR expected associated type + +fn bar_qualified() where ::method() -> (): Send {} +//~^ ERROR expected associated type + +fn baz_qualified() where ::method(): Send {} +//~^ ERROR expected associated type + 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 index 7e1695984f1c3..6808147008756 100644 --- 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 @@ -1,3 +1,21 @@ +error[E0575]: expected associated type, found associated function `Trait::method` + --> $DIR/bad-inputs-and-output.rs:28:36 + | +LL | fn foo_qualified() where ::method(i32): Send {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type + +error[E0575]: expected associated type, found associated function `Trait::method` + --> $DIR/bad-inputs-and-output.rs:31:36 + | +LL | fn bar_qualified() where ::method() -> (): Send {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type + +error[E0575]: expected associated type, found associated function `Trait::method` + --> $DIR/bad-inputs-and-output.rs:34:36 + | +LL | fn baz_qualified() where ::method(): Send {} + | ^^^^^^^^^^^^^^^^^^^^^^ not a associated 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 | @@ -25,5 +43,24 @@ error: return type notation arguments must be elided with `..` LL | fn baz>() {} | ^^ help: add `..`: `(..)` -error: aborting due to 3 previous errors; 1 warning emitted +error: argument types not allowed with return type notation + --> $DIR/bad-inputs-and-output.rs:19:40 + | +LL | fn foo_path() where T::method(i32): Send {} + | ^^^^^ help: remove the input types: `()` + +error: return type not allowed with return type notation + --> $DIR/bad-inputs-and-output.rs:22:42 + | +LL | fn bar_path() where T::method() -> (): Send {} + | ^^^^^^ help: remove the return type + +error: return type notation arguments must be elided with `..` + --> $DIR/bad-inputs-and-output.rs:25:40 + | +LL | fn baz_path() where T::method(): Send {} + | ^^ help: add `..`: `(..)` + +error: aborting due to 9 previous errors; 1 warning emitted +For more information about this error, try `rustc --explain E0575`. diff --git a/tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs b/tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs new file mode 100644 index 0000000000000..d4f21f47c6c64 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs @@ -0,0 +1,52 @@ +//@ check-pass + +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Trait<'a> { + fn late<'b>(&'b self, _: &'a ()) -> impl Sized; + fn early<'b: 'b>(&'b self, _: &'a ()) -> impl Sized; +} + +#[allow(refining_impl_trait_internal)] +impl<'a> Trait<'a> for () { + fn late<'b>(&'b self, _: &'a ()) -> i32 { 1 } + fn early<'b: 'b>(&'b self, _: &'a ()) -> i32 { 1 } +} + +trait Other<'c> {} +impl Other<'_> for i32 {} + +fn test(t: &T) +where + T: for<'a, 'c> Trait<'a, late(..): Other<'c>>, + // which is basically: + // for<'a, 'c> Trait<'a, for<'b> method<'b>: Other<'c>>, + T: for<'a, 'c> Trait<'a, early(..): Other<'c>>, + // which is basically: + // for<'a, 'c> Trait<'a, for<'b> method<'b>: Other<'c>>, +{ + is_other_impl(t.late(&())); + is_other_impl(t.early(&())); +} + +fn test_path(t: &T) +where +T: for<'a> Trait<'a>, + for<'a, 'c> >::late(..): Other<'c>, + // which is basically: + // for<'a, 'b, 'c> >::method::<'b>: Other<'c> + for<'a, 'c> >::early(..): Other<'c>, + // which is basically: + // for<'a, 'b, 'c> >::method::<'b>: Other<'c> +{ + is_other_impl(t.late(&())); + is_other_impl(t.early(&())); +} + +fn is_other_impl(_: impl for<'c> Other<'c>) {} + +fn main() { + test(&()); + test(&()); +} diff --git a/tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.stderr b/tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.stderr new file mode 100644 index 0000000000000..c67231c07f723 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.stderr @@ -0,0 +1,11 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/higher-ranked-bound-works.rs:3:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.rs b/tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.rs new file mode 100644 index 0000000000000..9bdc2d00233e0 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.rs @@ -0,0 +1,42 @@ +//@ check-pass + +#![allow(non_camel_case_types)] +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Foo { + type test; + + fn test() -> impl Bar; +} + +fn call_path() +where + T::test(..): Bar, +{ +} + +fn call_bound>() {} + +trait Bar {} +struct NotBar; +struct YesBar; +impl Bar for YesBar {} + +impl Foo for () { + type test = NotBar; + + // Use refinement here so we can observe `YesBar: Bar`. + #[allow(refining_impl_trait_internal)] + fn test() -> YesBar { + YesBar + } +} + +fn main() { + // If `T::test(..)` resolved to the GAT (erroneously), then this would be + // an error since `<() as Foo>::bar` -- the associated type -- does not + // implement `Bar`, but the return type of the method does. + call_path::<()>(); + call_bound::<()>(); +} diff --git a/tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.stderr b/tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.stderr new file mode 100644 index 0000000000000..f4ece074b2856 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.stderr @@ -0,0 +1,11 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/namespace-conflict.rs:4:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning 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 index d283c6eab370d..35d6dd799c703 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.rs +++ b/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.rs @@ -5,7 +5,10 @@ trait Trait { fn method() {} } -fn test>() {} -//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait` +fn bound>() {} +//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait` + +fn path() where T: Trait, T::method(..): Send {} +//~^ 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 index 79ced3c96ed5c..e308c927bf0e8 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/non-rpitit.stderr @@ -8,15 +8,26 @@ LL | #![feature(return_type_notation)] = 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 + --> $DIR/non-rpitit.rs:8:19 | LL | fn method() {} | ----------- this function must be `async` or return `impl Trait` ... -LL | fn test>() {} - | ^^^^^^^^^^^^^^^^ +LL | fn bound>() {} + | ^^^^^^^^^^^^^^^^ | = note: function returns `()`, which is not compatible with associated type return bounds -error: aborting due to 1 previous error; 1 warning emitted +error: return type notation used on function that is not `async` and does not return `impl Trait` + --> $DIR/non-rpitit.rs:11:30 + | +LL | fn method() {} + | ----------- this function must be `async` or return `impl Trait` +... +LL | fn path() where T: Trait, T::method(..): Send {} + | ^^^^^^^^^^^^^ + | + = note: function returns `()`, which is not compatible with associated type return bounds + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/associated-type-bounds/return-type-notation/not-a-method.rs b/tests/ui/associated-type-bounds/return-type-notation/not-a-method.rs new file mode 100644 index 0000000000000..d94ec6b74d9d2 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/not-a-method.rs @@ -0,0 +1,42 @@ +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +fn function() {} + +fn not_a_method() +where + function(..): Send, + //~^ ERROR expected function, found function `function` + //~| ERROR return type notation not allowed in this position yet +{ +} + +fn not_a_method_and_typoed() +where + function(): Send, + //~^ ERROR expected type, found function `function` +{ +} + +trait Tr { + fn method(); +} + +// Forgot the `T::` +fn maybe_method_overlaps() +where + method(..): Send, + //~^ ERROR cannot find function `method` in this scope + //~| ERROR return type notation not allowed in this position yet +{ +} + +// Forgot the `T::`, AND typoed `(..)` to `()` +fn maybe_method_overlaps_and_typoed() +where + method(): Send, + //~^ ERROR cannot find type `method` in this scope +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/not-a-method.stderr b/tests/ui/associated-type-bounds/return-type-notation/not-a-method.stderr new file mode 100644 index 0000000000000..8add2d4629675 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/not-a-method.stderr @@ -0,0 +1,49 @@ +error[E0575]: expected function, found function `function` + --> $DIR/not-a-method.rs:8:5 + | +LL | function(..): Send, + | ^^^^^^^^^^^^ not a function + +error[E0573]: expected type, found function `function` + --> $DIR/not-a-method.rs:16:5 + | +LL | function(): Send, + | ^^^^^^^^^^ not a type + +error[E0576]: cannot find function `method` in this scope + --> $DIR/not-a-method.rs:28:5 + | +LL | method(..): Send, + | ^^^^^^ not found in this scope + +error[E0412]: cannot find type `method` in this scope + --> $DIR/not-a-method.rs:37:5 + | +LL | method(): Send, + | ^^^^^^ not found in this scope + +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/not-a-method.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 not allowed in this position yet + --> $DIR/not-a-method.rs:8:5 + | +LL | function(..): Send, + | ^^^^^^^^^^^^ + +error: return type notation not allowed in this position yet + --> $DIR/not-a-method.rs:28:5 + | +LL | method(..): Send, + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0412, E0573, E0575, E0576. +For more information about an error, try `rustc --explain E0412`.