From 1924cc8c7d051bfe53922b965d9e8490f90fdbe5 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 13 Nov 2019 16:56:08 -0800 Subject: [PATCH 01/17] Remove `QUALIF_ERROR_BIT` checking --- src/librustc_mir/transform/promote_consts.rs | 27 +++----------------- src/librustc_mir/transform/qualify_consts.rs | 2 +- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index b4da437279db1..c79d382a37480 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -582,30 +582,9 @@ impl<'tcx> Validator<'_, 'tcx> { Operand::Copy(place) | Operand::Move(place) => self.validate_place(place.as_ref()), - Operand::Constant(constant) => { - if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val { - if self.tcx.trait_of_item(def_id).is_some() { - // Don't peek inside trait associated constants. - // (see below what we do for other consts, for now) - } else { - // HACK(eddyb) ensure that errors propagate correctly. - // FIXME(eddyb) remove this once the old promotion logic - // is gone - we can always promote constants even if they - // fail to pass const-checking, as compilation would've - // errored independently and promotion can't change that. - let bits = self.tcx.at(constant.span).mir_const_qualif(def_id); - if bits == super::qualify_consts::QUALIF_ERROR_BIT { - self.tcx.sess.delay_span_bug( - constant.span, - "promote_consts: MIR had errors", - ); - return Err(Unpromotable); - } - } - } - - Ok(()) - } + // The qualifs for a constant (e.g. `HasMutInterior`) are checked in + // `validate_rvalue` upon access. + Operand::Constant(_) => Ok(()), } } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 964efdec2b9d2..ee5dfae6ce251 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1285,7 +1285,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { if body.return_ty().references_error() { tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors"); - return QUALIF_ERROR_BIT; + return 0; } Checker::new(tcx, def_id, body, Mode::Const).check_const() From dbae15a41b11624b26cf25f1fa414e3280d9f47f Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 29 Oct 2019 17:17:30 -0700 Subject: [PATCH 02/17] Remove `requires_sync` --- src/librustc_mir/transform/check_consts/mod.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index a5b711e75a603..4d00aaf7abc21 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -95,16 +95,6 @@ impl ConstKind { ConstKind::ConstFn | ConstKind::Const => false, } } - - /// Returns `true` if the value returned by this item must be `Sync`. - /// - /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway. - pub fn requires_sync(self) -> bool { - match self { - ConstKind::Static => true, - ConstKind::ConstFn | ConstKind::Const | ConstKind::StaticMut => false, - } - } } impl fmt::Display for ConstKind { From 48811048f853128ad87bc62bb611ecb73522be41 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 28 Oct 2019 10:24:29 -0700 Subject: [PATCH 03/17] Allow `Validator` to get qualifs for the return place --- .../transform/check_consts/validation.rs | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 74b22d8e14366..53fb0bab80f83 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -17,7 +17,7 @@ use self::old_dataflow::IndirectlyMutableLocals; use super::ops::{self, NonConstOp}; use super::qualifs::{HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{ConstKind, Item, Qualif, is_lang_panic_fn}; +use super::{ConstKind, Item, Qualif, QualifSet, is_lang_panic_fn}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CheckOpResult { @@ -85,6 +85,19 @@ impl Qualifs<'a, 'mir, 'tcx> { || self.indirectly_mutable(local, location) } + /// Returns `true` if `local` is `HasMutInterior` at the given `Location`. + /// + /// Only updates the cursor if absolutely necessary. + fn has_mut_interior_lazy_seek(&mut self, local: Local, location: Location) -> bool { + if !self.has_mut_interior.in_any_value_of_ty.contains(local) { + return false; + } + + self.has_mut_interior.cursor.seek_before(location); + self.has_mut_interior.cursor.get().contains(local) + || self.indirectly_mutable(local, location) + } + /// Returns `true` if `local` is `HasMutInterior`, but requires the `has_mut_interior` and /// `indirectly_mutable` cursors to be updated beforehand. fn has_mut_interior_eager_seek(&self, local: Local) -> bool { @@ -95,6 +108,37 @@ impl Qualifs<'a, 'mir, 'tcx> { self.has_mut_interior.cursor.get().contains(local) || self.indirectly_mutable.get().contains(local) } + + fn in_return_place(&mut self, item: &Item<'_, 'tcx>) -> QualifSet { + // Find the `Return` terminator if one exists. + // + // If no `Return` terminator exists, this MIR is divergent. Just return the conservative + // qualifs for the return type. + let return_block = item.body + .basic_blocks() + .iter_enumerated() + .find(|(_, block)| { + match block.terminator().kind { + TerminatorKind::Return => true, + _ => false, + } + }) + .map(|(bb, _)| bb); + + let return_block = match return_block { + None => return QualifSet::in_any_value_of_ty(item, item.body.return_ty()), + Some(bb) => bb, + }; + + let return_loc = item.body.terminator_loc(return_block); + + let mut qualifs = QualifSet::default(); + + qualifs.set::(self.needs_drop_lazy_seek(RETURN_PLACE, return_loc)); + qualifs.set::(self.has_mut_interior_lazy_seek(RETURN_PLACE, return_loc)); + + qualifs + } } pub struct Validator<'a, 'mir, 'tcx> { From 973b16ab4b2d044f777616f59a2d33fbbbc0fe49 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 29 Oct 2019 17:19:58 -0700 Subject: [PATCH 04/17] 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`. --- .../transform/check_consts/validation.rs | 127 +++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 53fb0bab80f83..6687618b74859 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); + } + }); +} From 55da0c03680f9ce30423cb640256b11ed6c8cabd Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 28 Oct 2019 10:26:16 -0700 Subject: [PATCH 05/17] Use new const-checker for `mir_const_qualif` Now `mir_const_qualif` must be called for `static`s and `const fn`s as well as `const`s since it is responsible for const-checking. We return the qualifs in the return place for everything, even though they will only be used for `const`s. --- src/librustc_mir/transform/mod.rs | 39 +++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 02ed12eda67a2..64649e93481f4 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -2,7 +2,7 @@ use crate::{build, shim}; use rustc_index::vec::IndexVec; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::mir::{Body, MirPhase, Promoted}; -use rustc::ty::{TyCtxt, InstanceDef}; +use rustc::ty::{TyCtxt, InstanceDef, TypeFoldable}; use rustc::ty::query::Providers; use rustc::ty::steal::Steal; use rustc::hir; @@ -39,12 +39,12 @@ pub mod uniform_array_move_out; pub mod uninhabited_enum_branching; pub(crate) fn provide(providers: &mut Providers<'_>) { - self::qualify_consts::provide(providers); self::check_unsafety::provide(providers); *providers = Providers { mir_keys, mir_built, mir_const, + mir_const_qualif, mir_validated, optimized_mir, is_mir_available, @@ -185,6 +185,41 @@ pub fn run_passes( body.phase = mir_phase; } +fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { + let const_kind = check_consts::ConstKind::for_item(tcx, def_id); + + // No need to const-check a non-const `fn`. + if const_kind.is_none() { + return 0; + } + + // N.B., this `borrow()` is guaranteed to be valid (i.e., the value + // cannot yet be stolen), because `mir_validated()`, which steals + // from `mir_const(), forces this query to execute before + // performing the steal. + let body = &tcx.mir_const(def_id).borrow(); + + if body.return_ty().references_error() { + tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors"); + return 0; + } + + let item = check_consts::Item { + body, + tcx, + def_id, + const_kind, + param_env: tcx.param_env(def_id), + }; + + let mut validator = check_consts::validation::Validator::new(&item); + validator.check_body(); + + // We return the qualifs in the return place for every MIR body, even though it is only used + // when deciding to promote a reference to a `const` for now. + validator.qualifs_in_return_place().into() +} + fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal> { // Unsafety check uses the raw mir, so make sure it is run let _ = tcx.unsafety_check_result(def_id); From 6d4e2042f9dae20266292c45b2789962873b1d01 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 28 Oct 2019 21:25:51 -0700 Subject: [PATCH 06/17] Remove `qualify_consts` completely --- src/librustc_mir/lib.rs | 1 - src/librustc_mir/transform/mod.rs | 11 +- src/librustc_mir/transform/qualify_consts.rs | 1479 ------------------ 3 files changed, 3 insertions(+), 1488 deletions(-) delete mode 100644 src/librustc_mir/transform/qualify_consts.rs diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 6408c896e9301..6d19cd63bc32e 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -32,7 +32,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #[macro_use] extern crate log; #[macro_use] extern crate rustc; -#[macro_use] extern crate rustc_data_structures; #[macro_use] extern crate syntax; mod borrow_check; diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 64649e93481f4..5773d850fbc77 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -25,7 +25,6 @@ pub mod rustc_peek; pub mod elaborate_drops; pub mod add_call_guards; pub mod promote_consts; -pub mod qualify_consts; pub mod qualify_min_const_fn; pub mod remove_noop_landing_pads; pub mod dump_mir; @@ -238,18 +237,14 @@ fn mir_validated( tcx: TyCtxt<'tcx>, def_id: DefId, ) -> (&'tcx Steal>, &'tcx Steal>>) { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - if let hir::BodyOwnerKind::Const = tcx.hir().body_owner_kind(hir_id) { - // Ensure that we compute the `mir_const_qualif` for constants at - // this point, before we steal the mir-const result. - let _ = tcx.mir_const_qualif(def_id); - } + // Ensure that we compute the `mir_const_qualif` for constants at + // this point, before we steal the mir-const result. + let _ = tcx.mir_const_qualif(def_id); let mut body = tcx.mir_const(def_id).steal(); let promote_pass = promote_consts::PromoteTemps::default(); run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[ // What we need to run borrowck etc. - &qualify_consts::QualifyAndPromoteConstants::default(), &promote_pass, &simplify::SimplifyCfg::new("qualify-consts"), ]); diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs deleted file mode 100644 index ee5dfae6ce251..0000000000000 --- a/src/librustc_mir/transform/qualify_consts.rs +++ /dev/null @@ -1,1479 +0,0 @@ -//! A pass that qualifies constness of temporaries in constants, -//! static initializers and functions and also drives promotion. -//! -//! The Qualif flags below can be used to also provide better -//! diagnostics as to why a constant rvalue wasn't promoted. - -use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; -use rustc_target::spec::abi::Abi; -use rustc::hir; -use rustc::hir::def_id::DefId; -use rustc::traits::{self, TraitEngine}; -use rustc::ty::{self, TyCtxt, Ty, TypeFoldable}; -use rustc::ty::cast::CastTy; -use rustc::ty::query::Providers; -use rustc::mir::*; -use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext}; -use rustc::middle::lang_items; -use rustc::session::config::nightly_options; -use syntax::feature_gate::{emit_feature_err, GateIssue}; -use syntax::symbol::sym; -use syntax_pos::{Span, DUMMY_SP}; - -use std::borrow::Cow; -use std::cell::Cell; -use std::fmt; -use std::ops::{Deref, Index, IndexMut}; -use std::usize; - -use rustc::hir::HirId; -use crate::transform::{MirPass, MirSource}; -use crate::transform::check_consts::ops::{self, NonConstOp}; - -use rustc_error_codes::*; - -/// What kind of item we are in. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum Mode { - /// A `static` item. - Static, - /// A `static mut` item. - StaticMut, - /// A `const fn` item. - ConstFn, - /// A `const` item or an anonymous constant (e.g. in array lengths). - Const, - /// Other type of `fn`. - NonConstFn, -} - -impl Mode { - /// Determine whether we have to do full const-checking because syntactically, we - /// are required to be "const". - #[inline] - fn requires_const_checking(self) -> bool { - self != Mode::NonConstFn - } -} - -impl fmt::Display for Mode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Mode::Const => write!(f, "constant"), - Mode::Static | Mode::StaticMut => write!(f, "static"), - Mode::ConstFn => write!(f, "constant function"), - Mode::NonConstFn => write!(f, "function") - } - } -} - -const QUALIF_COUNT: usize = 2; - -// FIXME(eddyb) once we can use const generics, replace this array with -// something like `IndexVec` but for fixed-size arrays (`IndexArray`?). -#[derive(Copy, Clone, Default)] -struct PerQualif([T; QUALIF_COUNT]); - -impl PerQualif { - fn new(x: T) -> Self { - PerQualif([x.clone(), x]) - } -} - -impl PerQualif { - fn as_mut(&mut self) -> PerQualif<&mut T> { - let [x0, x1] = &mut self.0; - PerQualif([x0, x1]) - } - - fn zip(self, other: PerQualif) -> PerQualif<(T, U)> { - let [x0, x1] = self.0; - let [y0, y1] = other.0; - PerQualif([(x0, y0), (x1, y1)]) - } -} - -impl PerQualif { - fn encode_to_bits(self) -> u8 { - self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| { - bits | ((qualif as u8) << i) - }) - } - - fn decode_from_bits(bits: u8) -> Self { - let mut qualifs = Self::default(); - for (i, qualif) in qualifs.0.iter_mut().enumerate() { - *qualif = (bits & (1 << i)) != 0; - } - qualifs - } -} - -impl Index for PerQualif { - type Output = T; - - fn index(&self, _: Q) -> &T { - &self.0[Q::IDX] - } -} - -impl IndexMut for PerQualif { - fn index_mut(&mut self, _: Q) -> &mut T { - &mut self.0[Q::IDX] - } -} - -struct ConstCx<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - mode: Mode, - body: &'a Body<'tcx>, - - per_local: PerQualif>, -} - -impl<'a, 'tcx> ConstCx<'a, 'tcx> { - fn is_const_panic_fn(&self, def_id: DefId) -> bool { - Some(def_id) == self.tcx.lang_items().panic_fn() || - Some(def_id) == self.tcx.lang_items().begin_panic_fn() - } -} - -#[derive(Copy, Clone, Debug)] -enum ValueSource<'a, 'tcx> { - Rvalue(&'a Rvalue<'tcx>), - DropAndReplace(&'a Operand<'tcx>), - Call { - callee: &'a Operand<'tcx>, - args: &'a [Operand<'tcx>], - return_ty: Ty<'tcx>, - }, -} - -/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some -/// code for promotion or prevent it from evaluating at compile time. So `return true` means -/// "I found something bad, no reason to go on searching". `false` is only returned if we -/// definitely cannot find anything bad anywhere. -/// -/// The default implementations proceed structurally. -trait Qualif { - const IDX: usize; - - /// Return the qualification that is (conservatively) correct for any value - /// of the type, or `None` if the qualification is not value/type-based. - fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option { - None - } - - /// Return a mask for the qualification, given a type. This is `false` iff - /// no value of that type can have the qualification. - fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { - Self::in_any_value_of_ty(cx, ty).unwrap_or(true) - } - - fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool { - cx.per_local.0[Self::IDX].contains(local) - } - - fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool { - // FIXME(eddyb) should we do anything here for value properties? - false - } - - fn in_projection_structurally( - cx: &ConstCx<'_, 'tcx>, - place: PlaceRef<'_, 'tcx>, - ) -> bool { - if let [proj_base @ .., elem] = place.projection { - let base_qualif = Self::in_place(cx, PlaceRef { - base: place.base, - projection: proj_base, - }); - let qualif = base_qualif && Self::mask_for_ty( - cx, - Place::ty_from(place.base, proj_base, cx.body, cx.tcx) - .projection_ty(cx.tcx, elem) - .ty, - ); - match elem { - ProjectionElem::Deref | - ProjectionElem::Subslice { .. } | - ProjectionElem::Field(..) | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Downcast(..) => qualif, - - // FIXME(eddyb) shouldn't this be masked *after* including the - // index local? Then again, it's `usize` which is neither - // `HasMutInterior` nor `NeedsDrop`. - ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local), - } - } else { - bug!("This should be called if projection is not empty"); - } - } - - fn in_projection( - cx: &ConstCx<'_, 'tcx>, - place: PlaceRef<'_, 'tcx>, - ) -> bool { - Self::in_projection_structurally(cx, place) - } - - fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool { - match place { - PlaceRef { - base: PlaceBase::Local(local), - projection: [], - } => Self::in_local(cx, *local), - PlaceRef { - base: PlaceBase::Static(box Static { - kind: StaticKind::Promoted(..), - .. - }), - projection: [], - } => bug!("qualifying already promoted MIR"), - PlaceRef { - base: PlaceBase::Static(static_), - projection: [], - } => { - Self::in_static(cx, static_) - }, - PlaceRef { - base: _, - projection: [.., _], - } => Self::in_projection(cx, place), - } - } - - fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool { - match *operand { - Operand::Copy(ref place) | - Operand::Move(ref place) => Self::in_place(cx, place.as_ref()), - - Operand::Constant(ref constant) => { - if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val { - // Don't peek inside trait associated constants. - if cx.tcx.trait_of_item(def_id).is_some() { - Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false) - } else { - let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id); - - let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX]; - - // Just in case the type is more specific than - // the definition, e.g., impl associated const - // with type parameters, take it into account. - qualif && Self::mask_for_ty(cx, constant.literal.ty) - } - } else { - false - } - } - } - } - - fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { - match *rvalue { - Rvalue::NullaryOp(..) => false, - - Rvalue::Discriminant(ref place) | - Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()), - - Rvalue::Use(ref operand) | - Rvalue::Repeat(ref operand, _) | - Rvalue::UnaryOp(_, ref operand) | - Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand), - - Rvalue::BinaryOp(_, ref lhs, ref rhs) | - Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => { - Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs) - } - - Rvalue::Ref(_, _, ref place) => { - // Special-case reborrows to be more like a copy of the reference. - if let &[ref proj_base @ .., elem] = place.projection.as_ref() { - if ProjectionElem::Deref == elem { - let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty; - if let ty::Ref(..) = base_ty.kind { - return Self::in_place(cx, PlaceRef { - base: &place.base, - projection: proj_base, - }); - } - } - } - - Self::in_place(cx, place.as_ref()) - } - - Rvalue::Aggregate(_, ref operands) => { - operands.iter().any(|o| Self::in_operand(cx, o)) - } - } - } - - fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { - Self::in_rvalue_structurally(cx, rvalue) - } - - fn in_call( - cx: &ConstCx<'_, 'tcx>, - _callee: &Operand<'tcx>, - _args: &[Operand<'tcx>], - return_ty: Ty<'tcx>, - ) -> bool { - // Be conservative about the returned value of a const fn. - Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false) - } - - fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool { - match source { - ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue), - ValueSource::DropAndReplace(source) => Self::in_operand(cx, source), - ValueSource::Call { callee, args, return_ty } => { - Self::in_call(cx, callee, args, return_ty) - } - } - } -} - -/// Constant containing interior mutability (`UnsafeCell`). -/// This must be ruled out to make sure that evaluating the constant at compile-time -/// and at *any point* during the run-time would produce the same result. In particular, -/// promotion of temporaries must not change program behavior; if the promoted could be -/// written to, that would be a problem. -struct HasMutInterior; - -impl Qualif for HasMutInterior { - const IDX: usize = 0; - - fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option { - Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)) - } - - fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { - match *rvalue { - // Returning `true` for `Rvalue::Ref` indicates the borrow isn't - // allowed in constants (and the `Checker` will error), and/or it - // won't be promoted, due to `&mut ...` or interior mutability. - Rvalue::Ref(_, kind, ref place) => { - let ty = place.ty(cx.body, cx.tcx).ty; - - if let BorrowKind::Mut { .. } = kind { - // In theory, any zero-sized value could be borrowed - // mutably without consequences. However, only &mut [] - // is allowed right now, and only in functions. - if cx.mode == Mode::StaticMut { - // Inside a `static mut`, &mut [...] is also allowed. - match ty.kind { - ty::Array(..) | ty::Slice(_) => {} - _ => return true, - } - } else if let ty::Array(_, len) = ty.kind { - // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition - // seems unnecessary, given that this is merely a ZST. - match len.try_eval_usize(cx.tcx, cx.param_env) { - Some(0) if cx.mode == Mode::NonConstFn => {}, - _ => return true, - } - } else { - return true; - } - } - } - - Rvalue::Aggregate(ref kind, _) => { - if let AggregateKind::Adt(def, ..) = **kind { - if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() { - let ty = rvalue.ty(cx.body, cx.tcx); - assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true)); - return true; - } - } - } - - _ => {} - } - - Self::in_rvalue_structurally(cx, rvalue) - } -} - -/// Constant containing an ADT that implements `Drop`. -/// This must be ruled out (a) because we cannot run `Drop` during compile-time -/// as that might not be a `const fn`, and (b) because implicit promotion would -/// remove side-effects that occur as part of dropping that value. -struct NeedsDrop; - -impl Qualif for NeedsDrop { - const IDX: usize = 1; - - fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option { - Some(ty.needs_drop(cx.tcx, cx.param_env)) - } - - fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { - if let Rvalue::Aggregate(ref kind, _) = *rvalue { - if let AggregateKind::Adt(def, ..) = **kind { - if def.has_dtor(cx.tcx) { - return true; - } - } - } - - Self::in_rvalue_structurally(cx, rvalue) - } -} - -// Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`). -macro_rules! static_assert_seq_qualifs { - ($i:expr => $first:ident $(, $rest:ident)*) => { - static_assert!({ - static_assert_seq_qualifs!($i + 1 => $($rest),*); - - $first::IDX == $i - }); - }; - ($i:expr =>) => { - static_assert!(QUALIF_COUNT == $i); - }; -} -static_assert_seq_qualifs!( - 0 => HasMutInterior, NeedsDrop -); - -impl ConstCx<'_, 'tcx> { - fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif { - let mut qualifs = PerQualif::default(); - qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false); - qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false); - qualifs - } - - fn qualifs_in_local(&self, local: Local) -> PerQualif { - let mut qualifs = PerQualif::default(); - qualifs[HasMutInterior] = HasMutInterior::in_local(self, local); - qualifs[NeedsDrop] = NeedsDrop::in_local(self, local); - qualifs - } - - fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif { - let mut qualifs = PerQualif::default(); - qualifs[HasMutInterior] = HasMutInterior::in_value(self, source); - qualifs[NeedsDrop] = NeedsDrop::in_value(self, source); - qualifs - } -} - -/// Checks MIR for being admissible as a compile-time constant, using `ConstCx` -/// for value qualifications, and accumulates writes of -/// rvalue/call results to locals, in `local_qualif`. -/// It also records candidates for promotion in `promotion_candidates`, -/// both in functions and const/static items. -struct Checker<'a, 'tcx> { - cx: ConstCx<'a, 'tcx>, - - span: Span, - def_id: DefId, - - /// If `true`, do not emit errors to the user, merely collect them in `errors`. - suppress_errors: bool, - errors: Vec<(Span, String)>, -} - -macro_rules! unleash_miri { - ($this:expr) => {{ - if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { - if $this.mode.requires_const_checking() && !$this.suppress_errors { - $this.tcx.sess.span_warn($this.span, "skipping const checks"); - } - return; - } - }} -} - -impl Deref for Checker<'a, 'tcx> { - type Target = ConstCx<'a, 'tcx>; - - fn deref(&self) -> &Self::Target { - &self.cx - } -} - -impl<'a, 'tcx> Checker<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self { - assert!(def_id.is_local()); - - let param_env = tcx.param_env(def_id); - - let mut cx = ConstCx { - tcx, - param_env, - mode, - body, - per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())), - }; - - for (local, decl) in body.local_decls.iter_enumerated() { - if let LocalKind::Arg = body.local_kind(local) { - let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty); - for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 { - if *qualif { - per_local.insert(local); - } - } - } - } - - Checker { - cx, - span: body.span, - def_id, - errors: vec![], - suppress_errors: false, - } - } - - // FIXME(eddyb) we could split the errors into meaningful - // categories, but enabling full miri would make that - // slightly pointless (even with feature-gating). - fn not_const(&mut self, op: impl NonConstOp) { - unleash_miri!(self); - if self.mode.requires_const_checking() && !self.suppress_errors { - self.record_error(op); - let mut err = struct_span_err!( - self.tcx.sess, - self.span, - E0019, - "{} contains unimplemented expression type", - self.mode - ); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note("A function call isn't allowed in the const's initialization expression \ - because the expression's value must be known at compile-time."); - err.note("Remember: you can't use a function call inside a const's initialization \ - expression! However, you can use it anywhere else."); - } - err.emit(); - } - } - - fn record_error(&mut self, op: impl NonConstOp) { - self.record_error_spanned(op, self.span); - } - - fn record_error_spanned(&mut self, op: impl NonConstOp, span: Span) { - self.errors.push((span, format!("{:?}", op))); - } - - /// Assigns an rvalue/call qualification to the given destination. - fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) { - trace!("assign: {:?} <- {:?}", dest, source); - - let mut qualifs = self.qualifs_in_value(source); - - match source { - ValueSource::Rvalue(&Rvalue::Ref(_, kind, _)) => { - // Getting `true` from `HasMutInterior::in_rvalue` means - // the borrowed place is disallowed from being borrowed, - // due to either a mutable borrow (with some exceptions), - // or an shared borrow of a value with interior mutability. - // Then `HasMutInterior` is cleared - // to avoid duplicate errors (e.g. from reborrowing). - if qualifs[HasMutInterior] { - qualifs[HasMutInterior] = false; - - debug!("suppress_errors: {}", self.suppress_errors); - if self.mode.requires_const_checking() && !self.suppress_errors { - if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { - self.record_error(ops::MutBorrow(kind)); - if let BorrowKind::Mut { .. } = kind { - let mut err = struct_span_err!(self.tcx.sess, self.span, E0017, - "references in {}s may only refer \ - to immutable values", self.mode); - err.span_label(self.span, format!("{}s require immutable values", - self.mode)); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note("References in statics and constants may only refer \ - to immutable values.\n\n\ - Statics are shared everywhere, and if they refer to \ - mutable data one might violate memory safety since \ - holding multiple mutable references to shared data \ - is not allowed.\n\n\ - If you really want global mutable state, try using \ - static mut or a global UnsafeCell."); - } - err.emit(); - } else { - span_err!(self.tcx.sess, self.span, E0492, - "cannot borrow a constant which may contain \ - interior mutability, create a static instead"); - } - } - } - } - }, - _ => {}, - } - - let mut dest_projection = &dest.projection[..]; - let index = loop { - match (&dest.base, dest_projection) { - // We treat all locals equal in constants - (&PlaceBase::Local(index), []) => break index, - // projections are transparent for assignments - // we qualify the entire destination at once, even if just a field would have - // stricter qualification - (base, [proj_base @ .., _]) => { - // Catch more errors in the destination. `visit_place` also checks various - // projection rules like union field access and raw pointer deref - let context = PlaceContext::MutatingUse(MutatingUseContext::Store); - self.visit_place_base(base, context, location); - self.visit_projection(base, dest_projection, context, location); - dest_projection = proj_base; - }, - (&PlaceBase::Static(box Static { - kind: StaticKind::Promoted(..), - .. - }), []) => bug!("promoteds don't exist yet during promotion"), - (&PlaceBase::Static(box Static{ kind: _, .. }), []) => { - // Catch more errors in the destination. `visit_place` also checks that we - // do not try to access statics from constants or try to mutate statics - let context = PlaceContext::MutatingUse(MutatingUseContext::Store); - self.visit_place_base(&dest.base, context, location); - return; - } - } - }; - - let kind = self.body.local_kind(index); - debug!("store to {:?} {:?}", kind, index); - - // this is overly restrictive, because even full assignments do not clear the qualif - // While we could special case full assignments, this would be inconsistent with - // aggregates where we overwrite all fields via assignments, which would not get - // that feature. - for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 { - if *qualif { - per_local.insert(index); - } - } - } - - /// Check a whole const, static initializer or const fn. - fn check_const(&mut self) -> u8 { - use crate::transform::check_consts as new_checker; - - debug!("const-checking {} {:?}", self.mode, self.def_id); - - // FIXME: Also use the new validator when features that require it (e.g. `const_if`) are - // enabled. - let use_new_validator = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; - if use_new_validator { - debug!("Using dataflow-based const validator"); - } - - let item = new_checker::Item::new(self.tcx, self.def_id, self.body); - let mut validator = new_checker::validation::Validator::new(&item); - - validator.suppress_errors = !use_new_validator; - self.suppress_errors = use_new_validator; - - let body = self.body; - - let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len()); - let mut bb = START_BLOCK; - loop { - seen_blocks.insert(bb.index()); - - self.visit_basic_block_data(bb, &body[bb]); - validator.visit_basic_block_data(bb, &body[bb]); - - let target = match body[bb].terminator().kind { - TerminatorKind::Goto { target } | - TerminatorKind::FalseUnwind { real_target: target, .. } | - TerminatorKind::Drop { target, .. } | - TerminatorKind::DropAndReplace { target, .. } | - TerminatorKind::Assert { target, .. } | - TerminatorKind::Call { destination: Some((_, target)), .. } => { - Some(target) - } - - // Non-terminating calls cannot produce any value. - TerminatorKind::Call { destination: None, .. } => { - break; - } - - TerminatorKind::SwitchInt {..} | - TerminatorKind::Resume | - TerminatorKind::Abort | - TerminatorKind::GeneratorDrop | - TerminatorKind::Yield { .. } | - TerminatorKind::Unreachable | - TerminatorKind::FalseEdges { .. } => None, - - TerminatorKind::Return => { - break; - } - }; - - match target { - // No loops allowed. - Some(target) if !seen_blocks.contains(target.index()) => { - bb = target; - } - _ => { - 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", - ); - } - break; - } - } - } - - // The new validation pass should agree with the old when running on simple const bodies - // (e.g. no `if` or `loop`). - if !use_new_validator { - let mut new_errors = validator.take_errors(); - - // FIXME: each checker sometimes emits the same error with the same span twice in a row. - self.errors.dedup(); - new_errors.dedup(); - - if self.errors != new_errors { - validator_mismatch( - self.tcx, - body, - std::mem::replace(&mut self.errors, vec![]), - new_errors, - ); - } - } - - self.qualifs_in_local(RETURN_PLACE).encode_to_bits() - } -} - -impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { - fn visit_place_base( - &mut self, - place_base: &PlaceBase<'tcx>, - context: PlaceContext, - location: Location, - ) { - self.super_place_base(place_base, context, location); - match place_base { - PlaceBase::Local(_) => {} - PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => { - unreachable!() - } - PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => { - if self.tcx - .get_attrs(*def_id) - .iter() - .any(|attr| attr.check_name(sym::thread_local)) { - if self.mode.requires_const_checking() && !self.suppress_errors { - self.record_error(ops::ThreadLocalAccess); - span_err!(self.tcx.sess, self.span, E0625, - "thread-local statics cannot be \ - accessed at compile-time"); - } - return; - } - - // Only allow statics (not consts) to refer to other statics. - if self.mode == Mode::Static || self.mode == Mode::StaticMut { - return; - } - unleash_miri!(self); - - if self.mode.requires_const_checking() && !self.suppress_errors { - self.record_error(ops::StaticAccess); - let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, - "{}s cannot refer to statics, use \ - a constant instead", self.mode); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "Static and const variables can refer to other const variables. \ - But a const variable cannot refer to a static variable." - ); - err.help( - "To fix this, the value can be extracted as a const and then used." - ); - } - err.emit() - } - } - } - } - - fn visit_projection_elem( - &mut self, - place_base: &PlaceBase<'tcx>, - proj_base: &[PlaceElem<'tcx>], - elem: &PlaceElem<'tcx>, - context: PlaceContext, - location: Location, - ) { - debug!( - "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \ - context={:?} location={:?}", - place_base, - proj_base, - elem, - context, - location, - ); - - self.super_projection_elem(place_base, proj_base, elem, context, location); - - match elem { - ProjectionElem::Deref => { - if context.is_mutating_use() { - // `not_const` errors out in const contexts - self.not_const(ops::MutDeref) - } - let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty; - match self.mode { - Mode::NonConstFn => {} - _ if self.suppress_errors => {} - _ => { - if let ty::RawPtr(_) = base_ty.kind { - if !self.tcx.features().const_raw_ptr_deref { - self.record_error(ops::RawPtrDeref); - emit_feature_err( - &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref, - self.span, GateIssue::Language, - &format!( - "dereferencing raw pointers in {}s is unstable", - self.mode, - ), - ); - } - } - } - } - } - - ProjectionElem::ConstantIndex {..} | - ProjectionElem::Subslice {..} | - ProjectionElem::Field(..) | - ProjectionElem::Index(_) => { - let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty; - if let Some(def) = base_ty.ty_adt_def() { - if def.is_union() { - match self.mode { - Mode::ConstFn => { - if !self.tcx.features().const_fn_union - && !self.suppress_errors - { - self.record_error(ops::UnionAccess); - emit_feature_err( - &self.tcx.sess.parse_sess, sym::const_fn_union, - self.span, GateIssue::Language, - "unions in const fn are unstable", - ); - } - }, - - | Mode::NonConstFn - | Mode::Static - | Mode::StaticMut - | Mode::Const - => {}, - } - } - } - } - - ProjectionElem::Downcast(..) => { - self.not_const(ops::Downcast) - } - } - } - - fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { - debug!("visit_operand: operand={:?} location={:?}", operand, location); - self.super_operand(operand, location); - - match *operand { - Operand::Move(ref place) => { - // Mark the consumed locals to indicate later drops are noops. - if let Some(local) = place.as_local() { - self.cx.per_local[NeedsDrop].remove(local); - } - } - Operand::Copy(_) | - Operand::Constant(_) => {} - } - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location); - - // Check nested operands and places. - if let Rvalue::Ref(_, kind, ref place) = *rvalue { - // Special-case reborrows. - let mut reborrow_place = None; - if let &[ref proj_base @ .., elem] = place.projection.as_ref() { - if elem == ProjectionElem::Deref { - let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty; - if let ty::Ref(..) = base_ty.kind { - reborrow_place = Some(proj_base); - } - } - } - - if let Some(proj) = reborrow_place { - let ctx = match kind { - BorrowKind::Shared => PlaceContext::NonMutatingUse( - NonMutatingUseContext::SharedBorrow, - ), - BorrowKind::Shallow => PlaceContext::NonMutatingUse( - NonMutatingUseContext::ShallowBorrow, - ), - BorrowKind::Unique => PlaceContext::NonMutatingUse( - NonMutatingUseContext::UniqueBorrow, - ), - BorrowKind::Mut { .. } => PlaceContext::MutatingUse( - MutatingUseContext::Borrow, - ), - }; - self.visit_place_base(&place.base, ctx, location); - self.visit_projection(&place.base, proj, ctx, location); - } else { - self.super_rvalue(rvalue, location); - } - } else { - self.super_rvalue(rvalue, location); - } - - match *rvalue { - Rvalue::Use(_) | - Rvalue::Repeat(..) | - Rvalue::UnaryOp(UnOp::Neg, _) | - Rvalue::UnaryOp(UnOp::Not, _) | - Rvalue::NullaryOp(NullOp::SizeOf, _) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::Cast(CastKind::Pointer(_), ..) | - Rvalue::Discriminant(..) | - Rvalue::Len(_) | - Rvalue::Ref(..) | - Rvalue::Aggregate(..) => {} - - Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { - let operand_ty = operand.ty(self.body, self.tcx); - let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - match (cast_in, cast_out) { - (CastTy::Ptr(_), CastTy::Int(_)) | - (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => { - unleash_miri!(self); - if !self.tcx.features().const_raw_ptr_to_usize_cast - && !self.suppress_errors - { - // in const fn and constants require the feature gate - // FIXME: make it unsafe inside const fn and constants - self.record_error(ops::RawPtrToIntCast); - emit_feature_err( - &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast, - self.span, GateIssue::Language, - &format!( - "casting pointers to integers in {}s is unstable", - self.mode, - ), - ); - } - } - _ => {} - } - } - - Rvalue::BinaryOp(op, ref lhs, _) => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { - assert!(op == BinOp::Eq || op == BinOp::Ne || - op == BinOp::Le || op == BinOp::Lt || - op == BinOp::Ge || op == BinOp::Gt || - op == BinOp::Offset); - - unleash_miri!(self); - if self.mode.requires_const_checking() && - !self.tcx.features().const_compare_raw_pointers && - !self.suppress_errors - { - self.record_error(ops::RawPtrComparison); - // require the feature gate inside constants and const fn - // FIXME: make it unsafe to use these operations - emit_feature_err( - &self.tcx.sess.parse_sess, - sym::const_compare_raw_pointers, - self.span, - GateIssue::Language, - &format!("comparing raw pointers inside {}", self.mode), - ); - } - } - } - - Rvalue::NullaryOp(NullOp::Box, _) => { - unleash_miri!(self); - if self.mode.requires_const_checking() && !self.suppress_errors { - self.record_error(ops::HeapAllocation); - let mut err = struct_span_err!(self.tcx.sess, self.span, E0010, - "allocations are not allowed in {}s", self.mode); - err.span_label(self.span, format!("allocation not allowed in {}s", self.mode)); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "The value of statics and constants must be known at compile time, \ - and they live for the entire lifetime of a program. Creating a boxed \ - value allocates memory on the heap at runtime, and therefore cannot \ - be done at compile time." - ); - } - err.emit(); - } - } - } - } - - fn visit_terminator_kind(&mut self, - kind: &TerminatorKind<'tcx>, - location: Location) { - debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location); - if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind { - if let Some((ref dest, _)) = *destination { - self.assign(dest, ValueSource::Call { - callee: func, - args, - return_ty: dest.ty(self.body, self.tcx).ty, - }, location); - } - - let fn_ty = func.ty(self.body, self.tcx); - match fn_ty.kind { - ty::FnDef(def_id, _) => { - match self.tcx.fn_sig(def_id).abi() { - Abi::RustIntrinsic | - Abi::PlatformIntrinsic => { - assert!(!self.tcx.is_const_fn(def_id)); - match &*self.tcx.item_name(def_id).as_str() { - // special intrinsic that can be called diretly without an intrinsic - // feature gate needs a language feature gate - "transmute" => { - if self.mode.requires_const_checking() - && !self.suppress_errors - { - // const eval transmute calls only with the feature gate - if !self.tcx.features().const_transmute { - self.record_error(ops::Transmute); - emit_feature_err( - &self.tcx.sess.parse_sess, sym::const_transmute, - self.span, GateIssue::Language, - &format!("The use of std::mem::transmute() \ - is gated in {}s", self.mode)); - } - } - } - - // no need to check feature gates, intrinsics are only callable - // from the libstd or with forever unstable feature gates - _ => {} - } - } - _ => { - // In normal functions no calls are feature-gated. - if self.mode.requires_const_checking() { - let unleash_miri = self - .tcx - .sess - .opts - .debugging_opts - .unleash_the_miri_inside_of_you; - if self.tcx.is_const_fn(def_id) - || unleash_miri - || self.suppress_errors - { - // stable const fns or unstable const fns - // with their feature gate active - // FIXME(eddyb) move stability checks from `is_const_fn` here. - } else if self.is_const_panic_fn(def_id) { - // Check the const_panic feature gate. - // FIXME: cannot allow this inside `allow_internal_unstable` - // because that would make `panic!` insta stable in constants, - // since the macro is marked with the attribute. - if !self.tcx.features().const_panic { - // Don't allow panics in constants without the feature gate. - self.record_error(ops::Panic); - emit_feature_err( - &self.tcx.sess.parse_sess, - sym::const_panic, - self.span, - GateIssue::Language, - &format!("panicking in {}s is unstable", self.mode), - ); - } - } else if let Some(feature) - = self.tcx.is_unstable_const_fn(def_id) { - // Check `#[unstable]` const fns or `#[rustc_const_unstable]` - // functions without the feature gate active in this crate in - // order to report a better error message than the one below. - if !self.span.allows_unstable(feature) { - self.record_error(ops::FnCallUnstable(def_id, feature)); - let mut err = self.tcx.sess.struct_span_err(self.span, - &format!("`{}` is not yet stable as a const fn", - self.tcx.def_path_str(def_id))); - if nightly_options::is_nightly_build() { - help!(&mut err, - "add `#![feature({})]` to the \ - crate attributes to enable", - feature); - } - err.emit(); - } - } else { - self.record_error(ops::FnCallNonConst(def_id)); - let mut err = struct_span_err!( - self.tcx.sess, - self.span, - E0015, - "calls in {}s are limited to constant functions, \ - tuple structs and tuple variants", - self.mode, - ); - err.emit(); - } - } - } - } - } - ty::FnPtr(_) => { - unleash_miri!(self); - if self.mode.requires_const_checking() && !self.suppress_errors { - self.record_error(ops::FnCallIndirect); - let mut err = self.tcx.sess.struct_span_err( - self.span, - "function pointers are not allowed in const fn" - ); - err.emit(); - } - } - _ => { - self.not_const(ops::FnCallOther); - } - } - - // Check callee and argument operands. - self.visit_operand(func, location); - for arg in args { - self.visit_operand(arg, location); - } - } else if let TerminatorKind::Drop { - location: ref place, .. - } | TerminatorKind::DropAndReplace { - location: ref place, .. - } = *kind { - match *kind { - TerminatorKind::DropAndReplace { .. } => {} - _ => self.super_terminator_kind(kind, location), - } - - // Deny *any* live drops anywhere other than functions. - if self.mode.requires_const_checking() && !self.suppress_errors { - unleash_miri!(self); - // HACK(eddyb): emulate a bit of dataflow analysis, - // conservatively, that drop elaboration will do. - let needs_drop = if let Some(local) = place.as_local() { - if NeedsDrop::in_local(self, local) { - Some(self.body.local_decls[local].source_info.span) - } else { - None - } - } else { - Some(self.span) - }; - - if let Some(span) = needs_drop { - // Double-check the type being dropped, to minimize false positives. - let ty = place.ty(self.body, self.tcx).ty; - if ty.needs_drop(self.tcx, self.param_env) { - self.record_error_spanned(ops::LiveDrop, span); - struct_span_err!(self.tcx.sess, span, E0493, - "destructors cannot be evaluated at compile-time") - .span_label(span, format!("{}s cannot evaluate destructors", - self.mode)) - .emit(); - } - } - } - - match *kind { - TerminatorKind::DropAndReplace { ref value, .. } => { - self.assign(place, ValueSource::DropAndReplace(value), location); - self.visit_operand(value, location); - } - _ => {} - } - } else { - // Qualify any operands inside other terminators. - self.super_terminator_kind(kind, location); - } - } - - fn visit_assign(&mut self, - dest: &Place<'tcx>, - rvalue: &Rvalue<'tcx>, - location: Location) { - debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location); - self.assign(dest, ValueSource::Rvalue(rvalue), location); - - self.visit_rvalue(rvalue, location); - } - - fn visit_source_info(&mut self, source_info: &SourceInfo) { - debug!("visit_source_info: source_info={:?}", source_info); - self.span = source_info.span; - } - - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { - debug!("visit_statement: statement={:?} location={:?}", statement, location); - match statement.kind { - StatementKind::Assign(..) => { - self.super_statement(statement, location); - } - StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { - 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", - ); - } - } - // FIXME(eddyb) should these really do nothing? - StatementKind::FakeRead(..) | - StatementKind::SetDiscriminant { .. } | - StatementKind::StorageLive(_) | - StatementKind::StorageDead(_) | - StatementKind::InlineAsm {..} | - StatementKind::Retag { .. } | - StatementKind::AscribeUserType(..) | - StatementKind::Nop => {} - } - } -} - -pub fn provide(providers: &mut Providers<'_>) { - *providers = Providers { - mir_const_qualif, - ..*providers - }; -} - -// FIXME(eddyb) this is only left around for the validation logic -// in `promote_consts`, see the comment in `validate_operand`. -pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2; - -fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { - // N.B., this `borrow()` is guaranteed to be valid (i.e., the value - // cannot yet be stolen), because `mir_validated()`, which steals - // from `mir_const(), forces this query to execute before - // performing the steal. - let body = &tcx.mir_const(def_id).borrow(); - - if body.return_ty().references_error() { - tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors"); - return 0; - } - - Checker::new(tcx, def_id, body, Mode::Const).check_const() -} - -pub struct QualifyAndPromoteConstants<'tcx> { - pub promoted: Cell>>, -} - -impl<'tcx> Default for QualifyAndPromoteConstants<'tcx> { - fn default() -> Self { - QualifyAndPromoteConstants { - promoted: Cell::new(IndexVec::new()), - } - } -} - -impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { - // There's not really any point in promoting errorful MIR. - if body.return_ty().references_error() { - tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors"); - return; - } - - if src.promoted.is_some() { - return; - } - - let def_id = src.def_id(); - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - - let mode = determine_mode(tcx, hir_id, def_id); - - debug!("run_pass: mode={:?}", mode); - if let Mode::NonConstFn = mode { - // No need to const-check a non-const `fn` now that we don't do promotion here. - return; - } else if let Mode::ConstFn = mode { - let mut checker = Checker::new(tcx, def_id, body, mode); - let use_min_const_fn_checks = - !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you && - tcx.is_min_const_fn(def_id); - if use_min_const_fn_checks { - // Enforce `min_const_fn` for stable `const fn`s. - use super::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_const` should not produce any errors, but better safe than sorry - // FIXME(#53819) - // Enforce a constant-like CFG for `const fn`. - checker.check_const(); - } else { - check_short_circuiting_in_const_local(tcx, body, mode); - - match mode { - Mode::Const => tcx.mir_const_qualif(def_id), - _ => Checker::new(tcx, def_id, body, mode).check_const(), - }; - } - - if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) { - // `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`. - check_static_is_sync(tcx, body, hir_id); - } - } -} - -fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode { - match tcx.hir().body_owner_kind(hir_id) { - hir::BodyOwnerKind::Closure => Mode::NonConstFn, - hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn, - hir::BodyOwnerKind::Fn => Mode::NonConstFn, - hir::BodyOwnerKind::Const => Mode::Const, - hir::BodyOwnerKind::Static(hir::Mutability::Immutable) => Mode::Static, - hir::BodyOwnerKind::Static(hir::Mutability::Mutable) => Mode::StaticMut, - } -} - -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(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) { - 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 = tcx.sess.struct_span_err( - span, - &format!( - "new features like let bindings are not permitted in {}s \ - which also use short circuiting operators", - mode, - ), - ); - 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_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut 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); - } - }); -} - -fn validator_mismatch( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - mut old_errors: Vec<(Span, String)>, - mut new_errors: Vec<(Span, String)>, -) { - error!("old validator: {:?}", old_errors); - error!("new validator: {:?}", new_errors); - - // ICE on nightly if the validators do not emit exactly the same errors. - // Users can supress this panic with an unstable compiler flag (hopefully after - // filing an issue). - let opts = &tcx.sess.opts; - let strict_validation_enabled = opts.unstable_features.is_nightly_build() - && !opts.debugging_opts.suppress_const_validation_back_compat_ice; - - if !strict_validation_enabled { - return; - } - - // If this difference would cause a regression from the old to the new or vice versa, trigger - // the ICE. - if old_errors.is_empty() || new_errors.is_empty() { - span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR); - } - - // HACK: Borrows that would allow mutation are forbidden in const contexts, but they cause the - // new validator to be more conservative about when a dropped local has been moved out of. - // - // Supress the mismatch ICE in cases where the validators disagree only on the number of - // `LiveDrop` errors and both observe the same sequence of `MutBorrow`s. - - let is_live_drop = |(_, s): &mut (_, String)| s.starts_with("LiveDrop"); - let is_mut_borrow = |(_, s): &&(_, String)| s.starts_with("MutBorrow"); - - let old_live_drops: Vec<_> = old_errors.drain_filter(is_live_drop).collect(); - let new_live_drops: Vec<_> = new_errors.drain_filter(is_live_drop).collect(); - - let only_live_drops_differ = old_live_drops != new_live_drops && old_errors == new_errors; - - let old_mut_borrows = old_errors.iter().filter(is_mut_borrow); - let new_mut_borrows = new_errors.iter().filter(is_mut_borrow); - - let at_least_one_mut_borrow = old_mut_borrows.clone().next().is_some(); - - if only_live_drops_differ && at_least_one_mut_borrow && old_mut_borrows.eq(new_mut_borrows) { - return; - } - - span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR); -} - -const VALIDATOR_MISMATCH_ERR: &str = - r"Disagreement between legacy and dataflow-based const validators. - After filing an issue, use `-Zsuppress-const-validation-back-compat-ice` to compile your code."; From 752776699357be34e7819ffb235438df7ccf1a9a Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 10 Nov 2019 10:39:27 -0800 Subject: [PATCH 07/17] Move `delay_span_bug` into `emit_error` for if/loop --- .../transform/check_consts/ops.rs | 20 ++++++++++++++++-- .../transform/check_consts/validation.rs | 21 ++++--------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index 303c3984f7c0f..80f2925193a81 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -138,7 +138,15 @@ impl NonConstOp for HeapAllocation { #[derive(Debug)] pub struct IfOrMatch; -impl NonConstOp for IfOrMatch {} +impl NonConstOp for IfOrMatch { + fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + // This should be caught by the HIR const-checker. + item.tcx.sess.delay_span_bug( + span, + "complex control flow is forbidden in a const context", + ); + } +} #[derive(Debug)] pub struct LiveDrop; @@ -154,7 +162,15 @@ impl NonConstOp for LiveDrop { #[derive(Debug)] pub struct Loop; -impl NonConstOp for Loop {} +impl NonConstOp for Loop { + fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + // This should be caught by the HIR const-checker. + item.tcx.sess.delay_span_bug( + span, + "complex control flow is forbidden in a const context", + ); + } +} #[derive(Debug)] pub struct MutBorrow(pub BorrowKind); diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 6687618b74859..52481a89e2f1d 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -245,16 +245,10 @@ impl Validator<'a, 'mir, 'tcx> { 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", - ); - } + // We can't provide a good span for the error here, but this should be caught by the + // HIR const-checker anyways. + self.check_op_spanned(ops::Loop, body.span); } self.visit_body(body); @@ -565,14 +559,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { self.super_statement(statement, location); } StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { - // FIXME: make this the `emit_error` impl of `ops::IfOrMatch` 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.check_op(ops::IfOrMatch); } // FIXME(eddyb) should these really do nothing? StatementKind::FakeRead(..) | From 3d2bc50b516219e62d3233ffaebed26ccb6aeba8 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 10 Nov 2019 10:41:42 -0800 Subject: [PATCH 08/17] Remove remaining validator mismatch code --- .../transform/check_consts/validation.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 52481a89e2f1d..fa0bb806448fc 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -163,11 +163,6 @@ pub struct Validator<'a, 'mir, 'tcx> { /// this set is empty. Note that if we start removing locals from /// `derived_from_illegal_borrow`, just checking at the end won't be enough. derived_from_illegal_borrow: BitSet, - - errors: Vec<(Span, String)>, - - /// Whether to actually emit errors or just store them in `errors`. - pub(crate) suppress_errors: bool, } impl Deref for Validator<'_, 'mir, 'tcx> { @@ -221,9 +216,7 @@ impl Validator<'a, 'mir, 'tcx> { span: item.body.span, item, qualifs, - errors: vec![], derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()), - suppress_errors: false, } } @@ -267,10 +260,6 @@ impl Validator<'a, 'mir, 'tcx> { self.qualifs.in_return_place(self.item) } - pub fn take_errors(&mut self) -> Vec<(Span, String)> { - std::mem::replace(&mut self.errors, vec![]) - } - /// Emits an error at the given `span` if an expression cannot be evaluated in the current /// context. Returns `Forbidden` if an error was emitted. pub fn check_op_spanned(&mut self, op: O, span: Span) -> CheckOpResult @@ -293,11 +282,7 @@ impl Validator<'a, 'mir, 'tcx> { return CheckOpResult::Unleashed; } - if !self.suppress_errors { - op.emit_error(self, span); - } - - self.errors.push((span, format!("{:?}", op))); + op.emit_error(self, span); CheckOpResult::Forbidden } From c8156b9484991465eb3dfbe1018303558f59ca25 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 10 Nov 2019 10:42:30 -0800 Subject: [PATCH 09/17] Remove unnecessary `fmt::Debug` bound --- src/librustc_mir/transform/check_consts/validation.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index fa0bb806448fc..022f2bcd88144 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -14,7 +14,6 @@ use syntax::symbol::sym; use syntax_pos::Span; use std::borrow::Cow; -use std::fmt; use std::ops::Deref; use crate::dataflow::{self as old_dataflow, generic as dataflow}; @@ -264,7 +263,7 @@ impl Validator<'a, 'mir, 'tcx> { /// context. Returns `Forbidden` if an error was emitted. pub fn check_op_spanned(&mut self, op: O, span: Span) -> CheckOpResult where - O: NonConstOp + fmt::Debug + O: NonConstOp { trace!("check_op: op={:?}", op); @@ -287,7 +286,7 @@ impl Validator<'a, 'mir, 'tcx> { } /// Emits an error if an expression cannot be evaluated in the current context. - pub fn check_op(&mut self, op: impl NonConstOp + fmt::Debug) -> CheckOpResult { + pub fn check_op(&mut self, op: impl NonConstOp) -> CheckOpResult { let span = self.span; self.check_op_spanned(op, span) } From 8f5494b470db3abb0506d3693ca0c9f6b42c4cde Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 13 Nov 2019 12:50:41 -0800 Subject: [PATCH 10/17] Silence miri unleashed warnings in test --- .../miri_unleashed/enum_discriminants.rs | 11 ++--- .../miri_unleashed/enum_discriminants.stderr | 47 ------------------- 2 files changed, 5 insertions(+), 53 deletions(-) delete mode 100644 src/test/ui/consts/miri_unleashed/enum_discriminants.stderr diff --git a/src/test/ui/consts/miri_unleashed/enum_discriminants.rs b/src/test/ui/consts/miri_unleashed/enum_discriminants.rs index d7cdb0babc5c5..76d62f069f378 100644 --- a/src/test/ui/consts/miri_unleashed/enum_discriminants.rs +++ b/src/test/ui/consts/miri_unleashed/enum_discriminants.rs @@ -1,6 +1,9 @@ -// compile-flags: -Zunleash-the-miri-inside-of-you +// compile-flags: -Zunleash-the-miri-inside-of-you -Awarnings // run-pass +// miri unleashed warnings are not useful and change frequently, so they are silenced above. +#![feature(const_panic)] + //! Make sure that we read and write enum discriminants correctly for corner cases caused //! by layout optimizations. @@ -21,7 +24,7 @@ const OVERFLOW: usize = { } let x = Foo::B; - match x { //~ WARNING skipping const checks + match x { Foo::B => 0, _ => panic!(), } @@ -86,21 +89,17 @@ const MORE_OVERFLOW: usize = { } if let E1::V2 { .. } = (E1::V1 { f: true }) { - //~^ WARNING skipping const checks unreachable!() } if let E1::V1 { .. } = (E1::V1 { f: true }) { - //~^ WARNING skipping const checks } else { unreachable!() } if let E2::V1 { .. } = E2::V3:: { - //~^ WARNING skipping const checks unreachable!() } if let E2::V3 { .. } = E2::V3:: { - //~^ WARNING skipping const checks } else { unreachable!() } diff --git a/src/test/ui/consts/miri_unleashed/enum_discriminants.stderr b/src/test/ui/consts/miri_unleashed/enum_discriminants.stderr deleted file mode 100644 index b7fce223af80a..0000000000000 --- a/src/test/ui/consts/miri_unleashed/enum_discriminants.stderr +++ /dev/null @@ -1,47 +0,0 @@ -warning: skipping const checks - --> $DIR/enum_discriminants.rs:24:5 - | -LL | / match x { -LL | | Foo::B => 0, -LL | | _ => panic!(), -LL | | } - | |_____^ - -warning: skipping const checks - --> $DIR/enum_discriminants.rs:88:5 - | -LL | / if let E1::V2 { .. } = (E1::V1 { f: true }) { -LL | | -LL | | unreachable!() -LL | | } - | |_____^ - -warning: skipping const checks - --> $DIR/enum_discriminants.rs:92:5 - | -LL | / if let E1::V1 { .. } = (E1::V1 { f: true }) { -LL | | -LL | | } else { -LL | | unreachable!() -LL | | } - | |_____^ - -warning: skipping const checks - --> $DIR/enum_discriminants.rs:98:5 - | -LL | / if let E2::V1 { .. } = E2::V3:: { -LL | | -LL | | unreachable!() -LL | | } - | |_____^ - -warning: skipping const checks - --> $DIR/enum_discriminants.rs:102:5 - | -LL | / if let E2::V3 { .. } = E2::V3:: { -LL | | -LL | | } else { -LL | | unreachable!() -LL | | } - | |_____^ - From d1f0ef527ce589092e2afb5c635fb47278bf2905 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 13 Nov 2019 12:51:16 -0800 Subject: [PATCH 11/17] Add explanation of test for validation mismatch --- src/test/ui/consts/const-eval/issue-65394.rs | 7 +++++-- src/test/ui/consts/const-eval/issue-65394.stderr | 15 ++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/test/ui/consts/const-eval/issue-65394.rs b/src/test/ui/consts/const-eval/issue-65394.rs index 8cf527f0429f0..b1c058eac9e4b 100644 --- a/src/test/ui/consts/const-eval/issue-65394.rs +++ b/src/test/ui/consts/const-eval/issue-65394.rs @@ -1,7 +1,10 @@ -// Test for absence of validation mismatch ICE in #65394 +// This test originated from #65394. We conservatively assume that `x` is still `LiveDrop` even +// after it has been moved because a mutable reference to it exists at some point in the const body. +// +// We will likely have to change this behavior before we allow `&mut` in a `const`. const _: Vec = { - let mut x = Vec::::new(); + let mut x = Vec::::new(); //~ ERROR destructors cannot be evaluated at compile-time let r = &mut x; //~ ERROR references in constants may only refer to immutable values let y = x; y diff --git a/src/test/ui/consts/const-eval/issue-65394.stderr b/src/test/ui/consts/const-eval/issue-65394.stderr index 15df813836e5b..acf5cbaede665 100644 --- a/src/test/ui/consts/const-eval/issue-65394.stderr +++ b/src/test/ui/consts/const-eval/issue-65394.stderr @@ -1,11 +1,16 @@ error[E0017]: references in constants may only refer to immutable values - --> $DIR/issue-65394.rs:5:13 + --> $DIR/issue-65394.rs:8:13 | LL | let r = &mut x; | ^^^^^^ constants require immutable values -[ERROR rustc_mir::transform::qualify_consts] old validator: [($DIR/issue-65394.rs:5:13: 5:19, "MutBorrow(Mut { allow_two_phase_borrow: false })")] -[ERROR rustc_mir::transform::qualify_consts] new validator: [($DIR/issue-65394.rs:5:13: 5:19, "MutBorrow(Mut { allow_two_phase_borrow: false })"), ($DIR/issue-65394.rs:4:9: 4:14, "LiveDrop")] -error: aborting due to previous error +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/issue-65394.rs:7:9 + | +LL | let mut x = Vec::::new(); + | ^^^^^ constants cannot evaluate destructors + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0017`. +Some errors have detailed explanations: E0017, E0493. +For more information about an error, try `rustc --explain E0017`. From 9a2e53a7b385c3fbb0621a5a34e702f81a12342a Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 13 Nov 2019 12:57:58 -0800 Subject: [PATCH 12/17] Remove -Z flag for suppressing validation mismatch ICE --- src/librustc/session/config.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4a14960aa69bd..4474b008c7949 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1373,8 +1373,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "describes how to render the `rendered` field of json diagnostics"), unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], "take the breaks off const evaluation. NOTE: this is unsound"), - suppress_const_validation_back_compat_ice: bool = (false, parse_bool, [TRACKED], - "silence ICE triggered when the new const validator disagrees with the old"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], "pass `-install_name @rpath/...` to the macOS linker"), sanitizer: Option = (None, parse_sanitizer, [TRACKED], From a4ce2011cb0191e3be3dde2bca27b5c1ea4aadf2 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 13 Nov 2019 14:48:59 -0800 Subject: [PATCH 13/17] A `Downcast` is now reached when const-checking a `for` loop I believe this occurs because the old checker stopped processing basic blocks after a `SwitchInt`. --- src/test/compile-fail/consts/const-fn-error.rs | 1 + src/test/compile-fail/issue-52443.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/test/compile-fail/consts/const-fn-error.rs b/src/test/compile-fail/consts/const-fn-error.rs index 4adad16a57010..1a4fc72e81786 100644 --- a/src/test/compile-fail/consts/const-fn-error.rs +++ b/src/test/compile-fail/consts/const-fn-error.rs @@ -9,6 +9,7 @@ const fn f(x: usize) -> usize { //~| ERROR E0017 //~| ERROR E0080 //~| ERROR E0744 + //~| ERROR E0019 sum += i; } sum diff --git a/src/test/compile-fail/issue-52443.rs b/src/test/compile-fail/issue-52443.rs index 04eecb5687fd9..ee37aaa5e13b9 100644 --- a/src/test/compile-fail/issue-52443.rs +++ b/src/test/compile-fail/issue-52443.rs @@ -11,4 +11,5 @@ fn main() { //~| ERROR `for` is not allowed in a `const` //~| ERROR references in constants may only refer to immutable values //~| ERROR evaluation of constant value failed + //~| ERROR constant contains unimplemented expression type } From 056edc0d48ae19f9f458742e18b3e6f6f6042865 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 14 Nov 2019 09:15:23 -0800 Subject: [PATCH 14/17] Use a bespoke type for the result of `mir_const_qualif` --- src/librustc/mir/mod.rs | 7 +++++++ src/librustc/query/mod.rs | 2 +- src/librustc_metadata/rmeta/decoder.rs | 2 +- src/librustc_metadata/rmeta/encoder.rs | 6 +++++- src/librustc_metadata/rmeta/mod.rs | 2 +- src/librustc_mir/transform/mod.rs | 8 ++++---- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 39940dca99dc7..8a847115c9faf 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2769,6 +2769,13 @@ pub struct BorrowCheckResult<'tcx> { pub used_mut_upvars: SmallVec<[Field; 8]>, } +/// The result of the `mir_const_qualif` query. +#[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)] +pub struct QualifSet { + pub has_mut_interior: bool, + pub needs_drop: bool, +} + /// After we borrow check a closure, we are left with various /// requirements that we have inferred between the free regions that /// appear in the closure's signature or on its field types. These diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 9bd2a933c1c69..421d942373a0e 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -93,7 +93,7 @@ rustc_queries! { /// Maps DefId's that have an associated `mir::Body` to the result /// of the MIR qualify_consts pass. The actual meaning of /// the value isn't known except to the pass itself. - query mir_const_qualif(key: DefId) -> u8 { + query mir_const_qualif(key: DefId) -> mir::QualifSet { desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } } diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 7cebf2512d645..e3218eebcf05c 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -952,7 +952,7 @@ impl<'a, 'tcx> CrateMetadata { .decode((self, tcx)) } - fn mir_const_qualif(&self, id: DefIndex) -> u8 { + fn mir_const_qualif(&self, id: DefIndex) -> mir::QualifSet { match self.kind(id) { EntryKind::Const(qualif, _) | EntryKind::AssocConst(AssocContainer::ImplDefault, qualif, _) | diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index 26055d329bcbd..633b3daa8fa88 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -875,7 +875,11 @@ impl EncodeContext<'tcx> { hir::print::to_string(self.tcx.hir(), |s| s.print_trait_item(ast_item)); let rendered_const = self.lazy(RenderedConst(rendered)); - EntryKind::AssocConst(container, ConstQualif { mir: 0 }, rendered_const) + EntryKind::AssocConst( + container, + ConstQualif { mir: Default::default() }, + rendered_const, + ) } ty::AssocKind::Method => { let fn_data = if let hir::TraitItemKind::Method(m_sig, m) = &ast_item.kind { diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 990a3d984b225..b2fb725a11844 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -295,7 +295,7 @@ enum EntryKind<'tcx> { /// Additional data for EntryKind::Const and EntryKind::AssocConst #[derive(Clone, Copy, RustcEncodable, RustcDecodable)] struct ConstQualif { - mir: u8, + mir: mir::QualifSet, } /// Contains a constant which has been rendered to a String. diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 5773d850fbc77..32ccf961e3963 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,7 +1,7 @@ use crate::{build, shim}; use rustc_index::vec::IndexVec; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc::mir::{Body, MirPhase, Promoted}; +use rustc::mir::{Body, MirPhase, Promoted, QualifSet}; use rustc::ty::{TyCtxt, InstanceDef, TypeFoldable}; use rustc::ty::query::Providers; use rustc::ty::steal::Steal; @@ -184,12 +184,12 @@ pub fn run_passes( body.phase = mir_phase; } -fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { +fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> QualifSet { let const_kind = check_consts::ConstKind::for_item(tcx, def_id); // No need to const-check a non-const `fn`. if const_kind.is_none() { - return 0; + return Default::default(); } // N.B., this `borrow()` is guaranteed to be valid (i.e., the value @@ -200,7 +200,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { if body.return_ty().references_error() { tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors"); - return 0; + return Default::default(); } let item = check_consts::Item { From 1e840f8893ff620d4da161414b70d2ce037f3842 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 14 Nov 2019 09:16:08 -0800 Subject: [PATCH 15/17] Use `mir::QualifSet` to store qualifs --- .../transform/check_consts/qualifs.rs | 29 ++++++++++--------- .../transform/check_consts/validation.rs | 16 +++++----- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index 595ef2aad49d9..367e0e710c834 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -6,12 +6,10 @@ use syntax_pos::DUMMY_SP; use super::{ConstKind, Item as ConstCx}; -#[derive(Clone, Copy)] -pub struct QualifSet(u8); - -impl QualifSet { - fn contains(self) -> bool { - self.0 & (1 << Q::IDX) != 0 +pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> QualifSet { + QualifSet { + has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), + needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), } } @@ -22,14 +20,14 @@ impl QualifSet { /// /// The default implementations proceed structurally. pub trait Qualif { - const IDX: usize; - /// The name of the file used to debug the dataflow analysis that computes this qualif. const ANALYSIS_NAME: &'static str; /// Whether this `Qualif` is cleared when a local is moved from. const IS_CLEARED_ON_MOVE: bool = false; + fn in_qualif_set(set: &QualifSet) -> bool; + /// Return the qualification that is (conservatively) correct for any value /// of the type. fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool; @@ -122,9 +120,8 @@ pub trait Qualif { if cx.tcx.trait_of_item(def_id).is_some() { Self::in_any_value_of_ty(cx, constant.literal.ty) } else { - let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id); - - let qualif = QualifSet(bits).contains::(); + let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id); + let qualif = Self::in_qualif_set(&qualifs); // Just in case the type is more specific than // the definition, e.g., impl associated const @@ -210,9 +207,12 @@ pub trait Qualif { pub struct HasMutInterior; impl Qualif for HasMutInterior { - const IDX: usize = 0; const ANALYSIS_NAME: &'static str = "flow_has_mut_interior"; + fn in_qualif_set(set: &QualifSet) -> bool { + set.has_mut_interior + } + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) } @@ -275,10 +275,13 @@ impl Qualif for HasMutInterior { pub struct NeedsDrop; impl Qualif for NeedsDrop { - const IDX: usize = 1; const ANALYSIS_NAME: &'static str = "flow_needs_drop"; const IS_CLEARED_ON_MOVE: bool = true; + fn in_qualif_set(set: &QualifSet) -> bool { + set.needs_drop + } + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { ty.needs_drop(cx.tcx, cx.param_env) } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 022f2bcd88144..2f2c65b8469a3 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -19,9 +19,9 @@ use std::ops::Deref; use crate::dataflow::{self as old_dataflow, generic as dataflow}; use self::old_dataflow::IndirectlyMutableLocals; use super::ops::{self, NonConstOp}; -use super::qualifs::{HasMutInterior, NeedsDrop}; +use super::qualifs::{self, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{ConstKind, Item, Qualif, QualifSet, is_lang_panic_fn}; +use super::{ConstKind, Item, Qualif, is_lang_panic_fn}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CheckOpResult { @@ -130,18 +130,16 @@ impl Qualifs<'a, 'mir, 'tcx> { .map(|(bb, _)| bb); let return_block = match return_block { - None => return QualifSet::in_any_value_of_ty(item, item.body.return_ty()), + None => return qualifs::in_any_value_of_ty(item, item.body.return_ty()), Some(bb) => bb, }; let return_loc = item.body.terminator_loc(return_block); - let mut qualifs = QualifSet::default(); - - qualifs.set::(self.needs_drop_lazy_seek(RETURN_PLACE, return_loc)); - qualifs.set::(self.has_mut_interior_lazy_seek(RETURN_PLACE, return_loc)); - - qualifs + QualifSet { + needs_drop: self.needs_drop_lazy_seek(RETURN_PLACE, return_loc), + has_mut_interior: self.has_mut_interior_lazy_seek(RETURN_PLACE, return_loc), + } } } From ea95e94f518ccdd84483e59dfbb5db31a83fe016 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 14 Nov 2019 11:58:50 -0800 Subject: [PATCH 16/17] Fix nits from review --- src/librustc/mir/mod.rs | 6 +++++- src/librustc/query/mod.rs | 6 +++--- src/librustc_metadata/rmeta/decoder.rs | 2 +- src/librustc_metadata/rmeta/mod.rs | 2 +- .../transform/check_consts/qualifs.rs | 16 ++++++++-------- .../transform/check_consts/validation.rs | 6 +++--- src/librustc_mir/transform/mod.rs | 4 ++-- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 8a847115c9faf..53ef61f157665 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2770,8 +2770,12 @@ pub struct BorrowCheckResult<'tcx> { } /// The result of the `mir_const_qualif` query. +/// +/// Each field corresponds to an implementer of the `Qualif` trait in +/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each +/// `Qualif`. #[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)] -pub struct QualifSet { +pub struct ConstQualifs { pub has_mut_interior: bool, pub needs_drop: bool, } diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 421d942373a0e..e07726bfa2aa1 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -91,9 +91,9 @@ rustc_queries! { } /// Maps DefId's that have an associated `mir::Body` to the result - /// of the MIR qualify_consts pass. The actual meaning of - /// the value isn't known except to the pass itself. - query mir_const_qualif(key: DefId) -> mir::QualifSet { + /// of the MIR const-checking pass. This is the set of qualifs in + /// the final value of a `const`. + query mir_const_qualif(key: DefId) -> mir::ConstQualifs { desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } } diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index e3218eebcf05c..9e8b22e00d913 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -952,7 +952,7 @@ impl<'a, 'tcx> CrateMetadata { .decode((self, tcx)) } - fn mir_const_qualif(&self, id: DefIndex) -> mir::QualifSet { + fn mir_const_qualif(&self, id: DefIndex) -> mir::ConstQualifs { match self.kind(id) { EntryKind::Const(qualif, _) | EntryKind::AssocConst(AssocContainer::ImplDefault, qualif, _) | diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index b2fb725a11844..8bd7b3392b1a5 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -295,7 +295,7 @@ enum EntryKind<'tcx> { /// Additional data for EntryKind::Const and EntryKind::AssocConst #[derive(Clone, Copy, RustcEncodable, RustcDecodable)] struct ConstQualif { - mir: mir::QualifSet, + mir: mir::ConstQualifs, } /// Contains a constant which has been rendered to a String. diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index 367e0e710c834..aad14299c1d94 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -6,8 +6,8 @@ use syntax_pos::DUMMY_SP; use super::{ConstKind, Item as ConstCx}; -pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> QualifSet { - QualifSet { +pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs { + ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), } @@ -26,7 +26,7 @@ pub trait Qualif { /// Whether this `Qualif` is cleared when a local is moved from. const IS_CLEARED_ON_MOVE: bool = false; - fn in_qualif_set(set: &QualifSet) -> bool; + fn in_qualifs(qualifs: &ConstQualifs) -> bool; /// Return the qualification that is (conservatively) correct for any value /// of the type. @@ -121,7 +121,7 @@ pub trait Qualif { Self::in_any_value_of_ty(cx, constant.literal.ty) } else { let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id); - let qualif = Self::in_qualif_set(&qualifs); + let qualif = Self::in_qualifs(&qualifs); // Just in case the type is more specific than // the definition, e.g., impl associated const @@ -209,8 +209,8 @@ pub struct HasMutInterior; impl Qualif for HasMutInterior { const ANALYSIS_NAME: &'static str = "flow_has_mut_interior"; - fn in_qualif_set(set: &QualifSet) -> bool { - set.has_mut_interior + fn in_qualifs(qualifs: &ConstQualifs) -> bool { + qualifs.has_mut_interior } fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { @@ -278,8 +278,8 @@ impl Qualif for NeedsDrop { const ANALYSIS_NAME: &'static str = "flow_needs_drop"; const IS_CLEARED_ON_MOVE: bool = true; - fn in_qualif_set(set: &QualifSet) -> bool { - set.needs_drop + fn in_qualifs(qualifs: &ConstQualifs) -> bool { + qualifs.needs_drop } fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 2f2c65b8469a3..21e7c9ce565f0 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -113,7 +113,7 @@ impl Qualifs<'a, 'mir, 'tcx> { || self.indirectly_mutable.get().contains(local) } - fn in_return_place(&mut self, item: &Item<'_, 'tcx>) -> QualifSet { + fn in_return_place(&mut self, item: &Item<'_, 'tcx>) -> ConstQualifs { // Find the `Return` terminator if one exists. // // If no `Return` terminator exists, this MIR is divergent. Just return the conservative @@ -136,7 +136,7 @@ impl Qualifs<'a, 'mir, 'tcx> { let return_loc = item.body.terminator_loc(return_block); - QualifSet { + ConstQualifs { needs_drop: self.needs_drop_lazy_seek(RETURN_PLACE, return_loc), has_mut_interior: self.has_mut_interior_lazy_seek(RETURN_PLACE, return_loc), } @@ -253,7 +253,7 @@ impl Validator<'a, 'mir, 'tcx> { } } - pub fn qualifs_in_return_place(&mut self) -> QualifSet { + pub fn qualifs_in_return_place(&mut self) -> ConstQualifs { self.qualifs.in_return_place(self.item) } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 32ccf961e3963..897e37858a68e 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,7 +1,7 @@ use crate::{build, shim}; use rustc_index::vec::IndexVec; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc::mir::{Body, MirPhase, Promoted, QualifSet}; +use rustc::mir::{Body, MirPhase, Promoted, ConstQualifs}; use rustc::ty::{TyCtxt, InstanceDef, TypeFoldable}; use rustc::ty::query::Providers; use rustc::ty::steal::Steal; @@ -184,7 +184,7 @@ pub fn run_passes( body.phase = mir_phase; } -fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> QualifSet { +fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { let const_kind = check_consts::ConstKind::for_item(tcx, def_id); // No need to const-check a non-const `fn`. From a1135cc9463437ed806876b2406379c25321e7d3 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 14 Nov 2019 12:07:35 -0800 Subject: [PATCH 17/17] Remove newtype for qualifs in `rustc_metadata` We have a proper type for these now, so the wrapper is no longer necessary. --- src/librustc_metadata/rmeta/decoder.rs | 2 +- src/librustc_metadata/rmeta/encoder.rs | 17 +++++++++-------- src/librustc_metadata/rmeta/mod.rs | 10 ++-------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 9e8b22e00d913..fc0a0010240ad 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -957,7 +957,7 @@ impl<'a, 'tcx> CrateMetadata { EntryKind::Const(qualif, _) | EntryKind::AssocConst(AssocContainer::ImplDefault, qualif, _) | EntryKind::AssocConst(AssocContainer::ImplFinal, qualif, _) => { - qualif.mir + qualif } _ => bug!(), } diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index 633b3daa8fa88..483915f654ddd 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -877,7 +877,7 @@ impl EncodeContext<'tcx> { EntryKind::AssocConst( container, - ConstQualif { mir: Default::default() }, + Default::default(), rendered_const, ) } @@ -959,10 +959,11 @@ impl EncodeContext<'tcx> { record!(self.per_def.kind[def_id] <- match impl_item.kind { ty::AssocKind::Const => { if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind { - let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id); + let qualifs = self.tcx.at(ast_item.span).mir_const_qualif(def_id); - EntryKind::AssocConst(container, - ConstQualif { mir }, + EntryKind::AssocConst( + container, + qualifs, self.encode_rendered_const_for_body(body_id)) } else { bug!() @@ -1093,9 +1094,9 @@ impl EncodeContext<'tcx> { hir::ItemKind::Static(_, hir::Mutability::Mutable, _) => EntryKind::MutStatic, hir::ItemKind::Static(_, hir::Mutability::Immutable, _) => EntryKind::ImmStatic, hir::ItemKind::Const(_, body_id) => { - let mir = self.tcx.at(item.span).mir_const_qualif(def_id); + let qualifs = self.tcx.at(item.span).mir_const_qualif(def_id); EntryKind::Const( - ConstQualif { mir }, + qualifs, self.encode_rendered_const_for_body(body_id) ) } @@ -1372,9 +1373,9 @@ impl EncodeContext<'tcx> { let id = self.tcx.hir().as_local_hir_id(def_id).unwrap(); let body_id = self.tcx.hir().body_owned_by(id); let const_data = self.encode_rendered_const_for_body(body_id); - let mir = self.tcx.mir_const_qualif(def_id); + let qualifs = self.tcx.mir_const_qualif(def_id); - record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data)); + record!(self.per_def.kind[def_id] <- EntryKind::Const(qualifs, const_data)); record!(self.per_def.visibility[def_id] <- ty::Visibility::Public); record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); self.encode_item_type(def_id); diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 8bd7b3392b1a5..850ee5afbc808 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -260,7 +260,7 @@ crate struct LazyPerDefTables<'tcx> { #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] enum EntryKind<'tcx> { - Const(ConstQualif, Lazy), + Const(mir::ConstQualifs, Lazy), ImmStatic, MutStatic, ForeignImmStatic, @@ -288,16 +288,10 @@ enum EntryKind<'tcx> { Method(Lazy), AssocType(AssocContainer), AssocOpaqueTy(AssocContainer), - AssocConst(AssocContainer, ConstQualif, Lazy), + AssocConst(AssocContainer, mir::ConstQualifs, Lazy), TraitAlias, } -/// Additional data for EntryKind::Const and EntryKind::AssocConst -#[derive(Clone, Copy, RustcEncodable, RustcDecodable)] -struct ConstQualif { - mir: mir::ConstQualifs, -} - /// Contains a constant which has been rendered to a String. /// Used by rustdoc. #[derive(RustcEncodable, RustcDecodable)]