Skip to content

Commit 7fa0465

Browse files
committed
Auto merge of #66275 - oli-obk:organize-intrinsics-promotion-checks, r=RalfJung
Organize intrinsics promotion checks cc @vertexclique supersedes #61835 r? @RalfJung
2 parents 5f1d6c4 + 5cef094 commit 7fa0465

16 files changed

+248
-167
lines changed

src/libcore/intrinsics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,7 @@ extern "rust-intrinsic" {
939939
/// }
940940
/// ```
941941
#[stable(feature = "rust1", since = "1.0.0")]
942+
#[rustc_const_unstable(feature = "const_transmute")]
942943
pub fn transmute<T, U>(e: T) -> U;
943944

944945
/// Returns `true` if the actual type given as `T` requires drop

src/librustc/ty/constness.rs

+112-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use crate::ty::query::Providers;
22
use crate::hir::def_id::DefId;
33
use crate::hir;
44
use crate::ty::TyCtxt;
5-
use syntax_pos::symbol::Symbol;
5+
use syntax_pos::symbol::{sym, Symbol};
6+
use rustc_target::spec::abi::Abi;
67
use crate::hir::map::blocks::FnLikeNode;
78
use syntax::attr;
89

@@ -35,12 +36,51 @@ impl<'tcx> TyCtxt<'tcx> {
3536
}
3637
}
3738

39+
/// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
40+
/// for being called from stable `const fn`s (`min_const_fn`).
41+
///
42+
/// Adding more intrinsics requires sign-off from @rust-lang/lang.
43+
///
44+
/// This list differs from the list in `is_const_intrinsic` in the sense that any item on this
45+
/// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from
46+
/// stable, it must be callable at all.
47+
fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool {
48+
match self.item_name(def_id) {
49+
| sym::size_of
50+
| sym::min_align_of
51+
| sym::needs_drop
52+
// Arithmetic:
53+
| sym::add_with_overflow // ~> .overflowing_add
54+
| sym::sub_with_overflow // ~> .overflowing_sub
55+
| sym::mul_with_overflow // ~> .overflowing_mul
56+
| sym::wrapping_add // ~> .wrapping_add
57+
| sym::wrapping_sub // ~> .wrapping_sub
58+
| sym::wrapping_mul // ~> .wrapping_mul
59+
| sym::saturating_add // ~> .saturating_add
60+
| sym::saturating_sub // ~> .saturating_sub
61+
| sym::unchecked_shl // ~> .wrapping_shl
62+
| sym::unchecked_shr // ~> .wrapping_shr
63+
| sym::rotate_left // ~> .rotate_left
64+
| sym::rotate_right // ~> .rotate_right
65+
| sym::ctpop // ~> .count_ones
66+
| sym::ctlz // ~> .leading_zeros
67+
| sym::cttz // ~> .trailing_zeros
68+
| sym::bswap // ~> .swap_bytes
69+
| sym::bitreverse // ~> .reverse_bits
70+
=> true,
71+
_ => false,
72+
}
73+
}
74+
3875
/// Returns `true` if this function must conform to `min_const_fn`
3976
pub fn is_min_const_fn(self, def_id: DefId) -> bool {
4077
// Bail out if the signature doesn't contain `const`
4178
if !self.is_const_fn_raw(def_id) {
4279
return false;
4380
}
81+
if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
82+
return self.is_intrinsic_min_const_fn(def_id);
83+
}
4484

