Skip to content

Commit 088fc73

Browse files
committed
Auto 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 c67ea54 + c793391 commit 088fc73

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+484
-335
lines changed

src/libcore/mem.rs

+2
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ pub fn forget<T>(t: T) {
285285
/// [alignment]: ./fn.align_of.html
286286
#[inline]
287287
#[stable(feature = "rust1", since = "1.0.0")]
288+
#[cfg_attr(not(stage0), rustc_promotable)]
288289
pub const fn size_of<T>() -> usize {
289290
intrinsics::size_of::<T>()
290291
}
@@ -376,6 +377,7 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
376377
/// ```
377378
#[inline]
378379
#[stable(feature = "rust1", since = "1.0.0")]
380+
#[cfg_attr(not(stage0), rustc_promotable)]
379381
pub const fn align_of<T>() -> usize {
380382
intrinsics::min_align_of::<T>()
381383
}

src/libcore/num/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ $EndFeature, "
216216
```"),
217217
#[stable(feature = "rust1", since = "1.0.0")]
218218
#[inline]
219+
#[cfg_attr(not(stage0), rustc_promotable)]
219220
pub const fn min_value() -> Self {
220221
!0 ^ ((!0 as $UnsignedT) >> 1) as Self
221222
}
@@ -234,6 +235,7 @@ $EndFeature, "
234235
```"),
235236
#[stable(feature = "rust1", since = "1.0.0")]
236237
#[inline]
238+
#[cfg_attr(not(stage0), rustc_promotable)]
237239
pub const fn max_value() -> Self {
238240
!Self::min_value()
239241
}

src/libcore/ops/range.rs

+1
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

+2
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
209209
/// ```
210210
#[inline]
211211
#[stable(feature = "rust1", since = "1.0.0")]
212+
#[cfg_attr(not(stage0), rustc_promotable)]
212213
pub const fn null<T>() -> *const T { 0 as *const T }
213214

214215
/// Creates a null mutable raw pointer.
@@ -223,6 +224,7 @@ pub const fn null<T>() -> *const T { 0 as *const T }
223224
/// ```
224225
#[inline]
225226
#[stable(feature = "rust1", since = "1.0.0")]
227+
#[cfg_attr(not(stage0), rustc_promotable)]
226228
pub const fn null_mut<T>() -> *mut T { 0 as *mut T }
227229

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

src/libcore/time.rs

+4
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ impl Duration {
109109
/// ```
110110
#[stable(feature = "duration", since = "1.3.0")]
111111
#[inline]
112+
#[cfg_attr(not(stage0), rustc_promotable)]
112113
pub const fn from_secs(secs: u64) -> Duration {
113114
Duration { secs, nanos: 0 }
114115
}
@@ -127,6 +128,7 @@ impl Duration {
127128
/// ```
128129
#[stable(feature = "duration", since = "1.3.0")]
129130
#[inline]
131+
#[cfg_attr(not(stage0), rustc_promotable)]
130132
pub const fn from_millis(millis: u64) -> Duration {
131133
Duration {
132134
secs: millis / MILLIS_PER_SEC,
@@ -148,6 +150,7 @@ impl Duration {
148150
/// ```
149151
#[stable(feature = "duration_from_micros", since = "1.27.0")]
150152
#[inline]
153+
#[cfg_attr(not(stage0), rustc_promotable)]
151154
pub const fn from_micros(micros: u64) -> Duration {
152155
Duration {
153156
secs: micros / MICROS_PER_SEC,
@@ -169,6 +172,7 @@ impl Duration {
169172
/// ```
170173
#[stable(feature = "duration_extras", since = "1.27.0")]
171174
#[inline]
175+
#[cfg_attr(not(stage0), rustc_promotable)]
172176
pub const fn from_nanos(nanos: u64) -> Duration {
173177
Duration {
174178
secs: nanos / (NANOS_PER_SEC as u64),

src/librustc/dep_graph/dep_node.rs

+1
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

+1
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

+1
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

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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+
// needs 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+
| "needs_drop"
52+
=> return true,
53+
_ => {},
54+
}
55+
}
56+
// in order for a libstd function to be considered min_const_fn
57+
// it needs to be stable and have no `rustc_const_unstable` attribute
58+
match self.lookup_stability(def_id) {
59+
// stable functions with unstable const fn aren't `min_const_fn`
60+
Some(&attr::Stability { const_stability: Some(_), .. }) => false,
61+
// unstable functions don't need to conform
62+
Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
63+
// everything else needs to conform, because it would be callable from
64+
// other `min_const_fn` functions
65+
_ => true,
66+
}
67+
} else {
68+
// users enabling the `const_fn` can do what they want
69+
!self.sess.features_untracked().const_fn
70+
}
71+
}
72+
}
73+
74+
75+
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
76+
/// only checks whether the function has a `const` modifier
77+
fn is_const_fn_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
78+
let node_id = tcx.hir.as_local_node_id(def_id)
79+
.expect("Non-local call to local provider is_const_fn");
80+
81+
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
82+
fn_like.constness() == hir::Constness::Const
83+
} else {
84+
false
85+
}
86+
}
87+
88+
fn is_promotable_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
89+
tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
90+
Some(stab) => {
91+
if cfg!(debug_assertions) && stab.promotable {
92+
let sig = tcx.fn_sig(def_id);
93+
assert_eq!(
94+
sig.unsafety(),
95+
hir::Unsafety::Normal,
96+
"don't mark const unsafe fns as promotable",
97+
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
98+
);
99+
}
100+
stab.promotable
101+
},
102+
None => false,
103+
}
104+
}
105+
106+
*providers = Providers {
107+
is_const_fn_raw,
108+
is_promotable_const_fn,
109+
..*providers
110+
};
111+
}

src/librustc/ty/context.rs

-32
Original file line numberDiff line numberDiff line change
@@ -1134,38 +1134,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
11341134
local as usize == global as usize
11351135
}
11361136

