diff --git a/.mailmap b/.mailmap index aed3a4ca5b0..680aa04078f 100644 --- a/.mailmap +++ b/.mailmap @@ -49,6 +49,7 @@ Carol (Nichols || Goulding) <193874+carols10cents@user Carol (Nichols || Goulding) Carol (Nichols || Goulding) Carol Willing +Charles Lew CrLF0710 Chris C Cerami Chris C Cerami Chris Pressey Chris Thorn Chris Thorn diff --git a/src/librustc_attr/build.rs b/src/librustc_attr/build.rs index d230ba91039..863f2b7337b 100644 --- a/src/librustc_attr/build.rs +++ b/src/librustc_attr/build.rs @@ -1,4 +1,5 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CFG_VERSION"); + println!("cargo:rerun-if-env-changed=CFG_RELEASE"); + println!("cargo:rerun-if-env-changed=CFG_RELEASE_CHANNEL"); } diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs index ce38e3f5f4e..a592bbc2bf9 100644 --- a/src/librustc_attr/builtin.rs +++ b/src/librustc_attr/builtin.rs @@ -652,9 +652,12 @@ pub fn eval_condition( return false; } }; - let version = Version::parse(env!("CFG_VERSION")).unwrap(); + let channel = env!("CFG_RELEASE_CHANNEL"); + let nightly = channel == "nightly" || channel == "dev"; + let rustc_version = Version::parse(env!("CFG_RELEASE")).unwrap(); - version >= min_version + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-625474439 for details + if nightly { rustc_version > min_version } else { rustc_version >= min_version } } ast::MetaItemKind::List(ref mis) => { for mi in mis.iter() { diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index e6d673b30f7..75a5c198ec7 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -1291,6 +1291,53 @@ impl BodyOwnerKind { } } +/// The kind of an item that requires const-checking. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ConstContext { + /// A `const fn`. + ConstFn, + + /// A `static` or `static mut`. + Static(Mutability), + + /// A `const`, associated `const`, or other const context. + /// + /// Other contexts include: + /// - Array length expressions + /// - Enum discriminants + /// - Const generics + /// + /// For the most part, other contexts are treated just like a regular `const`, so they are + /// lumped into the same category. + Const, +} + +impl ConstContext { + /// A description of this const context that can appear between backticks in an error message. + /// + /// E.g. `const` or `static mut`. + pub fn keyword_name(self) -> &'static str { + match self { + Self::Const => "const", + Self::Static(Mutability::Not) => "static", + Self::Static(Mutability::Mut) => "static mut", + Self::ConstFn => "const fn", + } + } +} + +/// A colloquial, trivially pluralizable description of this const context for use in error +/// messages. +impl fmt::Display for ConstContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Const => write!(f, "constant"), + Self::Static(_) => write!(f, "static"), + Self::ConstFn => write!(f, "constant function"), + } + } +} + /// A literal. pub type Lit = Spanned; diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs index 1c71fc57bea..235d1d80192 100644 --- a/src/librustc_middle/hir/map/mod.rs +++ b/src/librustc_middle/hir/map/mod.rs @@ -408,6 +408,9 @@ impl<'hir> Map<'hir> { }) } + /// Returns the `BodyOwnerKind` of this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { match self.get(id) { Node::Item(&Item { kind: ItemKind::Const(..), .. }) @@ -424,6 +427,23 @@ impl<'hir> Map<'hir> { } } + /// Returns the `ConstContext` of the body associated with this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. + pub fn body_const_context(&self, did: LocalDefId) -> Option { + let hir_id = self.local_def_id_to_hir_id(did); + let ccx = match self.body_owner_kind(hir_id) { + BodyOwnerKind::Const => ConstContext::Const, + BodyOwnerKind::Static(mt) => ConstContext::Static(mt), + + BodyOwnerKind::Fn if self.tcx.is_constructor(did.to_def_id()) => return None, + BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(did.to_def_id()) => ConstContext::ConstFn, + BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None, + }; + + Some(ccx) + } + pub fn ty_param_owner(&self, id: HirId) -> HirId { match self.get(id) { Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => id, diff --git a/src/librustc_mir/borrow_check/def_use.rs b/src/librustc_mir/borrow_check/def_use.rs new file mode 100644 index 00000000000..689ec249a2f --- /dev/null +++ b/src/librustc_mir/borrow_check/def_use.rs @@ -0,0 +1,78 @@ +use rustc_middle::mir::visit::{ + MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, +}; + +#[derive(Eq, PartialEq, Clone)] +pub enum DefUse { + Def, + Use, + Drop, +} + +pub fn categorize(context: PlaceContext) -> Option { + match context { + /////////////////////////////////////////////////////////////////////////// + // DEFS + + PlaceContext::MutatingUse(MutatingUseContext::Store) | + + // This is potentially both a def and a use... + PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) | + + // We let Call define the result in both the success and + // unwind cases. This is not really correct, however it + // does not seem to be observable due to the way that we + // generate MIR. To do things properly, we would apply + // the def in call only to the input from the success + // path and not the unwind path. -nmatsakis + PlaceContext::MutatingUse(MutatingUseContext::Call) | + PlaceContext::MutatingUse(MutatingUseContext::Yield) | + + // Storage live and storage dead aren't proper defines, but we can ignore + // values that come before them. + PlaceContext::NonUse(NonUseContext::StorageLive) | + PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def), + + /////////////////////////////////////////////////////////////////////////// + // REGULAR USES + // + // These are uses that occur *outside* of a drop. For the + // purposes of NLL, these are special in that **all** the + // lifetimes appearing in the variable must be live for each regular use. + + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) | + PlaceContext::MutatingUse(MutatingUseContext::Projection) | + + // Borrows only consider their local used at the point of the borrow. + // This won't affect the results since we use this analysis for generators + // and we only care about the result at suspension points. Borrows cannot + // cross suspension points so this behavior is unproblematic. + PlaceContext::MutatingUse(MutatingUseContext::Borrow) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) | + + PlaceContext::MutatingUse(MutatingUseContext::AddressOf) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) | + PlaceContext::NonUse(NonUseContext::AscribeUserTy) | + PlaceContext::MutatingUse(MutatingUseContext::Retag) => + Some(DefUse::Use), + + /////////////////////////////////////////////////////////////////////////// + // DROP USES + // + // These are uses that occur in a DROP (a MIR drop, not a + // call to `std::mem::drop()`). For the purposes of NLL, + // uses in drop are special because `#[may_dangle]` + // attributes can affect whether lifetimes must be live. + + PlaceContext::MutatingUse(MutatingUseContext::Drop) => + Some(DefUse::Drop), + + // Debug info is neither def nor use. + PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None, + } +} diff --git a/src/librustc_mir/borrow_check/diagnostics/find_use.rs b/src/librustc_mir/borrow_check/diagnostics/find_use.rs index 6c6bde8ae2c..8d8cdfb5293 100644 --- a/src/librustc_mir/borrow_check/diagnostics/find_use.rs +++ b/src/librustc_mir/borrow_check/diagnostics/find_use.rs @@ -2,10 +2,10 @@ use std::collections::VecDeque; use std::rc::Rc; use crate::borrow_check::{ + def_use::{self, DefUse}, nll::ToRegionVid, region_infer::{Cause, RegionInferenceContext}, }; -use crate::util::liveness::{self, DefUse}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::visit::{MirVisitable, PlaceContext, Visitor}; use rustc_middle::mir::{Body, Local, Location}; @@ -117,7 +117,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for DefUseVisitor<'cx, 'tcx> { }); if found_it { - self.def_use_result = match liveness::categorize(context) { + self.def_use_result = match def_use::categorize(context) { Some(DefUse::Def) => Some(DefUseResult::Def), Some(DefUse::Use) => Some(DefUseResult::UseLive { local }), Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }), diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 465aa5847af..457f0f8444b 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -51,6 +51,7 @@ use self::path_utils::*; mod borrow_set; mod constraint_generation; mod constraints; +mod def_use; mod diagnostics; mod facts; mod invalidation; diff --git a/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs b/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs index 0fdf96710c6..995e3a60a0c 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs @@ -3,8 +3,7 @@ use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{Body, Local, Location}; -use crate::util::liveness::{categorize, DefUse}; - +use crate::borrow_check::def_use::{self, DefUse}; use crate::borrow_check::region_infer::values::{PointIndex, RegionValueElements}; /// A map that cross references each local with the locations where it @@ -160,7 +159,7 @@ impl LocalUseMapBuild<'_> { impl Visitor<'tcx> for LocalUseMapBuild<'_> { fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) { if self.locals_with_use_data[local] { - match categorize(context) { + match def_use::categorize(context) { Some(DefUse::Def) => self.insert_def(local, location), Some(DefUse::Use) => self.insert_use(local, location), Some(DefUse::Drop) => self.insert_drop(local, location), diff --git a/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs b/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs index 2e033896ce1..d285098c52a 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs @@ -1,7 +1,7 @@ +use crate::borrow_check::def_use::{self, DefUse}; use crate::borrow_check::location::{LocationIndex, LocationTable}; use crate::dataflow::indexes::MovePathIndex; use crate::dataflow::move_paths::{LookupResult, MoveData}; -use crate::util::liveness::{categorize, DefUse}; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{Body, Local, Location, Place}; use rustc_middle::ty::subst::GenericArg; @@ -56,7 +56,7 @@ impl UseFactsExtractor<'_> { impl Visitor<'tcx> for UseFactsExtractor<'_> { fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) { - match categorize(context) { + match def_use::categorize(context) { Some(DefUse::Def) => self.insert_def(local, location), Some(DefUse::Use) => self.insert_use(local, location), Some(DefUse::Drop) => self.insert_drop_use(local, location), diff --git a/src/librustc_mir/dataflow/impls/liveness.rs b/src/librustc_mir/dataflow/impls/liveness.rs index 5e9bec89ac0..d24faacd377 100644 --- a/src/librustc_mir/dataflow/impls/liveness.rs +++ b/src/librustc_mir/dataflow/impls/liveness.rs @@ -6,6 +6,13 @@ use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAna /// A [live-variable dataflow analysis][liveness]. /// +/// This analysis considers references as being used only at the point of the +/// borrow. In other words, this analysis does not track uses because of references that already +/// exist. See [this `mir-datalow` test][flow-test] for an example. You almost never want to use +/// this analysis without also looking at the results of [`MaybeBorrowedLocals`]. +/// +/// [`MaybeBorrowedLocals`]: ../struct.MaybeBorrowedLocals.html +/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis pub struct MaybeLiveLocals; diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index 46a46aa5ae9..7c439f80ef6 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -9,8 +9,6 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; -use std::fmt; - pub use self::qualifs::Qualif; mod ops; @@ -25,7 +23,7 @@ pub struct ConstCx<'mir, 'tcx> { pub tcx: TyCtxt<'tcx>, pub def_id: DefId, pub param_env: ty::ParamEnv<'tcx>, - pub const_kind: Option, + pub const_kind: Option, } impl ConstCx<'mir, 'tcx> { @@ -40,78 +38,18 @@ impl ConstCx<'mir, 'tcx> { body: &'mir mir::Body<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Self { - let const_kind = ConstKind::for_item(tcx, def_id); - + let const_kind = tcx.hir().body_const_context(def_id); ConstCx { body, tcx, def_id: def_id.to_def_id(), param_env, const_kind } } /// Returns the kind of const context this `Item` represents (`const`, `static`, etc.). /// /// Panics if this `Item` is not const. - pub fn const_kind(&self) -> ConstKind { + pub fn const_kind(&self) -> hir::ConstContext { self.const_kind.expect("`const_kind` must not be called on a non-const fn") } } -/// The kinds of items which require compile-time evaluation. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ConstKind { - /// 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, -} - -impl ConstKind { - /// Returns the validation mode for the item with the given `DefId`, or `None` if this item - /// does not require validation (e.g. a non-const `fn`). - pub fn for_item(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Option { - use hir::BodyOwnerKind as HirKind; - - let hir_id = tcx.hir().as_local_hir_id(def_id); - - let mode = match tcx.hir().body_owner_kind(hir_id) { - HirKind::Closure => return None, - - // Note: this is deliberately checking for `is_const_fn_raw`, as the `is_const_fn` - // checks take into account the `rustc_const_unstable` attribute combined with enabled - // feature gates. Otherwise, const qualification would _not check_ whether this - // function body follows the `const fn` rules, as an unstable `const fn` would - // be considered "not const". More details are available in issue #67053. - HirKind::Fn if tcx.is_const_fn_raw(def_id) => ConstKind::ConstFn, - HirKind::Fn => return None, - - HirKind::Const => ConstKind::Const, - - HirKind::Static(hir::Mutability::Not) => ConstKind::Static, - HirKind::Static(hir::Mutability::Mut) => ConstKind::StaticMut, - }; - - Some(mode) - } - - pub fn is_static(self) -> bool { - match self { - ConstKind::Static | ConstKind::StaticMut => true, - ConstKind::ConstFn | ConstKind::Const => false, - } - } -} - -impl fmt::Display for ConstKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ConstKind::Const => write!(f, "constant"), - ConstKind::Static | ConstKind::StaticMut => write!(f, "static"), - ConstKind::ConstFn => write!(f, "constant function"), - } - } -} - /// Returns `true` if this `DefId` points to one of the official `panic` lang items. pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn() diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index bb6e3681cc3..28743ee8e36 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -1,13 +1,14 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use rustc_errors::struct_span_err; +use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use super::{ConstCx, ConstKind}; +use super::ConstCx; /// An operation that is not *always* allowed in a const context. pub trait NonConstOp: std::fmt::Debug { @@ -326,7 +327,7 @@ impl NonConstOp for RawPtrToIntCast { pub struct StaticAccess; impl NonConstOp for StaticAccess { fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { - ccx.const_kind().is_static() + matches!(ccx.const_kind(), hir::ConstContext::Static(_)) } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -374,7 +375,7 @@ pub struct UnionAccess; impl NonConstOp for UnionAccess { fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { // Union accesses are stable in all contexts except `const fn`. - ccx.const_kind() != ConstKind::ConstFn + ccx.const_kind() != hir::ConstContext::ConstFn || ccx.tcx.features().enabled(Self::feature_gate().unwrap()) } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 1d03606f253..896ce981f92 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -1,7 +1,7 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. use rustc_errors::struct_span_err; -use rustc_hir::lang_items; +use rustc_hir::{self as hir, lang_items}; use rustc_hir::{def_id::DefId, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; @@ -18,7 +18,7 @@ use std::ops::Deref; use super::ops::{self, NonConstOp}; use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{is_lang_panic_fn, ConstCx, ConstKind, Qualif}; +use super::{is_lang_panic_fn, ConstCx, Qualif}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; use crate::dataflow::impls::MaybeMutBorrowedLocals; use crate::dataflow::{self, Analysis}; @@ -145,17 +145,13 @@ impl Qualifs<'mir, 'tcx> { // We don't care whether a `const fn` returns a value that is not structurally // matchable. Functions calls are opaque and always use type-based qualification, so // this value should never be used. - ConstKind::ConstFn => true, + hir::ConstContext::ConstFn => true, // If we know that all values of the return type are structurally matchable, there's no // need to run dataflow. - ConstKind::Const | ConstKind::Static | ConstKind::StaticMut - if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => - { - false - } + _ if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => false, - ConstKind::Const | ConstKind::Static | ConstKind::StaticMut => { + hir::ConstContext::Const | hir::ConstContext::Static(_) => { let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx) .into_engine(ccx.tcx, &ccx.body, ccx.def_id) .iterate_to_fixpoint() @@ -198,7 +194,7 @@ impl Validator<'mir, 'tcx> { pub fn check_body(&mut self) { let ConstCx { tcx, body, def_id, const_kind, .. } = *self.ccx; - let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn) + let use_min_const_fn_checks = (const_kind == Some(hir::ConstContext::ConstFn) && crate::const_eval::is_min_const_fn(tcx, def_id)) && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; @@ -222,8 +218,9 @@ impl Validator<'mir, 'tcx> { 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.is_thread_local_static(def_id); + let should_check_for_sync = const_kind + == Some(hir::ConstContext::Static(hir::Mutability::Not)) + && !tcx.is_thread_local_static(def_id); if should_check_for_sync { let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); @@ -351,7 +348,9 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { let ty = place.ty(self.body, self.tcx).ty; let is_allowed = match ty.kind { // Inside a `static mut`, `&mut [...]` is allowed. - ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut => { + ty::Array(..) | ty::Slice(_) + if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) => + { true } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 1d982d18eeb..02356a43699 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -182,7 +182,7 @@ pub fn run_passes( } fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { - let const_kind = check_consts::ConstKind::for_item(tcx, def_id.expect_local()); + let const_kind = tcx.hir().body_const_context(def_id.expect_local()); // No need to const-check a non-const `fn`. if const_kind.is_none() { diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index ad98920eb63..467e4188814 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -13,6 +13,7 @@ //! move analysis runs after promotion on broken MIR. use rustc_ast::ast::LitKind; +use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::traversal::ReversePostorder; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -30,7 +31,7 @@ use std::cell::Cell; use std::{cmp, iter, mem}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; -use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx, ConstKind}; +use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx}; use crate::transform::{MirPass, MirSource}; /// A `MirPass` for promotion. @@ -352,7 +353,9 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(ConstKind::StaticMut) { + if self.const_kind + == Some(hir::ConstContext::Static(hir::Mutability::Mut)) + { // Inside a `static mut`, &mut [...] is also allowed. match ty.kind { ty::Array(..) | ty::Slice(_) => {} @@ -517,7 +520,7 @@ impl<'tcx> Validator<'_, 'tcx> { if let Some(def_id) = c.check_static_ptr(self.tcx) { // Only allow statics (not consts) to refer to other statics. // FIXME(eddyb) does this matter at all for promotion? - let is_static = self.const_kind.map_or(false, |k| k.is_static()); + let is_static = matches!(self.const_kind, Some(hir::ConstContext::Static(_))); if !is_static { return Err(Unpromotable); } @@ -607,7 +610,7 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(ConstKind::StaticMut) { + if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) { // Inside a `static mut`, &mut [...] is also allowed. match ty.kind { ty::Array(..) | ty::Slice(_) => {} diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs deleted file mode 100644 index c261219cc73..00000000000 --- a/src/librustc_mir/util/liveness.rs +++ /dev/null @@ -1,326 +0,0 @@ -//! Liveness analysis which computes liveness of MIR local variables at the boundary of basic -//! blocks. -//! -//! This analysis considers references as being used only at the point of the -//! borrow. This means that this does not track uses because of references that -//! already exist: -//! -//! ```rust -//! fn foo() { -//! x = 0; -//! // `x` is live here ... -//! GLOBAL = &x: *const u32; -//! // ... but not here, even while it can be accessed through `GLOBAL`. -//! foo(); -//! x = 1; -//! // `x` is live again here, because it is assigned to `OTHER_GLOBAL`. -//! OTHER_GLOBAL = &x: *const u32; -//! // ... -//! } -//! ``` -//! -//! This means that users of this analysis still have to check whether -//! pre-existing references can be used to access the value (e.g., at movable -//! generator yield points, all pre-existing references are invalidated, so this -//! doesn't matter). - -use crate::transform::MirSource; -use crate::util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; -use rustc_data_structures::work_queue::WorkQueue; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::mir::visit::{ - MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor, -}; -use rustc_middle::mir::Local; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; -use std::fs; -use std::io::{self, BufWriter, Write}; -use std::path::{Path, PathBuf}; - -pub type LiveVarSet = BitSet; - -/// This gives the result of the liveness analysis at the boundary of -/// basic blocks. -/// -/// The `V` type defines the set of variables that we computed -/// liveness for. This is often `Local`, in which case we computed -/// liveness for all variables -- but it can also be some other type, -/// which indicates a subset of the variables within the graph. -pub struct LivenessResult { - /// Live variables on exit to each basic block. This is equal to - /// the union of the `ins` for each successor. - pub outs: IndexVec, -} - -/// Computes which local variables are live within the given function -/// `mir`, including drops. -pub fn liveness_of_locals(body: &Body<'_>) -> LivenessResult { - let num_live_vars = body.local_decls.len(); - - let def_use: IndexVec<_, DefsUses> = - body.basic_blocks().iter().map(|b| block(b, num_live_vars)).collect(); - - let mut outs: IndexVec<_, LiveVarSet> = - body.basic_blocks().indices().map(|_| LiveVarSet::new_empty(num_live_vars)).collect(); - - let mut bits = LiveVarSet::new_empty(num_live_vars); - - // The dirty queue contains the set of basic blocks whose entry sets have changed since they - // were last processed. At the start of the analysis, we initialize the queue in post-order to - // make it more likely that the entry set for a given basic block will have the effects of all - // its successors in the CFG applied before it is processed. - // - // FIXME(ecstaticmorse): Reverse post-order on the reverse CFG may generate a better iteration - // order when cycles are present, but the overhead of computing the reverse CFG may outweigh - // any benefits. Benchmark this and find out. - let mut dirty_queue: WorkQueue = WorkQueue::with_none(body.basic_blocks().len()); - for (bb, _) in traversal::postorder(&body) { - dirty_queue.insert(bb); - } - - // Add blocks which are not reachable from START_BLOCK to the work queue. These blocks will - // be processed after the ones added above. - for bb in body.basic_blocks().indices() { - dirty_queue.insert(bb); - } - - let predecessors = body.predecessors(); - - while let Some(bb) = dirty_queue.pop() { - // bits = use ∪ (bits - def) - bits.overwrite(&outs[bb]); - def_use[bb].apply(&mut bits); - - // `bits` now contains the live variables on entry. Therefore, - // add `bits` to the `out` set for each predecessor; if those - // bits were not already present, then enqueue the predecessor - // as dirty. - // - // (note that `union` returns true if the `self` set changed) - for &pred_bb in &predecessors[bb] { - if outs[pred_bb].union(&bits) { - dirty_queue.insert(pred_bb); - } - } - } - - LivenessResult { outs } -} - -#[derive(Eq, PartialEq, Clone)] -pub enum DefUse { - Def, - Use, - Drop, -} - -pub fn categorize(context: PlaceContext) -> Option { - match context { - /////////////////////////////////////////////////////////////////////////// - // DEFS - - PlaceContext::MutatingUse(MutatingUseContext::Store) | - - // This is potentially both a def and a use... - PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) | - - // We let Call define the result in both the success and - // unwind cases. This is not really correct, however it - // does not seem to be observable due to the way that we - // generate MIR. To do things properly, we would apply - // the def in call only to the input from the success - // path and not the unwind path. -nmatsakis - PlaceContext::MutatingUse(MutatingUseContext::Call) | - PlaceContext::MutatingUse(MutatingUseContext::Yield) | - - // Storage live and storage dead aren't proper defines, but we can ignore - // values that come before them. - PlaceContext::NonUse(NonUseContext::StorageLive) | - PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def), - - /////////////////////////////////////////////////////////////////////////// - // REGULAR USES - // - // These are uses that occur *outside* of a drop. For the - // purposes of NLL, these are special in that **all** the - // lifetimes appearing in the variable must be live for each regular use. - - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) | - PlaceContext::MutatingUse(MutatingUseContext::Projection) | - - // Borrows only consider their local used at the point of the borrow. - // This won't affect the results since we use this analysis for generators - // and we only care about the result at suspension points. Borrows cannot - // cross suspension points so this behavior is unproblematic. - PlaceContext::MutatingUse(MutatingUseContext::Borrow) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) | - - PlaceContext::MutatingUse(MutatingUseContext::AddressOf) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) | - PlaceContext::NonUse(NonUseContext::AscribeUserTy) | - PlaceContext::MutatingUse(MutatingUseContext::Retag) => - Some(DefUse::Use), - - /////////////////////////////////////////////////////////////////////////// - // DROP USES - // - // These are uses that occur in a DROP (a MIR drop, not a - // call to `std::mem::drop()`). For the purposes of NLL, - // uses in drop are special because `#[may_dangle]` - // attributes can affect whether lifetimes must be live. - - PlaceContext::MutatingUse(MutatingUseContext::Drop) => - Some(DefUse::Drop), - - // Debug info is neither def nor use. - PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None, - } -} - -struct DefsUsesVisitor { - defs_uses: DefsUses, -} - -#[derive(Eq, PartialEq, Clone)] -struct DefsUses { - defs: LiveVarSet, - uses: LiveVarSet, -} - -impl DefsUses { - fn apply(&self, bits: &mut LiveVarSet) -> bool { - bits.subtract(&self.defs) | bits.union(&self.uses) - } - - fn add_def(&mut self, index: Local) { - // If it was used already in the block, remove that use - // now that we found a definition. - // - // Example: - // - // // Defs = {X}, Uses = {} - // X = 5 - // // Defs = {}, Uses = {X} - // use(X) - self.uses.remove(index); - self.defs.insert(index); - } - - fn add_use(&mut self, index: Local) { - // Inverse of above. - // - // Example: - // - // // Defs = {}, Uses = {X} - // use(X) - // // Defs = {X}, Uses = {} - // X = 5 - // // Defs = {}, Uses = {X} - // use(X) - self.defs.remove(index); - self.uses.insert(index); - } -} - -impl<'tcx> Visitor<'tcx> for DefsUsesVisitor { - fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { - match categorize(context) { - Some(DefUse::Def) => self.defs_uses.add_def(local), - Some(DefUse::Use | DefUse::Drop) => self.defs_uses.add_use(local), - _ => (), - } - } -} - -fn block(b: &BasicBlockData<'_>, locals: usize) -> DefsUses { - let mut visitor = DefsUsesVisitor { - defs_uses: DefsUses { - defs: LiveVarSet::new_empty(locals), - uses: LiveVarSet::new_empty(locals), - }, - }; - - let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 }; - - // Visit the various parts of the basic block in reverse. If we go - // forward, the logic in `add_def` and `add_use` would be wrong. - visitor.visit_terminator(b.terminator(), dummy_location); - for statement in b.statements.iter().rev() { - visitor.visit_statement(statement, dummy_location); - } - - visitor.defs_uses -} - -pub fn dump_mir<'tcx>( - tcx: TyCtxt<'tcx>, - pass_name: &str, - source: MirSource<'tcx>, - body: &Body<'tcx>, - result: &LivenessResult, -) { - if !dump_enabled(tcx, pass_name, source.def_id()) { - return; - } - let node_path = ty::print::with_forced_impl_filename_line(|| { - // see notes on #41697 below - tcx.def_path_str(source.def_id()) - }); - dump_matched_mir_node(tcx, pass_name, &node_path, source, body, result); -} - -fn dump_matched_mir_node<'tcx>( - tcx: TyCtxt<'tcx>, - pass_name: &str, - node_path: &str, - source: MirSource<'tcx>, - body: &Body<'tcx>, - result: &LivenessResult, -) { - let mut file_path = PathBuf::new(); - file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); - let item_id = tcx.hir().as_local_hir_id(source.def_id().expect_local()); - let file_name = format!("rustc.node{}{}-liveness.mir", item_id, pass_name); - file_path.push(&file_name); - let _ = fs::File::create(&file_path).and_then(|file| { - let mut file = BufWriter::new(file); - writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?; - writeln!(file, "// source = {:?}", source)?; - writeln!(file, "// pass_name = {}", pass_name)?; - writeln!(file)?; - write_mir_fn(tcx, source, body, &mut file, result)?; - Ok(()) - }); -} - -pub fn write_mir_fn<'tcx>( - tcx: TyCtxt<'tcx>, - src: MirSource<'tcx>, - body: &Body<'tcx>, - w: &mut dyn Write, - result: &LivenessResult, -) -> io::Result<()> { - write_mir_intro(tcx, src, body, w)?; - for block in body.basic_blocks().indices() { - let print = |w: &mut dyn Write, prefix, result: &IndexVec| { - let live: Vec = - result[block].iter().map(|local| format!("{:?}", local)).collect(); - writeln!(w, "{} {{{}}}", prefix, live.join(", ")) - }; - write_basic_block(tcx, block, body, &mut |_, _| Ok(()), w)?; - print(w, " ", &result.outs)?; - if block.index() + 1 != body.basic_blocks().len() { - writeln!(w)?; - } - } - - writeln!(w, "}}")?; - Ok(()) -} diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index 3e501193e8d..8bbe207c077 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -8,7 +8,6 @@ pub mod storage; mod alignment; pub mod collect_writes; mod graphviz; -pub mod liveness; pub(crate) mod pretty; pub use self::aggregate::expand_aggregate; diff --git a/src/librustc_passes/check_const.rs b/src/librustc_passes/check_const.rs index c42f2fc264a..94f9c619a3a 100644 --- a/src/librustc_passes/check_const.rs +++ b/src/librustc_passes/check_const.rs @@ -7,7 +7,6 @@ //! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips //! through, but errors for structured control flow in a `const` should be emitted here. -use rustc_ast::ast::Mutability; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -19,8 +18,6 @@ use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::{sym, Span, Symbol}; -use std::fmt; - /// An expression that is not *always* legal in a const context. #[derive(Clone, Copy)] enum NonConstExpr { @@ -65,46 +62,6 @@ impl NonConstExpr { } } -#[derive(Copy, Clone)] -enum ConstKind { - Static, - StaticMut, - ConstFn, - Const, - AnonConst, -} - -impl ConstKind { - fn for_body(body: &hir::Body<'_>, tcx: TyCtxt<'_>) -> Option { - let owner = tcx.hir().body_owner(body.id()); - let const_kind = match tcx.hir().body_owner_kind(owner) { - hir::BodyOwnerKind::Const => Self::Const, - hir::BodyOwnerKind::Static(Mutability::Mut) => Self::StaticMut, - hir::BodyOwnerKind::Static(Mutability::Not) => Self::Static, - - hir::BodyOwnerKind::Fn if tcx.is_const_fn_raw(tcx.hir().local_def_id(owner)) => { - Self::ConstFn - } - hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => return None, - }; - - Some(const_kind) - } -} - -impl fmt::Display for ConstKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - Self::Static => "static", - Self::StaticMut => "static mut", - Self::Const | Self::AnonConst => "const", - Self::ConstFn => "const fn", - }; - - write!(f, "{}", s) - } -} - fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: DefId) { let mut vis = CheckConstVisitor::new(tcx); tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor()); @@ -117,7 +74,7 @@ pub(crate) fn provide(providers: &mut Providers<'_>) { #[derive(Copy, Clone)] struct CheckConstVisitor<'tcx> { tcx: TyCtxt<'tcx>, - const_kind: Option, + const_kind: Option, } impl<'tcx> CheckConstVisitor<'tcx> { @@ -147,7 +104,8 @@ impl<'tcx> CheckConstVisitor<'tcx> { let const_kind = self .const_kind .expect("`const_check_violated` may only be called inside a const context"); - let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind); + + let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); let required_gates = required_gates.unwrap_or(&[]); let missing_gates: Vec<_> = @@ -191,7 +149,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { } /// Saves the parent `const_kind` before calling `f` and restores it afterwards. - fn recurse_into(&mut self, kind: Option, f: impl FnOnce(&mut Self)) { + fn recurse_into(&mut self, kind: Option, f: impl FnOnce(&mut Self)) { let parent_kind = self.const_kind; self.const_kind = kind; f(self); @@ -207,12 +165,13 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { } fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { - let kind = Some(ConstKind::AnonConst); + let kind = Some(hir::ConstContext::Const); self.recurse_into(kind, |this| intravisit::walk_anon_const(this, anon)); } fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - let kind = ConstKind::for_body(body, self.tcx); + let owner = self.tcx.hir().body_owner_def_id(body.id()); + let kind = self.tcx.hir().body_const_context(owner); self.recurse_into(kind, |this| intravisit::walk_body(this, body)); } diff --git a/src/test/ui/const-generics/trait-const-args.rs b/src/test/ui/const-generics/trait-const-args.rs new file mode 100644 index 00000000000..b60d7e89651 --- /dev/null +++ b/src/test/ui/const-generics/trait-const-args.rs @@ -0,0 +1,29 @@ +// check-pass +#![allow(incomplete_features)] +#![feature(const_generics)] + +struct Const; +trait Foo {} + +impl Foo for Const {} + +fn foo_impl(_: impl Foo<3>) {} + +fn foo_explicit>(_: T) {} + +fn foo_where(_: T) +where + T: Foo<3>, +{ +} + +fn main() { + foo_impl(Const); + foo_impl(Const::<3>); + + foo_explicit(Const); + foo_explicit(Const::<3>); + + foo_where(Const); + foo_where(Const::<3>); +} diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr b/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr index 0c328a2594a..8dbfeff7972 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr +++ b/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr @@ -5,7 +5,7 @@ LL | const N: i32 = T::N << 42; | ^^^^^^^^^^ attempt to shift left with overflow | note: the lint level is defined here - --> $DIR/lint-exceeding-bitshifts.rs:8:9 + --> $DIR/lint-exceeding-bitshifts.rs:9:9 | LL | #![warn(arithmetic_overflow, const_err)] | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr b/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr index 0c328a2594a..8dbfeff7972 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr +++ b/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr @@ -5,7 +5,7 @@ LL | const N: i32 = T::N << 42; | ^^^^^^^^^^ attempt to shift left with overflow | note: the lint level is defined here - --> $DIR/lint-exceeding-bitshifts.rs:8:9 + --> $DIR/lint-exceeding-bitshifts.rs:9:9 | LL | #![warn(arithmetic_overflow, const_err)] | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr b/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr index 0c328a2594a..8dbfeff7972 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr +++ b/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr @@ -5,7 +5,7 @@ LL | const N: i32 = T::N << 42; | ^^^^^^^^^^ attempt to shift left with overflow | note: the lint level is defined here - --> $DIR/lint-exceeding-bitshifts.rs:8:9 + --> $DIR/lint-exceeding-bitshifts.rs:9:9 | LL | #![warn(arithmetic_overflow, const_err)] | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.rs b/src/test/ui/lint/lint-exceeding-bitshifts.rs index 565bef49c9f..4d56d103a83 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.rs +++ b/src/test/ui/lint/lint-exceeding-bitshifts.rs @@ -3,11 +3,11 @@ //[opt]compile-flags: -O //[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O // build-pass +// ignore-pass (test emits codegen-time warnings and verifies that they are not errors) #![crate_type="lib"] #![warn(arithmetic_overflow, const_err)] -#![allow(unused_variables)] -#![allow(dead_code)] + pub trait Foo { const N: i32;