Skip to content

Commit f6d5920

Browse files
committed
Auto merge of rust-lang#76850 - ecstatic-morse:const-checking-refactor, r=oli-obk
Remove `qualify_min_const_fn` ~~Blocked on rust-lang#76807 (the first six commits).~~ With this PR, all checks in `qualify_min_const_fn` are replicated in `check_consts`, and the former is no longer invoked. My goal was to have as few changes to test output as possible, since making sweeping changes to the code *while* doing big batches of diagnostics updates turned out to be a headache. To this end, there's a few `HACK`s in `check_consts` to achieve parity with `qualify_min_const_fn`. The new system that replaces `is_min_const_fn` is referred to as "const-stability" My end goal for the const-stability rules is this: * Const-stability is only applicable to functions defined in `staged_api` crates. * All functions not marked `rustc_const_unstable` are considered "const-stable". - NB. This is currently not implemented. `#[unstable]` functions are also const-unstable. This causes problems when searching for feature gates. - All "const-unstable" functions have an associated feature gate * const-stable functions can only call other const-stable functions - `allow_internal_unstable` can be used to circumvent this. * All const-stable functions are subject to some additional checks (the ones that were unique to `qualify_min_const_fn`) The plan is to remove each `HACK` individually in subsequent PRs. That way, changes to error message output can be reviewed in isolation.
2 parents e62323d + 186d148 commit f6d5920

File tree

54 files changed

+791
-259
lines changed

Some content is hidden

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

54 files changed

+791
-259
lines changed

compiler/rustc_ast/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))]
88
#![feature(box_syntax)]
99
#![feature(const_fn)] // For the `transmute` in `P::new`
10+
#![feature(const_fn_transmute)]
1011
#![feature(const_panic)]
1112
#![feature(crate_visibility_modifier)]
1213
#![feature(label_break_value)]

compiler/rustc_mir/src/transform/check_consts/mod.rs

+40
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ impl ConstCx<'mir, 'tcx> {
5151
pub fn const_kind(&self) -> hir::ConstContext {
5252
self.const_kind.expect("`const_kind` must not be called on a non-const fn")
5353
}
54+
55+
pub fn is_const_stable_const_fn(&self) -> bool {
56+
self.const_kind == Some(hir::ConstContext::ConstFn)
57+
&& self.tcx.features().staged_api
58+
&& is_const_stable_const_fn(self.tcx, self.def_id.to_def_id())
59+
}
5460
}
5561

5662
/// Returns `true` if this `DefId` points to one of the official `panic` lang items.
@@ -63,3 +69,37 @@ pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: S
6369
attr::allow_internal_unstable(&tcx.sess, attrs)
6470
.map_or(false, |mut features| features.any(|name| name == feature_gate))
6571
}
72+
73+
// Returns `true` if the given `const fn` is "const-stable".
74+
//
75+
// Panics if the given `DefId` does not refer to a `const fn`.
76+
//
77+
// Const-stability is only relevant for `const fn` within a `staged_api` crate. Only "const-stable"
78+
// functions can be called in a const-context by users of the stable compiler. "const-stable"
79+
// functions are subject to more stringent restrictions than "const-unstable" functions: They
80+
// cannot use unstable features and can only call other "const-stable" functions.
81+
pub fn is_const_stable_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
82+
use attr::{ConstStability, Stability, StabilityLevel};
83+
84+
// Const-stability is only relevant for `const fn`.
85+
assert!(tcx.is_const_fn_raw(def_id));
86+
87+
// Functions with `#[rustc_const_unstable]` are const-unstable.
88+
match tcx.lookup_const_stability(def_id) {
89+
Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. }) => return false,
90+
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => return true,
91+
None => {}
92+
}
93+
94+
// Functions with `#[unstable]` are const-unstable.
95+
//
96+
// FIXME(ecstaticmorse): We should keep const-stability attributes wholly separate from normal stability
97+
// attributes. `#[unstable]` should be irrelevant.
98+
if let Some(Stability { level: StabilityLevel::Unstable { .. }, .. }) =
99+
tcx.lookup_stability(def_id)
100+
{
101+
return false;
102+
}
103+
104+
true
105+
}

compiler/rustc_mir/src/transform/check_consts/ops.rs

+231-13
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,34 @@ use rustc_span::{Span, Symbol};
1010

1111
use super::ConstCx;
1212

