]> git.lizzy.rs Git - rust.git/blob - src/librustc_passes/consts.rs
trans-scheduler: Let main thread take over for other worker.
[rust.git] / src / librustc_passes / consts.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 the types and values of const and static items
12 // are safe. The rules enforced by this module are:
13 //
14 // - For each *mutable* static item, it checks that its **type**:
15 //     - doesn't have a destructor
16 //     - doesn't own a box
17 //
18 // - For each *immutable* static item, it checks that its **value**:
19 //       - doesn't own a box
20 //       - doesn't contain a struct literal or a call to an enum variant / struct constructor where
21 //           - the type of the struct/enum has a dtor
22 //
23 // Rules Enforced Elsewhere:
24 // - It's not possible to take the address of a static item with unsafe interior. This is enforced
25 // by borrowck::gather_loans
26
27 use rustc::ty::cast::CastKind;
28 use rustc_const_eval::ConstContext;
29 use rustc::middle::const_val::ConstEvalErr;
30 use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll};
31 use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
32 use rustc::middle::const_val::ErrKind::{TypeckError, Math, LayoutError};
33 use rustc_const_math::{ConstMathErr, Op};
34 use rustc::hir::def::{Def, CtorKind};
35 use rustc::hir::def_id::DefId;
36 use rustc::hir::map::blocks::FnLikeNode;
37 use rustc::middle::expr_use_visitor as euv;
38 use rustc::middle::mem_categorization as mc;
39 use rustc::middle::mem_categorization::Categorization;
40 use rustc::mir::transform::MirSource;
41 use rustc::ty::{self, Ty, TyCtxt};
42 use rustc::ty::subst::Substs;
43 use rustc::traits::Reveal;
44 use rustc::util::common::ErrorReported;
45 use rustc::util::nodemap::NodeSet;
46 use rustc::lint::builtin::CONST_ERR;
47
48 use rustc::hir::{self, PatKind, RangeEnd};
49 use syntax::ast;
50 use syntax_pos::{Span, DUMMY_SP};
51 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
52
53 use std::collections::hash_map::Entry;
54 use std::cmp::Ordering;
55
56 struct CheckCrateVisitor<'a, 'tcx: 'a> {
57     tcx: TyCtxt<'a, 'tcx, 'tcx>,
58     in_fn: bool,
59     promotable: bool,
60     mut_rvalue_borrows: NodeSet,
61     param_env: ty::ParamEnv<'tcx>,
62     identity_substs: &'tcx Substs<'tcx>,
63     tables: &'a ty::TypeckTables<'tcx>,
64 }
65
66 impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
67     fn const_cx(&self) -> ConstContext<'a, 'gcx> {
68         ConstContext::new(self.tcx, self.param_env.and(self.identity_substs), self.tables)
69     }
70
71     fn check_const_eval(&self, expr: &'gcx hir::Expr) {
72         if let Err(err) = self.const_cx().eval(expr) {
73             match err.kind {
74                 UnimplementedConstVal(_) => {}
75                 IndexOpFeatureGated => {}
76                 ErroneousReferencedConstant(_) => {}
77                 TypeckError => {}
78                 _ => {
79                     self.tcx.sess.add_lint(CONST_ERR,
80                                            expr.id,
81                                            expr.span,
82                                            format!("constant evaluation error: {}. This will \
83                                                     become a HARD ERROR in the future",
84                                                    err.description().into_oneline()))
85                 }
86             }
87         }
88     }
89
90     // Adds the worst effect out of all the values of one type.
91     fn add_type(&mut self, ty: Ty<'gcx>) {
92         if !ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) {
93             self.promotable = false;
94         }
95
96         if ty.needs_drop(self.tcx, self.param_env) {
97             self.promotable = false;
98         }
99     }
100
101     fn handle_const_fn_call(&mut self, def_id: DefId, ret_ty: Ty<'gcx>) {
102         self.add_type(ret_ty);
103
104         self.promotable &= if let Some(fn_id) = self.tcx.hir.as_local_node_id(def_id) {
105             FnLikeNode::from_node(self.tcx.hir.get(fn_id)).map_or(false, |fn_like| {
106                 fn_like.constness() == hir::Constness::Const
107             })
108         } else {
109             self.tcx.is_const_fn(def_id)
110         };
111     }
112 }
113
114 impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
115     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
116         NestedVisitorMap::None
117     }
118
119     fn visit_nested_body(&mut self, body_id: hir::BodyId) {
120         match self.tcx.rvalue_promotable_to_static.borrow_mut().entry(body_id.node_id) {
121             Entry::Occupied(_) => return,
122             Entry::Vacant(entry) => {
123                 // Prevent infinite recursion on re-entry.
124                 entry.insert(false);
125             }
126         }
127
128         let item_id = self.tcx.hir.body_owner(body_id);
129         let item_def_id = self.tcx.hir.local_def_id(item_id);
130
131         let outer_in_fn = self.in_fn;
132         let outer_tables = self.tables;
133         let outer_param_env = self.param_env;
134         let outer_identity_substs = self.identity_substs;
135
136         self.in_fn = match MirSource::from_node(self.tcx, item_id) {
137             MirSource::Fn(_) => true,
138             _ => false
139         };
140         self.tables = self.tcx.typeck_tables_of(item_def_id);
141         self.param_env = self.tcx.param_env(item_def_id);
142         self.identity_substs = Substs::identity_for_item(self.tcx, item_def_id);
143
144         let body = self.tcx.hir.body(body_id);
145         if !self.in_fn {
146             self.check_const_eval(&body.value);
147         }
148
149         let tcx = self.tcx;
150         let param_env = self.param_env;
151         let region_maps = self.tcx.region_maps(item_def_id);
152         euv::ExprUseVisitor::new(self, tcx, param_env, &region_maps, self.tables)
153             .consume_body(body);
154
155         self.visit_body(body);
156
157         self.in_fn = outer_in_fn;
158         self.tables = outer_tables;
159         self.param_env = outer_param_env;
160         self.identity_substs = outer_identity_substs;
161     }
162
163     fn visit_pat(&mut self, p: &'tcx hir::Pat) {
164         match p.node {
165             PatKind::Lit(ref lit) => {
166                 self.check_const_eval(lit);
167             }
168             PatKind::Range(ref start, ref end, RangeEnd::Excluded) => {
169                 match self.const_cx().compare_lit_exprs(p.span, start, end) {
170                     Ok(Ordering::Less) => {}
171                     Ok(Ordering::Equal) |
172                     Ok(Ordering::Greater) => {
173                         span_err!(self.tcx.sess,
174                                   start.span,
175                                   E0579,
176                                   "lower range bound must be less than upper");
177                     }
178                     Err(ErrorReported) => {}
179                 }
180             }
181             PatKind::Range(ref start, ref end, RangeEnd::Included) => {
182                 match self.const_cx().compare_lit_exprs(p.span, start, end) {
183                     Ok(Ordering::Less) |
184                     Ok(Ordering::Equal) => {}
185                     Ok(Ordering::Greater) => {
186                         struct_span_err!(self.tcx.sess, start.span, E0030,
187                             "lower range bound must be less than or equal to upper")
188                             .span_label(start.span, "lower bound larger than upper bound")
189                             .emit();
190                     }
191                     Err(ErrorReported) => {}
192                 }
193             }
194             _ => {}
195         }
196         intravisit::walk_pat(self, p);
197     }
198
199     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
200         match stmt.node {
201             hir::StmtDecl(ref decl, _) => {
202                 match decl.node {
203                     hir::DeclLocal(_) => {
204                         self.promotable = false;
205                     }
206                     // Item statements are allowed
207                     hir::DeclItem(_) => {}
208                 }
209             }
210             hir::StmtExpr(..) |
211             hir::StmtSemi(..) => {
212                 self.promotable = false;
213             }
214         }
215         intravisit::walk_stmt(self, stmt);
216     }
217
218     fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
219         let outer = self.promotable;
220         self.promotable = true;
221
222         let node_ty = self.tables.node_id_to_type(ex.id);
223         check_expr(self, ex, node_ty);
224         check_adjustments(self, ex);
225
226         if let hir::ExprMatch(ref discr, ref arms, _) = ex.node {
227             // Compute the most demanding borrow from all the arms'
228             // patterns and set that on the discriminator.
229             let mut mut_borrow = false;
230             for pat in arms.iter().flat_map(|arm| &arm.pats) {
231                 if self.mut_rvalue_borrows.remove(&pat.id) {
232                     mut_borrow = true;
233                 }
234             }
235             if mut_borrow {
236                 self.mut_rvalue_borrows.insert(discr.id);
237             }
238         }
239
240         intravisit::walk_expr(self, ex);
241
242         // Handle borrows on (or inside the autorefs of) this expression.
243         if self.mut_rvalue_borrows.remove(&ex.id) {
244             self.promotable = false;
245         }
246
247         if self.in_fn && self.promotable {
248             match self.const_cx().eval(ex) {
249                 Ok(_) => {}
250                 Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) |
251                 Err(ConstEvalErr { kind: MiscCatchAll, .. }) |
252                 Err(ConstEvalErr { kind: MiscBinaryOp, .. }) |
253                 Err(ConstEvalErr { kind: NonConstPath, .. }) |
254                 Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), .. }) |
255                 Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), .. }) |
256                 Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) |
257                 Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {}
258                 Err(ConstEvalErr { kind: TypeckError, .. }) => {}
259                 Err(ConstEvalErr {
260                     kind: LayoutError(ty::layout::LayoutError::Unknown(_)), ..
261                 }) => {}
262                 Err(msg) => {
263                     self.tcx.sess.add_lint(CONST_ERR,
264                                            ex.id,
265                                            msg.span,
266                                            msg.description().into_oneline().into_owned())
267                 }
268             }
269         }
270
271         self.tcx.rvalue_promotable_to_static.borrow_mut().insert(ex.id, self.promotable);
272         self.promotable &= outer;
273     }
274 }
275
276 /// This function is used to enforce the constraints on
277 /// const/static items. It walks through the *value*
278 /// of the item walking down the expression and evaluating
279 /// every nested expression. If the expression is not part
280 /// of a const/static item, it is qualified for promotion
281 /// instead of producing errors.
282 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node_ty: Ty<'tcx>) {
283     match node_ty.sty {
284         ty::TyAdt(def, _) if def.has_dtor(v.tcx) => {
285             v.promotable = false;
286         }
287         _ => {}
288     }
289
290     match e.node {
291         hir::ExprUnary(..) |
292         hir::ExprBinary(..) |
293         hir::ExprIndex(..) if v.tables.is_method_call(e) => {
294             v.promotable = false;
295         }
296         hir::ExprBox(_) => {
297             v.promotable = false;
298         }
299         hir::ExprUnary(op, ref inner) => {
300             match v.tables.node_id_to_type(inner.id).sty {
301                 ty::TyRawPtr(_) => {
302                     assert!(op == hir::UnDeref);
303
304                     v.promotable = false;
305                 }
306                 _ => {}
307             }
308         }
309         hir::ExprBinary(op, ref lhs, _) => {
310             match v.tables.node_id_to_type(lhs.id).sty {
311                 ty::TyRawPtr(_) => {
312                     assert!(op.node == hir::BiEq || op.node == hir::BiNe ||
313                             op.node == hir::BiLe || op.node == hir::BiLt ||
314                             op.node == hir::BiGe || op.node == hir::BiGt);
315
316                     v.promotable = false;
317                 }
318                 _ => {}
319             }
320         }
321         hir::ExprCast(ref from, _) => {
322             debug!("Checking const cast(id={})", from.id);
323             match v.tables.cast_kinds.get(&from.id) {
324                 None => span_bug!(e.span, "no kind for cast"),
325                 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
326                     v.promotable = false;
327                 }
328                 _ => {}
329             }
330         }
331         hir::ExprPath(ref qpath) => {
332             let def = v.tables.qpath_def(qpath, e.id);
333             match def {
334                 Def::VariantCtor(..) | Def::StructCtor(..) |
335                 Def::Fn(..) | Def::Method(..) => {}
336                 Def::AssociatedConst(_) => v.add_type(node_ty),
337                 Def::Const(did) => {
338                     v.promotable &= if let Some(node_id) = v.tcx.hir.as_local_node_id(did) {
339                         match v.tcx.hir.expect_item(node_id).node {
340                             hir::ItemConst(_, body) => {
341                                 v.visit_nested_body(body);
342                                 v.tcx.rvalue_promotable_to_static.borrow()[&body.node_id]
343                             }
344                             _ => false
345                         }
346                     } else {
347                         v.tcx.const_is_rvalue_promotable_to_static(did)
348                     };
349                 }
350                 _ => {
351                     v.promotable = false;
352                 }
353             }
354         }
355         hir::ExprCall(ref callee, _) => {
356             let mut callee = &**callee;
357             loop {
358                 callee = match callee.node {
359                     hir::ExprBlock(ref block) => match block.expr {
360                         Some(ref tail) => &tail,
361                         None => break
362                     },
363                     _ => break
364                 };
365             }
366             // The callee is an arbitrary expression, it doesn't necessarily have a definition.
367             let def = if let hir::ExprPath(ref qpath) = callee.node {
368                 v.tables.qpath_def(qpath, callee.id)
369             } else {
370                 Def::Err
371             };
372             match def {
373                 Def::StructCtor(_, CtorKind::Fn) |
374                 Def::VariantCtor(_, CtorKind::Fn) => {}
375                 Def::Fn(did) => {
376                     v.handle_const_fn_call(did, node_ty)
377                 }
378                 Def::Method(did) => {
379                     match v.tcx.associated_item(did).container {
380                         ty::ImplContainer(_) => {
381                             v.handle_const_fn_call(did, node_ty)
382                         }
383                         ty::TraitContainer(_) => v.promotable = false
384                     }
385                 }
386                 _ => v.promotable = false
387             }
388         }
389         hir::ExprMethodCall(..) => {
390             let def_id = v.tables.type_dependent_defs[&e.id].def_id();
391             match v.tcx.associated_item(def_id).container {
392                 ty::ImplContainer(_) => v.handle_const_fn_call(def_id, node_ty),
393                 ty::TraitContainer(_) => v.promotable = false
394             }
395         }
396         hir::ExprStruct(..) => {
397             if let ty::TyAdt(adt, ..) = v.tables.expr_ty(e).sty {
398                 // unsafe_cell_type doesn't necessarily exist with no_core
399                 if Some(adt.did) == v.tcx.lang_items.unsafe_cell_type() {
400                     v.promotable = false;
401                 }
402             }
403         }
404
405         hir::ExprLit(_) |
406         hir::ExprAddrOf(..) |
407         hir::ExprRepeat(..) => {}
408
409         hir::ExprClosure(..) => {
410             // Paths in constant contexts cannot refer to local variables,
411             // as there are none, and thus closures can't have upvars there.
412             if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
413                 v.promotable = false;
414             }
415         }
416
417         hir::ExprBlock(_) |
418         hir::ExprIndex(..) |
419         hir::ExprField(..) |
420         hir::ExprTupField(..) |
421         hir::ExprArray(_) |
422         hir::ExprType(..) |
423         hir::ExprTup(..) => {}
424
425         // Conditional control flow (possible to implement).
426         hir::ExprMatch(..) |
427         hir::ExprIf(..) |
428
429         // Loops (not very meaningful in constants).
430         hir::ExprWhile(..) |
431         hir::ExprLoop(..) |
432
433         // More control flow (also not very meaningful).
434         hir::ExprBreak(..) |
435         hir::ExprAgain(_) |
436         hir::ExprRet(_) |
437
438         // Expressions with side-effects.
439         hir::ExprAssign(..) |
440         hir::ExprAssignOp(..) |
441         hir::ExprInlineAsm(..) => {
442             v.promotable = false;
443         }
444     }
445 }
446
447 /// Check the adjustments of an expression
448 fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
449     use rustc::ty::adjustment::*;
450
451     for adjustment in v.tables.expr_adjustments(e) {
452         match adjustment.kind {
453             Adjust::NeverToAny |
454             Adjust::ReifyFnPointer |
455             Adjust::UnsafeFnPointer |
456             Adjust::ClosureFnPointer |
457             Adjust::MutToConstPointer |
458             Adjust::Borrow(_) |
459             Adjust::Unsize => {}
460
461             Adjust::Deref(ref overloaded) => {
462                 if overloaded.is_some() {
463                     v.promotable = false;
464                     break;
465                 }
466             }
467         }
468     }
469 }
470
471 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
472     tcx.hir.krate().visit_all_item_likes(&mut CheckCrateVisitor {
473         tcx: tcx,
474         tables: &ty::TypeckTables::empty(),
475         in_fn: false,
476         promotable: false,
477         mut_rvalue_borrows: NodeSet(),
478         param_env: ty::ParamEnv::empty(Reveal::UserFacing),
479         identity_substs: Substs::empty(),
480     }.as_deep_visitor());
481     tcx.sess.abort_if_errors();
482 }
483
484 impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> {
485     fn consume(&mut self,
486                _consume_id: ast::NodeId,
487                _consume_span: Span,
488                _cmt: mc::cmt,
489                _mode: euv::ConsumeMode) {}
490
491     fn borrow(&mut self,
492               borrow_id: ast::NodeId,
493               _borrow_span: Span,
494               cmt: mc::cmt<'tcx>,
495               _loan_region: ty::Region<'tcx>,
496               bk: ty::BorrowKind,
497               loan_cause: euv::LoanCause) {
498         // Kind of hacky, but we allow Unsafe coercions in constants.
499         // These occur when we convert a &T or *T to a *U, as well as
500         // when making a thin pointer (e.g., `*T`) into a fat pointer
501         // (e.g., `*Trait`).
502         match loan_cause {
503             euv::LoanCause::AutoUnsafe => {
504                 return;
505             }
506             _ => {}
507         }
508
509         let mut cur = &cmt;
510         loop {
511             match cur.cat {
512                 Categorization::Rvalue(..) => {
513                     if loan_cause == euv::MatchDiscriminant {
514                         // Ignore the dummy immutable borrow created by EUV.
515                         break;
516                     }
517                     if bk.to_mutbl_lossy() == hir::MutMutable {
518                         self.mut_rvalue_borrows.insert(borrow_id);
519                     }
520                     break;
521                 }
522                 Categorization::StaticItem => {
523                     break;
524                 }
525                 Categorization::Deref(ref cmt, _) |
526                 Categorization::Downcast(ref cmt, _) |
527                 Categorization::Interior(ref cmt, _) => {
528                     cur = cmt;
529                 }
530
531                 Categorization::Upvar(..) |
532                 Categorization::Local(..) => break,
533             }
534         }
535     }
536
537     fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {}
538     fn mutate(&mut self,
539               _assignment_id: ast::NodeId,
540               _assignment_span: Span,
541               _assignee_cmt: mc::cmt,
542               _mode: euv::MutateMode) {
543     }
544
545     fn matched_pat(&mut self, _: &hir::Pat, _: mc::cmt, _: euv::MatchMode) {}
546
547     fn consume_pat(&mut self, _consume_pat: &hir::Pat, _cmt: mc::cmt, _mode: euv::ConsumeMode) {}
548 }