]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #88558 - fee1-dead:const-drop, r=oli-obk
authorbors <bors@rust-lang.org>
Wed, 15 Sep 2021 03:51:03 +0000 (03:51 +0000)
committerbors <bors@rust-lang.org>
Wed, 15 Sep 2021 03:51:03 +0000 (03:51 +0000)
Const drop

The changes are pretty primitive at this point. But at least it works. ^-^

Problems with the current change that I can think of now:
 - [x] `~const Drop` shouldn't change anything in the non-const world.
 - [x] types that do not have drop glues shouldn't fail to satisfy `~const Drop` in const contexts. `struct S { a: u8, b: u16 }` This might not fail for `needs_non_const_drop`, but it will fail in `rustc_trait_selection`.
 - [x] The current change accepts types that have `const Drop` impls but have non-const `Drop` glue.

Fixes #88424.

Significant Changes:

- `~const Drop` is no longer treated as a normal trait bound. In non-const contexts, this bound has no effect, but in const contexts, this restricts the input type and all of its transitive fields to either a) have a `const Drop` impl or b) can be trivially dropped (i.e. no drop glue)
- `T: ~const Drop` will not be linted like `T: Drop`.
- Instead of recursing and iterating through the type in `rustc_mir::transform::check_consts`, we use the trait system to special case `~const Drop`. See [`rustc_trait_selection::...::candidate_assembly#assemble_const_drop_candidates`](https://github.com/fee1-dead/rust/blob/const-drop/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs#L817) and others.

Changes not related to `const Drop`ping and/or changes that are insignificant:

 - `Node.constness_for_typeck` no longer returns `hir::Constness::Const` for type aliases in traits. This was previously used to hack how we determine default bound constness for items. But because we now use an explicit opt-in, it is no longer needed.
 - Removed `is_const_impl_raw` query. We have `impl_constness`, and the only existing use of that query uses `HirId`, which means we can just operate it with hir.
 - `ty::Destructor` now has a field `constness`, which represents the constness of the destructor.

r? `@oli-obk`

26 files changed:
compiler/rustc_ast_passes/src/ast_validation.rs
compiler/rustc_const_eval/src/const_eval/fn_queries.rs
compiler/rustc_const_eval/src/transform/check_consts/check.rs
compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
compiler/rustc_const_eval/src/transform/promote_consts.rs
compiler/rustc_hir/src/hir.rs
compiler/rustc_lint/src/traits.rs
compiler/rustc_middle/src/query/mod.rs
compiler/rustc_middle/src/traits/mod.rs
compiler/rustc_middle/src/traits/select.rs
compiler/rustc_middle/src/traits/structural_impls.rs
compiler/rustc_middle/src/ty/adt.rs
compiler/rustc_middle/src/ty/mod.rs
compiler/rustc_middle/src/ty/util.rs
compiler/rustc_trait_selection/src/traits/project.rs
compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
compiler/rustc_trait_selection/src/traits/select/confirmation.rs
compiler/rustc_trait_selection/src/traits/select/mod.rs
compiler/rustc_ty_utils/src/instance.rs
compiler/rustc_ty_utils/src/needs_drop.rs
src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/const-drop.rs [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs [new file with mode: 0644]

index e9dce953c73888e3e80ea8258117bc3ac7a81878..11153284cad8c3abe6240a86e0c0d55517a5e71c 100644 (file)
@@ -1669,7 +1669,9 @@ fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
                 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);
index 40419a4d201ac0b198ab799ebf509a020ea23b7e..10afd9560fa956da688e68238a35b12d5c9a435e 100644 (file)
@@ -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<Symbol> {
 }
 
 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 };
 }
index 02b317b89810620357e2d88e0ef298b198845063..d5682f702ba854390a689d9b9272c214bd31971e 100644 (file)
@@ -22,7 +22,7 @@
 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 @@
 #[derive(Default)]
 pub struct Qualifs<'mir, 'tcx> {
     has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
-    needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
+    needs_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
     indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
 }
 
@@ -80,14 +80,14 @@ pub fn needs_drop(
         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)
@@ -988,12 +988,12 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
 
                 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_drop(self.tcx, self.param_env);
