Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  derive.rs   Sprache: unbekannt

 
//! Determining which types for which we cannot emit `#[derive(Trait)]`.

use std::fmt;

use super::{generate_dependencies, ConstrainResult, MonotoneFramework};
use crate::ir::analysis::has_vtable::HasVtable;
use crate::ir::comp::CompKind;
use crate::ir::context::{BindgenContext, ItemId};
use crate::ir::derive::CanDerive;
use crate::ir::function::FunctionSig;
use crate::ir::item::{IsOpaque, Item};
use crate::ir::layout::Layout;
use crate::ir::template::TemplateParameters;
use crate::ir::traversal::{EdgeKind, Trace};
use crate::ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
use crate::ir::ty::{Type, TypeKind};
use crate::{Entry, HashMap, HashSet};

/// Which trait to consider when doing the `CannotDerive` analysis.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum DeriveTrait {
    /// The `Copy` trait.
    Copy,
    /// The `Debug` trait.
    Debug,
    /// The `Default` trait.
    Default,
    /// The `Hash` trait.
    Hash,
    /// The `PartialEq` and `PartialOrd` traits.
    PartialEqOrPartialOrd,
}

/// An analysis that finds for each IR item whether a trait cannot be derived.
///
/// We use the monotone constraint function `cannot_derive`, defined as follows
/// for type T:
///
/// * If T is Opaque and the layout of the type is known, get this layout as an
///   opaquetype and check whether it can derive using trivial checks.
///
/// * If T is Array, a trait cannot be derived if the array is incomplete,
///   if the length of the array is larger than the limit (unless the trait
///   allows it), or the trait cannot be derived for the type of data the array
///   contains.
///
/// * If T is Vector, a trait cannot be derived if the trait cannot be derived
///   for the type of data the vector contains.
///
/// * If T is a type alias, a templated alias or an indirection to another type,
///   the trait cannot be derived if the trait cannot be derived for type T
///   refers to.
///
/// * If T is a compound type, the trait cannot be derived if the trait cannot
///   be derived for any of its base members or fields.
///
/// * If T is an instantiation of an abstract template definition, the trait
///   cannot be derived if any of the template arguments or template definition
///   cannot derive the trait.
///
/// * For all other (simple) types, compiler and standard library limitations
///   dictate whether the trait is implemented.
#[derive(Debug, Clone)]
pub(crate) struct CannotDerive<'ctx> {
    ctx: &'ctx BindgenContext,

    derive_trait: DeriveTrait,

    // The incremental result of this analysis's computation.
    // Contains information whether particular item can derive `derive_trait`
    can_derive: HashMap<ItemId, CanDerive>,

    // Dependencies saying that if a key ItemId has been inserted into the
    // `cannot_derive_partialeq_or_partialord` set, then each of the ids
    // in Vec<ItemId> need to be considered again.
    //
    // This is a subset of the natural IR graph with reversed edges, where we
    // only include the edges from the IR graph that can affect whether a type
    // can derive `derive_trait`.
    dependencies: HashMap<ItemId, Vec<ItemId>>,
}

type EdgePredicate = fn(EdgeKind) -> bool;

fn consider_edge_default(kind: EdgeKind) -> bool {
    match kind {
        // These are the only edges that can affect whether a type can derive
        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 |
        EdgeKind::Generic => false,
    }
}

impl<'ctx> CannotDerive<'ctx> {
    fn insert<Id: Into<ItemId>>(
        &mut self,
        id: Id,
        can_derive: CanDerive,
    ) -> ConstrainResult {
        let id = id.into();
        trace!(
            "inserting {:?} can_derive<{}>={:?}",
            id,
            self.derive_trait,
            can_derive
        );

        if let CanDerive::Yes = can_derive {
            return ConstrainResult::Same;
        }

        match self.can_derive.entry(id) {
            Entry::Occupied(mut entry) => {
                if *entry.get() < can_derive {
                    entry.insert(can_derive);
                    ConstrainResult::Changed
                } else {
                    ConstrainResult::Same
                }
            }
            Entry::Vacant(entry) => {
                entry.insert(can_derive);
                ConstrainResult::Changed
            }
        }
    }

    fn constrain_type(&mut self, item: &Item, ty: &Type) -> CanDerive {
        if !self.ctx.allowlisted_items().contains(&item.id()) {
            let can_derive = self
                .ctx
                .blocklisted_type_implements_trait(item, self.derive_trait);
            match can_derive {
                CanDerive::Yes => trace!(
                    "    blocklisted type explicitly implements {}",
                    self.derive_trait
                ),
                CanDerive::Manually => trace!(
                    "    blocklisted type requires manual implementation of {}",
                    self.derive_trait
                ),
                CanDerive::No => trace!(
                    "    cannot derive {} for blocklisted type",
                    self.derive_trait
                ),
            }
            return can_derive;
        }

        if self.derive_trait.not_by_name(self.ctx, item) {
            trace!(
                "    cannot derive {} for explicitly excluded type",
                self.derive_trait
            );
            return CanDerive::No;
        }

        trace!("ty: {:?}", ty);
        if item.is_opaque(self.ctx, &()) {
            if !self.derive_trait.can_derive_union() &&
                ty.is_union() &&
                self.ctx.options().untagged_union
            {
                trace!(
                    "    cannot derive {} for Rust unions",
                    self.derive_trait
                );
                return CanDerive::No;
            }

            let layout_can_derive =
                ty.layout(self.ctx).map_or(CanDerive::Yes, |l| {
                    l.opaque().array_size_within_derive_limit(self.ctx)
                });

            match layout_can_derive {
                CanDerive::Yes => {
                    trace!(
                        "    we can trivially derive {} for the layout",
                        self.derive_trait
                    );
                }
                _ => {
                    trace!(
                        "    we cannot derive {} for the layout",
                        self.derive_trait
                    );
                }
            };
            return layout_can_derive;
        }

        match *ty.kind() {
            // Handle the simple cases. These can derive traits without further
            // information.
            TypeKind::Void |
            TypeKind::NullPtr |
            TypeKind::Int(..) |
            TypeKind::Complex(..) |
            TypeKind::Float(..) |
            TypeKind::Enum(..) |
            TypeKind::TypeParam |
            TypeKind::UnresolvedTypeRef(..) |
            TypeKind::Reference(..) |
            TypeKind::ObjCInterface(..) |
            TypeKind::ObjCId |
            TypeKind::ObjCSel => {
                return self.derive_trait.can_derive_simple(ty.kind());
            }
            TypeKind::Pointer(inner) => {
                let inner_type =
                    self.ctx.resolve_type(inner).canonical_type(self.ctx);
                if let TypeKind::Function(ref sig) = *inner_type.kind() {
                    self.derive_trait.can_derive_fnptr(sig)
                } else {
                    self.derive_trait.can_derive_pointer()
                }
            }
            TypeKind::Function(ref sig) => {
                self.derive_trait.can_derive_fnptr(sig)
            }

            // Complex cases need more information
            TypeKind::Array(t, len) => {
                let inner_type =
                    self.can_derive.get(&t.into()).cloned().unwrap_or_default();
                if inner_type != CanDerive::Yes {
                    trace!(
                        "    arrays of T for which we cannot derive {} \
                         also cannot derive {}",
                        self.derive_trait,
                        self.derive_trait
                    );
                    return CanDerive::No;
                }

                if len == 0 && !self.derive_trait.can_derive_incomplete_array()
                {
                    trace!(
                        "    cannot derive {} for incomplete arrays",
                        self.derive_trait
                    );
                    return CanDerive::No;
                }

                if self.derive_trait.can_derive_large_array(self.ctx) {
                    trace!("    array can derive {}", self.derive_trait);
                    return CanDerive::Yes;
                }

                if len > RUST_DERIVE_IN_ARRAY_LIMIT {
                    trace!(
                        "    array is too large to derive {}, but it may be implemented", self.derive_trait
                    );
                    return CanDerive::Manually;
                }
                trace!(
                    "    array is small enough to derive {}",
                    self.derive_trait
                );
                CanDerive::Yes
            }
            TypeKind::Vector(t, len) => {
                let inner_type =
                    self.can_derive.get(&t.into()).cloned().unwrap_or_default();
                if inner_type != CanDerive::Yes {
                    trace!(
                        "    vectors of T for which we cannot derive {} \
                         also cannot derive {}",
                        self.derive_trait,
                        self.derive_trait
                    );
                    return CanDerive::No;
                }
                assert_ne!(len, 0, "vectors cannot have zero length");
                self.derive_trait.can_derive_vector()
            }

            TypeKind::Comp(ref info) => {
                assert!(
                    !info.has_non_type_template_params(),
                    "The early ty.is_opaque check should have handled this case"
                );

                if !self.derive_trait.can_derive_compound_forward_decl() &&
                    info.is_forward_declaration()
                {
                    trace!(
                        "    cannot derive {} for forward decls",
                        self.derive_trait
                    );
                    return CanDerive::No;
                }

                // NOTE: Take into account that while unions in C and C++ are copied by
                // default, the may have an explicit destructor in C++, so we can't
                // defer this check just for the union case.
                if !self.derive_trait.can_derive_compound_with_destructor() &&
                    self.ctx.lookup_has_destructor(
                        item.id().expect_type_id(self.ctx),
                    )
                {
                    trace!(
                        "    comp has destructor which cannot derive {}",
                        self.derive_trait
                    );
                    return CanDerive::No;
                }

                if info.kind() == CompKind::Union {
                    if self.derive_trait.can_derive_union() {
                        if self.ctx.options().untagged_union &&
                           // https://github.com/rust-lang/rust/issues/36640
                           (!info.self_template_params(self.ctx).is_empty() ||
                            !item.all_template_params(self.ctx).is_empty())
                        {
                            trace!(
                                "    cannot derive {} for Rust union because issue 36640", self.derive_trait
                            );
                            return CanDerive::No;
                        }
                    // fall through to be same as non-union handling
                    } else {
                        if self.ctx.options().untagged_union {
                            trace!(
                                "    cannot derive {} for Rust unions",
                                self.derive_trait
                            );
                            return CanDerive::No;
                        }

                        let layout_can_derive =
                            ty.layout(self.ctx).map_or(CanDerive::Yes, |l| {
                                l.opaque()
                                    .array_size_within_derive_limit(self.ctx)
                            });
                        match layout_can_derive {
                            CanDerive::Yes => {
                                trace!(
                                    "    union layout can trivially derive {}",
                                    self.derive_trait
                                );
                            }
                            _ => {
                                trace!(
                                    "    union layout cannot derive {}",
                                    self.derive_trait
                                );
                            }
                        };
                        return layout_can_derive;
                    }
                }

                if !self.derive_trait.can_derive_compound_with_vtable() &&
                    item.has_vtable(self.ctx)
                {
                    trace!(
                        "    cannot derive {} for comp with vtable",
                        self.derive_trait
                    );
                    return CanDerive::No;
                }

                // Bitfield units are always represented as arrays of u8, but
                // they're not traced as arrays, so we need to check here
                // instead.
                if !self.derive_trait.can_derive_large_array(self.ctx) &&
                    info.has_too_large_bitfield_unit() &&
                    !item.is_opaque(self.ctx, &())
                {
                    trace!(
                        "    cannot derive {} for comp with too large bitfield unit",
                        self.derive_trait
                    );
                    return CanDerive::No;
                }

                let pred = self.derive_trait.consider_edge_comp();
                self.constrain_join(item, pred)
            }

            TypeKind::ResolvedTypeRef(..) |
            TypeKind::TemplateAlias(..) |
            TypeKind::Alias(..) |
            TypeKind::BlockPointer(..) => {
                let pred = self.derive_trait.consider_edge_typeref();
                self.constrain_join(item, pred)
            }

            TypeKind::TemplateInstantiation(..) => {
                let pred = self.derive_trait.consider_edge_tmpl_inst();
                self.constrain_join(item, pred)
            }

            TypeKind::Opaque => unreachable!(
                "The early ty.is_opaque check should have handled this case"
            ),
        }
    }

