Skip to content

Commit 9a428e1

Browse files
committed
Merge branch 'master' into feature/print-msg-from-elf-entrypoint
2 parents 3ee0f48 + aeaaf8f commit 9a428e1

24 files changed

+305
-198
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_data_structures/obligation_forest/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,16 @@ impl<O: ForestObligation> ObligationForest<O> {
395395
let mut errors = vec![];
396396
let mut stalled = true;
397397

398-
for index in 0..self.nodes.len() {
398+
// Note that the loop body can append new nodes, and those new nodes
399+
// will then be processed by subsequent iterations of the loop.
400+
//
401+
// We can't use an iterator for the loop because `self.nodes` is
402+
// appended to and the borrow checker would complain. We also can't use
403+
// `for index in 0..self.nodes.len() { ... }` because the range would
404+
// be computed with the initial length, and we would miss the appended
405+
// nodes. Therefore we use a `while` loop.
406+
let mut index = 0;
407+
while index < self.nodes.len() {
399408
let node = &mut self.nodes[index];
400409

401410
debug!("process_obligations: node {} == {:?}", index, node);
@@ -406,6 +415,7 @@ impl<O: ForestObligation> ObligationForest<O> {
406415
// out of sync with `nodes`. It's not very common, but it does
407416
// happen, and code in `compress` has to allow for it.
408417
if node.state.get() != NodeState::Pending {
418+
index += 1;
409419
continue;
410420
}
411421
let result = processor.process_obligation(&mut node.obligation);
@@ -441,6 +451,7 @@ impl<O: ForestObligation> ObligationForest<O> {
441451
});
442452
}
443453
}
454+
index += 1;
444455
}
445456

446457
if stalled {

src/librustc_data_structures/obligation_forest/tests.rs

+21-9
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ fn push_pop() {
7070
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
7171
"B" => ProcessResult::Error("B is for broken"),
7272
"C" => ProcessResult::Changed(vec![]),
73+
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
7374
_ => unreachable!(),
7475
}
7576
}, |_| {}), DoCompleted::Yes);
@@ -94,6 +95,7 @@ fn push_pop() {
9495
"A.2" => ProcessResult::Unchanged,
9596
"A.3" => ProcessResult::Changed(vec!["A.3.i"]),
9697
"D" => ProcessResult::Changed(vec!["D.1", "D.2"]),
98+
"A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged,
9799
_ => unreachable!(),
98100
}
99101
}, |_| {}), DoCompleted::Yes);
@@ -113,6 +115,7 @@ fn push_pop() {
113115
"A.3.i" => ProcessResult::Changed(vec![]),
114116
"D.1" => ProcessResult::Changed(vec!["D.1.i"]),
115117
"D.2" => ProcessResult::Changed(vec!["D.2.i"]),
118+
"D.1.i" | "D.2.i" => ProcessResult::Unchanged,
116119
_ => unreachable!(),
117120
}
118121
}, |_| {}), DoCompleted::Yes);
@@ -161,35 +164,38 @@ fn success_in_grandchildren() {
161164
forest.process_obligations(&mut C(|obligation| {
162165
match *obligation {
163166
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
167+
"A.1" => ProcessResult::Changed(vec![]),
168+
"A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
169+
"A.3" => ProcessResult::Changed(vec![]),
170+
"A.2.i" | "A.2.ii" => ProcessResult::Unchanged,
164171
_ => unreachable!(),
165172
}
166173
}, |_| {}), DoCompleted::Yes);
167-
assert!(ok.unwrap().is_empty());
174+
let mut ok = ok.unwrap();
175+
ok.sort();
176+
assert_eq!(ok, vec!["A.1", "A.3"]);
168177
assert!(err.is_empty());
169178

170179
let Outcome { completed: ok, errors: err, .. } =
171180
forest.process_obligations(&mut C(|obligation| {
172181
match *obligation {
173-
"A.1" => ProcessResult::Changed(vec![]),
174-
"A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
175-
"A.3" => ProcessResult::Changed(vec![]),
182+
"A.2.i" => ProcessResult::Unchanged,
183+
"A.2.ii" => ProcessResult::Changed(vec![]),
176184
_ => unreachable!(),
177185
}
178186
}, |_| {}), DoCompleted::Yes);
179-
let mut ok = ok.unwrap();
180-
ok.sort();
181-
assert_eq!(ok, vec!["A.1", "A.3"]);
187+
assert_eq!(ok.unwrap(), vec!["A.2.ii"]);
182188
assert!(err.is_empty());
183189

184190
let Outcome { completed: ok, errors: err, .. } =
185191
forest.process_obligations(&mut C(|obligation| {
186192
match *obligation {
187193
"A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
188-
"A.2.ii" => ProcessResult::Changed(vec![]),
194+
"A.2.i.a" => ProcessResult::Unchanged,
189195
_ => unreachable!(),
190196
}
191197
}, |_| {}), DoCompleted::Yes);
192-
assert_eq!(ok.unwrap(), vec!["A.2.ii"]);
198+
assert!(ok.unwrap().is_empty());
193199
assert!(err.is_empty());
194200

195201
let Outcome { completed: ok, errors: err, .. } =
@@ -222,6 +228,7 @@ fn to_errors_no_throw() {
222228
forest.process_obligations(&mut C(|obligation| {
223229
match *obligation {
224230
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
231+
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
225232
_ => unreachable!(),
226233
}
227234
}, |_|{}), DoCompleted::Yes);
@@ -243,6 +250,7 @@ fn diamond() {
243250
forest.process_obligations(&mut C(|obligation| {
244251
match *obligation {
245252
"A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
253+
"A.1" | "A.2" => ProcessResult::Unchanged,
246254
_ => unreachable!(),
247255
}
248256
}, |_|{}), DoCompleted::Yes);
@@ -254,6 +262,7 @@ fn diamond() {
254262
match *obligation {
255263
"A.1" => ProcessResult::Changed(vec!["D"]),
256264
"A.2" => ProcessResult::Changed(vec!["D"]),
265+
"D" => ProcessResult::Unchanged,
257266
_ => unreachable!(),
258267
}
259268
}, |_|{}), DoCompleted::Yes);
@@ -282,6 +291,7 @@ fn diamond() {
282291
forest.process_obligations(&mut C(|obligation| {
283292
match *obligation {
284293
"A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
294+
"A'.1" | "A'.2" => ProcessResult::Unchanged,
285295
_ => unreachable!(),
286296
}
287297
}, |_|{}), DoCompleted::Yes);
@@ -293,6 +303,7 @@ fn diamond() {
293303
match *obligation {
294304
"A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
295305
"A'.2" => ProcessResult::Changed(vec!["D'"]),
306+
"D'" | "A'" => ProcessResult::Unchanged,
296307
_ => unreachable!(),
297308
}
298309
}, |_|{}), DoCompleted::Yes);
@@ -370,6 +381,7 @@ fn orphan() {
370381
"B" => ProcessResult::Unchanged,
371382
"C1" => ProcessResult::Changed(vec![]),
372383
"C2" => ProcessResult::Changed(vec![]),
384+
"D" | "E" => ProcessResult::Unchanged,
373385
_ => unreachable!(),
374386
}
375387
}, |_|{}), DoCompleted::Yes);

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))

