Skip to content

Commit

Permalink
Check const Drop impls considering ConstIfConst bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Jan 18, 2022
1 parent f9eb0b3 commit 6ed42a7
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 161 deletions.
43 changes: 31 additions & 12 deletions compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -161,28 +162,46 @@ impl Qualif for NeedsNonConstDrop {
// 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 param,
// then it's bad
return true;
}

// 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>(
Expand Down
22 changes: 12 additions & 10 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -581,10 +581,10 @@ impl<'tcx, N> ImplSource<'tcx, 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,
}
}

Expand All @@ -599,10 +599,10 @@ impl<'tcx, N> ImplSource<'tcx, 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,
}
}

Expand Down Expand Up @@ -661,9 +661,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
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(),
}),
}
}
}
Expand Down Expand Up @@ -755,8 +755,10 @@ 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 ImplSourceConstDropData<N> {
pub nested: Vec<N>,
}

#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
pub struct ImplSourceTraitAliasData<'tcx, N> {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_middle/src/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,17 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx,
}
}

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

TrivialTypeFoldableAndLiftImpls! {
super::IfExpressionCause,
super::ImplSourceDiscriminantKindData,
super::ImplSourcePointeeData,
super::ImplSourceConstDropData,
}
192 changes: 62 additions & 130 deletions compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
} 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
Expand Down Expand Up @@ -918,139 +912,77 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}

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))),
) {
// 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;
}

ty::Closure(_, substs) => {
let substs = substs.as_closure();
let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty());
stack.push((ty, depth + 1));
}
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::Never
| ty::Foreign(_)
| ty::Projection(_) => {
// We don't know if these are `~const Drop`, at least
// not structurally... so don't push a candidate.
}

ty::Generator(_, substs, _) => {
let substs = substs.as_generator();
let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty());
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::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));
}

stack.push((ty, depth + 1));
stack.push((substs.witness(), depth + 1));
}
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,
);

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(());
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)));
}
debug!("not returning");
} else {
// Otherwise check the ADT like a built-in type (structurally)
candidates.vec.push(ConstDropCandidate(None));
}
}
debug!(?stack, "assemble_const_drop_candidates - in loop");
}
// all types have passed.
candidates.vec.push(ConstDropCandidate);

Ok(())
ty::Infer(_) => {
candidates.ambiguous = true;
}
}
}
}
Loading

0 comments on commit 6ed42a7

Please sign in to comment.