]> git.lizzy.rs Git - rust.git/blob - src/librustc/ty/constness.rs
Auto merge of #67524 - LukasKalbertodt:improve-into-iter-lint, r=matthewjasper
[rust.git] / src / librustc / ty / constness.rs
1 use crate::hir;
2 use crate::hir::def_id::DefId;
3 use crate::hir::map::blocks::FnLikeNode;
4 use crate::ty::query::Providers;
5 use crate::ty::TyCtxt;
6 use rustc_target::spec::abi::Abi;
7 use syntax::attr;
8 use syntax_pos::symbol::Symbol;
9
10 impl<'tcx> TyCtxt<'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)
15             && match self.is_unstable_const_fn(def_id) {
16                 Some(feature_name) => {
17                     // has a `rustc_const_unstable` attribute, check whether the user enabled the
18                     // corresponding feature gate.
19                     self.features()
20                         .declared_lib_features
21                         .iter()
22                         .any(|&(sym, _)| sym == feature_name)
23                 }
24                 // functions without const stability are either stable user written
25                 // const fn or the user is using feature gates and we thus don't
26                 // care what they do
27                 None => true,
28             }
29     }
30
31     /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
32     pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
33         if self.is_const_fn_raw(def_id) {
34             let const_stab = self.lookup_const_stability(def_id)?;
35             if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None }
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         // Bail out if the signature doesn't contain `const`
44         if !self.is_const_fn_raw(def_id) {
45             return false;
46         }
47
48         if self.features().staged_api {
49             // In order for a libstd function to be considered min_const_fn
50             // it needs to be stable and have no `rustc_const_unstable` attribute.
51             match self.lookup_const_stability(def_id) {
52                 // `rustc_const_unstable` functions don't need to conform.
53                 Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
54                 None => {
55                     if let Some(stab) = self.lookup_stability(def_id) {
56                         if stab.level.is_stable() {
57                             self.sess.span_err(
58                                 self.def_span(def_id),
59                                 "stable const functions must have either `rustc_const_stable` or \
60                             `rustc_const_unstable` attribute",
61                             );
62                             // While we errored above, because we don't know if we need to conform, we
63                             // err on the "safe" side and require min_const_fn.
64                             true
65                         } else {
66                             // Unstable functions need not conform to min_const_fn.
67                             false
68                         }
69                     } else {
70                         // Internal functions are forced to conform to min_const_fn.
71                         // Annotate the internal function with a const stability attribute if
72                         // you need to use unstable features.
73                         // Note: this is an arbitrary choice that does not affect stability or const
74                         // safety or anything, it just changes whether we need to annotate some
75                         // internal functions with `rustc_const_stable` or with `rustc_const_unstable`
76                         true
77                     }
78                 }
79                 // Everything else needs to conform, because it would be callable from
80                 // other `min_const_fn` functions.
81                 _ => true,
82             }
83         } else {
84             // users enabling the `const_fn` feature gate can do what they want
85             !self.features().const_fn
86         }
87     }
88 }
89
90 pub fn provide(providers: &mut Providers<'_>) {
91     /// Const evaluability whitelist is here to check evaluability at the
92     /// top level beforehand.
93     fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
94         match tcx.fn_sig(def_id).abi() {
95             Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
96                 Some(tcx.lookup_const_stability(def_id).is_some())
97             }
98             _ => None,
99         }
100     }
101
102     /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
103     /// said intrinsic is on the whitelist for being const callable.
104     fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
105         let hir_id = tcx
106             .hir()
107             .as_local_hir_id(def_id)
108             .expect("Non-local call to local provider is_const_fn");
109
110         let node = tcx.hir().get(hir_id);
111
112         if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
113             whitelisted
114         } else if let Some(fn_like) = FnLikeNode::from_node(node) {
115             fn_like.constness() == hir::Constness::Const
116         } else if let hir::Node::Ctor(_) = node {
117             true
118         } else {
119             false
120         }
121     }
122
123     fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
124         tcx.is_const_fn(def_id)
125             && match tcx.lookup_const_stability(def_id) {
126                 Some(stab) => {
127                     if cfg!(debug_assertions) && stab.promotable {
128                         let sig = tcx.fn_sig(def_id);
129                         assert_eq!(
130                             sig.unsafety(),
131                             hir::Unsafety::Normal,
132                             "don't mark const unsafe fns as promotable",
133                             // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
134                         );
135                     }
136                     stab.promotable
137                 }
138                 None => false,
139             }
140     }
141
142     fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
143         tcx.is_const_fn(def_id)
144             && tcx
145                 .lookup_const_stability(def_id)
146                 .map(|stab| stab.allow_const_fn_ptr)
147                 .unwrap_or(false)
148     }
149
150     *providers = Providers {
151         is_const_fn_raw,
152         is_promotable_const_fn,
153         const_fn_is_allowed_fn_ptr,
154         ..*providers
155     };
156 }