| 1 | //! Discover which template type parameters are actually used. |
| 2 | //! |
| 3 | //! ### Why do we care? |
| 4 | //! |
| 5 | //! C++ allows ignoring template parameters, while Rust does not. Usually we can |
| 6 | //! blindly stick a `PhantomData<T>` inside a generic Rust struct to make up for |
| 7 | //! this. That doesn't work for templated type aliases, however: |
| 8 | //! |
| 9 | //! ```C++ |
| 10 | //! template <typename T> |
| 11 | //! using Fml = int; |
| 12 | //! ``` |
| 13 | //! |
| 14 | //! If we generate the naive Rust code for this alias, we get: |
| 15 | //! |
| 16 | //! ```ignore |
| 17 | //! pub(crate) type Fml<T> = ::std::os::raw::int; |
| 18 | //! ``` |
| 19 | //! |
| 20 | //! And this is rejected by `rustc` due to the unused type parameter. |
| 21 | //! |
| 22 | //! (Aside: in these simple cases, `libclang` will often just give us the |
| 23 | //! aliased type directly, and we will never even know we were dealing with |
| 24 | //! aliases, let alone templated aliases. It's the more convoluted scenarios |
| 25 | //! where we get to have some fun...) |
| 26 | //! |
| 27 | //! For such problematic template aliases, we could generate a tuple whose |
| 28 | //! second member is a `PhantomData<T>`. Or, if we wanted to go the extra mile, |
| 29 | //! we could even generate some smarter wrapper that implements `Deref`, |
| 30 | //! `DerefMut`, `From`, `Into`, `AsRef`, and `AsMut` to the actually aliased |
| 31 | //! type. However, this is still lackluster: |
| 32 | //! |
| 33 | //! 1. Even with a billion conversion-trait implementations, using the generated |
| 34 | //! bindings is rather un-ergonomic. |
| 35 | //! 2. With either of these solutions, we need to keep track of which aliases |
| 36 | //! we've transformed like this in order to generate correct uses of the |
| 37 | //! wrapped type. |
| 38 | //! |
| 39 | //! Given that we have to properly track which template parameters ended up used |
| 40 | //! for (2), we might as well leverage that information to make ergonomic |
| 41 | //! bindings that don't contain any unused type parameters at all, and |
| 42 | //! completely avoid the pain of (1). |
| 43 | //! |
| 44 | //! ### How do we determine which template parameters are used? |
| 45 | //! |
| 46 | //! Determining which template parameters are actually used is a trickier |
| 47 | //! problem than it might seem at a glance. On the one hand, trivial uses are |
| 48 | //! easy to detect: |
| 49 | //! |
| 50 | //! ```C++ |
| 51 | //! template <typename T> |
| 52 | //! class Foo { |
| 53 | //! T trivial_use_of_t; |
| 54 | //! }; |
| 55 | //! ``` |
| 56 | //! |
| 57 | //! It gets harder when determining if one template parameter is used depends on |
| 58 | //! determining if another template parameter is used. In this example, whether |
| 59 | //! `U` is used depends on whether `T` is used. |
| 60 | //! |
| 61 | //! ```C++ |
| 62 | //! template <typename T> |
| 63 | //! class DoesntUseT { |
| 64 | //! int x; |
| 65 | //! }; |
| 66 | //! |
| 67 | //! template <typename U> |
| 68 | //! class Fml { |
| 69 | //! DoesntUseT<U> lololol; |
| 70 | //! }; |
| 71 | //! ``` |
| 72 | //! |
| 73 | //! We can express the set of used template parameters as a constraint solving |
| 74 | //! problem (where the set of template parameters used by a given IR item is the |
| 75 | //! union of its sub-item's used template parameters) and iterate to a |
| 76 | //! fixed-point. |
| 77 | //! |
| 78 | //! We use the `ir::analysis::MonotoneFramework` infrastructure for this |
| 79 | //! fix-point analysis, where our lattice is the mapping from each IR item to |
| 80 | //! the powerset of the template parameters that appear in the input C++ header, |
| 81 | //! our join function is set union. The set of template parameters appearing in |
| 82 | //! the program is finite, as is the number of IR items. We start at our |
| 83 | //! lattice's bottom element: every item mapping to an empty set of template |
| 84 | //! parameters. Our analysis only adds members to each item's set of used |
| 85 | //! template parameters, never removes them, so it is monotone. Because our |
| 86 | //! lattice is finite and our constraint function is monotone, iteration to a |
| 87 | //! fix-point will terminate. |
| 88 | //! |
| 89 | //! See `src/ir/analysis.rs` for more. |
| 90 | |
| 91 | use super::{ConstrainResult, MonotoneFramework}; |
| 92 | use crate::ir::context::{BindgenContext, ItemId}; |
| 93 | use crate::ir::item::{Item, ItemSet}; |
| 94 | use crate::ir::template::{TemplateInstantiation, TemplateParameters}; |
| 95 | use crate::ir::traversal::{EdgeKind, Trace}; |
| 96 | use crate::ir::ty::TypeKind; |
| 97 | use crate::{HashMap, HashSet}; |
| 98 | |
| 99 | /// An analysis that finds for each IR item its set of template parameters that |
| 100 | /// it uses. |
| 101 | /// |
| 102 | /// We use the monotone constraint function `template_param_usage`, defined as |
| 103 | /// follows: |
| 104 | /// |
| 105 | /// * If `T` is a named template type parameter, it trivially uses itself: |
| 106 | /// |
| 107 | /// ```ignore |
| 108 | /// template_param_usage(T) = { T } |
| 109 | /// ``` |
| 110 | /// |
| 111 | /// * If `inst` is a template instantiation, `inst.args` are the template |
| 112 | /// instantiation's template arguments, `inst.def` is the template definition |
| 113 | /// being instantiated, and `inst.def.params` is the template definition's |
| 114 | /// template parameters, then the instantiation's usage is the union of each |
| 115 | /// of its arguments' usages *if* the corresponding template parameter is in |
| 116 | /// turn used by the template definition: |
| 117 | /// |
| 118 | /// ```ignore |
| 119 | /// template_param_usage(inst) = union( |
| 120 | /// template_param_usage(inst.args[i]) |
| 121 | /// for i in 0..length(inst.args.length) |
| 122 | /// if inst.def.params[i] in template_param_usage(inst.def) |
| 123 | /// ) |
| 124 | /// ``` |
| 125 | /// |
| 126 | /// * Finally, for all other IR item kinds, we use our lattice's `join` |
| 127 | /// operation: set union with each successor of the given item's template |
| 128 | /// parameter usage: |
| 129 | /// |
| 130 | /// ```ignore |
| 131 | /// template_param_usage(v) = |
| 132 | /// union(template_param_usage(w) for w in successors(v)) |
| 133 | /// ``` |
| 134 | /// |
| 135 | /// Note that we ignore certain edges in the graph, such as edges from a |
| 136 | /// template declaration to its template parameters' definitions for this |
| 137 | /// analysis. If we didn't, then we would mistakenly determine that ever |
| 138 | /// template parameter is always used. |
| 139 | /// |
| 140 | /// The final wrinkle is handling of blocklisted types. Normally, we say that |
| 141 | /// the set of allowlisted items is the transitive closure of items explicitly |
| 142 | /// called out for allowlisting, *without* any items explicitly called out as |
| 143 | /// blocklisted. However, for the purposes of this analysis's correctness, we |
| 144 | /// simplify and consider run the analysis on the full transitive closure of |
| 145 | /// allowlisted items. We do, however, treat instantiations of blocklisted items |
| 146 | /// specially; see `constrain_instantiation_of_blocklisted_template` and its |
| 147 | /// documentation for details. |
| 148 | #[derive (Debug, Clone)] |
| 149 | pub(crate) struct UsedTemplateParameters<'ctx> { |
| 150 | ctx: &'ctx BindgenContext, |
| 151 | |
| 152 | // The Option is only there for temporary moves out of the hash map. See the |
| 153 | // comments in `UsedTemplateParameters::constrain` below. |
| 154 | used: HashMap<ItemId, Option<ItemSet>>, |
| 155 | |
| 156 | dependencies: HashMap<ItemId, Vec<ItemId>>, |
| 157 | |
| 158 | // The set of allowlisted items, without any blocklisted items reachable |
| 159 | // from the allowlisted items which would otherwise be considered |
| 160 | // allowlisted as well. |
| 161 | allowlisted_items: HashSet<ItemId>, |
| 162 | } |
| 163 | |
| 164 | impl<'ctx> UsedTemplateParameters<'ctx> { |
| 165 | fn consider_edge(kind: EdgeKind) -> bool { |
| 166 | match kind { |
| 167 | // For each of these kinds of edges, if the referent uses a template |
| 168 | // parameter, then it should be considered that the origin of the |
| 169 | // edge also uses the template parameter. |
| 170 | EdgeKind::TemplateArgument | |
| 171 | EdgeKind::BaseMember | |
| 172 | EdgeKind::Field | |
| 173 | EdgeKind::Constructor | |
| 174 | EdgeKind::Destructor | |
| 175 | EdgeKind::VarType | |
| 176 | EdgeKind::FunctionReturn | |
| 177 | EdgeKind::FunctionParameter | |
| 178 | EdgeKind::TypeReference => true, |
| 179 | |
| 180 | // An inner var or type using a template parameter is orthogonal |
| 181 | // from whether we use it. See template-param-usage-{6,11}.hpp. |
| 182 | EdgeKind::InnerVar | EdgeKind::InnerType => false, |
| 183 | |
| 184 | // We can't emit machine code for new monomorphizations of class |
| 185 | // templates' methods (and don't detect explicit instantiations) so |
| 186 | // we must ignore template parameters that are only used by |
| 187 | // methods. This doesn't apply to a function type's return or |
| 188 | // parameter types, however, because of type aliases of function |
| 189 | // pointers that use template parameters, eg |
| 190 | // tests/headers/struct_with_typedef_template_arg.hpp |
| 191 | EdgeKind::Method => false, |
| 192 | |
| 193 | // If we considered these edges, we would end up mistakenly claiming |
| 194 | // that every template parameter always used. |
| 195 | EdgeKind::TemplateDeclaration | |
| 196 | EdgeKind::TemplateParameterDefinition => false, |
| 197 | |
| 198 | // Since we have to be careful about which edges we consider for |
| 199 | // this analysis to be correct, we ignore generic edges. We also |
| 200 | // avoid a `_` wild card to force authors of new edge kinds to |
| 201 | // determine whether they need to be considered by this analysis. |
| 202 | EdgeKind::Generic => false, |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | fn take_this_id_usage_set<Id: Into<ItemId>>( |
| 207 | &mut self, |
| 208 | this_id: Id, |
| 209 | ) -> ItemSet { |
| 210 | let this_id = this_id.into(); |
| 211 | self.used |
| 212 | .get_mut(&this_id) |
| 213 | .expect( |
| 214 | "Should have a set of used template params for every item \ |
| 215 | id" , |
| 216 | ) |
| 217 | .take() |
| 218 | .expect( |
| 219 | "Should maintain the invariant that all used template param \ |
| 220 | sets are `Some` upon entry of `constrain`" , |
| 221 | ) |
| 222 | } |
| 223 | |
| 224 | /// We say that blocklisted items use all of their template parameters. The |
| 225 | /// blocklisted type is most likely implemented explicitly by the user, |
| 226 | /// since it won't be in the generated bindings, and we don't know exactly |
| 227 | /// what they'll to with template parameters, but we can push the issue down |
| 228 | /// the line to them. |
| 229 | fn constrain_instantiation_of_blocklisted_template( |
| 230 | &self, |
| 231 | this_id: ItemId, |
| 232 | used_by_this_id: &mut ItemSet, |
| 233 | instantiation: &TemplateInstantiation, |
| 234 | ) { |
| 235 | trace!( |
| 236 | " instantiation of blocklisted template, uses all template \ |
| 237 | arguments" |
| 238 | ); |
| 239 | |
| 240 | let args = instantiation |
| 241 | .template_arguments() |
| 242 | .iter() |
| 243 | .map(|a| { |
| 244 | a.into_resolver() |
| 245 | .through_type_refs() |
| 246 | .through_type_aliases() |
| 247 | .resolve(self.ctx) |
| 248 | .id() |
| 249 | }) |
| 250 | .filter(|a| *a != this_id) |
| 251 | .flat_map(|a| { |
| 252 | self.used |
| 253 | .get(&a) |
| 254 | .expect("Should have a used entry for the template arg" ) |
| 255 | .as_ref() |
| 256 | .expect( |
| 257 | "Because a != this_id, and all used template \ |
| 258 | param sets other than this_id's are `Some`, \ |
| 259 | a's used template param set should be `Some`" , |
| 260 | ) |
| 261 | .iter() |
| 262 | .cloned() |
| 263 | }); |
| 264 | |
| 265 | used_by_this_id.extend(args); |
| 266 | } |
| 267 | |
| 268 | /// A template instantiation's concrete template argument is only used if |
| 269 | /// the template definition uses the corresponding template parameter. |
| 270 | fn constrain_instantiation( |
| 271 | &self, |
| 272 | this_id: ItemId, |
| 273 | used_by_this_id: &mut ItemSet, |
| 274 | instantiation: &TemplateInstantiation, |
| 275 | ) { |
| 276 | trace!(" template instantiation" ); |
| 277 | |
| 278 | let decl = self.ctx.resolve_type(instantiation.template_definition()); |
| 279 | let args = instantiation.template_arguments(); |
| 280 | |
| 281 | let params = decl.self_template_params(self.ctx); |
| 282 | |
| 283 | debug_assert!(this_id != instantiation.template_definition()); |
| 284 | let used_by_def = self.used |
| 285 | .get(&instantiation.template_definition().into()) |
| 286 | .expect("Should have a used entry for instantiation's template definition" ) |
| 287 | .as_ref() |
| 288 | .expect("And it should be Some because only this_id's set is None, and an \ |
| 289 | instantiation's template definition should never be the \ |
| 290 | instantiation itself" ); |
| 291 | |
| 292 | for (arg, param) in args.iter().zip(params.iter()) { |
| 293 | trace!( |
| 294 | " instantiation's argument {:?} is used if definition's \ |
| 295 | parameter {:?} is used" , |
| 296 | arg, |
| 297 | param |
| 298 | ); |
| 299 | |
| 300 | if used_by_def.contains(¶m.into()) { |
| 301 | trace!(" param is used by template definition" ); |
| 302 | |
| 303 | let arg = arg |
| 304 | .into_resolver() |
| 305 | .through_type_refs() |
| 306 | .through_type_aliases() |
| 307 | .resolve(self.ctx) |
| 308 | .id(); |
| 309 | |
| 310 | if arg == this_id { |
| 311 | continue; |
| 312 | } |
| 313 | |
| 314 | let used_by_arg = self |
| 315 | .used |
| 316 | .get(&arg) |
| 317 | .expect("Should have a used entry for the template arg" ) |
| 318 | .as_ref() |
| 319 | .expect( |
| 320 | "Because arg != this_id, and all used template \ |
| 321 | param sets other than this_id's are `Some`, \ |
| 322 | arg's used template param set should be \ |
| 323 | `Some`" , |
| 324 | ) |
| 325 | .iter() |
| 326 | .cloned(); |
| 327 | used_by_this_id.extend(used_by_arg); |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | /// The join operation on our lattice: the set union of all of this ID's |
| 333 | /// successors. |
| 334 | fn constrain_join(&self, used_by_this_id: &mut ItemSet, item: &Item) { |
| 335 | trace!(" other item: join with successors' usage" ); |
| 336 | |
| 337 | item.trace( |
| 338 | self.ctx, |
| 339 | &mut |sub_id, edge_kind| { |
| 340 | // Ignore ourselves, since union with ourself is a |
| 341 | // no-op. Ignore edges that aren't relevant to the |
| 342 | // analysis. |
| 343 | if sub_id == item.id() || !Self::consider_edge(edge_kind) { |
| 344 | return; |
| 345 | } |
| 346 | |
| 347 | let used_by_sub_id = self |
| 348 | .used |
| 349 | .get(&sub_id) |
| 350 | .expect("Should have a used set for the sub_id successor" ) |
| 351 | .as_ref() |
| 352 | .expect( |
| 353 | "Because sub_id != id, and all used template \ |
| 354 | param sets other than id's are `Some`, \ |
| 355 | sub_id's used template param set should be \ |
| 356 | `Some`" , |
| 357 | ) |
| 358 | .iter() |
| 359 | .cloned(); |
| 360 | |
| 361 | trace!( |
| 362 | " union with {:?}'s usage: {:?}" , |
| 363 | sub_id, |
| 364 | used_by_sub_id.clone().collect::<Vec<_>>() |
| 365 | ); |
| 366 | |
| 367 | used_by_this_id.extend(used_by_sub_id); |
| 368 | }, |
| 369 | &(), |
| 370 | ); |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | impl<'ctx> MonotoneFramework for UsedTemplateParameters<'ctx> { |
| 375 | type Node = ItemId; |
| 376 | type Extra = &'ctx BindgenContext; |
| 377 | type Output = HashMap<ItemId, ItemSet>; |
| 378 | |
| 379 | fn new(ctx: &'ctx BindgenContext) -> UsedTemplateParameters<'ctx> { |
| 380 | let mut used = HashMap::default(); |
| 381 | let mut dependencies = HashMap::default(); |
| 382 | let allowlisted_items: HashSet<_> = |
| 383 | ctx.allowlisted_items().iter().cloned().collect(); |
| 384 | |
| 385 | let allowlisted_and_blocklisted_items: ItemSet = allowlisted_items |
| 386 | .iter() |
| 387 | .cloned() |
| 388 | .flat_map(|i| { |
| 389 | let mut reachable = vec![i]; |
| 390 | i.trace( |
| 391 | ctx, |
| 392 | &mut |s, _| { |
| 393 | reachable.push(s); |
| 394 | }, |
| 395 | &(), |
| 396 | ); |
| 397 | reachable |
| 398 | }) |
| 399 | .collect(); |
| 400 | |
| 401 | for item in allowlisted_and_blocklisted_items { |
| 402 | dependencies.entry(item).or_insert_with(Vec::new); |
| 403 | used.entry(item).or_insert_with(|| Some(ItemSet::new())); |
| 404 | |
| 405 | { |
| 406 | // We reverse our natural IR graph edges to find dependencies |
| 407 | // between nodes. |
| 408 | item.trace( |
| 409 | ctx, |
| 410 | &mut |sub_item: ItemId, _| { |
| 411 | used.entry(sub_item) |
| 412 | .or_insert_with(|| Some(ItemSet::new())); |
| 413 | dependencies |
| 414 | .entry(sub_item) |
| 415 | .or_insert_with(Vec::new) |
| 416 | .push(item); |
| 417 | }, |
| 418 | &(), |
| 419 | ); |
| 420 | } |
| 421 | |
| 422 | // Additionally, whether a template instantiation's template |
| 423 | // arguments are used depends on whether the template declaration's |
| 424 | // generic template parameters are used. |
| 425 | let item_kind = |
| 426 | ctx.resolve_item(item).as_type().map(|ty| ty.kind()); |
| 427 | if let Some(TypeKind::TemplateInstantiation(inst)) = item_kind { |
| 428 | let decl = ctx.resolve_type(inst.template_definition()); |
| 429 | let args = inst.template_arguments(); |
| 430 | |
| 431 | // Although template definitions should always have |
| 432 | // template parameters, there is a single exception: |
| 433 | // opaque templates. Hence the unwrap_or. |
| 434 | let params = decl.self_template_params(ctx); |
| 435 | |
| 436 | for (arg, param) in args.iter().zip(params.iter()) { |
| 437 | let arg = arg |
| 438 | .into_resolver() |
| 439 | .through_type_aliases() |
| 440 | .through_type_refs() |
| 441 | .resolve(ctx) |
| 442 | .id(); |
| 443 | |
| 444 | let param = param |
| 445 | .into_resolver() |
| 446 | .through_type_aliases() |
| 447 | .through_type_refs() |
| 448 | .resolve(ctx) |
| 449 | .id(); |
| 450 | |
| 451 | used.entry(arg).or_insert_with(|| Some(ItemSet::new())); |
| 452 | used.entry(param).or_insert_with(|| Some(ItemSet::new())); |
| 453 | |
| 454 | dependencies |
| 455 | .entry(arg) |
| 456 | .or_insert_with(Vec::new) |
| 457 | .push(param); |
| 458 | } |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | if cfg!(feature = "__testing_only_extra_assertions" ) { |
| 463 | // Invariant: The `used` map has an entry for every allowlisted |
| 464 | // item, as well as all explicitly blocklisted items that are |
| 465 | // reachable from allowlisted items. |
| 466 | // |
| 467 | // Invariant: the `dependencies` map has an entry for every |
| 468 | // allowlisted item. |
| 469 | // |
| 470 | // (This is so that every item we call `constrain` on is guaranteed |
| 471 | // to have a set of template parameters, and we can allow |
| 472 | // blocklisted templates to use all of their parameters). |
| 473 | for item in allowlisted_items.iter() { |
| 474 | extra_assert!(used.contains_key(item)); |
| 475 | extra_assert!(dependencies.contains_key(item)); |
| 476 | item.trace( |
| 477 | ctx, |
| 478 | &mut |sub_item, _| { |
| 479 | extra_assert!(used.contains_key(&sub_item)); |
| 480 | extra_assert!(dependencies.contains_key(&sub_item)); |
| 481 | }, |
| 482 | &(), |
| 483 | ) |
| 484 | } |
| 485 | } |
| 486 | |
| 487 | UsedTemplateParameters { |
| 488 | ctx, |
| 489 | used, |
| 490 | dependencies, |
| 491 | allowlisted_items, |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | fn initial_worklist(&self) -> Vec<ItemId> { |
| 496 | // The transitive closure of all allowlisted items, including explicitly |
| 497 | // blocklisted items. |
| 498 | self.ctx |
| 499 | .allowlisted_items() |
| 500 | .iter() |
| 501 | .cloned() |
| 502 | .flat_map(|i| { |
| 503 | let mut reachable = vec![i]; |
| 504 | i.trace( |
| 505 | self.ctx, |
| 506 | &mut |s, _| { |
| 507 | reachable.push(s); |
| 508 | }, |
| 509 | &(), |
| 510 | ); |
| 511 | reachable |
| 512 | }) |
| 513 | .collect() |
| 514 | } |
| 515 | |
| 516 | fn constrain(&mut self, id: ItemId) -> ConstrainResult { |
| 517 | // Invariant: all hash map entries' values are `Some` upon entering and |
| 518 | // exiting this method. |
| 519 | extra_assert!(self.used.values().all(|v| v.is_some())); |
| 520 | |
| 521 | // Take the set for this ID out of the hash map while we mutate it based |
| 522 | // on other hash map entries. We *must* put it back into the hash map at |
| 523 | // the end of this method. This allows us to side-step HashMap's lack of |
| 524 | // an analog to slice::split_at_mut. |
| 525 | let mut used_by_this_id = self.take_this_id_usage_set(id); |
| 526 | |
| 527 | trace!("constrain {:?}" , id); |
| 528 | trace!(" initially, used set is {:?}" , used_by_this_id); |
| 529 | |
| 530 | let original_len = used_by_this_id.len(); |
| 531 | |
| 532 | let item = self.ctx.resolve_item(id); |
| 533 | let ty_kind = item.as_type().map(|ty| ty.kind()); |
| 534 | match ty_kind { |
| 535 | // Named template type parameters trivially use themselves. |
| 536 | Some(&TypeKind::TypeParam) => { |
| 537 | trace!(" named type, trivially uses itself" ); |
| 538 | used_by_this_id.insert(id); |
| 539 | } |
| 540 | // Template instantiations only use their template arguments if the |
| 541 | // template definition uses the corresponding template parameter. |
| 542 | Some(TypeKind::TemplateInstantiation(inst)) => { |
| 543 | if self |
| 544 | .allowlisted_items |
| 545 | .contains(&inst.template_definition().into()) |
| 546 | { |
| 547 | self.constrain_instantiation( |
| 548 | id, |
| 549 | &mut used_by_this_id, |
| 550 | inst, |
| 551 | ); |
| 552 | } else { |
| 553 | self.constrain_instantiation_of_blocklisted_template( |
| 554 | id, |
| 555 | &mut used_by_this_id, |
| 556 | inst, |
| 557 | ); |
| 558 | } |
| 559 | } |
| 560 | // Otherwise, add the union of each of its referent item's template |
| 561 | // parameter usage. |
| 562 | _ => self.constrain_join(&mut used_by_this_id, item), |
| 563 | } |
| 564 | |
| 565 | trace!(" finally, used set is {:?}" , used_by_this_id); |
| 566 | |
| 567 | let new_len = used_by_this_id.len(); |
| 568 | assert!( |
| 569 | new_len >= original_len, |
| 570 | "This is the property that ensures this function is monotone -- \ |
| 571 | if it doesn't hold, the analysis might never terminate!" |
| 572 | ); |
| 573 | |
| 574 | // Put the set back in the hash map and restore our invariant. |
| 575 | debug_assert!(self.used[&id].is_none()); |
| 576 | self.used.insert(id, Some(used_by_this_id)); |
| 577 | extra_assert!(self.used.values().all(|v| v.is_some())); |
| 578 | |
| 579 | if new_len != original_len { |
| 580 | ConstrainResult::Changed |
| 581 | } else { |
| 582 | ConstrainResult::Same |
| 583 | } |
| 584 | } |
| 585 | |
| 586 | fn each_depending_on<F>(&self, item: ItemId, mut f: F) |
| 587 | where |
| 588 | F: FnMut(ItemId), |
| 589 | { |
| 590 | if let Some(edges) = self.dependencies.get(&item) { |
| 591 | for item in edges { |
| 592 | trace!("enqueue {:?} into worklist" , item); |
| 593 | f(*item); |
| 594 | } |
| 595 | } |
| 596 | } |
| 597 | } |
| 598 | |
| 599 | impl<'ctx> From<UsedTemplateParameters<'ctx>> for HashMap<ItemId, ItemSet> { |
| 600 | fn from(used_templ_params: UsedTemplateParameters<'ctx>) -> Self { |
| 601 | used_templ_paramsimpl Iterator |
| 602 | .used |
| 603 | .into_iter() |
| 604 | .map(|(k: ItemId, v: Option>)| (k, v.unwrap())) |
| 605 | .collect() |
| 606 | } |
| 607 | } |
| 608 | |