]>
Commit | Line | Data |
---|---|---|
85aaf69f | 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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 | ||
85aaf69f SL |
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 | |
62682a34 | 16 | // - doesn't own a box |
85aaf69f SL |
17 | // |
18 | // - For each *immutable* static item, it checks that its **value**: | |
62682a34 | 19 | // - doesn't own a box |
85aaf69f SL |
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 | |
223e47cc | 26 | |
7453a54e | 27 | use rustc::dep_graph::DepNode; |
5bcae85e | 28 | use rustc::ty::cast::CastKind; |
54a0048b SL |
29 | use rustc_const_eval::{ConstEvalErr, lookup_const_fn_by_id, compare_lit_exprs}; |
30 | use rustc_const_eval::{eval_const_expr_partial, lookup_const_by_id}; | |
a7813a04 XL |
31 | use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll, Math}; |
32 | use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath}; | |
33 | use rustc_const_eval::ErrKind::UnresolvedPath; | |
54a0048b | 34 | use rustc_const_eval::EvalHint::ExprTypeChecked; |
a7813a04 | 35 | use rustc_const_math::{ConstMathErr, Op}; |
54a0048b SL |
36 | use rustc::hir::def::Def; |
37 | use rustc::hir::def_id::DefId; | |
7453a54e | 38 | use rustc::middle::expr_use_visitor as euv; |
7453a54e SL |
39 | use rustc::middle::mem_categorization as mc; |
40 | use rustc::middle::mem_categorization::Categorization; | |
54a0048b | 41 | use rustc::ty::{self, Ty, TyCtxt}; |
5bcae85e SL |
42 | use rustc::traits::Reveal; |
43 | use rustc::util::common::ErrorReported; | |
7453a54e SL |
44 | use rustc::util::nodemap::NodeMap; |
45 | use rustc::middle::const_qualif::ConstQualif; | |
46 | use rustc::lint::builtin::CONST_ERR; | |
47 | ||
54a0048b | 48 | use rustc::hir::{self, PatKind}; |
1a4d82fc | 49 | use syntax::ast; |
3157f602 | 50 | use syntax_pos::Span; |
54a0048b | 51 | use rustc::hir::intravisit::{self, FnKind, Visitor}; |
223e47cc | 52 | |
85aaf69f | 53 | use std::collections::hash_map::Entry; |
c1a9b12d | 54 | use std::cmp::Ordering; |
85aaf69f | 55 | |
92a42be0 | 56 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
85aaf69f SL |
57 | enum Mode { |
58 | Const, | |
62682a34 | 59 | ConstFn, |
85aaf69f SL |
60 | Static, |
61 | StaticMut, | |
62 | ||
63 | // An expression that occurs outside of any constant context | |
64 | // (i.e. `const`, `static`, array lengths, etc.). The value | |
65 | // can be variable at runtime, but will be promotable to | |
66 | // static memory if we can prove it is actually constant. | |
67 | Var, | |
68 | } | |
69 | ||
1a4d82fc | 70 | struct CheckCrateVisitor<'a, 'tcx: 'a> { |
a7813a04 | 71 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
85aaf69f SL |
72 | mode: Mode, |
73 | qualif: ConstQualif, | |
5bcae85e | 74 | rvalue_borrows: NodeMap<hir::Mutability>, |
223e47cc LB |
75 | } |
76 | ||
a7813a04 | 77 | impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { |
5bcae85e SL |
78 | fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R |
79 | where F: FnOnce(&mut CheckCrateVisitor<'a, 'gcx>) -> R | |
1a4d82fc | 80 | { |
85aaf69f SL |
81 | let (old_mode, old_qualif) = (self.mode, self.qualif); |
82 | self.mode = mode; | |
d9579d0f | 83 | self.qualif = ConstQualif::empty(); |
85aaf69f SL |
84 | let r = f(self); |
85 | self.mode = old_mode; | |
86 | self.qualif = old_qualif; | |
87 | r | |
1a4d82fc | 88 | } |
85aaf69f | 89 | |
5bcae85e SL |
90 | fn with_euv<F, R>(&mut self, item_id: Option<ast::NodeId>, f: F) -> R |
91 | where F: for<'b, 'tcx> FnOnce(&mut euv::ExprUseVisitor<'b, 'gcx, 'tcx>) -> R | |
1a4d82fc | 92 | { |
85aaf69f SL |
93 | let param_env = match item_id { |
94 | Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id), | |
5bcae85e | 95 | None => self.tcx.empty_parameter_environment(), |
85aaf69f | 96 | }; |
c1a9b12d | 97 | |
5bcae85e SL |
98 | self.tcx |
99 | .infer_ctxt(None, Some(param_env), Reveal::NotSpecializable) | |
100 | .enter(|infcx| f(&mut euv::ExprUseVisitor::new(self, &infcx))) | |
85aaf69f SL |
101 | } |
102 | ||
e9174d1e | 103 | fn global_expr(&mut self, mode: Mode, expr: &hir::Expr) -> ConstQualif { |
85aaf69f SL |
104 | assert!(mode != Mode::Var); |
105 | match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) { | |
106 | Entry::Occupied(entry) => return *entry.get(), | |
107 | Entry::Vacant(entry) => { | |
108 | // Prevent infinite recursion on re-entry. | |
d9579d0f | 109 | entry.insert(ConstQualif::empty()); |
85aaf69f SL |
110 | } |
111 | } | |
54a0048b SL |
112 | if let Err(err) = eval_const_expr_partial(self.tcx, expr, ExprTypeChecked, None) { |
113 | match err.kind { | |
5bcae85e SL |
114 | UnimplementedConstVal(_) => {} |
115 | IndexOpFeatureGated => {} | |
116 | ErroneousReferencedConstant(_) => {} | |
117 | _ => { | |
118 | self.tcx.sess.add_lint(CONST_ERR, | |
119 | expr.id, | |
120 | expr.span, | |
121 | format!("constant evaluation error: {}. This will \ | |
122 | become a HARD ERROR in the future", | |
123 | err.description().into_oneline())) | |
124 | } | |
54a0048b SL |
125 | } |
126 | } | |
85aaf69f SL |
127 | self.with_mode(mode, |this| { |
128 | this.with_euv(None, |euv| euv.consume_expr(expr)); | |
129 | this.visit_expr(expr); | |
130 | this.qualif | |
131 | }) | |
132 | } | |
133 | ||
62682a34 | 134 | fn fn_like(&mut self, |
e9174d1e SL |
135 | fk: FnKind, |
136 | fd: &hir::FnDecl, | |
137 | b: &hir::Block, | |
62682a34 SL |
138 | s: Span, |
139 | fn_id: ast::NodeId) | |
140 | -> ConstQualif { | |
141 | match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) { | |
142 | Entry::Occupied(entry) => return *entry.get(), | |
143 | Entry::Vacant(entry) => { | |
144 | // Prevent infinite recursion on re-entry. | |
145 | entry.insert(ConstQualif::empty()); | |
146 | } | |
147 | } | |
148 | ||
149 | let mode = match fk { | |
5bcae85e | 150 | FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _, _) => Mode::ConstFn, |
54a0048b | 151 | FnKind::Method(_, m, _, _) => { |
e9174d1e | 152 | if m.constness == hir::Constness::Const { |
62682a34 SL |
153 | Mode::ConstFn |
154 | } else { | |
155 | Mode::Var | |
156 | } | |
157 | } | |
5bcae85e | 158 | _ => Mode::Var, |
62682a34 SL |
159 | }; |
160 | ||
62682a34 SL |
161 | let qualif = self.with_mode(mode, |this| { |
162 | this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); | |
5bcae85e | 163 | intravisit::walk_fn(this, fk, fd, b, s, fn_id); |
62682a34 SL |
164 | this.qualif |
165 | }); | |
166 | ||
167 | // Keep only bits that aren't affected by function body (NON_ZERO_SIZED), | |
168 | // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE). | |
169 | let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); | |
170 | ||
171 | self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif); | |
172 | qualif | |
173 | } | |
174 | ||
85aaf69f SL |
175 | fn add_qualif(&mut self, qualif: ConstQualif) { |
176 | self.qualif = self.qualif | qualif; | |
177 | } | |
178 | ||
62682a34 | 179 | /// Returns true if the call is to a const fn or method. |
5bcae85e | 180 | fn handle_const_fn_call(&mut self, _expr: &hir::Expr, def_id: DefId, ret_ty: Ty<'gcx>) -> bool { |
54a0048b | 181 | if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) { |
62682a34 SL |
182 | let qualif = self.fn_like(fn_like.kind(), |
183 | fn_like.decl(), | |
184 | fn_like.body(), | |
185 | fn_like.span(), | |
186 | fn_like.id()); | |
187 | self.add_qualif(qualif); | |
188 | ||
c1a9b12d | 189 | if ret_ty.type_contents(self.tcx).interior_unsafe() { |
62682a34 SL |
190 | self.add_qualif(ConstQualif::MUTABLE_MEM); |
191 | } | |
192 | ||
193 | true | |
194 | } else { | |
195 | false | |
196 | } | |
197 | } | |
198 | ||
e9174d1e | 199 | fn record_borrow(&mut self, id: ast::NodeId, mutbl: hir::Mutability) { |
85aaf69f SL |
200 | match self.rvalue_borrows.entry(id) { |
201 | Entry::Occupied(mut entry) => { | |
202 | // Merge the two borrows, taking the most demanding | |
203 | // one, mutability-wise. | |
e9174d1e | 204 | if mutbl == hir::MutMutable { |
85aaf69f SL |
205 | entry.insert(mutbl); |
206 | } | |
207 | } | |
208 | Entry::Vacant(entry) => { | |
209 | entry.insert(mutbl); | |
210 | } | |
211 | } | |
212 | } | |
223e47cc LB |
213 | } |
214 | ||
1a4d82fc | 215 | impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { |
e9174d1e | 216 | fn visit_item(&mut self, i: &hir::Item) { |
62682a34 | 217 | debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id)); |
92a42be0 | 218 | assert_eq!(self.mode, Mode::Var); |
1a4d82fc | 219 | match i.node { |
e9174d1e | 220 | hir::ItemStatic(_, hir::MutImmutable, ref expr) => { |
7453a54e | 221 | self.global_expr(Mode::Static, &expr); |
85aaf69f | 222 | } |
e9174d1e | 223 | hir::ItemStatic(_, hir::MutMutable, ref expr) => { |
7453a54e | 224 | self.global_expr(Mode::StaticMut, &expr); |
85aaf69f | 225 | } |
e9174d1e | 226 | hir::ItemConst(_, ref expr) => { |
7453a54e | 227 | self.global_expr(Mode::Const, &expr); |
1a4d82fc | 228 | } |
e9174d1e | 229 | hir::ItemEnum(ref enum_definition, _) => { |
85aaf69f SL |
230 | for var in &enum_definition.variants { |
231 | if let Some(ref ex) = var.node.disr_expr { | |
7453a54e | 232 | self.global_expr(Mode::Const, &ex); |
1a4d82fc | 233 | } |
85aaf69f SL |
234 | } |
235 | } | |
236 | _ => { | |
92a42be0 | 237 | intravisit::walk_item(self, i); |
1a4d82fc | 238 | } |
223e47cc LB |
239 | } |
240 | } | |
85aaf69f | 241 | |
e9174d1e | 242 | fn visit_trait_item(&mut self, t: &'v hir::TraitItem) { |
d9579d0f | 243 | match t.node { |
e9174d1e | 244 | hir::ConstTraitItem(_, ref default) => { |
d9579d0f | 245 | if let Some(ref expr) = *default { |
7453a54e | 246 | self.global_expr(Mode::Const, &expr); |
d9579d0f | 247 | } else { |
92a42be0 | 248 | intravisit::walk_trait_item(self, t); |
d9579d0f AL |
249 | } |
250 | } | |
92a42be0 | 251 | _ => self.with_mode(Mode::Var, |v| intravisit::walk_trait_item(v, t)), |
d9579d0f AL |
252 | } |
253 | } | |
254 | ||
e9174d1e | 255 | fn visit_impl_item(&mut self, i: &'v hir::ImplItem) { |
d9579d0f | 256 | match i.node { |
92a42be0 | 257 | hir::ImplItemKind::Const(_, ref expr) => { |
7453a54e | 258 | self.global_expr(Mode::Const, &expr); |
d9579d0f | 259 | } |
92a42be0 | 260 | _ => self.with_mode(Mode::Var, |v| intravisit::walk_impl_item(v, i)), |
d9579d0f AL |
261 | } |
262 | } | |
263 | ||
85aaf69f | 264 | fn visit_fn(&mut self, |
e9174d1e SL |
265 | fk: FnKind<'v>, |
266 | fd: &'v hir::FnDecl, | |
267 | b: &'v hir::Block, | |
85aaf69f SL |
268 | s: Span, |
269 | fn_id: ast::NodeId) { | |
62682a34 | 270 | self.fn_like(fk, fd, b, s, fn_id); |
85aaf69f SL |
271 | } |
272 | ||
e9174d1e | 273 | fn visit_pat(&mut self, p: &hir::Pat) { |
85aaf69f | 274 | match p.node { |
7453a54e SL |
275 | PatKind::Lit(ref lit) => { |
276 | self.global_expr(Mode::Const, &lit); | |
85aaf69f | 277 | } |
7453a54e SL |
278 | PatKind::Range(ref start, ref end) => { |
279 | self.global_expr(Mode::Const, &start); | |
280 | self.global_expr(Mode::Const, &end); | |
c1a9b12d | 281 | |
5bcae85e SL |
282 | match compare_lit_exprs(self.tcx, p.span, start, end) { |
283 | Ok(Ordering::Less) | | |
284 | Ok(Ordering::Equal) => {} | |
285 | Ok(Ordering::Greater) => { | |
286 | span_err!(self.tcx.sess, | |
287 | start.span, | |
288 | E0030, | |
289 | "lower range bound must be less than or equal to upper"); | |
c1a9b12d | 290 | } |
5bcae85e | 291 | Err(ErrorReported) => {} |
c1a9b12d | 292 | } |
85aaf69f | 293 | } |
5bcae85e | 294 | _ => intravisit::walk_pat(self, p), |
85aaf69f | 295 | } |
223e47cc | 296 | } |
85aaf69f | 297 | |
e9174d1e | 298 | fn visit_block(&mut self, block: &hir::Block) { |
62682a34 SL |
299 | // Check all statements in the block |
300 | for stmt in &block.stmts { | |
9cc50fc6 | 301 | match stmt.node { |
e9174d1e | 302 | hir::StmtDecl(ref decl, _) => { |
62682a34 | 303 | match decl.node { |
5bcae85e | 304 | hir::DeclLocal(_) => {} |
62682a34 | 305 | // Item statements are allowed |
5bcae85e | 306 | hir::DeclItem(_) => continue, |
62682a34 SL |
307 | } |
308 | } | |
5bcae85e SL |
309 | hir::StmtExpr(_, _) => {} |
310 | hir::StmtSemi(_, _) => {} | |
62682a34 | 311 | } |
9cc50fc6 | 312 | self.add_qualif(ConstQualif::NOT_CONST); |
62682a34 | 313 | } |
92a42be0 | 314 | intravisit::walk_block(self, block); |
62682a34 SL |
315 | } |
316 | ||
e9174d1e | 317 | fn visit_expr(&mut self, ex: &hir::Expr) { |
85aaf69f | 318 | let mut outer = self.qualif; |
d9579d0f | 319 | self.qualif = ConstQualif::empty(); |
85aaf69f | 320 | |
c1a9b12d | 321 | let node_ty = self.tcx.node_id_to_type(ex.id); |
85aaf69f | 322 | check_expr(self, ex, node_ty); |
c1a9b12d | 323 | check_adjustments(self, ex); |
85aaf69f SL |
324 | |
325 | // Special-case some expressions to avoid certain flags bubbling up. | |
326 | match ex.node { | |
e9174d1e | 327 | hir::ExprCall(ref callee, ref args) => { |
62682a34 | 328 | for arg in args { |
7453a54e | 329 | self.visit_expr(&arg) |
85aaf69f SL |
330 | } |
331 | ||
332 | let inner = self.qualif; | |
7453a54e | 333 | self.visit_expr(&callee); |
85aaf69f SL |
334 | // The callee's size doesn't count in the call. |
335 | let added = self.qualif - inner; | |
d9579d0f | 336 | self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED); |
85aaf69f | 337 | } |
e9174d1e | 338 | hir::ExprRepeat(ref element, _) => { |
7453a54e | 339 | self.visit_expr(&element); |
85aaf69f SL |
340 | // The count is checked elsewhere (typeck). |
341 | let count = match node_ty.sty { | |
62682a34 | 342 | ty::TyArray(_, n) => n, |
5bcae85e | 343 | _ => bug!(), |
85aaf69f SL |
344 | }; |
345 | // [element; 0] is always zero-sized. | |
346 | if count == 0 { | |
d9579d0f | 347 | self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); |
85aaf69f SL |
348 | } |
349 | } | |
e9174d1e | 350 | hir::ExprMatch(ref discr, ref arms, _) => { |
85aaf69f SL |
351 | // Compute the most demanding borrow from all the arms' |
352 | // patterns and set that on the discriminator. | |
353 | let mut borrow = None; | |
62682a34 | 354 | for pat in arms.iter().flat_map(|arm| &arm.pats) { |
85aaf69f SL |
355 | let pat_borrow = self.rvalue_borrows.remove(&pat.id); |
356 | match (borrow, pat_borrow) { | |
5bcae85e SL |
357 | (None, _) | |
358 | (_, Some(hir::MutMutable)) => { | |
85aaf69f SL |
359 | borrow = pat_borrow; |
360 | } | |
361 | _ => {} | |
362 | } | |
363 | } | |
364 | if let Some(mutbl) = borrow { | |
365 | self.record_borrow(discr.id, mutbl); | |
366 | } | |
92a42be0 | 367 | intravisit::walk_expr(self, ex); |
85aaf69f | 368 | } |
5bcae85e | 369 | _ => intravisit::walk_expr(self, ex), |
85aaf69f SL |
370 | } |
371 | ||
372 | // Handle borrows on (or inside the autorefs of) this expression. | |
373 | match self.rvalue_borrows.remove(&ex.id) { | |
e9174d1e | 374 | Some(hir::MutImmutable) => { |
85aaf69f SL |
375 | // Constants cannot be borrowed if they contain interior mutability as |
376 | // it means that our "silent insertion of statics" could change | |
377 | // initializer values (very bad). | |
d9579d0f | 378 | // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has |
85aaf69f | 379 | // propagated from another error, so erroring again would be just noise. |
c1a9b12d | 380 | let tc = node_ty.type_contents(self.tcx); |
d9579d0f AL |
381 | if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() { |
382 | outer = outer | ConstQualif::NOT_CONST; | |
85aaf69f SL |
383 | } |
384 | // If the reference has to be 'static, avoid in-place initialization | |
385 | // as that will end up pointing to the stack instead. | |
d9579d0f AL |
386 | if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { |
387 | self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE; | |
388 | self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); | |
85aaf69f SL |
389 | } |
390 | } | |
e9174d1e | 391 | Some(hir::MutMutable) => { |
85aaf69f | 392 | // `&mut expr` means expr could be mutated, unless it's zero-sized. |
d9579d0f | 393 | if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) { |
85aaf69f | 394 | if self.mode == Mode::Var { |
d9579d0f AL |
395 | outer = outer | ConstQualif::NOT_CONST; |
396 | self.add_qualif(ConstQualif::MUTABLE_MEM); | |
85aaf69f SL |
397 | } |
398 | } | |
d9579d0f AL |
399 | if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { |
400 | self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); | |
85aaf69f SL |
401 | } |
402 | } | |
403 | None => {} | |
1a4d82fc | 404 | } |
a7813a04 XL |
405 | |
406 | if self.mode == Mode::Var && !self.qualif.intersects(ConstQualif::NOT_CONST) { | |
407 | match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) { | |
408 | Ok(_) => {} | |
5bcae85e SL |
409 | Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) | |
410 | Err(ConstEvalErr { kind: MiscCatchAll, .. }) | | |
411 | Err(ConstEvalErr { kind: MiscBinaryOp, .. }) | | |
412 | Err(ConstEvalErr { kind: NonConstPath, .. }) | | |
413 | Err(ConstEvalErr { kind: UnresolvedPath, .. }) | | |
414 | Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), .. }) | | |
415 | Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), .. }) | | |
416 | Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) | | |
417 | Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {} | |
a7813a04 | 418 | Err(msg) => { |
5bcae85e SL |
419 | self.tcx.sess.add_lint(CONST_ERR, |
420 | ex.id, | |
a7813a04 | 421 | msg.span, |
5bcae85e | 422 | msg.description().into_oneline().into_owned()) |
a7813a04 XL |
423 | } |
424 | } | |
425 | } | |
426 | ||
85aaf69f SL |
427 | self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif); |
428 | // Don't propagate certain flags. | |
d9579d0f | 429 | self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS); |
1a4d82fc JJ |
430 | } |
431 | } | |
432 | ||
85aaf69f SL |
433 | /// This function is used to enforce the constraints on |
434 | /// const/static items. It walks through the *value* | |
435 | /// of the item walking down the expression and evaluating | |
436 | /// every nested expression. If the expression is not part | |
437 | /// of a const/static item, it is qualified for promotion | |
438 | /// instead of producing errors. | |
5bcae85e | 439 | fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node_ty: Ty<'tcx>) { |
85aaf69f | 440 | match node_ty.sty { |
e9174d1e SL |
441 | ty::TyStruct(def, _) | |
442 | ty::TyEnum(def, _) if def.has_dtor() => { | |
d9579d0f | 443 | v.add_qualif(ConstQualif::NEEDS_DROP); |
85aaf69f SL |
444 | } |
445 | _ => {} | |
446 | } | |
223e47cc | 447 | |
85aaf69f | 448 | let method_call = ty::MethodCall::expr(e.id); |
1a4d82fc | 449 | match e.node { |
e9174d1e SL |
450 | hir::ExprUnary(..) | |
451 | hir::ExprBinary(..) | | |
452 | hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => { | |
d9579d0f | 453 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f | 454 | } |
b039eaaf | 455 | hir::ExprBox(_) => { |
d9579d0f | 456 | v.add_qualif(ConstQualif::NOT_CONST); |
1a4d82fc | 457 | } |
e9174d1e | 458 | hir::ExprUnary(op, ref inner) => { |
c1a9b12d | 459 | match v.tcx.node_id_to_type(inner.id).sty { |
62682a34 | 460 | ty::TyRawPtr(_) => { |
e9174d1e | 461 | assert!(op == hir::UnDeref); |
62682a34 | 462 | |
d9579d0f | 463 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f SL |
464 | } |
465 | _ => {} | |
223e47cc | 466 | } |
1a4d82fc | 467 | } |
e9174d1e | 468 | hir::ExprBinary(op, ref lhs, _) => { |
c1a9b12d | 469 | match v.tcx.node_id_to_type(lhs.id).sty { |
62682a34 | 470 | ty::TyRawPtr(_) => { |
e9174d1e SL |
471 | assert!(op.node == hir::BiEq || op.node == hir::BiNe || |
472 | op.node == hir::BiLe || op.node == hir::BiLt || | |
473 | op.node == hir::BiGe || op.node == hir::BiGt); | |
62682a34 SL |
474 | |
475 | v.add_qualif(ConstQualif::NOT_CONST); | |
85aaf69f | 476 | } |
62682a34 | 477 | _ => {} |
223e47cc | 478 | } |
62682a34 | 479 | } |
e9174d1e | 480 | hir::ExprCast(ref from, _) => { |
62682a34 SL |
481 | debug!("Checking const cast(id={})", from.id); |
482 | match v.tcx.cast_kinds.borrow().get(&from.id) { | |
54a0048b | 483 | None => span_bug!(e.span, "no kind for cast"), |
62682a34 SL |
484 | Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => { |
485 | v.add_qualif(ConstQualif::NOT_CONST); | |
85aaf69f | 486 | } |
62682a34 | 487 | _ => {} |
223e47cc | 488 | } |
1a4d82fc | 489 | } |
e9174d1e | 490 | hir::ExprPath(..) => { |
3157f602 XL |
491 | match v.tcx.expect_def(e.id) { |
492 | Def::Variant(..) => { | |
85aaf69f | 493 | // Count the discriminator or function pointer. |
d9579d0f | 494 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f | 495 | } |
3157f602 | 496 | Def::Struct(..) => { |
54a0048b | 497 | if let ty::TyFnDef(..) = node_ty.sty { |
85aaf69f | 498 | // Count the function pointer. |
d9579d0f | 499 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f SL |
500 | } |
501 | } | |
3157f602 | 502 | Def::Fn(..) | Def::Method(..) => { |
85aaf69f | 503 | // Count the function pointer. |
d9579d0f | 504 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f | 505 | } |
3157f602 | 506 | Def::Static(..) => { |
85aaf69f SL |
507 | match v.mode { |
508 | Mode::Static | Mode::StaticMut => {} | |
a7813a04 | 509 | Mode::Const | Mode::ConstFn => {} |
d9579d0f | 510 | Mode::Var => v.add_qualif(ConstQualif::NOT_CONST) |
85aaf69f SL |
511 | } |
512 | } | |
3157f602 | 513 | Def::Const(did) | Def::AssociatedConst(did) => { |
54a0048b SL |
514 | let substs = Some(v.tcx.node_id_item_substs(e.id).substs); |
515 | if let Some((expr, _)) = lookup_const_by_id(v.tcx, did, substs) { | |
85aaf69f SL |
516 | let inner = v.global_expr(Mode::Const, expr); |
517 | v.add_qualif(inner); | |
85aaf69f SL |
518 | } |
519 | } | |
3157f602 | 520 | Def::Local(..) if v.mode == Mode::ConstFn => { |
62682a34 SL |
521 | // Sadly, we can't determine whether the types are zero-sized. |
522 | v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED); | |
523 | } | |
a7813a04 | 524 | _ => { |
d9579d0f | 525 | v.add_qualif(ConstQualif::NOT_CONST); |
1a4d82fc | 526 | } |
223e47cc | 527 | } |
1a4d82fc | 528 | } |
e9174d1e | 529 | hir::ExprCall(ref callee, _) => { |
85aaf69f SL |
530 | let mut callee = &**callee; |
531 | loop { | |
532 | callee = match callee.node { | |
e9174d1e | 533 | hir::ExprBlock(ref block) => match block.expr { |
7453a54e | 534 | Some(ref tail) => &tail, |
85aaf69f SL |
535 | None => break |
536 | }, | |
537 | _ => break | |
538 | }; | |
539 | } | |
3157f602 XL |
540 | // The callee is an arbitrary expression, it doesn't necessarily have a definition. |
541 | let is_const = match v.tcx.expect_def_or_none(callee.id) { | |
7453a54e SL |
542 | Some(Def::Struct(..)) => true, |
543 | Some(Def::Variant(..)) => { | |
85aaf69f | 544 | // Count the discriminator. |
d9579d0f | 545 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
62682a34 | 546 | true |
85aaf69f | 547 | } |
7453a54e | 548 | Some(Def::Fn(did)) => { |
62682a34 | 549 | v.handle_const_fn_call(e, did, node_ty) |
223e47cc | 550 | } |
7453a54e | 551 | Some(Def::Method(did)) => { |
c1a9b12d SL |
552 | match v.tcx.impl_or_trait_item(did).container() { |
553 | ty::ImplContainer(_) => { | |
554 | v.handle_const_fn_call(e, did, node_ty) | |
555 | } | |
556 | ty::TraitContainer(_) => false | |
557 | } | |
558 | } | |
62682a34 SL |
559 | _ => false |
560 | }; | |
561 | if !is_const { | |
d9579d0f | 562 | v.add_qualif(ConstQualif::NOT_CONST); |
62682a34 SL |
563 | } |
564 | } | |
e9174d1e | 565 | hir::ExprMethodCall(..) => { |
c1a9b12d SL |
566 | let method = v.tcx.tables.borrow().method_map[&method_call]; |
567 | let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() { | |
568 | ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty), | |
569 | ty::TraitContainer(_) => false | |
62682a34 SL |
570 | }; |
571 | if !is_const { | |
572 | v.add_qualif(ConstQualif::NOT_CONST); | |
223e47cc LB |
573 | } |
574 | } | |
e9174d1e | 575 | hir::ExprStruct(..) => { |
3157f602 XL |
576 | // unsafe_cell_type doesn't necessarily exist with no_core |
577 | if Some(v.tcx.expect_def(e.id).def_id()) == v.tcx.lang_items.unsafe_cell_type() { | |
d9579d0f | 578 | v.add_qualif(ConstQualif::MUTABLE_MEM); |
85aaf69f SL |
579 | } |
580 | } | |
581 | ||
e9174d1e SL |
582 | hir::ExprLit(_) | |
583 | hir::ExprAddrOf(..) => { | |
d9579d0f | 584 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f SL |
585 | } |
586 | ||
e9174d1e | 587 | hir::ExprRepeat(..) => { |
d9579d0f | 588 | v.add_qualif(ConstQualif::PREFER_IN_PLACE); |
85aaf69f SL |
589 | } |
590 | ||
e9174d1e | 591 | hir::ExprClosure(..) => { |
62682a34 | 592 | // Paths in constant contexts cannot refer to local variables, |
85aaf69f | 593 | // as there are none, and thus closures can't have upvars there. |
c1a9b12d | 594 | if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) { |
85aaf69f SL |
595 | assert!(v.mode == Mode::Var, |
596 | "global closures can't capture anything"); | |
d9579d0f | 597 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f SL |
598 | } |
599 | } | |
600 | ||
e9174d1e SL |
601 | hir::ExprBlock(_) | |
602 | hir::ExprIndex(..) | | |
603 | hir::ExprField(..) | | |
604 | hir::ExprTupField(..) | | |
605 | hir::ExprVec(_) | | |
9cc50fc6 | 606 | hir::ExprType(..) | |
e9174d1e | 607 | hir::ExprTup(..) => {} |
85aaf69f SL |
608 | |
609 | // Conditional control flow (possible to implement). | |
e9174d1e SL |
610 | hir::ExprMatch(..) | |
611 | hir::ExprIf(..) | | |
85aaf69f SL |
612 | |
613 | // Loops (not very meaningful in constants). | |
e9174d1e SL |
614 | hir::ExprWhile(..) | |
615 | hir::ExprLoop(..) | | |
223e47cc | 616 | |
85aaf69f | 617 | // More control flow (also not very meaningful). |
e9174d1e SL |
618 | hir::ExprBreak(_) | |
619 | hir::ExprAgain(_) | | |
620 | hir::ExprRet(_) | | |
223e47cc | 621 | |
62682a34 | 622 | // Expressions with side-effects. |
e9174d1e SL |
623 | hir::ExprAssign(..) | |
624 | hir::ExprAssignOp(..) | | |
54a0048b | 625 | hir::ExprInlineAsm(..) => { |
d9579d0f | 626 | v.add_qualif(ConstQualif::NOT_CONST); |
223e47cc | 627 | } |
85aaf69f SL |
628 | } |
629 | } | |
630 | ||
c1a9b12d | 631 | /// Check the adjustments of an expression |
e9174d1e | 632 | fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) { |
c1a9b12d | 633 | match v.tcx.tables.borrow().adjustments.get(&e.id) { |
e9174d1e | 634 | None | |
5bcae85e | 635 | Some(&ty::adjustment::AdjustNeverToAny(..)) | |
e9174d1e | 636 | Some(&ty::adjustment::AdjustReifyFnPointer) | |
7453a54e SL |
637 | Some(&ty::adjustment::AdjustUnsafeFnPointer) | |
638 | Some(&ty::adjustment::AdjustMutToConstPointer) => {} | |
e9174d1e | 639 | |
5bcae85e SL |
640 | Some(&ty::adjustment::AdjustDerefRef(ty::adjustment::AutoDerefRef { autoderefs, .. })) => { |
641 | if (0..autoderefs as u32) | |
642 | .any(|autoderef| v.tcx.is_overloaded_autoderef(e.id, autoderef)) { | |
c1a9b12d | 643 | v.add_qualif(ConstQualif::NOT_CONST); |
c1a9b12d SL |
644 | } |
645 | } | |
646 | } | |
647 | } | |
648 | ||
a7813a04 | 649 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { |
5bcae85e SL |
650 | tcx.visit_all_items_in_krate(DepNode::CheckConst, |
651 | &mut CheckCrateVisitor { | |
652 | tcx: tcx, | |
653 | mode: Mode::Var, | |
654 | qualif: ConstQualif::NOT_CONST, | |
655 | rvalue_borrows: NodeMap(), | |
656 | }); | |
85aaf69f SL |
657 | tcx.sess.abort_if_errors(); |
658 | } | |
659 | ||
a7813a04 | 660 | impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> { |
85aaf69f SL |
661 | fn consume(&mut self, |
662 | _consume_id: ast::NodeId, | |
a7813a04 | 663 | _consume_span: Span, |
85aaf69f SL |
664 | cmt: mc::cmt, |
665 | _mode: euv::ConsumeMode) { | |
666 | let mut cur = &cmt; | |
667 | loop { | |
668 | match cur.cat { | |
92a42be0 | 669 | Categorization::StaticItem => { |
85aaf69f SL |
670 | break; |
671 | } | |
92a42be0 SL |
672 | Categorization::Deref(ref cmt, _, _) | |
673 | Categorization::Downcast(ref cmt, _) | | |
674 | Categorization::Interior(ref cmt, _) => cur = cmt, | |
85aaf69f | 675 | |
92a42be0 SL |
676 | Categorization::Rvalue(..) | |
677 | Categorization::Upvar(..) | | |
5bcae85e | 678 | Categorization::Local(..) => break, |
85aaf69f SL |
679 | } |
680 | } | |
681 | } | |
682 | fn borrow(&mut self, | |
683 | borrow_id: ast::NodeId, | |
a7813a04 | 684 | _borrow_span: Span, |
85aaf69f SL |
685 | cmt: mc::cmt<'tcx>, |
686 | _loan_region: ty::Region, | |
687 | bk: ty::BorrowKind, | |
5bcae85e | 688 | loan_cause: euv::LoanCause) { |
9346a6ac AL |
689 | // Kind of hacky, but we allow Unsafe coercions in constants. |
690 | // These occur when we convert a &T or *T to a *U, as well as | |
691 | // when making a thin pointer (e.g., `*T`) into a fat pointer | |
692 | // (e.g., `*Trait`). | |
693 | match loan_cause { | |
694 | euv::LoanCause::AutoUnsafe => { | |
695 | return; | |
696 | } | |
5bcae85e | 697 | _ => {} |
9346a6ac AL |
698 | } |
699 | ||
85aaf69f | 700 | let mut cur = &cmt; |
85aaf69f SL |
701 | loop { |
702 | match cur.cat { | |
92a42be0 | 703 | Categorization::Rvalue(..) => { |
85aaf69f SL |
704 | if loan_cause == euv::MatchDiscriminant { |
705 | // Ignore the dummy immutable borrow created by EUV. | |
706 | break; | |
707 | } | |
708 | let mutbl = bk.to_mutbl_lossy(); | |
e9174d1e | 709 | if mutbl == hir::MutMutable && self.mode == Mode::StaticMut { |
62682a34 SL |
710 | // Mutable slices are the only `&mut` allowed in |
711 | // globals, but only in `static mut`, nowhere else. | |
712 | // FIXME: This exception is really weird... there isn't | |
713 | // any fundamental reason to restrict this based on | |
714 | // type of the expression. `&mut [1]` has exactly the | |
715 | // same representation as &mut 1. | |
85aaf69f | 716 | match cmt.ty.sty { |
5bcae85e SL |
717 | ty::TyArray(_, _) | |
718 | ty::TySlice(_) => break, | |
85aaf69f SL |
719 | _ => {} |
720 | } | |
721 | } | |
722 | self.record_borrow(borrow_id, mutbl); | |
723 | break; | |
724 | } | |
92a42be0 | 725 | Categorization::StaticItem => { |
85aaf69f SL |
726 | break; |
727 | } | |
92a42be0 SL |
728 | Categorization::Deref(ref cmt, _, _) | |
729 | Categorization::Downcast(ref cmt, _) | | |
730 | Categorization::Interior(ref cmt, _) => { | |
85aaf69f SL |
731 | cur = cmt; |
732 | } | |
733 | ||
92a42be0 | 734 | Categorization::Upvar(..) | |
5bcae85e | 735 | Categorization::Local(..) => break, |
85aaf69f SL |
736 | } |
737 | } | |
223e47cc | 738 | } |
85aaf69f | 739 | |
5bcae85e | 740 | fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {} |
85aaf69f SL |
741 | fn mutate(&mut self, |
742 | _assignment_id: ast::NodeId, | |
743 | _assignment_span: Span, | |
744 | _assignee_cmt: mc::cmt, | |
5bcae85e SL |
745 | _mode: euv::MutateMode) { |
746 | } | |
85aaf69f | 747 | |
5bcae85e | 748 | fn matched_pat(&mut self, _: &hir::Pat, _: mc::cmt, _: euv::MatchMode) {} |
85aaf69f | 749 | |
5bcae85e | 750 | fn consume_pat(&mut self, _consume_pat: &hir::Pat, _cmt: mc::cmt, _mode: euv::ConsumeMode) {} |
223e47cc | 751 | } |