forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstatics.rs
More file actions
115 lines (101 loc) · 3.74 KB
/
statics.rs
File metadata and controls
115 lines (101 loc) · 3.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::graph::{DirectedGraph, Successors};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::def_id::DefId;
use rustc_index::{Idx, IndexVec, newtype_index};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::TyCtxt;
use crate::collector::UsageMap;
use crate::errors;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct StaticNodeIdx(usize);
impl Idx for StaticNodeIdx {
fn new(idx: usize) -> Self {
Self(idx)
}
fn index(self) -> usize {
self.0
}
}
impl From<usize> for StaticNodeIdx {
fn from(value: usize) -> Self {
StaticNodeIdx(value)
}
}
newtype_index! {
#[derive(Ord, PartialOrd)]
struct StaticSccIdx {}
}
// Adjacency-list graph for statics using `StaticNodeIdx` as node type.
// We cannot use `DefId` as the node type directly because each node must be
// represented by an index in the range `0..num_nodes`.
struct StaticRefGraph<'a, 'b, 'tcx> {
// maps from `StaticNodeIdx` to `DefId` and vice versa
statics: &'a FxIndexSet<DefId>,
// contains for each `MonoItem` the `MonoItem`s it uses
used_map: &'b UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
}
impl<'a, 'b, 'tcx> DirectedGraph for StaticRefGraph<'a, 'b, 'tcx> {
type Node = StaticNodeIdx;
fn num_nodes(&self) -> usize {
self.statics.len()
}
}
impl<'a, 'b, 'tcx> Successors for StaticRefGraph<'a, 'b, 'tcx> {
fn successors(&self, node_idx: StaticNodeIdx) -> impl Iterator<Item = StaticNodeIdx> {
let def_id = self.statics[node_idx.index()];
self.used_map[&MonoItem::Static(def_id)].iter().filter_map(|&mono_item| match mono_item {
MonoItem::Static(def_id) => self.statics.get_index_of(&def_id).map(|idx| idx.into()),
_ => None,
})
}
}
pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>(
tcx: TyCtxt<'tcx>,
mono_items: &'a [MonoItem<'tcx>],
usage_map: &'b UsageMap<'tcx>,
) {
// Collect statics
let statics: FxIndexSet<DefId> = mono_items
.iter()
.filter_map(|&mono_item| match mono_item {
MonoItem::Static(def_id) => Some(def_id),
_ => None,
})
.collect();
// If we don't have any statics the check is not necessary
if statics.is_empty() {
return;
}
// Create a subgraph from the mono item graph, which only contains statics
let graph = StaticRefGraph { statics: &statics, used_map: &usage_map.used_map };
// Calculate its SCCs
let sccs: Sccs<StaticNodeIdx, StaticSccIdx> = Sccs::new(&graph);
// Group statics by SCCs
let mut nodes_of_sccs: IndexVec<StaticSccIdx, Vec<StaticNodeIdx>> =
IndexVec::from_elem_n(Vec::new(), sccs.num_sccs());
for i in graph.iter_nodes() {
nodes_of_sccs[sccs.scc(i)].push(i);
}
let is_cyclic = |nodes_of_scc: &[StaticNodeIdx]| -> bool {
match nodes_of_scc.len() {
0 => false,
1 => graph.successors(nodes_of_scc[0]).any(|x| x == nodes_of_scc[0]),
2.. => true,
}
};
// Emit errors for all cycles
for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) {
// We sort the nodes by their Span to have consistent error line numbers
nodes.sort_by_key(|node| tcx.def_span(statics[node.index()]));
let head_def = statics[nodes[0].index()];
let head_span = tcx.def_span(head_def);
tcx.dcx().emit_err(errors::StaticInitializerCyclic {
span: head_span,
labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(),
head: &tcx.def_path_str(head_def),
target: &tcx.sess.target.llvm_target,
});
}
}