rustc: qualify expressions in check_const for potential promotion.
This commit is contained in:
parent
7be460ff37
commit
03295a715f
@ -59,6 +59,7 @@
|
|||||||
E0010,
|
E0010,
|
||||||
E0011,
|
E0011,
|
||||||
E0012,
|
E0012,
|
||||||
|
E0013,
|
||||||
E0014,
|
E0014,
|
||||||
E0015,
|
E0015,
|
||||||
E0016,
|
E0016,
|
||||||
|
@ -143,6 +143,7 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f
|
|||||||
tag_table_upvar_capture_map = 0x56,
|
tag_table_upvar_capture_map = 0x56,
|
||||||
tag_table_capture_modes = 0x57,
|
tag_table_capture_modes = 0x57,
|
||||||
tag_table_object_cast_map = 0x58,
|
tag_table_object_cast_map = 0x58,
|
||||||
|
tag_table_const_qualif = 0x59,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const tag_item_trait_item_sort: uint = 0x60;
|
pub const tag_item_trait_item_sort: uint = 0x60;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter};
|
use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter};
|
||||||
use metadata::tydecode::{RegionParameter, ClosureSource};
|
use metadata::tydecode::{RegionParameter, ClosureSource};
|
||||||
use metadata::tyencode;
|
use metadata::tyencode;
|
||||||
|
use middle::check_const::ConstQualif;
|
||||||
use middle::mem_categorization::Typer;
|
use middle::mem_categorization::Typer;
|
||||||
use middle::subst;
|
use middle::subst;
|
||||||
use middle::subst::VecPerParamSpace;
|
use middle::subst::VecPerParamSpace;
|
||||||
@ -1306,6 +1307,15 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for &qualif in tcx.const_qualif_map.borrow().get(&id).iter() {
|
||||||
|
rbml_w.tag(c::tag_table_const_qualif, |rbml_w| {
|
||||||
|
rbml_w.id(id);
|
||||||
|
rbml_w.tag(c::tag_table_val, |rbml_w| {
|
||||||
|
qualif.encode(rbml_w).unwrap()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait doc_decoder_helpers {
|
trait doc_decoder_helpers {
|
||||||
@ -1920,6 +1930,10 @@ fn decode_side_tables(dcx: &DecodeContext,
|
|||||||
dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id),
|
dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id),
|
||||||
closure_kind);
|
closure_kind);
|
||||||
}
|
}
|
||||||
|
c::tag_table_const_qualif => {
|
||||||
|
let qualif: ConstQualif = Decodable::decode(val_dsr).unwrap();
|
||||||
|
dcx.tcx.const_qualif_map.borrow_mut().insert(id, qualif);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
dcx.tcx.sess.bug(
|
dcx.tcx.sess.bug(
|
||||||
&format!("unknown tag found in side tables: {:x}",
|
&format!("unknown tag found in side tables: {:x}",
|
||||||
|
@ -24,14 +24,14 @@
|
|||||||
// - It's not possible to take the address of a static item with unsafe interior. This is enforced
|
// - It's not possible to take the address of a static item with unsafe interior. This is enforced
|
||||||
// by borrowck::gather_loans
|
// by borrowck::gather_loans
|
||||||
|
|
||||||
use self::Mode::*;
|
use middle::const_eval;
|
||||||
|
|
||||||
use middle::def;
|
use middle::def;
|
||||||
use middle::expr_use_visitor as euv;
|
use middle::expr_use_visitor as euv;
|
||||||
use middle::infer;
|
use middle::infer;
|
||||||
use middle::mem_categorization as mc;
|
use middle::mem_categorization as mc;
|
||||||
use middle::traits;
|
use middle::traits;
|
||||||
use middle::ty;
|
use middle::ty::{self, Ty};
|
||||||
|
use util::nodemap::NodeMap;
|
||||||
use util::ppaux;
|
use util::ppaux;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
@ -39,34 +39,127 @@
|
|||||||
use syntax::print::pprust;
|
use syntax::print::pprust;
|
||||||
use syntax::visit::{self, Visitor};
|
use syntax::visit::{self, Visitor};
|
||||||
|
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
|
// Const qualification, from partial to completely promotable.
|
||||||
|
bitflags! {
|
||||||
|
#[derive(RustcEncodable, RustcDecodable)]
|
||||||
|
flags ConstQualif: u8 {
|
||||||
|
// Const rvalue which can be placed behind a reference.
|
||||||
|
const PURE_CONST = 0b000000,
|
||||||
|
// Inner mutability (can not be placed behind a reference) or behind
|
||||||
|
// &mut in a non-global expression. Can be copied from static memory.
|
||||||
|
const MUTABLE_MEM = 0b000001,
|
||||||
|
// Constant value with a type that implements Drop. Can be copied
|
||||||
|
// from static memory, similar to MUTABLE_MEM.
|
||||||
|
const NEEDS_DROP = 0b000010,
|
||||||
|
// 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.
|
||||||
|
const PREFER_IN_PLACE = 0b000100,
|
||||||
|
// 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.
|
||||||
|
const NON_ZERO_SIZED = 0b001000,
|
||||||
|
// 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.
|
||||||
|
const HAS_STATIC_BORROWS = 0b010000,
|
||||||
|
// Invalid const for miscellaneous reasons (e.g. not implemented).
|
||||||
|
const NOT_CONST = 0b100000,
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
const NON_STATIC_BORROWS = MUTABLE_MEM.bits | NEEDS_DROP.bits | NOT_CONST.bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Eq, PartialEq)]
|
#[derive(Copy, Eq, PartialEq)]
|
||||||
enum Mode {
|
enum Mode {
|
||||||
InConstant,
|
Const,
|
||||||
InStatic,
|
Static,
|
||||||
InStaticMut,
|
StaticMut,
|
||||||
InNothing,
|
|
||||||
|
// 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CheckCrateVisitor<'a, 'tcx: 'a> {
|
struct CheckCrateVisitor<'a, 'tcx: 'a> {
|
||||||
tcx: &'a ty::ctxt<'tcx>,
|
tcx: &'a ty::ctxt<'tcx>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
qualif: ConstQualif,
|
||||||
|
rvalue_borrows: NodeMap<ast::Mutability>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
||||||
fn with_mode<F>(&mut self, mode: Mode, f: F) where
|
fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
|
||||||
F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>),
|
F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
|
||||||
{
|
{
|
||||||
let old = self.mode;
|
let (old_mode, old_qualif) = (self.mode, self.qualif);
|
||||||
self.mode = mode;
|
self.mode = mode;
|
||||||
f(self);
|
self.qualif = PURE_CONST;
|
||||||
self.mode = old;
|
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.
|
||||||
|
entry.insert(PURE_CONST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.with_mode(mode, |this| {
|
||||||
|
this.with_euv(None, |euv| euv.consume_expr(expr));
|
||||||
|
this.visit_expr(expr);
|
||||||
|
this.qualif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_qualif(&mut self, qualif: ConstQualif) {
|
||||||
|
self.qualif = self.qualif | qualif;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg(&self) -> &'static str {
|
fn msg(&self) -> &'static str {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
InConstant => "constant",
|
Mode::Const => "constant",
|
||||||
InStaticMut | InStatic => "static",
|
Mode::StaticMut | Mode::Static => "static",
|
||||||
InNothing => unreachable!(),
|
Mode::Var => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,43 +201,169 @@ fn visit_item(&mut self, i: &ast::Item) {
|
|||||||
match i.node {
|
match i.node {
|
||||||
ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
|
ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
|
||||||
self.check_static_type(&**expr);
|
self.check_static_type(&**expr);
|
||||||
self.with_mode(InStatic, |v| v.visit_expr(&**expr));
|
self.global_expr(Mode::Static, &**expr);
|
||||||
}
|
}
|
||||||
ast::ItemStatic(_, ast::MutMutable, ref expr) => {
|
ast::ItemStatic(_, ast::MutMutable, ref expr) => {
|
||||||
self.check_static_mut_type(&**expr);
|
self.check_static_mut_type(&**expr);
|
||||||
self.with_mode(InStaticMut, |v| v.visit_expr(&**expr));
|
self.global_expr(Mode::StaticMut, &**expr);
|
||||||
}
|
}
|
||||||
ast::ItemConst(_, ref expr) => {
|
ast::ItemConst(_, ref expr) => {
|
||||||
self.with_mode(InConstant, |v| v.visit_expr(&**expr));
|
self.global_expr(Mode::Const, &**expr);
|
||||||
}
|
}
|
||||||
ast::ItemEnum(ref enum_definition, _) => {
|
ast::ItemEnum(ref enum_definition, _) => {
|
||||||
self.with_mode(InConstant, |v| {
|
for var in &enum_definition.variants {
|
||||||
for var in &enum_definition.variants {
|
if let Some(ref ex) = var.node.disr_expr {
|
||||||
if let Some(ref ex) = var.node.disr_expr {
|
self.global_expr(Mode::Const, &**ex);
|
||||||
v.visit_expr(&**ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.with_mode(InNothing, |v| visit::walk_item(v, i));
|
self.with_mode(Mode::Var, |v| visit::walk_item(v, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_fn(&mut self,
|
||||||
|
fk: visit::FnKind<'v>,
|
||||||
|
fd: &'v ast::FnDecl,
|
||||||
|
b: &'v ast::Block,
|
||||||
|
s: Span,
|
||||||
|
fn_id: ast::NodeId) {
|
||||||
|
assert!(self.mode == Mode::Var);
|
||||||
|
self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
|
||||||
|
visit::walk_fn(self, fk, fd, b, s);
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_pat(&mut self, p: &ast::Pat) {
|
fn visit_pat(&mut self, p: &ast::Pat) {
|
||||||
let mode = match p.node {
|
match p.node {
|
||||||
ast::PatLit(_) | ast::PatRange(..) => InConstant,
|
ast::PatLit(ref lit) => {
|
||||||
_ => InNothing
|
self.global_expr(Mode::Const, &**lit);
|
||||||
};
|
}
|
||||||
self.with_mode(mode, |v| visit::walk_pat(v, p))
|
ast::PatRange(ref start, ref end) => {
|
||||||
|
self.global_expr(Mode::Const, &**start);
|
||||||
|
self.global_expr(Mode::Const, &**end);
|
||||||
|
}
|
||||||
|
_ => visit::walk_pat(self, p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, ex: &ast::Expr) {
|
fn visit_expr(&mut self, ex: &ast::Expr) {
|
||||||
if self.mode != InNothing {
|
let mut outer = self.qualif;
|
||||||
check_expr(self, ex);
|
self.qualif = PURE_CONST;
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
for arg in args.iter() {
|
||||||
|
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;
|
||||||
|
self.qualif = inner | (added - NON_ZERO_SIZED);
|
||||||
|
}
|
||||||
|
ast::ExprRepeat(ref element, _) => {
|
||||||
|
self.visit_expr(&**element);
|
||||||
|
// The count is checked elsewhere (typeck).
|
||||||
|
let count = match node_ty.sty {
|
||||||
|
ty::ty_vec(_, Some(n)) => n,
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
// [element; 0] is always zero-sized.
|
||||||
|
if count == 0 {
|
||||||
|
self.qualif = self.qualif - (NON_ZERO_SIZED | PREFER_IN_PLACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
for pat in arms.iter().flat_map(|arm| arm.pats.iter()) {
|
||||||
|
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 {
|
||||||
|
ty::ty_uint(_) | ty::ty_int(_) if div_or_rem => {
|
||||||
|
if !self.qualif.intersects(NOT_CONST) {
|
||||||
|
match const_eval::eval_const_expr_partial(self.tcx, ex, None) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(msg) => {
|
||||||
|
span_err!(self.tcx.sess, ex.span, E0020,
|
||||||
|
"{} in a constant expression", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => visit::walk_expr(self, ex)
|
||||||
}
|
}
|
||||||
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).
|
||||||
|
// If the type doesn't have interior mutability, then `MUTABLE_MEM` has
|
||||||
|
// propagated from another error, so erroring again would be just noise.
|
||||||
|
let tc = ty::type_contents(self.tcx, node_ty);
|
||||||
|
if self.qualif.intersects(MUTABLE_MEM) && tc.interior_unsafe() {
|
||||||
|
outer = outer | NOT_CONST;
|
||||||
|
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.
|
||||||
|
if !self.qualif.intersects(NON_STATIC_BORROWS) {
|
||||||
|
self.qualif = self.qualif - PREFER_IN_PLACE;
|
||||||
|
self.add_qualif(HAS_STATIC_BORROWS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ast::MutMutable) => {
|
||||||
|
// `&mut expr` means expr could be mutated, unless it's zero-sized.
|
||||||
|
if self.qualif.intersects(NON_ZERO_SIZED) {
|
||||||
|
if self.mode == Mode::Var {
|
||||||
|
outer = outer | NOT_CONST;
|
||||||
|
self.add_qualif(MUTABLE_MEM);
|
||||||
|
} else {
|
||||||
|
span_err!(self.tcx.sess, ex.span, E0017,
|
||||||
|
"references in {}s may only refer \
|
||||||
|
to immutable values", self.msg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.qualif.intersects(NON_STATIC_BORROWS) {
|
||||||
|
self.add_qualif(HAS_STATIC_BORROWS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
|
||||||
|
// Don't propagate certain flags.
|
||||||
|
self.qualif = outer | (self.qualif - HAS_STATIC_BORROWS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,32 +371,49 @@ fn visit_expr(&mut self, ex: &ast::Expr) {
|
|||||||
/// const/static items. It walks through the *value*
|
/// const/static items. It walks through the *value*
|
||||||
/// of the item walking down the expression and evaluating
|
/// of the item walking down the expression and evaluating
|
||||||
/// every nested expression. If the expression is not part
|
/// every nested expression. If the expression is not part
|
||||||
/// of a const/static item, this function does nothing but
|
/// of a const/static item, it is qualified for promotion
|
||||||
/// walking down through it.
|
/// instead of producing errors.
|
||||||
fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) {
|
fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
||||||
let node_ty = ty::node_id_to_type(v.tcx, e.id);
|
e: &ast::Expr, node_ty: Ty<'tcx>) {
|
||||||
|
|
||||||
match node_ty.sty {
|
match node_ty.sty {
|
||||||
ty::ty_struct(did, _) |
|
ty::ty_struct(did, _) |
|
||||||
ty::ty_enum(did, _) if ty::has_dtor(v.tcx, did) => {
|
ty::ty_enum(did, _) if ty::has_dtor(v.tcx, did) => {
|
||||||
v.tcx.sess.span_err(e.span,
|
v.add_qualif(NEEDS_DROP);
|
||||||
&format!("{}s are not allowed to have destructors",
|
if v.mode != Mode::Var {
|
||||||
v.msg())[])
|
v.tcx.sess.span_err(e.span,
|
||||||
|
&format!("{}s are not allowed to have destructors",
|
||||||
|
v.msg())[]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let method_call = ty::MethodCall::expr(e.id);
|
||||||
match e.node {
|
match e.node {
|
||||||
|
ast::ExprUnary(..) |
|
||||||
|
ast::ExprBinary(..) |
|
||||||
|
ast::ExprIndex(..) if v.tcx.method_map.borrow().contains_key(&method_call) => {
|
||||||
|
v.add_qualif(NOT_CONST);
|
||||||
|
if v.mode != Mode::Var {
|
||||||
|
span_err!(v.tcx.sess, e.span, E0011,
|
||||||
|
"user-defined operators are not allowed in {}s", v.msg());
|
||||||
|
}
|
||||||
|
}
|
||||||
ast::ExprBox(..) |
|
ast::ExprBox(..) |
|
||||||
ast::ExprUnary(ast::UnUniq, _) => {
|
ast::ExprUnary(ast::UnUniq, _) => {
|
||||||
span_err!(v.tcx.sess, e.span, E0010,
|
v.add_qualif(NOT_CONST);
|
||||||
"allocations are not allowed in {}s", v.msg());
|
if v.mode != Mode::Var {
|
||||||
|
span_err!(v.tcx.sess, e.span, E0010,
|
||||||
|
"allocations are not allowed in {}s", v.msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast::ExprBinary(..) | ast::ExprUnary(..) => {
|
ast::ExprUnary(ast::UnDeref, ref ptr) => {
|
||||||
let method_call = ty::MethodCall::expr(e.id);
|
match ty::node_id_to_type(v.tcx, ptr.id).sty {
|
||||||
if v.tcx.method_map.borrow().contains_key(&method_call) {
|
ty::ty_ptr(_) => {
|
||||||
span_err!(v.tcx.sess, e.span, E0011,
|
// This shouldn't be allowed in constants at all.
|
||||||
"user-defined operators are not allowed in {}s", v.msg());
|
v.add_qualif(NOT_CONST);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprCast(ref from, _) => {
|
ast::ExprCast(ref from, _) => {
|
||||||
@ -188,51 +424,110 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) {
|
|||||||
ty::type_is_unsafe_ptr(toty) ||
|
ty::type_is_unsafe_ptr(toty) ||
|
||||||
(ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty));
|
(ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty));
|
||||||
if !is_legal_cast {
|
if !is_legal_cast {
|
||||||
span_err!(v.tcx.sess, e.span, E0012,
|
v.add_qualif(NOT_CONST);
|
||||||
"can not cast to `{}` in {}s",
|
if v.mode != Mode::Var {
|
||||||
ppaux::ty_to_string(v.tcx, toty), v.msg());
|
span_err!(v.tcx.sess, e.span, E0012,
|
||||||
|
"can not cast to `{}` in {}s",
|
||||||
|
ppaux::ty_to_string(v.tcx, toty), v.msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) {
|
if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) {
|
||||||
span_err!(v.tcx.sess, e.span, E0018,
|
v.add_qualif(NOT_CONST);
|
||||||
"can not cast a pointer to an integer in {}s", v.msg());
|
if v.mode != Mode::Var {
|
||||||
|
span_err!(v.tcx.sess, e.span, E0018,
|
||||||
|
"can not cast a pointer to an integer in {}s", v.msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprPath(_) | ast::ExprQPath(_) => {
|
ast::ExprPath(_) | ast::ExprQPath(_) => {
|
||||||
match v.tcx.def_map.borrow()[e.id] {
|
let def = v.tcx.def_map.borrow().get(&e.id).cloned();
|
||||||
def::DefStatic(..) if v.mode == InConstant => {
|
match def {
|
||||||
span_err!(v.tcx.sess, e.span, E0013,
|
Some(def::DefVariant(_, _, _)) => {
|
||||||
"constants cannot refer to other statics, \
|
// Count the discriminator or function pointer.
|
||||||
insert an intermediate constant instead");
|
v.add_qualif(NON_ZERO_SIZED);
|
||||||
|
}
|
||||||
|
Some(def::DefStruct(_)) => {
|
||||||
|
if let ty::ty_bare_fn(..) = node_ty.sty {
|
||||||
|
// Count the function pointer.
|
||||||
|
v.add_qualif(NON_ZERO_SIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(def::DefFn(..)) |
|
||||||
|
Some(def::DefStaticMethod(..)) | Some(def::DefMethod(..)) => {
|
||||||
|
// Count the function pointer.
|
||||||
|
v.add_qualif(NON_ZERO_SIZED);
|
||||||
|
}
|
||||||
|
Some(def::DefStatic(..)) => {
|
||||||
|
match v.mode {
|
||||||
|
Mode::Static | Mode::StaticMut => {}
|
||||||
|
Mode::Const => {
|
||||||
|
span_err!(v.tcx.sess, e.span, E0013,
|
||||||
|
"constants cannot refer to other statics, \
|
||||||
|
insert an intermediate constant instead");
|
||||||
|
}
|
||||||
|
Mode::Var => v.add_qualif(NOT_CONST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(def::DefConst(did)) => {
|
||||||
|
if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did) {
|
||||||
|
let inner = v.global_expr(Mode::Const, expr);
|
||||||
|
v.add_qualif(inner);
|
||||||
|
} else {
|
||||||
|
v.tcx.sess.span_bug(e.span, "DefConst doesn't point \
|
||||||
|
to an ItemConst");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
def::DefStatic(..) | def::DefConst(..) |
|
|
||||||
def::DefFn(..) | def::DefStaticMethod(..) | def::DefMethod(..) |
|
|
||||||
def::DefStruct(_) | def::DefVariant(_, _, _) => {}
|
|
||||||
|
|
||||||
def => {
|
def => {
|
||||||
debug!("(checking const) found bad def: {:?}", def);
|
v.add_qualif(NOT_CONST);
|
||||||
span_err!(v.tcx.sess, e.span, E0014,
|
if v.mode != Mode::Var {
|
||||||
"paths in constants may only refer to constants \
|
debug!("(checking const) found bad def: {:?}", def);
|
||||||
or functions");
|
span_err!(v.tcx.sess, e.span, E0014,
|
||||||
|
"paths in {}s may only refer to constants \
|
||||||
|
or functions", v.msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprCall(ref callee, _) => {
|
ast::ExprCall(ref callee, _) => {
|
||||||
match v.tcx.def_map.borrow()[callee.id] {
|
let mut callee = &**callee;
|
||||||
def::DefStruct(..) | def::DefVariant(..) => {} // OK.
|
loop {
|
||||||
|
callee = match callee.node {
|
||||||
|
ast::ExprParen(ref inner) => &**inner,
|
||||||
|
ast::ExprBlock(ref block) => match block.expr {
|
||||||
|
Some(ref tail) => &**tail,
|
||||||
|
None => break
|
||||||
|
},
|
||||||
|
_ => break
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let def = v.tcx.def_map.borrow().get(&callee.id).cloned();
|
||||||
|
match def {
|
||||||
|
Some(def::DefStruct(..)) => {}
|
||||||
|
Some(def::DefVariant(..)) => {
|
||||||
|
// Count the discriminator.
|
||||||
|
v.add_qualif(NON_ZERO_SIZED);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
span_err!(v.tcx.sess, e.span, E0015,
|
v.add_qualif(NOT_CONST);
|
||||||
"function calls in constants are limited to \
|
if v.mode != Mode::Var {
|
||||||
struct and enum constructors");
|
span_err!(v.tcx.sess, e.span, E0015,
|
||||||
|
"function calls in {}s are limited to \
|
||||||
|
struct and enum constructors", v.msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprBlock(ref block) => {
|
ast::ExprBlock(ref block) => {
|
||||||
// Check all statements in the block
|
// Check all statements in the block
|
||||||
for stmt in &block.stmts {
|
let mut block_span_err = |span| {
|
||||||
let block_span_err = |span|
|
v.add_qualif(NOT_CONST);
|
||||||
|
if v.mode != Mode::Var {
|
||||||
span_err!(v.tcx.sess, span, E0016,
|
span_err!(v.tcx.sess, span, E0016,
|
||||||
"blocks in constants are limited to items and \
|
"blocks in {}s are limited to items and \
|
||||||
tail expressions");
|
tail expressions", v.msg());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for stmt in &block.stmts {
|
||||||
match stmt.node {
|
match stmt.node {
|
||||||
ast::StmtDecl(ref decl, _) => {
|
ast::StmtDecl(ref decl, _) => {
|
||||||
match decl.node {
|
match decl.node {
|
||||||
@ -251,26 +546,40 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprAddrOf(ast::MutMutable, ref inner) => {
|
ast::ExprStruct(..) => {
|
||||||
match inner.node {
|
let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
|
||||||
// Mutable slices are allowed. Only in `static mut`.
|
if did == v.tcx.lang_items.unsafe_cell_type() {
|
||||||
ast::ExprVec(_) if v.mode == InStaticMut => {}
|
v.add_qualif(MUTABLE_MEM);
|
||||||
_ => span_err!(v.tcx.sess, e.span, E0017,
|
|
||||||
"references in {}s may only refer \
|
|
||||||
to immutable values", v.msg())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprLit(_) |
|
ast::ExprLit(_) |
|
||||||
ast::ExprVec(_) |
|
ast::ExprAddrOf(..) => {
|
||||||
ast::ExprAddrOf(ast::MutImmutable, _) |
|
v.add_qualif(NON_ZERO_SIZED);
|
||||||
ast::ExprParen(..) |
|
}
|
||||||
|
|
||||||
|
ast::ExprRepeat(..) => {
|
||||||
|
v.add_qualif(PREFER_IN_PLACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::ExprClosure(..) => {
|
||||||
|
// Paths in constant constexts cannot refer to local variables,
|
||||||
|
// 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");
|
||||||
|
v.add_qualif(NOT_CONST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::ExprUnary(..) |
|
||||||
|
ast::ExprBinary(..) |
|
||||||
|
ast::ExprIndex(..) |
|
||||||
ast::ExprField(..) |
|
ast::ExprField(..) |
|
||||||
ast::ExprTupField(..) |
|
ast::ExprTupField(..) |
|
||||||
ast::ExprIndex(..) |
|
ast::ExprVec(_) |
|
||||||
ast::ExprTup(..) |
|
ast::ExprParen(..) |
|
||||||
ast::ExprRepeat(..) |
|
ast::ExprTup(..) => {}
|
||||||
ast::ExprStruct(..) => {}
|
|
||||||
|
|
||||||
// Conditional control flow (possible to implement).
|
// Conditional control flow (possible to implement).
|
||||||
ast::ExprMatch(..) |
|
ast::ExprMatch(..) |
|
||||||
@ -289,7 +598,6 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) {
|
|||||||
ast::ExprRet(_) |
|
ast::ExprRet(_) |
|
||||||
|
|
||||||
// Miscellaneous expressions that could be implemented.
|
// Miscellaneous expressions that could be implemented.
|
||||||
ast::ExprClosure(..) |
|
|
||||||
ast::ExprRange(..) |
|
ast::ExprRange(..) |
|
||||||
|
|
||||||
// Various other expressions.
|
// Various other expressions.
|
||||||
@ -298,50 +606,27 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) {
|
|||||||
ast::ExprAssignOp(..) |
|
ast::ExprAssignOp(..) |
|
||||||
ast::ExprInlineAsm(_) |
|
ast::ExprInlineAsm(_) |
|
||||||
ast::ExprMac(_) => {
|
ast::ExprMac(_) => {
|
||||||
span_err!(v.tcx.sess, e.span, E0019,
|
v.add_qualif(NOT_CONST);
|
||||||
"{} contains unimplemented expression type", v.msg());
|
if v.mode != Mode::Var {
|
||||||
|
span_err!(v.tcx.sess, e.span, E0019,
|
||||||
|
"{} contains unimplemented expression type", v.msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GlobalVisitor<'a,'b,'tcx:'a+'b>(
|
|
||||||
euv::ExprUseVisitor<'a,'b,'tcx,ty::ParameterEnvironment<'b,'tcx>>);
|
|
||||||
|
|
||||||
struct GlobalChecker<'a,'tcx:'a> {
|
|
||||||
tcx: &'a ty::ctxt<'tcx>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_crate(tcx: &ty::ctxt) {
|
pub fn check_crate(tcx: &ty::ctxt) {
|
||||||
let param_env = ty::empty_parameter_environment(tcx);
|
|
||||||
let mut checker = GlobalChecker {
|
|
||||||
tcx: tcx
|
|
||||||
};
|
|
||||||
let visitor = euv::ExprUseVisitor::new(&mut checker, ¶m_env);
|
|
||||||
visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate());
|
|
||||||
|
|
||||||
visit::walk_crate(&mut CheckCrateVisitor {
|
visit::walk_crate(&mut CheckCrateVisitor {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
mode: InNothing,
|
mode: Mode::Var,
|
||||||
|
qualif: NOT_CONST,
|
||||||
|
rvalue_borrows: NodeMap()
|
||||||
}, tcx.map.krate());
|
}, tcx.map.krate());
|
||||||
|
|
||||||
tcx.sess.abort_if_errors();
|
tcx.sess.abort_if_errors();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a,'b,'t,'v> Visitor<'v> for GlobalVisitor<'a,'b,'t> {
|
impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
||||||
fn visit_item(&mut self, item: &ast::Item) {
|
|
||||||
match item.node {
|
|
||||||
ast::ItemConst(_, ref e) |
|
|
||||||
ast::ItemStatic(_, _, ref e) => {
|
|
||||||
let GlobalVisitor(ref mut v) = *self;
|
|
||||||
v.consume_expr(&**e);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
visit::walk_item(self, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'tcx> euv::Delegate<'tcx> for GlobalChecker<'a, 'tcx> {
|
|
||||||
fn consume(&mut self,
|
fn consume(&mut self,
|
||||||
_consume_id: ast::NodeId,
|
_consume_id: ast::NodeId,
|
||||||
consume_span: Span,
|
consume_span: Span,
|
||||||
@ -351,12 +636,14 @@ fn consume(&mut self,
|
|||||||
loop {
|
loop {
|
||||||
match cur.cat {
|
match cur.cat {
|
||||||
mc::cat_static_item => {
|
mc::cat_static_item => {
|
||||||
// statics cannot be consumed by value at any time, that would imply
|
if self.mode != Mode::Var {
|
||||||
// that they're an initializer (what a const is for) or kept in sync
|
// statics cannot be consumed by value at any time, that would imply
|
||||||
// over time (not feasible), so deny it outright.
|
// that they're an initializer (what a const is for) or kept in sync
|
||||||
self.tcx.sess.span_err(consume_span,
|
// over time (not feasible), so deny it outright.
|
||||||
"cannot refer to other statics by value, use the \
|
self.tcx.sess.span_err(consume_span,
|
||||||
address-of operator or a constant instead");
|
"cannot refer to other statics by value, use the \
|
||||||
|
address-of operator or a constant instead");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mc::cat_deref(ref cmt, _, _) |
|
mc::cat_deref(ref cmt, _, _) |
|
||||||
@ -370,29 +657,35 @@ fn consume(&mut self,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn borrow(&mut self,
|
fn borrow(&mut self,
|
||||||
_borrow_id: ast::NodeId,
|
borrow_id: ast::NodeId,
|
||||||
borrow_span: Span,
|
borrow_span: Span,
|
||||||
cmt: mc::cmt<'tcx>,
|
cmt: mc::cmt<'tcx>,
|
||||||
_loan_region: ty::Region,
|
_loan_region: ty::Region,
|
||||||
_bk: ty::BorrowKind,
|
bk: ty::BorrowKind,
|
||||||
_loan_cause: euv::LoanCause) {
|
loan_cause: euv::LoanCause) {
|
||||||
let mut cur = &cmt;
|
let mut cur = &cmt;
|
||||||
let mut is_interior = false;
|
let mut is_interior = false;
|
||||||
loop {
|
loop {
|
||||||
match cur.cat {
|
match cur.cat {
|
||||||
mc::cat_rvalue(..) => {
|
mc::cat_rvalue(..) => {
|
||||||
// constants cannot be borrowed if they contain interior mutability as
|
if loan_cause == euv::MatchDiscriminant {
|
||||||
// it means that our "silent insertion of statics" could change
|
// Ignore the dummy immutable borrow created by EUV.
|
||||||
// initializer values (very bad).
|
break;
|
||||||
if ty::type_contents(self.tcx, cur.ty).interior_unsafe() {
|
|
||||||
self.tcx.sess.span_err(borrow_span,
|
|
||||||
"cannot borrow a constant which contains \
|
|
||||||
interior mutability, create a static instead");
|
|
||||||
}
|
}
|
||||||
|
let mutbl = bk.to_mutbl_lossy();
|
||||||
|
if mutbl == ast::MutMutable && self.mode == Mode::StaticMut {
|
||||||
|
// Mutable slices are the only `&mut` allowed in globals,
|
||||||
|
// but only in `static mut`, nowhere else.
|
||||||
|
match cmt.ty.sty {
|
||||||
|
ty::ty_vec(_, _) => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.record_borrow(borrow_id, mutbl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mc::cat_static_item => {
|
mc::cat_static_item => {
|
||||||
if is_interior {
|
if is_interior && self.mode != Mode::Var {
|
||||||
// Borrowed statics can specifically *only* have their address taken,
|
// Borrowed statics can specifically *only* have their address taken,
|
||||||
// not any number of other borrows such as borrowing fields, reading
|
// not any number of other borrows such as borrowing fields, reading
|
||||||
// elements of an array, etc.
|
// elements of an array, etc.
|
||||||
@ -433,4 +726,4 @@ fn consume_pat(&mut self,
|
|||||||
_consume_pat: &ast::Pat,
|
_consume_pat: &ast::Pat,
|
||||||
_cmt: mc::cmt,
|
_cmt: mc::cmt,
|
||||||
_mode: euv::ConsumeMode) {}
|
_mode: euv::ConsumeMode) {}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
use lint;
|
use lint;
|
||||||
use metadata::csearch;
|
use metadata::csearch;
|
||||||
use middle;
|
use middle;
|
||||||
|
use middle::check_const;
|
||||||
use middle::const_eval;
|
use middle::const_eval;
|
||||||
use middle::def::{self, DefMap, ExportMap};
|
use middle::def::{self, DefMap, ExportMap};
|
||||||
use middle::dependency_format;
|
use middle::dependency_format;
|
||||||
@ -838,6 +839,9 @@ pub struct ctxt<'tcx> {
|
|||||||
|
|
||||||
/// Caches whether traits are object safe
|
/// Caches whether traits are object safe
|
||||||
pub object_safety_cache: RefCell<DefIdMap<bool>>,
|
pub object_safety_cache: RefCell<DefIdMap<bool>>,
|
||||||
|
|
||||||
|
/// Maps Expr NodeId's to their constant qualification.
|
||||||
|
pub const_qualif_map: RefCell<NodeMap<check_const::ConstQualif>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags that we track on types. These flags are propagated upwards
|
// Flags that we track on types. These flags are propagated upwards
|
||||||
@ -2472,6 +2476,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
|
|||||||
type_impls_copy_cache: RefCell::new(HashMap::new()),
|
type_impls_copy_cache: RefCell::new(HashMap::new()),
|
||||||
type_impls_sized_cache: RefCell::new(HashMap::new()),
|
type_impls_sized_cache: RefCell::new(HashMap::new()),
|
||||||
object_safety_cache: RefCell::new(DefIdMap()),
|
object_safety_cache: RefCell::new(DefIdMap()),
|
||||||
|
const_qualif_map: RefCell::new(NodeMap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user