From 30f56413dfcc0638ab6b18868edff4ee2fb35bc1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 16 Dec 2025 20:25:58 +0000 Subject: [PATCH 1/2] Remove 'static requirement on try_as_dyn --- .../src/interpret/intrinsics.rs | 5 +- compiler/rustc_hir_analysis/src/collect.rs | 7 ++ compiler/rustc_infer/src/infer/mod.rs | 2 + .../rustc_infer/src/infer/opaque_types/mod.rs | 4 +- .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 6 ++ compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 9 +++ compiler/rustc_middle/src/ty/context.rs | 4 + compiler/rustc_middle/src/ty/generics.rs | 56 ++++++++++++- compiler/rustc_middle/src/ty/sty.rs | 55 ++++++++++++- .../src/solve/assembly/mod.rs | 2 + .../src/solve/effect_goals.rs | 1 + .../rustc_next_trait_solver/src/solve/mod.rs | 2 +- .../src/solve/normalizes_to/mod.rs | 2 + .../src/solve/normalizes_to/opaque_types.rs | 2 +- .../src/solve/search_graph.rs | 1 + .../src/solve/trait_goals.rs | 13 ++- .../src/solve/delegate.rs | 1 + .../src/solve/fulfill.rs | 1 + .../src/traits/fulfill.rs | 1 + .../src/traits/normalize.rs | 4 +- .../src/traits/project.rs | 1 + .../src/traits/query/normalize.rs | 2 +- .../src/traits/select/mod.rs | 11 +++ compiler/rustc_ty_utils/src/instance.rs | 1 + compiler/rustc_type_ir/src/infer_ctxt.rs | 4 + compiler/rustc_type_ir/src/interner.rs | 2 + compiler/rustc_type_ir/src/relate/combine.rs | 1 + library/core/src/any.rs | 10 +-- tests/ui/any/non_static.rs | 81 +++++++++++++++++++ tests/ui/any/static_method_bound.rs | 34 ++++++++ 32 files changed, 307 insertions(+), 20 deletions(-) create mode 100644 tests/ui/any/non_static.rs create mode 100644 tests/ui/any/static_method_bound.rs diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d70d157d88085..8780b728eb94c 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -236,7 +236,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; let (infcx, param_env) = - self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); + self.tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv { + typing_mode: ty::TypingMode::Reflection, + ..self.typing_env + }); let ocx = ObligationCtxt::new(&infcx); ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 9343bcd27a339..f294543b3e55b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -89,6 +89,7 @@ pub(crate) fn provide(providers: &mut Providers) { adt_def, fn_sig, impl_trait_header, + impl_is_fully_generic_for_reflection, coroutine_kind, coroutine_for_closure, opaque_ty_origin, @@ -1266,6 +1267,12 @@ pub fn suggest_impl_trait<'tcx>( None } +fn impl_is_fully_generic_for_reflection(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + tcx.type_of(def_id).instantiate_identity().is_fully_generic_for_reflection().is_ok_and( + |params| tcx.explicit_predicates_of(def_id).is_fully_generic_for_reflection(params), + ) +} + fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader<'_> { let icx = ItemCtxt::new(tcx, def_id); let item = tcx.hir_expect_item(def_id); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c9ea420944e23..f0feefb08f7a0 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1047,6 +1047,7 @@ impl<'tcx> InferCtxt<'tcx> { // and post-borrowck analysis mode. We may need to modify its uses // to support PostBorrowckAnalysis in the old solver as well. TypingMode::Coherence + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => false, } @@ -1371,6 +1372,7 @@ impl<'tcx> InferCtxt<'tcx> { } mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::Reflection | ty::TypingMode::PostAnalysis) => mode, }; ty::TypingEnv { typing_mode, param_env } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 9579abf7ec53c..d533876dbe8a7 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -276,7 +276,9 @@ impl<'tcx> InferCtxt<'tcx> { .map(|obligation| obligation.as_goal()), ); } - mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => { + mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis + | ty::TypingMode::Reflection) => { bug!("insert hidden type in {mode:?}") } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 36fe7f380069c..73f218bc82350 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -236,6 +236,7 @@ provide! { tcx, def_id, other, cdata, fn_sig => { table } codegen_fn_attrs => { table } impl_trait_header => { table } + impl_is_fully_generic_for_reflection => { table_direct } const_param_default => { table } object_lifetime_default => { table } thir_abstract_const => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 920c896d5a474..6a9d2e2e91732 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2167,6 +2167,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let header = tcx.impl_trait_header(def_id); record!(self.tables.impl_trait_header[def_id] <- header); + let impl_is_fully_generic_for_reflection = + tcx.impl_is_fully_generic_for_reflection(def_id); + self.tables + .impl_is_fully_generic_for_reflection + .set(def_id.index, impl_is_fully_generic_for_reflection); + self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id)); let trait_ref = header.trait_ref.instantiate_identity(); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 0de54cf874338..ddcd06cb5e7f8 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -407,6 +407,7 @@ define_tables! { constness: Table, safety: Table, defaultness: Table, + impl_is_fully_generic_for_reflection: Table, - optional: attributes: Table>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 08db16ba8ecbb..7c57aa5cbd61d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1127,6 +1127,15 @@ rustc_queries! { separate_provide_extern } + /// Whether all generic parameters of the type are unique unconstrained generic parameters + /// of the impl. `Bar<'static>` or `Foo<'a, 'a>` or outlives bounds on the lifetimes cause + /// this boolean to be false and `try_as_dyn` to return `None`. + query impl_is_fully_generic_for_reflection(impl_id: DefId) -> bool { + desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } + cache_on_disk_if { impl_id.is_local() } + separate_provide_extern + } + /// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct /// whose tail is one of those types. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 25cc739f5ff95..8ddb15cb59805 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -690,6 +690,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.impl_polarity(impl_def_id) } + fn is_fully_generic_for_reflection(self, impl_def_id: Self::ImplId) -> bool { + self.impl_is_fully_generic_for_reflection(impl_def_id) + } + fn trait_is_auto(self, trait_def_id: DefId) -> bool { self.trait_is_auto(trait_def_id) } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index b6b10e2458575..9c65979502c09 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -1,13 +1,15 @@ +use std::ops::ControlFlow; + use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::{Span, Symbol, kw}; +use rustc_type_ir::{TypeSuperVisitable as _, TypeVisitable, TypeVisitor}; use tracing::instrument; use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt}; -use crate::ty; -use crate::ty::{EarlyBinder, GenericArgsRef}; +use crate::ty::{self, EarlyBinder, GenericArgsRef, Region, RegionKind, TyKind}; #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum GenericParamDefKind { @@ -417,6 +419,54 @@ impl<'tcx> GenericPredicates<'tcx> { instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); } + + pub fn is_fully_generic_for_reflection(self, params: FxHashSet) -> bool { + #[derive(Default)] + struct ParamChecker { + params: FxHashSet, + } + impl<'tcx> TypeVisitor> for ParamChecker { + type Result = ControlFlow<()>; + fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { + match r.kind() { + RegionKind::ReEarlyParam(param) => { + if self.params.contains(¶m.index) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } + RegionKind::ReBound(..) + | RegionKind::ReLateParam(_) + | RegionKind::ReStatic + | RegionKind::ReVar(_) + | RegionKind::RePlaceholder(_) + | RegionKind::ReErased + | RegionKind::ReError(_) => ControlFlow::Continue(()), + } + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + TyKind::Param(p) => { + // Reject using parameters used in the type in where bounds + if self.params.contains(&p.index) { + return ControlFlow::Break(()); + } + } + TyKind::Alias(..) => return ControlFlow::Break(()), + _ => (), + } + t.super_visit_with(self) + } + } + + let mut checker = ParamChecker { params }; + + // Pessimistic: if any of the lifetimes used in the type show up in where bounds + // don't allow this impl to be used. + self.predicates.iter().all(|(clause, _)| clause.visit_with(&mut checker).is_continue()) + } } /// `[const]` bounds for a given item. This is represented using a struct much like diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c282f2211f650..e44a6588c23f8 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -8,6 +8,7 @@ use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; use rustc_abi::{FIRST_VARIANT, FieldIdx, ScalableElt, VariantIdx}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::LangItem; @@ -27,7 +28,8 @@ use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, - Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + Region, RegionKind, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, + UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -1365,6 +1367,57 @@ impl<'tcx> Ty<'tcx> { } } + /// For self-types of impls, checks whether the type only has generic parameters in its + /// arguments and only uses the generic params once, too. + /// Pessimistic analysis, so it will reject projection types (except for weak aliases) + /// and other types that may be ok. + /// Returns the list of used params if successful. + pub fn is_fully_generic_for_reflection(self) -> Result, ()> { + #[derive(Default)] + struct ParamFinder { + seen: FxHashSet, + } + + impl<'tcx> TypeVisitor> for ParamFinder { + type Result = ControlFlow<()>; + fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { + match r.kind() { + RegionKind::ReEarlyParam(param) => { + if self.seen.insert(param.index) { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + } + } + RegionKind::ReBound(..) | RegionKind::ReLateParam(_) => { + ControlFlow::Continue(()) + } + RegionKind::ReStatic + | RegionKind::ReVar(_) + | RegionKind::RePlaceholder(_) + | RegionKind::ReErased + | RegionKind::ReError(_) => ControlFlow::Break(()), + } + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + TyKind::Param(p) => { + // Reject using a parameter twice (e.g. in `Foo`) + if !self.seen.insert(p.index) { + return ControlFlow::Break(()); + } + } + TyKind::Alias(..) => return ControlFlow::Break(()), + _ => (), + } + t.super_visit_with(self) + } + } + let mut finder = ParamFinder::default(); + if finder.visit_ty(self).is_continue() { Ok(finder.seen) } else { Err(()) } + } + pub fn boxed_ty(self) -> Option> { match self.kind() { Adt(def, args) if def.is_box() => Some(args.type_at(0)), diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index d27d80a086ad1..8b7404cdc3a75 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -928,6 +928,7 @@ where TypingMode::Coherence => return, TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} } @@ -991,6 +992,7 @@ where TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty), TypingMode::Coherence | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => vec![], }; diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 2837b8565f603..342818da327ef 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -142,6 +142,7 @@ where TypingMode::Coherence => Certainty::AMBIGUOUS, TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), }, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 52a1479d70a15..4baae58d549b3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -368,7 +368,7 @@ where fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { match self.typing_mode() { // Opaques are never rigid outside of analysis mode. - TypingMode::Coherence | TypingMode::PostAnalysis => false, + TypingMode::Reflection | TypingMode::Coherence | TypingMode::PostAnalysis => false, // During analysis, opaques are rigid unless they may be defined by // the current body. TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 70c28421c57ea..d17accf9c0ec7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -298,6 +298,7 @@ where // Outside of coherence, we treat the associated item as rigid instead. ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis => { ecx.structurally_instantiate_normalizes_to_term( @@ -336,6 +337,7 @@ where } ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis => { ecx.structurally_instantiate_normalizes_to_term( diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index a5f857a1dd85b..0fe17eedf89da 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -126,7 +126,7 @@ where self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - TypingMode::PostAnalysis => { + TypingMode::Reflection | TypingMode::PostAnalysis => { // FIXME: Add an assertion that opaque type storage is empty. let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args); self.eq(goal.param_env, expected, actual)?; diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index ea45d50969909..efe4f0d8683fe 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -68,6 +68,7 @@ where } TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => Err(NoSolution), }, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 651f073efb828..02c4f5a7b70e6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -78,13 +78,22 @@ where TypingMode::Coherence => Certainty::AMBIGUOUS, TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), }, // Impl matches polarity (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) - | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => Certainty::Yes, + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => { + if let TypingMode::Reflection = ecx.typing_mode() + && !cx.is_fully_generic_for_reflection(impl_def_id) + { + return Err(NoSolution); + } else { + Certainty::Yes + } + } // Impl doesn't match polarity (ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative) @@ -1330,6 +1339,7 @@ where TypingMode::Coherence => return, TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} } @@ -1504,6 +1514,7 @@ where } TypingMode::Coherence | TypingMode::PostAnalysis + | TypingMode::Reflection | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {} } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index b6bdf1067a35e..b70efceadd7ce 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -278,6 +278,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } => false, TypingMode::PostAnalysis => { let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 7b61a653ae31e..97b167a82bad0 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -289,6 +289,7 @@ where TypingMode::Coherence | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::Reflection | TypingMode::PostAnalysis => return Default::default(), }; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 681f015c17990..b62afd37dc671 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -179,6 +179,7 @@ where TypingMode::Coherence | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::Reflection | TypingMode::PostAnalysis => return Default::default(), }; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 71e9914f93fa8..1c3b11d2a16b6 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -138,7 +138,7 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), - TypingMode::PostAnalysis => {} + TypingMode::Reflection | TypingMode::PostAnalysis => {} } value.has_type_flags(flags) @@ -402,7 +402,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => ty.super_fold_with(self), - TypingMode::PostAnalysis => { + TypingMode::Reflection | TypingMode::PostAnalysis => { let recursion_limit = self.cx().recursion_limit(); if !recursion_limit.value_within_limit(self.depth) { self.selcx.infcx.err_ctxt().report_overflow_error( diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e5c2adaa261d3..1d707b6919062 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -954,6 +954,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( TypingMode::Coherence | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } => { debug!( assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 02438b24ca7f4..97d68c6ff48a5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -222,7 +222,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => ty.try_super_fold_with(self)?, - TypingMode::PostAnalysis => { + TypingMode::Reflection | TypingMode::PostAnalysis => { let args = data.args.try_fold_with(self)?; let recursion_limit = self.cx().recursion_limit(); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d6c9adfb28177..bf01d7cbdb0f2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1468,6 +1468,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TypingMode::Coherence => {} TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Ok(()), } @@ -1520,6 +1521,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { defining_opaque_types.is_empty() || (!pred.has_opaque_types() && !pred.has_coroutines()) } + // Impls that are not fully generic are completely ignored as "nonexistent" + // in this mode, so the results wildly differ from normal trait solving. + TypingMode::Reflection => false, // The hidden types of `defined_opaque_types` is not local to the current // inference context, so we can freely move this to the global cache. TypingMode::PostBorrowckAnalysis { .. } => true, @@ -2549,6 +2553,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> { debug!("reservation impls only apply in intercrate mode"); return Err(()); } + if matches!(self.infcx.typing_mode(), TypingMode::Reflection) + && !self.tcx().impl_is_fully_generic_for_reflection(impl_def_id) + { + debug!("reflection mode only allows fully generic impls"); + return Err(()); + } Ok(Normalized { value: impl_args, obligations: nested_obligations }) } @@ -2885,6 +2895,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } TypingMode::Coherence | TypingMode::PostAnalysis + | TypingMode::Reflection | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => false, } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 23bbd9ca6d639..5209956d00277 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -158,6 +158,7 @@ fn resolve_associated_item<'tcx>( ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::Reflection | ty::TypingMode::PostBorrowckAnalysis { .. } => false, ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(), } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index feafcee7bad9e..912df806b4d7e 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -79,6 +79,10 @@ pub enum TypingMode { /// This is currently only used by the new solver, but should be implemented in /// the old solver as well. PostBorrowckAnalysis { defined_opaque_types: I::LocalDefIds }, + /// During the evaluation of reflection logic that ignores lifetimes, we can only + /// handle impls that are fully generic over all lifetimes without constraints on + /// those lifetimes (other than implied bounds). + Reflection, /// After analysis, mostly during codegen and MIR optimizations, we're able to /// reveal all opaque types. As the hidden type should *never* be observable /// directly by the user, this should not be used by checks which may expose diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 03cf738c05987..dca8ad4c2e0b7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -368,6 +368,8 @@ pub trait Interner: fn impl_polarity(self, impl_def_id: Self::ImplId) -> ty::ImplPolarity; + fn is_fully_generic_for_reflection(self, impl_def_id: Self::ImplId) -> bool; + fn trait_is_auto(self, trait_def_id: Self::TraitId) -> bool; fn trait_is_coinductive(self, trait_def_id: Self::TraitId) -> bool; diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 64b87fac77f94..f64faf1a9d5a3 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -140,6 +140,7 @@ where Ok(a) } TypingMode::Analysis { .. } + | TypingMode::Reflection | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 42f332f7d8ba8..7972548644970 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -942,10 +942,7 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { /// ``` #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] -pub const fn try_as_dyn< - T: Any + 'static, - U: ptr::Pointee> + ?Sized + 'static, ->( +pub const fn try_as_dyn> + ?Sized>( t: &T, ) -> Option<&U> { let vtable: Option> = const { intrinsics::vtable_for::() }; @@ -995,10 +992,7 @@ pub const fn try_as_dyn< /// ``` #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] -pub const fn try_as_dyn_mut< - T: Any + 'static, - U: ptr::Pointee> + ?Sized + 'static, ->( +pub const fn try_as_dyn_mut> + ?Sized>( t: &mut T, ) -> Option<&mut U> { let vtable: Option> = const { intrinsics::vtable_for::() }; diff --git a/tests/ui/any/non_static.rs b/tests/ui/any/non_static.rs new file mode 100644 index 0000000000000..22c91feed5fdc --- /dev/null +++ b/tests/ui/any/non_static.rs @@ -0,0 +1,81 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@check-pass +#![feature(try_as_dyn)] + +trait Trait {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&42_i32).is_none()); +}; + +impl<'a> Trait for &'a [(); 1] {} +const _: () = { + let x = (); + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&[x]).is_some()); +}; + +type Foo = &'static [(); 2]; + +// Ensure type aliases don't skip these checks +impl Trait for Foo {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&[(), ()]).is_none()); +}; + +impl Trait for &() {} +const _: () = { + let x = (); + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&x).is_some()); +}; + +impl Trait for () {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&()).is_some()); +}; + +// Not fully generic impl -> returns None even tho +// implemented for *some* lifetimes +impl<'a> Trait for (&'a (), &'a ()) {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&(&(), &())).is_none()); +}; + +// Not fully generic impl -> returns None even tho +// implemented for *some* lifetimes +impl<'a, 'b: 'a> Trait for (&'a (), &'b (), ()) {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&(&(), &(), ())).is_none()); +}; + +// Only valid for 'static lifetimes -> returns None +// even though we are actually using a `'static` lifetime. +// We can't know what lifetimes are there during codegen, so +// we pessimistically assume it could be a shorter one +impl Trait for &'static u32 {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&&42_u32).is_none()); +}; + +trait Trait2 {} + +struct Struct(T); + +// While this is the impl for `Trait`, in `Reflection` solver mode +// we reject the impl for `Trait2` below, and thus this impl also +// doesn't match. +impl Trait for Struct {} + +impl Trait2 for &'static u32 {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait>(&Struct(&42_u32)).is_none()); +}; + +const _: () = { + trait Homo {} + impl Homo for (T, T) {} + + // Let's pick `T = &'_ i32`. + assert!(std::any::try_as_dyn::<_, dyn Homo>(&(&42_i32, &27_i32)).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/static_method_bound.rs b/tests/ui/any/static_method_bound.rs new file mode 100644 index 0000000000000..aaab8623868f1 --- /dev/null +++ b/tests/ui/any/static_method_bound.rs @@ -0,0 +1,34 @@ +//@run-fail +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +type Payload = Box; + +trait Trait { + fn as_static(&self) -> &'static Payload + where + Self: 'static; +} + +impl<'a> Trait for &'a Payload { + fn as_static(&self) -> &'static Payload + where + Self: 'static, + { + *self + } +} + +fn main() { + let storage: Box = Box::new(Box::new(1i32)); + let wrong: &'static Payload = extend(&*storage); + drop(storage); + println!("{wrong}"); +} + +fn extend(a: &Payload) -> &'static Payload { + // TODO: should panic at the `unwrap` here + let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); + b.as_static() +} From 72b8afb244d44032f9f94760393feecb5c0e7cc9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 13 Jan 2026 13:43:04 +0000 Subject: [PATCH 2/2] Reject traits that have methods with where bounds --- compiler/rustc_hir/src/lang_items.rs | 2 + compiler/rustc_middle/src/traits/select.rs | 2 + compiler/rustc_middle/src/ty/assoc.rs | 4 ++ compiler/rustc_middle/src/ty/context.rs | 21 ++++++ .../src/solve/assembly/mod.rs | 8 +++ .../src/solve/effect_goals.rs | 7 ++ .../src/solve/normalizes_to/mod.rs | 7 ++ .../src/solve/trait_goals.rs | 46 ++++++++++++- compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/select/candidate_assembly.rs | 65 ++++++++++++++++++- .../src/traits/select/confirmation.rs | 9 +++ .../src/traits/select/mod.rs | 1 + compiler/rustc_type_ir/src/interner.rs | 2 + compiler/rustc_type_ir/src/lang_items.rs | 1 + library/core/src/any.rs | 18 +++-- tests/ui/any/static_method_bound.rs | 3 +- tests/ui/any/static_method_bound.stderr | 12 ++++ 17 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 tests/ui/any/static_method_bound.stderr diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 4ac3e4e83e80a..443d6cfc42441 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -201,6 +201,8 @@ language_item_table! { CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); + TryAsDyn, sym::try_as_dyn, try_as_dyn, Target::Trait, GenericRequirement::Exact(0); + // lang items relating to transmutability TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(2); diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 3861efd364281..12a0a1d162ded 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -180,6 +180,8 @@ pub enum SelectionCandidate<'tcx> { BuiltinUnsizeCandidate, BikeshedGuaranteedNoDropCandidate, + + TryAsDynCandidate, } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 5e20bc142ffe6..feb350079545d 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -250,6 +250,10 @@ impl AssocItems { self.items.iter().map(|(_, v)| v) } + pub fn all(&self, f: impl Fn(&ty::AssocItem) -> bool) -> bool { + self.in_definition_order().all(f) + } + pub fn len(&self) -> usize { self.items.len() } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8ddb15cb59805..58e0b76f5cab2 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -718,6 +718,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(trait_def_id).implement_via_object } + fn trait_is_try_as_dyn_compatible(self, def_id: DefId) -> bool { + self.trait_is_try_as_dyn_compatible(def_id) + } + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { self.trait_def(trait_def_id).safety.is_unsafe() } @@ -862,6 +866,7 @@ bidirectional_lang_item_map! { Sized, TransmuteTrait, TrivialClone, + TryAsDyn, Tuple, Unpin, Unsize, @@ -2386,6 +2391,22 @@ impl<'tcx> TyCtxt<'tcx> { None } + pub fn trait_is_try_as_dyn_compatible(self, def_id: DefId) -> bool { + self.associated_items(def_id).all(|method| { + if let ty::AssocKind::Fn { .. } = method.kind { + // Methods with `Self: Sized` are not accessible in `dyn Trait`, so + // we do not need to care about them. + if !self.generics_require_sized_self(method.def_id) { + // FIXME(try_as_dyn): can probably allow other bounds + if !self.explicit_predicates_of(method.def_id).predicates.is_empty() { + return false; + } + } + } + true + }) + } + /// Determines whether identifiers in the assembly have strict naming rules. /// Currently, only NVPTX* targets need it. pub fn has_strict_asm_symbol_naming(self) -> bool { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 8b7404cdc3a75..51a8e98cde744 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -337,6 +337,11 @@ where goal: Goal, ) -> Result, NoSolution>; + fn consider_builtin_try_as_dyn_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution>; + /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. /// @@ -617,6 +622,9 @@ where Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => { G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } + Some(SolverTraitLangItem::TryAsDyn) => { + G::consider_builtin_try_as_dyn_candidate(self, goal) + } _ => Err(NoSolution), } }; diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 342818da327ef..e94697d99881d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -425,6 +425,13 @@ where unreachable!("BikeshedGuaranteedNoDrop is not const"); } + fn consider_builtin_try_as_dyn_candidate( + _ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + unreachable!("`TryAsDynCompat` is not const: {:?}", goal) + } + fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index d17accf9c0ec7..cd54e0f69438c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -952,6 +952,13 @@ where ) -> Result, NoSolution> { unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) } + + fn consider_builtin_try_as_dyn_candidate( + _ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + unreachable!("`TryAsDynCompat` does not have an associated type: {:?}", goal) + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 02c4f5a7b70e6..b4c78cc13e831 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -8,8 +8,8 @@ use rustc_type_ir::solve::{ AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, SizedTraitKind, }; use rustc_type_ir::{ - self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, - TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, + self as ty, ExistentialPredicate, Interner, Movability, PredicatePolarity, TraitPredicate, + TraitRef, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace}; @@ -850,6 +850,48 @@ where } }) } + + fn consider_builtin_try_as_dyn_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + let cx = ecx.cx(); + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let ty = goal.predicate.self_ty(); + match ty.kind() { + ty::Dynamic(bounds, _lifetime) => { + for bound in bounds.as_slice() { + match bound.skip_binder() { + ExistentialPredicate::Trait(trait_ref) => { + if !cx.trait_is_try_as_dyn_compatible(trait_ref.def_id) { + return Err(NoSolution); + } + } + // FIXME(try_as_dyn): check what kind of projections we can allow + ExistentialPredicate::Projection(_) => return Err(NoSolution), + // Auto traits do not affect lifetimes outside of specialization, + // which is disabled in reflection. + ExistentialPredicate::AutoTrait(_) => {} + } + } + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + ty::Bound(..) + | ty::Infer( + ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), + ) => { + panic!("unexpected type `{ty:?}`") + } + + _ => Err(NoSolution), + } + }) + } } /// Small helper function to change the `def_id` of a trait predicate - this is not normally diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72709753b1dff..06ae7e5f8d716 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2294,6 +2294,7 @@ symbols! { truncf32, truncf64, truncf128, + try_as_dyn, try_blocks, try_blocks_heterogeneous, try_capture, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index d0833f0308350..156ae65c8276c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -14,7 +14,9 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate}; +use rustc_middle::ty::{ + self, ExistentialPredicate, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate, +}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument, trace}; @@ -128,6 +130,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut candidates, ); } + Some(LangItem::TryAsDyn) => { + self.assemble_candidates_for_try_as_dyn(obligation, &mut candidates); + } _ => { // We re-match here for traits that can have both builtin impls and user written impls. // After the builtin impls we need to also add user written impls, which we do not want to @@ -1437,4 +1442,62 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_try_as_dyn( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match *obligation.predicate.self_ty().skip_binder().kind() { + ty::Dynamic(bounds, _lifetime) => { + for bound in bounds { + match bound.skip_binder() { + ExistentialPredicate::Trait(trait_ref) => { + if !self.tcx().trait_is_try_as_dyn_compatible(trait_ref.def_id) { + return; + } + } + // FIXME(try_as_dyn): check what kind of projections we can allow + ExistentialPredicate::Projection(_) => return, + // Auto traits do not affect lifetimes outside of specialization, + // which is disabled in reflection. + ExistentialPredicate::AutoTrait(_) => {} + } + } + candidates.vec.push(TryAsDynCandidate); + } + ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(_) + | ty::Array(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) + | ty::Bound(..) => {} + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + candidates.ambiguous = true; + } + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 20a8842f2e8e5..af44c5edb1473 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -141,6 +141,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BikeshedGuaranteedNoDropCandidate => { self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation) } + + TryAsDynCandidate => self.confirm_try_as_dyn_candidate(obligation), }) } @@ -1333,4 +1335,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, obligations) } + + fn confirm_try_as_dyn_candidate( + &mut self, + _obligation: &PolyTraitObligation<'tcx>, + ) -> ImplSource<'tcx, PredicateObligation<'tcx>> { + ImplSource::Builtin(BuiltinImplSource::Misc, PredicateObligations::new()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index bf01d7cbdb0f2..e00bd68b6dfb3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2042,6 +2042,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinObjectCandidate | BuiltinUnsizeCandidate | PointerLikeCandidate + | TryAsDynCandidate | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index dca8ad4c2e0b7..9fde7f65a1491 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -385,6 +385,8 @@ pub trait Interner: /// Returns `true` if this is an `unsafe trait`. fn trait_is_unsafe(self, trait_def_id: Self::TraitId) -> bool; + fn trait_is_try_as_dyn_compatible(self, def_id: Self::TraitId) -> bool; + fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool; fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 39b575ebab63b..2b108f1b09486 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -49,6 +49,7 @@ pub enum SolverTraitLangItem { Sized, TransmuteTrait, TrivialClone, + TryAsDyn, Tuple, Unpin, Unsize, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7972548644970..4583f6c624b1d 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -907,6 +907,16 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() } +/// Trait that is automatically implemented for all `dyn Trait` where `Trait` does +/// not have any methods with `where` bounds unless the method also has a +/// `where Self: Sized` bound. +/// +/// This is required for `try_as_dyn` to be able to +/// +#[unstable(feature = "try_as_dyn", issue = "144361")] +#[lang = "try_as_dyn"] +pub trait TryAsDynCompatible: ptr::Pointee> {} + /// Returns `Some(&U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. /// /// # Compile-time failures @@ -942,9 +952,7 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { /// ``` #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] -pub const fn try_as_dyn> + ?Sized>( - t: &T, -) -> Option<&U> { +pub const fn try_as_dyn(t: &T) -> Option<&U> { let vtable: Option> = const { intrinsics::vtable_for::() }; match vtable { Some(dyn_metadata) => { @@ -992,9 +1000,7 @@ pub const fn try_as_dyn> + ?Si /// ``` #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] -pub const fn try_as_dyn_mut> + ?Sized>( - t: &mut T, -) -> Option<&mut U> { +pub const fn try_as_dyn_mut(t: &mut T) -> Option<&mut U> { let vtable: Option> = const { intrinsics::vtable_for::() }; match vtable { Some(dyn_metadata) => { diff --git a/tests/ui/any/static_method_bound.rs b/tests/ui/any/static_method_bound.rs index aaab8623868f1..47bbba6fbef4d 100644 --- a/tests/ui/any/static_method_bound.rs +++ b/tests/ui/any/static_method_bound.rs @@ -1,4 +1,3 @@ -//@run-fail #![feature(try_as_dyn)] use std::any::try_as_dyn; @@ -28,7 +27,7 @@ fn main() { } fn extend(a: &Payload) -> &'static Payload { - // TODO: should panic at the `unwrap` here let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); + //~^ ERROR: the trait bound `(dyn Trait + 'static): TryAsDynCompatible` is not satisfied b.as_static() } diff --git a/tests/ui/any/static_method_bound.stderr b/tests/ui/any/static_method_bound.stderr new file mode 100644 index 0000000000000..74da9fd35327d --- /dev/null +++ b/tests/ui/any/static_method_bound.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `(dyn Trait + 'static): TryAsDynCompatible` is not satisfied + --> $DIR/static_method_bound.rs:30:60 + | +LL | let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); + | ^^^^^^^^^^^^^^^^^^^ the trait `TryAsDynCompatible` is not implemented for `(dyn Trait + 'static)` + | +note: required by a bound in `try_as_dyn` + --> $SRC_DIR/core/src/any.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.