]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/check_static.rs
Merge pull request #20510 from tshepang/patch-6
[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 use self::Mode::*;
27
28 use middle::ty;
29 use middle::def;
30 use middle::infer;
31 use middle::traits;
32 use middle::mem_categorization as mc;
33 use middle::expr_use_visitor as euv;
34 use util::nodemap::NodeSet;
35
36 use syntax::ast;
37 use syntax::print::pprust;
38 use syntax::visit::Visitor;
39 use syntax::codemap::Span;
40 use syntax::visit;
41
42 #[derive(Copy, Eq, PartialEq)]
43 enum Mode {
44     InConstant,
45     InStatic,
46     InStaticMut,
47     InNothing,
48 }
49
50 struct CheckStaticVisitor<'a, 'tcx: 'a> {
51     tcx: &'a ty::ctxt<'tcx>,
52     mode: Mode,
53     checker: &'a mut GlobalChecker,
54 }
55
56 struct GlobalVisitor<'a,'b,'tcx:'a+'b>(
57     euv::ExprUseVisitor<'a,'b,'tcx,ty::ParameterEnvironment<'b,'tcx>>);
58 struct GlobalChecker {
59     static_consumptions: NodeSet,
60     const_borrows: NodeSet,
61     static_interior_borrows: NodeSet,
62     static_local_borrows: NodeSet,
63 }
64
65 pub fn check_crate(tcx: &ty::ctxt) {
66     let mut checker = GlobalChecker {
67         static_consumptions: NodeSet::new(),
68         const_borrows: NodeSet::new(),
69         static_interior_borrows: NodeSet::new(),
70         static_local_borrows: NodeSet::new(),
71     };
72     {
73         let param_env = ty::empty_parameter_environment(tcx);
74         let visitor = euv::ExprUseVisitor::new(&mut checker, &param_env);
75         visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate());
76     }
77     visit::walk_crate(&mut CheckStaticVisitor {
78         tcx: tcx,
79         mode: InNothing,
80         checker: &mut checker,
81     }, tcx.map.krate());
82 }
83
84 impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> {
85     fn with_mode<F>(&mut self, mode: Mode, f: F) where
86         F: FnOnce(&mut CheckStaticVisitor<'a, 'tcx>),
87     {
88         let old = self.mode;
89         self.mode = mode;
90         f(self);
91         self.mode = old;
92     }
93
94     fn msg(&self) -> &'static str {
95         match self.mode {
96             InConstant => "constants",
97             InStaticMut | InStatic => "statics",
98             InNothing => unreachable!(),
99         }
100     }
101
102     fn check_static_mut_type(&self, e: &ast::Expr) {
103         let node_ty = ty::node_id_to_type(self.tcx, e.id);
104         let tcontents = ty::type_contents(self.tcx, node_ty);
105
106         let suffix = if tcontents.has_dtor() {
107             "destructors"
108         } else if tcontents.owns_owned() {
109             "owned pointers"
110         } else {
111             return
112         };
113
114         self.tcx.sess.span_err(e.span, format!("mutable statics are not allowed \
115                                                 to have {}", suffix)[]);
116     }
117
118     fn check_static_type(&self, e: &ast::Expr) {
119         let ty = ty::node_id_to_type(self.tcx, e.id);
120         let infcx = infer::new_infer_ctxt(self.tcx);
121         let mut fulfill_cx = traits::FulfillmentContext::new();
122         let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
123         fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
124         let env = ty::empty_parameter_environment(self.tcx);
125         match fulfill_cx.select_all_or_error(&infcx, &env) {
126             Ok(()) => { },
127             Err(ref errors) => {
128                 traits::report_fulfillment_errors(&infcx, errors);
129             }
130         }
131     }
132 }
133
134 impl<'a, 'tcx, 'v> Visitor<'v> for CheckStaticVisitor<'a, 'tcx> {
135     fn visit_item(&mut self, i: &ast::Item) {
136         debug!("visit_item(item={})", pprust::item_to_string(i));
137         match i.node {
138             ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
139                 self.check_static_type(&**expr);
140                 self.with_mode(InStatic, |v| v.visit_expr(&**expr));
141             }
142             ast::ItemStatic(_, ast::MutMutable, ref expr) => {
143                 self.check_static_mut_type(&**expr);
144                 self.with_mode(InStaticMut, |v| v.visit_expr(&**expr));
145             }
146             ast::ItemConst(_, ref expr) => {
147                 self.with_mode(InConstant, |v| v.visit_expr(&**expr));
148             }
149             _ => {
150                 self.with_mode(InNothing, |v| visit::walk_item(v, i));
151             }
152         }
153     }
154
155     /// This method is used to enforce the constraints on
156     /// immutable static items. It walks through the *value*
157     /// of the item walking down the expression and evaluating
158     /// every nested expression. if the expression is not part
159     /// of a static item, this method does nothing but walking
160     /// down through it.
161     fn visit_expr(&mut self, e: &ast::Expr) {
162         if self.mode == InNothing {
163             return visit::walk_expr(self, e);
164         }
165
166         let node_ty = ty::node_id_to_type(self.tcx, e.id);
167
168         match node_ty.sty {
169             ty::ty_struct(did, _) |
170             ty::ty_enum(did, _) if ty::has_dtor(self.tcx, did) => {
171                 self.tcx.sess.span_err(e.span,
172                                        format!("{} are not allowed to have \
173                                                 destructors", self.msg())[])
174             }
175             _ => {}
176         }
177
178         // statics cannot be consumed by value at any time, that would imply
179         // that they're an initializer (what a const is for) or kept in sync
180         // over time (not feasible), so deny it outright.
181         if self.checker.static_consumptions.remove(&e.id) {
182             self.tcx.sess.span_err(e.span, "cannot refer to other statics by \
183                                             value, use the address-of operator \
184                                             or a constant instead");
185         }
186
187         // Borrowed statics can specifically *only* have their address taken,
188         // not any number of other borrows such as borrowing fields, reading
189         // elements of an array, etc.
190         if self.checker.static_interior_borrows.remove(&e.id) {
191             self.tcx.sess.span_err(e.span, "cannot refer to the interior of \
192                                             another static, use a constant \
193                                             instead");
194         }
195
196         // constants cannot be borrowed if they contain interior mutability as
197         // it means that our "silent insertion of statics" could change
198         // initializer values (very bad).
199         if self.checker.const_borrows.remove(&e.id) {
200             let node_ty = ty::node_id_to_type(self.tcx, e.id);
201             let tcontents = ty::type_contents(self.tcx, node_ty);
202             if tcontents.interior_unsafe() {
203                 self.tcx.sess.span_err(e.span, "cannot borrow a constant which \
204                                                 contains interior mutability, \
205                                                 create a static instead");
206             }
207         }
208
209         // local variables in a block expression in a static context (i.e. being
210         // assigned to a static variable) cannot be borrowed.
211         if self.checker.static_local_borrows.remove(&e.id) {
212             self.tcx.sess.span_err(e.span, "cannot borrow a local variable inside \
213                                             a static block, define a separate static \
214                                             instead");
215         }
216
217         match e.node {
218             ast::ExprAddrOf(ast::MutMutable, _) => {
219                 if self.mode != InStaticMut {
220                     span_err!(self.tcx.sess, e.span, E0020,
221                               "{} are not allowed to have mutable references",
222                               self.msg());
223                 }
224             },
225             ast::ExprBox(..) |
226             ast::ExprUnary(ast::UnUniq, _) => {
227                 span_err!(self.tcx.sess, e.span, E0022,
228                           "{} are not allowed to have custom pointers",
229                           self.msg());
230             }
231             ast::ExprPath(..) => {
232                 match ty::resolve_expr(self.tcx, e) {
233                     def::DefStatic(..) if self.mode == InConstant => {
234                         let msg = "constants cannot refer to other statics, \
235                                    insert an intermediate constant \
236                                    instead";
237                         self.tcx.sess.span_err(e.span, msg[]);
238                     }
239                     _ => {}
240                 }
241             }
242             _ => {}
243         }
244         visit::walk_expr(self, e);
245     }
246 }
247
248 impl<'a,'b,'t,'v> Visitor<'v> for GlobalVisitor<'a,'b,'t> {
249     fn visit_item(&mut self, item: &ast::Item) {
250         match item.node {
251             ast::ItemConst(_, ref e) |
252             ast::ItemStatic(_, _, ref e) => {
253                 let GlobalVisitor(ref mut v) = *self;
254                 v.consume_expr(&**e);
255             }
256             _ => {}
257         }
258         visit::walk_item(self, item);
259     }
260 }
261
262 impl<'tcx> euv::Delegate<'tcx> for GlobalChecker {
263     fn consume(&mut self,
264                consume_id: ast::NodeId,
265                _consume_span: Span,
266                cmt: mc::cmt,
267                _mode: euv::ConsumeMode) {
268         let mut cur = &cmt;
269         loop {
270             match cur.cat {
271                 mc::cat_static_item => {
272                     self.static_consumptions.insert(consume_id);
273                     break
274                 }
275                 mc::cat_deref(ref cmt, _, _) |
276                 mc::cat_downcast(ref cmt, _) |
277                 mc::cat_interior(ref cmt, _) => cur = cmt,
278
279                 mc::cat_rvalue(..) |
280                 mc::cat_upvar(..) |
281                 mc::cat_local(..) => break,
282             }
283         }
284     }
285     fn borrow(&mut self,
286               borrow_id: ast::NodeId,
287               _borrow_span: Span,
288               cmt: mc::cmt,
289               _loan_region: ty::Region,
290               _bk: ty::BorrowKind,
291               _loan_cause: euv::LoanCause) {
292         let mut cur = &cmt;
293         let mut is_interior = false;
294         loop {
295             match cur.cat {
296                 mc::cat_rvalue(..) => {
297                     self.const_borrows.insert(borrow_id);
298                     break
299                 }
300                 mc::cat_static_item => {
301                     if is_interior {
302                         self.static_interior_borrows.insert(borrow_id);
303                     }
304                     break
305                 }
306                 mc::cat_deref(ref cmt, _, _) |
307                 mc::cat_interior(ref cmt, _) => {
308                     is_interior = true;
309                     cur = cmt;
310                 }
311
312                 mc::cat_downcast(..) |
313                 mc::cat_upvar(..) => unreachable!(),
314
315                 mc::cat_local(..) => {
316                     self.static_local_borrows.insert(borrow_id);
317                     break
318                 }
319             }
320         }
321     }
322
323     fn decl_without_init(&mut self,
324                          _id: ast::NodeId,
325                          _span: Span) {}
326     fn mutate(&mut self,
327               _assignment_id: ast::NodeId,
328               _assignment_span: Span,
329               _assignee_cmt: mc::cmt,
330               _mode: euv::MutateMode) {}
331
332     fn matched_pat(&mut self,
333                    _: &ast::Pat,
334                    _: mc::cmt,
335                    _: euv::MatchMode) {}
336
337     fn consume_pat(&mut self,
338                    _consume_pat: &ast::Pat,
339                    _cmt: mc::cmt,
340                    _mode: euv::ConsumeMode) {}
341 }