about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan MacKenzie <ecstaticmorse@gmail.com>2019-12-10 12:43:15 -0800
committerDylan MacKenzie <ecstaticmorse@gmail.com>2019-12-13 10:38:29 -0800
commitee233c07c6bde5e4140e898be605325cdd21967e (patch)
treed9ead9deb402a16575b1e7d725917a119847616f
parent8f59902aad038fe32571740356844592b42a4376 (diff)
downloadrust-ee233c07c6bde5e4140e898be605325cdd21967e.tar.gz
rust-ee233c07c6bde5e4140e898be605325cdd21967e.zip
Restructue HIR const-checker to handle features with multiple gates
-rw-r--r--src/librustc_passes/check_const.rs87
-rw-r--r--src/librustc_passes/lib.rs1
2 files changed, 69 insertions, 19 deletions
diff --git a/src/librustc_passes/check_const.rs b/src/librustc_passes/check_const.rs
index 63c6e60de79..e108dfb8173 100644
--- a/src/librustc_passes/check_const.rs
+++ b/src/librustc_passes/check_const.rs
@@ -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();
+            }
         }
     }
 
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index 81f06a14d95..f01867f32c6 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -8,6 +8,7 @@
 
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
+#![feature(slice_patterns)]
 
 #![recursion_limit="256"]