Skip to content

Commit

Permalink
Auto merge of rust-lang#93028 - compiler-errors:const_drop_bounds, r=…
Browse files Browse the repository at this point in the history
…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 rust-lang#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 rust-lang#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 rust-lang#92881
  • Loading branch information
bors committed Jan 24, 2022
2 parents 42313dd + b7e4433 commit ef119d7
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 238 deletions.
60 changes: 39 additions & 21 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 @@ -145,44 +146,61 @@ impl Qualif for NeedsNonConstDrop {
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 {
// 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,
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>(
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,
}
36 changes: 36 additions & 0 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]>>();
Expand Down
Loading

0 comments on commit ef119d7

Please sign in to comment.