]>
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 | |
62682a34 | 27 | use middle::cast::{CastKind}; |
85aaf69f | 28 | use middle::const_eval; |
c1a9b12d | 29 | use middle::const_eval::EvalHint::ExprTypeChecked; |
85aaf69f SL |
30 | use middle::def; |
31 | use middle::expr_use_visitor as euv; | |
32 | use middle::infer; | |
33 | use middle::mem_categorization as mc; | |
34 | use middle::traits; | |
35 | use middle::ty::{self, Ty}; | |
36 | use util::nodemap::NodeMap; | |
223e47cc | 37 | |
1a4d82fc | 38 | use syntax::ast; |
85aaf69f | 39 | use syntax::codemap::Span; |
1a4d82fc | 40 | use syntax::visit::{self, Visitor}; |
223e47cc | 41 | |
85aaf69f | 42 | use std::collections::hash_map::Entry; |
c1a9b12d | 43 | use std::cmp::Ordering; |
85aaf69f SL |
44 | |
45 | // Const qualification, from partial to completely promotable. | |
46 | bitflags! { | |
47 | #[derive(RustcEncodable, RustcDecodable)] | |
48 | flags ConstQualif: u8 { | |
85aaf69f SL |
49 | // Inner mutability (can not be placed behind a reference) or behind |
50 | // &mut in a non-global expression. Can be copied from static memory. | |
d9579d0f | 51 | const MUTABLE_MEM = 1 << 0, |
85aaf69f SL |
52 | // Constant value with a type that implements Drop. Can be copied |
53 | // from static memory, similar to MUTABLE_MEM. | |
d9579d0f | 54 | const NEEDS_DROP = 1 << 1, |
85aaf69f SL |
55 | // Even if the value can be placed in static memory, copying it from |
56 | // there is more expensive than in-place instantiation, and/or it may | |
57 | // be too large. This applies to [T; N] and everything containing it. | |
58 | // N.B.: references need to clear this flag to not end up on the stack. | |
d9579d0f | 59 | const PREFER_IN_PLACE = 1 << 2, |
85aaf69f SL |
60 | // May use more than 0 bytes of memory, doesn't impact the constness |
61 | // directly, but is not allowed to be borrowed mutably in a constant. | |
d9579d0f | 62 | const NON_ZERO_SIZED = 1 << 3, |
85aaf69f SL |
63 | // Actually borrowed, has to always be in static memory. Does not |
64 | // propagate, and requires the expression to behave like a 'static | |
65 | // lvalue. The set of expressions with this flag is the minimum | |
66 | // that have to be promoted. | |
d9579d0f | 67 | const HAS_STATIC_BORROWS = 1 << 4, |
85aaf69f | 68 | // Invalid const for miscellaneous reasons (e.g. not implemented). |
d9579d0f | 69 | const NOT_CONST = 1 << 5, |
85aaf69f SL |
70 | |
71 | // Borrowing the expression won't produce &'static T if any of these | |
72 | // bits are set, though the value could be copied from static memory | |
73 | // if `NOT_CONST` isn't set. | |
d9579d0f AL |
74 | const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits | |
75 | ConstQualif::NEEDS_DROP.bits | | |
76 | ConstQualif::NOT_CONST.bits | |
85aaf69f SL |
77 | } |
78 | } | |
79 | ||
c34b1796 | 80 | #[derive(Copy, Clone, Eq, PartialEq)] |
85aaf69f SL |
81 | enum Mode { |
82 | Const, | |
62682a34 | 83 | ConstFn, |
85aaf69f SL |
84 | Static, |
85 | StaticMut, | |
86 | ||
87 | // An expression that occurs outside of any constant context | |
88 | // (i.e. `const`, `static`, array lengths, etc.). The value | |
89 | // can be variable at runtime, but will be promotable to | |
90 | // static memory if we can prove it is actually constant. | |
91 | Var, | |
92 | } | |
93 | ||
1a4d82fc JJ |
94 | struct CheckCrateVisitor<'a, 'tcx: 'a> { |
95 | tcx: &'a ty::ctxt<'tcx>, | |
85aaf69f SL |
96 | mode: Mode, |
97 | qualif: ConstQualif, | |
98 | rvalue_borrows: NodeMap<ast::Mutability> | |
223e47cc LB |
99 | } |
100 | ||
1a4d82fc | 101 | impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { |
85aaf69f SL |
102 | fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where |
103 | F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R, | |
1a4d82fc | 104 | { |
85aaf69f SL |
105 | let (old_mode, old_qualif) = (self.mode, self.qualif); |
106 | self.mode = mode; | |
d9579d0f | 107 | self.qualif = ConstQualif::empty(); |
85aaf69f SL |
108 | let r = f(self); |
109 | self.mode = old_mode; | |
110 | self.qualif = old_qualif; | |
111 | r | |
1a4d82fc | 112 | } |
85aaf69f SL |
113 | |
114 | fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where | |
c1a9b12d | 115 | F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'b, 'tcx>) -> R, |
1a4d82fc | 116 | { |
85aaf69f SL |
117 | let param_env = match item_id { |
118 | Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id), | |
c1a9b12d | 119 | None => self.tcx.empty_parameter_environment() |
85aaf69f | 120 | }; |
c1a9b12d SL |
121 | |
122 | let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env), false); | |
123 | ||
124 | f(&mut euv::ExprUseVisitor::new(self, &infcx)) | |
85aaf69f SL |
125 | } |
126 | ||
127 | fn global_expr(&mut self, mode: Mode, expr: &ast::Expr) -> ConstQualif { | |
128 | assert!(mode != Mode::Var); | |
129 | match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) { | |
130 | Entry::Occupied(entry) => return *entry.get(), | |
131 | Entry::Vacant(entry) => { | |
132 | // Prevent infinite recursion on re-entry. | |
d9579d0f | 133 | entry.insert(ConstQualif::empty()); |
85aaf69f SL |
134 | } |
135 | } | |
136 | self.with_mode(mode, |this| { | |
137 | this.with_euv(None, |euv| euv.consume_expr(expr)); | |
138 | this.visit_expr(expr); | |
139 | this.qualif | |
140 | }) | |
141 | } | |
142 | ||
62682a34 SL |
143 | fn fn_like(&mut self, |
144 | fk: visit::FnKind, | |
145 | fd: &ast::FnDecl, | |
146 | b: &ast::Block, | |
147 | s: Span, | |
148 | fn_id: ast::NodeId) | |
149 | -> ConstQualif { | |
150 | match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) { | |
151 | Entry::Occupied(entry) => return *entry.get(), | |
152 | Entry::Vacant(entry) => { | |
153 | // Prevent infinite recursion on re-entry. | |
154 | entry.insert(ConstQualif::empty()); | |
155 | } | |
156 | } | |
157 | ||
158 | let mode = match fk { | |
159 | visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => { | |
160 | Mode::ConstFn | |
161 | } | |
162 | visit::FkMethod(_, m, _) => { | |
163 | if m.constness == ast::Constness::Const { | |
164 | Mode::ConstFn | |
165 | } else { | |
166 | Mode::Var | |
167 | } | |
168 | } | |
169 | _ => Mode::Var | |
170 | }; | |
171 | ||
172 | // Ensure the arguments are simple, not mutable/by-ref or patterns. | |
173 | if mode == Mode::ConstFn { | |
174 | for arg in &fd.inputs { | |
175 | match arg.pat.node { | |
176 | ast::PatIdent(ast::BindByValue(ast::MutImmutable), _, None) => {} | |
177 | _ => { | |
178 | span_err!(self.tcx.sess, arg.pat.span, E0022, | |
179 | "arguments of constant functions can only \ | |
180 | be immutable by-value bindings"); | |
181 | } | |
182 | } | |
183 | } | |
184 | } | |
185 | ||
186 | let qualif = self.with_mode(mode, |this| { | |
187 | this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); | |
188 | visit::walk_fn(this, fk, fd, b, s); | |
189 | this.qualif | |
190 | }); | |
191 | ||
192 | // Keep only bits that aren't affected by function body (NON_ZERO_SIZED), | |
193 | // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE). | |
194 | let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); | |
195 | ||
196 | self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif); | |
197 | qualif | |
198 | } | |
199 | ||
85aaf69f SL |
200 | fn add_qualif(&mut self, qualif: ConstQualif) { |
201 | self.qualif = self.qualif | qualif; | |
202 | } | |
203 | ||
62682a34 SL |
204 | /// Returns true if the call is to a const fn or method. |
205 | fn handle_const_fn_call(&mut self, | |
206 | expr: &ast::Expr, | |
207 | def_id: ast::DefId, | |
208 | ret_ty: Ty<'tcx>) | |
209 | -> bool { | |
210 | if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) { | |
211 | if | |
212 | // we are in a static/const initializer | |
213 | self.mode != Mode::Var && | |
214 | ||
215 | // feature-gate is not enabled | |
216 | !self.tcx.sess.features.borrow().const_fn && | |
217 | ||
218 | // this doesn't come from a macro that has #[allow_internal_unstable] | |
219 | !self.tcx.sess.codemap().span_allows_unstable(expr.span) | |
220 | { | |
221 | self.tcx.sess.span_err( | |
222 | expr.span, | |
223 | &format!("const fns are an unstable feature")); | |
224 | fileline_help!( | |
225 | self.tcx.sess, | |
226 | expr.span, | |
227 | "in Nightly builds, add `#![feature(const_fn)]` to the crate \ | |
228 | attributes to enable"); | |
229 | } | |
230 | ||
231 | let qualif = self.fn_like(fn_like.kind(), | |
232 | fn_like.decl(), | |
233 | fn_like.body(), | |
234 | fn_like.span(), | |
235 | fn_like.id()); | |
236 | self.add_qualif(qualif); | |
237 | ||
c1a9b12d | 238 | if ret_ty.type_contents(self.tcx).interior_unsafe() { |
62682a34 SL |
239 | self.add_qualif(ConstQualif::MUTABLE_MEM); |
240 | } | |
241 | ||
242 | true | |
243 | } else { | |
244 | false | |
245 | } | |
246 | } | |
247 | ||
85aaf69f SL |
248 | fn record_borrow(&mut self, id: ast::NodeId, mutbl: ast::Mutability) { |
249 | match self.rvalue_borrows.entry(id) { | |
250 | Entry::Occupied(mut entry) => { | |
251 | // Merge the two borrows, taking the most demanding | |
252 | // one, mutability-wise. | |
253 | if mutbl == ast::MutMutable { | |
254 | entry.insert(mutbl); | |
255 | } | |
256 | } | |
257 | Entry::Vacant(entry) => { | |
258 | entry.insert(mutbl); | |
259 | } | |
260 | } | |
261 | } | |
262 | ||
263 | fn msg(&self) -> &'static str { | |
264 | match self.mode { | |
265 | Mode::Const => "constant", | |
62682a34 | 266 | Mode::ConstFn => "constant function", |
85aaf69f SL |
267 | Mode::StaticMut | Mode::Static => "static", |
268 | Mode::Var => unreachable!(), | |
269 | } | |
270 | } | |
271 | ||
272 | fn check_static_mut_type(&self, e: &ast::Expr) { | |
c1a9b12d SL |
273 | let node_ty = self.tcx.node_id_to_type(e.id); |
274 | let tcontents = node_ty.type_contents(self.tcx); | |
85aaf69f SL |
275 | |
276 | let suffix = if tcontents.has_dtor() { | |
277 | "destructors" | |
278 | } else if tcontents.owns_owned() { | |
62682a34 | 279 | "boxes" |
85aaf69f SL |
280 | } else { |
281 | return | |
282 | }; | |
283 | ||
62682a34 SL |
284 | span_err!(self.tcx.sess, e.span, E0397, |
285 | "mutable statics are not allowed to have {}", suffix); | |
85aaf69f SL |
286 | } |
287 | ||
288 | fn check_static_type(&self, e: &ast::Expr) { | |
c1a9b12d SL |
289 | let ty = self.tcx.node_id_to_type(e.id); |
290 | let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None, false); | |
85aaf69f | 291 | let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic); |
c1a9b12d | 292 | let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut(); |
85aaf69f | 293 | fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause); |
c1a9b12d | 294 | match fulfill_cx.select_all_or_error(&infcx) { |
85aaf69f SL |
295 | Ok(()) => { }, |
296 | Err(ref errors) => { | |
297 | traits::report_fulfillment_errors(&infcx, errors); | |
298 | } | |
299 | } | |
223e47cc LB |
300 | } |
301 | } | |
302 | ||
1a4d82fc JJ |
303 | impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { |
304 | fn visit_item(&mut self, i: &ast::Item) { | |
62682a34 | 305 | debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id)); |
1a4d82fc | 306 | match i.node { |
85aaf69f SL |
307 | ast::ItemStatic(_, ast::MutImmutable, ref expr) => { |
308 | self.check_static_type(&**expr); | |
309 | self.global_expr(Mode::Static, &**expr); | |
310 | } | |
311 | ast::ItemStatic(_, ast::MutMutable, ref expr) => { | |
312 | self.check_static_mut_type(&**expr); | |
313 | self.global_expr(Mode::StaticMut, &**expr); | |
314 | } | |
315 | ast::ItemConst(_, ref expr) => { | |
316 | self.global_expr(Mode::Const, &**expr); | |
1a4d82fc JJ |
317 | } |
318 | ast::ItemEnum(ref enum_definition, _) => { | |
85aaf69f SL |
319 | for var in &enum_definition.variants { |
320 | if let Some(ref ex) = var.node.disr_expr { | |
321 | self.global_expr(Mode::Const, &**ex); | |
1a4d82fc | 322 | } |
85aaf69f SL |
323 | } |
324 | } | |
325 | _ => { | |
326 | self.with_mode(Mode::Var, |v| visit::walk_item(v, i)); | |
1a4d82fc | 327 | } |
223e47cc LB |
328 | } |
329 | } | |
85aaf69f | 330 | |
d9579d0f AL |
331 | fn visit_trait_item(&mut self, t: &'v ast::TraitItem) { |
332 | match t.node { | |
333 | ast::ConstTraitItem(_, ref default) => { | |
334 | if let Some(ref expr) = *default { | |
335 | self.global_expr(Mode::Const, &*expr); | |
336 | } else { | |
337 | visit::walk_trait_item(self, t); | |
338 | } | |
339 | } | |
340 | _ => self.with_mode(Mode::Var, |v| visit::walk_trait_item(v, t)), | |
341 | } | |
342 | } | |
343 | ||
344 | fn visit_impl_item(&mut self, i: &'v ast::ImplItem) { | |
345 | match i.node { | |
346 | ast::ConstImplItem(_, ref expr) => { | |
347 | self.global_expr(Mode::Const, &*expr); | |
348 | } | |
349 | _ => self.with_mode(Mode::Var, |v| visit::walk_impl_item(v, i)), | |
350 | } | |
351 | } | |
352 | ||
85aaf69f SL |
353 | fn visit_fn(&mut self, |
354 | fk: visit::FnKind<'v>, | |
355 | fd: &'v ast::FnDecl, | |
356 | b: &'v ast::Block, | |
357 | s: Span, | |
358 | fn_id: ast::NodeId) { | |
62682a34 | 359 | self.fn_like(fk, fd, b, s, fn_id); |
85aaf69f SL |
360 | } |
361 | ||
1a4d82fc | 362 | fn visit_pat(&mut self, p: &ast::Pat) { |
85aaf69f SL |
363 | match p.node { |
364 | ast::PatLit(ref lit) => { | |
365 | self.global_expr(Mode::Const, &**lit); | |
366 | } | |
367 | ast::PatRange(ref start, ref end) => { | |
368 | self.global_expr(Mode::Const, &**start); | |
369 | self.global_expr(Mode::Const, &**end); | |
c1a9b12d SL |
370 | |
371 | match const_eval::compare_lit_exprs(self.tcx, start, end) { | |
372 | Some(Ordering::Less) | | |
373 | Some(Ordering::Equal) => {} | |
374 | Some(Ordering::Greater) => { | |
375 | span_err!(self.tcx.sess, start.span, E0030, | |
376 | "lower range bound must be less than or equal to upper"); | |
377 | } | |
378 | None => { | |
379 | self.tcx.sess.span_bug( | |
380 | start.span, "literals of different types in range pat"); | |
381 | } | |
382 | } | |
85aaf69f SL |
383 | } |
384 | _ => visit::walk_pat(self, p) | |
385 | } | |
223e47cc | 386 | } |
85aaf69f | 387 | |
62682a34 SL |
388 | fn visit_block(&mut self, block: &ast::Block) { |
389 | // Check all statements in the block | |
390 | for stmt in &block.stmts { | |
391 | let span = match stmt.node { | |
392 | ast::StmtDecl(ref decl, _) => { | |
393 | match decl.node { | |
394 | ast::DeclLocal(_) => decl.span, | |
395 | ||
396 | // Item statements are allowed | |
397 | ast::DeclItem(_) => continue | |
398 | } | |
399 | } | |
400 | ast::StmtExpr(ref expr, _) => expr.span, | |
401 | ast::StmtSemi(ref semi, _) => semi.span, | |
402 | ast::StmtMac(..) => { | |
403 | self.tcx.sess.span_bug(stmt.span, "unexpanded statement \ | |
404 | macro in const?!") | |
405 | } | |
406 | }; | |
407 | self.add_qualif(ConstQualif::NOT_CONST); | |
408 | if self.mode != Mode::Var { | |
409 | span_err!(self.tcx.sess, span, E0016, | |
410 | "blocks in {}s are limited to items and \ | |
411 | tail expressions", self.msg()); | |
412 | } | |
413 | } | |
414 | visit::walk_block(self, block); | |
415 | } | |
416 | ||
1a4d82fc | 417 | fn visit_expr(&mut self, ex: &ast::Expr) { |
85aaf69f | 418 | let mut outer = self.qualif; |
d9579d0f | 419 | self.qualif = ConstQualif::empty(); |
85aaf69f | 420 | |
c1a9b12d | 421 | let node_ty = self.tcx.node_id_to_type(ex.id); |
85aaf69f | 422 | check_expr(self, ex, node_ty); |
c1a9b12d | 423 | check_adjustments(self, ex); |
85aaf69f SL |
424 | |
425 | // Special-case some expressions to avoid certain flags bubbling up. | |
426 | match ex.node { | |
427 | ast::ExprCall(ref callee, ref args) => { | |
62682a34 | 428 | for arg in args { |
85aaf69f SL |
429 | self.visit_expr(&**arg) |
430 | } | |
431 | ||
432 | let inner = self.qualif; | |
433 | self.visit_expr(&**callee); | |
434 | // The callee's size doesn't count in the call. | |
435 | let added = self.qualif - inner; | |
d9579d0f | 436 | self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED); |
85aaf69f SL |
437 | } |
438 | ast::ExprRepeat(ref element, _) => { | |
439 | self.visit_expr(&**element); | |
440 | // The count is checked elsewhere (typeck). | |
441 | let count = match node_ty.sty { | |
62682a34 | 442 | ty::TyArray(_, n) => n, |
85aaf69f SL |
443 | _ => unreachable!() |
444 | }; | |
445 | // [element; 0] is always zero-sized. | |
446 | if count == 0 { | |
d9579d0f | 447 | self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); |
85aaf69f SL |
448 | } |
449 | } | |
450 | ast::ExprMatch(ref discr, ref arms, _) => { | |
451 | // Compute the most demanding borrow from all the arms' | |
452 | // patterns and set that on the discriminator. | |
453 | let mut borrow = None; | |
62682a34 | 454 | for pat in arms.iter().flat_map(|arm| &arm.pats) { |
85aaf69f SL |
455 | let pat_borrow = self.rvalue_borrows.remove(&pat.id); |
456 | match (borrow, pat_borrow) { | |
457 | (None, _) | (_, Some(ast::MutMutable)) => { | |
458 | borrow = pat_borrow; | |
459 | } | |
460 | _ => {} | |
461 | } | |
462 | } | |
463 | if let Some(mutbl) = borrow { | |
464 | self.record_borrow(discr.id, mutbl); | |
465 | } | |
466 | visit::walk_expr(self, ex); | |
467 | } | |
468 | // Division by zero and overflow checking. | |
469 | ast::ExprBinary(op, _, _) => { | |
470 | visit::walk_expr(self, ex); | |
471 | let div_or_rem = op.node == ast::BiDiv || op.node == ast::BiRem; | |
472 | match node_ty.sty { | |
62682a34 | 473 | ty::TyUint(_) | ty::TyInt(_) if div_or_rem => { |
d9579d0f | 474 | if !self.qualif.intersects(ConstQualif::NOT_CONST) { |
c1a9b12d SL |
475 | match const_eval::eval_const_expr_partial( |
476 | self.tcx, ex, ExprTypeChecked) { | |
85aaf69f SL |
477 | Ok(_) => {} |
478 | Err(msg) => { | |
c34b1796 AL |
479 | span_err!(self.tcx.sess, msg.span, E0020, |
480 | "{} in a constant expression", | |
481 | msg.description()) | |
85aaf69f SL |
482 | } |
483 | } | |
484 | } | |
485 | } | |
486 | _ => {} | |
487 | } | |
488 | } | |
489 | _ => visit::walk_expr(self, ex) | |
490 | } | |
491 | ||
492 | // Handle borrows on (or inside the autorefs of) this expression. | |
493 | match self.rvalue_borrows.remove(&ex.id) { | |
494 | Some(ast::MutImmutable) => { | |
495 | // Constants cannot be borrowed if they contain interior mutability as | |
496 | // it means that our "silent insertion of statics" could change | |
497 | // initializer values (very bad). | |
d9579d0f | 498 | // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has |
85aaf69f | 499 | // propagated from another error, so erroring again would be just noise. |
c1a9b12d | 500 | let tc = node_ty.type_contents(self.tcx); |
d9579d0f AL |
501 | if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() { |
502 | outer = outer | ConstQualif::NOT_CONST; | |
85aaf69f SL |
503 | if self.mode != Mode::Var { |
504 | self.tcx.sess.span_err(ex.span, | |
505 | "cannot borrow a constant which contains \ | |
506 | interior mutability, create a static instead"); | |
507 | } | |
508 | } | |
509 | // If the reference has to be 'static, avoid in-place initialization | |
510 | // as that will end up pointing to the stack instead. | |
d9579d0f AL |
511 | if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { |
512 | self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE; | |
513 | self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); | |
85aaf69f SL |
514 | } |
515 | } | |
516 | Some(ast::MutMutable) => { | |
517 | // `&mut expr` means expr could be mutated, unless it's zero-sized. | |
d9579d0f | 518 | if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) { |
85aaf69f | 519 | if self.mode == Mode::Var { |
d9579d0f AL |
520 | outer = outer | ConstQualif::NOT_CONST; |
521 | self.add_qualif(ConstQualif::MUTABLE_MEM); | |
85aaf69f SL |
522 | } else { |
523 | span_err!(self.tcx.sess, ex.span, E0017, | |
524 | "references in {}s may only refer \ | |
525 | to immutable values", self.msg()) | |
526 | } | |
527 | } | |
d9579d0f AL |
528 | if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { |
529 | self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); | |
85aaf69f SL |
530 | } |
531 | } | |
532 | None => {} | |
1a4d82fc | 533 | } |
85aaf69f SL |
534 | self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif); |
535 | // Don't propagate certain flags. | |
d9579d0f | 536 | self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS); |
1a4d82fc JJ |
537 | } |
538 | } | |
539 | ||
85aaf69f SL |
540 | /// This function is used to enforce the constraints on |
541 | /// const/static items. It walks through the *value* | |
542 | /// of the item walking down the expression and evaluating | |
543 | /// every nested expression. If the expression is not part | |
544 | /// of a const/static item, it is qualified for promotion | |
545 | /// instead of producing errors. | |
546 | fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, | |
547 | e: &ast::Expr, node_ty: Ty<'tcx>) { | |
548 | match node_ty.sty { | |
62682a34 | 549 | ty::TyStruct(did, _) | |
c1a9b12d | 550 | ty::TyEnum(did, _) if v.tcx.has_dtor(did) => { |
d9579d0f | 551 | v.add_qualif(ConstQualif::NEEDS_DROP); |
85aaf69f SL |
552 | if v.mode != Mode::Var { |
553 | v.tcx.sess.span_err(e.span, | |
554 | &format!("{}s are not allowed to have destructors", | |
c34b1796 | 555 | v.msg())); |
85aaf69f SL |
556 | } |
557 | } | |
558 | _ => {} | |
559 | } | |
223e47cc | 560 | |
85aaf69f | 561 | let method_call = ty::MethodCall::expr(e.id); |
1a4d82fc | 562 | match e.node { |
85aaf69f SL |
563 | ast::ExprUnary(..) | |
564 | ast::ExprBinary(..) | | |
c1a9b12d | 565 | ast::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => { |
d9579d0f | 566 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f SL |
567 | if v.mode != Mode::Var { |
568 | span_err!(v.tcx.sess, e.span, E0011, | |
569 | "user-defined operators are not allowed in {}s", v.msg()); | |
570 | } | |
571 | } | |
572 | ast::ExprBox(..) | | |
1a4d82fc | 573 | ast::ExprUnary(ast::UnUniq, _) => { |
d9579d0f | 574 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f SL |
575 | if v.mode != Mode::Var { |
576 | span_err!(v.tcx.sess, e.span, E0010, | |
577 | "allocations are not allowed in {}s", v.msg()); | |
578 | } | |
1a4d82fc | 579 | } |
62682a34 | 580 | ast::ExprUnary(op, ref inner) => { |
c1a9b12d | 581 | match v.tcx.node_id_to_type(inner.id).sty { |
62682a34 SL |
582 | ty::TyRawPtr(_) => { |
583 | assert!(op == ast::UnDeref); | |
584 | ||
d9579d0f | 585 | v.add_qualif(ConstQualif::NOT_CONST); |
62682a34 SL |
586 | if v.mode != Mode::Var { |
587 | span_err!(v.tcx.sess, e.span, E0396, | |
588 | "raw pointers cannot be dereferenced in {}s", v.msg()); | |
589 | } | |
85aaf69f SL |
590 | } |
591 | _ => {} | |
223e47cc | 592 | } |
1a4d82fc | 593 | } |
62682a34 | 594 | ast::ExprBinary(op, ref lhs, _) => { |
c1a9b12d | 595 | match v.tcx.node_id_to_type(lhs.id).sty { |
62682a34 SL |
596 | ty::TyRawPtr(_) => { |
597 | assert!(op.node == ast::BiEq || op.node == ast::BiNe || | |
598 | op.node == ast::BiLe || op.node == ast::BiLt || | |
599 | op.node == ast::BiGe || op.node == ast::BiGt); | |
600 | ||
601 | v.add_qualif(ConstQualif::NOT_CONST); | |
602 | if v.mode != Mode::Var { | |
603 | span_err!(v.tcx.sess, e.span, E0395, | |
604 | "raw pointers cannot be compared in {}s", v.msg()); | |
605 | } | |
85aaf69f | 606 | } |
62682a34 | 607 | _ => {} |
223e47cc | 608 | } |
62682a34 SL |
609 | } |
610 | ast::ExprCast(ref from, _) => { | |
611 | debug!("Checking const cast(id={})", from.id); | |
612 | match v.tcx.cast_kinds.borrow().get(&from.id) { | |
613 | None => v.tcx.sess.span_bug(e.span, "no kind for cast"), | |
614 | Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => { | |
615 | v.add_qualif(ConstQualif::NOT_CONST); | |
616 | if v.mode != Mode::Var { | |
617 | span_err!(v.tcx.sess, e.span, E0018, | |
618 | "raw pointers cannot be cast to integers in {}s", v.msg()); | |
619 | } | |
85aaf69f | 620 | } |
62682a34 | 621 | _ => {} |
223e47cc | 622 | } |
1a4d82fc | 623 | } |
c34b1796 AL |
624 | ast::ExprPath(..) => { |
625 | let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def()); | |
85aaf69f SL |
626 | match def { |
627 | Some(def::DefVariant(_, _, _)) => { | |
628 | // Count the discriminator or function pointer. | |
d9579d0f | 629 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f SL |
630 | } |
631 | Some(def::DefStruct(_)) => { | |
62682a34 | 632 | if let ty::TyBareFn(..) = node_ty.sty { |
85aaf69f | 633 | // Count the function pointer. |
d9579d0f | 634 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f SL |
635 | } |
636 | } | |
c34b1796 | 637 | Some(def::DefFn(..)) | Some(def::DefMethod(..)) => { |
85aaf69f | 638 | // Count the function pointer. |
d9579d0f | 639 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f SL |
640 | } |
641 | Some(def::DefStatic(..)) => { | |
642 | match v.mode { | |
643 | Mode::Static | Mode::StaticMut => {} | |
62682a34 | 644 | Mode::Const | Mode::ConstFn => { |
85aaf69f | 645 | span_err!(v.tcx.sess, e.span, E0013, |
62682a34 SL |
646 | "{}s cannot refer to other statics, insert \ |
647 | an intermediate constant instead", v.msg()); | |
85aaf69f | 648 | } |
d9579d0f | 649 | Mode::Var => v.add_qualif(ConstQualif::NOT_CONST) |
85aaf69f SL |
650 | } |
651 | } | |
d9579d0f | 652 | Some(def::DefConst(did)) | |
c1a9b12d | 653 | Some(def::DefAssociatedConst(did)) => { |
d9579d0f AL |
654 | if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did, |
655 | Some(e.id)) { | |
85aaf69f SL |
656 | let inner = v.global_expr(Mode::Const, expr); |
657 | v.add_qualif(inner); | |
658 | } else { | |
d9579d0f AL |
659 | v.tcx.sess.span_bug(e.span, |
660 | "DefConst or DefAssociatedConst \ | |
661 | doesn't point to a constant"); | |
85aaf69f SL |
662 | } |
663 | } | |
62682a34 SL |
664 | Some(def::DefLocal(_)) if v.mode == Mode::ConstFn => { |
665 | // Sadly, we can't determine whether the types are zero-sized. | |
666 | v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED); | |
667 | } | |
1a4d82fc | 668 | def => { |
d9579d0f | 669 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f SL |
670 | if v.mode != Mode::Var { |
671 | debug!("(checking const) found bad def: {:?}", def); | |
672 | span_err!(v.tcx.sess, e.span, E0014, | |
673 | "paths in {}s may only refer to constants \ | |
674 | or functions", v.msg()); | |
675 | } | |
1a4d82fc | 676 | } |
223e47cc | 677 | } |
1a4d82fc JJ |
678 | } |
679 | ast::ExprCall(ref callee, _) => { | |
85aaf69f SL |
680 | let mut callee = &**callee; |
681 | loop { | |
682 | callee = match callee.node { | |
683 | ast::ExprParen(ref inner) => &**inner, | |
684 | ast::ExprBlock(ref block) => match block.expr { | |
685 | Some(ref tail) => &**tail, | |
686 | None => break | |
687 | }, | |
688 | _ => break | |
689 | }; | |
690 | } | |
c34b1796 | 691 | let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def()); |
62682a34 SL |
692 | let is_const = match def { |
693 | Some(def::DefStruct(..)) => true, | |
85aaf69f SL |
694 | Some(def::DefVariant(..)) => { |
695 | // Count the discriminator. | |
d9579d0f | 696 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
62682a34 | 697 | true |
85aaf69f | 698 | } |
62682a34 SL |
699 | Some(def::DefFn(did, _)) => { |
700 | v.handle_const_fn_call(e, did, node_ty) | |
223e47cc | 701 | } |
c1a9b12d SL |
702 | Some(def::DefMethod(did)) => { |
703 | match v.tcx.impl_or_trait_item(did).container() { | |
704 | ty::ImplContainer(_) => { | |
705 | v.handle_const_fn_call(e, did, node_ty) | |
706 | } | |
707 | ty::TraitContainer(_) => false | |
708 | } | |
709 | } | |
62682a34 SL |
710 | _ => false |
711 | }; | |
712 | if !is_const { | |
d9579d0f | 713 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f | 714 | if v.mode != Mode::Var { |
62682a34 SL |
715 | span_err!(v.tcx.sess, e.span, E0015, |
716 | "function calls in {}s are limited to \ | |
717 | constant functions, \ | |
718 | struct and enum constructors", v.msg()); | |
85aaf69f | 719 | } |
62682a34 SL |
720 | } |
721 | } | |
722 | ast::ExprMethodCall(..) => { | |
c1a9b12d SL |
723 | let method = v.tcx.tables.borrow().method_map[&method_call]; |
724 | let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() { | |
725 | ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty), | |
726 | ty::TraitContainer(_) => false | |
62682a34 SL |
727 | }; |
728 | if !is_const { | |
729 | v.add_qualif(ConstQualif::NOT_CONST); | |
730 | if v.mode != Mode::Var { | |
731 | span_err!(v.tcx.sess, e.span, E0378, | |
732 | "method calls in {}s are limited to \ | |
733 | constant inherent methods", v.msg()); | |
1a4d82fc | 734 | } |
223e47cc LB |
735 | } |
736 | } | |
85aaf69f SL |
737 | ast::ExprStruct(..) => { |
738 | let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id()); | |
739 | if did == v.tcx.lang_items.unsafe_cell_type() { | |
d9579d0f | 740 | v.add_qualif(ConstQualif::MUTABLE_MEM); |
85aaf69f SL |
741 | } |
742 | } | |
743 | ||
744 | ast::ExprLit(_) | | |
745 | ast::ExprAddrOf(..) => { | |
d9579d0f | 746 | v.add_qualif(ConstQualif::NON_ZERO_SIZED); |
85aaf69f SL |
747 | } |
748 | ||
749 | ast::ExprRepeat(..) => { | |
d9579d0f | 750 | v.add_qualif(ConstQualif::PREFER_IN_PLACE); |
85aaf69f SL |
751 | } |
752 | ||
753 | ast::ExprClosure(..) => { | |
62682a34 | 754 | // Paths in constant contexts cannot refer to local variables, |
85aaf69f | 755 | // as there are none, and thus closures can't have upvars there. |
c1a9b12d | 756 | if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) { |
85aaf69f SL |
757 | assert!(v.mode == Mode::Var, |
758 | "global closures can't capture anything"); | |
d9579d0f | 759 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f SL |
760 | } |
761 | } | |
762 | ||
62682a34 | 763 | ast::ExprBlock(_) | |
85aaf69f | 764 | ast::ExprIndex(..) | |
1a4d82fc JJ |
765 | ast::ExprField(..) | |
766 | ast::ExprTupField(..) | | |
85aaf69f SL |
767 | ast::ExprVec(_) | |
768 | ast::ExprParen(..) | | |
769 | ast::ExprTup(..) => {} | |
770 | ||
771 | // Conditional control flow (possible to implement). | |
772 | ast::ExprMatch(..) | | |
773 | ast::ExprIf(..) | | |
774 | ast::ExprIfLet(..) | | |
775 | ||
776 | // Loops (not very meaningful in constants). | |
777 | ast::ExprWhile(..) | | |
778 | ast::ExprWhileLet(..) | | |
779 | ast::ExprForLoop(..) | | |
780 | ast::ExprLoop(..) | | |
223e47cc | 781 | |
85aaf69f SL |
782 | // More control flow (also not very meaningful). |
783 | ast::ExprBreak(_) | | |
784 | ast::ExprAgain(_) | | |
785 | ast::ExprRet(_) | | |
223e47cc | 786 | |
85aaf69f SL |
787 | // Miscellaneous expressions that could be implemented. |
788 | ast::ExprRange(..) | | |
789 | ||
62682a34 | 790 | // Expressions with side-effects. |
85aaf69f SL |
791 | ast::ExprAssign(..) | |
792 | ast::ExprAssignOp(..) | | |
793 | ast::ExprInlineAsm(_) | | |
794 | ast::ExprMac(_) => { | |
d9579d0f | 795 | v.add_qualif(ConstQualif::NOT_CONST); |
85aaf69f SL |
796 | if v.mode != Mode::Var { |
797 | span_err!(v.tcx.sess, e.span, E0019, | |
798 | "{} contains unimplemented expression type", v.msg()); | |
1a4d82fc | 799 | } |
223e47cc | 800 | } |
85aaf69f SL |
801 | } |
802 | } | |
803 | ||
c1a9b12d SL |
804 | /// Check the adjustments of an expression |
805 | fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &ast::Expr) { | |
806 | match v.tcx.tables.borrow().adjustments.get(&e.id) { | |
807 | None | Some(&ty::AdjustReifyFnPointer) | Some(&ty::AdjustUnsafeFnPointer) => {} | |
808 | Some(&ty::AdjustDerefRef(ty::AutoDerefRef { autoderefs, .. })) => { | |
809 | if (0..autoderefs as u32).any(|autoderef| { | |
810 | v.tcx.is_overloaded_autoderef(e.id, autoderef) | |
811 | }) { | |
812 | v.add_qualif(ConstQualif::NOT_CONST); | |
813 | if v.mode != Mode::Var { | |
814 | span_err!(v.tcx.sess, e.span, E0400, | |
815 | "user-defined dereference operators are not allowed in {}s", | |
816 | v.msg()); | |
817 | } | |
818 | } | |
819 | } | |
820 | } | |
821 | } | |
822 | ||
85aaf69f SL |
823 | pub fn check_crate(tcx: &ty::ctxt) { |
824 | visit::walk_crate(&mut CheckCrateVisitor { | |
825 | tcx: tcx, | |
826 | mode: Mode::Var, | |
d9579d0f | 827 | qualif: ConstQualif::NOT_CONST, |
85aaf69f SL |
828 | rvalue_borrows: NodeMap() |
829 | }, tcx.map.krate()); | |
223e47cc | 830 | |
85aaf69f SL |
831 | tcx.sess.abort_if_errors(); |
832 | } | |
833 | ||
834 | impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> { | |
835 | fn consume(&mut self, | |
836 | _consume_id: ast::NodeId, | |
837 | consume_span: Span, | |
838 | cmt: mc::cmt, | |
839 | _mode: euv::ConsumeMode) { | |
840 | let mut cur = &cmt; | |
841 | loop { | |
842 | match cur.cat { | |
843 | mc::cat_static_item => { | |
844 | if self.mode != Mode::Var { | |
845 | // statics cannot be consumed by value at any time, that would imply | |
846 | // that they're an initializer (what a const is for) or kept in sync | |
847 | // over time (not feasible), so deny it outright. | |
62682a34 SL |
848 | span_err!(self.tcx.sess, consume_span, E0394, |
849 | "cannot refer to other statics by value, use the \ | |
850 | address-of operator or a constant instead"); | |
85aaf69f SL |
851 | } |
852 | break; | |
853 | } | |
854 | mc::cat_deref(ref cmt, _, _) | | |
855 | mc::cat_downcast(ref cmt, _) | | |
856 | mc::cat_interior(ref cmt, _) => cur = cmt, | |
857 | ||
858 | mc::cat_rvalue(..) | | |
859 | mc::cat_upvar(..) | | |
860 | mc::cat_local(..) => break | |
861 | } | |
862 | } | |
863 | } | |
864 | fn borrow(&mut self, | |
865 | borrow_id: ast::NodeId, | |
866 | borrow_span: Span, | |
867 | cmt: mc::cmt<'tcx>, | |
868 | _loan_region: ty::Region, | |
869 | bk: ty::BorrowKind, | |
9346a6ac AL |
870 | loan_cause: euv::LoanCause) |
871 | { | |
872 | // Kind of hacky, but we allow Unsafe coercions in constants. | |
873 | // These occur when we convert a &T or *T to a *U, as well as | |
874 | // when making a thin pointer (e.g., `*T`) into a fat pointer | |
875 | // (e.g., `*Trait`). | |
876 | match loan_cause { | |
877 | euv::LoanCause::AutoUnsafe => { | |
878 | return; | |
879 | } | |
880 | _ => { } | |
881 | } | |
882 | ||
85aaf69f SL |
883 | let mut cur = &cmt; |
884 | let mut is_interior = false; | |
885 | loop { | |
886 | match cur.cat { | |
887 | mc::cat_rvalue(..) => { | |
888 | if loan_cause == euv::MatchDiscriminant { | |
889 | // Ignore the dummy immutable borrow created by EUV. | |
890 | break; | |
891 | } | |
892 | let mutbl = bk.to_mutbl_lossy(); | |
893 | if mutbl == ast::MutMutable && self.mode == Mode::StaticMut { | |
62682a34 SL |
894 | // Mutable slices are the only `&mut` allowed in |
895 | // globals, but only in `static mut`, nowhere else. | |
896 | // FIXME: This exception is really weird... there isn't | |
897 | // any fundamental reason to restrict this based on | |
898 | // type of the expression. `&mut [1]` has exactly the | |
899 | // same representation as &mut 1. | |
85aaf69f | 900 | match cmt.ty.sty { |
62682a34 | 901 | ty::TyArray(_, _) | ty::TySlice(_) => break, |
85aaf69f SL |
902 | _ => {} |
903 | } | |
904 | } | |
905 | self.record_borrow(borrow_id, mutbl); | |
906 | break; | |
907 | } | |
908 | mc::cat_static_item => { | |
909 | if is_interior && self.mode != Mode::Var { | |
910 | // Borrowed statics can specifically *only* have their address taken, | |
911 | // not any number of other borrows such as borrowing fields, reading | |
912 | // elements of an array, etc. | |
913 | self.tcx.sess.span_err(borrow_span, | |
914 | "cannot refer to the interior of another \ | |
915 | static, use a constant instead"); | |
916 | } | |
917 | break; | |
918 | } | |
919 | mc::cat_deref(ref cmt, _, _) | | |
920 | mc::cat_downcast(ref cmt, _) | | |
921 | mc::cat_interior(ref cmt, _) => { | |
922 | is_interior = true; | |
923 | cur = cmt; | |
924 | } | |
925 | ||
926 | mc::cat_upvar(..) | | |
927 | mc::cat_local(..) => break | |
928 | } | |
929 | } | |
223e47cc | 930 | } |
85aaf69f SL |
931 | |
932 | fn decl_without_init(&mut self, | |
933 | _id: ast::NodeId, | |
934 | _span: Span) {} | |
935 | fn mutate(&mut self, | |
936 | _assignment_id: ast::NodeId, | |
937 | _assignment_span: Span, | |
938 | _assignee_cmt: mc::cmt, | |
939 | _mode: euv::MutateMode) {} | |
940 | ||
941 | fn matched_pat(&mut self, | |
942 | _: &ast::Pat, | |
943 | _: mc::cmt, | |
944 | _: euv::MatchMode) {} | |
945 | ||
946 | fn consume_pat(&mut self, | |
947 | _consume_pat: &ast::Pat, | |
948 | _cmt: mc::cmt, | |
949 | _mode: euv::ConsumeMode) {} | |
223e47cc | 950 | } |