about summary refs log tree commit diff
path: root/src/librustc_passes
diff options
context:
space:
mode:
authorOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>2016-01-21 10:52:37 +0100
committerOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>2016-01-21 10:52:37 +0100
commitc124deca7b3d93e72b5d849b392a5bea83eacfb5 (patch)
tree27d1033733ebf043f634a2621d33de0e05077502 /src/librustc_passes
parentd5ec3ab6851eefaf87ea2247c2ce373f1e905351 (diff)
downloadrust-c124deca7b3d93e72b5d849b392a5bea83eacfb5.tar.gz
rust-c124deca7b3d93e72b5d849b392a5bea83eacfb5.zip
move more checks out of librustc
Diffstat (limited to 'src/librustc_passes')
-rw-r--r--src/librustc_passes/consts.rs916
-rw-r--r--src/librustc_passes/diagnostics.rs542
-rw-r--r--src/librustc_passes/lib.rs7
-rw-r--r--src/librustc_passes/loops.rs80
-rw-r--r--src/librustc_passes/rvalues.rs105
-rw-r--r--src/librustc_passes/static_recursion.rs290
6 files changed, 1940 insertions, 0 deletions
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
new file mode 100644
index 00000000000..60cc658eeca
--- /dev/null
+++ b/src/librustc_passes/consts.rs
@@ -0,0 +1,916 @@
+// Copyright 2012-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 const and 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 a box
+//
+// - For each *immutable* static item, it checks that its **value**:
+//       - doesn't own a box
+//       - doesn't contain a struct literal or a call to an enum variant / struct constructor where
+//           - the type of the struct/enum has a dtor
+//
+// Rules Enforced Elsewhere:
+// - It's not possible to take the address of a static item with unsafe interior. This is enforced
+// by borrowck::gather_loans
+
+use rustc::dep_graph::DepNode;
+use rustc::middle::ty::cast::{CastKind};
+use rustc::middle::const_eval::{self, ConstEvalErr};
+use rustc::middle::const_eval::ErrKind::IndexOpFeatureGated;
+use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
+use rustc::middle::def::Def;
+use rustc::middle::def_id::DefId;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::infer;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::mem_categorization::Categorization;
+use rustc::middle::traits;
+use rustc::middle::ty::{self, Ty};
+use rustc::util::nodemap::NodeMap;
+use rustc::middle::const_qualif::ConstQualif;
+use rustc::lint::builtin::CONST_ERR;
+
+use rustc_front::hir;
+use syntax::ast;
+use syntax::codemap::Span;
+use syntax::feature_gate::UnstableFeatures;
+use rustc_front::intravisit::{self, FnKind, Visitor};
+
+use std::collections::hash_map::Entry;
+use std::cmp::Ordering;
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+enum Mode {
+    Const,
+    ConstFn,
+    Static,
+    StaticMut,
+
+    // An expression that occurs outside of any constant context
+    // (i.e. `const`, `static`, array lengths, etc.). The value
+    // can be variable at runtime, but will be promotable to
+    // static memory if we can prove it is actually constant.
+    Var,
+}
+
+struct CheckCrateVisitor<'a, 'tcx: 'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    mode: Mode,
+    qualif: ConstQualif,
+    rvalue_borrows: NodeMap<hir::Mutability>
+}
+
+impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
+    fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
+        F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
+    {
+        let (old_mode, old_qualif) = (self.mode, self.qualif);
+        self.mode = mode;
+        self.qualif = ConstQualif::empty();
+        let r = f(self);
+        self.mode = old_mode;
+        self.qualif = old_qualif;
+        r
+    }
+
+    fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where
+        F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'b, 'tcx>) -> R,
+    {
+        let param_env = match item_id {
+            Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
+            None => self.tcx.empty_parameter_environment()
+        };
+
+        let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
+
+        f(&mut euv::ExprUseVisitor::new(self, &infcx))
+    }
+
+    fn global_expr(&mut self, mode: Mode, expr: &hir::Expr) -> ConstQualif {
+        assert!(mode != Mode::Var);
+        match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
+            Entry::Occupied(entry) => return *entry.get(),
+            Entry::Vacant(entry) => {
+                // Prevent infinite recursion on re-entry.
+                entry.insert(ConstQualif::empty());
+            }
+        }
+        self.with_mode(mode, |this| {
+            this.with_euv(None, |euv| euv.consume_expr(expr));
+            this.visit_expr(expr);
+            this.qualif
+        })
+    }
+
+    fn fn_like(&mut self,
+               fk: FnKind,
+               fd: &hir::FnDecl,
+               b: &hir::Block,
+               s: Span,
+               fn_id: ast::NodeId)
+               -> ConstQualif {
+        match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
+            Entry::Occupied(entry) => return *entry.get(),
+            Entry::Vacant(entry) => {
+                // Prevent infinite recursion on re-entry.
+                entry.insert(ConstQualif::empty());
+            }
+        }
+
+        let mode = match fk {
+            FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => {
+                Mode::ConstFn
+            }
+            FnKind::Method(_, m, _) => {
+                if m.constness == hir::Constness::Const {
+                    Mode::ConstFn
+                } else {
+                    Mode::Var
+                }
+            }
+            _ => Mode::Var
+        };
+
+        let qualif = self.with_mode(mode, |this| {
+            this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
+            intravisit::walk_fn(this, fk, fd, b, s);
+            this.qualif
+        });
+
+        // Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
+        // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
+        let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
+
+        self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
+        qualif
+    }
+
+    fn add_qualif(&mut self, qualif: ConstQualif) {
+        self.qualif = self.qualif | qualif;
+    }
+
+    /// Returns true if the call is to a const fn or method.
+    fn handle_const_fn_call(&mut self,
+                            expr: &hir::Expr,
+                            def_id: DefId,
+                            ret_ty: Ty<'tcx>)
+                            -> bool {
+        if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) {
+            if
+                // we are in a static/const initializer
+                self.mode != Mode::Var &&
+
+                // feature-gate is not enabled
+                !self.tcx.sess.features.borrow().const_fn &&
+
+                // this doesn't come from a macro that has #[allow_internal_unstable]
+                !self.tcx.sess.codemap().span_allows_unstable(expr.span)
+            {
+                let mut err = self.tcx.sess.struct_span_err(
+                    expr.span,
+                    "const fns are an unstable feature");
+                fileline_help!(
+                    &mut err,
+                    expr.span,
+                    "in Nightly builds, add `#![feature(const_fn)]` to the crate \
+                     attributes to enable");
+                err.emit();
+            }
+
+            let qualif = self.fn_like(fn_like.kind(),
+                                      fn_like.decl(),
+                                      fn_like.body(),
+                                      fn_like.span(),
+                                      fn_like.id());
+            self.add_qualif(qualif);
+
+            if ret_ty.type_contents(self.tcx).interior_unsafe() {
+                self.add_qualif(ConstQualif::MUTABLE_MEM);
+            }
+
+            true
+        } else {
+            false
+        }
+    }
+
+    fn record_borrow(&mut self, id: ast::NodeId, mutbl: hir::Mutability) {
+        match self.rvalue_borrows.entry(id) {
+            Entry::Occupied(mut entry) => {
+                // Merge the two borrows, taking the most demanding
+                // one, mutability-wise.
+                if mutbl == hir::MutMutable {
+                    entry.insert(mutbl);
+                }
+            }
+            Entry::Vacant(entry) => {
+                entry.insert(mutbl);
+            }
+        }
+    }
+
+    fn msg(&self) -> &'static str {
+        match self.mode {
+            Mode::Const => "constant",
+            Mode::ConstFn => "constant function",
+            Mode::StaticMut | Mode::Static => "static",
+            Mode::Var => unreachable!(),
+        }
+    }
+
+    fn check_static_mut_type(&self, e: &hir::Expr) {
+        let node_ty = self.tcx.node_id_to_type(e.id);
+        let tcontents = node_ty.type_contents(self.tcx);
+
+        let suffix = if tcontents.has_dtor() {
+            "destructors"
+        } else if tcontents.owns_owned() {
+            "boxes"
+        } else {
+            return
+        };
+
+        span_err!(self.tcx.sess, e.span, E0397,
+                 "mutable statics are not allowed to have {}", suffix);
+    }
+
+    fn check_static_type(&self, e: &hir::Expr) {
+        let ty = self.tcx.node_id_to_type(e.id);
+        let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
+        let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
+        let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut();
+        fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
+        match fulfill_cx.select_all_or_error(&infcx) {
+            Ok(()) => { },
+            Err(ref errors) => {
+                traits::report_fulfillment_errors(&infcx, errors);
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
+    fn visit_item(&mut self, i: &hir::Item) {
+        debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id));
+        assert_eq!(self.mode, Mode::Var);
+        match i.node {
+            hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
+                self.check_static_type(&**expr);
+                self.global_expr(Mode::Static, &**expr);
+            }
+            hir::ItemStatic(_, hir::MutMutable, ref expr) => {
+                self.check_static_mut_type(&**expr);
+                self.global_expr(Mode::StaticMut, &**expr);
+            }
+            hir::ItemConst(_, ref expr) => {
+                self.global_expr(Mode::Const, &**expr);
+            }
+            hir::ItemEnum(ref enum_definition, _) => {
+                for var in &enum_definition.variants {
+                    if let Some(ref ex) = var.node.disr_expr {
+                        self.global_expr(Mode::Const, &**ex);
+                    }
+                }
+            }
+            _ => {
+                intravisit::walk_item(self, i);
+            }
+        }
+    }
+
+    fn visit_trait_item(&mut self, t: &'v hir::TraitItem) {
+        match t.node {
+            hir::ConstTraitItem(_, ref default) => {
+                if let Some(ref expr) = *default {
+                    self.global_expr(Mode::Const, &*expr);
+                } else {
+                    intravisit::walk_trait_item(self, t);
+                }
+            }
+            _ => self.with_mode(Mode::Var, |v| intravisit::walk_trait_item(v, t)),
+        }
+    }
+
+    fn visit_impl_item(&mut self, i: &'v hir::ImplItem) {
+        match i.node {
+            hir::ImplItemKind::Const(_, ref expr) => {
+                self.global_expr(Mode::Const, &*expr);
+            }
+            _ => self.with_mode(Mode::Var, |v| intravisit::walk_impl_item(v, i)),
+        }
+    }
+
+    fn visit_fn(&mut self,
+                fk: FnKind<'v>,
+                fd: &'v hir::FnDecl,
+                b: &'v hir::Block,
+                s: Span,
+                fn_id: ast::NodeId) {
+        self.fn_like(fk, fd, b, s, fn_id);
+    }
+
+    fn visit_pat(&mut self, p: &hir::Pat) {
+        match p.node {
+            hir::PatLit(ref lit) => {
+                self.global_expr(Mode::Const, &**lit);
+            }
+            hir::PatRange(ref start, ref end) => {
+                self.global_expr(Mode::Const, &**start);
+                self.global_expr(Mode::Const, &**end);
+
+                match const_eval::compare_lit_exprs(self.tcx, start, end) {
+                    Some(Ordering::Less) |
+                    Some(Ordering::Equal) => {}
+                    Some(Ordering::Greater) => {
+                        span_err!(self.tcx.sess, start.span, E0030,
+                            "lower range bound must be less than or equal to upper");
+                    }
+                    None => {
+                        self.tcx.sess.delay_span_bug(start.span,
+                                                     "non-constant path in constant expr");
+                    }
+                }
+            }
+            _ => intravisit::walk_pat(self, p)
+        }
+    }
+
+    fn visit_block(&mut self, block: &hir::Block) {
+        // Check all statements in the block
+        for stmt in &block.stmts {
+            match stmt.node {
+                hir::StmtDecl(ref decl, _) => {
+                    match decl.node {
+                        hir::DeclLocal(_) => {},
+                        // Item statements are allowed
+                        hir::DeclItem(_) => continue
+                    }
+                }
+                hir::StmtExpr(_, _) => {},
+                hir::StmtSemi(_, _) => {},
+            }
+            self.add_qualif(ConstQualif::NOT_CONST);
+            // anything else should have been caught by check_const_fn
+            assert_eq!(self.mode, Mode::Var);
+        }
+        intravisit::walk_block(self, block);
+    }
+
+    fn visit_expr(&mut self, ex: &hir::Expr) {
+        let mut outer = self.qualif;
+        self.qualif = ConstQualif::empty();
+
+        let node_ty = self.tcx.node_id_to_type(ex.id);
+        check_expr(self, ex, node_ty);
+        check_adjustments(self, ex);
+
+        // Special-case some expressions to avoid certain flags bubbling up.
+        match ex.node {
+            hir::ExprCall(ref callee, ref args) => {
+                for arg in args {
+                    self.visit_expr(&**arg)
+                }
+
+                let inner = self.qualif;
+                self.visit_expr(&**callee);
+                // The callee's size doesn't count in the call.
+                let added = self.qualif - inner;
+                self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
+            }
+            hir::ExprRepeat(ref element, _) => {
+                self.visit_expr(&**element);
+                // The count is checked elsewhere (typeck).
+                let count = match node_ty.sty {
+                    ty::TyArray(_, n) => n,
+                    _ => unreachable!()
+                };
+                // [element; 0] is always zero-sized.
+                if count == 0 {
+                    self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
+                }
+            }
+            hir::ExprMatch(ref discr, ref arms, _) => {
+                // Compute the most demanding borrow from all the arms'
+                // patterns and set that on the discriminator.
+                let mut borrow = None;
+                for pat in arms.iter().flat_map(|arm| &arm.pats) {
+                    let pat_borrow = self.rvalue_borrows.remove(&pat.id);
+                    match (borrow, pat_borrow) {
+                        (None, _) | (_, Some(hir::MutMutable)) => {
+                            borrow = pat_borrow;
+                        }
+                        _ => {}
+                    }
+                }
+                if let Some(mutbl) = borrow {
+                    self.record_borrow(discr.id, mutbl);
+                }
+                intravisit::walk_expr(self, ex);
+            }
+            // Division by zero and overflow checking.
+            hir::ExprBinary(op, _, _) => {
+                intravisit::walk_expr(self, ex);
+                let div_or_rem = op.node == hir::BiDiv || op.node == hir::BiRem;
+                match node_ty.sty {
+                    ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
+                        if !self.qualif.intersects(ConstQualif::NOT_CONST) {
+                            match const_eval::eval_const_expr_partial(
+                                    self.tcx, ex, ExprTypeChecked, None) {
+                                Ok(_) => {}
+                                Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
+                                Err(msg) => {
+                                    self.tcx.sess.add_lint(CONST_ERR, ex.id,
+                                                           msg.span,
+                                                           msg.description().into_owned())
+                                }
+                            }
+                        }
+                    }
+                    _ => {}
+                }
+            }
+            _ => intravisit::walk_expr(self, ex)
+        }
+
+        // Handle borrows on (or inside the autorefs of) this expression.
+        match self.rvalue_borrows.remove(&ex.id) {
+            Some(hir::MutImmutable) => {
+                // Constants cannot be borrowed if they contain interior mutability as
+                // it means that our "silent insertion of statics" could change
+                // initializer values (very bad).
+                // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
+                // propagated from another error, so erroring again would be just noise.
+                let tc = node_ty.type_contents(self.tcx);
+                if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
+                    outer = outer | ConstQualif::NOT_CONST;
+                    if self.mode != Mode::Var {
+                        span_err!(self.tcx.sess, ex.span, E0492,
+                                  "cannot borrow a constant which contains \
+                                   interior mutability, create a static instead");
+                    }
+                }
+                // If the reference has to be 'static, avoid in-place initialization
+                // as that will end up pointing to the stack instead.
+                if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
+                    self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE;
+                    self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
+                }
+            }
+            Some(hir::MutMutable) => {
+                // `&mut expr` means expr could be mutated, unless it's zero-sized.
+                if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
+                    if self.mode == Mode::Var {
+                        outer = outer | ConstQualif::NOT_CONST;
+                        self.add_qualif(ConstQualif::MUTABLE_MEM);
+                    } else {
+                        span_err!(self.tcx.sess, ex.span, E0017,
+                            "references in {}s may only refer \
+                             to immutable values", self.msg())
+                    }
+                }
+                if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
+                    self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
+                }
+            }
+            None => {}
+        }
+        self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
+        // Don't propagate certain flags.
+        self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
+    }
+}
+
+/// This function is used to enforce the constraints on
+/// const/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 const/static item, it is qualified for promotion
+/// instead of producing errors.
+fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
+                        e: &hir::Expr, node_ty: Ty<'tcx>) {
+    match node_ty.sty {
+        ty::TyStruct(def, _) |
+        ty::TyEnum(def, _) if def.has_dtor() => {
+            v.add_qualif(ConstQualif::NEEDS_DROP);
+            if v.mode != Mode::Var {
+                span_err!(v.tcx.sess, e.span, E0493,
+                          "{}s are not allowed to have destructors",
+                          v.msg());
+            }
+        }
+        _ => {}
+    }
+
+    let method_call = ty::MethodCall::expr(e.id);
+    match e.node {
+        hir::ExprUnary(..) |
+        hir::ExprBinary(..) |
+        hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
+            v.add_qualif(ConstQualif::NOT_CONST);
+            if v.mode != Mode::Var {
+                span_err!(v.tcx.sess, e.span, E0011,
+                            "user-defined operators are not allowed in {}s", v.msg());
+            }
+        }
+        hir::ExprBox(_) => {
+            v.add_qualif(ConstQualif::NOT_CONST);
+            if v.mode != Mode::Var {
+                span_err!(v.tcx.sess, e.span, E0010,
+                          "allocations are not allowed in {}s", v.msg());
+            }
+        }
+        hir::ExprUnary(op, ref inner) => {
+            match v.tcx.node_id_to_type(inner.id).sty {
+                ty::TyRawPtr(_) => {
+                    assert!(op == hir::UnDeref);
+
+                    v.add_qualif(ConstQualif::NOT_CONST);
+                    if v.mode != Mode::Var {
+                        span_err!(v.tcx.sess, e.span, E0396,
+                                  "raw pointers cannot be dereferenced in {}s", v.msg());
+                    }
+                }
+                _ => {}
+            }
+        }
+        hir::ExprBinary(op, ref lhs, _) => {
+            match v.tcx.node_id_to_type(lhs.id).sty {
+                ty::TyRawPtr(_) => {
+                    assert!(op.node == hir::BiEq || op.node == hir::BiNe ||
+                            op.node == hir::BiLe || op.node == hir::BiLt ||
+                            op.node == hir::BiGe || op.node == hir::BiGt);
+
+                    v.add_qualif(ConstQualif::NOT_CONST);
+                    if v.mode != Mode::Var {
+                        span_err!(v.tcx.sess, e.span, E0395,
+                                  "raw pointers cannot be compared in {}s", v.msg());
+                    }
+                }
+                _ => {}
+            }
+        }
+        hir::ExprCast(ref from, _) => {
+            debug!("Checking const cast(id={})", from.id);
+            match v.tcx.cast_kinds.borrow().get(&from.id) {
+                None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
+                Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
+                    v.add_qualif(ConstQualif::NOT_CONST);
+                    if v.mode != Mode::Var {
+                        span_err!(v.tcx.sess, e.span, E0018,
+                                  "raw pointers cannot be cast to integers in {}s", v.msg());
+                    }
+                }
+                _ => {}
+            }
+        }
+        hir::ExprPath(..) => {
+            let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
+            match def {
+                Some(Def::Variant(..)) => {
+                    // Count the discriminator or function pointer.
+                    v.add_qualif(ConstQualif::NON_ZERO_SIZED);
+                }
+                Some(Def::Struct(..)) => {
+                    if let ty::TyBareFn(..) = node_ty.sty {
+                        // Count the function pointer.
+                        v.add_qualif(ConstQualif::NON_ZERO_SIZED);
+                    }
+                }
+                Some(Def::Fn(..)) | Some(Def::Method(..)) => {
+                    // Count the function pointer.
+                    v.add_qualif(ConstQualif::NON_ZERO_SIZED);
+                }
+                Some(Def::Static(..)) => {
+                    match v.mode {
+                        Mode::Static | Mode::StaticMut => {}
+                        Mode::Const | Mode::ConstFn => {
+                            span_err!(v.tcx.sess, e.span, E0013,
+                                "{}s cannot refer to other statics, insert \
+                                 an intermediate constant instead", v.msg());
+                        }
+                        Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
+                    }
+                }
+                Some(Def::Const(did)) |
+                Some(Def::AssociatedConst(did)) => {
+                    if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
+                                                                       Some(e.id),
+                                                                       None) {
+                        let inner = v.global_expr(Mode::Const, expr);
+                        v.add_qualif(inner);
+                    }
+                }
+                Some(Def::Local(..)) if v.mode == Mode::ConstFn => {
+                    // Sadly, we can't determine whether the types are zero-sized.
+                    v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
+                }
+                def => {
+                    v.add_qualif(ConstQualif::NOT_CONST);
+                    if v.mode != Mode::Var {
+                        debug!("(checking const) found bad def: {:?}", def);
+                        span_err!(v.tcx.sess, e.span, E0014,
+                                  "paths in {}s may only refer to constants \
+                                   or functions", v.msg());
+                    }
+                }
+            }
+        }
+        hir::ExprCall(ref callee, _) => {
+            let mut callee = &**callee;
+            loop {
+                callee = match callee.node {
+                    hir::ExprBlock(ref block) => match block.expr {
+                        Some(ref tail) => &**tail,
+                        None => break
+                    },
+                    _ => break
+                };
+            }
+            let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
+            let is_const = match def {
+                Some(Def::Struct(..)) => true,
+                Some(Def::Variant(..)) => {
+                    // Count the discriminator.
+                    v.add_qualif(ConstQualif::NON_ZERO_SIZED);
+                    true
+                }
+                Some(Def::Fn(did)) => {
+                    v.handle_const_fn_call(e, did, node_ty)
+                }
+                Some(Def::Method(did)) => {
+                    match v.tcx.impl_or_trait_item(did).container() {
+                        ty::ImplContainer(_) => {
+                            v.handle_const_fn_call(e, did, node_ty)
+                        }
+                        ty::TraitContainer(_) => false
+                    }
+                }
+                _ => false
+            };
+            if !is_const {
+                v.add_qualif(ConstQualif::NOT_CONST);
+                if v.mode != Mode::Var {
+                    // FIXME(#24111) Remove this check when const fn stabilizes
+                    let (msg, note) =
+                        if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
+                        (format!("function calls in {}s are limited to \
+                                  struct and enum constructors",
+                                 v.msg()),
+                         Some("a limited form of compile-time function \
+                               evaluation is available on a nightly \
+                               compiler via `const fn`"))
+                    } else {
+                        (format!("function calls in {}s are limited \
+                                  to constant functions, \
+                                  struct and enum constructors",
+                                 v.msg()),
+                         None)
+                    };
+                    let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
+                    if let Some(note) = note {
+                        err.span_note(e.span, note);
+                    }
+                    err.emit();
+                }
+            }
+        }
+        hir::ExprMethodCall(..) => {
+            let method = v.tcx.tables.borrow().method_map[&method_call];
+            let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() {
+                ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty),
+                ty::TraitContainer(_) => false
+            };
+            if !is_const {
+                v.add_qualif(ConstQualif::NOT_CONST);
+                if v.mode != Mode::Var {
+                    span_err!(v.tcx.sess, e.span, E0378,
+                              "method calls in {}s are limited to \
+                               constant inherent methods", v.msg());
+                }
+            }
+        }
+        hir::ExprStruct(..) => {
+            let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
+            if did == v.tcx.lang_items.unsafe_cell_type() {
+                v.add_qualif(ConstQualif::MUTABLE_MEM);
+            }
+        }
+
+        hir::ExprLit(_) |
+        hir::ExprAddrOf(..) => {
+            v.add_qualif(ConstQualif::NON_ZERO_SIZED);
+        }
+
+        hir::ExprRepeat(..) => {
+            v.add_qualif(ConstQualif::PREFER_IN_PLACE);
+        }
+
+        hir::ExprClosure(..) => {
+            // Paths in constant contexts cannot refer to local variables,
+            // as there are none, and thus closures can't have upvars there.
+            if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
+                assert!(v.mode == Mode::Var,
+                        "global closures can't capture anything");
+                v.add_qualif(ConstQualif::NOT_CONST);
+            }
+        }
+
+        hir::ExprBlock(_) |
+        hir::ExprIndex(..) |
+        hir::ExprField(..) |
+        hir::ExprTupField(..) |
+        hir::ExprVec(_) |
+        hir::ExprType(..) |
+        hir::ExprTup(..) => {}
+
+        // Conditional control flow (possible to implement).
+        hir::ExprMatch(..) |
+        hir::ExprIf(..) |
+
+        // Loops (not very meaningful in constants).
+        hir::ExprWhile(..) |
+        hir::ExprLoop(..) |
+
+        // More control flow (also not very meaningful).
+        hir::ExprBreak(_) |
+        hir::ExprAgain(_) |
+        hir::ExprRet(_) |
+
+        // Miscellaneous expressions that could be implemented.
+        hir::ExprRange(..) |
+
+        // Expressions with side-effects.
+        hir::ExprAssign(..) |
+        hir::ExprAssignOp(..) |
+        hir::ExprInlineAsm(_) => {
+            v.add_qualif(ConstQualif::NOT_CONST);
+            if v.mode != Mode::Var {
+                span_err!(v.tcx.sess, e.span, E0019,
+                          "{} contains unimplemented expression type", v.msg());
+            }
+        }
+    }
+}
+
+/// Check the adjustments of an expression
+fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
+    match v.tcx.tables.borrow().adjustments.get(&e.id) {
+        None |
+        Some(&ty::adjustment::AdjustReifyFnPointer) |
+        Some(&ty::adjustment::AdjustUnsafeFnPointer) => {}
+
+        Some(&ty::adjustment::AdjustDerefRef(
+            ty::adjustment::AutoDerefRef { autoderefs, .. }
+        )) => {
+            if (0..autoderefs as u32).any(|autoderef| {
+                    v.tcx.is_overloaded_autoderef(e.id, autoderef)
+            }) {
+                v.add_qualif(ConstQualif::NOT_CONST);
+                if v.mode != Mode::Var {
+                    span_err!(v.tcx.sess, e.span, E0400,
+                              "user-defined dereference operators are not allowed in {}s",
+                              v.msg());
+                }
+            }
+        }
+    }
+}
+
+pub fn check_crate(tcx: &ty::ctxt) {
+    tcx.visit_all_items_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor {
+        tcx: tcx,
+        mode: Mode::Var,
+        qualif: ConstQualif::NOT_CONST,
+        rvalue_borrows: NodeMap()
+    });
+    tcx.sess.abort_if_errors();
+}
+
+impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
+    fn consume(&mut self,
+               _consume_id: ast::NodeId,
+               consume_span: Span,
+               cmt: mc::cmt,
+               _mode: euv::ConsumeMode) {
+        let mut cur = &cmt;
+        loop {
+            match cur.cat {
+                Categorization::StaticItem => {
+                    if self.mode != Mode::Var {
+                        // statics cannot be consumed by value at any time, that would imply
+                        // that they're an initializer (what a const is for) or kept in sync
+                        // over time (not feasible), so deny it outright.
+                        span_err!(self.tcx.sess, consume_span, E0394,
+                                  "cannot refer to other statics by value, use the \
+                                   address-of operator or a constant instead");
+                    }
+                    break;
+                }
+                Categorization::Deref(ref cmt, _, _) |
+                Categorization::Downcast(ref cmt, _) |
+                Categorization::Interior(ref cmt, _) => cur = cmt,
+
+                Categorization::Rvalue(..) |
+                Categorization::Upvar(..) |
+                Categorization::Local(..) => break
+            }
+        }
+    }
+    fn borrow(&mut self,
+              borrow_id: ast::NodeId,
+              borrow_span: Span,
+              cmt: mc::cmt<'tcx>,
+              _loan_region: ty::Region,
+              bk: ty::BorrowKind,
+              loan_cause: euv::LoanCause)
+    {
+        // Kind of hacky, but we allow Unsafe coercions in constants.
+        // These occur when we convert a &T or *T to a *U, as well as
+        // when making a thin pointer (e.g., `*T`) into a fat pointer
+        // (e.g., `*Trait`).
+        match loan_cause {
+            euv::LoanCause::AutoUnsafe => {
+                return;
+            }
+            _ => { }
+        }
+
+        let mut cur = &cmt;
+        let mut is_interior = false;
+        loop {
+            match cur.cat {
+                Categorization::Rvalue(..) => {
+                    if loan_cause == euv::MatchDiscriminant {
+                        // Ignore the dummy immutable borrow created by EUV.
+                        break;
+                    }
+                    let mutbl = bk.to_mutbl_lossy();
+                    if mutbl == hir::MutMutable && self.mode == Mode::StaticMut {
+                        // Mutable slices are the only `&mut` allowed in
+                        // globals, but only in `static mut`, nowhere else.
+                        // FIXME: This exception is really weird... there isn't
+                        // any fundamental reason to restrict this based on
+                        // type of the expression.  `&mut [1]` has exactly the
+                        // same representation as &mut 1.
+                        match cmt.ty.sty {
+                            ty::TyArray(_, _) | ty::TySlice(_) => break,
+                            _ => {}
+                        }
+                    }
+                    self.record_borrow(borrow_id, mutbl);
+                    break;
+                }
+                Categorization::StaticItem => {
+                    if is_interior && self.mode != Mode::Var {
+                        // Borrowed statics can specifically *only* have their address taken,
+                        // not any number of other borrows such as borrowing fields, reading
+                        // elements of an array, etc.
+                        span_err!(self.tcx.sess, borrow_span, E0494,
+                                  "cannot refer to the interior of another \
+                                   static, use a constant instead");
+                    }
+                    break;
+                }
+                Categorization::Deref(ref cmt, _, _) |
+                Categorization::Downcast(ref cmt, _) |
+                Categorization::Interior(ref cmt, _) => {
+                    is_interior = true;
+                    cur = cmt;
+                }
+
+                Categorization::Upvar(..) |
+                Categorization::Local(..) => break
+            }
+        }
+    }
+
+    fn decl_without_init(&mut self,
+                         _id: ast::NodeId,
+                         _span: Span) {}
+    fn mutate(&mut self,
+              _assignment_id: ast::NodeId,
+              _assignment_span: Span,
+              _assignee_cmt: mc::cmt,
+              _mode: euv::MutateMode) {}
+
+    fn matched_pat(&mut self,
+                   _: &hir::Pat,
+                   _: mc::cmt,
+                   _: euv::MatchMode) {}
+
+    fn consume_pat(&mut self,
+                   _consume_pat: &hir::Pat,
+                   _cmt: mc::cmt,
+                   _mode: euv::ConsumeMode) {}
+}
diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs
index 380eada18a1..2c08cbd3233 100644
--- a/src/librustc_passes/diagnostics.rs
+++ b/src/librustc_passes/diagnostics.rs
@@ -11,6 +11,108 @@
 #![allow(non_snake_case)]
 
 register_long_diagnostics! {
+
+E0010: r##"
+The value of statics and constants must be known at compile time, and they live
+for the entire lifetime of a program. Creating a boxed value allocates memory on
+the heap at runtime, and therefore cannot be done at compile time. Erroneous
+code example:
+
+```
+#![feature(box_syntax)]
+
+const CON : Box<i32> = box 0;
+```
+"##,
+
+E0011: r##"
+Initializers for constants and statics are evaluated at compile time.
+User-defined operators rely on user-defined functions, which cannot be evaluated
+at compile time.
+
+Bad example:
+
+```
+use std::ops::Index;
+
+struct Foo { a: u8 }
+
+impl Index<u8> for Foo {
+    type Output = u8;
+
+    fn index<'a>(&'a self, idx: u8) -> &'a u8 { &self.a }
+}
+
+const a: Foo = Foo { a: 0u8 };
+const b: u8 = a[0]; // Index trait is defined by the user, bad!
+```
+
+Only operators on builtin types are allowed.
+
+Example:
+
+```
+const a: &'static [i32] = &[1, 2, 3];
+const b: i32 = a[0]; // Good!
+```
+"##,
+
+E0013: r##"
+Static and const variables can refer to other const variables. But a const
+variable cannot refer to a static variable. For example, `Y` cannot refer to `X`
+here:
+
+```
+static X: i32 = 42;
+const Y: i32 = X;
+```
+
+To fix this, the value can be extracted as a const and then used:
+
+```
+const A: i32 = 42;
+static X: i32 = A;
+const Y: i32 = A;
+```
+"##,
+
+E0014: r##"
+Constants can only be initialized by a constant value or, in a future
+version of Rust, a call to a const function. This error indicates the use
+of a path (like a::b, or x) denoting something other than one of these
+allowed items. Example:
+
+```
+const FOO: i32 = { let x = 0; x }; // 'x' isn't a constant nor a function!
+```
+
+To avoid it, you have to replace the non-constant value:
+
+```
+const FOO: i32 = { const X : i32 = 0; X };
+// or even:
+const FOO: i32 = { 0 }; // but brackets are useless here
+```
+"##,
+
+// FIXME(#24111) Change the language here when const fn stabilizes
+E0015: r##"
+The only functions that can be called in static or constant expressions are
+`const` functions, and struct/enum constructors. `const` functions are only
+available on a nightly compiler. Rust currently does not support more general
+compile-time function execution.
+
+```
+const FOO: Option<u8> = Some(1); // enum constructor
+struct Bar {x: u8}
+const BAR: Bar = Bar {x: 1}; // struct constructor
+```
+
+See [RFC 911] for more details on the design of `const fn`s.
+
+[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
+"##,
+
 E0016: r##"
 Blocks in constants may only contain items (such as constant, function
 definition, etc...) and a tail expression. Example:
@@ -26,6 +128,86 @@ const FOO: i32 = { const X : i32 = 0; X };
 ```
 "##,
 
+E0017: r##"
+References in statics and constants may only refer to immutable values. Example:
+
+```
+static X: i32 = 1;
+const C: i32 = 2;
+
+// these three are not allowed:
+const CR: &'static mut i32 = &mut C;
+static STATIC_REF: &'static mut i32 = &mut X;
+static CONST_REF: &'static mut i32 = &mut C;
+```
+
+Statics are shared everywhere, and if they refer to mutable data one might
+violate memory safety since holding multiple mutable references to shared data
+is not allowed.
+
+If you really want global mutable state, try using `static mut` or a global
+`UnsafeCell`.
+"##,
+
+E0018: r##"
+The value of static and const variables must be known at compile time. You
+can't cast a pointer as an integer because we can't know what value the
+address will take.
+
+However, pointers to other constants' addresses are allowed in constants,
+example:
+
+```
+const X: u32 = 50;
+const Y: *const u32 = &X;
+```
+
+Therefore, casting one of these non-constant pointers to an integer results
+in a non-constant integer which lead to this error. Example:
+
+```
+const X: u32 = 1;
+const Y: usize = &X as *const u32 as usize;
+println!("{}", Y);
+```
+"##,
+
+E0019: r##"
+A function call isn't allowed in the const's initialization expression
+because the expression's value must be known at compile-time. Example of
+erroneous code:
+
+```
+enum Test {
+    V1
+}
+
+impl Test {
+    fn test(&self) -> i32 {
+        12
+    }
+}
+
+fn main() {
+    const FOO: Test = Test::V1;
+
+    const A: i32 = FOO.test(); // You can't call Test::func() here !
+}
+```
+
+Remember: you can't use a function call inside a const's initialization
+expression! However, you can totally use it anywhere else:
+
+```
+fn main() {
+    const FOO: Test = Test::V1;
+
+    FOO.func(); // here is good
+    let x = FOO.func(); // or even here!
+}
+```
+"##,
+
 E0022: r##"
 Constant functions are not allowed to mutate anything. Thus, binding to an
 argument with a mutable pattern is not allowed. For example,
@@ -43,6 +225,366 @@ you need to mutate the argument, try lazily initializing a global variable
 instead of using a `const fn`, or refactoring the code to a functional style to
 avoid mutation if possible.
 "##,
+
+E0030: r##"
+When matching against a range, the compiler verifies that the range is
+non-empty.  Range patterns include both end-points, so this is equivalent to
+requiring the start of the range to be less than or equal to the end of the
+range.
+
+For example:
+
+```
+match 5u32 {
+    // This range is ok, albeit pointless.
+    1 ... 1 => ...
+    // This range is empty, and the compiler can tell.
+    1000 ... 5 => ...
+}
+```
+"##,
+
+E0161: r##"
+In Rust, you can only move a value when its size is known at compile time.
+
+To work around this restriction, consider "hiding" the value behind a reference:
+either `&x` or `&mut x`. Since a reference has a fixed size, this lets you move
+it around as usual.
+"##,
+
+E0265: r##"
+This error indicates that a static or constant references itself.
+All statics and constants need to resolve to a value in an acyclic manner.
+
+For example, neither of the following can be sensibly compiled:
+
+```
+const X: u32 = X;
+```
+
+```
+const X: u32 = Y;
+const Y: u32 = X;
+```
+"##,
+
+E0267: r##"
+This error indicates the use of a loop keyword (`break` or `continue`) inside a
+closure but outside of any loop. Erroneous code example:
+
+```
+let w = || { break; }; // error: `break` inside of a closure
+```
+
+`break` and `continue` keywords can be used as normal inside closures as long as
+they are also contained within a loop. To halt the execution of a closure you
+should instead use a return statement. Example:
+
+```
+let w = || {
+    for _ in 0..10 {
+        break;
+    }
+};
+
+w();
+```
+"##,
+
+E0268: r##"
+This error indicates the use of a loop keyword (`break` or `continue`) outside
+of a loop. Without a loop to break out of or continue in, no sensible action can
+be taken. Erroneous code example:
+
+```
+fn some_func() {
+    break; // error: `break` outside of loop
+}
+```
+
+Please verify that you are using `break` and `continue` only in loops. Example:
+
+```
+fn some_func() {
+    for _ in 0..10 {
+        break; // ok!
+    }
+}
+```
+"##,
+
+E0378: r##"
+Method calls that aren't calls to inherent `const` methods are disallowed
+in statics, constants, and constant functions.
+
+For example:
+
+```
+const BAZ: i32 = Foo(25).bar(); // error, `bar` isn't `const`
+
+struct Foo(i32);
+
+impl Foo {
+    const fn foo(&self) -> i32 {
+        self.bar() // error, `bar` isn't `const`
+    }
+
+    fn bar(&self) -> i32 { self.0 }
+}
+```
+
+For more information about `const fn`'s, see [RFC 911].
+
+[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
+"##,
+
+E0394: r##"
+From [RFC 246]:
+
+ > It is invalid for a static to reference another static by value. It is
+ > required that all references be borrowed.
+
+[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
+"##,
+
+E0395: r##"
+The value assigned to a constant expression must be known at compile time,
+which is not the case when comparing raw pointers. Erroneous code example:
+
+```
+static foo: i32 = 42;
+static bar: i32 = 43;
+
+static baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
+// error: raw pointers cannot be compared in statics!
+```
+
+Please check that the result of the comparison can be determined at compile time
+or isn't assigned to a constant expression. Example:
+
+```
+static foo: i32 = 42;
+static bar: i32 = 43;
+
+let baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
+// baz isn't a constant expression so it's ok
+```
+"##,
+
+E0396: r##"
+The value assigned to a constant expression must be known at compile time,
+which is not the case when dereferencing raw pointers. Erroneous code
+example:
+
+```
+const foo: i32 = 42;
+const baz: *const i32 = (&foo as *const i32);
+
+const deref: i32 = *baz;
+// error: raw pointers cannot be dereferenced in constants
+```
+
+To fix this error, please do not assign this value to a constant expression.
+Example:
+
+```
+const foo: i32 = 42;
+const baz: *const i32 = (&foo as *const i32);
+
+unsafe { let deref: i32 = *baz; }
+// baz isn't a constant expression so it's ok
+```
+
+You'll also note that this assignment must be done in an unsafe block!
+"##,
+
+E0397: r##"
+It is not allowed for a mutable static to allocate or have destructors. For
+example:
+
+```
+// error: mutable statics are not allowed to have boxes
+static mut FOO: Option<Box<usize>> = None;
+
+// error: mutable statics are not allowed to have destructors
+static mut BAR: Option<Vec<i32>> = None;
+```
+"##,
+
+E0400: r##"
+A user-defined dereference was attempted in an invalid context. Erroneous
+code example:
+
+```
+use std::ops::Deref;
+
+struct A;
+
+impl Deref for A {
+    type Target = str;
+
+    fn deref(&self)-> &str { "foo" }
+}
+
+const S: &'static str = &A;
+// error: user-defined dereference operators are not allowed in constants
+
+fn main() {
+    let foo = S;
+}
+```
+
+You cannot directly use a dereference operation whilst initializing a constant
+or a static. To fix this error, restructure your code to avoid this dereference,
+perhaps moving it inline:
+
+```
+use std::ops::Deref;
+
+struct A;
+
+impl Deref for A {
+    type Target = str;
+
+    fn deref(&self)-> &str { "foo" }
+}
+
+fn main() {
+    let foo : &str = &A;
+}
+```
+"##,
+
+E0492: r##"
+A borrow of a constant containing interior mutability was attempted. Erroneous
+code example:
+
+```
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
+
+const A: AtomicUsize = ATOMIC_USIZE_INIT;
+static B: &'static AtomicUsize = &A;
+// error: cannot borrow a constant which contains interior mutability, create a
+//        static instead
+```
+
+A `const` represents a constant value that should never change. If one takes
+a `&` reference to the constant, then one is taking a pointer to some memory
+location containing the value. Normally this is perfectly fine: most values
+can't be changed via a shared `&` pointer, but interior mutability would allow
+it. That is, a constant value could be mutated. On the other hand, a `static` is
+explicitly a single memory location, which can be mutated at will.
+
+So, in order to solve this error, either use statics which are `Sync`:
+
+```
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
+
+static A: AtomicUsize = ATOMIC_USIZE_INIT;
+static B: &'static AtomicUsize = &A; // ok!
+```
+
+You can also have this error while using a cell type:
+
+```
+#![feature(const_fn)]
+
+use std::cell::Cell;
+
+const A: Cell<usize> = Cell::new(1);
+const B: &'static Cell<usize> = &A;
+// error: cannot borrow a constant which contains interior mutability, create
+//        a static instead
+
+// or:
+struct C { a: Cell<usize> }
+
+const D: C = C { a: Cell::new(1) };
+const E: &'static Cell<usize> = &D.a; // error
+
+// or:
+const F: &'static C = &D; // error
+```
+
+This is because cell types do operations that are not thread-safe. Due to this,
+they don't implement Sync and thus can't be placed in statics. In this
+case, `StaticMutex` would work just fine, but it isn't stable yet:
+https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
+
+However, if you still wish to use these types, you can achieve this by an unsafe
+wrapper:
+
+```
+#![feature(const_fn)]
+
+use std::cell::Cell;
+use std::marker::Sync;
+
+struct NotThreadSafe<T> {
+    value: Cell<T>,
+}
+
+unsafe impl<T> Sync for NotThreadSafe<T> {}
+
+static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
+static B: &'static NotThreadSafe<usize> = &A; // ok!
+```
+
+Remember this solution is unsafe! You will have to ensure that accesses to the
+cell are synchronized.
+"##,
+
+E0493: r##"
+A type with a destructor was assigned to an invalid type of variable. Erroneous
+code example:
+
+```
+struct Foo {
+    a: u32
+}
+
+impl Drop for Foo {
+    fn drop(&mut self) {}
+}
+
+const F : Foo = Foo { a : 0 };
+// error: constants are not allowed to have destructors
+static S : Foo = Foo { a : 0 };
+// error: statics are not allowed to have destructors
+```
+
+To solve this issue, please use a type which does allow the usage of type with
+destructors.
+"##,
+
+E0494: r##"
+A reference of an interior static was assigned to another const/static.
+Erroneous code example:
+
+```
+struct Foo {
+    a: u32
+}
+
+static S : Foo = Foo { a : 0 };
+static A : &'static u32 = &S.a;
+// error: cannot refer to the interior of another static, use a
+//        constant instead
+```
+
+The "base" variable has to be a const if you want another static/const variable
+to refer to one of its fields. Example:
+
+```
+struct Foo {
+    a: u32
+}
+
+const S : Foo = Foo { a : 0 };
+static A : &'static u32 = &S.a; // ok!
+```
+"##,
+
 }
 
 register_diagnostics! {
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index 4adaa0cab7a..fcdbd6384d5 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -28,9 +28,16 @@
 
 extern crate core;
 extern crate rustc;
+extern crate rustc_front;
 
+#[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
 
 pub mod diagnostics;
+
 pub mod const_fn;
+pub mod consts;
+pub mod loops;
 pub mod no_asm;
+pub mod rvalues;
+pub mod static_recursion;
diff --git a/src/librustc_passes/loops.rs b/src/librustc_passes/loops.rs
new file mode 100644
index 00000000000..eb2e445f9b0
--- /dev/null
+++ b/src/librustc_passes/loops.rs
@@ -0,0 +1,80 @@
+// Copyright 2012-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.
+use self::Context::*;
+
+use rustc::session::Session;
+
+use syntax::codemap::Span;
+use rustc_front::intravisit::{self, Visitor};
+use rustc_front::hir;
+
+#[derive(Clone, Copy, PartialEq)]
+enum Context {
+    Normal, Loop, Closure
+}
+
+#[derive(Copy, Clone)]
+struct CheckLoopVisitor<'a> {
+    sess: &'a Session,
+    cx: Context
+}
+
+pub fn check_crate(sess: &Session, krate: &hir::Crate) {
+    krate.visit_all_items(&mut CheckLoopVisitor { sess: sess, cx: Normal });
+}
+
+impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> {
+    fn visit_item(&mut self, i: &hir::Item) {
+        self.with_context(Normal, |v| intravisit::walk_item(v, i));
+    }
+
+    fn visit_expr(&mut self, e: &hir::Expr) {
+        match e.node {
+            hir::ExprWhile(ref e, ref b, _) => {
+                self.visit_expr(&**e);
+                self.with_context(Loop, |v| v.visit_block(&**b));
+            }
+            hir::ExprLoop(ref b, _) => {
+                self.with_context(Loop, |v| v.visit_block(&**b));
+            }
+            hir::ExprClosure(_, _, ref b) => {
+                self.with_context(Closure, |v| v.visit_block(&**b));
+            }
+            hir::ExprBreak(_) => self.require_loop("break", e.span),
+            hir::ExprAgain(_) => self.require_loop("continue", e.span),
+            _ => intravisit::walk_expr(self, e)
+        }
+    }
+}
+
+impl<'a> CheckLoopVisitor<'a> {
+    fn with_context<F>(&mut self, cx: Context, f: F) where
+        F: FnOnce(&mut CheckLoopVisitor<'a>),
+    {
+        let old_cx = self.cx;
+        self.cx = cx;
+        f(self);
+        self.cx = old_cx;
+    }
+
+    fn require_loop(&self, name: &str, span: Span) {
+        match self.cx {
+            Loop => {}
+            Closure => {
+                span_err!(self.sess, span, E0267,
+                                   "`{}` inside of a closure", name);
+            }
+            Normal => {
+                span_err!(self.sess, span, E0268,
+                                   "`{}` outside of loop", name);
+            }
+        }
+    }
+}
diff --git a/src/librustc_passes/rvalues.rs b/src/librustc_passes/rvalues.rs
new file mode 100644
index 00000000000..f5cc020932b
--- /dev/null
+++ b/src/librustc_passes/rvalues.rs
@@ -0,0 +1,105 @@
+// 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.
+
+// Checks that all rvalues in a crate have statically known size. check_crate
+// is the public starting point.
+
+use rustc::dep_graph::DepNode;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::infer;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::ty::{self, ParameterEnvironment};
+
+use rustc_front::hir;
+use rustc_front::intravisit;
+use syntax::ast;
+use syntax::codemap::Span;
+
+pub fn check_crate(tcx: &ty::ctxt) {
+    let mut rvcx = RvalueContext { tcx: tcx };
+    tcx.visit_all_items_in_krate(DepNode::RvalueCheck, &mut rvcx);
+}
+
+struct RvalueContext<'a, 'tcx: 'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+}
+
+impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for RvalueContext<'a, 'tcx> {
+    fn visit_fn(&mut self,
+                fk: intravisit::FnKind<'v>,
+                fd: &'v hir::FnDecl,
+                b: &'v hir::Block,
+                s: Span,
+                fn_id: ast::NodeId) {
+        {
+            // FIXME (@jroesch) change this to be an inference context
+            let param_env = ParameterEnvironment::for_item(self.tcx, fn_id);
+            let infcx = infer::new_infer_ctxt(self.tcx,
+                                              &self.tcx.tables,
+                                              Some(param_env.clone()));
+            let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: &param_env };
+            let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx);
+            euv.walk_fn(fd, b);
+        }
+        intravisit::walk_fn(self, fk, fd, b, s)
+    }
+}
+
+struct RvalueContextDelegate<'a, 'tcx: 'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    param_env: &'a ty::ParameterEnvironment<'a,'tcx>,
+}
+
+impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> {
+    fn consume(&mut self,
+               _: ast::NodeId,
+               span: Span,
+               cmt: mc::cmt<'tcx>,
+               _: euv::ConsumeMode) {
+        debug!("consume; cmt: {:?}; type: {:?}", *cmt, cmt.ty);
+        if !cmt.ty.is_sized(self.param_env, span) {
+            span_err!(self.tcx.sess, span, E0161,
+                "cannot move a value of type {0}: the size of {0} cannot be statically determined",
+                cmt.ty);
+        }
+    }
+
+    fn matched_pat(&mut self,
+                   _matched_pat: &hir::Pat,
+                   _cmt: mc::cmt,
+                   _mode: euv::MatchMode) {}
+
+    fn consume_pat(&mut self,
+                   _consume_pat: &hir::Pat,
+                   _cmt: mc::cmt,
+                   _mode: euv::ConsumeMode) {
+    }
+
+    fn borrow(&mut self,
+              _borrow_id: ast::NodeId,
+              _borrow_span: Span,
+              _cmt: mc::cmt,
+              _loan_region: ty::Region,
+              _bk: ty::BorrowKind,
+              _loan_cause: euv::LoanCause) {
+    }
+
+    fn decl_without_init(&mut self,
+                         _id: ast::NodeId,
+                         _span: Span) {
+    }
+
+    fn mutate(&mut self,
+              _assignment_id: ast::NodeId,
+              _assignment_span: Span,
+              _assignee_cmt: mc::cmt,
+              _mode: euv::MutateMode) {
+    }
+}
diff --git a/src/librustc_passes/static_recursion.rs b/src/librustc_passes/static_recursion.rs
new file mode 100644
index 00000000000..b49db16b4ce
--- /dev/null
+++ b/src/librustc_passes/static_recursion.rs
@@ -0,0 +1,290 @@
+// 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.
+
+// This compiler pass detects constants that refer to themselves
+// recursively.
+
+use rustc::front::map as ast_map;
+use rustc::session::Session;
+use rustc::middle::def::{Def, DefMap};
+use rustc::util::nodemap::NodeMap;
+
+use syntax::{ast};
+use syntax::codemap::Span;
+use syntax::feature_gate::{GateIssue, emit_feature_err};
+use rustc_front::intravisit::{self, Visitor};
+use rustc_front::hir;
+
+use std::cell::RefCell;
+
+struct CheckCrateVisitor<'a, 'ast: 'a> {
+    sess: &'a Session,
+    def_map: &'a DefMap,
+    ast_map: &'a ast_map::Map<'ast>,
+    // `discriminant_map` is a cache that associates the `NodeId`s of local
+    // variant definitions with the discriminant expression that applies to
+    // each one. If the variant uses the default values (starting from `0`),
+    // then `None` is stored.
+    discriminant_map: RefCell<NodeMap<Option<&'ast hir::Expr>>>,
+}
+
+impl<'a, 'ast: 'a> Visitor<'ast> for CheckCrateVisitor<'a, 'ast> {
+    fn visit_item(&mut self, it: &'ast hir::Item) {
+        match it.node {
+            hir::ItemStatic(..) |
+            hir::ItemConst(..) => {
+                let mut recursion_visitor =
+                    CheckItemRecursionVisitor::new(self, &it.span);
+                recursion_visitor.visit_item(it);
+            },
+            hir::ItemEnum(ref enum_def, ref generics) => {
+                // We could process the whole enum, but handling the variants
+                // with discriminant expressions one by one gives more specific,
+                // less redundant output.
+                for variant in &enum_def.variants {
+                    if let Some(_) = variant.node.disr_expr {
+                        let mut recursion_visitor =
+                            CheckItemRecursionVisitor::new(self, &variant.span);
+                        recursion_visitor.populate_enum_discriminants(enum_def);
+                        recursion_visitor.visit_variant(variant, generics, it.id);
+                    }
+                }
+            }
+            _ => {}
+        }
+        intravisit::walk_item(self, it)
+    }
+
+    fn visit_trait_item(&mut self, ti: &'ast hir::TraitItem) {
+        match ti.node {
+            hir::ConstTraitItem(_, ref default) => {
+                if let Some(_) = *default {
+                    let mut recursion_visitor =
+                        CheckItemRecursionVisitor::new(self, &ti.span);
+                    recursion_visitor.visit_trait_item(ti);
+                }
+            }
+            _ => {}
+        }
+        intravisit::walk_trait_item(self, ti)
+    }
+
+    fn visit_impl_item(&mut self, ii: &'ast hir::ImplItem) {
+        match ii.node {
+            hir::ImplItemKind::Const(..) => {
+                let mut recursion_visitor =
+                    CheckItemRecursionVisitor::new(self, &ii.span);
+                recursion_visitor.visit_impl_item(ii);
+            }
+            _ => {}
+        }
+        intravisit::walk_impl_item(self, ii)
+    }
+}
+
+pub fn check_crate<'ast>(sess: &Session,
+                         krate: &'ast hir::Crate,
+                         def_map: &DefMap,
+                         ast_map: &ast_map::Map<'ast>) {
+    let mut visitor = CheckCrateVisitor {
+        sess: sess,
+        def_map: def_map,
+        ast_map: ast_map,
+        discriminant_map: RefCell::new(NodeMap()),
+    };
+    sess.abort_if_new_errors(|| {
+        krate.visit_all_items(&mut visitor);
+    });
+}
+
+struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
+    root_span: &'a Span,
+    sess: &'a Session,
+    ast_map: &'a ast_map::Map<'ast>,
+    def_map: &'a DefMap,
+    discriminant_map: &'a RefCell<NodeMap<Option<&'ast hir::Expr>>>,
+    idstack: Vec<ast::NodeId>,
+}
+
+impl<'a, 'ast: 'a> CheckItemRecursionVisitor<'a, 'ast> {
+    fn new(v: &'a CheckCrateVisitor<'a, 'ast>, span: &'a Span)
+           -> CheckItemRecursionVisitor<'a, 'ast> {
+        CheckItemRecursionVisitor {
+            root_span: span,
+            sess: v.sess,
+            ast_map: v.ast_map,
+            def_map: v.def_map,
+            discriminant_map: &v.discriminant_map,
+            idstack: Vec::new(),
+        }
+    }
+    fn with_item_id_pushed<F>(&mut self, id: ast::NodeId, f: F)
+          where F: Fn(&mut Self) {
+        if self.idstack.iter().any(|&x| x == id) {
+            let any_static = self.idstack.iter().any(|&x| {
+                if let ast_map::NodeItem(item) = self.ast_map.get(x) {
+                    if let hir::ItemStatic(..) = item.node {
+                        true
+                    } else {
+                        false
+                    }
+                } else {
+                    false
+                }
+            });
+            if any_static {
+                if !self.sess.features.borrow().static_recursion {
+                    emit_feature_err(&self.sess.parse_sess.span_diagnostic,
+                                     "static_recursion",
+                                     *self.root_span, GateIssue::Language, "recursive static");
+                }
+            } else {
+                span_err!(self.sess, *self.root_span, E0265, "recursive constant");
+            }
+            return;
+        }
+        self.idstack.push(id);
+        f(self);
+        self.idstack.pop();
+    }
+    // If a variant has an expression specifying its discriminant, then it needs
+    // to be checked just like a static or constant. However, if there are more
+    // variants with no explicitly specified discriminant, those variants will
+    // increment the same expression to get their values.
+    //
+    // So for every variant, we need to track whether there is an expression
+    // somewhere in the enum definition that controls its discriminant. We do
+    // this by starting from the end and searching backward.
+    fn populate_enum_discriminants(&self, enum_definition: &'ast hir::EnumDef) {
+        // Get the map, and return if we already processed this enum or if it
+        // has no variants.
+        let mut discriminant_map = self.discriminant_map.borrow_mut();
+        match enum_definition.variants.first() {
+            None => { return; }
+            Some(variant) if discriminant_map.contains_key(&variant.node.data.id()) => {
+                return;
+            }
+            _ => {}
+        }
+
+        // Go through all the variants.
+        let mut variant_stack: Vec<ast::NodeId> = Vec::new();
+        for variant in enum_definition.variants.iter().rev() {
+            variant_stack.push(variant.node.data.id());
+            // When we find an expression, every variant currently on the stack
+            // is affected by that expression.
+            if let Some(ref expr) = variant.node.disr_expr {
+                for id in &variant_stack {
+                    discriminant_map.insert(*id, Some(expr));
+                }
+                variant_stack.clear()
+            }
+        }
+        // If we are at the top, that always starts at 0, so any variant on the
+        // stack has a default value and does not need to be checked.
+        for id in &variant_stack {
+            discriminant_map.insert(*id, None);
+        }
+    }
+}
+
+impl<'a, 'ast: 'a> Visitor<'ast> for CheckItemRecursionVisitor<'a, 'ast> {
+    fn visit_item(&mut self, it: &'ast hir::Item) {
+        self.with_item_id_pushed(it.id, |v| intravisit::walk_item(v, it));
+    }
+
+    fn visit_enum_def(&mut self, enum_definition: &'ast hir::EnumDef,
+                      generics: &'ast hir::Generics, item_id: ast::NodeId, _: Span) {
+        self.populate_enum_discriminants(enum_definition);
+        intravisit::walk_enum_def(self, enum_definition, generics, item_id);
+    }
+
+    fn visit_variant(&mut self, variant: &'ast hir::Variant,
+                     _: &'ast hir::Generics, _: ast::NodeId) {
+        let variant_id = variant.node.data.id();
+        let maybe_expr;
+        if let Some(get_expr) = self.discriminant_map.borrow().get(&variant_id) {
+            // This is necessary because we need to let the `discriminant_map`
+            // borrow fall out of scope, so that we can reborrow farther down.
+            maybe_expr = (*get_expr).clone();
+        } else {
+            self.sess.span_bug(variant.span,
+                               "`check_static_recursion` attempted to visit \
+                                variant with unknown discriminant")
+        }
+        // If `maybe_expr` is `None`, that's because no discriminant is
+        // specified that affects this variant. Thus, no risk of recursion.
+        if let Some(expr) = maybe_expr {
+            self.with_item_id_pushed(expr.id, |v| intravisit::walk_expr(v, expr));
+        }
+    }
+
+    fn visit_trait_item(&mut self, ti: &'ast hir::TraitItem) {
+        self.with_item_id_pushed(ti.id, |v| intravisit::walk_trait_item(v, ti));
+    }
+
+    fn visit_impl_item(&mut self, ii: &'ast hir::ImplItem) {
+        self.with_item_id_pushed(ii.id, |v| intravisit::walk_impl_item(v, ii));
+    }
+
+    fn visit_expr(&mut self, e: &'ast hir::Expr) {
+        match e.node {
+            hir::ExprPath(..) => {
+                match self.def_map.get(&e.id).map(|d| d.base_def) {
+                    Some(Def::Static(def_id, _)) |
+                    Some(Def::AssociatedConst(def_id)) |
+                    Some(Def::Const(def_id)) => {
+                        if let Some(node_id) = self.ast_map.as_local_node_id(def_id) {
+                            match self.ast_map.get(node_id) {
+                                ast_map::NodeItem(item) =>
+                                    self.visit_item(item),
+                                ast_map::NodeTraitItem(item) =>
+                                    self.visit_trait_item(item),
+                                ast_map::NodeImplItem(item) =>
+                                    self.visit_impl_item(item),
+                                ast_map::NodeForeignItem(_) => {},
+                                _ => {
+                                    self.sess.span_bug(
+                                        e.span,
+                                        &format!("expected item, found {}",
+                                                 self.ast_map.node_to_string(node_id)));
+                                }
+                            }
+                        }
+                    }
+                    // For variants, we only want to check expressions that
+                    // affect the specific variant used, but we need to check
+                    // the whole enum definition to see what expression that
+                    // might be (if any).
+                    Some(Def::Variant(enum_id, variant_id)) => {
+                        if let Some(enum_node_id) = self.ast_map.as_local_node_id(enum_id) {
+                            if let hir::ItemEnum(ref enum_def, ref generics) =
+                                self.ast_map.expect_item(enum_node_id).node
+                            {
+                                self.populate_enum_discriminants(enum_def);
+                                let enum_id = self.ast_map.as_local_node_id(enum_id).unwrap();
+                                let variant_id = self.ast_map.as_local_node_id(variant_id).unwrap();
+                                let variant = self.ast_map.expect_variant(variant_id);
+                                self.visit_variant(variant, generics, enum_id);
+                            } else {
+                                self.sess.span_bug(e.span,
+                                                   "`check_static_recursion` found \
+                                                    non-enum in Def::Variant");
+                            }
+                        }
+                    }
+                    _ => ()
+                }
+            },
+            _ => ()
+        }
+        intravisit::walk_expr(self, e);
+    }
+}