Skip to content

Commit 4d187ae

Browse files
Rollup merge of #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

src/librustc/ty/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub mod binding;
9494
pub mod cast;
9595
#[macro_use]
9696
pub mod codec;
97+
mod constness;
9798
pub mod error;
9899
mod erase_regions;
99100
pub mod fast_reject;
@@ -3034,6 +3035,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
30343035
erase_regions::provide(providers);
30353036
layout::provide(providers);
30363037
util::provide(providers);
3038+
constness::provide(providers);
30373039
*providers = ty::query::Providers {
30383040
associated_item,
30393041
associated_item_def_ids,

src/librustc/ty/query/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_object_safe<'tcx> {
434434
}
435435
}
436436

437-
impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn<'tcx> {
437+
impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn_raw<'tcx> {
438438
fn describe(tcx: TyCtxt, def_id: DefId) -> String {
439439
format!("checking if item is const fn: `{}`", tcx.item_path_str(def_id))
440440
}

src/librustc/ty/query/mod.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,23 @@ define_queries! { <'tcx>
160160
DefId
161161
) -> Result<DtorckConstraint<'tcx>, NoSolution>,
162162

163-
/// True if this is a const fn
164-
[] fn is_const_fn: IsConstFn(DefId) -> bool,
163+
/// True if this is a const fn, use the `is_const_fn` to know whether your crate actually
164+
/// sees it as const fn (e.g. the const-fn-ness might be unstable and you might not have
165+
/// the feature gate active)
166+
///
167+
/// DO NOT CALL MANUALLY, it is only meant to cache the base data for the `is_const_fn`
168+
/// function
169+
[] fn is_const_fn_raw: IsConstFn(DefId) -> bool,
170+
171+
172+
/// Returns true if calls to the function may be promoted
173+
///
174+
/// This is either because the function is e.g. a tuple-struct or tuple-variant constructor,
175+
/// or because it has the `#[rustc_promotable]` attribute. The attribute should be removed
176+
/// in the future in favour of some form of check which figures out whether the function
177+
/// does not inspect the bits of any of its arguments (so is essentially just a constructor
178+
/// function)
179+
[] fn is_promotable_const_fn: IsPromotableConstFn(DefId) -> bool,
165180

166181
/// True if this is a foreign item (i.e., linked via `extern { ... }`).
167182
[] fn is_foreign_item: IsForeignItem(DefId) -> bool,

src/librustc/ty/query/plumbing.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,8 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
11251125
DepKind::FnSignature => { force!(fn_sig, def_id!()); }
11261126
DepKind::CoerceUnsizedInfo => { force!(coerce_unsized_info, def_id!()); }
11271127
DepKind::ItemVariances => { force!(variances_of, def_id!()); }
1128-
DepKind::IsConstFn => { force!(is_const_fn, def_id!()); }
1128+
DepKind::IsConstFn => { force!(is_const_fn_raw, def_id!()); }
1129+
DepKind::IsPromotableConstFn => { force!(is_promotable_const_fn, def_id!()); }
11291130
DepKind::IsForeignItem => { force!(is_foreign_item, def_id!()); }
11301131
DepKind::SizedConstraint => { force!(adt_sized_constraint, def_id!()); }
11311132
DepKind::DtorckConstraint => { force!(adt_dtorck_constraint, def_id!()); }

src/librustc_metadata/cstore_impl.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use rustc::ty::{self, TyCtxt};
2626
use rustc::ty::query::Providers;
2727
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
2828
use rustc::hir::map::{DefKey, DefPath, DefPathHash};
29-
use rustc::hir::map::blocks::FnLikeNode;
3029
use rustc::hir::map::definitions::DefPathTable;
3130
use rustc::util::nodemap::DefIdMap;
3231
use rustc_data_structures::svh::Svh;
@@ -43,7 +42,6 @@ use syntax::parse::source_file_to_stream;
4342
use syntax::symbol::Symbol;
4443
use syntax_pos::{Span, NO_EXPANSION, FileName};
4544
use rustc_data_structures::indexed_set::IdxSet;
46-
use rustc::hir;
4745

4846
macro_rules! provide {
4947
(<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
@@ -145,7 +143,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
145143
}
146144
fn_sig => { cdata.fn_sig(def_id.index, tcx) }
147145
inherent_impls => { Lrc::new(cdata.get_inherent_implementations_for_type(def_id.index)) }
148-
is_const_fn => { cdata.is_const_fn(def_id.index) }
146+
is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }
149147
is_foreign_item => { cdata.is_foreign_item(def_id.index) }
150148
describe_def => { cdata.get_def(def_id.index) }
151149
def_span => { cdata.get_span(def_id.index, &tcx.sess) }
@@ -270,22 +268,10 @@ provide! { <'tcx> tcx, def_id, other, cdata,
270268
}
271269

272270
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
273-
fn is_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
274-
let node_id = tcx.hir.as_local_node_id(def_id)
275-
.expect("Non-local call to local provider is_const_fn");
276-
277-
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
278-
fn_like.constness() == hir::Constness::Const
279-
} else {
280-
false
281-
}
282-
}
283-
284271
// FIXME(#44234) - almost all of these queries have no sub-queries and
285272
// therefore no actual inputs, they're just reading tables calculated in
286273
// resolve! Does this work? Unsure! That's what the issue is about
287274
*providers = Providers {
288-
is_const_fn,
289275
is_dllimport_foreign_item: |tcx, id| {
290276
tcx.native_library_kind(id) == Some(NativeLibraryKind::NativeUnknown)
291277
},

src/librustc_metadata/decoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ impl<'a, 'tcx> CrateMetadata {
10771077
}
10781078
}
10791079

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

0 commit comments

Comments
 (0)