    fn constrain_join(
        &mut self,
        item: &Item,
        consider_edge: EdgePredicate,
    ) -> CanDerive {
        let mut candidate = None;

        item.trace(
            self.ctx,
            &mut |sub_id, edge_kind| {
                // Ignore ourselves, since union with ourself is a
                // no-op. Ignore edges that aren't relevant to the
                // analysis.
                if sub_id == item.id() || !consider_edge(edge_kind) {
                    return;
                }

                let can_derive = self.can_derive
                    .get(&sub_id)
                    .cloned()
                    .unwrap_or_default();

                match can_derive {
                    CanDerive::Yes => trace!("    member {:?} can derive {}", sub_id, self.derive_trait),
                    CanDerive::Manually => trace!("    member {:?} cannot derive {}, but it may be implemented", sub_id, self.derive_trait),
                    CanDerive::No => trace!("    member {:?} cannot derive {}", sub_id, self.derive_trait),
                }

                *candidate.get_or_insert(CanDerive::Yes) |= can_derive;
            },
            &(),
        );

        if candidate.is_none() {
            trace!(
                "    can derive {} because there are no members",
                self.derive_trait
            );
        }
        candidate.unwrap_or_default()
    }
}

impl DeriveTrait {
    fn not_by_name(&self, ctx: &BindgenContext, item: &Item) -> bool {
        match self {
            DeriveTrait::Copy => ctx.no_copy_by_name(item),
            DeriveTrait::Debug => ctx.no_debug_by_name(item),
            DeriveTrait::Default => ctx.no_default_by_name(item),
            DeriveTrait::Hash => ctx.no_hash_by_name(item),
            DeriveTrait::PartialEqOrPartialOrd => {
                ctx.no_partialeq_by_name(item)
            }
        }
    }

