]> git.lizzy.rs Git - rust.git/blob - src/librustc/ty/constness.rs
Clean up the logic in `is_min_const_fn`
[rust.git] / src / librustc / ty / constness.rs
1 use ty::query::Providers;
2 use hir::def_id::DefId;
3 use hir;
4 use ty::TyCtxt;
5 use syntax_pos::symbol::Symbol;
6 use hir::map::blocks::FnLikeNode;
7 use syntax::attr;
8 use rustc_target::spec::abi;
9
10 impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
11     /// Whether the `def_id` counts as const fn in your current crate, considering all active
12     /// feature gates
13     pub fn is_const_fn(self, def_id: DefId) -> bool {
14         self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
15             Some(stab) => match stab.const_stability {
16                 // has a `rustc_const_unstable` attribute, check whether the user enabled the
17                 // corresponding feature gate
18                 Some(feature_name) => self.features()
19                     .declared_lib_features
20                     .iter()
21                     .any(|&(sym, _)| sym == feature_name),
22                 // the function has no stability attribute, it is stable as const fn or the user
23                 // needs to use feature gates to use the function at all
24                 None => true,
25             },
26             // functions without stability are either stable user written const fn or the user is
27             // using feature gates and we thus don't care what they do
28             None => true,
29         }
30     }
31
32     /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
33     pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
34         if self.is_const_fn_raw(def_id) {
35             self.lookup_stability(def_id)?.const_stability
36         } else {
37             None
38         }
39     }
40
41     /// Returns true if this function must conform to `min_const_fn`
42     pub fn is_min_const_fn(self, def_id: DefId) -> bool {
43         // some intrinsics are waved through if called inside the
44         // standard library. Users never need to call them directly
45         if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
46             match &self.item_name(def_id).as_str()[..] {
47                 | "size_of"
48                 | "min_align_of"
49                 | "needs_drop"
50                 => return true,
51                 _ => {},
52             }
53         }
54
55         // Bail out if the signature doesn't contain `const`
56         if !self.is_const_fn_raw(def_id) {
57             return false;
58         }
59
60         if self.features().staged_api {
61             // in order for a libstd function to be considered min_const_fn
62             // it needs to be stable and have no `rustc_const_unstable` attribute
63             self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
64                 // stable functions with unstable const fn aren't `min_const_fn`
65                 Some(&attr::Stability { const_stability: Some(_), .. }) => false,
66                 // unstable functions don't need to conform
67                 Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
68                 // everything else needs to conform, because it would be callable from
69                 // other `min_const_fn` functions
70                 _ => true,
71             }
72         } else {
73             // users enabling the `const_fn` feature gate can do what they want
74             !self.features().const_fn
75         }
76     }
77 }
78
79
80 pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
81     /// only checks whether the function has a `const` modifier
82     fn is_const_fn_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
83         let node_id = tcx.hir.as_local_node_id(def_id)
84                              .expect("Non-local call to local provider is_const_fn");
85
86         if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
87             fn_like.constness() == hir::Constness::Const
88         } else {
89             false
90         }
91     }
92
93     fn is_promotable_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
94         tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
95             Some(stab) => {
96                 if cfg!(debug_assertions) && stab.promotable {
97                     let sig = tcx.fn_sig(def_id);
98                     assert_eq!(
99                         sig.unsafety(),
100                         hir::Unsafety::Normal,
101                         "don't mark const unsafe fns as promotable",
102                         // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
103                     );
104                 }
105                 stab.promotable
106             },
107             None => false,
108         }
109     }
110
111     *providers = Providers {
112         is_const_fn_raw,
113         is_promotable_const_fn,
114         ..*providers
115     };
116 }