1
Fork 0

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:
bors 2021-09-15 03:51:03 +00:00
commit cdeba02ff7
26 changed files with 554 additions and 108 deletions

View file

@ -1477,7 +1477,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
}
super::ImplSource::AutoImpl(..)
| super::ImplSource::Builtin(..)
| super::ImplSource::TraitUpcasting(_) => {
| super::ImplSource::TraitUpcasting(_)
| super::ImplSource::ConstDrop(_) => {
// These traits have no associated types.
selcx.tcx().sess.delay_span_bug(
obligation.cause.span,
@ -1549,7 +1550,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
| super::ImplSource::Param(..)
| super::ImplSource::Builtin(..)
| super::ImplSource::TraitUpcasting(_)
| super::ImplSource::TraitAlias(..) => {
| super::ImplSource::TraitAlias(..)
| super::ImplSource::ConstDrop(_) => {
// we don't create Select candidates with this kind of resolution
span_bug!(
obligation.cause.span,

View file

@ -8,7 +8,7 @@
use rustc_hir as hir;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TypeFoldable};
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_target::spec::abi::Abi;
use crate::traits::coherence::Conflict;
@ -277,6 +277,16 @@ 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(&copy_obligation, &mut copy_candidates);
let copy_conditions = self.copy_clone_conditions(&copy_obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut copy_candidates);
if !copy_candidates.vec.is_empty() {
noreturn = true;
}
debug!(?copy_candidates.vec, "assemble_const_drop_candidates - copy");
match ty.kind() {
ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Infer(ty::IntVar(_))
| ty::Infer(ty::FloatVar(_))
| ty::FnPtr(_)
| ty::Never
| ty::Ref(..)
| ty::FnDef(..)
| ty::RawPtr(_)
| ty::Bool
| ty::Char
| ty::Str
| ty::Foreign(_) => {} // Do nothing. These types satisfy `const Drop`.
ty::Adt(def, subst) => {
let mut set = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
self.assemble_candidates_from_impls(
&obligation.with(obligation.predicate.map_bound(|mut pred| {
pred.trait_ref.substs = self.tcx().mk_substs_trait(ty, &[]);
pred
})),
&mut set,
);
stack.extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1)));
debug!(?set.vec, "assemble_const_drop_candidates - ty::Adt");
if set.vec.into_iter().any(|candidate| {
if let SelectionCandidate::ImplCandidate(did) = candidate {
matches!(self.tcx().impl_constness(did), hir::Constness::NotConst)
} else {
false
}
}) {
if !noreturn {
// has non-const Drop
return Ok(());
}
debug!("not returning");
}
}
ty::Array(ty, _) => stack.push((ty, depth + 1)),
ty::Tuple(_) => stack.extend(ty.tuple_fields().map(|t| (t, depth + 1))),
ty::Closure(_, substs) => {
stack.extend(substs.as_closure().upvar_tys().map(|t| (t, depth + 1)))
}
ty::Generator(_, substs, _) => {
let substs = substs.as_generator();
stack.extend(substs.upvar_tys().map(|t| (t, depth + 1)));
stack.push((substs.witness(), depth + 1));
}
ty::GeneratorWitness(tys) => stack.extend(
self.tcx().erase_late_bound_regions(*tys).iter().map(|t| (t, depth + 1)),
),
ty::Slice(ty) => stack.push((ty, depth + 1)),
ty::Opaque(..)
| ty::Dynamic(..)
| ty::Error(_)
| ty::Bound(..)
| ty::Infer(_)
| ty::Placeholder(_)
| ty::Projection(..)
| ty::Param(..) => {
if !noreturn {
return Ok(());
}
debug!("not returning");
}
}
debug!(?stack, "assemble_const_drop_candidates - in loop");
}
// all types have passed.
candidates.vec.push(ConstDropCandidate);
Ok(())
}
}

View file

@ -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)),
}
}

View file

@ -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)) => {