]> git.lizzy.rs Git - rust.git/blob - src/librustc/ty/constness.rs
268015a56240e52290ecd360be42220da92a5108
[rust.git] / src / librustc / ty / constness.rs
1 use crate::ty::query::Providers;
2 use crate::hir::def_id::DefId;
3 use crate::hir;
4 use crate::ty::TyCtxt;
5 use syntax_pos::symbol::{sym, Symbol};
6 use rustc_target::spec::abi::Abi;
7 use crate::hir::map::blocks::FnLikeNode;
8 use syntax::attr;
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) && match self.is_unstable_const_fn(def_id) {
15             Some(feature_name) => {
16                 // has a `rustc_const_unstable` attribute, check whether the user enabled the
17                 // corresponding feature gate.
18                 self.features()
19                     .declared_lib_features
20                     .iter()
21                     .any(|&(sym, _)| sym == feature_name)
22             },
23             // functions without const stability are either stable user written
24             // const fn or the user is using feature gates and we thus don't
25             // care what they do
26             None => true,
27         }
28     }
29
30     /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
31     pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
32         if self.is_const_fn_raw(def_id) {
33             self.lookup_stability(def_id)?.const_stability
34         } else {
35             None
36         }
37     }
38
39     /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
40     /// for being called from stable `const fn`s (`min_const_fn`).
41     ///
42     /// Adding more intrinsics requires sign-off from @rust-lang/lang.
43     ///
44     /// This list differs from the list in `is_const_intrinsic` in the sense that any item on this
45     /// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from
46     /// stable, it must be callable at all.
47     fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool {
48         match self.item_name(def_id) {
49             | sym::size_of
50             | sym::min_align_of
51             | sym::needs_drop
52             // Arithmetic:
53             | sym::add_with_overflow // ~> .overflowing_add
54             | sym::sub_with_overflow // ~> .overflowing_sub
55             | sym::mul_with_overflow // ~> .overflowing_mul
56             | sym::wrapping_add // ~> .wrapping_add
57             | sym::wrapping_sub // ~> .wrapping_sub
58             | sym::wrapping_mul // ~> .wrapping_mul
59             | sym::saturating_add // ~> .saturating_add
60             | sym::saturating_sub // ~> .saturating_sub
61             | sym::unchecked_shl // ~> .wrapping_shl
62             | sym::unchecked_shr // ~> .wrapping_shr
63             | sym::rotate_left // ~> .rotate_left
64             | sym::rotate_right // ~> .rotate_right
65             | sym::ctpop // ~> .count_ones
66             | sym::ctlz // ~> .leading_zeros
67             | sym::cttz // ~> .trailing_zeros
68             | sym::bswap // ~> .swap_bytes
69             | sym::bitreverse // ~> .reverse_bits
70             => true,
71             _ => false,
72         }
73     }
74
75     /// Returns `true` if this function must conform to `min_const_fn`
76     pub fn is_min_const_fn(self, def_id: DefId) -> bool {
77         // Bail out if the signature doesn't contain `const`
78         if !self.is_const_fn_raw(def_id) {
79             return false;
80         }
81         if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
82             return self.is_intrinsic_min_const_fn(def_id);
83         }
84
85         if self.features().staged_api {
86             // in order for a libstd function to be considered min_const_fn
87             // it needs to be stable and have no `rustc_const_unstable` attribute
88             match self.lookup_stability(def_id) {
89                 // stable functions with unstable const fn aren't `min_const_fn`
90                 Some(&attr::Stability { const_stability: Some(_), .. }) => false,
91                 // unstable functions don't need to conform
92                 Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
93                 // everything else needs to conform, because it would be callable from
94                 // other `min_const_fn` functions
95                 _ => true,
96             }
97         } else {
98             // users enabling the `const_fn` feature gate can do what they want
99             !self.features().const_fn
100         }
101     }
102 }
103
104
105 pub fn provide(providers: &mut Providers<'_>) {
106     /// Const evaluability whitelist is here to check evaluability at the
107     /// top level beforehand.
108     fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
109         match tcx.fn_sig(def_id).abi() {
110             Abi::RustIntrinsic |
111             Abi::PlatformIntrinsic => {
112                 // FIXME: deduplicate these two lists as much as possible
113                 match tcx.item_name(def_id) {
114                     // Keep this list in the same order as the match patterns in
115                     // `librustc_mir/interpret/intrinsics.rs`
116
117                     // This whitelist is a list of intrinsics that have a miri-engine implementation
118                     // and can thus be called when enabling enough feature gates. The similar
119                     // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing
120                     // the intrinsics to be called by stable const fns.
121                     | sym::caller_location
122
123                     | sym::min_align_of
124                     | sym::pref_align_of
125                     | sym::needs_drop
126                     | sym::size_of
127                     | sym::type_id
128                     | sym::type_name
129
130                     | sym::ctpop
131                     | sym::cttz
132                     | sym::cttz_nonzero
133                     | sym::ctlz
134                     | sym::ctlz_nonzero
135                     | sym::bswap
136                     | sym::bitreverse
137
138                     | sym::wrapping_add
139                     | sym::wrapping_sub
140                     | sym::wrapping_mul
141                     | sym::add_with_overflow
142                     | sym::sub_with_overflow
143                     | sym::mul_with_overflow
144
145                     | sym::saturating_add
146                     | sym::saturating_sub
147
148                     | sym::unchecked_shl
149                     | sym::unchecked_shr
150
151                     | sym::rotate_left
152                     | sym::rotate_right
153
154                     | sym::ptr_offset_from
155
156                     | sym::transmute
157
158                     | sym::simd_insert
159
160                     | sym::simd_extract
161
162                     => Some(true),
163
164                     _ => Some(false)
165                 }
166             }
167             _ => None
168         }
169     }
170
171     /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
172     /// said intrinsic is on the whitelist for being const callable.
173     fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
174         let hir_id = tcx.hir().as_local_hir_id(def_id)
175                               .expect("Non-local call to local provider is_const_fn");
176
177         let node = tcx.hir().get(hir_id);
178
179         if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
180             whitelisted
181         } else if let Some(fn_like) = FnLikeNode::from_node(node) {
182             fn_like.constness() == hir::Constness::Const
183         } else if let hir::Node::Ctor(_) = node {
184             true
185         } else {
186             false
187         }
188     }
189
190     fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
191         tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
192             Some(stab) => {
193                 if cfg!(debug_assertions) && stab.promotable {
194                     let sig = tcx.fn_sig(def_id);
195                     assert_eq!(
196                         sig.unsafety(),
197                         hir::Unsafety::Normal,
198                         "don't mark const unsafe fns as promotable",
199                         // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
200                     );
201                 }
202                 stab.promotable
203             },
204             None => false,
205         }
206     }
207
208     fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
209         tcx.is_const_fn(def_id) &&
210             tcx.lookup_stability(def_id)
211                 .map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
212     }
213
214     *providers = Providers {
215         is_const_fn_raw,
216         is_promotable_const_fn,
217         const_fn_is_allowed_fn_ptr,
218         ..*providers
219     };
220 }