Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up check_consts now that new promotion pass is implemented #65839

Merged
merged 5 commits into from
Oct 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 79 additions & 30 deletions src/librustc_mir/transform/check_consts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,27 @@
//! has interior mutability or needs to be dropped, as well as the visitor that emits errors when
//! it finds operations that are invalid in a certain context.

use rustc::hir::def_id::DefId;
use rustc::hir::{self, def_id::DefId};
use rustc::mir;
use rustc::ty::{self, TyCtxt};

use std::fmt;

pub use self::qualifs::Qualif;

pub mod ops;
pub mod qualifs;
mod resolver;
pub mod validation;

/// Information about the item currently being validated, as well as a reference to the global
/// Information about the item currently being const-checked, as well as a reference to the global
/// context.
pub struct Item<'mir, 'tcx> {
body: &'mir mir::Body<'tcx>,
tcx: TyCtxt<'tcx>,
def_id: DefId,
param_env: ty::ParamEnv<'tcx>,
mode: validation::Mode,
for_promotion: bool,
pub body: &'mir mir::Body<'tcx>,
pub tcx: TyCtxt<'tcx>,
pub def_id: DefId,
pub param_env: ty::ParamEnv<'tcx>,
pub const_kind: Option<ConstKind>,
}

impl Item<'mir, 'tcx> {
Expand All @@ -33,43 +34,91 @@ impl Item<'mir, 'tcx> {
body: &'mir mir::Body<'tcx>,
) -> Self {
let param_env = tcx.param_env(def_id);
let mode = validation::Mode::for_item(tcx, def_id)
.expect("const validation must only be run inside a const context");
let const_kind = ConstKind::for_item(tcx, def_id);

Item {
body,
tcx,
def_id,
param_env,
mode,
for_promotion: false,
const_kind,
}
}

// HACK(eddyb) this is to get around the panic for a runtime fn from `Item::new`.
// Also, it allows promoting `&mut []`.
pub fn for_promotion(
tcx: TyCtxt<'tcx>,
def_id: DefId,
body: &'mir mir::Body<'tcx>,
) -> Self {
let param_env = tcx.param_env(def_id);
let mode = validation::Mode::for_item(tcx, def_id)
.unwrap_or(validation::Mode::ConstFn);
/// 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 {
self.const_kind.expect("`const_kind` must not be called on a non-const fn")
}
}

Item {
body,
tcx,
def_id,
param_env,
mode,
for_promotion: true,
/// 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: DefId) -> Option<Self> {
use hir::BodyOwnerKind as HirKind;

let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();

let mode = match tcx.hir().body_owner_kind(hir_id) {
HirKind::Closure => return None,

HirKind::Fn if tcx.is_const_fn(def_id) => ConstKind::ConstFn,
HirKind::Fn => return None,

HirKind::Const => ConstKind::Const,

HirKind::Static(hir::MutImmutable) => ConstKind::Static,
HirKind::Static(hir::MutMutable) => ConstKind::StaticMut,
};

Some(mode)
}

pub fn is_static(self) -> bool {
match self {
ConstKind::Static | ConstKind::StaticMut => true,
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 {
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"),
}
}
}

fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
/// 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()
}
33 changes: 16 additions & 17 deletions src/librustc_mir/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use syntax::feature_gate::{emit_feature_err, GateIssue};
use syntax::symbol::sym;
use syntax_pos::{Span, Symbol};

