]> git.lizzy.rs Git - rust.git/blob - src/librustc_passes/const_fn.rs
Rollup merge of #31031 - brson:issue-30123, r=nikomatsakis
[rust.git] / src / librustc_passes / const_fn.rs
1 // Copyright 2012-2014 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 //! Verifies that const fn arguments are immutable by value bindings
12 //! and the const fn body doesn't contain any statements
13
14 use rustc::session::Session;
15
16 use syntax::ast;
17 use syntax::visit::{self, Visitor, FnKind};
18 use syntax::codemap::Span;
19
20 pub fn check_crate(sess: &Session, krate: &ast::Crate) {
21     visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
22     sess.abort_if_errors();
23 }
24
25 struct CheckConstFn<'a> {
26     sess: &'a Session,
27 }
28
29 struct CheckBlock<'a> {
30     sess: &'a Session,
31     kind: &'static str,
32 }
33
34 impl<'a, 'v> Visitor<'v> for CheckBlock<'a> {
35     fn visit_block(&mut self, block: &'v ast::Block) {
36         check_block(&self.sess, block, self.kind);
37         CheckConstFn{ sess: self.sess}.visit_block(block);
38     }
39     fn visit_expr(&mut self, e: &'v ast::Expr) {
40         if let ast::ExprClosure(..) = e.node {
41             CheckConstFn{ sess: self.sess}.visit_expr(e);
42         } else {
43             visit::walk_expr(self, e);
44         }
45     }
46     fn visit_item(&mut self, _i: &'v ast::Item) { panic!("should be handled in CheckConstFn") }
47     fn visit_fn(&mut self,
48                 _fk: FnKind<'v>,
49                 _fd: &'v ast::FnDecl,
50                 _b: &'v ast::Block,
51                 _s: Span,
52                 _fn_id: ast::NodeId) { panic!("should be handled in CheckConstFn") }
53 }
54
55 fn check_block(sess: &Session, b: &ast::Block, kind: &'static str) {
56     // Check all statements in the block
57     for stmt in &b.stmts {
58         let span = match stmt.node {
59             ast::StmtDecl(ref decl, _) => {
60                 match decl.node {
61                     ast::DeclLocal(_) => decl.span,
62
63                     // Item statements are allowed
64                     ast::DeclItem(_) => continue,
65                 }
66             }
67             ast::StmtExpr(ref expr, _) => expr.span,
68             ast::StmtSemi(ref semi, _) => semi.span,
69             ast::StmtMac(..) => unreachable!(),
70         };
71         span_err!(sess, span, E0016,
72                   "blocks in {}s are limited to items and tail expressions", kind);
73     }
74 }
75
76 impl<'a, 'v> Visitor<'v> for CheckConstFn<'a> {
77     fn visit_item(&mut self, i: &'v ast::Item) {
78         visit::walk_item(self, i);
79         match i.node {
80             ast::ItemConst(_, ref e) => {
81                 CheckBlock{ sess: self.sess, kind: "constant"}.visit_expr(e)
82             },
83             ast::ItemStatic(_, _, ref e) => {
84                 CheckBlock{ sess: self.sess, kind: "static"}.visit_expr(e)
85             },
86             _ => {},
87         }
88     }
89
90     fn visit_fn(&mut self,
91                 fk: FnKind<'v>,
92                 fd: &'v ast::FnDecl,
93                 b: &'v ast::Block,
94                 s: Span,
95                 _fn_id: ast::NodeId) {
96         visit::walk_fn(self, fk, fd, b, s);
97         match fk {
98             FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {},
99             FnKind::Method(_, m, _) if m.constness == ast::Constness::Const => {},
100             _ => return,
101         }
102
103         // Ensure the arguments are simple, not mutable/by-ref or patterns.
104         for arg in &fd.inputs {
105             match arg.pat.node {
106                 ast::PatWild => {}
107                 ast::PatIdent(ast::BindingMode::ByValue(ast::MutImmutable), _, None) => {}
108                 _ => {
109                     span_err!(self.sess, arg.pat.span, E0022,
110                               "arguments of constant functions can only \
111                                be immutable by-value bindings");
112                 }
113             }
114         }
115         check_block(&self.sess, b, "const function");
116     }
117 }