]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/check_static.rs
7b00bb4589c71e8983c2aaaea38d992b8d655d44
[rust.git] / src / librustc / middle / check_static.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // Verifies that the types and values of static items
12 // are safe. The rules enforced by this module are:
13 //
14 // - For each *mutable* static item, it checks that its **type**:
15 //     - doesn't have a destructor
16 //     - doesn't own an owned pointer
17 //
18 // - For each *immutable* static item, it checks that its **value**:
19 //       - doesn't own owned, managed pointers
20 //       - doesn't contain a struct literal or a call to an enum variant / struct constructor where
21 //           - the type of the struct/enum has a dtor
22 //
23 // Rules Enforced Elsewhere:
24 // - It's not possible to take the address of a static item with unsafe interior. This is enforced
25 // by borrowck::gather_loans
26
27 use middle::ty;
28
29 use syntax::ast;
30 use syntax::codemap::Span;
31 use syntax::visit::Visitor;
32 use syntax::visit;
33 use syntax::print::pprust;
34
35
36 fn safe_type_for_static_mut(cx: &ty::ctxt, e: &ast::Expr) -> Option<String> {
37     let node_ty = ty::node_id_to_type(cx, e.id);
38     let tcontents = ty::type_contents(cx, node_ty);
39     debug!("safe_type_for_static_mut(dtor={}, managed={}, owned={})",
40            tcontents.has_dtor(), tcontents.owns_managed(), tcontents.owns_owned())
41
42     let suffix = if tcontents.has_dtor() {
43         "destructors"
44     } else if tcontents.owns_managed() {
45         "managed pointers"
46     } else if tcontents.owns_owned() {
47         "owned pointers"
48     } else {
49         return None;
50     };
51
52     Some(format!("mutable static items are not allowed to have {}", suffix))
53 }
54
55 struct CheckStaticVisitor<'a, 'tcx: 'a> {
56     tcx: &'a ty::ctxt<'tcx>,
57 }
58
59 pub fn check_crate(tcx: &ty::ctxt, krate: &ast::Crate) {
60     visit::walk_crate(&mut CheckStaticVisitor { tcx: tcx }, krate, false)
61 }
62
63 impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> {
64     fn report_error(&self, span: Span, result: Option<String>) -> bool {
65         match result {
66             None => { false }
67             Some(msg) => {
68                 self.tcx.sess.span_err(span, msg.as_slice());
69                 true
70             }
71         }
72     }
73 }
74
75 impl<'a, 'tcx> Visitor<bool> for CheckStaticVisitor<'a, 'tcx> {
76
77     fn visit_item(&mut self, i: &ast::Item, _is_const: bool) {
78         debug!("visit_item(item={})", pprust::item_to_string(i));
79         match i.node {
80             ast::ItemStatic(_, mutability, ref expr) => {
81                 match mutability {
82                     ast::MutImmutable => {
83                         self.visit_expr(&**expr, true);
84                     }
85                     ast::MutMutable => {
86                         let safe = safe_type_for_static_mut(self.tcx, &**expr);
87                         self.report_error(expr.span, safe);
88                     }
89                 }
90             }
91             _ => { visit::walk_item(self, i, false) }
92         }
93     }
94
95     /// This method is used to enforce the constraints on
96     /// immutable static items. It walks through the *value*
97     /// of the item walking down the expression and evaluating
98     /// every nested expression. if the expression is not part
99     /// of a static item, this method does nothing but walking
100     /// down through it.
101     fn visit_expr(&mut self, e: &ast::Expr, is_const: bool) {
102         debug!("visit_expr(expr={})", pprust::expr_to_string(e));
103
104         if !is_const {
105             return visit::walk_expr(self, e, is_const);
106         }
107
108         match e.node {
109             ast::ExprField(..) | ast::ExprVec(..) |
110             ast::ExprBlock(..) | ast::ExprTup(..)  => {
111                 visit::walk_expr(self, e, is_const);
112             }
113             ast::ExprAddrOf(ast::MutMutable, _) => {
114                 span_err!(self.tcx.sess, e.span, E0020,
115                     "static items are not allowed to have mutable slices");
116             },
117             ast::ExprUnary(ast::UnBox, _) => {
118                 span_err!(self.tcx.sess, e.span, E0021,
119                     "static items are not allowed to have managed pointers");
120             }
121             ast::ExprBox(..) |
122             ast::ExprUnary(ast::UnUniq, _) => {
123                 span_err!(self.tcx.sess, e.span, E0022,
124                     "static items are not allowed to have custom pointers");
125             }
126             _ => {
127                 let node_ty = ty::node_id_to_type(self.tcx, e.id);
128
129                 match ty::get(node_ty).sty {
130                     ty::ty_struct(did, _) |
131                     ty::ty_enum(did, _) => {
132                         if ty::has_dtor(self.tcx, did) {
133                             self.report_error(e.span,
134                              Some("static items are not allowed to have \
135                                    destructors".to_string()));
136                             return;
137                         }
138                     }
139                     _ => {}
140                 }
141                 visit::walk_expr(self, e, is_const);
142             }
143         }
144     }
145 }