4585
if self.features().staged_api {
4686
// in order for a libstd function to be considered min_const_fn
@@ -63,13 +103,82 @@ impl<'tcx> TyCtxt<'tcx> {
63103

64104

65105
pub fn provide(providers: &mut Providers<'_>) {
66-
/// only checks whether the function has a `const` modifier
106+
/// Const evaluability whitelist is here to check evaluability at the
107+
/// top level beforehand.
108+
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
109+
match tcx.fn_sig(def_id).abi() {
110+
Abi::RustIntrinsic |
111+
Abi::PlatformIntrinsic => {
112+
// FIXME: deduplicate these two lists as much as possible
113+
match tcx.item_name(def_id) {
114+
// Keep this list in the same order as the match patterns in
115+
// `librustc_mir/interpret/intrinsics.rs`
116+
117+
// This whitelist is a list of intrinsics that have a miri-engine implementation
118+
// and can thus be called when enabling enough feature gates. The similar
119+
// whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing
120+
// the intrinsics to be called by stable const fns.
121+
| sym::caller_location
122+
123+
| sym::min_align_of
124+
| sym::pref_align_of
125+
| sym::needs_drop
126+
| sym::size_of
127+
| sym::type_id
128+
| sym::type_name
129+
130+
| sym::ctpop
131+
| sym::cttz
132+
| sym::cttz_nonzero
133+
| sym::ctlz
134+
| sym::ctlz_nonzero
135+
| sym::bswap
136+
| sym::bitreverse
137+
138+
| sym::wrapping_add
139+
| sym::wrapping_sub
140+
| sym::wrapping_mul
141+
| sym::add_with_overflow
142+
| sym::sub_with_overflow
143+
| sym::mul_with_overflow
144+
145+
| sym::saturating_add
146+
| sym::saturating_sub
147+
148+
| sym::unchecked_shl
149+
| sym::unchecked_shr
150+
151+
| sym::rotate_left
152+
| sym::rotate_right
153+
154+
| sym::ptr_offset_from
155+
156+
| sym::transmute
157+
158+
| sym::simd_insert
159+
160+
| sym::simd_extract
161+
162+
=> Some(true),
163+
164+
_ => Some(false)
165+
}
166+
}
167+
_ => None
168+
}
169+
}
170+
171+
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
172+
/// said intrinsic is on the whitelist for being const callable.
67173
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
68174
let hir_id = tcx.hir().as_local_hir_id(def_id)
69175
.expect("Non-local call to local provider is_const_fn");
70176

71177
let node = tcx.hir().get(hir_id);
72-
if let Some(fn_like) = FnLikeNode::from_node(node) {
178+
179+
if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
180+
whitelisted
181+
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
73182
fn_like.constness() == hir::Constness::Const
74183
} else if let hir::Node::Ctor(_) = node {
75184
true

src/librustc_feature/active.rs

-4
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,6 @@ declare_features! (
408408
/// Allows using `#[doc(keyword = "...")]`.
409409
(active, doc_keyword, "1.28.0", Some(51315), None),
410410

411-
/// Allows reinterpretation of the bits of a value of one type as another
412-
/// type during const eval.
413-
(active, const_transmute, "1.29.0", Some(53605), None),
414-
415411
/// Allows using `try {...}` expressions.
416412
(active, try_blocks, "1.29.0", Some(31436), None),
417413

src/librustc_metadata/rmeta/decoder.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1360,10 +1360,16 @@ impl<'a, 'tcx> CrateMetadata {
13601360
}
13611361
}
13621362

1363+
// This replicates some of the logic of the crate-local `is_const_fn_raw` query, because we
1364+
// don't serialize constness for tuple variant and tuple struct constructors.
13631365
fn is_const_fn_raw(&self, id: DefIndex) -> bool {
13641366
let constness = match self.kind(id) {
13651367
EntryKind::Method(data) => data.decode(self).fn_data.constness,
13661368
EntryKind::Fn(data) => data.decode(self).constness,
1369+
// Some intrinsics can be const fn. While we could recompute this (at least until we
1370+
// stop having hardcoded whitelists and move to stability attributes), it seems cleaner
1371+
// to treat all const fns equally.
1372+
EntryKind::ForeignFn(data) => data.decode(self).constness,
13671373
EntryKind::Variant(..) | EntryKind::Struct(..) => hir::Constness::Const,
13681374
_ => hir::Constness::NotConst,
13691375
};

src/librustc_metadata/rmeta/encoder.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,11 @@ impl EncodeContext<'tcx> {
15251525
hir::ForeignItemKind::Fn(_, ref names, _) => {
15261526
let data = FnData {
15271527
asyncness: hir::IsAsync::NotAsync,
1528-
constness: hir::Constness::NotConst,
1528+
constness: if self.tcx.is_const_fn_raw(def_id) {
1529+
hir::Constness::Const
1530+
} else {
1531+
hir::Constness::NotConst
1532+
},
15291533
param_names: self.encode_fn_param_names(names),
15301534
};
15311535
EntryKind::ForeignFn(self.lazy(data))

0 commit comments

Comments
 (0)