]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
1 | //! Concrete error types for all operations which may be invalid in a certain const context. |
2 | ||
a2a8927a | 3 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; |
1b1a35ee XL |
4 | use rustc_hir as hir; |
5 | use rustc_hir::def_id::DefId; | |
5099ac24 FG |
6 | use rustc_infer::infer::TyCtxtInferExt; |
7 | use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; | |
8 | use rustc_middle::mir; | |
9 | use rustc_middle::ty::print::with_no_trimmed_paths; | |
a2a8927a | 10 | use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; |
5099ac24 FG |
11 | use rustc_middle::ty::{ |
12 | suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, TraitPredicate, Ty, | |
13 | }; | |
14 | use rustc_middle::ty::{Binder, BoundConstness, ImplPolarity, TraitRef}; | |
1b1a35ee XL |
15 | use rustc_session::parse::feature_err; |
16 | use rustc_span::symbol::sym; | |
5099ac24 FG |
17 | use rustc_span::{BytePos, Pos, Span, Symbol}; |
18 | use rustc_trait_selection::traits::SelectionContext; | |
1b1a35ee XL |
19 | |
20 | use super::ConstCx; | |
5099ac24 | 21 | use crate::util::{call_kind, CallDesugaringKind, CallKind}; |
1b1a35ee XL |
22 | |
23 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | |
24 | pub enum Status { | |
25 | Allowed, | |
26 | Unstable(Symbol), | |
27 | Forbidden, | |
28 | } | |
29 | ||
30 | #[derive(Clone, Copy)] | |
31 | pub enum DiagnosticImportance { | |
32 | /// An operation that must be removed for const-checking to pass. | |
33 | Primary, | |
34 | ||
35 | /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere. | |
36 | Secondary, | |
37 | } | |
38 | ||
39 | /// An operation that is not *always* allowed in a const context. | |
5099ac24 | 40 | pub trait NonConstOp<'tcx>: std::fmt::Debug { |
1b1a35ee | 41 | /// Returns an enum indicating whether this operation is allowed within the given item. |
5099ac24 | 42 | fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { |
1b1a35ee XL |
43 | Status::Forbidden |
44 | } | |
45 | ||
46 | fn importance(&self) -> DiagnosticImportance { | |
47 | DiagnosticImportance::Primary | |
48 | } | |
49 | ||
5099ac24 | 50 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; |
1b1a35ee XL |
51 | } |
52 | ||
1b1a35ee XL |
53 | #[derive(Debug)] |
54 | pub struct FloatingPointOp; | |
5099ac24 FG |
55 | impl<'tcx> NonConstOp<'tcx> for FloatingPointOp { |
56 | fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { | |
1b1a35ee XL |
57 | if ccx.const_kind() == hir::ConstContext::ConstFn { |
58 | Status::Unstable(sym::const_fn_floating_point_arithmetic) | |
59 | } else { | |
60 | Status::Allowed | |
61 | } | |
62 | } | |
63 | ||
5099ac24 | 64 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
65 | feature_err( |
66 | &ccx.tcx.sess.parse_sess, | |
67 | sym::const_fn_floating_point_arithmetic, | |
68 | span, | |
69 | &format!("floating point arithmetic is not allowed in {}s", ccx.const_kind()), | |
70 | ) | |
71 | } | |
72 | } | |
73 | ||
74 | /// A function call where the callee is a pointer. | |
75 | #[derive(Debug)] | |
76 | pub struct FnCallIndirect; | |
5099ac24 FG |
77 | impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { |
78 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
1b1a35ee XL |
79 | ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn") |
80 | } | |
81 | } | |
82 | ||
83 | /// A function call where the callee is not marked as `const`. | |
5099ac24 FG |
84 | #[derive(Debug, Clone, Copy)] |
85 | pub struct FnCallNonConst<'tcx> { | |
86 | pub caller: DefId, | |
87 | pub callee: DefId, | |
88 | pub substs: SubstsRef<'tcx>, | |
89 | pub span: Span, | |
90 | pub from_hir_call: bool, | |
91 | } | |
a2a8927a | 92 | |
5099ac24 FG |
93 | impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { |
94 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> { | |
95 | let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self; | |
96 | let ConstCx { tcx, param_env, .. } = *ccx; | |
97 | ||
98 | let diag_trait = |mut err, self_ty: Ty<'_>, trait_id| { | |
99 | let trait_ref = TraitRef::from_method(tcx, trait_id, substs); | |
100 | ||
101 | match self_ty.kind() { | |
102 | Param(param_ty) => { | |
103 | debug!(?param_ty); | |
104 | if let Some(generics) = caller | |
105 | .as_local() | |
106 | .map(|id| tcx.hir().local_def_id_to_hir_id(id)) | |
107 | .map(|id| tcx.hir().get(id)) | |
108 | .as_ref() | |
109 | .and_then(|node| node.generics()) | |
110 | { | |
111 | let constraint = with_no_trimmed_paths(|| { | |
112 | format!("~const {}", trait_ref.print_only_trait_path()) | |
113 | }); | |
114 | suggest_constraining_type_param( | |
115 | tcx, | |
116 | generics, | |
117 | &mut err, | |
118 | ¶m_ty.name.as_str(), | |
119 | &constraint, | |
120 | None, | |
121 | ); | |
122 | } | |
123 | } | |
124 | Adt(..) => { | |
125 | let obligation = Obligation::new( | |
126 | ObligationCause::dummy(), | |
127 | param_env, | |
128 | Binder::dummy(TraitPredicate { | |
129 | trait_ref, | |
130 | constness: BoundConstness::NotConst, | |
131 | polarity: ImplPolarity::Positive, | |
132 | }), | |
133 | ); | |
134 | ||
135 | let implsrc = tcx.infer_ctxt().enter(|infcx| { | |
136 | let mut selcx = SelectionContext::new(&infcx); | |
137 | selcx.select(&obligation) | |
138 | }); | |
139 | ||
140 | if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { | |
141 | let span = | |
142 | tcx.sess.source_map().guess_head_span(tcx.def_span(data.impl_def_id)); | |
143 | err.span_note(span, "impl defined here, but it is not `const`"); | |
144 | } | |
145 | } | |
146 | _ => {} | |
147 | } | |
148 | ||
149 | err | |
150 | }; | |
151 | ||
152 | let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None); | |
153 | ||
154 | debug!(?call_kind); | |
155 | ||
156 | let mut err = match call_kind { | |
157 | CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { | |
158 | macro_rules! error { | |
159 | ($fmt:literal) => { | |
160 | struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind()) | |
161 | }; | |
162 | } | |
163 | ||
164 | let err = match kind { | |
165 | CallDesugaringKind::ForLoopIntoIter => { | |
166 | error!("cannot convert `{}` into an iterator in {}s") | |
167 | } | |
168 | CallDesugaringKind::QuestionBranch => { | |
169 | error!("`?` cannot determine the branch of `{}` in {}s") | |
170 | } | |
171 | CallDesugaringKind::QuestionFromResidual => { | |
172 | error!("`?` cannot convert from residual of `{}` in {}s") | |
173 | } | |
174 | CallDesugaringKind::TryBlockFromOutput => { | |
175 | error!("`try` block cannot convert `{}` to the result in {}s") | |
176 | } | |
177 | }; | |
178 | ||
179 | diag_trait(err, self_ty, kind.trait_def_id(tcx)) | |
180 | } | |
181 | CallKind::FnCall { fn_trait_id, self_ty } => { | |
182 | let mut err = struct_span_err!( | |
183 | tcx.sess, | |
184 | span, | |
185 | E0015, | |
186 | "cannot call non-const closure in {}s", | |
187 | ccx.const_kind(), | |
188 | ); | |
189 | ||
190 | match self_ty.kind() { | |
191 | FnDef(def_id, ..) => { | |
192 | let span = tcx.sess.source_map().guess_head_span(tcx.def_span(*def_id)); | |
193 | if ccx.tcx.is_const_fn_raw(*def_id) { | |
194 | span_bug!(span, "calling const FnDef errored when it shouldn't"); | |
195 | } | |
196 | ||
197 | err.span_note(span, "function defined here, but it is not `const`"); | |
198 | } | |
199 | FnPtr(..) => { | |
200 | err.note(&format!( | |
201 | "function pointers need an RFC before allowed to be called in {}s", | |
202 | ccx.const_kind() | |
203 | )); | |
204 | } | |
205 | Closure(..) => { | |
206 | err.note(&format!( | |
207 | "closures need an RFC before allowed to be called in {}s", | |
208 | ccx.const_kind() | |
209 | )); | |
210 | } | |
211 | _ => {} | |
212 | } | |
213 | ||
214 | diag_trait(err, self_ty, fn_trait_id) | |
215 | } | |
216 | CallKind::Operator { trait_id, self_ty, .. } => { | |
217 | let mut err = struct_span_err!( | |
218 | tcx.sess, | |
219 | span, | |
220 | E0015, | |
221 | "cannot call non-const operator in {}s", | |
222 | ccx.const_kind() | |
223 | ); | |
224 | ||
225 | if Some(trait_id) == ccx.tcx.lang_items().eq_trait() { | |
226 | match (substs[0].unpack(), substs[1].unpack()) { | |
227 | (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) | |
228 | if self_ty == rhs_ty | |
229 | && self_ty.is_ref() | |
230 | && self_ty.peel_refs().is_primitive() => | |
231 | { | |
232 | let mut num_refs = 0; | |
233 | let mut tmp_ty = self_ty; | |
234 | while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { | |
235 | num_refs += 1; | |
236 | tmp_ty = *inner_ty; | |
237 | } | |
238 | let deref = "*".repeat(num_refs); | |
239 | ||
240 | if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) { | |
241 | if let Some(eq_idx) = call_str.find("==") { | |
242 | if let Some(rhs_idx) = | |
243 | call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) | |
244 | { | |
245 | let rhs_pos = | |
246 | span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); | |
247 | let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); | |
248 | err.multipart_suggestion( | |
249 | "consider dereferencing here", | |
250 | vec![ | |
251 | (span.shrink_to_lo(), deref.clone()), | |
252 | (rhs_span, deref), | |
253 | ], | |
254 | Applicability::MachineApplicable, | |
255 | ); | |
a2a8927a XL |
256 | } |
257 | } | |
258 | } | |
a2a8927a | 259 | } |
5099ac24 | 260 | _ => {} |
a2a8927a XL |
261 | } |
262 | } | |
5099ac24 FG |
263 | |
264 | diag_trait(err, self_ty, trait_id) | |
a2a8927a | 265 | } |
5099ac24 FG |
266 | CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { |
267 | let mut err = struct_span_err!( | |
268 | tcx.sess, | |
269 | span, | |
270 | E0015, | |
271 | "cannot perform deref coercion on `{}` in {}s", | |
272 | self_ty, | |
273 | ccx.const_kind() | |
274 | ); | |
275 | ||
276 | err.note(&format!("attempting to deref into `{}`", deref_target_ty)); | |
277 | ||
278 | // Check first whether the source is accessible (issue #87060) | |
279 | if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { | |
280 | err.span_note(deref_target, "deref defined here"); | |
281 | } | |
282 | ||
283 | diag_trait(err, self_ty, tcx.lang_items().deref_trait().unwrap()) | |
284 | } | |
285 | _ => struct_span_err!( | |
286 | ccx.tcx.sess, | |
287 | span, | |
288 | E0015, | |
289 | "cannot call non-const fn `{}` in {}s", | |
290 | ccx.tcx.def_path_str_with_substs(callee, substs), | |
291 | ccx.const_kind(), | |
292 | ), | |
293 | }; | |
294 | ||
295 | err.note(&format!( | |
296 | "calls in {}s are limited to constant functions, \ | |
297 | tuple structs and tuple variants", | |
298 | ccx.const_kind(), | |
299 | )); | |
a2a8927a XL |
300 | |
301 | err | |
1b1a35ee XL |
302 | } |
303 | } | |
304 | ||
94222f64 | 305 | /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function. |
1b1a35ee XL |
306 | /// |
307 | /// Contains the name of the feature that would allow the use of this function. | |
308 | #[derive(Debug)] | |
309 | pub struct FnCallUnstable(pub DefId, pub Option<Symbol>); | |
310 | ||
5099ac24 FG |
311 | impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { |
312 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
1b1a35ee XL |
313 | let FnCallUnstable(def_id, feature) = *self; |
314 | ||
315 | let mut err = ccx.tcx.sess.struct_span_err( | |
316 | span, | |
317 | &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)), | |
318 | ); | |
319 | ||
320 | if ccx.is_const_stable_const_fn() { | |
c295e0f8 | 321 | err.help("const-stable functions can only call other const-stable functions"); |
fc512014 | 322 | } else if ccx.tcx.sess.is_nightly_build() { |
1b1a35ee XL |
323 | if let Some(feature) = feature { |
324 | err.help(&format!( | |
325 | "add `#![feature({})]` to the crate attributes to enable", | |
326 | feature | |
327 | )); | |
328 | } | |
329 | } | |
330 | ||
331 | err | |
332 | } | |
333 | } | |
334 | ||
335 | #[derive(Debug)] | |
336 | pub struct FnPtrCast; | |
5099ac24 FG |
337 | impl<'tcx> NonConstOp<'tcx> for FnPtrCast { |
338 | fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { | |
1b1a35ee XL |
339 | if ccx.const_kind() != hir::ConstContext::ConstFn { |
340 | Status::Allowed | |
341 | } else { | |
342 | Status::Unstable(sym::const_fn_fn_ptr_basics) | |
343 | } | |
344 | } | |
345 | ||
5099ac24 | 346 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
347 | feature_err( |
348 | &ccx.tcx.sess.parse_sess, | |
349 | sym::const_fn_fn_ptr_basics, | |
350 | span, | |
351 | &format!("function pointer casts are not allowed in {}s", ccx.const_kind()), | |
352 | ) | |
353 | } | |
354 | } | |
355 | ||
356 | #[derive(Debug)] | |
357 | pub struct Generator(pub hir::GeneratorKind); | |
5099ac24 FG |
358 | impl<'tcx> NonConstOp<'tcx> for Generator { |
359 | fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { | |
17df50a5 XL |
360 | if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { |
361 | Status::Unstable(sym::const_async_blocks) | |
362 | } else { | |
363 | Status::Forbidden | |
364 | } | |
1b1a35ee XL |
365 | } |
366 | ||
5099ac24 | 367 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee | 368 | let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind()); |
17df50a5 XL |
369 | if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { |
370 | feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg) | |
371 | } else { | |
372 | ccx.tcx.sess.struct_span_err(span, &msg) | |
373 | } | |
1b1a35ee XL |
374 | } |
375 | } | |
376 | ||
377 | #[derive(Debug)] | |
378 | pub struct HeapAllocation; | |
5099ac24 FG |
379 | impl<'tcx> NonConstOp<'tcx> for HeapAllocation { |
380 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
1b1a35ee XL |
381 | let mut err = struct_span_err!( |
382 | ccx.tcx.sess, | |
383 | span, | |
384 | E0010, | |
385 | "allocations are not allowed in {}s", | |
386 | ccx.const_kind() | |
387 | ); | |
388 | err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind())); | |
389 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { | |
390 | err.note( | |
391 | "The value of statics and constants must be known at compile time, \ | |
392 | and they live for the entire lifetime of a program. Creating a boxed \ | |
393 | value allocates memory on the heap at runtime, and therefore cannot \ | |
394 | be done at compile time.", | |
395 | ); | |
396 | } | |
397 | err | |
398 | } | |
399 | } | |
400 | ||
401 | #[derive(Debug)] | |
402 | pub struct InlineAsm; | |
5099ac24 FG |
403 | impl<'tcx> NonConstOp<'tcx> for InlineAsm { |
404 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
1b1a35ee XL |
405 | struct_span_err!( |
406 | ccx.tcx.sess, | |
407 | span, | |
408 | E0015, | |
409 | "inline assembly is not allowed in {}s", | |
410 | ccx.const_kind() | |
411 | ) | |
412 | } | |
413 | } | |
414 | ||
415 | #[derive(Debug)] | |
416 | pub struct LiveDrop { | |
417 | pub dropped_at: Option<Span>, | |
418 | } | |
5099ac24 FG |
419 | impl<'tcx> NonConstOp<'tcx> for LiveDrop { |
420 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
1b1a35ee XL |
421 | let mut err = struct_span_err!( |
422 | ccx.tcx.sess, | |
423 | span, | |
424 | E0493, | |
425 | "destructors cannot be evaluated at compile-time" | |
426 | ); | |
427 | err.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind())); | |
428 | if let Some(span) = self.dropped_at { | |
429 | err.span_label(span, "value is dropped here"); | |
430 | } | |
431 | err | |
432 | } | |
433 | } | |
434 | ||
435 | #[derive(Debug)] | |
5869c6ff XL |
436 | /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to |
437 | /// the final value of the constant. | |
438 | pub struct TransientCellBorrow; | |
5099ac24 FG |
439 | impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow { |
440 | fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { | |
5869c6ff XL |
441 | Status::Unstable(sym::const_refs_to_cell) |
442 | } | |
443 | fn importance(&self) -> DiagnosticImportance { | |
444 | // The cases that cannot possibly work will already emit a `CellBorrow`, so we should | |
445 | // not additionally emit a feature gate error if activating the feature gate won't work. | |
446 | DiagnosticImportance::Secondary | |
447 | } | |
5099ac24 | 448 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
5869c6ff XL |
449 | feature_err( |
450 | &ccx.tcx.sess.parse_sess, | |
451 | sym::const_refs_to_cell, | |
452 | span, | |
453 | "cannot borrow here, since the borrowed element may contain interior mutability", | |
454 | ) | |
455 | } | |
456 | } | |
457 | ||
458 | #[derive(Debug)] | |
459 | /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to | |
460 | /// the final value of the constant, and thus we cannot allow this (for now). We may allow | |
461 | /// it in the future for static items. | |
1b1a35ee | 462 | pub struct CellBorrow; |
5099ac24 FG |
463 | impl<'tcx> NonConstOp<'tcx> for CellBorrow { |
464 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
5869c6ff | 465 | let mut err = struct_span_err!( |
1b1a35ee XL |
466 | ccx.tcx.sess, |
467 | span, | |
468 | E0492, | |
5869c6ff XL |
469 | "{}s cannot refer to interior mutable data", |
470 | ccx.const_kind(), | |
471 | ); | |
472 | err.span_label( | |
473 | span, | |
94222f64 | 474 | "this borrow of an interior mutable value may end up in the final value", |
5869c6ff XL |
475 | ); |
476 | if let hir::ConstContext::Static(_) = ccx.const_kind() { | |
477 | err.help( | |
478 | "to fix this, the value can be extracted to a separate \ | |
479 | `static` item and then referenced", | |
480 | ); | |
481 | } | |
482 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { | |
483 | err.note( | |
484 | "A constant containing interior mutable data behind a reference can allow you | |
485 | to modify that data. This would make multiple uses of a constant to be able to | |
486 | see different values and allow circumventing the `Send` and `Sync` requirements | |
487 | for shared mutable data, which is unsound.", | |
488 | ); | |
489 | } | |
490 | err | |
1b1a35ee XL |
491 | } |
492 | } | |
493 | ||
494 | #[derive(Debug)] | |
5869c6ff XL |
495 | /// This op is for `&mut` borrows in the trailing expression of a constant |
496 | /// which uses the "enclosing scopes rule" to leak its locals into anonymous | |
497 | /// static or const items. | |
29967ef6 XL |
498 | pub struct MutBorrow(pub hir::BorrowKind); |
499 | ||
5099ac24 FG |
500 | impl<'tcx> NonConstOp<'tcx> for MutBorrow { |
501 | fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { | |
5869c6ff XL |
502 | Status::Forbidden |
503 | } | |
504 | ||
505 | fn importance(&self) -> DiagnosticImportance { | |
506 | // If there were primary errors (like non-const function calls), do not emit further | |
507 | // errors about mutable references. | |
508 | DiagnosticImportance::Secondary | |
1b1a35ee XL |
509 | } |
510 | ||
5099ac24 | 511 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
29967ef6 XL |
512 | let raw = match self.0 { |
513 | hir::BorrowKind::Raw => "raw ", | |
514 | hir::BorrowKind::Ref => "", | |
515 | }; | |
516 | ||
5869c6ff XL |
517 | let mut err = struct_span_err!( |
518 | ccx.tcx.sess, | |
519 | span, | |
520 | E0764, | |
521 | "{}mutable references are not allowed in the final value of {}s", | |
522 | raw, | |
523 | ccx.const_kind(), | |
524 | ); | |
525 | ||
1b1a35ee XL |
526 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { |
527 | err.note( | |
528 | "References in statics and constants may only refer \ | |
529 | to immutable values.\n\n\ | |
530 | Statics are shared everywhere, and if they refer to \ | |
531 | mutable data one might violate memory safety since \ | |
532 | holding multiple mutable references to shared data \ | |
533 | is not allowed.\n\n\ | |
534 | If you really want global mutable state, try using \ | |
535 | static mut or a global UnsafeCell.", | |
536 | ); | |
537 | } | |
538 | err | |
539 | } | |
540 | } | |
541 | ||
5869c6ff XL |
542 | #[derive(Debug)] |
543 | pub struct TransientMutBorrow(pub hir::BorrowKind); | |
544 | ||
5099ac24 FG |
545 | impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { |
546 | fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { | |
5869c6ff XL |
547 | Status::Unstable(sym::const_mut_refs) |
548 | } | |
549 | ||
5099ac24 | 550 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
5869c6ff XL |
551 | let raw = match self.0 { |
552 | hir::BorrowKind::Raw => "raw ", | |
553 | hir::BorrowKind::Ref => "", | |
554 | }; | |
555 | ||
556 | feature_err( | |
557 | &ccx.tcx.sess.parse_sess, | |
558 | sym::const_mut_refs, | |
559 | span, | |
560 | &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()), | |
561 | ) | |
562 | } | |
563 | } | |
564 | ||
1b1a35ee XL |
565 | #[derive(Debug)] |
566 | pub struct MutDeref; | |
5099ac24 FG |
567 | impl<'tcx> NonConstOp<'tcx> for MutDeref { |
568 | fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { | |
1b1a35ee XL |
569 | Status::Unstable(sym::const_mut_refs) |
570 | } | |
571 | ||
572 | fn importance(&self) -> DiagnosticImportance { | |
5869c6ff | 573 | // Usually a side-effect of a `TransientMutBorrow` somewhere. |
1b1a35ee XL |
574 | DiagnosticImportance::Secondary |
575 | } | |
576 | ||
5099ac24 | 577 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
578 | feature_err( |
579 | &ccx.tcx.sess.parse_sess, | |
580 | sym::const_mut_refs, | |
581 | span, | |
582 | &format!("mutation through a reference is not allowed in {}s", ccx.const_kind()), | |
583 | ) | |
584 | } | |
585 | } | |
586 | ||
6a06907d XL |
587 | /// A call to a `panic()` lang item where the first argument is _not_ a `&str`. |
588 | #[derive(Debug)] | |
589 | pub struct PanicNonStr; | |
5099ac24 FG |
590 | impl<'tcx> NonConstOp<'tcx> for PanicNonStr { |
591 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
6a06907d XL |
592 | ccx.tcx.sess.struct_span_err( |
593 | span, | |
594 | "argument to `panic!()` in a const context must have type `&str`", | |
595 | ) | |
596 | } | |
597 | } | |
598 | ||
136023e0 XL |
599 | /// Comparing raw pointers for equality. |
600 | /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on | |
601 | /// allocation base addresses that are not known at compile-time. | |
1b1a35ee XL |
602 | #[derive(Debug)] |
603 | pub struct RawPtrComparison; | |
5099ac24 FG |
604 | impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { |
605 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
1b1a35ee XL |
606 | let mut err = ccx |
607 | .tcx | |
608 | .sess | |
c295e0f8 | 609 | .struct_span_err(span, "pointers cannot be reliably compared during const eval"); |
1b1a35ee XL |
610 | err.note( |
611 | "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \ | |
612 | for more information", | |
613 | ); | |
614 | err | |
615 | } | |
616 | } | |
617 | ||
618 | #[derive(Debug)] | |
3c0e092e | 619 | pub struct RawMutPtrDeref; |
5099ac24 | 620 | impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { |
1b1a35ee | 621 | fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { |
3c0e092e | 622 | Status::Unstable(sym::const_mut_refs) |
1b1a35ee XL |
623 | } |
624 | ||
5099ac24 | 625 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
626 | feature_err( |
627 | &ccx.tcx.sess.parse_sess, | |
3c0e092e | 628 | sym::const_mut_refs, |
1b1a35ee | 629 | span, |
3c0e092e | 630 | &format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),), |
1b1a35ee XL |
631 | ) |
632 | } | |
633 | } | |
634 | ||
136023e0 XL |
635 | /// Casting raw pointer or function pointer to an integer. |
636 | /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on | |
637 | /// allocation base addresses that are not known at compile-time. | |
1b1a35ee XL |
638 | #[derive(Debug)] |
639 | pub struct RawPtrToIntCast; | |
5099ac24 FG |
640 | impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { |
641 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
136023e0 XL |
642 | let mut err = ccx |
643 | .tcx | |
644 | .sess | |
c295e0f8 | 645 | .struct_span_err(span, "pointers cannot be cast to integers during const eval"); |
136023e0 XL |
646 | err.note("at compile-time, pointers do not have an integer value"); |
647 | err.note( | |
648 | "avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior", | |
649 | ); | |
650 | err | |
1b1a35ee XL |
651 | } |
652 | } | |
653 | ||
654 | /// An access to a (non-thread-local) `static`. | |
655 | #[derive(Debug)] | |
656 | pub struct StaticAccess; | |
5099ac24 FG |
657 | impl<'tcx> NonConstOp<'tcx> for StaticAccess { |
658 | fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { | |
1b1a35ee XL |
659 | if let hir::ConstContext::Static(_) = ccx.const_kind() { |
660 | Status::Allowed | |
661 | } else { | |
662 | Status::Forbidden | |
663 | } | |
664 | } | |
665 | ||
5099ac24 | 666 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
667 | let mut err = struct_span_err!( |
668 | ccx.tcx.sess, | |
669 | span, | |
670 | E0013, | |
671 | "{}s cannot refer to statics", | |
672 | ccx.const_kind() | |
673 | ); | |
674 | err.help( | |
675 | "consider extracting the value of the `static` to a `const`, and referring to that", | |
676 | ); | |
677 | if ccx.tcx.sess.teach(&err.get_code().unwrap()) { | |
678 | err.note( | |
679 | "`static` and `const` variables can refer to other `const` variables. \ | |
680 | A `const` variable, however, cannot refer to a `static` variable.", | |
681 | ); | |
682 | err.help("To fix this, the value can be extracted to a `const` and then used."); | |
683 | } | |
684 | err | |
685 | } | |
686 | } | |
687 | ||
688 | /// An access to a thread-local `static`. | |
689 | #[derive(Debug)] | |
690 | pub struct ThreadLocalAccess; | |
5099ac24 FG |
691 | impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { |
692 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { | |
1b1a35ee XL |
693 | struct_span_err!( |
694 | ccx.tcx.sess, | |
695 | span, | |
696 | E0625, | |
697 | "thread-local statics cannot be \ | |
698 | accessed at compile-time" | |
699 | ) | |
700 | } | |
701 | } | |
702 | ||
1b1a35ee XL |
703 | // Types that cannot appear in the signature or locals of a `const fn`. |
704 | pub mod ty { | |
705 | use super::*; | |
706 | ||
707 | #[derive(Debug)] | |
708 | pub struct MutRef(pub mir::LocalKind); | |
5099ac24 FG |
709 | impl<'tcx> NonConstOp<'tcx> for MutRef { |
710 | fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { | |
1b1a35ee XL |
711 | Status::Unstable(sym::const_mut_refs) |
712 | } | |
713 | ||
714 | fn importance(&self) -> DiagnosticImportance { | |
715 | match self.0 { | |
716 | mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, | |
717 | mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => { | |
718 | DiagnosticImportance::Primary | |
719 | } | |
720 | } | |
721 | } | |
722 | ||
5099ac24 | 723 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
724 | feature_err( |
725 | &ccx.tcx.sess.parse_sess, | |
726 | sym::const_mut_refs, | |
727 | span, | |
728 | &format!("mutable references are not allowed in {}s", ccx.const_kind()), | |
729 | ) | |
730 | } | |
731 | } | |
732 | ||
733 | #[derive(Debug)] | |
734 | pub struct FnPtr(pub mir::LocalKind); | |
5099ac24 | 735 | impl<'tcx> NonConstOp<'tcx> for FnPtr { |
1b1a35ee XL |
736 | fn importance(&self) -> DiagnosticImportance { |
737 | match self.0 { | |
738 | mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, | |
739 | mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => { | |
740 | DiagnosticImportance::Primary | |
741 | } | |
742 | } | |
743 | } | |
744 | ||
5099ac24 | 745 | fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { |
1b1a35ee XL |
746 | if ccx.const_kind() != hir::ConstContext::ConstFn { |
747 | Status::Allowed | |
748 | } else { | |
749 | Status::Unstable(sym::const_fn_fn_ptr_basics) | |
750 | } | |
751 | } | |
752 | ||
5099ac24 | 753 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
754 | feature_err( |
755 | &ccx.tcx.sess.parse_sess, | |
756 | sym::const_fn_fn_ptr_basics, | |
757 | span, | |
758 | &format!("function pointers cannot appear in {}s", ccx.const_kind()), | |
759 | ) | |
760 | } | |
761 | } | |
762 | ||
763 | #[derive(Debug)] | |
764 | pub struct ImplTrait; | |
5099ac24 | 765 | impl<'tcx> NonConstOp<'tcx> for ImplTrait { |
29967ef6 XL |
766 | fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { |
767 | Status::Unstable(sym::const_impl_trait) | |
1b1a35ee XL |
768 | } |
769 | ||
5099ac24 | 770 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
29967ef6 XL |
771 | feature_err( |
772 | &ccx.tcx.sess.parse_sess, | |
773 | sym::const_impl_trait, | |
774 | span, | |
775 | &format!("`impl Trait` is not allowed in {}s", ccx.const_kind()), | |
776 | ) | |
1b1a35ee XL |
777 | } |
778 | } | |
779 | ||
780 | #[derive(Debug)] | |
781 | pub struct TraitBound(pub mir::LocalKind); | |
5099ac24 | 782 | impl<'tcx> NonConstOp<'tcx> for TraitBound { |
1b1a35ee XL |
783 | fn importance(&self) -> DiagnosticImportance { |
784 | match self.0 { | |
785 | mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, | |
786 | mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => { | |
787 | DiagnosticImportance::Primary | |
788 | } | |
789 | } | |
790 | } | |
791 | ||
5099ac24 | 792 | fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { |
cdc7bbd5 XL |
793 | if ccx.const_kind() != hir::ConstContext::ConstFn { |
794 | Status::Allowed | |
795 | } else { | |
796 | Status::Unstable(sym::const_fn_trait_bound) | |
797 | } | |
1b1a35ee XL |
798 | } |
799 | ||
5099ac24 | 800 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
c295e0f8 | 801 | let mut err = feature_err( |
cdc7bbd5 XL |
802 | &ccx.tcx.sess.parse_sess, |
803 | sym::const_fn_trait_bound, | |
1b1a35ee XL |
804 | span, |
805 | "trait bounds other than `Sized` on const fn parameters are unstable", | |
c295e0f8 XL |
806 | ); |
807 | ||
808 | match ccx.fn_sig() { | |
809 | Some(fn_sig) if !fn_sig.span.contains(span) => { | |
810 | err.span_label(fn_sig.span, "function declared as const here"); | |
811 | } | |
812 | _ => {} | |
813 | } | |
814 | ||
815 | err | |
816 | } | |
817 | } | |
818 | ||
819 | #[derive(Debug)] | |
820 | pub struct DynTrait(pub mir::LocalKind); | |
5099ac24 | 821 | impl<'tcx> NonConstOp<'tcx> for DynTrait { |
c295e0f8 XL |
822 | fn importance(&self) -> DiagnosticImportance { |
823 | match self.0 { | |
824 | mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, | |
825 | mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => { | |
826 | DiagnosticImportance::Primary | |
827 | } | |
828 | } | |
829 | } | |
830 | ||
5099ac24 | 831 | fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { |
c295e0f8 XL |
832 | if ccx.const_kind() != hir::ConstContext::ConstFn { |
833 | Status::Allowed | |
834 | } else { | |
835 | Status::Unstable(sym::const_fn_trait_bound) | |
836 | } | |
837 | } | |
838 | ||
5099ac24 | 839 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
c295e0f8 XL |
840 | let mut err = feature_err( |
841 | &ccx.tcx.sess.parse_sess, | |
842 | sym::const_fn_trait_bound, | |
843 | span, | |
844 | "trait objects in const fn are unstable", | |
845 | ); | |
846 | ||
847 | match ccx.fn_sig() { | |
848 | Some(fn_sig) if !fn_sig.span.contains(span) => { | |
849 | err.span_label(fn_sig.span, "function declared as const here"); | |
850 | } | |
851 | _ => {} | |
852 | } | |
853 | ||
854 | err | |
1b1a35ee XL |
855 | } |
856 | } | |
857 | ||
858 | /// A trait bound with the `?const Trait` opt-out | |
859 | #[derive(Debug)] | |
860 | pub struct TraitBoundNotConst; | |
5099ac24 FG |
861 | impl<'tcx> NonConstOp<'tcx> for TraitBoundNotConst { |
862 | fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { | |
1b1a35ee XL |
863 | Status::Unstable(sym::const_trait_bound_opt_out) |
864 | } | |
865 | ||
5099ac24 | 866 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { |
1b1a35ee XL |
867 | feature_err( |
868 | &ccx.tcx.sess.parse_sess, | |
869 | sym::const_trait_bound_opt_out, | |
870 | span, | |
871 | "`?const Trait` syntax is unstable", | |
872 | ) | |
873 | } | |
874 | } | |
875 | } |