Skip to content

Elaborate destruct host effect clauses with structurally implied clauses #144856

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,19 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
ty::BoundConstness::Const
}
};

// Only elaborate host-effect destruct obligations when comparing impl method bounds
// against the corresponding trait method bounds.
let elaborate = matches!(
tcx.hir_node_by_def_id(body_id),
hir::Node::ImplItem(hir::ImplItem {
trait_item_def_id: Some(did),
..
}) if *did == callee,
);
let param_env =
if elaborate { param_env.elaborate_host_effect_destruct(tcx) } else { param_env };

let const_conditions =
ocx.normalize(&ObligationCause::misc(call_span, body_id), param_env, const_conditions);
ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, span)| {
Expand Down
18 changes: 17 additions & 1 deletion compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,10 +922,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ObligationCauseCode::WhereClause(callee_did, pred_span)
},
);

// Only elaborate host-effect destruct obligations when comparing impl method
// bounds against the corresponding trait method bounds.
let elaborate = matches!(
self.tcx.hir_node_by_def_id(self.body_id),
hir::Node::ImplItem(hir::ImplItem {
trait_item_def_id: Some(did),
..
}) if *did == callee_did,
);
let param_env = if elaborate {
self.param_env.elaborate_host_effect_destruct(self.tcx)
} else {
self.param_env
};

self.register_predicate(Obligation::new(
self.tcx,
cause,
self.param_env,
param_env,
cond.to_host_effect_clause(self.tcx, host),
));
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
self.is_manually_drop()
}

fn all_fields(self) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = &'tcx FieldDef>> {
ty::EarlyBinder::bind(self.all_fields())
}

fn all_field_tys(
self,
tcx: TyCtxt<'tcx>,
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type AllocId = crate::mir::interpret::AllocId;
type Pat = Pattern<'tcx>;
type PatList = &'tcx List<Pattern<'tcx>>;
type Visibility = Visibility<DefId>;
type Safety = hir::Safety;
type Abi = ExternAbi;
type Const = ty::Const<'tcx>;
Expand Down Expand Up @@ -238,6 +239,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.adt_def(adt_def_id)
}

type FieldDef = &'tcx ty::FieldDef;

fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind {
match self.def_kind(alias.def_id) {
DefKind::AssocTy => {
Expand Down Expand Up @@ -806,6 +809,12 @@ impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for ExternAbi {
}
}

impl<'tcx> rustc_type_ir::inherent::Visibility<TyCtxt<'tcx>> for Visibility<DefId> {
fn is_public(self) -> bool {
matches!(self, Visibility::Public)
}
}

impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
fn safe() -> Self {
hir::Safety::Safe
Expand Down
22 changes: 21 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ pub use rustc_type_ir::fast_reject::DeepRejectCtxt;
)]
use rustc_type_ir::inherent;
pub use rustc_type_ir::relate::VarianceDiagInfo;
pub use rustc_type_ir::solve::SizedTraitKind;
pub use rustc_type_ir::solve::{
DestructConstCondition, SizedTraitKind, const_conditions_for_destruct,
};
pub use rustc_type_ir::*;
#[allow(hidden_glob_reexports, unused_imports)]
use rustc_type_ir::{InferCtxtLike, Interner};
Expand Down Expand Up @@ -1009,6 +1011,14 @@ impl<'tcx> ParamEnv<'tcx> {
pub fn and<T: TypeVisitable<TyCtxt<'tcx>>>(self, value: T) -> ParamEnvAnd<'tcx, T> {
ParamEnvAnd { param_env: self, value }
}

pub fn elaborate_host_effect_destruct(self, tcx: TyCtxt<'tcx>) -> Self {
let caller_bounds = tcx.mk_clauses_from_iter(
ty::elaborate::elaborate(tcx, self.caller_bounds.iter())
.elaborate_host_effect_destruct(),
);
ParamEnv { caller_bounds }
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
Expand Down Expand Up @@ -1422,6 +1432,16 @@ impl<'tcx> FieldDef {
}
}

impl<'tcx> rustc_type_ir::inherent::FieldDef<TyCtxt<'tcx>> for &'tcx FieldDef {
fn def_id(self) -> DefId {
self.did
}

fn visibility(self) -> ty::Visibility<DefId> {
self.vis
}
}

#[derive(Debug, PartialEq, Eq)]
pub enum ImplOverlapKind {
/// These impls are always allowed to overlap.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
use tracing::instrument;

use crate::delegate::SolverDelegate;
use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution};
use crate::solve::{EvalCtxt, Goal, NoSolution};

// Calculates the constituent types of a type for `auto trait` purposes.
#[instrument(level = "trace", skip(ecx), ret)]
Expand Down Expand Up @@ -738,87 +738,6 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
}
}

// NOTE: Keep this in sync with `evaluate_host_effect_for_destruct_goal` in
// the old solver, for as long as that exists.
pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
cx: I,
self_ty: I::Ty,
) -> Result<Vec<ty::TraitRef<I>>, NoSolution> {
let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct);

