Skip to content

Commit 4d187ae

Browse files
Rollup merge of rust-lang#53851 - oli-obk:local_promotion, r=eddyb
Limit the promotion of const fns to the libstd and the `rustc_promotable` attribute There are so many questions around promoting const fn calls... it seems saner to try to limit automatic promotion to const fns which were explicitly opted in for promotion. I added the attribute to all public stable const fns that were already promotable (e.g. not Cell::new) in order to not cause any breakage r? @eddyb cc @nikomatsakis
2 parents 968d95e + 9da9ca5 commit 4d187ae

40 files changed

+442
-241
lines changed

src/libcore/mem.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ pub fn forget<T>(t: T) {
286286
#[inline]
287287
#[stable(feature = "rust1", since = "1.0.0")]
288288
#[cfg(not(stage0))]
289+
#[rustc_promotable]
289290
pub const fn size_of<T>() -> usize {
290291
intrinsics::size_of::<T>()
291292
}
@@ -396,6 +397,7 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
396397
#[inline]
397398
#[stable(feature = "rust1", since = "1.0.0")]
398399
#[cfg(not(stage0))]
400+
#[rustc_promotable]
399401
pub const fn align_of<T>() -> usize {
400402
intrinsics::min_align_of::<T>()
401403
}

src/libcore/num/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ $EndFeature, "
206206
```"),
207207
#[stable(feature = "rust1", since = "1.0.0")]
208208
#[inline]
209+
#[cfg_attr(not(stage0), rustc_promotable)]
209210
pub const fn min_value() -> Self {
210211
!0 ^ ((!0 as $UnsignedT) >> 1) as Self
211212
}
@@ -224,6 +225,7 @@ $EndFeature, "
224225
```"),
225226
#[stable(feature = "rust1", since = "1.0.0")]
226227
#[inline]
228+
#[cfg_attr(not(stage0), rustc_promotable)]
227229
pub const fn max_value() -> Self {
228230
!Self::min_value()
229231
}

src/libcore/ops/range.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ impl<Idx> RangeInclusive<Idx> {
391391
/// ```
392392
#[stable(feature = "inclusive_range_methods", since = "1.27.0")]
393393
#[inline]
394+
#[cfg_attr(not(stage0), rustc_promotable)]
394395
pub const fn new(start: Idx, end: Idx) -> Self {
395396
Self { start, end, is_empty: None }
396397
}

src/libcore/ptr.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
7474
/// ```
7575
#[inline]
7676
#[stable(feature = "rust1", since = "1.0.0")]
77+
#[cfg_attr(not(stage0), rustc_promotable)]
7778
pub const fn null<T>() -> *const T { 0 as *const T }
7879

7980
/// Creates a null mutable raw pointer.
@@ -88,6 +89,7 @@ pub const fn null<T>() -> *const T { 0 as *const T }
8889
/// ```
8990
#[inline]
9091
#[stable(feature = "rust1", since = "1.0.0")]
92+
#[cfg_attr(not(stage0), rustc_promotable)]
9193
pub const fn null_mut<T>() -> *mut T { 0 as *mut T }
9294

9395
/// Swaps the values at two mutable locations of the same type, without

src/libcore/time.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ impl Duration {
108108
/// ```
109109
#[stable(feature = "duration", since = "1.3.0")]
110110
#[inline]
111+
#[cfg_attr(not(stage0), rustc_promotable)]
111112
pub const fn from_secs(secs: u64) -> Duration {
112113
Duration { secs, nanos: 0 }
113114
}
@@ -126,6 +127,7 @@ impl Duration {
126127
/// ```
127128
#[stable(feature = "duration", since = "1.3.0")]
128129
#[inline]
130+
#[cfg_attr(not(stage0), rustc_promotable)]
129131
pub const fn from_millis(millis: u64) -> Duration {
130132
Duration {
131133
secs: millis / MILLIS_PER_SEC,
@@ -147,6 +149,7 @@ impl Duration {
147149
/// ```
148150
#[stable(feature = "duration_from_micros", since = "1.27.0")]
149151
#[inline]
152+
#[cfg_attr(not(stage0), rustc_promotable)]
150153
pub const fn from_micros(micros: u64) -> Duration {
151154
Duration {
152155
secs: micros / MICROS_PER_SEC,
@@ -168,6 +171,7 @@ impl Duration {
168171
/// ```
169172
#[stable(feature = "duration_extras", since = "1.27.0")]
170173
#[inline]
174+
#[cfg_attr(not(stage0), rustc_promotable)]
171175
pub const fn from_nanos(nanos: u64) -> Duration {
172176
Duration {
173177
secs: nanos / (NANOS_PER_SEC as u64),

src/librustc/dep_graph/dep_node.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ define_dep_nodes!( <'tcx>
515515
[] ItemVarianceConstraints(DefId),
516516
[] ItemVariances(DefId),
517517
[] IsConstFn(DefId),
518+
[] IsPromotableConstFn(DefId),
518519
[] IsForeignItem(DefId),
519520
[] TypeParamPredicates { item_id: DefId, param_id: DefId },
520521
[] SizedConstraint(DefId),

src/librustc/ich/impls_syntax.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability {
130130
level,
131131
feature,
132132
rustc_depr,
133+
promotable,
133134
const_stability
134135
});
135136

src/librustc/middle/stability.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ impl<'a, 'tcx> Index<'tcx> {
441441
feature: Symbol::intern("rustc_private"),
442442
rustc_depr: None,
443443
const_stability: None,
444+
promotable: false,
444445
});
445446
annotator.parent_stab = Some(stability);
446447
}

src/librustc/ty/constness.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use ty::query::Providers;
2+
use hir::def_id::DefId;
3+
use hir;
4+
use ty::TyCtxt;
5+
use syntax_pos::symbol::Symbol;
6+
use hir::map::blocks::FnLikeNode;
7+
use syntax::attr;
8+
use rustc_target::spec::abi;
9+
10+
impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
11+
/// Whether the `def_id` counts as const fn in your current crate, considering all active
12+
/// feature gates
13+
pub fn is_const_fn(self, def_id: DefId) -> bool {
14+
self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
15+
Some(stab) => match stab.const_stability {
16+
// has a `rustc_const_unstable` attribute, check whether the user enabled the
17+
// corresponding feature gate
18+
Some(feature_name) => self.features()
19+
.declared_lib_features
20+
.iter()
21+
.any(|&(sym, _)| sym == feature_name),
22+
// the function has no stability attribute, it is stable as const fn or the user
23+
// nees to use feature gates to use the function at all
24+
None => true,
25+
},
26+
// functions without stability are either stable user written const fn or the user is
27+
// using feature gates and we thus don't care what they do
28+
None => true,
29+
}
30+
}
31+
32+
/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
33+
pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
34+
if self.is_const_fn_raw(def_id) {
35+
self.lookup_stability(def_id)?.const_stability
36+
} else {
37+
None
38+
}
39+
}
40+
41+
/// Returns true if this function must conform to `min_const_fn`
42+
pub fn is_min_const_fn(self, def_id: DefId) -> bool {
43+
if self.features().staged_api {
44+
// some intrinsics are waved through if called inside the
45+
// standard library. Users never need to call them directly
46+
if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
47+
assert!(!self.is_const_fn(def_id));
48+
match &self.item_name(def_id).as_str()[..] {
49+
| "size_of"
50+
| "min_align_of"
51+
=> return true,
52+
_ => {},
53+
}
54+
}
55+
// in order for a libstd function to be considered min_const_fn
56+
// it needs to be stable and have no `rustc_const_unstable` attribute
57+
match self.lookup_stability(def_id) {
58+
// stable functions with unstable const fn aren't `min_const_fn`
59+
Some(&attr::Stability { const_stability: Some(_), .. }) => false,
60+
// unstable functions don't need to conform
61+
Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
62+
// everything else needs to conform, because it would be callable from
63+
// other `min_const_fn` functions
64+
_ => true,
65+
}
66+
} else {
67+
// users enabling the `const_fn` can do what they want
68+
!self.sess.features_untracked().const_fn
69+
}
70+
}
71+
}
72+
73+
74+
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
75+
/// only checks whether the function has a `const` modifier
76+
fn is_const_fn_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
77+
let node_id = tcx.hir.as_local_node_id(def_id)
78+
.expect("Non-local call to local provider is_const_fn");
79+
80+
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
81+
fn_like.constness() == hir::Constness::Const
82+
} else {
83+
false
84+
}
85+
}
86+
87+
fn is_promotable_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
88+
tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
89+
Some(stab) => {
90+
if cfg!(debug_assertions) && stab.promotable {
91+
let sig = tcx.fn_sig(def_id);
92+
assert_eq!(
93+
sig.unsafety(),
94+
hir::Unsafety::Normal,
95+
"don't mark const unsafe fns as promotable",
96+
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
97+
);
98+
}
99+
stab.promotable
100+
},
101+
None => false,
102+
}
103+
}
104+
105+
*providers = Providers {
106+
is_const_fn_raw,
107+
is_promotable_const_fn,
108+
..*providers
109+
};
110+
}

