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.
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.
12 use driver
::session
::Session
;
20 use syntax
::{visit, ast_util, ast_map}
;
22 pub fn check_crate(sess
: Session
,
24 ast_map
: ast_map
::map
,
25 def_map
: resolve
::DefMap
,
26 method_map
: typeck
::method_map
,
28 visit
::visit_crate(crate, (false, visit
::mk_vt(@visit
::Visitor
{
29 visit_item
: |a
,b
| check_item(sess
, ast_map
, def_map
, a
, b
),
32 check_expr(sess
, def_map
, method_map
, tcx
, a
, b
),
33 .. *visit
::default_visitor()
35 sess
.abort_if_errors();
38 pub fn check_item(sess
: Session
,
39 ast_map
: ast_map
::map
,
40 def_map
: resolve
::DefMap
,
42 (_is_const
, v
): (bool
,
45 item_static(_
, _
, ex
) => {
46 (v
.visit_expr
)(ex
, (true, v
));
47 check_item_recursion(sess
, ast_map
, def_map
, it
);
49 item_enum(ref enum_definition
, _
) => {
50 for (*enum_definition
).variants
.iter().advance
|var
| {
51 for var
.node
.disr_expr
.iter().advance
|ex
| {
52 (v
.visit_expr
)(*ex
, (true, v
));
56 _
=> visit
::visit_item(it
, (false, v
))
60 pub fn check_pat(p
: @pat
, (_is_const
, v
): (bool
, visit
::vt
<bool
>)) {
61 fn is_str(e
: @expr
) -> bool
{
64 @expr
{ node
: expr_lit(@codemap
::spanned
{
74 // Let through plain ~-string literals here
75 pat_lit(a
) => if !is_str(a
) { (v.visit_expr)(a, (true, v)); }
,
77 if !is_str(a
) { (v.visit_expr)(a, (true, v)); }
78 if !is_str(b
) { (v.visit_expr)(b, (true, v)); }
80 _
=> visit
::visit_pat(p
, (false, v
))
84 pub fn check_expr(sess
: Session
,
85 def_map
: resolve
::DefMap
,
86 method_map
: typeck
::method_map
,
93 expr_unary(_
, deref
, _
) => { }
94 expr_unary(_
, box(_
), _
) | expr_unary(_
, uniq
, _
) => {
96 "disallowed operator in constant expression");
99 expr_lit(@codemap
::spanned {node: lit_str(_), _}
) => { }
100 expr_binary(*) | expr_unary(*) => {
101 if method_map
.contains_key(&e
.id
) {
102 sess
.span_err(e
.span
, "user-defined operators are not \
103 allowed in constant expressions");
108 let ety
= ty
::expr_ty(tcx
, e
);
109 if !ty
::type_is_numeric(ety
) && !ty
::type_is_unsafe_ptr(ety
) {
110 sess
.span_err(e
.span
, ~"can not cast to `" +
111 ppaux
::ty_to_str(tcx
, ety
) +
112 "` in a constant expression");
116 // NB: In the future you might wish to relax this slightly
117 // to handle on-demand instantiation of functions via
118 // foo::<bar> in a const. Currently that is only done on
119 // a path in trans::callee that only works in block contexts.
120 if pth
.types
.len() != 0 {
122 e
.span
, "paths in constants may only refer to \
123 items without type parameters");
125 match def_map
.find(&e
.id
) {
126 Some(&def_static(*)) |
127 Some(&def_fn(_
, _
)) |
128 Some(&def_variant(_
, _
)) |
129 Some(&def_struct(_
)) => { }
132 debug
!("(checking const) found bad def: %?", def
);
135 "paths in constants may only refer to \
136 constants or functions");
139 sess
.span_bug(e
.span
, "unbound path in const?!");
143 expr_call(callee
, _
, NoSugar
) => {
144 match def_map
.find(&callee
.id
) {
145 Some(&def_struct(*)) => {}
// OK.
146 Some(&def_variant(*)) => {}
// OK.
150 "function calls in constants are limited to \
151 struct and enum constructors");
155 expr_paren(e
) => { check_expr(sess
, def_map
, method_map
,
156 tcx
, e
, (is_const
, v
)); }
157 expr_vstore(_
, expr_vstore_slice
) |
159 expr_addr_of(m_imm
, _
) |
163 expr_struct(_
, _
, None
) => { }
167 "borrowed pointers in constants may only refer to \
171 sess
.span_err(e
.span
,
172 "constant contains unimplemented expression type");
178 expr_lit(@codemap
::spanned {node: lit_int(v, t), _}
) => {
180 if (v
as u64) > ast_util
::int_ty_max(
181 if t
== ty_i { sess.targ_cfg.int_type }
else { t }
) {
182 sess
.span_err(e
.span
, "literal out of range for its type");
186 expr_lit(@codemap
::spanned {node: lit_uint(v, t), _}
) => {
187 if v
> ast_util
::uint_ty_max(
188 if t
== ty_u { sess.targ_cfg.uint_type }
else { t }
) {
189 sess
.span_err(e
.span
, "literal out of range for its type");
194 visit
::visit_expr(e
, (is_const
, v
));
197 // Make sure a const item doesn't recursively refer to itself
198 // FIXME: Should use the dependency graph when it's available (#1356)
199 pub fn check_item_recursion(sess
: Session
,
200 ast_map
: ast_map
::map
,
201 def_map
: resolve
::DefMap
,
206 ast_map
: ast_map
::map
,
207 def_map
: resolve
::DefMap
,
208 idstack
: @
mut ~[node_id
]
219 let visitor
= visit
::mk_vt(@visit
::Visitor
{
220 visit_item
: visit_item
,
221 visit_expr
: visit_expr
,
222 .. *visit
::default_visitor()
224 (visitor
.visit_item
)(it
, (env
, visitor
));
226 fn visit_item(it
: @item
, (env
, v
): (env
, visit
::vt
<env
>)) {
227 if env
.idstack
.iter().any_(|x
| x
== &(it
.id
)) {
228 env
.sess
.span_fatal(env
.root_it
.span
, "recursive constant");
230 env
.idstack
.push(it
.id
);
231 visit
::visit_item(it
, (env
, v
));
235 fn visit_expr(e
: @expr
, (env
, v
): (env
, visit
::vt
<env
>)) {
237 expr_path(*) => match env
.def_map
.find(&e
.id
) {
238 Some(&def_static(def_id
, _
)) if ast_util
::is_local(def_id
) =>
239 match env
.ast_map
.get_copy(&def_id
.node
) {
240 ast_map
::node_item(it
, _
) => {
241 (v
.visit_item
)(it
, (env
, v
));
243 _
=> fail
!("const not bound to an item")
249 visit
::visit_expr(e
, (env
, v
));