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

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri Apr 25 22:40:04 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  
  12  use driver::session::Session;
  13  use middle::resolve;
  14  use middle::ty;
  15  use middle::typeck;
  16  use util::ppaux;
  17  
  18  use syntax::ast::*;
  19  use syntax::{ast_util, ast_map};
  20  use syntax::visit::Visitor;
  21  use syntax::visit;
  22  
  23  pub struct CheckCrateVisitor<'a> {
  24      tcx: &'a ty::ctxt,
  25  }
  26  
  27  impl<'a> Visitor<bool> for CheckCrateVisitor<'a> {
  28      fn visit_item(&mut self, i&Item, envbool) {
  29          check_item(self, i, env);
  30      }
  31      fn visit_pat(&mut self, p&Pat, envbool) {
  32          check_pat(self, p, env);
  33      }
  34      fn visit_expr(&mut self, ex&Expr, envbool) {
  35          check_expr(self, ex, env);
  36      }
  37  }
  38  
  39  pub fn check_crate(krate: &Crate, tcx: &ty::ctxt) {
  40      visit::walk_crate(&mut CheckCrateVisitor { tcx: tcx }, krate, false);
  41      tcx.sess.abort_if_errors();
  42  }
  43  
  44  fn check_item(v: &mut CheckCrateVisitor, it: &Item, _is_const: bool) {
  45      match it.node {
  46          ItemStatic(_, _, ex) => {
  47              v.visit_expr(ex, true);
  48              check_item_recursion(&v.tcx.sess, &v.tcx.map, &v.tcx.def_map, it);
  49          }
  50          ItemEnum(ref enum_definition, _) => {
  51              for var in (*enum_definition).variants.iter() {
  52                  for ex in var.node.disr_expr.iter() {
  53                      v.visit_expr(*ex, true);
  54                  }
  55              }
  56          }
  57          _ => visit::walk_item(v, it, false)
  58      }
  59  }
  60  
  61  fn check_pat(v: &mut CheckCrateVisitor, p: &Pat, _is_const: bool) {
  62      fn is_str(e: &Expr) -> bool {
  63          match e.node {
  64              ExprVstore(expr, ExprVstoreUniq) => {
  65                  match expr.node {
  66                      ExprLit(lit) => ast_util::lit_is_str(lit),
  67                      _ => false,
  68                  }
  69              }
  70              _ => false,
  71          }
  72      }
  73      match p.node {
  74        // Let through plain ~-string literals here
  75        PatLit(a) => if !is_str(a) { v.visit_expr(a, true); },
  76        PatRange(a, b) => {
  77          if !is_str(a) { v.visit_expr(a, true); }
  78          if !is_str(b) { v.visit_expr(b, true); }
  79        }
  80        _ => visit::walk_pat(v, p, false)
  81      }
  82  }
  83  
  84  fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) {
  85      if is_const {
  86          match e.node {
  87            ExprUnary(UnDeref, _) => { }
  88            ExprUnary(UnBox, _) | ExprUnary(UnUniq, _) => {
  89              v.tcx.sess.span_err(e.span,
  90                                  "cannot do allocations in constant expressions");
  91              return;
  92            }
  93            ExprLit(lit) if ast_util::lit_is_str(lit) => {}
  94            ExprBinary(..) | ExprUnary(..) => {
  95              let method_call = typeck::MethodCall::expr(e.id);
  96              if v.tcx.method_map.borrow().contains_key(&method_call) {
  97                  v.tcx.sess.span_err(e.span, "user-defined operators are not \
  98                                               allowed in constant expressions");
  99              }
 100            }
 101            ExprLit(_) => (),
 102            ExprCast(_, _) => {
 103              let ety = ty::expr_ty(v.tcx, e);
 104              if !ty::type_is_numeric(ety) && !ty::type_is_unsafe_ptr(ety) {
 105                  v.tcx.sess.span_err(e.span, "can not cast to `".to_owned() +
 106                                                ppaux::ty_to_str(v.tcx, ety) +
 107                                               "` in a constant expression");
 108              }
 109            }
 110            ExprPath(ref pth) => {
 111              // NB: In the future you might wish to relax this slightly
 112              // to handle on-demand instantiation of functions via
 113              // foo::<bar> in a const. Currently that is only done on
 114              // a path in trans::callee that only works in block contexts.
 115              if !pth.segments.iter().all(|segment| segment.types.is_empty()) {
 116                  v.tcx.sess.span_err(e.span,
 117                                      "paths in constants may only refer to \
 118                                       items without type parameters");
 119              }
 120              match v.tcx.def_map.borrow().find(&e.id) {
 121                Some(&DefStatic(..)) |
 122                Some(&DefFn(_, _)) |
 123                Some(&DefVariant(_, _, _)) |
 124                Some(&DefStruct(_)) => { }
 125  
 126                Some(&def) => {
 127                  debug!("(checking const) found bad def: {:?}", def);
 128                  v.tcx.sess.span_err(e.span,
 129                      "paths in constants may only refer to \
 130                       constants or functions");
 131                }
 132                None => {
 133                  v.tcx.sess.span_bug(e.span, "unbound path in const?!");
 134                }
 135              }
 136            }
 137            ExprCall(callee, _) => {
 138              match v.tcx.def_map.borrow().find(&callee.id) {
 139                  Some(&DefStruct(..)) => {}    // OK.
 140                  Some(&DefVariant(..)) => {}    // OK.
 141                  _ => {
 142                      v.tcx.sess.span_err(e.span,
 143                          "function calls in constants are limited to \
 144                           struct and enum constructors");
 145                  }
 146              }
 147            }
 148            ExprVstore(_, ExprVstoreMutSlice) |
 149            ExprVstore(_, ExprVstoreSlice) |
 150            ExprVec(_) |
 151            ExprAddrOf(MutImmutable, _) |
 152            ExprParen(..) |
 153            ExprField(..) |
 154            ExprIndex(..) |
 155            ExprTup(..) |
 156            ExprRepeat(..) |
 157            ExprStruct(..) => { }
 158            ExprAddrOf(..) => {
 159                  v.tcx.sess.span_err(e.span,
 160                      "references in constants may only refer to \
 161                       immutable values");
 162            },
 163            ExprVstore(_, ExprVstoreUniq) => {
 164                v.tcx.sess.span_err(e.span, "cannot allocate vectors in constant expressions")
 165            },
 166  
 167            _ => {
 168              v.tcx.sess.span_err(e.span,
 169                                  "constant contains unimplemented expression type");
 170              return;
 171            }
 172          }
 173      }
 174      visit::walk_expr(v, e, is_const);
 175  }
 176  
 177  struct CheckItemRecursionVisitor<'a> {
 178      root_it: &'a Item,
 179      sess: &'a Session,
 180      ast_map: &'a ast_map::Map,
 181      def_map: &'a resolve::DefMap,
 182      idstack: Vec<NodeId> }
 183  
 184  // Make sure a const item doesn't recursively refer to itself
 185  // FIXME: Should use the dependency graph when it's available (#1356)
 186  pub fn check_item_recursion<'a>(sess: &'a Session,
 187                                  ast_map: &'a ast_map::Map,
 188                                  def_map: &'a resolve::DefMap,
 189                                  it: &'a Item) {
 190  
 191      let mut visitor = CheckItemRecursionVisitor {
 192          root_it: it,
 193          sess: sess,
 194          ast_map: ast_map,
 195          def_map: def_map,
 196          idstack: Vec::new()
 197      };
 198      visitor.visit_item(it, ());
 199  }
 200  
 201  impl<'a> Visitor<()> for CheckItemRecursionVisitor<'a> {
 202      fn visit_item(&mut self, it&Item, _()) {
 203          if self.idstack.iter().any(|x| x == &(it.id)) {
 204              self.sess.span_fatal(self.root_it.span, "recursive constant");
 205          }
 206          self.idstack.push(it.id);
 207          visit::walk_item(self, it, ());
 208          self.idstack.pop();
 209      }
 210  
 211      fn visit_expr(&mut self, e&Expr, _()) {
 212          match e.node {
 213              ExprPath(..) => {
 214                  match self.def_map.borrow().find(&e.id) {
 215                      Some(&DefStatic(def_id, _)) if
 216                              ast_util::is_local(def_id) => {
 217                          self.visit_item(self.ast_map.expect_item(def_id.node), ());
 218                      }
 219                      _ => ()
 220                  }
 221              },
 222              _ => ()
 223          }
 224          visit::walk_expr(self, e, ());
 225      }
 226  }