13-
/// Emits an error if `op` is not allowed in the given const context.
14-
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
13+
/// Emits an error and returns `true` if `op` is not allowed in the given const context.
14+
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) -> bool {
1515
debug!("illegal_op: op={:?}", op);
1616

1717
let gate = match op.status_in_item(ccx) {
18-
Status::Allowed => return,
18+
Status::Allowed => return false,
1919

2020
Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => {
21-
let unstable_in_stable = ccx.const_kind() == hir::ConstContext::ConstFn
22-
&& ccx.tcx.features().enabled(sym::staged_api)
23-
&& !ccx.tcx.has_attr(ccx.def_id.to_def_id(), sym::rustc_const_unstable)
21+
let unstable_in_stable = ccx.is_const_stable_const_fn()
2422
&& !super::allow_internal_unstable(ccx.tcx, ccx.def_id.to_def_id(), gate);
2523

2624
if unstable_in_stable {
2725
ccx.tcx.sess
28-
.struct_span_err(span, &format!("`#[feature({})]` cannot be depended on in a const-stable function", gate.as_str()))
26+
.struct_span_err(
27+
span,
28+
&format!("const-stable function cannot use `#[feature({})]`", gate.as_str()),
29+
)
2930
.span_suggestion(
3031
ccx.body.span,
3132
"if it is not part of the public API, make this function unstably const",
3233
concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(),
3334
Applicability::HasPlaceholders,
3435
)
35-
.help("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
36+
.note("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
3637
.emit();
3738
}
3839

39-
return;
40+
return unstable_in_stable;
4041
}
4142

4243
Status::Unstable(gate) => Some(gate),
@@ -45,12 +46,14 @@ pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
4546

4647
if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
4748
ccx.tcx.sess.miri_unleashed_feature(span, gate);
48-
return;
49+
return false;
4950
}
5051

5152
op.emit_error(ccx, span);
53+
true
5254
}
5355

56+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5457
pub enum Status {
5558
Allowed,
5659
Unstable(Symbol),
@@ -59,6 +62,8 @@ pub enum Status {
5962

6063
/// An operation that is not *always* allowed in a const context.
6164
pub trait NonConstOp: std::fmt::Debug {
65+
const STOPS_CONST_CHECKING: bool = false;
66+
6267
/// Returns an enum indicating whether this operation is allowed within the given item.
6368
fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
6469
Status::Forbidden
@@ -93,6 +98,34 @@ pub trait NonConstOp: std::fmt::Debug {
9398
}
9499
}
95100

101+
#[derive(Debug)]
102+
pub struct Abort;
103+
impl NonConstOp for Abort {
104+
const STOPS_CONST_CHECKING: bool = true;
105+
106+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
107+
mcf_status_in_item(ccx)
108+
}
109+
110+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
111+
mcf_emit_error(ccx, span, "abort is not stable in const fn")
112+
}
113+
}
114+
115+
#[derive(Debug)]
116+
pub struct NonPrimitiveOp;
117+
impl NonConstOp for NonPrimitiveOp {
118+
const STOPS_CONST_CHECKING: bool = true;
119+
120+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
121+
mcf_status_in_item(ccx)
122+
}
123+
124+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
125+
mcf_emit_error(ccx, span, "only int, `bool` and `char` operations are stable in const fn")
126+
}
127+
}
128+
96129
/// A function call where the callee is a pointer.
97130
#[derive(Debug)]
98131
pub struct FnCallIndirect;
@@ -125,7 +158,8 @@ impl NonConstOp for FnCallNonConst {
125158
///
126159
/// Contains the name of the feature that would allow the use of this function.
127160
#[derive(Debug)]
128-
pub struct FnCallUnstable(pub DefId, pub Symbol);
161+
pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
162+
129163
impl NonConstOp for FnCallUnstable {
130164
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
131165
let FnCallUnstable(def_id, feature) = *self;
@@ -134,13 +168,51 @@ impl NonConstOp for FnCallUnstable {
134168
span,
135169
&format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
136170
);
137-
if nightly_options::is_nightly_build() {
138-
err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
171+
172+
if ccx.is_const_stable_const_fn() {
173+
err.help("Const-stable functions can only call other const-stable functions");
174+
} else if nightly_options::is_nightly_build() {
175+
if let Some(feature) = feature {
176+
err.help(&format!(
177+
"add `#![feature({})]` to the crate attributes to enable",
178+
feature
179+
));
180+
}
139181
}
140182
err.emit();
141183
}
142184
}
143185

186+
#[derive(Debug)]
187+
pub struct FnPtrCast;
188+
impl NonConstOp for FnPtrCast {
189+
const STOPS_CONST_CHECKING: bool = true;
190+
191+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
192+
mcf_status_in_item(ccx)
193+
}
194+
195+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
196+
mcf_emit_error(ccx, span, "function pointer casts are not allowed in const fn");
197+
}
198+
}
199+
200+
#[derive(Debug)]
201+
pub struct Generator;
202+
impl NonConstOp for Generator {
203+
const STOPS_CONST_CHECKING: bool = true;
204+
205+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
206+
// FIXME: This means generator-only MIR is only forbidden in const fn. This is for
207+
// compatibility with the old code. Such MIR should be forbidden everywhere.
208+
mcf_status_in_item(ccx)
209+
}
210+
211+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
212+
mcf_emit_error(ccx, span, "const fn generators are unstable");
213+
}
214+
}
215+
144216
#[derive(Debug)]
145217
pub struct HeapAllocation;
146218
impl NonConstOp for HeapAllocation {
@@ -403,6 +475,24 @@ impl NonConstOp for ThreadLocalAccess {
403475
}
404476
}
405477

