use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
use ir::comp::CompKind;
use ir::comp::Field;
use ir::comp::FieldMethods;
use ir::context::{BindgenContext, ItemId};
use ir::derive::CanTriviallyDeriveDebug;
use ir::item::IsOpaque;
use ir::traversal::EdgeKind;
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
use ir::ty::TypeKind;
use std::collections::HashMap;
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct CannotDeriveDebug<'ctx> {
ctx: &'ctx BindgenContext,
cannot_derive_debug: HashSet<ItemId>,
dependencies: HashMap<ItemId, Vec<ItemId>>,
}
impl<'ctx> CannotDeriveDebug<'ctx> {
fn consider_edge(kind: EdgeKind) -> bool {
match kind {
EdgeKind::BaseMember |
EdgeKind::Field |
EdgeKind::TypeReference |
EdgeKind::VarType |
EdgeKind::TemplateArgument |
EdgeKind::TemplateDeclaration |
EdgeKind::TemplateParameterDefinition => true,
EdgeKind::Constructor |
EdgeKind::Destructor |
EdgeKind::FunctionReturn |
EdgeKind::FunctionParameter |
EdgeKind::InnerType |
EdgeKind::InnerVar |
EdgeKind::Method => false,
EdgeKind::Generic => false,
}
}
fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
let id = id.into();
trace!("inserting {:?} into the cannot_derive_debug set", id);
let was_not_already_in_set = self.cannot_derive_debug.insert(id);
assert!(
was_not_already_in_set,
"We shouldn't try and insert {:?} twice because if it was \
already in the set, `constrain` should have exited early.",
id
);
ConstrainResult::Changed
}
fn is_not_debug<Id: Into<ItemId>>(&self, id: Id) -> bool {
let id = id.into();
self.cannot_derive_debug.contains(&id) ||
!self.ctx.whitelisted_items().contains(&id)
}
}
impl<'ctx> MonotoneFramework for CannotDeriveDebug<'ctx> {
type Node = ItemId;
type Extra = &'ctx BindgenContext;
type Output = HashSet<ItemId>;
fn new(ctx: &'ctx BindgenContext) -> CannotDeriveDebug<'ctx> {
let cannot_derive_debug = HashSet::new();
let dependencies = generate_dependencies(ctx, Self::consider_edge);
CannotDeriveDebug {
ctx,
cannot_derive_debug,
dependencies,
}
}
fn initial_worklist(&self) -> Vec<ItemId> {
self.ctx.whitelisted_items().iter().cloned().collect()
}
fn constrain(&mut self, id: ItemId) -> ConstrainResult {
trace!("constrain: {:?}", id);
if self.cannot_derive_debug.contains(&id) {
trace!(" already know it cannot derive Debug");
return ConstrainResult::Same;
}
if !self.ctx.whitelisted_items().contains(&id) {
trace!(" blacklisted items are assumed not to be Debug");
return ConstrainResult::Same;
}
let item = self.ctx.resolve_item(id);
let ty = match item.as_type() {
Some(ty) => ty,
None => {
trace!(" not a type; ignoring");
return ConstrainResult::Same;
}
};
if item.is_opaque(self.ctx, &()) {
let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| {
l.opaque().can_trivially_derive_debug()
});
return if layout_can_derive &&
!(ty.is_union() &&
self.ctx.options().rust_features().untagged_union) {
trace!(" we can trivially derive Debug for the layout");
ConstrainResult::Same
} else {
trace!(" we cannot derive Debug for the layout");
self.insert(id)
};
}
if ty.layout(self.ctx).map_or(false, |l| {
l.align > RUST_DERIVE_IN_ARRAY_LIMIT
})
{
return self.insert(id);
}
match *ty.kind() {
TypeKind::Void |
TypeKind::NullPtr |
TypeKind::Int(..) |
TypeKind::Float(..) |
TypeKind::Complex(..) |
TypeKind::Function(..) |
TypeKind::Enum(..) |
TypeKind::Reference(..) |
TypeKind::BlockPointer |
TypeKind::TypeParam |
TypeKind::UnresolvedTypeRef(..) |
TypeKind::ObjCInterface(..) |
TypeKind::ObjCId |
TypeKind::ObjCSel => {
trace!(" simple type that can always derive Debug");
ConstrainResult::Same
}
TypeKind::Array(t, len) => {
if self.is_not_debug(t) {
trace!(
" arrays of T for which we cannot derive Debug \
also cannot derive Debug"
);
return self.insert(id);
}
if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
trace!(" array is small enough to derive Debug");
ConstrainResult::Same
} else {
trace!(" array is too large to derive Debug");
self.insert(id)
}
}
TypeKind::ResolvedTypeRef(t) |
TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(t) => {
if self.is_not_debug(t) {
trace!(
" aliases and type refs to T which cannot derive \
Debug also cannot derive Debug"
);
self.insert(id)
} else {
trace!(
" aliases and type refs to T which can derive \
Debug can also derive Debug"
);
ConstrainResult::Same
}
}
TypeKind::Comp(ref info) => {
assert!(
!info.has_non_type_template_params(),
"The early ty.is_opaque check should have handled this case"
);
if info.kind() == CompKind::Union {
if self.ctx.options().rust_features().untagged_union {
trace!(" cannot derive Debug for Rust unions");
return self.insert(id);
}
if ty.layout(self.ctx).map_or(true, |l| {
l.opaque().can_trivially_derive_debug()
})
{
trace!(" union layout can trivially derive Debug");
return ConstrainResult::Same;
} else {
trace!(" union layout cannot derive Debug");
return self.insert(id);
}
}
let bases_cannot_derive =
info.base_members().iter().any(|base| {
self.is_not_debug(base.ty)
});
if bases_cannot_derive {
trace!(
" base members cannot derive Debug, so we can't \
either"
);
return self.insert(id);
}
let fields_cannot_derive =
info.fields().iter().any(|f| match *f {
Field::DataMember(ref data) => {
self.is_not_debug(data.ty())
}
Field::Bitfields(ref bfu) => {
if bfu.layout().align > RUST_DERIVE_IN_ARRAY_LIMIT {
trace!(
" we cannot derive Debug for a bitfield larger then \
the limit"
);
return true;
}
bfu.bitfields().iter().any(|b| {
self.is_not_debug(b.ty())
})
}
});
if fields_cannot_derive {
trace!(
" fields cannot derive Debug, so we can't either"
);
return self.insert(id);
}
trace!(" comp can derive Debug");
ConstrainResult::Same
}
TypeKind::Pointer(inner) => {
let inner_type =
self.ctx.resolve_type(inner).canonical_type(self.ctx);
if let TypeKind::Function(ref sig) = *inner_type.kind() {
if !sig.can_trivially_derive_debug() {
trace!(
" function pointer that can't trivially derive Debug"
);
return self.insert(id);
}
}
trace!(" pointers can derive Debug");
ConstrainResult::Same
}
TypeKind::TemplateInstantiation(ref template) => {
let args_cannot_derive =
template.template_arguments().iter().any(|arg| {
self.is_not_debug(*arg)
});
if args_cannot_derive {
trace!(
" template args cannot derive Debug, so \
insantiation can't either"
);
return self.insert(id);
}
assert!(
!template.template_definition().is_opaque(self.ctx, &()),
"The early ty.is_opaque check should have handled this case"
);
let def_cannot_derive = self.is_not_debug(
template.template_definition()
);
if def_cannot_derive {
trace!(
" template definition cannot derive Debug, so \
insantiation can't either"
);
return self.insert(id);
}
trace!(" template instantiation can derive Debug");
ConstrainResult::Same
}
TypeKind::Opaque => {
unreachable!(
"The early ty.is_opaque check should have handled this case"
)
}
}
}
fn each_depending_on<F>(&self, id: ItemId, mut f: F)
where
F: FnMut(ItemId),
{
if let Some(edges) = self.dependencies.get(&id) {
for item in edges {
trace!("enqueue {:?} into worklist", item);
f(*item);
}
}
}
}
impl<'ctx> From<CannotDeriveDebug<'ctx>> for HashSet<ItemId> {
fn from(analysis: CannotDeriveDebug<'ctx>) -> Self {
analysis.cannot_derive_debug
}
}