]> git.proxmox.com Git - rustc.git/blob - src/librustc/middle/check_const.rs
Merge tag 'upstream-tar/1.0.0_0alpha'
[rustc.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 }