2019-11-07 12:02:01 -06:00
|
|
|
//! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`,
|
|
|
|
//! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const
|
|
|
|
//! context.
|
2019-11-06 13:44:56 -06:00
|
|
|
//!
|
|
|
|
//! By the time the MIR const-checker runs, these high-level constructs have been lowered to
|
|
|
|
//! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute
|
|
|
|
//! 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.
|
|
|
|
|
2019-12-22 16:42:04 -06:00
|
|
|
use rustc::hir;
|
2019-11-06 13:44:56 -06:00
|
|
|
use rustc::hir::def_id::DefId;
|
2019-12-22 16:42:04 -06:00
|
|
|
use rustc::hir::intravisit::{NestedVisitorMap, Visitor};
|
2019-11-06 13:44:56 -06:00
|
|
|
use rustc::hir::map::Map;
|
2019-12-10 14:43:15 -06:00
|
|
|
use rustc::session::config::nightly_options;
|
2019-12-22 16:42:04 -06:00
|
|
|
use rustc::ty::query::Providers;
|
|
|
|
use rustc::ty::TyCtxt;
|
|
|
|
use rustc_error_codes::*;
|
2019-11-13 12:46:44 -06:00
|
|
|
use syntax::ast::Mutability;
|
2019-11-30 00:40:28 -06:00
|
|
|
use syntax::feature_gate::feature_err;
|
2019-11-06 13:44:56 -06:00
|
|
|
use syntax::span_err;
|
2019-12-10 14:43:15 -06:00
|
|
|
use syntax_pos::{sym, Span, Symbol};
|
2019-11-06 13:44:56 -06:00
|
|
|
|
|
|
|
use std::fmt;
|
|
|
|
|
2019-11-16 01:08:30 -06:00
|
|
|
/// An expression that is not *always* legal in a const context.
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
enum NonConstExpr {
|
|
|
|
Loop(hir::LoopSource),
|
|
|
|
Match(hir::MatchSource),
|
2019-12-24 10:43:17 -06:00
|
|
|
OrPattern,
|
2019-11-16 01:08:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl NonConstExpr {
|
2019-12-24 10:43:17 -06:00
|
|
|
fn name(self) -> String {
|
2019-11-16 01:08:30 -06:00
|
|
|
match self {
|
2019-12-24 10:43:17 -06:00
|
|
|
Self::Loop(src) => format!("`{}`", src.name()),
|
|
|
|
Self::Match(src) => format!("`{}`", src.name()),
|
|
|
|
Self::OrPattern => format!("or-pattern"),
|
2019-11-16 01:08:30 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 14:43:15 -06:00
|
|
|
fn required_feature_gates(self) -> Option<&'static [Symbol]> {
|
|
|
|
use hir::LoopSource::*;
|
2019-12-22 16:42:04 -06:00
|
|
|
use hir::MatchSource::*;
|
2019-12-10 14:43:15 -06:00
|
|
|
|
|
|
|
let gates: &[_] = match self {
|
2019-12-22 16:42:04 -06:00
|
|
|
Self::Match(Normal)
|
2019-11-16 01:08:30 -06:00
|
|
|
| Self::Match(IfDesugar { .. })
|
2019-12-24 10:43:17 -06:00
|
|
|
| Self::Match(IfLetDesugar { .. })
|
|
|
|
| Self::OrPattern => &[sym::const_if_match],
|
2019-11-16 01:08:30 -06:00
|
|
|
|
2019-12-22 16:42:04 -06:00
|
|
|
Self::Loop(Loop) => &[sym::const_loop],
|
2019-12-10 14:43:15 -06:00
|
|
|
|
2019-12-22 16:42:04 -06:00
|
|
|
Self::Loop(While)
|
2019-12-10 14:43:15 -06:00
|
|
|
| Self::Loop(WhileLet)
|
|
|
|
| Self::Match(WhileDesugar)
|
2019-12-22 16:42:04 -06:00
|
|
|
| Self::Match(WhileLetDesugar) => &[sym::const_loop, sym::const_if_match],
|
2019-12-10 14:43:15 -06:00
|
|
|
|
2019-12-11 12:20:50 -06:00
|
|
|
// A `for` loop's desugaring contains a call to `IntoIterator::into_iter`,
|
2019-12-11 00:24:10 -06:00
|
|
|
// so they are not yet allowed with `#![feature(const_loop)]`.
|
2019-12-10 14:43:15 -06:00
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(gates)
|
2019-11-16 01:08:30 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-06 13:44:56 -06:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
enum ConstKind {
|
|
|
|
Static,
|
|
|
|
StaticMut,
|
|
|
|
ConstFn,
|
|
|
|
Const,
|
|
|
|
AnonConst,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConstKind {
|
2019-11-29 04:09:23 -06:00
|
|
|
fn for_body(body: &hir::Body<'_>, hir_map: &Map<'_>) -> Option<Self> {
|
2019-11-06 13:44:56 -06:00
|
|
|
let is_const_fn = |id| hir_map.fn_sig_by_hir_id(id).unwrap().header.is_const();
|
|
|
|
|
|
|
|
let owner = hir_map.body_owner(body.id());
|
|
|
|
let const_kind = match hir_map.body_owner_kind(owner) {
|
|
|
|
hir::BodyOwnerKind::Const => Self::Const,
|
2019-12-16 10:28:40 -06:00
|
|
|
hir::BodyOwnerKind::Static(Mutability::Mut) => Self::StaticMut,
|
|
|
|
hir::BodyOwnerKind::Static(Mutability::Not) => Self::Static,
|
2019-11-06 13:44:56 -06:00
|
|
|
|
|
|
|
hir::BodyOwnerKind::Fn if is_const_fn(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());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn provide(providers: &mut Providers<'_>) {
|
2019-12-22 16:42:04 -06:00
|
|
|
*providers = Providers { check_mod_const_bodies, ..*providers };
|
2019-11-06 13:44:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
struct CheckConstVisitor<'tcx> {
|
2019-11-15 16:46:11 -06:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-11-06 13:44:56 -06:00
|
|
|
const_kind: Option<ConstKind>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> CheckConstVisitor<'tcx> {
|
|
|
|
fn new(tcx: TyCtxt<'tcx>) -> Self {
|
2019-12-22 16:42:04 -06:00
|
|
|
CheckConstVisitor { tcx, const_kind: None }
|
2019-11-06 13:44:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Emits an error when an unsupported expression is found in a const context.
|
2019-11-16 01:08:30 -06:00
|
|
|
fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
|
2019-12-10 14:43:15 -06:00
|
|
|
let features = self.tcx.features();
|
2019-12-11 12:26:06 -06:00
|
|
|
let required_gates = expr.required_feature_gates();
|
|
|
|
match required_gates {
|
2019-11-16 01:08:30 -06:00
|
|
|
// Don't emit an error if the user has enabled the requisite feature gates.
|
2019-12-11 12:24:40 -06:00
|
|
|
Some(gates) if gates.iter().all(|&g| features.enabled(g)) => return,
|
2019-11-16 01:08:30 -06:00
|
|
|
|
2019-12-10 14:43:15 -06:00
|
|
|
// `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
|
|
|
|
// corresponding feature gate. This encourages nightly users to use feature gates when
|
|
|
|
// possible.
|
2019-11-16 01:08:30 -06:00
|
|
|
None if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
|
|
|
|
self.tcx.sess.span_warn(span, "skipping const checks");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {}
|
2019-11-09 11:07:13 -06:00
|
|
|
}
|
|
|
|
|
2019-12-22 16:42:04 -06:00
|
|
|
let const_kind = self
|
|
|
|
.const_kind
|
2019-11-06 13:44:56 -06:00
|
|
|
.expect("`const_check_violated` may only be called inside a const context");
|
2019-12-24 10:43:17 -06:00
|
|
|
let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind);
|
2019-11-16 01:08:30 -06:00
|
|
|
|
2019-12-11 12:26:06 -06:00
|
|
|
let required_gates = required_gates.unwrap_or(&[]);
|
2019-12-22 16:42:04 -06:00
|
|
|
let missing_gates: Vec<_> =
|
|
|
|
required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
|
2019-12-10 14:43:15 -06:00
|
|
|
|
|
|
|
match missing_gates.as_slice() {
|
|
|
|
&[] => span_err!(self.tcx.sess, span, E0744, "{}", msg),
|
|
|
|
|
|
|
|
// If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
|
|
|
|
// explain why their `while` loop is being rejected.
|
2019-12-11 12:26:06 -06:00
|
|
|
&[gate @ sym::const_if_match] if required_gates.contains(&sym::const_loop) => {
|
2019-12-11 12:20:50 -06:00
|
|
|
feature_err(&self.tcx.sess.parse_sess, gate, span, &msg)
|
2019-12-22 16:42:04 -06:00
|
|
|
.note(
|
|
|
|
"`#![feature(const_loop)]` alone is not sufficient, \
|
|
|
|
since this loop expression contains an implicit conditional",
|
|
|
|
)
|
2019-12-11 12:20:50 -06:00
|
|
|
.emit();
|
2019-12-10 14:43:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
&[missing_primary, ref missing_secondary @ ..] => {
|
|
|
|
let mut err = feature_err(&self.tcx.sess.parse_sess, missing_primary, span, &msg);
|
|
|
|
|
|
|
|
// If multiple feature gates would be required to enable this expression, include
|
|
|
|
// them as help messages. Don't emit a separate error for each missing feature gate.
|
|
|
|
//
|
|
|
|
// FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
|
|
|
|
// is a pretty narrow case, however.
|
|
|
|
if nightly_options::is_nightly_build() {
|
|
|
|
for gate in missing_secondary {
|
|
|
|
let note = format!(
|
|
|
|
"add `#![feature({})]` to the crate attributes to enable",
|
|
|
|
gate,
|
|
|
|
);
|
|
|
|
err.help(¬e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err.emit();
|
|
|
|
}
|
2019-11-16 01:08:30 -06:00
|
|
|
}
|
2019-11-06 13:44:56 -06:00
|
|
|
}
|
|
|
|
|
2019-11-07 12:02:01 -06:00
|
|
|
/// Saves the parent `const_kind` before calling `f` and restores it afterwards.
|
2019-11-06 13:44:56 -06:00
|
|
|
fn recurse_into(&mut self, kind: Option<ConstKind>, f: impl FnOnce(&mut Self)) {
|
|
|
|
let parent_kind = self.const_kind;
|
|
|
|
self.const_kind = kind;
|
|
|
|
f(self);
|
|
|
|
self.const_kind = parent_kind;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
|
|
|
|
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
2019-11-15 16:46:11 -06:00
|
|
|
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
|
2019-11-06 13:44:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
|
|
|
|
let kind = Some(ConstKind::AnonConst);
|
|
|
|
self.recurse_into(kind, |this| hir::intravisit::walk_anon_const(this, anon));
|
|
|
|
}
|
|
|
|
|
2019-11-29 04:09:23 -06:00
|
|
|
fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
|
2019-11-15 16:46:11 -06:00
|
|
|
let kind = ConstKind::for_body(body, self.tcx.hir());
|
2019-11-06 13:44:56 -06:00
|
|
|
self.recurse_into(kind, |this| hir::intravisit::walk_body(this, body));
|
|
|
|
}
|
|
|
|
|
2019-11-30 08:08:22 -06:00
|
|
|
fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
|
2019-12-24 10:43:17 -06:00
|
|
|
if self.const_kind.is_some() {
|
|
|
|
if let hir::PatKind::Or { .. } = p.kind {
|
|
|
|
self.const_check_violated(NonConstExpr::OrPattern, p.span);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hir::intravisit::walk_pat(self, p)
|
|
|
|
}
|
|
|
|
|
2019-11-30 08:08:22 -06:00
|
|
|
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
|
2019-11-06 13:44:56 -06:00
|
|
|
match &e.kind {
|
2019-11-07 12:02:01 -06:00
|
|
|
// Skip the following checks if we are not currently in a const context.
|
2019-11-06 13:44:56 -06:00
|
|
|
_ if self.const_kind.is_none() => {}
|
|
|
|
|
|
|
|
hir::ExprKind::Loop(_, _, source) => {
|
2019-11-16 01:08:30 -06:00
|
|
|
self.const_check_violated(NonConstExpr::Loop(*source), e.span);
|
2019-11-06 13:44:56 -06:00
|
|
|
}
|
|
|
|
|
2019-11-16 01:08:30 -06:00
|
|
|
hir::ExprKind::Match(_, _, source) => {
|
|
|
|
let non_const_expr = match source {
|
2019-11-06 13:44:56 -06:00
|
|
|
// These are handled by `ExprKind::Loop` above.
|
2019-12-22 16:42:04 -06:00
|
|
|
hir::MatchSource::WhileDesugar
|
2019-11-16 01:08:30 -06:00
|
|
|
| hir::MatchSource::WhileLetDesugar
|
2019-12-22 16:42:04 -06:00
|
|
|
| hir::MatchSource::ForLoopDesugar => None,
|
2019-11-16 01:08:30 -06:00
|
|
|
|
|
|
|
_ => Some(NonConstExpr::Match(*source)),
|
2019-11-06 13:44:56 -06:00
|
|
|
};
|
|
|
|
|
2019-11-16 01:08:30 -06:00
|
|
|
if let Some(expr) = non_const_expr {
|
|
|
|
self.const_check_violated(expr, e.span);
|
2019-11-06 13:44:56 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 16:42:04 -06:00
|
|
|
_ => {}
|
2019-11-06 13:44:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
hir::intravisit::walk_expr(self, e);
|
|
|
|
}
|
|
|
|
}
|