about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/transform/check_consts/validation.rs127
1 files changed, 126 insertions, 1 deletions
diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs
index 53fb0bab80f..6687618b748 100644
--- a/src/librustc_mir/transform/check_consts/validation.rs
+++ b/src/librustc_mir/transform/check_consts/validation.rs
@@ -1,14 +1,19 @@
 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
 
+use rustc::hir::HirId;
+use rustc::middle::lang_items;
 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
 use rustc::mir::*;
+use rustc::traits::{self, TraitEngine};
 use rustc::ty::cast::CastTy;
-use rustc::ty;
+use rustc::ty::{self, TyCtxt};
 use rustc_index::bit_set::BitSet;
 use rustc_target::spec::abi::Abi;
+use rustc_error_codes::*;
 use syntax::symbol::sym;
 use syntax_pos::Span;
 
+use std::borrow::Cow;
 use std::fmt;
 use std::ops::Deref;
 
@@ -222,6 +227,52 @@ impl Validator<'a, 'mir, 'tcx> {
         }
     }
 
+    pub fn check_body(&mut self) {
+        let Item { tcx, body, def_id, const_kind, ..  } = *self.item;
+
+        let use_min_const_fn_checks =
+            tcx.is_min_const_fn(def_id)
+            && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
+
+        if use_min_const_fn_checks {
+            // Enforce `min_const_fn` for stable `const fn`s.
+            use crate::transform::qualify_min_const_fn::is_min_const_fn;
+            if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
+                error_min_const_fn_violation(tcx, span, err);
+                return;
+            }
+        }
+
+        check_short_circuiting_in_const_local(self.item);
+
+        // FIXME: give a span for the loop
+        if body.is_cfg_cyclic() {
+            // FIXME: make this the `emit_error` impl of `ops::Loop` once the const
+            // checker is no longer run in compatability mode.
+            if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
+                self.tcx.sess.delay_span_bug(
+                    self.span,
+                    "complex control flow is forbidden in a const context",
+                );
+            }
+        }
+
+        self.visit_body(body);
+
+        // Ensure that the end result is `Sync` in a non-thread local `static`.
+        let should_check_for_sync = const_kind == Some(ConstKind::Static)
+            && !tcx.has_attr(def_id, sym::thread_local);
+
+        if should_check_for_sync {
+            let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+            check_return_ty_is_sync(tcx, body, hir_id);
+        }
+    }
+
+    pub fn qualifs_in_return_place(&mut self) -> QualifSet {
+        self.qualifs.in_return_place(self.item)
+    }
+
     pub fn take_errors(&mut self) -> Vec<(Span, String)> {
         std::mem::replace(&mut self.errors, vec![])
     }
@@ -264,6 +315,25 @@ impl Validator<'a, 'mir, 'tcx> {
 }
 
 impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
+    fn visit_basic_block_data(
+        &mut self,
+        bb: BasicBlock,
+        block: &BasicBlockData<'tcx>,
+    ) {
+        trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
+
+        // Just as the old checker did, we skip const-checking basic blocks on the unwind path.
+        // These blocks often drop locals that would otherwise be returned from the function.
+        //
+        // FIXME: This shouldn't be unsound since a panic at compile time will cause a compiler
+        // error anyway, but maybe we should do more here?
+        if block.is_cleanup {
+            return;
+        }
+
+        self.super_basic_block_data(bb, block);
+    }
+
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
 
@@ -608,3 +678,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
         }
     }
 }
+
+fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
+    struct_span_err!(tcx.sess, span, E0723, "{}", msg)
+        .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
+        .help("add `#![feature(const_fn)]` to the crate attributes to enable")
+        .emit();
+}
+
+fn check_short_circuiting_in_const_local(item: &Item<'_, 'tcx>) {
+    let body = item.body;
+
+    if body.control_flow_destroyed.is_empty() {
+        return;
+    }
+
+    let mut locals = body.vars_iter();
+    if let Some(local) = locals.next() {
+        let span = body.local_decls[local].source_info.span;
+        let mut error = item.tcx.sess.struct_span_err(
+            span,
+            &format!(
+                "new features like let bindings are not permitted in {}s \
+                which also use short circuiting operators",
+                item.const_kind(),
+            ),
+        );
+        for (span, kind) in body.control_flow_destroyed.iter() {
+            error.span_note(
+                *span,
+                &format!("use of {} here does not actually short circuit due to \
+                the const evaluator presently not being able to do control flow. \
+                See https://github.com/rust-lang/rust/issues/49146 for more \
+                information.", kind),
+            );
+        }
+        for local in locals {
+            let span = body.local_decls[local].source_info.span;
+            error.span_note(span, "more locals defined here");
+        }
+        error.emit();
+    }
+}
+
+fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) {
+    let ty = body.return_ty();
+    tcx.infer_ctxt().enter(|infcx| {
+        let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
+        let mut fulfillment_cx = traits::FulfillmentContext::new();
+        let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
+        fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
+        if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
+            infcx.report_fulfillment_errors(&err, None, false);
+        }
+    });
+}