]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_const_eval/src/transform/check_consts/check.rs
Rollup merge of #89876 - AlexApps99:const_ops, r=oli-obk
[rust.git] / compiler / rustc_const_eval / src / transform / check_consts / check.rs
1 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
2
3 use rustc_errors::{Applicability, Diagnostic, ErrorReported};
4 use rustc_hir::def_id::DefId;
5 use rustc_hir::{self as hir, HirId, LangItem};
6 use rustc_index::bit_set::BitSet;
7 use rustc_infer::infer::TyCtxtInferExt;
8 use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
9 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
10 use rustc_middle::mir::*;
11 use rustc_middle::ty::cast::CastTy;
12 use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
13 use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt};
14 use rustc_middle::ty::{Binder, TraitPredicate, TraitRef};
15 use rustc_mir_dataflow::{self, Analysis};
16 use rustc_span::{sym, Span, Symbol};
17 use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
18 use rustc_trait_selection::traits::{self, SelectionContext, TraitEngine};
19
20 use std::mem;
21 use std::ops::Deref;
22
23 use super::ops::{self, NonConstOp, Status};
24 use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
25 use super::resolver::FlowSensitiveAnalysis;
26 use super::{ConstCx, Qualif};
27 use crate::const_eval::is_unstable_const_fn;
28
29 type QualifResults<'mir, 'tcx, Q> =
30     rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
31
32 #[derive(Default)]
33 pub struct Qualifs<'mir, 'tcx> {
34     has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
35     needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
36     needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
37 }
38
39 impl Qualifs<'mir, 'tcx> {
40     /// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
41     ///
42     /// Only updates the cursor if absolutely necessary
43     pub fn needs_drop(
44         &mut self,
45         ccx: &'mir ConstCx<'mir, 'tcx>,
46         local: Local,
47         location: Location,
48     ) -> bool {
49         let ty = ccx.body.local_decls[local].ty;
50         if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
51             return false;
52         }
53
54         let needs_drop = self.needs_drop.get_or_insert_with(|| {
55             let ConstCx { tcx, body, .. } = *ccx;
56
57             FlowSensitiveAnalysis::new(NeedsDrop, ccx)
58                 .into_engine(tcx, &body)
59                 .iterate_to_fixpoint()
60                 .into_results_cursor(&body)
61         });
62
63         needs_drop.seek_before_primary_effect(location);
64         needs_drop.get().contains(local)
65     }
66
67     /// Returns `true` if `local` is `NeedsNonConstDrop` at the given `Location`.
68     ///
69     /// Only updates the cursor if absolutely necessary
70     pub fn needs_non_const_drop(
71         &mut self,
72         ccx: &'mir ConstCx<'mir, 'tcx>,
73         local: Local,
74         location: Location,
75     ) -> bool {
76         let ty = ccx.body.local_decls[local].ty;
77         if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) {
78             return false;
79         }
80
81         let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| {
82             let ConstCx { tcx, body, .. } = *ccx;
83
84             FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
85                 .into_engine(tcx, &body)
86                 .iterate_to_fixpoint()
87                 .into_results_cursor(&body)
88         });
89
90         needs_non_const_drop.seek_before_primary_effect(location);
91         needs_non_const_drop.get().contains(local)
92     }
93
94     /// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
95     ///
96     /// Only updates the cursor if absolutely necessary.
97     pub fn has_mut_interior(
98         &mut self,
99         ccx: &'mir ConstCx<'mir, 'tcx>,
100         local: Local,
101         location: Location,
102     ) -> bool {
103         let ty = ccx.body.local_decls[local].ty;
104         if !HasMutInterior::in_any_value_of_ty(ccx, ty) {
105             return false;
106         }
107
108         let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| {
109             let ConstCx { tcx, body, .. } = *ccx;
110
111             FlowSensitiveAnalysis::new(HasMutInterior, ccx)
112                 .into_engine(tcx, &body)
113                 .iterate_to_fixpoint()
114                 .into_results_cursor(&body)
115         });
116
117         has_mut_interior.seek_before_primary_effect(location);
118         has_mut_interior.get().contains(local)
119     }
120
121     fn in_return_place(
122         &mut self,
123         ccx: &'mir ConstCx<'mir, 'tcx>,
124         error_occured: Option<ErrorReported>,
125     ) -> ConstQualifs {
126         // Find the `Return` terminator if one exists.
127         //
128         // If no `Return` terminator exists, this MIR is divergent. Just return the conservative
129         // qualifs for the return type.
130         let return_block = ccx
131             .body
132             .basic_blocks()
133             .iter_enumerated()
134             .find(|(_, block)| match block.terminator().kind {
135                 TerminatorKind::Return => true,
136                 _ => false,
137             })
138             .map(|(bb, _)| bb);
139
140         let return_block = match return_block {
141             None => return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty(), error_occured),
142             Some(bb) => bb,
143         };
144
145         let return_loc = ccx.body.terminator_loc(return_block);
146
147         let custom_eq = match ccx.const_kind() {
148             // We don't care whether a `const fn` returns a value that is not structurally
149             // matchable. Functions calls are opaque and always use type-based qualification, so
150             // this value should never be used.
151             hir::ConstContext::ConstFn => true,
152
153             // If we know that all values of the return type are structurally matchable, there's no
154             // need to run dataflow.
155             _ if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => false,
156
157             hir::ConstContext::Const | hir::ConstContext::Static(_) => {
158                 let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx)
159                     .into_engine(ccx.tcx, &ccx.body)
160                     .iterate_to_fixpoint()
161                     .into_results_cursor(&ccx.body);
162
163                 cursor.seek_after_primary_effect(return_loc);
164                 cursor.get().contains(RETURN_PLACE)
165             }
166         };
167
168         ConstQualifs {
169             needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
170             needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc),
171             has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
172             custom_eq,
173             error_occured,
174         }
175     }
176 }
177
178 pub struct Checker<'mir, 'tcx> {
179     ccx: &'mir ConstCx<'mir, 'tcx>,
180     qualifs: Qualifs<'mir, 'tcx>,
181
182     /// The span of the current statement.
183     span: Span,
184
185     /// A set that stores for each local whether it has a `StorageDead` for it somewhere.
186     local_has_storage_dead: Option<BitSet<Local>>,
187
188     error_emitted: Option<ErrorReported>,
189     secondary_errors: Vec<Diagnostic>,
190 }
191
192 impl Deref for Checker<'mir, 'tcx> {
193     type Target = ConstCx<'mir, 'tcx>;
194
195     fn deref(&self) -> &Self::Target {
196         &self.ccx
197     }
198 }
199
200 impl Checker<'mir, 'tcx> {
201     pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
202         Checker {
203             span: ccx.body.span,
204             ccx,
205             qualifs: Default::default(),
206             local_has_storage_dead: None,
207             error_emitted: None,
208             secondary_errors: Vec::new(),
209         }
210     }
211
212     pub fn check_body(&mut self) {
213         let ConstCx { tcx, body, .. } = *self.ccx;
214         let def_id = self.ccx.def_id();
215
216         // `async` functions cannot be `const fn`. This is checked during AST lowering, so there's
217         // no need to emit duplicate errors here.
218         if is_async_fn(self.ccx) || body.generator.is_some() {
219             tcx.sess.delay_span_bug(body.span, "`async` functions cannot be `const fn`");
220             return;
221         }
222
223         // The local type and predicate checks are not free and only relevant for `const fn`s.
224         if self.const_kind() == hir::ConstContext::ConstFn {
225             // Prevent const trait methods from being annotated as `stable`.
226             // FIXME: Do this as part of stability checking.
227             if self.is_const_stable_const_fn() {
228                 let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
229                 if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
230                     self.ccx
231                         .tcx
232                         .sess
233                         .struct_span_err(self.span, "trait methods cannot be stable const fn")
234                         .emit();
235                 }
236             }
237
238             self.check_item_predicates();
239
240             for (idx, local) in body.local_decls.iter_enumerated() {
241                 // Handle the return place below.
242                 if idx == RETURN_PLACE || local.internal {
243                     continue;
244                 }
245
246                 self.span = local.source_info.span;
247                 self.check_local_or_return_ty(local.ty, idx);
248             }
249
250             // impl trait is gone in MIR, so check the return type of a const fn by its signature
251             // instead of the type of the return place.
252             self.span = body.local_decls[RETURN_PLACE].source_info.span;
253             let return_ty = tcx.fn_sig(def_id).output();
254             self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE);
255         }
256
257         if !tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
258             self.visit_body(&body);
259         }
260
261         // Ensure that the end result is `Sync` in a non-thread local `static`.
262         let should_check_for_sync = self.const_kind()
263             == hir::ConstContext::Static(hir::Mutability::Not)
264             && !tcx.is_thread_local_static(def_id.to_def_id());
265
266         if should_check_for_sync {
267             let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
268             check_return_ty_is_sync(tcx, &body, hir_id);
269         }
270
271         // If we got through const-checking without emitting any "primary" errors, emit any
272         // "secondary" errors if they occurred.
273         let secondary_errors = mem::take(&mut self.secondary_errors);
274         if self.error_emitted.is_none() {
275             for error in secondary_errors {
276                 self.tcx.sess.diagnostic().emit_diagnostic(&error);
277             }
278         } else {
279             assert!(self.tcx.sess.has_errors());
280         }
281     }
282
283     fn local_has_storage_dead(&mut self, local: Local) -> bool {
284         let ccx = self.ccx;
285         self.local_has_storage_dead
286             .get_or_insert_with(|| {
287                 struct StorageDeads {
288                     locals: BitSet<Local>,
289                 }
290                 impl Visitor<'tcx> for StorageDeads {
291                     fn visit_statement(&mut self, stmt: &Statement<'tcx>, _: Location) {
292                         if let StatementKind::StorageDead(l) = stmt.kind {
293                             self.locals.insert(l);
294                         }
295                     }
296                 }
297                 let mut v = StorageDeads { locals: BitSet::new_empty(ccx.body.local_decls.len()) };
298                 v.visit_body(ccx.body);
299                 v.locals
300             })
301             .contains(local)
302     }
303
304     pub fn qualifs_in_return_place(&mut self) -> ConstQualifs {
305         self.qualifs.in_return_place(self.ccx, self.error_emitted)
306     }
307
308     /// Emits an error if an expression cannot be evaluated in the current context.
309     pub fn check_op(&mut self, op: impl NonConstOp) {
310         self.check_op_spanned(op, self.span);
311     }
312
313     /// Emits an error at the given `span` if an expression cannot be evaluated in the current
314     /// context.
315     pub fn check_op_spanned<O: NonConstOp>(&mut self, op: O, span: Span) {
316         let gate = match op.status_in_item(self.ccx) {
317             Status::Allowed => return,
318
319             Status::Unstable(gate) if self.tcx.features().enabled(gate) => {
320                 let unstable_in_stable = self.ccx.is_const_stable_const_fn()
321                     && !super::rustc_allow_const_fn_unstable(
322                         self.tcx,
323                         self.def_id().to_def_id(),
324                         gate,
325                     );
326                 if unstable_in_stable {
327                     emit_unstable_in_stable_error(self.ccx, span, gate);
328                 }
329
330                 return;
331             }
332
333             Status::Unstable(gate) => Some(gate),
334             Status::Forbidden => None,
335         };
336
337         if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
338             self.tcx.sess.miri_unleashed_feature(span, gate);
339             return;
340         }
341
342         let mut err = op.build_error(self.ccx, span);
343         assert!(err.is_error());
344
345         match op.importance() {
346             ops::DiagnosticImportance::Primary => {
347                 self.error_emitted = Some(ErrorReported);
348                 err.emit();
349             }
350
351             ops::DiagnosticImportance::Secondary => err.buffer(&mut self.secondary_errors),
352         }
353     }
354
355     fn check_static(&mut self, def_id: DefId, span: Span) {
356         if self.tcx.is_thread_local_static(def_id) {
357             self.tcx.sess.delay_span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef");
358         }
359         self.check_op_spanned(ops::StaticAccess, span)
360     }
361
362     fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) {
363         let kind = self.body.local_kind(local);
364
365         for ty in ty.walk(self.tcx) {
366             let ty = match ty.unpack() {
367                 GenericArgKind::Type(ty) => ty,
368
369                 // No constraints on lifetimes or constants, except potentially
370                 // constants' types, but `walk` will get to them as well.
371                 GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
372             };
373
374             match *ty.kind() {
375                 ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef(kind)),
376                 ty::Opaque(..) => self.check_op(ops::ty::ImplTrait),
377                 ty::FnPtr(..) => self.check_op(ops::ty::FnPtr(kind)),
378
379                 ty::Dynamic(preds, _) => {
380                     for pred in preds.iter() {
381                         match pred.skip_binder() {
382                             ty::ExistentialPredicate::AutoTrait(_)
383                             | ty::ExistentialPredicate::Projection(_) => {
384                                 self.check_op(ops::ty::DynTrait(kind))
385                             }
386                             ty::ExistentialPredicate::Trait(trait_ref) => {
387                                 if Some(trait_ref.def_id) != self.tcx.lang_items().sized_trait() {
388                                     self.check_op(ops::ty::DynTrait(kind))
389                                 }
390                             }
391                         }
392                     }
393                 }
394                 _ => {}
395             }
396         }
397     }
398
399     fn check_item_predicates(&mut self) {
400         let ConstCx { tcx, .. } = *self.ccx;
401
402         let mut current = self.def_id().to_def_id();
403         loop {
404             let predicates = tcx.predicates_of(current);
405             for (predicate, _) in predicates.predicates {
406                 match predicate.kind().skip_binder() {
407                     ty::PredicateKind::RegionOutlives(_)
408                     | ty::PredicateKind::TypeOutlives(_)
409                     | ty::PredicateKind::WellFormed(_)
410                     | ty::PredicateKind::Projection(_)
411                     | ty::PredicateKind::ConstEvaluatable(..)
412                     | ty::PredicateKind::ConstEquate(..)
413                     | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
414                     ty::PredicateKind::ObjectSafe(_) => {
415                         bug!("object safe predicate on function: {:#?}", predicate)
416                     }
417                     ty::PredicateKind::ClosureKind(..) => {
418                         bug!("closure kind predicate on function: {:#?}", predicate)
419                     }
420                     ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) => {
421                         bug!("subtype/coerce predicate on function: {:#?}", predicate)
422                     }
423                     ty::PredicateKind::Trait(pred) => {
424                         if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
425                             continue;
426                         }
427                         match pred.self_ty().kind() {
428                             ty::Param(p) => {
429                                 let generics = tcx.generics_of(current);
430                                 let def = generics.type_param(p, tcx);
431                                 let span = tcx.def_span(def.def_id);
432
433                                 // These are part of the function signature, so treat them like
434                                 // arguments when determining importance.
435                                 let kind = LocalKind::Arg;
436
437                                 self.check_op_spanned(ops::ty::TraitBound(kind), span);
438                             }
439                             // other kinds of bounds are either tautologies
440                             // or cause errors in other passes
441                             _ => continue,
442                         }
443                     }
444                 }
445             }
446             match predicates.parent {
447                 Some(parent) => current = parent,
448                 None => break,
449             }
450         }
451     }
452
453     fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
454         match self.const_kind() {
455             // In a const fn all borrows are transient or point to the places given via
456             // references in the arguments (so we already checked them with
457             // TransientMutBorrow/MutBorrow as appropriate).
458             // The borrow checker guarantees that no new non-transient borrows are created.
459             // NOTE: Once we have heap allocations during CTFE we need to figure out
460             // how to prevent `const fn` to create long-lived allocations that point
461             // to mutable memory.
462             hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)),
463             _ => {
464                 // Locals with StorageDead do not live beyond the evaluation and can
465                 // thus safely be borrowed without being able to be leaked to the final
466                 // value of the constant.
467                 if self.local_has_storage_dead(local) {
468                     self.check_op(ops::TransientMutBorrow(kind));
469                 } else {
470                     self.check_op(ops::MutBorrow(kind));
471                 }
472             }
473         }
474     }
475 }
476
477 impl Visitor<'tcx> for Checker<'mir, 'tcx> {
478     fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &BasicBlockData<'tcx>) {
479         trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
480
481         // We don't const-check basic blocks on the cleanup path since we never unwind during
482         // const-eval: a panic causes an immediate compile error. In other words, cleanup blocks
483         // are unreachable during const-eval.
484         //
485         // We can't be more conservative (e.g., by const-checking cleanup blocks anyways) because
486         // locals that would never be dropped during normal execution are sometimes dropped during
487         // unwinding, which means backwards-incompatible live-drop errors.
488         if block.is_cleanup {
489             return;
490         }
491
492         self.super_basic_block_data(bb, block);
493     }
494
495     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
496         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
497
498         // Special-case reborrows to be more like a copy of a reference.
499         match *rvalue {
500             Rvalue::Ref(_, kind, place) => {
501                 if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
502                     let ctx = match kind {
503                         BorrowKind::Shared => {
504                             PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
505                         }
506                         BorrowKind::Shallow => {
507                             PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
508                         }
509                         BorrowKind::Unique => {
510                             PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
511                         }
512                         BorrowKind::Mut { .. } => {
513                             PlaceContext::MutatingUse(MutatingUseContext::Borrow)
514                         }
515                     };
516                     self.visit_local(&reborrowed_place_ref.local, ctx, location);
517                     self.visit_projection(reborrowed_place_ref, ctx, location);
518                     return;
519                 }
520             }
521             Rvalue::AddressOf(mutbl, place) => {
522                 if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
523                     let ctx = match mutbl {
524                         Mutability::Not => {
525                             PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
526                         }
527                         Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::AddressOf),
528                     };
529                     self.visit_local(&reborrowed_place_ref.local, ctx, location);
530                     self.visit_projection(reborrowed_place_ref, ctx, location);
531                     return;
532                 }
533             }
534             _ => {}
535         }
536
537         self.super_rvalue(rvalue, location);
538
539         match *rvalue {
540             Rvalue::ThreadLocalRef(_) => self.check_op(ops::ThreadLocalAccess),
541
542             Rvalue::Use(_)
543             | Rvalue::Repeat(..)
544             | Rvalue::Discriminant(..)
545             | Rvalue::Len(_)
546             | Rvalue::Aggregate(..) => {}
547
548             Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
549             | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
550                 let ty = place.ty(self.body, self.tcx).ty;
551                 let is_allowed = match ty.kind() {
552                     // Inside a `static mut`, `&mut [...]` is allowed.
553                     ty::Array(..) | ty::Slice(_)
554                         if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) =>
555                     {
556                         true
557                     }
558
559                     // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
560                     // that this is merely a ZST and it is already eligible for promotion.
561                     // This may require an RFC?
562                     /*
563                     ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
564                         => true,
565                     */
566                     _ => false,
567                 };
568
569                 if !is_allowed {
570                     if let BorrowKind::Mut { .. } = kind {
571                         self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
572                     } else {
573                         self.check_op(ops::CellBorrow);
574                     }
575                 }
576             }
577
578             Rvalue::AddressOf(Mutability::Mut, ref place) => {
579                 self.check_mut_borrow(place.local, hir::BorrowKind::Raw)
580             }
581
582             Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)
583             | Rvalue::AddressOf(Mutability::Not, ref place) => {
584                 let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
585                     &self.ccx,
586                     &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
587                     place.as_ref(),
588                 );
589
590                 if borrowed_place_has_mut_interior {
591                     match self.const_kind() {
592                         // In a const fn all borrows are transient or point to the places given via
593                         // references in the arguments (so we already checked them with
594                         // TransientCellBorrow/CellBorrow as appropriate).
595                         // The borrow checker guarantees that no new non-transient borrows are created.
596                         // NOTE: Once we have heap allocations during CTFE we need to figure out
597                         // how to prevent `const fn` to create long-lived allocations that point
598                         // to (interior) mutable memory.
599                         hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow),
600                         _ => {
601                             // Locals with StorageDead are definitely not part of the final constant value, and
602                             // it is thus inherently safe to permit such locals to have their
603                             // address taken as we can't end up with a reference to them in the
604                             // final value.
605                             // Note: This is only sound if every local that has a `StorageDead` has a
606                             // `StorageDead` in every control flow path leading to a `return` terminator.
607                             if self.local_has_storage_dead(place.local) {
608                                 self.check_op(ops::TransientCellBorrow);
609                             } else {
610                                 self.check_op(ops::CellBorrow);
611                             }
612                         }
613                     }
614                 }
615             }
616
617             Rvalue::Cast(
618                 CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
619                 _,
620                 _,
621             ) => {}
622
623             Rvalue::Cast(
624                 CastKind::Pointer(
625                     PointerCast::UnsafeFnPointer
626                     | PointerCast::ClosureFnPointer(_)
627                     | PointerCast::ReifyFnPointer,
628                 ),
629                 _,
630                 _,
631             ) => self.check_op(ops::FnPtrCast),
632
633             Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => {
634                 // Nothing to check here (`check_local_or_return_ty` ensures no trait objects occur
635                 // in the type of any local, which also excludes casts).
636             }
637
638             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
639                 let operand_ty = operand.ty(self.body, self.tcx);
640                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
641                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
642
643                 if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
644                     self.check_op(ops::RawPtrToIntCast);
645                 }
646             }
647
648             Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {}
649             Rvalue::NullaryOp(NullOp::Box, _) => self.check_op(ops::HeapAllocation),
650             Rvalue::ShallowInitBox(_, _) => {}
651
652             Rvalue::UnaryOp(_, ref operand) => {
653                 let ty = operand.ty(self.body, self.tcx);
654                 if is_int_bool_or_char(ty) {
655                     // Int, bool, and char operations are fine.
656                 } else if ty.is_floating_point() {
657                     self.check_op(ops::FloatingPointOp);
658                 } else {
659                     span_bug!(self.span, "non-primitive type in `Rvalue::UnaryOp`: {:?}", ty);
660                 }
661             }
662
663             Rvalue::BinaryOp(op, box (ref lhs, ref rhs))
664             | Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => {
665                 let lhs_ty = lhs.ty(self.body, self.tcx);
666                 let rhs_ty = rhs.ty(self.body, self.tcx);
667
668                 if is_int_bool_or_char(lhs_ty) && is_int_bool_or_char(rhs_ty) {
669                     // Int, bool, and char operations are fine.
670                 } else if lhs_ty.is_fn_ptr() || lhs_ty.is_unsafe_ptr() {
671                     assert_eq!(lhs_ty, rhs_ty);
672                     assert!(
673                         op == BinOp::Eq
674                             || op == BinOp::Ne
675                             || op == BinOp::Le
676                             || op == BinOp::Lt
677                             || op == BinOp::Ge
678                             || op == BinOp::Gt
679                             || op == BinOp::Offset
680                     );
681
682                     self.check_op(ops::RawPtrComparison);
683                 } else if lhs_ty.is_floating_point() || rhs_ty.is_floating_point() {
684                     self.check_op(ops::FloatingPointOp);
685                 } else {
686                     span_bug!(
687                         self.span,
688                         "non-primitive type in `Rvalue::BinaryOp`: {:?} âš¬ {:?}",
689                         lhs_ty,
690                         rhs_ty
691                     );
692                 }
693             }
694         }
695     }
696
697     fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
698         self.super_operand(op, location);
699         if let Operand::Constant(c) = op {
700             if let Some(def_id) = c.check_static_ptr(self.tcx) {
701                 self.check_static(def_id, self.span);
702             }
703         }
704     }
705     fn visit_projection_elem(
706         &mut self,
707         place_local: Local,
708         proj_base: &[PlaceElem<'tcx>],
709         elem: PlaceElem<'tcx>,
710         context: PlaceContext,
711         location: Location,
712     ) {
713         trace!(
714             "visit_projection_elem: place_local={:?} proj_base={:?} elem={:?} \
715             context={:?} location={:?}",
716             place_local,
717             proj_base,
718             elem,
719             context,
720             location,
721         );
722
723         self.super_projection_elem(place_local, proj_base, elem, context, location);
724
725         match elem {
726             ProjectionElem::Deref => {
727                 let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
728                 if let ty::RawPtr(_) = base_ty.kind() {
729                     if proj_base.is_empty() {
730                         let decl = &self.body.local_decls[place_local];
731                         if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
732                             let span = decl.source_info.span;
733                             self.check_static(def_id, span);
734                             return;
735                         }
736                     }
737                     self.check_op(ops::RawPtrDeref);
738                 }
739
740                 if context.is_mutating_use() {
741                     self.check_op(ops::MutDeref);
742                 }
743             }
744
745             ProjectionElem::ConstantIndex { .. }
746             | ProjectionElem::Downcast(..)
747             | ProjectionElem::Subslice { .. }
748             | ProjectionElem::Field(..)
749             | ProjectionElem::Index(_) => {}
750         }
751     }
752
753     fn visit_source_info(&mut self, source_info: &SourceInfo) {
754         trace!("visit_source_info: source_info={:?}", source_info);
755         self.span = source_info.span;
756     }
757
758     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
759         trace!("visit_statement: statement={:?} location={:?}", statement, location);
760
761         self.super_statement(statement, location);
762
763         match statement.kind {
764             StatementKind::LlvmInlineAsm { .. } => {
765                 self.check_op(ops::InlineAsm);
766             }
767
768             StatementKind::Assign(..)
769             | StatementKind::SetDiscriminant { .. }
770             | StatementKind::FakeRead(..)
771             | StatementKind::StorageLive(_)
772             | StatementKind::StorageDead(_)
773             | StatementKind::Retag { .. }
774             | StatementKind::AscribeUserType(..)
775             | StatementKind::Coverage(..)
776             | StatementKind::CopyNonOverlapping(..)
777             | StatementKind::Nop => {}
778         }
779     }
780
781     #[instrument(level = "debug", skip(self))]
782     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
783         use rustc_target::spec::abi::Abi::RustIntrinsic;
784
785         self.super_terminator(terminator, location);
786
787         match &terminator.kind {
788             TerminatorKind::Call { func, args, .. } => {
789                 let ConstCx { tcx, body, param_env, .. } = *self.ccx;
790                 let caller = self.def_id().to_def_id();
791
792                 let fn_ty = func.ty(body, tcx);
793
794                 let (mut callee, mut substs) = match *fn_ty.kind() {
795                     ty::FnDef(def_id, substs) => (def_id, substs),
796
797                     ty::FnPtr(_) => {
798                         self.check_op(ops::FnCallIndirect);
799                         return;
800                     }
801                     _ => {
802                         span_bug!(terminator.source_info.span, "invalid callee of type {:?}", fn_ty)
803                     }
804                 };
805
806                 let mut nonconst_call_permission = false;
807
808                 // Attempting to call a trait method?
809                 if let Some(trait_id) = tcx.trait_of_item(callee) {
810                     trace!("attempting to call a trait method");
811                     if !self.tcx.features().const_trait_impl {
812                         self.check_op(ops::FnCallNonConst);
813                         return;
814                     }
815
816                     let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
817                     let obligation = Obligation::new(
818                         ObligationCause::dummy(),
819                         param_env,
820                         Binder::dummy(TraitPredicate {
821                             trait_ref,
822                             constness: ty::BoundConstness::ConstIfConst,
823                             polarity: ty::ImplPolarity::Positive,
824                         }),
825                     );
826
827                     let implsrc = tcx.infer_ctxt().enter(|infcx| {
828                         let mut selcx =
829                             SelectionContext::with_constness(&infcx, hir::Constness::Const);
830                         selcx.select(&obligation)
831                     });
832
833                     match implsrc {
834                         Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
835                             debug!(
836                                 "const_trait_impl: provided {:?} via where-clause in {:?}",
837                                 trait_ref, param_env
838                             );
839                             return;
840                         }
841                         Ok(Some(ImplSource::UserDefined(data))) => {
842                             let callee_name = tcx.item_name(callee);
843                             if let Some(&did) = tcx
844                                 .associated_item_def_ids(data.impl_def_id)
845                                 .iter()
846                                 .find(|did| tcx.item_name(**did) == callee_name)
847                             {
848                                 // using internal substs is ok here, since this is only
849                                 // used for the `resolve` call below
850                                 substs = InternalSubsts::identity_for_item(tcx, did);
851                                 callee = did;
852                             }
853                         }
854                         _ if !tcx.is_const_fn_raw(callee) => {
855                             // At this point, it is only legal when the caller is marked with
856                             // #[default_method_body_is_const], and the callee is in the same
857                             // trait.
858                             let callee_trait = tcx.trait_of_item(callee);
859                             if callee_trait.is_some() {
860                                 if tcx.has_attr(caller, sym::default_method_body_is_const) {
861                                     if tcx.trait_of_item(caller) == callee_trait {
862                                         nonconst_call_permission = true;
863                                     }
864                                 }
865                             }
866
867                             if !nonconst_call_permission {
868                                 self.check_op(ops::FnCallNonConst);
869                                 return;
870                             }
871                         }
872                         _ => {}
873                     }
874
875                     // Resolve a trait method call to its concrete implementation, which may be in a
876                     // `const` trait impl.
877                     let instance = Instance::resolve(tcx, param_env, callee, substs);
878                     debug!("Resolving ({:?}) -> {:?}", callee, instance);
879                     if let Ok(Some(func)) = instance {
880                         if let InstanceDef::Item(def) = func.def {
881                             callee = def.did;
882                         }
883                     }
884                 }
885
886                 // At this point, we are calling a function, `callee`, whose `DefId` is known...
887
888                 // `begin_panic` and `panic_display` are generic functions that accept
889                 // types other than str. Check to enforce that only str can be used in
890                 // const-eval.
891
892                 // const-eval of the `begin_panic` fn assumes the argument is `&str`
893                 if Some(callee) == tcx.lang_items().begin_panic_fn() {
894                     match args[0].ty(&self.ccx.body.local_decls, tcx).kind() {
895                         ty::Ref(_, ty, _) if ty.is_str() => return,
896                         _ => self.check_op(ops::PanicNonStr),
897                     }
898                 }
899
900                 // const-eval of the `panic_display` fn assumes the argument is `&&str`
901                 if Some(callee) == tcx.lang_items().panic_display() {
902                     match args[0].ty(&self.ccx.body.local_decls, tcx).kind() {
903                         ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) =>
904                         {
905                             return;
906                         }
907                         _ => self.check_op(ops::PanicNonStr),
908                     }
909                 }
910
911                 if Some(callee) == tcx.lang_items().exchange_malloc_fn() {
912                     self.check_op(ops::HeapAllocation);
913                     return;
914                 }
915
916                 // `async` blocks get lowered to `std::future::from_generator(/* a closure */)`.
917                 let is_async_block = Some(callee) == tcx.lang_items().from_generator_fn();
918                 if is_async_block {
919                     let kind = hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block);
920                     self.check_op(ops::Generator(kind));
921                     return;
922                 }
923
924                 let is_intrinsic = tcx.fn_sig(callee).abi() == RustIntrinsic;
925
926                 if !tcx.is_const_fn_raw(callee) {
927                     if tcx.trait_of_item(callee).is_some() {
928                         if tcx.has_attr(callee, sym::default_method_body_is_const) {
929                             // To get to here we must have already found a const impl for the
930                             // trait, but for it to still be non-const can be that the impl is
931                             // using default method bodies.
932                             nonconst_call_permission = true;
933                         }
934                     }
935
936                     if !nonconst_call_permission {
937                         self.check_op(ops::FnCallNonConst);
938                         return;
939                     }
940                 }
941
942                 // If the `const fn` we are trying to call is not const-stable, ensure that we have
943                 // the proper feature gate enabled.
944                 if let Some(gate) = is_unstable_const_fn(tcx, callee) {
945                     trace!(?gate, "calling unstable const fn");
946                     if self.span.allows_unstable(gate) {
947                         return;
948                     }
949
950                     // Calling an unstable function *always* requires that the corresponding gate
951                     // be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
952                     if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) {
953                         self.check_op(ops::FnCallUnstable(callee, Some(gate)));
954                         return;
955                     }
956
957                     // If this crate is not using stability attributes, or the caller is not claiming to be a
958                     // stable `const fn`, that is all that is required.
959                     if !self.ccx.is_const_stable_const_fn() {
960                         trace!("crate not using stability attributes or caller not stably const");
961                         return;
962                     }
963
964                     // Otherwise, we are something const-stable calling a const-unstable fn.
965
966                     if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
967                         trace!("rustc_allow_const_fn_unstable gate active");
968                         return;
969                     }
970
971                     self.check_op(ops::FnCallUnstable(callee, Some(gate)));
972                     return;
973                 }
974
975                 // FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that
976                 // have no `rustc_const_stable` attributes to be const-unstable as well. This
977                 // should be fixed later.
978                 let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none()
979                     && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable());
980                 if callee_is_unstable_unmarked {
981                     trace!("callee_is_unstable_unmarked");
982                     // We do not use `const` modifiers for intrinsic "functions", as intrinsics are
983                     // `extern` funtions, and these have no way to get marked `const`. So instead we
984                     // use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const`
985                     if self.ccx.is_const_stable_const_fn() || is_intrinsic {
986                         self.check_op(ops::FnCallUnstable(callee, None));
987                         return;
988                     }
989                 }
990                 trace!("permitting call");
991             }
992
993             // Forbid all `Drop` terminators unless the place being dropped is a local with no
994             // projections that cannot be `NeedsNonConstDrop`.
995             TerminatorKind::Drop { place: dropped_place, .. }
996             | TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
997                 // If we are checking live drops after drop-elaboration, don't emit duplicate
998                 // errors here.
999                 if super::post_drop_elaboration::checking_enabled(self.ccx) {
1000                     return;
1001                 }
1002
1003                 let mut err_span = self.span;
1004
1005                 let ty_needs_non_const_drop = qualifs::NeedsNonConstDrop::in_any_value_of_ty(
1006                     self.ccx,
1007                     dropped_place.ty(self.body, self.tcx).ty,
1008                 );
1009
1010                 if !ty_needs_non_const_drop {
1011                     return;
1012                 }
1013
1014                 let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
1015                     // Use the span where the local was declared as the span of the drop error.
1016                     err_span = self.body.local_decls[local].source_info.span;
1017                     self.qualifs.needs_non_const_drop(self.ccx, local, location)
1018                 } else {
1019                     true
1020                 };
1021
1022                 if needs_non_const_drop {
1023                     self.check_op_spanned(
1024                         ops::LiveDrop { dropped_at: Some(terminator.source_info.span) },
1025                         err_span,
1026                     );
1027                 }
1028             }
1029
1030             TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
1031
1032             TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
1033                 self.check_op(ops::Generator(hir::GeneratorKind::Gen))
1034             }
1035
1036             TerminatorKind::Abort => {
1037                 // Cleanup blocks are skipped for const checking (see `visit_basic_block_data`).
1038                 span_bug!(self.span, "`Abort` terminator outside of cleanup block")
1039             }
1040
1041             TerminatorKind::Assert { .. }
1042             | TerminatorKind::FalseEdge { .. }
1043             | TerminatorKind::FalseUnwind { .. }
1044             | TerminatorKind::Goto { .. }
1045             | TerminatorKind::Resume
1046             | TerminatorKind::Return
1047             | TerminatorKind::SwitchInt { .. }
1048             | TerminatorKind::Unreachable => {}
1049         }
1050     }
1051 }
1052
1053 fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) {
1054     let ty = body.return_ty();
1055     tcx.infer_ctxt().enter(|infcx| {
1056         let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
1057         let mut fulfillment_cx = traits::FulfillmentContext::new();
1058         let sync_def_id = tcx.require_lang_item(LangItem::Sync, Some(body.span));
1059         fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
1060         if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1061             infcx.report_fulfillment_errors(&err, None, false);
1062         }
1063     });
1064 }
1065
1066 fn place_as_reborrow(
1067     tcx: TyCtxt<'tcx>,
1068     body: &Body<'tcx>,
1069     place: Place<'tcx>,
1070 ) -> Option<PlaceRef<'tcx>> {
1071     match place.as_ref().last_projection() {
1072         Some((place_base, ProjectionElem::Deref)) => {
1073             // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
1074             // that points to the allocation for the static. Don't treat these as reborrows.
1075             if body.local_decls[place_base.local].is_ref_to_static() {
1076                 None
1077             } else {
1078                 // Ensure the type being derefed is a reference and not a raw pointer.
1079                 // This is sufficient to prevent an access to a `static mut` from being marked as a
1080                 // reborrow, even if the check above were to disappear.
1081                 let inner_ty = place_base.ty(body, tcx).ty;
1082
1083                 if let ty::Ref(..) = inner_ty.kind() {
1084                     return Some(place_base);
1085                 } else {
1086                     return None;
1087                 }
1088             }
1089         }
1090         _ => None,
1091     }
1092 }
1093
1094 fn is_int_bool_or_char(ty: Ty<'_>) -> bool {
1095     ty.is_bool() || ty.is_integral() || ty.is_char()
1096 }
1097
1098 fn is_async_fn(ccx: &ConstCx<'_, '_>) -> bool {
1099     ccx.fn_sig().map_or(false, |sig| sig.header.asyncness == hir::IsAsync::Async)
1100 }
1101
1102 fn emit_unstable_in_stable_error(ccx: &ConstCx<'_, '_>, span: Span, gate: Symbol) {
1103     let attr_span = ccx.fn_sig().map_or(ccx.body.span, |sig| sig.span.shrink_to_lo());
1104
1105     ccx.tcx
1106         .sess
1107         .struct_span_err(
1108             span,
1109             &format!("const-stable function cannot use `#[feature({})]`", gate.as_str()),
1110         )
1111         .span_suggestion(
1112             attr_span,
1113             "if it is not part of the public API, make this function unstably const",
1114             concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(),
1115             Applicability::HasPlaceholders,
1116         )
1117         .span_suggestion(
1118             attr_span,
1119             "otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks",
1120             format!("#[rustc_allow_const_fn_unstable({})]\n", gate),
1121             Applicability::MaybeIncorrect,
1122         )
1123         .emit();
1124 }