]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. |
2 | ||
60c5eb7d XL |
3 | use rustc::hir::{HirId, def_id::DefId}; |
4 | use rustc::middle::lang_items; | |
e74abb32 XL |
5 | use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext}; |
6 | use rustc::mir::*; | |
60c5eb7d | 7 | use rustc::traits::{self, TraitEngine}; |
e74abb32 | 8 | use rustc::ty::cast::CastTy; |
60c5eb7d | 9 | use rustc::ty::{self, TyCtxt}; |
e74abb32 | 10 | use rustc_index::bit_set::BitSet; |
60c5eb7d | 11 | use rustc_error_codes::*; |
e74abb32 XL |
12 | use syntax::symbol::sym; |
13 | use syntax_pos::Span; | |
14 | ||
60c5eb7d | 15 | use std::borrow::Cow; |
e74abb32 XL |
16 | use std::ops::Deref; |
17 | ||
18 | use crate::dataflow::{self as old_dataflow, generic as dataflow}; | |
19 | use self::old_dataflow::IndirectlyMutableLocals; | |
20 | use super::ops::{self, NonConstOp}; | |
60c5eb7d | 21 | use super::qualifs::{self, HasMutInterior, NeedsDrop}; |
e74abb32 XL |
22 | use super::resolver::FlowSensitiveAnalysis; |
23 | use super::{ConstKind, Item, Qualif, is_lang_panic_fn}; | |
24 | ||
e74abb32 XL |
25 | pub type IndirectlyMutableResults<'mir, 'tcx> = |
26 | old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>; | |
27 | ||
28 | struct QualifCursor<'a, 'mir, 'tcx, Q: Qualif> { | |
29 | cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>, | |
30 | in_any_value_of_ty: BitSet<Local>, | |
31 | } | |
32 | ||
33 | impl<Q: Qualif> QualifCursor<'a, 'mir, 'tcx, Q> { | |
34 | pub fn new( | |
35 | q: Q, | |
36 | item: &'a Item<'mir, 'tcx>, | |
37 | dead_unwinds: &BitSet<BasicBlock>, | |
38 | ) -> Self { | |
39 | let analysis = FlowSensitiveAnalysis::new(q, item); | |
40 | let results = | |
60c5eb7d | 41 | dataflow::Engine::new(item.tcx, &item.body, item.def_id, dead_unwinds, analysis) |
e74abb32 | 42 | .iterate_to_fixpoint(); |
60c5eb7d | 43 | let cursor = dataflow::ResultsCursor::new(*item.body, results); |
e74abb32 XL |
44 | |
45 | let mut in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len()); | |
46 | for (local, decl) in item.body.local_decls.iter_enumerated() { | |
47 | if Q::in_any_value_of_ty(item, decl.ty) { | |
48 | in_any_value_of_ty.insert(local); | |
49 | } | |
50 | } | |
51 | ||
52 | QualifCursor { | |
53 | cursor, | |
54 | in_any_value_of_ty, | |
55 | } | |
56 | } | |
57 | } | |
58 | ||
59 | pub struct Qualifs<'a, 'mir, 'tcx> { | |
60 | has_mut_interior: QualifCursor<'a, 'mir, 'tcx, HasMutInterior>, | |
61 | needs_drop: QualifCursor<'a, 'mir, 'tcx, NeedsDrop>, | |
62 | indirectly_mutable: IndirectlyMutableResults<'mir, 'tcx>, | |
63 | } | |
64 | ||
65 | impl Qualifs<'a, 'mir, 'tcx> { | |
66 | fn indirectly_mutable(&mut self, local: Local, location: Location) -> bool { | |
67 | self.indirectly_mutable.seek(location); | |
68 | self.indirectly_mutable.get().contains(local) | |
69 | } | |
70 | ||
71 | /// Returns `true` if `local` is `NeedsDrop` at the given `Location`. | |
72 | /// | |
73 | /// Only updates the cursor if absolutely necessary | |
74 | fn needs_drop_lazy_seek(&mut self, local: Local, location: Location) -> bool { | |
75 | if !self.needs_drop.in_any_value_of_ty.contains(local) { | |
76 | return false; | |
77 | } | |
78 | ||
79 | self.needs_drop.cursor.seek_before(location); | |
80 | self.needs_drop.cursor.get().contains(local) | |
81 | || self.indirectly_mutable(local, location) | |
82 | } | |
83 | ||
60c5eb7d XL |
84 | /// Returns `true` if `local` is `HasMutInterior` at the given `Location`. |
85 | /// | |
86 | /// Only updates the cursor if absolutely necessary. | |
87 | fn has_mut_interior_lazy_seek(&mut self, local: Local, location: Location) -> bool { | |
88 | if !self.has_mut_interior.in_any_value_of_ty.contains(local) { | |
89 | return false; | |
90 | } | |
91 | ||
92 | self.has_mut_interior.cursor.seek_before(location); | |
93 | self.has_mut_interior.cursor.get().contains(local) | |
94 | || self.indirectly_mutable(local, location) | |
95 | } | |
96 | ||
e74abb32 XL |
97 | /// Returns `true` if `local` is `HasMutInterior`, but requires the `has_mut_interior` and |
98 | /// `indirectly_mutable` cursors to be updated beforehand. | |
99 | fn has_mut_interior_eager_seek(&self, local: Local) -> bool { | |
100 | if !self.has_mut_interior.in_any_value_of_ty.contains(local) { | |
101 | return false; | |
102 | } | |
103 | ||
104 | self.has_mut_interior.cursor.get().contains(local) | |
105 | || self.indirectly_mutable.get().contains(local) | |
106 | } | |
60c5eb7d XL |
107 | |
108 | fn in_return_place(&mut self, item: &Item<'_, 'tcx>) -> ConstQualifs { | |
109 | // Find the `Return` terminator if one exists. | |
110 | // | |
111 | // If no `Return` terminator exists, this MIR is divergent. Just return the conservative | |
112 | // qualifs for the return type. | |
113 | let return_block = item.body | |
114 | .basic_blocks() | |
115 | .iter_enumerated() | |
116 | .find(|(_, block)| { | |
117 | match block.terminator().kind { | |
118 | TerminatorKind::Return => true, | |
119 | _ => false, | |
120 | } | |
121 | }) | |
122 | .map(|(bb, _)| bb); | |
123 | ||
124 | let return_block = match return_block { | |
125 | None => return qualifs::in_any_value_of_ty(item, item.body.return_ty()), | |
126 | Some(bb) => bb, | |
127 | }; | |
128 | ||
129 | let return_loc = item.body.terminator_loc(return_block); | |
130 | ||
131 | ConstQualifs { | |
132 | needs_drop: self.needs_drop_lazy_seek(RETURN_PLACE, return_loc), | |
133 | has_mut_interior: self.has_mut_interior_lazy_seek(RETURN_PLACE, return_loc), | |
134 | } | |
135 | } | |
e74abb32 XL |
136 | } |
137 | ||
138 | pub struct Validator<'a, 'mir, 'tcx> { | |
139 | item: &'a Item<'mir, 'tcx>, | |
140 | qualifs: Qualifs<'a, 'mir, 'tcx>, | |
141 | ||
142 | /// The span of the current statement. | |
143 | span: Span, | |
e74abb32 XL |
144 | } |
145 | ||
146 | impl Deref for Validator<'_, 'mir, 'tcx> { | |
147 | type Target = Item<'mir, 'tcx>; | |
148 | ||
149 | fn deref(&self) -> &Self::Target { | |
150 | &self.item | |
151 | } | |
152 | } | |
153 | ||
154 | impl Validator<'a, 'mir, 'tcx> { | |
155 | pub fn new( | |
156 | item: &'a Item<'mir, 'tcx>, | |
157 | ) -> Self { | |
158 | let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len()); | |
159 | ||
160 | let needs_drop = QualifCursor::new( | |
161 | NeedsDrop, | |
162 | item, | |
163 | &dead_unwinds, | |
164 | ); | |
165 | ||
166 | let has_mut_interior = QualifCursor::new( | |
167 | HasMutInterior, | |
168 | item, | |
169 | &dead_unwinds, | |
170 | ); | |
171 | ||
172 | let indirectly_mutable = old_dataflow::do_dataflow( | |
173 | item.tcx, | |
60c5eb7d | 174 | &*item.body, |
e74abb32 XL |
175 | item.def_id, |
176 | &item.tcx.get_attrs(item.def_id), | |
177 | &dead_unwinds, | |
60c5eb7d | 178 | old_dataflow::IndirectlyMutableLocals::new(item.tcx, *item.body, item.param_env), |
e74abb32 XL |
179 | |_, local| old_dataflow::DebugFormatted::new(&local), |
180 | ); | |
181 | ||
182 | let indirectly_mutable = old_dataflow::DataflowResultsCursor::new( | |
183 | indirectly_mutable, | |
60c5eb7d | 184 | *item.body, |
e74abb32 XL |
185 | ); |
186 | ||
187 | let qualifs = Qualifs { | |
188 | needs_drop, | |
189 | has_mut_interior, | |
190 | indirectly_mutable, | |
191 | }; | |
192 | ||
193 | Validator { | |
194 | span: item.body.span, | |
195 | item, | |
196 | qualifs, | |
e74abb32 XL |
197 | } |
198 | } | |
199 | ||
60c5eb7d XL |
200 | pub fn check_body(&mut self) { |
201 | let Item { tcx, body, def_id, const_kind, .. } = *self.item; | |
202 | ||
203 | let use_min_const_fn_checks = | |
204 | (const_kind == Some(ConstKind::ConstFn) && tcx.is_min_const_fn(def_id)) | |
205 | && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; | |
206 | ||
207 | if use_min_const_fn_checks { | |
208 | // Enforce `min_const_fn` for stable `const fn`s. | |
209 | use crate::transform::qualify_min_const_fn::is_min_const_fn; | |
210 | if let Err((span, err)) = is_min_const_fn(tcx, def_id, &body) { | |
211 | error_min_const_fn_violation(tcx, span, err); | |
212 | return; | |
213 | } | |
214 | } | |
215 | ||
216 | check_short_circuiting_in_const_local(self.item); | |
217 | ||
218 | if body.is_cfg_cyclic() { | |
219 | // We can't provide a good span for the error here, but this should be caught by the | |
220 | // HIR const-checker anyways. | |
221 | self.check_op_spanned(ops::Loop, body.span); | |
222 | } | |
223 | ||
224 | self.visit_body(body); | |
225 | ||
226 | // Ensure that the end result is `Sync` in a non-thread local `static`. | |
227 | let should_check_for_sync = const_kind == Some(ConstKind::Static) | |
228 | && !tcx.has_attr(def_id, sym::thread_local); | |
229 | ||
230 | if should_check_for_sync { | |
231 | let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); | |
232 | check_return_ty_is_sync(tcx, &body, hir_id); | |
233 | } | |
234 | } | |
235 | ||
236 | pub fn qualifs_in_return_place(&mut self) -> ConstQualifs { | |
237 | self.qualifs.in_return_place(self.item) | |
e74abb32 XL |
238 | } |
239 | ||
240 | /// Emits an error at the given `span` if an expression cannot be evaluated in the current | |
60c5eb7d XL |
241 | /// context. |
242 | pub fn check_op_spanned<O>(&mut self, op: O, span: Span) | |
e74abb32 | 243 | where |
60c5eb7d | 244 | O: NonConstOp |
e74abb32 XL |
245 | { |
246 | trace!("check_op: op={:?}", op); | |
247 | ||
248 | if op.is_allowed_in_item(self) { | |
60c5eb7d | 249 | return; |
e74abb32 XL |
250 | } |
251 | ||
252 | // If an operation is supported in miri (and is not already controlled by a feature gate) it | |
253 | // can be turned on with `-Zunleash-the-miri-inside-of-you`. | |
254 | let is_unleashable = O::IS_SUPPORTED_IN_MIRI | |
255 | && O::feature_gate(self.tcx).is_none(); | |
256 | ||
257 | if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { | |
258 | self.tcx.sess.span_warn(span, "skipping const checks"); | |
60c5eb7d | 259 | return; |
e74abb32 XL |
260 | } |
261 | ||
60c5eb7d | 262 | op.emit_error(self, span); |
e74abb32 XL |
263 | } |
264 | ||
265 | /// Emits an error if an expression cannot be evaluated in the current context. | |
60c5eb7d | 266 | pub fn check_op(&mut self, op: impl NonConstOp) { |
e74abb32 XL |
267 | let span = self.span; |
268 | self.check_op_spanned(op, span) | |
269 | } | |
60c5eb7d XL |
270 | |
271 | fn check_static(&mut self, def_id: DefId, span: Span) { | |
272 | let is_thread_local = self.tcx.has_attr(def_id, sym::thread_local); | |
273 | if is_thread_local { | |
274 | self.check_op_spanned(ops::ThreadLocalAccess, span) | |
275 | } else { | |
276 | self.check_op_spanned(ops::StaticAccess, span) | |
277 | } | |
278 | } | |
e74abb32 XL |
279 | } |
280 | ||
281 | impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { | |
60c5eb7d XL |
282 | fn visit_basic_block_data( |
283 | &mut self, | |
284 | bb: BasicBlock, | |
285 | block: &BasicBlockData<'tcx>, | |
286 | ) { | |
287 | trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup); | |
288 | ||
289 | // Just as the old checker did, we skip const-checking basic blocks on the unwind path. | |
290 | // These blocks often drop locals that would otherwise be returned from the function. | |
291 | // | |
292 | // FIXME: This shouldn't be unsound since a panic at compile time will cause a compiler | |
293 | // error anyway, but maybe we should do more here? | |
294 | if block.is_cleanup { | |
295 | return; | |
296 | } | |
297 | ||
298 | self.super_basic_block_data(bb, block); | |
299 | } | |
300 | ||
e74abb32 XL |
301 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { |
302 | trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location); | |
303 | ||
60c5eb7d | 304 | // Special-case reborrows to be more like a copy of a reference. |
e74abb32 | 305 | if let Rvalue::Ref(_, kind, ref place) = *rvalue { |
60c5eb7d | 306 | if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, *self.body, place) { |
e74abb32 XL |
307 | let ctx = match kind { |
308 | BorrowKind::Shared => PlaceContext::NonMutatingUse( | |
309 | NonMutatingUseContext::SharedBorrow, | |
310 | ), | |
311 | BorrowKind::Shallow => PlaceContext::NonMutatingUse( | |
312 | NonMutatingUseContext::ShallowBorrow, | |
313 | ), | |
314 | BorrowKind::Unique => PlaceContext::NonMutatingUse( | |
315 | NonMutatingUseContext::UniqueBorrow, | |
316 | ), | |
317 | BorrowKind::Mut { .. } => PlaceContext::MutatingUse( | |
318 | MutatingUseContext::Borrow, | |
319 | ), | |
320 | }; | |
321 | self.visit_place_base(&place.base, ctx, location); | |
60c5eb7d XL |
322 | self.visit_projection(&place.base, reborrowed_proj, ctx, location); |
323 | return; | |
e74abb32 | 324 | } |
e74abb32 XL |
325 | } |
326 | ||
60c5eb7d XL |
327 | self.super_rvalue(rvalue, location); |
328 | ||
e74abb32 XL |
329 | match *rvalue { |
330 | Rvalue::Use(_) | | |
331 | Rvalue::Repeat(..) | | |
332 | Rvalue::UnaryOp(UnOp::Neg, _) | | |
333 | Rvalue::UnaryOp(UnOp::Not, _) | | |
334 | Rvalue::NullaryOp(NullOp::SizeOf, _) | | |
335 | Rvalue::CheckedBinaryOp(..) | | |
336 | Rvalue::Cast(CastKind::Pointer(_), ..) | | |
337 | Rvalue::Discriminant(..) | | |
338 | Rvalue::Len(_) | | |
e74abb32 XL |
339 | Rvalue::Aggregate(..) => {} |
340 | ||
60c5eb7d XL |
341 | | Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place) |
342 | | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) | |
343 | => { | |
344 | let ty = place.ty(*self.body, self.tcx).ty; | |
345 | let is_allowed = match ty.kind { | |
346 | // Inside a `static mut`, `&mut [...]` is allowed. | |
347 | ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut | |
348 | => true, | |
349 | ||
350 | // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given | |
351 | // that this is merely a ZST and it is already eligible for promotion. | |
352 | // This may require an RFC? | |
353 | /* | |
354 | ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0) | |
355 | => true, | |
356 | */ | |
357 | ||
358 | _ => false, | |
359 | }; | |
360 | ||
361 | if !is_allowed { | |
362 | if let BorrowKind::Mut{ .. } = kind { | |
363 | self.check_op(ops::MutBorrow); | |
364 | } else { | |
365 | self.check_op(ops::CellBorrow); | |
366 | } | |
367 | } | |
368 | } | |
369 | ||
370 | // At the moment, `PlaceBase::Static` is only used for promoted MIR. | |
371 | | Rvalue::Ref(_, BorrowKind::Shared, ref place) | |
372 | | Rvalue::Ref(_, BorrowKind::Shallow, ref place) | |
373 | if matches!(place.base, PlaceBase::Static(_)) | |
374 | => bug!("Saw a promoted during const-checking, which must run before promotion"), | |
375 | ||
376 | | Rvalue::Ref(_, kind @ BorrowKind::Shared, ref place) | |
377 | | Rvalue::Ref(_, kind @ BorrowKind::Shallow, ref place) | |
378 | => { | |
379 | // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually | |
380 | // seek the cursors beforehand. | |
381 | self.qualifs.has_mut_interior.cursor.seek_before(location); | |
382 | self.qualifs.indirectly_mutable.seek(location); | |
383 | ||
384 | let borrowed_place_has_mut_interior = HasMutInterior::in_place( | |
385 | &self.item, | |
386 | &|local| self.qualifs.has_mut_interior_eager_seek(local), | |
387 | place.as_ref(), | |
388 | ); | |
389 | ||
390 | if borrowed_place_has_mut_interior { | |
391 | if let BorrowKind::Mut{ .. } = kind { | |
392 | self.check_op(ops::MutBorrow); | |
393 | } else { | |
394 | self.check_op(ops::CellBorrow); | |
395 | } | |
396 | } | |
397 | } | |
398 | ||
e74abb32 | 399 | Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { |
60c5eb7d | 400 | let operand_ty = operand.ty(*self.body, self.tcx); |
e74abb32 XL |
401 | let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); |
402 | let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); | |
403 | ||
404 | if let (CastTy::Ptr(_), CastTy::Int(_)) | |
405 | | (CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { | |
406 | self.check_op(ops::RawPtrToIntCast); | |
407 | } | |
408 | } | |
409 | ||
410 | Rvalue::BinaryOp(op, ref lhs, _) => { | |
60c5eb7d | 411 | if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(*self.body, self.tcx).kind { |
e74abb32 XL |
412 | assert!(op == BinOp::Eq || op == BinOp::Ne || |
413 | op == BinOp::Le || op == BinOp::Lt || | |
414 | op == BinOp::Ge || op == BinOp::Gt || | |
415 | op == BinOp::Offset); | |
416 | ||
417 | ||
418 | self.check_op(ops::RawPtrComparison); | |
419 | } | |
420 | } | |
421 | ||
422 | Rvalue::NullaryOp(NullOp::Box, _) => { | |
423 | self.check_op(ops::HeapAllocation); | |
424 | } | |
425 | } | |
426 | } | |
427 | ||
428 | fn visit_place_base( | |
429 | &mut self, | |
430 | place_base: &PlaceBase<'tcx>, | |
431 | context: PlaceContext, | |
432 | location: Location, | |
433 | ) { | |
434 | trace!( | |
435 | "visit_place_base: place_base={:?} context={:?} location={:?}", | |
436 | place_base, | |
437 | context, | |
438 | location, | |
439 | ); | |
440 | self.super_place_base(place_base, context, location); | |
441 | ||
442 | match place_base { | |
443 | PlaceBase::Local(_) => {} | |
60c5eb7d | 444 | PlaceBase::Static(_) => { |
e74abb32 XL |
445 | bug!("Promotion must be run after const validation"); |
446 | } | |
e74abb32 XL |
447 | } |
448 | } | |
449 | ||
60c5eb7d XL |
450 | fn visit_operand( |
451 | &mut self, | |
452 | op: &Operand<'tcx>, | |
453 | location: Location, | |
454 | ) { | |
455 | self.super_operand(op, location); | |
456 | if let Operand::Constant(c) = op { | |
457 | if let Some(def_id) = c.check_static_ptr(self.tcx) { | |
458 | self.check_static(def_id, self.span); | |
e74abb32 XL |
459 | } |
460 | } | |
e74abb32 | 461 | } |
e74abb32 XL |
462 | fn visit_projection_elem( |
463 | &mut self, | |
464 | place_base: &PlaceBase<'tcx>, | |
465 | proj_base: &[PlaceElem<'tcx>], | |
466 | elem: &PlaceElem<'tcx>, | |
467 | context: PlaceContext, | |
468 | location: Location, | |
469 | ) { | |
470 | trace!( | |
471 | "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \ | |
472 | context={:?} location={:?}", | |
473 | place_base, | |
474 | proj_base, | |
475 | elem, | |
476 | context, | |
477 | location, | |
478 | ); | |
479 | ||
480 | self.super_projection_elem(place_base, proj_base, elem, context, location); | |
481 | ||
482 | match elem { | |
483 | ProjectionElem::Deref => { | |
60c5eb7d | 484 | let base_ty = Place::ty_from(place_base, proj_base, *self.body, self.tcx).ty; |
e74abb32 | 485 | if let ty::RawPtr(_) = base_ty.kind { |
60c5eb7d XL |
486 | if proj_base.is_empty() { |
487 | if let (PlaceBase::Local(local), []) = (place_base, proj_base) { | |
488 | let decl = &self.body.local_decls[*local]; | |
489 | if let LocalInfo::StaticRef { def_id, .. } = decl.local_info { | |
490 | let span = decl.source_info.span; | |
491 | self.check_static(def_id, span); | |
492 | return; | |
493 | } | |
494 | } | |
495 | } | |
e74abb32 XL |
496 | self.check_op(ops::RawPtrDeref); |
497 | } | |
60c5eb7d XL |
498 | |
499 | if context.is_mutating_use() { | |
500 | self.check_op(ops::MutDeref); | |
501 | } | |
e74abb32 XL |
502 | } |
503 | ||
504 | ProjectionElem::ConstantIndex {..} | | |
505 | ProjectionElem::Subslice {..} | | |
506 | ProjectionElem::Field(..) | | |
507 | ProjectionElem::Index(_) => { | |
60c5eb7d | 508 | let base_ty = Place::ty_from(place_base, proj_base, *self.body, self.tcx).ty; |
e74abb32 XL |
509 | match base_ty.ty_adt_def() { |
510 | Some(def) if def.is_union() => { | |
511 | self.check_op(ops::UnionAccess); | |
512 | } | |
513 | ||
514 | _ => {} | |
515 | } | |
516 | } | |
517 | ||
518 | ProjectionElem::Downcast(..) => { | |
519 | self.check_op(ops::Downcast); | |
520 | } | |
521 | } | |
522 | } | |
523 | ||
524 | ||
525 | fn visit_source_info(&mut self, source_info: &SourceInfo) { | |
526 | trace!("visit_source_info: source_info={:?}", source_info); | |
527 | self.span = source_info.span; | |
528 | } | |
529 | ||
530 | fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { | |
531 | trace!("visit_statement: statement={:?} location={:?}", statement, location); | |
532 | ||
533 | match statement.kind { | |
60c5eb7d | 534 | StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } => { |
e74abb32 XL |
535 | self.super_statement(statement, location); |
536 | } | |
537 | StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { | |
538 | self.check_op(ops::IfOrMatch); | |
539 | } | |
540 | // FIXME(eddyb) should these really do nothing? | |
541 | StatementKind::FakeRead(..) | | |
e74abb32 XL |
542 | StatementKind::StorageLive(_) | |
543 | StatementKind::StorageDead(_) | | |
544 | StatementKind::InlineAsm {..} | | |
545 | StatementKind::Retag { .. } | | |
546 | StatementKind::AscribeUserType(..) | | |
547 | StatementKind::Nop => {} | |
548 | } | |
549 | } | |
550 | ||
551 | fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) { | |
552 | trace!("visit_terminator_kind: kind={:?} location={:?}", kind, location); | |
553 | self.super_terminator_kind(kind, location); | |
554 | ||
555 | match kind { | |
556 | TerminatorKind::Call { func, .. } => { | |
60c5eb7d | 557 | let fn_ty = func.ty(*self.body, self.tcx); |
e74abb32 XL |
558 | |
559 | let def_id = match fn_ty.kind { | |
560 | ty::FnDef(def_id, _) => def_id, | |
561 | ||
562 | ty::FnPtr(_) => { | |
563 | self.check_op(ops::FnCallIndirect); | |
564 | return; | |
565 | } | |
566 | _ => { | |
567 | self.check_op(ops::FnCallOther); | |
568 | return; | |
569 | } | |
570 | }; | |
571 | ||
572 | // At this point, we are calling a function whose `DefId` is known... | |
e74abb32 XL |
573 | if self.tcx.is_const_fn(def_id) { |
574 | return; | |
575 | } | |
576 | ||
577 | if is_lang_panic_fn(self.tcx, def_id) { | |
578 | self.check_op(ops::Panic); | |
579 | } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) { | |
580 | // Exempt unstable const fns inside of macros with | |
581 | // `#[allow_internal_unstable]`. | |
582 | if !self.span.allows_unstable(feature) { | |
583 | self.check_op(ops::FnCallUnstable(def_id, feature)); | |
584 | } | |
585 | } else { | |
586 | self.check_op(ops::FnCallNonConst(def_id)); | |
587 | } | |
588 | ||
589 | } | |
590 | ||
591 | // Forbid all `Drop` terminators unless the place being dropped is a local with no | |
592 | // projections that cannot be `NeedsDrop`. | |
593 | | TerminatorKind::Drop { location: dropped_place, .. } | |
594 | | TerminatorKind::DropAndReplace { location: dropped_place, .. } | |
595 | => { | |
596 | let mut err_span = self.span; | |
597 | ||
598 | // Check to see if the type of this place can ever have a drop impl. If not, this | |
599 | // `Drop` terminator is frivolous. | |
600 | let ty_needs_drop = dropped_place | |
60c5eb7d | 601 | .ty(*self.body, self.tcx) |
e74abb32 XL |
602 | .ty |
603 | .needs_drop(self.tcx, self.param_env); | |
604 | ||
605 | if !ty_needs_drop { | |
606 | return; | |
607 | } | |
608 | ||
609 | let needs_drop = if let Some(local) = dropped_place.as_local() { | |
610 | // Use the span where the local was declared as the span of the drop error. | |
611 | err_span = self.body.local_decls[local].source_info.span; | |
612 | self.qualifs.needs_drop_lazy_seek(local, location) | |
613 | } else { | |
614 | true | |
615 | }; | |
616 | ||
617 | if needs_drop { | |
618 | self.check_op_spanned(ops::LiveDrop, err_span); | |
619 | } | |
620 | } | |
621 | ||
622 | _ => {} | |
623 | } | |
624 | } | |
625 | } | |
60c5eb7d XL |
626 | |
627 | fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) { | |
628 | struct_span_err!(tcx.sess, span, E0723, "{}", msg) | |
629 | .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563") | |
630 | .help("add `#![feature(const_fn)]` to the crate attributes to enable") | |
631 | .emit(); | |
632 | } | |
633 | ||
634 | fn check_short_circuiting_in_const_local(item: &Item<'_, 'tcx>) { | |
635 | let body = item.body; | |
636 | ||
637 | if body.control_flow_destroyed.is_empty() { | |
638 | return; | |
639 | } | |
640 | ||
641 | let mut locals = body.vars_iter(); | |
642 | if let Some(local) = locals.next() { | |
643 | let span = body.local_decls[local].source_info.span; | |
644 | let mut error = item.tcx.sess.struct_span_err( | |
645 | span, | |
646 | &format!( | |
647 | "new features like let bindings are not permitted in {}s \ | |
648 | which also use short circuiting operators", | |
649 | item.const_kind(), | |
650 | ), | |
651 | ); | |
652 | for (span, kind) in body.control_flow_destroyed.iter() { | |
653 | error.span_note( | |
654 | *span, | |
655 | &format!("use of {} here does not actually short circuit due to \ | |
656 | the const evaluator presently not being able to do control flow. \ | |
657 | See https://github.com/rust-lang/rust/issues/49146 for more \ | |
658 | information.", kind), | |
659 | ); | |
660 | } | |
661 | for local in locals { | |
662 | let span = body.local_decls[local].source_info.span; | |
663 | error.span_note(span, "more locals defined here"); | |
664 | } | |
665 | error.emit(); | |
666 | } | |
667 | } | |
668 | ||
669 | fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) { | |
670 | let ty = body.return_ty(); | |
671 | tcx.infer_ctxt().enter(|infcx| { | |
672 | let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic); | |
673 | let mut fulfillment_cx = traits::FulfillmentContext::new(); | |
674 | let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span)); | |
675 | fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause); | |
676 | if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { | |
677 | infcx.report_fulfillment_errors(&err, None, false); | |
678 | } | |
679 | }); | |
680 | } | |
681 | ||
682 | fn place_as_reborrow( | |
683 | tcx: TyCtxt<'tcx>, | |
684 | body: &Body<'tcx>, | |
685 | place: &'a Place<'tcx>, | |
686 | ) -> Option<&'a [PlaceElem<'tcx>]> { | |
687 | place | |
688 | .projection | |
689 | .split_last() | |
690 | .and_then(|(outermost, inner)| { | |
691 | if outermost != &ProjectionElem::Deref { | |
692 | return None; | |
693 | } | |
694 | ||
695 | // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` | |
696 | // that points to the allocation for the static. Don't treat these as reborrows. | |
697 | if let PlaceBase::Local(local) = place.base { | |
698 | if body.local_decls[local].is_ref_to_static() { | |
699 | return None; | |
700 | } | |
701 | } | |
702 | ||
703 | // Ensure the type being derefed is a reference and not a raw pointer. | |
704 | // | |
705 | // This is sufficient to prevent an access to a `static mut` from being marked as a | |
706 | // reborrow, even if the check above were to disappear. | |
707 | let inner_ty = Place::ty_from(&place.base, inner, body, tcx).ty; | |
708 | match inner_ty.kind { | |
709 | ty::Ref(..) => Some(inner), | |
710 | _ => None, | |
711 | } | |
712 | }) | |
713 | } |