+                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_drop {
+                if !ty_needs_non_const_drop {
                     return;
                 }
 
index b08ce219034ce09eb5b38d2b4dd7fcea7b7b5959..f2ba5a1ebb19be757884b27d555268116199e560 100644 (file)
@@ -5,7 +5,7 @@
 
 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,10 +78,10 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Loc
         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) {
-                    bug!(
-                        "Drop elaboration left behind a Drop for a type that does not need dropping"
-                    );
+                if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
+                    // 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() {
index 413a9638eb37b172717d4473f48079215170cf72..dc3927ed85b3da8fa8182549a11c3bc5cc16e762 100644 (file)
@@ -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;
 
@@ -17,7 +21,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 +101,10 @@ fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tc
 /// 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 {
@@ -108,11 +112,37 @@ fn in_qualifs(qualifs: &ConstQualifs) -> bool {
     }
 
     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
-        ty.needs_drop(cx.tcx, cx.param_env)
+        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: drop_trait, 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 {
-        adt.has_dtor(cx.tcx)
+        adt.has_non_const_dtor(cx.tcx)
     }
 }
 
index 8c24c9fa9769cff229679c50544c1f126988a1e2..52d04cb4ff1e0f689a6794e6d5d7631b4ece72fd 100644 (file)
@@ -231,7 +231,7 @@ fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
 
                         // We cannot promote things that need dropping, since the promoted value
                         // would not get dropped.
-                        if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
+                        if self.qualif_local::<qualifs::NeedsNonConstDrop>(place.local) {
                             return Err(Unpromotable);
                         }
 
index 84bc37170c6346d1bd819d54bacb17972cd0c900..5655c4c0e973ef3cd70c165617a394aa52c39b57 100644 (file)
@@ -3234,12 +3234,7 @@ pub fn hir_id(&self) -> Option<HirId> {
         }
     }
 
-    /// 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 @@ pub fn constness_for_typeck(&self) -> Constness {
 
             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,
index edb158dd378063228b0376003854828459ac59f2..5435ff1396de800c0841de33c4c74ae4dbfb9df0 100644 (file)
@@ -86,6 +86,7 @@
 
 impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
+        use rustc_middle::ty;
         use rustc_middle::ty::PredicateKind::*;
 
         let predicates = cx.tcx.explicit_predicates_of(item.def_id);
@@ -94,6 +95,10 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
                 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.
index 985d35ff9a9b18e9d9ddb4e05164f206e5ae25ed..4296acce1ffbe294862ea6bec9ab15f933eafa7c 100644 (file)
         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) }
     }
index e21a2d1034cdd3e923e8bba1838fc515dca6832a..07c2ffac8c78c097c50d3fa5575eb211e9855503 100644 (file)
@@ -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 @@ pub fn nested_obligations(self) -> Vec<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 @@ pub fn borrow_nested_obligations(&self) -> &[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 @@ pub fn map<M, F>(self, f: F) -> ImplSource<'tcx, M>
                     nested: d.nested.into_iter().map(f).collect(),
                 })
             }
+            ImplSource::ConstDrop(ImplSourceConstDropData) => {
+                ImplSource::ConstDrop(ImplSourceConstDropData)
+            }
         }
     }
 }
@@ -712,6 +720,9 @@ pub struct ImplSourceFnPointerData<'tcx, N> {
 #[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,
index 62996bf4cbef20cbabd6ac505afbde0533ad811b..3c0fedb360827ef9a0900f384afee996e3407ccf 100644 (file)
@@ -143,6 +143,9 @@ pub enum SelectionCandidate<'tcx> {
     BuiltinObjectCandidate,
 
     BuiltinUnsizeCandidate,
+
+    /// Implementation of `const Drop`.
+    ConstDropCandidate,
 }
 
 /// The result of trait evaluation. The order is important
index aa16e14fedcde9318c9a43bb29a75310b7de5da3..6032004e6077652a408506098dad933be5ba54d4 100644 (file)
@@ -32,6 +32,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             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 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     super::IfExpressionCause,
     super::ImplSourceDiscriminantKindData,
     super::ImplSourcePointeeData,
+    super::ImplSourceConstDropData,
 }
index 27927bcca72b7a6accc5a017c64f2ee5c68d0417..c32f0ea9ca55a2c147c9177071379f92960b6a92 100644 (file)
@@ -7,6 +7,7 @@
 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 @@ pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool {
         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());
index cd1e38445ae8c7a4c12194b5eb38ae79009e82ea..777c6035be831c503043aacd9e89ca503952979c 100644 (file)
@@ -1377,6 +1377,8 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
 pub struct Destructor {
     /// The `DefId` of the destructor method
     pub did: DefId,
+    /// The constness of the destructor method
+    pub constness: hir::Constness,
 }
 
 bitflags! {
index 8a2abb037579839342b8b99eb9cb9fc6622c7761..2ec06d472fee0b9889f33ff146c267d2ee707f82 100644 (file)
@@ -324,16 +324,16 @@ pub fn calculate_dtor(
         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
index 92db0ca2a7c403d6c5f6c2666f6f9f33bd158cdd..4f22543950c0bf5785e00c10383b34c5b4879f3e 100644 (file)
@@ -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,
index e18828fec3f6bf6aedaabb5ff8e5f605ae0ee007..6d64dc8254bb4a426379aa977ba7790e6000a16a 100644 (file)
@@ -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,16 @@ pub(super) fn assemble_candidates<'o>(
             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 {
+                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)
+            }
         } else {
             if lang_items.clone_trait() == Some(def_id) {
                 // Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -803,4 +813,118 @@ fn assemble_builtin_bound_candidates(
             }
         }
     }
+
+    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() {
+            let mut noreturn = false;
+
+            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(&copy_obligation, &mut copy_candidates);
+            let copy_conditions = self.copy_clone_conditions(&copy_obligation);
+            self.assemble_builtin_bound_candidates(copy_conditions, &mut copy_candidates);
+            if !copy_candidates.vec.is_empty() {
+                noreturn = true;
+            }
+            debug!(?copy_candidates.vec, "assemble_const_drop_candidates - copy");
+
+            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.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");
+                    }
+                }
+
+                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(..) => {
+                    if !noreturn {
+                        return Ok(());
+                    }
+                    debug!("not returning");
+                }
+            }
+            debug!(?stack, "assemble_const_drop_candidates - in loop");
+        }
+        // all types have passed.
+        candidates.vec.push(ConstDropCandidate);
+
+        Ok(())
+    }
 }
index 6fae8173985be82c233b0ce04dcac77e09b7e55c..3b6555de912e959465d14a13180ee4ed2ffdd9a7 100644 (file)
@@ -28,7 +28,7 @@
 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 @@ pub(super) fn confirm_candidate(
                 let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
                 Ok(ImplSource::TraitUpcasting(data))
             }
+
+            ConstDropCandidate => Ok(ImplSource::ConstDrop(ImplSourceConstDropData)),
         }
     }
 
index 8adf9015933c3f6121a2e1cd9ea726b16163c548..7b948a0939fff4233e040a6ce2f2671b0fc67e15 100644 (file)
@@ -1038,19 +1038,15 @@ fn add_depth<T: 'cx, I: Iterator<Item = &'cx mut Obligation<'tcx, T>>>(
         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<T: Display + TypeFoldable<'tcx>, V: Display + TypeFoldable<'tcx>>(
+    fn check_recursion_depth<T: Display + TypeFoldable<'tcx>>(
         &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 @@ fn check_recursion_limit<T: Display + TypeFoldable<'tcx>, V: Display + TypeFolda
         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<T: Display + TypeFoldable<'tcx>, 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<OP, R>(&mut self, op: OP) -> (R, DepNodeIndex)
     where
         OP: FnOnce(&mut Self) -> R,
@@ -1079,28 +1087,23 @@ fn filter_impls(
         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)
-                        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 => {}
-                    _ => {
-                        // 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);
                 }
             }
         }
@@ -1476,14 +1479,16 @@ fn candidate_should_be_dropped_in_favor_of(
             (
                 BuiltinCandidate { has_nested: false }
                 | DiscriminantKindCandidate
-                | PointeeCandidate,
+                | PointeeCandidate
+                | ConstDropCandidate,
                 _,
             ) => true,
             (
                 _,
                 BuiltinCandidate { has_nested: false }
                 | DiscriminantKindCandidate
-                | PointeeCandidate,
+                | PointeeCandidate
+                | ConstDropCandidate,
             ) => false,
 
             (ParamCandidate(other), ParamCandidate(victim)) => {
index b00d2ab35617f5d8986357ffe9c13d4a9ba545d9..87b729faa54e0ad5f34796d330d236b9270612e7 100644 (file)
@@ -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,
     })
 }
 
index d837af85d58ae912933b202a95a927b043026b97..32d271d94c8ea2ac79984c2fc5b7562237191e12 100644 (file)
 type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
 
 fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    let adt_fields =
