From 19881b5a5a17b4fbc91529f867ca4e577af6438a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 14:51:26 -0400 Subject: Conditionally allow lowering RTN (..) in paths --- compiler/rustc_hir_analysis/src/errors.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'compiler/rustc_hir_analysis/src/errors.rs') diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index e4e5f76ae32..11b6fedf1f9 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, +} -- cgit 1.4.1-3-g733a5 From 7c8e281f735d02cddfd5c5ff350482a19a3d62c5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 15:53:39 -0400 Subject: 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 +++++++++++++++---- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 15 ++++++- compiler/rustc_resolve/src/late.rs | 4 +- 7 files changed, 97 insertions(+), 19 deletions(-) (limited to 'compiler/rustc_hir_analysis/src/errors.rs') diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index f5e8119cdca..262277aaa2a 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 ac9976148e2..435a0e31b56 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 7d8fb39710f..b241f8a5317 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 11b6fedf1f9..f332080a71d 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 1aaacf8db32..033334bec8b 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 d42a70308d6..6998f580d80 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 813d569710a..f1d9028f88c 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(..)) -- cgit 1.4.1-3-g733a5 From 174c3f95192068b3d11c00ae77d06808f45c3ef3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Aug 2024 16:31:06 -0400 Subject: 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 +++++++++-- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 3 +- .../return-type-notation/path-ambiguous.rs | 27 +++++++++++ .../return-type-notation/path-ambiguous.stderr | 54 ++++++++++++++++++++++ .../return-type-notation/path-higher-ranked.rs | 25 ++++++++++ .../return-type-notation/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 +++++++++ .../return-type-notation/path-non-param-qself.rs | 21 +++++++++ .../path-non-param-qself.stderr | 30 ++++++++++++ .../return-type-notation/path-self-qself.rs | 16 +++++++ .../return-type-notation/path-self-qself.stderr | 11 +++++ .../return-type-notation/path-type-param.rs | 22 +++++++++ .../return-type-notation/path-type-param.stderr | 29 ++++++++++++ .../return-type-notation/path-unsatisfied.rs | 25 ++++++++++ .../return-type-notation/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 (limited to 'compiler/rustc_hir_analysis/src/errors.rs') diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5266eb0e91f..d13e26d2510 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 6f0641dbc40..03c8097e4c3 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 435a0e31b56..4c59f7540ee 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 b241f8a5317..3be908fbaea 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 f332080a71d..4edb68e6199 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 033334bec8b..6f7f3128347 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 6998f580d80..4416caa71ff 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 00000000000..cb42c33e364 --- /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 00000000000..e841049ac66 --- /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 00000000000..a4d8f005371 --- /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 00000000000..22de6165503 --- /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 00000000000..c1a7b95ca2d --- /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 00000000000..0130c3bc614 --- /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 00000000000..d2636789c10 --- /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 00000000000..d66b0a109fc --- /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 00000000000..b0e6ea852b0 --- /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 00000000000..cd1aa9813e3 --- /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 00000000000..1173101ae45 --- /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 00000000000..ab33647583c --- /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 00000000000..693a300eb1d --- /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 00000000000..0d33d4f97d7 --- /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 00000000000..a5b0b0e4e25 --- /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 00000000000..7d32a428555 --- /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 00000000000..027bc89f13e --- /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 00000000000..b1ec8069ba0 --- /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 + -- cgit 1.4.1-3-g733a5