Skip to content

Commit ee233c0

Browse files
Restructue HIR const-checker to handle features with multiple gates
1 parent 8f59902 commit ee233c0

File tree

2 files changed

+69
-19
lines changed

2 files changed

+69
-19
lines changed

src/librustc_passes/check_const.rs

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use rustc::hir::map::Map;
1313
use rustc::hir;
1414
use rustc::ty::TyCtxt;
1515
use rustc::ty::query::Providers;
16-
use rustc_feature::Features;
16+
use rustc::session::config::nightly_options;
1717
use syntax::ast::Mutability;
1818
use syntax::feature_gate::feature_err;
1919
use syntax::span_err;
20-
use syntax_pos::{sym, Span};
20+
use syntax_pos::{sym, Span, Symbol};
2121
use rustc_error_codes::*;
2222

2323
use std::fmt;
@@ -37,18 +37,31 @@ impl NonConstExpr {
3737
}
3838
}
3939

40-
/// Returns `true` if all feature gates required to enable this expression are turned on, or
41-
/// `None` if there is no feature gate corresponding to this expression.
42-
fn is_feature_gate_enabled(self, features: &Features) -> Option<bool> {
40+
fn required_feature_gates(self) -> Option<&'static [Symbol]> {
4341
use hir::MatchSource::*;
44-
match self {
42+
use hir::LoopSource::*;
43+
44+
let gates: &[_] = match self {
4545
| Self::Match(Normal)
4646
| Self::Match(IfDesugar { .. })
4747
| Self::Match(IfLetDesugar { .. })
48-
=> Some(features.const_if_match),
48+
=> &[sym::const_if_match],
4949

50-
_ => None,
51-
}
50+
| Self::Loop(Loop)
51+
=> &[sym::const_loop],
52+
53+
| Self::Loop(While)
54+
| Self::Loop(WhileLet)
55+
| Self::Match(WhileDesugar)
56+
| Self::Match(WhileLetDesugar)
57+
=> &[sym::const_loop, sym::const_if_match],
58+
59+
// `for` loops desugar to a call to `FromIterator::from_iterator`,
60+
// so they are not yet supported behind a feature flag.
61+
_ => return None,
62+
};
63+
64+
Some(gates)
5265
}
5366
}
5467

@@ -120,11 +133,15 @@ impl<'tcx> CheckConstVisitor<'tcx> {
120133

121134
/// Emits an error when an unsupported expression is found in a const context.
122135
fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
123-
match expr.is_feature_gate_enabled(self.tcx.features()) {
136+
let features = self.tcx.features();
137+
let gates = expr.required_feature_gates();
138+
match gates {
124139
// Don't emit an error if the user has enabled the requisite feature gates.
125-
Some(true) => return,
140+
Some(gates) if gates.iter().all(|&g| features[g]) => return,
126141

127-
// Users of `-Zunleash-the-miri-inside-of-you` must use feature gates when possible.
142+
// `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
143+
// corresponding feature gate. This encourages nightly users to use feature gates when
144+
// possible.
128145
None if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
129146
self.tcx.sess.span_warn(span, "skipping const checks");
130147
return;
@@ -135,15 +152,47 @@ impl<'tcx> CheckConstVisitor<'tcx> {
135152

136153
let const_kind = self.const_kind
137154
.expect("`const_check_violated` may only be called inside a const context");
138-
139155
let msg = format!("`{}` is not allowed in a `{}`", expr.name(), const_kind);
140-
match expr {
141-
| NonConstExpr::Match(hir::MatchSource::Normal)
142-
| NonConstExpr::Match(hir::MatchSource::IfDesugar { .. })
143-
| NonConstExpr::Match(hir::MatchSource::IfLetDesugar { .. })
144-
=> feature_err(&self.tcx.sess.parse_sess, sym::const_if_match, span, &msg).emit(),
145156

146-
_ => span_err!(self.tcx.sess, span, E0744, "{}", msg),
157+
let gates = gates.unwrap_or(&[]);
158+
let missing_gates: Vec<_> = gates
159+
.iter()
160+
.copied()
161+
.filter(|&g| !features[g])
162+
.collect();
163+
164+
match missing_gates.as_slice() {
165+
&[] => span_err!(self.tcx.sess, span, E0744, "{}", msg),
166+
167+
// If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
168+
// explain why their `while` loop is being rejected.
169+
&[gate @ sym::const_if_match] if gates.contains(&sym::const_loop) => {
170+
let mut err = feature_err(&self.tcx.sess.parse_sess, gate, span, &msg);
171+
err.note("`#![feature(const_loop)]` alone is not sufficient, \
172+
since this loop expression contains an implicit conditional");
173+
err.emit();
174+
}
175+
176+
&[missing_primary, ref missing_secondary @ ..] => {
177+
let mut err = feature_err(&self.tcx.sess.parse_sess, missing_primary, span, &msg);
178+
179+
// If multiple feature gates would be required to enable this expression, include
180+
// them as help messages. Don't emit a separate error for each missing feature gate.
181+
//
182+
// FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
183+
// is a pretty narrow case, however.
184+
if nightly_options::is_nightly_build() {
185+
for gate in missing_secondary {
186+
let note = format!(
187+
"add `#![feature({})]` to the crate attributes to enable",
188+
gate,
189+
);
190+
err.help(&note);
191+
}
192+
}
193+
194+
err.emit();
195+
}
147196
}
148197
}
149198

src/librustc_passes/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#![feature(in_band_lifetimes)]
1010
#![feature(nll)]
11+
#![feature(slice_patterns)]
1112

1213
#![recursion_limit="256"]
1314

0 commit comments

Comments
 (0)