+    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 = NeedsDropTypes::new(tcx, query.param_env, query.value, adt_fields).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
 }
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
new file mode 100644 (file)
index 0000000..1ac62f0
--- /dev/null
@@ -0,0 +1,59 @@
+error: `~const` is not allowed here
+  --> $DIR/const-drop-fail.rs:27:35
+   |
+LL | struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
+   |                                   ^^^^^^^^
+   |
+   = 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:45:5
+   |
+LL |     NonTrivialDrop,
+   |     ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop`
+   |
+note: required by a bound in `check`
+  --> $DIR/const-drop-fail.rs:36:19
+   |
+LL | const fn check<T: ~const Drop>(_: T) {}
+   |                   ^^^^^^^^^^^ required by this bound in `check`
+
+error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied
+  --> $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:36:19
+   |
+LL | const fn check<T: ~const Drop>(_: T) {}
+   |                   ^^^^^^^^^^^ required by this bound in `check`
+
+error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied
+  --> $DIR/const-drop-fail.rs:49:5
+   |
+LL |     ConstDropImplWithBounds::<NonTrivialDrop>(PhantomData),
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop`
+   |
+note: required by `ConstDropImplWithBounds`
+  --> $DIR/const-drop-fail.rs:27:1
+   |
+LL | struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied
+  --> $DIR/const-drop-fail.rs:49:5
+   |
+LL |     ConstDropImplWithBounds::<NonTrivialDrop>(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<T: ~const A>(PhantomData<T>);
+   |                                   ^^^^^^^^ 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
new file mode 100644 (file)
index 0000000..3d4de08
--- /dev/null
@@ -0,0 +1,54 @@
+// revisions: stock precise
+#![feature(const_trait_impl)]
+#![feature(const_mut_refs)]
+#![feature(const_fn_trait_bound)]
+#![cfg_attr(precise, feature(const_precise_live_drops))]
+
+use std::marker::PhantomData;
+
+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) {}
+}
+
+trait A { fn a() { println!("A"); } }
+
+impl A for NonTrivialDrop {}
+
+struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
+//~^ ERROR `~const` is not allowed
+
+impl<T: ~const A> const Drop for ConstDropImplWithBounds<T> {
+    fn drop(&mut self) {
+        T::a();
+    }
+}
+
+const fn check<T: ~const Drop>(_: T) {}
+
+macro_rules! check_all {
+    ($($exp:expr),*$(,)?) => {$(
+        const _: () = check($exp);
+    )*};
+}
+
+check_all! {
+    NonTrivialDrop,
+    //~^ ERROR the trait bound
+    ConstImplWithDropGlue(NonTrivialDrop),
+    //~^ ERROR the trait bound
+    ConstDropImplWithBounds::<NonTrivialDrop>(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
new file mode 100644 (file)
index 0000000..1ac62f0
--- /dev/null
@@ -0,0 +1,59 @@
+error: `~const` is not allowed here
+  --> $DIR/const-drop-fail.rs:27:35
+   |
+LL | struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
+   |                                   ^^^^^^^^
+   |
+   = 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:45:5
+   |
+LL |     NonTrivialDrop,
+   |     ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop`
+   |
+note: required by a bound in `check`
+  --> $DIR/const-drop-fail.rs:36:19
+   |
+LL | const fn check<T: ~const Drop>(_: T) {}
+   |                   ^^^^^^^^^^^ required by this bound in `check`
+
+error[E0277]: the trait bound `ConstImplWithDropGlue: Drop` is not satisfied
+  --> $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:36:19
+   |
+LL | const fn check<T: ~const Drop>(_: T) {}
+   |                   ^^^^^^^^^^^ required by this bound in `check`
+
+error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied
+  --> $DIR/const-drop-fail.rs:49:5
+   |
+LL |     ConstDropImplWithBounds::<NonTrivialDrop>(PhantomData),
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `A` is not implemented for `NonTrivialDrop`
+   |
+note: required by `ConstDropImplWithBounds`
+  --> $DIR/const-drop-fail.rs:27:1
+   |
+LL | struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied
+  --> $DIR/const-drop-fail.rs:49:5
+   |
+LL |     ConstDropImplWithBounds::<NonTrivialDrop>(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<T: ~const A>(PhantomData<T>);
+   |                                   ^^^^^^^^ 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.rs b/src/test/ui/rfc-2632-const-trait-impl/const-drop.rs
new file mode 100644 (file)
index 0000000..9a1b554
--- /dev/null
@@ -0,0 +1,81 @@
+// 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);
+
+impl<'a> const Drop for S<'a> {
+    fn drop(&mut self) {
+        *self.0 += 1;
+    }
+}
+
+const fn a<T: ~const Drop>(_: T) {}
+
+const fn b() -> u8 {
+    let mut c = 0;
+    let _ = S(&mut c);
+    a(S(&mut c));
+    c
+}
+
+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<u8>);
+    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);
+}
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 (file)
index 0000000..fe1015b
--- /dev/null
@@ -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<T: ~const A>() where T: ~const B {
+
+    }
+}
+
+const _: () = S::a::<S>();
+
+fn main() {}