mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-02 18:12:51 +00:00
Auto merge of #88558 - fee1-dead:const-drop, r=oli-obk
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`
This commit is contained in:
commit
cdeba02ff7
@ -1669,7 +1669,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);
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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<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 @@ 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)
|
||||
@ -988,12 +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_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;
|
||||
}
|
||||
|
||||
|
@ -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,10 +78,10 @@ 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) {
|
||||
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() {
|
||||
|
@ -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 @@ 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 {
|
||||
@ -108,11 +112,37 @@ impl Qualif for NeedsDrop {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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::<qualifs::NeedsDrop>(place.local) {
|
||||
if self.qualif_local::<qualifs::NeedsNonConstDrop>(place.local) {
|
||||
return Err(Unpromotable);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -86,6 +86,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;
|
||||
use rustc_middle::ty::PredicateKind::*;
|
||||
|
||||
let predicates = cx.tcx.explicit_predicates_of(item.def_id);
|
||||
@ -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.
|
||||
|
@ -552,14 +552,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) }
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -143,6 +143,9 @@ pub enum SelectionCandidate<'tcx> {
|
||||
BuiltinObjectCandidate,
|
||||
|
||||
BuiltinUnsizeCandidate,
|
||||
|
||||
/// Implementation of `const Drop`.
|
||||
ConstDropCandidate,
|
||||
}
|
||||
|
||||
/// The result of trait evaluation. The order is important
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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! {
|
||||
|
@ -324,16 +324,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
|
||||
|
@ -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,
|
||||
|
@ -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 @@ 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 {
|
||||
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 @@ 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() {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1038,6 +1038,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1);
|
||||
}
|
||||
|
||||
fn check_recursion_depth<T: Display + TypeFoldable<'tcx>>(
|
||||
&self,
|
||||
depth: usize,
|
||||
error_obligation: &Obligation<'tcx, T>,
|
||||
) -> Result<(), OverflowError> {
|
||||
if !self.infcx.tcx.recursion_limit().value_within_limit(depth) {
|
||||
match self.query_mode {
|
||||
TraitQueryMode::Standard => {
|
||||
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` (`?`)
|
||||
@ -1047,17 +1065,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
obligation: &Obligation<'tcx, T>,
|
||||
error_obligation: &Obligation<'tcx, V>,
|
||||
) -> Result<(), OverflowError> {
|
||||
if !self.infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
|
||||
match self.query_mode {
|
||||
TraitQueryMode::Standard => {
|
||||
self.infcx().report_overflow_error(error_obligation, true);
|
||||
}
|
||||
TraitQueryMode::Canonical => {
|
||||
return Err(OverflowError);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
self.check_recursion_depth(obligation.recursion_depth, error_obligation)
|
||||
}
|
||||
|
||||
fn in_task<OP, R>(&mut self, op: OP) -> (R, DepNodeIndex)
|
||||
@ -1079,28 +1087,23 @@ 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)
|
||||
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 @@ 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)) => {
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,15 @@ use rustc_span::{sym, DUMMY_SP};
|
||||
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
|
||||
}
|
||||
|
@ -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`.
|
54
src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs
Normal file
54
src/test/ui/rfc-2632-const-trait-impl/const-drop-fail.rs
Normal file
@ -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() {}
|
@ -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`.
|
81
src/test/ui/rfc-2632-const-trait-impl/const-drop.rs
Normal file
81
src/test/ui/rfc-2632-const-trait-impl/const-drop.rs
Normal file
@ -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);
|
||||
}
|
@ -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() {}
|
Loading…
Reference in New Issue
Block a user