7
7
//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
8
8
//! through, but errors for structured control flow in a `const` should be emitted here.
9
9
10
+ use rustc_attr as attr;
10
11
use rustc_errors:: struct_span_err;
11
12
use rustc_hir as hir;
12
13
use rustc_hir:: def_id:: LocalDefId ;
@@ -70,35 +71,63 @@ pub(crate) fn provide(providers: &mut Providers<'_>) {
70
71
struct CheckConstVisitor < ' tcx > {
71
72
tcx : TyCtxt < ' tcx > ,
72
73
const_kind : Option < hir:: ConstContext > ,
74
+ def_id : Option < DefId > ,
73
75
}
74
76
75
77
impl < ' tcx > CheckConstVisitor < ' tcx > {
76
78
fn new ( tcx : TyCtxt < ' tcx > ) -> Self {
77
- CheckConstVisitor { tcx, const_kind : None }
79
+ CheckConstVisitor { tcx, const_kind : None , def_id : None }
78
80
}
79
81
80
82
/// Emits an error when an unsupported expression is found in a const context.
81
83
fn const_check_violated ( & self , expr : NonConstExpr , span : Span ) {
82
- let features = self . tcx . features ( ) ;
84
+ let Self { tcx, def_id, const_kind } = * self ;
85
+
86
+ let features = tcx. features ( ) ;
83
87
let required_gates = expr. required_feature_gates ( ) ;
88
+
89
+ let is_feature_allowed = |feature_gate| {
90
+ // All features require that the corresponding gate be enabled,
91
+ // even if the function has `#[allow_internal_unstable(the_gate)]`.
92
+ if !tcx. features ( ) . enabled ( feature_gate) {
93
+ return false ;
94
+ }
95
+
96
+ // If `def_id` is `None`, we don't need to consider stability attributes.
97
+ let def_id = match def_id {
98
+ Some ( x) => x,
99
+ None => return true ,
100
+ } ;
101
+
102
+ // If this crate is not using stability attributes, or this function is not claiming to be a
103
+ // stable `const fn`, that is all that is required.
104
+ if !tcx. features ( ) . staged_api || tcx. has_attr ( def_id, sym:: rustc_const_unstable) {
105
+ return true ;
106
+ }
107
+
108
+ // However, we cannot allow stable `const fn`s to use unstable features without an explicit
109
+ // opt-in via `allow_internal_unstable`.
110
+ attr:: allow_internal_unstable ( & tcx. get_attrs ( def_id) , & tcx. sess . diagnostic ( ) )
111
+ . map_or ( false , |mut features| features. any ( |name| name == feature_gate) )
112
+ } ;
113
+
84
114
match required_gates {
85
115
// Don't emit an error if the user has enabled the requisite feature gates.
86
- Some ( gates) if gates. iter ( ) . all ( | & g| features . enabled ( g ) ) => return ,
116
+ Some ( gates) if gates. iter ( ) . copied ( ) . all ( is_feature_allowed ) => return ,
87
117
88
118
// `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
89
119
// corresponding feature gate. This encourages nightly users to use feature gates when
90
120
// possible.
91
- None if self . tcx . sess . opts . debugging_opts . unleash_the_miri_inside_of_you => {
92
- self . tcx . sess . span_warn ( span, "skipping const checks" ) ;
121
+ None if tcx. sess . opts . debugging_opts . unleash_the_miri_inside_of_you => {
122
+ tcx. sess . span_warn ( span, "skipping const checks" ) ;
93
123
return ;
94
124
}
95
125
96
126
_ => { }
97
127
}
98
128
99
- let const_kind = self
100
- . const_kind
101
- . expect ( "`const_check_violated` may only be called inside a const context" ) ;
129
+ let const_kind =
130
+ const_kind. expect ( "`const_check_violated` may only be called inside a const context" ) ;
102
131
103
132
let msg = format ! ( "{} is not allowed in a `{}`" , expr. name( ) , const_kind. keyword_name( ) ) ;
104
133
@@ -107,10 +136,10 @@ impl<'tcx> CheckConstVisitor<'tcx> {
107
136
required_gates. iter ( ) . copied ( ) . filter ( |& g| !features. enabled ( g) ) . collect ( ) ;
108
137
109
138
match missing_gates. as_slice ( ) {
110
- & [ ] => struct_span_err ! ( self . tcx. sess, span, E0744 , "{}" , msg) . emit ( ) ,
139
+ & [ ] => struct_span_err ! ( tcx. sess, span, E0744 , "{}" , msg) . emit ( ) ,
111
140
112
141
& [ missing_primary, ref missing_secondary @ ..] => {
113
- let mut err = feature_err ( & self . tcx . sess . parse_sess , missing_primary, span, & msg) ;
142
+ let mut err = feature_err ( & tcx. sess . parse_sess , missing_primary, span, & msg) ;
114
143
115
144
// If multiple feature gates would be required to enable this expression, include
116
145
// them as help messages. Don't emit a separate error for each missing feature gate.
@@ -133,10 +162,18 @@ impl<'tcx> CheckConstVisitor<'tcx> {
133
162
}
134
163
135
164
/// Saves the parent `const_kind` before calling `f` and restores it afterwards.
136
- fn recurse_into ( & mut self , kind : Option < hir:: ConstContext > , f : impl FnOnce ( & mut Self ) ) {
165
+ fn recurse_into (
166
+ & mut self ,
167
+ kind : Option < hir:: ConstContext > ,
168
+ def_id : Option < DefId > ,
169
+ f : impl FnOnce ( & mut Self ) ,
170
+ ) {
171
+ let parent_def_id = self . def_id ;
137
172
let parent_kind = self . const_kind ;
173
+ self . def_id = def_id;
138
174
self . const_kind = kind;
139
175
f ( self ) ;
176
+ self . def_id = parent_def_id;
140
177
self . const_kind = parent_kind;
141
178
}
142
179
}
@@ -150,13 +187,13 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
150
187
151
188
fn visit_anon_const ( & mut self , anon : & ' tcx hir:: AnonConst ) {
152
189
let kind = Some ( hir:: ConstContext :: Const ) ;
153
- self . recurse_into ( kind, |this| intravisit:: walk_anon_const ( this, anon) ) ;
190
+ self . recurse_into ( kind, None , |this| intravisit:: walk_anon_const ( this, anon) ) ;
154
191
}
155
192
156
193
fn visit_body ( & mut self , body : & ' tcx hir:: Body < ' tcx > ) {
157
194
let owner = self . tcx . hir ( ) . body_owner_def_id ( body. id ( ) ) ;
158
195
let kind = self . tcx . hir ( ) . body_const_context ( owner) ;
159
- self . recurse_into ( kind, |this| intravisit:: walk_body ( this, body) ) ;
196
+ self . recurse_into ( kind, Some ( owner . to_def_id ( ) ) , |this| intravisit:: walk_body ( this, body) ) ;
160
197
}
161
198
162
199
fn visit_expr ( & mut self , e : & ' tcx hir:: Expr < ' tcx > ) {
0 commit comments