2014-12-30 22:36:03 +02:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2012-12-03 16:48:01 -08:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2014-12-30 22:36:03 +02:00
|
|
|
// Verifies that the types and values of const and static items
|
|
|
|
// are safe. The rules enforced by this module are:
|
|
|
|
//
|
|
|
|
// - For each *mutable* static item, it checks that its **type**:
|
|
|
|
// - doesn't have a destructor
|
2015-06-09 16:26:21 -04:00
|
|
|
// - doesn't own a box
|
2014-12-30 22:36:03 +02:00
|
|
|
//
|
|
|
|
// - For each *immutable* static item, it checks that its **value**:
|
2015-06-09 16:26:21 -04:00
|
|
|
// - doesn't own a box
|
2014-12-30 22:36:03 +02:00
|
|
|
// - doesn't contain a struct literal or a call to an enum variant / struct constructor where
|
|
|
|
// - the type of the struct/enum has a dtor
|
|
|
|
//
|
|
|
|
// Rules Enforced Elsewhere:
|
|
|
|
// - It's not possible to take the address of a static item with unsafe interior. This is enforced
|
|
|
|
// by borrowck::gather_loans
|
2013-05-17 15:28:44 -07:00
|
|
|
|
2015-05-05 19:36:47 +03:00
|
|
|
use middle::cast::{CastKind};
|
2015-01-29 13:57:06 +02:00
|
|
|
use middle::const_eval;
|
2014-12-30 22:36:03 +02:00
|
|
|
use middle::def;
|
|
|
|
use middle::expr_use_visitor as euv;
|
|
|
|
use middle::infer;
|
|
|
|
use middle::mem_categorization as mc;
|
|
|
|
use middle::traits;
|
2015-01-29 13:57:06 +02:00
|
|
|
use middle::ty::{self, Ty};
|
|
|
|
use util::nodemap::NodeMap;
|
2015-05-05 19:36:47 +03:00
|
|
|
use util::ppaux::Repr;
|
2012-12-23 17:41:37 -05:00
|
|
|
|
2014-09-13 20:10:34 +03:00
|
|
|
use syntax::ast;
|
2014-12-30 22:36:03 +02:00
|
|
|
use syntax::codemap::Span;
|
2015-01-04 17:58:56 +02:00
|
|
|
use syntax::visit::{self, Visitor};
|
2013-08-13 02:10:10 +02:00
|
|
|
|
2015-01-29 13:57:06 +02:00
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
|
|
|
|
// Const qualification, from partial to completely promotable.
|
|
|
|
bitflags! {
|
|
|
|
#[derive(RustcEncodable, RustcDecodable)]
|
|
|
|
flags ConstQualif: u8 {
|
|
|
|
// Inner mutability (can not be placed behind a reference) or behind
|
|
|
|
// &mut in a non-global expression. Can be copied from static memory.
|
2015-04-28 16:36:22 -07:00
|
|
|
const MUTABLE_MEM = 1 << 0,
|
2015-01-29 13:57:06 +02:00
|
|
|
// Constant value with a type that implements Drop. Can be copied
|
|
|
|
// from static memory, similar to MUTABLE_MEM.
|
2015-04-28 16:36:22 -07:00
|
|
|
const NEEDS_DROP = 1 << 1,
|
2015-01-29 13:57:06 +02:00
|
|
|
// Even if the value can be placed in static memory, copying it from
|
|
|
|
// there is more expensive than in-place instantiation, and/or it may
|
|
|
|
// be too large. This applies to [T; N] and everything containing it.
|
|
|
|
// N.B.: references need to clear this flag to not end up on the stack.
|
2015-04-28 16:36:22 -07:00
|
|
|
const PREFER_IN_PLACE = 1 << 2,
|
2015-01-29 13:57:06 +02:00
|
|
|
// May use more than 0 bytes of memory, doesn't impact the constness
|
|
|
|
// directly, but is not allowed to be borrowed mutably in a constant.
|
2015-04-28 16:36:22 -07:00
|
|
|
const NON_ZERO_SIZED = 1 << 3,
|
2015-01-29 13:57:06 +02:00
|
|
|
// Actually borrowed, has to always be in static memory. Does not
|
|
|
|
// propagate, and requires the expression to behave like a 'static
|
|
|
|
// lvalue. The set of expressions with this flag is the minimum
|
|
|
|
// that have to be promoted.
|
2015-04-28 16:36:22 -07:00
|
|
|
const HAS_STATIC_BORROWS = 1 << 4,
|
2015-01-29 13:57:06 +02:00
|
|
|
// Invalid const for miscellaneous reasons (e.g. not implemented).
|
2015-04-28 16:36:22 -07:00
|
|
|
const NOT_CONST = 1 << 5,
|
2015-01-29 13:57:06 +02:00
|
|
|
|
|
|
|
// Borrowing the expression won't produce &'static T if any of these
|
|
|
|
// bits are set, though the value could be copied from static memory
|
|
|
|
// if `NOT_CONST` isn't set.
|
2015-04-28 16:36:22 -07:00
|
|
|
const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits |
|
|
|
|
ConstQualif::NEEDS_DROP.bits |
|
|
|
|
ConstQualif::NOT_CONST.bits
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
2014-12-30 22:36:03 +02:00
|
|
|
enum Mode {
|
2015-01-29 13:57:06 +02:00
|
|
|
Const,
|
2015-02-25 22:06:08 +02:00
|
|
|
ConstFn,
|
2015-01-29 13:57:06 +02:00
|
|
|
Static,
|
|
|
|
StaticMut,
|
|
|
|
|
|
|
|
// An expression that occurs outside of any constant context
|
|
|
|
// (i.e. `const`, `static`, array lengths, etc.). The value
|
|
|
|
// can be variable at runtime, but will be promotable to
|
|
|
|
// static memory if we can prove it is actually constant.
|
|
|
|
Var,
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
|
2014-09-12 13:10:30 +03:00
|
|
|
struct CheckCrateVisitor<'a, 'tcx: 'a> {
|
2014-04-22 15:56:37 +03:00
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2014-12-30 22:36:03 +02:00
|
|
|
mode: Mode,
|
2015-01-29 13:57:06 +02:00
|
|
|
qualif: ConstQualif,
|
|
|
|
rvalue_borrows: NodeMap<ast::Mutability>
|
2013-08-13 02:10:10 +02:00
|
|
|
}
|
|
|
|
|
2014-09-12 13:10:30 +03:00
|
|
|
impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
2015-01-29 13:57:06 +02:00
|
|
|
fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
|
|
|
|
F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
|
2014-12-08 20:26:43 -05:00
|
|
|
{
|
2015-01-29 13:57:06 +02:00
|
|
|
let (old_mode, old_qualif) = (self.mode, self.qualif);
|
2014-12-30 22:36:03 +02:00
|
|
|
self.mode = mode;
|
2015-04-29 14:58:43 -07:00
|
|
|
self.qualif = ConstQualif::empty();
|
2015-01-29 13:57:06 +02:00
|
|
|
let r = f(self);
|
|
|
|
self.mode = old_mode;
|
|
|
|
self.qualif = old_qualif;
|
|
|
|
r
|
|
|
|
}
|
|
|
|
|
|
|
|
fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where
|
|
|
|
F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'tcx,
|
|
|
|
ty::ParameterEnvironment<'a, 'tcx>>) -> R,
|
|
|
|
{
|
|
|
|
let param_env = match item_id {
|
|
|
|
Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
|
|
|
|
None => ty::empty_parameter_environment(self.tcx)
|
|
|
|
};
|
|
|
|
f(&mut euv::ExprUseVisitor::new(self, ¶m_env))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn global_expr(&mut self, mode: Mode, expr: &ast::Expr) -> ConstQualif {
|
|
|
|
assert!(mode != Mode::Var);
|
|
|
|
match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
|
|
|
|
Entry::Occupied(entry) => return *entry.get(),
|
|
|
|
Entry::Vacant(entry) => {
|
|
|
|
// Prevent infinite recursion on re-entry.
|
2015-04-29 14:58:43 -07:00
|
|
|
entry.insert(ConstQualif::empty());
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
self.with_mode(mode, |this| {
|
|
|
|
this.with_euv(None, |euv| euv.consume_expr(expr));
|
|
|
|
this.visit_expr(expr);
|
|
|
|
this.qualif
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:06:08 +02:00
|
|
|
fn fn_like(&mut self,
|
|
|
|
fk: visit::FnKind,
|
|
|
|
fd: &ast::FnDecl,
|
|
|
|
b: &ast::Block,
|
|
|
|
s: Span,
|
|
|
|
fn_id: ast::NodeId)
|
|
|
|
-> ConstQualif {
|
|
|
|
match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
|
|
|
|
Entry::Occupied(entry) => return *entry.get(),
|
|
|
|
Entry::Vacant(entry) => {
|
|
|
|
// Prevent infinite recursion on re-entry.
|
2015-05-05 08:47:04 -04:00
|
|
|
entry.insert(ConstQualif::empty());
|
2015-02-25 22:06:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mode = match fk {
|
2015-05-05 08:47:04 -04:00
|
|
|
visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => {
|
2015-02-25 22:06:08 +02:00
|
|
|
Mode::ConstFn
|
|
|
|
}
|
2015-05-05 08:47:04 -04:00
|
|
|
visit::FkMethod(_, m, _) => {
|
|
|
|
if m.constness == ast::Constness::Const {
|
2015-02-25 22:06:08 +02:00
|
|
|
Mode::ConstFn
|
|
|
|
} else {
|
|
|
|
Mode::Var
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Mode::Var
|
|
|
|
};
|
|
|
|
|
|
|
|
// Ensure the arguments are simple, not mutable/by-ref or patterns.
|
|
|
|
if mode == Mode::ConstFn {
|
|
|
|
for arg in &fd.inputs {
|
|
|
|
match arg.pat.node {
|
|
|
|
ast::PatIdent(ast::BindByValue(ast::MutImmutable), _, None) => {}
|
|
|
|
_ => {
|
|
|
|
span_err!(self.tcx.sess, arg.pat.span, E0022,
|
|
|
|
"arguments of constant functions can only \
|
|
|
|
be immutable by-value bindings");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let qualif = self.with_mode(mode, |this| {
|
|
|
|
this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
|
|
|
|
visit::walk_fn(this, fk, fd, b, s);
|
|
|
|
this.qualif
|
|
|
|
});
|
|
|
|
|
|
|
|
// Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
|
|
|
|
// and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
|
2015-05-05 08:47:04 -04:00
|
|
|
let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
|
2015-02-25 22:06:08 +02:00
|
|
|
|
|
|
|
self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
|
|
|
|
qualif
|
|
|
|
}
|
|
|
|
|
2015-01-29 13:57:06 +02:00
|
|
|
fn add_qualif(&mut self, qualif: ConstQualif) {
|
|
|
|
self.qualif = self.qualif | qualif;
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:06:08 +02:00
|
|
|
/// Returns true if the call is to a const fn or method.
|
2015-05-28 11:22:00 -04:00
|
|
|
fn handle_const_fn_call(&mut self,
|
|
|
|
expr: &ast::Expr,
|
|
|
|
def_id: ast::DefId,
|
|
|
|
ret_ty: Ty<'tcx>)
|
|
|
|
-> bool {
|
2015-02-25 22:06:08 +02:00
|
|
|
if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) {
|
2015-05-28 15:18:47 -04:00
|
|
|
if
|
|
|
|
// we are in a static/const initializer
|
|
|
|
self.mode != Mode::Var &&
|
|
|
|
|
|
|
|
// feature-gate is not enabled
|
|
|
|
!self.tcx.sess.features.borrow().const_fn &&
|
|
|
|
|
|
|
|
// this doesn't come from a macro that has #[allow_internal_unstable]
|
|
|
|
!self.tcx.sess.codemap().span_allows_unstable(expr.span)
|
|
|
|
{
|
2015-05-28 11:22:00 -04:00
|
|
|
self.tcx.sess.span_err(
|
|
|
|
expr.span,
|
|
|
|
&format!("const fns are an unstable feature"));
|
|
|
|
fileline_help!(
|
|
|
|
self.tcx.sess,
|
|
|
|
expr.span,
|
|
|
|
"in Nightly builds, add `#![feature(const_fn)]` to the crate \
|
|
|
|
attributes to enable");
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:06:08 +02:00
|
|
|
let qualif = self.fn_like(fn_like.kind(),
|
|
|
|
fn_like.decl(),
|
|
|
|
fn_like.body(),
|
|
|
|
fn_like.span(),
|
|
|
|
fn_like.id());
|
|
|
|
self.add_qualif(qualif);
|
|
|
|
|
|
|
|
if ty::type_contents(self.tcx, ret_ty).interior_unsafe() {
|
2015-05-05 08:47:04 -04:00
|
|
|
self.add_qualif(ConstQualif::MUTABLE_MEM);
|
2015-02-25 22:06:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-29 13:57:06 +02:00
|
|
|
fn record_borrow(&mut self, id: ast::NodeId, mutbl: ast::Mutability) {
|
|
|
|
match self.rvalue_borrows.entry(id) {
|
|
|
|
Entry::Occupied(mut entry) => {
|
|
|
|
// Merge the two borrows, taking the most demanding
|
|
|
|
// one, mutability-wise.
|
|
|
|
if mutbl == ast::MutMutable {
|
|
|
|
entry.insert(mutbl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Entry::Vacant(entry) => {
|
|
|
|
entry.insert(mutbl);
|
|
|
|
}
|
|
|
|
}
|
2013-08-13 02:10:10 +02:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
|
|
|
|
fn msg(&self) -> &'static str {
|
|
|
|
match self.mode {
|
2015-01-29 13:57:06 +02:00
|
|
|
Mode::Const => "constant",
|
2015-02-25 22:06:08 +02:00
|
|
|
Mode::ConstFn => "constant function",
|
2015-01-29 13:57:06 +02:00
|
|
|
Mode::StaticMut | Mode::Static => "static",
|
|
|
|
Mode::Var => unreachable!(),
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_static_mut_type(&self, e: &ast::Expr) {
|
|
|
|
let node_ty = ty::node_id_to_type(self.tcx, e.id);
|
|
|
|
let tcontents = ty::type_contents(self.tcx, node_ty);
|
|
|
|
|
|
|
|
let suffix = if tcontents.has_dtor() {
|
|
|
|
"destructors"
|
|
|
|
} else if tcontents.owns_owned() {
|
2015-05-28 13:11:05 -05:00
|
|
|
"boxes"
|
2014-12-30 22:36:03 +02:00
|
|
|
} else {
|
|
|
|
return
|
|
|
|
};
|
|
|
|
|
2015-05-28 13:11:05 -05:00
|
|
|
span_err!(self.tcx.sess, e.span, E0397,
|
|
|
|
"mutable statics are not allowed to have {}", suffix);
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_static_type(&self, e: &ast::Expr) {
|
|
|
|
let ty = ty::node_id_to_type(self.tcx, e.id);
|
|
|
|
let infcx = infer::new_infer_ctxt(self.tcx);
|
Add a (somewhat hacky) cache to the tcx that tracks "global" trait refs
that are known to have been satisfied *somewhere*. This means that if
one fn finds that `SomeType: Foo`, then every other fn can just consider
that to hold.
Unfortunately, there are some complications:
1. If `SomeType: Foo` includes dependent conditions, those conditions
may trigger an error. This error will be repored in the first fn
where `SomeType: Foo` is evaluated, but not in the other fns, which
can lead to uneven error reporting (which is sometimes confusing).
2. This kind of caching can be unsound in the presence of
unsatisfiable where clauses. For example, suppose that the first fn
has a where-clause like `i32: Bar<u32>`, which in fact does not
hold. This will "fool" trait resolution into thinking that `i32:
Bar<u32>` holds. This is ok currently, because it means that the
first fn can never be calle (since its where clauses cannot be
satisfied), but if the first fn's successful resolution is cached, it
can allow other fns to compile that should not. This problem is fixed
in the next commit.
2015-06-09 17:02:18 -04:00
|
|
|
let mut fulfill_cx = traits::FulfillmentContext::new(false);
|
2014-12-30 22:36:03 +02:00
|
|
|
let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
|
|
|
|
fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
|
|
|
|
let env = ty::empty_parameter_environment(self.tcx);
|
|
|
|
match fulfill_cx.select_all_or_error(&infcx, &env) {
|
|
|
|
Ok(()) => { },
|
|
|
|
Err(ref errors) => {
|
|
|
|
traits::report_fulfillment_errors(&infcx, errors);
|
|
|
|
}
|
|
|
|
}
|
2013-08-13 02:10:10 +02:00
|
|
|
}
|
2014-09-12 13:10:30 +03:00
|
|
|
}
|
|
|
|
|
2014-09-10 01:54:36 +03:00
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
2014-09-13 20:10:34 +03:00
|
|
|
fn visit_item(&mut self, i: &ast::Item) {
|
2015-06-17 01:39:06 +03:00
|
|
|
debug!("visit_item(item={})", i.repr());
|
2015-01-04 17:58:56 +02:00
|
|
|
match i.node {
|
2014-12-30 22:36:03 +02:00
|
|
|
ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
|
|
|
|
self.check_static_type(&**expr);
|
2015-01-29 13:57:06 +02:00
|
|
|
self.global_expr(Mode::Static, &**expr);
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
ast::ItemStatic(_, ast::MutMutable, ref expr) => {
|
|
|
|
self.check_static_mut_type(&**expr);
|
2015-01-29 13:57:06 +02:00
|
|
|
self.global_expr(Mode::StaticMut, &**expr);
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
ast::ItemConst(_, ref expr) => {
|
2015-01-29 13:57:06 +02:00
|
|
|
self.global_expr(Mode::Const, &**expr);
|
2015-01-04 17:58:56 +02:00
|
|
|
}
|
|
|
|
ast::ItemEnum(ref enum_definition, _) => {
|
2015-01-29 13:57:06 +02:00
|
|
|
for var in &enum_definition.variants {
|
|
|
|
if let Some(ref ex) = var.node.disr_expr {
|
|
|
|
self.global_expr(Mode::Const, &**ex);
|
2015-01-04 17:58:56 +02:00
|
|
|
}
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
2015-01-04 17:58:56 +02:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
_ => {
|
2015-01-29 13:57:06 +02:00
|
|
|
self.with_mode(Mode::Var, |v| visit::walk_item(v, i));
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
2015-01-04 17:58:56 +02:00
|
|
|
}
|
2014-09-12 13:10:30 +03:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
|
2015-03-15 19:35:25 -06:00
|
|
|
fn visit_trait_item(&mut self, t: &'v ast::TraitItem) {
|
|
|
|
match t.node {
|
|
|
|
ast::ConstTraitItem(_, ref default) => {
|
|
|
|
if let Some(ref expr) = *default {
|
|
|
|
self.global_expr(Mode::Const, &*expr);
|
|
|
|
} else {
|
|
|
|
visit::walk_trait_item(self, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.with_mode(Mode::Var, |v| visit::walk_trait_item(v, t)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_impl_item(&mut self, i: &'v ast::ImplItem) {
|
|
|
|
match i.node {
|
|
|
|
ast::ConstImplItem(_, ref expr) => {
|
|
|
|
self.global_expr(Mode::Const, &*expr);
|
|
|
|
}
|
|
|
|
_ => self.with_mode(Mode::Var, |v| visit::walk_impl_item(v, i)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-29 13:57:06 +02:00
|
|
|
fn visit_fn(&mut self,
|
|
|
|
fk: visit::FnKind<'v>,
|
|
|
|
fd: &'v ast::FnDecl,
|
|
|
|
b: &'v ast::Block,
|
|
|
|
s: Span,
|
|
|
|
fn_id: ast::NodeId) {
|
2015-02-25 22:06:08 +02:00
|
|
|
self.fn_like(fk, fd, b, s, fn_id);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
|
2014-09-13 20:10:34 +03:00
|
|
|
fn visit_pat(&mut self, p: &ast::Pat) {
|
2015-01-29 13:57:06 +02:00
|
|
|
match p.node {
|
|
|
|
ast::PatLit(ref lit) => {
|
|
|
|
self.global_expr(Mode::Const, &**lit);
|
|
|
|
}
|
|
|
|
ast::PatRange(ref start, ref end) => {
|
|
|
|
self.global_expr(Mode::Const, &**start);
|
|
|
|
self.global_expr(Mode::Const, &**end);
|
|
|
|
}
|
|
|
|
_ => visit::walk_pat(self, p)
|
|
|
|
}
|
2014-09-12 13:10:30 +03:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
|
2015-02-25 22:06:08 +02:00
|
|
|
fn visit_block(&mut self, block: &ast::Block) {
|
|
|
|
// Check all statements in the block
|
|
|
|
for stmt in &block.stmts {
|
|
|
|
let span = match stmt.node {
|
|
|
|
ast::StmtDecl(ref decl, _) => {
|
|
|
|
match decl.node {
|
|
|
|
ast::DeclLocal(_) => decl.span,
|
|
|
|
|
|
|
|
// Item statements are allowed
|
|
|
|
ast::DeclItem(_) => continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast::StmtExpr(ref expr, _) => expr.span,
|
|
|
|
ast::StmtSemi(ref semi, _) => semi.span,
|
|
|
|
ast::StmtMac(..) => {
|
|
|
|
self.tcx.sess.span_bug(stmt.span, "unexpanded statement \
|
|
|
|
macro in const?!")
|
|
|
|
}
|
|
|
|
};
|
2015-05-05 08:47:04 -04:00
|
|
|
self.add_qualif(ConstQualif::NOT_CONST);
|
2015-02-25 22:06:08 +02:00
|
|
|
if self.mode != Mode::Var {
|
|
|
|
span_err!(self.tcx.sess, span, E0016,
|
|
|
|
"blocks in {}s are limited to items and \
|
|
|
|
tail expressions", self.msg());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
visit::walk_block(self, block);
|
|
|
|
}
|
|
|
|
|
2014-09-13 20:10:34 +03:00
|
|
|
fn visit_expr(&mut self, ex: &ast::Expr) {
|
2015-01-29 13:57:06 +02:00
|
|
|
let mut outer = self.qualif;
|
2015-04-29 14:58:43 -07:00
|
|
|
self.qualif = ConstQualif::empty();
|
2015-01-29 13:57:06 +02:00
|
|
|
|
|
|
|
let node_ty = ty::node_id_to_type(self.tcx, ex.id);
|
|
|
|
check_expr(self, ex, node_ty);
|
|
|
|
|
|
|
|
// Special-case some expressions to avoid certain flags bubbling up.
|
|
|
|
match ex.node {
|
|
|
|
ast::ExprCall(ref callee, ref args) => {
|
2015-06-10 17:22:20 +01:00
|
|
|
for arg in args {
|
2015-01-29 13:57:06 +02:00
|
|
|
self.visit_expr(&**arg)
|
|
|
|
}
|
|
|
|
|
|
|
|
let inner = self.qualif;
|
|
|
|
self.visit_expr(&**callee);
|
|
|
|
// The callee's size doesn't count in the call.
|
|
|
|
let added = self.qualif - inner;
|
2015-04-28 16:36:22 -07:00
|
|
|
self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
ast::ExprRepeat(ref element, _) => {
|
|
|
|
self.visit_expr(&**element);
|
|
|
|
// The count is checked elsewhere (typeck).
|
|
|
|
let count = match node_ty.sty {
|
2015-06-12 16:50:13 -07:00
|
|
|
ty::TyArray(_, n) => n,
|
2015-01-29 13:57:06 +02:00
|
|
|
_ => unreachable!()
|
|
|
|
};
|
|
|
|
// [element; 0] is always zero-sized.
|
|
|
|
if count == 0 {
|
2015-04-28 16:36:22 -07:00
|
|
|
self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast::ExprMatch(ref discr, ref arms, _) => {
|
|
|
|
// Compute the most demanding borrow from all the arms'
|
|
|
|
// patterns and set that on the discriminator.
|
|
|
|
let mut borrow = None;
|
2015-06-10 17:22:20 +01:00
|
|
|
for pat in arms.iter().flat_map(|arm| &arm.pats) {
|
2015-01-29 13:57:06 +02:00
|
|
|
let pat_borrow = self.rvalue_borrows.remove(&pat.id);
|
|
|
|
match (borrow, pat_borrow) {
|
|
|
|
(None, _) | (_, Some(ast::MutMutable)) => {
|
|
|
|
borrow = pat_borrow;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(mutbl) = borrow {
|
|
|
|
self.record_borrow(discr.id, mutbl);
|
|
|
|
}
|
|
|
|
visit::walk_expr(self, ex);
|
|
|
|
}
|
|
|
|
// Division by zero and overflow checking.
|
|
|
|
ast::ExprBinary(op, _, _) => {
|
|
|
|
visit::walk_expr(self, ex);
|
|
|
|
let div_or_rem = op.node == ast::BiDiv || op.node == ast::BiRem;
|
|
|
|
match node_ty.sty {
|
2015-06-11 16:21:46 -07:00
|
|
|
ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
|
2015-04-28 16:36:22 -07:00
|
|
|
if !self.qualif.intersects(ConstQualif::NOT_CONST) {
|
2015-01-29 13:57:06 +02:00
|
|
|
match const_eval::eval_const_expr_partial(self.tcx, ex, None) {
|
|
|
|
Ok(_) => {}
|
|
|
|
Err(msg) => {
|
2015-02-22 16:34:26 +01:00
|
|
|
span_err!(self.tcx.sess, msg.span, E0020,
|
|
|
|
"{} in a constant expression",
|
|
|
|
msg.description())
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => visit::walk_expr(self, ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle borrows on (or inside the autorefs of) this expression.
|
|
|
|
match self.rvalue_borrows.remove(&ex.id) {
|
|
|
|
Some(ast::MutImmutable) => {
|
|
|
|
// Constants cannot be borrowed if they contain interior mutability as
|
|
|
|
// it means that our "silent insertion of statics" could change
|
|
|
|
// initializer values (very bad).
|
2015-04-28 16:36:22 -07:00
|
|
|
// If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
|
2015-01-29 13:57:06 +02:00
|
|
|
// propagated from another error, so erroring again would be just noise.
|
|
|
|
let tc = ty::type_contents(self.tcx, node_ty);
|
2015-04-28 16:36:22 -07:00
|
|
|
if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
|
|
|
|
outer = outer | ConstQualif::NOT_CONST;
|
2015-01-29 13:57:06 +02:00
|
|
|
if self.mode != Mode::Var {
|
|
|
|
self.tcx.sess.span_err(ex.span,
|
|
|
|
"cannot borrow a constant which contains \
|
|
|
|
interior mutability, create a static instead");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If the reference has to be 'static, avoid in-place initialization
|
|
|
|
// as that will end up pointing to the stack instead.
|
2015-04-28 16:36:22 -07:00
|
|
|
if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
|
|
|
|
self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE;
|
|
|
|
self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(ast::MutMutable) => {
|
|
|
|
// `&mut expr` means expr could be mutated, unless it's zero-sized.
|
2015-04-28 16:36:22 -07:00
|
|
|
if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
|
2015-01-29 13:57:06 +02:00
|
|
|
if self.mode == Mode::Var {
|
2015-04-28 16:36:22 -07:00
|
|
|
outer = outer | ConstQualif::NOT_CONST;
|
|
|
|
self.add_qualif(ConstQualif::MUTABLE_MEM);
|
2015-01-29 13:57:06 +02:00
|
|
|
} else {
|
|
|
|
span_err!(self.tcx.sess, ex.span, E0017,
|
|
|
|
"references in {}s may only refer \
|
|
|
|
to immutable values", self.msg())
|
|
|
|
}
|
|
|
|
}
|
2015-04-28 16:36:22 -07:00
|
|
|
if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
|
|
|
|
self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2015-01-29 13:57:06 +02:00
|
|
|
self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
|
|
|
|
// Don't propagate certain flags.
|
2015-04-28 16:36:22 -07:00
|
|
|
self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
|
2013-08-13 02:10:10 +02:00
|
|
|
}
|
|
|
|
}
|
2011-11-08 18:26:02 -08:00
|
|
|
|
2014-12-30 22:36:03 +02:00
|
|
|
/// This function is used to enforce the constraints on
|
|
|
|
/// const/static items. It walks through the *value*
|
|
|
|
/// of the item walking down the expression and evaluating
|
|
|
|
/// every nested expression. If the expression is not part
|
2015-01-29 13:57:06 +02:00
|
|
|
/// of a const/static item, it is qualified for promotion
|
|
|
|
/// instead of producing errors.
|
|
|
|
fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|
|
|
e: &ast::Expr, node_ty: Ty<'tcx>) {
|
2014-12-30 22:36:03 +02:00
|
|
|
match node_ty.sty {
|
2015-06-11 16:21:46 -07:00
|
|
|
ty::TyStruct(did, _) |
|
|
|
|
ty::TyEnum(did, _) if ty::has_dtor(v.tcx, did) => {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NEEDS_DROP);
|
2015-01-29 13:57:06 +02:00
|
|
|
if v.mode != Mode::Var {
|
|
|
|
v.tcx.sess.span_err(e.span,
|
|
|
|
&format!("{}s are not allowed to have destructors",
|
2015-02-20 14:08:14 -05:00
|
|
|
v.msg()));
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
2015-01-29 13:57:06 +02:00
|
|
|
let method_call = ty::MethodCall::expr(e.id);
|
2014-10-06 21:32:58 -07:00
|
|
|
match e.node {
|
2015-01-29 13:57:06 +02:00
|
|
|
ast::ExprUnary(..) |
|
|
|
|
ast::ExprBinary(..) |
|
|
|
|
ast::ExprIndex(..) if v.tcx.method_map.borrow().contains_key(&method_call) => {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
2015-01-29 13:57:06 +02:00
|
|
|
if v.mode != Mode::Var {
|
|
|
|
span_err!(v.tcx.sess, e.span, E0011,
|
|
|
|
"user-defined operators are not allowed in {}s", v.msg());
|
|
|
|
}
|
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
ast::ExprBox(..) |
|
2014-09-13 20:10:34 +03:00
|
|
|
ast::ExprUnary(ast::UnUniq, _) => {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
2015-01-29 13:57:06 +02:00
|
|
|
if v.mode != Mode::Var {
|
|
|
|
span_err!(v.tcx.sess, e.span, E0010,
|
|
|
|
"allocations are not allowed in {}s", v.msg());
|
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2015-05-28 00:35:56 +03:00
|
|
|
ast::ExprUnary(op, ref inner) => {
|
|
|
|
match ty::node_id_to_type(v.tcx, inner.id).sty {
|
2015-06-11 16:21:46 -07:00
|
|
|
ty::TyRawPtr(_) => {
|
2015-05-28 00:35:56 +03:00
|
|
|
assert!(op == ast::UnDeref);
|
|
|
|
|
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
|
|
|
if v.mode != Mode::Var {
|
|
|
|
span_err!(v.tcx.sess, e.span, E0396,
|
|
|
|
"raw pointers cannot be dereferenced in {}s", v.msg());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast::ExprBinary(op, ref lhs, _) => {
|
|
|
|
match ty::node_id_to_type(v.tcx, lhs.id).sty {
|
2015-06-11 16:21:46 -07:00
|
|
|
ty::TyRawPtr(_) => {
|
2015-05-28 00:35:56 +03:00
|
|
|
assert!(op.node == ast::BiEq || op.node == ast::BiNe ||
|
|
|
|
op.node == ast::BiLe || op.node == ast::BiLt ||
|
|
|
|
op.node == ast::BiGe || op.node == ast::BiGt);
|
|
|
|
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
2015-05-28 00:35:56 +03:00
|
|
|
if v.mode != Mode::Var {
|
|
|
|
span_err!(v.tcx.sess, e.span, E0395,
|
|
|
|
"raw pointers cannot be compared in {}s", v.msg());
|
|
|
|
}
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
_ => {}
|
2012-01-26 12:26:14 +01:00
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2014-09-13 20:10:34 +03:00
|
|
|
ast::ExprCast(ref from, _) => {
|
2015-05-05 19:36:47 +03:00
|
|
|
debug!("Checking const cast(id={})", from.id);
|
|
|
|
match v.tcx.cast_kinds.borrow().get(&from.id) {
|
|
|
|
None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
|
2015-05-14 15:04:49 +03:00
|
|
|
Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
|
2015-05-05 19:36:47 +03:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
|
|
|
if v.mode != Mode::Var {
|
|
|
|
span_err!(v.tcx.sess, e.span, E0018,
|
2015-05-28 00:35:56 +03:00
|
|
|
"raw pointers cannot be cast to integers in {}s", v.msg());
|
2015-05-05 19:36:47 +03:00
|
|
|
}
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
2015-05-05 19:36:47 +03:00
|
|
|
_ => {}
|
2012-03-14 18:04:03 +01:00
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2015-02-17 19:29:13 +02:00
|
|
|
ast::ExprPath(..) => {
|
2015-02-17 06:44:23 +02:00
|
|
|
let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
|
2015-01-29 13:57:06 +02:00
|
|
|
match def {
|
|
|
|
Some(def::DefVariant(_, _, _)) => {
|
|
|
|
// Count the discriminator or function pointer.
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NON_ZERO_SIZED);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
Some(def::DefStruct(_)) => {
|
2015-06-11 16:21:46 -07:00
|
|
|
if let ty::TyBareFn(..) = node_ty.sty {
|
2015-01-29 13:57:06 +02:00
|
|
|
// Count the function pointer.
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NON_ZERO_SIZED);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
2015-02-11 09:32:25 +02:00
|
|
|
Some(def::DefFn(..)) | Some(def::DefMethod(..)) => {
|
2015-01-29 13:57:06 +02:00
|
|
|
// Count the function pointer.
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NON_ZERO_SIZED);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
Some(def::DefStatic(..)) => {
|
|
|
|
match v.mode {
|
|
|
|
Mode::Static | Mode::StaticMut => {}
|
2015-02-25 22:06:08 +02:00
|
|
|
Mode::Const | Mode::ConstFn => {
|
2015-01-29 13:57:06 +02:00
|
|
|
span_err!(v.tcx.sess, e.span, E0013,
|
2015-02-25 22:06:08 +02:00
|
|
|
"{}s cannot refer to other statics, insert \
|
|
|
|
an intermediate constant instead", v.msg());
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
2015-04-28 16:36:22 -07:00
|
|
|
Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
2015-03-15 19:35:25 -06:00
|
|
|
Some(def::DefConst(did)) |
|
|
|
|
Some(def::DefAssociatedConst(did, _)) => {
|
|
|
|
if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
|
|
|
|
Some(e.id)) {
|
2015-01-29 13:57:06 +02:00
|
|
|
let inner = v.global_expr(Mode::Const, expr);
|
|
|
|
v.add_qualif(inner);
|
|
|
|
} else {
|
2015-03-15 19:35:25 -06:00
|
|
|
v.tcx.sess.span_bug(e.span,
|
|
|
|
"DefConst or DefAssociatedConst \
|
|
|
|
doesn't point to a constant");
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
2015-02-25 22:06:08 +02:00
|
|
|
Some(def::DefLocal(_)) if v.mode == Mode::ConstFn => {
|
|
|
|
// Sadly, we can't determine whether the types are zero-sized.
|
2015-05-05 08:47:04 -04:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
|
2015-02-25 22:06:08 +02:00
|
|
|
}
|
2015-01-04 17:58:56 +02:00
|
|
|
def => {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
2015-01-29 13:57:06 +02:00
|
|
|
if v.mode != Mode::Var {
|
|
|
|
debug!("(checking const) found bad def: {:?}", def);
|
|
|
|
span_err!(v.tcx.sess, e.span, E0014,
|
|
|
|
"paths in {}s may only refer to constants \
|
|
|
|
or functions", v.msg());
|
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2012-11-30 16:29:28 -08:00
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2014-09-13 20:10:34 +03:00
|
|
|
ast::ExprCall(ref callee, _) => {
|
2015-01-29 13:57:06 +02:00
|
|
|
let mut callee = &**callee;
|
|
|
|
loop {
|
|
|
|
callee = match callee.node {
|
|
|
|
ast::ExprParen(ref inner) => &**inner,
|
|
|
|
ast::ExprBlock(ref block) => match block.expr {
|
|
|
|
Some(ref tail) => &**tail,
|
|
|
|
None => break
|
|
|
|
},
|
|
|
|
_ => break
|
|
|
|
};
|
|
|
|
}
|
2015-02-17 06:44:23 +02:00
|
|
|
let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
|
2015-02-25 22:06:08 +02:00
|
|
|
let is_const = match def {
|
|
|
|
Some(def::DefStruct(..)) => true,
|
2015-01-29 13:57:06 +02:00
|
|
|
Some(def::DefVariant(..)) => {
|
|
|
|
// Count the discriminator.
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NON_ZERO_SIZED);
|
2015-02-25 22:06:08 +02:00
|
|
|
true
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
2015-02-25 22:06:08 +02:00
|
|
|
Some(def::DefMethod(did, def::FromImpl(_))) |
|
|
|
|
Some(def::DefFn(did, _)) => {
|
2015-05-28 11:22:00 -04:00
|
|
|
v.handle_const_fn_call(e, did, node_ty)
|
2015-02-25 22:06:08 +02:00
|
|
|
}
|
|
|
|
_ => false
|
|
|
|
};
|
|
|
|
if !is_const {
|
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
|
|
|
if v.mode != Mode::Var {
|
|
|
|
span_err!(v.tcx.sess, e.span, E0015,
|
|
|
|
"function calls in {}s are limited to \
|
|
|
|
constant functions, \
|
|
|
|
struct and enum constructors", v.msg());
|
2012-11-30 16:29:28 -08:00
|
|
|
}
|
2012-04-04 15:02:25 -07:00
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2015-02-25 22:06:08 +02:00
|
|
|
ast::ExprMethodCall(..) => {
|
2015-05-05 08:47:04 -04:00
|
|
|
let method_did = match v.tcx.method_map.borrow()[&method_call].origin {
|
2015-02-25 22:06:08 +02:00
|
|
|
ty::MethodStatic(did) => Some(did),
|
|
|
|
_ => None
|
2015-01-29 13:57:06 +02:00
|
|
|
};
|
2015-02-25 22:06:08 +02:00
|
|
|
let is_const = match method_did {
|
2015-05-28 11:22:00 -04:00
|
|
|
Some(did) => v.handle_const_fn_call(e, did, node_ty),
|
2015-02-25 22:06:08 +02:00
|
|
|
None => false
|
|
|
|
};
|
|
|
|
if !is_const {
|
2015-05-05 08:47:04 -04:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
2015-02-25 22:06:08 +02:00
|
|
|
if v.mode != Mode::Var {
|
2015-05-05 08:47:04 -04:00
|
|
|
span_err!(v.tcx.sess, e.span, E0378,
|
2015-02-25 22:06:08 +02:00
|
|
|
"method calls in {}s are limited to \
|
|
|
|
constant inherent methods", v.msg());
|
2014-05-04 10:39:11 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2015-01-29 13:57:06 +02:00
|
|
|
ast::ExprStruct(..) => {
|
|
|
|
let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
|
|
|
|
if did == v.tcx.lang_items.unsafe_cell_type() {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::MUTABLE_MEM);
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprLit(_) |
|
2015-01-29 13:57:06 +02:00
|
|
|
ast::ExprAddrOf(..) => {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NON_ZERO_SIZED);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprRepeat(..) => {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::PREFER_IN_PLACE);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprClosure(..) => {
|
2015-02-25 22:06:08 +02:00
|
|
|
// Paths in constant contexts cannot refer to local variables,
|
2015-01-29 13:57:06 +02:00
|
|
|
// as there are none, and thus closures can't have upvars there.
|
|
|
|
if ty::with_freevars(v.tcx, e.id, |fv| !fv.is_empty()) {
|
|
|
|
assert!(v.mode == Mode::Var,
|
|
|
|
"global closures can't capture anything");
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-25 22:06:08 +02:00
|
|
|
ast::ExprBlock(_) |
|
2015-01-29 13:57:06 +02:00
|
|
|
ast::ExprIndex(..) |
|
2014-09-13 20:10:34 +03:00
|
|
|
ast::ExprField(..) |
|
|
|
|
ast::ExprTupField(..) |
|
2015-01-29 13:57:06 +02:00
|
|
|
ast::ExprVec(_) |
|
|
|
|
ast::ExprParen(..) |
|
|
|
|
ast::ExprTup(..) => {}
|
2014-09-13 20:10:34 +03:00
|
|
|
|
2014-12-30 22:36:03 +02:00
|
|
|
// Conditional control flow (possible to implement).
|
|
|
|
ast::ExprMatch(..) |
|
|
|
|
ast::ExprIf(..) |
|
|
|
|
ast::ExprIfLet(..) |
|
2014-10-06 21:32:58 -07:00
|
|
|
|
2014-12-30 22:36:03 +02:00
|
|
|
// Loops (not very meaningful in constants).
|
|
|
|
ast::ExprWhile(..) |
|
|
|
|
ast::ExprWhileLet(..) |
|
|
|
|
ast::ExprForLoop(..) |
|
|
|
|
ast::ExprLoop(..) |
|
|
|
|
|
|
|
|
// More control flow (also not very meaningful).
|
|
|
|
ast::ExprBreak(_) |
|
|
|
|
ast::ExprAgain(_) |
|
|
|
|
ast::ExprRet(_) |
|
|
|
|
|
|
|
|
// Miscellaneous expressions that could be implemented.
|
|
|
|
ast::ExprRange(..) |
|
|
|
|
|
2015-02-25 22:06:08 +02:00
|
|
|
// Expressions with side-effects.
|
2014-12-30 22:36:03 +02:00
|
|
|
ast::ExprAssign(..) |
|
|
|
|
ast::ExprAssignOp(..) |
|
|
|
|
ast::ExprInlineAsm(_) |
|
|
|
|
ast::ExprMac(_) => {
|
2015-04-28 16:36:22 -07:00
|
|
|
v.add_qualif(ConstQualif::NOT_CONST);
|
2015-01-29 13:57:06 +02:00
|
|
|
if v.mode != Mode::Var {
|
|
|
|
span_err!(v.tcx.sess, e.span, E0019,
|
|
|
|
"{} contains unimplemented expression type", v.msg());
|
|
|
|
}
|
2014-10-06 21:32:58 -07:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
|
|
|
}
|
2013-11-16 01:58:51 -05:00
|
|
|
|
2014-12-30 22:36:03 +02:00
|
|
|
pub fn check_crate(tcx: &ty::ctxt) {
|
|
|
|
visit::walk_crate(&mut CheckCrateVisitor {
|
|
|
|
tcx: tcx,
|
2015-01-29 13:57:06 +02:00
|
|
|
mode: Mode::Var,
|
2015-04-28 16:36:22 -07:00
|
|
|
qualif: ConstQualif::NOT_CONST,
|
2015-01-29 13:57:06 +02:00
|
|
|
rvalue_borrows: NodeMap()
|
2014-12-30 22:36:03 +02:00
|
|
|
}, tcx.map.krate());
|
|
|
|
|
|
|
|
tcx.sess.abort_if_errors();
|
|
|
|
}
|
|
|
|
|
2015-01-29 13:57:06 +02:00
|
|
|
impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
2014-12-30 22:36:03 +02:00
|
|
|
fn consume(&mut self,
|
|
|
|
_consume_id: ast::NodeId,
|
|
|
|
consume_span: Span,
|
|
|
|
cmt: mc::cmt,
|
|
|
|
_mode: euv::ConsumeMode) {
|
|
|
|
let mut cur = &cmt;
|
|
|
|
loop {
|
|
|
|
match cur.cat {
|
|
|
|
mc::cat_static_item => {
|
2015-01-29 13:57:06 +02:00
|
|
|
if self.mode != Mode::Var {
|
|
|
|
// statics cannot be consumed by value at any time, that would imply
|
|
|
|
// that they're an initializer (what a const is for) or kept in sync
|
|
|
|
// over time (not feasible), so deny it outright.
|
2015-05-19 16:53:34 -05:00
|
|
|
span_err!(self.tcx.sess, consume_span, E0394,
|
|
|
|
"cannot refer to other statics by value, use the \
|
|
|
|
address-of operator or a constant instead");
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|
2014-12-30 22:36:03 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
mc::cat_deref(ref cmt, _, _) |
|
|
|
|
mc::cat_downcast(ref cmt, _) |
|
|
|
|
mc::cat_interior(ref cmt, _) => cur = cmt,
|
|
|
|
|
|
|
|
mc::cat_rvalue(..) |
|
|
|
|
mc::cat_upvar(..) |
|
|
|
|
mc::cat_local(..) => break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn borrow(&mut self,
|
2015-01-29 13:57:06 +02:00
|
|
|
borrow_id: ast::NodeId,
|
2014-12-30 22:36:03 +02:00
|
|
|
borrow_span: Span,
|
|
|
|
cmt: mc::cmt<'tcx>,
|
|
|
|
_loan_region: ty::Region,
|
2015-01-29 13:57:06 +02:00
|
|
|
bk: ty::BorrowKind,
|
2015-04-08 04:31:51 -04:00
|
|
|
loan_cause: euv::LoanCause)
|
|
|
|
{
|
|
|
|
// Kind of hacky, but we allow Unsafe coercions in constants.
|
|
|
|
// These occur when we convert a &T or *T to a *U, as well as
|
|
|
|
// when making a thin pointer (e.g., `*T`) into a fat pointer
|
|
|
|
// (e.g., `*Trait`).
|
|
|
|
match loan_cause {
|
|
|
|
euv::LoanCause::AutoUnsafe => {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_ => { }
|
|
|
|
}
|
|
|
|
|
2014-12-30 22:36:03 +02:00
|
|
|
let mut cur = &cmt;
|
|
|
|
let mut is_interior = false;
|
|
|
|
loop {
|
|
|
|
match cur.cat {
|
|
|
|
mc::cat_rvalue(..) => {
|
2015-01-29 13:57:06 +02:00
|
|
|
if loan_cause == euv::MatchDiscriminant {
|
|
|
|
// Ignore the dummy immutable borrow created by EUV.
|
|
|
|
break;
|
2014-12-30 22:36:03 +02:00
|
|
|
}
|
2015-01-29 13:57:06 +02:00
|
|
|
let mutbl = bk.to_mutbl_lossy();
|
|
|
|
if mutbl == ast::MutMutable && self.mode == Mode::StaticMut {
|
2015-06-12 16:50:13 -07:00
|
|
|
// Mutable slices are the only `&mut` allowed in
|
|
|
|
// globals, but only in `static mut`, nowhere else.
|
|
|
|
// FIXME: This exception is really weird... there isn't
|
|
|
|
// any fundamental reason to restrict this based on
|
|
|
|
// type of the expression. `&mut [1]` has exactly the
|
|
|
|
// same representation as &mut 1.
|
2015-01-29 13:57:06 +02:00
|
|
|
match cmt.ty.sty {
|
2015-06-12 16:50:13 -07:00
|
|
|
ty::TyArray(_, _) | ty::TySlice(_) => break,
|
2015-01-29 13:57:06 +02:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.record_borrow(borrow_id, mutbl);
|
2014-12-30 22:36:03 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
mc::cat_static_item => {
|
2015-01-29 13:57:06 +02:00
|
|
|
if is_interior && self.mode != Mode::Var {
|
2014-12-30 22:36:03 +02:00
|
|
|
// Borrowed statics can specifically *only* have their address taken,
|
|
|
|
// not any number of other borrows such as borrowing fields, reading
|
|
|
|
// elements of an array, etc.
|
|
|
|
self.tcx.sess.span_err(borrow_span,
|
|
|
|
"cannot refer to the interior of another \
|
|
|
|
static, use a constant instead");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mc::cat_deref(ref cmt, _, _) |
|
|
|
|
mc::cat_downcast(ref cmt, _) |
|
|
|
|
mc::cat_interior(ref cmt, _) => {
|
|
|
|
is_interior = true;
|
|
|
|
cur = cmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
mc::cat_upvar(..) |
|
|
|
|
mc::cat_local(..) => break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn decl_without_init(&mut self,
|
|
|
|
_id: ast::NodeId,
|
|
|
|
_span: Span) {}
|
|
|
|
fn mutate(&mut self,
|
|
|
|
_assignment_id: ast::NodeId,
|
|
|
|
_assignment_span: Span,
|
|
|
|
_assignee_cmt: mc::cmt,
|
|
|
|
_mode: euv::MutateMode) {}
|
|
|
|
|
|
|
|
fn matched_pat(&mut self,
|
|
|
|
_: &ast::Pat,
|
|
|
|
_: mc::cmt,
|
|
|
|
_: euv::MatchMode) {}
|
|
|
|
|
|
|
|
fn consume_pat(&mut self,
|
|
|
|
_consume_pat: &ast::Pat,
|
|
|
|
_cmt: mc::cmt,
|
|
|
|
_mode: euv::ConsumeMode) {}
|
2015-01-29 13:57:06 +02:00
|
|
|
}
|