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);
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;
}
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
}
}
-/// 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) {
}
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 };
}
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;
#[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>>,
}
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)
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;
}
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
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() {
//! 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;
) -> 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,
}
/// 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 {
}
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)
}
}
// 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);
}
}
}
- /// 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 {
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,
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);
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.
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) }
}
/// ImplSource for a trait alias.
TraitAlias(ImplSourceTraitAliasData<'tcx, N>),
+
+ /// ImplSource for a `const Drop` implementation.
+ ConstDrop(ImplSourceConstDropData),
}
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,
}
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[..],
}
nested: d.nested.into_iter().map(f).collect(),
})
}
+ ImplSource::ConstDrop(ImplSourceConstDropData) => {
+ ImplSource::ConstDrop(ImplSourceConstDropData)
+ }
}
}
}
#[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,
BuiltinObjectCandidate,
BuiltinUnsizeCandidate,
+
+ /// Implementation of `const Drop`.
+ ConstDropCandidate,
}
/// The result of trait evaluation. The order is important
super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d),
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
+
+ super::ImplSource::ConstDrop(ref d) => write!(f, "{:?}", d),
}
}
}
super::IfExpressionCause,
super::ImplSourceDiscriminantKindData,
super::ImplSourcePointeeData,
+ super::ImplSourceConstDropData,
}
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};
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());
pub struct Destructor {
/// The `DefId` of the destructor method
pub did: DefId,
+ /// The constness of the destructor method
+ pub constness: hir::Constness,
}
bitflags! {
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
}
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,
| 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,
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;
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
}
}
}
+
+ 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(©_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() {
+ 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(())
+ }
}
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,
let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
Ok(ImplSource::TraitUpcasting(data))
}
+
+ ConstDropCandidate => Ok(ImplSource::ConstDrop(ImplSourceConstDropData)),
}
}
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);
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,
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);
}
}
}
(
BuiltinCandidate { has_nested: false }
| DiscriminantKindCandidate
- | PointeeCandidate,
+ | PointeeCandidate
+ | ConstDropCandidate,
_,
) => true,
(
_,
BuiltinCandidate { has_nested: false }
| DiscriminantKindCandidate
- | PointeeCandidate,
+ | PointeeCandidate
+ | ConstDropCandidate,
) => false,
(ParamCandidate(other), ParamCandidate(victim)) => {
| traits::ImplSource::TraitAlias(..)
| traits::ImplSource::DiscriminantKind(..)
| traits::ImplSource::Pointee(..)
- | traits::ImplSource::TraitUpcasting(_) => None,
+ | traits::ImplSource::TraitUpcasting(_)
+ | traits::ImplSource::ConstDrop(_) => None,
})
}
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
}
--- /dev/null
+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`.
--- /dev/null
+// 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() {}
--- /dev/null
+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`.
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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() {}