]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/validation.rs
Rollup merge of #64937 - estebank:dedup-closure-err, r=Centril
[rust.git] / src / librustc_mir / transform / check_consts / validation.rs
1 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
2
3 use rustc::hir::{self, def_id::DefId};
4 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
5 use rustc::mir::*;
6 use rustc::ty::cast::CastTy;
7 use rustc::ty::{self, TyCtxt};
8 use rustc_index::bit_set::BitSet;
9 use rustc_target::spec::abi::Abi;
10 use syntax::symbol::sym;
11 use syntax_pos::Span;
12
13 use std::cell::RefCell;
14 use std::fmt;
15 use std::ops::Deref;
16
17 use crate::dataflow as old_dataflow;
18 use super::{Item, Qualif, is_lang_panic_fn};
19 use super::resolver::{FlowSensitiveResolver, IndirectlyMutableResults, QualifResolver};
20 use super::qualifs::{HasMutInterior, NeedsDrop};
21 use super::ops::{self, NonConstOp};
22
23 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
24 pub enum CheckOpResult {
25     Forbidden,
26     Unleashed,
27     Allowed,
28 }
29
30 /// What kind of item we are in.
31 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
32 pub enum Mode {
33     /// A `static` item.
34     Static,
35     /// A `static mut` item.
36     StaticMut,
37     /// A `const fn` item.
38     ConstFn,
39     /// A `const` item or an anonymous constant (e.g. in array lengths).
40     Const,
41 }
42
43 impl Mode {
44     /// Returns the validation mode for the item with the given `DefId`, or `None` if this item
45     /// does not require validation (e.g. a non-const `fn`).
46     pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Self> {
47         use hir::BodyOwnerKind as HirKind;
48
49         let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
50
51         let mode = match tcx.hir().body_owner_kind(hir_id) {
52             HirKind::Closure => return None,
53
54             HirKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
55             HirKind::Fn => return None,
56
57             HirKind::Const => Mode::Const,
58
59             HirKind::Static(hir::MutImmutable) => Mode::Static,
60             HirKind::Static(hir::MutMutable) => Mode::StaticMut,
61         };
62
63         Some(mode)
64     }
65
66     pub fn is_static(self) -> bool {
67         match self {
68             Mode::Static | Mode::StaticMut => true,
69             Mode::ConstFn | Mode::Const => false,
70         }
71     }
72
73     /// Returns `true` if the value returned by this item must be `Sync`.
74     ///
75     /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway.
76     pub fn requires_sync(self) -> bool {
77         match self {
78             Mode::Static => true,
79             Mode::ConstFn | Mode::Const |  Mode::StaticMut => false,
80         }
81     }
82 }
83
84 impl fmt::Display for Mode {
85     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86         match *self {
87             Mode::Const => write!(f, "constant"),
88             Mode::Static | Mode::StaticMut => write!(f, "static"),
89             Mode::ConstFn => write!(f, "constant function"),
90         }
91     }
92 }
93
94 pub struct Qualifs<'a, 'mir, 'tcx> {
95     has_mut_interior: FlowSensitiveResolver<'a, 'mir, 'tcx, HasMutInterior>,
96     needs_drop: FlowSensitiveResolver<'a, 'mir, 'tcx, NeedsDrop>,
97 }
98
99 pub struct Validator<'a, 'mir, 'tcx> {
100     item: &'a Item<'mir, 'tcx>,
101     qualifs: Qualifs<'a, 'mir, 'tcx>,
102
103     /// The span of the current statement.
104     span: Span,
105
106     /// True if the local was assigned the result of an illegal borrow (`ops::MutBorrow`).
107     ///
108     /// This is used to hide errors from {re,}borrowing the newly-assigned local, instead pointing
109     /// the user to the place where the illegal borrow occurred. This set is only populated once an
110     /// error has been emitted, so it will never cause an erroneous `mir::Body` to pass validation.
111     ///
112     /// FIXME(ecstaticmorse): assert at the end of checking that if `tcx.has_errors() == false`,
113     /// this set is empty. Note that if we start removing locals from
114     /// `derived_from_illegal_borrow`, just checking at the end won't be enough.
115     derived_from_illegal_borrow: BitSet<Local>,
116
117     errors: Vec<(Span, String)>,
118
119     /// Whether to actually emit errors or just store them in `errors`.
120     pub(crate) suppress_errors: bool,
121 }
122
123 impl Deref for Validator<'_, 'mir, 'tcx> {
124     type Target = Item<'mir, 'tcx>;
125
126     fn deref(&self) -> &Self::Target {
127         &self.item
128     }
129 }
130
131 pub fn compute_indirectly_mutable_locals<'mir, 'tcx>(
132     item: &Item<'mir, 'tcx>,
133 ) -> RefCell<IndirectlyMutableResults<'mir, 'tcx>> {
134     let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
135
136     let indirectly_mutable_locals = old_dataflow::do_dataflow(
137         item.tcx,
138         item.body,
139         item.def_id,
140         &[],
141         &dead_unwinds,
142         old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
143         |_, local| old_dataflow::DebugFormatted::new(&local),
144     );
145
146     let indirectly_mutable_locals = old_dataflow::DataflowResultsCursor::new(
147         indirectly_mutable_locals,
148         item.body,
149     );
150
151     RefCell::new(indirectly_mutable_locals)
152 }
153
154 impl Validator<'a, 'mir, 'tcx> {
155     pub fn new(
156         item: &'a Item<'mir, 'tcx>,
157         indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
158     ) -> Self {
159         let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
160
161         let needs_drop = FlowSensitiveResolver::new(
162             NeedsDrop,
163             item,
164             indirectly_mutable_locals,
165             &dead_unwinds,
166         );
167
168         let has_mut_interior = FlowSensitiveResolver::new(
169             HasMutInterior,
170             item,
171             indirectly_mutable_locals,
172             &dead_unwinds,
173         );
174
175         let qualifs = Qualifs {
176             needs_drop,
177             has_mut_interior,
178         };
179
180         Validator {
181             span: item.body.span,
182             item,
183             qualifs,
184             errors: vec![],
185             derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()),
186             suppress_errors: false,
187         }
188     }
189
190     /// Resets the `QualifResolver`s used by this `Validator` and returns them so they can be
191     /// reused.
192     pub fn into_qualifs(mut self) -> Qualifs<'a, 'mir, 'tcx> {
193         self.qualifs.needs_drop.reset();
194         self.qualifs.has_mut_interior.reset();
195         self.qualifs
196     }
197
198     pub fn take_errors(&mut self) -> Vec<(Span, String)> {
199         std::mem::replace(&mut self.errors, vec![])
200     }
201
202     /// Emits an error at the given `span` if an expression cannot be evaluated in the current
203     /// context. Returns `Forbidden` if an error was emitted.
204     pub fn check_op_spanned<O>(&mut self, op: O, span: Span) -> CheckOpResult
205     where
206         O: NonConstOp + fmt::Debug
207     {
208         trace!("check_op: op={:?}", op);
209
210         if op.is_allowed_in_item(self) {
211             return CheckOpResult::Allowed;
212         }
213
214         // If an operation is supported in miri (and is not already controlled by a feature gate) it
215         // can be turned on with `-Zunleash-the-miri-inside-of-you`.
216         let is_unleashable = O::IS_SUPPORTED_IN_MIRI
217             && O::feature_gate(self.tcx).is_none();
218
219         if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
220             self.tcx.sess.span_warn(span, "skipping const checks");
221             return CheckOpResult::Unleashed;
222         }
223
224         if !self.suppress_errors {
225             op.emit_error(self, span);
226         }
227
228         self.errors.push((span, format!("{:?}", op)));
229         CheckOpResult::Forbidden
230     }
231
232     /// Emits an error if an expression cannot be evaluated in the current context.
233     pub fn check_op(&mut self, op: impl NonConstOp + fmt::Debug) -> CheckOpResult {
234         let span = self.span;
235         self.check_op_spanned(op, span)
236     }
237 }
238
239 impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
240     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
241         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
242
243         // Check nested operands and places.
244         if let Rvalue::Ref(_, kind, ref place) = *rvalue {
245             // Special-case reborrows to be more like a copy of a reference.
246             let mut reborrow_place = None;
247             if let box [proj_base @ .., elem] = &place.projection {
248                 if *elem == ProjectionElem::Deref {
249                     let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
250                     if let ty::Ref(..) = base_ty.kind {
251                         reborrow_place = Some(proj_base);
252                     }
253                 }
254             }
255
256             if let Some(proj) = reborrow_place {
257                 let ctx = match kind {
258                     BorrowKind::Shared => PlaceContext::NonMutatingUse(
259                         NonMutatingUseContext::SharedBorrow,
260                     ),
261                     BorrowKind::Shallow => PlaceContext::NonMutatingUse(
262                         NonMutatingUseContext::ShallowBorrow,
263                     ),
264                     BorrowKind::Unique => PlaceContext::NonMutatingUse(
265                         NonMutatingUseContext::UniqueBorrow,
266                     ),
267                     BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
268                         MutatingUseContext::Borrow,
269                     ),
270                 };
271                 self.visit_place_base(&place.base, ctx, location);
272                 self.visit_projection(&place.base, proj, ctx, location);
273             } else {
274                 self.super_rvalue(rvalue, location);
275             }
276         } else {
277             self.super_rvalue(rvalue, location);
278         }
279
280         match *rvalue {
281             Rvalue::Use(_) |
282             Rvalue::Repeat(..) |
283             Rvalue::UnaryOp(UnOp::Neg, _) |
284             Rvalue::UnaryOp(UnOp::Not, _) |
285             Rvalue::NullaryOp(NullOp::SizeOf, _) |
286             Rvalue::CheckedBinaryOp(..) |
287             Rvalue::Cast(CastKind::Pointer(_), ..) |
288             Rvalue::Discriminant(..) |
289             Rvalue::Len(_) |
290             Rvalue::Ref(..) |
291             Rvalue::Aggregate(..) => {}
292
293             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
294                 let operand_ty = operand.ty(self.body, self.tcx);
295                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
296                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
297
298                 if let (CastTy::Ptr(_), CastTy::Int(_))
299                      | (CastTy::FnPtr,  CastTy::Int(_)) = (cast_in, cast_out) {
300                     self.check_op(ops::RawPtrToIntCast);
301                 }
302             }
303
304             Rvalue::BinaryOp(op, ref lhs, _) => {
305                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
306                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
307                             op == BinOp::Le || op == BinOp::Lt ||
308                             op == BinOp::Ge || op == BinOp::Gt ||
309                             op == BinOp::Offset);
310
311
312                     self.check_op(ops::RawPtrComparison);
313                 }
314             }
315
316             Rvalue::NullaryOp(NullOp::Box, _) => {
317                 self.check_op(ops::HeapAllocation);
318             }
319         }
320     }
321
322     fn visit_place_base(
323         &mut self,
324         place_base: &PlaceBase<'tcx>,
325         context: PlaceContext,
326         location: Location,
327     ) {
328         trace!(
329             "visit_place_base: place_base={:?} context={:?} location={:?}",
330             place_base,
331             context,
332             location,
333         );
334         self.super_place_base(place_base, context, location);
335
336         match place_base {
337             PlaceBase::Local(_) => {}
338             PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
339                 bug!("Promotion must be run after const validation");
340             }
341
342             PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
343                 let is_thread_local = self.tcx.has_attr(*def_id, sym::thread_local);
344                 if is_thread_local {
345                     self.check_op(ops::ThreadLocalAccess);
346                 } else if self.mode == Mode::Static && context.is_mutating_use() {
347                     // this is not strictly necessary as miri will also bail out
348                     // For interior mutability we can't really catch this statically as that
349                     // goes through raw pointers and intermediate temporaries, so miri has
350                     // to catch this anyway
351
352                     self.tcx.sess.span_err(
353                         self.span,
354                         "cannot mutate statics in the initializer of another static",
355                     );
356                 } else {
357                     self.check_op(ops::StaticAccess);
358                 }
359             }
360         }
361     }
362
363     fn visit_assign(&mut self, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
364         trace!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
365
366         // Error on mutable borrows or shared borrows of values with interior mutability.
367         //
368         // This replicates the logic at the start of `assign` in the old const checker.  Note that
369         // it depends on `HasMutInterior` being set for mutable borrows as well as values with
370         // interior mutability.
371         if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
372             let rvalue_has_mut_interior = HasMutInterior::in_rvalue(
373                 &self.item,
374                 self.qualifs.has_mut_interior.get(),
375                 rvalue,
376             );
377
378             if rvalue_has_mut_interior {
379                 let is_derived_from_illegal_borrow = match *borrowed_place {
380                     // If an unprojected local was borrowed and its value was the result of an
381                     // illegal borrow, suppress this error and mark the result of this borrow as
382                     // illegal as well.
383                     Place { base: PlaceBase::Local(borrowed_local), projection: box [] }
384                         if self.derived_from_illegal_borrow.contains(borrowed_local) => true,
385
386                     // Otherwise proceed normally: check the legality of a mutable borrow in this
387                     // context.
388                     _ => self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden,
389                 };
390
391                 // When the target of the assignment is a local with no projections, mark it as
392                 // derived from an illegal borrow if necessary.
393                 //
394                 // FIXME: should we also clear `derived_from_illegal_borrow` when a local is
395                 // assigned a new value?
396                 if is_derived_from_illegal_borrow {
397                     if let Place { base: PlaceBase::Local(dest), projection: box [] } = *dest {
398                         self.derived_from_illegal_borrow.insert(dest);
399                     }
400                 }
401             }
402         }
403
404         self.super_assign(dest, rvalue, location);
405     }
406
407     fn visit_projection(
408         &mut self,
409         place_base: &PlaceBase<'tcx>,
410         proj: &[PlaceElem<'tcx>],
411         context: PlaceContext,
412         location: Location,
413     ) {
414         trace!(
415             "visit_place_projection: proj={:?} context={:?} location={:?}",
416             proj,
417             context,
418             location,
419         );
420         self.super_projection(place_base, proj, context, location);
421
422         let (elem, proj_base) = match proj.split_last() {
423             Some(x) => x,
424             None => return,
425         };
426
427         match elem {
428             ProjectionElem::Deref => {
429                 if context.is_mutating_use() {
430                     self.check_op(ops::MutDeref);
431                 }
432
433                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
434                 if let ty::RawPtr(_) = base_ty.kind {
435                     self.check_op(ops::RawPtrDeref);
436                 }
437             }
438
439             ProjectionElem::ConstantIndex {..} |
440             ProjectionElem::Subslice {..} |
441             ProjectionElem::Field(..) |
442             ProjectionElem::Index(_) => {
443                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
444                 match base_ty.ty_adt_def() {
445                     Some(def) if def.is_union() => {
446                         self.check_op(ops::UnionAccess);
447                     }
448
449                     _ => {}
450                 }
451             }
452
453             ProjectionElem::Downcast(..) => {
454                 self.check_op(ops::Downcast);
455             }
456         }
457     }
458
459
460     fn visit_source_info(&mut self, source_info: &SourceInfo) {
461         trace!("visit_source_info: source_info={:?}", source_info);
462         self.span = source_info.span;
463     }
464
465     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
466         trace!("visit_statement: statement={:?} location={:?}", statement, location);
467
468         self.qualifs.needs_drop.visit_statement(statement, location);
469         self.qualifs.has_mut_interior.visit_statement(statement, location);
470
471         match statement.kind {
472             StatementKind::Assign(..) => {
473                 self.super_statement(statement, location);
474             }
475             StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
476                 self.check_op(ops::IfOrMatch);
477             }
478             // FIXME(eddyb) should these really do nothing?
479             StatementKind::FakeRead(..) |
480             StatementKind::SetDiscriminant { .. } |
481             StatementKind::StorageLive(_) |
482             StatementKind::StorageDead(_) |
483             StatementKind::InlineAsm {..} |
484             StatementKind::Retag { .. } |
485             StatementKind::AscribeUserType(..) |
486             StatementKind::Nop => {}
487         }
488     }
489
490     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
491         trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
492
493         self.qualifs.needs_drop.visit_terminator(terminator, location);
494         self.qualifs.has_mut_interior.visit_terminator(terminator, location);
495
496         self.super_terminator(terminator, location);
497     }
498
499     fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) {
500         trace!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
501         self.super_terminator_kind(kind, location);
502
503         match kind {
504             TerminatorKind::Call { func, .. } => {
505                 let fn_ty = func.ty(self.body, self.tcx);
506
507                 let def_id = match fn_ty.kind {
508                     ty::FnDef(def_id, _) => def_id,
509
510                     ty::FnPtr(_) => {
511                         self.check_op(ops::FnCallIndirect);
512                         return;
513                     }
514                     _ => {
515                         self.check_op(ops::FnCallOther);
516                         return;
517                     }
518                 };
519
520                 // At this point, we are calling a function whose `DefId` is known...
521
522                 if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = self.tcx.fn_sig(def_id).abi() {
523                     assert!(!self.tcx.is_const_fn(def_id));
524
525                     if self.tcx.item_name(def_id) == sym::transmute {
526                         self.check_op(ops::Transmute);
527                         return;
528                     }
529
530                     // To preserve the current semantics, we return early, allowing all
531                     // intrinsics (except `transmute`) to pass unchecked to miri.
532                     //
533                     // FIXME: We should keep a whitelist of allowed intrinsics (or at least a
534                     // blacklist of unimplemented ones) and fail here instead.
535                     return;
536                 }
537
538                 if self.tcx.is_const_fn(def_id) {
539                     return;
540                 }
541
542                 if is_lang_panic_fn(self.tcx, def_id) {
543                     self.check_op(ops::Panic);
544                 } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
545                     // Exempt unstable const fns inside of macros with
546                     // `#[allow_internal_unstable]`.
547                     if !self.span.allows_unstable(feature) {
548                         self.check_op(ops::FnCallUnstable(def_id, feature));
549                     }
550                 } else {
551                     self.check_op(ops::FnCallNonConst(def_id));
552                 }
553
554             }
555
556             // Forbid all `Drop` terminators unless the place being dropped is a local with no
557             // projections that cannot be `NeedsDrop`.
558             | TerminatorKind::Drop { location: dropped_place, .. }
559             | TerminatorKind::DropAndReplace { location: dropped_place, .. }
560             => {
561                 let mut err_span = self.span;
562
563                 // Check to see if the type of this place can ever have a drop impl. If not, this
564                 // `Drop` terminator is frivolous.
565                 let ty_needs_drop = dropped_place
566                     .ty(self.body, self.tcx)
567                     .ty
568                     .needs_drop(self.tcx, self.param_env);
569
570                 if !ty_needs_drop {
571                     return;
572                 }
573
574                 let needs_drop = if let Place {
575                     base: PlaceBase::Local(local),
576                     projection: box [],
577                 } = *dropped_place {
578                     // Use the span where the local was declared as the span of the drop error.
579                     err_span = self.body.local_decls[local].source_info.span;
580                     self.qualifs.needs_drop.contains(local)
581                 } else {
582                     true
583                 };
584
585                 if needs_drop {
586                     self.check_op_spanned(ops::LiveDrop, err_span);
587                 }
588             }
589
590             _ => {}
591         }
592     }
593 }