charon_lib/transform/add_missing_info/
reorder_decls.rs1use crate::common::*;
10use crate::transform::TransformCtx;
11use crate::ullbc_ast::*;
12use derive_generic_visitor::*;
13use itertools::Itertools;
14use petgraph::graphmap::DiGraphMap;
15use std::collections::{HashMap, HashSet};
16use std::fmt::{Debug, Display, Error};
17use std::vec::Vec;
18
19use crate::transform::ctx::TransformPass;
20
21impl<Id: Copy> GDeclarationGroup<Id> {
22 pub fn get_ids(&self) -> &[Id] {
23 use GDeclarationGroup::*;
24 match self {
25 NonRec(id) => std::slice::from_ref(id),
26 Rec(ids) => ids.as_slice(),
27 }
28 }
29
30 pub fn get_any_trans_ids(&self) -> Vec<ItemId>
31 where
32 Id: Into<ItemId>,
33 {
34 self.get_ids().iter().copied().map(|id| id.into()).collect()
35 }
36
37 fn make_group(is_rec: bool, ids: Vec<ItemId>) -> Self
38 where
39 Id: TryFrom<ItemId>,
40 Id::Error: Debug,
41 {
42 let ids: Vec<_> = ids.into_iter().map(|x| x.try_into().unwrap()).collect();
43 if is_rec {
44 GDeclarationGroup::Rec(ids)
45 } else {
46 assert!(ids.len() == 1);
47 GDeclarationGroup::NonRec(ids[0])
48 }
49 }
50
51 fn to_mixed(&self) -> GDeclarationGroup<ItemId>
52 where
53 Id: Into<ItemId>,
54 {
55 match self {
56 GDeclarationGroup::NonRec(x) => GDeclarationGroup::NonRec((*x).into()),
57 GDeclarationGroup::Rec(_) => GDeclarationGroup::Rec(self.get_any_trans_ids()),
58 }
59 }
60}
61
62impl DeclarationGroup {
63 fn make_group(is_rec: bool, ids: Vec<ItemId>) -> Self {
64 let id0 = ids[0];
65 let all_same_kind = ids
66 .iter()
67 .all(|id| id0.variant_index_arity() == id.variant_index_arity());
68 match id0 {
69 _ if !all_same_kind => {
70 DeclarationGroup::Mixed(GDeclarationGroup::make_group(is_rec, ids))
71 }
72 ItemId::Type(_) => DeclarationGroup::Type(GDeclarationGroup::make_group(is_rec, ids)),
73 ItemId::Fun(_) => DeclarationGroup::Fun(GDeclarationGroup::make_group(is_rec, ids)),
74 ItemId::Global(_) => {
75 DeclarationGroup::Global(GDeclarationGroup::make_group(is_rec, ids))
76 }
77 ItemId::TraitDecl(_) => {
78 DeclarationGroup::TraitDecl(GDeclarationGroup::make_group(is_rec, ids))
79 }
80 ItemId::TraitImpl(_) => {
81 DeclarationGroup::TraitImpl(GDeclarationGroup::make_group(is_rec, ids))
82 }
83 }
84 }
85
86 pub fn to_mixed_group(&self) -> GDeclarationGroup<ItemId> {
87 use DeclarationGroup::*;
88 match self {
89 Type(gr) => gr.to_mixed(),
90 Fun(gr) => gr.to_mixed(),
91 Global(gr) => gr.to_mixed(),
92 TraitDecl(gr) => gr.to_mixed(),
93 TraitImpl(gr) => gr.to_mixed(),
94 Mixed(gr) => gr.clone(),
95 }
96 }
97
98 pub fn get_ids(&self) -> Vec<ItemId> {
99 use DeclarationGroup::*;
100 match self {
101 Type(gr) => gr.get_any_trans_ids(),
102 Fun(gr) => gr.get_any_trans_ids(),
103 Global(gr) => gr.get_any_trans_ids(),
104 TraitDecl(gr) => gr.get_any_trans_ids(),
105 TraitImpl(gr) => gr.get_any_trans_ids(),
106 Mixed(gr) => gr.get_any_trans_ids(),
107 }
108 }
109}
110
111impl<Id: Display> Display for GDeclarationGroup<Id> {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), Error> {
113 match self {
114 GDeclarationGroup::NonRec(id) => write!(f, "non-rec: {id}"),
115 GDeclarationGroup::Rec(ids) => {
116 write!(
117 f,
118 "rec: {}",
119 pretty_display_list(|id| format!(" {id}"), ids)
120 )
121 }
122 }
123 }
124}
125
126impl Display for DeclarationGroup {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), Error> {
128 match self {
129 DeclarationGroup::Type(decl) => write!(f, "{{ Type(s): {decl} }}"),
130 DeclarationGroup::Fun(decl) => write!(f, "{{ Fun(s): {decl} }}"),
131 DeclarationGroup::Global(decl) => write!(f, "{{ Global(s): {decl} }}"),
132 DeclarationGroup::TraitDecl(decl) => write!(f, "{{ Trait decls(s): {decl} }}"),
133 DeclarationGroup::TraitImpl(decl) => write!(f, "{{ Trait impl(s): {decl} }}"),
134 DeclarationGroup::Mixed(decl) => write!(f, "{{ Mixed items: {decl} }}"),
135 }
136 }
137}
138
139#[derive(Default)]
140pub struct Deps {
141 graph: DiGraphMap<ItemId, ()>,
144 unprocessed: Vec<ItemId>,
145 visited: HashSet<ItemId>,
146}
147
148#[derive(Visitor)]
150pub struct DepsForItem<'a> {
151 ctx: &'a TransformCtx,
152 deps: &'a mut Deps,
153 current_id: ItemId,
154 parent_trait_impl: Option<TraitImplId>,
196 parent_trait_decl: Option<TraitDeclId>,
197}
198
199impl Deps {
200 fn visitor_for_item<'a>(
201 &'a mut self,
202 ctx: &'a TransformCtx,
203 item: ItemRef<'_>,
204 ) -> DepsForItem<'a> {
205 let current_id = item.id();
206 self.graph.add_node(current_id);
207
208 let mut for_item = DepsForItem {
209 ctx,
210 deps: self,
211 current_id,
212 parent_trait_impl: None,
213 parent_trait_decl: None,
214 };
215
216 match item.parent_info() {
218 ItemSource::TraitDecl { trait_ref, .. } => {
219 for_item.parent_trait_decl = Some(trait_ref.id)
220 }
221 ItemSource::TraitImpl { impl_ref, .. } => {
222 for_item.parent_trait_impl = Some(impl_ref.id)
223 }
224 _ => {}
225 }
226
227 for_item
228 }
229}
230
231impl DepsForItem<'_> {
232 fn insert_node(&mut self, tgt: impl Into<ItemId>) {
233 let tgt = tgt.into();
234 if self.ctx.translated.get_item(tgt).is_some() {
236 if !self.deps.visited.contains(&tgt) {
237 self.deps.unprocessed.push(tgt);
238 }
239 }
240 }
241 fn insert_edge(&mut self, tgt: impl Into<ItemId>) {
242 let tgt = tgt.into();
243 self.insert_node(tgt);
244 if self.ctx.translated.get_item(tgt).is_some() {
246 self.deps.graph.add_edge(self.current_id, tgt, ());
247 }
248 }
249}
250
251impl VisitAst for DepsForItem<'_> {
252 fn enter_type_decl_id(&mut self, id: &TypeDeclId) {
253 self.insert_edge(*id);
254 }
255
256 fn enter_global_decl_id(&mut self, id: &GlobalDeclId) {
257 self.insert_edge(*id);
258 }
259
260 fn enter_trait_impl_id(&mut self, id: &TraitImplId) {
261 if self.parent_trait_impl != Some(*id) {
266 self.insert_edge(*id);
267 }
268 }
269
270 fn enter_trait_decl_id(&mut self, id: &TraitDeclId) {
271 if self.parent_trait_decl != Some(*id) {
275 self.insert_edge(*id);
276 }
277 }
278
279 fn enter_fun_decl_id(&mut self, id: &FunDeclId) {
280 self.insert_edge(*id);
281 }
282
283 fn visit_item_meta(&mut self, _: &ItemMeta) -> ControlFlow<Self::Break> {
284 Continue(())
286 }
287 fn visit_item_source(&mut self, _: &ItemSource) -> ControlFlow<Self::Break> {
288 Continue(())
291 }
292}
293
294fn compute_declarations_graph<'tcx>(ctx: &'tcx TransformCtx) -> DiGraphMap<ItemId, ()> {
295 let mut deps = Deps::default();
296 deps.unprocessed = ctx
299 .translated
300 .all_items()
301 .filter(|item| {
302 ctx.options
303 .start_from
304 .iter()
305 .any(|pat| pat.matches(&ctx.translated, &item.item_meta().name))
306 })
307 .map(|item| item.id())
308 .collect();
309
310 while let Some(id) = deps.unprocessed.pop() {
312 if !deps.visited.insert(id) {
313 continue;
314 }
315 let Some(item) = ctx.translated.get_item(id) else {
316 continue;
317 };
318 let mut visitor = deps.visitor_for_item(ctx, item);
319 match item {
320 ItemRef::Type(..) | ItemRef::TraitImpl(..) | ItemRef::Global(..) => {
321 let _ = item.drive(&mut visitor);
322 }
323 ItemRef::Fun(d) => {
324 let FunDecl {
325 def_id: _,
326 item_meta: _,
327 generics,
328 signature,
329 src,
330 is_global_initializer: _,
331 body,
332 } = d;
333 let _ = generics.drive(&mut visitor);
336 let _ = signature.drive(&mut visitor);
337 let _ = body.drive(&mut visitor);
338 match src {
339 ItemSource::TraitDecl { trait_ref, .. } => {
340 visitor.insert_edge(trait_ref.id);
341 }
342 ItemSource::TraitImpl { impl_ref, .. } => {
343 visitor.insert_node(impl_ref.id);
345 }
346 _ => (),
347 }
348 }
349 ItemRef::TraitDecl(d) => {
350 let TraitDecl {
351 def_id: _,
352 item_meta: _,
353 generics,
354 implied_clauses: parent_clauses,
355 consts,
356 types,
357 methods,
358 vtable,
359 } = d;
360 let _ = generics.drive(&mut visitor);
362
363 let _ = parent_clauses.drive(&mut visitor);
365
366 let _ = types.drive(&mut visitor);
368 let _ = vtable.drive(&mut visitor);
369
370 for assoc_const in consts {
373 let TraitAssocConst {
374 name: _,
375 ty,
376 default,
377 } = assoc_const;
378 let _ = ty.drive(&mut visitor);
379 if let Some(gref) = default {
380 visitor.insert_node(gref.id); let _ = gref.generics.drive(&mut visitor);
382 }
383 }
384 for bound_method in methods {
385 let id = bound_method.skip_binder.item.id;
386 visitor.insert_node(id); let _ = bound_method.params.drive(&mut visitor);
388 if let Some(decl) = ctx.translated.fun_decls.get(id) {
389 let _ = decl.signature.drive(&mut visitor);
390 }
391 }
392 }
393 }
394 }
395 deps.graph
396}
397
398fn compute_reordered_decls(ctx: &mut TransformCtx) -> DeclarationsGroups {
399 let graph = compute_declarations_graph(ctx);
401
402 for id in ctx.translated.all_ids() {
404 if !graph.contains_node(id) {
405 ctx.translated.remove_item(id);
406 }
407 }
408
409 let sorted_file_ids: IndexMap<FileId, usize> = ctx
412 .translated
413 .files
414 .all_indices()
415 .sorted_by_cached_key(|&file_id| {
416 let file = &ctx.translated.files[file_id];
417 let is_std = file.crate_name == "std" || file.crate_name == "core";
418 (!is_std, &file.crate_name, &file.name)
419 })
420 .enumerate()
421 .sorted_by_key(|(_i, file_id)| *file_id)
422 .map(|(i, _file_id)| i)
423 .collect();
424 assert_eq!(ctx.translated.files.len(), sorted_file_ids.slot_count());
425
426 let sort_by = |item: &ItemRef| {
429 let item_meta = item.item_meta();
430 let span = item_meta.span.data;
431 let file_name_order = sorted_file_ids.get(span.file_id);
432 (item_meta.is_local, file_name_order, span.beg, item.id())
433 };
434 let item_sorted_index: HashMap<ItemId, usize> = ctx
436 .translated
437 .all_items()
438 .sorted_by_cached_key(sort_by)
439 .enumerate()
440 .map(|(i, item)| (item.id(), i))
441 .collect();
442 let sort_by = |id: &ItemId| item_sorted_index.get(id).unwrap();
443
444 let reordered_sccs = super::sccs::ordered_scc(&graph, sort_by);
447
448 let reordered_decls = reordered_sccs
450 .into_iter()
451 .filter(|scc| !scc.is_empty())
453 .map(|scc| {
454 let id0 = scc[0];
462 let is_non_rec =
463 scc.len() == 1 && (id0.is_trait_decl() || !graph.neighbors(id0).contains(&id0));
464
465 DeclarationGroup::make_group(!is_non_rec, scc)
466 })
467 .collect();
468
469 trace!("{:?}", reordered_decls);
470 reordered_decls
471}
472
473pub struct Transform;
474impl TransformPass for Transform {
475 fn transform_ctx(&self, ctx: &mut TransformCtx) {
476 let reordered_decls = compute_reordered_decls(ctx);
477 ctx.translated.ordered_decls = Some(reordered_decls);
478 }
479}