@@ -2,7 +2,8 @@ use crate::ty::query::Providers;
2
2
use crate :: hir:: def_id:: DefId ;
3
3
use crate :: hir;
4
4
use crate :: ty:: TyCtxt ;
5
- use syntax_pos:: symbol:: Symbol ;
5
+ use syntax_pos:: symbol:: { sym, Symbol } ;
6
+ use rustc_target:: spec:: abi:: Abi ;
6
7
use crate :: hir:: map:: blocks:: FnLikeNode ;
7
8
use syntax:: attr;
8
9
@@ -35,12 +36,51 @@ impl<'tcx> TyCtxt<'tcx> {
35
36
}
36
37
}
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
+
38
75
/// Returns `true` if this function must conform to `min_const_fn`
39
76
pub fn is_min_const_fn ( self , def_id : DefId ) -> bool {
40
77
// Bail out if the signature doesn't contain `const`
41
78
if !self . is_const_fn_raw ( def_id) {
42
79
return false ;
43
80
}
81
+ if let Abi :: RustIntrinsic = self . fn_sig ( def_id) . abi ( ) {
82
+ return self . is_intrinsic_min_const_fn ( def_id) ;
83
+ }
44
84
45
85
if self . features ( ) . staged_api {
46
86
// in order for a libstd function to be considered min_const_fn
@@ -63,13 +103,82 @@ impl<'tcx> TyCtxt<'tcx> {
63
103
64
104
65
105
pub fn provide ( providers : & mut Providers < ' _ > ) {
66
- /// only checks whether the function has a `const` modifier
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.
67
173
fn is_const_fn_raw ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> bool {
68
174
let hir_id = tcx. hir ( ) . as_local_hir_id ( def_id)
69
175
. expect ( "Non-local call to local provider is_const_fn" ) ;
70
176
71
177
let node = tcx. hir ( ) . get ( hir_id) ;
72
- if let Some ( fn_like) = FnLikeNode :: from_node ( node) {
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) {
73
182
fn_like. constness ( ) == hir:: Constness :: Const
74
183
} else if let hir:: Node :: Ctor ( _) = node {
75
184
true
0 commit comments