1 //! Determining which types has vtable 2 3 use super::{generate_dependencies, ConstrainResult, MonotoneFramework}; 4 use crate::ir::context::{BindgenContext, ItemId}; 5 use crate::ir::traversal::EdgeKind; 6 use crate::ir::ty::TypeKind; 7 use crate::{Entry, HashMap}; 8 use std::cmp; 9 use std::ops; 10 11 /// The result of the `HasVtableAnalysis` for an individual item. 12 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 13 pub enum HasVtableResult { 14 /// The item does not have a vtable pointer. 15 No, 16 17 /// The item has a vtable and the actual vtable pointer is within this item. 18 SelfHasVtable, 19 20 /// The item has a vtable, but the actual vtable pointer is in a base 21 /// member. 22 BaseHasVtable, 23 } 24 25 impl Default for HasVtableResult { default() -> Self26 fn default() -> Self { 27 HasVtableResult::No 28 } 29 } 30 31 impl HasVtableResult { 32 /// Take the least upper bound of `self` and `rhs`. join(self, rhs: Self) -> Self33 pub fn join(self, rhs: Self) -> Self { 34 cmp::max(self, rhs) 35 } 36 } 37 38 impl ops::BitOr for HasVtableResult { 39 type Output = Self; 40 bitor(self, rhs: HasVtableResult) -> Self::Output41 fn bitor(self, rhs: HasVtableResult) -> Self::Output { 42 self.join(rhs) 43 } 44 } 45 46 impl ops::BitOrAssign for HasVtableResult { bitor_assign(&mut self, rhs: HasVtableResult)47 fn bitor_assign(&mut self, rhs: HasVtableResult) { 48 *self = self.join(rhs) 49 } 50 } 51 52 /// An analysis that finds for each IR item whether it has vtable or not 53 /// 54 /// We use the monotone function `has vtable`, defined as follows: 55 /// 56 /// * If T is a type alias, a templated alias, an indirection to another type, 57 /// or a reference of a type, T has vtable if the type T refers to has vtable. 58 /// * If T is a compound type, T has vtable if we saw a virtual function when 59 /// parsing it or any of its base member has vtable. 60 /// * If T is an instantiation of an abstract template definition, T has 61 /// vtable if template definition has vtable 62 #[derive(Debug, Clone)] 63 pub struct HasVtableAnalysis<'ctx> { 64 ctx: &'ctx BindgenContext, 65 66 // The incremental result of this analysis's computation. Everything in this 67 // set definitely has a vtable. 68 have_vtable: HashMap<ItemId, HasVtableResult>, 69 70 // Dependencies saying that if a key ItemId has been inserted into the 71 // `have_vtable` set, then each of the ids in Vec<ItemId> need to be 72 // considered again. 73 // 74 // This is a subset of the natural IR graph with reversed edges, where we 75 // only include the edges from the IR graph that can affect whether a type 76 // has a vtable or not. 77 dependencies: HashMap<ItemId, Vec<ItemId>>, 78 } 79 80 impl<'ctx> HasVtableAnalysis<'ctx> { consider_edge(kind: EdgeKind) -> bool81 fn consider_edge(kind: EdgeKind) -> bool { 82 match kind { 83 // These are the only edges that can affect whether a type has a 84 // vtable or not. 85 EdgeKind::TypeReference | 86 EdgeKind::BaseMember | 87 EdgeKind::TemplateDeclaration => true, 88 _ => false, 89 } 90 } 91 insert<Id: Into<ItemId>>( &mut self, id: Id, result: HasVtableResult, ) -> ConstrainResult92 fn insert<Id: Into<ItemId>>( 93 &mut self, 94 id: Id, 95 result: HasVtableResult, 96 ) -> ConstrainResult { 97 if let HasVtableResult::No = result { 98 return ConstrainResult::Same; 99 } 100 101 let id = id.into(); 102 match self.have_vtable.entry(id) { 103 Entry::Occupied(mut entry) => { 104 if *entry.get() < result { 105 entry.insert(result); 106 ConstrainResult::Changed 107 } else { 108 ConstrainResult::Same 109 } 110 } 111 Entry::Vacant(entry) => { 112 entry.insert(result); 113 ConstrainResult::Changed 114 } 115 } 116 } 117 forward<Id1, Id2>(&mut self, from: Id1, to: Id2) -> ConstrainResult where Id1: Into<ItemId>, Id2: Into<ItemId>,118 fn forward<Id1, Id2>(&mut self, from: Id1, to: Id2) -> ConstrainResult 119 where 120 Id1: Into<ItemId>, 121 Id2: Into<ItemId>, 122 { 123 let from = from.into(); 124 let to = to.into(); 125 126 match self.have_vtable.get(&from).cloned() { 127 None => ConstrainResult::Same, 128 Some(r) => self.insert(to, r), 129 } 130 } 131 } 132 133 impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> { 134 type Node = ItemId; 135 type Extra = &'ctx BindgenContext; 136 type Output = HashMap<ItemId, HasVtableResult>; 137 new(ctx: &'ctx BindgenContext) -> HasVtableAnalysis<'ctx>138 fn new(ctx: &'ctx BindgenContext) -> HasVtableAnalysis<'ctx> { 139 let have_vtable = HashMap::default(); 140 let dependencies = generate_dependencies(ctx, Self::consider_edge); 141 142 HasVtableAnalysis { 143 ctx, 144 have_vtable, 145 dependencies, 146 } 147 } 148 initial_worklist(&self) -> Vec<ItemId>149 fn initial_worklist(&self) -> Vec<ItemId> { 150 self.ctx.allowlisted_items().iter().cloned().collect() 151 } 152 constrain(&mut self, id: ItemId) -> ConstrainResult153 fn constrain(&mut self, id: ItemId) -> ConstrainResult { 154 trace!("constrain {:?}", id); 155 156 let item = self.ctx.resolve_item(id); 157 let ty = match item.as_type() { 158 None => return ConstrainResult::Same, 159 Some(ty) => ty, 160 }; 161 162 // TODO #851: figure out a way to handle deriving from template type parameters. 163 match *ty.kind() { 164 TypeKind::TemplateAlias(t, _) | 165 TypeKind::Alias(t) | 166 TypeKind::ResolvedTypeRef(t) | 167 TypeKind::Reference(t) => { 168 trace!( 169 " aliases and references forward to their inner type" 170 ); 171 self.forward(t, id) 172 } 173 174 TypeKind::Comp(ref info) => { 175 trace!(" comp considers its own methods and bases"); 176 let mut result = HasVtableResult::No; 177 178 if info.has_own_virtual_method() { 179 trace!(" comp has its own virtual method"); 180 result |= HasVtableResult::SelfHasVtable; 181 } 182 183 let bases_has_vtable = info.base_members().iter().any(|base| { 184 trace!(" comp has a base with a vtable: {:?}", base); 185 self.have_vtable.contains_key(&base.ty.into()) 186 }); 187 if bases_has_vtable { 188 result |= HasVtableResult::BaseHasVtable; 189 } 190 191 self.insert(id, result) 192 } 193 194 TypeKind::TemplateInstantiation(ref inst) => { 195 self.forward(inst.template_definition(), id) 196 } 197 198 _ => ConstrainResult::Same, 199 } 200 } 201 each_depending_on<F>(&self, id: ItemId, mut f: F) where F: FnMut(ItemId),202 fn each_depending_on<F>(&self, id: ItemId, mut f: F) 203 where 204 F: FnMut(ItemId), 205 { 206 if let Some(edges) = self.dependencies.get(&id) { 207 for item in edges { 208 trace!("enqueue {:?} into worklist", item); 209 f(*item); 210 } 211 } 212 } 213 } 214 215 impl<'ctx> From<HasVtableAnalysis<'ctx>> for HashMap<ItemId, HasVtableResult> { from(analysis: HasVtableAnalysis<'ctx>) -> Self216 fn from(analysis: HasVtableAnalysis<'ctx>) -> Self { 217 // We let the lack of an entry mean "No" to save space. 218 extra_assert!(analysis 219 .have_vtable 220 .values() 221 .all(|v| { *v != HasVtableResult::No })); 222 223 analysis.have_vtable 224 } 225 } 226 227 /// A convenience trait for the things for which we might wonder if they have a 228 /// vtable during codegen. 229 /// 230 /// This is not for _computing_ whether the thing has a vtable, it is for 231 /// looking up the results of the HasVtableAnalysis's computations for a 232 /// specific thing. 233 pub trait HasVtable { 234 /// Return `true` if this thing has vtable, `false` otherwise. has_vtable(&self, ctx: &BindgenContext) -> bool235 fn has_vtable(&self, ctx: &BindgenContext) -> bool; 236 237 /// Return `true` if this thing has an actual vtable pointer in itself, as 238 /// opposed to transitively in a base member. has_vtable_ptr(&self, ctx: &BindgenContext) -> bool239 fn has_vtable_ptr(&self, ctx: &BindgenContext) -> bool; 240 } 241