match self_ty.kind() {
// `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it.
ty::Adt(adt_def, _) if adt_def.is_manually_drop() => Ok(vec![]),

// An ADT is `[const] Destruct` only if all of the fields are,
// *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`.
ty::Adt(adt_def, args) => {
let mut const_conditions: Vec<_> = adt_def
.all_field_tys(cx)
.iter_instantiated(cx, args)
.map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty]))
.collect();
match adt_def.destructor(cx) {
// `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`.
Some(AdtDestructorKind::NotConst) => return Err(NoSolution),
// `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold.
Some(AdtDestructorKind::Const) => {
let drop_def_id = cx.require_lang_item(TraitSolverLangItem::Drop);
let drop_trait_ref = ty::TraitRef::new(cx, drop_def_id, [self_ty]);
const_conditions.push(drop_trait_ref);
}
// No `Drop` impl, no need to require anything else.
None => {}
}
Ok(const_conditions)
}

ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
Ok(vec![ty::TraitRef::new(cx, destruct_def_id, [ty])])
}

ty::Tuple(tys) => Ok(tys
.iter()
.map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty]))
.collect()),

// Trivially implement `[const] Destruct`
ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Str
| ty::RawPtr(..)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::Never
| ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
| ty::Error(_) => Ok(vec![]),

// Coroutines and closures could implement `[const] Drop`,
// but they don't really need to right now.
ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(_, _) => Err(NoSolution),

// FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop`
// if their inner type implements it.
ty::UnsafeBinder(_) => Err(NoSolution),

ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => {
Err(NoSolution)
}

ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
panic!("unexpected type `{self_ty:?}`")
}
}
}

/// Assemble a list of predicates that would be present on a theoretical
/// user impl for an object type. These predicates must be checked any time
/// we assemble a built-in object candidate for an object type, since they
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
use rustc_type_ir::fast_reject::DeepRejectCtxt;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::TraitSolverLangItem;
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::solve::inspect::ProbeKind;
use rustc_type_ir::solve::{DestructConstCondition, SizedTraitKind, const_conditions_for_destruct};
use rustc_type_ir::{self as ty, Interner, elaborate};
use tracing::instrument;

Expand Down Expand Up @@ -343,7 +343,11 @@ where
let cx = ecx.cx();

let self_ty = goal.predicate.self_ty();
let const_conditions = structural_traits::const_conditions_for_destruct(cx, self_ty)?;
let const_conditions = match const_conditions_for_destruct(cx, self_ty, false) {
DestructConstCondition::Trivial { .. } => vec![],
DestructConstCondition::Never => return Err(NoSolution),
DestructConstCondition::Structural(trait_refs) => trait_refs,
};

ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.add_goals(
Expand Down
97 changes: 15 additions & 82 deletions compiler/rustc_trait_selection/src/traits/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,95 +311,28 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>(
}
}

// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
fn evaluate_host_effect_for_destruct_goal<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &HostEffectObligation<'tcx>,
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
let tcx = selcx.tcx();
let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, obligation.cause.span);
let self_ty = obligation.predicate.self_ty();

let const_conditions = match *self_ty.kind() {
// `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it.
ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![],

// An ADT is `[const] Destruct` only if all of the fields are,
// *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`.
ty::Adt(adt_def, args) => {
let mut const_conditions: ThinVec<_> = adt_def
.all_fields()
.map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)]))
.collect();
match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) {
// `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`.
Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
// `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold.
Some(hir::Constness::Const) => {
let drop_def_id = tcx.require_lang_item(LangItem::Drop, obligation.cause.span);
let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]);
const_conditions.push(drop_trait_ref);
}
// No `Drop` impl, no need to require anything else.
None => {}
}
const_conditions
}

ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])]
}

ty::Tuple(tys) => {
tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect()
}

// Trivially implement `[const] Destruct`
ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Str
| ty::RawPtr(..)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::Never
| ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
| ty::Error(_) => thin_vec![],

// Coroutines and closures could implement `[const] Drop`,
// but they don't really need to right now.
ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution),

// FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop`
// if their inner type implements it.
ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution),

ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => {
return Err(EvaluationFailure::NoSolution);
}

ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
panic!("unexpected type `{self_ty:?}`")
}
};

Ok(const_conditions
.into_iter()
.map(|trait_ref| {
obligation.with(
tcx,
ty::Binder::dummy(trait_ref)
.to_host_effect_clause(tcx, obligation.predicate.constness),
)
})
.collect())
let const_conditions = ty::const_conditions_for_destruct(tcx, self_ty, false);
match const_conditions {
ty::DestructConstCondition::Trivial { .. } => Ok(thin_vec![]),
ty::DestructConstCondition::Never => Err(EvaluationFailure::NoSolution),
ty::DestructConstCondition::Structural(trait_refs) => Ok(trait_refs
.into_iter()
.map(|trait_ref| {
obligation.with(
tcx,
ty::Binder::dummy(trait_ref)
.to_host_effect_clause(tcx, obligation.predicate.constness),
)
})
.collect()),
}
}

// NOTE: Keep this in sync with `extract_fn_def_from_const_callable` in the new solver.
Expand Down
Loading
Loading