Skip to content

Commit 565be31

Browse files
Require allow_internal_unstable in HIR const-checker
1 parent 69a5cb1 commit 565be31

File tree

1 file changed

+50
-13
lines changed

1 file changed

+50
-13
lines changed

src/librustc_passes/check_const.rs

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
88
//! through, but errors for structured control flow in a `const` should be emitted here.
99
10+
use rustc_attr as attr;
1011
use rustc_errors::struct_span_err;
1112
use rustc_hir as hir;
1213
use rustc_hir::def_id::LocalDefId;
@@ -70,35 +71,63 @@ pub(crate) fn provide(providers: &mut Providers<'_>) {
7071
struct CheckConstVisitor<'tcx> {
7172
tcx: TyCtxt<'tcx>,
7273
const_kind: Option<hir::ConstContext>,
74+
def_id: Option<DefId>,
7375
}
7476

7577
impl<'tcx> CheckConstVisitor<'tcx> {
7678
fn new(tcx: TyCtxt<'tcx>) -> Self {
77-
CheckConstVisitor { tcx, const_kind: None }
79+
CheckConstVisitor { tcx, const_kind: None, def_id: None }
7880
}
7981

8082
/// Emits an error when an unsupported expression is found in a const context.
8183
fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
82-
let features = self.tcx.features();
84+
let Self { tcx, def_id, const_kind } = *self;
85+
86+
let features = tcx.features();
8387
let required_gates = expr.required_feature_gates();
88+
89+
let is_feature_allowed = |feature_gate| {
90+
// All features require that the corresponding gate be enabled,
91+
// even if the function has `#[allow_internal_unstable(the_gate)]`.
92+
if !tcx.features().enabled(feature_gate) {
93+
return false;
94+
}
95+
96+
// If `def_id` is `None`, we don't need to consider stability attributes.
97+
let def_id = match def_id {
98+
Some(x) => x,
99+
None => return true,
100+
};
101+
102+
// If this crate is not using stability attributes, or this function is not claiming to be a
103+
// stable `const fn`, that is all that is required.
104+
if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
105+
return true;
106+
}
107+
108+
// However, we cannot allow stable `const fn`s to use unstable features without an explicit
109+
// opt-in via `allow_internal_unstable`.
110+
attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
111+
.map_or(false, |mut features| features.any(|name| name == feature_gate))
112+
};
113+
84114
match required_gates {
85115
// Don't emit an error if the user has enabled the requisite feature gates.
86-
Some(gates) if gates.iter().all(|&g| features.enabled(g)) => return,
116+
Some(gates) if gates.iter().copied().all(is_feature_allowed) => return,
87117

88118
// `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
89119
// corresponding feature gate. This encourages nightly users to use feature gates when
90120
// possible.
91-
None if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
92-
self.tcx.sess.span_warn(span, "skipping const checks");
121+
None if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
122+
tcx.sess.span_warn(span, "skipping const checks");
93123
return;
94124
}
95125

96126
_ => {}
97127
}
98128

99-
let const_kind = self
100-
.const_kind
101-
.expect("`const_check_violated` may only be called inside a const context");
129+
let const_kind =
130+
const_kind.expect("`const_check_violated` may only be called inside a const context");
102131

103132
let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());
104133

@@ -107,10 +136,10 @@ impl<'tcx> CheckConstVisitor<'tcx> {
107136
required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
108137

109138
match missing_gates.as_slice() {
110-
&[] => struct_span_err!(self.tcx.sess, span, E0744, "{}", msg).emit(),
139+
&[] => struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(),
111140

112141
&[missing_primary, ref missing_secondary @ ..] => {
113-
let mut err = feature_err(&self.tcx.sess.parse_sess, missing_primary, span, &msg);
142+
let mut err = feature_err(&tcx.sess.parse_sess, missing_primary, span, &msg);
114143

115144
// If multiple feature gates would be required to enable this expression, include
116145
// them as help messages. Don't emit a separate error for each missing feature gate.
@@ -133,10 +162,18 @@ impl<'tcx> CheckConstVisitor<'tcx> {
133162
}
134163

135164
/// Saves the parent `const_kind` before calling `f` and restores it afterwards.
136-
fn recurse_into(&mut self, kind: Option<hir::ConstContext>, f: impl FnOnce(&mut Self)) {
165+
fn recurse_into(
166+
&mut self,
167+
kind: Option<hir::ConstContext>,
168+
def_id: Option<DefId>,
169+
f: impl FnOnce(&mut Self),
170+
) {
171+
let parent_def_id = self.def_id;
137172
let parent_kind = self.const_kind;
173+
self.def_id = def_id;
138174
self.const_kind = kind;
139175
f(self);
176+
self.def_id = parent_def_id;
140177
self.const_kind = parent_kind;
141178
}
142179
}
@@ -150,13 +187,13 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
150187

151188
fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
152189
let kind = Some(hir::ConstContext::Const);
153-
self.recurse_into(kind, |this| intravisit::walk_anon_const(this, anon));
190+
self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
154191
}
155192

156193
fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
157194
let owner = self.tcx.hir().body_owner_def_id(body.id());
158195
let kind = self.tcx.hir().body_const_context(owner);
159-
self.recurse_into(kind, |this| intravisit::walk_body(this, body));
196+
self.recurse_into(kind, Some(owner.to_def_id()), |this| intravisit::walk_body(this, body));
160197
}
161198

162199
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {

0 commit comments

Comments
 (0)