use rustc_errors::ErrorReported;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits;
use super::ConstCx;
pub fn in_any_value_of_ty(
cx: &ConstCx<'_, 'tcx>,
ty: Ty<'tcx>,
error_occured: Option<ErrorReported>,
) -> ConstQualifs {
ConstQualifs {
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
error_occured,
}
}
pub trait Qualif {
const ANALYSIS_NAME: &'static str;
const IS_CLEARED_ON_MOVE: bool = false;
fn in_qualifs(qualifs: &ConstQualifs) -> bool;
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;
fn in_adt_inherently(
cx: &ConstCx<'_, 'tcx>,
adt: &'tcx AdtDef,
substs: SubstsRef<'tcx>,
) -> bool;
}
pub struct HasMutInterior;
impl Qualif for HasMutInterior {
const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
qualifs.has_mut_interior
}
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
!ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env)
}
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type()
}
}
pub struct NeedsDrop;
impl Qualif for NeedsDrop {
const ANALYSIS_NAME: &'static str = "flow_needs_drop";
const IS_CLEARED_ON_MOVE: bool = true;
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
qualifs.needs_drop
}
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
ty.needs_drop(cx.tcx, cx.param_env)
}
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
adt.has_dtor(cx.tcx)
}
}
pub struct CustomEq;
impl Qualif for CustomEq {
const ANALYSIS_NAME: &'static str = "flow_custom_eq";
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
qualifs.custom_eq
}
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id());
traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some()
}
fn in_adt_inherently(
cx: &ConstCx<'_, 'tcx>,
adt: &'tcx AdtDef,
substs: SubstsRef<'tcx>,
) -> bool {
let ty = cx.tcx.mk_ty(ty::Adt(adt, substs));
!ty.is_structural_eq_shallow(cx.tcx)
}
}
pub fn in_rvalue<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, rvalue: &Rvalue<'tcx>) -> bool
where
Q: Qualif,
F: FnMut(Local) -> bool,
{
match rvalue {
Rvalue::ThreadLocalRef(_) | Rvalue::NullaryOp(..) => {
Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx))
}
Rvalue::Discriminant(place) | Rvalue::Len(place) => {
in_place::<Q, _>(cx, in_local, place.as_ref())
}
Rvalue::Use(operand)
| Rvalue::Repeat(operand, _)
| Rvalue::UnaryOp(_, operand)
| Rvalue::Cast(_, operand, _) => in_operand::<Q, _>(cx, in_local, operand),
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
in_operand::<Q, _>(cx, in_local, lhs) || in_operand::<Q, _>(cx, in_local, rhs)
}
Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty;
if let ty::Ref(..) = base_ty.kind() {
return in_place::<Q, _>(
cx,
in_local,
PlaceRef { local: place.local, projection: proj_base },
);
}
}
in_place::<Q, _>(cx, in_local, place.as_ref())
}
Rvalue::Aggregate(kind, operands) => {
if let AggregateKind::Adt(def, _, substs, ..) = **kind {
if Q::in_adt_inherently(cx, def, substs) {
return true;
}
}
operands.iter().any(|o| in_operand::<Q, _>(cx, in_local, o))
}
}
}
pub fn in_place<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: PlaceRef<'tcx>) -> bool
where
Q: Qualif,
F: FnMut(Local) -> bool,
{
let mut projection = place.projection;
while let &[ref proj_base @ .., proj_elem] = projection {
match proj_elem {
ProjectionElem::Index(index) if in_local(index) => return true,
ProjectionElem::Deref
| ProjectionElem::Field(_, _)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(_, _)
| ProjectionElem::Index(_) => {}
}
let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx);
let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty;
if !Q::in_any_value_of_ty(cx, proj_ty) {
return false;
}
projection = proj_base;
}
assert!(projection.is_empty());
in_local(place.local)
}
pub fn in_operand<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, operand: &Operand<'tcx>) -> bool
where
Q: Qualif,
F: FnMut(Local) -> bool,
{
let constant = match operand {
Operand::Copy(place) | Operand::Move(place) => {
return in_place::<Q, _>(cx, in_local, place.as_ref());
}
Operand::Constant(c) => c,
};
if let ty::ConstKind::Unevaluated(def, _, promoted) = constant.literal.val {
assert!(promoted.is_none());
if cx.tcx.trait_of_item(def.did).is_none() {
let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
} else {
cx.tcx.at(constant.span).mir_const_qualif(def.did)
};
if !Q::in_qualifs(&qualifs) {
return false;
}
}
}
Q::in_any_value_of_ty(cx, constant.literal.ty)
}