From 9c1a91628bb502deec644173d21830822a0f786a Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 08:08:40 +0000 Subject: [PATCH 01/16] cleanup hir hack --- compiler/rustc_hir/src/hir.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 84bc37170c634..5655c4c0e973e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3234,12 +3234,7 @@ impl<'hir> Node<'hir> { } } - /// Returns `Constness::Const` when this node is a const fn/impl/item, - /// - /// HACK(fee1-dead): or an associated type in a trait. This works because - /// only typeck cares about const trait predicates, so although the predicates - /// query would return const predicates when it does not need to be const, - /// it wouldn't have any effect. + /// Returns `Constness::Const` when this node is a const fn/impl/item. pub fn constness_for_typeck(&self) -> Constness { match self { Node::Item(Item { @@ -3258,7 +3253,6 @@ impl<'hir> Node<'hir> { Node::Item(Item { kind: ItemKind::Const(..), .. }) | Node::TraitItem(TraitItem { kind: TraitItemKind::Const(..), .. }) - | Node::TraitItem(TraitItem { kind: TraitItemKind::Type(..), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Const(..), .. }) => Constness::Const, _ => Constness::NotConst, From d9797d23d5e03aeafeac4eed6e147045af65783b Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 08:19:58 +0000 Subject: [PATCH 02/16] Remove unused query --- .../src/const_eval/fn_queries.rs | 32 ++++++------------- compiler/rustc_middle/src/query/mod.rs | 8 ----- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 40419a4d201ac..10afd9560fa95 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,5 +1,5 @@ use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; @@ -34,8 +34,14 @@ pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { - let parent_id = tcx.hir().get_parent_did(hir_id); - if !parent_id.is_top_level_module() { is_const_impl_raw(tcx, parent_id) } else { false } + let parent_id = tcx.hir().get_parent_node(hir_id); + matches!( + tcx.hir().get(parent_id), + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }), + .. + }) + ) } /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether @@ -70,19 +76,6 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } } -/// Checks whether the given item is an `impl` that has a `const` modifier. -fn is_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let node = tcx.hir().get(hir_id); - matches!( - node, - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }), - .. - }) - ) -} - fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { is_const_fn(tcx, def_id) && match tcx.lookup_const_stability(def_id) { @@ -103,10 +96,5 @@ fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } pub fn provide(providers: &mut Providers) { - *providers = Providers { - is_const_fn_raw, - is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, def_id.expect_local()), - is_promotable_const_fn, - ..*providers - }; + *providers = Providers { is_const_fn_raw, is_promotable_const_fn, ..*providers }; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c93996162e3e3..dd5753e95d077 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -544,14 +544,6 @@ rustc_queries! { desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } } - /// Returns `true` if this is a const `impl`. **Do not call this function manually.** - /// - /// This query caches the base data for the `is_const_impl` helper function, which also - /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). - query is_const_impl_raw(key: DefId) -> bool { - desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } - } - query asyncness(key: DefId) -> hir::IsAsync { desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } } From 104e40fb740155f7dbd471f1550800ae458c672b Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 11:06:15 +0000 Subject: [PATCH 03/16] Const dropping --- .../src/transform/check_consts/check.rs | 10 +-- .../check_consts/post_drop_elaboration.rs | 4 +- .../src/transform/check_consts/qualifs.rs | 10 +-- .../src/transform/promote_consts.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 12 +++ compiler/rustc_middle/src/ty/adt.rs | 5 ++ compiler/rustc_middle/src/ty/mod.rs | 2 + compiler/rustc_middle/src/ty/util.rs | 37 +++++++- compiler/rustc_ty_utils/src/needs_drop.rs | 87 ++++++++++++++++--- 9 files changed, 141 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index d02b4286c175a..189e1b043b3d5 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -22,7 +22,7 @@ use std::mem; use std::ops::Deref; use super::ops::{self, NonConstOp, Status}; -use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; +use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{is_lang_panic_fn, ConstCx, Qualif}; use crate::const_eval::is_unstable_const_fn; @@ -39,7 +39,7 @@ type QualifResults<'mir, 'tcx, Q> = #[derive(Default)] pub struct Qualifs<'mir, 'tcx> { has_mut_interior: Option>, - needs_drop: Option>, + needs_drop: Option>, indirectly_mutable: Option>, } @@ -80,14 +80,14 @@ impl Qualifs<'mir, 'tcx> { location: Location, ) -> bool { let ty = ccx.body.local_decls[local].ty; - if !NeedsDrop::in_any_value_of_ty(ccx, ty) { + if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) { return false; } let needs_drop = self.needs_drop.get_or_insert_with(|| { let ConstCx { tcx, body, .. } = *ccx; - FlowSensitiveAnalysis::new(NeedsDrop, ccx) + FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx) .into_engine(tcx, &body) .iterate_to_fixpoint() .into_results_cursor(&body) @@ -991,7 +991,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { // Check to see if the type of this place can ever have a drop impl. If not, this // `Drop` terminator is frivolous. let ty_needs_drop = - dropped_place.ty(self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env); + dropped_place.ty(self.body, self.tcx).ty.needs_non_const_drop(self.tcx, self.param_env); if !ty_needs_drop { return; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index b08ce219034ce..4f66e6be29707 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -5,7 +5,7 @@ use rustc_span::Span; use super::check::Qualifs; use super::ops::{self, NonConstOp}; -use super::qualifs::{NeedsDrop, Qualif}; +use super::qualifs::{NeedsNonConstDrop, Qualif}; use super::ConstCx; /// Returns `true` if we should use the more precise live drop checker that runs after drop @@ -78,7 +78,7 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> { match &terminator.kind { mir::TerminatorKind::Drop { place: dropped_place, .. } => { let dropped_ty = dropped_place.ty(self.body, self.tcx).ty; - if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) { + if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) { bug!( "Drop elaboration left behind a Drop for a type that does not need dropping" ); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 413a9638eb37b..50b691dffe83c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -17,7 +17,7 @@ pub fn in_any_value_of_ty( ) -> ConstQualifs { ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), - needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), + needs_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty), custom_eq: CustomEq::in_any_value_of_ty(cx, ty), error_occured, } @@ -97,10 +97,10 @@ impl Qualif for HasMutInterior { /// This must be ruled out (a) because we cannot run `Drop` during compile-time /// as that might not be a `const fn`, and (b) because implicit promotion would /// remove side-effects that occur as part of dropping that value. -pub struct NeedsDrop; +pub struct NeedsNonConstDrop; -impl Qualif for NeedsDrop { - const ANALYSIS_NAME: &'static str = "flow_needs_drop"; +impl Qualif for NeedsNonConstDrop { + const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop"; const IS_CLEARED_ON_MOVE: bool = true; fn in_qualifs(qualifs: &ConstQualifs) -> bool { @@ -112,7 +112,7 @@ impl Qualif for NeedsDrop { } fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { - adt.has_dtor(cx.tcx) + adt.has_non_const_dtor(cx.tcx) } } diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 6822ad2d7b5dd..94c0d8db84714 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -231,7 +231,7 @@ impl<'tcx> Validator<'_, 'tcx> { // We cannot promote things that need dropping, since the promoted value // would not get dropped. - if self.qualif_local::(place.local) { + if self.qualif_local::(place.local) { return Err(Unpromotable); } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index dd5753e95d077..4f13ca892dddc 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1077,6 +1077,10 @@ rustc_queries! { query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } } + /// Query backing `Tys::needs_non_const_drop`. + query needs_non_const_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs non-const drop", env.value } + } /// Query backing `TyS::has_significant_drop_raw`. query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` has a significant drop", env.value } @@ -1101,6 +1105,14 @@ rustc_queries! { cache_on_disk_if { true } } + /// A list of types where the ADT requires drop if and only if any of + /// those types require non-const drop. If the ADT is known to always need + /// non-const drop then `Err(AlwaysRequiresDrop)` is returned. + query adt_drop_tys_non_const(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs non-const drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + /// A list of types where the ADT requires drop if and only if any of those types /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor` /// is considered to not be significant. A drop is significant if it is implemented diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 27927bcca72b7..c32f0ea9ca55a 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -7,6 +7,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorReported; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_index::vec::{Idx, IndexVec}; @@ -288,6 +289,10 @@ impl<'tcx> AdtDef { self.destructor(tcx).is_some() } + pub fn has_non_const_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { + matches!(self.destructor(tcx), Some(Destructor { constness: hir::Constness::NotConst, .. })) + } + /// Asserts this is a struct or union and returns its unique variant. pub fn non_enum_variant(&self) -> &VariantDef { assert!(self.is_struct() || self.is_union()); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index fddfd6e435c05..bab223ac6d535 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1377,6 +1377,8 @@ where pub struct Destructor { /// The `DefId` of the destructor method pub did: DefId, + /// The constness of the destructor method + pub constness: hir::Constness, } bitflags! { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 1b8e94260b9b5..1d86715772db7 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -336,16 +336,16 @@ impl<'tcx> TyCtxt<'tcx> { self.ensure().coherent_trait(drop_trait); let ty = self.type_of(adt_did); - let dtor_did = self.find_map_relevant_impl(drop_trait, ty, |impl_did| { + let (did, constness) = self.find_map_relevant_impl(drop_trait, ty, |impl_did| { if let Some(item) = self.associated_items(impl_did).in_definition_order().next() { if validate(self, impl_did).is_ok() { - return Some(item.def_id); + return Some((item.def_id, self.impl_constness(impl_did))); } } None - }); + })?; - Some(ty::Destructor { did: dtor_did? }) + Some(ty::Destructor { did, constness }) } /// Returns the set of types that are required to be alive in @@ -792,6 +792,35 @@ impl<'tcx> ty::TyS<'tcx> { } } } + /// If `ty.needs_non_const_drop(...)` returns true, then `ty` is definitely + /// non-copy and *might* have a non-const destructor attached; if it returns + /// `false`, then `ty` definitely has a const destructor or no destructor at all. + /// + /// (Note that this implies that if `ty` has a non-const destructor attached, + /// then `needs_non_const_drop` will definitely return `true` for `ty`.) + pub fn needs_non_const_drop( + &'tcx self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + // Avoid querying in simple cases. + match needs_drop_components(self, &tcx.data_layout) { + Err(AlwaysRequiresDrop) => true, + Ok(components) => { + let query_ty = match *components { + [] => return false, + // if we've got a single component, call the query with that + // to increase the chance that we hit the query cache. + [component_ty] => component_ty, + _ => self, + }; + // This doesn't depend on regions, so try to minimize distinct + // query keys used. + let erased = tcx.normalize_erasing_regions(param_env, query_ty); + tcx.needs_non_const_drop_raw(param_env.and(erased)) + } + } + } /// Checks if `ty` has has a significant drop. /// diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index d837af85d58ae..d93379aa3866b 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -1,22 +1,41 @@ //! Check whether a type has (potentially) non-trivial drop glue. use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::Limit; use rustc_span::{sym, DUMMY_SP}; +use rustc_trait_selection::traits::{Obligation, ObligationCause, SelectionContext}; type NeedsDropResult = Result; -fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - let adt_fields = - move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter()); +fn needs_drop_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + needs_non_const_drop: bool, +) -> bool { // If we don't know a type doesn't need drop, for example if it's a type // parameter without a `Copy` bound, then we conservatively return that it // needs drop. - let res = NeedsDropTypes::new(tcx, query.param_env, query.value, adt_fields).next().is_some(); + let res = if needs_non_const_drop { + let adt_components = move |adt_def: &ty::AdtDef| { + tcx.adt_drop_tys_non_const(adt_def.did).map(|tys| tys.iter()) + }; + NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop) + .next() + .is_some() + } else { + let adt_components = + move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter()); + NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop) + .next() + .is_some() + }; + debug!("needs_drop_raw({:?}) = {:?}", query, res); res } @@ -27,9 +46,10 @@ fn has_significant_drop_raw<'tcx>( ) -> bool { let significant_drop_fields = move |adt_def: &ty::AdtDef| tcx.adt_significant_drop_tys(adt_def.did).map(|tys| tys.iter()); - let res = NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields) - .next() - .is_some(); + let res = + NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields, false) + .next() + .is_some(); debug!("has_significant_drop_raw({:?}) = {:?}", query, res); res } @@ -46,6 +66,7 @@ struct NeedsDropTypes<'tcx, F> { unchecked_tys: Vec<(Ty<'tcx>, usize)>, recursion_limit: Limit, adt_components: F, + needs_non_const_drop: bool, } impl<'tcx, F> NeedsDropTypes<'tcx, F> { @@ -54,6 +75,7 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, adt_components: F, + needs_non_const_drop: bool, ) -> Self { let mut seen_tys = FxHashSet::default(); seen_tys.insert(ty); @@ -65,6 +87,7 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { unchecked_tys: vec![(ty, 0)], recursion_limit: tcx.recursion_limit(), adt_components, + needs_non_const_drop, } } } @@ -147,6 +170,35 @@ where queue_type(self, subst_ty); } } + ty::Param(_) + if self.needs_non_const_drop && self.tcx.features().const_trait_impl => + { + // Check if the param is bounded to have a `~const Drop` impl. + let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, None); + let trait_ref = ty::TraitRef { + def_id: drop_trait, + substs: self.tcx.mk_substs_trait(component, &[]), + }; + + let obligation = Obligation::new( + ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::ConstIfConst, + }), + ); + + let implsrc = tcx.infer_ctxt().enter(|infcx| { + let mut selcx = + SelectionContext::with_constness(&infcx, hir::Constness::Const); + selcx.select(&obligation) + }); + + if let Ok(Some(_)) = implsrc { + return None; + } + } ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => { if ty == component { // Return the type to the caller: they may be able @@ -176,6 +228,7 @@ fn adt_drop_tys_helper( tcx: TyCtxt<'_>, def_id: DefId, adt_has_dtor: impl Fn(&ty::AdtDef) -> bool, + needs_non_const_drop: bool, ) -> Result<&ty::List>, AlwaysRequiresDrop> { let adt_components = move |adt_def: &ty::AdtDef| { if adt_def.is_manually_drop() { @@ -194,7 +247,7 @@ fn adt_drop_tys_helper( let adt_ty = tcx.type_of(def_id); let param_env = tcx.param_env(def_id); let res: Result, _> = - NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components).collect(); + NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components, needs_non_const_drop).collect(); debug!("adt_drop_tys(`{}`) = `{:?}`", tcx.def_path_str(def_id), res); res.map(|components| tcx.intern_type_list(&components)) @@ -202,7 +255,17 @@ fn adt_drop_tys_helper( fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List>, AlwaysRequiresDrop> { let adt_has_dtor = |adt_def: &ty::AdtDef| adt_def.destructor(tcx).is_some(); - adt_drop_tys_helper(tcx, def_id, adt_has_dtor) + adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false) +} + +fn adt_drop_tys_non_const( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Result<&ty::List>, AlwaysRequiresDrop> { + let adt_has_dtor = |adt_def: &ty::AdtDef| { + adt_def.destructor(tcx).map(|d| d.constness) == Some(hir::Constness::NotConst) + }; + adt_drop_tys_helper(tcx, def_id, adt_has_dtor, true) } fn adt_significant_drop_tys( @@ -215,14 +278,16 @@ fn adt_significant_drop_tys( .map(|dtor| !tcx.has_attr(dtor.did, sym::rustc_insignificant_dtor)) .unwrap_or(false) }; - adt_drop_tys_helper(tcx, def_id, adt_has_dtor) + adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false) } pub(crate) fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { - needs_drop_raw, + needs_drop_raw: |tcx, query| needs_drop_raw(tcx, query, false), + needs_non_const_drop_raw: |tcx, query| needs_drop_raw(tcx, query, true), has_significant_drop_raw, adt_drop_tys, + adt_drop_tys_non_const, adt_significant_drop_tys, ..*providers }; From 9125fbfd60afd9dafca843857498232503be365a Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 11:55:03 +0000 Subject: [PATCH 04/16] Do not lint for ~const Drop bounds --- compiler/rustc_lint/src/traits.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index edb158dd37806..ac1b106f73b52 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -87,6 +87,7 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { use rustc_middle::ty::PredicateKind::*; + use rustc_middle::ty; let predicates = cx.tcx.explicit_predicates_of(item.def_id); for &(predicate, span) in predicates.predicates { @@ -94,6 +95,10 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { Trait(trait_predicate) => trait_predicate, _ => continue, }; + if trait_predicate.constness == ty::BoundConstness::ConstIfConst { + // `~const Drop` definitely have meanings so avoid linting here. + continue + } let def_id = trait_predicate.trait_ref.def_id; if cx.tcx.lang_items().drop_trait() == Some(def_id) { // Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern. From f6f5180d5fef3b3a4cc8a383eb20b83716935e6a Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 11:55:16 +0000 Subject: [PATCH 05/16] Add a simple test case --- .../rfc-2632-const-trait-impl/const-drop.rs | 23 +++++++++++++++++ .../const-drop.stderr | 25 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-drop.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs new file mode 100644 index 0000000000000..90fe28c7367b4 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs @@ -0,0 +1,23 @@ +#![feature(const_trait_impl)] +#![feature(const_fn_trait_bound)] +#![feature(const_mut_refs)] +#![feature(const_panic)] + +struct S; + +impl const Drop for S { + fn drop(&mut self) { + // NB: There is no way to tell that a const destructor is ran, + // because even if we can operate on mutable variables, it will + // not be reflected because everything is `const`. So we panic + // here, attempting to make the CTFE engine error. + panic!("much const drop") + //~^ ERROR evaluation of constant value failed + } +} + +const fn a(_: T) {} + +const _: () = a(S); + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr new file mode 100644 index 0000000000000..33eba4f4d25e9 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr @@ -0,0 +1,25 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/const-drop.rs:14:9 + | +LL | panic!("much const drop") + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the evaluated program panicked at 'much const drop', $DIR/const-drop.rs:14:9 + | inside `::drop` at $SRC_DIR/std/src/panic.rs:LL:COL +... +LL | const fn a(_: T) {} + | - inside `a::` at $DIR/const-drop.rs:19:35 +LL | +LL | const _: () = a(S); + | ---- inside `_` at $DIR/const-drop.rs:21:15 + | + ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | +LL | pub unsafe fn drop_in_place(to_drop: *mut T) { + | ------------------------------------------------------- inside `std::ptr::drop_in_place:: - shim(Some(S))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. From 48a3ba9a335987cfae90dec92b08a8833e12ba07 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 12:53:51 +0000 Subject: [PATCH 06/16] fmt --- .../rustc_const_eval/src/transform/check_consts/check.rs | 6 ++++-- compiler/rustc_lint/src/traits.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 189e1b043b3d5..133451831242c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -990,8 +990,10 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { // Check to see if the type of this place can ever have a drop impl. If not, this // `Drop` terminator is frivolous. - let ty_needs_drop = - dropped_place.ty(self.body, self.tcx).ty.needs_non_const_drop(self.tcx, self.param_env); + let ty_needs_drop = dropped_place + .ty(self.body, self.tcx) + .ty + .needs_non_const_drop(self.tcx, self.param_env); if !ty_needs_drop { return; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index ac1b106f73b52..5435ff1396de8 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -86,8 +86,8 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc_middle::ty::PredicateKind::*; use rustc_middle::ty; + use rustc_middle::ty::PredicateKind::*; let predicates = cx.tcx.explicit_predicates_of(item.def_id); for &(predicate, span) in predicates.predicates { @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { }; if trait_predicate.constness == ty::BoundConstness::ConstIfConst { // `~const Drop` definitely have meanings so avoid linting here. - continue + continue; } let def_id = trait_predicate.trait_ref.def_id; if cx.tcx.lang_items().drop_trait() == Some(def_id) { From 894ce921a091cb9252fd9d72f1c97b3996e3501c Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 13:07:49 +0000 Subject: [PATCH 07/16] better test --- .../rfc-2632-const-trait-impl/const-drop.rs | 25 +++++++++++-------- .../const-drop.stderr | 25 ------------------- 2 files changed, 15 insertions(+), 35 deletions(-) delete mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs index 90fe28c7367b4..24b0d53b6c3e7 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs @@ -1,23 +1,28 @@ +// run-pass #![feature(const_trait_impl)] #![feature(const_fn_trait_bound)] #![feature(const_mut_refs)] #![feature(const_panic)] -struct S; +struct S<'a>(&'a mut u8); -impl const Drop for S { +impl<'a> const Drop for S<'a> { fn drop(&mut self) { - // NB: There is no way to tell that a const destructor is ran, - // because even if we can operate on mutable variables, it will - // not be reflected because everything is `const`. So we panic - // here, attempting to make the CTFE engine error. - panic!("much const drop") - //~^ ERROR evaluation of constant value failed + *self.0 += 1; } } const fn a(_: T) {} -const _: () = a(S); +const fn b() -> u8 { + let mut c = 0; + let _ = S(&mut c); + a(S(&mut c)); + c +} + +const C: u8 = b(); -fn main() {} +fn main() { + assert_eq!(C, 2); +} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr deleted file mode 100644 index 33eba4f4d25e9..0000000000000 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0080]: evaluation of constant value failed - --> $DIR/const-drop.rs:14:9 - | -LL | panic!("much const drop") - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | the evaluated program panicked at 'much const drop', $DIR/const-drop.rs:14:9 - | inside `::drop` at $SRC_DIR/std/src/panic.rs:LL:COL -... -LL | const fn a(_: T) {} - | - inside `a::` at $DIR/const-drop.rs:19:35 -LL | -LL | const _: () = a(S); - | ---- inside `_` at $DIR/const-drop.rs:21:15 - | - ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ------------------------------------------------------- inside `std::ptr::drop_in_place:: - shim(Some(S))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. From a13b13ff46b2d669f0d7a8e024938366fb4a3281 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 16:34:28 +0000 Subject: [PATCH 08/16] Const drop selection candidates --- compiler/rustc_middle/src/traits/mod.rs | 15 ++- compiler/rustc_middle/src/traits/select.rs | 3 + .../src/traits/structural_impls.rs | 3 + .../src/traits/project.rs | 6 +- .../src/traits/select/candidate_assembly.rs | 110 +++++++++++++++++- .../src/traits/select/confirmation.rs | 4 +- .../src/traits/select/mod.rs | 38 +++--- compiler/rustc_ty_utils/src/instance.rs | 3 +- .../rfc-2632-const-trait-impl/const-drop.rs | 51 ++++++++ 9 files changed, 210 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 74edb17fe32f1..444bc2ff043df 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -529,6 +529,9 @@ pub enum ImplSource<'tcx, N> { /// ImplSource for a trait alias. TraitAlias(ImplSourceTraitAliasData<'tcx, N>), + + /// ImplSource for a `const Drop` implementation. + ConstDrop(ImplSourceConstDropData), } impl<'tcx, N> ImplSource<'tcx, N> { @@ -543,7 +546,8 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => d.nested, ImplSource::FnPointer(d) => d.nested, ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(), + | ImplSource::Pointee(ImplSourcePointeeData) + | ImplSource::ConstDrop(ImplSourceConstDropData) => Vec::new(), ImplSource::TraitAlias(d) => d.nested, ImplSource::TraitUpcasting(d) => d.nested, } @@ -560,7 +564,8 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => &d.nested[..], ImplSource::FnPointer(d) => &d.nested[..], ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) => &[], + | ImplSource::Pointee(ImplSourcePointeeData) + | ImplSource::ConstDrop(ImplSourceConstDropData) => &[], ImplSource::TraitAlias(d) => &d.nested[..], ImplSource::TraitUpcasting(d) => &d.nested[..], } @@ -621,6 +626,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { nested: d.nested.into_iter().map(f).collect(), }) } + ImplSource::ConstDrop(ImplSourceConstDropData) => { + ImplSource::ConstDrop(ImplSourceConstDropData) + } } } } @@ -712,6 +720,9 @@ pub struct ImplSourceDiscriminantKindData; #[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub struct ImplSourcePointeeData; +#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub struct ImplSourceConstDropData; + #[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] pub struct ImplSourceTraitAliasData<'tcx, N> { pub alias_def_id: DefId, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 62996bf4cbef2..3c0fedb360827 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -143,6 +143,9 @@ pub enum SelectionCandidate<'tcx> { BuiltinObjectCandidate, BuiltinUnsizeCandidate, + + /// Implementation of `const Drop`. + ConstDropCandidate, } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index aa16e14fedcde..d0a2d0d9afb3d 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -32,6 +32,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d), super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), + + super::ImplSource::ConstDrop(ref d) => write!(f, "{:?}", d), } } } @@ -125,4 +127,5 @@ TrivialTypeFoldableAndLiftImpls! { super::IfExpressionCause, super::ImplSourceDiscriminantKindData, super::ImplSourcePointeeData, + super::ImplSourceConstDropData, } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 7038f16a2c9c4..4910924766353 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1477,7 +1477,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) - | super::ImplSource::TraitUpcasting(_) => { + | super::ImplSource::TraitUpcasting(_) + | super::ImplSource::ConstDrop(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1549,7 +1550,8 @@ fn confirm_select_candidate<'cx, 'tcx>( | super::ImplSource::Param(..) | super::ImplSource::Builtin(..) | super::ImplSource::TraitUpcasting(_) - | super::ImplSource::TraitAlias(..) => { + | super::ImplSource::TraitAlias(..) + | super::ImplSource::ConstDrop(_) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, 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 e18828fec3f6b..ffaef566cd59d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; use rustc_target::spec::abi::Abi; use crate::traits::coherence::Conflict; @@ -277,6 +277,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); } else if lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else if lang_items.drop_trait() == Some(def_id) + && obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst + { + if self.is_in_const_context { + self.assemble_const_drop_candidates(obligation, &mut candidates)?; + } else { + // `~const Drop` when we are not in a const context has no effect. + candidates.vec.push(ConstDropCandidate) + } } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -803,4 +812,103 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_const_drop_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + let mut stack: Vec<(Ty<'tcx>, usize)> = vec![(obligation.self_ty().skip_binder(), 0)]; + + while let Some((ty, depth)) = stack.pop() { + self.check_recursion_depth(depth, obligation)?; + let mut copy_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; + let mut copy_obligation = + obligation.with(obligation.predicate.rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: self.tcx().require_lang_item(hir::LangItem::Copy, None), + substs: self.tcx().mk_substs_trait(ty, &[]), + }, + constness: ty::BoundConstness::NotConst, + })); + copy_obligation.recursion_depth = depth + 1; + self.assemble_candidates_from_impls(©_obligation, &mut copy_candidates); + let copy_conditions = self.copy_clone_conditions(©_obligation); + self.assemble_builtin_bound_candidates(copy_conditions, &mut copy_candidates); + if !copy_candidates.vec.is_empty() { + continue; + } + + match ty.kind() { + ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::FnPtr(_) + | ty::Never + | ty::Ref(..) + | ty::FnDef(..) + | ty::RawPtr(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Foreign(_) => {} // Do nothing. These types satisfy `const Drop`. + + ty::Adt(def, subst) => { + let mut set = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; + self.assemble_candidates_from_impls(obligation, &mut set); + if set + .vec + .into_iter() + .find(|candidate| { + if let SelectionCandidate::ImplCandidate(did) = candidate { + matches!(self.tcx().impl_constness(*did), hir::Constness::NotConst) + } else { + false + } + }) + .is_none() + { + // could not find a const impl for Drop, iterate over its fields. + stack + .extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1))); + } + } + + ty::Array(ty, _) => stack.push((ty, depth + 1)), + + ty::Tuple(_) => stack.extend(ty.tuple_fields().map(|t| (t, depth + 1))), + + ty::Closure(_, substs) => { + stack.extend(substs.as_closure().upvar_tys().map(|t| (t, depth + 1))) + } + + ty::Generator(_, substs, _) => { + let substs = substs.as_generator(); + stack.extend(substs.upvar_tys().map(|t| (t, depth + 1))); + stack.push((substs.witness(), depth + 1)); + } + + ty::GeneratorWitness(tys) => stack.extend( + self.tcx().erase_late_bound_regions(*tys).iter().map(|t| (t, depth + 1)), + ), + + ty::Slice(ty) => stack.push((ty, depth + 1)), + + ty::Opaque(..) + | ty::Dynamic(..) + | ty::Error(_) + | ty::Bound(..) + | ty::Infer(_) + | ty::Placeholder(_) + | ty::Projection(..) + | ty::Param(..) => return Ok(()), + } + } + // all types have passed. + candidates.vec.push(ConstDropCandidate); + + Ok(()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 6fae8173985be..3b6555de912e9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,7 +28,7 @@ use crate::traits::TraitNotObjectSafe; use crate::traits::VtblSegment; use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation}; use crate::traits::{ - ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, + ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, ImplSourceConstDropData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, @@ -124,6 +124,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; Ok(ImplSource::TraitUpcasting(data)) } + + ConstDropCandidate => Ok(ImplSource::ConstDrop(ImplSourceConstDropData)), } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 22013fb79cf79..777e4ffc080a4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1038,19 +1038,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); } - /// Checks that the recursion limit has not been exceeded. - /// - /// The weird return type of this function allows it to be used with the `try` (`?`) - /// operator within certain functions. - fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( + fn check_recursion_depth>( &self, - obligation: &Obligation<'tcx, T>, - error_obligation: &Obligation<'tcx, V>, + depth: usize, + error_obligation: &Obligation<'tcx, T>, ) -> Result<(), OverflowError> { - if !self.infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { + if !self.infcx.tcx.recursion_limit().value_within_limit(depth) { match self.query_mode { TraitQueryMode::Standard => { - self.infcx().report_overflow_error(error_obligation, true); + self.infcx.report_overflow_error(error_obligation, true); } TraitQueryMode::Canonical => { return Err(OverflowError); @@ -1060,6 +1056,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(()) } + /// Checks that the recursion limit has not been exceeded. + /// + /// The weird return type of this function allows it to be used with the `try` (`?`) + /// operator within certain functions. + fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( + &self, + obligation: &Obligation<'tcx, T>, + error_obligation: &Obligation<'tcx, V>, + ) -> Result<(), OverflowError> { + self.check_recursion_depth(obligation.recursion_depth, error_obligation) + } + fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) where OP: FnOnce(&mut Self) -> R, @@ -1079,10 +1087,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); // Respect const trait obligations if self.is_trait_predicate_const(obligation.predicate.skip_binder()) { - if Some(obligation.predicate.skip_binder().trait_ref.def_id) - != tcx.lang_items().sized_trait() - // const Sized bounds are skipped - { match candidate { // const impl ImplCandidate(def_id) @@ -1097,12 +1101,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // generator, this will raise error in other places // or ignore error with const_async_blocks feature GeneratorCandidate => {} + ConstDropCandidate => {} _ => { // reject all other types of candidates return Err(Unimplemented); } } - } } // Treat negative impls as unimplemented, and reservation impls as ambiguity. if let ImplCandidate(def_id) = candidate { @@ -1476,14 +1480,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ( BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate - | PointeeCandidate, + | PointeeCandidate + | ConstDropCandidate, _, ) => true, ( _, BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate - | PointeeCandidate, + | PointeeCandidate + | ConstDropCandidate, ) => false, (ParamCandidate(other), ParamCandidate(victim)) => { diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index b00d2ab35617f..87b729faa54e0 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -386,7 +386,8 @@ fn resolve_associated_item<'tcx>( | traits::ImplSource::TraitAlias(..) | traits::ImplSource::DiscriminantKind(..) | traits::ImplSource::Pointee(..) - | traits::ImplSource::TraitUpcasting(_) => None, + | traits::ImplSource::TraitUpcasting(_) + | traits::ImplSource::ConstDrop(_) => None, }) } diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs index 24b0d53b6c3e7..3fcfd42f94e02 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs @@ -23,6 +23,57 @@ const fn b() -> u8 { const C: u8 = b(); +macro_rules! implements_const_drop { + ($($exp:expr),*$(,)?) => { + $( + const _: () = a($exp); + )* + } +} + +#[allow(dead_code)] +mod t { + pub struct Foo; + pub enum Bar { A } + pub fn foo() {} + pub struct ConstDrop; + + impl const Drop for ConstDrop { + fn drop(&mut self) {} + } + + pub struct HasConstDrop(pub ConstDrop); + pub struct TrivialFields(pub u8, pub i8, pub usize, pub isize); +} + +use t::*; + +implements_const_drop! { + 1u8, + 2, + 3.0, + Foo, + Bar::A, + foo, + ConstDrop, + HasConstDrop(ConstDrop), + TrivialFields(1, 2, 3, 4), + &1, + &1 as *const i32, +} + fn main() { + struct HasDropGlue(Box); + struct HasDropImpl; + impl Drop for HasDropImpl { + fn drop(&mut self) { + println!("not trivial drop"); + } + } + + // These types should pass because ~const in a non-const context should have no effect. + a(HasDropGlue(Box::new(0))); + a(HasDropImpl); + assert_eq!(C, 2); } From 4eab5c1f7b672b2d59328e24e05bc191e448d7d9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 1 Sep 2021 16:40:42 +0000 Subject: [PATCH 09/16] Failing test --- .../const-drop-fail.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs new file mode 100644 index 0000000000000..fd7c491c7da81 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs @@ -0,0 +1,31 @@ +#![feature(const_trait_impl)] +#![feature(const_mut_refs)] +#![feature(const_fn_trait_bound)] + +struct NonTrivialDrop; + +impl Drop for NonTrivialDrop { + fn drop(&mut self) { + println!("Non trivial drop"); + } +} + +struct ConstImplWithDropGlue(NonTrivialDrop); + +impl const Drop for ConstImplWithDropGlue { + fn drop(&mut self) {} +} + +const fn check() {} + +macro_rules! check_all { + ($($T:ty),*$(,)?) => {$( + const _: () = check::<$T>(); + )*}; +} + +check_all! { + ConstImplWithDropGlue, +} + +fn main() {} From f0a52128ee2d522e12893637428d88a3287c818e Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 2 Sep 2021 09:02:19 +0000 Subject: [PATCH 10/16] fmt --- .../src/traits/structural_impls.rs | 2 +- .../src/traits/select/mod.rs | 37 +++++++++---------- .../const-drop-fail.rs | 2 +- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index d0a2d0d9afb3d..6032004e60776 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -32,7 +32,7 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d), super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), - + super::ImplSource::ConstDrop(ref d) => write!(f, "{:?}", d), } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 777e4ffc080a4..987ad4bc31b94 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1087,26 +1087,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); // Respect const trait obligations if self.is_trait_predicate_const(obligation.predicate.skip_binder()) { - match candidate { - // const impl - ImplCandidate(def_id) - if tcx.impl_constness(def_id) == hir::Constness::Const => {} - // const param - ParamCandidate(ty::ConstnessAnd { - constness: ty::BoundConstness::ConstIfConst, - .. - }) => {} - // auto trait impl - AutoImplCandidate(..) => {} - // generator, this will raise error in other places - // or ignore error with const_async_blocks feature - GeneratorCandidate => {} - ConstDropCandidate => {} - _ => { - // reject all other types of candidates - return Err(Unimplemented); - } + match candidate { + // const impl + ImplCandidate(def_id) if tcx.impl_constness(def_id) == hir::Constness::Const => {} + // const param + ParamCandidate(ty::ConstnessAnd { + constness: ty::BoundConstness::ConstIfConst, + .. + }) => {} + // auto trait impl + AutoImplCandidate(..) => {} + // generator, this will raise error in other places + // or ignore error with const_async_blocks feature + GeneratorCandidate => {} + ConstDropCandidate => {} + _ => { + // reject all other types of candidates + return Err(Unimplemented); } + } } // Treat negative impls as unimplemented, and reservation impls as ambiguity. if let ImplCandidate(def_id) = candidate { diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs index fd7c491c7da81..a79b67fb0db0a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs @@ -20,7 +20,7 @@ const fn check() {} macro_rules! check_all { ($($T:ty),*$(,)?) => {$( - const _: () = check::<$T>(); + const _: () = check::<$T>(); )*}; } From 1ca83c6451783aaa77aa69643b70b22ef9e9a01a Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 2 Sep 2021 10:59:53 +0000 Subject: [PATCH 11/16] Use trait select logic instead of query --- .../src/transform/check_consts/check.rs | 14 +++-- .../src/transform/check_consts/qualifs.rs | 29 ++++++++++- .../src/traits/select/candidate_assembly.rs | 52 ++++++++++++------- .../const-drop-fail.rs | 11 ++-- .../const-drop-fail.stderr | 27 ++++++++++ 5 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 133451831242c..f1c7d3035b977 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -988,14 +988,12 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { let mut err_span = self.span; - // Check to see if the type of this place can ever have a drop impl. If not, this - // `Drop` terminator is frivolous. - let ty_needs_drop = dropped_place - .ty(self.body, self.tcx) - .ty - .needs_non_const_drop(self.tcx, self.param_env); - - if !ty_needs_drop { + let ty_needs_non_const_drop = qualifs::NeedsNonConstDrop::in_any_value_of_ty( + self.ccx, + dropped_place.ty(self.body, self.tcx).ty, + ); + + if !ty_needs_non_const_drop { return; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 50b691dffe83c..ea8f0a29181ef 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -3,10 +3,14 @@ //! See the `Qualif` trait for more info. use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; -use rustc_trait_selection::traits; +use rustc_trait_selection::traits::{ + self, ImplSource, Obligation, ObligationCause, SelectionContext, +}; use super::ConstCx; @@ -108,7 +112,28 @@ impl Qualif for NeedsNonConstDrop { } fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.needs_drop(cx.tcx, cx.param_env) + let trait_ref = ty::TraitRef { + def_id: cx.tcx.require_lang_item(hir::LangItem::Drop, None), + substs: cx.tcx.mk_substs_trait(ty, &[]), + }; + let obligation = Obligation::new( + ObligationCause::dummy(), + cx.param_env, + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::ConstIfConst, + }), + ); + + let implsrc = cx.tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::with_constness(&infcx, hir::Constness::Const); + selcx.select(&obligation) + }); + match implsrc { + Ok(Some(ImplSource::ConstDrop(_))) + | Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => false, + _ => true, + } } fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { 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 ffaef566cd59d..6d64dc8254bb4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -283,6 +283,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.is_in_const_context { self.assemble_const_drop_candidates(obligation, &mut candidates)?; } else { + debug!("passing ~const Drop bound; in non-const context"); // `~const Drop` when we are not in a const context has no effect. candidates.vec.push(ConstDropCandidate) } @@ -821,6 +822,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut stack: Vec<(Ty<'tcx>, usize)> = vec![(obligation.self_ty().skip_binder(), 0)]; while let Some((ty, depth)) = stack.pop() { + let mut noreturn = false; + self.check_recursion_depth(depth, obligation)?; let mut copy_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; let mut copy_obligation = @@ -836,8 +839,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let copy_conditions = self.copy_clone_conditions(©_obligation); self.assemble_builtin_bound_candidates(copy_conditions, &mut copy_candidates); if !copy_candidates.vec.is_empty() { - continue; + noreturn = true; } + debug!(?copy_candidates.vec, "assemble_const_drop_candidates - copy"); match ty.kind() { ty::Int(_) @@ -857,22 +861,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Adt(def, subst) => { let mut set = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - self.assemble_candidates_from_impls(obligation, &mut set); - if set - .vec - .into_iter() - .find(|candidate| { - if let SelectionCandidate::ImplCandidate(did) = candidate { - matches!(self.tcx().impl_constness(*did), hir::Constness::NotConst) - } else { - false - } - }) - .is_none() - { - // could not find a const impl for Drop, iterate over its fields. - stack - .extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1))); + self.assemble_candidates_from_impls( + &obligation.with(obligation.predicate.map_bound(|mut pred| { + pred.trait_ref.substs = self.tcx().mk_substs_trait(ty, &[]); + pred + })), + &mut set, + ); + stack.extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1))); + + debug!(?set.vec, "assemble_const_drop_candidates - ty::Adt"); + if set.vec.into_iter().any(|candidate| { + if let SelectionCandidate::ImplCandidate(did) = candidate { + matches!(self.tcx().impl_constness(did), hir::Constness::NotConst) + } else { + false + } + }) { + if !noreturn { + // has non-const Drop + return Ok(()); + } + debug!("not returning"); } } @@ -903,8 +913,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Infer(_) | ty::Placeholder(_) | ty::Projection(..) - | ty::Param(..) => return Ok(()), + | ty::Param(..) => { + if !noreturn { + return Ok(()); + } + debug!("not returning"); + } } + debug!(?stack, "assemble_const_drop_candidates - in loop"); } // all types have passed. candidates.vec.push(ConstDropCandidate); diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs index a79b67fb0db0a..17442e1b05a16 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs @@ -16,16 +16,19 @@ impl const Drop for ConstImplWithDropGlue { fn drop(&mut self) {} } -const fn check() {} +const fn check(_: T) {} macro_rules! check_all { - ($($T:ty),*$(,)?) => {$( - const _: () = check::<$T>(); + ($($exp:expr),*$(,)?) => {$( + const _: () = check($exp); )*}; } check_all! { - ConstImplWithDropGlue, + NonTrivialDrop, + //~^ ERROR the trait bound + ConstImplWithDropGlue(NonTrivialDrop), + //~^ ERROR the trait bound } fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr new file mode 100644 index 0000000000000..e962503d7df88 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied + --> $DIR/const-drop-fail.rs:28:5 + | +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:19:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied + --> $DIR/const-drop-fail.rs:30:5 + | +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:19:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 82117289f2ac8623348725ae20e68b28331508a1 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 2 Sep 2021 11:04:29 +0000 Subject: [PATCH 12/16] Remove the queries --- compiler/rustc_middle/src/query/mod.rs | 12 ---- compiler/rustc_middle/src/ty/util.rs | 29 -------- compiler/rustc_ty_utils/src/needs_drop.rs | 88 ++++------------------- 3 files changed, 13 insertions(+), 116 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 4f13ca892dddc..dd5753e95d077 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1077,10 +1077,6 @@ rustc_queries! { query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } } - /// Query backing `Tys::needs_non_const_drop`. - query needs_non_const_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` needs non-const drop", env.value } - } /// Query backing `TyS::has_significant_drop_raw`. query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` has a significant drop", env.value } @@ -1105,14 +1101,6 @@ rustc_queries! { cache_on_disk_if { true } } - /// A list of types where the ADT requires drop if and only if any of - /// those types require non-const drop. If the ADT is known to always need - /// non-const drop then `Err(AlwaysRequiresDrop)` is returned. - query adt_drop_tys_non_const(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` needs non-const drop", tcx.def_path_str(def_id) } - cache_on_disk_if { true } - } - /// A list of types where the ADT requires drop if and only if any of those types /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor` /// is considered to not be significant. A drop is significant if it is implemented diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 1d86715772db7..808c93abc8ec2 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -792,35 +792,6 @@ impl<'tcx> ty::TyS<'tcx> { } } } - /// If `ty.needs_non_const_drop(...)` returns true, then `ty` is definitely - /// non-copy and *might* have a non-const destructor attached; if it returns - /// `false`, then `ty` definitely has a const destructor or no destructor at all. - /// - /// (Note that this implies that if `ty` has a non-const destructor attached, - /// then `needs_non_const_drop` will definitely return `true` for `ty`.) - pub fn needs_non_const_drop( - &'tcx self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - // Avoid querying in simple cases. - match needs_drop_components(self, &tcx.data_layout) { - Err(AlwaysRequiresDrop) => true, - Ok(components) => { - let query_ty = match *components { - [] => return false, - // if we've got a single component, call the query with that - // to increase the chance that we hit the query cache. - [component_ty] => component_ty, - _ => self, - }; - // This doesn't depend on regions, so try to minimize distinct - // query keys used. - let erased = tcx.normalize_erasing_regions(param_env, query_ty); - tcx.needs_non_const_drop_raw(param_env.and(erased)) - } - } - } /// Checks if `ty` has has a significant drop. /// diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index d93379aa3866b..32d271d94c8ea 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -1,40 +1,24 @@ //! Check whether a type has (potentially) non-trivial drop glue. use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::Limit; use rustc_span::{sym, DUMMY_SP}; -use rustc_trait_selection::traits::{Obligation, ObligationCause, SelectionContext}; type NeedsDropResult = Result; -fn needs_drop_raw<'tcx>( - tcx: TyCtxt<'tcx>, - query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - needs_non_const_drop: bool, -) -> bool { +fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + let adt_components = + move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter()); + // If we don't know a type doesn't need drop, for example if it's a type // parameter without a `Copy` bound, then we conservatively return that it // needs drop. - let res = if needs_non_const_drop { - let adt_components = move |adt_def: &ty::AdtDef| { - tcx.adt_drop_tys_non_const(adt_def.did).map(|tys| tys.iter()) - }; - NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop) - .next() - .is_some() - } else { - let adt_components = - move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter()); - NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop) - .next() - .is_some() - }; + let res = + NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components).next().is_some(); debug!("needs_drop_raw({:?}) = {:?}", query, res); res @@ -46,10 +30,9 @@ fn has_significant_drop_raw<'tcx>( ) -> bool { let significant_drop_fields = move |adt_def: &ty::AdtDef| tcx.adt_significant_drop_tys(adt_def.did).map(|tys| tys.iter()); - let res = - NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields, false) - .next() - .is_some(); + let res = NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields) + .next() + .is_some(); debug!("has_significant_drop_raw({:?}) = {:?}", query, res); res } @@ -66,7 +49,6 @@ struct NeedsDropTypes<'tcx, F> { unchecked_tys: Vec<(Ty<'tcx>, usize)>, recursion_limit: Limit, adt_components: F, - needs_non_const_drop: bool, } impl<'tcx, F> NeedsDropTypes<'tcx, F> { @@ -75,7 +57,6 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, adt_components: F, - needs_non_const_drop: bool, ) -> Self { let mut seen_tys = FxHashSet::default(); seen_tys.insert(ty); @@ -87,7 +68,6 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { unchecked_tys: vec![(ty, 0)], recursion_limit: tcx.recursion_limit(), adt_components, - needs_non_const_drop, } } } @@ -170,35 +150,6 @@ where queue_type(self, subst_ty); } } - ty::Param(_) - if self.needs_non_const_drop && self.tcx.features().const_trait_impl => - { - // Check if the param is bounded to have a `~const Drop` impl. - let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, None); - let trait_ref = ty::TraitRef { - def_id: drop_trait, - substs: self.tcx.mk_substs_trait(component, &[]), - }; - - let obligation = Obligation::new( - ObligationCause::dummy(), - self.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::ConstIfConst, - }), - ); - - let implsrc = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = - SelectionContext::with_constness(&infcx, hir::Constness::Const); - selcx.select(&obligation) - }); - - if let Ok(Some(_)) = implsrc { - return None; - } - } ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => { if ty == component { // Return the type to the caller: they may be able @@ -228,7 +179,6 @@ fn adt_drop_tys_helper( tcx: TyCtxt<'_>, def_id: DefId, adt_has_dtor: impl Fn(&ty::AdtDef) -> bool, - needs_non_const_drop: bool, ) -> Result<&ty::List>, AlwaysRequiresDrop> { let adt_components = move |adt_def: &ty::AdtDef| { if adt_def.is_manually_drop() { @@ -247,7 +197,7 @@ fn adt_drop_tys_helper( let adt_ty = tcx.type_of(def_id); let param_env = tcx.param_env(def_id); let res: Result, _> = - NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components, needs_non_const_drop).collect(); + NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components).collect(); debug!("adt_drop_tys(`{}`) = `{:?}`", tcx.def_path_str(def_id), res); res.map(|components| tcx.intern_type_list(&components)) @@ -255,17 +205,7 @@ fn adt_drop_tys_helper( fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List>, AlwaysRequiresDrop> { let adt_has_dtor = |adt_def: &ty::AdtDef| adt_def.destructor(tcx).is_some(); - adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false) -} - -fn adt_drop_tys_non_const( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> Result<&ty::List>, AlwaysRequiresDrop> { - let adt_has_dtor = |adt_def: &ty::AdtDef| { - adt_def.destructor(tcx).map(|d| d.constness) == Some(hir::Constness::NotConst) - }; - adt_drop_tys_helper(tcx, def_id, adt_has_dtor, true) + adt_drop_tys_helper(tcx, def_id, adt_has_dtor) } fn adt_significant_drop_tys( @@ -278,16 +218,14 @@ fn adt_significant_drop_tys( .map(|dtor| !tcx.has_attr(dtor.did, sym::rustc_insignificant_dtor)) .unwrap_or(false) }; - adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false) + adt_drop_tys_helper(tcx, def_id, adt_has_dtor) } pub(crate) fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { - needs_drop_raw: |tcx, query| needs_drop_raw(tcx, query, false), - needs_non_const_drop_raw: |tcx, query| needs_drop_raw(tcx, query, true), + needs_drop_raw, has_significant_drop_raw, adt_drop_tys, - adt_drop_tys_non_const, adt_significant_drop_tys, ..*providers }; From 122e91e3307ac2e36565f52623074badae09e02c Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 3 Sep 2021 09:25:27 +0000 Subject: [PATCH 13/16] do not require lang item --- .../src/transform/check_consts/qualifs.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index ea8f0a29181ef..c6f786ca53890 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -112,8 +112,15 @@ impl Qualif for NeedsNonConstDrop { } fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { + let drop_trait = if let Some(did) = cx.tcx.lang_items().drop_trait() { + did + } else { + // there is no way to define a type that needs non-const drop + // without having the lang item present. + return false; + }; let trait_ref = ty::TraitRef { - def_id: cx.tcx.require_lang_item(hir::LangItem::Drop, None), + def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]), }; let obligation = Obligation::new( From 49ac725d51b136698f41b17c64187570d1f28ecd Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 3 Sep 2021 09:53:57 +0000 Subject: [PATCH 14/16] fix precise live drops --- .../check_consts/post_drop_elaboration.rs | 6 ++--- ....stderr => const-drop-fail.precise.stderr} | 8 +++--- .../const-drop-fail.rs | 2 ++ .../const-drop-fail.stock.stderr | 27 +++++++++++++++++++ .../rfc-2632-const-trait-impl/const-drop.rs | 2 ++ 5 files changed, 38 insertions(+), 7 deletions(-) rename src/test/ui/rfc-2632-const-trait-impl/{const-drop-fail.stderr => const-drop-fail.precise.stderr} (89%) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index 4f66e6be29707..f2ba5a1ebb19b 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -79,9 +79,9 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> { mir::TerminatorKind::Drop { place: dropped_place, .. } => { let dropped_ty = dropped_place.ty(self.body, self.tcx).ty; if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) { - bug!( - "Drop elaboration left behind a Drop for a type that does not need dropping" - ); + // Instead of throwing a bug, we just return here. This is because we have to + // run custom `const Drop` impls. + return; } if dropped_place.is_indirect() { diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr similarity index 89% rename from src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr rename to src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr index e962503d7df88..acfa03a61753a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr @@ -1,23 +1,23 @@ error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:28:5 + --> $DIR/const-drop-fail.rs:30:5 | LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:19:19 + --> $DIR/const-drop-fail.rs:21:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:30:5 + --> $DIR/const-drop-fail.rs:32:5 | LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:19:19 + --> $DIR/const-drop-fail.rs:21:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs index 17442e1b05a16..3d505c7008608 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs @@ -1,6 +1,8 @@ +// revisions: stock precise #![feature(const_trait_impl)] #![feature(const_mut_refs)] #![feature(const_fn_trait_bound)] +#![cfg_attr(precise, feature(const_precise_live_drops))] struct NonTrivialDrop; diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr new file mode 100644 index 0000000000000..acfa03a61753a --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied + --> $DIR/const-drop-fail.rs:30:5 + | +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:21:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied + --> $DIR/const-drop-fail.rs:32:5 + | +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:21:19 + | +LL | const fn check(_: T) {} + | ^^^^^^^^^^^ required by this bound in `check` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs index 3fcfd42f94e02..9a1b554f45f65 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs @@ -1,8 +1,10 @@ // run-pass +// revisions: stock precise #![feature(const_trait_impl)] #![feature(const_fn_trait_bound)] #![feature(const_mut_refs)] #![feature(const_panic)] +#![cfg_attr(precise, feature(const_precise_live_drops))] struct S<'a>(&'a mut u8); From 146abdd1198d25541b19ce7e17c588f041d88518 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 3 Sep 2021 10:04:43 +0000 Subject: [PATCH 15/16] Add another test case + fmt --- .../src/transform/check_consts/qualifs.rs | 6 +-- .../const-drop-fail.precise.stderr | 42 ++++++++++++++++--- .../const-drop-fail.rs | 18 ++++++++ .../const-drop-fail.stock.stderr | 42 ++++++++++++++++--- 4 files changed, 94 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index c6f786ca53890..dc3927ed85b3d 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -119,10 +119,8 @@ impl Qualif for NeedsNonConstDrop { // without having the lang item present. return false; }; - let trait_ref = ty::TraitRef { - def_id: drop_trait, - substs: cx.tcx.mk_substs_trait(ty, &[]), - }; + let trait_ref = + ty::TraitRef { def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]) }; let obligation = Obligation::new( ObligationCause::dummy(), cx.param_env, diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr index acfa03a61753a..1ac62f0bfec05 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr @@ -1,27 +1,59 @@ +error: `~const` is not allowed here + --> $DIR/const-drop-fail.rs:27:35 + | +LL | struct ConstDropImplWithBounds(PhantomData); + | ^^^^^^^^ + | + = note: only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions + error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:30:5 + --> $DIR/const-drop-fail.rs:45:5 | LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:21:19 + --> $DIR/const-drop-fail.rs:36:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:32:5 + --> $DIR/const-drop-fail.rs:47:5 | LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:21:19 + --> $DIR/const-drop-fail.rs:36:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 2 previous errors +error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied + --> $DIR/const-drop-fail.rs:49:5 + | +LL | ConstDropImplWithBounds::(PhantomData), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` + | +note: required by `ConstDropImplWithBounds` + --> $DIR/const-drop-fail.rs:27:1 + | +LL | struct ConstDropImplWithBounds(PhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied + --> $DIR/const-drop-fail.rs:49:5 + | +LL | ConstDropImplWithBounds::(PhantomData), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` + | +note: required by a bound in `ConstDropImplWithBounds` + --> $DIR/const-drop-fail.rs:27:35 + | +LL | struct ConstDropImplWithBounds(PhantomData); + | ^^^^^^^^ required by this bound in `ConstDropImplWithBounds` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs index 3d505c7008608..3d4de088f5530 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs @@ -4,6 +4,8 @@ #![feature(const_fn_trait_bound)] #![cfg_attr(precise, feature(const_precise_live_drops))] +use std::marker::PhantomData; + struct NonTrivialDrop; impl Drop for NonTrivialDrop { @@ -18,6 +20,19 @@ impl const Drop for ConstImplWithDropGlue { fn drop(&mut self) {} } +trait A { fn a() { println!("A"); } } + +impl A for NonTrivialDrop {} + +struct ConstDropImplWithBounds(PhantomData); +//~^ ERROR `~const` is not allowed + +impl const Drop for ConstDropImplWithBounds { + fn drop(&mut self) { + T::a(); + } +} + const fn check(_: T) {} macro_rules! check_all { @@ -31,6 +46,9 @@ check_all! { //~^ ERROR the trait bound ConstImplWithDropGlue(NonTrivialDrop), //~^ ERROR the trait bound + ConstDropImplWithBounds::(PhantomData), + //~^ ERROR the trait bound + //~| ERROR the trait bound } fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr index acfa03a61753a..1ac62f0bfec05 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr @@ -1,27 +1,59 @@ +error: `~const` is not allowed here + --> $DIR/const-drop-fail.rs:27:35 + | +LL | struct ConstDropImplWithBounds(PhantomData); + | ^^^^^^^^ + | + = note: only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions + error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:30:5 + --> $DIR/const-drop-fail.rs:45:5 | LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:21:19 + --> $DIR/const-drop-fail.rs:36:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied - --> $DIR/const-drop-fail.rs:32:5 + --> $DIR/const-drop-fail.rs:47:5 | LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue` | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:21:19 + --> $DIR/const-drop-fail.rs:36:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 2 previous errors +error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied + --> $DIR/const-drop-fail.rs:49:5 + | +LL | ConstDropImplWithBounds::(PhantomData), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` + | +note: required by `ConstDropImplWithBounds` + --> $DIR/const-drop-fail.rs:27:1 + | +LL | struct ConstDropImplWithBounds(PhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied + --> $DIR/const-drop-fail.rs:49:5 + | +LL | ConstDropImplWithBounds::(PhantomData), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop` + | +note: required by a bound in `ConstDropImplWithBounds` + --> $DIR/const-drop-fail.rs:27:35 + | +LL | struct ConstDropImplWithBounds(PhantomData); + | ^^^^^^^^ required by this bound in `ConstDropImplWithBounds` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0277`. From f749e05f6b95b66485c0417996d9aeb369def8da Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 3 Sep 2021 10:37:57 +0000 Subject: [PATCH 16/16] Allow ~const bounds on inherent impls --- .../rustc_ast_passes/src/ast_validation.rs | 4 +++- .../inherent-impl-const-bounds.rs | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index aca4503903c06..4f355ef543c3b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1655,7 +1655,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_ty, ty); } AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, ref body)) - if self.in_const_trait_impl || ctxt == AssocCtxt::Trait => + if self.in_const_trait_impl + || ctxt == AssocCtxt::Trait + || matches!(sig.header.constness, Const::Yes(_)) => { self.visit_vis(&item.vis); self.visit_ident(item.ident); diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs new file mode 100644 index 0000000000000..fe1015b3bf7ee --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs @@ -0,0 +1,21 @@ +// check-pass +#![feature(const_trait_impl)] +#![feature(const_fn_trait_bound)] + +struct S; + +trait A {} +trait B {} + +impl const A for S {} +impl const B for S {} + +impl S { + const fn a() where T: ~const B { + + } +} + +const _: () = S::a::(); + +fn main() {}