1
Fork 0

Only promote calls to #[rustc_promotable] const fns

This commit is contained in:
Oliver Schneider 2018-08-31 13:50:14 +02:00
parent 4cf11765dc
commit 9e46c0b689
42 changed files with 525 additions and 242 deletions

View file

@ -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>()
}
@ -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>()
}

View file

@ -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
}
@ -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()
}

View file

@ -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 }
}

View file

@ -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.
@ -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

View file

@ -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 }
}
@ -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,
@ -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,
@ -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),

View file

@ -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),

View file

@ -130,6 +130,7 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability {
level,
feature,
rustc_depr,
promotable,
const_stability
});

View file

@ -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);
}

View file

@ -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
};
}

View file

@ -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

View file

@ -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;
@ -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,

View file

@ -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))
}

View file

@ -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,

View file

@ -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!()); }

View file

@ -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;
@ -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,
@ -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) }
@ -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)
},

View file

@ -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,

View file

@ -213,7 +213,6 @@ impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
#[derive(Clone, Debug)]
enum ConstEvalError {
NeedsRfc(String),
NotConst(String),
}
impl fmt::Display for ConstEvalError {
@ -227,7 +226,6 @@ impl fmt::Display for ConstEvalError {
msg
)
}
NotConst(ref msg) => write!(f, "{}", msg),
}
}
}
@ -237,7 +235,6 @@ impl Error for ConstEvalError {
use self::ConstEvalError::*;
match *self {
NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
NotConst(_) => "this feature is not compatible with constant evaluation",
}
}
@ -293,9 +290,6 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
ecx.goto_block(ret)?; // fully evaluated and done
return Ok(None);
}
return Err(
ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
);
}
// This is a const fn. Call it.
Ok(Some(match ecx.load_mir(instance.def) {

View file

@ -29,7 +29,6 @@ use rustc::mir::traversal::ReversePostorder;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::middle::lang_items;
use rustc_target::spec::abi::Abi;
use syntax::attr;
use syntax::ast::LitKind;
use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue};
use syntax_pos::{Span, DUMMY_SP};
@ -813,7 +812,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
let fn_ty = func.ty(self.mir, self.tcx);
let mut callee_def_id = None;
let (mut is_shuffle, mut is_const_fn) = (false, None);
let (mut is_shuffle, mut is_const_fn) = (false, false);
if let ty::FnDef(def_id, _) = fn_ty.sty {
callee_def_id = Some(def_id);
match self.tcx.fn_sig(def_id).abi() {
@ -839,10 +838,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
| "unchecked_shr"
| "add_with_overflow"
| "sub_with_overflow"
| "mul_with_overflow" => is_const_fn = Some(def_id),
| "mul_with_overflow"
// no need to check feature gates, intrinsics are only callable from the
// libstd or with forever unstable feature gates
=> is_const_fn = true,
// special intrinsic that can be called diretly without an intrinsic
// feature gate needs a language feature gate
"transmute" => {
// never promote transmute calls
if self.mode != Mode::Fn {
is_const_fn = Some(def_id);
is_const_fn = true;
// const eval transmute calls only with the feature gate
if !self.tcx.sess.features_untracked().const_transmute {
emit_feature_err(
&self.tcx.sess.parse_sess, "const_transmute",
@ -861,8 +867,81 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
}
}
_ => {
if self.tcx.is_const_fn(def_id) || self.is_const_panic_fn(def_id) {
is_const_fn = Some(def_id);
// in normal functions we only care about promotion
if self.mode == Mode::Fn {
// never promote const fn calls of
// functions without #[rustc_promotable]
if self.tcx.is_promotable_const_fn(def_id) {
is_const_fn = true;
}
} else {
// stable const fn or unstable const fns with their feature gate
// active
if self.tcx.is_const_fn(def_id) {
is_const_fn = true;
} else if self.is_const_panic_fn(def_id) {
// check the const_panic feature gate
// FIXME: cannot allow this inside `allow_internal_unstable` because
// that would make `panic!` insta stable in constants, since the
// macro is marked with the attr
if self.tcx.sess.features_untracked().const_panic {
is_const_fn = true;
} else {
// don't allow panics in constants without the feature gate
emit_feature_err(
&self.tcx.sess.parse_sess,
"const_panic",
self.span,
GateIssue::Language,
&format!("panicking in {}s is unstable", self.mode),
);
}
} else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
// check `#[unstable]` const fns or `#[rustc_const_unstable]`
// functions without the feature gate active in this crate to report
// a better error message than the one below
if self.span.allows_unstable() {
// `allow_internal_unstable` can make such calls stable
is_const_fn = true;
} else {
let mut err = self.tcx.sess.struct_span_err(self.span,
&format!("`{}` is not yet stable as a const fn",
self.tcx.item_path_str(def_id)));
help!(&mut err,
"in Nightly builds, add `#![feature({})]` \
to the crate attributes to enable",
feature);
err.emit();
}
} else {
// FIXME(#24111) Remove this check when const fn stabilizes
let (msg, note) = if let UnstableFeatures::Disallow =
self.tcx.sess.opts.unstable_features {
(format!("calls in {}s are limited to \
tuple structs and tuple variants",
self.mode),
Some("a limited form of compile-time function \
evaluation is available on a nightly \
compiler via `const fn`"))
} else {
(format!("calls in {}s are limited \
to constant functions, \
tuple structs and tuple variants",
self.mode),
None)
};
let mut err = struct_span_err!(
self.tcx.sess,
self.span,
E0015,
"{}",
msg,
);
if let Some(note) = note {
err.span_note(self.span, note);
}
err.emit();
}
}
}
}
@ -905,78 +984,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
});
}
// Const fn calls.
if let Some(def_id) = is_const_fn {
// check the const_panic feature gate or
// find corresponding rustc_const_unstable feature
// FIXME: cannot allow this inside `allow_internal_unstable` because that would make
// `panic!` insta stable in constants, since the macro is marked with the attr
if self.is_const_panic_fn(def_id) {
if self.mode == Mode::Fn {
// never promote panics
// non-const fn calls.
if !is_const_fn {
self.qualif = Qualif::NOT_CONST;
} else if !self.tcx.sess.features_untracked().const_panic {
// don't allow panics in constants without the feature gate
emit_feature_err(
&self.tcx.sess.parse_sess,
"const_panic",
if self.mode != Mode::Fn {
self.tcx.sess.delay_span_bug(
self.span,
GateIssue::Language,
&format!("panicking in {}s is unstable", self.mode),
);
}
} else if let Some(&attr::Stability {
const_stability: Some(ref feature_name),
.. }) = self.tcx.lookup_stability(def_id) {
if
// feature-gate is not enabled,
!self.tcx.features()
.declared_lib_features
.iter()
.any(|&(ref sym, _)| sym == feature_name) &&
// this doesn't come from a macro that has #[allow_internal_unstable]
!self.span.allows_unstable()
{
self.qualif = Qualif::NOT_CONST;
if self.mode != Mode::Fn {
// inside a constant environment, not having the feature gate is
// an error
let mut err = self.tcx.sess.struct_span_err(self.span,
&format!("`{}` is not yet stable as a const fn",
self.tcx.item_path_str(def_id)));
help!(&mut err,
"in Nightly builds, add `#![feature({})]` \
to the crate attributes to enable",
feature_name);
err.emit();
}
}
}
} else {
self.qualif = Qualif::NOT_CONST;
if self.mode != Mode::Fn {
// FIXME(#24111) Remove this check when const fn stabilizes
let (msg, note) = if let UnstableFeatures::Disallow =
self.tcx.sess.opts.unstable_features {
(format!("calls in {}s are limited to \
tuple structs and tuple variants",
self.mode),
Some("a limited form of compile-time function \
evaluation is available on a nightly \
compiler via `const fn`"))
} else {
(format!("calls in {}s are limited \
to constant functions, \
tuple structs and tuple variants",
self.mode),
None)
};
let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
if let Some(note) = note {
err.span_note(self.span, note);
}
err.emit();
"should have reported an error about non-const fn calls in constants",
)
}
}

View file

@ -27,7 +27,6 @@
use rustc::ty::cast::CastKind;
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::def_id::DefId;
use rustc::hir::map::blocks::FnLikeNode;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
@ -38,7 +37,6 @@ use rustc::util::nodemap::{ItemLocalSet, NodeSet};
use rustc::hir;
use rustc_data_structures::sync::Lrc;
use syntax::ast;
use syntax::attr;
use syntax_pos::{Span, DUMMY_SP};
use self::Promotability::*;
use std::ops::{BitAnd, BitAndAssign, BitOr};
@ -160,41 +158,15 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
}
}
fn handle_const_fn_call(&mut self, def_id: DefId,
ret_ty: Ty<'gcx>, span: Span) -> Promotability {
if self.type_promotability(ret_ty) == NotPromotable {
return NotPromotable;
}
let node_check = if let Some(fn_id) = self.tcx.hir.as_local_node_id(def_id) {
FnLikeNode::from_node(self.tcx.hir.get(fn_id)).map_or(false, |fn_like| {
fn_like.constness() == hir::Constness::Const
})
} else {
self.tcx.is_const_fn(def_id)
};
if !node_check {
return NotPromotable
}
if let Some(&attr::Stability {
const_stability: Some(ref feature_name),
.. }) = self.tcx.lookup_stability(def_id) {
let stable_check =
// feature-gate is enabled,
self.tcx.features()
.declared_lib_features
.iter()
.any(|&(ref sym, _)| sym == feature_name) ||
// this comes from a macro that has #[allow_internal_unstable]
span.allows_unstable();
if !stable_check {
return NotPromotable
}
};
fn handle_const_fn_call(
&mut self,
def_id: DefId,
) -> Promotability {
if self.tcx.is_promotable_const_fn(def_id) {
Promotable
} else {
NotPromotable
}
}
/// While the `ExprUseVisitor` walks, we will identify which
@ -443,14 +415,10 @@ fn check_expr_kind<'a, 'tcx>(
Def::StructCtor(_, CtorKind::Fn) |
Def::VariantCtor(_, CtorKind::Fn) |
Def::SelfCtor(..) => Promotable,
Def::Fn(did) => {
v.handle_const_fn_call(did, node_ty, e.span)
}
Def::Fn(did) => v.handle_const_fn_call(did),
Def::Method(did) => {
match v.tcx.associated_item(did).container {
ty::ImplContainer(_) => {
v.handle_const_fn_call(did, node_ty, e.span)
}
ty::ImplContainer(_) => v.handle_const_fn_call(did),
ty::TraitContainer(_) => NotPromotable,
}
}
@ -466,16 +434,13 @@ fn check_expr_kind<'a, 'tcx>(
if let Some(def) = v.tables.type_dependent_defs().get(e.hir_id) {
let def_id = def.def_id();
match v.tcx.associated_item(def_id).container {
ty::ImplContainer(_) => {
method_call_result = method_call_result
& v.handle_const_fn_call(def_id, node_ty, e.span);
ty::ImplContainer(_) => method_call_result & v.handle_const_fn_call(def_id),
ty::TraitContainer(_) => NotPromotable,
}
ty::TraitContainer(_) => return NotPromotable,
};
} else {
v.tcx.sess.delay_span_bug(e.span, "no type-dependent def for method call");
NotPromotable
}
method_call_result
}
hir::ExprKind::Struct(ref _qpath, ref hirvec, ref option_expr) => {
let mut struct_result = Promotable;

View file

@ -112,6 +112,8 @@ pub struct Stability {
/// `Some` contains the feature gate required to be able to use the function
/// as const fn
pub const_stability: Option<Symbol>,
/// whether the function has a `#[rustc_promotable]` attribute
pub promotable: bool,
}
/// The available stability levels.
@ -176,6 +178,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
let mut stab: Option<Stability> = None;
let mut rustc_depr: Option<RustcDeprecation> = None;
let mut rustc_const_unstable: Option<Symbol> = None;
let mut promotable = false;
'outer: for attr in attrs_iter {
if ![
@ -183,6 +186,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
"rustc_const_unstable",
"unstable",
"stable",
"rustc_promotable",
].iter().any(|&s| attr.path == s) {
continue // not a stability level
}
@ -190,8 +194,12 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
mark_used(attr);
let meta = attr.meta();
if attr.path == "rustc_promotable" {
promotable = true;
}
// attributes with data
if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
else if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
let meta = meta.as_ref().unwrap();
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
@ -329,6 +337,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
feature,
rustc_depr: None,
const_stability: None,
promotable: false,
})
}
(None, _, _) => {
@ -378,6 +387,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
feature,
rustc_depr: None,
const_stability: None,
promotable: false,
})
}
(None, _) => {
@ -420,6 +430,17 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
}
}
// Merge the const-unstable info into the stability info
if promotable {
if let Some(ref mut stab) = stab {
stab.promotable = true;
} else {
span_err!(diagnostic, item_sp, E0713,
"rustc_promotable attribute must be paired with \
either stable or unstable attribute");
}
}
stab
}

