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