Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check const Drop impls considering ~const Bounds #93028

Merged
merged 8 commits into from
Jan 24, 2022
Merged
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(_)
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
| 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