diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 2ea5e4a25c116..ae0bc0fe3d93f 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/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 557f76208bfe6..f3e7d1de8dcba 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(1); + // 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_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 03bbc9c0a7c9b..f07afaa3c8704 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -86,6 +86,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, @@ -1274,6 +1275,11 @@ pub fn suggest_impl_trait<'tcx>( None } +fn impl_is_fully_generic_for_reflection(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + tcx.impl_trait_header(def_id).is_fully_generic_for_reflection() + && tcx.explicit_predicates_of(def_id).is_fully_generic_for_reflection() +} + 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 e15b25500bb57..3f7d201a5683e 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 6ea9a28528042..07773200d8713 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -237,6 +237,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 4988cafdd3637..d1db12226cc54 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2173,6 +2173,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 408b50ae48df1..695b3092e529e 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/queries.rs b/compiler/rustc_middle/src/queries.rs index 32fc7198425b3..14601b3264b49 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1126,6 +1126,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/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 3a32029c60bd0..444a47df4c7e7 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -183,6 +183,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/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 5a15d132048dd..d898426410d43 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -626,6 +626,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) } @@ -799,6 +803,7 @@ bidirectional_lang_item_map! { Sized, TransmuteTrait, TrivialClone, + TryAsDyn, Tuple, Unpin, Unsize, diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index ed587cbc3c285..fb0ad6fb01ce5 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_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, ClauseKind, EarlyBinder, GenericArgsRef, Region, RegionKind, TyKind}; #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum GenericParamDefKind { @@ -421,6 +423,69 @@ impl<'tcx> GenericPredicates<'tcx> { instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); } + + /// Allow simple where bounds like `T: Debug`, but prevent any kind of + /// outlives bounds or uses of generic parameters on the right hand side. + pub fn is_fully_generic_for_reflection(self) -> bool { + struct ParamChecker; + impl<'tcx> TypeVisitor> for ParamChecker { + type Result = ControlFlow<()>; + fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { + match r.kind() { + RegionKind::ReEarlyParam(_) + | RegionKind::ReStatic + | RegionKind::ReVar(_) + | RegionKind::RePlaceholder(_) + | RegionKind::ReErased + | RegionKind::ReError(_) => ControlFlow::Break(()), + RegionKind::ReBound(..) | RegionKind::ReLateParam(_) => { + 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 + return ControlFlow::Break(()); + } + TyKind::Alias(..) => return ControlFlow::Break(()), + _ => (), + } + t.super_visit_with(self) + } + } + + // Pessimistic: if any of the parameters have where bounds + // don't allow this impl to be used. + self.predicates.iter().all(|(clause, _)| { + match clause.kind().skip_binder() { + ClauseKind::Trait(trait_predicate) => { + // In a `T: Trait`, if the rhs bound does not contain any generic params + // or 'static lifetimes, then it cannot transitively cause such requirements, + // considering we apply the fully-generic-for-reflection rules to any impls for + // that trait, too. + if matches!(trait_predicate.self_ty().kind(), ty::Param(_)) + && trait_predicate.trait_ref.args[1..] + .iter() + .all(|arg| arg.visit_with(&mut ParamChecker).is_continue()) + { + return true; + } + } + ClauseKind::RegionOutlives(_) + | ClauseKind::TypeOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::ConstEvaluatable(_) + | ClauseKind::HostEffect(_) + | ClauseKind::UnstableFeature(_) => {} + } + clause.visit_with(&mut ParamChecker).is_continue() + }) + } } /// `[const]` bounds for a given item. This is represented using a struct much like diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 37871aad4a775..639a038471ec0 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -15,6 +15,7 @@ use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::num::NonZero; +use std::ops::ControlFlow; use std::ptr::NonNull; use std::{fmt, iter, str}; @@ -278,6 +279,57 @@ pub struct ImplTraitHeader<'tcx> { pub constness: hir::Constness, } +impl<'tcx> ImplTraitHeader<'tcx> { + /// For trait impls, checks whether the type and trait only have generic lifetime parameters in their + /// arguments and only use any generic param once. + /// This is a pessimistic analysis, so it will reject projection types (except for weak aliases) + /// and other types that may be actually ok. We can allow more in the future. + pub fn is_fully_generic_for_reflection(self) -> bool { + #[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) + } + } + self.trait_ref.instantiate_identity().visit_with(&mut ParamFinder::default()).is_continue() + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] #[derive(TypeFoldable, TypeVisitable, Default)] pub enum Asyncness { diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 3baeb7141de50..6f5fe7aefd16e 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -557,6 +557,12 @@ impl<'tcx> UpcastFrom, TypeOutlivesPredicate<'tcx>> for Predicate<' } } +impl<'tcx> UpcastFrom, PolyTypeOutlivesPredicate<'tcx>> for Predicate<'tcx> { + fn upcast_from(from: PolyTypeOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + from.map_bound(|p| PredicateKind::Clause(ClauseKind::TypeOutlives(p))).upcast(tcx) + } +} + impl<'tcx> UpcastFrom, ProjectionPredicate<'tcx>> for Predicate<'tcx> { fn upcast_from(from: ProjectionPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { ty::Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(from))).upcast(tcx) 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 63f246db8a5fb..581d5f8e4da3a 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`. /// @@ -525,6 +530,10 @@ where let cx = self.cx(); let trait_def_id = goal.predicate.trait_def_id(cx); + if let TypingMode::Reflection = self.typing_mode() { + return; + } + // N.B. When assembling built-in candidates for lang items that are also // `auto` traits, then the auto trait candidate that is assembled in // `consider_auto_trait_candidate` MUST be disqualified to remain sound. @@ -617,6 +626,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), } }; @@ -798,6 +810,10 @@ where return; } + if let TypingMode::Reflection = self.typing_mode() { + return; + } + let self_ty = goal.predicate.self_ty(); let bounds = match self_ty.kind() { ty::Bool @@ -930,6 +946,7 @@ where TypingMode::Coherence => return, TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} } @@ -993,6 +1010,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..e94697d99881d 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), }, @@ -424,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/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..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 @@ -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( @@ -950,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/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 73044b7943aeb..977cbcd56a449 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 003841f3af3fd..b3051501a4ec2 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) @@ -841,6 +850,39 @@ 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 self_ty = goal.predicate.self_ty(); + let ty = goal.predicate.trait_ref.args.type_at(1); + match self_ty.kind() { + ty::Dynamic(_bounds, lifetime) => { + ecx.add_goal( + GoalSource::Misc, + goal.with(cx, ty::OutlivesPredicate(ty, lifetime)), + ); + 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 @@ -1330,6 +1372,7 @@ where TypingMode::Coherence => return, TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => {} } @@ -1504,6 +1547,7 @@ where } TypingMode::Coherence | TypingMode::PostAnalysis + | TypingMode::Reflection | TypingMode::Borrowck { defining_opaque_types: _ } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {} } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0f248925602ff..8c38c14338495 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2319,6 +2319,7 @@ symbols! { truncf32, truncf64, truncf128, + try_as_dyn, try_blocks, try_blocks_heterogeneous, try_capture, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 62572694de326..3fa1b60c33659 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 3f4c75a5b133a..b4f7f91ac8671 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -139,7 +139,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) @@ -403,7 +403,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 5bbbad0b5bbfa..7d8d538ecf918 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -950,6 +950,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/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index f5bf74a799192..b31d6889765f3 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 @@ -1439,4 +1444,32 @@ 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(_) => {} + // 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::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 4f65b30775ed0..ff76a7f14bc29 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), }) } @@ -1332,4 +1334,31 @@ 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>> { + let tcx = self.tcx(); + + let mut obligations = PredicateObligations::new(); + + let self_ty = obligation.predicate.self_ty(); + let ty = obligation.predicate.map_bound(|p| p.trait_ref.args.type_at(1)); + + match *self_ty.skip_binder().kind() { + ty::Dynamic(_bounds, lifetime) => { + obligations.push( + obligation.with(tcx, ty.map_bound(|ty| ty::OutlivesPredicate(ty, lifetime))), + ); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + + _ => {} + } + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4469afc3a4ebf..6a4aa73af847b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -19,7 +19,7 @@ use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::relate::TypeRelation; -use rustc_infer::traits::{PredicateObligations, TraitObligation}; +use rustc_infer::traits::{ImplSource, PredicateObligations, TraitObligation}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds}; @@ -278,6 +278,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), + Ok(ImplSource::Builtin(..)) + if matches!(self.infcx.typing_mode(), TypingMode::Reflection) => + { + Err(SelectionError::Unimplemented) + } Ok(candidate) => Ok(Some(candidate)), } } @@ -1281,6 +1286,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match this.confirm_candidate(stack.obligation, candidate.clone()) { Ok(selection) => { debug!(?selection); + if let ImplSource::Builtin(..) = selection + && matches!(this.infcx.typing_mode(), TypingMode::Reflection) + { + return Ok(EvaluatedToErr); + } this.evaluate_predicates_recursively( stack.list(), selection.nested_obligations().into_iter(), @@ -1467,6 +1477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TypingMode::Coherence => {} TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } + | TypingMode::Reflection | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Ok(()), } @@ -1519,6 +1530,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, @@ -2037,6 +2051,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinObjectCandidate | BuiltinUnsizeCandidate | PointerLikeCandidate + | TryAsDynCandidate | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. @@ -2548,6 +2563,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 }) } @@ -2884,6 +2905,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 cbe15eb54787f..d491a6f852287 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 8f446cdfba6d5..3238e4d990f5b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -367,6 +367,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/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/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..e49b226d86395 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -907,12 +907,67 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() } -/// Returns `Some(&U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. +/// Trait that is automatically implemented for all `dyn Trait<'b, C> + 'a` without assoc type bounds. +/// It implicitly adds `T: 'a` requirements, which is otherwise not representable on `Pointee`. +/// +/// This is required for `try_as_dyn` to be able to soundly convert non-static +/// types to `dyn Trait`. +/// +/// Note: these requirements are sufficient for soundness, but it is unclear +/// if they are all necessary. We may be able to lift some requirements in favor +/// of more precise ones. +/// +#[unstable(feature = "try_as_dyn", issue = "144361")] +#[lang = "try_as_dyn"] +#[rustc_deny_explicit_impl] +pub trait TryAsDynCompatible: ptr::Pointee> {} + +/// Returns `Some(&U)` if `T` can be coerced to the dyn trait type `U`. Otherwise, it returns `None`. +/// +/// # Run-time failures +/// +/// There are multiple ways to get a `None`, and you need to manually analyze which one it is, as the +/// compiler does not provide any help here. +/// +/// * `T` does not implement `Trait` at all, +/// * `T`'s impl for `Trait` is not fully generic, +/// * `T`'s impl for `Trait` is a builtin impl (e.g. `dyn Debug` implements `Debug`) +/// +/// ## Fully generic impls +/// +/// `try_as_dyn` does not have access to lifetime information, thus it cannot differentiate between +/// `'static`, other lifetimes, and can't reason about outlives bounds on impls. Thus we can only accept +/// impls that do not have `'static` lifetimes, or outlives bounds of any kind. You can have simple +/// trait bounds, and the compiler will transitively only use impls of those simple trait bounds that satisfy +/// the same rules as the main trait you're converting to. +/// +/// An example of a legal impl is: +/// +/// ```rust +/// # trait Trait<'a, T> {} +/// # struct Type<'b, U>(&'b U); +/// # use std::fmt::{Debug, Display}; +/// impl<'a, 'b, T: Debug, U: Display> Trait<'a, T> for Type<'b, U> {} +/// ``` +/// +/// Impls without generic parameters at all are also legal, as long as they contain no `'static` lifetimes. +/// +/// ## Builtin impls +/// +/// Builtin impls (like `impl Debug for dyn Debug`) have various obscure rules and often are not fully generic. +/// To simplify reasoning about what is allowed and what not, all builtin impls are rejected and will neither +/// directly nor indirectly contribute to a `Some` result. /// /// # Compile-time failures -/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. +/// Determining whether `T` can be coerced to the dyn trait type `U` requires compiler trait resolution. /// In some cases, that resolution can exceed the recursion limit, /// and compilation will fail instead of this function returning `None`. +/// +/// The input type `T` must outlive the lifetime `'a` on the `dyn Trait + 'a`. +/// This is basically the same rule that forbids `let x: &dyn Trait + 'static = &&some_local_variable;` +/// So if you see borrow check errors around `try_as_dyn`, think about whether a normal unsizing +/// coercion would be possible at all if you were using concrete types or had bounds on the input type. +/// /// # Examples /// /// ```rust @@ -942,12 +997,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, ->( - t: &T, -) -> Option<&U> { +pub const fn try_as_dyn + ?Sized>(t: &T) -> Option<&U> { let vtable: Option> = const { intrinsics::vtable_for::() }; match vtable { Some(dyn_metadata) => { @@ -962,45 +1012,10 @@ pub const fn try_as_dyn< /// Returns `Some(&mut U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. /// -/// # Compile-time failures -/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. -/// In some cases, that resolution can exceed the recursion limit, -/// and compilation will fail instead of this function returning `None`. -/// # Examples -/// -/// ```rust -/// #![feature(try_as_dyn)] -/// -/// use core::any::try_as_dyn_mut; -/// -/// trait Animal { -/// fn speak(&self) -> &'static str; -/// } -/// -/// struct Dog; -/// impl Animal for Dog { -/// fn speak(&self) -> &'static str { "woof" } -/// } -/// -/// struct Rock; // does not implement Animal -/// -/// let mut dog = Dog; -/// let mut rock = Rock; -/// -/// let as_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut dog); -/// assert_eq!(as_animal.unwrap().speak(), "woof"); -/// -/// let not_an_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut rock); -/// assert!(not_an_animal.is_none()); -/// ``` +/// See documentation of [try_as_dyn] for details about the behaviour and limitations. #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] -pub const fn try_as_dyn_mut< - T: Any + 'static, - U: ptr::Pointee> + ?Sized + 'static, ->( - t: &mut T, -) -> Option<&mut U> { +pub const fn try_as_dyn_mut + ?Sized>(t: &mut T) -> Option<&mut U> { let vtable: Option> = const { intrinsics::vtable_for::() }; match vtable { Some(dyn_metadata) => { diff --git a/tests/ui/any/any_static.next.stderr b/tests/ui/any/any_static.next.stderr new file mode 100644 index 0000000000000..9765ab43691f6 --- /dev/null +++ b/tests/ui/any/any_static.next.stderr @@ -0,0 +1,16 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/any_static.rs:17:35 + | +LL | fn extend(a: &Payload) -> &'static Payload { + | - - let's call the lifetime of this reference `'1` + | | + | `a` is a reference that is only valid in the function body +LL | let b: &(dyn Any + 'static) = try_as_dyn::<&Payload, dyn Any + 'static>(&a).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `a` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/any/any_static.old.stderr b/tests/ui/any/any_static.old.stderr new file mode 100644 index 0000000000000..9765ab43691f6 --- /dev/null +++ b/tests/ui/any/any_static.old.stderr @@ -0,0 +1,16 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/any_static.rs:17:35 + | +LL | fn extend(a: &Payload) -> &'static Payload { + | - - let's call the lifetime of this reference `'1` + | | + | `a` is a reference that is only valid in the function body +LL | let b: &(dyn Any + 'static) = try_as_dyn::<&Payload, dyn Any + 'static>(&a).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `a` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/any/any_static.rs b/tests/ui/any/any_static.rs new file mode 100644 index 0000000000000..14ecfe3b07ce5 --- /dev/null +++ b/tests/ui/any/any_static.rs @@ -0,0 +1,21 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +#![feature(try_as_dyn)] + +use std::any::{Any, try_as_dyn}; + +type Payload = Box; + +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 { + let b: &(dyn Any + 'static) = try_as_dyn::<&Payload, dyn Any + 'static>(&a).unwrap(); + //~^ ERROR: borrowed data escapes outside of function + let c: &&'static Payload = b.downcast_ref::<&'static Payload>().unwrap(); + *c +} diff --git a/tests/ui/any/hrtb.next.stderr b/tests/ui/any/hrtb.next.stderr new file mode 100644 index 0000000000000..5ba8cc04817a7 --- /dev/null +++ b/tests/ui/any/hrtb.next.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb.rs:14:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb.old.stderr b/tests/ui/any/hrtb.old.stderr new file mode 100644 index 0000000000000..5ba8cc04817a7 --- /dev/null +++ b/tests/ui/any/hrtb.old.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb.rs:14:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb.rs b/tests/ui/any/hrtb.rs new file mode 100644 index 0000000000000..20b19a1533b89 --- /dev/null +++ b/tests/ui/any/hrtb.rs @@ -0,0 +1,18 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +trait Foo<'a, 'b> {} + +trait Bar {} +impl Foo<'a, 'b> + ?Sized> Bar for Option<*const T> {} + +const _: () = { + let x: Option<*const dyn for<'a> Foo<'a, 'a>> = None; + let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + //~^ ERROR: `Option::unwrap()` on a `None` value +}; + +fn main() {} diff --git a/tests/ui/any/hrtb2.next.stderr b/tests/ui/any/hrtb2.next.stderr new file mode 100644 index 0000000000000..0b22387de490b --- /dev/null +++ b/tests/ui/any/hrtb2.next.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb2.rs:14:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb2.old.stderr b/tests/ui/any/hrtb2.old.stderr new file mode 100644 index 0000000000000..0b22387de490b --- /dev/null +++ b/tests/ui/any/hrtb2.old.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/hrtb2.rs:14:25 + | +LL | let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/hrtb2.rs b/tests/ui/any/hrtb2.rs new file mode 100644 index 0000000000000..fefe88aa7cdb8 --- /dev/null +++ b/tests/ui/any/hrtb2.rs @@ -0,0 +1,18 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +trait Foo<'a> {} + +trait Bar {} +impl Bar for Option<*const T> where T: for<'a> Foo<'a> {} + +const _: () = { + let x: Option<*const dyn Foo<'_>> = None; + let _dy: &dyn Bar = try_as_dyn(&x).unwrap(); + //~^ ERROR: `Option::unwrap()` on a `None` value +}; + +fn main() {} diff --git a/tests/ui/any/non_static.rs b/tests/ui/any/non_static.rs new file mode 100644 index 0000000000000..ad04b7f475e0b --- /dev/null +++ b/tests/ui/any/non_static.rs @@ -0,0 +1,88 @@ +//@ 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()); +}; + +trait Trait3<'a> {} + +impl Trait3<'static> for () {} +const _: () = { + assert!(std::any::try_as_dyn::<_, dyn Trait3<'_>>(&()).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/reject_manual_impl.rs b/tests/ui/any/reject_manual_impl.rs new file mode 100644 index 0000000000000..538a8ff456129 --- /dev/null +++ b/tests/ui/any/reject_manual_impl.rs @@ -0,0 +1,29 @@ +#![feature(try_as_dyn)] + +use std::any::TryAsDynCompatible; + +struct Foo(dyn Iterator); + +impl TryAsDynCompatible for Foo {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +struct Bar(dyn Iterator); + +impl TryAsDynCompatible for Bar {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +struct Baz; + +impl TryAsDynCompatible for Baz {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +trait Trait {} + +impl TryAsDynCompatible for dyn Trait {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted + +impl TryAsDynCompatible for dyn Iterator {} +//~^ ERROR: explicit impls for the `TryAsDynCompatible` trait are not permitted +//~| ERROR: only traits defined in the current crate can be implemented for arbitrary types + +fn main() {} diff --git a/tests/ui/any/reject_manual_impl.stderr b/tests/ui/any/reject_manual_impl.stderr new file mode 100644 index 0000000000000..b4be5cbf9d93c --- /dev/null +++ b/tests/ui/any/reject_manual_impl.stderr @@ -0,0 +1,47 @@ +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:7:1 + | +LL | impl TryAsDynCompatible for Foo {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:12:1 + | +LL | impl TryAsDynCompatible for Bar {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:17:1 + | +LL | impl TryAsDynCompatible for Baz {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:22:1 + | +LL | impl TryAsDynCompatible for dyn Trait {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0322]: explicit impls for the `TryAsDynCompatible` trait are not permitted + --> $DIR/reject_manual_impl.rs:25:1 + | +LL | impl TryAsDynCompatible for dyn Iterator {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `TryAsDynCompatible` not allowed + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/reject_manual_impl.rs:25:1 + | +LL | impl TryAsDynCompatible for dyn Iterator {} + | ^^^^^-----------------------^^^^^------------------------ + | | | + | | `dyn Iterator` is not defined in the current crate + | `u32` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0117, E0322. +For more information about an error, try `rustc --explain E0117`. diff --git a/tests/ui/any/static_method_bound.rs b/tests/ui/any/static_method_bound.rs new file mode 100644 index 0000000000000..b2cc77e4d9cfe --- /dev/null +++ b/tests/ui/any/static_method_bound.rs @@ -0,0 +1,33 @@ +#![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 { + let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); + //~^ ERROR: borrowed data escapes outside of function + 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..152672d024ab1 --- /dev/null +++ b/tests/ui/any/static_method_bound.stderr @@ -0,0 +1,16 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/static_method_bound.rs:30:37 + | +LL | fn extend(a: &Payload) -> &'static Payload { + | - - let's call the lifetime of this reference `'1` + | | + | `a` is a reference that is only valid in the function body +LL | let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `a` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/any/try_as_dyn.rs b/tests/ui/any/try_as_dyn.rs index ee220f797ced9..17a5fb45bcb6e 100644 --- a/tests/ui/any/try_as_dyn.rs +++ b/tests/ui/any/try_as_dyn.rs @@ -1,3 +1,5 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver //@ run-pass #![feature(try_as_dyn)] @@ -7,7 +9,7 @@ use std::fmt::Debug; fn debug_format_with_try_as_dyn(t: &T) -> String { match std::any::try_as_dyn::<_, dyn Debug>(t) { Some(d) => format!("{d:?}"), - None => "default".to_string() + None => "default".to_string(), } } @@ -16,7 +18,7 @@ fn main() { #[allow(dead_code)] #[derive(Debug)] struct A { - index: usize + index: usize, } let a = A { index: 42 }; let result = debug_format_with_try_as_dyn(&a); diff --git a/tests/ui/any/try_as_dyn_builtin_impl.rs b/tests/ui/any/try_as_dyn_builtin_impl.rs new file mode 100644 index 0000000000000..d1410eb847e90 --- /dev/null +++ b/tests/ui/any/try_as_dyn_builtin_impl.rs @@ -0,0 +1,47 @@ +#![feature(try_as_dyn)] +//@ run-fail +//@ revisions: next old +//@[next] compile-flags: -Znext-solver + +use std::any::{Any, try_as_dyn}; + +type Payload = Box; + +trait Outlives<'b>: 'b {} + +trait WithLt { + type Ref; +} +impl<'a> WithLt for dyn for<'b> Outlives<'b> + 'a { + type Ref = &'a Payload; +} + +struct Thing(T::Ref); + +trait Trait { + fn get(&self) -> &'static Payload; +} +impl Trait for Thing +where + T: WithLt + for<'b> Outlives<'b> + ?Sized, +{ + fn get(&self) -> &'static Payload { + let x: &::Ref = &self.0; + let y: &(dyn Any + 'static) = x; + let z: &&'static Payload = y.downcast_ref().unwrap(); + *z + } +} + +fn extend<'a>(payload: &'a Payload) -> &'static Payload { + let thing: Thing Outlives<'b> + 'a> = Thing(payload); + let dy: &dyn Trait = try_as_dyn(&thing).unwrap(); + dy.get() +} + +fn main() { + let payload: Box = Box::new(Box::new(1)); + let wrong: &'static Payload = extend(&*payload); + drop(payload); + println!("{wrong}"); +} diff --git a/tests/ui/any/try_as_dyn_generic_impl.rs b/tests/ui/any/try_as_dyn_generic_impl.rs new file mode 100644 index 0000000000000..623056efb64e9 --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_impl.rs @@ -0,0 +1,36 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +#![feature(try_as_dyn)] +//@ check-pass + +use std::any::try_as_dyn; + +struct Thing(T); +trait Trait {} +impl Trait for Thing {} + +const _: () = { + let thing = Thing(1); + assert!(try_as_dyn::<_, dyn Trait>(&thing).is_some()); +}; + +struct Thing2(T); +impl Trait for Thing2 {} + +const _: () = { + let thing = Thing2(1); + assert!(try_as_dyn::<_, dyn Trait>(&thing).is_some()); +}; + +trait Trait2 {} +impl<'a, 'b> Trait2 for &'a &'b () {} + +struct Thing3(T); +impl Trait for Thing3 {} + +const _: () = { + let thing = Thing3(&&()); + assert!(try_as_dyn::<_, dyn Trait2>(&thing).is_none()); +}; + +fn main() {} diff --git a/tests/ui/any/try_as_dyn_generic_trait.next.stderr b/tests/ui/any/try_as_dyn_generic_trait.next.stderr new file mode 100644 index 0000000000000..55c9b004862f6 --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_trait.next.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/try_as_dyn_generic_trait.rs:21:51 + | +LL | let convert: &dyn Convert<&'static Payload> = try_as_dyn(&payload).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/try_as_dyn_generic_trait.old.stderr b/tests/ui/any/try_as_dyn_generic_trait.old.stderr new file mode 100644 index 0000000000000..55c9b004862f6 --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_trait.old.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: called `Option::unwrap()` on a `None` value + --> $DIR/try_as_dyn_generic_trait.rs:21:51 + | +LL | let convert: &dyn Convert<&'static Payload> = try_as_dyn(&payload).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/any/try_as_dyn_generic_trait.rs b/tests/ui/any/try_as_dyn_generic_trait.rs new file mode 100644 index 0000000000000..47fdb49c2cf34 --- /dev/null +++ b/tests/ui/any/try_as_dyn_generic_trait.rs @@ -0,0 +1,25 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +type Payload = *const i32; + +trait Convert { + fn convert(&self) -> &T; +} + +impl Convert for T { + fn convert(&self) -> &T { + self + } +} + +const _: () = { + let payload: Payload = std::ptr::null(); + let convert: &dyn Convert<&'static Payload> = try_as_dyn(&payload).unwrap(); + //~^ ERROR: `Option::unwrap()` on a `None` value +}; + +fn main() {} diff --git a/tests/ui/any/try_as_dyn_mut.rs b/tests/ui/any/try_as_dyn_mut.rs index ff7baa32ea874..464221fef20f7 100644 --- a/tests/ui/any/try_as_dyn_mut.rs +++ b/tests/ui/any/try_as_dyn_mut.rs @@ -1,3 +1,5 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver //@ run-pass #![feature(try_as_dyn)] @@ -7,7 +9,7 @@ use std::fmt::{Error, Write}; fn try_as_dyn_mut_write(t: &mut T, s: &str) -> Result<(), Error> { match std::any::try_as_dyn_mut::<_, dyn Write>(t) { Some(w) => w.write_str(s), - None => Ok(()) + None => Ok(()), } } diff --git a/tests/ui/any/try_as_dyn_soundness_test1.rs b/tests/ui/any/try_as_dyn_soundness_test1.rs index 0772e9a2d77ee..6bc6bc5a66b97 100644 --- a/tests/ui/any/try_as_dyn_soundness_test1.rs +++ b/tests/ui/any/try_as_dyn_soundness_test1.rs @@ -1,19 +1,15 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver //@ run-pass #![feature(try_as_dyn)] use std::any::try_as_dyn; -trait Trait { +trait Trait {} -} - -impl Trait for for<'a> fn(&'a Box) { - -} - -fn store(_: &'static Box) { +impl Trait for for<'a> fn(&'a Box) {} -} +fn store(_: &'static Box) {} fn main() { let fn_ptr: fn(&'static Box) = store; diff --git a/tests/ui/any/try_as_dyn_soundness_test2.rs b/tests/ui/any/try_as_dyn_soundness_test2.rs index c16b50d0261ed..a65fe2955374f 100644 --- a/tests/ui/any/try_as_dyn_soundness_test2.rs +++ b/tests/ui/any/try_as_dyn_soundness_test2.rs @@ -1,14 +1,12 @@ +//@ revisions: next old +//@[next] compile-flags: -Znext-solver //@ run-pass #![feature(try_as_dyn)] use std::any::try_as_dyn; -trait Trait { +trait Trait {} -} - -impl Trait fn(&'a Box)> for () { - -} +impl Trait fn(&'a Box)> for () {} fn main() { let dt = try_as_dyn::<_, dyn Trait)>>(&());