src/librustc_mir/const_eval.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -328,20 +328,32 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
328328
false // for now, we don't enforce validity
329329
}
330330

331-
fn find_fn(
331+
fn find_mir_or_eval_fn(
332332
ecx: &mut InterpCx<'mir, 'tcx, Self>,
333333
instance: ty::Instance<'tcx>,
334334
args: &[OpTy<'tcx>],
335335
ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
336336
_unwind: Option<mir::BasicBlock> // unwinding is not supported in consts
337337
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
338-
debug!("eval_fn_call: {:?}", instance);
338+
debug!("find_mir_or_eval_fn: {:?}", instance);
339+
339340
// Only check non-glue functions
340341
if let ty::InstanceDef::Item(def_id) = instance.def {
341342
// Execution might have wandered off into other crates, so we cannot do a stability-
342343
// sensitive check here. But we can at least rule out functions that are not const
343344
// at all.
344-
if !ecx.tcx.is_const_fn_raw(def_id) {
345+
if ecx.tcx.is_const_fn_raw(def_id) {
346+
// If this function is a `const fn` then as an optimization we can query this
347+
// evaluation immediately.
348+
//
349+
// For the moment we only do this for functions which take no arguments
350+
// (or all arguments are ZSTs) so that we don't memoize too much.
351+
if args.iter().all(|a| a.layout.is_zst()) {
352+
let gid = GlobalId { instance, promoted: None };
353+
ecx.eval_const_fn_call(gid, ret)?;
354+
return Ok(None);
355+
}
356+
} else {
345357
// Some functions we support even if they are non-const -- but avoid testing
346358
// that for const fn! We certainly do *not* want to actually call the fn
347359
// though, so be sure we return here.

0 commit comments

Comments
 (0)