09d67fd777
This is accomplished by: 1. Add `MatchMode` enum to `expr_use_visitor`. 2. Computing the match mode for each pattern via a pre-pass, and then passing the mode along when visiting the pattern in expr_use_visitor. 3. Adding a `fn matched_pat` callback to expr_use_visitor, which is called on interior struct and enum nodes of the pattern (as opposed to `fn consume_pat`, which is only invoked for identifiers at the leaves of the pattern), and invoking it accordingly. Of particular interest are the `cat_downcast` instances established when matching enum variants.
339 lines
12 KiB
Rust
339 lines
12 KiB
Rust
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
|
// 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.
|
|
|
|
// Verifies that the types and values of 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
|
|
// - doesn't own an owned pointer
|
|
//
|
|
// - For each *immutable* static item, it checks that its **value**:
|
|
// - doesn't own owned, managed pointers
|
|
// - 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
|
|
use self::Mode::*;
|
|
|
|
use middle::ty;
|
|
use middle::def;
|
|
use middle::typeck;
|
|
use middle::traits;
|
|
use middle::mem_categorization as mc;
|
|
use middle::expr_use_visitor as euv;
|
|
use util::nodemap::NodeSet;
|
|
|
|
use syntax::ast;
|
|
use syntax::print::pprust;
|
|
use syntax::visit::Visitor;
|
|
use syntax::codemap::{DUMMY_SP, Span};
|
|
use syntax::visit;
|
|
|
|
#[deriving(Eq, PartialEq)]
|
|
enum Mode {
|
|
InConstant,
|
|
InStatic,
|
|
InStaticMut,
|
|
InNothing,
|
|
}
|
|
|
|
struct CheckStaticVisitor<'a, 'tcx: 'a> {
|
|
tcx: &'a ty::ctxt<'tcx>,
|
|
mode: Mode,
|
|
checker: &'a mut GlobalChecker,
|
|
}
|
|
|
|
struct GlobalVisitor<'a, 'b, 'tcx: 'b>(euv::ExprUseVisitor<'a, 'b, 'tcx, ty::ctxt<'tcx>>);
|
|
struct GlobalChecker {
|
|
static_consumptions: NodeSet,
|
|
const_borrows: NodeSet,
|
|
static_interior_borrows: NodeSet,
|
|
static_local_borrows: NodeSet,
|
|
}
|
|
|
|
pub fn check_crate(tcx: &ty::ctxt) {
|
|
let mut checker = GlobalChecker {
|
|
static_consumptions: NodeSet::new(),
|
|
const_borrows: NodeSet::new(),
|
|
static_interior_borrows: NodeSet::new(),
|
|
static_local_borrows: NodeSet::new(),
|
|
};
|
|
{
|
|
let visitor = euv::ExprUseVisitor::new(&mut checker, tcx);
|
|
visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate());
|
|
}
|
|
visit::walk_crate(&mut CheckStaticVisitor {
|
|
tcx: tcx,
|
|
mode: InNothing,
|
|
checker: &mut checker,
|
|
}, tcx.map.krate());
|
|
}
|
|
|
|
impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> {
|
|
fn with_mode(&mut self, mode: Mode, f: |&mut CheckStaticVisitor<'a, 'tcx>|) {
|
|
let old = self.mode;
|
|
self.mode = mode;
|
|
f(self);
|
|
self.mode = old;
|
|
}
|
|
|
|
fn msg(&self) -> &'static str {
|
|
match self.mode {
|
|
InConstant => "constants",
|
|
InStaticMut | InStatic => "statics",
|
|
InNothing => unreachable!(),
|
|
}
|
|
}
|
|
|
|
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() {
|
|
"owned pointers"
|
|
} else {
|
|
return
|
|
};
|
|
|
|
self.tcx.sess.span_err(e.span, format!("mutable statics are not allowed \
|
|
to have {}", suffix).as_slice());
|
|
}
|
|
|
|
fn check_static_type(&self, e: &ast::Expr) {
|
|
let ty = ty::node_id_to_type(self.tcx, e.id);
|
|
let infcx = typeck::infer::new_infer_ctxt(self.tcx);
|
|
let mut fulfill_cx = traits::FulfillmentContext::new();
|
|
let cause = traits::ObligationCause::misc(DUMMY_SP);
|
|
let obligation = traits::obligation_for_builtin_bound(self.tcx, cause, ty,
|
|
ty::BoundSync);
|
|
fulfill_cx.register_obligation(self.tcx, obligation.unwrap());
|
|
let env = ty::empty_parameter_environment();
|
|
let result = fulfill_cx.select_all_or_error(&infcx, &env, self.tcx).is_ok();
|
|
if !result {
|
|
self.tcx.sess.span_err(e.span, "shared static items must have a \
|
|
type which implements Sync");
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for CheckStaticVisitor<'a, 'tcx> {
|
|
fn visit_item(&mut self, i: &ast::Item) {
|
|
debug!("visit_item(item={})", pprust::item_to_string(i));
|
|
match i.node {
|
|
ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
|
|
self.check_static_type(&**expr);
|
|
self.with_mode(InStatic, |v| v.visit_expr(&**expr));
|
|
}
|
|
ast::ItemStatic(_, ast::MutMutable, ref expr) => {
|
|
self.check_static_mut_type(&**expr);
|
|
self.with_mode(InStaticMut, |v| v.visit_expr(&**expr));
|
|
}
|
|
ast::ItemConst(_, ref expr) => {
|
|
self.with_mode(InConstant, |v| v.visit_expr(&**expr));
|
|
}
|
|
_ => {
|
|
self.with_mode(InNothing, |v| visit::walk_item(v, i));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This method is used to enforce the constraints on
|
|
/// immutable 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
|
|
/// of a static item, this method does nothing but walking
|
|
/// down through it.
|
|
fn visit_expr(&mut self, e: &ast::Expr) {
|
|
if self.mode == InNothing {
|
|
return visit::walk_expr(self, e);
|
|
}
|
|
|
|
let node_ty = ty::node_id_to_type(self.tcx, e.id);
|
|
|
|
match node_ty.sty {
|
|
ty::ty_struct(did, _) |
|
|
ty::ty_enum(did, _) if ty::has_dtor(self.tcx, did) => {
|
|
self.tcx.sess.span_err(e.span,
|
|
format!("{} are not allowed to have \
|
|
destructors", self.msg()).as_slice())
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
// 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.
|
|
if self.checker.static_consumptions.remove(&e.id) {
|
|
self.tcx.sess.span_err(e.span, "cannot refer to other statics by \
|
|
value, use the address-of operator \
|
|
or a constant instead");
|
|
}
|
|
|
|
// 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.
|
|
if self.checker.static_interior_borrows.remove(&e.id) {
|
|
self.tcx.sess.span_err(e.span, "cannot refer to the interior of \
|
|
another static, use a constant \
|
|
instead");
|
|
}
|
|
|
|
// 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 self.checker.const_borrows.remove(&e.id) {
|
|
let node_ty = ty::node_id_to_type(self.tcx, e.id);
|
|
let tcontents = ty::type_contents(self.tcx, node_ty);
|
|
if tcontents.interior_unsafe() {
|
|
self.tcx.sess.span_err(e.span, "cannot borrow a constant which \
|
|
contains interior mutability, \
|
|
create a static instead");
|
|
}
|
|
}
|
|
|
|
// local variables in a block expression in a static context (i.e. being
|
|
// assigned to a static variable) cannot be borrowed.
|
|
if self.checker.static_local_borrows.remove(&e.id) {
|
|
self.tcx.sess.span_err(e.span, "cannot borrow a local variable inside \
|
|
a static block, define a separate static \
|
|
instead");
|
|
}
|
|
|
|
match e.node {
|
|
ast::ExprAddrOf(ast::MutMutable, _) => {
|
|
if self.mode != InStaticMut {
|
|
span_err!(self.tcx.sess, e.span, E0020,
|
|
"{} are not allowed to have mutable references",
|
|
self.msg());
|
|
}
|
|
},
|
|
ast::ExprBox(..) |
|
|
ast::ExprUnary(ast::UnUniq, _) => {
|
|
span_err!(self.tcx.sess, e.span, E0022,
|
|
"{} are not allowed to have custom pointers",
|
|
self.msg());
|
|
}
|
|
ast::ExprPath(..) => {
|
|
match ty::resolve_expr(self.tcx, e) {
|
|
def::DefStatic(..) if self.mode == InConstant => {
|
|
let msg = "constants cannot refer to other statics, \
|
|
insert an intermediate constant \
|
|
instead";
|
|
self.tcx.sess.span_err(e.span, msg.as_slice());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
visit::walk_expr(self, e);
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b, 't, 'v> Visitor<'v> for GlobalVisitor<'a, 'b, 't> {
|
|
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<'tcx> euv::Delegate<'tcx> for GlobalChecker {
|
|
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 => {
|
|
self.static_consumptions.insert(consume_id);
|
|
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,
|
|
borrow_id: ast::NodeId,
|
|
_borrow_span: Span,
|
|
cmt: mc::cmt,
|
|
_loan_region: ty::Region,
|
|
_bk: ty::BorrowKind,
|
|
_loan_cause: euv::LoanCause) {
|
|
let mut cur = &cmt;
|
|
let mut is_interior = false;
|
|
loop {
|
|
match cur.cat {
|
|
mc::cat_rvalue(..) => {
|
|
self.const_borrows.insert(borrow_id);
|
|
break
|
|
}
|
|
mc::cat_static_item => {
|
|
if is_interior {
|
|
self.static_interior_borrows.insert(borrow_id);
|
|
}
|
|
break
|
|
}
|
|
mc::cat_deref(ref cmt, _, _) |
|
|
mc::cat_interior(ref cmt, _) => {
|
|
is_interior = true;
|
|
cur = cmt;
|
|
}
|
|
|
|
mc::cat_downcast(..) |
|
|
mc::cat_upvar(..) => unreachable!(),
|
|
|
|
mc::cat_local(..) => {
|
|
self.static_local_borrows.insert(borrow_id);
|
|
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) {}
|
|
}
|