]> git.lizzy.rs Git - rust.git/commitdiff
Forbid certain types for static items
authorFlavio Percoco <flaper87@gmail.com>
Wed, 26 Feb 2014 18:22:41 +0000 (19:22 +0100)
committerFlavio Percoco <flaper87@gmail.com>
Thu, 27 Feb 2014 17:09:33 +0000 (18:09 +0100)
- 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

src/librustc/driver/driver.rs
src/librustc/lib.rs
src/librustc/middle/check_static.rs [new file with mode: 0644]
src/librustc/middle/ty.rs
src/test/compile-fail/check-static-values-constraints.rs [new file with mode: 0644]
src/test/compile-fail/issue-10487.rs

index 5f9910340c4860e8c4d301a5f65d04c00e9679c1..4c552acc936922eed6535bd364b9aff18c2c5a47 100644 (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));
index 9806c39345ba737e916ab9a566864c15b2c37dd7..97718849e631f259351af09ea17340470ebf7c3b 100644 (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;
diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs
new file mode 100644 (file)
index 0000000..86078c4
--- /dev/null
@@ -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);
+            }
+        }
+    }
+}
index a52b1c62923c5eaf30fe872f05a1608799f2c5c3..8ea7f75d2b44e9d0c38d5ff12698871901a6be8d 100644 (file)
@@ -1950,6 +1950,10 @@ pub fn owns_managed(&self) -> bool {
         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 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+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)
 }
diff --git a/src/test/compile-fail/check-static-values-constraints.rs b/src/test/compile-fail/check-static-values-constraints.rs
new file mode 100644 (file)
index 0000000..1248f56
--- /dev/null
@@ -0,0 +1,128 @@
+// 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.
+#[feature(managed_boxes)];
+
+// Verifies all possible restrictions for static items values.
+
+struct WithDtor;
+
+impl Drop for WithDtor {
+    fn drop(&mut self) {}
+}
+
+// This enum will be used to test the following rules:
+// 1. Variants are safe for static
+// 2. Expr calls are allowed as long as they arguments are safe
+// 3. Expr calls with unsafe arguments for static items are rejected
+enum SafeEnum {
+    Variant1,
+    Variant2(int),
+    Variant3(WithDtor),
+    Variant4(~str)
+}
+
+// These should be ok
+static STATIC1: SafeEnum = Variant1;
+static STATIC2: SafeEnum = Variant2(0);
+
+// This one should fail
+static STATIC3: SafeEnum = Variant3(WithDtor);
+//~^ ERROR static items are not allowed to have destructors
+
+
+// This enum will be used to test that variants
+// are considered unsafe if their enum type implements
+// a destructor.
+enum UnsafeEnum {
+    Variant5,
+    Variant6(int)
+}
+
+impl Drop for UnsafeEnum {
+    fn drop(&mut self) {}
+}
+
+
+static STATIC4: UnsafeEnum = Variant5;
+//~^ ERROR static items are not allowed to have destructors
+static STATIC5: UnsafeEnum = Variant6(0);
+//~^ ERROR static items are not allowed to have destructors
+
+
+struct SafeStruct {
+    field1: SafeEnum,
+    field2: SafeEnum,
+}
+
+
+// Struct fields are safe, hence this static should be safe
+static STATIC6: SafeStruct = SafeStruct{field1: Variant1, field2: Variant2(0)};
+
+// field2 has an unsafe value, hence this should fail
+static STATIC7: SafeStruct = SafeStruct{field1: Variant1, field2: Variant3(WithDtor)};
+//~^ ERROR static items are not allowed to have destructors
+
+// Test variadic constructor for structs. The base struct should be examined
+// as well as every field persent in the constructor.
+// This example shouldn't fail because all the fields are safe.
+static STATIC8: SafeStruct = SafeStruct{field1: Variant1,
+                                        ..SafeStruct{field1: Variant1, field2: Variant1}};
+
+// This example should fail because field1 in the base struct is not safe
+static STATIC9: SafeStruct = SafeStruct{field1: Variant1,
+                                        ..SafeStruct{field1: Variant3(WithDtor), field2: Variant1}};
+//~^ ERROR static items are not allowed to have destructors
+
+struct UnsafeStruct;
+
+impl Drop for UnsafeStruct {
+    fn drop(&mut self) {}
+}
+
+// Types with destructors are not allowed for statics
+static STATIC10: UnsafeStruct = UnsafeStruct;
+//~^ ERROR static items are not allowed to have destructor
+
+static STATIC11: ~str = ~"Owned pointers are not allowed either";
+//~^ ERROR static items are not allowed to have owned pointers
+
+// The following examples test that mutable structs are just forbidden
+// to have types with destructors
+// These should fail
+static mut STATIC12: UnsafeStruct = UnsafeStruct;
+//~^ ERROR mutable static items are not allowed to have destructors
+
+static mut STATIC13: SafeStruct = SafeStruct{field1: Variant1, field2: Variant3(WithDtor)};
+//~^ ERROR mutable static items are not allowed to have destructors
+
+static mut STATIC14: SafeStruct = SafeStruct{field1: Variant1, field2: Variant4(~"str")};
+//~^ ERROR mutable static items are not allowed to have destructors
+
+static STATIC15: &'static [~str] = &'static [~"str", ~"str"];
+//~^ ERROR static items are not allowed to have owned pointers
+//~^^ ERROR static items are not allowed to have owned pointers
+
+static STATIC16: (~str, ~str) = (~"str", ~"str");
+//~^ ERROR static items are not allowed to have owned pointers
+//~^^ ERROR static items are not allowed to have owned pointers
+
+static mut STATIC17: SafeEnum = Variant1;
+//~^ ERROR mutable static items are not allowed to have destructors
+
+static STATIC18: @SafeStruct = @SafeStruct{field1: Variant1, field2: Variant2(0)};
+//~^ ERROR static items are not allowed to have managed pointers
+
+static STATIC19: ~int = box 3;
+//~^ ERROR static items are not allowed to have owned pointers
+
+pub fn main() {
+    let y = { static x: ~int = ~3; x };
+    //~^ ERROR static items are not allowed to have owned pointers
+}
index 3fc6106f7480b6455dc4489b50e8194ed0c3b285..01fb2ea9427370af28d396471b98094259758e4e 100644 (file)
@@ -10,6 +10,6 @@
 
 #[feature(managed_boxes)];
 
-static x: ~[int] = ~[123, 456]; //~ ERROR: cannot allocate vectors in constant expressions
+static x: ~[int] = ~[123, 456]; //~ ERROR: static items are not allowed to have owned pointers
 
 fn main() {}