    fn consider_edge_comp(&self) -> EdgePredicate {
        match self {
            DeriveTrait::PartialEqOrPartialOrd => consider_edge_default,
            _ => |kind| matches!(kind, EdgeKind::BaseMember | EdgeKind::Field),
        }
    }

    fn consider_edge_typeref(&self) -> EdgePredicate {
        match self {
            DeriveTrait::PartialEqOrPartialOrd => consider_edge_default,
            _ => |kind| kind == EdgeKind::TypeReference,
        }
    }

    fn consider_edge_tmpl_inst(&self) -> EdgePredicate {
        match self {
            DeriveTrait::PartialEqOrPartialOrd => consider_edge_default,
            _ => |kind| {
                matches!(
                    kind,
                    EdgeKind::TemplateArgument | EdgeKind::TemplateDeclaration
                )
            },
        }
    }

    fn can_derive_large_array(&self, ctx: &BindgenContext) -> bool {
        if ctx.options().rust_features().larger_arrays {
            !matches!(self, DeriveTrait::Default)
        } else {
            matches!(self, DeriveTrait::Copy)
        }
    }

    fn can_derive_union(&self) -> bool {
        matches!(self, DeriveTrait::Copy)
    }

    fn can_derive_compound_with_destructor(&self) -> bool {
        !matches!(self, DeriveTrait::Copy)
    }

    fn can_derive_compound_with_vtable(&self) -> bool {
        !matches!(self, DeriveTrait::Default)
    }

    fn can_derive_compound_forward_decl(&self) -> bool {
        matches!(self, DeriveTrait::Copy | DeriveTrait::Debug)
    }

    fn can_derive_incomplete_array(&self) -> bool {
        !matches!(
            self,
            DeriveTrait::Copy |
                DeriveTrait::Hash |
                DeriveTrait::PartialEqOrPartialOrd
        )
    }

    fn can_derive_fnptr(&self, f: &FunctionSig) -> CanDerive {
        match (self, f.function_pointers_can_derive()) {
            (DeriveTrait::Copy, _) | (DeriveTrait::Default, _) | (_, true) => {
                trace!("    function pointer can derive {}", self);
                CanDerive::Yes
            }
            (DeriveTrait::Debug, false) => {
                trace!("    function pointer cannot derive {}, but it may be implemented", self);
                CanDerive::Manually
            }
            (_, false) => {
                trace!("    function pointer cannot derive {}", self);
                CanDerive::No
            }
        }
    }

    fn can_derive_vector(&self) -> CanDerive {
        match self {
            DeriveTrait::PartialEqOrPartialOrd => {
                // FIXME: vectors always can derive PartialEq, but they should
                // not derive PartialOrd:
                // https://github.com/rust-lang-nursery/packed_simd/issues/48
                trace!("    vectors cannot derive PartialOrd");
                CanDerive::No
            }
            _ => {
                trace!("    vector can derive {}", self);
                CanDerive::Yes
            }
        }
    }

    fn can_derive_pointer(&self) -> CanDerive {
        match self {
            DeriveTrait::Default => {
                trace!("    pointer cannot derive Default");
                CanDerive::No
            }
            _ => {
                trace!("    pointer can derive {}", self);
                CanDerive::Yes
            }
        }
    }

