diff --git a/src/doc/unstable-book/src/language-features/promotable-const-fn.md b/src/doc/unstable-book/src/language-features/promotable-const-fn.md new file mode 100644 index 0000000000000..25bd5cbd1a03d --- /dev/null +++ b/src/doc/unstable-book/src/language-features/promotable-const-fn.md @@ -0,0 +1,43 @@ +# promotable_const_fn + +The tracking issue for this feature is: None. + +The `#[promotable_const_fn]` attribute can be used if the `promotable_const_fn` feature gate is +active. +While there are no checks on where it can be used, it only has an effect if applied to a `const fn`. +Since this attribute is never meant to be stabilized, this seems like an ok footgun. + +The attribute exists, so it is possible to stabilize the constness of a function without stabilizing +the promotablity of the function at the same time. We eventually want to have a better strategy, +but we need to hash out the details. Until then, any const fn that is doing anything nontrivial +should not be marked with `#[promotable_const_fn]`. Even better: do not mark any functions with the +attribute. The existing "promotable on stable" functions were marked, but we could simply not +stabilize the promotability of any further functions until we have figured out a clean solution +for all the issues that come with promotion. + +The reason this attribute even exists are functions like + +```rust,ignore +#![feature(const_fn)] + +fn main() { + let x: &'static bool = &foo(&1, &2); +} + +union Foo<'a> { + a: &'a u8, + b: usize, +} + +const fn foo(a: &u8, b: &u8) -> bool { + unsafe { Foo { a: a }.b == Foo { a: b }.b } +} +``` + +Where this would be perfectly fine at runtime, but changing the function to a const fn would cause +it to be evaluated at compile-time and thus produce a lint about not being able to compute the value +and then emitting a llvm `undef` because there is no value to place into the promoted. + +This attribute patches this hole by not promoting functions without the attribute and at the same +time preventing functions with the attribute from using unions or calling other functions that do +not have said attribute. \ No newline at end of file diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 74bbd65924630..c50c480ae52ca 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -103,6 +103,7 @@ #![feature(optin_builtin_traits)] #![feature(pattern)] #![feature(pin)] +#![feature(promotable_const_fn)] #![feature(ptr_internals)] #![feature(ptr_offset_from)] #![feature(repr_transparent)] diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index d1f140e96a3ae..aad0991d8d846 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -56,6 +56,7 @@ pub struct RawVec { impl RawVec { /// Like `new` but parameterized over the choice of allocator for /// the returned RawVec. + #[promotable_const_fn] pub const fn new_in(a: A) -> Self { // !0 is usize::MAX. This branch should be stripped at compile time. // FIXME(mark-i-m): use this line when `if`s are allowed in `const` @@ -123,6 +124,7 @@ impl RawVec { /// RawVec with capacity 0. If T has 0 size, then it makes a /// RawVec with capacity `usize::MAX`. Useful for implementing /// delayed allocation. + #[promotable_const_fn] pub const fn new() -> Self { Self::new_in(Global) } diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index a988b6a26d9df..9448bf5b22354 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -381,6 +381,7 @@ impl String { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_string_new")] + #[promotable_const_fn] pub const fn new() -> String { String { vec: Vec::new() } } diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index b5739e1a82553..38c81f121a322 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -320,6 +320,7 @@ impl Vec { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_vec_new")] + #[promotable_const_fn] pub const fn new() -> Vec { Vec { buf: RawVec::new(), diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 4437c36c15a5b..f14f6ca10d84d 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -458,6 +458,7 @@ impl TypeId { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature="const_type_id")] + #[promotable_const_fn] pub const fn of() -> TypeId { TypeId { t: unsafe { intrinsics::type_id::() }, diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index d50ad580ed574..9d846ffecf0e0 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -374,6 +374,7 @@ impl Cell { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[promotable_const_fn] pub const fn new(value: T) -> Cell { Cell { value: UnsafeCell::new(value), @@ -588,6 +589,7 @@ impl RefCell { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[promotable_const_fn] pub const fn new(value: T) -> RefCell { RefCell { value: UnsafeCell::new(value), @@ -1304,6 +1306,7 @@ impl UnsafeCell { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[promotable_const_fn] pub const fn new(value: T) -> UnsafeCell { UnsafeCell { value: value } } diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 5ba77edee6e48..1b04582fe3cc6 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -99,6 +99,7 @@ #![feature(on_unimplemented)] #![feature(optin_builtin_traits)] #![feature(prelude_import)] +#![feature(promotable_const_fn)] #![feature(repr_simd, platform_intrinsics)] #![feature(repr_transparent)] #![feature(rustc_attrs)] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 31635ffa53c83..18b89e0ea0326 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -314,6 +314,7 @@ pub fn forget(t: T) { /// [alignment]: ./fn.align_of.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[promotable_const_fn] pub const fn size_of() -> usize { unsafe { intrinsics::size_of::() } } @@ -405,6 +406,7 @@ pub fn min_align_of_val(val: &T) -> usize { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[promotable_const_fn] pub const fn align_of() -> usize { unsafe { intrinsics::min_align_of::() } } @@ -966,6 +968,7 @@ impl ManuallyDrop { #[stable(feature = "manually_drop", since = "1.20.0")] #[rustc_const_unstable(feature = "const_manually_drop_new")] #[inline] + #[promotable_const_fn] pub const fn new(value: T) -> ManuallyDrop { ManuallyDrop { value: value } } diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 1168126c47c93..5924cbfb97374 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -200,6 +200,7 @@ $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[promotable_const_fn] pub const fn min_value() -> Self { !0 ^ ((!0 as $UnsignedT) >> 1) as Self } @@ -218,6 +219,7 @@ $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[promotable_const_fn] pub const fn max_value() -> Self { !Self::min_value() } @@ -2103,6 +2105,7 @@ Basic usage: ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[promotable_const_fn] pub const fn min_value() -> Self { 0 } } @@ -2119,6 +2122,7 @@ stringify!($MaxV), ");", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[promotable_const_fn] pub const fn max_value() -> Self { !0 } } diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index d7f87d37f5b33..3f29cceee28d5 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -344,6 +344,7 @@ assert_eq!(>::min_value(), ", ```"), #[unstable(feature = "wrapping_int_impl", issue = "32463")] #[inline] + #[promotable_const_fn] pub const fn min_value() -> Self { Wrapping(<$t>::min_value()) } @@ -365,6 +366,7 @@ assert_eq!(>::max_value(), ", ```"), #[unstable(feature = "wrapping_int_impl", issue = "32463")] #[inline] + #[promotable_const_fn] pub const fn max_value() -> Self { Wrapping(<$t>::max_value()) } diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 01e279589da98..0accef8625d7e 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -349,6 +349,7 @@ impl RangeInclusive { /// ``` #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] + #[promotable_const_fn] pub const fn new(start: Idx, end: Idx) -> Self { Self { start, end } } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 81a8b3ef0474d..36a9ca0082f00 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -74,6 +74,7 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[promotable_const_fn] pub const fn null() -> *const T { 0 as *const T } /// Creates a null mutable raw pointer. @@ -88,6 +89,7 @@ pub const fn null() -> *const T { 0 as *const T } /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[promotable_const_fn] pub const fn null_mut() -> *mut T { 0 as *mut T } /// Swaps the values at two mutable locations of the same type, without @@ -2730,6 +2732,7 @@ impl Unique { /// This is useful for initializing types which lazily allocate, like /// `Vec::new` does. // FIXME: rename to dangling() to match NonNull? + #[promotable_const_fn] pub const fn empty() -> Self { unsafe { Unique::new_unchecked(mem::align_of::() as *mut T) @@ -2744,6 +2747,7 @@ impl Unique { /// # Safety /// /// `ptr` must be non-null. + #[promotable_const_fn] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { Unique { pointer: NonZero(ptr as _), _marker: PhantomData } } diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 7aba8b51cff51..c02483bc03f34 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -246,6 +246,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[promotable_const_fn] pub const fn new(v: bool) -> AtomicBool { AtomicBool { v: UnsafeCell::new(v as u8) } } @@ -659,6 +660,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[promotable_const_fn] pub const fn new(p: *mut T) -> AtomicPtr { AtomicPtr { p: UnsafeCell::new(p) } } @@ -1011,6 +1013,7 @@ let atomic_forty_two = ", stringify!($atomic_type), "::new(42); ```"), #[inline] #[$stable] + #[promotable_const_fn] pub const fn new(v: $int_type) -> Self { $atomic_type {v: UnsafeCell::new(v)} } diff --git a/src/libcore/time.rs b/src/libcore/time.rs index 563eea0066d59..2040f3986fb4c 100644 --- a/src/libcore/time.rs +++ b/src/libcore/time.rs @@ -108,6 +108,7 @@ impl Duration { /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] + #[promotable_const_fn] pub const fn from_secs(secs: u64) -> Duration { Duration { secs: secs, nanos: 0 } } @@ -126,6 +127,7 @@ impl Duration { /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] + #[promotable_const_fn] pub const fn from_millis(millis: u64) -> Duration { Duration { secs: millis / MILLIS_PER_SEC, @@ -147,6 +149,7 @@ impl Duration { /// ``` #[stable(feature = "duration_from_micros", since = "1.27.0")] #[inline] + #[promotable_const_fn] pub const fn from_micros(micros: u64) -> Duration { Duration { secs: micros / MICROS_PER_SEC, @@ -168,6 +171,7 @@ impl Duration { /// ``` #[stable(feature = "duration_extras", since = "1.27.0")] #[inline] + #[promotable_const_fn] pub const fn from_nanos(nanos: u64) -> Duration { Duration { secs: nanos / (NANOS_PER_SEC as u64), diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index f3fba5b47be14..93e873d41ae57 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -97,7 +97,7 @@ enum Mode { Const, Static, StaticMut, - ConstFn, + ConstFn { promotable: bool }, Fn } @@ -106,7 +106,8 @@ impl fmt::Display for Mode { match *self { Mode::Const => write!(f, "constant"), Mode::Static | Mode::StaticMut => write!(f, "static"), - Mode::ConstFn => write!(f, "constant function"), + Mode::ConstFn { promotable: false } => write!(f, "constant function"), + Mode::ConstFn { promotable: true } => write!(f, "promotable constant function"), Mode::Fn => write!(f, "function") } } @@ -505,21 +506,24 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } } - - if self.mode == Mode::Const || self.mode == Mode::ConstFn { - let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, - "{}s cannot refer to statics, use \ - a constant instead", self.mode); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "Static and const variables can refer to other const variables. But a \ - const variable cannot refer to a static variable." - ); - err.help( - "To fix this, the value can be extracted as a const and then used." - ); - } - err.emit() + match self.mode { + | Mode::Const + | Mode::ConstFn { .. } => { + let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, + "{}s cannot refer to statics, use \ + a constant instead", self.mode); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "Static and const variables can refer to other const variables. \ + But a const variable cannot refer to a static variable." + ); + err.help( + "To fix this, the value can be extracted as a const and then used." + ); + } + err.emit() + }, + _ => {}, } } Place::Projection(ref proj) => { @@ -566,7 +570,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { ProjectionElem::Field(..) | ProjectionElem::Index(_) => { - if this.mode == Mode::Fn { + if this.mode == Mode::Fn || + this.mode == (Mode::ConstFn { promotable: true }) { let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); if let Some(def) = base_ty.ty_adt_def() { if def.is_union() { @@ -906,6 +911,7 @@ This does not pose a problem by itself because they can't be accessed directly." 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 promotable = false; if let ty::TyFnDef(def_id, _) = fn_ty.sty { callee_def_id = Some(def_id); match self.tcx.fn_sig(def_id).abi() { @@ -913,9 +919,17 @@ This does not pose a problem by itself because they can't be accessed directly." Abi::PlatformIntrinsic => { assert!(!self.tcx.is_const_fn(def_id)); match &self.tcx.item_name(def_id).as_str()[..] { + // Do not add anything to this list without consulting + // @eddyb, @oli-obk and @nikomatsakis | "size_of" | "min_align_of" - | "type_id" + | "type_id" => { + is_const_fn = Some(def_id); + promotable = true; + }, + // When adding something to this list, make sure to also implement + // the corresponding entry in the `call_intrinsic` method in + // rustc_mir::interpret::const_eval | "bswap" | "ctpop" | "cttz" @@ -933,6 +947,11 @@ This does not pose a problem by itself because they can't be accessed directly." _ => { if self.tcx.is_const_fn(def_id) { is_const_fn = Some(def_id); + promotable = self + .tcx + .get_attrs(def_id) + .iter() + .any(|attr| attr.check_name("promotable_const_fn")); } } } @@ -977,12 +996,33 @@ This does not pose a problem by itself because they can't be accessed directly." // Const fn calls. if let Some(def_id) = is_const_fn { + let stability = self.tcx.lookup_stability(def_id); + + // only allow const fns explicitly checked for promotability + if !promotable { + match self.mode { + Mode::Fn => self.qualif = Qualif::NOT_CONST, + Mode::ConstFn { promotable: true } => { + self.qualif = Qualif::NOT_CONST; + let mut err = self.tcx.sess.struct_span_err( + self.span, + &format!("`{}` is not a promotable const fn and can thus not \ + be used inside a function with the \ + `#[promotable_const_fn]` attribute", + self.tcx.item_path_str(def_id)), + ); + err.emit(); + }, + _ => {}, + } + } + // find corresponding rustc_const_unstable feature if let Some(&attr::Stability { rustc_const_unstable: Some(attr::RustcConstUnstable { feature: ref feature_name }), - .. }) = self.tcx.lookup_stability(def_id) { + .. }) = stability { if // feature-gate is not enabled, !self.tcx.features() @@ -1094,7 +1134,7 @@ This does not pose a problem by itself because they can't be accessed directly." self.visit_rvalue(rvalue, location); // Check the allowed const fn argument forms. - if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) { + if let (Mode::ConstFn { .. }, &Place::Local(index)) = (self.mode, dest) { if self.mir.local_kind(index) == LocalKind::Var && self.const_fn_arg_vars.insert(index.index()) && !self.tcx.sess.features_untracked().const_let { @@ -1216,10 +1256,15 @@ impl MirPass for QualifyAndPromoteConstants { let def_id = src.def_id; let id = tcx.hir.as_local_node_id(def_id).unwrap(); let mut const_promoted_temps = None; - let mode = match tcx.hir.body_owner_kind(id) { + let body_owner_kind = tcx.hir.body_owner_kind(id); + let mode = match body_owner_kind { hir::BodyOwnerKind::Fn => { if tcx.is_const_fn(def_id) { - Mode::ConstFn + let promotable = tcx + .get_attrs(def_id) + .iter() + .any(|attr| attr.check_name("promotable_const_fn")); + Mode::ConstFn { promotable } } else { Mode::Fn } @@ -1232,12 +1277,12 @@ impl MirPass for QualifyAndPromoteConstants { hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut, }; - if mode == Mode::Fn || mode == Mode::ConstFn { + if let hir::BodyOwnerKind::Fn = body_owner_kind { // This is ugly because Qualifier holds onto mir, // which can't be mutated until its scope ends. let (temps, candidates) = { let mut qualifier = Qualifier::new(tcx, def_id, mir, mode); - if mode == Mode::ConstFn { + if let Mode::ConstFn { .. } = mode { // Enforce a constant-like CFG for `const fn`. qualifier.qualify_const(); } else { diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index 82ac112b534e9..bd37b7e030dc2 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -131,6 +131,12 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { self.tcx.is_const_fn(def_id) }; + self.promotable &= self + .tcx + .get_attrs(def_id) + .iter() + .any(|attr| attr.check_name("promotable_const_fn")); + if let Some(&attr::Stability { rustc_const_unstable: Some(attr::RustcConstUnstable { feature: ref feature_name diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs index 9cef4e3cdf1ca..0dceda6cf18bb 100644 --- a/src/libstd/io/lazy.rs +++ b/src/libstd/io/lazy.rs @@ -23,6 +23,7 @@ pub struct Lazy { unsafe impl Sync for Lazy {} impl Lazy { + #[promotable_const_fn] pub const fn new(init: fn() -> Arc) -> Lazy { Lazy { lock: Mutex::new(), diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 1bdc1dc2b7cfb..0a5ef9cf75887 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -253,6 +253,7 @@ #![feature(collections_range)] #![feature(compiler_builtins_lib)] #![feature(const_fn)] +#![feature(custom_attribute)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] @@ -286,6 +287,7 @@ #![feature(pin)] #![feature(placement_new_protocol)] #![feature(prelude_import)] +#![feature(promotable_const_fn)] #![feature(ptr_internals)] #![feature(rand)] #![feature(raw)] diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 7eb7be23128b3..e3577b6433433 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -157,6 +157,7 @@ struct Finish { impl Once { /// Creates a new `Once` value. #[stable(feature = "once_new", since = "1.2.0")] + #[promotable_const_fn] pub const fn new() -> Once { Once { state: AtomicUsize::new(INCOMPLETE), diff --git a/src/libstd/sys/cloudabi/condvar.rs b/src/libstd/sys/cloudabi/condvar.rs index c05c837ade274..68c7ca9ad273c 100644 --- a/src/libstd/sys/cloudabi/condvar.rs +++ b/src/libstd/sys/cloudabi/condvar.rs @@ -29,6 +29,7 @@ unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} impl Condvar { + #[promotable_const_fn] pub const fn new() -> Condvar { Condvar { condvar: UnsafeCell::new(AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0)), diff --git a/src/libstd/sys/cloudabi/mutex.rs b/src/libstd/sys/cloudabi/mutex.rs index d4ba6bcfc8062..a3bfa1d23879c 100644 --- a/src/libstd/sys/cloudabi/mutex.rs +++ b/src/libstd/sys/cloudabi/mutex.rs @@ -29,6 +29,7 @@ pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 { } impl Mutex { + #[promotable_const_fn] pub const fn new() -> Mutex { Mutex(RWLock::new()) } diff --git a/src/libstd/sys/cloudabi/rwlock.rs b/src/libstd/sys/cloudabi/rwlock.rs index 8539aec5e2c07..24d4eb738d0d6 100644 --- a/src/libstd/sys/cloudabi/rwlock.rs +++ b/src/libstd/sys/cloudabi/rwlock.rs @@ -33,6 +33,7 @@ unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} impl RWLock { + #[promotable_const_fn] pub const fn new() -> RWLock { RWLock { lock: UnsafeCell::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)), diff --git a/src/libstd/sys/redox/condvar.rs b/src/libstd/sys/redox/condvar.rs index 2a611ed7dabbe..7f9c3763e2dba 100644 --- a/src/libstd/sys/redox/condvar.rs +++ b/src/libstd/sys/redox/condvar.rs @@ -22,6 +22,7 @@ pub struct Condvar { } impl Condvar { + #[promotable_const_fn] pub const fn new() -> Condvar { Condvar { lock: UnsafeCell::new(ptr::null_mut()), diff --git a/src/libstd/sys/redox/fast_thread_local.rs b/src/libstd/sys/redox/fast_thread_local.rs index 6a007e98827b6..2aded05a9f59b 100644 --- a/src/libstd/sys/redox/fast_thread_local.rs +++ b/src/libstd/sys/redox/fast_thread_local.rs @@ -28,6 +28,7 @@ pub struct Key { unsafe impl ::marker::Sync for Key { } impl Key { + #[promotable_const_fn] pub const fn new() -> Key { Key { inner: UnsafeCell::new(None), diff --git a/src/libstd/sys/redox/mutex.rs b/src/libstd/sys/redox/mutex.rs index a995f597fc46a..1456f7a924bbf 100644 --- a/src/libstd/sys/redox/mutex.rs +++ b/src/libstd/sys/redox/mutex.rs @@ -61,6 +61,7 @@ pub struct Mutex { impl Mutex { /// Create a new mutex. + #[promotable_const_fn] pub const fn new() -> Self { Mutex { lock: UnsafeCell::new(0), @@ -107,6 +108,7 @@ pub struct ReentrantMutex { } impl ReentrantMutex { + #[promotable_const_fn] pub const fn uninitialized() -> Self { ReentrantMutex { lock: UnsafeCell::new(0), diff --git a/src/libstd/sys/redox/rwlock.rs b/src/libstd/sys/redox/rwlock.rs index d74b614ba47de..c565a8db6ad0c 100644 --- a/src/libstd/sys/redox/rwlock.rs +++ b/src/libstd/sys/redox/rwlock.rs @@ -18,6 +18,7 @@ unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} impl RWLock { + #[promotable_const_fn] pub const fn new() -> RWLock { RWLock { mutex: Mutex::new() diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs index 4f878d8ad1fa8..8d63869868e08 100644 --- a/src/libstd/sys/unix/condvar.rs +++ b/src/libstd/sys/unix/condvar.rs @@ -32,6 +32,7 @@ fn saturating_cast_to_time_t(value: u64) -> libc::time_t { } impl Condvar { + #[promotable_const_fn] pub const fn new() -> Condvar { // Might be moved and address is changing it is better to avoid // initialization of potentially opaque OS data before it landed diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs index 52cf3f97c5c83..577e3c88d07ba 100644 --- a/src/libstd/sys/unix/mutex.rs +++ b/src/libstd/sys/unix/mutex.rs @@ -24,6 +24,7 @@ unsafe impl Sync for Mutex {} #[allow(dead_code)] // sys isn't exported yet impl Mutex { + #[promotable_const_fn] pub const fn new() -> Mutex { // Might be moved and address is changing it is better to avoid // initialization of potentially opaque OS data before it landed diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs index c754d5b8359a9..932af81b81c91 100644 --- a/src/libstd/sys/unix/rwlock.rs +++ b/src/libstd/sys/unix/rwlock.rs @@ -22,6 +22,7 @@ unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} impl RWLock { + #[promotable_const_fn] pub const fn new() -> RWLock { RWLock { inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), diff --git a/src/libstd/sys/unix/weak.rs b/src/libstd/sys/unix/weak.rs index 18944be58ee7e..9cfea8a93df27 100644 --- a/src/libstd/sys/unix/weak.rs +++ b/src/libstd/sys/unix/weak.rs @@ -47,6 +47,7 @@ pub struct Weak { } impl Weak { + #[promotable_const_fn] pub const fn new(name: &'static str) -> Weak { Weak { name, diff --git a/src/libstd/sys/wasm/condvar.rs b/src/libstd/sys/wasm/condvar.rs index afa7afeef5988..d21bf28d4d652 100644 --- a/src/libstd/sys/wasm/condvar.rs +++ b/src/libstd/sys/wasm/condvar.rs @@ -14,6 +14,7 @@ use time::Duration; pub struct Condvar { } impl Condvar { + #[promotable_const_fn] pub const fn new() -> Condvar { Condvar { } } diff --git a/src/libstd/sys/wasm/mutex.rs b/src/libstd/sys/wasm/mutex.rs index 4197bdcc80839..bd1eaa73fdfc8 100644 --- a/src/libstd/sys/wasm/mutex.rs +++ b/src/libstd/sys/wasm/mutex.rs @@ -18,6 +18,7 @@ unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} // no threads on wasm impl Mutex { + #[promotable_const_fn] pub const fn new() -> Mutex { Mutex { locked: UnsafeCell::new(false) } } diff --git a/src/libstd/sys/wasm/rwlock.rs b/src/libstd/sys/wasm/rwlock.rs index 6516010af4759..42a42500314e2 100644 --- a/src/libstd/sys/wasm/rwlock.rs +++ b/src/libstd/sys/wasm/rwlock.rs @@ -18,6 +18,7 @@ unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} // no threads on wasm impl RWLock { + #[promotable_const_fn] pub const fn new() -> RWLock { RWLock { mode: UnsafeCell::new(0), diff --git a/src/libstd/sys/windows/condvar.rs b/src/libstd/sys/windows/condvar.rs index d708b327c55cb..de5fb3dc48ea0 100644 --- a/src/libstd/sys/windows/condvar.rs +++ b/src/libstd/sys/windows/condvar.rs @@ -20,6 +20,7 @@ unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} impl Condvar { + #[promotable_const_fn] pub const fn new() -> Condvar { Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) } } diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs index 9bf9f749d4df2..ce9da02b1c9f1 100644 --- a/src/libstd/sys/windows/mutex.rs +++ b/src/libstd/sys/windows/mutex.rs @@ -56,6 +56,7 @@ pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { } impl Mutex { + #[promotable_const_fn] pub const fn new() -> Mutex { Mutex { lock: AtomicUsize::new(0), diff --git a/src/libstd/sys/windows/rwlock.rs b/src/libstd/sys/windows/rwlock.rs index 3e81ebfcedfb9..057c1975039c2 100644 --- a/src/libstd/sys/windows/rwlock.rs +++ b/src/libstd/sys/windows/rwlock.rs @@ -17,6 +17,7 @@ unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} impl RWLock { + #[promotable_const_fn] pub const fn new() -> RWLock { RWLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } } diff --git a/src/libstd/sys_common/condvar.rs b/src/libstd/sys_common/condvar.rs index b6f29dd5fc3d3..a4cb755e27192 100644 --- a/src/libstd/sys_common/condvar.rs +++ b/src/libstd/sys_common/condvar.rs @@ -25,6 +25,7 @@ impl Condvar { /// /// Behavior is undefined if the condition variable is moved after it is /// first used with any of the functions below. + #[promotable_const_fn] pub const fn new() -> Condvar { Condvar(imp::Condvar::new()) } /// Prepares the condition variable for use. diff --git a/src/libstd/sys_common/mutex.rs b/src/libstd/sys_common/mutex.rs index d1a738770d389..5a920434d8a7e 100644 --- a/src/libstd/sys_common/mutex.rs +++ b/src/libstd/sys_common/mutex.rs @@ -24,6 +24,7 @@ impl Mutex { /// /// Behavior is undefined if the mutex is moved after it is /// first used with any of the functions below. + #[promotable_const_fn] pub const fn new() -> Mutex { Mutex(imp::Mutex::new()) } /// Prepare the mutex for use. diff --git a/src/libstd/sys_common/poison.rs b/src/libstd/sys_common/poison.rs index e74c40ae04b5d..0546767b52785 100644 --- a/src/libstd/sys_common/poison.rs +++ b/src/libstd/sys_common/poison.rs @@ -27,6 +27,7 @@ pub struct Flag { failed: AtomicBool } // all cases. impl Flag { + #[promotable_const_fn] pub const fn new() -> Flag { Flag { failed: AtomicBool::new(false) } } diff --git a/src/libstd/sys_common/rwlock.rs b/src/libstd/sys_common/rwlock.rs index 71a4f01ec4cab..fb3107442d111 100644 --- a/src/libstd/sys_common/rwlock.rs +++ b/src/libstd/sys_common/rwlock.rs @@ -22,6 +22,7 @@ impl RWLock { /// /// Behavior is undefined if the reader-writer lock is moved after it is /// first used with any of the functions below. + #[promotable_const_fn] pub const fn new() -> RWLock { RWLock(imp::RWLock::new()) } /// Acquires shared access to the underlying lock, blocking the current diff --git a/src/libstd/sys_common/thread_local.rs b/src/libstd/sys_common/thread_local.rs index d0d6224de0a15..5ffd48bb669b5 100644 --- a/src/libstd/sys_common/thread_local.rs +++ b/src/libstd/sys_common/thread_local.rs @@ -125,6 +125,7 @@ pub struct Key { pub const INIT: StaticKey = StaticKey::new(None); impl StaticKey { + #[promotable_const_fn] pub const fn new(dtor: Option) -> StaticKey { StaticKey { key: atomic::AtomicUsize::new(0), diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 40d3280baa687..2febbecff52e1 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -320,6 +320,7 @@ pub mod statik { } impl Key { + #[promotable_const_fn] pub const fn new() -> Key { Key { inner: UnsafeCell::new(None), @@ -357,6 +358,7 @@ pub mod fast { } impl Key { + #[promotable_const_fn] pub const fn new() -> Key { Key { inner: UnsafeCell::new(None), @@ -432,6 +434,7 @@ pub mod os { } impl Key { + #[promotable_const_fn] pub const fn new() -> Key { Key { os: OsStaticKey::new(Some(destroy_value::)), diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index 9775a6475ccd6..335419829c97b 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -366,6 +366,8 @@ register_diagnostics! { E0584, // file for module `..` found at both .. and .. E0629, // missing 'feature' (rustc_const_unstable) E0630, // rustc_const_unstable attribute must be paired with stable/unstable attribute + E0635, // multiple #[promotable_const_fn] + E0636, // promotable_const_fn attribute must be paired with stable/unstable attribute E0693, // incorrect `repr(align)` attribute format E0694, // an unknown tool name found in scoped attributes } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1535e6495067b..b2d1acdae87af 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -145,6 +145,7 @@ declare_features! ( // rustc internal (active, rustc_diagnostic_macros, "1.0.0", None, None), (active, rustc_const_unstable, "1.0.0", None, None), + (active, promotable_const_fn, "1.0.0", None, None), (active, box_syntax, "1.0.0", Some(49733), None), (active, unboxed_closures, "1.0.0", Some(29625), None), @@ -777,6 +778,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG is an internal feature", cfg_fn!(rustc_const_unstable))), ("global_allocator", Normal, Ungated), + ("promotable_const_fn", Whitelisted, Gated(Stability::Unstable, + "promotable_const_fn", + "the `#[promotable_const_fn]` attribute \ + is an internal feature", + cfg_fn!(promotable_const_fn))), ("default_lib_allocator", Whitelisted, Gated(Stability::Unstable, "allocator_internals", "the `#[default_lib_allocator]` \ diff --git a/src/stdsimd b/src/stdsimd index a19ca1cd91cf9..c774e38ce71b2 160000 --- a/src/stdsimd +++ b/src/stdsimd @@ -1 +1 @@ -Subproject commit a19ca1cd91cf97777af8268a6136bd2e4648e189 +Subproject commit c774e38ce71b21d70748f6cc945e6fd6b432f43d diff --git a/src/test/compile-fail/rustc-args-required-const.rs b/src/test/compile-fail/rustc-args-required-const.rs index aac9299eaafb9..0323467e76f3e 100644 --- a/src/test/compile-fail/rustc-args-required-const.rs +++ b/src/test/compile-fail/rustc-args-required-const.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(attr_literals, rustc_attrs, const_fn)] +#![feature(attr_literals, rustc_attrs, const_fn, promotable_const_fn)] #[rustc_args_required_const(0)] fn foo(_a: i32) { @@ -20,6 +20,7 @@ fn bar(_a: i32, _b: i32) { const A: i32 = 3; +#[promotable_const_fn] const fn baz() -> i32 { 3 } diff --git a/src/test/run-pass/mir_heavy_promoted.rs b/src/test/run-pass/mir_heavy_promoted.rs index b50852175776c..bb67ac74c8911 100644 --- a/src/test/run-pass/mir_heavy_promoted.rs +++ b/src/test/run-pass/mir_heavy_promoted.rs @@ -16,5 +16,8 @@ const TEST_DATA: [u8; 32 * 1024 * 1024] = [42; 32 * 1024 * 1024]; // leave an alloca from an unused temp behind, which, // without optimizations, can still blow the stack. fn main() { - println!("{}", TEST_DATA.len()); + // use intermediate variable, otherwise the entire `TEST_DATA.len()` call could get promoted + // resulting in just a `usize` promoted + let promoted: &'static [u8; 32 * 1024 * 1024] = &TEST_DATA; + println!("{}", promoted.len()); } diff --git a/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr b/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr index 684fa1c997bf1..c3ec7825fff05 100644 --- a/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr +++ b/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr @@ -11,6 +11,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 | //~^ ERROR does not live long enough LL | } | - temporary value only lives until here | diff --git a/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs b/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs index a590e569947f8..a371d5485e146 100644 --- a/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs +++ b/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs @@ -31,4 +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(); + //~^ ERROR does not live long enough } diff --git a/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr b/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr index 7963cbb4e4569..b3d7ba3e5c47a 100644 --- a/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr +++ b/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr @@ -21,12 +21,23 @@ error[E0597]: borrowed value does not live long enough | LL | let _: &'static u32 = &meh(); //~ 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/dont_promote_unstable_const_fn.rs:33:26 + | LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough +LL | //~^ ERROR 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 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/const-eval/promote-const-fn.rs b/src/test/ui/const-eval/promote-const-fn.rs new file mode 100644 index 0000000000000..2fbb44af57436 --- /dev/null +++ b/src/test/ui/const-eval/promote-const-fn.rs @@ -0,0 +1,35 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// gate-test-promotable_const_fn + +#![feature(const_fn, promotable_const_fn)] + +const fn foo() {} + +#[promotable_const_fn] +const fn bar() {} + +union Foo { + a: &'static u32, + b: usize, +} + +#[promotable_const_fn] +const fn boo() -> bool { + unsafe { + Foo { a: &1 }.b == 42 //~ ERROR promotable constant function contains + } +} + +fn main() { + let x: &'static () = &foo(); //~ borrowed value does not live long enough + let x: &'static () = &bar(); +} diff --git a/src/test/ui/const-eval/promote-const-fn.stderr b/src/test/ui/const-eval/promote-const-fn.stderr new file mode 100644 index 0000000000000..62d56997ca9c6 --- /dev/null +++ b/src/test/ui/const-eval/promote-const-fn.stderr @@ -0,0 +1,21 @@ +error[E0019]: promotable constant function contains unimplemented expression type + --> $DIR/promote-const-fn.rs:28:9 + | +LL | Foo { a: &1 }.b == 42 //~ ERROR promotable constant function contains + | ^^^^^^^^^^^^^^^ + +error[E0597]: borrowed value does not live long enough + --> $DIR/promote-const-fn.rs:33:27 + | +LL | let x: &'static () = &foo(); //~ borrowed value does not live long enough + | ^^^^^ temporary value does not live long enough +LL | let x: &'static () = &bar(); +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... + +error: aborting due to 2 previous errors + +Some errors occurred: E0019, E0597. +For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/const-eval/promoted_const_fn_fail.rs b/src/test/ui/const-eval/promoted_const_fn_fail.rs index 4888ed6e8dc7b..fd067abd9685e 100644 --- a/src/test/ui/const-eval/promoted_const_fn_fail.rs +++ b/src/test/ui/const-eval/promoted_const_fn_fail.rs @@ -8,7 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_fn)] +#![unstable(feature = "humans", + reason = "who ever let humans program computers, + we're apparently really bad at it", + issue = "0")] + +#![feature(const_fn, promotable_const_fn, staged_api)] #![deny(const_err)] @@ -17,21 +22,19 @@ union Bar { b: usize, } +#[stable(feature="zing", since="1.0.0")] +#[promotable_const_fn] const fn bar() -> u8 { unsafe { // this will error as long as this test // is run on a system whose pointers need more // than 8 bits Bar { a: &42 }.b as u8 - //~^ ERROR this expression will panic at runtime - //~| ERROR this expression will panic at runtime + //~^ ERROR promotable constant function } } fn main() { - // FIXME(oli-obk): this should compile but panic at runtime - // if we change the `const_err` lint to allow this will actually compile, but then - // continue with undefined values. let x: &'static u8 = &(bar() + 1); let y = *x; unreachable!(); diff --git a/src/test/ui/const-eval/promoted_const_fn_fail.stderr b/src/test/ui/const-eval/promoted_const_fn_fail.stderr index d805e1a27c993..030a7473746d1 100644 --- a/src/test/ui/const-eval/promoted_const_fn_fail.stderr +++ b/src/test/ui/const-eval/promoted_const_fn_fail.stderr @@ -1,20 +1,9 @@ -error: this expression will panic at runtime - --> $DIR/promoted_const_fn_fail.rs:25:9 +error[E0019]: promotable constant function contains unimplemented expression type + --> $DIR/promoted_const_fn_fail.rs:32:9 | LL | Bar { a: &42 }.b as u8 - | ^^^^^^^^^^^^^^^^^^^^^^ a raw memory access tried to access part of a pointer value as raw bytes - | -note: lint level defined here - --> $DIR/promoted_const_fn_fail.rs:13:9 - | -LL | #![deny(const_err)] - | ^^^^^^^^^ - -error: this expression will panic at runtime - --> $DIR/promoted_const_fn_fail.rs:25:9 - | -LL | Bar { a: &42 }.b as u8 - | ^^^^^^^^^^^^^^^^^^^^^^ a raw memory access tried to access part of a pointer value as raw bytes + | ^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to previous error +For more information about this error, try `rustc --explain E0019`.