From 49aaa3750898124d57b37c3437f6ca20ea9ff728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 15 Jan 2026 16:59:45 +0100 Subject: [PATCH 01/10] mGCA: Make traits with type assoc consts dyn compatible... ...but require all assoc consts to be specified via bindings. --- .../src/hir_ty_lowering/dyn_trait.rs | 82 +++++++++---------- compiler/rustc_middle/src/traits/mod.rs | 40 ++++++--- compiler/rustc_middle/src/ty/assoc.rs | 9 +- compiler/rustc_middle/src/ty/sty.rs | 5 +- .../src/traits/dyn_compatibility.rs | 44 ++++++---- .../associated-const-in-trait.rs | 2 - .../associated-const-in-trait.stderr | 40 +-------- tests/ui/associated-item/issue-48027.stderr | 6 +- tests/ui/async-await/async-fn/dyn-pos.stderr | 2 +- .../dyn-compat-basic.rs | 38 +++++++++ .../dyn-compat-const-mismatch.rs | 18 ++++ .../dyn-compat-const-mismatch.stderr | 13 +++ .../dyn-compat-generic-assoc-const.rs | 16 ++++ .../dyn-compat-generic-assoc-const.stderr | 19 +++++ .../dyn-compat-non-type-assoc-const.rs | 20 +++++ .../dyn-compat-non-type-assoc-const.stderr | 43 ++++++++++ .../dyn-compat-unspecified-assoc-consts.rs | 25 ++++++ ...dyn-compat-unspecified-assoc-consts.stderr | 30 +++++++ .../issues/cg-in-dyn-issue-128176.stderr | 2 +- .../associated-consts.stderr | 6 +- .../gat-incompatible-supertrait.stderr | 2 +- .../missing-assoc-type.stderr | 2 +- .../no-duplicate-e0038.stderr | 2 +- .../supertrait-mentions-GAT.stderr | 2 +- .../gat-in-trait-path.stderr | 4 +- .../issue-67510-pass.stderr | 2 +- .../issue-76535.stderr | 2 +- .../issue-78671.stderr | 2 +- .../issue-79422.stderr | 2 +- .../trait-objects.stderr | 2 +- tests/ui/traits/item-privacy.stderr | 12 +-- tests/ui/wf/issue-87495.stderr | 11 +-- 32 files changed, 359 insertions(+), 146 deletions(-) create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-basic.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.stderr create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.stderr create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.stderr create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr 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 2aed66d27e963..695a0793ccd1f 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 @@ -56,7 +56,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } let mut user_written_bounds = Vec::new(); - let mut potential_assoc_types = Vec::new(); + let mut potential_assoc_items = Vec::new(); for poly_trait_ref in hir_bounds.iter() { let result = self.lower_poly_trait_ref( poly_trait_ref, @@ -66,7 +66,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { OverlappingAsssocItemConstraints::Forbidden, ); if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct { - potential_assoc_types.extend(invalid_args); + potential_assoc_items.extend(invalid_args); } } @@ -138,7 +138,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // Map the projection bounds onto a key that makes it easy to remove redundant - // bounds that are constrained by supertraits of the principal def id. + // bounds that are constrained by supertraits of the principal trait. // // Also make sure we detect conflicting bounds from expanding a trait alias and // also specifying it manually, like: @@ -191,13 +191,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let principal_trait = regular_traits.into_iter().next(); - // A stable ordering of associated types from the principal trait and all its - // supertraits. We use this to ensure that different substitutions of a trait - // don't result in `dyn Trait` types with different projections lists, which - // can be unsound: . - // We achieve a stable ordering by walking over the unsubstituted principal - // trait ref. - let mut ordered_associated_types = vec![]; + // A stable ordering of associated types & consts from the principal trait and all its + // supertraits. We use this to ensure that different substitutions of a trait don't + // result in `dyn Trait` types with different projections lists, which can be unsound: + // . + // We achieve a stable ordering by walking over the unsubstituted principal trait ref. + let mut ordered_associated_items = vec![]; if let Some((principal_trait, ref spans)) = principal_trait { let principal_trait = principal_trait.map_bound(|trait_pred| { @@ -223,12 +222,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // FIXME(negative_bounds): Handle this correctly... let trait_ref = tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref)); - ordered_associated_types.extend( + ordered_associated_items.extend( tcx.associated_items(pred.trait_ref.def_id) .in_definition_order() - // We only care about associated types. - .filter(|item| item.is_type()) - // No RPITITs -- they're not dyn-compatible for now. + // Only associated types & consts can possibly be constrained via a binding. + .filter(|item| item.is_type() || item.is_const()) + // Traits with RPITITs are simply not dyn compatible (for now). .filter(|item| !item.is_impl_trait_in_trait()) .map(|item| (item.def_id, trait_ref)), ); @@ -283,15 +282,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - // `dyn Trait` desugars to (not Rust syntax) `dyn Trait where - // ::Assoc = Foo`. So every `Projection` clause is an - // `Assoc = Foo` bound. `needed_associated_types` contains all associated - // types that we expect to be provided by the user, so the following loop - // removes all the associated types that have a corresponding `Projection` - // clause, either from expanding trait aliases or written by the user. + // Flag assoc item bindings that didn't really need to be specified. for &(projection_bound, span) in projection_bounds.values() { let def_id = projection_bound.item_def_id(); if tcx.generics_require_sized_self(def_id) { + // FIXME(mgca): Ideally we would generalize the name of this lint to sth. like + // `unused_associated_item_bindings` since this can now also trigger on *const* + // projections / assoc *const* bindings. tcx.emit_node_span_lint( UNUSED_ASSOCIATED_TYPE_BOUNDS, hir_id, @@ -301,35 +298,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - // We compute the list of projection bounds taking the ordered associated types, - // and check if there was an entry in the collected `projection_bounds`. Those - // are computed by first taking the user-written associated types, then elaborating - // the principal trait ref, and only using those if there was no user-written. - // See note below about how we handle missing associated types with `Self: Sized`, - // which are not required to be provided, but are still used if they are provided. - let mut missing_assoc_types = FxIndexSet::default(); - let projection_bounds: Vec<_> = ordered_associated_types + // The user has to constrain all associated types & consts via bindings unless the + // corresponding associated item has a `where Self: Sized` clause. This can be done + // in the `dyn Trait` directly, in the supertrait bounds or behind trait aliases. + // + // Collect all associated items that weren't specified and compute the list of + // projection bounds which we'll later turn into existential ones. + // + // We intentionally keep around projections whose associated item has a `Self: Sized` + // bound in order to be able to wfcheck the RHS, allow the RHS to constrain generic + // parameters and to imply bounds. + // See also . + let mut missing_assoc_items = FxIndexSet::default(); + let projection_bounds: Vec<_> = ordered_associated_items .into_iter() - .filter_map(|key| { - if let Some(assoc) = projection_bounds.get(&key) { - Some(*assoc) - } else { - // If the associated type has a `where Self: Sized` bound, then - // we do not need to provide the associated type. This results in - // a `dyn Trait` type that has a different number of projection - // bounds, which may lead to type mismatches. - if !tcx.generics_require_sized_self(key.0) { - missing_assoc_types.insert(key); - } - None + .filter_map(|key @ (def_id, _)| { + if let Some(&assoc) = projection_bounds.get(&key) { + return Some(assoc); + } + if !tcx.generics_require_sized_self(def_id) { + missing_assoc_items.insert(key); } + None }) .collect(); + // If there are any associated items whose value wasn't provided, bail out with an error. if let Err(guar) = self.check_for_required_assoc_tys( principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()), - missing_assoc_types, - potential_assoc_types, + missing_assoc_items, + potential_assoc_items, hir_bounds, ) { return Ty::new_error(tcx, guar); diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index c064313f67b23..537c339ad125e 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -759,7 +759,7 @@ pub struct ImplSourceUserDefinedData<'tcx, N> { pub nested: ThinVec, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] pub enum DynCompatibilityViolation { /// `Self: Sized` declared on the trait. SizedSelf(SmallVec<[Span; 1]>), @@ -777,11 +777,17 @@ pub enum DynCompatibilityViolation { /// Method has something illegal. Method(Symbol, MethodViolationCode, Span), - /// Associated const. + /// Associated constant. AssocConst(Symbol, Span), - /// GAT - GAT(Symbol, Span), + /// Generic associated constant. + GenericAssocConst(Symbol, Span), + + /// Associated constant that wasn't marked `#[type_const]`. + NonTypeAssocConst(Symbol, Span), + + /// Generic associated type. + GenericAssocTy(Symbol, Span), } impl DynCompatibilityViolation { @@ -846,14 +852,18 @@ impl DynCompatibilityViolation { MethodViolationCode::UndispatchableReceiver(_), _, ) => format!("method `{name}`'s `self` parameter cannot be dispatched on").into(), - DynCompatibilityViolation::AssocConst(name, DUMMY_SP) => { - format!("it contains associated `const` `{name}`").into() + DynCompatibilityViolation::AssocConst(name, _) => { + format!("it contains associated const `{name}`").into() + } + DynCompatibilityViolation::GenericAssocConst(name, _) => { + format!("it contains generic associated const `{name}`").into() } - DynCompatibilityViolation::AssocConst(..) => { - "it contains this associated `const`".into() + DynCompatibilityViolation::NonTypeAssocConst(name, _) => { + format!("it contains associated const `{name}` that's not marked `#[type_const]`") + .into() } - DynCompatibilityViolation::GAT(name, _) => { - format!("it contains the generic associated type `{name}`").into() + DynCompatibilityViolation::GenericAssocTy(name, _) => { + format!("it contains generic associated type `{name}`").into() } } } @@ -881,7 +891,9 @@ impl DynCompatibilityViolation { _, ) => DynCompatibilityViolationSolution::ChangeToRefSelf(*name, *span), DynCompatibilityViolation::AssocConst(name, _) - | DynCompatibilityViolation::GAT(name, _) + | DynCompatibilityViolation::GenericAssocConst(name, _) + | DynCompatibilityViolation::NonTypeAssocConst(name, _) + | DynCompatibilityViolation::GenericAssocTy(name, _) | DynCompatibilityViolation::Method(name, ..) => { DynCompatibilityViolationSolution::MoveToAnotherTrait(*name) } @@ -897,7 +909,9 @@ impl DynCompatibilityViolation { | DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) | DynCompatibilityViolation::SupertraitConst(spans) => spans.clone(), DynCompatibilityViolation::AssocConst(_, span) - | DynCompatibilityViolation::GAT(_, span) + | DynCompatibilityViolation::GenericAssocConst(_, span) + | DynCompatibilityViolation::NonTypeAssocConst(_, span) + | DynCompatibilityViolation::GenericAssocTy(_, span) | DynCompatibilityViolation::Method(_, _, span) => { if *span != DUMMY_SP { smallvec![*span] @@ -964,7 +978,7 @@ impl DynCompatibilityViolationSolution { } /// Reasons a method might not be dyn-compatible. -#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] pub enum MethodViolationCode { /// e.g., `fn foo()` StaticMethod(Option<(/* add &self */ (String, Span), /* add Self: Sized */ (String, Span))>), diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 5e20bc142ffe6..0957d103d9971 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -148,8 +148,9 @@ impl AssocItem { AssocKind::Type { .. } => DefKind::AssocTy, } } - pub fn is_type(&self) -> bool { - matches!(self.kind, ty::AssocKind::Type { .. }) + + pub fn is_const(&self) -> bool { + matches!(self.kind, ty::AssocKind::Const { .. }) } pub fn is_fn(&self) -> bool { @@ -160,6 +161,10 @@ impl AssocItem { matches!(self.kind, ty::AssocKind::Fn { has_self: true, .. }) } + pub fn is_type(&self) -> bool { + matches!(self.kind, ty::AssocKind::Type { .. }) + } + pub fn as_tag(&self) -> AssocTag { match self.kind { AssocKind::Const { .. } => AssocTag::Const, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c282f2211f650..3519766aae01a 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -762,8 +762,7 @@ impl<'tcx> Ty<'tcx> { .principal_def_id() .into_iter() .flat_map(|principal_def_id| { - // NOTE: This should agree with `needed_associated_types` in - // dyn trait lowering, or else we'll have ICEs. + // IMPORTANT: This has to agree with HIR ty lowering of dyn trait! elaborate::supertraits( tcx, ty::Binder::dummy(ty::TraitRef::identity(tcx, principal_def_id)), @@ -771,7 +770,7 @@ impl<'tcx> Ty<'tcx> { .map(|principal| { tcx.associated_items(principal.def_id()) .in_definition_order() - .filter(|item| item.is_type()) + .filter(|item| item.is_type() || item.is_const()) .filter(|item| !item.is_impl_trait_in_trait()) .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .count() diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 6ab92531e4eff..d3e02210b85f0 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -7,8 +7,9 @@ use std::ops::ControlFlow; use rustc_errors::FatalError; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, @@ -285,7 +286,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { return false; /* No Sized trait, can't require it! */ }; - // Search for a predicate like `Self : Sized` amongst the trait bounds. + // Search for a predicate like `Self: Sized` amongst the trait bounds. let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; elaborate(tcx, predicates).any(|pred| match pred.kind().skip_binder() { @@ -303,24 +304,35 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { }) } -/// Returns `Some(_)` if this item makes the containing trait dyn-incompatible. #[instrument(level = "debug", skip(tcx), ret)] pub fn dyn_compatibility_violations_for_assoc_item( tcx: TyCtxt<'_>, trait_def_id: DefId, item: ty::AssocItem, ) -> Vec { - // Any item that has a `Self : Sized` requisite is otherwise - // exempt from the regulations. + // Any item that has a `Self: Sized` requisite is otherwise exempt from the regulations. if tcx.generics_require_sized_self(item.def_id) { return Vec::new(); } + let span = || item.ident(tcx).span; + match item.kind { - // Associated consts are never dyn-compatible, as they can't have `where` bounds yet at all, - // and associated const bounds in trait objects aren't a thing yet either. ty::AssocKind::Const { name } => { - vec![DynCompatibilityViolation::AssocConst(name, item.ident(tcx).span)] + if tcx.features().min_generic_const_args() { + if !tcx.generics_of(item.def_id).is_own_empty() { + vec![DynCompatibilityViolation::GenericAssocConst(name, span())] + } else if !find_attr!(tcx.get_all_attrs(item.def_id), AttributeKind::TypeConst(_)) { + vec![DynCompatibilityViolation::NonTypeAssocConst(name, span())] + } else { + // We will permit type associated consts if they are explicitly mentioned in the + // trait object type. We can't check this here, as here we only check if it is + // guaranteed to not be possible. + Vec::new() + } + } else { + vec![DynCompatibilityViolation::AssocConst(name, span())] + } } ty::AssocKind::Fn { name, .. } => { virtual_call_violations_for_method(tcx, trait_def_id, item) @@ -335,20 +347,22 @@ pub fn dyn_compatibility_violations_for_assoc_item( (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) } - _ => item.ident(tcx).span, + _ => span(), }; DynCompatibilityViolation::Method(name, v, span) }) .collect() } - // Associated types can only be dyn-compatible if they have `Self: Sized` bounds. - ty::AssocKind::Type { .. } => { - if !tcx.generics_of(item.def_id).is_own_empty() && !item.is_impl_trait_in_trait() { - vec![DynCompatibilityViolation::GAT(item.name(), item.ident(tcx).span)] + ty::AssocKind::Type { data } => { + if !tcx.generics_of(item.def_id).is_own_empty() + && let ty::AssocTypeData::Normal(name) = data + { + vec![DynCompatibilityViolation::GenericAssocTy(name, span())] } else { - // We will permit associated types if they are explicitly mentioned in the trait object. - // We can't check this here, as here we only check if it is guaranteed to not be possible. + // We will permit associated types if they are explicitly mentioned in the trait + // object type. We can't check this here, as here we only check if it is + // guaranteed to not be possible. Vec::new() } } diff --git a/tests/ui/associated-consts/associated-const-in-trait.rs b/tests/ui/associated-consts/associated-const-in-trait.rs index 4d88f4ff53161..6b0b43feb109c 100644 --- a/tests/ui/associated-consts/associated-const-in-trait.rs +++ b/tests/ui/associated-consts/associated-const-in-trait.rs @@ -7,8 +7,6 @@ trait Trait { impl dyn Trait { //~^ ERROR the trait `Trait` is not dyn compatible [E0038] const fn n() -> usize { Self::N } - //~^ ERROR the trait `Trait` is not dyn compatible [E0038] - //~| ERROR the trait `Trait` is not dyn compatible } fn main() {} diff --git a/tests/ui/associated-consts/associated-const-in-trait.stderr b/tests/ui/associated-consts/associated-const-in-trait.stderr index fba7f53c097f2..fb4a55110b4e8 100644 --- a/tests/ui/associated-consts/associated-const-in-trait.stderr +++ b/tests/ui/associated-consts/associated-const-in-trait.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Trait` is not dyn compatible - --> $DIR/associated-const-in-trait.rs:7:6 + --> $DIR/associated-const-in-trait.rs:7:10 | LL | impl dyn Trait { - | ^^^^^^^^^ `Trait` is not dyn compatible + | ^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -11,41 +11,9 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Trait { | ----- this trait is not dyn compatible... LL | const N: usize; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `N` = help: consider moving `N` to another trait -error[E0038]: the trait `Trait` is not dyn compatible - --> $DIR/associated-const-in-trait.rs:9:29 - | -LL | const fn n() -> usize { Self::N } - | ^^^^ `Trait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/associated-const-in-trait.rs:4:11 - | -LL | trait Trait { - | ----- this trait is not dyn compatible... -LL | const N: usize; - | ^ ...because it contains this associated `const` - = help: consider moving `N` to another trait - -error[E0038]: the trait `Trait` is not dyn compatible - --> $DIR/associated-const-in-trait.rs:9:29 - | -LL | const fn n() -> usize { Self::N } - | ^^^^^^^ `Trait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/associated-const-in-trait.rs:4:11 - | -LL | trait Trait { - | ----- this trait is not dyn compatible... -LL | const N: usize; - | ^ ...because it contains this associated `const` - = help: consider moving `N` to another trait - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/associated-item/issue-48027.stderr b/tests/ui/associated-item/issue-48027.stderr index e5c1ced934136..7abcabc1c79d8 100644 --- a/tests/ui/associated-item/issue-48027.stderr +++ b/tests/ui/associated-item/issue-48027.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-48027.rs:6:6 + --> $DIR/issue-48027.rs:6:10 | LL | impl dyn Bar {} - | ^^^^^^^ `Bar` is not dyn compatible + | ^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Bar { | --- this trait is not dyn compatible... LL | const X: usize; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `X` = help: consider moving `X` to another trait error[E0790]: cannot refer to the associated constant on trait without specifying the corresponding `impl` type diff --git a/tests/ui/async-await/async-fn/dyn-pos.stderr b/tests/ui/async-await/async-fn/dyn-pos.stderr index 7d5b37bdbe73b..efd3357ca679a 100644 --- a/tests/ui/async-await/async-fn/dyn-pos.stderr +++ b/tests/ui/async-await/async-fn/dyn-pos.stderr @@ -8,7 +8,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | - = note: the trait is not dyn compatible because it contains the generic associated type `CallRefFuture` + = note: the trait is not dyn compatible because it contains generic associated type `CallRefFuture` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-basic.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-basic.rs new file mode 100644 index 0000000000000..6e35d56dc6e7d --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-basic.rs @@ -0,0 +1,38 @@ +// Traits with type associated consts are dyn compatible. +// Check that we allow the corresp. trait object types if all assoc consts are specified. + +//@ check-pass + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait: SuperTrait { + #[type_const] + const K: usize; +} + +trait SuperTrait { + #[type_const] + const Q: usize; + #[type_const] + const C: usize; +} + +trait Bound { + #[type_const] + const N: usize; +} + +impl Bound for () { + #[type_const] + const N: usize = 10; +} + +fn main() { + let _: dyn Trait; + + let obj: &dyn Bound = &(); + _ = identity(obj); + + fn identity(x: &(impl ?Sized + Bound)) -> &(impl ?Sized + Bound) { x } +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.rs new file mode 100644 index 0000000000000..be41906a4f2f2 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.rs @@ -0,0 +1,18 @@ +// Ensure that we actually enforce equality constraints found in trait object types. + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const N: usize; +} + +impl Trait for () { + #[type_const] + const N: usize = 1; +} + +fn main() { + let _: &dyn Trait = &(); //~ ERROR type mismatch resolving `<() as Trait>::N == 0` +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.stderr new file mode 100644 index 0000000000000..2ab02068eb34a --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-mismatch.stderr @@ -0,0 +1,13 @@ +error[E0271]: type mismatch resolving `<() as Trait>::N == 0` + --> $DIR/dyn-compat-const-mismatch.rs:17:32 + | +LL | let _: &dyn Trait = &(); + | ^^^ expected `0`, found `1` + | + = note: expected constant `0` + found constant `1` + = note: required for the cast from `&()` to `&dyn Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.rs new file mode 100644 index 0000000000000..05e8aeea5ed5a --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.rs @@ -0,0 +1,16 @@ +// Ensure that traits with generic associated consts (GACs) are dyn *in*compatible. +// It would be very hard to make dyn Trait with GACs sound just like with GATs. + +//@ dont-require-annotations: NOTE + +#![feature(min_generic_const_args, generic_const_items)] +#![expect(incomplete_features)] + +trait Trait { + const POLY: T; + //~^ NOTE it contains generic associated const `POLY` +} + +fn main() { + let _: dyn Trait; //~ ERROR the trait `Trait` is not dyn compatible +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.stderr new file mode 100644 index 0000000000000..ed187e0287e9d --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-generic-assoc-const.stderr @@ -0,0 +1,19 @@ +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/dyn-compat-generic-assoc-const.rs:15:16 + | +LL | let _: dyn Trait; + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dyn-compat-generic-assoc-const.rs:10:11 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +LL | const POLY: T; + | ^^^^ ...because it contains generic associated const `POLY` + = help: consider moving `POLY` to another trait + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs new file mode 100644 index 0000000000000..67650bce8c896 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs @@ -0,0 +1,20 @@ +// Ensure that traits with non-type associated consts are dyn *in*compatible. + +//@ dont-require-annotations: NOTE + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + const K: usize; + //~^ NOTE it contains associated const `K` that's not marked `#[type_const]` +} + +fn main() { + let _: dyn Trait; //~ ERROR the trait `Trait` is not dyn compatible + + // Check that specifying the non-type assoc const doesn't "magically make it work". + let _: dyn Trait; + //~^ ERROR the trait `Trait` is not dyn compatible + //~| ERROR use of trait associated const without `#[type_const]` +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.stderr new file mode 100644 index 0000000000000..c579cd312f1fe --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.stderr @@ -0,0 +1,43 @@ +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/dyn-compat-non-type-assoc-const.rs:14:16 + | +LL | let _: dyn Trait; + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dyn-compat-non-type-assoc-const.rs:9:11 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +LL | const K: usize; + | ^ ...because it contains associated const `K` that's not marked `#[type_const]` + = help: consider moving `K` to another trait + +error: use of trait associated const without `#[type_const]` + --> $DIR/dyn-compat-non-type-assoc-const.rs:17:22 + | +LL | let _: dyn Trait; + | ^^^^^ + | + = note: the declaration in the trait must be marked with `#[type_const]` + +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/dyn-compat-non-type-assoc-const.rs:17:16 + | +LL | let _: dyn Trait; + | ^^^^^^^^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dyn-compat-non-type-assoc-const.rs:9:11 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +LL | const K: usize; + | ^ ...because it contains associated const `K` that's not marked `#[type_const]` + = help: consider moving `K` to another trait + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs new file mode 100644 index 0000000000000..532c853c32b83 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs @@ -0,0 +1,25 @@ +// Traits with type associated consts are dyn compatible. However, all associated consts must +// be specified in the corresp. trait object type (barring exceptions) similiar to associated +// types. Check that we reject code that doesn't provide the necessary bindings. + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const K: usize; +} + +// fn ctxt / body +fn main() { + let _: dyn Trait; + //~^ ERROR the value of the associated type `K` in `Trait` must be specified +} + +// item ctxt / signature / non-body +struct Store(dyn Trait); +//~^ ERROR the value of the associated type `K` in `Trait` must be specified + +// item ctxt & no wfcking (eager ty alias) +type DynTrait = dyn Trait; +//~^ ERROR the value of the associated type `K` in `Trait` must be specified diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr new file mode 100644 index 0000000000000..37a4fdd38d3b2 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr @@ -0,0 +1,30 @@ +error[E0191]: the value of the associated type `K` in `Trait` must be specified + --> $DIR/dyn-compat-unspecified-assoc-consts.rs:20:18 + | +LL | const K: usize; + | -------------- `K` defined here +... +LL | struct Store(dyn Trait); + | ^^^^^ help: specify the associated type: `Trait` + +error[E0191]: the value of the associated type `K` in `Trait` must be specified + --> $DIR/dyn-compat-unspecified-assoc-consts.rs:24:21 + | +LL | const K: usize; + | -------------- `K` defined here +... +LL | type DynTrait = dyn Trait; + | ^^^^^ help: specify the associated type: `Trait` + +error[E0191]: the value of the associated type `K` in `Trait` must be specified + --> $DIR/dyn-compat-unspecified-assoc-consts.rs:15:16 + | +LL | const K: usize; + | -------------- `K` defined here +... +LL | let _: dyn Trait; + | ^^^^^ help: specify the associated type: `Trait` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0191`. diff --git a/tests/ui/const-generics/issues/cg-in-dyn-issue-128176.stderr b/tests/ui/const-generics/issues/cg-in-dyn-issue-128176.stderr index 7d563e3b60542..87ec5973fa28c 100644 --- a/tests/ui/const-generics/issues/cg-in-dyn-issue-128176.stderr +++ b/tests/ui/const-generics/issues/cg-in-dyn-issue-128176.stderr @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait X { | - this trait is not dyn compatible... LL | type Y; - | ^ ...because it contains the generic associated type `Y` + | ^ ...because it contains generic associated type `Y` = help: consider moving `Y` to another trait error: aborting due to 1 previous error diff --git a/tests/ui/dyn-compatibility/associated-consts.stderr b/tests/ui/dyn-compatibility/associated-consts.stderr index dc64c93a577ec..a92557ea7b8bd 100644 --- a/tests/ui/dyn-compatibility/associated-consts.stderr +++ b/tests/ui/dyn-compatibility/associated-consts.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/associated-consts.rs:8:31 + --> $DIR/associated-consts.rs:8:35 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^ `Bar` is not dyn compatible + | ^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Bar { | --- this trait is not dyn compatible... LL | const X: usize; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `X` = help: consider moving `X` to another trait error: aborting due to 1 previous error diff --git a/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr index cfebd5d694704..1189c8dc2a6af 100644 --- a/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr +++ b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Super { | ----- this trait is not dyn compatible... LL | type Assoc<'a>; - | ^^^^^ ...because it contains the generic associated type `Assoc` + | ^^^^^ ...because it contains generic associated type `Assoc` = help: consider moving `Assoc` to another trait error: aborting due to 1 previous error diff --git a/tests/ui/dyn-compatibility/missing-assoc-type.stderr b/tests/ui/dyn-compatibility/missing-assoc-type.stderr index 5a7560682f2e8..5a5dc20590d08 100644 --- a/tests/ui/dyn-compatibility/missing-assoc-type.stderr +++ b/tests/ui/dyn-compatibility/missing-assoc-type.stderr @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Foo { | --- this trait is not dyn compatible... LL | type Bar; - | ^^^ ...because it contains the generic associated type `Bar` + | ^^^ ...because it contains generic associated type `Bar` = help: consider moving `Bar` to another trait error: aborting due to 1 previous error diff --git a/tests/ui/dyn-compatibility/no-duplicate-e0038.stderr b/tests/ui/dyn-compatibility/no-duplicate-e0038.stderr index 94037387c3e11..bef580bcedb53 100644 --- a/tests/ui/dyn-compatibility/no-duplicate-e0038.stderr +++ b/tests/ui/dyn-compatibility/no-duplicate-e0038.stderr @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Tr { | -- this trait is not dyn compatible... LL | const N: usize; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `N` = help: consider moving `N` to another trait = help: only type `u8` implements `Tr`; consider using it directly instead. diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr index ba4ce47539956..a8c2f512a9ada 100644 --- a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr +++ b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr @@ -18,7 +18,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable --> $DIR/supertrait-mentions-GAT.rs:4:10 | LL | type Gat<'a> - | ^^^ ...because it contains the generic associated type `Gat` + | ^^^ ...because it contains generic associated type `Gat` ... LL | trait SuperTrait: for<'a> GatTrait = T> { | ---------- this trait is not dyn compatible... diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.stderr index d4ccd80f14658..8dec88f6d932f 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path.stderr +++ b/tests/ui/generic-associated-types/gat-in-trait-path.stderr @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Foo { | --- this trait is not dyn compatible... LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` + | ^ ...because it contains generic associated type `A` = help: consider moving `A` to another trait error[E0038]: the trait `Foo` is not dyn compatible @@ -27,7 +27,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait Foo { | --- this trait is not dyn compatible... LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` + | ^ ...because it contains generic associated type `A` = help: consider moving `A` to another trait error: aborting due to 2 previous errors diff --git a/tests/ui/generic-associated-types/issue-67510-pass.stderr b/tests/ui/generic-associated-types/issue-67510-pass.stderr index 4b56c4ef35f45..8f47407089c70 100644 --- a/tests/ui/generic-associated-types/issue-67510-pass.stderr +++ b/tests/ui/generic-associated-types/issue-67510-pass.stderr @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait X { | - this trait is not dyn compatible... LL | type Y<'a>; - | ^ ...because it contains the generic associated type `Y` + | ^ ...because it contains generic associated type `Y` = help: consider moving `Y` to another trait error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/issue-76535.stderr b/tests/ui/generic-associated-types/issue-76535.stderr index 2daf9d817bb35..324f35e88293d 100644 --- a/tests/ui/generic-associated-types/issue-76535.stderr +++ b/tests/ui/generic-associated-types/issue-76535.stderr @@ -27,7 +27,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | pub trait SuperTrait { | ---------- this trait is not dyn compatible... LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` + | ^^^^^^^ ...because it contains generic associated type `SubType` = help: consider moving `SubType` to another trait = help: only type `SuperStruct` implements `SuperTrait` within this crate; consider using it directly instead. = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type diff --git a/tests/ui/generic-associated-types/issue-78671.stderr b/tests/ui/generic-associated-types/issue-78671.stderr index fff061a8ada75..8b744622fc819 100644 --- a/tests/ui/generic-associated-types/issue-78671.stderr +++ b/tests/ui/generic-associated-types/issue-78671.stderr @@ -27,7 +27,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait CollectionFamily { | ---------------- this trait is not dyn compatible... LL | type Member; - | ^^^^^^ ...because it contains the generic associated type `Member` + | ^^^^^^ ...because it contains generic associated type `Member` = help: consider moving `Member` to another trait error: aborting due to 2 previous errors diff --git a/tests/ui/generic-associated-types/issue-79422.stderr b/tests/ui/generic-associated-types/issue-79422.stderr index dcf3a9008de5f..d22cc48e5290e 100644 --- a/tests/ui/generic-associated-types/issue-79422.stderr +++ b/tests/ui/generic-associated-types/issue-79422.stderr @@ -27,7 +27,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait MapLike { | ------- this trait is not dyn compatible... LL | type VRefCont<'a>: RefCont<'a, V> - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` + | ^^^^^^^^ ...because it contains generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait error: aborting due to 2 previous errors diff --git a/tests/ui/generic-associated-types/trait-objects.stderr b/tests/ui/generic-associated-types/trait-objects.stderr index 8c3af6b654ab7..03a7eb76d91be 100644 --- a/tests/ui/generic-associated-types/trait-objects.stderr +++ b/tests/ui/generic-associated-types/trait-objects.stderr @@ -11,7 +11,7 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait StreamingIterator { | ----------------- this trait is not dyn compatible... LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` + | ^^^^ ...because it contains generic associated type `Item` = help: consider moving `Item` to another trait error: aborting due to 1 previous error diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index eb72b307ed0f3..acb67af53f18c 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -141,15 +141,15 @@ note: for a trait to be dyn compatible it needs to allow building a vtable --> $DIR/item-privacy.rs:26:15 | LL | const A: u8 = 0; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `A` ... LL | const B: u8 = 0; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `B` ... LL | pub trait C: A + B { | - this trait is not dyn compatible... LL | const C: u8 = 0; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `C` = help: consider moving `C` to another trait = help: consider moving `A` to another trait = help: consider moving `B` to another trait @@ -166,15 +166,15 @@ note: for a trait to be dyn compatible it needs to allow building a vtable --> $DIR/item-privacy.rs:26:15 | LL | const A: u8 = 0; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `A` ... LL | const B: u8 = 0; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `B` ... LL | pub trait C: A + B { | - this trait is not dyn compatible... LL | const C: u8 = 0; - | ^ ...because it contains this associated `const` + | ^ ...because it contains associated const `C` = help: consider moving `C` to another trait = help: consider moving `A` to another trait = help: consider moving `B` to another trait diff --git a/tests/ui/wf/issue-87495.stderr b/tests/ui/wf/issue-87495.stderr index bf79535df116c..49651e8d6c05c 100644 --- a/tests/ui/wf/issue-87495.stderr +++ b/tests/ui/wf/issue-87495.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `T` is not dyn compatible - --> $DIR/issue-87495.rs:4:25 + --> $DIR/issue-87495.rs:4:29 | LL | const CONST: (bool, dyn T); - | ^^^^^ `T` is not dyn compatible + | ^ `T` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -11,13 +11,8 @@ note: for a trait to be dyn compatible it needs to allow building a vtable LL | trait T { | - this trait is not dyn compatible... LL | const CONST: (bool, dyn T); - | ^^^^^ ...because it contains this associated `const` + | ^^^^^ ...because it contains associated const `CONST` = help: consider moving `CONST` to another trait -help: you might have meant to use `Self` to refer to the implementing type - | -LL - const CONST: (bool, dyn T); -LL + const CONST: (bool, Self); - | error: aborting due to 1 previous error From b88d3abcffb724a9d3073ab09d16c4ecd26cd0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 8 Jan 2026 08:51:34 +0100 Subject: [PATCH 02/10] Fix v0 symbol mangling for assoc const bindings --- Cargo.lock | 4 +-- compiler/rustc_symbol_mangling/Cargo.toml | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 5 +++- src/doc/rustc/src/symbol-mangling/v0.md | 9 ++++-- .../dyn-compat-symbol-mangling.rs | 28 +++++++++++++++++++ .../dyn-compat-symbol-mangling.v0.stderr | 20 +++++++++++++ 6 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.v0.stderr diff --git a/Cargo.lock b/Cargo.lock index bf28939ac87b2..7a1533baaca8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3352,9 +3352,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 0df9c7682bf8a..d28eb3f047069 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start punycode = "0.4.0" -rustc-demangle = "0.1.21" +rustc-demangle = "0.1.27" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 755b4923e9cd4..95cbb9e07ebb7 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -648,7 +648,10 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { p.push_ident(name.as_str()); match projection.term.kind() { ty::TermKind::Ty(ty) => ty.print(p), - ty::TermKind::Const(c) => c.print(p), + ty::TermKind::Const(c) => { + p.push("K"); + c.print(p) + } }?; } ty::ExistentialPredicate::AutoTrait(def_id) => { diff --git a/src/doc/rustc/src/symbol-mangling/v0.md b/src/doc/rustc/src/symbol-mangling/v0.md index 2bcc453a53265..89d10658bb7e8 100644 --- a/src/doc/rustc/src/symbol-mangling/v0.md +++ b/src/doc/rustc/src/symbol-mangling/v0.md @@ -857,7 +857,11 @@ Remaining primitives are encoded as a crate production, e.g. `C4f128`. > > dyn-trait → *[path]* {*[dyn-trait-assoc-binding]*} > - > dyn-trait-assoc-binding → `p` *[undisambiguated-identifier]* *[type]* + > dyn-trait-assoc-binding → `p` *[undisambiguated-identifier]* *[type-or-const]* + > + > type-or-const → \ + >       *[type]* \ + >    | `K` *[const]* The tag `D` is followed by a *[dyn-bounds]* which encodes the trait bounds, followed by a *[lifetime]* of the trait object lifetime bound. @@ -867,11 +871,12 @@ Remaining primitives are encoded as a crate production, e.g. `C4f128`. Each *[dyn-trait]* represents a trait bound, which consists of a *[path]* to the trait followed by zero or more *[dyn-trait-assoc-binding]* which list the associated types. - Each *[dyn-trait-assoc-binding]* consists of a character `p` followed a *[undisambiguated-identifier]* representing the associated binding name, and finally a *[type]*. + Each *[dyn-trait-assoc-binding]* consists of a character `p` followed a *[undisambiguated-identifier]* representing the associated binding name, and finally a *[type-or-const]*. [dyn-bounds]: #dyn-bounds [dyn-trait]: #dyn-trait [dyn-trait-assoc-binding]: #dyn-trait-assoc-binding +[type-or-const]: #type-or-const * A *[path]* to a named type. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.rs new file mode 100644 index 0000000000000..18be0fccc0519 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.rs @@ -0,0 +1,28 @@ +// Ensure that we can successfully mangle & demangle trait object types w/ assoc const bindings. + +// FIXME(mgca): Legacy mangling still crashes: +// "finding type for [impl], encountered [crate root] with no parent" + +//@ build-fail +//@ revisions: v0 +//\@[legacy] compile-flags: -C symbol-mangling-version=legacy -Z unstable-options +//@ [v0] compile-flags: -C symbol-mangling-version=v0 +//\@[legacy] normalize-stderr: "h[[:xdigit:]]{16}" -> "h[HASH]" +//@ [v0] normalize-stderr: "sym\[.*?\]" -> "sym[HASH]" + +#![feature(min_generic_const_args, rustc_attrs)] +#![expect(incomplete_features)] +#![crate_name = "sym"] + +trait Trait { + #[type_const] + const N: usize; +} + +#[rustc_symbol_name] +//~^ ERROR symbol-name(_RMCs +//~| ERROR demangling(>) +impl dyn Trait {} + +fn main() {} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.v0.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.v0.stderr new file mode 100644 index 0000000000000..424251acc3e97 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-symbol-mangling.v0.stderr @@ -0,0 +1,20 @@ +error: symbol-name(_RMCsCRATE_HASH_3symDNtB_5Traitp1NKj0_EL_) + --> $DIR/dyn-compat-symbol-mangling.rs:22:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(>) + --> $DIR/dyn-compat-symbol-mangling.rs:22:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(>) + --> $DIR/dyn-compat-symbol-mangling.rs:22:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + From 058307ed288a20bd025aa5e79405939f82b6b90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 5 Jan 2026 00:37:30 +0100 Subject: [PATCH 03/10] mGCA: Permit certain const projections in dyn-compatible traits --- .../src/traits/dyn_compatibility.rs | 127 ++++++++++-------- ...ompat-self-const-projections-in-methods.rs | 56 ++++++++ 2 files changed, 124 insertions(+), 59 deletions(-) create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-methods.rs diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index d3e02210b85f0..83ef81081c182 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -683,13 +683,16 @@ enum AllowSelfProjections { No, } -/// This is somewhat subtle. In general, we want to forbid -/// references to `Self` in the argument and return types, -/// since the value of `Self` is erased. However, there is one -/// exception: it is ok to reference `Self` in order to access -/// an associated type of the current trait, since we retain -/// the value of those associated types in the object type -/// itself. +/// Check if the given value contains illegal `Self` references. +/// +/// This is somewhat subtle. In general, we want to forbid references to `Self` in the +/// argument and return types, since the value of `Self` is erased. +/// +/// However, there is one exception: It is ok to reference `Self` in order to access an +/// associated type of the current trait, since we retain the value of those associated +/// types in the trait object type itself. +/// +/// The same thing holds for associated consts under feature `min_generic_const_args`. /// /// ```rust,ignore (example) /// trait SuperTrait { @@ -744,82 +747,88 @@ struct IllegalSelfTypeVisitor<'tcx> { allow_self_projections: AllowSelfProjections, } +impl<'tcx> IllegalSelfTypeVisitor<'tcx> { + fn is_supertrait_of_current_trait(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool { + // Compute supertraits of current trait lazily. + let supertraits = self.supertraits.get_or_insert_with(|| { + util::supertraits( + self.tcx, + ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id)), + ) + .map(|trait_ref| { + self.tcx.erase_and_anonymize_regions( + self.tcx.instantiate_bound_regions_with_erased(trait_ref), + ) + }) + .collect() + }); + + // Determine whether the given trait ref is in fact a supertrait of the current trait. + // In that case, any derived projections are legal, because the term will be specified + // in the trait object type. + // Note that we can just use direct equality here because all of these types are part of + // the formal parameter listing, and hence there should be no inference variables. + let trait_ref = trait_ref + .fold_with(&mut EraseEscapingBoundRegions { tcx: self.tcx, binder: ty::INNERMOST }); + supertraits.contains(&trait_ref) + } +} + impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { type Result = ControlFlow<()>; - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - match t.kind() { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + match ty.kind() { ty::Param(_) => { - if t == self.tcx.types.self_param { + if ty == self.tcx.types.self_param { ControlFlow::Break(()) } else { ControlFlow::Continue(()) } } - ty::Alias(ty::Projection, data) if self.tcx.is_impl_trait_in_trait(data.def_id) => { + ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => { // We'll deny these later in their own pass ControlFlow::Continue(()) } - ty::Alias(ty::Projection, data) => { + ty::Alias(ty::Projection, proj) => { match self.allow_self_projections { AllowSelfProjections::Yes => { - // This is a projected type `::X`. - - // Compute supertraits of current trait lazily. - if self.supertraits.is_none() { - self.supertraits = Some( - util::supertraits( - self.tcx, - ty::Binder::dummy(ty::TraitRef::identity( - self.tcx, - self.trait_def_id, - )), - ) - .map(|trait_ref| { - self.tcx.erase_and_anonymize_regions( - self.tcx.instantiate_bound_regions_with_erased(trait_ref), - ) - }) - .collect(), - ); - } - - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let is_supertrait_of_current_trait = - self.supertraits.as_ref().unwrap().contains( - &data.trait_ref(self.tcx).fold_with( - &mut EraseEscapingBoundRegions { - tcx: self.tcx, - binder: ty::INNERMOST, - }, - ), - ); - - // only walk contained types if it's not a super trait - if is_supertrait_of_current_trait { + // Only walk contained types if the parent trait is not a supertrait. + if self.is_supertrait_of_current_trait(proj.trait_ref(self.tcx)) { ControlFlow::Continue(()) } else { - t.super_visit_with(self) // POSSIBLY reporting an error + ty.super_visit_with(self) } } - AllowSelfProjections::No => t.super_visit_with(self), + AllowSelfProjections::No => ty.super_visit_with(self), } } - _ => t.super_visit_with(self), + _ => ty.super_visit_with(self), } } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { - // Constants can only influence dyn-compatibility if they are generic and reference `Self`. - // This is only possible for unevaluated constants, so we walk these here. - self.tcx.expand_abstract_consts(ct).super_visit_with(self) + let ct = self.tcx.expand_abstract_consts(ct); + + match ct.kind() { + ty::ConstKind::Unevaluated(proj) if self.tcx.features().min_generic_const_args() => { + match self.allow_self_projections { + AllowSelfProjections::Yes => { + let trait_def_id = self.tcx.parent(proj.def); + let trait_ref = ty::TraitRef::from_assoc(self.tcx, trait_def_id, proj.args); + + // Only walk contained consts if the parent trait is not a supertrait. + if self.is_supertrait_of_current_trait(trait_ref) { + ControlFlow::Continue(()) + } else { + ct.super_visit_with(self) + } + } + AllowSelfProjections::No => ct.super_visit_with(self), + } + } + _ => ct.super_visit_with(self), + } } } diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-methods.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-methods.rs new file mode 100644 index 0000000000000..f5a5fbc8188ca --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-methods.rs @@ -0,0 +1,56 @@ +// While mentioning `Self` in the method signature of dyn compatible traits is generally forbidden +// due to type erasure, we can make an exception for const projections from `Self` where the trait +// is the principal trait or a supertrait thereof. That's sound because we force users to specify +// all associated consts in the trait object type, so the projections are all normalizable. +// +// Check that we can define & use dyn compatible traits that reference `Self` const projections. + +// This is a run-pass test to ensure that codegen can actually deal with such method instances +// (e.g., const projections normalize flawlessly to something concrete, symbols get mangled +// properly, the vtable is fine) and simply to ensure that the generated code "received" the +// correct values from the type assoc consts). +//@ run-pass + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const N: usize; + + fn process(&self, _: [u8; Self::N]) -> [u8; Self::N]; +} + +impl Trait for u8 { + #[type_const] + const N: usize = 2; + + fn process(&self, [x, y]: [u8; Self::N]) -> [u8; Self::N] { + [self * x, self + y] + } +} + +impl Trait for [u8; N] { + #[type_const] + const N: usize = N; + + fn process(&self, other: [u8; Self::N]) -> [u8; Self::N] { + let mut result = [0; _]; + for i in 0..Self::N { + result[i] = self[i] + other[i]; + } + result + } +} + +fn main() { + let ops: [Box>; _] = [Box::new(3), Box::new([1, 1])]; + + let mut data = [16, 32]; + + for op in ops { + data = op.process(data); + } + + assert_eq!(data, [49, 36]); +} From feed826ab6f7c6dc2ad85a1f7c26e61c4182740a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 5 Jan 2026 09:50:38 +0100 Subject: [PATCH 04/10] Generalize diag for missing assoc types to account for assoc consts --- .../src/hir_ty_lowering/dyn_trait.rs | 2 +- .../src/hir_ty_lowering/errors.rs | 232 ++++++++++-------- compiler/rustc_middle/src/ty/assoc.rs | 2 +- .../overlaping-bound-suggestion.stderr | 2 +- .../associated-types-incomplete-object.stderr | 6 +- .../ui/associated-types/issue-23595-1.stderr | 2 +- .../dyn-compat-unspecified-assoc-consts.rs | 6 +- ...dyn-compat-unspecified-assoc-consts.stderr | 12 +- .../assoc_type_bounds.stderr | 4 +- .../assoc_type_bounds2.stderr | 4 +- .../assoc_type_bounds_sized_others.stderr | 4 +- .../require-assoc-for-all-super-substs.stderr | 2 +- tests/ui/error-codes/E0191.stderr | 2 +- tests/ui/error-codes/E0220.stderr | 2 +- ...dynless-turbofish-e0191-issue-91997.stderr | 2 +- .../impl-trait/associated-type-cycle.stderr | 2 +- tests/ui/issues/issue-19482.stderr | 2 +- tests/ui/issues/issue-28344.stderr | 4 +- .../ui/suggestions/trait-hidden-method.stderr | 2 +- tests/ui/traits/alias/object-fail.stderr | 2 +- ...dyn-trait-wo-assoc-type-issue-21950.stderr | 2 +- .../with-self-in-projection-output-bad.stderr | 4 +- ...sociated-type-in-trait-object-22434.stderr | 2 +- 23 files changed, 171 insertions(+), 133 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 695a0793ccd1f..d700bd544b0bd 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 @@ -324,7 +324,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect(); // If there are any associated items whose value wasn't provided, bail out with an error. - if let Err(guar) = self.check_for_required_assoc_tys( + if let Err(guar) = self.check_for_required_assoc_items( principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()), missing_assoc_items, potential_assoc_items, 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 92b77a2042b18..f467780143ef6 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -909,43 +909,49 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.emit() } - /// When there are any missing associated types, emit an E0191 error and attempt to supply a - /// reasonable suggestion on how to write it. For the case of multiple associated types in the - /// same trait bound have the same name (as they come from different supertraits), we instead - /// emit a generic note suggesting using a `where` clause to constraint instead. - pub(crate) fn check_for_required_assoc_tys( + /// If there are any missing associated items, emit an error instructing the user to provide + /// them unless that's impossible due to shadowing. Moreover, if any corresponding trait refs + /// are dyn incompatible due to associated items we emit an dyn incompatibility error instead. + pub(crate) fn check_for_required_assoc_items( &self, spans: SmallVec<[Span; 1]>, - missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>, - potential_assoc_types: Vec, + missing_assoc_items: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>, + potential_assoc_items: Vec, trait_bounds: &[hir::PolyTraitRef<'_>], ) -> Result<(), ErrorGuaranteed> { - if missing_assoc_types.is_empty() { + if missing_assoc_items.is_empty() { return Ok(()); } + let tcx = self.tcx(); let principal_span = *spans.first().unwrap(); - let tcx = self.tcx(); // FIXME: This logic needs some more care w.r.t handling of conflicts - let missing_assoc_types: Vec<_> = missing_assoc_types + let missing_assoc_items: Vec<_> = missing_assoc_items .into_iter() .map(|(def_id, trait_ref)| (tcx.associated_item(def_id), trait_ref)) .collect(); - let mut names: FxIndexMap<_, Vec> = Default::default(); + let mut names: FxIndexMap<_, Vec<_>> = Default::default(); let mut names_len = 0; + let mut descr = None; - // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and - // `issue-22560.rs`. - let mut dyn_compatibility_violations = Ok(()); - for (assoc_item, trait_ref) in &missing_assoc_types { - names.entry(trait_ref).or_default().push(assoc_item.name()); - names_len += 1; + enum Descr { + Item, + Tag(ty::AssocTag), + } + for &(assoc_item, trait_ref) in &missing_assoc_items { + // We don't want to suggest specifying associated items if there's something wrong with + // any of them that renders the trait dyn incompatible; providing them certainly won't + // fix the issue and we could also risk suggesting invalid code. + // + // Note that this check is only truly necessary in item ctxts where we merely perform + // *minimal* dyn compatibility checks. In fn ctxts we would've already bailed out with + // an error by this point if the trait was dyn incompatible. let violations = - dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), *assoc_item); + dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), assoc_item); if !violations.is_empty() { - dyn_compatibility_violations = Err(report_dyn_incompatibility( + return Err(report_dyn_incompatibility( tcx, principal_span, None, @@ -954,15 +960,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) .emit()); } - } - if let Err(guar) = dyn_compatibility_violations { - return Err(guar); + names.entry(trait_ref).or_default().push(assoc_item.name()); + names_len += 1; + + descr = match descr { + None => Some(Descr::Tag(assoc_item.as_tag())), + Some(Descr::Tag(tag)) if tag != assoc_item.as_tag() => Some(Descr::Item), + _ => continue, + }; } // related to issue #91997, turbofishes added only when in an expr or pat let mut in_expr_or_pat = false; - if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { + if let ([], [bound]) = (&potential_assoc_items[..], &trait_bounds) { let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); in_expr_or_pat = match grandparent { hir::Node::Expr(_) | hir::Node::Pat(_) => true, @@ -970,37 +981,41 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; } - // We get all the associated items that _are_ set, - // so that we can check if any of their names match one of the ones we are missing. - // This would mean that they are shadowing the associated type we are missing, - // and we can then use their span to indicate this to the user. - let bound_names = trait_bounds - .iter() - .filter_map(|poly_trait_ref| { - let path = poly_trait_ref.trait_ref.path.segments.last()?; - let args = path.args?; - - Some(args.constraints.iter().filter_map(|constraint| { - let ident = constraint.ident; - - let Res::Def(DefKind::Trait, trait_def) = path.res else { - return None; - }; - - let assoc_item = tcx.associated_items(trait_def).find_by_ident_and_kind( - tcx, - ident, - ty::AssocTag::Type, - trait_def, - ); - - Some((ident.name, assoc_item?)) - })) - }) - .flatten() - .collect::>(); + // We get all the associated items that *are* set, so that we can check if any of + // their names match one of the ones we are missing. + // This would mean that they are shadowing the associated item we are missing, and + // we can then use their span to indicate this to the user. + // + // FIXME: This does not account for trait aliases. I think we should just make + // `lower_trait_object_ty` compute the list of all specified items or give us the + // necessary ingredients if it's too expensive to compute in the happy path. + let bound_names: UnordMap<_, _> = + trait_bounds + .iter() + .filter_map(|poly_trait_ref| { + let path = poly_trait_ref.trait_ref.path.segments.last()?; + let args = path.args?; + let Res::Def(DefKind::Trait, trait_def_id) = path.res else { return None }; + + Some(args.constraints.iter().filter_map(move |constraint| { + let hir::AssocItemConstraintKind::Equality { term } = constraint.kind + else { + return None; + }; + let tag = match term { + hir::Term::Ty(_) => ty::AssocTag::Type, + hir::Term::Const(_) => ty::AssocTag::Const, + }; + let assoc_item = tcx + .associated_items(trait_def_id) + .find_by_ident_and_kind(tcx, constraint.ident, tag, trait_def_id)?; + Some(((constraint.ident.name, tag), assoc_item.def_id)) + })) + }) + .flatten() + .collect(); - let mut names = names + let mut names: Vec<_> = names .into_iter() .map(|(trait_, mut assocs)| { assocs.sort(); @@ -1010,66 +1025,71 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { listify(&assocs[..], |a| format!("`{a}`")).unwrap_or_default() ) }) - .collect::>(); + .collect(); names.sort(); let names = names.join(", "); + let descr = match descr.unwrap() { + // FIXME(fmease): Create `ty::AssocTag::descr`. + Descr::Tag(ty::AssocTag::Type) => "associated type", + Descr::Tag(ty::AssocTag::Const) => "associated constant", + _ => "associated item", + }; let mut err = struct_span_code_err!( self.dcx(), principal_span, E0191, - "the value of the associated type{} {} must be specified", - pluralize!(names_len), - names, + "the value of the {descr}{s} {names} must be specified", + s = pluralize!(names_len), ); let mut suggestions = vec![]; - let mut types_count = 0; + let mut items_count = 0; let mut where_constraints = vec![]; let mut already_has_generics_args_suggestion = false; let mut names: UnordMap<_, usize> = Default::default(); - for (item, _) in &missing_assoc_types { - types_count += 1; - *names.entry(item.name()).or_insert(0) += 1; + for (item, _) in &missing_assoc_items { + items_count += 1; + *names.entry((item.name(), item.as_tag())).or_insert(0) += 1; } let mut dupes = false; let mut shadows = false; - for (item, trait_ref) in &missing_assoc_types { + for (item, trait_ref) in &missing_assoc_items { let name = item.name(); - let prefix = if names[&name] > 1 { - let trait_def_id = trait_ref.def_id(); + let key = (name, item.as_tag()); + + if names[&key] > 1 { dupes = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else if bound_names.get(&name).is_some_and(|x| *x != item) { - let trait_def_id = trait_ref.def_id(); + } else if bound_names.get(&key).is_some_and(|&def_id| def_id != item.def_id) { shadows = true; - format!("{}::", tcx.def_path_str(trait_def_id)) + } + + let prefix = if dupes || shadows { + format!("{}::", tcx.def_path_str(trait_ref.def_id())) } else { String::new() }; - let mut is_shadowed = false; - if let Some(assoc_item) = bound_names.get(&name) - && *assoc_item != item + if let Some(&def_id) = bound_names.get(&key) + && def_id != item.def_id { is_shadowed = true; - let rename_message = - if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; + let rename_message = if def_id.is_local() { ", consider renaming it" } else { "" }; err.span_label( - tcx.def_span(assoc_item.def_id), - format!("`{}{}` shadowed here{}", prefix, name, rename_message), + tcx.def_span(def_id), + format!("`{prefix}{name}` shadowed here{rename_message}"), ); } let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; if let Some(sp) = tcx.hir_span_if_local(item.def_id) { - err.span_label(sp, format!("`{}{}` defined here{}", prefix, name, rename_message)); + err.span_label(sp, format!("`{prefix}{name}` defined here{rename_message}")); } } - if potential_assoc_types.len() == missing_assoc_types.len() { + if potential_assoc_items.len() == missing_assoc_items.len() { // When the amount of missing associated types equals the number of // extra type arguments present. A suggesting to replace the generic args with // associated types is already emitted. @@ -1077,30 +1097,48 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else if let (Ok(snippet), false, false) = (tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows) { - let types: Vec<_> = missing_assoc_types + let bindings: Vec<_> = missing_assoc_items .iter() - .map(|(item, _)| format!("{} = Type", item.name())) + .map(|(item, _)| { + format!( + "{} = /* {} */", + item.name(), + match item.kind { + ty::AssocKind::Const { .. } => "CONST", + ty::AssocKind::Type { .. } => "Type", + ty::AssocKind::Fn { .. } => unreachable!(), + } + ) + }) .collect(); + // FIXME(fmease): Does not account for `dyn Trait<>` (suggs `dyn Trait<, X = Y>`). let code = if let Some(snippet) = snippet.strip_suffix('>') { - // The user wrote `Trait<'a>` or similar and we don't have a type we can - // suggest, but at least we can clue them to the correct syntax - // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the - // suggestion. - format!("{}, {}>", snippet, types.join(", ")) + // The user wrote `Trait<'a>` or similar and we don't have a term we can suggest, + // but at least we can clue them to the correct syntax `Trait<'a, Item = /* ... */>` + // while accounting for the `<'a>` in the suggestion. + format!("{}, {}>", snippet, bindings.join(", ")) } else if in_expr_or_pat { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator::`. - format!("{}::<{}>", snippet, types.join(", ")) + // The user wrote `Trait`, so we don't have a term we can suggest, but at least we + // can clue them to the correct syntax `Trait::`. + format!("{}::<{}>", snippet, bindings.join(", ")) } else { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator`. - format!("{}<{}>", snippet, types.join(", ")) + // The user wrote `Trait`, so we don't have a term we can suggest, but at least we + // can clue them to the correct syntax `Trait`. + format!("{}<{}>", snippet, bindings.join(", ")) }; suggestions.push((principal_span, code)); } else if dupes { where_constraints.push(principal_span); } + // FIXME: This note doesn't make sense, get rid of this outright. + // I don't see how adding a type param (to the trait?) would help. + // If the user can modify the trait, they should just rename one of the assoc tys. + // What does it mean with the rest of the message? + // Does it suggest adding equality predicates (unimplemented) to the trait object + // type? (pseudo) "dyn B + ::X = T + ::X = U"? + // Instead, maybe mention shadowing if applicable (yes, even when no "relevant" + // bindings were provided). let where_msg = "consider introducing a new type parameter, adding `where` constraints \ using the fully-qualified path to the associated types"; if !where_constraints.is_empty() && suggestions.is_empty() { @@ -1112,12 +1150,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if suggestions.len() != 1 || already_has_generics_args_suggestion { // We don't need this label if there's an inline suggestion, show otherwise. let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); - for (item, _) in &missing_assoc_types { - types_count += 1; + for (item, _) in &missing_assoc_items { + items_count += 1; *names.entry(item.name()).or_insert(0) += 1; } let mut label = vec![]; - for (item, trait_ref) in &missing_assoc_types { + for (item, trait_ref) in &missing_assoc_items { let name = item.name(); let postfix = if names[&name] > 1 { format!(" (from trait `{}`)", trait_ref.print_trait_sugared()) @@ -1130,9 +1168,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_label( principal_span, format!( - "associated type{} {} must be specified", - pluralize!(label.len()), - label.join(", "), + "{descr}{s} {names} must be specified", + s = pluralize!(label.len()), + names = label.join(", "), ), ); } @@ -1153,7 +1191,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let overlaps = suggestions.windows(2).any(|pair| pair[0].0.overlaps(pair[1].0)); if !suggestions.is_empty() && !overlaps { err.multipart_suggestion( - format!("specify the associated type{}", pluralize!(types_count)), + format!("specify the {descr}{s}", s = pluralize!(items_count)), suggestions, Applicability::HasPlaceholders, ); diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 0957d103d9971..4a54a31094629 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -223,7 +223,7 @@ impl std::fmt::Display for AssocKind { } // Like `AssocKind`, but just the tag, no fields. Used in various kinds of matching. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum AssocTag { Const, Fn, diff --git a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr index ad97df377b75a..54bcef4557463 100644 --- a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr +++ b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr @@ -2,7 +2,7 @@ error[E0191]: the value of the associated types `Item` and `IntoIter` in `IntoIt --> $DIR/overlaping-bound-suggestion.rs:7:13 | LL | inner: >::IntoIterator as Item>::Core, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated types: `IntoIterator, Item = Type, IntoIter = Type>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated types: `IntoIterator, Item = /* Type */, IntoIter = /* Type */>` error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/associated-types-incomplete-object.stderr b/tests/ui/associated-types/associated-types-incomplete-object.stderr index 0c9761afeb517..52aa6adc4695c 100644 --- a/tests/ui/associated-types/associated-types-incomplete-object.stderr +++ b/tests/ui/associated-types/associated-types-incomplete-object.stderr @@ -5,7 +5,7 @@ LL | type B; | ------ `B` defined here ... LL | let b = &42isize as &dyn Foo; - | ^^^^^^^^^^^^ help: specify the associated type: `Foo` + | ^^^^^^^^^^^^ help: specify the associated type: `Foo` error[E0191]: the value of the associated type `A` in `Foo` must be specified --> $DIR/associated-types-incomplete-object.rs:26:30 @@ -14,7 +14,7 @@ LL | type A; | ------ `A` defined here ... LL | let c = &42isize as &dyn Foo; - | ^^^^^^^^^^^ help: specify the associated type: `Foo` + | ^^^^^^^^^^^ help: specify the associated type: `Foo` error[E0191]: the value of the associated types `A` and `B` in `Foo` must be specified --> $DIR/associated-types-incomplete-object.rs:29:30 @@ -25,7 +25,7 @@ LL | type B; | ------ `B` defined here ... LL | let d = &42isize as &dyn Foo; - | ^^^ help: specify the associated types: `Foo` + | ^^^ help: specify the associated types: `Foo` error: aborting due to 3 previous errors diff --git a/tests/ui/associated-types/issue-23595-1.stderr b/tests/ui/associated-types/issue-23595-1.stderr index 694b68ef0901a..8083355deb766 100644 --- a/tests/ui/associated-types/issue-23595-1.stderr +++ b/tests/ui/associated-types/issue-23595-1.stderr @@ -6,7 +6,7 @@ LL | type Value; LL | type ChildKey; | ------------- `ChildKey` defined here LL | type Children = dyn Index; - | ------------- `Children` defined here ^^^^^^^^^ help: specify the associated types: `Hierarchy` + | ------------- `Children` defined here ^^^^^^^^^ help: specify the associated types: `Hierarchy` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs index 532c853c32b83..3525cbceb87bf 100644 --- a/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.rs @@ -13,13 +13,13 @@ trait Trait { // fn ctxt / body fn main() { let _: dyn Trait; - //~^ ERROR the value of the associated type `K` in `Trait` must be specified + //~^ ERROR the value of the associated constant `K` in `Trait` must be specified } // item ctxt / signature / non-body struct Store(dyn Trait); -//~^ ERROR the value of the associated type `K` in `Trait` must be specified +//~^ ERROR the value of the associated constant `K` in `Trait` must be specified // item ctxt & no wfcking (eager ty alias) type DynTrait = dyn Trait; -//~^ ERROR the value of the associated type `K` in `Trait` must be specified +//~^ ERROR the value of the associated constant `K` in `Trait` must be specified diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr index 37a4fdd38d3b2..68658a5711cb8 100644 --- a/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-unspecified-assoc-consts.stderr @@ -1,29 +1,29 @@ -error[E0191]: the value of the associated type `K` in `Trait` must be specified +error[E0191]: the value of the associated constant `K` in `Trait` must be specified --> $DIR/dyn-compat-unspecified-assoc-consts.rs:20:18 | LL | const K: usize; | -------------- `K` defined here ... LL | struct Store(dyn Trait); - | ^^^^^ help: specify the associated type: `Trait` + | ^^^^^ help: specify the associated constant: `Trait` -error[E0191]: the value of the associated type `K` in `Trait` must be specified +error[E0191]: the value of the associated constant `K` in `Trait` must be specified --> $DIR/dyn-compat-unspecified-assoc-consts.rs:24:21 | LL | const K: usize; | -------------- `K` defined here ... LL | type DynTrait = dyn Trait; - | ^^^^^ help: specify the associated type: `Trait` + | ^^^^^ help: specify the associated constant: `Trait` -error[E0191]: the value of the associated type `K` in `Trait` must be specified +error[E0191]: the value of the associated constant `K` in `Trait` must be specified --> $DIR/dyn-compat-unspecified-assoc-consts.rs:15:16 | LL | const K: usize; | -------------- `K` defined here ... LL | let _: dyn Trait; - | ^^^^^ help: specify the associated type: `Trait` + | ^^^^^ help: specify the associated constant: `Trait` error: aborting due to 3 previous errors diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds.stderr index 21ba903011739..717d8949e2d8d 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds.stderr @@ -5,7 +5,7 @@ LL | type Bar | -------- `Bar` defined here ... LL | fn foo(_: &dyn Foo<()>) {} - | ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>` + | ^^^^^^^ help: specify the associated type: `Foo<(), Bar = /* Type */>` error[E0191]: the value of the associated type `Bar` in `Foo` must be specified --> $DIR/assoc_type_bounds.rs:11:16 @@ -14,7 +14,7 @@ LL | type Bar | -------- `Bar` defined here ... LL | fn bar(_: &dyn Foo) {} - | ^^^^^^^^ help: specify the associated type: `Foo` + | ^^^^^^^^ help: specify the associated type: `Foo` error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr index 5c4163b196936..9ddded9addfaf 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr @@ -5,7 +5,7 @@ LL | type Bar | -------- `Bar` defined here ... LL | fn foo(_: &dyn Foo<()>) {} - | ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>` + | ^^^^^^^ help: specify the associated type: `Foo<(), Bar = /* Type */>` error[E0191]: the value of the associated type `Bar` in `Foo` must be specified --> $DIR/assoc_type_bounds2.rs:11:16 @@ -14,7 +14,7 @@ LL | type Bar | -------- `Bar` defined here ... LL | fn bar(_: &dyn Foo) {} - | ^^^^^^^^ help: specify the associated type: `Foo` + | ^^^^^^^^ help: specify the associated type: `Foo` error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_others.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_others.stderr index 5438faaaf0546..8f75f19f571a4 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_others.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_others.stderr @@ -5,7 +5,7 @@ LL | type Bop; | -------- `Bop` defined here ... LL | fn foo(_: &dyn Foo) {} - | ^^^ help: specify the associated type: `Foo` + | ^^^ help: specify the associated type: `Foo` error[E0191]: the value of the associated type `Bop` in `Bar` must be specified --> $DIR/assoc_type_bounds_sized_others.rs:22:16 @@ -14,7 +14,7 @@ LL | type Bop; | -------- `Bop` defined here ... LL | fn bar(_: &dyn Bar) {} - | ^^^ help: specify the associated type: `Bar` + | ^^^ help: specify the associated type: `Bar` error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr b/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr index 3d89b52d522db..f39be936f4177 100644 --- a/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr +++ b/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr @@ -5,7 +5,7 @@ LL | type Assoc: Default; | ------------------- `Assoc` defined here ... LL | let q: as Sup>::Assoc = Default::default(); - | ^^^^^^^^^^^^^ help: specify the associated type: `Dyn` + | ^^^^^^^^^^^^^ help: specify the associated type: `Dyn` error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0191.stderr b/tests/ui/error-codes/E0191.stderr index 63974fd6cbbbd..6fbb20839777a 100644 --- a/tests/ui/error-codes/E0191.stderr +++ b/tests/ui/error-codes/E0191.stderr @@ -5,7 +5,7 @@ LL | type Bar; | -------- `Bar` defined here ... LL | type Foo = dyn Trait; - | ^^^^^ help: specify the associated type: `Trait` + | ^^^^^ help: specify the associated type: `Trait` error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0220.stderr b/tests/ui/error-codes/E0220.stderr index 0e0b5c7084ccd..48197c9a8ccfd 100644 --- a/tests/ui/error-codes/E0220.stderr +++ b/tests/ui/error-codes/E0220.stderr @@ -11,7 +11,7 @@ LL | type Bar; | -------- `Bar` defined here ... LL | type Foo = dyn Trait; - | ^^^^^^^^^^^^ help: specify the associated type: `Trait` + | ^^^^^^^^^^^^ help: specify the associated type: `Trait` error: aborting due to 2 previous errors diff --git a/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr b/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr index c1584e66e3306..cae414bf12255 100644 --- a/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr +++ b/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr @@ -16,7 +16,7 @@ error[E0191]: the value of the associated type `Item` in `Iterator` must be spec --> $DIR/dynless-turbofish-e0191-issue-91997.rs:5:13 | LL | let _ = MyIterator::next; - | ^^^^^^^^^^ help: specify the associated type: `MyIterator::` + | ^^^^^^^^^^ help: specify the associated type: `MyIterator::` error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/impl-trait/associated-type-cycle.stderr b/tests/ui/impl-trait/associated-type-cycle.stderr index 7eef8d1e33893..438a5a92ffc28 100644 --- a/tests/ui/impl-trait/associated-type-cycle.stderr +++ b/tests/ui/impl-trait/associated-type-cycle.stderr @@ -5,7 +5,7 @@ LL | type Bar; | -------- `Bar` defined here ... LL | impl Foo for Box { - | ^^^ help: specify the associated type: `Foo` + | ^^^ help: specify the associated type: `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-19482.stderr b/tests/ui/issues/issue-19482.stderr index 903a9f98c7588..7683e16610e69 100644 --- a/tests/ui/issues/issue-19482.stderr +++ b/tests/ui/issues/issue-19482.stderr @@ -5,7 +5,7 @@ LL | type A; | ------ `A` defined here ... LL | fn bar(x: &dyn Foo) {} - | ^^^ help: specify the associated type: `Foo` + | ^^^ help: specify the associated type: `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-28344.stderr b/tests/ui/issues/issue-28344.stderr index c85424e227941..9940f005b7f8e 100644 --- a/tests/ui/issues/issue-28344.stderr +++ b/tests/ui/issues/issue-28344.stderr @@ -16,7 +16,7 @@ error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be s --> $DIR/issue-28344.rs:5:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - | ^^^^^^ help: specify the associated type: `BitXor::` + | ^^^^^^ help: specify the associated type: `BitXor::` warning: trait objects without an explicit `dyn` are deprecated --> $DIR/issue-28344.rs:10:13 @@ -35,7 +35,7 @@ error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be s --> $DIR/issue-28344.rs:10:13 | LL | let g = BitXor::bitor; - | ^^^^^^ help: specify the associated type: `BitXor::` + | ^^^^^^ help: specify the associated type: `BitXor::` error: aborting due to 2 previous errors; 2 warnings emitted diff --git a/tests/ui/suggestions/trait-hidden-method.stderr b/tests/ui/suggestions/trait-hidden-method.stderr index 87753e5784629..c764034d84664 100644 --- a/tests/ui/suggestions/trait-hidden-method.stderr +++ b/tests/ui/suggestions/trait-hidden-method.stderr @@ -2,7 +2,7 @@ error[E0191]: the value of the associated type `Item` in `Iterator` must be spec --> $DIR/trait-hidden-method.rs:4:33 | LL | Box::new(1..=10) as Box - | ^^^^^^^^ help: specify the associated type: `Iterator` + | ^^^^^^^^ help: specify the associated type: `Iterator` error: aborting due to 1 previous error diff --git a/tests/ui/traits/alias/object-fail.stderr b/tests/ui/traits/alias/object-fail.stderr index d60d884340777..088af686258ab 100644 --- a/tests/ui/traits/alias/object-fail.stderr +++ b/tests/ui/traits/alias/object-fail.stderr @@ -19,7 +19,7 @@ error[E0191]: the value of the associated type `Item` in `Iterator` must be spec --> $DIR/object-fail.rs:9:17 | LL | let _: &dyn IteratorAlias = &vec![123].into_iter(); - | ^^^^^^^^^^^^^ help: specify the associated type: `IteratorAlias` + | ^^^^^^^^^^^^^ help: specify the associated type: `IteratorAlias` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr index 5f4974e6f237d..6a7d3c850c92f 100644 --- a/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr +++ b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr @@ -5,7 +5,7 @@ LL | type Output; | ----------- `Output` defined here ... LL | let x = &10 as &dyn Add; - | ^^^ help: specify the associated type: `Add` + | ^^^ help: specify the associated type: `Add` error: aborting due to 1 previous error diff --git a/tests/ui/traits/object/with-self-in-projection-output-bad.stderr b/tests/ui/traits/object/with-self-in-projection-output-bad.stderr index c9b36e8d29de7..d0cc7f7fc9241 100644 --- a/tests/ui/traits/object/with-self-in-projection-output-bad.stderr +++ b/tests/ui/traits/object/with-self-in-projection-output-bad.stderr @@ -5,7 +5,7 @@ LL | type Output; | ----------- `Output` defined here ... LL | let _x: Box> = Box::new(2u32); - | ^^^^^^^^^^^^^^^^^^ help: specify the associated type: `Helper` + | ^^^^^^^^^^^^^^^^^^ help: specify the associated type: `Helper` error[E0191]: the value of the associated type `Output` in `Base` must be specified --> $DIR/with-self-in-projection-output-bad.rs:48:21 @@ -14,7 +14,7 @@ LL | type Output; | ----------- `Output` defined here ... LL | let _y: Box> = Box::new(2u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `NormalizableHelper` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `NormalizableHelper` error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias/missing-associated-type-in-trait-object-22434.stderr b/tests/ui/type-alias/missing-associated-type-in-trait-object-22434.stderr index 73afefa5a1fdb..c198b83e96886 100644 --- a/tests/ui/type-alias/missing-associated-type-in-trait-object-22434.stderr +++ b/tests/ui/type-alias/missing-associated-type-in-trait-object-22434.stderr @@ -5,7 +5,7 @@ LL | type A; | ------ `A` defined here ... LL | type I<'a> = &'a (dyn Foo + 'a); - | ^^^ help: specify the associated type: `Foo` + | ^^^ help: specify the associated type: `Foo` error: aborting due to 1 previous error From 8b822cc09af660f5a4ee490f2a9bfe967addee2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 5 Jan 2026 10:06:33 +0100 Subject: [PATCH 05/10] Generalize diag for conflicting assoc type bindings to account for assoc consts --- .../src/hir_ty_lowering/dyn_trait.rs | 18 +++++++++--------- .../duplicate-bound-err.rs | 4 ++-- .../duplicate-bound-err.stderr | 4 ++-- .../associated-types-overridden-binding-2.rs | 2 +- ...ssociated-types-overridden-binding-2.stderr | 2 +- .../associated-types-overridden-binding.rs | 2 +- .../associated-types-overridden-binding.stderr | 2 +- 7 files changed, 17 insertions(+), 17 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 d700bd544b0bd..f4002396b9d85 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 @@ -161,8 +161,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { b }); + let item_def_id = proj.item_def_id(); + let key = ( - proj.skip_binder().projection_term.def_id, + item_def_id, tcx.anonymize_bound_vars( proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)), ), @@ -171,19 +173,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { projection_bounds.insert(key, (proj, proj_span)) && tcx.anonymize_bound_vars(proj) != tcx.anonymize_bound_vars(old_proj) { - let item = tcx.item_name(proj.item_def_id()); + let kind = tcx.def_descr(item_def_id); + let name = tcx.item_name(item_def_id); self.dcx() - .struct_span_err( - span, - format!("conflicting associated type bounds for `{item}`"), - ) + .struct_span_err(span, format!("conflicting {kind} bindings for `{name}`")) .with_span_label( old_proj_span, - format!("`{item}` is specified to be `{}` here", old_proj.term()), + format!("`{name}` is specified to be `{}` here", old_proj.term()), ) .with_span_label( proj_span, - format!("`{item}` is specified to be `{}` here", proj.term()), + format!("`{name}` is specified to be `{}` here", proj.term()), ) .emit(); } @@ -261,7 +261,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // the discussion in #56288 for alternatives. if !references_self { let key = ( - pred.skip_binder().projection_term.def_id, + pred.item_def_id(), tcx.anonymize_bound_vars( pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)), ), diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.rs b/tests/ui/associated-type-bounds/duplicate-bound-err.rs index 1587be7200e7e..07eb86108bff4 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound-err.rs +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.rs @@ -86,7 +86,7 @@ fn uncallable_rtn( type MustFail = dyn Iterator; //~^ ERROR [E0719] -//~| ERROR conflicting associated type bounds +//~| ERROR conflicting associated type bindings trait Trait2 { #[type_const] @@ -95,7 +95,7 @@ trait Trait2 { type MustFail2 = dyn Trait2; //~^ ERROR [E0719] -//~| ERROR conflicting associated type bounds +//~| ERROR conflicting associated constant bindings type MustFail3 = dyn Iterator; //~^ ERROR [E0719] diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr index ec864c1dd96e8..80db2cdb933f4 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr @@ -116,7 +116,7 @@ LL | type MustFail = dyn Iterator; | | | `Item` bound here first -error: conflicting associated type bounds for `Item` +error: conflicting associated type bindings for `Item` --> $DIR/duplicate-bound-err.rs:87:17 | LL | type MustFail = dyn Iterator; @@ -133,7 +133,7 @@ LL | type MustFail2 = dyn Trait2; | | | `ASSOC` bound here first -error: conflicting associated type bounds for `ASSOC` +error: conflicting associated constant bindings for `ASSOC` --> $DIR/duplicate-bound-err.rs:96:18 | LL | type MustFail2 = dyn Trait2; diff --git a/tests/ui/associated-types/associated-types-overridden-binding-2.rs b/tests/ui/associated-types/associated-types-overridden-binding-2.rs index 247724eaaf112..fe80413632467 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding-2.rs +++ b/tests/ui/associated-types/associated-types-overridden-binding-2.rs @@ -4,5 +4,5 @@ trait I32Iterator = Iterator; fn main() { let _: &dyn I32Iterator = &vec![42].into_iter(); - //~^ ERROR conflicting associated type bounds + //~^ ERROR conflicting associated type bindings } diff --git a/tests/ui/associated-types/associated-types-overridden-binding-2.stderr b/tests/ui/associated-types/associated-types-overridden-binding-2.stderr index e96a2446b6ce0..1594e57297814 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding-2.stderr +++ b/tests/ui/associated-types/associated-types-overridden-binding-2.stderr @@ -1,4 +1,4 @@ -error: conflicting associated type bounds for `Item` +error: conflicting associated type bindings for `Item` --> $DIR/associated-types-overridden-binding-2.rs:6:13 | LL | trait I32Iterator = Iterator; diff --git a/tests/ui/associated-types/associated-types-overridden-binding.rs b/tests/ui/associated-types/associated-types-overridden-binding.rs index 333a3e30c7dcf..82004f042ea1f 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding.rs +++ b/tests/ui/associated-types/associated-types-overridden-binding.rs @@ -8,5 +8,5 @@ trait U32Iterator = I32Iterator; //~ ERROR type annotations needed fn main() { let _: &dyn I32Iterator; - //~^ ERROR conflicting associated type bounds + //~^ ERROR conflicting associated type bindings } diff --git a/tests/ui/associated-types/associated-types-overridden-binding.stderr b/tests/ui/associated-types/associated-types-overridden-binding.stderr index 08ab9b63ee9fd..d31b050657192 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding.stderr +++ b/tests/ui/associated-types/associated-types-overridden-binding.stderr @@ -22,7 +22,7 @@ note: required by a bound in `I32Iterator` LL | trait I32Iterator = Iterator; | ^^^^^^^^^^ required by this bound in `I32Iterator` -error: conflicting associated type bounds for `Item` +error: conflicting associated type bindings for `Item` --> $DIR/associated-types-overridden-binding.rs:10:13 | LL | trait I32Iterator = Iterator; From 835f905f4eba8dc0a05654a4d288ce8928c16afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 6 Jan 2026 23:40:21 +0100 Subject: [PATCH 06/10] Introduce `AssocTag::descr` & refactor in the vicinity --- .../src/check/compare_impl_item.rs | 2 +- .../src/hir_ty_lowering/errors.rs | 26 +++---- .../src/hir_ty_lowering/mod.rs | 2 +- compiler/rustc_middle/src/ty/assoc.rs | 77 ++++++++++--------- src/librustdoc/clean/inline.rs | 2 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 2 +- .../elided-lifetime.rs | 2 +- .../elided-lifetime.stderr | 6 +- .../static-trait-impl.rs | 2 +- .../static-trait-impl.stderr | 6 +- .../assoc-const-missing-type.rs | 2 +- .../assoc-const-missing-type.stderr | 6 +- .../generic-const-items/compare-impl-item.rs | 10 +-- .../compare-impl-item.stderr | 14 ++-- 14 files changed, 81 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index b09e039417253..4534cfcf962e8 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1960,7 +1960,7 @@ fn compare_generic_param_kinds<'tcx>( trait_item: ty::AssocItem, delay: bool, ) -> Result<(), ErrorGuaranteed> { - assert_eq!(impl_item.as_tag(), trait_item.as_tag()); + assert_eq!(impl_item.tag(), trait_item.tag()); let ty_const_params_of = |def_id| { tcx.generics_of(def_id).own_params.iter().filter(|param| { 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 f467780143ef6..3a77bad3ff520 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -171,7 +171,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let all_candidate_names: Vec<_> = all_candidates() .flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order()) .filter_map(|item| { - if !item.is_impl_trait_in_trait() && item.as_tag() == assoc_tag { + if !item.is_impl_trait_in_trait() && item.tag() == assoc_tag { item.opt_name() } else { None @@ -207,7 +207,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .iter() .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order()) .filter_map(|item| { - (!item.is_impl_trait_in_trait() && item.as_tag() == assoc_tag).then(|| item.name()) + (!item.is_impl_trait_in_trait() && item.tag() == assoc_tag).then(|| item.name()) }) .collect(); @@ -220,7 +220,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .filter(|trait_def_id| { tcx.associated_items(trait_def_id) .filter_by_name_unhygienic(suggested_name) - .any(|item| item.as_tag() == assoc_tag) + .any(|item| item.tag() == assoc_tag) }) .collect::>()[..] { @@ -383,9 +383,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::Term::Ty(ty) => ty.span, hir::Term::Const(ct) => ct.span, }; - (span, Some(ident.span), assoc_item.as_tag(), assoc_tag) + (span, Some(ident.span), assoc_item.tag(), assoc_tag) } else { - (ident.span, None, assoc_tag, assoc_item.as_tag()) + (ident.span, None, assoc_tag, assoc_item.tag()) }; self.dcx().emit_err(errors::AssocKindMismatch { @@ -393,7 +393,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { expected: assoc_tag_str(expected), got: assoc_tag_str(got), expected_because_label, - assoc_kind: assoc_tag_str(assoc_item.as_tag()), + assoc_kind: assoc_tag_str(assoc_item.tag()), def_span: tcx.def_span(assoc_item.def_id), bound_on_assoc_const_label, wrap_in_braces_sugg, @@ -965,8 +965,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { names_len += 1; descr = match descr { - None => Some(Descr::Tag(assoc_item.as_tag())), - Some(Descr::Tag(tag)) if tag != assoc_item.as_tag() => Some(Descr::Item), + None => Some(Descr::Tag(assoc_item.tag())), + Some(Descr::Tag(tag)) if tag != assoc_item.tag() => Some(Descr::Item), _ => continue, }; } @@ -1030,10 +1030,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let names = names.join(", "); let descr = match descr.unwrap() { - // FIXME(fmease): Create `ty::AssocTag::descr`. - Descr::Tag(ty::AssocTag::Type) => "associated type", - Descr::Tag(ty::AssocTag::Const) => "associated constant", - _ => "associated item", + Descr::Item => "associated item", + Descr::Tag(tag) => tag.descr(), }; let mut err = struct_span_code_err!( self.dcx(), @@ -1050,13 +1048,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut names: UnordMap<_, usize> = Default::default(); for (item, _) in &missing_assoc_items { items_count += 1; - *names.entry((item.name(), item.as_tag())).or_insert(0) += 1; + *names.entry((item.name(), item.tag())).or_insert(0) += 1; } let mut dupes = false; let mut shadows = false; for (item, trait_ref) in &missing_assoc_items { let name = item.name(); - let key = (name, item.as_tag()); + let key = (name, item.tag()); if names[&key] > 1 { dupes = true; 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 d2cbf89336d8b..1cfc614ffc1fc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1758,7 +1758,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let item = tcx .associated_items(scope) .filter_by_name_unhygienic(ident.name) - .find(|i| i.as_tag() == assoc_tag && i.ident(tcx).normalize_to_macros_2_0() == ident)?; + .find(|i| i.tag() == assoc_tag && i.ident(tcx).normalize_to_macros_2_0() == ident)?; Some((*item, def_scope)) } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 4a54a31094629..8c560df4e1626 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -126,27 +126,15 @@ impl AssocItem { } pub fn descr(&self) -> &'static str { - match self.kind { - ty::AssocKind::Const { .. } => "associated const", - ty::AssocKind::Fn { has_self: true, .. } => "method", - ty::AssocKind::Fn { has_self: false, .. } => "associated function", - ty::AssocKind::Type { .. } => "associated type", - } + self.kind.descr() } pub fn namespace(&self) -> Namespace { - match self.kind { - ty::AssocKind::Type { .. } => Namespace::TypeNS, - ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => Namespace::ValueNS, - } + self.kind.namespace() } pub fn as_def_kind(&self) -> DefKind { - match self.kind { - AssocKind::Const { .. } => DefKind::AssocConst, - AssocKind::Fn { .. } => DefKind::AssocFn, - AssocKind::Type { .. } => DefKind::AssocTy, - } + self.kind.as_def_kind() } pub fn is_const(&self) -> bool { @@ -165,12 +153,8 @@ impl AssocItem { matches!(self.kind, ty::AssocKind::Type { .. }) } - pub fn as_tag(&self) -> AssocTag { - match self.kind { - AssocKind::Const { .. } => AssocTag::Const, - AssocKind::Fn { .. } => AssocTag::Fn, - AssocKind::Type { .. } => AssocTag::Type, - } + pub fn tag(&self) -> AssocTag { + self.kind.tag() } pub fn is_impl_trait_in_trait(&self) -> bool { @@ -196,33 +180,43 @@ pub enum AssocKind { impl AssocKind { pub fn namespace(&self) -> Namespace { - match *self { - ty::AssocKind::Type { .. } => Namespace::TypeNS, - ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => Namespace::ValueNS, + match self { + Self::Type { .. } => Namespace::TypeNS, + Self::Const { .. } | Self::Fn { .. } => Namespace::ValueNS, + } + } + + pub fn tag(&self) -> AssocTag { + match self { + Self::Const { .. } => AssocTag::Const, + Self::Fn { .. } => AssocTag::Fn, + Self::Type { .. } => AssocTag::Type, } } pub fn as_def_kind(&self) -> DefKind { match self { - AssocKind::Const { .. } => DefKind::AssocConst, - AssocKind::Fn { .. } => DefKind::AssocFn, - AssocKind::Type { .. } => DefKind::AssocTy, + Self::Const { .. } => DefKind::AssocConst, + Self::Fn { .. } => DefKind::AssocFn, + Self::Type { .. } => DefKind::AssocTy, + } + } + + pub fn descr(&self) -> &'static str { + match self { + Self::Fn { has_self: true, .. } => "method", + _ => self.tag().descr(), } } } impl std::fmt::Display for AssocKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AssocKind::Fn { has_self: true, .. } => write!(f, "method"), - AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"), - AssocKind::Const { .. } => write!(f, "associated const"), - AssocKind::Type { .. } => write!(f, "associated type"), - } + f.write_str(self.descr()) } } -// Like `AssocKind`, but just the tag, no fields. Used in various kinds of matching. +/// Like [`AssocKind`], but just the tag, no fields. Used in various kinds of matching. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum AssocTag { Const, @@ -230,6 +224,17 @@ pub enum AssocTag { Type, } +impl AssocTag { + pub fn descr(self) -> &'static str { + // This should match `DefKind::descr`. + match self { + Self::Const => "associated constant", + Self::Fn => "associated function", + Self::Type => "associated type", + } + } +} + /// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. /// /// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since @@ -277,7 +282,7 @@ impl AssocItems { name: Symbol, assoc_tag: AssocTag, ) -> impl '_ + Iterator { - self.filter_by_name_unhygienic(name).filter(move |item| item.as_tag() == assoc_tag) + self.filter_by_name_unhygienic(name).filter(move |item| item.tag() == assoc_tag) } /// Returns the associated item with the given identifier and `AssocKind`, if one exists. @@ -290,7 +295,7 @@ impl AssocItems { parent_def_id: DefId, ) -> Option<&ty::AssocItem> { self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.as_tag() == assoc_tag) + .filter(|item| item.tag() == assoc_tag) .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id)) } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index c86a655c083a5..0bae48b3293ca 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -561,7 +561,7 @@ pub(crate) fn build_impl( .find_by_ident_and_kind( tcx, item.ident(tcx), - item.as_tag(), + item.tag(), associated_trait.def_id, ) .unwrap(); // corresponding associated item has to exist diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index a90d64e972c1f..c1be4acc70683 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -1226,7 +1226,7 @@ pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_n .associated_items(did) .filter_by_name_unhygienic(method_name) .next() - .filter(|item| item.as_tag() == AssocTag::Fn) + .filter(|item| item.tag() == AssocTag::Fn) }) } else { None diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs index d60fe7d409af9..989de389180a1 100644 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs @@ -16,7 +16,7 @@ impl Bar for Foo<'_> { const STATIC: &str = ""; //~^ ERROR `&` without an explicit lifetime name cannot be used here //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration + //~| ERROR lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration } fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr index bb8365b0ae560..ae4a48e4e9328 100644 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr @@ -39,14 +39,14 @@ help: use the `'static` lifetime LL | const STATIC: &'static str = ""; | +++++++ -error[E0195]: lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration --> $DIR/elided-lifetime.rs:16:17 | LL | const STATIC: &str; - | - lifetimes in impl do not match this associated const in trait + | - lifetimes in impl do not match this associated constant in trait ... LL | const STATIC: &str = ""; - | ^ lifetimes do not match associated const in trait + | ^ lifetimes do not match associated constant in trait error: aborting due to 3 previous errors diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs index 85746df146fc7..ecc163aecbf1a 100644 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs @@ -9,7 +9,7 @@ impl Bar<'_> for A { const STATIC: &str = ""; //~^ ERROR `&` without an explicit lifetime name cannot be used here //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration + //~| ERROR lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration } struct B; diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr index 38d24db1317f5..2bc271dccad91 100644 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr @@ -21,14 +21,14 @@ help: use the `'static` lifetime LL | const STATIC: &'static str = ""; | +++++++ -error[E0195]: lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated constant `STATIC` do not match the trait declaration --> $DIR/static-trait-impl.rs:9:17 | LL | const STATIC: &'a str; - | - lifetimes in impl do not match this associated const in trait + | - lifetimes in impl do not match this associated constant in trait ... LL | const STATIC: &str = ""; - | ^ lifetimes do not match associated const in trait + | ^ lifetimes do not match associated constant in trait error: aborting due to 2 previous errors diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.rs b/tests/ui/generic-const-items/assoc-const-missing-type.rs index dde47cf993ea0..2b7167ad067e2 100644 --- a/tests/ui/generic-const-items/assoc-const-missing-type.rs +++ b/tests/ui/generic-const-items/assoc-const-missing-type.rs @@ -14,7 +14,7 @@ impl Trait for () { //~| ERROR mismatched types const Q = ""; //~^ ERROR missing type for `const` item - //~| ERROR lifetime parameters or bounds on associated const `Q` do not match the trait declaration + //~| ERROR lifetime parameters or bounds on associated constant `Q` do not match the trait declaration } fn main() {} diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.stderr b/tests/ui/generic-const-items/assoc-const-missing-type.stderr index 9f6db575ec239..7c79133d64ec3 100644 --- a/tests/ui/generic-const-items/assoc-const-missing-type.stderr +++ b/tests/ui/generic-const-items/assoc-const-missing-type.stderr @@ -15,14 +15,14 @@ error: missing type for `const` item LL | const K = (); | ^ help: provide a type for the associated constant: `()` -error[E0195]: lifetime parameters or bounds on associated const `Q` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated constant `Q` do not match the trait declaration --> $DIR/assoc-const-missing-type.rs:15:12 | LL | const Q<'a>: &'a str; - | ---- lifetimes in impl do not match this associated const in trait + | ---- lifetimes in impl do not match this associated constant in trait ... LL | const Q = ""; - | ^ lifetimes do not match associated const in trait + | ^ lifetimes do not match associated constant in trait error: missing type for `const` item --> $DIR/assoc-const-missing-type.rs:15:12 diff --git a/tests/ui/generic-const-items/compare-impl-item.rs b/tests/ui/generic-const-items/compare-impl-item.rs index b301cd0dae0c9..e8a23b5c44bb2 100644 --- a/tests/ui/generic-const-items/compare-impl-item.rs +++ b/tests/ui/generic-const-items/compare-impl-item.rs @@ -14,15 +14,15 @@ trait Trait

{ impl

Trait

for () { const A: () = (); - //~^ ERROR const `A` has 1 type parameter but its trait declaration has 0 type parameters + //~^ ERROR constant `A` has 1 type parameter but its trait declaration has 0 type parameters const B: u64 = 0; - //~^ ERROR const `B` has 1 const parameter but its trait declaration has 2 const parameters + //~^ ERROR constant `B` has 1 const parameter but its trait declaration has 2 const parameters const C<'a>: &'a str = ""; - //~^ ERROR const `C` has 0 type parameters but its trait declaration has 1 type parameter + //~^ ERROR constant `C` has 0 type parameters but its trait declaration has 1 type parameter const D: u16 = N; - //~^ ERROR const `D` has an incompatible generic parameter for trait `Trait` + //~^ ERROR constant `D` has an incompatible generic parameter for trait `Trait` const E: &'static () = &(); - //~^ ERROR lifetime parameters or bounds on associated const `E` do not match the trait declaration + //~^ ERROR lifetime parameters or bounds on associated constant `E` do not match the trait declaration const F: usize = 1024 where diff --git a/tests/ui/generic-const-items/compare-impl-item.stderr b/tests/ui/generic-const-items/compare-impl-item.stderr index f7e3ff6501b11..ffe65f9fc0886 100644 --- a/tests/ui/generic-const-items/compare-impl-item.stderr +++ b/tests/ui/generic-const-items/compare-impl-item.stderr @@ -1,4 +1,4 @@ -error[E0049]: associated const `A` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated constant `A` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/compare-impl-item.rs:16:13 | LL | const A: (); @@ -7,7 +7,7 @@ LL | const A: (); LL | const A: () = (); | ^ found 1 type parameter -error[E0049]: associated const `B` has 1 const parameter but its trait declaration has 2 const parameters +error[E0049]: associated constant `B` has 1 const parameter but its trait declaration has 2 const parameters --> $DIR/compare-impl-item.rs:18:13 | LL | const B: u64; @@ -18,7 +18,7 @@ LL | const B: u64; LL | const B: u64 = 0; | ^^^^^^^^^^^^ found 1 const parameter -error[E0049]: associated const `C` has 0 type parameters but its trait declaration has 1 type parameter +error[E0049]: associated constant `C` has 0 type parameters but its trait declaration has 1 type parameter --> $DIR/compare-impl-item.rs:20:13 | LL | const C: T; @@ -27,7 +27,7 @@ LL | const C: T; LL | const C<'a>: &'a str = ""; | ^^ found 0 type parameters -error[E0053]: associated const `D` has an incompatible generic parameter for trait `Trait` +error[E0053]: associated constant `D` has an incompatible generic parameter for trait `Trait` --> $DIR/compare-impl-item.rs:22:13 | LL | trait Trait

{ @@ -42,14 +42,14 @@ LL | impl

Trait

for () { LL | const D: u16 = N; | ^^^^^^^^^^^^ found const parameter of type `u16` -error[E0195]: lifetime parameters or bounds on associated const `E` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated constant `E` do not match the trait declaration --> $DIR/compare-impl-item.rs:24:12 | LL | const E<'a>: &'a (); - | ---- lifetimes in impl do not match this associated const in trait + | ---- lifetimes in impl do not match this associated constant in trait ... LL | const E: &'static () = &(); - | ^ lifetimes do not match associated const in trait + | ^ lifetimes do not match associated constant in trait error[E0276]: impl has stricter requirements than trait --> $DIR/compare-impl-item.rs:29:12 From 37bfc235e77a646dcde4b46df2e394d1565f19cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 7 Jan 2026 16:32:14 +0100 Subject: [PATCH 07/10] Fix handling of const params defaults that ref `Self` & generalize diag We used to lower such bad defaulted const args in trait object types to `{type error}`; now correctly lower them to `{const error}`. The added tests used to ICE prior to this change. --- compiler/rustc_hir_analysis/messages.ftl | 45 +++++++------ compiler/rustc_hir_analysis/src/errors.rs | 63 +++++++++++++------ .../src/hir_ty_lowering/dyn_trait.rs | 12 ++-- .../src/hir_ty_lowering/errors.rs | 16 +++-- tests/crashes/136063.rs | 6 -- tests/crashes/137260.rs | 11 ---- tests/crashes/137514.rs | 9 --- ...ompat-const-param-default-mentions-self.rs | 21 +++++++ ...t-const-param-default-mentions-self.stderr | 18 ++++++ .../default-param-self-projection.stderr | 8 +-- tests/ui/error-codes/E0393.stderr | 8 +-- tests/ui/issues/issue-22370.stderr | 8 +-- .../unspecified-self-in-trait-ref.stderr | 2 +- ...parameter-defaults-referencing-Self.stderr | 8 +-- 14 files changed, 133 insertions(+), 102 deletions(-) delete mode 100644 tests/crashes/136063.rs delete mode 100644 tests/crashes/137260.rs delete mode 100644 tests/crashes/137514.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 416a6b19edfc8..69e0c605b23df 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -329,37 +329,18 @@ hir_analysis_manual_implementation = hir_analysis_method_should_return_future = method should be `async` or return a future, but it is synchronous .note = this method is `async` so it expects a future to be returned -hir_analysis_missing_one_of_trait_item = not all trait items implemented, missing one of: `{$missing_items_msg}` - .label = missing one of `{$missing_items_msg}` in implementation - .note = required because of this annotation - -hir_analysis_missing_trait_item = not all trait items implemented, missing: `{$missing_items_msg}` - .label = missing `{$missing_items_msg}` in implementation - -hir_analysis_missing_trait_item_label = `{$item}` from trait - -hir_analysis_missing_trait_item_suggestion = implement the missing item: `{$snippet}` - -hir_analysis_missing_trait_item_unstable = not all trait items implemented, missing: `{$missing_item_name}` - .note = default implementation of `{$missing_item_name}` is unstable - .some_note = use of unstable library feature `{$feature}`: {$reason} - .none_note = use of unstable library feature `{$feature}` - -hir_analysis_missing_type_params = - the type {$parameterCount -> +hir_analysis_missing_generic_params = + the {$descr} {$parameterCount -> [one] parameter *[other] parameters } {$parameters} must be explicitly specified - .label = type {$parameterCount -> + .label = {$descr} {$parameterCount -> [one] parameter *[other] parameters } {$parameters} must be specified for this - .suggestion = set the type {$parameterCount -> + .suggestion = explicitly specify the {$descr} {$parameterCount -> [one] parameter *[other] parameters - } to the desired {$parameterCount -> - [one] type - *[other] types } .no_suggestion_label = missing {$parameterCount -> [one] reference @@ -371,7 +352,23 @@ hir_analysis_missing_type_params = } `Self`, the {$parameterCount -> [one] parameter *[other] parameters - } must be specified on the object type + } must be specified on the trait object type + +hir_analysis_missing_one_of_trait_item = not all trait items implemented, missing one of: `{$missing_items_msg}` + .label = missing one of `{$missing_items_msg}` in implementation + .note = required because of this annotation + +hir_analysis_missing_trait_item = not all trait items implemented, missing: `{$missing_items_msg}` + .label = missing `{$missing_items_msg}` in implementation + +hir_analysis_missing_trait_item_label = `{$item}` from trait + +hir_analysis_missing_trait_item_suggestion = implement the missing item: `{$snippet}` + +hir_analysis_missing_trait_item_unstable = not all trait items implemented, missing: `{$missing_item_name}` + .note = default implementation of `{$missing_item_name}` is unstable + .some_note = use of unstable library feature `{$feature}`: {$reason} + .none_note = use of unstable library feature `{$feature}` hir_analysis_no_variant_named = no variant named `{$ident}` found for enum `{$ty}` diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index b388396ac4fcd..b6ba87e4594be 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -4,11 +4,11 @@ use rustc_abi::ExternAbi; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, - MultiSpan, + MultiSpan, listify, }; use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; @@ -400,35 +400,58 @@ pub(crate) struct UnconstrainedOpaqueType { pub what: &'static str, } -pub(crate) struct MissingTypeParams { +pub(crate) struct MissingGenericParams { pub span: Span, pub def_span: Span, pub span_snippet: Option, - pub missing_type_params: Vec, + pub missing_generic_params: Vec<(Symbol, ty::GenericParamDefKind)>, pub empty_generic_args: bool, } -// Manual implementation of `Diagnostic` to be able to call `span_to_snippet`. -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams { +// FIXME: This doesn't need to be a manual impl! +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingGenericParams { #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let mut err = Diag::new(dcx, level, fluent::hir_analysis_missing_type_params); + let mut err = Diag::new(dcx, level, fluent::hir_analysis_missing_generic_params); err.span(self.span); err.code(E0393); - err.arg("parameterCount", self.missing_type_params.len()); + err.span_label(self.def_span, fluent::hir_analysis_label); + + enum Descr { + Generic, + Type, + Const, + } + + let mut descr = None; + for (_, kind) in &self.missing_generic_params { + descr = match (&descr, kind) { + (None, ty::GenericParamDefKind::Type { .. }) => Some(Descr::Type), + (None, ty::GenericParamDefKind::Const { .. }) => Some(Descr::Const), + (Some(Descr::Type), ty::GenericParamDefKind::Const { .. }) + | (Some(Descr::Const), ty::GenericParamDefKind::Type { .. }) => { + Some(Descr::Generic) + } + _ => continue, + } + } + + err.arg( + "descr", + match descr.unwrap() { + Descr::Generic => "generic", + Descr::Type => "type", + Descr::Const => "const", + }, + ); + err.arg("parameterCount", self.missing_generic_params.len()); err.arg( "parameters", - self.missing_type_params - .iter() - .map(|n| format!("`{n}`")) - .collect::>() - .join(", "), + listify(&self.missing_generic_params, |(n, _)| format!("`{n}`")).unwrap(), ); - err.span_label(self.def_span, fluent::hir_analysis_label); - let mut suggested = false; - // Don't suggest setting the type params if there are some already: the order is + // Don't suggest setting the generic params if there are some already: The order is // tricky to get right and the user will already know what the syntax is. if let Some(snippet) = self.span_snippet && self.empty_generic_args @@ -438,16 +461,16 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams { // we would have to preserve the right order. For now, as clearly the user is // aware of the syntax, we do nothing. } else { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator`. + // The user wrote `Trait`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Trait`. err.span_suggestion_verbose( self.span.shrink_to_hi(), fluent::hir_analysis_suggestion, format!( "<{}>", - self.missing_type_params + self.missing_generic_params .iter() - .map(|n| n.to_string()) + .map(|(n, _)| format!("/* {n} */")) .collect::>() .join(", ") ), 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 f4002396b9d85..3b4812bc94697 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 @@ -352,9 +352,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let span = *spans.first().unwrap(); - // Verify that `dummy_self` did not leak inside default type parameters. This + // Verify that `dummy_self` did not leak inside generic parameter defaults. This // could not be done at path creation, since we need to see through trait aliases. - let mut missing_type_params = vec![]; + let mut missing_generic_params = Vec::new(); let generics = tcx.generics_of(trait_ref.def_id); let args: Vec<_> = trait_ref .args @@ -365,8 +365,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|(index, arg)| { if arg.walk().any(|arg| arg == dummy_self.into()) { let param = &generics.own_params[index]; - missing_type_params.push(param.name); - Ty::new_misc_error(tcx).into() + missing_generic_params.push((param.name, param.kind.clone())); + param.to_error(tcx) } else { arg } @@ -377,8 +377,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); - self.report_missing_type_params( - missing_type_params, + self.report_missing_generic_params( + missing_generic_params, trait_ref.def_id, span, empty_generic_args, 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 3a77bad3ff520..d114691b25e82 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -28,31 +28,29 @@ use tracing::debug; use super::InherentAssocCandidate; use crate::errors::{ - self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams, - ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, + self, AssocItemConstraintsNotAllowedHere, ManualImplementation, ParenthesizedFnTraitExpansion, + TraitObjectDeclaredWithNoTraits, }; use crate::fluent_generated as fluent; use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - /// On missing type parameters, emit an E0393 error and provide a structured suggestion using - /// the type parameter's name as a placeholder. - pub(crate) fn report_missing_type_params( + pub(crate) fn report_missing_generic_params( &self, - missing_type_params: Vec, + missing_generic_params: Vec<(Symbol, ty::GenericParamDefKind)>, def_id: DefId, span: Span, empty_generic_args: bool, ) { - if missing_type_params.is_empty() { + if missing_generic_params.is_empty() { return; } - self.dcx().emit_err(MissingTypeParams { + self.dcx().emit_err(errors::MissingGenericParams { span, def_span: self.tcx().def_span(def_id), span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(), - missing_type_params, + missing_generic_params, empty_generic_args, }); } diff --git a/tests/crashes/136063.rs b/tests/crashes/136063.rs deleted file mode 100644 index 078cc59dfa28a..0000000000000 --- a/tests/crashes/136063.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #136063 -#![feature(generic_const_exprs)] -trait A {} -impl A<1> for bool {} -fn bar(arg : &dyn A) { bar(true) } -pub fn main() {} diff --git a/tests/crashes/137260.rs b/tests/crashes/137260.rs deleted file mode 100644 index f1fa8a660dcd8..0000000000000 --- a/tests/crashes/137260.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: #137260 -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] - -trait Iter {} - -fn needs_iter>() {} - -fn test() { - needs_iter::<1, dyn Iter<()>>(); -} diff --git a/tests/crashes/137514.rs b/tests/crashes/137514.rs deleted file mode 100644 index 7ae5f29e36e63..0000000000000 --- a/tests/crashes/137514.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ known-bug: #137514 -//@ needs-rustc-debug-assertions -#![feature(generic_const_exprs)] - -trait Bar {} - -trait BB = Bar<{ 1i32 + 1 }>; - -fn foo(x: &dyn BB) {} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.rs new file mode 100644 index 0000000000000..30e7a78c2a857 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.rs @@ -0,0 +1,21 @@ +// Test that we force users to explicitly specify const arguments for const parameters that +// have defaults if the default mentions the `Self` type parameter. + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait X::N }> {} + +trait Y { + #[type_const] + const N: usize; +} + +impl Y for T { + #[type_const] + const N: usize = 1; +} + +fn main() { + let _: dyn X; //~ ERROR the const parameter `N` must be explicitly specified +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.stderr new file mode 100644 index 0000000000000..d92fd620f0ea7 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-param-default-mentions-self.stderr @@ -0,0 +1,18 @@ +error[E0393]: the const parameter `N` must be explicitly specified + --> $DIR/dyn-compat-const-param-default-mentions-self.rs:20:16 + | +LL | trait X::N }> {} + | -------------------------------------------- const parameter `N` must be specified for this +... +LL | let _: dyn X; + | ^ + | + = note: because the parameter default references `Self`, the parameter must be specified on the trait object type +help: explicitly specify the const parameter + | +LL | let _: dyn X; + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0393`. diff --git a/tests/ui/dyn-compatibility/default-param-self-projection.stderr b/tests/ui/dyn-compatibility/default-param-self-projection.stderr index ea252a99b0480..86f85f4da8e69 100644 --- a/tests/ui/dyn-compatibility/default-param-self-projection.stderr +++ b/tests/ui/dyn-compatibility/default-param-self-projection.stderr @@ -7,11 +7,11 @@ LL | trait A::E> {} LL | let B: &dyn A = &(); | ^ | - = note: because the parameter default references `Self`, the parameter must be specified on the object type -help: set the type parameter to the desired type + = note: because the parameter default references `Self`, the parameter must be specified on the trait object type +help: explicitly specify the type parameter | -LL | let B: &dyn A = &(); - | +++ +LL | let B: &dyn A = &(); + | +++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0393.stderr b/tests/ui/error-codes/E0393.stderr index 4847aa2508dad..a6991aa355fa3 100644 --- a/tests/ui/error-codes/E0393.stderr +++ b/tests/ui/error-codes/E0393.stderr @@ -7,11 +7,11 @@ LL | LL | fn together_we_will_rule_the_galaxy(son: &dyn A) {} | ^ | - = note: because the parameter default references `Self`, the parameter must be specified on the object type -help: set the type parameter to the desired type + = note: because the parameter default references `Self`, the parameter must be specified on the trait object type +help: explicitly specify the type parameter | -LL | fn together_we_will_rule_the_galaxy(son: &dyn A) {} - | +++ +LL | fn together_we_will_rule_the_galaxy(son: &dyn A) {} + | +++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-22370.stderr b/tests/ui/issues/issue-22370.stderr index b02d867eb7dd9..cd1580e844cad 100644 --- a/tests/ui/issues/issue-22370.stderr +++ b/tests/ui/issues/issue-22370.stderr @@ -7,11 +7,11 @@ LL | LL | fn f(a: &dyn A) {} | ^ | - = note: because the parameter default references `Self`, the parameter must be specified on the object type -help: set the type parameter to the desired type + = note: because the parameter default references `Self`, the parameter must be specified on the trait object type +help: explicitly specify the type parameter | -LL | fn f(a: &dyn A) {} - | +++ +LL | fn f(a: &dyn A) {} + | +++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/unspecified-self-in-trait-ref.stderr b/tests/ui/traits/unspecified-self-in-trait-ref.stderr index 86b77193155d7..b7d78a3284e3c 100644 --- a/tests/ui/traits/unspecified-self-in-trait-ref.stderr +++ b/tests/ui/traits/unspecified-self-in-trait-ref.stderr @@ -97,7 +97,7 @@ LL | pub trait Bar { LL | let e = Bar::::lol(); | ^^^^^^^^^^^^ missing reference to `A` | - = note: because the parameter default references `Self`, the parameter must be specified on the object type + = note: because the parameter default references `Self`, the parameter must be specified on the trait object type error: aborting due to 5 previous errors; 5 warnings emitted diff --git a/tests/ui/type/type-parameter-defaults-referencing-Self.stderr b/tests/ui/type/type-parameter-defaults-referencing-Self.stderr index 23f10c9262c7b..300ad1998e42b 100644 --- a/tests/ui/type/type-parameter-defaults-referencing-Self.stderr +++ b/tests/ui/type/type-parameter-defaults-referencing-Self.stderr @@ -7,11 +7,11 @@ LL | trait Foo { LL | fn foo(x: &dyn Foo) { } | ^^^ | - = note: because the parameter default references `Self`, the parameter must be specified on the object type -help: set the type parameter to the desired type + = note: because the parameter default references `Self`, the parameter must be specified on the trait object type +help: explicitly specify the type parameter | -LL | fn foo(x: &dyn Foo) { } - | +++ +LL | fn foo(x: &dyn Foo) { } + | +++++++++ error: aborting due to 1 previous error From 498cf33951de977085ade4ccaa96cc112a38d330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 7 Jan 2026 18:02:47 +0100 Subject: [PATCH 08/10] mGCA: Force users to specify type assoc consts from supertraits that would otherwise ref `Self` The added test used to ICE prior to this change. --- .../src/hir_ty_lowering/dyn_trait.rs | 7 ++----- ...rojection-from-supertrait-mentions-self.rs | 20 +++++++++++++++++++ ...ction-from-supertrait-mentions-self.stderr | 12 +++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.stderr 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 3b4812bc94697..40a23a1f51f1d 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 @@ -236,11 +236,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let pred = bound_predicate.rebind(pred); // A `Self` within the original bound will be instantiated with a // `trait_object_dummy_self`, so check for that. - let references_self = match pred.skip_binder().term.kind() { - ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()), - // FIXME(mgca): We should walk the const instead of not doing anything - ty::TermKind::Const(_) => false, - }; + let references_self = + pred.skip_binder().term.walk().any(|arg| arg == dummy_self.into()); // If the projection output contains `Self`, force the user to // elaborate it explicitly to avoid a lot of complexity. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.rs new file mode 100644 index 0000000000000..aa93edd025313 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.rs @@ -0,0 +1,20 @@ +// Test that we force users to explicitly specify associated constants (via bindings) +// which reference the `Self` type parameter. + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait X: Y { + #[type_const] + const Q: usize; +} + +trait Y { + #[type_const] + const K: usize; +} + +fn main() { + let _: dyn X; + //~^ ERROR the value of the associated constant `K` in `Y` must be specified +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.stderr new file mode 100644 index 0000000000000..373c4f0e66116 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-from-supertrait-mentions-self.stderr @@ -0,0 +1,12 @@ +error[E0191]: the value of the associated constant `K` in `Y` must be specified + --> $DIR/dyn-compat-const-projection-from-supertrait-mentions-self.rs:18:16 + | +LL | const K: usize; + | -------------- `K` defined here +... +LL | let _: dyn X; + | ^^^^^^^^^ help: specify the associated constant: `X` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0191`. From 3a14d0425c3a50d1950ddee3c4b5473c747634b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 11 Jan 2026 18:16:45 +0100 Subject: [PATCH 09/10] Reject const projections behind trait aliases that mention `Self` This fully rewords the diagnostic that was previously only emitted for assoc ty bindings. That's because it incorrectly called trait aliases *type aliases* and didn't really make it clear what the root cause is. The added test used to ICE prior to this change. I've double-checked that the preexisting test I've modified still ICEs in nightly-2025-03-29. --- compiler/rustc_hir_analysis/messages.ftl | 8 ++-- compiler/rustc_hir_analysis/src/errors.rs | 7 +++- .../src/hir_ty_lowering/dyn_trait.rs | 38 +++++++++---------- ...ection-behind-trait-alias-mentions-self.rs | 22 +++++++++++ ...on-behind-trait-alias-mentions-self.stderr | 11 ++++++ .../trait-alias-self-projection.rs | 12 ------ .../trait-alias-self-projection.stderr | 9 ----- ...ection-behind-trait-alias-mentions-self.rs | 18 +++++++++ ...on-behind-trait-alias-mentions-self.stderr | 20 ++++++++++ 9 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.stderr delete mode 100644 tests/ui/dyn-compatibility/trait-alias-self-projection.rs delete mode 100644 tests/ui/dyn-compatibility/trait-alias-self-projection.stderr create mode 100644 tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.rs create mode 100644 tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 69e0c605b23df..9d3b159a5151f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -165,6 +165,11 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice .label = parameter captured again here +hir_analysis_dyn_trait_assoc_item_binding_mentions_self = + {$kind} binding in trait object type mentions `Self` + .label = contains a mention of `Self` + .binding_label = this binding mentions `Self` + hir_analysis_eii_with_generics = #[{$eii_name}] cannot have generic parameters other than lifetimes .label = required by this attribute @@ -477,9 +482,6 @@ hir_analysis_self_in_impl_self = `Self` is not valid in the self type of an impl block .note = replace `Self` with a different type -hir_analysis_self_in_type_alias = `Self` is not allowed in type aliases - .label = `Self` is only available in impls, traits, and concrete type definitions - hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>` .label = `Self` type parameter is implicitly captured by this `impl Trait` .note = currently, all type parameters are required to be mentioned in the precise captures list diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index b6ba87e4594be..5a0f7bc33ba85 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1632,11 +1632,14 @@ pub(crate) enum SupertraitItemShadowee { } #[derive(Diagnostic)] -#[diag(hir_analysis_self_in_type_alias, code = E0411)] -pub(crate) struct SelfInTypeAlias { +#[diag(hir_analysis_dyn_trait_assoc_item_binding_mentions_self)] +pub(crate) struct DynTraitAssocItemBindingMentionsSelf { #[primary_span] #[label] pub span: Span, + pub kind: &'static str, + #[label(hir_analysis_binding_label)] + pub binding: Span, } #[derive(Diagnostic)] 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 40a23a1f51f1d..c1cbc44953ad4 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 @@ -22,7 +22,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use super::HirTyLowerer; -use crate::errors::SelfInTypeAlias; +use crate::errors::DynTraitAssocItemBindingMentionsSelf; use crate::hir_ty_lowering::{ GenericArgCountMismatch, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, @@ -140,29 +140,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Map the projection bounds onto a key that makes it easy to remove redundant // bounds that are constrained by supertraits of the principal trait. // - // Also make sure we detect conflicting bounds from expanding a trait alias and - // also specifying it manually, like: - // ``` - // type Alias = Trait; - // let _: &dyn Alias = /* ... */; - // ``` + // Also make sure we detect conflicting bounds from expanding trait aliases. + // + // FIXME(#150936): Since the elaborated projection bounds also include the user-written ones + // and we're separately rejecting duplicate+conflicting bindings for trait + // object types when lowering assoc item bindings, there are basic cases + // where we're emitting two distinct but very similar diagnostics. let mut projection_bounds = FxIndexMap::default(); for (proj, proj_span) in elaborated_projection_bounds { - let proj = proj.map_bound(|mut b| { - if let Some(term_ty) = &b.term.as_type() { - let references_self = term_ty.walk().any(|arg| arg == dummy_self.into()); - if references_self { - // With trait alias and type alias combined, type resolver - // may not be able to catch all illegal `Self` usages (issue 139082) - let guar = self.dcx().emit_err(SelfInTypeAlias { span }); - b.term = replace_dummy_self_with_error(tcx, b.term, guar); - } + let item_def_id = proj.item_def_id(); + + let proj = proj.map_bound(|mut proj| { + let references_self = proj.term.walk().any(|arg| arg == dummy_self.into()); + if references_self { + let guar = self.dcx().emit_err(DynTraitAssocItemBindingMentionsSelf { + span, + kind: tcx.def_descr(item_def_id), + binding: proj_span, + }); + proj.term = replace_dummy_self_with_error(tcx, proj.term, guar); } - b + proj }); - let item_def_id = proj.item_def_id(); - let key = ( item_def_id, tcx.anonymize_bound_vars( diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs new file mode 100644 index 0000000000000..93036fbc01a8b --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs @@ -0,0 +1,22 @@ +// Check that we reject const projections behind trait aliases that mention `Self`. +// The code below is pretty artifical and contains a type mismatch anyway but we still need to +// reject it & lower the `Self` ty param to a `{type error}` to avoid ICEs down the line. +// +// The author of the trait object type can't fix this unlike the supertrait bound +// equivalent where they just need to explicitly specify the assoc const. + +#![feature(min_generic_const_args, trait_alias)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const Y: i32; +} + +struct Hold(T); + +trait Bound = Trait }>; + +fn main() { + let _: dyn Bound; //~ ERROR associated constant binding in trait object type mentions `Self` +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.stderr new file mode 100644 index 0000000000000..109cb7602dcd7 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.stderr @@ -0,0 +1,11 @@ +error: associated constant binding in trait object type mentions `Self` + --> $DIR/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs:21:12 + | +LL | trait Bound = Trait }>; + | -------------------- this binding mentions `Self` +... +LL | let _: dyn Bound; + | ^^^^^^^^^ contains a mention of `Self` + +error: aborting due to 1 previous error + diff --git a/tests/ui/dyn-compatibility/trait-alias-self-projection.rs b/tests/ui/dyn-compatibility/trait-alias-self-projection.rs deleted file mode 100644 index 0badb738809e9..0000000000000 --- a/tests/ui/dyn-compatibility/trait-alias-self-projection.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(trait_alias)] -trait B = Fn() -> Self; -type D = &'static dyn B; -//~^ ERROR E0411 - -fn a() -> D { - unreachable!(); -} - -fn main() { - _ = a(); -} diff --git a/tests/ui/dyn-compatibility/trait-alias-self-projection.stderr b/tests/ui/dyn-compatibility/trait-alias-self-projection.stderr deleted file mode 100644 index dccee02e9cd18..0000000000000 --- a/tests/ui/dyn-compatibility/trait-alias-self-projection.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0411]: `Self` is not allowed in type aliases - --> $DIR/trait-alias-self-projection.rs:3:19 - | -LL | type D = &'static dyn B; - | ^^^^^ `Self` is only available in impls, traits, and concrete type definitions - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0411`. diff --git a/tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.rs b/tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.rs new file mode 100644 index 0000000000000..9122ddaaff7b3 --- /dev/null +++ b/tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.rs @@ -0,0 +1,18 @@ +// Check that we reject type projections behind trait aliases that mention `Self`. +// +// The author of the trait object type can't fix this unlike the supertrait bound +// equivalent where they just need to explicitly specify the assoc type. + +// issue: + +#![feature(trait_alias)] + +trait F = Fn() -> Self; + +trait G = H; +trait H { type T: ?Sized; } + +fn main() { + let _: dyn F; //~ ERROR associated type binding in trait object type mentions `Self` + let _: dyn G; //~ ERROR associated type binding in trait object type mentions `Self` +} diff --git a/tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.stderr b/tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.stderr new file mode 100644 index 0000000000000..0b7fb55a908c2 --- /dev/null +++ b/tests/ui/dyn-compatibility/type-projection-behind-trait-alias-mentions-self.stderr @@ -0,0 +1,20 @@ +error: associated type binding in trait object type mentions `Self` + --> $DIR/type-projection-behind-trait-alias-mentions-self.rs:16:12 + | +LL | trait F = Fn() -> Self; + | ---- this binding mentions `Self` +... +LL | let _: dyn F; + | ^^^^^ contains a mention of `Self` + +error: associated type binding in trait object type mentions `Self` + --> $DIR/type-projection-behind-trait-alias-mentions-self.rs:17:12 + | +LL | trait G = H; + | -------- this binding mentions `Self` +... +LL | let _: dyn G; + | ^^^^^ contains a mention of `Self` + +error: aborting due to 2 previous errors + From 87fe6b1fe8cd0ebe7025b40e94d1648a06d0db7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 19 Jan 2026 20:13:48 +0100 Subject: [PATCH 10/10] mGCA: Render traits dyn incompatible if the ty of an assoc const refs `Self` (barring `Self` projections) --- .../src/hir_ty_lowering/dyn_trait.rs | 5 + compiler/rustc_middle/src/traits/mod.rs | 155 +++++++++--------- .../src/traits/dyn_compatibility.rs | 73 +++++---- ...dyn-compat-assoc-const-ty-mentions-self.rs | 38 +++++ ...compat-assoc-const-ty-mentions-self.stderr | 24 +++ ...und-on-assoc-const-allowed-and-enforced.rs | 24 +++ ...on-assoc-const-allowed-and-enforced.stderr | 20 +++ ...elf-const-projections-in-assoc-const-ty.rs | 30 ++++ ...const-projections-in-assoc-const-ty.stderr | 27 +++ ...-const-projections-in-supertrait-bounds.rs | 20 +++ ...st-projections-in-supertrait-bounds.stderr | 18 ++ 11 files changed, 324 insertions(+), 110 deletions(-) create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.stderr create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.stderr create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.rs create mode 100644 tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.stderr 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 c1cbc44953ad4..29f29761b6055 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 @@ -58,6 +58,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut user_written_bounds = Vec::new(); let mut potential_assoc_items = Vec::new(); for poly_trait_ref in hir_bounds.iter() { + // FIXME(mgca): We actually leak `trait_object_dummy_self` if the type of any assoc + // const mentions `Self` (in "Self projections" which we intentionally + // allow). That's because we query feed the instantiated type to `type_of`. + // That's really bad, dummy self should never escape lowering! It leads us + // to accept less code we'd like to support and can lead to ICEs later. let result = self.lower_poly_trait_ref( poly_trait_ref, dummy_self, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 537c339ad125e..aa19a362d4a2a 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -775,26 +775,23 @@ pub enum DynCompatibilityViolation { SupertraitConst(SmallVec<[Span; 1]>), /// Method has something illegal. - Method(Symbol, MethodViolationCode, Span), + Method(Symbol, MethodViolation, Span), - /// Associated constant. - AssocConst(Symbol, Span), + /// Associated constant is faulty. + AssocConst(Symbol, AssocConstViolation, Span), - /// Generic associated constant. - GenericAssocConst(Symbol, Span), - - /// Associated constant that wasn't marked `#[type_const]`. - NonTypeAssocConst(Symbol, Span), - - /// Generic associated type. + /// Generic associated type (GAT). GenericAssocTy(Symbol, Span), } impl DynCompatibilityViolation { pub fn error_msg(&self) -> Cow<'static, str> { + // FIXME(mgca): For method violations we just say "method ..." but for assoc const ones we + // say "it contains ... associated constant ...". Make it consistent. + match self { - DynCompatibilityViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), - DynCompatibilityViolation::SupertraitSelf(spans) => { + Self::SizedSelf(_) => "it requires `Self: Sized`".into(), + Self::SupertraitSelf(spans) => { if spans.iter().any(|sp| *sp != DUMMY_SP) { "it uses `Self` as a type parameter".into() } else { @@ -802,67 +799,55 @@ impl DynCompatibilityViolation { .into() } } - DynCompatibilityViolation::SupertraitNonLifetimeBinder(_) => { + Self::SupertraitNonLifetimeBinder(_) => { "where clause cannot reference non-lifetime `for<...>` variables".into() } - DynCompatibilityViolation::SupertraitConst(_) => { - "it cannot have a `const` supertrait".into() - } - DynCompatibilityViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { + Self::SupertraitConst(_) => "it cannot have a `const` supertrait".into(), + Self::Method(name, MethodViolation::StaticMethod(_), _) => { format!("associated function `{name}` has no `self` parameter").into() } - DynCompatibilityViolation::Method( - name, - MethodViolationCode::ReferencesSelfInput(_), - DUMMY_SP, - ) => format!("method `{name}` references the `Self` type in its parameters").into(), - DynCompatibilityViolation::Method( - name, - MethodViolationCode::ReferencesSelfInput(_), - _, - ) => format!("method `{name}` references the `Self` type in this parameter").into(), - DynCompatibilityViolation::Method( - name, - MethodViolationCode::ReferencesSelfOutput, - _, - ) => format!("method `{name}` references the `Self` type in its return type").into(), - DynCompatibilityViolation::Method( - name, - MethodViolationCode::ReferencesImplTraitInTrait(_), - _, - ) => { + Self::Method(name, MethodViolation::ReferencesSelfInput(_), DUMMY_SP) => { + format!("method `{name}` references the `Self` type in its parameters").into() + } + Self::Method(name, MethodViolation::ReferencesSelfInput(_), _) => { + format!("method `{name}` references the `Self` type in this parameter").into() + } + Self::Method(name, MethodViolation::ReferencesSelfOutput, _) => { + format!("method `{name}` references the `Self` type in its return type").into() + } + Self::Method(name, MethodViolation::ReferencesImplTraitInTrait(_), _) => { format!("method `{name}` references an `impl Trait` type in its return type").into() } - DynCompatibilityViolation::Method(name, MethodViolationCode::AsyncFn, _) => { + Self::Method(name, MethodViolation::AsyncFn, _) => { format!("method `{name}` is `async`").into() } - DynCompatibilityViolation::Method(name, MethodViolationCode::CVariadic, _) => { + Self::Method(name, MethodViolation::CVariadic, _) => { format!("method `{name}` is C-variadic").into() } - DynCompatibilityViolation::Method( - name, - MethodViolationCode::WhereClauseReferencesSelf, - _, - ) => format!("method `{name}` references the `Self` type in its `where` clause").into(), - DynCompatibilityViolation::Method(name, MethodViolationCode::Generic, _) => { + Self::Method(name, MethodViolation::WhereClauseReferencesSelf, _) => { + format!("method `{name}` references the `Self` type in its `where` clause").into() + } + Self::Method(name, MethodViolation::Generic, _) => { format!("method `{name}` has generic type parameters").into() } - DynCompatibilityViolation::Method( - name, - MethodViolationCode::UndispatchableReceiver(_), - _, - ) => format!("method `{name}`'s `self` parameter cannot be dispatched on").into(), - DynCompatibilityViolation::AssocConst(name, _) => { + Self::Method(name, MethodViolation::UndispatchableReceiver(_), _) => { + format!("method `{name}`'s `self` parameter cannot be dispatched on").into() + } + Self::AssocConst(name, AssocConstViolation::FeatureNotEnabled, _) => { format!("it contains associated const `{name}`").into() } - DynCompatibilityViolation::GenericAssocConst(name, _) => { + Self::AssocConst(name, AssocConstViolation::Generic, _) => { format!("it contains generic associated const `{name}`").into() } - DynCompatibilityViolation::NonTypeAssocConst(name, _) => { + Self::AssocConst(name, AssocConstViolation::NonType, _) => { format!("it contains associated const `{name}` that's not marked `#[type_const]`") .into() } - DynCompatibilityViolation::GenericAssocTy(name, _) => { + Self::AssocConst(name, AssocConstViolation::TypeReferencesSelf, _) => format!( + "it contains associated const `{name}` whose type references the `Self` type" + ) + .into(), + Self::GenericAssocTy(name, _) => { format!("it contains generic associated type `{name}`").into() } } @@ -870,31 +855,23 @@ impl DynCompatibilityViolation { pub fn solution(&self) -> DynCompatibilityViolationSolution { match self { - DynCompatibilityViolation::SizedSelf(_) - | DynCompatibilityViolation::SupertraitSelf(_) - | DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) - | DynCompatibilityViolation::SupertraitConst(_) => { - DynCompatibilityViolationSolution::None - } - DynCompatibilityViolation::Method( + Self::SizedSelf(_) + | Self::SupertraitSelf(_) + | Self::SupertraitNonLifetimeBinder(..) + | Self::SupertraitConst(_) => DynCompatibilityViolationSolution::None, + Self::Method( name, - MethodViolationCode::StaticMethod(Some((add_self_sugg, make_sized_sugg))), + MethodViolation::StaticMethod(Some((add_self_sugg, make_sized_sugg))), _, ) => DynCompatibilityViolationSolution::AddSelfOrMakeSized { name: *name, add_self_sugg: add_self_sugg.clone(), make_sized_sugg: make_sized_sugg.clone(), }, - DynCompatibilityViolation::Method( - name, - MethodViolationCode::UndispatchableReceiver(Some(span)), - _, - ) => DynCompatibilityViolationSolution::ChangeToRefSelf(*name, *span), - DynCompatibilityViolation::AssocConst(name, _) - | DynCompatibilityViolation::GenericAssocConst(name, _) - | DynCompatibilityViolation::NonTypeAssocConst(name, _) - | DynCompatibilityViolation::GenericAssocTy(name, _) - | DynCompatibilityViolation::Method(name, ..) => { + Self::Method(name, MethodViolation::UndispatchableReceiver(Some(span)), _) => { + DynCompatibilityViolationSolution::ChangeToRefSelf(*name, *span) + } + Self::Method(name, ..) | Self::AssocConst(name, ..) | Self::GenericAssocTy(name, _) => { DynCompatibilityViolationSolution::MoveToAnotherTrait(*name) } } @@ -904,15 +881,13 @@ impl DynCompatibilityViolation { // When `span` comes from a separate crate, it'll be `DUMMY_SP`. Treat it as `None` so // diagnostics use a `note` instead of a `span_label`. match self { - DynCompatibilityViolation::SupertraitSelf(spans) - | DynCompatibilityViolation::SizedSelf(spans) - | DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) - | DynCompatibilityViolation::SupertraitConst(spans) => spans.clone(), - DynCompatibilityViolation::AssocConst(_, span) - | DynCompatibilityViolation::GenericAssocConst(_, span) - | DynCompatibilityViolation::NonTypeAssocConst(_, span) - | DynCompatibilityViolation::GenericAssocTy(_, span) - | DynCompatibilityViolation::Method(_, _, span) => { + Self::SupertraitSelf(spans) + | Self::SizedSelf(spans) + | Self::SupertraitNonLifetimeBinder(spans) + | Self::SupertraitConst(spans) => spans.clone(), + Self::Method(_, _, span) + | Self::AssocConst(_, _, span) + | Self::GenericAssocTy(_, span) => { if *span != DUMMY_SP { smallvec![*span] } else { @@ -979,7 +954,7 @@ impl DynCompatibilityViolationSolution { /// Reasons a method might not be dyn-compatible. #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] -pub enum MethodViolationCode { +pub enum MethodViolation { /// e.g., `fn foo()` StaticMethod(Option<(/* add &self */ (String, Span), /* add Self: Sized */ (String, Span))>), @@ -1008,6 +983,22 @@ pub enum MethodViolationCode { UndispatchableReceiver(Option), } +/// Reasons an associated const might not be dyn compatible. +#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum AssocConstViolation { + /// Unstable feature `min_generic_const_args` wasn't enabled. + FeatureNotEnabled, + + /// Has own generic parameters (GAC). + Generic, + + /// Isn't marked `#[type_const]`. + NonType, + + /// Its type mentions the `Self` type parameter. + TypeReferencesSelf, +} + /// These are the error cases for `codegen_select_candidate`. #[derive(Copy, Clone, Debug, Hash, HashStable, Encodable, Decodable)] pub enum CodegenObligationError { diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 83ef81081c182..3943eacbfb341 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -25,7 +25,8 @@ use crate::infer::TyCtxtInferExt; pub use crate::traits::DynCompatibilityViolation; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{ - MethodViolationCode, Obligation, ObligationCause, normalize_param_env_or_error, util, + AssocConstViolation, MethodViolation, Obligation, ObligationCause, + normalize_param_env_or_error, util, }; /// Returns the dyn-compatibility violations that affect HIR ty lowering. @@ -227,7 +228,7 @@ fn predicate_references_self<'tcx>( // types for trait objects. // // Note that we *do* allow projection *outputs* to contain - // `self` (i.e., `trait Foo: Bar { type Result; }`), + // `Self` (i.e., `trait Foo: Bar { type Result; }`), // we just require the user to specify *both* outputs // in the object type (i.e., `dyn Foo`). // @@ -319,20 +320,36 @@ pub fn dyn_compatibility_violations_for_assoc_item( match item.kind { ty::AssocKind::Const { name } => { + // We will permit type associated consts if they are explicitly mentioned in the + // trait object type. We can't check this here, as here we only check if it is + // guaranteed to not be possible. + + let mut errors = Vec::new(); + if tcx.features().min_generic_const_args() { if !tcx.generics_of(item.def_id).is_own_empty() { - vec![DynCompatibilityViolation::GenericAssocConst(name, span())] + errors.push(AssocConstViolation::Generic); } else if !find_attr!(tcx.get_all_attrs(item.def_id), AttributeKind::TypeConst(_)) { - vec![DynCompatibilityViolation::NonTypeAssocConst(name, span())] - } else { - // We will permit type associated consts if they are explicitly mentioned in the - // trait object type. We can't check this here, as here we only check if it is - // guaranteed to not be possible. - Vec::new() + errors.push(AssocConstViolation::NonType); + } + + let ty = ty::Binder::dummy(tcx.type_of(item.def_id).instantiate_identity()); + if contains_illegal_self_type_reference( + tcx, + trait_def_id, + ty, + AllowSelfProjections::Yes, + ) { + errors.push(AssocConstViolation::TypeReferencesSelf); } } else { - vec![DynCompatibilityViolation::AssocConst(name, span())] + errors.push(AssocConstViolation::FeatureNotEnabled); } + + errors + .into_iter() + .map(|error| DynCompatibilityViolation::AssocConst(name, error, span())) + .collect() } ty::AssocKind::Fn { name, .. } => { virtual_call_violations_for_method(tcx, trait_def_id, item) @@ -341,10 +358,10 @@ pub fn dyn_compatibility_violations_for_assoc_item( let node = tcx.hir_get_if_local(item.def_id); // Get an accurate span depending on the violation. let span = match (&v, node) { - (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, - (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, - (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, - (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { + (MethodViolation::ReferencesSelfInput(Some(span)), _) => *span, + (MethodViolation::UndispatchableReceiver(Some(span)), _) => *span, + (MethodViolation::ReferencesImplTraitInTrait(span), _) => *span, + (MethodViolation::ReferencesSelfOutput, Some(node)) => { node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) } _ => span(), @@ -377,7 +394,7 @@ fn virtual_call_violations_for_method<'tcx>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem, -) -> Vec { +) -> Vec { let sig = tcx.fn_sig(method.def_id).instantiate_identity(); // The method's first parameter must be named `self` @@ -405,7 +422,7 @@ fn virtual_call_violations_for_method<'tcx>( // Not having `self` parameter messes up the later checks, // so we need to return instead of pushing - return vec![MethodViolationCode::StaticMethod(sugg)]; + return vec![MethodViolation::StaticMethod(sugg)]; } let mut errors = Vec::new(); @@ -426,7 +443,7 @@ fn virtual_call_violations_for_method<'tcx>( } else { None }; - errors.push(MethodViolationCode::ReferencesSelfInput(span)); + errors.push(MethodViolation::ReferencesSelfInput(span)); } } if contains_illegal_self_type_reference( @@ -435,19 +452,19 @@ fn virtual_call_violations_for_method<'tcx>( sig.output(), AllowSelfProjections::Yes, ) { - errors.push(MethodViolationCode::ReferencesSelfOutput); + errors.push(MethodViolation::ReferencesSelfOutput); } - if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { - errors.push(code); + if let Some(error) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { + errors.push(error); } if sig.skip_binder().c_variadic { - errors.push(MethodViolationCode::CVariadic); + errors.push(MethodViolation::CVariadic); } // We can't monomorphize things like `fn foo(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); if own_counts.types > 0 || own_counts.consts > 0 { - errors.push(MethodViolationCode::Generic); + errors.push(MethodViolation::Generic); } let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); @@ -467,7 +484,7 @@ fn virtual_call_violations_for_method<'tcx>( } else { None }; - errors.push(MethodViolationCode::UndispatchableReceiver(span)); + errors.push(MethodViolation::UndispatchableReceiver(span)); } else { // We confirm that the `receiver_is_dispatchable` is accurate later, // see `check_receiver_correct`. It should be kept in sync with this code. @@ -525,7 +542,7 @@ fn virtual_call_violations_for_method<'tcx>( contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes) }) { - errors.push(MethodViolationCode::WhereClauseReferencesSelf); + errors.push(MethodViolation::WhereClauseReferencesSelf); } errors @@ -867,12 +884,12 @@ fn contains_illegal_impl_trait_in_trait<'tcx>( tcx: TyCtxt<'tcx>, fn_def_id: DefId, ty: ty::Binder<'tcx, Ty<'tcx>>, -) -> Option { +) -> Option { let ty = tcx.liberate_late_bound_regions(fn_def_id, ty); if tcx.asyncness(fn_def_id).is_async() { // Rendering the error as a separate `async-specific` message is better. - Some(MethodViolationCode::AsyncFn) + Some(MethodViolation::AsyncFn) } else { ty.visit_with(&mut IllegalRpititVisitor { tcx, allowed: None }).break_value() } @@ -884,14 +901,14 @@ struct IllegalRpititVisitor<'tcx> { } impl<'tcx> TypeVisitor> for IllegalRpititVisitor<'tcx> { - type Result = ControlFlow; + type Result = ControlFlow; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { if let ty::Alias(ty::Projection, proj) = *ty.kind() && Some(proj) != self.allowed && self.tcx.is_impl_trait_in_trait(proj.def_id) { - ControlFlow::Break(MethodViolationCode::ReferencesImplTraitInTrait( + ControlFlow::Break(MethodViolation::ReferencesImplTraitInTrait( self.tcx.def_span(proj.def_id), )) } else { diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.rs new file mode 100644 index 0000000000000..d19e7acbaff0d --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.rs @@ -0,0 +1,38 @@ +// Ensure that we consider traits dyn *in*compatible if the type of any (type) assoc const +// mentions `Self` (barring "`Self` projections") + +//@ dont-require-annotations: NOTE + +#![feature(generic_const_items)] +#![feature(generic_const_parameter_types)] +#![feature(min_generic_const_args)] +#![feature(unsized_const_params)] +#![expect(incomplete_features)] + +trait Trait { + // NOTE: The `ConstParamTy_` bound is intentionally on the assoc const and not on the trait as + // doing the latter would already render the trait dyn incompatible due to it being + // bounded by `PartialEq` and supertrait bounds cannot mention `Self` like this. + #[type_const] + const K: Self where Self: std::marker::ConstParamTy_; + //~^ NOTE it contains associated const `K` whose type references the `Self` type + + // This is not a "`Self` projection" in our sense (which would be allowed) + // since the trait is not the principal trait or a supertrait thereof. + #[type_const] + const Q: ::Output; + //~^ NOTE it contains associated const `Q` whose type references the `Self` type +} + +trait SomeOtherTrait { + type Output: std::marker::ConstParamTy_; +} + +// You could imagine this impl being more interesting and mention `T` somewhere in `Output`... +impl SomeOtherTrait for T { + type Output = (); +} + +fn main() { + let _: dyn Trait; //~ ERROR the trait `Trait` is not dyn compatible +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.stderr new file mode 100644 index 0000000000000..dba1643d2c5c6 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-assoc-const-ty-mentions-self.stderr @@ -0,0 +1,24 @@ +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/dyn-compat-assoc-const-ty-mentions-self.rs:37:16 + | +LL | let _: dyn Trait; + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dyn-compat-assoc-const-ty-mentions-self.rs:17:11 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +... +LL | const K: Self where Self: std::marker::ConstParamTy_; + | ^ ...because it contains associated const `K` whose type references the `Self` type +... +LL | const Q: ::Output; + | ^ ...because it contains associated const `Q` whose type references the `Self` type + = help: consider moving `K` to another trait + = help: consider moving `Q` to another trait + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.rs new file mode 100644 index 0000000000000..07ce629ab7be0 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.rs @@ -0,0 +1,24 @@ +// Ensure that the where-clause of assoc consts in dyn-compatible traits are allowed to freely +// reference the `Self` type parameter (contrary to methods) and that such where clauses are +// actually enforced. + +#![feature(min_generic_const_args, generic_const_items)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const N: i32 where Self: Bound; +} + +impl Trait for () { + #[type_const] + const N: i32 = 0; +} + +trait Bound {} + +fn main() { + let _: dyn Trait; // OK + + let _: &dyn Trait = &(); //~ ERROR the trait bound `(): Bound` is not satisfied +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.stderr new file mode 100644 index 0000000000000..8eeb60d55c384 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `(): Bound` is not satisfied + --> $DIR/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.rs:23:32 + | +LL | let _: &dyn Trait = &(); + | ^^^ the trait `Bound` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.rs:18:1 + | +LL | trait Bound {} + | ^^^^^^^^^^^ +note: required by a bound in `Trait::N` + --> $DIR/dyn-compat-self-bound-on-assoc-const-allowed-and-enforced.rs:10:30 + | +LL | const N: i32 where Self: Bound; + | ^^^^^ required by this bound in `Trait::N` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.rs new file mode 100644 index 0000000000000..936556e957cad --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.rs @@ -0,0 +1,30 @@ +// FIXME(mgca): Ideally this would compile -- at least if the user annotated the instantiated type +// of the assoc const (but we don't have the syntax for this (yet)). In any case, we +// should not leak `trait_object_dummy_self` (defined as `FreshTy(0)` under the hood) +// to the rest of the compiler and by extension the user via diagnostics. +//@ known-bug: unknown + +#![feature(min_generic_const_args, unsized_const_params, generic_const_parameter_types)] +#![expect(incomplete_features)] + +trait A { + type Ty: std::marker::ConstParamTy_; + #[type_const] const CT: Self::Ty; +} + +impl A for () { + type Ty = i32; + #[type_const] const CT: i32 = 0; +} + +fn main() { + // NOTE: As alluded to above, if we can't get the examples below to compile as written, + // we might want to allow the user to manually specify the instantiated type somehow. + // The hypothetical syntax for that *might* look sth. like + // * `dyn A i32 { 0 }>` + // * `dyn A` + + let _: dyn A; + + let _: &dyn A = &(); +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr new file mode 100644 index 0000000000000..8ee231ec070fd --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `FreshTy(0): A` is not satisfied + --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:27:33 + | +LL | let _: dyn A; + | ^ the trait `A` is not implemented for `FreshTy(0)` + | +help: the trait `A` is implemented for `()` + --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:15:1 + | +LL | impl A for () { + | ^^^^^^^^^^^^^ + +error[E0277]: the trait bound `FreshTy(0): A` is not satisfied + --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:29:34 + | +LL | let _: &dyn A = &(); + | ^ the trait `A` is not implemented for `FreshTy(0)` + | +help: the trait `A` is implemented for `()` + --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:15:1 + | +LL | impl A for () { + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.rs new file mode 100644 index 0000000000000..c8929750f61c5 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.rs @@ -0,0 +1,20 @@ +// Ensure that we reject the `Self` type parameter in supertrait bounds of dyn-compatible traits +// even if they're part of a "`Self` projection" (contrary to method signatures and the type of +// assoc consts). + +//@ dont-require-annotations: NOTE + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait: SuperTrait<{ Self::N }> { +//~^ NOTE it uses `Self` as a type parameter + #[type_const] + const N: usize; +} + +trait SuperTrait {} + +fn main() { + let _: dyn Trait; //~ ERROR the trait `Trait` is not dyn compatible +} diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.stderr new file mode 100644 index 0000000000000..33edc96117d56 --- /dev/null +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-supertrait-bounds.stderr @@ -0,0 +1,18 @@ +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/dyn-compat-self-const-projections-in-supertrait-bounds.rs:19:16 + | +LL | let _: dyn Trait; + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dyn-compat-self-const-projections-in-supertrait-bounds.rs:10:14 + | +LL | trait Trait: SuperTrait<{ Self::N }> { + | ----- ^^^^^^^^^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter + | | + | this trait is not dyn compatible... + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0038`.