Restructue HIR const-checker to handle features with multiple gates
This commit is contained in:
parent
8f59902aad
commit
ee233c07c6
|
@ -13,11 +13,11 @@ use rustc::hir::map::Map;
|
|||
use rustc::hir;
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::ty::query::Providers;
|
||||
use rustc_feature::Features;
|
||||
use rustc::session::config::nightly_options;
|
||||
use syntax::ast::Mutability;
|
||||
use syntax::feature_gate::feature_err;
|
||||
use syntax::span_err;
|
||||
use syntax_pos::{sym, Span};
|
||||
use syntax_pos::{sym, Span, Symbol};
|
||||
use rustc_error_codes::*;
|
||||
|
||||
use std::fmt;
|
||||
|
@ -37,18 +37,31 @@ impl NonConstExpr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if all feature gates required to enable this expression are turned on, or
|
||||
/// `None` if there is no feature gate corresponding to this expression.
|
||||
fn is_feature_gate_enabled(self, features: &Features) -> Option<bool> {
|
||||
fn required_feature_gates(self) -> Option<&'static [Symbol]> {
|
||||
use hir::MatchSource::*;
|
||||
match self {
|
||||
use hir::LoopSource::*;
|
||||
|
||||
let gates: &[_] = match self {
|
||||
| Self::Match(Normal)
|
||||
| Self::Match(IfDesugar { .. })
|
||||
| Self::Match(IfLetDesugar { .. })
|
||||
=> Some(features.const_if_match),
|
||||
=> &[sym::const_if_match],
|
||||
|
||||
_ => None,
|
||||
}
|
||||
| Self::Loop(Loop)
|
||||
=> &[sym::const_loop],
|
||||
|
||||
| Self::Loop(While)
|
||||
| Self::Loop(WhileLet)
|
||||
| Self::Match(WhileDesugar)
|
||||
| Self::Match(WhileLetDesugar)
|
||||
=> &[sym::const_loop, sym::const_if_match],
|
||||
|
||||
// `for` loops desugar to a call to `FromIterator::from_iterator`,
|
||||
// so they are not yet supported behind a feature flag.
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(gates)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,11 +133,15 @@ impl<'tcx> CheckConstVisitor<'tcx> {
|
|||
|
||||
/// Emits an error when an unsupported expression is found in a const context.
|
||||
fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
|
||||
match expr.is_feature_gate_enabled(self.tcx.features()) {
|
||||
let features = self.tcx.features();
|
||||
let gates = expr.required_feature_gates();
|
||||
match gates {
|
||||
// Don't emit an error if the user has enabled the requisite feature gates.
|
||||
Some(true) => return,
|
||||
Some(gates) if gates.iter().all(|&g| features[g]) => return,
|
||||
|
||||
// Users of `-Zunleash-the-miri-inside-of-you` must use feature gates when possible.
|
||||
// `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
|
||||
// corresponding feature gate. This encourages nightly users to use feature gates when
|
||||
// possible.
|
||||
None if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
|
||||
self.tcx.sess.span_warn(span, "skipping const checks");
|
||||
return;
|
||||
|
@ -135,15 +152,47 @@ impl<'tcx> CheckConstVisitor<'tcx> {
|
|||
|
||||
let const_kind = self.const_kind
|
||||
.expect("`const_check_violated` may only be called inside a const context");
|
||||
|
||||
let msg = format!("`{}` is not allowed in a `{}`", expr.name(), const_kind);
|
||||
match expr {
|
||||
| NonConstExpr::Match(hir::MatchSource::Normal)
|
||||
| NonConstExpr::Match(hir::MatchSource::IfDesugar { .. })
|
||||
| NonConstExpr::Match(hir::MatchSource::IfLetDesugar { .. })
|
||||
=> feature_err(&self.tcx.sess.parse_sess, sym::const_if_match, span, &msg).emit(),
|
||||
|
||||
_ => span_err!(self.tcx.sess, span, E0744, "{}", msg),
|
||||
let gates = gates.unwrap_or(&[]);
|
||||
let missing_gates: Vec<_> = gates
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&g| !features[g])
|
||||
.collect();
|
||||
|
||||
match missing_gates.as_slice() {
|
||||
&[] => span_err!(self.tcx.sess, span, E0744, "{}", msg),
|
||||
|
||||
// If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
|
||||
// explain why their `while` loop is being rejected.
|
||||
&[gate @ sym::const_if_match] if gates.contains(&sym::const_loop) => {
|
||||
let mut err = feature_err(&self.tcx.sess.parse_sess, gate, span, &msg);
|
||||
err.note("`#![feature(const_loop)]` alone is not sufficient, \
|
||||
since this loop expression contains an implicit conditional");
|
||||
err.emit();
|
||||
}
|
||||
|
||||
&[missing_primary, ref missing_secondary @ ..] => {
|
||||
let mut err = feature_err(&self.tcx.sess.parse_sess, missing_primary, span, &msg);
|
||||
|
||||
// If multiple feature gates would be required to enable this expression, include
|
||||
// them as help messages. Don't emit a separate error for each missing feature gate.
|
||||
//
|
||||
// FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
|
||||
// is a pretty narrow case, however.
|
||||
if nightly_options::is_nightly_build() {
|
||||
for gate in missing_secondary {
|
||||
let note = format!(
|
||||
"add `#![feature({})]` to the crate attributes to enable",
|
||||
gate,
|
||||
);
|
||||
err.help(¬e);
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#![feature(in_band_lifetimes)]
|
||||
#![feature(nll)]
|
||||
#![feature(slice_patterns)]
|
||||
|
||||
#![recursion_limit="256"]
|
||||
|
||||
|
|
Loading…
Reference in New Issue