|
1 | 1 | //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
|
2 | 2 |
|
| 3 | +use rustc::hir::HirId; |
| 4 | +use rustc::middle::lang_items; |
3 | 5 | use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
|
4 | 6 | use rustc::mir::*;
|
| 7 | +use rustc::traits::{self, TraitEngine}; |
5 | 8 | use rustc::ty::cast::CastTy;
|
6 |
| -use rustc::ty; |
| 9 | +use rustc::ty::{self, TyCtxt}; |
7 | 10 | use rustc_index::bit_set::BitSet;
|
8 | 11 | use rustc_target::spec::abi::Abi;
|
| 12 | +use rustc_error_codes::*; |
9 | 13 | use syntax::symbol::sym;
|
10 | 14 | use syntax_pos::Span;
|
11 | 15 |
|
| 16 | +use std::borrow::Cow; |
12 | 17 | use std::fmt;
|
13 | 18 | use std::ops::Deref;
|
14 | 19 |
|
@@ -222,6 +227,52 @@ impl Validator<'a, 'mir, 'tcx> {
|
222 | 227 | }
|
223 | 228 | }
|
224 | 229 |
|
| 230 | + pub fn check_body(&mut self) { |
| 231 | + let Item { tcx, body, def_id, const_kind, .. } = *self.item; |
| 232 | + |
| 233 | + let use_min_const_fn_checks = |
| 234 | + tcx.is_min_const_fn(def_id) |
| 235 | + && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; |
| 236 | + |
| 237 | + if use_min_const_fn_checks { |
| 238 | + // Enforce `min_const_fn` for stable `const fn`s. |
| 239 | + use crate::transform::qualify_min_const_fn::is_min_const_fn; |
| 240 | + if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) { |
| 241 | + error_min_const_fn_violation(tcx, span, err); |
| 242 | + return; |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + check_short_circuiting_in_const_local(self.item); |
| 247 | + |
| 248 | + // FIXME: give a span for the loop |
| 249 | + if body.is_cfg_cyclic() { |
| 250 | + // FIXME: make this the `emit_error` impl of `ops::Loop` once the const |
| 251 | + // checker is no longer run in compatability mode. |
| 252 | + if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { |
| 253 | + self.tcx.sess.delay_span_bug( |
| 254 | + self.span, |
| 255 | + "complex control flow is forbidden in a const context", |
| 256 | + ); |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + self.visit_body(body); |
| 261 | + |
| 262 | + // Ensure that the end result is `Sync` in a non-thread local `static`. |
| 263 | + let should_check_for_sync = const_kind == Some(ConstKind::Static) |
| 264 | + && !tcx.has_attr(def_id, sym::thread_local); |
| 265 | + |
| 266 | + if should_check_for_sync { |
| 267 | + let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); |
| 268 | + check_return_ty_is_sync(tcx, body, hir_id); |
| 269 | + } |
| 270 | + } |
| 271 | + |
| 272 | + pub fn qualifs_in_return_place(&mut self) -> QualifSet { |
| 273 | + self.qualifs.in_return_place(self.item) |
| 274 | + } |
| 275 | + |
225 | 276 | pub fn take_errors(&mut self) -> Vec<(Span, String)> {
|
226 | 277 | std::mem::replace(&mut self.errors, vec![])
|
227 | 278 | }
|
@@ -264,6 +315,25 @@ impl Validator<'a, 'mir, 'tcx> {
|
264 | 315 | }
|
265 | 316 |
|
266 | 317 | impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
|
| 318 | + fn visit_basic_block_data( |
| 319 | + &mut self, |
| 320 | + bb: BasicBlock, |
| 321 | + block: &BasicBlockData<'tcx>, |
| 322 | + ) { |
| 323 | + trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup); |
| 324 | + |
| 325 | + // Just as the old checker did, we skip const-checking basic blocks on the unwind path. |
| 326 | + // These blocks often drop locals that would otherwise be returned from the function. |
| 327 | + // |
| 328 | + // FIXME: This shouldn't be unsound since a panic at compile time will cause a compiler |
| 329 | + // error anyway, but maybe we should do more here? |
| 330 | + if block.is_cleanup { |
| 331 | + return; |
| 332 | + } |
| 333 | + |
| 334 | + self.super_basic_block_data(bb, block); |
| 335 | + } |
| 336 | + |
267 | 337 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
268 | 338 | trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
|
269 | 339 |
|
@@ -608,3 +678,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
|
608 | 678 | }
|
609 | 679 | }
|
610 | 680 | }
|
| 681 | + |
| 682 | +fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) { |
| 683 | + struct_span_err!(tcx.sess, span, E0723, "{}", msg) |
| 684 | + .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563") |
| 685 | + .help("add `#![feature(const_fn)]` to the crate attributes to enable") |
| 686 | + .emit(); |
| 687 | +} |
| 688 | + |
| 689 | +fn check_short_circuiting_in_const_local(item: &Item<'_, 'tcx>) { |
| 690 | + let body = item.body; |
| 691 | + |
| 692 | + if body.control_flow_destroyed.is_empty() { |
| 693 | + return; |
| 694 | + } |
| 695 | + |
| 696 | + let mut locals = body.vars_iter(); |
| 697 | + if let Some(local) = locals.next() { |
| 698 | + let span = body.local_decls[local].source_info.span; |
| 699 | + let mut error = item.tcx.sess.struct_span_err( |
| 700 | + span, |
| 701 | + &format!( |
| 702 | + "new features like let bindings are not permitted in {}s \ |
| 703 | + which also use short circuiting operators", |
| 704 | + item.const_kind(), |
| 705 | + ), |
| 706 | + ); |
| 707 | + for (span, kind) in body.control_flow_destroyed.iter() { |
| 708 | + error.span_note( |
| 709 | + *span, |
| 710 | + &format!("use of {} here does not actually short circuit due to \ |
| 711 | + the const evaluator presently not being able to do control flow. \ |
| 712 | + See https://github.com/rust-lang/rust/issues/49146 for more \ |
| 713 | + information.", kind), |
| 714 | + ); |
| 715 | + } |
| 716 | + for local in locals { |
| 717 | + let span = body.local_decls[local].source_info.span; |
| 718 | + error.span_note(span, "more locals defined here"); |
| 719 | + } |
| 720 | + error.emit(); |
| 721 | + } |
| 722 | +} |
| 723 | + |
| 724 | +fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) { |
| 725 | + let ty = body.return_ty(); |
| 726 | + tcx.infer_ctxt().enter(|infcx| { |
| 727 | + let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic); |
| 728 | + let mut fulfillment_cx = traits::FulfillmentContext::new(); |
| 729 | + let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span)); |
| 730 | + fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause); |
| 731 | + if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { |
| 732 | + infcx.report_fulfillment_errors(&err, None, false); |
| 733 | + } |
| 734 | + }); |
| 735 | +} |
0 commit comments