@@ -2,7 +2,8 @@ use crate::ty::query::Providers;
22use crate :: hir:: def_id:: DefId ;
33use crate :: hir;
44use crate :: ty:: TyCtxt ;
5- use syntax_pos:: symbol:: Symbol ;
5+ use syntax_pos:: symbol:: { sym, Symbol } ;
6+ use rustc_target:: spec:: abi:: Abi ;
67use crate :: hir:: map:: blocks:: FnLikeNode ;
78use syntax:: attr;
89
@@ -35,12 +36,51 @@ impl<'tcx> TyCtxt<'tcx> {
3536 }
3637 }
3738
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+
3875 /// Returns `true` if this function must conform to `min_const_fn`
3976 pub fn is_min_const_fn ( self , def_id : DefId ) -> bool {
4077 // Bail out if the signature doesn't contain `const`
4178 if !self . is_const_fn_raw ( def_id) {
4279 return false ;
4380 }
81+ if let Abi :: RustIntrinsic = self . fn_sig ( def_id) . abi ( ) {
82+ return self . is_intrinsic_min_const_fn ( def_id) ;
83+ }
4484
4585 if self . features ( ) . staged_api {
4686 // in order for a libstd function to be considered min_const_fn
@@ -63,13 +103,82 @@ impl<'tcx> TyCtxt<'tcx> {
63103
64104
65105pub 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.
67173 fn is_const_fn_raw ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> bool {
68174 let hir_id = tcx. hir ( ) . as_local_hir_id ( def_id)
69175 . expect ( "Non-local call to local provider is_const_fn" ) ;
70176
71177 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) {
73182 fn_like. constness ( ) == hir:: Constness :: Const
74183 } else if let hir:: Node :: Ctor ( _) = node {
75184 true
0 commit comments