Skip to main content

bindgen/ir/analysis/
derive.rs

1//! Determining which types for which we cannot emit `#[derive(Trait)]`.
2
3use std::fmt;
4
5use super::{generate_dependencies, ConstrainResult, MonotoneFramework};
6use crate::ir::analysis::has_vtable::HasVtable;
7use crate::ir::comp::CompKind;
8use crate::ir::context::{BindgenContext, ItemId};
9use crate::ir::derive::CanDerive;
10use crate::ir::function::FunctionSig;
11use crate::ir::item::{IsOpaque, Item};
12use crate::ir::layout::Layout;
13use crate::ir::template::TemplateParameters;
14use crate::ir::traversal::{EdgeKind, Trace};
15use crate::ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
16use crate::ir::ty::{Type, TypeKind};
17use crate::{Entry, HashMap, HashSet};
18
19/// Which trait to consider when doing the `CannotDerive` analysis.
20#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
21pub enum DeriveTrait {
22    /// The `Copy` trait.
23    Copy,
24    /// The `Debug` trait.
25    Debug,
26    /// The `Default` trait.
27    Default,
28    /// The `Hash` trait.
29    Hash,
30    /// The `PartialEq` and `PartialOrd` traits.
31    PartialEqOrPartialOrd,
32}
33
34/// An analysis that finds for each IR item whether a trait cannot be derived.
35///
36/// We use the monotone constraint function `cannot_derive`, defined as follows
37/// for type T:
38///
39/// * If T is Opaque and the layout of the type is known, get this layout as an
40///   opaquetype and check whether it can derive using trivial checks.
41///
42/// * If T is Array, a trait cannot be derived if the array is incomplete,
43///   if the length of the array is larger than the limit (unless the trait
44///   allows it), or the trait cannot be derived for the type of data the array
45///   contains.
46///
47/// * If T is Vector, a trait cannot be derived if the trait cannot be derived
48///   for the type of data the vector contains.
49///
50/// * If T is a type alias, a templated alias or an indirection to another type,
51///   the trait cannot be derived if the trait cannot be derived for type T
52///   refers to.
53///
54/// * If T is a compound type, the trait cannot be derived if the trait cannot
55///   be derived for any of its base members or fields.
56///
57/// * If T is an instantiation of an abstract template definition, the trait
58///   cannot be derived if any of the template arguments or template definition
59///   cannot derive the trait.
60///
61/// * For all other (simple) types, compiler and standard library limitations
62///   dictate whether the trait is implemented.
63#[derive(Debug, Clone)]
64pub(crate) struct CannotDerive<'ctx> {
65    ctx: &'ctx BindgenContext,
66
67    derive_trait: DeriveTrait,
68
69    // The incremental result of this analysis's computation.
70    // Contains information whether particular item can derive `derive_trait`
71    can_derive: HashMap<ItemId, CanDerive>,
72
73    // Dependencies saying that if a key ItemId has been inserted into the
74    // `cannot_derive_partialeq_or_partialord` set, then each of the ids
75    // in Vec<ItemId> need to be considered again.
76    //
77    // This is a subset of the natural IR graph with reversed edges, where we
78    // only include the edges from the IR graph that can affect whether a type
79    // can derive `derive_trait`.
80    dependencies: HashMap<ItemId, Vec<ItemId>>,
81}
82
83type EdgePredicate = fn(EdgeKind) -> bool;
84
85fn consider_edge_default(kind: EdgeKind) -> bool {
86    match kind {
87        // These are the only edges that can affect whether a type can derive
88        EdgeKind::BaseMember |
89        EdgeKind::Field |
90        EdgeKind::TypeReference |
91        EdgeKind::VarType |
92        EdgeKind::TemplateArgument |
93        EdgeKind::TemplateDeclaration |
94        EdgeKind::TemplateParameterDefinition => true,
95
96        EdgeKind::Constructor |
97        EdgeKind::Destructor |
98        EdgeKind::FunctionReturn |
99        EdgeKind::FunctionParameter |
100        EdgeKind::InnerType |
101        EdgeKind::InnerVar |
102        EdgeKind::Method |
103        EdgeKind::Generic => false,
104    }
105}
106
107impl CannotDerive<'_> {
108    fn insert<Id: Into<ItemId>>(
109        &mut self,
110        id: Id,
111        can_derive: CanDerive,
112    ) -> ConstrainResult {
113        let id = id.into();
114        trace!(
115            "inserting {id:?} can_derive<{}>={can_derive:?}",
116            self.derive_trait,
117        );
118
119        if let CanDerive::Yes = can_derive {
120            return ConstrainResult::Same;
121        }
122
123        match self.can_derive.entry(id) {
124            Entry::Occupied(mut entry) => {
125                if *entry.get() < can_derive {
126                    entry.insert(can_derive);
127                    ConstrainResult::Changed
128                } else {
129                    ConstrainResult::Same
130                }
131            }
132            Entry::Vacant(entry) => {
133                entry.insert(can_derive);
134                ConstrainResult::Changed
135            }
136        }
137    }
138
139    fn constrain_type(&mut self, item: &Item, ty: &Type) -> CanDerive {
140        if !self.ctx.allowlisted_items().contains(&item.id()) {
141            let can_derive = self
142                .ctx
143                .blocklisted_type_implements_trait(item, self.derive_trait);
144            match can_derive {
145                CanDerive::Yes => trace!(
146                    "    blocklisted type explicitly implements {}",
147                    self.derive_trait
148                ),
149                CanDerive::Manually => trace!(
150                    "    blocklisted type requires manual implementation of {}",
151                    self.derive_trait
152                ),
153                CanDerive::No => trace!(
154                    "    cannot derive {} for blocklisted type",
155                    self.derive_trait
156                ),
157            }
158            return can_derive;
159        }
160
161        if self.derive_trait.not_by_name(self.ctx, item) {
162            trace!(
163                "    cannot derive {} for explicitly excluded type",
164                self.derive_trait
165            );
166            return CanDerive::No;
167        }
168
169        trace!("ty: {ty:?}");
170        if item.is_opaque(self.ctx, &()) {
171            if !self.derive_trait.can_derive_union() &&
172                ty.is_union() &&
173                self.ctx.options().untagged_union
174            {
175                trace!(
176                    "    cannot derive {} for Rust unions",
177                    self.derive_trait
178                );
179                return CanDerive::No;
180            }
181
182            trace!(
183                "    we can trivially derive {} for the layout",
184                self.derive_trait
185            );
186            return CanDerive::Yes;
187        }
188
189        match *ty.kind() {
190            // Handle the simple cases. These can derive traits without further
191            // information.
192            TypeKind::Void |
193            TypeKind::NullPtr |
194            TypeKind::Int(..) |
195            TypeKind::Complex(..) |
196            TypeKind::Float(..) |
197            TypeKind::Enum(..) |
198            TypeKind::TypeParam |
199            TypeKind::UnresolvedTypeRef(..) |
200            TypeKind::Reference(..) |
201            TypeKind::ObjCInterface(..) |
202            TypeKind::ObjCId |
203            TypeKind::ObjCSel => self.derive_trait.can_derive_simple(ty.kind()),
204            TypeKind::Pointer(inner) => {
205                let inner_type =
206                    self.ctx.resolve_type(inner).canonical_type(self.ctx);
207                if let TypeKind::Function(ref sig) = *inner_type.kind() {
208                    self.derive_trait.can_derive_fnptr(sig)
209                } else {
210                    self.derive_trait.can_derive_pointer()
211                }
212            }
213            TypeKind::Function(ref sig) => {
214                self.derive_trait.can_derive_fnptr(sig)
215            }
216
217            // Complex cases need more information
218            TypeKind::Array(t, len) => {
219                let inner_type =
220                    self.can_derive.get(&t.into()).copied().unwrap_or_default();
221                if inner_type != CanDerive::Yes {
222                    trace!(
223                        "    arrays of T for which we cannot derive {} \
224                         also cannot derive {}",
225                        self.derive_trait,
226                        self.derive_trait
227                    );
228                    return CanDerive::No;
229                }
230
231                if len == 0 && !self.derive_trait.can_derive_incomplete_array()
232                {
233                    trace!(
234                        "    cannot derive {} for incomplete arrays",
235                        self.derive_trait
236                    );
237                    return CanDerive::No;
238                }
239
240                if self.derive_trait.can_derive_large_array(self.ctx) {
241                    trace!("    array can derive {}", self.derive_trait);
242                    return CanDerive::Yes;
243                }
244
245                if len > RUST_DERIVE_IN_ARRAY_LIMIT {
246                    trace!(
247                        "    array is too large to derive {}, but it may be implemented", self.derive_trait
248                    );
249                    return CanDerive::Manually;
250                }
251                trace!(
252                    "    array is small enough to derive {}",
253                    self.derive_trait
254                );
255                CanDerive::Yes
256            }
257            TypeKind::Vector(t, len) => {
258                let inner_type =
259                    self.can_derive.get(&t.into()).copied().unwrap_or_default();
260                if inner_type != CanDerive::Yes {
261                    trace!(
262                        "    vectors of T for which we cannot derive {} \
263                         also cannot derive {}",
264                        self.derive_trait,
265                        self.derive_trait
266                    );
267                    return CanDerive::No;
268                }
269                assert_ne!(len, 0, "vectors cannot have zero length");
270                self.derive_trait.can_derive_vector()
271            }
272
273            TypeKind::Comp(ref info) => {
274                assert!(
275                    !info.has_non_type_template_params(),
276                    "The early ty.is_opaque check should have handled this case"
277                );
278
279                if !self.derive_trait.can_derive_compound_forward_decl() &&
280                    info.is_forward_declaration()
281                {
282                    trace!(
283                        "    cannot derive {} for forward decls",
284                        self.derive_trait
285                    );
286                    return CanDerive::No;
287                }
288
289                // NOTE: Take into account that while unions in C and C++ are copied by
290                // default, the may have an explicit destructor in C++, so we can't
291                // defer this check just for the union case.
292                if !self.derive_trait.can_derive_compound_with_destructor() &&
293                    self.ctx.lookup_has_destructor(
294                        item.id().expect_type_id(self.ctx),
295                    )
296                {
297                    trace!(
298                        "    comp has destructor which cannot derive {}",
299                        self.derive_trait
300                    );
301                    return CanDerive::No;
302                }
303
304                if info.kind() == CompKind::Union {
305                    if self.derive_trait.can_derive_union() {
306                        if self.ctx.options().untagged_union &&
307                           // https://github.com/rust-lang/rust/issues/36640
308                           (!info.self_template_params(self.ctx).is_empty() ||
309                            !item.all_template_params(self.ctx).is_empty())
310                        {
311                            trace!(
312                                "    cannot derive {} for Rust union because issue 36640", self.derive_trait
313                            );
314                            return CanDerive::No;
315                        }
316                    // fall through to be same as non-union handling
317                    } else {
318                        if self.ctx.options().untagged_union {
319                            trace!(
320                                "    cannot derive {} for Rust unions",
321                                self.derive_trait
322                            );
323                            return CanDerive::No;
324                        }
325
326                        trace!(
327                            "    union layout can trivially derive {}",
328                            self.derive_trait
329                        );
330                        return CanDerive::Yes;
331                    }
332                }
333
334                if !self.derive_trait.can_derive_compound_with_vtable() &&
335                    item.has_vtable(self.ctx)
336                {
337                    trace!(
338                        "    cannot derive {} for comp with vtable",
339                        self.derive_trait
340                    );
341                    return CanDerive::No;
342                }
343
344                // Bitfield units are always represented as arrays of u8, but
345                // they're not traced as arrays, so we need to check here
346                // instead.
347                if !self.derive_trait.can_derive_large_array(self.ctx) &&
348                    info.has_too_large_bitfield_unit() &&
349                    !item.is_opaque(self.ctx, &())
350                {
351                    trace!(
352                        "    cannot derive {} for comp with too large bitfield unit",
353                        self.derive_trait
354                    );
355                    return CanDerive::No;
356                }
357
358                let pred = self.derive_trait.consider_edge_comp();
359                self.constrain_join(item, pred)
360            }
361
362            TypeKind::ResolvedTypeRef(..) |
363            TypeKind::TemplateAlias(..) |
364            TypeKind::Alias(..) |
365            TypeKind::BlockPointer(..) => {
366                let pred = self.derive_trait.consider_edge_typeref();
367                self.constrain_join(item, pred)
368            }
369
370            TypeKind::TemplateInstantiation(..) => {
371                let pred = self.derive_trait.consider_edge_tmpl_inst();
372                self.constrain_join(item, pred)
373            }
374
375            TypeKind::Opaque => unreachable!(
376                "The early ty.is_opaque check should have handled this case"
377            ),
378        }
379    }
380
381    fn constrain_join(
382        &mut self,
383        item: &Item,
384        consider_edge: EdgePredicate,
385    ) -> CanDerive {
386        let mut candidate = None;
387
388        item.trace(
389            self.ctx,
390            &mut |sub_id, edge_kind| {
391                // Ignore ourselves, since union with ourself is a
392                // no-op. Ignore edges that aren't relevant to the
393                // analysis.
394                if sub_id == item.id() || !consider_edge(edge_kind) {
395                    return;
396                }
397
398                let can_derive = self.can_derive
399                    .get(&sub_id)
400                    .copied()
401                    .unwrap_or_default();
402
403                match can_derive {
404                    CanDerive::Yes => trace!("    member {sub_id:?} can derive {}", self.derive_trait),
405                    CanDerive::Manually => trace!("    member {sub_id:?} cannot derive {}, but it may be implemented", self.derive_trait),
406                    CanDerive::No => trace!("    member {sub_id:?} cannot derive {}", self.derive_trait),
407                }
408
409                *candidate.get_or_insert(CanDerive::Yes) |= can_derive;
410            },
411            &(),
412        );
413
414        if candidate.is_none() {
415            trace!(
416                "    can derive {} because there are no members",
417                self.derive_trait
418            );
419        }
420        candidate.unwrap_or_default()
421    }
422}
423
424impl DeriveTrait {
425    fn not_by_name(self, ctx: &BindgenContext, item: &Item) -> bool {
426        match self {
427            DeriveTrait::Copy => ctx.no_copy_by_name(item),
428            DeriveTrait::Debug => ctx.no_debug_by_name(item),
429            DeriveTrait::Default => ctx.no_default_by_name(item),
430            DeriveTrait::Hash => ctx.no_hash_by_name(item),
431            DeriveTrait::PartialEqOrPartialOrd => {
432                ctx.no_partialeq_by_name(item)
433            }
434        }
435    }
436
437    fn consider_edge_comp(self) -> EdgePredicate {
438        match self {
439            DeriveTrait::PartialEqOrPartialOrd => consider_edge_default,
440            _ => |kind| matches!(kind, EdgeKind::BaseMember | EdgeKind::Field),
441        }
442    }
443
444    fn consider_edge_typeref(self) -> EdgePredicate {
445        match self {
446            DeriveTrait::PartialEqOrPartialOrd => consider_edge_default,
447            _ => |kind| kind == EdgeKind::TypeReference,
448        }
449    }
450
451    fn consider_edge_tmpl_inst(self) -> EdgePredicate {
452        match self {
453            DeriveTrait::PartialEqOrPartialOrd => consider_edge_default,
454            _ => |kind| {
455                matches!(
456                    kind,
457                    EdgeKind::TemplateArgument | EdgeKind::TemplateDeclaration
458                )
459            },
460        }
461    }
462
463    fn can_derive_large_array(self, _: &BindgenContext) -> bool {
464        !matches!(self, DeriveTrait::Default)
465    }
466
467    fn can_derive_union(self) -> bool {
468        matches!(self, DeriveTrait::Copy)
469    }
470
471    fn can_derive_compound_with_destructor(self) -> bool {
472        !matches!(self, DeriveTrait::Copy)
473    }
474
475    fn can_derive_compound_with_vtable(self) -> bool {
476        !matches!(self, DeriveTrait::Default)
477    }
478
479    fn can_derive_compound_forward_decl(self) -> bool {
480        matches!(self, DeriveTrait::Debug)
481    }
482
483    fn can_derive_incomplete_array(self) -> bool {
484        !matches!(
485            self,
486            DeriveTrait::Copy |
487                DeriveTrait::Hash |
488                DeriveTrait::PartialEqOrPartialOrd
489        )
490    }
491
492    fn can_derive_fnptr(self, f: &FunctionSig) -> CanDerive {
493        match (self, f.function_pointers_can_derive()) {
494            (DeriveTrait::Copy | DeriveTrait::Default, _) | (_, true) => {
495                trace!("    function pointer can derive {self}");
496                CanDerive::Yes
497            }
498            (DeriveTrait::Debug, false) => {
499                trace!("    function pointer cannot derive {self}, but it may be implemented");
500                CanDerive::Manually
501            }
502            (_, false) => {
503                trace!("    function pointer cannot derive {self}");
504                CanDerive::No
505            }
506        }
507    }
508
509    fn can_derive_vector(self) -> CanDerive {
510        if self == DeriveTrait::PartialEqOrPartialOrd {
511            // FIXME: vectors always can derive PartialEq, but they should
512            // not derive PartialOrd:
513            // https://github.com/rust-lang-nursery/packed_simd/issues/48
514            trace!("    vectors cannot derive PartialOrd");
515            CanDerive::No
516        } else {
517            trace!("    vector can derive {self}");
518            CanDerive::Yes
519        }
520    }
521
522    fn can_derive_pointer(self) -> CanDerive {
523        if self == DeriveTrait::Default {
524            trace!("    pointer cannot derive Default");
525            CanDerive::No
526        } else {
527            trace!("    pointer can derive {self}");
528            CanDerive::Yes
529        }
530    }
531
532    fn can_derive_simple(self, kind: &TypeKind) -> CanDerive {
533        match (self, kind) {
534            // === Default ===
535            (
536                DeriveTrait::Default,
537                TypeKind::Void |
538                TypeKind::NullPtr |
539                TypeKind::Enum(..) |
540                TypeKind::Reference(..) |
541                TypeKind::TypeParam |
542                TypeKind::ObjCInterface(..) |
543                TypeKind::ObjCId |
544                TypeKind::ObjCSel,
545            ) => {
546                trace!("    types that always cannot derive Default");
547                CanDerive::No
548            }
549            (DeriveTrait::Default, TypeKind::UnresolvedTypeRef(..)) => {
550                unreachable!(
551                    "Type with unresolved type ref can't reach derive default"
552                )
553            }
554            // === Hash ===
555            (
556                DeriveTrait::Hash,
557                TypeKind::Float(..) | TypeKind::Complex(..),
558            ) => {
559                trace!("    float cannot derive Hash");
560                CanDerive::No
561            }
562            // === others ===
563            _ => {
564                trace!("    simple type that can always derive {self}");
565                CanDerive::Yes
566            }
567        }
568    }
569}
570
571impl fmt::Display for DeriveTrait {
572    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
573        let s = match self {
574            DeriveTrait::Copy => "Copy",
575            DeriveTrait::Debug => "Debug",
576            DeriveTrait::Default => "Default",
577            DeriveTrait::Hash => "Hash",
578            DeriveTrait::PartialEqOrPartialOrd => "PartialEq/PartialOrd",
579        };
580        s.fmt(f)
581    }
582}
583
584impl<'ctx> MonotoneFramework for CannotDerive<'ctx> {
585    type Node = ItemId;
586    type Extra = (&'ctx BindgenContext, DeriveTrait);
587    type Output = HashMap<ItemId, CanDerive>;
588
589    fn new(
590        (ctx, derive_trait): (&'ctx BindgenContext, DeriveTrait),
591    ) -> CannotDerive<'ctx> {
592        let can_derive = HashMap::default();
593        let dependencies = generate_dependencies(ctx, consider_edge_default);
594
595        CannotDerive {
596            ctx,
597            derive_trait,
598            can_derive,
599            dependencies,
600        }
601    }
602
603    fn initial_worklist(&self) -> Vec<ItemId> {
604        // The transitive closure of all allowlisted items, including explicitly
605        // blocklisted items.
606        self.ctx
607            .allowlisted_items()
608            .iter()
609            .copied()
610            .flat_map(|i| {
611                let mut reachable = vec![i];
612                i.trace(
613                    self.ctx,
614                    &mut |s, _| {
615                        reachable.push(s);
616                    },
617                    &(),
618                );
619                reachable
620            })
621            .collect()
622    }
623
624    fn constrain(&mut self, id: ItemId) -> ConstrainResult {
625        trace!("constrain: {id:?}");
626
627        if let Some(CanDerive::No) = self.can_derive.get(&id) {
628            trace!("    already know it cannot derive {}", self.derive_trait);
629            return ConstrainResult::Same;
630        }
631
632        let item = self.ctx.resolve_item(id);
633        let can_derive = match item.as_type() {
634            Some(ty) => {
635                let mut can_derive = self.constrain_type(item, ty);
636                if let CanDerive::Yes = can_derive {
637                    let is_reached_limit =
638                        |l: Layout| l.align > RUST_DERIVE_IN_ARRAY_LIMIT;
639                    if !self.derive_trait.can_derive_large_array(self.ctx) &&
640                        ty.layout(self.ctx).is_some_and(is_reached_limit)
641                    {
642                        // We have to be conservative: the struct *could* have enough
643                        // padding that we emit an array that is longer than
644                        // `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations
645                        // into the IR and computed them before this analysis, then we could
646                        // be precise rather than conservative here.
647                        can_derive = CanDerive::Manually;
648                    }
649                }
650                can_derive
651            }
652            None => self.constrain_join(item, consider_edge_default),
653        };
654
655        self.insert(id, can_derive)
656    }
657
658    fn each_depending_on<F>(&self, id: ItemId, mut f: F)
659    where
660        F: FnMut(ItemId),
661    {
662        if let Some(edges) = self.dependencies.get(&id) {
663            for item in edges {
664                trace!("enqueue {item:?} into worklist");
665                f(*item);
666            }
667        }
668    }
669}
670
671impl<'ctx> From<CannotDerive<'ctx>> for HashMap<ItemId, CanDerive> {
672    fn from(analysis: CannotDerive<'ctx>) -> Self {
673        extra_assert!(analysis
674            .can_derive
675            .values()
676            .all(|v| *v != CanDerive::Yes));
677
678        analysis.can_derive
679    }
680}
681
682/// Convert a `HashMap<ItemId, CanDerive>` into a `HashSet<ItemId>`.
683///
684/// Elements that are not `CanDerive::Yes` are kept in the set, so that it
685/// represents all items that cannot derive.
686pub(crate) fn as_cannot_derive_set(
687    can_derive: HashMap<ItemId, CanDerive>,
688) -> HashSet<ItemId> {
689    can_derive
690        .into_iter()
691        .filter_map(|(k, v)| if v == CanDerive::Yes { None } else { Some(k) })
692        .collect()
693}