Auto merge of #93028 - compiler-errors:const_drop_bounds, r=fee1-dead

Check `const Drop` impls considering `~const` Bounds

 This PR adds logic to trait selection to account for `~const` bounds in custom `impl const Drop` for types, elaborates the `const Drop` check in `rustc_const_eval` to check those bounds, and steals some drop linting fixes from #92922, thanks `@DrMeepster.`

r? `@fee1-dead` `@oli-obk` <sup>(edit: guess I can't request review from two people, lol)</sup>
since each of you wrote and reviewed #88558, respectively.

Since the logic here is more complicated than what existed, it's possible that this is a perf regression. But it works correctly with tests, and that makes me happy.

Fixes #92881
This commit is contained in:
bors 2022-01-24 08:05:37 +00:00
commit ef119d704d
13 changed files with 384 additions and 244 deletions

View file

@ -4,11 +4,12 @@
use rustc_errors::ErrorReported;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::TraitEngine;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::{
self, ImplSource, Obligation, ObligationCause, SelectionContext,
self, FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext,
};
use super::ConstCx;
@ -145,15 +146,10 @@ fn in_qualifs(qualifs: &ConstQualifs) -> bool {
qualifs.needs_non_const_drop
}
fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, mut ty: Ty<'tcx>) -> bool {
// Avoid selecting for simple cases.
match ty::util::needs_drop_components(ty, &cx.tcx.data_layout).as_deref() {
Ok([]) => return false,
Err(ty::util::AlwaysRequiresDrop) => return true,
// If we've got a single component, select with that
// to increase the chance that we hit the selection cache.
Ok([t]) => ty = t,
Ok([..]) => {}
fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
// Avoid selecting for simple cases, such as builtin types.
if ty::util::is_trivially_const_drop(ty) {
return false;
}
let Some(drop_trait) = cx.tcx.lang_items().drop_trait() else {
@ -161,28 +157,50 @@ fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, mut ty: Ty<'tcx>) -> bool {
// 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,
trait_ref: ty::TraitRef {
def_id: drop_trait,
substs: cx.tcx.mk_substs_trait(ty, &[]),
},
constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
}),
);
let implsrc = cx.tcx.infer_ctxt().enter(|infcx| {
cx.tcx.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
selcx.select(&obligation)
});
!matches!(
implsrc,
Ok(Some(
let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
// If we couldn't select a const drop candidate, then it's bad
return true;
};
if !matches!(
impl_src,
ImplSource::ConstDrop(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
))
)
) {
// If our const drop candidate is not ConstDrop or implied by the param env,
// then it's bad
return true;
}
if impl_src.borrow_nested_obligations().is_empty() {
return false;
}
// If we successfully found one, then select all of the predicates
// implied by our const drop impl.
let mut fcx = FulfillmentContext::new();
for nested in impl_src.nested_obligations() {
fcx.register_predicate_obligation(&infcx, nested);
}
// If we had any errors, then it's bad
!fcx.select_all_or_error(&infcx).is_empty()
})
}
fn in_adt_inherently<'tcx>(

View file

@ -566,7 +566,7 @@ pub enum ImplSource<'tcx, N> {
TraitAlias(ImplSourceTraitAliasData<'tcx, N>),
/// ImplSource for a `const Drop` implementation.
ConstDrop(ImplSourceConstDropData),
ConstDrop(ImplSourceConstDropData<N>),
}
impl<'tcx, N> ImplSource<'tcx, N> {
@ -581,10 +581,10 @@ pub fn nested_obligations(self) -> Vec<N> {
ImplSource::Object(d) => d.nested,
ImplSource::FnPointer(d) => d.nested,
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
| ImplSource::Pointee(ImplSourcePointeeData)
| ImplSource::ConstDrop(ImplSourceConstDropData) => Vec::new(),
| ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
ImplSource::TraitAlias(d) => d.nested,
ImplSource::TraitUpcasting(d) => d.nested,
ImplSource::ConstDrop(i) => i.nested,
}
}
@ -599,10 +599,10 @@ pub fn borrow_nested_obligations(&self) -> &[N] {
ImplSource::Object(d) => &d.nested,
ImplSource::FnPointer(d) => &d.nested,
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
| ImplSource::Pointee(ImplSourcePointeeData)
| ImplSource::ConstDrop(ImplSourceConstDropData) => &[],
| ImplSource::Pointee(ImplSourcePointeeData) => &[],
ImplSource::TraitAlias(d) => &d.nested,
ImplSource::TraitUpcasting(d) => &d.nested,
ImplSource::ConstDrop(i) => &i.nested,
}
}
@ -661,9 +661,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)
}
ImplSource::ConstDrop(i) => ImplSource::ConstDrop(ImplSourceConstDropData {
nested: i.nested.into_iter().map(f).collect(),
}),
}
}
}
@ -755,8 +755,10 @@ 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 ImplSourceConstDropData<N> {
pub nested: Vec<N>,
}
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
pub struct ImplSourceTraitAliasData<'tcx, N> {

View file

@ -146,8 +146,8 @@ pub enum SelectionCandidate<'tcx> {
BuiltinUnsizeCandidate,
/// Implementation of `const Drop`.
ConstDropCandidate,
/// Implementation of `const Drop`, optionally from a custom `impl const Drop`.
ConstDropCandidate(Option<DefId>),
}
/// The result of trait evaluation. The order is important

View file

@ -120,6 +120,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
}
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDropData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ImplSourceConstDropData(nested={:?})", self.nested)
}
}
///////////////////////////////////////////////////////////////////////////
// Lift implementations
@ -127,5 +133,4 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
super::IfExpressionCause,
super::ImplSourceDiscriminantKindData,
super::ImplSourcePointeeData,
super::ImplSourceConstDropData,
}

View file

@ -1041,6 +1041,42 @@ pub fn needs_drop_components<'tcx>(
}
}
pub fn is_trivially_const_drop<'tcx>(ty: Ty<'tcx>) -> bool {
match *ty.kind() {
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Infer(ty::IntVar(_))
| ty::Infer(ty::FloatVar(_))
| ty::Str
| ty::RawPtr(_)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(_)
| ty::Never
| ty::Foreign(_) => true,
ty::Opaque(..)
| ty::Dynamic(..)
| ty::Error(_)
| ty::Bound(..)
| ty::Param(_)
| ty::Placeholder(_)
| ty::Projection(_)
| ty::Infer(_) => false,
// Not trivial because they have components, and instead of looking inside,
// we'll just perform trait selection.
ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(_) | ty::Adt(..) => false,
ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty),
ty::Tuple(tys) => tys.iter().all(|ty| is_trivially_const_drop(ty.expect_ty())),
}
}
// Does the equivalent of
// ```
// let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();

View file

@ -307,13 +307,7 @@ pub(super) fn assemble_candidates<'o>(
} else if lang_items.drop_trait() == Some(def_id)
&& obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst
{
if obligation.param_env.constness() == hir::Constness::Const {
self.assemble_const_drop_candidates(obligation, stack, &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)
}
self.assemble_const_drop_candidates(obligation, &mut candidates);
} else {
if lang_items.clone_trait() == Some(def_id) {
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@ -918,139 +912,77 @@ fn assemble_builtin_bound_candidates(
}
}
fn assemble_const_drop_candidates<'a>(
fn assemble_const_drop_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
obligation_stack: &TraitObligationStack<'a, '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 new_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,
polarity: ty::ImplPolarity::Positive,
}));
copy_obligation.recursion_depth = depth + 1;
self.assemble_candidates_from_impls(&copy_obligation, &mut new_candidates);
let copy_conditions = self.copy_clone_conditions(&copy_obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut new_candidates);
let copy_stack = self.push_stack(obligation_stack.list(), &copy_obligation);
self.assemble_candidates_from_caller_bounds(&copy_stack, &mut new_candidates)?;
let const_drop_obligation =
obligation.with(obligation.predicate.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef {
def_id: self.tcx().require_lang_item(hir::LangItem::Drop, None),
substs: self.tcx().mk_substs_trait(ty, &[]),
},
constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
}));
let const_drop_stack = self.push_stack(obligation_stack.list(), &const_drop_obligation);
self.assemble_candidates_from_caller_bounds(&const_drop_stack, &mut new_candidates)?;
if !new_candidates.vec.is_empty() {
noreturn = true;
}
debug!(?new_candidates.vec, "assemble_const_drop_candidates");
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) => {
let substs = substs.as_closure();
let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty());
stack.push((ty, depth + 1));
}
ty::Generator(_, substs, _) => {
let substs = substs.as_generator();
let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty());
stack.push((ty, 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");
) {
// If the predicate is `~const Drop` in a non-const environment, we don't actually need
// to check anything. We'll short-circuit checking any obligations in confirmation, too.
if obligation.param_env.constness() == hir::Constness::NotConst {
candidates.vec.push(ConstDropCandidate(None));
return;
}
// all types have passed.
candidates.vec.push(ConstDropCandidate);
Ok(())
let self_ty = self.infcx().shallow_resolve(obligation.self_ty());
match self_ty.skip_binder().kind() {
ty::Opaque(..)
| ty::Dynamic(..)
| ty::Error(_)
| ty::Bound(..)
| ty::Param(_)
| ty::Placeholder(_)
| ty::Projection(_) => {
// We don't know if these are `~const Drop`, at least
// not structurally... so don't push a candidate.
}
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Infer(ty::IntVar(_))
| ty::Infer(ty::FloatVar(_))
| ty::Str
| ty::RawPtr(_)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(_)
| ty::Never
| ty::Foreign(_)
| ty::Array(..)
| ty::Slice(_)
| ty::Closure(..)
| ty::Generator(..)
| ty::Tuple(_)
| ty::GeneratorWitness(_) => {
// These are built-in, and cannot have a custom `impl const Drop`.
candidates.vec.push(ConstDropCandidate(None));
}
ty::Adt(..) => {
// Find a custom `impl Drop` impl, if it exists
let relevant_impl = self.tcx().find_map_relevant_impl(
obligation.predicate.def_id(),
obligation.predicate.skip_binder().trait_ref.self_ty(),
Some,
);
if let Some(impl_def_id) = relevant_impl {
// Check that `impl Drop` is actually const, if there is a custom impl
if self.tcx().impl_constness(impl_def_id) == hir::Constness::Const {
candidates.vec.push(ConstDropCandidate(Some(impl_def_id)));
}
} else {
// Otherwise check the ADT like a built-in type (structurally)
candidates.vec.push(ConstDropCandidate(None));
}
}
ty::Infer(_) => {
candidates.ambiguous = true;
}
}
}
}

View file

@ -80,7 +80,6 @@ pub(super) fn confirm_candidate(
param_env: obligation.param_env.without_const(),
..*obligation
};
obligation = &new_obligation;
}
@ -159,7 +158,10 @@ pub(super) fn confirm_candidate(
Ok(ImplSource::TraitUpcasting(data))
}
ConstDropCandidate => Ok(ImplSource::ConstDrop(ImplSourceConstDropData)),
ConstDropCandidate(def_id) => {
let data = self.confirm_const_drop_candidate(obligation, def_id)?;
Ok(ImplSource::ConstDrop(data))
}
}
}
@ -1087,4 +1089,128 @@ fn confirm_builtin_unsize_candidate(
Ok(ImplSourceBuiltinData { nested })
}
fn confirm_const_drop_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
impl_def_id: Option<DefId>,
) -> Result<ImplSourceConstDropData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// `~const Drop` in a non-const environment is always trivially true, since our type is `Drop`
if obligation.param_env.constness() == Constness::NotConst {
return Ok(ImplSourceConstDropData { nested: vec![] });
}
let tcx = self.tcx();
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
let mut nested = vec![];
let cause = obligation.derived_cause(BuiltinDerivedObligation);
// If we have a custom `impl const Drop`, then
// first check it like a regular impl candidate
if let Some(impl_def_id) = impl_def_id {
nested.extend(self.confirm_impl_candidate(obligation, impl_def_id).nested);
}
// We want to confirm the ADT's fields if we have an ADT
let mut stack = match *self_ty.skip_binder().kind() {
ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(tcx, substs)).collect(),
_ => vec![self_ty.skip_binder()],
};
while let Some(nested_ty) = stack.pop() {
match *nested_ty.kind() {
// We know these types are trivially drop
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Infer(ty::IntVar(_))
| ty::Infer(ty::FloatVar(_))
| ty::Str
| ty::RawPtr(_)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(_)
| ty::Never
| ty::Foreign(_) => {}
// These types are built-in, so we can fast-track by registering
// nested predicates for their constituient type(s)
ty::Array(ty, _) | ty::Slice(ty) => {
stack.push(ty);
}
ty::Tuple(tys) => {
stack.extend(tys.iter().map(|ty| ty.expect_ty()));
}
ty::Closure(_, substs) => {
stack.push(substs.as_closure().tupled_upvars_ty());
}
ty::Generator(_, substs, _) => {
let generator = substs.as_generator();
stack.extend([generator.tupled_upvars_ty(), generator.witness()]);
}
ty::GeneratorWitness(tys) => {
stack.extend(tcx.erase_late_bound_regions(tys).to_vec());
}
// If we have a projection type, make sure to normalize it so we replace it
// with a fresh infer variable
ty::Projection(..) => {
self.infcx.commit_unconditionally(|_| {
let predicate = normalize_with_depth_to(
self,
obligation.param_env,
cause.clone(),
obligation.recursion_depth + 1,
self_ty
.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef {
def_id: self.tcx().require_lang_item(LangItem::Drop, None),
substs: self.tcx().mk_substs_trait(nested_ty, &[]),
},
constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
})
.to_predicate(tcx),
&mut nested,
);
nested.push(Obligation::with_depth(
cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
predicate,
));
});
}
// If we have any other type (e.g. an ADT), just register a nested obligation
// since it's either not `const Drop` (and we raise an error during selection),
// or it's an ADT (and we need to check for a custom impl during selection)
_ => {
let predicate = self_ty
.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef {
def_id: self.tcx().require_lang_item(LangItem::Drop, None),
substs: self.tcx().mk_substs_trait(nested_ty, &[]),
},
constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
})
.to_predicate(tcx);
nested.push(Obligation::with_depth(
cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
predicate,
));
}
}
}
Ok(ImplSourceConstDropData { nested })
}
}

