Skip to content

Commit

Permalink
Only promote calls to #[rustc_promotable] const fns
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Oct 3, 2018
1 parent 4cf1176 commit 9e46c0b
Show file tree
Hide file tree
Showing 42 changed files with 525 additions and 242 deletions.
2 changes: 2 additions & 0 deletions src/libcore/mem.rs
Expand Up @@ -285,6 +285,7 @@ pub fn forget<T>(t: T) {
/// [alignment]: ./fn.align_of.html
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn size_of<T>() -> usize {
intrinsics::size_of::<T>()
}
Expand Down Expand Up @@ -376,6 +377,7 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn align_of<T>() -> usize {
intrinsics::min_align_of::<T>()
}
Expand Down
2 changes: 2 additions & 0 deletions src/libcore/num/mod.rs
Expand Up @@ -216,6 +216,7 @@ $EndFeature, "
```"),
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn min_value() -> Self {
!0 ^ ((!0 as $UnsignedT) >> 1) as Self
}
Expand All @@ -234,6 +235,7 @@ $EndFeature, "
```"),
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn max_value() -> Self {
!Self::min_value()
}
Expand Down
1 change: 1 addition & 0 deletions src/libcore/ops/range.rs
Expand Up @@ -391,6 +391,7 @@ impl<Idx> RangeInclusive<Idx> {
/// ```
#[stable(feature = "inclusive_range_methods", since = "1.27.0")]
#[inline]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn new(start: Idx, end: Idx) -> Self {
Self { start, end, is_empty: None }
}
Expand Down
2 changes: 2 additions & 0 deletions src/libcore/ptr.rs
Expand Up @@ -209,6 +209,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn null<T>() -> *const T { 0 as *const T }

/// Creates a null mutable raw pointer.
Expand All @@ -223,6 +224,7 @@ pub const fn null<T>() -> *const T { 0 as *const T }
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn null_mut<T>() -> *mut T { 0 as *mut T }

/// Swaps the values at two mutable locations of the same type, without
Expand Down
4 changes: 4 additions & 0 deletions src/libcore/time.rs
Expand Up @@ -109,6 +109,7 @@ impl Duration {
/// ```
#[stable(feature = "duration", since = "1.3.0")]
#[inline]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn from_secs(secs: u64) -> Duration {
Duration { secs, nanos: 0 }
}
Expand All @@ -127,6 +128,7 @@ impl Duration {
/// ```
#[stable(feature = "duration", since = "1.3.0")]
#[inline]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn from_millis(millis: u64) -> Duration {
Duration {
secs: millis / MILLIS_PER_SEC,
Expand All @@ -148,6 +150,7 @@ impl Duration {
/// ```
#[stable(feature = "duration_from_micros", since = "1.27.0")]
#[inline]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn from_micros(micros: u64) -> Duration {
Duration {
secs: micros / MICROS_PER_SEC,
Expand All @@ -169,6 +172,7 @@ impl Duration {
/// ```
#[stable(feature = "duration_extras", since = "1.27.0")]
#[inline]
#[cfg_attr(not(stage0), rustc_promotable)]
pub const fn from_nanos(nanos: u64) -> Duration {
Duration {
secs: nanos / (NANOS_PER_SEC as u64),
Expand Down
1 change: 1 addition & 0 deletions src/librustc/dep_graph/dep_node.rs
Expand Up @@ -515,6 +515,7 @@ define_dep_nodes!( <'tcx>
[] ItemVarianceConstraints(DefId),
[] ItemVariances(DefId),
[] IsConstFn(DefId),
[] IsPromotableConstFn(DefId),
[] IsForeignItem(DefId),
[] TypeParamPredicates { item_id: DefId, param_id: DefId },
[] SizedConstraint(DefId),
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ich/impls_syntax.rs
Expand Up @@ -130,6 +130,7 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability {
level,
feature,
rustc_depr,
promotable,
const_stability
});

Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/stability.rs
Expand Up @@ -441,6 +441,7 @@ impl<'a, 'tcx> Index<'tcx> {
feature: Symbol::intern("rustc_private"),
rustc_depr: None,
const_stability: None,
promotable: false,
});
annotator.parent_stab = Some(stability);
}
Expand Down
111 changes: 111 additions & 0 deletions src/librustc/ty/constness.rs
@@ -0,0 +1,111 @@
use ty::query::Providers;
use hir::def_id::DefId;
use hir;
use ty::TyCtxt;
use syntax_pos::symbol::Symbol;
use hir::map::blocks::FnLikeNode;
use syntax::attr;
use rustc_target::spec::abi;

impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
/// Whether the `def_id` counts as const fn in your current crate, considering all active
/// feature gates
pub fn is_const_fn(self, def_id: DefId) -> bool {
self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
Some(stab) => match stab.const_stability {
// has a `rustc_const_unstable` attribute, check whether the user enabled the
// corresponding feature gate
Some(feature_name) => self.features()
.declared_lib_features
.iter()
.any(|&(sym, _)| sym == feature_name),
// the function has no stability attribute, it is stable as const fn or the user
// nees to use feature gates to use the function at all
None => true,
},
// functions without stability are either stable user written const fn or the user is
// using feature gates and we thus don't care what they do
None => true,
}
}

/// 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
} else {
None
}
}

/// Returns true if this function must conform to `min_const_fn`
pub fn is_min_const_fn(self, def_id: DefId) -> bool {
if self.features().staged_api {
// some intrinsics are waved through if called inside the
// standard library. Users never need to call them directly
if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
assert!(!self.is_const_fn(def_id));
match &self.item_name(def_id).as_str()[..] {
| "size_of"
| "min_align_of"
| "needs_drop"
=> return true,
_ => {},
}
}
// 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
_ => true,
}
} else {
// users enabling the `const_fn` can do what they want
!self.sess.features_untracked().const_fn
}
}
}


pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
/// only checks whether the function has a `const` modifier
fn is_const_fn_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
let node_id = tcx.hir.as_local_node_id(def_id)
.expect("Non-local call to local provider is_const_fn");

if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
fn_like.constness() == hir::Constness::Const
} else {
false
}
}

fn is_promotable_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
Some(stab) => {
if cfg!(debug_assertions) && stab.promotable {
let sig = tcx.fn_sig(def_id);
assert_eq!(
sig.unsafety(),
hir::Unsafety::Normal,
"don't mark const unsafe fns as promotable",
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
);
}
stab.promotable
},
None => false,
}
}

*providers = Providers {
is_const_fn_raw,
is_promotable_const_fn,
..*providers
};
}
32 changes: 0 additions & 32 deletions src/librustc/ty/context.rs
Expand Up @@ -1134,38 +1134,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
local as usize == global as usize
}

/// Returns true if this function must conform to `min_const_fn`
pub fn is_min_const_fn(self, def_id: DefId) -> bool {
if self.features().staged_api {
// some intrinsics are waved through if called inside the
// standard library. Users never need to call them directly
if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
assert!(!self.is_const_fn(def_id));
match &self.item_name(def_id).as_str()[..] {
| "size_of"
| "min_align_of"
| "needs_drop"
=> return true,
_ => {},
}
}
// 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
_ => true,
}
} else {
// users enabling the `const_fn` can do what they want
!self.sess.features_untracked().const_fn
}
}

/// Create a type context and call the closure with a `TyCtxt` reference
/// to the context. The closure enforces that the type context and any interned
/// value (types, substs, etc.) can only be used while `ty::tls` has a valid
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/ty/mod.rs
Expand Up @@ -94,6 +94,7 @@ pub mod binding;
pub mod cast;
#[macro_use]
pub mod codec;
mod constness;
pub mod error;
mod erase_regions;
pub mod fast_reject;
Expand Down Expand Up @@ -3056,6 +3057,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
erase_regions::provide(providers);
layout::provide(providers);
util::provide(providers);
constness::provide(providers);
*providers = ty::query::Providers {
associated_item,
associated_item_def_ids,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/query/config.rs
Expand Up @@ -437,7 +437,7 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_object_safe<'tcx> {
}
}

impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn<'tcx> {
impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn_raw<'tcx> {
fn describe(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> String {
format!("checking if item is const fn: `{}`", tcx.item_path_str(def_id))
}
Expand Down
19 changes: 17 additions & 2 deletions src/librustc/ty/query/mod.rs
Expand Up @@ -160,8 +160,23 @@ define_queries! { <'tcx>
DefId
) -> Result<DtorckConstraint<'tcx>, NoSolution>,

/// True if this is a const fn
[] fn is_const_fn: IsConstFn(DefId) -> bool,
/// True if this is a const fn, use the `is_const_fn` to know whether your crate actually
/// sees it as const fn (e.g. the const-fn-ness might be unstable and you might not have
/// the feature gate active)
///
/// DO NOT CALL MANUALLY, it is only meant to cache the base data for the `is_const_fn`
/// function
[] fn is_const_fn_raw: IsConstFn(DefId) -> bool,


/// Returns true if calls to the function may be promoted
///
/// This is either because the function is e.g. a tuple-struct or tuple-variant constructor,
/// or because it has the `#[rustc_promotable]` attribute. The attribute should be removed
/// in the future in favour of some form of check which figures out whether the function
/// does not inspect the bits of any of its arguments (so is essentially just a constructor
/// function)
[] fn is_promotable_const_fn: IsPromotableConstFn(DefId) -> bool,

/// True if this is a foreign item (i.e., linked via `extern { ... }`).
[] fn is_foreign_item: IsForeignItem(DefId) -> bool,
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/ty/query/plumbing.rs
Expand Up @@ -1136,7 +1136,8 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::FnSignature => { force!(fn_sig, def_id!()); }
DepKind::CoerceUnsizedInfo => { force!(coerce_unsized_info, def_id!()); }
DepKind::ItemVariances => { force!(variances_of, def_id!()); }
DepKind::IsConstFn => { force!(is_const_fn, def_id!()); }
DepKind::IsConstFn => { force!(is_const_fn_raw, def_id!()); }
DepKind::IsPromotableConstFn => { force!(is_promotable_const_fn, def_id!()); }
DepKind::IsForeignItem => { force!(is_foreign_item, def_id!()); }
DepKind::SizedConstraint => { force!(adt_sized_constraint, def_id!()); }
DepKind::DtorckConstraint => { force!(adt_dtorck_constraint, def_id!()); }
Expand Down
16 changes: 1 addition & 15 deletions src/librustc_metadata/cstore_impl.rs
Expand Up @@ -26,7 +26,6 @@ use rustc::ty::{self, TyCtxt};
use rustc::ty::query::Providers;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
use rustc::hir::map::{DefKey, DefPath, DefPathHash};
use rustc::hir::map::blocks::FnLikeNode;
use rustc::hir::map::definitions::DefPathTable;
use rustc::util::nodemap::DefIdMap;
use rustc_data_structures::svh::Svh;
Expand All @@ -43,7 +42,6 @@ use syntax::parse::source_file_to_stream;
use syntax::symbol::Symbol;
use syntax_pos::{Span, NO_EXPANSION, FileName};
use rustc_data_structures::bit_set::BitSet;
use rustc::hir;

macro_rules! provide {
(<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
Expand Down Expand Up @@ -145,7 +143,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
}
fn_sig => { cdata.fn_sig(def_id.index, tcx) }
inherent_impls => { Lrc::new(cdata.get_inherent_implementations_for_type(def_id.index)) }
is_const_fn => { cdata.is_const_fn(def_id.index) }
is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }
is_foreign_item => { cdata.is_foreign_item(def_id.index) }
describe_def => { cdata.get_def(def_id.index) }
def_span => { cdata.get_span(def_id.index, &tcx.sess) }
Expand Down Expand Up @@ -264,22 +262,10 @@ provide! { <'tcx> tcx, def_id, other, cdata,
}

pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
fn is_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
let node_id = tcx.hir.as_local_node_id(def_id)
.expect("Non-local call to local provider is_const_fn");

if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
fn_like.constness() == hir::Constness::Const
} else {
false
}
}

// FIXME(#44234) - almost all of these queries have no sub-queries and
// therefore no actual inputs, they're just reading tables calculated in
// resolve! Does this work? Unsure! That's what the issue is about
*providers = Providers {
is_const_fn,
is_dllimport_foreign_item: |tcx, id| {
tcx.native_library_kind(id) == Some(NativeLibraryKind::NativeUnknown)
},
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_metadata/decoder.rs
Expand Up @@ -1114,7 +1114,7 @@ impl<'a, 'tcx> CrateMetadata {
}
}

pub fn is_const_fn(&self, id: DefIndex) -> bool {
crate fn is_const_fn_raw(&self, id: DefIndex) -> bool {
let constness = match self.entry(id).kind {
EntryKind::Method(data) => data.decode(self).fn_data.constness,
EntryKind::Fn(data) => data.decode(self).constness,
Expand Down

0 comments on commit 9e46c0b

Please sign in to comment.