]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/check_const.rs
Merge pull request #20510 from tshepang/patch-6
[rust.git] / src / librustc / middle / check_const.rs
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 middle::def::*;
13 use middle::ty;
14 use util::ppaux;
15
16 use syntax::ast;
17 use syntax::visit::{self, Visitor};
18
19 struct CheckCrateVisitor<'a, 'tcx: 'a> {
20     tcx: &'a ty::ctxt<'tcx>,
21     in_const: bool
22 }
23
24 impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
25     fn with_const<F>(&mut self, in_const: bool, f: F) where
26         F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>),
27     {
28         let was_const = self.in_const;
29         self.in_const = in_const;
30         f(self);
31         self.in_const = was_const;
32     }
33     fn inside_const<F>(&mut self, f: F) where
34         F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>),
35     {
36         self.with_const(true, f);
37     }
38 }
39
40 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
41     fn visit_item(&mut self, i: &ast::Item) {
42         match i.node {
43             ast::ItemStatic(_, _, ref ex) |
44             ast::ItemConst(_, ref ex) => {
45                 self.inside_const(|v| v.visit_expr(&**ex));
46             }
47             ast::ItemEnum(ref enum_definition, _) => {
48                 self.inside_const(|v| {
49                     for var in enum_definition.variants.iter() {
50                         if let Some(ref ex) = var.node.disr_expr {
51                             v.visit_expr(&**ex);
52                         }
53                     }
54                 });
55             }
56             _ => self.with_const(false, |v| visit::walk_item(v, i))
57         }
58     }
59     fn visit_pat(&mut self, p: &ast::Pat) {
60         let is_const = match p.node {
61             ast::PatLit(_) | ast::PatRange(..) => true,
62             _ => false
63         };
64         self.with_const(is_const, |v| visit::walk_pat(v, p))
65     }
66     fn visit_expr(&mut self, ex: &ast::Expr) {
67         if self.in_const {
68             check_expr(self, ex);
69         }
70         visit::walk_expr(self, ex);
71     }
72 }
73
74 pub fn check_crate(tcx: &ty::ctxt) {
75     visit::walk_crate(&mut CheckCrateVisitor { tcx: tcx, in_const: false },
76                       tcx.map.krate());
77     tcx.sess.abort_if_errors();
78 }
79
80 fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) {
81     match e.node {
82         ast::ExprUnary(ast::UnDeref, _) => {}
83         ast::ExprUnary(ast::UnUniq, _) => {
84             span_err!(v.tcx.sess, e.span, E0010,
85                       "cannot do allocations in constant expressions");
86         }
87         ast::ExprBinary(..) | ast::ExprUnary(..) => {
88             let method_call = ty::MethodCall::expr(e.id);
89             if v.tcx.method_map.borrow().contains_key(&method_call) {
90                 span_err!(v.tcx.sess, e.span, E0011,
91                           "user-defined operators are not allowed in constant \
92                            expressions");
93             }
94         }
95         ast::ExprLit(_) => {}
96         ast::ExprCast(ref from, _) => {
97             let toty = ty::expr_ty(v.tcx, e);
98             let fromty = ty::expr_ty(v.tcx, &**from);
99             let is_legal_cast =
100                 ty::type_is_numeric(toty) ||
101                 ty::type_is_unsafe_ptr(toty) ||
102                 (ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty));
103             if !is_legal_cast {
104                 span_err!(v.tcx.sess, e.span, E0012,
105                           "can not cast to `{}` in a constant expression",
106                           ppaux::ty_to_string(v.tcx, toty));
107             }
108             if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) {
109                 span_err!(v.tcx.sess, e.span, E0018,
110                           "can not cast a pointer to an integer in a constant \
111                            expression");
112             }
113         }
114         ast::ExprPath(_) => {
115             match v.tcx.def_map.borrow()[e.id] {
116                 DefStatic(..) | DefConst(..) |
117                 DefFn(..) | DefStaticMethod(..) | DefMethod(..) |
118                 DefStruct(_) | DefVariant(_, _, _) => {}
119
120                 def => {
121                     debug!("(checking const) found bad def: {}", def);
122                     span_err!(v.tcx.sess, e.span, E0014,
123                               "paths in constants may only refer to constants \
124                                or functions");
125                 }
126             }
127         }
128         ast::ExprCall(ref callee, _) => {
129             match v.tcx.def_map.borrow()[callee.id] {
130                 DefStruct(..) | DefVariant(..) => {}    // OK.
131                 _ => {
132                     span_err!(v.tcx.sess, e.span, E0015,
133                               "function calls in constants are limited to \
134                                struct and enum constructors");
135                 }
136             }
137         }
138         ast::ExprBlock(ref block) => {
139             // Check all statements in the block
140             for stmt in block.stmts.iter() {
141                 let block_span_err = |&: span|
142                     span_err!(v.tcx.sess, span, E0016,
143                               "blocks in constants are limited to items and \
144                                tail expressions");
145                 match stmt.node {
146                     ast::StmtDecl(ref decl, _) => {
147                         match decl.node {
148                             ast::DeclLocal(_) => block_span_err(decl.span),
149
150                             // Item statements are allowed
151                             ast::DeclItem(_) => {}
152                         }
153                     }
154                     ast::StmtExpr(ref expr, _) => block_span_err(expr.span),
155                     ast::StmtSemi(ref semi, _) => block_span_err(semi.span),
156                     ast::StmtMac(..) => {
157                         v.tcx.sess.span_bug(e.span, "unexpanded statement \
158                                                      macro in const?!")
159                     }
160                 }
161             }
162         }
163         ast::ExprVec(_) |
164         ast::ExprAddrOf(ast::MutImmutable, _) |
165         ast::ExprParen(..) |
166         ast::ExprField(..) |
167         ast::ExprTupField(..) |
168         ast::ExprIndex(..) |
169         ast::ExprTup(..) |
170         ast::ExprRepeat(..) |
171         ast::ExprStruct(..) => {}
172
173         ast::ExprAddrOf(_, ref inner) => {
174             match inner.node {
175                 // Mutable slices are allowed.
176                 ast::ExprVec(_) => {}
177                 _ => span_err!(v.tcx.sess, e.span, E0017,
178                                "references in constants may only refer \
179                                 to immutable values")
180
181             }
182         }
183
184         _ => span_err!(v.tcx.sess, e.span, E0019,
185                        "constant contains unimplemented expression type")
186     }
187 }