use super::Item;
use super::validation::Mode;
use super::{ConstKind, Item};

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
Expand All @@ -36,7 +35,7 @@ pub trait NonConstOp: std::fmt::Debug {
span,
E0019,
"{} contains unimplemented expression type",
item.mode
item.const_kind()
);
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note("A function call isn't allowed in the const's initialization expression \
Expand Down Expand Up @@ -76,7 +75,7 @@ impl NonConstOp for FnCallNonConst {
E0015,
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
item.mode,
item.const_kind(),
);
err.emit();
}
Expand Down Expand Up @@ -121,8 +120,8 @@ impl NonConstOp for HeapAllocation {

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let mut err = struct_span_err!(item.tcx.sess, span, E0010,
"allocations are not allowed in {}s", item.mode);
err.span_label(span, format!("allocation not allowed in {}s", item.mode));
"allocations are not allowed in {}s", item.const_kind());
err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"The value of statics and constants must be known at compile time, \
Expand All @@ -146,7 +145,7 @@ impl NonConstOp for LiveDrop {
struct_span_err!(item.tcx.sess, span, E0493,
"destructors cannot be evaluated at compile-time")
.span_label(span, format!("{}s cannot evaluate destructors",
item.mode))
item.const_kind()))
.emit();
}
}
Expand All @@ -163,9 +162,9 @@ impl NonConstOp for MutBorrow {
if let BorrowKind::Mut { .. } = kind {
let mut err = struct_span_err!(item.tcx.sess, span, E0017,
"references in {}s may only refer \
to immutable values", item.mode);
to immutable values", item.const_kind());
err.span_label(span, format!("{}s require immutable values",
item.mode));
item.const_kind()));
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note("References in statics and constants may only refer \
to immutable values.\n\n\
Expand Down Expand Up @@ -202,7 +201,7 @@ impl NonConstOp for Panic {
sym::const_panic,
span,
GateIssue::Language,
&format!("panicking in {}s is unstable", item.mode),
&format!("panicking in {}s is unstable", item.const_kind()),
);
}
}
Expand All @@ -220,7 +219,7 @@ impl NonConstOp for RawPtrComparison {
sym::const_compare_raw_pointers,
span,
GateIssue::Language,
&format!("comparing raw pointers inside {}", item.mode),
&format!("comparing raw pointers inside {}", item.const_kind()),
);
}
}
Expand All @@ -238,7 +237,7 @@ impl NonConstOp for RawPtrDeref {
span, GateIssue::Language,
&format!(
"dereferencing raw pointers in {}s is unstable",
item.mode,
item.const_kind(),
),
);
}
Expand All @@ -257,7 +256,7 @@ impl NonConstOp for RawPtrToIntCast {
span, GateIssue::Language,
&format!(
"casting pointers to integers in {}s is unstable",
item.mode,
item.const_kind(),
),
);
}
Expand All @@ -268,13 +267,13 @@ impl NonConstOp for RawPtrToIntCast {
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
item.mode.is_static()
item.const_kind().is_static()
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let mut err = struct_span_err!(item.tcx.sess, span, E0013,
"{}s cannot refer to statics, use \
a constant instead", item.mode);
a constant instead", item.const_kind());
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"Static and const variables can refer to other const variables. \
Expand Down Expand Up @@ -313,7 +312,7 @@ impl NonConstOp for Transmute {
&item.tcx.sess.parse_sess, sym::const_transmute,
span, GateIssue::Language,
&format!("The use of std::mem::transmute() \
is gated in {}s", item.mode));
is gated in {}s", item.const_kind()));
}
}

Expand All @@ -322,7 +321,7 @@ pub struct UnionAccess;
impl NonConstOp for UnionAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
// Union accesses are stable in all contexts except `const fn`.
item.mode != Mode::ConstFn || Self::feature_gate(item.tcx).unwrap()
item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap()
}

fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
Expand Down
21 changes: 12 additions & 9 deletions src/librustc_mir/transform/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use rustc::mir::interpret::ConstValue;
use rustc::ty::{self, Ty};
use syntax_pos::DUMMY_SP;

use super::Item as ConstCx;
use super::validation::Mode;
use super::{ConstKind, Item as ConstCx};

#[derive(Clone, Copy)]
pub struct QualifSet(u8);
Expand Down Expand Up @@ -236,13 +235,17 @@ impl Qualif for HasMutInterior {
// mutably without consequences.
match ty.kind {
// Inside a `static mut`, &mut [...] is also allowed.
ty::Array(..) | ty::Slice(_) if cx.mode == Mode::StaticMut => {},

// FIXME(eddyb) the `cx.for_promotion` condition
// seems unnecessary, given that this is merely a ZST.
ty::Array(_, len)
if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
&& cx.for_promotion => {},
| ty::Array(..)
| ty::Slice(_)
if cx.const_kind == Some(ConstKind::StaticMut)
=> {},

// FIXME(eddyb): We only return false for `&mut []` outside a const
// context which seems unnecessary given that this is merely a ZST.
| ty::Array(_, len)
if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
&& cx.const_kind == None
=> {},

_ => return true,
}
Expand Down
Loading