(index<- )        ./librustc/middle/effect.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2012-2013 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  //! Enforces the Rust effect system. Currently there is just one effect,
  12  /// `unsafe`.
  13  
  14  use middle::ty;
  15  use middle::typeck::MethodCall;
  16  use util::ppaux;
  17  
  18  use syntax::ast;
  19  use syntax::codemap::Span;
  20  use syntax::visit;
  21  use syntax::visit::Visitor;
  22  
  23  #[deriving(Eq)]
  24  enum UnsafeContext {
  25      SafeContext,
  26      UnsafeFn,
  27      UnsafeBlock(ast::NodeId),
  28  }
  29  
  30  fn type_is_unsafe_function(tyty::t) -> bool {
  31      match ty::get(ty).sty {
  32          ty::ty_bare_fn(ref f) => f.fn_style == ast::UnsafeFn,
  33          ty::ty_closure(ref f) => f.fn_style == ast::UnsafeFn,
  34          _ => false,
  35      }
  36  }
  37  
  38  struct EffectCheckVisitor<'a> {
  39      tcx: &'a ty::ctxt,
  40  
  41      /// Whether we're in an unsafe context.
  42      unsafe_context: UnsafeContext,
  43  }
  44  
  45  impl<'a> EffectCheckVisitor<'a> {
  46      fn require_unsafe(&mut self, spanSpan, description&str) {
  47          match self.unsafe_context {
  48              SafeContext => {
  49                  // Report an error.
  50                  self.tcx.sess.span_err(span,
  51                                    format!("{} requires unsafe function or block",
  52                                         description))
  53              }
  54              UnsafeBlock(block_id) => {
  55                  // OK, but record this.
  56                  debug!("effect: recording unsafe block as used: {:?}", block_id);
  57                  self.tcx.used_unsafe.borrow_mut().insert(block_id);
  58              }
  59              UnsafeFn => {}
  60          }
  61      }
  62  
  63      fn check_str_index(&mut self, e@ast::Expr) {
  64          let base_type = match e.node {
  65              ast::ExprIndex(base, _) => ty::node_id_to_type(self.tcx, base.id),
  66              _ => return
  67          };
  68          debug!("effect: checking index with base type {}",
  69                  ppaux::ty_to_str(self.tcx, base_type));
  70          match ty::get(base_type).sty {
  71              ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty {
  72                  ty::ty_str => {
  73                      self.tcx.sess.span_err(e.span,
  74                          "modification of string types is not allowed");
  75                  }
  76                  _ => {}
  77              },
  78              ty::ty_str => {
  79                  self.tcx.sess.span_err(e.span,
  80                      "modification of string types is not allowed");
  81              }
  82              _ => {}
  83          }
  84      }
  85  }
  86  
  87  impl<'a> Visitor<()> for EffectCheckVisitor<'a> {
  88      fn visit_fn(&mut self, fn_kind&visit::FnKind, fn_decl&ast::FnDecl,
  89                  block&ast::Block, spanSpan, node_idast::NodeId, _:()) {
  90  
  91          let (is_item_fn, is_unsafe_fn) = match *fn_kind {
  92              visit::FkItemFn(_, _, fn_style, _) =>
  93                  (true, fn_style == ast::UnsafeFn),
  94              visit::FkMethod(_, _, method) =>
  95                  (true, method.fn_style == ast::UnsafeFn),
  96              _ => (false, false),
  97          };
  98  
  99          let old_unsafe_context = self.unsafe_context;
 100          if is_unsafe_fn {
 101              self.unsafe_context = UnsafeFn
 102          } else if is_item_fn {
 103              self.unsafe_context = SafeContext
 104          }
 105  
 106          visit::walk_fn(self, fn_kind, fn_decl, block, span, node_id, ());
 107  
 108          self.unsafe_context = old_unsafe_context
 109      }
 110  
 111      fn visit_block(&mut self, block&ast::Block, _:()) {
 112          let old_unsafe_context = self.unsafe_context;
 113          match block.rules {
 114              ast::DefaultBlock => {}
 115              ast::UnsafeBlock(source) => {
 116                  // By default only the outermost `unsafe` block is
 117                  // "used" and so nested unsafe blocks are pointless
 118                  // (the inner ones are unnecessary and we actually
 119                  // warn about them). As such, there are two cases when
 120                  // we need to create a new context, when we're
 121                  // - outside `unsafe` and found a `unsafe` block
 122                  //   (normal case)
 123                  // - inside `unsafe` but found an `unsafe` block
 124                  //   created internally to the compiler
 125                  //
 126                  // The second case is necessary to ensure that the
 127                  // compiler `unsafe` blocks don't accidentally "use"
 128                  // external blocks (e.g. `unsafe { println("") }`,
 129                  // expands to `unsafe { ... unsafe { ... } }` where
 130                  // the inner one is compiler generated).
 131                  if self.unsafe_context == SafeContext || source == ast::CompilerGenerated {
 132                      self.unsafe_context = UnsafeBlock(block.id)
 133                  }
 134              }
 135          }
 136  
 137          visit::walk_block(self, block, ());
 138  
 139          self.unsafe_context = old_unsafe_context
 140      }
 141  
 142      fn visit_expr(&mut self, expr&ast::Expr, _:()) {
 143          match expr.node {
 144              ast::ExprMethodCall(_, _, _) => {
 145                  let method_call = MethodCall::expr(expr.id);
 146                  let base_type = self.tcx.method_map.borrow().get(&method_call).ty;
 147                  debug!("effect: method call case, base type is {}",
 148                         ppaux::ty_to_str(self.tcx, base_type));
 149                  if type_is_unsafe_function(base_type) {
 150                      self.require_unsafe(expr.span,
 151                                          "invocation of unsafe method")
 152                  }
 153              }
 154              ast::ExprCall(base, _) => {
 155                  let base_type = ty::node_id_to_type(self.tcx, base.id);
 156                  debug!("effect: call case, base type is {}",
 157                         ppaux::ty_to_str(self.tcx, base_type));
 158                  if type_is_unsafe_function(base_type) {
 159                      self.require_unsafe(expr.span, "call to unsafe function")
 160                  }
 161              }
 162              ast::ExprUnary(ast::UnDeref, base) => {
 163                  let base_type = ty::node_id_to_type(self.tcx, base.id);
 164                  debug!("effect: unary case, base type is {}",
 165                          ppaux::ty_to_str(self.tcx, base_type));
 166                  match ty::get(base_type).sty {
 167                      ty::ty_ptr(_) => {
 168                          self.require_unsafe(expr.span,
 169                                              "dereference of unsafe pointer")
 170                      }
 171                      _ => {}
 172                  }
 173              }
 174              ast::ExprAssign(base, _) | ast::ExprAssignOp(_, base, _) => {
 175                  self.check_str_index(base);
 176              }
 177              ast::ExprAddrOf(ast::MutMutable, base) => {
 178                  self.check_str_index(base);
 179              }
 180              ast::ExprInlineAsm(..) => {
 181                  self.require_unsafe(expr.span, "use of inline assembly")
 182              }
 183              ast::ExprPath(..) => {
 184                  match ty::resolve_expr(self.tcx, expr) {
 185                      ast::DefStatic(_, true) => {
 186                          self.require_unsafe(expr.span, "use of mutable static")
 187                      }
 188                      _ => {}
 189                  }
 190              }
 191              _ => {}
 192          }
 193  
 194          visit::walk_expr(self, expr, ());
 195      }
 196  }
 197  
 198  pub fn check_crate(tcx: &ty::ctxt, krate: &ast::Crate) {
 199      let mut visitor = EffectCheckVisitor {
 200          tcx: tcx,
 201          unsafe_context: SafeContext,
 202      };
 203  
 204      visit::walk_crate(&mut visitor, krate, ());
 205  }