src/librustc/ty/context.rs

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,37 +1099,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
10991099
local as usize == global as usize
11001100
}
11011101

1102-
/// Returns true if this function must conform to `min_const_fn`
1103-
pub fn is_min_const_fn(self, def_id: DefId) -> bool {
1104-
if self.features().staged_api {
1105-
// some intrinsics are waved through if called inside the
1106-
// standard library. Users never need to call them directly
1107-
if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
1108-
assert!(!self.is_const_fn(def_id));
1109-
match &self.item_name(def_id).as_str()[..] {
1110-
| "size_of"
1111-
| "min_align_of"
1112-
=> return true,
1113-
_ => {},
1114-
}
1115-
}
1116-
// in order for a libstd function to be considered min_const_fn
1117-
// it needs to be stable and have no `rustc_const_unstable` attribute
1118-
match self.lookup_stability(def_id) {
1119-
// stable functions with unstable const fn aren't `min_const_fn`
1120-
Some(&attr::Stability { const_stability: Some(_), .. }) => false,
1121-
// unstable functions don't need to conform
1122-
Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
1123-
// everything else needs to conform, because it would be callable from
1124-
// other `min_const_fn` functions
1125-
_ => true,
1126-
}
1127-
} else {
1128-
// users enabling the `const_fn` can do what they want
1129-
!self.sess.features_untracked().const_fn
1130-
}
1131-
}
1132-
11331102
/// Create a type context and call the closure with a `TyCtxt` reference
11341103
/// to the context. The closure enforces that the type context and any interned
11351104
/// value (types, substs, etc.) can only be used while `ty::tls` has a valid

0 commit comments

Comments
 (0)