1137-
/// Returns true if this function must conform to `min_const_fn`
1138-
pub fn is_min_const_fn(self, def_id: DefId) -> bool {
1139-
if self.features().staged_api {
1140-
// some intrinsics are waved through if called inside the
1141-
// standard library. Users never need to call them directly
1142-
if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
1143-
assert!(!self.is_const_fn(def_id));
1144-
match &self.item_name(def_id).as_str()[..] {
1145-
| "size_of"
1146-
| "min_align_of"
1147-
| "needs_drop"
1148-
=> return true,
1149-
_ => {},
1150-
}
1151-
}
1152-
// in order for a libstd function to be considered min_const_fn
1153-
// it needs to be stable and have no `rustc_const_unstable` attribute
1154-
match self.lookup_stability(def_id) {
1155-
// stable functions with unstable const fn aren't `min_const_fn`
1156-
Some(&attr::Stability { const_stability: Some(_), .. }) => false,
1157-
// unstable functions don't need to conform
1158-
Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
1159-
// everything else needs to conform, because it would be callable from
1160-
// other `min_const_fn` functions
1161-
_ => true,
1162-
}
1163-
} else {
1164-
// users enabling the `const_fn` can do what they want
1165-
!self.sess.features_untracked().const_fn
1166-
}
1167-
}
1168-
11691137
/// Create a type context and call the closure with a `TyCtxt` reference
11701138
/// to the context. The closure enforces that the type context and any interned
11711139
/// value (types, substs, etc.) can only be used while `ty::tls` has a valid

src/librustc/ty/instance.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ impl<'tcx> InstanceDef<'tcx> {
109109
return true
110110
}
111111
let codegen_fn_attrs = tcx.codegen_fn_attrs(self.def_id());
112-
codegen_fn_attrs.requests_inline() || tcx.is_const_fn(self.def_id())
112+
// need to use `is_const_fn_raw` since we don't really care if the user can use it as a
113+
// const fn, just whether the function should be inlined
114+
codegen_fn_attrs.requests_inline() || tcx.is_const_fn_raw(self.def_id())
113115
}
114116
}
115117

src/librustc/ty/mod.rs

+2
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;
@@ -3056,6 +3057,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
30563057
erase_regions::provide(providers);
30573058
layout::provide(providers);
30583059
util::provide(providers);
3060+
constness::provide(providers);
30593061
*providers = ty::query::Providers {
30603062
associated_item,
30613063
associated_item_def_ids,

src/librustc/ty/query/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_object_safe<'tcx> {
437437
}
438438
}
439439

440-
impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn<'tcx> {
440+
impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn_raw<'tcx> {
441441
fn describe(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> String {
442442
format!("checking if item is const fn: `{}`", tcx.item_path_str(def_id))
443443
}

src/librustc/ty/query/mod.rs

+17-2
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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,8 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
11361136
DepKind::FnSignature => { force!(fn_sig, def_id!()); }
11371137
DepKind::CoerceUnsizedInfo => { force!(coerce_unsized_info, def_id!()); }
11381138
DepKind::ItemVariances => { force!(variances_of, def_id!()); }
1139-
DepKind::IsConstFn => { force!(is_const_fn, def_id!()); }
1139+
DepKind::IsConstFn => { force!(is_const_fn_raw, def_id!()); }
1140+
DepKind::IsPromotableConstFn => { force!(is_promotable_const_fn, def_id!()); }
11401141
DepKind::IsForeignItem => { force!(is_foreign_item, def_id!()); }
11411142
DepKind::SizedConstraint => { force!(adt_sized_constraint, def_id!()); }
11421143
DepKind::DtorckConstraint => { force!(adt_dtorck_constraint, def_id!()); }

src/librustc_metadata/cstore_impl.rs

+1-15
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::bit_set::BitSet;
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) }
@@ -264,22 +262,10 @@ provide! { <'tcx> tcx, def_id, other, cdata,
264262
}
265263

266264
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
267-
fn is_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
268-
let node_id = tcx.hir.as_local_node_id(def_id)
269-
.expect("Non-local call to local provider is_const_fn");
270-
271-
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
272-
fn_like.constness() == hir::Constness::Const
273-
} else {
274-
false
275-
}
276-
}
277-
278265
// FIXME(#44234) - almost all of these queries have no sub-queries and
279266
// therefore no actual inputs, they're just reading tables calculated in
280267
// resolve! Does this work? Unsure! That's what the issue is about
281268
*providers = Providers {
282-
is_const_fn,
283269
is_dllimport_foreign_item: |tcx, id| {
284270
tcx.native_library_kind(id) == Some(NativeLibraryKind::NativeUnknown)
285271
},

src/librustc_metadata/decoder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ impl<'a, 'tcx> CrateMetadata {
11141114
}
11151115
}
11161116

1117-
pub fn is_const_fn(&self, id: DefIndex) -> bool {
1117+
crate fn is_const_fn_raw(&self, id: DefIndex) -> bool {
11181118
let constness = match self.entry(id).kind {
11191119
EntryKind::Method(data) => data.decode(self).fn_data.constness,
11201120
EntryKind::Fn(data) => data.decode(self).constness,

0 commit comments

Comments
 (0)