diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index dc15cc3eb5539..e1ec0e7c5c48d 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -179,16 +179,39 @@ impl> Elaborator { ), }; } - // `T: [const] Trait` implies `T: [const] Supertrait`. - ty::ClauseKind::HostEffect(data) => self.extend_deduped( - cx.explicit_implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| { - elaboratable.child( - trait_ref - .to_host_effect_clause(cx, data.constness) - .instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)), - ) - }), - ), + ty::ClauseKind::HostEffect(data) => { + // `T: [const] Trait` implies `T: [const] Supertrait`. + self.extend_deduped( + cx.explicit_implied_const_bounds(data.def_id()).iter_identity().map( + |trait_ref| { + elaboratable.child( + trait_ref + .to_host_effect_clause(cx, data.constness) + .instantiate_supertrait( + cx, + bound_clause.rebind(data.trait_ref), + ), + ) + }, + ), + ); + + // `Adt: [const] Trait` implies each field also implements `[const] Trait` + let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct); + if data.def_id() == destruct_def_id + && let ty::Adt(adt_def, args) = data.self_ty().kind() + { + self.extend_deduped(adt_def.all_field_tys(cx).iter_instantiated(cx, args).map( + |ty| { + elaboratable.child( + bound_clause + .rebind(data.trait_ref.with_self_ty(cx, ty)) + .to_host_effect_clause(cx, data.constness), + ) + }, + )); + } + } ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { // We know that `T: 'a` for some type `T`. We can // often elaborate this. For example, if we know that diff --git a/library/core/src/option.rs b/library/core/src/option.rs index ed070fbd22746..006f7deca912b 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2147,9 +2147,7 @@ const fn expect_failed(msg: &str) -> ! { #[rustc_const_unstable(feature = "const_try", issue = "74935")] impl const Clone for Option where - // FIXME(const_hack): the T: ~const Destruct should be inferred from the Self: ~const Destruct in clone_from. - // See https://github.com/rust-lang/rust/issues/144207 - T: ~const Clone + ~const Destruct, + T: ~const Clone, { #[inline] fn clone(&self) -> Self { @@ -2160,7 +2158,10 @@ where } #[inline] - fn clone_from(&mut self, source: &Self) { + fn clone_from(&mut self, source: &Self) + where + Self: ~const Destruct, + { match (self, source) { (Some(to), Some(from)) => to.clone_from(from), (to, from) => *to = from.clone(), diff --git a/tests/ui/traits/const-traits/conditionally-const-option-impl-clone.rs b/tests/ui/traits/const-traits/conditionally-const-option-impl-clone.rs new file mode 100644 index 0000000000000..6a60993e3747c --- /dev/null +++ b/tests/ui/traits/const-traits/conditionally-const-option-impl-clone.rs @@ -0,0 +1,50 @@ +// Demonstrates that `impl const Clone for Option` does not require const_hack bounds. +// See issue #144207. +//@ revisions: next old +//@ [next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(const_trait_impl, const_destruct)] + +use std::marker::Destruct; + +#[const_trait] +pub trait CloneLike: Sized { + fn clone(&self) -> Self; + + fn clone_from(&mut self, source: &Self) + where + Self: [const] Destruct, + { + *self = source.clone() + } +} + +enum OptionLike { + None, + Some(T), +} + +impl const CloneLike for OptionLike +where + T: [const] CloneLike, +{ + fn clone(&self) -> Self { + match self { + Self::Some(x) => Self::Some(x.clone()), + Self::None => Self::None, + } + } + + fn clone_from(&mut self, source: &Self) + where + Self: [const] Destruct, + { + match (self, source) { + (Self::Some(to), Self::Some(from)) => to.clone_from(from), + (to, from) => *to = from.clone(), + } + } +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.rs b/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.rs new file mode 100644 index 0000000000000..a8920694baecd --- /dev/null +++ b/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.rs @@ -0,0 +1,41 @@ +//@ revisions: next old +//@ [next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(const_trait_impl, const_destruct)] + +use std::marker::Destruct; + +const fn ensure_const_destruct(_t: T) {} + +enum Either { + Left(T), + Right(U), +} + +struct Foo(Either, Option); + +struct Bar(T, Result); + +const fn f(x: T) +where + Option: [const] Destruct, +{ + ensure_const_destruct(x); +} + +const fn g(x: Result) +where + Either: [const] Destruct, +{ + ensure_const_destruct(x); +} + +const fn h(x: Foo) +where + Bar: [const] Destruct, +{ + ensure_const_destruct(x); +} + +fn main() {}