478+
#[derive(Debug)]
479+
pub struct Transmute;
480+
impl NonConstOp for Transmute {
481+
const STOPS_CONST_CHECKING: bool = true;
482+
483+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
484+
if ccx.const_kind() != hir::ConstContext::ConstFn {
485+
Status::Allowed
486+
} else {
487+
Status::Unstable(sym::const_fn_transmute)
488+
}
489+
}
490+
491+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
492+
mcf_emit_error(ccx, span, "can only call `transmute` from const items, not `const fn`");
493+
}
494+
}
495+
406496
#[derive(Debug)]
407497
pub struct UnionAccess;
408498
impl NonConstOp for UnionAccess {
@@ -425,3 +515,131 @@ impl NonConstOp for UnionAccess {
425515
.emit();
426516
}
427517
}
518+
519+
/// See [#64992].
520+
///
521+
/// [#64992]: https://github.com/rust-lang/rust/issues/64992
522+
#[derive(Debug)]
523+
pub struct UnsizingCast;
524+
impl NonConstOp for UnsizingCast {
525+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
526+
mcf_status_in_item(ccx)
527+
}
528+
529+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
530+
mcf_emit_error(
531+
ccx,
532+
span,
533+
"unsizing casts to types besides slices are not allowed in const fn",
534+
);
535+
}
536+
}
537+
538+
pub mod ty {
539+
use super::*;
540+
541+
#[derive(Debug)]
542+
pub struct MutRef;
543+
impl NonConstOp for MutRef {
544+
const STOPS_CONST_CHECKING: bool = true;
545+
546+
fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
547+
Status::Unstable(sym::const_mut_refs)
548+
}
549+
550+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
551+
mcf_emit_error(ccx, span, "mutable references in const fn are unstable");
552+
}
553+
}
554+
555+
#[derive(Debug)]
556+
pub struct FnPtr;
557+
impl NonConstOp for FnPtr {
558+
const STOPS_CONST_CHECKING: bool = true;
559+
560+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
561+
// FIXME: This attribute a hack to allow the specialization of the `futures` API. See
562+
// #59739. We should have a proper feature gate for this.
563+
if ccx.tcx.has_attr(ccx.def_id.to_def_id(), sym::rustc_allow_const_fn_ptr) {
564+
Status::Allowed
565+
} else {
566+
mcf_status_in_item(ccx)
567+
}
568+
}
569+
570+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
571+
mcf_emit_error(ccx, span, "function pointers in const fn are unstable");
572+
}
573+
}
574+
575+
#[derive(Debug)]
576+
pub struct ImplTrait;
577+
impl NonConstOp for ImplTrait {
578+
const STOPS_CONST_CHECKING: bool = true;
579+
580+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
581+
mcf_status_in_item(ccx)
582+
}
583+
584+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
585+
mcf_emit_error(ccx, span, "`impl Trait` in const fn is unstable");
586+
}
587+
}
588+
589+
#[derive(Debug)]
590+
pub struct TraitBound;
591+
impl NonConstOp for TraitBound {
592+
const STOPS_CONST_CHECKING: bool = true;
593+
594+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
595+
mcf_status_in_item(ccx)
596+
}
597+
598+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
599+
mcf_emit_error(
600+
ccx,
601+
span,
602+
"trait bounds other than `Sized` on const fn parameters are unstable",
603+
);
604+
}
605+
}
606+
607+
/// A trait bound with the `?const Trait` opt-out
608+
#[derive(Debug)]
609+
pub struct TraitBoundNotConst;
610+
impl NonConstOp for TraitBoundNotConst {
611+
const STOPS_CONST_CHECKING: bool = true;
612+
613+
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
614+
Status::Unstable(sym::const_trait_bound_opt_out)
615+
}
616+
617+
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
618+
feature_err(
619+
&ccx.tcx.sess.parse_sess,
620+
sym::const_trait_bound_opt_out,
621+
span,
622+
"`?const Trait` syntax is unstable",
623+
)
624+
.emit()
625+
}
626+
}
627+
}
628+
629+
fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
630+
if ccx.const_kind() != hir::ConstContext::ConstFn {
631+
Status::Allowed
632+
} else {
633+
Status::Unstable(sym::const_fn)
634+
}
635+
}
636+
637+
fn mcf_emit_error(ccx: &ConstCx<'_, '_>, span: Span, msg: &str) {
638+
struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg)
639+
.note(
640+
"see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
641+
for more information",
642+
)
643+
.help("add `#![feature(const_fn)]` to the crate attributes to enable")
644+
.emit();
645+
}

0 commit comments

Comments
 (0)