Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit the promotion of const fns to the libstd and the rustc_promotable attribute #53851

Merged
merged 5 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libcore/mem.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I collected all the logic in this one file in order to make our lives easier and have less code duplication which might cause odd inconsistencies

/// 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
// needs 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
Original file line number Diff line number Diff line change
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
4 changes: 3 additions & 1 deletion src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ impl<'tcx> InstanceDef<'tcx> {
return true
}
let codegen_fn_attrs = tcx.codegen_fn_attrs(self.def_id());
codegen_fn_attrs.requests_inline() || tcx.is_const_fn(self.def_id())
// need to use `is_const_fn_raw` since we don't really care if the user can use it as a
// const fn, just whether the function should be inlined
codegen_fn_attrs.requests_inline() || tcx.is_const_fn_raw(self.def_id())
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading