rustc_monomorphize/graph_checks/
statics.rs1use rustc_data_structures::fx::FxIndexSet;
2use rustc_data_structures::graph::scc::Sccs;
3use rustc_data_structures::graph::{DirectedGraph, Successors};
4use rustc_data_structures::unord::UnordMap;
5use rustc_hir::def_id::DefId;
6use rustc_index::{Idx, IndexVec, newtype_index};
7use rustc_middle::mono::MonoItem;
8use rustc_middle::ty::TyCtxt;
9
10use crate::collector::UsageMap;
11use crate::diagnostics;
12
13#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for StaticNodeIdx { }
#[automatically_derived]
impl ::core::clone::Clone for StaticNodeIdx {
#[inline]
fn clone(&self) -> StaticNodeIdx {
let _: ::core::clone::AssertParamIsClone<usize>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for StaticNodeIdx { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for StaticNodeIdx {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "StaticNodeIdx",
&&self.0)
}
}Debug, #[automatically_derived]
impl ::core::cmp::Eq for StaticNodeIdx {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<usize>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for StaticNodeIdx {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for StaticNodeIdx { }
#[automatically_derived]
impl ::core::cmp::PartialEq for StaticNodeIdx {
#[inline]
fn eq(&self, other: &StaticNodeIdx) -> bool { self.0 == other.0 }
}PartialEq)]
14struct StaticNodeIdx(usize);
15
16impl Idx for StaticNodeIdx {
17 fn new(idx: usize) -> Self {
18 Self(idx)
19 }
20
21 fn index(self) -> usize {
22 self.0
23 }
24}
25
26impl From<usize> for StaticNodeIdx {
27 fn from(value: usize) -> Self {
28 StaticNodeIdx(value)
29 }
30}
31
32#[automatically_derived]
impl ::core::marker::Copy for StaticSccIdx { }
impl StaticSccIdx {
#[doc = r" Maximum value the index can take, as a `u32`."]
const MAX_AS_U32: u32 = 0xFFFF_FF00;
#[doc = r" Maximum value the index can take."]
const MAX: Self = Self::from_u32(0xFFFF_FF00);
#[doc = r" Zero value of the index."]
const ZERO: Self = Self::from_u32(0);
#[doc = r" Creates a new index from a given `usize`."]
#[doc = r""]
#[doc = r" # Panics"]
#[doc = r""]
#[doc = r" Will panic if `value` exceeds `MAX`."]
#[inline]
const fn from_usize(value: usize) -> Self {
if !(value <= (0xFFFF_FF00 as usize)) {
::core::panicking::panic("assertion failed: value <= (0xFFFF_FF00 as usize)")
};
unsafe { Self::from_u32_unchecked(value as u32) }
}
#[doc = r" Creates a new index from a given `u32`."]
#[doc = r""]
#[doc = r" # Panics"]
#[doc = r""]
#[doc = r" Will panic if `value` exceeds `MAX`."]
#[inline]
const fn from_u32(value: u32) -> Self {
if !(value <= 0xFFFF_FF00) {
::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
};
unsafe { Self::from_u32_unchecked(value) }
}
#[doc = r" Creates a new index from a given `u16`."]
#[doc = r""]
#[doc = r" # Panics"]
#[doc = r""]
#[doc = r" Will panic if `value` exceeds `MAX`."]
#[inline]
const fn from_u16(value: u16) -> Self {
let value = value as u32;
if !(value <= 0xFFFF_FF00) {
::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
};
unsafe { Self::from_u32_unchecked(value) }
}
#[doc = r" Creates a new index from a given `u32`."]
#[doc = r""]
#[doc = r" # Safety"]
#[doc = r""]
#[doc =
r" The provided value must be less than or equal to the maximum value for the newtype."]
#[doc =
r" Providing a value outside this range is undefined due to layout restrictions."]
#[doc = r""]
#[doc = r" Prefer using `from_u32`."]
#[inline]
const unsafe fn from_u32_unchecked(value: u32) -> Self {
Self {
private_use_as_methods_instead: unsafe {
std::mem::transmute(value)
},
}
}
#[doc = r" Extracts the value of this index as a `usize`."]
#[inline]
const fn index(self) -> usize { self.as_usize() }
#[doc = r" Extracts the value of this index as a `u32`."]
#[inline]
const fn as_u32(self) -> u32 {
unsafe { std::mem::transmute(self.private_use_as_methods_instead) }
}
#[doc = r" Extracts the value of this index as a `usize`."]
#[inline]
const fn as_usize(self) -> usize { self.as_u32() as usize }
}
impl std::ops::Add<usize> for StaticSccIdx {
type Output = Self;
#[inline]
fn add(self, other: usize) -> Self {
Self::from_usize(self.index() + other)
}
}
impl std::ops::AddAssign<usize> for StaticSccIdx {
#[inline]
fn add_assign(&mut self, other: usize) { *self = *self + other; }
}
impl rustc_index::Idx for StaticSccIdx {
#[inline]
fn new(value: usize) -> Self { Self::from_usize(value) }
#[inline]
fn index(self) -> usize { self.as_usize() }
}
impl ::std::iter::Step for StaticSccIdx {
#[inline]
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
<usize as
::std::iter::Step>::steps_between(&Self::index(*start),
&Self::index(*end))
}
#[inline]
fn forward_checked(start: Self, u: usize) -> Option<Self> {
Self::index(start).checked_add(u).map(Self::from_usize)
}
#[inline]
fn backward_checked(start: Self, u: usize) -> Option<Self> {
Self::index(start).checked_sub(u).map(Self::from_usize)
}
#[inline]
fn forward_overflowing(start: Self, u: usize) -> (Self, bool) {
let (s, o) = Self::index(start).overflowing_add(u);
(Self::from_usize(s), o)
}
#[inline]
fn backward_overflowing(start: Self, u: usize) -> (Self, bool) {
let (s, o) = Self::index(start).overflowing_sub(u);
(Self::from_usize(s), o)
}
}
impl ::std::cmp::Ord for StaticSccIdx {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_u32().cmp(&other.as_u32())
}
}
impl ::std::cmp::PartialOrd for StaticSccIdx {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<StaticSccIdx> for u32 {
#[inline]
fn from(v: StaticSccIdx) -> u32 { v.as_u32() }
}
impl From<StaticSccIdx> for usize {
#[inline]
fn from(v: StaticSccIdx) -> usize { v.as_usize() }
}
impl From<usize> for StaticSccIdx {
#[inline]
fn from(value: usize) -> Self { Self::from_usize(value) }
}
impl From<u32> for StaticSccIdx {
#[inline]
fn from(value: u32) -> Self { Self::from_u32(value) }
}
impl ::std::cmp::Eq for StaticSccIdx {}
impl ::std::cmp::PartialEq for StaticSccIdx {
fn eq(&self, other: &Self) -> bool { self.as_u32().eq(&other.as_u32()) }
}
impl ::std::marker::StructuralPartialEq for StaticSccIdx {}
impl ::std::hash::Hash for StaticSccIdx {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.as_u32().hash(state)
}
}
impl ::std::fmt::Debug for StaticSccIdx {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_fmt(format_args!("{0}", self.as_u32()))
}
}newtype_index! {
33 #[orderable]
34 struct StaticSccIdx {}
35}
36
37struct StaticRefGraph<'a, 'tcx> {
41 statics: &'a FxIndexSet<DefId>,
43 used_map: &'a UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
45}
46
47impl<'a, 'tcx> DirectedGraph for StaticRefGraph<'a, 'tcx> {
48 type Node = StaticNodeIdx;
49
50 fn num_nodes(&self) -> usize {
51 self.statics.len()
52 }
53}
54
55impl<'a, 'tcx> Successors for StaticRefGraph<'a, 'tcx> {
56 fn successors(&self, node_idx: StaticNodeIdx) -> impl Iterator<Item = StaticNodeIdx> {
57 let def_id = self.statics[node_idx.index()];
58 self.used_map[&MonoItem::Static(def_id)].iter().filter_map(|&mono_item| match mono_item {
59 MonoItem::Static(def_id) => self.statics.get_index_of(&def_id).map(|idx| idx.into()),
60 _ => None,
61 })
62 }
63}
64
65pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>(
66 tcx: TyCtxt<'tcx>,
67 mono_items: &'a [MonoItem<'tcx>],
68 usage_map: &'b UsageMap<'tcx>,
69) {
70 let statics: FxIndexSet<DefId> = mono_items
72 .iter()
73 .filter_map(|&mono_item| match mono_item {
74 MonoItem::Static(def_id) => Some(def_id),
75 _ => None,
76 })
77 .collect();
78
79 if statics.is_empty() {
81 return;
82 }
83 let graph = StaticRefGraph { statics: &statics, used_map: &usage_map.used_map };
85 let sccs: Sccs<StaticNodeIdx, StaticSccIdx> = Sccs::new(&graph);
87 let mut nodes_of_sccs: IndexVec<StaticSccIdx, Vec<StaticNodeIdx>> =
89 IndexVec::from_elem_n(Vec::new(), sccs.num_sccs());
90 for i in graph.iter_nodes() {
91 nodes_of_sccs[sccs.scc(i)].push(i);
92 }
93 let is_cyclic = |nodes_of_scc: &[StaticNodeIdx]| -> bool {
94 match nodes_of_scc.len() {
95 0 => false,
96 1 => graph.successors(nodes_of_scc[0]).any(|x| x == nodes_of_scc[0]),
97 2.. => true,
98 }
99 };
100 for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) {
102 nodes.sort_by_key(|node| tcx.def_span(statics[node.index()]));
104
105 let head_def = statics[nodes[0].index()];
106 let head_span = tcx.def_span(head_def);
107
108 tcx.dcx().emit_err(diagnostics::StaticInitializerCyclic {
109 span: head_span,
110 labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(),
111 head: &tcx.def_path_str(head_def),
112 target: &tcx.sess.target.llvm_target,
113 });
114 }
115}