Forbid certain types for static items

- For each *mutable* static item, check that the **type**:
    - cannot own any value whose type has a dtor
    - cannot own any values whose type is an owned pointer

- For each *immutable* static item, check that the **value**:
    - does not contain any ~ or box expressions
        (including ~[1, 2, 3] sort of things)
    - does not contain a struct literal or call to an enum
        variant / struct constructor where
        - the type of the struct/enum has a dtor
This commit is contained in:
Flavio Percoco 2014-02-26 19:22:41 +01:00
parent 8784d2fa95
commit ee2f001a42
6 changed files with 279 additions and 1 deletions

View file

@ -301,6 +301,9 @@ pub fn phase_3_run_analysis_passes(sess: Session,
// passes are timed inside typeck
let (method_map, vtable_map) = typeck::check_crate(ty_cx, trait_map, krate);
time(time_passes, "check static items", (), |_|
middle::check_static::check_crate(ty_cx, krate));
// These next two const passes can probably be merged
time(time_passes, "const marking", (), |_|
middle::const_eval::process_crate(krate, ty_cx));

View file

@ -69,6 +69,7 @@ pub mod middle {
pub mod check_loop;
pub mod check_match;
pub mod check_const;
pub mod check_static;
pub mod lint;
pub mod borrowck;
pub mod dataflow;

View file

@ -0,0 +1,138 @@
// 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 is not freeze
// - the type of the struct/enum has a dtor
use middle::ty;
use syntax::ast;
use syntax::codemap::Span;
use syntax::visit::Visitor;
use syntax::visit;
use syntax::print::pprust;
fn safe_type_for_static_mut(cx: ty::ctxt, e: &ast::Expr) -> Option<~str> {
let node_ty = ty::node_id_to_type(cx, e.id);
let tcontents = ty::type_contents(cx, node_ty);
debug!("safe_type_for_static_mut(dtor={}, managed={}, owned={})",
tcontents.has_dtor(), tcontents.owns_managed(), tcontents.owns_owned())
let suffix = if tcontents.has_dtor() {
"destructors"
} else if tcontents.owns_managed() {
"managed pointers"
} else if tcontents.owns_owned() {
"owned pointers"
} else {
return None;
};
Some(format!("mutable static items are not allowed to have {}", suffix))
}
struct CheckStaticVisitor {
tcx: ty::ctxt,
}
pub fn check_crate(tcx: ty::ctxt, krate: &ast::Crate) {
visit::walk_crate(&mut CheckStaticVisitor { tcx: tcx }, krate, false)
}
impl CheckStaticVisitor {
fn report_error(&self, span: Span, result: Option<~str>) -> bool {
match result {
None => { false }
Some(msg) => {
self.tcx.sess.span_err(span, msg);
true
}
}
}
}
impl Visitor<bool> for CheckStaticVisitor {
fn visit_item(&mut self, i: &ast::Item, _is_const: bool) {
debug!("visit_item(item={})", pprust::item_to_str(i));
match i.node {
ast::ItemStatic(_, mutability, expr) => {
match mutability {
ast::MutImmutable => {
self.visit_expr(expr, true);
}
ast::MutMutable => {
self.report_error(expr.span, safe_type_for_static_mut(self.tcx, expr));
}
}
}
_ => { visit::walk_item(self, i, false) }
}
}
/// 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, is_const: bool) {
debug!("visit_expr(expr={})", pprust::expr_to_str(e));
if !is_const {
return visit::walk_expr(self, e, is_const);
}
match e.node {
ast::ExprField(..) | ast::ExprVec(..) |
ast::ExprBlock(..) | ast::ExprTup(..) |
ast::ExprVstore(_, ast::ExprVstoreSlice) => {
visit::walk_expr(self, e, is_const);
}
ast::ExprUnary(ast::UnBox, _) => {
self.tcx.sess.span_err(e.span,
"static items are not allowed to have managed pointers");
}
ast::ExprBox(..) |
ast::ExprUnary(ast::UnUniq, _) |
ast::ExprVstore(_, ast::ExprVstoreUniq) => {
self.tcx.sess.span_err(e.span,
"static items are not allowed to have owned pointers");
}
_ => {
let node_ty = ty::node_id_to_type(self.tcx, e.id);
match ty::get(node_ty).sty {
ty::ty_struct(did, _) |
ty::ty_enum(did, _) => {
if ty::has_dtor(self.tcx, did) {
self.report_error(e.span,
Some(~"static items are not allowed to have destructors"));
return;
}
}
_ => {}
}
visit::walk_expr(self, e, is_const);
}
}
}
}

View file

@ -1950,6 +1950,10 @@ impl TypeContents {
self.intersects(TC::OwnsManaged)
}
pub fn owns_owned(&self) -> bool {
self.intersects(TC::OwnsOwned)
}
pub fn is_freezable(&self, _: ctxt) -> bool {
!self.intersects(TC::Nonfreezable)
}
@ -2042,6 +2046,10 @@ impl fmt::Show for TypeContents {
}
}
pub fn type_has_dtor(cx: ctxt, t: ty::t) -> bool {
type_contents(cx, t).has_dtor()
}
pub fn type_is_static(cx: ctxt, t: ty::t) -> bool {
type_contents(cx, t).is_static(cx)
}