]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/check_static.rs
auto merge of #12742 : FlaPer87/rust/issue-11411-static-mut-slice, r=nikomatsakis
[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 is not freeze
22 //           - the type of the struct/enum has a dtor
23
24 use middle::ty;
25
26 use syntax::ast;
27 use syntax::codemap::Span;
28 use syntax::visit::Visitor;
29 use syntax::visit;
30 use syntax::print::pprust;
31
32
33 fn safe_type_for_static_mut(cx: &ty::ctxt, e: &ast::Expr) -> Option<~str> {
34     let node_ty = ty::node_id_to_type(cx, e.id);
35     let tcontents = ty::type_contents(cx, node_ty);
36     debug!("safe_type_for_static_mut(dtor={}, managed={}, owned={})",
37            tcontents.has_dtor(), tcontents.owns_managed(), tcontents.owns_owned())
38
39     let suffix = if tcontents.has_dtor() {
40         "destructors"
41     } else if tcontents.owns_managed() {
42         "managed pointers"
43     } else if tcontents.owns_owned() {
44         "owned pointers"
45     } else {
46         return None;
47     };
48
49     Some(format!("mutable static items are not allowed to have {}", suffix))
50 }
51
52 struct CheckStaticVisitor<'a> {
53     tcx: &'a ty::ctxt,
54 }
55
56 pub fn check_crate(tcx: &ty::ctxt, krate: &ast::Crate) {
57     visit::walk_crate(&mut CheckStaticVisitor { tcx: tcx }, krate, false)
58 }
59
60 impl<'a> CheckStaticVisitor<'a> {
61     fn report_error(&self, span: Span, result: Option<~str>) -> bool {
62         match result {
63             None => { false }
64             Some(msg) => {
65                 self.tcx.sess.span_err(span, msg);
66                 true
67             }
68         }
69     }
70 }
71
72 impl<'a> Visitor<bool> for CheckStaticVisitor<'a> {
73
74     fn visit_item(&mut self, i: &ast::Item, _is_const: bool) {
75         debug!("visit_item(item={})", pprust::item_to_str(i));
76         match i.node {
77             ast::ItemStatic(_, mutability, expr) => {
78                 match mutability {
79                     ast::MutImmutable => {
80                         self.visit_expr(expr, true);
81                     }
82                     ast::MutMutable => {
83                         self.report_error(expr.span, safe_type_for_static_mut(self.tcx, expr));
84                     }
85                 }
86             }
87             _ => { visit::walk_item(self, i, false) }
88         }
89     }
90
91     /// This method is used to enforce the constraints on
92     /// immutable static items. It walks through the *value*
93     /// of the item walking down the expression and evaluating
94     /// every nested expression. if the expression is not part
95     /// of a static item, this method does nothing but walking
96     /// down through it.
97     fn visit_expr(&mut self, e: &ast::Expr, is_const: bool) {
98         debug!("visit_expr(expr={})", pprust::expr_to_str(e));
99
100         if !is_const {
101             return visit::walk_expr(self, e, is_const);
102         }
103
104         match e.node {
105             ast::ExprField(..) | ast::ExprVec(..) |
106             ast::ExprBlock(..) | ast::ExprTup(..) |
107             ast::ExprVstore(_, ast::ExprVstoreSlice) => {
108                 visit::walk_expr(self, e, is_const);
109             }
110             ast::ExprVstore(_, ast::ExprVstoreMutSlice) => {
111                 self.tcx.sess.span_err(e.span,
112                                        "static items are not allowed to have mutable slices");
113            },
114             ast::ExprUnary(ast::UnBox, _) => {
115                 self.tcx.sess.span_err(e.span,
116                                    "static items are not allowed to have managed pointers");
117             }
118             ast::ExprBox(..) |
119             ast::ExprUnary(ast::UnUniq, _) |
120             ast::ExprVstore(_, ast::ExprVstoreUniq) => {
121                 self.tcx.sess.span_err(e.span,
122                                    "static items are not allowed to have owned pointers");
123             }
124             ast::ExprProc(..) => {
125                 self.report_error(e.span,
126                                   Some(~"immutable static items must be `Freeze`"));
127                 return;
128             }
129             ast::ExprAddrOf(mutability, _) => {
130                 match mutability {
131                     ast::MutMutable => {
132                         self.report_error(e.span,
133                                   Some(~"immutable static items must be `Freeze`"));
134                         return;
135                     }
136                     _ => {}
137                 }
138             }
139             _ => {
140                 let node_ty = ty::node_id_to_type(self.tcx, e.id);
141
142                 match ty::get(node_ty).sty {
143                     ty::ty_struct(did, _) |
144                     ty::ty_enum(did, _) => {
145                         if ty::has_dtor(self.tcx, did) {
146                             self.report_error(e.span,
147                                      Some(~"static items are not allowed to have destructors"));
148                             return;
149                         }
150                         if Some(did) == self.tcx.lang_items.no_freeze_bound() {
151                             self.report_error(e.span,
152                                               Some(~"immutable static items must be `Freeze`"));
153                             return;
154                         }
155                     }
156                     _ => {}
157                 }
158                 visit::walk_expr(self, e, is_const);
159             }
160         }
161     }
162 }