Skip to main content

rustc_monomorphize/graph_checks/
statics.rs

1use 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
37// Adjacency-list graph for statics using `StaticNodeIdx` as node type.
38// We cannot use `DefId` as the node type directly because each node must be
39// represented by an index in the range `0..num_nodes`.
40struct StaticRefGraph<'a, 'tcx> {
41    // maps from `StaticNodeIdx` to `DefId` and vice versa
42    statics: &'a FxIndexSet<DefId>,
43    // contains for each `MonoItem` the `MonoItem`s it uses
44    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    // Collect statics
71    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 we don't have any statics the check is not necessary
80    if statics.is_empty() {
81        return;
82    }
83    // Create a subgraph from the mono item graph, which only contains statics
84    let graph = StaticRefGraph { statics: &statics, used_map: &usage_map.used_map };
85    // Calculate its SCCs
86    let sccs: Sccs<StaticNodeIdx, StaticSccIdx> = Sccs::new(&graph);
87    // Group statics by SCCs
88    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    // Emit errors for all cycles
101    for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) {
102        // We sort the nodes by their Span to have consistent error line numbers
103        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}