mirror of https://github.com/rust-lang/rust.git
Auto merge of #72010 - Dylan-DPC:rollup-prdj0pk, r=Dylan-DPC
Rollup of 6 pull requests Successful merges: - #71989 (Use a single enum for the kind of a const context) - #71993 (Remove old `util/liveness.rs` module) - #71999 (Add myself to mailmap.) - #72001 (Adjust cfg(version) to lang team decision) - #72007 (Fix some tests failing in `--pass check` mode) - #72008 (Add const-generics test) Failed merges: r? @ghost
This commit is contained in:
commit
7b805396bf
1
.mailmap
1
.mailmap
|
@ -49,6 +49,7 @@ Carol (Nichols || Goulding) <carol.nichols@gmail.com> <193874+carols10cents@user
|
|||
Carol (Nichols || Goulding) <carol.nichols@gmail.com> <carol.nichols@gmail.com>
|
||||
Carol (Nichols || Goulding) <carol.nichols@gmail.com> <cnichols@thinkthroughmath.com>
|
||||
Carol Willing <carolcode@willingconsulting.com>
|
||||
Charles Lew <crlf0710@gmail.com> CrLF0710 <crlf0710@gmail.com>
|
||||
Chris C Cerami <chrisccerami@users.noreply.github.com> Chris C Cerami <chrisccerami@gmail.com>
|
||||
Chris Pressey <cpressey@gmail.com>
|
||||
Chris Thorn <chris@thorn.co> Chris Thorn <thorn@thoughtbot.com>
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<LitKind>;
|
||||
|
||||
|
|
|
@ -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<ConstContext> {
|
||||
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,
|
||||
|
|
|
@ -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<DefUse> {
|
||||
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,
|
||||
}
|
||||
}
|
|
@ -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 }),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<ConstKind>,
|
||||
pub const_kind: Option<hir::ConstContext>,
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
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()
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(_) => {}
|
||||
|
|
|
@ -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<Local>;
|
||||
|
||||
/// 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<BasicBlock, LiveVarSet>,
|
||||
}
|
||||
|
||||
/// 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<BasicBlock> = 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<DefUse> {
|
||||
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<BasicBlock, LiveVarSet>| {
|
||||
let live: Vec<String> =
|
||||
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(())
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<Self> {
|
||||
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<ConstKind>,
|
||||
const_kind: Option<hir::ConstContext>,
|
||||
}
|
||||
|
||||
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<ConstKind>, f: impl FnOnce(&mut Self)) {
|
||||
fn recurse_into(&mut self, kind: Option<hir::ConstContext>, 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));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// check-pass
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_generics)]
|
||||
|
||||
struct Const<const N: usize>;
|
||||
trait Foo<const N: usize> {}
|
||||
|
||||
impl<const N: usize> Foo<N> for Const<N> {}
|
||||
|
||||
fn foo_impl(_: impl Foo<3>) {}
|
||||
|
||||
fn foo_explicit<T: Foo<3>>(_: T) {}
|
||||
|
||||
fn foo_where<T>(_: 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>);
|
||||
}
|
|
@ -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)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue