]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc/ty/constness.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc / ty / constness.rs
index 676916f530a4d9cbe0ae7c450cd442059ed569ea..897a3678c40046ee7700e682caa1d9d08890e5d5 100644 (file)
@@ -2,7 +2,8 @@ use crate::ty::query::Providers;
 use crate::hir::def_id::DefId;
 use crate::hir;
 use crate::ty::TyCtxt;
-use syntax_pos::symbol::Symbol;
+use syntax_pos::symbol::{sym, Symbol};
+use rustc_target::spec::abi::Abi;
 use crate::hir::map::blocks::FnLikeNode;
 use syntax::attr;
 
@@ -29,29 +30,94 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
     pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
         if self.is_const_fn_raw(def_id) {
-            self.lookup_stability(def_id)?.const_stability
+            let const_stab = self.lookup_const_stability(def_id)?;
+            if const_stab.level.is_unstable() {
+                Some(const_stab.feature)
+            } else {
+                None
+            }
         } else {
             None
         }
     }
 
+    /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
+    /// for being called from stable `const fn`s (`min_const_fn`).
+    ///
+    /// Adding more intrinsics requires sign-off from @rust-lang/lang.
+    ///
+    /// This list differs from the list in `is_const_intrinsic` in the sense that any item on this
+    /// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from
+    /// stable, it must be callable at all.
+    fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool {
+        match self.item_name(def_id) {
+            | sym::size_of
+            | sym::min_align_of
+            | sym::needs_drop
+            // Arithmetic:
+            | sym::add_with_overflow // ~> .overflowing_add
+            | sym::sub_with_overflow // ~> .overflowing_sub
+            | sym::mul_with_overflow // ~> .overflowing_mul
+            | sym::wrapping_add // ~> .wrapping_add
+            | sym::wrapping_sub // ~> .wrapping_sub
+            | sym::wrapping_mul // ~> .wrapping_mul
+            | sym::saturating_add // ~> .saturating_add
+            | sym::saturating_sub // ~> .saturating_sub
+            | sym::unchecked_shl // ~> .wrapping_shl
+            | sym::unchecked_shr // ~> .wrapping_shr
+            | sym::rotate_left // ~> .rotate_left
+            | sym::rotate_right // ~> .rotate_right
+            | sym::ctpop // ~> .count_ones
+            | sym::ctlz // ~> .leading_zeros
+            | sym::cttz // ~> .trailing_zeros
+            | sym::bswap // ~> .swap_bytes
+            | sym::bitreverse // ~> .reverse_bits
+            => true,
+            _ => false,
+        }
+    }
+
     /// Returns `true` if this function must conform to `min_const_fn`
     pub fn is_min_const_fn(self, def_id: DefId) -> bool {
         // Bail out if the signature doesn't contain `const`
         if !self.is_const_fn_raw(def_id) {
             return false;
         }
+        if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
+            return self.is_intrinsic_min_const_fn(def_id);
+        }
 
         if self.features().staged_api {
-            // in order for a libstd function to be considered min_const_fn
-            // it needs to be stable and have no `rustc_const_unstable` attribute
-            match self.lookup_stability(def_id) {
-                // stable functions with unstable const fn aren't `min_const_fn`
-                Some(&attr::Stability { const_stability: Some(_), .. }) => false,
-                // unstable functions don't need to conform
-                Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
-                // everything else needs to conform, because it would be callable from
-                // other `min_const_fn` functions
+            // In order for a libstd function to be considered min_const_fn
+            // it needs to be stable and have no `rustc_const_unstable` attribute.
+            match self.lookup_const_stability(def_id) {
+                // `rustc_const_unstable` functions don't need to conform.
+                Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
+                None => if let Some(stab) = self.lookup_stability(def_id) {
+                    if stab.level.is_stable() {
+                        self.sess.span_err(
+                            self.def_span(def_id),
+                            "stable const functions must have either `rustc_const_stable` or \
+                            `rustc_const_unstable` attribute",
+                        );
+                        // While we errored above, because we don't know if we need to conform, we
+                        // err on the "safe" side and require min_const_fn.
+                        true
+                    } else {
+                        // Unstable functions need not conform to min_const_fn.
+                        false
+                    }
+                } else {
+                    // Internal functions are forced to conform to min_const_fn.
+                    // Annotate the internal function with a const stability attribute if
+                    // you need to use unstable features.
+                    // Note: this is an arbitrary choice that does not affect stability or const
+                    // safety or anything, it just changes whether we need to annotate some
+                    // internal functions with `rustc_const_stable` or with `rustc_const_unstable`
+                    true
+                },
+                // Everything else needs to conform, because it would be callable from
+                // other `min_const_fn` functions.
                 _ => true,
             }
         } else {
@@ -63,13 +129,82 @@ impl<'tcx> TyCtxt<'tcx> {
 
 
 pub fn provide(providers: &mut Providers<'_>) {
-    /// only checks whether the function has a `const` modifier
+    /// Const evaluability whitelist is here to check evaluability at the
+    /// top level beforehand.
+    fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
+        match tcx.fn_sig(def_id).abi() {
+            Abi::RustIntrinsic |
+            Abi::PlatformIntrinsic => {
+                // FIXME: deduplicate these two lists as much as possible
+                match tcx.item_name(def_id) {
+                    // Keep this list in the same order as the match patterns in
+                    // `librustc_mir/interpret/intrinsics.rs`
+
+                    // This whitelist is a list of intrinsics that have a miri-engine implementation
+                    // and can thus be called when enabling enough feature gates. The similar
+                    // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing
+                    // the intrinsics to be called by stable const fns.
+                    | sym::caller_location
+
+                    | sym::min_align_of
+                    | sym::pref_align_of
+                    | sym::needs_drop
+                    | sym::size_of
+                    | sym::type_id
+                    | sym::type_name
+
+                    | sym::ctpop
+                    | sym::cttz
+                    | sym::cttz_nonzero
+                    | sym::ctlz
+                    | sym::ctlz_nonzero
+                    | sym::bswap
+                    | sym::bitreverse
+
+                    | sym::wrapping_add
+                    | sym::wrapping_sub
+                    | sym::wrapping_mul
+                    | sym::add_with_overflow
+                    | sym::sub_with_overflow
+                    | sym::mul_with_overflow
+
+                    | sym::saturating_add
+                    | sym::saturating_sub
+
+                    | sym::unchecked_shl
+                    | sym::unchecked_shr
+
+                    | sym::rotate_left
+                    | sym::rotate_right
+
+                    | sym::ptr_offset_from
+
+                    | sym::transmute
+
+                    | sym::simd_insert
+
+                    | sym::simd_extract
+
+                    => Some(true),
+
+                    _ => Some(false)
+                }
+            }
+            _ => None
+        }
+    }
+
+    /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
+    /// said intrinsic is on the whitelist for being const callable.
     fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
         let hir_id = tcx.hir().as_local_hir_id(def_id)
                               .expect("Non-local call to local provider is_const_fn");
 
         let node = tcx.hir().get(hir_id);
-        if let Some(fn_like) = FnLikeNode::from_node(node) {
+
+        if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
+            whitelisted
+        } else if let Some(fn_like) = FnLikeNode::from_node(node) {
             fn_like.constness() == hir::Constness::Const
         } else if let hir::Node::Ctor(_) = node {
             true
@@ -79,7 +214,7 @@ pub fn provide(providers: &mut Providers<'_>) {
     }
 
     fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
-        tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
+        tcx.is_const_fn(def_id) && match tcx.lookup_const_stability(def_id) {
             Some(stab) => {
                 if cfg!(debug_assertions) && stab.promotable {
                     let sig = tcx.fn_sig(def_id);
@@ -98,7 +233,7 @@ pub fn provide(providers: &mut Providers<'_>) {
 
     fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
         tcx.is_const_fn(def_id) &&
-            tcx.lookup_stability(def_id)
+            tcx.lookup_const_stability(def_id)
                 .map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
     }