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 { + 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(); + } } } 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"]