]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/validation.rs
Rollup merge of #75485 - RalfJung:pin, r=nagisa
[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_errors::struct_span_err;
4 use rustc_hir::{self as hir, lang_items};
5 use rustc_hir::{def_id::DefId, HirId};
6 use rustc_infer::infer::TyCtxtInferExt;
7 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
8 use rustc_middle::mir::*;
9 use rustc_middle::ty::cast::CastTy;
10 use rustc_middle::ty::{self, Instance, InstanceDef, TyCtxt};
11 use rustc_span::Span;
12 use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
13 use rustc_trait_selection::traits::{self, TraitEngine};
14
15 use std::borrow::Cow;
16 use std::ops::Deref;
17
18 use super::ops::{self, NonConstOp};
19 use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
20 use super::resolver::FlowSensitiveAnalysis;
21 use super::{is_lang_panic_fn, ConstCx, Qualif};
22 use crate::const_eval::{is_const_fn, is_unstable_const_fn};
23 use crate::dataflow::impls::MaybeMutBorrowedLocals;
24 use crate::dataflow::{self, Analysis};
25
26 // We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated
27 // through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals`
28 // kills locals upon `StorageDead` because a local will never be used after a `StorageDead`.
29 type IndirectlyMutableResults<'mir, 'tcx> =
30     dataflow::ResultsCursor<'mir, 'tcx, MaybeMutBorrowedLocals<'mir, 'tcx>>;
31
32 type QualifResults<'mir, 'tcx, Q> =
33     dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
34
35 #[derive(Default)]
36 pub struct Qualifs<'mir, 'tcx> {
37     has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
38     needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
39     indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
40 }
41
42 impl Qualifs<'mir, 'tcx> {
43     pub fn indirectly_mutable(
44         &mut self,
45         ccx: &'mir ConstCx<'mir, 'tcx>,
46         local: Local,
47         location: Location,
48     ) -> bool {
49         let indirectly_mutable = self.indirectly_mutable.get_or_insert_with(|| {
50             let ConstCx { tcx, body, def_id, param_env, .. } = *ccx;
51
52             // We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not
53             // allowed in a const.
54             //
55             // FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this
56             // without breaking stable code?
57             MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env)
58                 .unsound_ignore_borrow_on_drop()
59                 .into_engine(tcx, &body, def_id.to_def_id())
60                 .iterate_to_fixpoint()
61                 .into_results_cursor(&body)
62         });
63
64         indirectly_mutable.seek_before_primary_effect(location);
65         indirectly_mutable.get().contains(local)
66     }
67
68     /// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
69     ///
70     /// Only updates the cursor if absolutely necessary
71     pub fn needs_drop(
72         &mut self,
73         ccx: &'mir ConstCx<'mir, 'tcx>,
74         local: Local,
75         location: Location,
76     ) -> bool {
77         let ty = ccx.body.local_decls[local].ty;
78         if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
79             return false;
80         }
81
82         let needs_drop = self.needs_drop.get_or_insert_with(|| {
83             let ConstCx { tcx, body, def_id, .. } = *ccx;
84
85             FlowSensitiveAnalysis::new(NeedsDrop, ccx)
86                 .into_engine(tcx, &body, def_id.to_def_id())
87                 .iterate_to_fixpoint()
88                 .into_results_cursor(&body)
89         });
90
91         needs_drop.seek_before_primary_effect(location);
92         needs_drop.get().contains(local) || self.indirectly_mutable(ccx, local, location)
93     }
94
95     /// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
96     ///
97     /// Only updates the cursor if absolutely necessary.
98     pub fn has_mut_interior(
99         &mut self,
100         ccx: &'mir ConstCx<'mir, 'tcx>,
101         local: Local,
102         location: Location,
103     ) -> bool {
104         let ty = ccx.body.local_decls[local].ty;
105         if !HasMutInterior::in_any_value_of_ty(ccx, ty) {
106             return false;
107         }
108
109         let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| {
110             let ConstCx { tcx, body, def_id, .. } = *ccx;
111
112             FlowSensitiveAnalysis::new(HasMutInterior, ccx)
113                 .into_engine(tcx, &body, def_id.to_def_id())
114                 .iterate_to_fixpoint()
115                 .into_results_cursor(&body)
116         });
117
118         has_mut_interior.seek_before_primary_effect(location);
119         has_mut_interior.get().contains(local) || self.indirectly_mutable(ccx, local, location)
120     }
121
122     fn in_return_place(&mut self, ccx: &'mir ConstCx<'mir, 'tcx>) -> ConstQualifs {
123         // Find the `Return` terminator if one exists.
124         //
125         // If no `Return` terminator exists, this MIR is divergent. Just return the conservative
126         // qualifs for the return type.
127         let return_block = ccx
128             .body
129             .basic_blocks()
130             .iter_enumerated()
131             .find(|(_, block)| match block.terminator().kind {
132                 TerminatorKind::Return => true,
133                 _ => false,
134             })
135             .map(|(bb, _)| bb);
136
137         let return_block = match return_block {
138             None => return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty()),
139             Some(bb) => bb,
140         };
141
142         let return_loc = ccx.body.terminator_loc(return_block);
143
144         let custom_eq = match ccx.const_kind() {
145             // We don't care whether a `const fn` returns a value that is not structurally
146             // matchable. Functions calls are opaque and always use type-based qualification, so
147             // this value should never be used.
148             hir::ConstContext::ConstFn => true,
149
150             // If we know that all values of the return type are structurally matchable, there's no
151             // need to run dataflow.
152             _ if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => false,
153
154             hir::ConstContext::Const | hir::ConstContext::Static(_) => {
155                 let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx)
156                     .into_engine(ccx.tcx, &ccx.body, ccx.def_id.to_def_id())
157                     .iterate_to_fixpoint()
158                     .into_results_cursor(&ccx.body);
159
160                 cursor.seek_after_primary_effect(return_loc);
161                 cursor.contains(RETURN_PLACE)
162             }
163         };
164
165         ConstQualifs {
166             needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
167             has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
168             custom_eq,
169         }
170     }
171 }
172
173 pub struct Validator<'mir, 'tcx> {
174     ccx: &'mir ConstCx<'mir, 'tcx>,
175     qualifs: Qualifs<'mir, 'tcx>,
176
177     /// The span of the current statement.
178     span: Span,
179 }
180
181 impl Deref for Validator<'mir, 'tcx> {
182     type Target = ConstCx<'mir, 'tcx>;
183
184     fn deref(&self) -> &Self::Target {
185         &self.ccx
186     }
187 }
188
189 impl Validator<'mir, 'tcx> {
190     pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
191         Validator { span: ccx.body.span, ccx, qualifs: Default::default() }
192     }
193
194     pub fn check_body(&mut self) {
195         let ConstCx { tcx, body, def_id, const_kind, .. } = *self.ccx;
196
197         let use_min_const_fn_checks = (const_kind == Some(hir::ConstContext::ConstFn)
198             && crate::const_eval::is_min_const_fn(tcx, def_id.to_def_id()))
199             && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
200
201         if use_min_const_fn_checks {
202             // Enforce `min_const_fn` for stable `const fn`s.
203             use crate::transform::qualify_min_const_fn::is_min_const_fn;
204             if let Err((span, err)) = is_min_const_fn(tcx, def_id.to_def_id(), &body) {
205                 error_min_const_fn_violation(tcx, span, err);
206                 return;
207             }
208         }
209
210         self.visit_body(&body);
211
212         // Ensure that the end result is `Sync` in a non-thread local `static`.
213         let should_check_for_sync = const_kind
214             == Some(hir::ConstContext::Static(hir::Mutability::Not))
215             && !tcx.is_thread_local_static(def_id.to_def_id());
216
217         if should_check_for_sync {
218             let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
219             check_return_ty_is_sync(tcx, &body, hir_id);
220         }
221     }
222
223     pub fn qualifs_in_return_place(&mut self) -> ConstQualifs {
224         self.qualifs.in_return_place(self.ccx)
225     }
226
227     /// Emits an error if an expression cannot be evaluated in the current context.
228     pub fn check_op(&mut self, op: impl NonConstOp) {
229         ops::non_const(self.ccx, op, self.span);
230     }
231
232     /// Emits an error at the given `span` if an expression cannot be evaluated in the current
233     /// context.
234     pub fn check_op_spanned(&mut self, op: impl NonConstOp, span: Span) {
235         ops::non_const(self.ccx, op, span);
236     }
237
238     fn check_static(&mut self, def_id: DefId, span: Span) {
239         assert!(
240             !self.tcx.is_thread_local_static(def_id),
241             "tls access is checked in `Rvalue::ThreadLocalRef"
242         );
243         self.check_op_spanned(ops::StaticAccess, span)
244     }
245 }
246
247 impl Visitor<'tcx> for Validator<'mir, 'tcx> {
248     fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &BasicBlockData<'tcx>) {
249         trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
250
251         // Just as the old checker did, we skip const-checking basic blocks on the unwind path.
252         // These blocks often drop locals that would otherwise be returned from the function.
253         //
254         // FIXME: This shouldn't be unsound since a panic at compile time will cause a compiler
255         // error anyway, but maybe we should do more here?
256         if block.is_cleanup {
257             return;
258         }
259
260         self.super_basic_block_data(bb, block);
261     }
262
263     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
264         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
265
266         // Special-case reborrows to be more like a copy of a reference.
267         match *rvalue {
268             Rvalue::Ref(_, kind, place) => {
269                 if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) {
270                     let ctx = match kind {
271                         BorrowKind::Shared => {
272                             PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
273                         }
274                         BorrowKind::Shallow => {
275                             PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
276                         }
277                         BorrowKind::Unique => {
278                             PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
279                         }
280                         BorrowKind::Mut { .. } => {
281                             PlaceContext::MutatingUse(MutatingUseContext::Borrow)
282                         }
283                     };
284                     self.visit_local(&place.local, ctx, location);
285                     self.visit_projection(place.local, reborrowed_proj, ctx, location);
286                     return;
287                 }
288             }
289             Rvalue::AddressOf(mutbl, place) => {
290                 if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) {
291                     let ctx = match mutbl {
292                         Mutability::Not => {
293                             PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
294                         }
295                         Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::AddressOf),
296                     };
297                     self.visit_local(&place.local, ctx, location);
298                     self.visit_projection(place.local, reborrowed_proj, ctx, location);
299                     return;
300                 }
301             }
302             _ => {}
303         }
304
305         self.super_rvalue(rvalue, location);
306
307         match *rvalue {
308             Rvalue::ThreadLocalRef(_) => self.check_op(ops::ThreadLocalAccess),
309
310             Rvalue::Use(_)
311             | Rvalue::Repeat(..)
312             | Rvalue::UnaryOp(UnOp::Neg, _)
313             | Rvalue::UnaryOp(UnOp::Not, _)
314             | Rvalue::NullaryOp(NullOp::SizeOf, _)
315             | Rvalue::CheckedBinaryOp(..)
316             | Rvalue::Cast(CastKind::Pointer(_), ..)
317             | Rvalue::Discriminant(..)
318             | Rvalue::Len(_)
319             | Rvalue::Aggregate(..) => {}
320
321             Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
322             | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
323                 let ty = place.ty(self.body, self.tcx).ty;
324                 let is_allowed = match ty.kind {
325                     // Inside a `static mut`, `&mut [...]` is allowed.
326                     ty::Array(..) | ty::Slice(_)
327                         if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) =>
328                     {
329                         true
330                     }
331
332                     // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
333                     // that this is merely a ZST and it is already eligible for promotion.
334                     // This may require an RFC?
335                     /*
336                     ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
337                         => true,
338                     */
339                     _ => false,
340                 };
341
342                 if !is_allowed {
343                     if let BorrowKind::Mut { .. } = kind {
344                         self.check_op(ops::MutBorrow);
345                     } else {
346                         self.check_op(ops::CellBorrow);
347                     }
348                 }
349             }
350
351             Rvalue::AddressOf(Mutability::Mut, _) => self.check_op(ops::MutAddressOf),
352
353             Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)
354             | Rvalue::AddressOf(Mutability::Not, ref place) => {
355                 let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
356                     &self.ccx,
357                     &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
358                     place.as_ref(),
359                 );
360
361                 if borrowed_place_has_mut_interior {
362                     self.check_op(ops::CellBorrow);
363                 }
364             }
365
366             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
367                 let operand_ty = operand.ty(self.body, self.tcx);
368                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
369                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
370
371                 if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
372                     self.check_op(ops::RawPtrToIntCast);
373                 }
374             }
375
376             Rvalue::BinaryOp(op, ref lhs, _) => {
377                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
378                     assert!(
379                         op == BinOp::Eq
380                             || op == BinOp::Ne
381                             || op == BinOp::Le
382                             || op == BinOp::Lt
383                             || op == BinOp::Ge
384                             || op == BinOp::Gt
385                             || op == BinOp::Offset
386                     );
387
388                     self.check_op(ops::RawPtrComparison);
389                 }
390             }
391
392             Rvalue::NullaryOp(NullOp::Box, _) => {
393                 self.check_op(ops::HeapAllocation);
394             }
395         }
396     }
397
398     fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
399         self.super_operand(op, location);
400         if let Operand::Constant(c) = op {
401             if let Some(def_id) = c.check_static_ptr(self.tcx) {
402                 self.check_static(def_id, self.span);
403             }
404         }
405     }
406     fn visit_projection_elem(
407         &mut self,
408         place_local: Local,
409         proj_base: &[PlaceElem<'tcx>],
410         elem: PlaceElem<'tcx>,
411         context: PlaceContext,
412         location: Location,
413     ) {
414         trace!(
415             "visit_projection_elem: place_local={:?} proj_base={:?} elem={:?} \
416             context={:?} location={:?}",
417             place_local,
418             proj_base,
419             elem,
420             context,
421             location,
422         );
423
424         self.super_projection_elem(place_local, proj_base, elem, context, location);
425
426         match elem {
427             ProjectionElem::Deref => {
428                 let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
429                 if let ty::RawPtr(_) = base_ty.kind {
430                     if proj_base.is_empty() {
431                         if let (local, []) = (place_local, proj_base) {
432                             let decl = &self.body.local_decls[local];
433                             if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
434                                 let span = decl.source_info.span;
435                                 self.check_static(def_id, span);
436                                 return;
437                             }
438                         }
439                     }
440                     self.check_op(ops::RawPtrDeref);
441                 }
442
443                 if context.is_mutating_use() {
444                     self.check_op(ops::MutDeref);
445                 }
446             }
447
448             ProjectionElem::ConstantIndex { .. }
449             | ProjectionElem::Downcast(..)
450             | ProjectionElem::Subslice { .. }
451             | ProjectionElem::Field(..)
452             | ProjectionElem::Index(_) => {
453                 let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
454                 match base_ty.ty_adt_def() {
455                     Some(def) if def.is_union() => {
456                         self.check_op(ops::UnionAccess);
457                     }
458
459                     _ => {}
460                 }
461             }
462         }
463     }
464
465     fn visit_source_info(&mut self, source_info: &SourceInfo) {
466         trace!("visit_source_info: source_info={:?}", source_info);
467         self.span = source_info.span;
468     }
469
470     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
471         trace!("visit_statement: statement={:?} location={:?}", statement, location);
472
473         match statement.kind {
474             StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } => {
475                 self.super_statement(statement, location);
476             }
477
478             StatementKind::LlvmInlineAsm { .. } => {
479                 self.super_statement(statement, location);
480                 self.check_op(ops::InlineAsm);
481             }
482
483             StatementKind::FakeRead(..)
484             | StatementKind::StorageLive(_)
485             | StatementKind::StorageDead(_)
486             | StatementKind::Retag { .. }
487             | StatementKind::AscribeUserType(..)
488             | StatementKind::Nop => {}
489         }
490     }
491
492     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
493         trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
494         self.super_terminator(terminator, location);
495
496         match &terminator.kind {
497             TerminatorKind::Call { func, .. } => {
498                 let fn_ty = func.ty(self.body, self.tcx);
499
500                 let (def_id, substs) = match fn_ty.kind {
501                     ty::FnDef(def_id, substs) => (def_id, substs),
502
503                     ty::FnPtr(_) => {
504                         self.check_op(ops::FnCallIndirect);
505                         return;
506                     }
507                     _ => {
508                         span_bug!(terminator.source_info.span, "invalid callee of type {:?}", fn_ty)
509                     }
510                 };
511
512                 // At this point, we are calling a function whose `DefId` is known...
513                 if is_const_fn(self.tcx, def_id) {
514                     return;
515                 }
516
517                 // See if this is a trait method for a concrete type whose impl of that trait is
518                 // `const`.
519                 if self.tcx.features().const_trait_impl {
520                     let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs);
521                     debug!("Resolving ({:?}) -> {:?}", def_id, instance);
522                     if let Ok(Some(func)) = instance {
523                         if let InstanceDef::Item(def) = func.def {
524                             if is_const_fn(self.tcx, def.did) {
525                                 return;
526                             }
527                         }
528                     }
529                 }
530
531                 if is_lang_panic_fn(self.tcx, def_id) {
532                     self.check_op(ops::Panic);
533                 } else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {
534                     // Exempt unstable const fns inside of macros or functions with
535                     // `#[allow_internal_unstable]`.
536                     use crate::transform::qualify_min_const_fn::lib_feature_allowed;
537                     if !self.span.allows_unstable(feature)
538                         && !lib_feature_allowed(self.tcx, self.def_id.to_def_id(), feature)
539                     {
540                         self.check_op(ops::FnCallUnstable(def_id, feature));
541                     }
542                 } else {
543                     self.check_op(ops::FnCallNonConst(def_id));
544                 }
545             }
546
547             // Forbid all `Drop` terminators unless the place being dropped is a local with no
548             // projections that cannot be `NeedsDrop`.
549             TerminatorKind::Drop { place: dropped_place, .. }
550             | TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
551                 // If we are checking live drops after drop-elaboration, don't emit duplicate
552                 // errors here.
553                 if super::post_drop_elaboration::checking_enabled(self.tcx) {
554                     return;
555                 }
556
557                 let mut err_span = self.span;
558
559                 // Check to see if the type of this place can ever have a drop impl. If not, this
560                 // `Drop` terminator is frivolous.
561                 let ty_needs_drop =
562                     dropped_place.ty(self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env);
563
564                 if !ty_needs_drop {
565                     return;
566                 }
567
568                 let needs_drop = if let Some(local) = dropped_place.as_local() {
569                     // Use the span where the local was declared as the span of the drop error.
570                     err_span = self.body.local_decls[local].source_info.span;
571                     self.qualifs.needs_drop(self.ccx, local, location)
572                 } else {
573                     true
574                 };
575
576                 if needs_drop {
577                     self.check_op_spanned(
578                         ops::LiveDrop(Some(terminator.source_info.span)),
579                         err_span,
580                     );
581                 }
582             }
583
584             TerminatorKind::InlineAsm { .. } => {
585                 self.check_op(ops::InlineAsm);
586             }
587
588             // FIXME: Some of these are only caught by `min_const_fn`, but should error here
589             // instead.
590             TerminatorKind::Abort
591             | TerminatorKind::Assert { .. }
592             | TerminatorKind::FalseEdge { .. }
593             | TerminatorKind::FalseUnwind { .. }
594             | TerminatorKind::GeneratorDrop
595             | TerminatorKind::Goto { .. }
596             | TerminatorKind::Resume
597             | TerminatorKind::Return
598             | TerminatorKind::SwitchInt { .. }
599             | TerminatorKind::Unreachable
600             | TerminatorKind::Yield { .. } => {}
601         }
602     }
603 }
604
605 fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
606     struct_span_err!(tcx.sess, span, E0723, "{}", msg)
607         .note(
608             "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
609              for more information",
610         )
611         .help("add `#![feature(const_fn)]` to the crate attributes to enable")
612         .emit();
613 }
614
615 fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) {
616     let ty = body.return_ty();
617     tcx.infer_ctxt().enter(|infcx| {
618         let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
619         let mut fulfillment_cx = traits::FulfillmentContext::new();
620         let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
621         fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
622         if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
623             infcx.report_fulfillment_errors(&err, None, false);
624         }
625     });
626 }
627
628 fn place_as_reborrow(
629     tcx: TyCtxt<'tcx>,
630     body: &Body<'tcx>,
631     place: Place<'tcx>,
632 ) -> Option<&'a [PlaceElem<'tcx>]> {
633     place.projection.split_last().and_then(|(outermost, inner)| {
634         if outermost != &ProjectionElem::Deref {
635             return None;
636         }
637
638         // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
639         // that points to the allocation for the static. Don't treat these as reborrows.
640         if body.local_decls[place.local].is_ref_to_static() {
641             return None;
642         }
643
644         // Ensure the type being derefed is a reference and not a raw pointer.
645         //
646         // This is sufficient to prevent an access to a `static mut` from being marked as a
647         // reborrow, even if the check above were to disappear.
648         let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty;
649         match inner_ty.kind {
650             ty::Ref(..) => Some(inner),
651             _ => None,
652         }
653     })
654 }