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