    fn can_derive_simple(&self, kind: &TypeKind) -> CanDerive {
        match (self, kind) {
            // === Default ===
            (DeriveTrait::Default, TypeKind::Void) |
            (DeriveTrait::Default, TypeKind::NullPtr) |
            (DeriveTrait::Default, TypeKind::Enum(..)) |
            (DeriveTrait::Default, TypeKind::Reference(..)) |
            (DeriveTrait::Default, TypeKind::TypeParam) |
            (DeriveTrait::Default, TypeKind::ObjCInterface(..)) |
            (DeriveTrait::Default, TypeKind::ObjCId) |
            (DeriveTrait::Default, TypeKind::ObjCSel) => {
                trace!("    types that always cannot derive Default");
                CanDerive::No
            }
            (DeriveTrait::Default, TypeKind::UnresolvedTypeRef(..)) => {
                unreachable!(
                    "Type with unresolved type ref can't reach derive default"
                )
            }
            // === Hash ===
            (DeriveTrait::Hash, TypeKind::Float(..)) |
            (DeriveTrait::Hash, TypeKind::Complex(..)) => {
                trace!("    float cannot derive Hash");
                CanDerive::No
            }
            // === others ===
            _ => {
                trace!("    simple type that can always derive {}", self);
                CanDerive::Yes
            }
        }
    }
}

impl fmt::Display for DeriveTrait {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match self {
            DeriveTrait::Copy => "Copy",
            DeriveTrait::Debug => "Debug",
            DeriveTrait::Default => "Default",
            DeriveTrait::Hash => "Hash",
            DeriveTrait::PartialEqOrPartialOrd => "PartialEq/PartialOrd",
        };
        s.fmt(f)
    }
}

impl<'ctx> MonotoneFramework for CannotDerive<'ctx> {
    type Node = ItemId;
    type Extra = (&'ctx BindgenContext, DeriveTrait);
    type Output = HashMap<ItemId, CanDerive>;

    fn new(
        (ctx, derive_trait): (&'ctx BindgenContext, DeriveTrait),
    ) -> CannotDerive<'ctx> {
        let can_derive = HashMap::default();
        let dependencies = generate_dependencies(ctx, consider_edge_default);

        CannotDerive {
            ctx,
            derive_trait,
            can_derive,
            dependencies,
        }
    }

    fn initial_worklist(&self) -> Vec<ItemId> {
        // The transitive closure of all allowlisted items, including explicitly
        // blocklisted items.
        self.ctx
            .allowlisted_items()
            .iter()
            .cloned()
            .flat_map(|i| {
                let mut reachable = vec![i];
                i.trace(
                    self.ctx,
                    &mut |s, _| {
                        reachable.push(s);
                    },
                    &(),
                );
                reachable
            })
            .collect()
    }

    fn constrain(&mut self, id: ItemId) -> ConstrainResult {
        trace!("constrain: {:?}", id);

        if let Some(CanDerive::No) = self.can_derive.get(&id).cloned() {
            trace!("    already know it cannot derive {}", self.derive_trait);
            return ConstrainResult::Same;
        }

        let item = self.ctx.resolve_item(id);
        let can_derive = match item.as_type() {
            Some(ty) => {
                let mut can_derive = self.constrain_type(item, ty);
                if let CanDerive::Yes = can_derive {
                    let is_reached_limit =
                        |l: Layout| l.align > RUST_DERIVE_IN_ARRAY_LIMIT;
                    if !self.derive_trait.can_derive_large_array(self.ctx) &&
                        ty.layout(self.ctx).map_or(false, is_reached_limit)
                    {
                        // We have to be conservative: the struct *could* have enough
                        // padding that we emit an array that is longer than
                        // `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations
                        // into the IR and computed them before this analysis, then we could
                        // be precise rather than conservative here.
                        can_derive = CanDerive::Manually;
                    }
                }
                can_derive
            }
            None => self.constrain_join(item, consider_edge_default),
        };

        self.insert(id, can_derive)
    }

    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<CannotDerive<'ctx>> for HashMap<ItemId, CanDerive> {
    fn from(analysis: CannotDerive<'ctx>) -> Self {
        extra_assert!(analysis
            .can_derive
            .values()
            .all(|v| *v != CanDerive::Yes));

        analysis.can_derive
    }
}

/// Convert a `HashMap<ItemId, CanDerive>` into a `HashSet<ItemId>`.
///
/// Elements that are not `CanDerive::Yes` are kept in the set, so that it
/// represents all items that cannot derive.
pub(crate) fn as_cannot_derive_set(
    can_derive: HashMap<ItemId, CanDerive>,
) -> HashSet<ItemId> {
    can_derive
        .into_iter()
        .filter_map(|(k, v)| if v != CanDerive::Yes { Some(k) } else { None })
        .collect()
}

[ Dauer der Verarbeitung: 0.34 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge