From 34062b124f00eb7c35da2e3981cfb5b45dd26039 Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Wed, 17 Sep 2025 05:34:19 +0200 Subject: Merge modules dyn_compatibility and lint into dyn_trait --- .../src/hir_ty_lowering/dyn_compatibility.rs | 500 ---------- .../src/hir_ty_lowering/dyn_trait.rs | 1041 ++++++++++++++++++++ .../rustc_hir_analysis/src/hir_ty_lowering/lint.rs | 533 ---------- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 14 +- 4 files changed, 1044 insertions(+), 1044 deletions(-) delete mode 100644 compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs create mode 100644 compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs delete mode 100644 compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs deleted file mode 100644 index c823a4a0430..00000000000 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ /dev/null @@ -1,500 +0,0 @@ -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_errors::codes::*; -use rustc_errors::struct_span_code_err; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; -use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; -use rustc_middle::ty::{ - self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, Upcast, -}; -use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; -use rustc_trait_selection::traits; -use smallvec::{SmallVec, smallvec}; -use tracing::{debug, instrument}; - -use super::HirTyLowerer; -use crate::errors::SelfInTypeAlias; -use crate::hir_ty_lowering::{GenericArgCountMismatch, PredicateFilter, RegionInferReason}; - -impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - /// Lower a trait object type from the HIR to our internal notion of a type. - #[instrument(level = "debug", skip_all, ret)] - pub(super) fn lower_trait_object_ty( - &self, - span: Span, - hir_id: hir::HirId, - hir_bounds: &[hir::PolyTraitRef<'tcx>], - lifetime: &hir::Lifetime, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - let dummy_self = tcx.types.trait_object_dummy_self; - - let mut user_written_bounds = Vec::new(); - let mut potential_assoc_types = Vec::new(); - for poly_trait_ref in hir_bounds.iter() { - let result = self.lower_poly_trait_ref( - poly_trait_ref, - dummy_self, - &mut user_written_bounds, - PredicateFilter::SelfOnly, - ); - if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct { - potential_assoc_types.extend(invalid_args); - } - } - - let ast_bounds: Vec<_> = - hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect(); - - self.add_default_traits(&mut user_written_bounds, dummy_self, &ast_bounds, None, span); - - let (elaborated_trait_bounds, elaborated_projection_bounds) = - traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); - let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds - .into_iter() - .partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); - - // We don't support empty trait objects. - if regular_traits.is_empty() && auto_traits.is_empty() { - let guar = - self.report_trait_object_with_no_traits(span, user_written_bounds.iter().copied()); - return Ty::new_error(tcx, guar); - } - // We don't support >1 principal - if regular_traits.len() > 1 { - let guar = self.report_trait_object_addition_traits(®ular_traits); - return Ty::new_error(tcx, guar); - } - // Don't create a dyn trait if we have errors in the principal. - if let Err(guar) = regular_traits.error_reported() { - return Ty::new_error(tcx, guar); - } - - // Check that there are no gross dyn-compatibility violations; - // most importantly, that the supertraits don't contain `Self`, - // to avoid ICEs. - for (clause, span) in user_written_bounds { - if let Some(trait_pred) = clause.as_trait_clause() { - let violations = self.dyn_compatibility_violations(trait_pred.def_id()); - if !violations.is_empty() { - let reported = report_dyn_incompatibility( - tcx, - span, - Some(hir_id), - trait_pred.def_id(), - &violations, - ) - .emit(); - return Ty::new_error(tcx, reported); - } - } - } - - // 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. - // - // Also make sure we detect conflicting bounds from expanding a trait alias and - // also specifying it manually, like: - // ``` - // type Alias = Trait; - // let _: &dyn Alias = /* ... */; - // ``` - 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); - } - } - b - }); - - let key = ( - proj.skip_binder().projection_term.def_id, - tcx.anonymize_bound_vars( - proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)), - ), - ); - if let Some((old_proj, old_proj_span)) = - 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()); - self.dcx() - .struct_span_err( - span, - format!( - "conflicting associated type bounds for `{item}` when \ - expanding trait alias" - ), - ) - .with_span_label( - old_proj_span, - format!("`{item}` is specified to be `{}` here", old_proj.term()), - ) - .with_span_label( - proj_span, - format!("`{item}` is specified to be `{}` here", proj.term()), - ) - .emit(); - } - } - - 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![]; - - if let Some((principal_trait, ref spans)) = principal_trait { - let principal_trait = principal_trait.map_bound(|trait_pred| { - assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); - trait_pred.trait_ref - }); - - for ClauseWithSupertraitSpan { clause, supertrait_span } in traits::elaborate( - tcx, - [ClauseWithSupertraitSpan::new( - ty::TraitRef::identity(tcx, principal_trait.def_id()).upcast(tcx), - *spans.last().unwrap(), - )], - ) - .filter_only_self() - { - let clause = clause.instantiate_supertrait(tcx, principal_trait); - debug!("observing object predicate `{clause:?}`"); - - let bound_predicate = clause.kind(); - match bound_predicate.skip_binder() { - ty::ClauseKind::Trait(pred) => { - // FIXME(negative_bounds): Handle this correctly... - let trait_ref = - tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref)); - ordered_associated_types.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. - .filter(|item| !item.is_impl_trait_in_trait()) - .map(|item| (item.def_id, trait_ref)), - ); - } - ty::ClauseKind::Projection(pred) => { - 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(associated_const_equality): We should walk the const instead of not doing anything - ty::TermKind::Const(_) => false, - }; - - // If the projection output contains `Self`, force the user to - // elaborate it explicitly to avoid a lot of complexity. - // - // The "classically useful" case is the following: - // ``` - // trait MyTrait: FnMut() -> ::MyOutput { - // type MyOutput; - // } - // ``` - // - // Here, the user could theoretically write `dyn MyTrait`, - // but actually supporting that would "expand" to an infinitely-long type - // `fix $ τ → dyn MyTrait::MyOutput`. - // - // Instead, we force the user to write - // `dyn MyTrait`, which is uglier but works. See - // the discussion in #56288 for alternatives. - if !references_self { - let key = ( - pred.skip_binder().projection_term.def_id, - tcx.anonymize_bound_vars( - pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)), - ), - ); - if !projection_bounds.contains_key(&key) { - projection_bounds.insert(key, (pred, supertrait_span)); - } - } - - self.check_elaborated_projection_mentions_input_lifetimes( - pred, - *spans.first().unwrap(), - supertrait_span, - ); - } - _ => (), - } - } - } - - // `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. - for &(projection_bound, span) in projection_bounds.values() { - let def_id = projection_bound.item_def_id(); - if tcx.generics_require_sized_self(def_id) { - tcx.emit_node_span_lint( - UNUSED_ASSOCIATED_TYPE_BOUNDS, - hir_id, - span, - crate::errors::UnusedAssociatedTypeBounds { span }, - ); - } - } - - // 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 - .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 - } - }) - .collect(); - - 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, - hir_bounds, - ) { - return Ty::new_error(tcx, guar); - } - - // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as - // `dyn Trait + Send`. - // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering - // the bounds - let mut duplicates = FxHashSet::default(); - auto_traits.retain(|(trait_pred, _)| duplicates.insert(trait_pred.def_id())); - - debug!(?principal_trait); - debug!(?auto_traits); - - // Erase the `dummy_self` (`trait_object_dummy_self`) used above. - let principal_trait_ref = principal_trait.map(|(trait_pred, spans)| { - trait_pred.map_bound(|trait_pred| { - let trait_ref = trait_pred.trait_ref; - assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); - assert_eq!(trait_ref.self_ty(), dummy_self); - - let span = *spans.first().unwrap(); - - // Verify that `dummy_self` did not leak inside default type parameters. This - // could not be done at path creation, since we need to see through trait aliases. - let mut missing_type_params = vec![]; - let generics = tcx.generics_of(trait_ref.def_id); - let args: Vec<_> = trait_ref - .args - .iter() - .enumerate() - // Skip `Self` - .skip(1) - .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() - } else { - arg - } - }) - .collect(); - - let empty_generic_args = hir_bounds.iter().any(|hir_bound| { - 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, - trait_ref.def_id, - span, - empty_generic_args, - ); - - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new( - tcx, - trait_ref.def_id, - args, - )) - }) - }); - - let existential_projections = projection_bounds.into_iter().map(|(bound, _)| { - bound.map_bound(|mut b| { - assert_eq!(b.projection_term.self_ty(), dummy_self); - - // Like for trait refs, verify that `dummy_self` did not leak inside default type - // parameters. - let references_self = b.projection_term.args.iter().skip(1).any(|arg| { - if arg.walk().any(|arg| arg == dummy_self.into()) { - return true; - } - false - }); - if references_self { - let guar = tcx - .dcx() - .span_delayed_bug(span, "trait object projection bounds reference `Self`"); - b.projection_term = replace_dummy_self_with_error(tcx, b.projection_term, guar); - } - - ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( - tcx, b, - )) - }) - }); - - let mut auto_trait_predicates: Vec<_> = auto_traits - .into_iter() - .map(|(trait_pred, _)| { - assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive); - assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self); - - ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id())) - }) - .collect(); - auto_trait_predicates.dedup(); - - // N.b. principal, projections, auto traits - // FIXME: This is actually wrong with multiple principals in regards to symbol mangling - let mut v = principal_trait_ref - .into_iter() - .chain(existential_projections) - .chain(auto_trait_predicates) - .collect::>(); - v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); - let existential_predicates = tcx.mk_poly_existential_predicates(&v); - - // Use explicitly-specified region bound, unless the bound is missing. - let region_bound = if !lifetime.is_elided() { - self.lower_lifetime(lifetime, RegionInferReason::ExplicitObjectLifetime) - } else { - self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { - // Curiously, we prefer object lifetime default for `+ '_`... - if tcx.named_bound_var(lifetime.hir_id).is_some() { - self.lower_lifetime(lifetime, RegionInferReason::ExplicitObjectLifetime) - } else { - let reason = - if let hir::LifetimeKind::ImplicitObjectLifetimeDefault = lifetime.kind { - if let hir::Node::Ty(hir::Ty { - kind: hir::TyKind::Ref(parent_lifetime, _), - .. - }) = tcx.parent_hir_node(hir_id) - && tcx.named_bound_var(parent_lifetime.hir_id).is_none() - { - // Parent lifetime must have failed to resolve. Don't emit a redundant error. - RegionInferReason::ExplicitObjectLifetime - } else { - RegionInferReason::ObjectLifetimeDefault - } - } else { - RegionInferReason::ExplicitObjectLifetime - }; - self.re_infer(span, reason) - } - }) - }; - debug!(?region_bound); - - Ty::new_dynamic(tcx, existential_predicates, region_bound) - } - - /// Check that elaborating the principal of a trait ref doesn't lead to projections - /// that are unconstrained. This can happen because an otherwise unconstrained - /// *type variable* can be substituted with a type that has late-bound regions. See - /// `elaborated-predicates-unconstrained-late-bound.rs` for a test. - fn check_elaborated_projection_mentions_input_lifetimes( - &self, - pred: ty::PolyProjectionPredicate<'tcx>, - span: Span, - supertrait_span: Span, - ) { - let tcx = self.tcx(); - - // Find any late-bound regions declared in `ty` that are not - // declared in the trait-ref or assoc_item. These are not well-formed. - // - // Example: - // - // for<'a> ::Item = &'a str // <-- 'a is bad - // for<'a> >::Output = &'a str // <-- 'a is ok - let late_bound_in_projection_term = - tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term)); - let late_bound_in_term = - tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term)); - debug!(?late_bound_in_projection_term); - debug!(?late_bound_in_term); - - // FIXME: point at the type params that don't have appropriate lifetimes: - // struct S1 Fn(&i32, &i32) -> &'a i32>(F); - // ---- ---- ^^^^^^^ - // NOTE(associated_const_equality): This error should be impossible to trigger - // with associated const equality constraints. - self.validate_late_bound_regions( - late_bound_in_projection_term, - late_bound_in_term, - |br_name| { - let item_name = tcx.item_name(pred.item_def_id()); - struct_span_code_err!( - self.dcx(), - span, - E0582, - "binding for associated type `{}` references {}, \ - which does not appear in the trait input types", - item_name, - br_name - ) - .with_span_label(supertrait_span, "due to this supertrait") - }, - ); - } -} - -fn replace_dummy_self_with_error<'tcx, T: TypeFoldable>>( - tcx: TyCtxt<'tcx>, - t: T, - guar: ErrorGuaranteed, -) -> T { - t.fold_with(&mut BottomUpFolder { - tcx, - ty_op: |ty| { - if ty == tcx.types.trait_object_dummy_self { Ty::new_error(tcx, guar) } else { ty } - }, - lt_op: |lt| lt, - ct_op: |ct| ct, - }) -} 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 new file mode 100644 index 00000000000..c248cd7fec2 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -0,0 +1,1041 @@ +use rustc_ast::TraitObjectSyntax; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_errors::codes::*; +use rustc_errors::{ + Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS}; +use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; +use rustc_middle::ty::{ + self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, Upcast, +}; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::{ErrorGuaranteed, Span}; +use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; +use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; +use rustc_trait_selection::traits; +use smallvec::{SmallVec, smallvec}; +use tracing::{debug, instrument}; + +use super::HirTyLowerer; +use crate::errors::SelfInTypeAlias; +use crate::hir_ty_lowering::{GenericArgCountMismatch, PredicateFilter, RegionInferReason}; + +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Lower a trait object type from the HIR to our internal notion of a type. + #[instrument(level = "debug", skip_all, ret)] + pub(super) fn lower_trait_object_ty( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + lifetime: &hir::Lifetime, + syntax: TraitObjectSyntax, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + let dummy_self = tcx.types.trait_object_dummy_self; + + match syntax { + TraitObjectSyntax::Dyn => {} + TraitObjectSyntax::None => { + match self.prohibit_or_lint_bare_trait_object_ty(span, hir_id, hir_bounds) { + // Don't continue with type analysis if the `dyn` keyword is missing. + // It generates confusing errors, especially if the user meant to use + // another keyword like `impl`. + Some(guar) => return Ty::new_error(tcx, guar), + None => {} + } + } + } + + let mut user_written_bounds = Vec::new(); + let mut potential_assoc_types = Vec::new(); + for poly_trait_ref in hir_bounds.iter() { + let result = self.lower_poly_trait_ref( + poly_trait_ref, + dummy_self, + &mut user_written_bounds, + PredicateFilter::SelfOnly, + ); + if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct { + potential_assoc_types.extend(invalid_args); + } + } + + self.add_default_traits( + &mut user_written_bounds, + dummy_self, + &hir_bounds + .iter() + .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) + .collect::>(), + None, + span, + ); + + let (elaborated_trait_bounds, elaborated_projection_bounds) = + traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); + let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds + .into_iter() + .partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); + + // We don't support empty trait objects. + if regular_traits.is_empty() && auto_traits.is_empty() { + let guar = + self.report_trait_object_with_no_traits(span, user_written_bounds.iter().copied()); + return Ty::new_error(tcx, guar); + } + // We don't support >1 principal + if regular_traits.len() > 1 { + let guar = self.report_trait_object_addition_traits(®ular_traits); + return Ty::new_error(tcx, guar); + } + // Don't create a dyn trait if we have errors in the principal. + if let Err(guar) = regular_traits.error_reported() { + return Ty::new_error(tcx, guar); + } + + // Check that there are no gross dyn-compatibility violations; + // most importantly, that the supertraits don't contain `Self`, + // to avoid ICEs. + for (clause, span) in user_written_bounds { + if let Some(trait_pred) = clause.as_trait_clause() { + let violations = self.dyn_compatibility_violations(trait_pred.def_id()); + if !violations.is_empty() { + let reported = report_dyn_incompatibility( + tcx, + span, + Some(hir_id), + trait_pred.def_id(), + &violations, + ) + .emit(); + return Ty::new_error(tcx, reported); + } + } + } + + // 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. + // + // Also make sure we detect conflicting bounds from expanding a trait alias and + // also specifying it manually, like: + // ``` + // type Alias = Trait; + // let _: &dyn Alias = /* ... */; + // ``` + 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); + } + } + b + }); + + let key = ( + proj.skip_binder().projection_term.def_id, + tcx.anonymize_bound_vars( + proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)), + ), + ); + if let Some((old_proj, old_proj_span)) = + 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()); + self.dcx() + .struct_span_err( + span, + format!( + "conflicting associated type bounds for `{item}` when \ + expanding trait alias" + ), + ) + .with_span_label( + old_proj_span, + format!("`{item}` is specified to be `{}` here", old_proj.term()), + ) + .with_span_label( + proj_span, + format!("`{item}` is specified to be `{}` here", proj.term()), + ) + .emit(); + } + } + + 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![]; + + if let Some((principal_trait, ref spans)) = principal_trait { + let principal_trait = principal_trait.map_bound(|trait_pred| { + assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); + trait_pred.trait_ref + }); + + for ClauseWithSupertraitSpan { clause, supertrait_span } in traits::elaborate( + tcx, + [ClauseWithSupertraitSpan::new( + ty::TraitRef::identity(tcx, principal_trait.def_id()).upcast(tcx), + *spans.last().unwrap(), + )], + ) + .filter_only_self() + { + let clause = clause.instantiate_supertrait(tcx, principal_trait); + debug!("observing object predicate `{clause:?}`"); + + let bound_predicate = clause.kind(); + match bound_predicate.skip_binder() { + ty::ClauseKind::Trait(pred) => { + // FIXME(negative_bounds): Handle this correctly... + let trait_ref = + tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref)); + ordered_associated_types.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. + .filter(|item| !item.is_impl_trait_in_trait()) + .map(|item| (item.def_id, trait_ref)), + ); + } + ty::ClauseKind::Projection(pred) => { + 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(associated_const_equality): We should walk the const instead of not doing anything + ty::TermKind::Const(_) => false, + }; + + // If the projection output contains `Self`, force the user to + // elaborate it explicitly to avoid a lot of complexity. + // + // The "classically useful" case is the following: + // ``` + // trait MyTrait: FnMut() -> ::MyOutput { + // type MyOutput; + // } + // ``` + // + // Here, the user could theoretically write `dyn MyTrait`, + // but actually supporting that would "expand" to an infinitely-long type + // `fix $ τ → dyn MyTrait::MyOutput`. + // + // Instead, we force the user to write + // `dyn MyTrait`, which is uglier but works. See + // the discussion in #56288 for alternatives. + if !references_self { + let key = ( + pred.skip_binder().projection_term.def_id, + tcx.anonymize_bound_vars( + pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)), + ), + ); + if !projection_bounds.contains_key(&key) { + projection_bounds.insert(key, (pred, supertrait_span)); + } + } + + self.check_elaborated_projection_mentions_input_lifetimes( + pred, + *spans.first().unwrap(), + supertrait_span, + ); + } + _ => (), + } + } + } + + // `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. + for &(projection_bound, span) in projection_bounds.values() { + let def_id = projection_bound.item_def_id(); + if tcx.generics_require_sized_self(def_id) { + tcx.emit_node_span_lint( + UNUSED_ASSOCIATED_TYPE_BOUNDS, + hir_id, + span, + crate::errors::UnusedAssociatedTypeBounds { span }, + ); + } + } + + // 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 + .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 + } + }) + .collect(); + + 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, + hir_bounds, + ) { + return Ty::new_error(tcx, guar); + } + + // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as + // `dyn Trait + Send`. + // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering + // the bounds + let mut duplicates = FxHashSet::default(); + auto_traits.retain(|(trait_pred, _)| duplicates.insert(trait_pred.def_id())); + + debug!(?principal_trait); + debug!(?auto_traits); + + // Erase the `dummy_self` (`trait_object_dummy_self`) used above. + let principal_trait_ref = principal_trait.map(|(trait_pred, spans)| { + trait_pred.map_bound(|trait_pred| { + let trait_ref = trait_pred.trait_ref; + assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); + assert_eq!(trait_ref.self_ty(), dummy_self); + + let span = *spans.first().unwrap(); + + // Verify that `dummy_self` did not leak inside default type parameters. This + // could not be done at path creation, since we need to see through trait aliases. + let mut missing_type_params = vec![]; + let generics = tcx.generics_of(trait_ref.def_id); + let args: Vec<_> = trait_ref + .args + .iter() + .enumerate() + // Skip `Self` + .skip(1) + .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() + } else { + arg + } + }) + .collect(); + + let empty_generic_args = hir_bounds.iter().any(|hir_bound| { + 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, + trait_ref.def_id, + span, + empty_generic_args, + ); + + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new( + tcx, + trait_ref.def_id, + args, + )) + }) + }); + + let existential_projections = projection_bounds.into_iter().map(|(bound, _)| { + bound.map_bound(|mut b| { + assert_eq!(b.projection_term.self_ty(), dummy_self); + + // Like for trait refs, verify that `dummy_self` did not leak inside default type + // parameters. + let references_self = b.projection_term.args.iter().skip(1).any(|arg| { + if arg.walk().any(|arg| arg == dummy_self.into()) { + return true; + } + false + }); + if references_self { + let guar = tcx + .dcx() + .span_delayed_bug(span, "trait object projection bounds reference `Self`"); + b.projection_term = replace_dummy_self_with_error(tcx, b.projection_term, guar); + } + + ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( + tcx, b, + )) + }) + }); + + let mut auto_trait_predicates: Vec<_> = auto_traits + .into_iter() + .map(|(trait_pred, _)| { + assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive); + assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self); + + ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id())) + }) + .collect(); + auto_trait_predicates.dedup(); + + // N.b. principal, projections, auto traits + // FIXME: This is actually wrong with multiple principals in regards to symbol mangling + let mut v = principal_trait_ref + .into_iter() + .chain(existential_projections) + .chain(auto_trait_predicates) + .collect::>(); + v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + let existential_predicates = tcx.mk_poly_existential_predicates(&v); + + // Use explicitly-specified region bound, unless the bound is missing. + let region_bound = if !lifetime.is_elided() { + self.lower_lifetime(lifetime, RegionInferReason::ExplicitObjectLifetime) + } else { + self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { + // Curiously, we prefer object lifetime default for `+ '_`... + if tcx.named_bound_var(lifetime.hir_id).is_some() { + self.lower_lifetime(lifetime, RegionInferReason::ExplicitObjectLifetime) + } else { + let reason = + if let hir::LifetimeKind::ImplicitObjectLifetimeDefault = lifetime.kind { + if let hir::Node::Ty(hir::Ty { + kind: hir::TyKind::Ref(parent_lifetime, _), + .. + }) = tcx.parent_hir_node(hir_id) + && tcx.named_bound_var(parent_lifetime.hir_id).is_none() + { + // Parent lifetime must have failed to resolve. Don't emit a redundant error. + RegionInferReason::ExplicitObjectLifetime + } else { + RegionInferReason::ObjectLifetimeDefault + } + } else { + RegionInferReason::ExplicitObjectLifetime + }; + self.re_infer(span, reason) + } + }) + }; + debug!(?region_bound); + + Ty::new_dynamic(tcx, existential_predicates, region_bound) + } + + /// Check that elaborating the principal of a trait ref doesn't lead to projections + /// that are unconstrained. This can happen because an otherwise unconstrained + /// *type variable* can be substituted with a type that has late-bound regions. See + /// `elaborated-predicates-unconstrained-late-bound.rs` for a test. + fn check_elaborated_projection_mentions_input_lifetimes( + &self, + pred: ty::PolyProjectionPredicate<'tcx>, + span: Span, + supertrait_span: Span, + ) { + let tcx = self.tcx(); + + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref or assoc_item. These are not well-formed. + // + // Example: + // + // for<'a> ::Item = &'a str // <-- 'a is bad + // for<'a> >::Output = &'a str // <-- 'a is ok + let late_bound_in_projection_term = + tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term)); + let late_bound_in_term = + tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term)); + debug!(?late_bound_in_projection_term); + debug!(?late_bound_in_term); + + // FIXME: point at the type params that don't have appropriate lifetimes: + // struct S1 Fn(&i32, &i32) -> &'a i32>(F); + // ---- ---- ^^^^^^^ + // NOTE(associated_const_equality): This error should be impossible to trigger + // with associated const equality constraints. + self.validate_late_bound_regions( + late_bound_in_projection_term, + late_bound_in_term, + |br_name| { + let item_name = tcx.item_name(pred.item_def_id()); + struct_span_code_err!( + self.dcx(), + span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + item_name, + br_name + ) + .with_span_label(supertrait_span, "due to this supertrait") + }, + ); + } + + /// Prohibit or lint against *bare* trait object types depending on the edition. + /// + /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. + /// In edition 2021 and onward we emit a hard error for them. + fn prohibit_or_lint_bare_trait_object_ty( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + ) -> Option { + let tcx = self.tcx(); + let [poly_trait_ref, ..] = hir_bounds else { return None }; + + let in_path = match tcx.parent_hir_node(hir_id) { + hir::Node::Ty(hir::Ty { + kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) if qself.hir_id == hir_id => true, + _ => false, + }; + let needs_bracket = in_path + && !tcx + .sess + .source_map() + .span_to_prev_source(span) + .ok() + .is_some_and(|s| s.trim_end().ends_with('<')); + + let is_global = poly_trait_ref.trait_ref.path.is_global(); + + let mut sugg = vec![( + span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, + ), + )]; + + if is_global || needs_bracket { + sugg.push(( + span.shrink_to_hi(), + format!( + "{}{}", + if is_global { ")" } else { "" }, + if needs_bracket { ">" } else { "" }, + ), + )); + } + + if span.edition().at_least_rust_2021() { + let mut diag = rustc_errors::struct_span_code_err!( + self.dcx(), + span, + E0782, + "{}", + "expected a type, found a trait" + ); + if span.can_be_used_for_suggestions() + && poly_trait_ref.trait_ref.trait_def_id().is_some() + && !self.maybe_suggest_impl_trait(span, hir_id, hir_bounds, &mut diag) + && !self.maybe_suggest_dyn_trait(hir_id, sugg, &mut diag) + { + self.maybe_suggest_add_generic_impl_trait(span, hir_id, &mut diag); + } + // Check if the impl trait that we are considering is an impl of a local trait. + self.maybe_suggest_blanket_trait_impl(span, hir_id, &mut diag); + self.maybe_suggest_assoc_ty_bound(hir_id, &mut diag); + self.maybe_suggest_typoed_method( + hir_id, + poly_trait_ref.trait_ref.trait_def_id(), + &mut diag, + ); + // In case there is an associated type with the same name + // Add the suggestion to this error + if let Some(mut sugg) = + self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) + && let Suggestions::Enabled(ref mut s1) = diag.suggestions + && let Suggestions::Enabled(ref mut s2) = sugg.suggestions + { + s1.append(s2); + sugg.cancel(); + } + Some(diag.emit()) + } else { + tcx.node_span_lint(BARE_TRAIT_OBJECTS, hir_id, span, |lint| { + lint.primary_message("trait objects without an explicit `dyn` are deprecated"); + if span.can_be_used_for_suggestions() { + lint.multipart_suggestion_verbose( + "if this is a dyn-compatible trait, use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + } + self.maybe_suggest_blanket_trait_impl(span, hir_id, lint); + }); + None + } + } + + /// For a struct or enum with an invalid bare trait object field, suggest turning + /// it into a generic type bound. + fn maybe_suggest_add_generic_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + let parent_hir_id = tcx.parent_hir_id(hir_id); + let parent_item = tcx.hir_get_parent_item(hir_id).def_id; + + let generics = match tcx.hir_node_by_def_id(parent_item) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, generics, variant), + .. + }) => { + if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { + return false; + } + generics + } + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { + if !def + .variants + .iter() + .flat_map(|variant| variant.data.fields().iter()) + .any(|field| field.hir_id == parent_hir_id) + { + return false; + } + generics + } + _ => return false, + }; + + let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + + let param = "TUV" + .chars() + .map(|c| c.to_string()) + .chain((0..).map(|i| format!("P{i}"))) + .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) + .expect("we definitely can find at least one param name to generate"); + let mut sugg = vec![(span, param.to_string())]; + if let Some(insertion_span) = generics.span_for_param_suggestion() { + sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); + } else { + sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); + } + diag.multipart_suggestion_verbose( + "you might be missing a type parameter", + sugg, + Applicability::MachineApplicable, + ); + true + } + + /// Make sure that we are in the condition to suggest the blanket implementation. + fn maybe_suggest_blanket_trait_impl( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_, G>, + ) { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), + .. + }) = tcx.hir_node_by_def_id(parent_id) + && hir_id == impl_self_ty.hir_id + { + let Some(of_trait) = of_trait else { + diag.span_suggestion_verbose( + impl_self_ty.span.shrink_to_hi(), + "you might have intended to implement this trait for a given type", + format!(" for /* Type */"), + Applicability::HasPlaceholders, + ); + return; + }; + if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { + return; + } + let of_trait_span = of_trait.trait_ref.path.span; + // make sure that we are not calling unwrap to abort during the compilation + let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { + return; + }; + + let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(span) else { + return; + }; + let sugg = self.add_generic_param_suggestion(generics, span, &impl_trait_name); + diag.multipart_suggestion( + format!( + "alternatively use a blanket implementation to implement `{of_trait_name}` for \ + all types that also implement `{impl_trait_name}`" + ), + sugg, + Applicability::MaybeIncorrect, + ); + } + } + + /// Try our best to approximate when adding `dyn` would be helpful for a bare + /// trait object. + /// + /// Right now, this is if the type is either directly nested in another ty, + /// or if it's in the tail field within a struct. This approximates what the + /// user would've gotten on edition 2015, except for the case where we have + /// an *obvious* knock-on `Sized` error. + fn maybe_suggest_dyn_trait( + &self, + hir_id: hir::HirId, + sugg: Vec<(Span, String)>, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + // Look at the direct HIR parent, since we care about the relationship between + // the type and the thing that directly encloses it. + match tcx.parent_hir_node(hir_id) { + // These are all generally ok. Namely, when a trait object is nested + // into another expression or ty, it's either very certain that they + // missed the ty (e.g. `&Trait`) or it's not really possible to tell + // what their intention is, so let's not give confusing suggestions and + // just mention `dyn`. The user can make up their mind what to do here. + hir::Node::Ty(_) + | hir::Node::Expr(_) + | hir::Node::PatExpr(_) + | hir::Node::PathSegment(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Item(_) + | hir::Node::WherePredicate(_) => {} + + hir::Node::Field(field) => { + // Enums can't have unsized fields, fields can only have an unsized tail field. + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, _, variant), .. + }) = tcx.parent_hir_node(field.hir_id) + && variant + .fields() + .last() + .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) + { + // Ok + } else { + return false; + } + } + _ => return false, + } + + // FIXME: Only emit this suggestion if the trait is dyn-compatible. + diag.multipart_suggestion_verbose( + "you can add the `dyn` keyword if you want a trait object", + sugg, + Applicability::MachineApplicable, + ); + true + } + + fn add_generic_param_suggestion( + &self, + generics: &hir::Generics<'_>, + self_ty_span: Span, + impl_trait_name: &str, + ) -> Vec<(Span, String)> { + // check if the trait has generics, to make a correct suggestion + let param_name = generics.params.next_type_param_name(None); + + let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { + (span, format!(", {param_name}: {impl_trait_name}")) + } else { + (generics.span, format!("<{param_name}: {impl_trait_name}>")) + }; + vec![(self_ty_span, param_name), add_generic_sugg] + } + + /// Make sure that we are in the condition to suggest `impl Trait`. + fn maybe_suggest_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` + // and suggest `Trait0`. + // Functions are found in three different contexts. + // 1. Independent functions + // 2. Functions inside trait blocks + // 3. Functions inside impl blocks + let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, generics, .. }, .. + }) => (sig, generics), + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + _ => return false, + }; + let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + let impl_sugg = vec![(span.shrink_to_lo(), "impl ".to_string())]; + // Check if trait object is safe for suggesting dynamic dispatch. + let is_dyn_compatible = hir_bounds.iter().all(|bound| match bound.trait_ref.path.res { + Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), + _ => false, + }); + + let borrowed = matches!( + tcx.parent_hir_node(hir_id), + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) + ); + + // Suggestions for function return type. + if let hir::FnRetTy::Return(ty) = sig.decl.output + && ty.peel_refs().hir_id == hir_id + { + let pre = if !is_dyn_compatible { + format!("`{trait_name}` is dyn-incompatible, ") + } else { + String::new() + }; + let msg = format!( + "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ + single underlying type", + ); + + diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); + + // Suggest `Box` for return type + if is_dyn_compatible { + // If the return type is `&Trait`, we don't want + // the ampersand to be displayed in the `Box` + // suggestion. + let suggestion = if borrowed { + vec![(ty.span, format!("Box"))] + } else { + vec![ + (ty.span.shrink_to_lo(), "Box".to_string()), + ] + }; + + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + suggestion, + Applicability::MachineApplicable, + ); + } + return true; + } + + // Suggestions for function parameters. + for ty in sig.decl.inputs { + if ty.peel_refs().hir_id != hir_id { + continue; + } + let sugg = self.add_generic_param_suggestion(generics, span, &trait_name); + diag.multipart_suggestion_verbose( + format!("use a new generic type parameter, constrained by `{trait_name}`"), + sugg, + Applicability::MachineApplicable, + ); + diag.multipart_suggestion_verbose( + "you can also use an opaque type, but users won't be able to specify the type \ + parameter when calling the `fn`, having to rely exclusively on type inference", + impl_sugg, + Applicability::MachineApplicable, + ); + if !is_dyn_compatible { + diag.note(format!( + "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" + )); + } else { + // No ampersand in suggestion if it's borrowed already + let (dyn_str, paren_dyn_str) = + if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; + + let sugg = if let [_, _, ..] = hir_bounds { + // There is more than one trait bound, we need surrounding parentheses. + vec![ + (span.shrink_to_lo(), paren_dyn_str.to_string()), + (span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(span.shrink_to_lo(), dyn_str.to_string())] + }; + diag.multipart_suggestion_verbose( + format!( + "alternatively, use a trait object to accept any type that implements \ + `{trait_name}`, accessing its methods at runtime using dynamic dispatch", + ), + sugg, + Applicability::MachineApplicable, + ); + } + return true; + } + false + } + + fn maybe_suggest_assoc_ty_bound(&self, hir_id: hir::HirId, diag: &mut Diag<'_>) { + let mut parents = self.tcx().hir_parent_iter(hir_id); + + if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() + && let Some(obj_ty) = constraint.ty() + && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() + { + if let Some((_, hir::Node::Ty(ty))) = parents.next() + && let hir::TyKind::TraitObject(..) = ty.kind + { + // Assoc ty bounds aren't permitted inside trait object types. + return; + } + + if trait_ref + .path + .segments + .iter() + .find_map(|seg| { + seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) + }) + .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) + { + // Only consider angle-bracketed args (where we have a `=` to replace with `:`). + return; + } + + let lo = if constraint.gen_args.span_ext.is_dummy() { + constraint.ident.span + } else { + constraint.gen_args.span_ext + }; + let hi = obj_ty.span; + + if !lo.eq_ctxt(hi) { + return; + } + + diag.span_suggestion_verbose( + lo.between(hi), + "you might have meant to write a bound here", + ": ", + Applicability::MaybeIncorrect, + ); + } + } + + fn maybe_suggest_typoed_method( + &self, + hir_id: hir::HirId, + trait_def_id: Option, + diag: &mut Diag<'_>, + ) { + let tcx = self.tcx(); + let Some(trait_def_id) = trait_def_id else { + return; + }; + let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), + .. + }) = tcx.parent_hir_node(hir_id) + else { + return; + }; + if path_ty.hir_id != hir_id { + return; + } + let names: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|assoc| assoc.namespace() == hir::def::Namespace::ValueNS) + .map(|cand| cand.name()) + .collect(); + if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { + diag.span_suggestion_verbose( + segment.ident.span, + format!( + "you may have misspelled this associated item, causing `{}` \ + to be interpreted as a type rather than a trait", + tcx.item_name(trait_def_id), + ), + typo, + Applicability::MaybeIncorrect, + ); + } + } +} + +fn replace_dummy_self_with_error<'tcx, T: TypeFoldable>>( + tcx: TyCtxt<'tcx>, + t: T, + guar: ErrorGuaranteed, +) -> T { + t.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| { + if ty == tcx.types.trait_object_dummy_self { Ty::new_error(tcx, guar) } else { ty } + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }) +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs deleted file mode 100644 index 56998b5b53c..00000000000 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ /dev/null @@ -1,533 +0,0 @@ -use rustc_ast::TraitObjectSyntax; -use rustc_errors::codes::*; -use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_lint_defs::Applicability; -use rustc_lint_defs::builtin::BARE_TRAIT_OBJECTS; -use rustc_span::Span; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; - -use super::HirTyLowerer; - -impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - /// Prohibit or lint against *bare* trait object types depending on the edition. - /// - /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. - /// In edition 2021 and onward we emit a hard error for them. - pub(super) fn prohibit_or_lint_bare_trait_object_ty( - &self, - self_ty: &hir::Ty<'_>, - ) -> Option { - let tcx = self.tcx(); - - let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = - self_ty.kind - && let TraitObjectSyntax::None = tagged_ptr.tag() - { - poly_trait_ref - } else { - return None; - }; - - let in_path = match tcx.parent_hir_node(self_ty.hir_id) { - hir::Node::Ty(hir::Ty { - kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::PatExpr(hir::PatExpr { - kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) if qself.hir_id == self_ty.hir_id => true, - _ => false, - }; - let needs_bracket = in_path - && !tcx - .sess - .source_map() - .span_to_prev_source(self_ty.span) - .ok() - .is_some_and(|s| s.trim_end().ends_with('<')); - - let is_global = poly_trait_ref.trait_ref.path.is_global(); - - let mut sugg = vec![( - self_ty.span.shrink_to_lo(), - format!( - "{}dyn {}", - if needs_bracket { "<" } else { "" }, - if is_global { "(" } else { "" }, - ), - )]; - - if is_global || needs_bracket { - sugg.push(( - self_ty.span.shrink_to_hi(), - format!( - "{}{}", - if is_global { ")" } else { "" }, - if needs_bracket { ">" } else { "" }, - ), - )); - } - - if self_ty.span.edition().at_least_rust_2021() { - let mut diag = rustc_errors::struct_span_code_err!( - self.dcx(), - self_ty.span, - E0782, - "{}", - "expected a type, found a trait" - ); - if self_ty.span.can_be_used_for_suggestions() - && poly_trait_ref.trait_ref.trait_def_id().is_some() - && !self.maybe_suggest_impl_trait(self_ty, &mut diag) - && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag) - { - self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag); - } - // Check if the impl trait that we are considering is an impl of a local trait. - self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); - self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); - self.maybe_suggest_typoed_method( - self_ty, - poly_trait_ref.trait_ref.trait_def_id(), - &mut diag, - ); - // In case there is an associated type with the same name - // Add the suggestion to this error - if let Some(mut sugg) = - self.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) - && let Suggestions::Enabled(ref mut s1) = diag.suggestions - && let Suggestions::Enabled(ref mut s2) = sugg.suggestions - { - s1.append(s2); - sugg.cancel(); - } - Some(diag.emit()) - } else { - tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { - lint.primary_message("trait objects without an explicit `dyn` are deprecated"); - if self_ty.span.can_be_used_for_suggestions() { - lint.multipart_suggestion_verbose( - "if this is a dyn-compatible trait, use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - } - self.maybe_suggest_blanket_trait_impl(self_ty, lint); - }); - None - } - } - - /// For a struct or enum with an invalid bare trait object field, suggest turning - /// it into a generic type bound. - fn maybe_suggest_add_generic_impl_trait( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id); - let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - - let generics = match tcx.hir_node_by_def_id(parent_item) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, generics, variant), - .. - }) => { - if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { - return false; - } - generics - } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { - if !def - .variants - .iter() - .flat_map(|variant| variant.data.fields().iter()) - .any(|field| field.hir_id == parent_hir_id) - { - return false; - } - generics - } - _ => return false, - }; - - let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - - let param = "TUV" - .chars() - .map(|c| c.to_string()) - .chain((0..).map(|i| format!("P{i}"))) - .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) - .expect("we definitely can find at least one param name to generate"); - let mut sugg = vec![(self_ty.span, param.to_string())]; - if let Some(insertion_span) = generics.span_for_param_suggestion() { - sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); - } else { - sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); - } - diag.multipart_suggestion_verbose( - "you might be missing a type parameter", - sugg, - Applicability::MachineApplicable, - ); - true - } - /// Make sure that we are in the condition to suggest the blanket implementation. - fn maybe_suggest_blanket_trait_impl( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_, G>, - ) { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), - .. - }) = tcx.hir_node_by_def_id(parent_id) - && self_ty.hir_id == impl_self_ty.hir_id - { - let Some(of_trait) = of_trait else { - diag.span_suggestion_verbose( - impl_self_ty.span.shrink_to_hi(), - "you might have intended to implement this trait for a given type", - format!(" for /* Type */"), - Applicability::HasPlaceholders, - ); - return; - }; - if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { - return; - } - let of_trait_span = of_trait.trait_ref.path.span; - // make sure that we are not calling unwrap to abort during the compilation - let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { - return; - }; - - let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span) - else { - return; - }; - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); - diag.multipart_suggestion( - format!( - "alternatively use a blanket implementation to implement `{of_trait_name}` for \ - all types that also implement `{impl_trait_name}`" - ), - sugg, - Applicability::MaybeIncorrect, - ); - } - } - - /// Try our best to approximate when adding `dyn` would be helpful for a bare - /// trait object. - /// - /// Right now, this is if the type is either directly nested in another ty, - /// or if it's in the tail field within a struct. This approximates what the - /// user would've gotten on edition 2015, except for the case where we have - /// an *obvious* knock-on `Sized` error. - fn maybe_suggest_dyn_trait( - &self, - self_ty: &hir::Ty<'_>, - sugg: Vec<(Span, String)>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - // Look at the direct HIR parent, since we care about the relationship between - // the type and the thing that directly encloses it. - match tcx.parent_hir_node(self_ty.hir_id) { - // These are all generally ok. Namely, when a trait object is nested - // into another expression or ty, it's either very certain that they - // missed the ty (e.g. `&Trait`) or it's not really possible to tell - // what their intention is, so let's not give confusing suggestions and - // just mention `dyn`. The user can make up their mind what to do here. - hir::Node::Ty(_) - | hir::Node::Expr(_) - | hir::Node::PatExpr(_) - | hir::Node::PathSegment(_) - | hir::Node::AssocItemConstraint(_) - | hir::Node::TraitRef(_) - | hir::Node::Item(_) - | hir::Node::WherePredicate(_) => {} - - hir::Node::Field(field) => { - // Enums can't have unsized fields, fields can only have an unsized tail field. - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, _, variant), .. - }) = tcx.parent_hir_node(field.hir_id) - && variant - .fields() - .last() - .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) - { - // Ok - } else { - return false; - } - } - _ => return false, - } - - // FIXME: Only emit this suggestion if the trait is dyn-compatible. - diag.multipart_suggestion_verbose( - "you can add the `dyn` keyword if you want a trait object", - sugg, - Applicability::MachineApplicable, - ); - true - } - - fn add_generic_param_suggestion( - &self, - generics: &hir::Generics<'_>, - self_ty_span: Span, - impl_trait_name: &str, - ) -> Vec<(Span, String)> { - // check if the trait has generics, to make a correct suggestion - let param_name = generics.params.next_type_param_name(None); - - let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { - (span, format!(", {param_name}: {impl_trait_name}")) - } else { - (generics.span, format!("<{param_name}: {impl_trait_name}>")) - }; - vec![(self_ty_span, param_name), add_generic_sugg] - } - - /// Make sure that we are in the condition to suggest `impl Trait`. - fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` - // and suggest `Trait0`. - // Functions are found in three different contexts. - // 1. Independent functions - // 2. Functions inside trait blocks - // 3. Functions inside impl blocks - let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn { sig, generics, .. }, .. - }) => (sig, generics), - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - _ => return false, - }; - let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; - // Check if trait object is safe for suggesting dynamic dispatch. - let is_dyn_compatible = match self_ty.kind { - hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { - Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), - _ => false, - }) - } - _ => false, - }; - - let borrowed = matches!( - tcx.parent_hir_node(self_ty.hir_id), - hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) - ); - - // Suggestions for function return type. - if let hir::FnRetTy::Return(ty) = sig.decl.output - && ty.peel_refs().hir_id == self_ty.hir_id - { - let pre = if !is_dyn_compatible { - format!("`{trait_name}` is dyn-incompatible, ") - } else { - String::new() - }; - let msg = format!( - "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ - single underlying type", - ); - - diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); - - // Suggest `Box` for return type - if is_dyn_compatible { - // If the return type is `&Trait`, we don't want - // the ampersand to be displayed in the `Box` - // suggestion. - let suggestion = if borrowed { - vec![(ty.span, format!("Box"))] - } else { - vec![ - (ty.span.shrink_to_lo(), "Box".to_string()), - ] - }; - - diag.multipart_suggestion_verbose( - "alternatively, you can return an owned trait object", - suggestion, - Applicability::MachineApplicable, - ); - } - return true; - } - - // Suggestions for function parameters. - for ty in sig.decl.inputs { - if ty.peel_refs().hir_id != self_ty.hir_id { - continue; - } - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); - diag.multipart_suggestion_verbose( - format!("use a new generic type parameter, constrained by `{trait_name}`"), - sugg, - Applicability::MachineApplicable, - ); - diag.multipart_suggestion_verbose( - "you can also use an opaque type, but users won't be able to specify the type \ - parameter when calling the `fn`, having to rely exclusively on type inference", - impl_sugg, - Applicability::MachineApplicable, - ); - if !is_dyn_compatible { - diag.note(format!( - "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" - )); - } else { - // No ampersand in suggestion if it's borrowed already - let (dyn_str, paren_dyn_str) = - if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { - // There is more than one trait bound, we need surrounding parentheses. - vec![ - (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), - (self_ty.span.shrink_to_hi(), ")".to_string()), - ] - } else { - vec![(self_ty.span.shrink_to_lo(), dyn_str.to_string())] - }; - diag.multipart_suggestion_verbose( - format!( - "alternatively, use a trait object to accept any type that implements \ - `{trait_name}`, accessing its methods at runtime using dynamic dispatch", - ), - sugg, - Applicability::MachineApplicable, - ); - } - return true; - } - false - } - - fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) { - let mut parents = self.tcx().hir_parent_iter(self_ty.hir_id); - - if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() - && let Some(obj_ty) = constraint.ty() - && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() - { - if let Some((_, hir::Node::Ty(ty))) = parents.next() - && let hir::TyKind::TraitObject(..) = ty.kind - { - // Assoc ty bounds aren't permitted inside trait object types. - return; - } - - if trait_ref - .path - .segments - .iter() - .find_map(|seg| { - seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) - }) - .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) - { - // Only consider angle-bracketed args (where we have a `=` to replace with `:`). - return; - } - - let lo = if constraint.gen_args.span_ext.is_dummy() { - constraint.ident.span - } else { - constraint.gen_args.span_ext - }; - let hi = obj_ty.span; - - if !lo.eq_ctxt(hi) { - return; - } - - diag.span_suggestion_verbose( - lo.between(hi), - "you might have meant to write a bound here", - ": ", - Applicability::MaybeIncorrect, - ); - } - } - - fn maybe_suggest_typoed_method( - &self, - self_ty: &hir::Ty<'_>, - trait_def_id: Option, - diag: &mut Diag<'_>, - ) { - let tcx = self.tcx(); - let Some(trait_def_id) = trait_def_id else { - return; - }; - let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), - .. - }) = tcx.parent_hir_node(self_ty.hir_id) - else { - return; - }; - if path_ty.hir_id != self_ty.hir_id { - return; - } - let names: Vec<_> = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter(|assoc| assoc.namespace() == Namespace::ValueNS) - .map(|cand| cand.name()) - .collect(); - if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { - diag.span_suggestion_verbose( - segment.ident.span, - format!( - "you may have misspelled this associated item, causing `{}` \ - to be interpreted as a type rather than a trait", - tcx.item_name(trait_def_id), - ), - typo, - Applicability::MaybeIncorrect, - ); - } - } -} 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 b64129dc54e..9b198d04454 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -15,10 +15,9 @@ mod bounds; mod cmse; -mod dyn_compatibility; +mod dyn_trait; pub mod errors; pub mod generics; -mod lint; use std::assert_matches::assert_matches; use std::slice; @@ -2427,15 +2426,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ), hir::TyKind::TraitObject(bounds, tagged_ptr) => { let lifetime = tagged_ptr.pointer(); - - if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { - // Don't continue with type analysis if the `dyn` keyword is missing - // It generates confusing errors, especially if the user meant to use another - // keyword like `impl` - Ty::new_error(tcx, guar) - } else { - self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime) - } + let syntax = tagged_ptr.tag(); + self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, syntax) } // If we encounter a fully qualified path with RTN generics, then it must have // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore -- cgit 1.4.1-3-g733a5