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.
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.
11 // Verifies that the types and values of static items
12 // are safe. The rules enforced by this module are:
14 // - For each *mutable* static item, it checks that its **type**:
15 // - doesn't have a destructor
16 // - doesn't own an owned pointer
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
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
32 use middle::mem_categorization as mc;
33 use middle::expr_use_visitor as euv;
34 use util::nodemap::NodeSet;
37 use syntax::print::pprust;
38 use syntax::visit::Visitor;
39 use syntax::codemap::Span;
42 #[derive(Copy, Eq, PartialEq)]
50 struct CheckStaticVisitor<'a, 'tcx: 'a> {
51 tcx: &'a ty::ctxt<'tcx>,
53 checker: &'a mut GlobalChecker,
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,
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(),
73 let param_env = ty::empty_parameter_environment(tcx);
74 let visitor = euv::ExprUseVisitor::new(&mut checker, ¶m_env);
75 visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate());
77 visit::walk_crate(&mut CheckStaticVisitor {
80 checker: &mut checker,
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>),
94 fn msg(&self) -> &'static str {
96 InConstant => "constants",
97 InStaticMut | InStatic => "statics",
98 InNothing => unreachable!(),
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);
106 let suffix = if tcontents.has_dtor() {
108 } else if tcontents.owns_owned() {
114 self.tcx.sess.span_err(e.span, format!("mutable statics are not allowed \
115 to have {}", suffix)[]);
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) {
128 traits::report_fulfillment_errors(&infcx, errors);
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));
138 ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
139 self.check_static_type(&**expr);
140 self.with_mode(InStatic, |v| v.visit_expr(&**expr));
142 ast::ItemStatic(_, ast::MutMutable, ref expr) => {
143 self.check_static_mut_type(&**expr);
144 self.with_mode(InStaticMut, |v| v.visit_expr(&**expr));
146 ast::ItemConst(_, ref expr) => {
147 self.with_mode(InConstant, |v| v.visit_expr(&**expr));
150 self.with_mode(InNothing, |v| visit::walk_item(v, i));
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
161 fn visit_expr(&mut self, e: &ast::Expr) {
162 if self.mode == InNothing {
163 return visit::walk_expr(self, e);
166 let node_ty = ty::node_id_to_type(self.tcx, e.id);
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())[])
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");
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 \
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");
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 \
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",
226 ast::ExprUnary(ast::UnUniq, _) => {
227 span_err!(self.tcx.sess, e.span, E0022,
228 "{} are not allowed to have custom pointers",
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 \
237 self.tcx.sess.span_err(e.span, msg[]);
244 visit::walk_expr(self, e);
248 impl<'a,'b,'t,'v> Visitor<'v> for GlobalVisitor<'a,'b,'t> {
249 fn visit_item(&mut self, item: &ast::Item) {
251 ast::ItemConst(_, ref e) |
252 ast::ItemStatic(_, _, ref e) => {
253 let GlobalVisitor(ref mut v) = *self;
254 v.consume_expr(&**e);
258 visit::walk_item(self, item);
262 impl<'tcx> euv::Delegate<'tcx> for GlobalChecker {
263 fn consume(&mut self,
264 consume_id: ast::NodeId,
267 _mode: euv::ConsumeMode) {
271 mc::cat_static_item => {
272 self.static_consumptions.insert(consume_id);
275 mc::cat_deref(ref cmt, _, _) |
276 mc::cat_downcast(ref cmt, _) |
277 mc::cat_interior(ref cmt, _) => cur = cmt,
281 mc::cat_local(..) => break,
286 borrow_id: ast::NodeId,
289 _loan_region: ty::Region,
291 _loan_cause: euv::LoanCause) {
293 let mut is_interior = false;
296 mc::cat_rvalue(..) => {
297 self.const_borrows.insert(borrow_id);
300 mc::cat_static_item => {
302 self.static_interior_borrows.insert(borrow_id);
306 mc::cat_deref(ref cmt, _, _) |
307 mc::cat_interior(ref cmt, _) => {
312 mc::cat_downcast(..) |
313 mc::cat_upvar(..) => unreachable!(),
315 mc::cat_local(..) => {
316 self.static_local_borrows.insert(borrow_id);
323 fn decl_without_init(&mut self,
327 _assignment_id: ast::NodeId,
328 _assignment_span: Span,
329 _assignee_cmt: mc::cmt,
330 _mode: euv::MutateMode) {}
332 fn matched_pat(&mut self,
335 _: euv::MatchMode) {}
337 fn consume_pat(&mut self,
338 _consume_pat: &ast::Pat,
340 _mode: euv::ConsumeMode) {}