]> git.lizzy.rs Git - rust.git/blob - src/librustc/ty/constness.rs
Rollup merge of #67160 - matthewjasper:gat-generics, r=nikomatsakis
[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             let const_stab = self.lookup_const_stability(def_id)?;
34             if const_stab.level.is_unstable() {
35                 Some(const_stab.feature)
36             } else {
37                 None
38             }
39         } else {
40             None
41         }
42     }
43
44     /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
45     /// for being called from stable `const fn`s (`min_const_fn`).
46     ///
47     /// Adding more intrinsics requires sign-off from @rust-lang/lang.
48     ///
49     /// This list differs from the list in `is_const_intrinsic` in the sense that any item on this
50     /// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from
51     /// stable, it must be callable at all.
52     fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool {
53         match self.item_name(def_id) {
54             | sym::size_of
55             | sym::min_align_of
56             | sym::needs_drop
57             // Arithmetic:
58             | sym::add_with_overflow // ~> .overflowing_add
59             | sym::sub_with_overflow // ~> .overflowing_sub
60             | sym::mul_with_overflow // ~> .overflowing_mul
61             | sym::wrapping_add // ~> .wrapping_add
62             | sym::wrapping_sub // ~> .wrapping_sub
63             | sym::wrapping_mul // ~> .wrapping_mul
64             | sym::saturating_add // ~> .saturating_add
65             | sym::saturating_sub // ~> .saturating_sub
66             | sym::unchecked_shl // ~> .wrapping_shl
67             | sym::unchecked_shr // ~> .wrapping_shr
68             | sym::rotate_left // ~> .rotate_left
69             | sym::rotate_right // ~> .rotate_right
70             | sym::ctpop // ~> .count_ones
71             | sym::ctlz // ~> .leading_zeros
72             | sym::cttz // ~> .trailing_zeros
73             | sym::bswap // ~> .swap_bytes
74             | sym::bitreverse // ~> .reverse_bits
75             => true,
76             _ => false,
77         }
78     }
79
80     /// Returns `true` if this function must conform to `min_const_fn`
81     pub fn is_min_const_fn(self, def_id: DefId) -> bool {
82         // Bail out if the signature doesn't contain `const`
83         if !self.is_const_fn_raw(def_id) {
84             return false;
85         }
86         if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
87             return self.is_intrinsic_min_const_fn(def_id);
88         }
89
90         if self.features().staged_api {
91             // In order for a libstd function to be considered min_const_fn
92             // it needs to be stable and have no `rustc_const_unstable` attribute.
93             match self.lookup_const_stability(def_id) {
94                 // `rustc_const_unstable` functions don't need to conform.
95                 Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
96                 None => if let Some(stab) = self.lookup_stability(def_id) {
97                     if stab.level.is_stable() {
98                         self.sess.span_err(
99                             self.def_span(def_id),
100                             "stable const functions must have either `rustc_const_stable` or \
101                             `rustc_const_unstable` attribute",
102                         );
103                         // While we errored above, because we don't know if we need to conform, we
104                         // err on the "safe" side and require min_const_fn.
105                         true
106                     } else {
107                         // Unstable functions need not conform to min_const_fn.
108                         false
109                     }
110                 } else {
111                     // Internal functions are forced to conform to min_const_fn.
112                     // Annotate the internal function with a const stability attribute if
113                     // you need to use unstable features.
114                     // Note: this is an arbitrary choice that does not affect stability or const
115                     // safety or anything, it just changes whether we need to annotate some
116                     // internal functions with `rustc_const_stable` or with `rustc_const_unstable`
117                     true
118                 },
119                 // Everything else needs to conform, because it would be callable from
120                 // other `min_const_fn` functions.
121                 _ => true,
122             }
123         } else {
124             // users enabling the `const_fn` feature gate can do what they want
125             !self.features().const_fn
126         }
127     }
128 }
129
130
131 pub fn provide(providers: &mut Providers<'_>) {
132     /// Const evaluability whitelist is here to check evaluability at the
133     /// top level beforehand.
134     fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
135         match tcx.fn_sig(def_id).abi() {
136             Abi::RustIntrinsic |
137             Abi::PlatformIntrinsic => {
138                 // FIXME: deduplicate these two lists as much as possible
139                 match tcx.item_name(def_id) {
140                     // Keep this list in the same order as the match patterns in
141                     // `librustc_mir/interpret/intrinsics.rs`
142
143                     // This whitelist is a list of intrinsics that have a miri-engine implementation
144                     // and can thus be called when enabling enough feature gates. The similar
145                     // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing
146                     // the intrinsics to be called by stable const fns.
147                     | sym::caller_location
148
149                     | sym::min_align_of
150                     | sym::pref_align_of
151                     | sym::needs_drop
152                     | sym::size_of
153                     | sym::type_id
154                     | sym::type_name
155
156                     | sym::ctpop
157                     | sym::cttz
158                     | sym::cttz_nonzero
159                     | sym::ctlz
160                     | sym::ctlz_nonzero
161                     | sym::bswap
162                     | sym::bitreverse
163
164                     | sym::wrapping_add
165                     | sym::wrapping_sub
166                     | sym::wrapping_mul
167                     | sym::add_with_overflow
168                     | sym::sub_with_overflow
169                     | sym::mul_with_overflow
170
171                     | sym::saturating_add
172                     | sym::saturating_sub
173
174                     | sym::unchecked_shl
175                     | sym::unchecked_shr
176
177                     | sym::rotate_left
178                     | sym::rotate_right
179
180                     | sym::ptr_offset_from
181
182                     | sym::transmute
183
184                     | sym::simd_insert
185
186                     | sym::simd_extract
187
188                     => Some(true),
189
190                     _ => Some(false)
191                 }
192             }
193             _ => None
194         }
195     }
196
197     /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
198     /// said intrinsic is on the whitelist for being const callable.
199     fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
200         let hir_id = tcx.hir().as_local_hir_id(def_id)
201                               .expect("Non-local call to local provider is_const_fn");
202
203         let node = tcx.hir().get(hir_id);
204
205         if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
206             whitelisted
207         } else if let Some(fn_like) = FnLikeNode::from_node(node) {
208             fn_like.constness() == hir::Constness::Const
209         } else if let hir::Node::Ctor(_) = node {
210             true
211         } else {
212             false
213         }
214     }
215
216     fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
217         tcx.is_const_fn(def_id) && match tcx.lookup_const_stability(def_id) {
218             Some(stab) => {
219                 if cfg!(debug_assertions) && stab.promotable {
220                     let sig = tcx.fn_sig(def_id);
221                     assert_eq!(
222                         sig.unsafety(),
223                         hir::Unsafety::Normal,
224                         "don't mark const unsafe fns as promotable",
225                         // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
226                     );
227                 }
228                 stab.promotable
229             },
230             None => false,
231         }
232     }
233
234     fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
235         tcx.is_const_fn(def_id) &&
236             tcx.lookup_const_stability(def_id)
237                 .map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
238     }
239
240     *providers = Providers {
241         is_const_fn_raw,
242         is_promotable_const_fn,
243         const_fn_is_allowed_fn_ptr,
244         ..*providers
245     };
246 }