@@ -13,11 +13,11 @@ use rustc::hir::map::Map;
1313use rustc:: hir;
1414use rustc:: ty:: TyCtxt ;
1515use rustc:: ty:: query:: Providers ;
16- use rustc_feature :: Features ;
16+ use rustc :: session :: config :: nightly_options ;
1717use syntax:: ast:: Mutability ;
1818use syntax:: feature_gate:: feature_err;
1919use syntax:: span_err;
20- use syntax_pos:: { sym, Span } ;
20+ use syntax_pos:: { sym, Span , Symbol } ;
2121use rustc_error_codes:: * ;
2222
2323use 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+ // A `for` loop's desugaring contains a call to `IntoIterator::into_iter`,
60+ // so they are not yet allowed with `#![feature(const_loop)]`.
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 required_gates = expr. required_feature_gates ( ) ;
138+ match required_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 . enabled ( 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 required_gates = required_gates. unwrap_or ( & [ ] ) ;
158+ let missing_gates: Vec < _ > = required_gates
159+ . iter ( )
160+ . copied ( )
161+ . filter ( |& g| !features. enabled ( 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 required_gates. contains ( & sym:: const_loop) => {
170+ feature_err ( & self . tcx . sess . parse_sess , gate, span, & msg)
171+ . note ( "`#![feature(const_loop)]` alone is not sufficient, \
172+ since this loop expression contains an implicit conditional")
173+ . 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
0 commit comments