Skip to content

Commit b85b1dd

Browse files
authored
Rollup merge of rust-lang#67781 - cjgillot:passes-const, r=oli-obk
Move `is_min_const_fn` query to librustc_mir. The only two uses of the associated methods are in `librustc_mir` and `librustdoc`. Please tell me if there is a better choice. cc rust-lang#65031
2 parents 9f8f97b + c1c09be commit b85b1dd

File tree

12 files changed

+170
-168
lines changed

12 files changed

+170
-168
lines changed

src/librustc/ty/constness.rs

Lines changed: 0 additions & 156 deletions
This file was deleted.

src/librustc/ty/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ pub mod cast;
9797
#[macro_use]
9898
pub mod codec;
9999
pub mod _match;
100-
mod constness;
101100
mod erase_regions;
102101
pub mod error;
103102
pub mod fast_reject;
@@ -3318,7 +3317,6 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
33183317
context::provide(providers);
33193318
erase_regions::provide(providers);
33203319
layout::provide(providers);
3321-
constness::provide(providers);
33223320
*providers = ty::query::Providers {
33233321
asyncness,
33243322
associated_item,

src/librustc_mir/const_eval.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ use crate::interpret::{intern_const_alloc_recursive, ConstValue, InterpCx};
99

1010
mod error;
1111
mod eval_queries;
12+
mod fn_queries;
1213
mod machine;
1314

1415
pub use error::*;
1516
pub use eval_queries::*;
17+
pub use fn_queries::*;
1618
pub use machine::*;
1719

1820
/// Extracts a field of a (variant of a) const.
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
use rustc::hir::map::blocks::FnLikeNode;
2+
use rustc::ty::query::Providers;
3+
use rustc::ty::TyCtxt;
4+
use rustc_hir as hir;
5+
use rustc_hir::def_id::DefId;
6+
use rustc_span::symbol::Symbol;
7+
use rustc_target::spec::abi::Abi;
8+
use syntax::attr;
9+
10+
/// Whether the `def_id` counts as const fn in your current crate, considering all active
11+
/// feature gates
12+
pub fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
13+
tcx.is_const_fn_raw(def_id)
14+
&& match is_unstable_const_fn(tcx, def_id) {
15+
Some(feature_name) => {
16+
// has a `rustc_const_unstable` attribute, check whether the user enabled the
17+
// corresponding feature gate.
18+
tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature_name)
19+
}
20+
// functions without const stability are either stable user written
21+
// const fn or the user is using feature gates and we thus don't
22+
// care what they do
23+
None => true,
24+
}
25+
}
26+
27+
/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
28+
pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
29+
if tcx.is_const_fn_raw(def_id) {
30+
let const_stab = tcx.lookup_const_stability(def_id)?;
31+
if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None }
32+
} else {
33+
None
34+
}
35+
}
36+
37+
/// Returns `true` if this function must conform to `min_const_fn`
38+
pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
39+
// Bail out if the signature doesn't contain `const`
40+
if !tcx.is_const_fn_raw(def_id) {
41+
return false;
42+
}
43+
44+
if tcx.features().staged_api {
45+
// In order for a libstd function to be considered min_const_fn
46+
// it needs to be stable and have no `rustc_const_unstable` attribute.
47+
match tcx.lookup_const_stability(def_id) {
48+
// `rustc_const_unstable` functions don't need to conform.
49+
Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
50+
None => {
51+
if let Some(stab) = tcx.lookup_stability(def_id) {
52+
if stab.level.is_stable() {
53+
tcx.sess.span_err(
54+
tcx.def_span(def_id),
55+
"stable const functions must have either `rustc_const_stable` or \
56+
`rustc_const_unstable` attribute",
57+
);
58+
// While we errored above, because we don't know if we need to conform, we
59+
// err on the "safe" side and require min_const_fn.
60+
true
61+
} else {
62+
// Unstable functions need not conform to min_const_fn.
63+
false
64+
}
65+
} else {
66+
// Internal functions are forced to conform to min_const_fn.
67+
// Annotate the internal function with a const stability attribute if
68+
// you need to use unstable features.
69+
// Note: this is an arbitrary choice that does not affect stability or const
70+
// safety or anything, it just changes whether we need to annotate some
71+
// internal functions with `rustc_const_stable` or with `rustc_const_unstable`
72+
true
73+
}
74+
}
75+
// Everything else needs to conform, because it would be callable from
76+
// other `min_const_fn` functions.
77+
_ => true,
78+
}
79+
} else {
80+
// users enabling the `const_fn` feature gate can do what they want
81+
!tcx.features().const_fn
82+
}
83+
}
84+
85+
pub fn provide(providers: &mut Providers<'_>) {
86+
/// Const evaluability whitelist is here to check evaluability at the
87+
/// top level beforehand.
88+
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
89+
match tcx.fn_sig(def_id).abi() {
90+
Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
91+
Some(tcx.lookup_const_stability(def_id).is_some())
92+
}
93+
_ => None,
94+
}
95+
}
96+
97+
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
98+
/// said intrinsic is on the whitelist for being const callable.
99+
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
100+
let hir_id = tcx
101+
.hir()
102+
.as_local_hir_id(def_id)
103+
.expect("Non-local call to local provider is_const_fn");
104+
105+
let node = tcx.hir().get(hir_id);
106+
107+
if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
108+
whitelisted
109+
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
110+
fn_like.constness() == hir::Constness::Const
111+
} else if let hir::Node::Ctor(_) = node {
112+
true
113+
} else {
114+
false
115+
}
116+
}
117+
118+
fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
119+
is_const_fn(tcx, def_id)
120+
&& match tcx.lookup_const_stability(def_id) {
121+
Some(stab) => {
122+
if cfg!(debug_assertions) && stab.promotable {
123+
let sig = tcx.fn_sig(def_id);
124+
assert_eq!(
125+
sig.unsafety(),
126+
hir::Unsafety::Normal,
127+
"don't mark const unsafe fns as promotable",
128+
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
129+
);
130+
}
131+
stab.promotable
132+
}
133+
None => false,
134+
}
135+
}
136+
137+
fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
138+
is_const_fn(tcx, def_id)
139+
&& tcx
140+
.lookup_const_stability(def_id)
141+
.map(|stab| stab.allow_const_fn_ptr)
142+
.unwrap_or(false)
143+
}
144+
145+
*providers = Providers {
146+
is_const_fn_raw,
147+
is_promotable_const_fn,
148+
const_fn_is_allowed_fn_ptr,
149+
..*providers
150+
};
151+
}

