]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | //! Concrete error types for all operations which may be invalid in a certain const context. |
2 | ||
dfeec247 | 3 | use rustc_errors::struct_span_err; |
f9f354fc | 4 | use rustc_hir as hir; |
dfeec247 | 5 | use rustc_hir::def_id::DefId; |
ba9703b0 XL |
6 | use rustc_session::config::nightly_options; |
7 | use rustc_session::parse::feature_err; | |
dfeec247 XL |
8 | use rustc_span::symbol::sym; |
9 | use rustc_span::{Span, Symbol}; | |
e74abb32 | 10 | |
f9f354fc | 11 | use super::ConstCx; |
e74abb32 | 12 | |
f035d41b XL |
13 | /// Emits an error if `op` is not allowed in the given const context. |
14 | pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) { | |
15 | debug!("illegal_op: op={:?}", op); | |
16 | ||
17 | if op.is_allowed_in_item(ccx) { | |
18 | return; | |
19 | } | |
20 | ||
21 | if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { | |
22 | ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate()); | |
23 | return; | |
24 | } | |
25 | ||
26 | op.emit_error(ccx, span); | |
27 | } | |
28 | ||
e74abb32 XL |
29 | /// An operation that is not *always* allowed in a const context. |
30 | pub trait NonConstOp: std::fmt::Debug { | |
ba9703b0 XL |
31 | /// Returns the `Symbol` corresponding to the feature gate that would enable this operation, |
32 | /// or `None` if such a feature gate does not exist. | |
33 | fn feature_gate() -> Option<Symbol> { | |
e74abb32 XL |
34 | None |
35 | } | |
36 | ||
37 | /// Returns `true` if this operation is allowed in the given item. | |
38 | /// | |
39 | /// This check should assume that we are not in a non-const `fn`, where all operations are | |
40 | /// legal. | |
ba9703b0 XL |
41 | /// |
42 | /// By default, it returns `true` if and only if this operation has a corresponding feature | |
43 | /// gate and that gate is enabled. | |
f9f354fc XL |
44 | fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { |
45 | Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate)) | |
e74abb32 XL |
46 | } |
47 | ||
f9f354fc | 48 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
e74abb32 | 49 | let mut err = struct_span_err!( |
f9f354fc | 50 | ccx.tcx.sess, |
e74abb32 XL |
51 | span, |
52 | E0019, | |
53 | "{} contains unimplemented expression type", | |
f9f354fc | 54 | ccx.const_kind() |
e74abb32 | 55 | ); |
f9f354fc XL |
56 | if let Some(feat) = Self::feature_gate() { |
57 | err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat)); | |
58 | } | |
59 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { | |
dfeec247 XL |
60 | err.note( |
61 | "A function call isn't allowed in the const's initialization expression \ | |
62 | because the expression's value must be known at compile-time.", | |
63 | ); | |
64 | err.note( | |
65 | "Remember: you can't use a function call inside a const's initialization \ | |
66 | expression! However, you can use it anywhere else.", | |
67 | ); | |
e74abb32 XL |
68 | } |
69 | err.emit(); | |
70 | } | |
71 | } | |
72 | ||
e74abb32 XL |
73 | /// A function call where the callee is a pointer. |
74 | #[derive(Debug)] | |
75 | pub struct FnCallIndirect; | |
76 | impl NonConstOp for FnCallIndirect { | |
f9f354fc | 77 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
74b04a01 | 78 | let mut err = |
f9f354fc | 79 | ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn"); |
e74abb32 XL |
80 | err.emit(); |
81 | } | |
82 | } | |
83 | ||
84 | /// A function call where the callee is not marked as `const`. | |
85 | #[derive(Debug)] | |
86 | pub struct FnCallNonConst(pub DefId); | |
87 | impl NonConstOp for FnCallNonConst { | |
f9f354fc | 88 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
e74abb32 | 89 | let mut err = struct_span_err!( |
f9f354fc | 90 | ccx.tcx.sess, |
e74abb32 XL |
91 | span, |
92 | E0015, | |
93 | "calls in {}s are limited to constant functions, \ | |
94 | tuple structs and tuple variants", | |
f9f354fc | 95 | ccx.const_kind(), |
e74abb32 XL |
96 | ); |
97 | err.emit(); | |
98 | } | |
99 | } | |
100 | ||
e74abb32 XL |
101 | /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function. |
102 | /// | |
103 | /// Contains the name of the feature that would allow the use of this function. | |
104 | #[derive(Debug)] | |
105 | pub struct FnCallUnstable(pub DefId, pub Symbol); | |
106 | impl NonConstOp for FnCallUnstable { | |
f9f354fc | 107 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
e74abb32 XL |
108 | let FnCallUnstable(def_id, feature) = *self; |
109 | ||
f9f354fc | 110 | let mut err = ccx.tcx.sess.struct_span_err( |
dfeec247 | 111 | span, |
f9f354fc | 112 | &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)), |
dfeec247 | 113 | ); |
e74abb32 | 114 | if nightly_options::is_nightly_build() { |
dfeec247 | 115 | err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature)); |
e74abb32 XL |
116 | } |
117 | err.emit(); | |
118 | } | |
119 | } | |
120 | ||
121 | #[derive(Debug)] | |
122 | pub struct HeapAllocation; | |
123 | impl NonConstOp for HeapAllocation { | |
f9f354fc | 124 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
dfeec247 | 125 | let mut err = struct_span_err!( |
f9f354fc | 126 | ccx.tcx.sess, |
dfeec247 XL |
127 | span, |
128 | E0010, | |
129 | "allocations are not allowed in {}s", | |
f9f354fc | 130 | ccx.const_kind() |
dfeec247 | 131 | ); |
f9f354fc XL |
132 | err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind())); |
133 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { | |
e74abb32 XL |
134 | err.note( |
135 | "The value of statics and constants must be known at compile time, \ | |
136 | and they live for the entire lifetime of a program. Creating a boxed \ | |
137 | value allocates memory on the heap at runtime, and therefore cannot \ | |
dfeec247 | 138 | be done at compile time.", |
e74abb32 XL |
139 | ); |
140 | } | |
141 | err.emit(); | |
142 | } | |
143 | } | |
144 | ||
f9f354fc XL |
145 | #[derive(Debug)] |
146 | pub struct InlineAsm; | |
147 | impl NonConstOp for InlineAsm {} | |
148 | ||
e74abb32 | 149 | #[derive(Debug)] |
f035d41b | 150 | pub struct LiveDrop(pub Option<Span>); |
e74abb32 | 151 | impl NonConstOp for LiveDrop { |
f9f354fc | 152 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
f035d41b | 153 | let mut diagnostic = struct_span_err!( |
f9f354fc | 154 | ccx.tcx.sess, |
dfeec247 XL |
155 | span, |
156 | E0493, | |
157 | "destructors cannot be evaluated at compile-time" | |
f035d41b XL |
158 | ); |
159 | diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind())); | |
160 | if let Some(span) = self.0 { | |
161 | diagnostic.span_label(span, "value is dropped here"); | |
162 | } | |
163 | diagnostic.emit(); | |
60c5eb7d XL |
164 | } |
165 | } | |
166 | ||
167 | #[derive(Debug)] | |
168 | pub struct CellBorrow; | |
169 | impl NonConstOp for CellBorrow { | |
f9f354fc | 170 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
dfeec247 | 171 | struct_span_err!( |
f9f354fc | 172 | ccx.tcx.sess, |
dfeec247 XL |
173 | span, |
174 | E0492, | |
60c5eb7d | 175 | "cannot borrow a constant which may contain \ |
dfeec247 XL |
176 | interior mutability, create a static instead" |
177 | ) | |
178 | .emit(); | |
60c5eb7d XL |
179 | } |
180 | } | |
e74abb32 XL |
181 | |
182 | #[derive(Debug)] | |
60c5eb7d | 183 | pub struct MutBorrow; |
e74abb32 | 184 | impl NonConstOp for MutBorrow { |
f035d41b XL |
185 | fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { |
186 | // Forbid everywhere except in const fn | |
187 | ccx.const_kind() == hir::ConstContext::ConstFn | |
188 | && ccx.tcx.features().enabled(Self::feature_gate().unwrap()) | |
189 | } | |
190 | ||
ba9703b0 XL |
191 | fn feature_gate() -> Option<Symbol> { |
192 | Some(sym::const_mut_refs) | |
60c5eb7d XL |
193 | } |
194 | ||
f9f354fc | 195 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
f035d41b XL |
196 | let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn { |
197 | feature_err( | |
198 | &ccx.tcx.sess.parse_sess, | |
199 | sym::const_mut_refs, | |
200 | span, | |
201 | &format!("mutable references are not allowed in {}s", ccx.const_kind()), | |
202 | ) | |
203 | } else { | |
204 | struct_span_err!( | |
205 | ccx.tcx.sess, | |
206 | span, | |
207 | E0764, | |
208 | "mutable references are not allowed in {}s", | |
209 | ccx.const_kind(), | |
210 | ) | |
211 | }; | |
212 | err.span_label(span, "`&mut` is only allowed in `const fn`".to_string()); | |
f9f354fc | 213 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { |
dfeec247 XL |
214 | err.note( |
215 | "References in statics and constants may only refer \ | |
60c5eb7d XL |
216 | to immutable values.\n\n\ |
217 | Statics are shared everywhere, and if they refer to \ | |
218 | mutable data one might violate memory safety since \ | |
219 | holding multiple mutable references to shared data \ | |
220 | is not allowed.\n\n\ | |
221 | If you really want global mutable state, try using \ | |
dfeec247 XL |
222 | static mut or a global UnsafeCell.", |
223 | ); | |
e74abb32 | 224 | } |
60c5eb7d | 225 | err.emit(); |
e74abb32 XL |
226 | } |
227 | } | |
228 | ||
dfeec247 XL |
229 | #[derive(Debug)] |
230 | pub struct MutAddressOf; | |
231 | impl NonConstOp for MutAddressOf { | |
ba9703b0 XL |
232 | fn feature_gate() -> Option<Symbol> { |
233 | Some(sym::const_mut_refs) | |
dfeec247 XL |
234 | } |
235 | ||
f9f354fc | 236 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
dfeec247 | 237 | feature_err( |
f9f354fc | 238 | &ccx.tcx.sess.parse_sess, |
dfeec247 XL |
239 | sym::const_mut_refs, |
240 | span, | |
f9f354fc | 241 | &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()), |
dfeec247 XL |
242 | ) |
243 | .emit(); | |
244 | } | |
245 | } | |
246 | ||
e74abb32 XL |
247 | #[derive(Debug)] |
248 | pub struct MutDeref; | |
60c5eb7d | 249 | impl NonConstOp for MutDeref { |
ba9703b0 XL |
250 | fn feature_gate() -> Option<Symbol> { |
251 | Some(sym::const_mut_refs) | |
60c5eb7d XL |
252 | } |
253 | } | |
e74abb32 XL |
254 | |
255 | #[derive(Debug)] | |
256 | pub struct Panic; | |
257 | impl NonConstOp for Panic { | |
ba9703b0 XL |
258 | fn feature_gate() -> Option<Symbol> { |
259 | Some(sym::const_panic) | |
e74abb32 XL |
260 | } |
261 | ||
f9f354fc | 262 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
60c5eb7d | 263 | feature_err( |
f9f354fc | 264 | &ccx.tcx.sess.parse_sess, |
e74abb32 XL |
265 | sym::const_panic, |
266 | span, | |
f9f354fc | 267 | &format!("panicking in {}s is unstable", ccx.const_kind()), |
60c5eb7d XL |
268 | ) |
269 | .emit(); | |
e74abb32 XL |
270 | } |
271 | } | |
272 | ||
273 | #[derive(Debug)] | |
274 | pub struct RawPtrComparison; | |
275 | impl NonConstOp for RawPtrComparison { | |
f9f354fc | 276 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
f035d41b XL |
277 | let mut err = ccx |
278 | .tcx | |
279 | .sess | |
280 | .struct_span_err(span, "pointers cannot be reliably compared during const eval."); | |
281 | err.note( | |
282 | "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \ | |
283 | for more information", | |
284 | ); | |
285 | err.emit(); | |
e74abb32 XL |
286 | } |
287 | } | |
288 | ||
289 | #[derive(Debug)] | |
290 | pub struct RawPtrDeref; | |
291 | impl NonConstOp for RawPtrDeref { | |
ba9703b0 XL |
292 | fn feature_gate() -> Option<Symbol> { |
293 | Some(sym::const_raw_ptr_deref) | |
e74abb32 XL |
294 | } |
295 | ||
f9f354fc | 296 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
60c5eb7d | 297 | feature_err( |
f9f354fc | 298 | &ccx.tcx.sess.parse_sess, |
dfeec247 XL |
299 | sym::const_raw_ptr_deref, |
300 | span, | |
f9f354fc | 301 | &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),), |
60c5eb7d XL |
302 | ) |
303 | .emit(); | |
e74abb32 XL |
304 | } |
305 | } | |
306 | ||
307 | #[derive(Debug)] | |
308 | pub struct RawPtrToIntCast; | |
309 | impl NonConstOp for RawPtrToIntCast { | |
ba9703b0 XL |
310 | fn feature_gate() -> Option<Symbol> { |
311 | Some(sym::const_raw_ptr_to_usize_cast) | |
e74abb32 XL |
312 | } |
313 | ||
f9f354fc | 314 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
60c5eb7d | 315 | feature_err( |
f9f354fc | 316 | &ccx.tcx.sess.parse_sess, |
dfeec247 XL |
317 | sym::const_raw_ptr_to_usize_cast, |
318 | span, | |
f9f354fc | 319 | &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),), |
60c5eb7d XL |
320 | ) |
321 | .emit(); | |
e74abb32 XL |
322 | } |
323 | } | |
324 | ||
325 | /// An access to a (non-thread-local) `static`. | |
326 | #[derive(Debug)] | |
327 | pub struct StaticAccess; | |
328 | impl NonConstOp for StaticAccess { | |
f9f354fc XL |
329 | fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { |
330 | matches!(ccx.const_kind(), hir::ConstContext::Static(_)) | |
e74abb32 XL |
331 | } |
332 | ||
f9f354fc | 333 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
dfeec247 | 334 | let mut err = struct_span_err!( |
f9f354fc | 335 | ccx.tcx.sess, |
dfeec247 XL |
336 | span, |
337 | E0013, | |
338 | "{}s cannot refer to statics", | |
f9f354fc | 339 | ccx.const_kind() |
dfeec247 XL |
340 | ); |
341 | err.help( | |
342 | "consider extracting the value of the `static` to a `const`, and referring to that", | |
343 | ); | |
f9f354fc | 344 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { |
e74abb32 | 345 | err.note( |
dfeec247 XL |
346 | "`static` and `const` variables can refer to other `const` variables. \ |
347 | A `const` variable, however, cannot refer to a `static` variable.", | |
e74abb32 | 348 | ); |
dfeec247 | 349 | err.help("To fix this, the value can be extracted to a `const` and then used."); |
e74abb32 XL |
350 | } |
351 | err.emit(); | |
352 | } | |
353 | } | |
354 | ||
355 | /// An access to a thread-local `static`. | |
356 | #[derive(Debug)] | |
357 | pub struct ThreadLocalAccess; | |
358 | impl NonConstOp for ThreadLocalAccess { | |
f9f354fc | 359 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
dfeec247 | 360 | struct_span_err!( |
f9f354fc | 361 | ccx.tcx.sess, |
dfeec247 XL |
362 | span, |
363 | E0625, | |
e74abb32 | 364 | "thread-local statics cannot be \ |
dfeec247 XL |
365 | accessed at compile-time" |
366 | ) | |
367 | .emit(); | |
e74abb32 XL |
368 | } |
369 | } | |
370 | ||
e74abb32 XL |
371 | #[derive(Debug)] |
372 | pub struct UnionAccess; | |
373 | impl NonConstOp for UnionAccess { | |
f9f354fc | 374 | fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { |
e74abb32 | 375 | // Union accesses are stable in all contexts except `const fn`. |
f9f354fc XL |
376 | ccx.const_kind() != hir::ConstContext::ConstFn |
377 | || ccx.tcx.features().enabled(Self::feature_gate().unwrap()) | |
e74abb32 XL |
378 | } |
379 | ||
ba9703b0 XL |
380 | fn feature_gate() -> Option<Symbol> { |
381 | Some(sym::const_fn_union) | |
e74abb32 XL |
382 | } |
383 | ||
f9f354fc | 384 | fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { |
60c5eb7d | 385 | feature_err( |
f9f354fc | 386 | &ccx.tcx.sess.parse_sess, |
dfeec247 XL |
387 | sym::const_fn_union, |
388 | span, | |
e74abb32 | 389 | "unions in const fn are unstable", |
60c5eb7d XL |
390 | ) |
391 | .emit(); | |
e74abb32 XL |
392 | } |
393 | } |