summary refs log tree commit diff
path: root/src/librustc/ty/constness.rs
blob: e32913b8905b7b2ac8f761aa875ed5da22064acd (plain)
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
use ty::query::Providers;
use hir::def_id::DefId;
use hir;
use ty::TyCtxt;
use syntax_pos::symbol::Symbol;
use hir::map::blocks::FnLikeNode;
use syntax::attr;
use rustc_target::spec::abi;

impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
    /// Whether the `def_id` counts as const fn in your current crate, considering all active
    /// feature gates
    pub fn is_const_fn(self, def_id: DefId) -> bool {
        self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
            Some(stab) => match stab.const_stability {
                // has a `rustc_const_unstable` attribute, check whether the user enabled the
                // corresponding feature gate
                Some(feature_name) => self.features()
                    .declared_lib_features
                    .iter()
                    .any(|&(sym, _)| sym == feature_name),
                // the function has no stability attribute, it is stable as const fn or the user
                // needs to use feature gates to use the function at all
                None => true,
            },
            // functions without stability are either stable user written const fn or the user is
            // using feature gates and we thus don't care what they do
            None => true,
        }
    }

    /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
    pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
        if self.is_const_fn_raw(def_id) {
            self.lookup_stability(def_id)?.const_stability
        } else {
            None
        }
    }

    /// Returns true if this function must conform to `min_const_fn`
    pub fn is_min_const_fn(self, def_id: DefId) -> bool {
        if self.features().staged_api {
            // some intrinsics are waved through if called inside the
            // standard library. Users never need to call them directly
            if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
                assert!(!self.is_const_fn(def_id));
                match &self.item_name(def_id).as_str()[..] {
                    | "size_of"
                    | "min_align_of"
                    | "needs_drop"
                    => return true,
                    _ => {},
                }
            }
            // in order for a libstd function to be considered min_const_fn
            // it needs to be stable and have no `rustc_const_unstable` attribute
            match self.lookup_stability(def_id) {
                // stable functions with unstable const fn aren't `min_const_fn`
                Some(&attr::Stability { const_stability: Some(_), .. }) => false,
                // unstable functions don't need to conform
                Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
                // everything else needs to conform, because it would be callable from
                // other `min_const_fn` functions
                _ => true,
            }
        } else {
            // users enabling the `const_fn` feature gate can do what they want
            !self.features().const_fn
        }
    }
}


pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
    /// only checks whether the function has a `const` modifier
    fn is_const_fn_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
        let node_id = tcx.hir.as_local_node_id(def_id)
                             .expect("Non-local call to local provider is_const_fn");

        if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
            fn_like.constness() == hir::Constness::Const
        } else {
            false
        }
    }

    fn is_promotable_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
        tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
            Some(stab) => {
                if cfg!(debug_assertions) && stab.promotable {
                    let sig = tcx.fn_sig(def_id);
                    assert_eq!(
                        sig.unsafety(),
                        hir::Unsafety::Normal,
                        "don't mark const unsafe fns as promotable",
                        // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
                    );
                }
                stab.promotable
            },
            None => false,
        }
    }

    *providers = Providers {
        is_const_fn_raw,
        is_promotable_const_fn,
        ..*providers
    };
}