Skip to content

Commit 973b16a

Browse files
Enable standalone const-checking with Validator
Unlike the original pass, we check *every* non-cleanup basic block instead of stopping at `SwitchInt`. We use the `is_cfg_cyclic` function to check for loops unlike the original checker which could not differentiate between true cycles and basic blocks with more than two predecessors. The last three functions are all copied verbatim from `qualify_consts`.
1 parent 4881104 commit 973b16a

File tree

1 file changed

+126
-1
lines changed

1 file changed

+126
-1
lines changed

src/librustc_mir/transform/check_consts/validation.rs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
22
3+
use rustc::hir::HirId;
4+
use rustc::middle::lang_items;
35
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
46
use rustc::mir::*;
7+
use rustc::traits::{self, TraitEngine};
58
use rustc::ty::cast::CastTy;
6-
use rustc::ty;
9+
use rustc::ty::{self, TyCtxt};
710
use rustc_index::bit_set::BitSet;
811
use rustc_target::spec::abi::Abi;
12+
use rustc_error_codes::*;
913
use syntax::symbol::sym;
1014
use syntax_pos::Span;
1115

16+
use std::borrow::Cow;
1217
use std::fmt;
1318
use std::ops::Deref;
1419

@@ -222,6 +227,52 @@ impl Validator<'a, 'mir, 'tcx> {
222227
}
223228
}
224229

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+
225276
pub fn take_errors(&mut self) -> Vec<(Span, String)> {
226277
std::mem::replace(&mut self.errors, vec![])
227278
}
@@ -264,6 +315,25 @@ impl Validator<'a, 'mir, 'tcx> {
264315
}
265316

266317
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+
267337
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
268338
trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
269339

@@ -608,3 +678,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
608678
}
609679
}
610680
}
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

Comments
 (0)