diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 8e2c138282af3..5b5abffbd494f 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -400,6 +400,19 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { ty::BoundConstness::Const } }; + + // Only elaborate host-effect destruct obligations when comparing impl method bounds + // against the corresponding trait method bounds. + let elaborate = matches!( + tcx.hir_node_by_def_id(body_id), + hir::Node::ImplItem(hir::ImplItem { + trait_item_def_id: Some(did), + .. + }) if *did == callee, + ); + let param_env = + if elaborate { param_env.elaborate_host_effect_destruct(tcx) } else { param_env }; + let const_conditions = ocx.normalize(&ObligationCause::misc(call_span, body_id), param_env, const_conditions); ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, span)| { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 48bb45de53eb4..d5f419779f50e 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -922,10 +922,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ObligationCauseCode::WhereClause(callee_did, pred_span) }, ); + + // Only elaborate host-effect destruct obligations when comparing impl method + // bounds against the corresponding trait method bounds. + let elaborate = matches!( + self.tcx.hir_node_by_def_id(self.body_id), + hir::Node::ImplItem(hir::ImplItem { + trait_item_def_id: Some(did), + .. + }) if *did == callee_did, + ); + let param_env = if elaborate { + self.param_env.elaborate_host_effect_destruct(self.tcx) + } else { + self.param_env + }; + self.register_predicate(Obligation::new( self.tcx, cause, - self.param_env, + param_env, cond.to_host_effect_clause(self.tcx, host), )); } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index df82c7a826be9..56ea42c2d8578 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -220,6 +220,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self.is_manually_drop() } + fn all_fields(self) -> ty::EarlyBinder<'tcx, impl IntoIterator> { + ty::EarlyBinder::bind(self.all_fields()) + } + fn all_field_tys( self, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index db56082c71aaa..1c0cbbecc13fc 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -146,6 +146,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AllocId = crate::mir::interpret::AllocId; type Pat = Pattern<'tcx>; type PatList = &'tcx List>; + type Visibility = Visibility; type Safety = hir::Safety; type Abi = ExternAbi; type Const = ty::Const<'tcx>; @@ -238,6 +239,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } + type FieldDef = &'tcx ty::FieldDef; + fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind { match self.def_kind(alias.def_id) { DefKind::AssocTy => { @@ -806,6 +809,12 @@ impl<'tcx> rustc_type_ir::inherent::Abi> for ExternAbi { } } +impl<'tcx> rustc_type_ir::inherent::Visibility> for Visibility { + fn is_public(self) -> bool { + matches!(self, Visibility::Public) + } +} + impl<'tcx> rustc_type_ir::inherent::Safety> for hir::Safety { fn safe() -> Self { hir::Safety::Safe diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0deb2482c6fe9..1da18a46f30d7 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -59,7 +59,9 @@ pub use rustc_type_ir::fast_reject::DeepRejectCtxt; )] use rustc_type_ir::inherent; pub use rustc_type_ir::relate::VarianceDiagInfo; -pub use rustc_type_ir::solve::SizedTraitKind; +pub use rustc_type_ir::solve::{ + DestructConstCondition, SizedTraitKind, const_conditions_for_destruct, +}; pub use rustc_type_ir::*; #[allow(hidden_glob_reexports, unused_imports)] use rustc_type_ir::{InferCtxtLike, Interner}; @@ -1009,6 +1011,14 @@ impl<'tcx> ParamEnv<'tcx> { pub fn and>>(self, value: T) -> ParamEnvAnd<'tcx, T> { ParamEnvAnd { param_env: self, value } } + + pub fn elaborate_host_effect_destruct(self, tcx: TyCtxt<'tcx>) -> Self { + let caller_bounds = tcx.mk_clauses_from_iter( + ty::elaborate::elaborate(tcx, self.caller_bounds.iter()) + .elaborate_host_effect_destruct(), + ); + ParamEnv { caller_bounds } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] @@ -1422,6 +1432,16 @@ impl<'tcx> FieldDef { } } +impl<'tcx> rustc_type_ir::inherent::FieldDef> for &'tcx FieldDef { + fn def_id(self) -> DefId { + self.did + } + + fn visibility(self) -> ty::Visibility { + self.vis + } +} + #[derive(Debug, PartialEq, Eq)] pub enum ImplOverlapKind { /// These impls are always allowed to overlap. diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index faa86734d08c2..b66ced553e0df 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -15,7 +15,7 @@ use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; use crate::delegate::SolverDelegate; -use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution}; +use crate::solve::{EvalCtxt, Goal, NoSolution}; // Calculates the constituent types of a type for `auto trait` purposes. #[instrument(level = "trace", skip(ecx), ret)] @@ -738,87 +738,6 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( } } -// NOTE: Keep this in sync with `evaluate_host_effect_for_destruct_goal` in -// the old solver, for as long as that exists. -pub(in crate::solve) fn const_conditions_for_destruct( - cx: I, - self_ty: I::Ty, -) -> Result>, NoSolution> { - let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct); - - match self_ty.kind() { - // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it. - ty::Adt(adt_def, _) if adt_def.is_manually_drop() => Ok(vec![]), - - // An ADT is `[const] Destruct` only if all of the fields are, - // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`. - ty::Adt(adt_def, args) => { - let mut const_conditions: Vec<_> = adt_def - .all_field_tys(cx) - .iter_instantiated(cx, args) - .map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty])) - .collect(); - match adt_def.destructor(cx) { - // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`. - Some(AdtDestructorKind::NotConst) => return Err(NoSolution), - // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold. - Some(AdtDestructorKind::Const) => { - let drop_def_id = cx.require_lang_item(TraitSolverLangItem::Drop); - let drop_trait_ref = ty::TraitRef::new(cx, drop_def_id, [self_ty]); - const_conditions.push(drop_trait_ref); - } - // No `Drop` impl, no need to require anything else. - None => {} - } - Ok(const_conditions) - } - - ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => { - Ok(vec![ty::TraitRef::new(cx, destruct_def_id, [ty])]) - } - - ty::Tuple(tys) => Ok(tys - .iter() - .map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty])) - .collect()), - - // Trivially implement `[const] Destruct` - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) - | ty::Error(_) => Ok(vec![]), - - // Coroutines and closures could implement `[const] Drop`, - // but they don't really need to right now. - ty::Closure(_, _) - | ty::CoroutineClosure(_, _) - | ty::Coroutine(_, _) - | ty::CoroutineWitness(_, _) => Err(NoSolution), - - // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop` - // if their inner type implements it. - ty::UnsafeBinder(_) => Err(NoSolution), - - ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => { - Err(NoSolution) - } - - ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - panic!("unexpected type `{self_ty:?}`") - } - } -} - /// Assemble a list of predicates that would be present on a theoretical /// user impl for an object type. These predicates must be checked any time /// we assemble a built-in object candidate for an object type, since they 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 9a22bf58c0352..60e59ea8453f5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -4,8 +4,8 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; +use rustc_type_ir::solve::{DestructConstCondition, SizedTraitKind, const_conditions_for_destruct}; use rustc_type_ir::{self as ty, Interner, elaborate}; use tracing::instrument; @@ -343,7 +343,11 @@ where let cx = ecx.cx(); let self_ty = goal.predicate.self_ty(); - let const_conditions = structural_traits::const_conditions_for_destruct(cx, self_ty)?; + let const_conditions = match const_conditions_for_destruct(cx, self_ty, false) { + DestructConstCondition::Trivial { .. } => vec![], + DestructConstCondition::Never => return Err(NoSolution), + DestructConstCondition::Structural(trait_refs) => trait_refs, + }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.add_goals( diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index d694a092853af..2eaca3de6f43d 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -311,95 +311,28 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>( } } -// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver. fn evaluate_host_effect_for_destruct_goal<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, ) -> Result>, EvaluationFailure> { let tcx = selcx.tcx(); - let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, obligation.cause.span); let self_ty = obligation.predicate.self_ty(); - let const_conditions = match *self_ty.kind() { - // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it. - ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![], - - // An ADT is `[const] Destruct` only if all of the fields are, - // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`. - ty::Adt(adt_def, args) => { - let mut const_conditions: ThinVec<_> = adt_def - .all_fields() - .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)])) - .collect(); - match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) { - // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`. - Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution), - // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold. - Some(hir::Constness::Const) => { - let drop_def_id = tcx.require_lang_item(LangItem::Drop, obligation.cause.span); - let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]); - const_conditions.push(drop_trait_ref); - } - // No `Drop` impl, no need to require anything else. - None => {} - } - const_conditions - } - - ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => { - thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])] - } - - ty::Tuple(tys) => { - tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect() - } - - // Trivially implement `[const] Destruct` - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) - | ty::Error(_) => thin_vec![], - - // Coroutines and closures could implement `[const] Drop`, - // but they don't really need to right now. - ty::Closure(_, _) - | ty::CoroutineClosure(_, _) - | ty::Coroutine(_, _) - | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution), - - // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop` - // if their inner type implements it. - ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution), - - ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => { - return Err(EvaluationFailure::NoSolution); - } - - ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - panic!("unexpected type `{self_ty:?}`") - } - }; - - Ok(const_conditions - .into_iter() - .map(|trait_ref| { - obligation.with( - tcx, - ty::Binder::dummy(trait_ref) - .to_host_effect_clause(tcx, obligation.predicate.constness), - ) - }) - .collect()) + let const_conditions = ty::const_conditions_for_destruct(tcx, self_ty, false); + match const_conditions { + ty::DestructConstCondition::Trivial { .. } => Ok(thin_vec![]), + ty::DestructConstCondition::Never => Err(EvaluationFailure::NoSolution), + ty::DestructConstCondition::Structural(trait_refs) => Ok(trait_refs + .into_iter() + .map(|trait_ref| { + obligation.with( + tcx, + ty::Binder::dummy(trait_ref) + .to_host_effect_clause(tcx, obligation.predicate.constness), + ) + }) + .collect()), + } } // NOTE: Keep this in sync with `extract_fn_def_from_const_callable` in the new solver. diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index dc15cc3eb5539..f5fb48bce4cf6 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -6,7 +6,7 @@ use crate::data_structures::HashSet; use crate::inherent::*; use crate::lang_items::TraitSolverLangItem; use crate::outlives::{Component, push_outlives_components}; -use crate::{self as ty, Interner, Upcast as _}; +use crate::{self as ty, Interner, Upcast as _, solve}; /// "Elaboration" is the process of identifying all the predicates that /// are implied by a source predicate. Currently, this basically means @@ -20,6 +20,7 @@ pub struct Elaborator { visited: HashSet>>, mode: Filter, elaborate_sized: ElaborateSized, + elaborate_host_effect_destruct: ElaborateHostEffectDestruct, } enum Filter { @@ -33,6 +34,12 @@ enum ElaborateSized { No, } +#[derive(Eq, PartialEq)] +enum ElaborateHostEffectDestruct { + Yes, + No, +} + /// Describes how to elaborate an obligation into a sub-obligation. pub trait Elaboratable { fn predicate(&self) -> I::Predicate; @@ -91,6 +98,7 @@ pub fn elaborate>( visited: HashSet::default(), mode: Filter::All, elaborate_sized: ElaborateSized::No, + elaborate_host_effect_destruct: ElaborateHostEffectDestruct::No, }; elaborator.extend_deduped(obligations); elaborator @@ -124,6 +132,12 @@ impl> Elaborator { self } + /// Start elaborating `[const] Destruct`. Should only be used in narrowly scoped contexts. + pub fn elaborate_host_effect_destruct(mut self) -> Self { + self.elaborate_host_effect_destruct = ElaborateHostEffectDestruct::Yes; + self + } + fn elaborate(&mut self, elaboratable: &O) { let cx = self.cx; @@ -179,16 +193,43 @@ 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), + ), + ) + }, + ), + ); + + // Elaborate structurally implied obligations: + // e.g., `ADT: [const] Destruct` implies that each public field also implements + // `[const] Destruct`. + if self.elaborate_host_effect_destruct == ElaborateHostEffectDestruct::Yes { + let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct); + if data.def_id() == destruct_def_id { + match solve::const_conditions_for_destruct(cx, data.self_ty(), true) { + solve::DestructConstCondition::Structural(trait_refs) => self + .extend_deduped(trait_refs.into_iter().map(|trait_ref| { + elaboratable.child( + bound_clause + .rebind(trait_ref) + .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/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 1a6c99ce7dec0..21e01aec35ec9 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -203,6 +203,10 @@ pub trait Abi>: Copy + Debug + Hash + Eq { fn is_rust(self) -> bool; } +pub trait Visibility>: Copy + Debug + Hash + Eq { + fn is_public(self) -> bool; +} + pub trait Safety>: Copy + Debug + Hash + Eq { fn safe() -> Self; @@ -606,8 +610,13 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_manually_drop(self) -> bool; - // FIXME: perhaps use `all_fields` and expose `FieldDef`. - fn all_field_tys(self, interner: I) -> ty::EarlyBinder>; + fn all_fields(self) -> ty::EarlyBinder>; + + fn all_field_tys(self, interner: I) -> ty::EarlyBinder> { + self.all_fields().map_bound(move |fields| { + fields.into_iter().map(move |field| interner.type_of(field.def_id()).skip_binder()) + }) + } fn sizedness_constraint( self, @@ -620,6 +629,12 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn destructor(self, interner: I) -> Option; } +pub trait FieldDef: Copy + Debug + Hash + Eq { + fn def_id(self) -> I::DefId; + + fn visibility(self) -> I::Visibility; +} + pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { fn caller_bounds(self) -> impl SliceLike; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e3231244577f6..2ab4f7f0b17c3 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -127,6 +127,7 @@ pub trait Interner: + Eq + TypeVisitable + SliceLike; + type Visibility: Visibility; type Safety: Safety; type Abi: Abi; @@ -191,6 +192,8 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef; + type FieldDef: FieldDef; + fn alias_ty_kind(self, alias: ty::AliasTy) -> ty::AliasTyKind; fn alias_term_kind(self, alias: ty::AliasTerm) -> ty::AliasTermKind; diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index a6571ef261a16..20b8b19335700 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -8,6 +8,7 @@ use derive_where::derive_where; use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use crate::inherent::*; use crate::lang_items::TraitSolverLangItem; use crate::search_graph::PathKind; use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; @@ -368,6 +369,121 @@ pub enum AdtDestructorKind { Const, } +/// Describes under what conditions a type implements `[const] Destruct`, +/// or what can be implied given assuming it does. +pub enum DestructConstCondition { + /// The type never satisfies `[const] Destruct`. + Never, + + /// The type always satisfies `[const] Destruct`. + /// + /// This may be due to: + /// - The type being `ManuallyDrop`, which is always trivially `Destruct`. + /// - All conditions being satisfied statically. + Trivial { + /// True if the type is `ManuallyDrop`, which guarantees `[const] Destruct`. + manually_drop: bool, + }, + + /// The type satisfies `[const] Destruct` conditionally, + /// based on whether certain trait obligations hold for its components. + /// + /// For example: + /// - ADTs whose fields must also implement `[const] Destruct`. + /// - Arrays whose element type must implement `[const] Destruct`. + Structural(Vec>), +} + +pub fn const_conditions_for_destruct( + cx: I, + self_ty: I::Ty, + adt_pub_fields_only: bool, +) -> DestructConstCondition { + let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct); + + match self_ty.kind() { + // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it. + ty::Adt(adt_def, _) if adt_def.is_manually_drop() => { + DestructConstCondition::Trivial { manually_drop: true } + } + + // An ADT is `[const] Destruct` only if all of the fields are, + // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`. + ty::Adt(adt_def, args) => { + let mut const_conditions: Vec<_> = adt_def + .all_fields() + .map_bound(|fields| { + fields.into_iter().filter_map(|field| { + if adt_pub_fields_only && !field.visibility().is_public() { + None + } else { + Some(cx.type_of(field.def_id()).skip_binder()) + } + }) + }) + .iter_instantiated(cx, args) + .map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty])) + .collect(); + match adt_def.destructor(cx) { + // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`. + Some(AdtDestructorKind::NotConst) => return DestructConstCondition::Never, + // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold. + Some(AdtDestructorKind::Const) => { + let drop_def_id = cx.require_lang_item(TraitSolverLangItem::Drop); + let drop_trait_ref = ty::TraitRef::new(cx, drop_def_id, [self_ty]); + const_conditions.push(drop_trait_ref); + } + // No `Drop` impl, no need to require anything else. + None => {} + } + DestructConstCondition::Structural(const_conditions) + } + + ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => { + DestructConstCondition::Structural(vec![ty::TraitRef::new(cx, destruct_def_id, [ty])]) + } + + ty::Tuple(tys) => DestructConstCondition::Structural( + tys.iter().map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty])).collect(), + ), + + // Trivially implement `[const] Destruct` + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Never + | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::Error(_) => DestructConstCondition::Trivial { manually_drop: false }, + + // Coroutines and closures could implement `[const] Drop`, + // but they don't really need to right now. + ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) => DestructConstCondition::Never, + + // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop` + // if their inner type implements it. + ty::UnsafeBinder(_) => DestructConstCondition::Never, + + ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => { + DestructConstCondition::Never + } + + ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + } +} + /// Which sizedness trait - `Sized`, `MetaSized`? `PointeeSized` is omitted as it is removed during /// lowering. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] 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..1283383eec961 --- /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() + } +} + +pub 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.next.stderr b/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.next.stderr new file mode 100644 index 0000000000000..e32b8d8c3b30f --- /dev/null +++ b/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.next.stderr @@ -0,0 +1,62 @@ +error[E0277]: the trait bound `U: [const] Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:41:11 + | +LL | u.f(); + | ^ + | +note: required by a bound in `TraitA::f` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:14:15 + | +LL | fn f(self) + | - required by a bound in this associated function +LL | where +LL | Self: [const] Destruct; + | ^^^^^^^^^^^^^^^^ required by this bound in `TraitA::f` + +error[E0277]: the trait bound `T: const Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:49:27 + | +LL | ensure_const_destruct(x.unwrap()) + | --------------------- ^^^^^^^^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `ensure_const_destruct` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:9:35 + | +LL | const fn ensure_const_destruct(_t: T) {} + | ^^^^^^^^^^^^^^ required by this bound in `ensure_const_destruct` + +error[E0277]: the trait bound `T: [const] Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:64:16 + | +LL | self.0.g() + | ^ + | +note: required by a bound in `TraitB::g` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:56:15 + | +LL | fn g(self) + | - required by a bound in this associated function +LL | where +LL | Self: [const] Destruct; + | ^^^^^^^^^^^^^^^^ required by this bound in `TraitB::g` + +error[E0277]: the trait bound `T: [const] Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:74:40 + | +LL | ManuallyDrop::into_inner(self).f() + | ^ + | +note: required by a bound in `TraitA::f` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:14:15 + | +LL | fn f(self) + | - required by a bound in this associated function +LL | where +LL | Self: [const] Destruct; + | ^^^^^^^^^^^^^^^^ required by this bound in `TraitA::f` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.old.stderr b/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.old.stderr new file mode 100644 index 0000000000000..e32b8d8c3b30f --- /dev/null +++ b/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.old.stderr @@ -0,0 +1,62 @@ +error[E0277]: the trait bound `U: [const] Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:41:11 + | +LL | u.f(); + | ^ + | +note: required by a bound in `TraitA::f` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:14:15 + | +LL | fn f(self) + | - required by a bound in this associated function +LL | where +LL | Self: [const] Destruct; + | ^^^^^^^^^^^^^^^^ required by this bound in `TraitA::f` + +error[E0277]: the trait bound `T: const Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:49:27 + | +LL | ensure_const_destruct(x.unwrap()) + | --------------------- ^^^^^^^^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `ensure_const_destruct` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:9:35 + | +LL | const fn ensure_const_destruct(_t: T) {} + | ^^^^^^^^^^^^^^ required by this bound in `ensure_const_destruct` + +error[E0277]: the trait bound `T: [const] Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:64:16 + | +LL | self.0.g() + | ^ + | +note: required by a bound in `TraitB::g` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:56:15 + | +LL | fn g(self) + | - required by a bound in this associated function +LL | where +LL | Self: [const] Destruct; + | ^^^^^^^^^^^^^^^^ required by this bound in `TraitB::g` + +error[E0277]: the trait bound `T: [const] Destruct` is not satisfied + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:74:40 + | +LL | ManuallyDrop::into_inner(self).f() + | ^ + | +note: required by a bound in `TraitA::f` + --> $DIR/conditionally-const-trait-structural-destruct-implied-bounds.rs:14:15 + | +LL | fn f(self) + | - required by a bound in this associated function +LL | where +LL | Self: [const] Destruct; + | ^^^^^^^^^^^^^^^^ required by this bound in `TraitA::f` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. 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..cb1f080fcc24c --- /dev/null +++ b/tests/ui/traits/const-traits/conditionally-const-trait-structural-destruct-implied-bounds.rs @@ -0,0 +1,79 @@ +//@ revisions: next old +//@ [next] compile-flags: -Znext-solver + +#![feature(const_trait_impl, const_destruct)] + +use std::marker::Destruct; +use std::mem::ManuallyDrop; + +const fn ensure_const_destruct(_t: T) {} + +const trait TraitA { + fn f(self) + where + Self: [const] Destruct; +} + +impl const TraitA for [T; 1] { + fn f(self) + where + Self: [const] Destruct, + { + let [t] = self; + t.f() + } +} + +pub struct Foo { + pub pub_field: T, + u: U, +} + +pub struct Bar(pub T); + +impl const TraitA for Foo, U> { + fn f(self) + where + Self: [const] Destruct, + { + let Foo { pub_field: Bar(t), u } = self; + t.f(); + u.f(); //~ ERROR the trait bound `U: [const] Destruct` is not satisfied + } +} + +const fn h(x: Option) +where + Option: [const] Destruct, +{ + ensure_const_destruct(x.unwrap()) + //~^ ERROR the trait bound `T: const Destruct` is not satisfied +} + +const trait TraitB { + fn g(self) + where + Self: [const] Destruct; +} + +impl const TraitA for Bar { + fn f(self) + where + Self: [const] Destruct, + { + self.0.g() + //~^ ERROR the trait bound `T: [const] Destruct` is not satisfied + } +} + +impl const TraitA for ManuallyDrop { + fn f(self) + where + Self: [const] Destruct, + { + ManuallyDrop::into_inner(self).f() + //~^ ERROR the trait bound `T: [const] Destruct` is not satisfied + } +} + +fn main() {}