Restructue HIR const-checker to handle features with multiple gates

This commit is contained in:
Dylan MacKenzie 2019-12-10 12:43:15 -08:00
parent 8f59902aad
commit ee233c07c6
2 changed files with 69 additions and 19 deletions

View File

@ -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(&note);
}
}
err.emit();
}
}
}

View File

@ -8,6 +8,7 @@
#![feature(in_band_lifetimes)]
#![feature(nll)]
#![feature(slice_patterns)]
#![recursion_limit="256"]