View file

@ -1153,7 +1153,7 @@ fn filter_impls(
GeneratorCandidate => {}
// FnDef where the function is const
FnPointerCandidate { is_const: true } => {}
ConstDropCandidate => {}
ConstDropCandidate(_) => {}
_ => {
// reject all other types of candidates
continue;
@ -1537,7 +1537,7 @@ fn candidate_should_be_dropped_in_favor_of(
};
// (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
// and `DiscriminantKindCandidate` to anything else.
// `DiscriminantKindCandidate`, and `ConstDropCandidate` to anything else.
//
// This is a fix for #53123 and prevents winnowing from accidentally extending the
// lifetime of a variable.
@ -1554,7 +1554,7 @@ fn candidate_should_be_dropped_in_favor_of(
BuiltinCandidate { has_nested: false }
| DiscriminantKindCandidate
| PointeeCandidate
| ConstDropCandidate,
| ConstDropCandidate(_),
_,
) => true,
(
@ -1562,7 +1562,7 @@ fn candidate_should_be_dropped_in_favor_of(
BuiltinCandidate { has_nested: false }
| DiscriminantKindCandidate
| PointeeCandidate
| ConstDropCandidate,
| ConstDropCandidate(_),
) => false,
(ParamCandidate(other), ParamCandidate(victim)) => {

View file

@ -229,7 +229,13 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
let p = p.kind();
match (predicate.skip_binder(), p.skip_binder()) {
(ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => {
relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
// Since struct predicates cannot have ~const, project the impl predicate
// onto one that ignores the constness. This is equivalent to saying that
// we match a `Trait` bound on the struct with a `Trait` or `~const Trait`
// in the impl.
let non_const_a =
ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a };
relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok()
}
(ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => {
relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()

View file

@ -1,13 +1,5 @@
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
--> $DIR/const-drop-fail.rs:44:5
|
LL | const _: () = check($exp);
| ----- required by a bound introduced by this call
@ -16,50 +8,51 @@ LL | NonTrivialDrop,
| ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop`
|
note: required by a bound in `check`
--> $DIR/const-drop-fail.rs:36:19
--> $DIR/const-drop-fail.rs:35: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
error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied in `ConstImplWithDropGlue`
--> $DIR/const-drop-fail.rs:46:5
|
LL | const _: () = check($exp);
| ----- required by a bound introduced by this call
...
LL | ConstImplWithDropGlue(NonTrivialDrop),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `ConstImplWithDropGlue`, the trait `Drop` is not implemented for `NonTrivialDrop`
|
note: required because it appears within the type `ConstImplWithDropGlue`
--> $DIR/const-drop-fail.rs:17:8
|
LL | struct ConstImplWithDropGlue(NonTrivialDrop);
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check`
--> $DIR/const-drop-fail.rs:36:19
--> $DIR/const-drop-fail.rs:35: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 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[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied
--> $DIR/const-drop-fail.rs:49:5
--> $DIR/const-drop-fail.rs:48:5
|
LL | const _: () = check($exp);
| ----- required by a bound introduced by this call
...
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
note: required because of the requirements on the impl of `Drop` for `ConstDropImplWithBounds<NonTrivialDrop>`
--> $DIR/const-drop-fail.rs:29:25
|
LL | struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
| ^^^^^^^^ required by this bound in `ConstDropImplWithBounds`
LL | impl<T: ~const A> const Drop for ConstDropImplWithBounds<T> {
| ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check`
--> $DIR/const-drop-fail.rs:35:19
|
LL | const fn check<T: ~const Drop>(_: T) {}
| ^^^^^^^^^^^ required by this bound in `check`
error: aborting due to 5 previous errors
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -24,8 +24,7 @@ fn drop(&mut self) {}
impl A for NonTrivialDrop {}
struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
//~^ ERROR `~const` is not allowed
struct ConstDropImplWithBounds<T: A>(PhantomData<T>);
impl<T: ~const A> const Drop for ConstDropImplWithBounds<T> {
fn drop(&mut self) {
@ -48,7 +47,6 @@ macro_rules! check_all {
//~^ ERROR the trait bound
ConstDropImplWithBounds::<NonTrivialDrop>(PhantomData),
//~^ ERROR the trait bound
//~| ERROR the trait bound
}
fn main() {}

View file

@ -1,13 +1,5 @@
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
--> $DIR/const-drop-fail.rs:44:5
|
LL | const _: () = check($exp);
| ----- required by a bound introduced by this call
@ -16,50 +8,51 @@ LL | NonTrivialDrop,
| ^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `NonTrivialDrop`
|
note: required by a bound in `check`
--> $DIR/const-drop-fail.rs:36:19
--> $DIR/const-drop-fail.rs:35: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
error[E0277]: the trait bound `NonTrivialDrop: Drop` is not satisfied in `ConstImplWithDropGlue`
--> $DIR/const-drop-fail.rs:46:5
|
LL | const _: () = check($exp);
| ----- required by a bound introduced by this call
...
LL | ConstImplWithDropGlue(NonTrivialDrop),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Drop` is not implemented for `ConstImplWithDropGlue`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `ConstImplWithDropGlue`, the trait `Drop` is not implemented for `NonTrivialDrop`
|
note: required because it appears within the type `ConstImplWithDropGlue`
--> $DIR/const-drop-fail.rs:17:8
|
LL | struct ConstImplWithDropGlue(NonTrivialDrop);
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check`
--> $DIR/const-drop-fail.rs:36:19
--> $DIR/const-drop-fail.rs:35: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 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[E0277]: the trait bound `NonTrivialDrop: A` is not satisfied
--> $DIR/const-drop-fail.rs:49:5
--> $DIR/const-drop-fail.rs:48:5
|
LL | const _: () = check($exp);
| ----- required by a bound introduced by this call
...
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
note: required because of the requirements on the impl of `Drop` for `ConstDropImplWithBounds<NonTrivialDrop>`
--> $DIR/const-drop-fail.rs:29:25
|
LL | struct ConstDropImplWithBounds<T: ~const A>(PhantomData<T>);
| ^^^^^^^^ required by this bound in `ConstDropImplWithBounds`
LL | impl<T: ~const A> const Drop for ConstDropImplWithBounds<T> {
| ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check`
--> $DIR/const-drop-fail.rs:35:19
|
LL | const fn check<T: ~const Drop>(_: T) {}
| ^^^^^^^^^^^ required by this bound in `check`
error: aborting due to 5 previous errors
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -3,6 +3,7 @@
#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]
#![feature(const_mut_refs)]
#![feature(never_type)]
#![cfg_attr(precise, feature(const_precise_live_drops))]
struct S<'a>(&'a mut u8);
@ -45,6 +46,33 @@ fn drop(&mut self) {}
pub struct HasConstDrop(pub ConstDrop);
pub struct TrivialFields(pub u8, pub i8, pub usize, pub isize);
pub trait SomeTrait {
fn foo();
}
impl const SomeTrait for () {
fn foo() {}
}
// non-const impl
impl SomeTrait for i32 {
fn foo() {}
}
pub struct ConstDropWithBound<T: SomeTrait>(pub core::marker::PhantomData<T>);
impl<T: ~const SomeTrait> const Drop for ConstDropWithBound<T> {
fn drop(&mut self) {
T::foo();
}
}
pub struct ConstDropWithNonconstBound<T: SomeTrait>(pub core::marker::PhantomData<T>);
impl<T: SomeTrait> const Drop for ConstDropWithNonconstBound<T> {
fn drop(&mut self) {
// Note: we DON'T use the `T: SomeTrait` bound
}
}
}
use t::*;
@ -61,6 +89,9 @@ fn drop(&mut self) {}
TrivialFields(1, 2, 3, 4),
&1,
&1 as *const i32,
ConstDropWithBound::<()>,
ConstDropWithNonconstBound::<i32>,
Result::<i32, !>::Ok(1),
}
fn main() {