View file

@ -413,4 +413,5 @@ register_diagnostics! {
E0694, // an unknown tool name found in scoped attributes
E0703, // invalid ABI
E0704, // incorrect visibility restriction
E0713, // rustc_promotable without stability attribute
}

View file

@ -14,7 +14,8 @@
// compile-flags: -C debug_assertions=yes
#![feature(const_fn, libc)]
#![stable(feature = "rustc", since = "1.0.0")]
#![feature(const_fn, libc, staged_api, rustc_attrs)]
#![allow(const_err)]
extern crate libc;
@ -23,6 +24,8 @@ use std::env;
use std::process::{Command, Stdio};
// this will panic in debug mode and overflow in release mode
#[stable(feature = "rustc", since = "1.0.0")]
#[rustc_promotable]
const fn bar() -> usize { 0 - 1 }
fn foo() {

View file

@ -13,13 +13,15 @@
use std::cell::Cell;
const FIVE: Cell<i32> = Cell::new(5);
#[inline(never)]
fn tuple_field() -> &'static u32 {
// This test is MIR-borrowck-only because the old borrowck
// doesn't agree that borrows of "frozen" (i.e. without any
// interior mutability) fields of non-frozen temporaries,
// should be promoted, while MIR promotion does promote them.
&(Cell::new(5), 42).1
&(FIVE, 42).1
}
fn main() {

View file

@ -15,5 +15,4 @@ fn f(x: usize) -> usize {
fn main() {
let _ = [0; f(2)];
//~^ ERROR calls in constants are limited to constant functions
//~| E0080
}

View file

@ -4,13 +4,6 @@ error[E0015]: calls in constants are limited to constant functions, tuple struct
LL | let _ = [0; f(2)];
| ^^^^
error[E0080]: could not evaluate repeat length
--> $DIR/const-call.rs:16:17
|
LL | let _ = [0; f(2)];
| ^^^^ calling non-const fn `f`
error: aborting due to previous error
error: aborting due to 2 previous errors
Some errors occurred: E0015, E0080.
For more information about an error, try `rustc --explain E0015`.
For more information about this error, try `rustc --explain E0015`.

View file

@ -32,7 +32,7 @@ error[E0716]: temporary value dropped while borrowed
|
LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
LL | //~^ does not live long enough
LL | //~^ ERROR does not live long enough
LL | }
| - temporary value is freed at the end of this statement
|

View file

@ -31,5 +31,5 @@ fn a() {
fn main() {
let _: &'static u32 = &meh(); //~ ERROR does not live long enough
let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
//~^ does not live long enough
//~^ ERROR does not live long enough
}

View file

@ -32,7 +32,7 @@ error[E0597]: borrowed value does not live long enough
|
LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
LL | //~^ does not live long enough
LL | //~^ ERROR does not live long enough
LL | }
| - temporary value only lives until here
|

View file

@ -43,7 +43,28 @@ error[E0080]: could not evaluate repeat length
LL | [(); { for _ in 0usize.. {}; 0}]; //~ ERROR calls in constants are limited to constant functions
| ^^^^^^^^^^^--------^^^^^^^
| |
| calling non-const fn `<I as std::iter::IntoIterator><std::ops::RangeFrom<usize>>::into_iter`
| inside call to `std::iter::range::<impl std::iter::Iterator for std::ops::RangeFrom<A>><usize>::next`
|
::: $SRC_DIR/libcore/ptr.rs:LL:COL
|
LL | let z = read(x);
| ------- inside call to `std::ptr::read::<usize>`
...
LL | let mut tmp: T = mem::uninitialized();
| -------------------- inside call to `std::mem::uninitialized::<usize>`
|
::: $SRC_DIR/libcore/iter/range.rs:LL:COL
|
LL | mem::swap(&mut n, &mut self.start);
| ---------------------------------- inside call to `std::mem::swap::<usize>`
|
::: $SRC_DIR/libcore/mem.rs:LL:COL
|
LL | intrinsics::uninit()
| -------------------- "calling intrinsic `uninit`" needs an rfc before being allowed inside constants
...
LL | ptr::swap_nonoverlapping_one(x, y);
| ---------------------------------- inside call to `std::ptr::swap_nonoverlapping_one::<usize>`
error: aborting due to 6 previous errors

View file

@ -0,0 +1,14 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_const_fn_fail.rs:30:27
|
LL | let x: &'static u8 = &(bar() + 1); //~ ERROR does not live long enough
| ^^^^^^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to previous error
For more information about this error, try `rustc --explain E0716`.

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-pass
#![feature(const_fn, const_fn_union)]
#![deny(const_err)]
@ -29,10 +27,7 @@ const fn bar() -> u8 {
}
fn main() {
// FIXME(oli-obk): this should panic at runtime
// this will actually compile, but then
// abort at runtime (not panic, hard abort).
let x: &'static u8 = &(bar() + 1);
let x: &'static u8 = &(bar() + 1); //~ ERROR does not live long enough
let y = *x;
unreachable!();
}

View file

@ -0,0 +1,14 @@
error[E0597]: borrowed value does not live long enough
--> $DIR/promoted_const_fn_fail.rs:30:27
|
LL | let x: &'static u8 = &(bar() + 1); //~ ERROR does not live long enough
| ^^^^^^^^^^^ temporary value does not live long enough
...
LL | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`.

View file

@ -30,12 +30,33 @@ error[E0080]: could not evaluate constant expression
--> $DIR/const-fn-error.rs:29:13
|
LL | for i in 0..x {
| ---- calling non-const fn `<I as std::iter::IntoIterator><std::ops::Range<usize>>::into_iter`
| ---- inside call to `std::iter::range::<impl std::iter::Iterator for std::ops::Range<A>><usize>::next`
...
LL | let a : [i32; f(X)]; //~ ERROR E0080
| ^^^^^^----^
| |
| inside call to `f`
|
::: $SRC_DIR/libcore/ptr.rs:LL:COL
|
LL | let z = read(x);
| ------- inside call to `std::ptr::read::<usize>`
...
LL | let mut tmp: T = mem::uninitialized();
| -------------------- inside call to `std::mem::uninitialized::<usize>`
|
::: $SRC_DIR/libcore/iter/range.rs:LL:COL
|
LL | mem::swap(&mut n, &mut self.start);
| ---------------------------------- inside call to `std::mem::swap::<usize>`
|
::: $SRC_DIR/libcore/mem.rs:LL:COL
|
LL | intrinsics::uninit()
| -------------------- "calling intrinsic `uninit`" needs an rfc before being allowed inside constants
...
LL | ptr::swap_nonoverlapping_one(x, y);
| ---------------------------------- inside call to `std::ptr::swap_nonoverlapping_one::<usize>`
error: aborting due to 5 previous errors

View file

@ -0,0 +1,68 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/promotion.rs:13:27
|
LL | let x: &'static () = &foo1(); //~ ERROR does not live long enough
| ^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...
error[E0716]: temporary value dropped while borrowed
--> $DIR/promotion.rs:14:28
|
LL | let y: &'static i32 = &foo2(42); //~ ERROR does not live long enough
| ^^^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...
error[E0716]: temporary value dropped while borrowed
--> $DIR/promotion.rs:15:28
|
LL | let z: &'static i32 = &foo3(); //~ ERROR does not live long enough
| ^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...
error[E0716]: temporary value dropped while borrowed
--> $DIR/promotion.rs:16:34
|
LL | let a: &'static Cell<i32> = &foo4(); //~ ERROR does not live long enough
| ^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...
error[E0716]: temporary value dropped while borrowed
--> $DIR/promotion.rs:17:42
|
LL | let a: &'static Option<Cell<i32>> = &foo5(); //~ ERROR does not live long enough
| ^^^^^^ creates a temporary which is freed while still in use
LL | let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...
error[E0716]: temporary value dropped while borrowed
--> $DIR/promotion.rs:18:42
|
LL | let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
| ^^^^^^ creates a temporary which is freed while still in use
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0716`.

View file

@ -0,0 +1,19 @@
#![feature(min_const_fn)]
use std::cell::Cell;
const fn foo1() {}
const fn foo2(x: i32) -> i32 { x }
const fn foo3() -> i32 { 42 }
const fn foo4() -> Cell<i32> { Cell::new(42) }
const fn foo5() -> Option<Cell<i32>> { Some(Cell::new(42)) }
const fn foo6() -> Option<Cell<i32>> { None }
fn main() {
let x: &'static () = &foo1(); //~ ERROR does not live long enough
let y: &'static i32 = &foo2(42); //~ ERROR does not live long enough
let z: &'static i32 = &foo3(); //~ ERROR does not live long enough
let a: &'static Cell<i32> = &foo4(); //~ ERROR does not live long enough
let a: &'static Option<Cell<i32>> = &foo5(); //~ ERROR does not live long enough
let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
}

View file

@ -0,0 +1,68 @@
error[E0597]: borrowed value does not live long enough
--> $DIR/promotion.rs:13:27
|
LL | let x: &'static () = &foo1(); //~ ERROR does not live long enough
| ^^^^^^ temporary value does not live long enough
...
LL | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: borrowed value does not live long enough
--> $DIR/promotion.rs:14:28
|
LL | let y: &'static i32 = &foo2(42); //~ ERROR does not live long enough
| ^^^^^^^^ temporary value does not live long enough
...
LL | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: borrowed value does not live long enough
--> $DIR/promotion.rs:15:28
|
LL | let z: &'static i32 = &foo3(); //~ ERROR does not live long enough
| ^^^^^^ temporary value does not live long enough
...
LL | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: borrowed value does not live long enough
--> $DIR/promotion.rs:16:34
|
LL | let a: &'static Cell<i32> = &foo4(); //~ ERROR does not live long enough
| ^^^^^^ temporary value does not live long enough
...
LL | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: borrowed value does not live long enough
--> $DIR/promotion.rs:17:42
|
LL | let a: &'static Option<Cell<i32>> = &foo5(); //~ ERROR does not live long enough
| ^^^^^^ temporary value does not live long enough
LL | let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
LL | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: borrowed value does not live long enough
--> $DIR/promotion.rs:18:42
|
LL | let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
| ^^^^^^ temporary value does not live long enough
LL | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0597`.

View file

@ -23,8 +23,6 @@ impl Dim for Dim3 {
fn main() {
let array: [usize; Dim3::dim()]
//~^ ERROR E0015
//~| ERROR E0080
= [0; Dim3::dim()];
//~^ ERROR E0015
//~| ERROR E0080
}

View file

@ -5,26 +5,11 @@ LL | let array: [usize; Dim3::dim()]
| ^^^^^^^^^^^
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> $DIR/issue-39559-2.rs:27:15
--> $DIR/issue-39559-2.rs:26:15
|
LL | = [0; Dim3::dim()];
| ^^^^^^^^^^^
error[E0080]: could not evaluate repeat length
--> $DIR/issue-39559-2.rs:27:15
|
LL | = [0; Dim3::dim()];
| ^^^^^^^^^^^ calling non-const fn `<Dim3 as Dim>::dim`
error: aborting due to 2 previous errors
error[E0080]: could not evaluate constant expression
--> $DIR/issue-39559-2.rs:24:16
|
LL | let array: [usize; Dim3::dim()]
| ^^^^^^^^-----------^
| |
| calling non-const fn `<Dim3 as Dim>::dim`
error: aborting due to 4 previous errors
Some errors occurred: E0015, E0080.
For more information about an error, try `rustc --explain E0015`.
For more information about this error, try `rustc --explain E0015`.

View file

@ -16,7 +16,6 @@ const NUM: u8 = xyz();
fn main() {
match 1 {
NUM => unimplemented!(),
//~^ ERROR could not evaluate constant pattern
_ => unimplemented!(),
}
}

View file

@ -4,16 +4,6 @@ error[E0015]: calls in constants are limited to constant functions, tuple struct
LL | const NUM: u8 = xyz();
| ^^^^^
error[E0080]: could not evaluate constant pattern
--> $DIR/issue-43105.rs:18:9
|
LL | const NUM: u8 = xyz();
| ----- calling non-const fn `xyz`
...
LL | NUM => unimplemented!(),
| ^^^
error: aborting due to previous error
error: aborting due to 2 previous errors
Some errors occurred: E0015, E0080.
For more information about an error, try `rustc --explain E0015`.
For more information about this error, try `rustc --explain E0015`.

View file

@ -27,7 +27,8 @@ const fn baz() -> i32 {
fn main() {
foo(2);
foo(2 + 3);
foo(baz());
const BAZ: i32 = baz();
foo(BAZ);
let a = 4;
foo(A);
foo(a); //~ ERROR: argument 1 is required to be a constant

View file

@ -1,11 +1,11 @@
error: argument 1 is required to be a constant
--> $DIR/rustc-args-required-const.rs:33:5
--> $DIR/rustc-args-required-const.rs:34:5
|
LL | foo(a); //~ ERROR: argument 1 is required to be a constant
| ^^^^^^
error: argument 2 is required to be a constant
--> $DIR/rustc-args-required-const.rs:35:5
--> $DIR/rustc-args-required-const.rs:36:5
|
LL | bar(a, a); //~ ERROR: argument 2 is required to be a constant
| ^^^^^^^^^