From 4f1630d3aadc3a4aa003134caaff25354ba1dc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 29 May 2026 20:29:16 +0200 Subject: [PATCH 1/3] Move `compute_object_lifetime_bound` into submodule `dyn_trait` --- .../src/hir_ty_lowering/dyn_trait.rs | 42 +++++++++++++++++ .../src/hir_ty_lowering/mod.rs | 45 +------------------ 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index a498e97403881..818c418ad7c60 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -515,6 +515,48 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } + /// Given the bounds on an object, determines what single region bound (if any) we can + /// use to summarize this type. + /// + /// The basic idea is that we will use the bound the user + /// provided, if they provided one, and otherwise search the supertypes of trait bounds + /// for region bounds. It may be that we can derive no bound at all, in which case + /// we return `None`. + #[instrument(level = "debug", skip(self, span), ret)] + fn compute_object_lifetime_bound( + &self, + span: Span, + existential_predicates: &'tcx ty::List>, + ) -> Option> // if None, use the default + { + let tcx = self.tcx(); + + // No explicit region bound specified. Therefore, examine trait + // bounds and see if we can derive region bounds from those. + let derived_region_bounds = traits::wf::object_region_bounds(tcx, existential_predicates); + + // If there are no derived region bounds, then report back that we + // can find no region bound. The caller will use the default. + if derived_region_bounds.is_empty() { + return None; + } + + // If any of the derived region bounds are 'static, that is always + // the best choice. + if derived_region_bounds.iter().any(|r| r.is_static()) { + return Some(tcx.lifetimes.re_static); + } + + // Determine whether there is exactly one unique region in the set + // of derived region bounds. If so, use that. Otherwise, report an + // error. + let r = derived_region_bounds[0]; + if derived_region_bounds[1..].iter().any(|r1| r != *r1) { + self.dcx().emit_err(crate::errors::AmbiguousLifetimeBound { span }); + } + Some(r) + } + /// Prohibit or lint against *bare* trait object types depending on the edition. /// /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. 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 5a86e8186a5aa..89bf398a9d166 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -47,12 +47,11 @@ use rustc_session::errors::feature_err; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, FulfillmentError}; use tracing::{debug, instrument}; use crate::check::check_abi; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoFieldOnType}; +use crate::errors::{BadReturnTypeNotation, NoFieldOnType}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -3738,48 +3737,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Given the bounds on an object, determines what single region bound (if any) we can - /// use to summarize this type. - /// - /// The basic idea is that we will use the bound the user - /// provided, if they provided one, and otherwise search the supertypes of trait bounds - /// for region bounds. It may be that we can derive no bound at all, in which case - /// we return `None`. - #[instrument(level = "debug", skip(self, span), ret)] - fn compute_object_lifetime_bound( - &self, - span: Span, - existential_predicates: &'tcx ty::List>, - ) -> Option> // if None, use the default - { - let tcx = self.tcx(); - - // No explicit region bound specified. Therefore, examine trait - // bounds and see if we can derive region bounds from those. - let derived_region_bounds = object_region_bounds(tcx, existential_predicates); - - // If there are no derived region bounds, then report back that we - // can find no region bound. The caller will use the default. - if derived_region_bounds.is_empty() { - return None; - } - - // If any of the derived region bounds are 'static, that is always - // the best choice. - if derived_region_bounds.iter().any(|r| r.is_static()) { - return Some(tcx.lifetimes.re_static); - } - - // Determine whether there is exactly one unique region in the set - // of derived region bounds. If so, use that. Otherwise, report an - // error. - let r = derived_region_bounds[0]; - if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - self.dcx().emit_err(AmbiguousLifetimeBound { span }); - } - Some(r) - } - fn construct_const_ctor_value( &self, ctor_def_id: DefId, From a866e4279db74d724eba32c2369ca0fa8606f76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 29 May 2026 16:22:53 +0200 Subject: [PATCH 2/3] Rename `AmbiguousAssocItem` to `AmbiguityBetweenVariantAndAssocItem` and move its `Diagnostic` impl --- .../src/hir_ty_lowering/errors.rs | 52 +++++++++++++++++++ .../src/hir_ty_lowering/mod.rs | 52 +------------------ 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 5e11819805e15..710d527146a53 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1847,6 +1847,58 @@ fn generics_args_err_extend<'a>( } } +pub(super) struct AmbiguityBetweenVariantAndAssocItem<'tcx> { + pub(super) variant_def_id: DefId, + pub(super) item_def_id: DefId, + pub(super) span: Span, + pub(super) segment_ident: Ident, + pub(super) bound_def_id: DefId, + pub(super) self_ty: Ty<'tcx>, + pub(super) tcx: TyCtxt<'tcx>, + pub(super) mode: super::LowerTypeRelativePathMode, +} + +impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAssocItem<'tcx> { + fn into_diag( + self, + dcx: rustc_errors::DiagCtxtHandle<'a>, + level: rustc_errors::Level, + ) -> Diag<'a, ()> { + let Self { + variant_def_id, + item_def_id, + span, + segment_ident, + bound_def_id, + self_ty, + tcx, + mode, + } = self; + let mut lint = Diag::new(dcx, level, "ambiguous associated item"); + + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + segment_ident, + also, + tcx.def_kind_descr(kind, def_id) + ); + lint.span_note(tcx.def_span(def_id), note_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), + Applicability::MachineApplicable, + ); + lint + } +} + pub(crate) fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { match assoc_tag { ty::AssocTag::Fn => "function", 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 89bf398a9d166..e7071861d2ab0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -26,7 +26,7 @@ use rustc_ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level, StashKey, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, StashKey, struct_span_code_err, }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -1549,54 +1549,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, mode: LowerTypeRelativePathMode, ) -> Result, ErrorGuaranteed> { - struct AmbiguousAssocItem<'tcx> { - variant_def_id: DefId, - item_def_id: DefId, - span: Span, - segment_ident: Ident, - bound_def_id: DefId, - self_ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - mode: LowerTypeRelativePathMode, - } - - impl<'a, 'tcx> Diagnostic<'a, ()> for AmbiguousAssocItem<'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { - let Self { - variant_def_id, - item_def_id, - span, - segment_ident, - bound_def_id, - self_ty, - tcx, - mode, - } = self; - let mut lint = Diag::new(dcx, level, "ambiguous associated item"); - - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - segment_ident, - also, - tcx.def_kind_descr(kind, def_id) - ); - lint.span_note(tcx.def_span(def_id), note_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); - - lint.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), - Applicability::MachineApplicable, - ); - lint - } - } - debug!(%self_ty, ?segment.ident); let tcx = self.tcx(); @@ -1676,7 +1628,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { AMBIGUOUS_ASSOCIATED_ITEMS, qpath_hir_id, span, - AmbiguousAssocItem { + errors::AmbiguityBetweenVariantAndAssocItem { variant_def_id, item_def_id, span, From fd36a801b1ea39cc9a251058aa231de09d844f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 29 May 2026 16:27:03 +0200 Subject: [PATCH 3/3] Move distractingly lengthy error reporting code into new `report_ambiguous_assoc_item` Moreover, in `probe_single_bound_for_assoc_item` unconditionally return an `Err(_)` if there's more than a single bound (aka ambiguity) instead of returning the first bound in some cases. This avoids triggering a debug assertion later on ("not enough bound vars"). See the added test for details. --- .../src/hir_ty_lowering/errors.rs | 139 ++++++++++++++++- .../src/hir_ty_lowering/mod.rs | 141 ++---------------- tests/crashes/139387.rs | 15 -- .../path-ambiguous-late-bound-vars.rs | 31 ++++ .../path-ambiguous-late-bound-vars.stderr | 26 ++++ 5 files changed, 205 insertions(+), 147 deletions(-) delete mode 100644 tests/crashes/139387.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 710d527146a53..b8cdd67519bef 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -403,6 +403,141 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) } + pub(super) fn report_ambiguous_assoc_item( + &self, + bound1: ty::PolyTraitRef<'tcx>, + bound2: ty::PolyTraitRef<'tcx>, + matching_candidates: impl Iterator>, + qself: AssocItemQSelf, + assoc_tag: ty::AssocTag, + assoc_ident: Ident, + span: Span, + constraint: Option<&hir::AssocItemConstraint<'tcx>>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + + let assoc_kind_str = assoc_tag_str(assoc_tag); + let qself_str = qself.to_string(tcx); + let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { + span, + assoc_kind: assoc_kind_str, + assoc_ident, + qself: &qself_str, + }); + // Provide a more specific error code index entry for equality bindings. + err.code( + if let Some(constraint) = constraint + && let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind + { + E0222 + } else { + E0221 + }, + ); + + // FIXME(#97583): Print associated item bindings properly (i.e., not as equality + // predicates!). + // FIXME: Turn this into a structured, translatable & more actionable suggestion. + let mut where_bounds = vec![]; + for bound in [bound1, bound2].into_iter().chain(matching_candidates) { + let bound_id = bound.def_id(); + let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( + tcx, + assoc_ident, + assoc_tag, + bound_id, + ); + let bound_span = assoc_item.and_then(|item| tcx.hir_span_if_local(item.def_id)); + + if let Some(bound_span) = bound_span { + err.span_label( + bound_span, + format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),), + ); + if let Some(constraint) = constraint { + match constraint.kind { + hir::AssocItemConstraintKind::Equality { term } => { + let term: ty::Term<'_> = match term { + hir::Term::Ty(ty) => self.lower_ty(ty).into(), + hir::Term::Const(ct) => { + let assoc_item = + assoc_item.expect("assoc_item should be present"); + let projection_term = bound.map_bound(|trait_ref| { + let item_segment = hir::PathSegment { + ident: constraint.ident, + hir_id: constraint.hir_id, + res: Res::Err, + args: Some(constraint.gen_args), + infer_args: false, + }; + + let alias_args = self.lower_generic_args_of_assoc_item( + constraint.ident.span, + assoc_item.def_id, + &item_segment, + trait_ref.args, + ); + ty::AliasTerm::new_from_def_id( + tcx, + assoc_item.def_id, + alias_args, + ) + }); + + // FIXME(mgca): code duplication with other places we lower + // the rhs' of associated const bindings + let ty = projection_term.map_bound(|alias| { + tcx.type_of(alias.def_id()) + .instantiate(tcx, alias.args) + .skip_norm_wip() + }); + let ty = super::bounds::check_assoc_const_binding_type( + self, + constraint.ident, + ty, + constraint.hir_id, + ); + + self.lower_const_arg(ct, ty).into() + } + }; + if term.references_error() { + continue; + } + // FIXME(#97583): This isn't syntactically well-formed! + where_bounds.push(format!( + " T: {trait}::{assoc_ident} = {term}", + trait = bound.print_only_trait_path(), + )); + } + // FIXME: Provide a suggestion. + hir::AssocItemConstraintKind::Bound { bounds: _ } => {} + } + } else { + err.span_suggestion_verbose( + span.with_hi(assoc_ident.span.lo()), + "use fully-qualified syntax to disambiguate", + format!("<{qself_str} as {}>::", bound.print_only_trait_path()), + Applicability::MaybeIncorrect, + ); + } + } else { + let trait_ = tcx.short_string(bound.print_only_trait_path(), err.long_ty_path()); + err.note(format!( + "associated {assoc_kind_str} `{assoc_ident}` could derive from `{trait_}`", + )); + } + } + if !where_bounds.is_empty() { + err.help(format!( + "consider introducing a new type parameter `T` and adding `where` constraints:\ + \n where\n T: {qself_str},\n{}", + where_bounds.join(",\n"), + )); + } + err.emit() + } + pub(crate) fn report_missing_self_ty_for_resolved_path( &self, trait_def_id: DefId, @@ -568,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub(super) fn report_ambiguous_assoc_item_path( + fn report_ambiguous_assoc_item_path( &self, span: Span, types: &[String], @@ -1899,7 +2034,7 @@ impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAs } } -pub(crate) fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { +fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { match assoc_tag { ty::AssocTag::Fn => "function", ty::AssocTag::Const => "constant", 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 e7071861d2ab0..d2c236b5e0a12 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -36,7 +36,6 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::DynCompatibilityViolation; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ self, Const, FnSigKind, GenericArgKind, GenericArgsRef, GenericParamDefKind, LitToConstInput, Ty, TyCtxt, TypeSuperFoldable, TypeVisitableExt, TypingMode, Unnormalized, Upcast, @@ -1281,13 +1280,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { where I: Iterator>, { - let tcx = self.tcx(); - let mut matching_candidates = all_candidates().filter(|r| { self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_tag, assoc_ident) }); - let Some(bound) = matching_candidates.next() else { + let Some(bound1) = matching_candidates.next() else { return Err(self.report_unresolved_assoc_item( all_candidates, qself, @@ -1297,137 +1294,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { constraint, )); }; - debug!(?bound); if let Some(bound2) = matching_candidates.next() { - debug!(?bound2); - - let assoc_kind_str = errors::assoc_tag_str(assoc_tag); - let qself_str = qself.to_string(tcx); - let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { - span, - assoc_kind: assoc_kind_str, + return Err(self.report_ambiguous_assoc_item( + bound1, + bound2, + matching_candidates, + qself, + assoc_tag, assoc_ident, - qself: &qself_str, - }); - // Provide a more specific error code index entry for equality bindings. - err.code( - if let Some(constraint) = constraint - && let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind - { - E0222 - } else { - E0221 - }, - ); - - // FIXME(#97583): Print associated item bindings properly (i.e., not as equality - // predicates!). - // FIXME: Turn this into a structured, translatable & more actionable suggestion. - let mut where_bounds = vec![]; - for bound in [bound, bound2].into_iter().chain(matching_candidates) { - let bound_id = bound.def_id(); - let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( - tcx, - assoc_ident, - assoc_tag, - bound_id, - ); - let bound_span = assoc_item.and_then(|item| tcx.hir_span_if_local(item.def_id)); - - if let Some(bound_span) = bound_span { - err.span_label( - bound_span, - format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),), - ); - if let Some(constraint) = constraint { - match constraint.kind { - hir::AssocItemConstraintKind::Equality { term } => { - let term: ty::Term<'_> = match term { - hir::Term::Ty(ty) => self.lower_ty(ty).into(), - hir::Term::Const(ct) => { - let assoc_item = - assoc_item.expect("assoc_item should be present"); - let projection_term = bound.map_bound(|trait_ref| { - let item_segment = hir::PathSegment { - ident: constraint.ident, - hir_id: constraint.hir_id, - res: Res::Err, - args: Some(constraint.gen_args), - infer_args: false, - }; - - let alias_args = self.lower_generic_args_of_assoc_item( - constraint.ident.span, - assoc_item.def_id, - &item_segment, - trait_ref.args, - ); - ty::AliasTerm::new_from_def_id( - tcx, - assoc_item.def_id, - alias_args, - ) - }); - - // FIXME(mgca): code duplication with other places we lower - // the rhs' of associated const bindings - let ty = projection_term.map_bound(|alias| { - tcx.type_of(alias.def_id()) - .instantiate(tcx, alias.args) - .skip_norm_wip() - }); - let ty = bounds::check_assoc_const_binding_type( - self, - constraint.ident, - ty, - constraint.hir_id, - ); - - self.lower_const_arg(ct, ty).into() - } - }; - if term.references_error() { - continue; - } - // FIXME(#97583): This isn't syntactically well-formed! - where_bounds.push(format!( - " T: {trait}::{assoc_ident} = {term}", - trait = bound.print_only_trait_path(), - )); - } - // FIXME: Provide a suggestion. - hir::AssocItemConstraintKind::Bound { bounds: _ } => {} - } - } else { - err.span_suggestion_verbose( - span.with_hi(assoc_ident.span.lo()), - "use fully-qualified syntax to disambiguate", - format!("<{qself_str} as {}>::", bound.print_only_trait_path()), - Applicability::MaybeIncorrect, - ); - } - } else { - let trait_ = - tcx.short_string(bound.print_only_trait_path(), err.long_ty_path()); - err.note(format!( - "associated {assoc_kind_str} `{assoc_ident}` could derive from `{trait_}`", - )); - } - } - if !where_bounds.is_empty() { - err.help(format!( - "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {qself_str},\n{}", - where_bounds.join(",\n"), - )); - let reported = err.emit(); - return Err(reported); - } - err.emit(); + span, + constraint, + )); } - Ok(bound) + Ok(bound1) } /// Lower a [type-relative](hir::QPath::TypeRelative) path in type position to a type. diff --git a/tests/crashes/139387.rs b/tests/crashes/139387.rs deleted file mode 100644 index 133643ad084ba..0000000000000 --- a/tests/crashes/139387.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: #139387 -//@ needs-rustc-debug-assertions - -trait A { - fn method() -> impl Sized; -} -trait B { - fn method(Hash: Wrap Epsilon<'_, SI1: Eta>>>) -> impl Sized; -} - -fn ambiguous() -where - T::method(..): Send, -{ -} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs new file mode 100644 index 0000000000000..d994e26ad03a9 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs @@ -0,0 +1,31 @@ +// We used to lower the ambiguous `T::f(..)` to `::f::{type#0}` after emitting the error. +// Meaning we picked one of the candidates and proceeded instead of bailing out early. +// However, sensibly RBV doesn't register any bound vars for ambiguous RTN[^1], so later on when +// wrapping the predicate (here: WellFormed) into a Binder we would correctly fail bound var +// validation (in debug mode). +// +// We now bail out early and thus prevent nonsensical types from getting leaked to subsequent +// compiler passes. +// +// [^1]: It actually maintains its own bespoke lowering function for type-relative paths that +// relatively closely mirrors the one in HIR ty lowering. + +// issue: +//@ needs-rustc-debug-assertions +#![feature(return_type_notation)] + +trait A { + fn f() -> impl Sized; +} + +trait B { + fn f<'b>() -> impl Sized; +} + +fn f() +where + T::f(..):, //~ ERROR ambiguous associated function +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr new file mode 100644 index 0000000000000..5b6804095de0d --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr @@ -0,0 +1,26 @@ +error[E0221]: ambiguous associated function `f` in bounds of `T` + --> $DIR/path-ambiguous-late-bound-vars.rs:27:5 + | +LL | fn f() -> impl Sized; + | --------------------- ambiguous `f` from `A` +... +LL | fn f<'b>() -> impl Sized; + | ------------------------- ambiguous `f` from `B` +... +LL | T::f(..):, + | ^^^^^^^^ ambiguous associated function `f` + | +help: use fully-qualified syntax to disambiguate + | +LL - T::f(..):, +LL + ::f(..):, + | +help: use fully-qualified syntax to disambiguate + | +LL - T::f(..):, +LL + ::f(..):, + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0221`.