src/librustc_mir/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ use rustc::ty::query::Providers;
5252

5353
pub fn provide(providers: &mut Providers<'_>) {
5454
borrow_check::provide(providers);
55+
const_eval::provide(providers);
5556
shim::provide(providers);
5657
transform::provide(providers);
5758
monomorphize::partitioning::provide(providers);

src/librustc_mir/transform/check_consts/validation.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use super::ops::{self, NonConstOp};
2121
use super::qualifs::{self, HasMutInterior, NeedsDrop};
2222
use super::resolver::FlowSensitiveAnalysis;
2323
use super::{is_lang_panic_fn, ConstKind, Item, Qualif};
24+
use crate::const_eval::{is_const_fn, is_unstable_const_fn};
2425
use crate::dataflow::{self as old_dataflow, generic as dataflow};
2526

2627
pub type IndirectlyMutableResults<'mir, 'tcx> =
@@ -173,7 +174,7 @@ impl Validator<'a, 'mir, 'tcx> {
173174
let Item { tcx, body, def_id, const_kind, .. } = *self.item;
174175

175176
let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn)
176-
&& tcx.is_min_const_fn(def_id))
177+
&& crate::const_eval::is_min_const_fn(tcx, def_id))
177178
&& !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
178179

179180
if use_min_const_fn_checks {
@@ -560,13 +561,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
560561
};
561562

562563
// At this point, we are calling a function whose `DefId` is known...
563-
if self.tcx.is_const_fn(def_id) {
564+
if is_const_fn(self.tcx, def_id) {
564565
return;
565566
}
566567

567568
if is_lang_panic_fn(self.tcx, def_id) {
568569
self.check_op(ops::Panic);
569-
} else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
570+
} else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {
570571
// Exempt unstable const fns inside of macros with
571572
// `#[allow_internal_unstable]`.
572573
if !self.span.allows_unstable(feature) {

src/librustc_mir/transform/check_unsafety.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_span::symbol::{sym, Symbol};
1414

1515
use std::ops::Bound;
1616

17+
use crate::const_eval::{is_const_fn, is_min_const_fn};
1718
use crate::util;
1819

1920
use rustc_error_codes::*;
@@ -523,7 +524,7 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult
523524
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
524525
let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
525526
hir::BodyOwnerKind::Closure => (false, false),
526-
hir::BodyOwnerKind::Fn => (tcx.is_const_fn(def_id), tcx.is_min_const_fn(def_id)),
527+
hir::BodyOwnerKind::Fn => (is_const_fn(tcx, def_id), is_min_const_fn(tcx, def_id)),
527528
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
528529
};
529530
let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);

0 commit comments

Comments
 (0)