]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | // Copyright 2016 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! A pass that qualifies constness of temporaries in constants, | |
12 | //! static initializers and functions and also drives promotion. | |
13 | //! | |
14 | //! The Qualif flags below can be used to also provide better | |
15 | //! diagnostics as to why a constant rvalue wasn't promoted. | |
16 | ||
17 | use rustc_data_structures::bitvec::BitVector; | |
ea8adc8c | 18 | use rustc_data_structures::indexed_set::IdxSetBuf; |
3157f602 | 19 | use rustc_data_structures::indexed_vec::{IndexVec, Idx}; |
2c00a5a8 | 20 | use rustc_data_structures::fx::FxHashSet; |
a7813a04 XL |
21 | use rustc::hir; |
22 | use rustc::hir::def_id::DefId; | |
ea8adc8c | 23 | use rustc::middle::const_val::ConstVal; |
0531ce1d | 24 | use rustc::traits::{self, TraitEngine}; |
8bb4bdeb | 25 | use rustc::ty::{self, TyCtxt, Ty, TypeFoldable}; |
a7813a04 | 26 | use rustc::ty::cast::CastTy; |
8bb4bdeb | 27 | use rustc::ty::maps::Providers; |
c30ab7b3 SL |
28 | use rustc::mir::*; |
29 | use rustc::mir::traversal::ReversePostorder; | |
ff7c6d11 | 30 | use rustc::mir::visit::{PlaceContext, Visitor}; |
476ff2be | 31 | use rustc::middle::lang_items; |
83c7162d | 32 | use rustc_target::spec::abi::Abi; |
ea8adc8c | 33 | use syntax::attr; |
2c00a5a8 | 34 | use syntax::ast::LitKind; |
a7813a04 | 35 | use syntax::feature_gate::UnstableFeatures; |
8bb4bdeb | 36 | use syntax_pos::{Span, DUMMY_SP}; |
a7813a04 | 37 | |
a7813a04 | 38 | use std::fmt; |
0531ce1d | 39 | use rustc_data_structures::sync::Lrc; |
9e0c209e | 40 | use std::usize; |
a7813a04 | 41 | |
abe05a73 | 42 | use transform::{MirPass, MirSource}; |
a7813a04 XL |
43 | use super::promote_consts::{self, Candidate, TempState}; |
44 | ||
45 | bitflags! { | |
ff7c6d11 XL |
46 | // Borrows of temporaries can be promoted only if |
47 | // they have none of these qualifications, with | |
48 | // the exception of `STATIC_REF` (in statics only). | |
ea8adc8c | 49 | struct Qualif: u8 { |
a7813a04 | 50 | // Constant containing interior mutability (UnsafeCell). |
ea8adc8c | 51 | const MUTABLE_INTERIOR = 1 << 0; |
a7813a04 XL |
52 | |
53 | // Constant containing an ADT that implements Drop. | |
ea8adc8c | 54 | const NEEDS_DROP = 1 << 1; |
a7813a04 XL |
55 | |
56 | // Function argument. | |
ea8adc8c | 57 | const FN_ARGUMENT = 1 << 2; |
a7813a04 | 58 | |
ff7c6d11 | 59 | // Static place or move from a static. |
ea8adc8c | 60 | const STATIC = 1 << 3; |
a7813a04 XL |
61 | |
62 | // Reference to a static. | |
ea8adc8c | 63 | const STATIC_REF = 1 << 4; |
a7813a04 XL |
64 | |
65 | // Not constant at all - non-`const fn` calls, asm!, | |
66 | // pointer comparisons, ptr-to-int casts, etc. | |
ea8adc8c | 67 | const NOT_CONST = 1 << 5; |
a7813a04 XL |
68 | |
69 | // Refers to temporaries which cannot be promoted as | |
70 | // promote_consts decided they weren't simple enough. | |
ea8adc8c | 71 | const NOT_PROMOTABLE = 1 << 6; |
a7813a04 | 72 | |
a7813a04 XL |
73 | // Const items can only have MUTABLE_INTERIOR |
74 | // and NOT_PROMOTABLE without producing an error. | |
75 | const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits & | |
ea8adc8c | 76 | !Qualif::NOT_PROMOTABLE.bits; |
a7813a04 XL |
77 | } |
78 | } | |
79 | ||
80 | impl<'a, 'tcx> Qualif { | |
81 | /// Remove flags which are impossible for the given type. | |
82 | fn restrict(&mut self, ty: Ty<'tcx>, | |
83 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
7cac9316 | 84 | param_env: ty::ParamEnv<'tcx>) { |
cc61c64b | 85 | if ty.is_freeze(tcx, param_env, DUMMY_SP) { |
a7813a04 XL |
86 | *self = *self - Qualif::MUTABLE_INTERIOR; |
87 | } | |
cc61c64b | 88 | if !ty.needs_drop(tcx, param_env) { |
a7813a04 XL |
89 | *self = *self - Qualif::NEEDS_DROP; |
90 | } | |
91 | } | |
92 | } | |
93 | ||
94 | /// What kind of item we are in. | |
95 | #[derive(Copy, Clone, PartialEq, Eq)] | |
96 | enum Mode { | |
97 | Const, | |
98 | Static, | |
99 | StaticMut, | |
100 | ConstFn, | |
101 | Fn | |
102 | } | |
103 | ||
104 | impl fmt::Display for Mode { | |
105 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
106 | match *self { | |
107 | Mode::Const => write!(f, "constant"), | |
108 | Mode::Static | Mode::StaticMut => write!(f, "static"), | |
109 | Mode::ConstFn => write!(f, "constant function"), | |
110 | Mode::Fn => write!(f, "function") | |
111 | } | |
112 | } | |
113 | } | |
114 | ||
a7813a04 XL |
115 | struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { |
116 | mode: Mode, | |
117 | span: Span, | |
118 | def_id: DefId, | |
119 | mir: &'a Mir<'tcx>, | |
120 | rpo: ReversePostorder<'a, 'tcx>, | |
121 | tcx: TyCtxt<'a, 'gcx, 'tcx>, | |
7cac9316 | 122 | param_env: ty::ParamEnv<'tcx>, |
c30ab7b3 | 123 | temp_qualif: IndexVec<Local, Option<Qualif>>, |
a7813a04 XL |
124 | return_qualif: Option<Qualif>, |
125 | qualif: Qualif, | |
126 | const_fn_arg_vars: BitVector, | |
c30ab7b3 | 127 | temp_promotion_state: IndexVec<Local, TempState>, |
a7813a04 XL |
128 | promotion_candidates: Vec<Candidate> |
129 | } | |
130 | ||
131 | impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { | |
132 | fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
a7813a04 XL |
133 | def_id: DefId, |
134 | mir: &'a Mir<'tcx>, | |
135 | mode: Mode) | |
136 | -> Qualifier<'a, 'tcx, 'tcx> { | |
137 | let mut rpo = traversal::reverse_postorder(mir); | |
138 | let temps = promote_consts::collect_temps(mir, &mut rpo); | |
139 | rpo.reset(); | |
2c00a5a8 XL |
140 | |
141 | let param_env = tcx.param_env(def_id); | |
142 | ||
143 | let mut temp_qualif = IndexVec::from_elem(None, &mir.local_decls); | |
144 | for arg in mir.args_iter() { | |
145 | let mut qualif = Qualif::NEEDS_DROP; | |
146 | qualif.restrict(mir.local_decls[arg].ty, tcx, param_env); | |
147 | temp_qualif[arg] = Some(qualif); | |
148 | } | |
149 | ||
a7813a04 | 150 | Qualifier { |
3b2f2976 | 151 | mode, |
a7813a04 | 152 | span: mir.span, |
3b2f2976 XL |
153 | def_id, |
154 | mir, | |
155 | rpo, | |
156 | tcx, | |
2c00a5a8 XL |
157 | param_env, |
158 | temp_qualif, | |
a7813a04 XL |
159 | return_qualif: None, |
160 | qualif: Qualif::empty(), | |
c30ab7b3 | 161 | const_fn_arg_vars: BitVector::new(mir.local_decls.len()), |
a7813a04 XL |
162 | temp_promotion_state: temps, |
163 | promotion_candidates: vec![] | |
164 | } | |
165 | } | |
166 | ||
167 | // FIXME(eddyb) we could split the errors into meaningful | |
168 | // categories, but enabling full miri would make that | |
169 | // slightly pointless (even with feature-gating). | |
170 | fn not_const(&mut self) { | |
171 | self.add(Qualif::NOT_CONST); | |
172 | if self.mode != Mode::Fn { | |
2c00a5a8 XL |
173 | let mut err = struct_span_err!( |
174 | self.tcx.sess, | |
175 | self.span, | |
176 | E0019, | |
177 | "{} contains unimplemented expression type", | |
178 | self.mode | |
179 | ); | |
180 | if self.tcx.sess.teach(&err.get_code().unwrap()) { | |
181 | err.note("A function call isn't allowed in the const's initialization expression \ | |
182 | because the expression's value must be known at compile-time."); | |
183 | err.note("Remember: you can't use a function call inside a const's initialization \ | |
184 | expression! However, you can use it anywhere else."); | |
185 | } | |
186 | err.emit(); | |
a7813a04 XL |
187 | } |
188 | } | |
189 | ||
190 | /// Error about extra statements in a constant. | |
191 | fn statement_like(&mut self) { | |
192 | self.add(Qualif::NOT_CONST); | |
193 | if self.mode != Mode::Fn { | |
2c00a5a8 XL |
194 | let mut err = struct_span_err!( |
195 | self.tcx.sess, | |
196 | self.span, | |
197 | E0016, | |
198 | "blocks in {}s are limited to items and tail expressions", | |
199 | self.mode | |
200 | ); | |
201 | if self.tcx.sess.teach(&err.get_code().unwrap()) { | |
202 | err.note("Blocks in constants may only contain items (such as constant, function \ | |
203 | definition, etc...) and a tail expression."); | |
204 | err.help("To avoid it, you have to replace the non-item object."); | |
205 | } | |
206 | err.emit(); | |
a7813a04 XL |
207 | } |
208 | } | |
209 | ||
210 | /// Add the given qualification to self.qualif. | |
211 | fn add(&mut self, qualif: Qualif) { | |
212 | self.qualif = self.qualif | qualif; | |
213 | } | |
214 | ||
215 | /// Add the given type's qualification to self.qualif. | |
216 | fn add_type(&mut self, ty: Ty<'tcx>) { | |
217 | self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP); | |
7cac9316 | 218 | self.qualif.restrict(ty, self.tcx, self.param_env); |
a7813a04 XL |
219 | } |
220 | ||
221 | /// Within the provided closure, self.qualif will start | |
222 | /// out empty, and its value after the closure returns will | |
223 | /// be combined with the value before the call to nest. | |
224 | fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) { | |
225 | let original = self.qualif; | |
226 | self.qualif = Qualif::empty(); | |
227 | f(self); | |
228 | self.add(original); | |
229 | } | |
230 | ||
ff7c6d11 XL |
231 | /// Check if a Local with the current qualifications is promotable. |
232 | fn can_promote(&mut self) -> bool { | |
233 | // References to statics are allowed, but only in other statics. | |
234 | if self.mode == Mode::Static || self.mode == Mode::StaticMut { | |
235 | (self.qualif - Qualif::STATIC_REF).is_empty() | |
236 | } else { | |
237 | self.qualif.is_empty() | |
238 | } | |
239 | } | |
240 | ||
241 | /// Check if a Place with the current qualifications could | |
a7813a04 XL |
242 | /// be consumed, by either an operand or a Deref projection. |
243 | fn try_consume(&mut self) -> bool { | |
244 | if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn { | |
245 | let msg = if self.mode == Mode::Static || | |
246 | self.mode == Mode::StaticMut { | |
247 | "cannot refer to other statics by value, use the \ | |
248 | address-of operator or a constant instead" | |
249 | } else { | |
250 | "cannot refer to statics by value, use a constant instead" | |
251 | }; | |
9e0c209e | 252 | struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg) |
7cac9316 XL |
253 | .span_label(self.span, "referring to another static by value") |
254 | .note("use the address-of operator or a constant instead") | |
9e0c209e | 255 | .emit(); |
a7813a04 XL |
256 | |
257 | // Replace STATIC with NOT_CONST to avoid further errors. | |
258 | self.qualif = self.qualif - Qualif::STATIC; | |
259 | self.add(Qualif::NOT_CONST); | |
260 | ||
261 | false | |
262 | } else { | |
263 | true | |
264 | } | |
265 | } | |
266 | ||
267 | /// Assign the current qualification to the given destination. | |
ff7c6d11 | 268 | fn assign(&mut self, dest: &Place<'tcx>, location: Location) { |
a7813a04 XL |
269 | let qualif = self.qualif; |
270 | let span = self.span; | |
271 | let store = |slot: &mut Option<Qualif>| { | |
272 | if slot.is_some() { | |
273 | span_bug!(span, "multiple assignments to {:?}", dest); | |
274 | } | |
275 | *slot = Some(qualif); | |
276 | }; | |
277 | ||
278 | // Only handle promotable temps in non-const functions. | |
279 | if self.mode == Mode::Fn { | |
ff7c6d11 | 280 | if let Place::Local(index) = *dest { |
c30ab7b3 SL |
281 | if self.mir.local_kind(index) == LocalKind::Temp |
282 | && self.temp_promotion_state[index].is_promotable() { | |
283 | debug!("store to promotable temp {:?}", index); | |
3157f602 | 284 | store(&mut self.temp_qualif[index]); |
a7813a04 XL |
285 | } |
286 | } | |
287 | return; | |
288 | } | |
289 | ||
290 | match *dest { | |
ff7c6d11 | 291 | Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => { |
c30ab7b3 SL |
292 | debug!("store to temp {:?}", index); |
293 | store(&mut self.temp_qualif[index]) | |
294 | } | |
ff7c6d11 XL |
295 | Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => { |
296 | debug!("store to return place {:?}", index); | |
c30ab7b3 SL |
297 | store(&mut self.return_qualif) |
298 | } | |
a7813a04 | 299 | |
ff7c6d11 XL |
300 | Place::Projection(box Projection { |
301 | base: Place::Local(index), | |
a7813a04 | 302 | elem: ProjectionElem::Deref |
c30ab7b3 | 303 | }) if self.mir.local_kind(index) == LocalKind::Temp |
32a655c1 | 304 | && self.mir.local_decls[index].ty.is_box() |
3157f602 | 305 | && self.temp_qualif[index].map_or(false, |qualif| { |
a7813a04 XL |
306 | qualif.intersects(Qualif::NOT_CONST) |
307 | }) => { | |
308 | // Part of `box expr`, we should've errored | |
309 | // already for the Box allocation Rvalue. | |
310 | } | |
311 | ||
312 | // This must be an explicit assignment. | |
313 | _ => { | |
314 | // Catch more errors in the destination. | |
ff7c6d11 | 315 | self.visit_place(dest, PlaceContext::Store, location); |
a7813a04 XL |
316 | self.statement_like(); |
317 | } | |
318 | } | |
319 | } | |
320 | ||
a7813a04 | 321 | /// Qualify a whole const, static initializer or const fn. |
0531ce1d | 322 | fn qualify_const(&mut self) -> (Qualif, Lrc<IdxSetBuf<Local>>) { |
7cac9316 | 323 | debug!("qualifying {} {:?}", self.mode, self.def_id); |
c30ab7b3 | 324 | |
a7813a04 XL |
325 | let mir = self.mir; |
326 | ||
3157f602 | 327 | let mut seen_blocks = BitVector::new(mir.basic_blocks().len()); |
a7813a04 XL |
328 | let mut bb = START_BLOCK; |
329 | loop { | |
330 | seen_blocks.insert(bb.index()); | |
331 | ||
332 | self.visit_basic_block_data(bb, &mir[bb]); | |
333 | ||
334 | let target = match mir[bb].terminator().kind { | |
335 | TerminatorKind::Goto { target } | | |
a7813a04 | 336 | TerminatorKind::Drop { target, .. } | |
3157f602 | 337 | TerminatorKind::Assert { target, .. } | |
a7813a04 XL |
338 | TerminatorKind::Call { destination: Some((_, target)), .. } => { |
339 | Some(target) | |
340 | } | |
341 | ||
342 | // Non-terminating calls cannot produce any value. | |
343 | TerminatorKind::Call { destination: None, .. } => { | |
ea8adc8c | 344 | break; |
a7813a04 XL |
345 | } |
346 | ||
a7813a04 | 347 | TerminatorKind::SwitchInt {..} | |
3157f602 XL |
348 | TerminatorKind::DropAndReplace { .. } | |
349 | TerminatorKind::Resume | | |
ff7c6d11 | 350 | TerminatorKind::Abort | |
ea8adc8c XL |
351 | TerminatorKind::GeneratorDrop | |
352 | TerminatorKind::Yield { .. } | | |
abe05a73 | 353 | TerminatorKind::Unreachable | |
2c00a5a8 XL |
354 | TerminatorKind::FalseEdges { .. } | |
355 | TerminatorKind::FalseUnwind { .. } => None, | |
a7813a04 XL |
356 | |
357 | TerminatorKind::Return => { | |
358 | // Check for unused values. This usually means | |
359 | // there are extra statements in the AST. | |
c30ab7b3 | 360 | for temp in mir.temps_iter() { |
3157f602 | 361 | if self.temp_qualif[temp].is_none() { |
a7813a04 XL |
362 | continue; |
363 | } | |
364 | ||
3157f602 | 365 | let state = self.temp_promotion_state[temp]; |
a7813a04 XL |
366 | if let TempState::Defined { location, uses: 0 } = state { |
367 | let data = &mir[location.block]; | |
368 | let stmt_idx = location.statement_index; | |
369 | ||
370 | // Get the span for the initialization. | |
3157f602 XL |
371 | let source_info = if stmt_idx < data.statements.len() { |
372 | data.statements[stmt_idx].source_info | |
a7813a04 | 373 | } else { |
3157f602 XL |
374 | data.terminator().source_info |
375 | }; | |
376 | self.span = source_info.span; | |
a7813a04 XL |
377 | |
378 | // Treat this as a statement in the AST. | |
379 | self.statement_like(); | |
380 | } | |
381 | } | |
382 | ||
383 | // Make sure there are no extra unassigned variables. | |
384 | self.qualif = Qualif::NOT_CONST; | |
c30ab7b3 SL |
385 | for index in mir.vars_iter() { |
386 | if !self.const_fn_arg_vars.contains(index.index()) { | |
387 | debug!("unassigned variable {:?}", index); | |
ff7c6d11 | 388 | self.assign(&Place::Local(index), Location { |
9e0c209e SL |
389 | block: bb, |
390 | statement_index: usize::MAX, | |
391 | }); | |
a7813a04 XL |
392 | } |
393 | } | |
394 | ||
395 | break; | |
396 | } | |
397 | }; | |
398 | ||
399 | match target { | |
400 | // No loops allowed. | |
401 | Some(target) if !seen_blocks.contains(target.index()) => { | |
402 | bb = target; | |
403 | } | |
404 | _ => { | |
405 | self.not_const(); | |
406 | break; | |
407 | } | |
408 | } | |
409 | } | |
410 | ||
a7813a04 XL |
411 | self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST); |
412 | ||
ea8adc8c XL |
413 | // Account for errors in consts by using the |
414 | // conservative type qualification instead. | |
415 | if self.qualif.intersects(Qualif::CONST_ERROR) { | |
416 | self.qualif = Qualif::empty(); | |
abe05a73 | 417 | let return_ty = mir.return_ty(); |
ea8adc8c XL |
418 | self.add_type(return_ty); |
419 | } | |
420 | ||
421 | ||
422 | // Collect all the temps we need to promote. | |
423 | let mut promoted_temps = IdxSetBuf::new_empty(self.temp_promotion_state.len()); | |
424 | ||
425 | for candidate in &self.promotion_candidates { | |
426 | match *candidate { | |
427 | Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { | |
428 | match self.mir[bb].statements[stmt_idx].kind { | |
ff7c6d11 | 429 | StatementKind::Assign(_, Rvalue::Ref(_, _, Place::Local(index))) => { |
ea8adc8c XL |
430 | promoted_temps.add(&index); |
431 | } | |
432 | _ => {} | |
433 | } | |
a7813a04 | 434 | } |
2c00a5a8 | 435 | Candidate::Argument { .. } => {} |
a7813a04 XL |
436 | } |
437 | } | |
ea8adc8c | 438 | |
0531ce1d | 439 | (self.qualif, Lrc::new(promoted_temps)) |
a7813a04 XL |
440 | } |
441 | } | |
442 | ||
443 | /// Accumulates an Rvalue or Call's effects in self.qualif. | |
444 | /// For functions (constant or not), it also records | |
445 | /// candidates for promotion in promotion_candidates. | |
446 | impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | |
ea8adc8c XL |
447 | fn visit_local(&mut self, |
448 | &local: &Local, | |
ff7c6d11 | 449 | _: PlaceContext<'tcx>, |
ea8adc8c | 450 | _: Location) { |
2c00a5a8 XL |
451 | let kind = self.mir.local_kind(local); |
452 | match kind { | |
ea8adc8c XL |
453 | LocalKind::ReturnPointer => { |
454 | self.not_const(); | |
455 | } | |
ea8adc8c XL |
456 | LocalKind::Var => { |
457 | self.add(Qualif::NOT_CONST); | |
458 | } | |
2c00a5a8 | 459 | LocalKind::Arg | |
ea8adc8c | 460 | LocalKind::Temp => { |
2c00a5a8 XL |
461 | if let LocalKind::Arg = kind { |
462 | self.add(Qualif::FN_ARGUMENT); | |
463 | } | |
464 | ||
ea8adc8c XL |
465 | if !self.temp_promotion_state[local].is_promotable() { |
466 | self.add(Qualif::NOT_PROMOTABLE); | |
467 | } | |
468 | ||
469 | if let Some(qualif) = self.temp_qualif[local] { | |
470 | self.add(qualif); | |
471 | } else { | |
472 | self.not_const(); | |
473 | } | |
474 | } | |
475 | } | |
476 | } | |
477 | ||
ff7c6d11 XL |
478 | fn visit_place(&mut self, |
479 | place: &Place<'tcx>, | |
480 | context: PlaceContext<'tcx>, | |
9e0c209e | 481 | location: Location) { |
ff7c6d11 XL |
482 | match *place { |
483 | Place::Local(ref local) => self.visit_local(local, context, location), | |
484 | Place::Static(ref global) => { | |
a7813a04 | 485 | self.add(Qualif::STATIC); |
3b2f2976 XL |
486 | |
487 | if self.mode != Mode::Fn { | |
488 | for attr in &self.tcx.get_attrs(global.def_id)[..] { | |
489 | if attr.check_name("thread_local") { | |
490 | span_err!(self.tcx.sess, self.span, E0625, | |
491 | "thread-local statics cannot be \ | |
492 | accessed at compile-time"); | |
ea8adc8c | 493 | self.add(Qualif::NOT_CONST); |
3b2f2976 XL |
494 | return; |
495 | } | |
496 | } | |
497 | } | |
498 | ||
a7813a04 | 499 | if self.mode == Mode::Const || self.mode == Mode::ConstFn { |
2c00a5a8 XL |
500 | let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, |
501 | "{}s cannot refer to statics, use \ | |
502 | a constant instead", self.mode); | |
503 | if self.tcx.sess.teach(&err.get_code().unwrap()) { | |
504 | err.note( | |
505 | "Static and const variables can refer to other const variables. But a \ | |
506 | const variable cannot refer to a static variable." | |
507 | ); | |
508 | err.help( | |
509 | "To fix this, the value can be extracted as a const and then used." | |
510 | ); | |
511 | } | |
512 | err.emit() | |
a7813a04 XL |
513 | } |
514 | } | |
ff7c6d11 | 515 | Place::Projection(ref proj) => { |
a7813a04 | 516 | self.nest(|this| { |
ff7c6d11 | 517 | this.super_place(place, context, location); |
a7813a04 XL |
518 | match proj.elem { |
519 | ProjectionElem::Deref => { | |
520 | if !this.try_consume() { | |
521 | return; | |
522 | } | |
523 | ||
524 | if this.qualif.intersects(Qualif::STATIC_REF) { | |
525 | this.qualif = this.qualif - Qualif::STATIC_REF; | |
526 | this.add(Qualif::STATIC); | |
527 | } | |
528 | ||
0531ce1d XL |
529 | this.add(Qualif::NOT_CONST); |
530 | ||
5bcae85e | 531 | let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); |
a7813a04 | 532 | if let ty::TyRawPtr(_) = base_ty.sty { |
a7813a04 | 533 | if this.mode != Mode::Fn { |
2c00a5a8 XL |
534 | let mut err = struct_span_err!( |
535 | this.tcx.sess, | |
536 | this.span, | |
537 | E0396, | |
9e0c209e | 538 | "raw pointers cannot be dereferenced in {}s", |
2c00a5a8 XL |
539 | this.mode |
540 | ); | |
541 | err.span_label(this.span, | |
542 | "dereference of raw pointer in constant"); | |
543 | if this.tcx.sess.teach(&err.get_code().unwrap()) { | |
544 | err.note( | |
545 | "The value behind a raw pointer can't be determined \ | |
546 | at compile-time (or even link-time), which means it \ | |
547 | can't be used in a constant expression." | |
548 | ); | |
549 | err.help("A possible fix is to dereference your pointer \ | |
550 | at some point in run-time."); | |
551 | } | |
552 | err.emit(); | |
a7813a04 XL |
553 | } |
554 | } | |
555 | } | |
556 | ||
557 | ProjectionElem::Field(..) | | |
558 | ProjectionElem::Index(_) => { | |
83c7162d XL |
559 | if this.mode == Mode::Fn { |
560 | let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); | |
561 | if let Some(def) = base_ty.ty_adt_def() { | |
562 | if def.is_union() { | |
563 | this.not_const(); | |
564 | } | |
565 | } | |
566 | } else if this.qualif.intersects(Qualif::STATIC) { | |
a7813a04 XL |
567 | span_err!(this.tcx.sess, this.span, E0494, |
568 | "cannot refer to the interior of another \ | |
569 | static, use a constant instead"); | |
570 | } | |
ff7c6d11 | 571 | let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); |
7cac9316 | 572 | this.qualif.restrict(ty, this.tcx, this.param_env); |
a7813a04 XL |
573 | } |
574 | ||
575 | ProjectionElem::ConstantIndex {..} | | |
3157f602 | 576 | ProjectionElem::Subslice {..} | |
a7813a04 XL |
577 | ProjectionElem::Downcast(..) => { |
578 | this.not_const() | |
579 | } | |
580 | } | |
581 | }); | |
582 | } | |
583 | } | |
584 | } | |
585 | ||
9e0c209e | 586 | fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { |
a7813a04 | 587 | match *operand { |
2c00a5a8 XL |
588 | Operand::Copy(_) | |
589 | Operand::Move(_) => { | |
a7813a04 | 590 | self.nest(|this| { |
9e0c209e | 591 | this.super_operand(operand, location); |
a7813a04 XL |
592 | this.try_consume(); |
593 | }); | |
ea8adc8c XL |
594 | |
595 | // Mark the consumed locals to indicate later drops are noops. | |
2c00a5a8 XL |
596 | if let Operand::Move(Place::Local(local)) = *operand { |
597 | self.temp_qualif[local] = self.temp_qualif[local].map(|q| | |
598 | q - Qualif::NEEDS_DROP | |
599 | ); | |
ea8adc8c | 600 | } |
a7813a04 XL |
601 | } |
602 | Operand::Constant(ref constant) => { | |
ea8adc8c XL |
603 | if let Literal::Value { |
604 | value: &ty::Const { val: ConstVal::Unevaluated(def_id, _), ty } | |
605 | } = constant.literal { | |
606 | // Don't peek inside trait associated constants. | |
607 | if self.tcx.trait_of_item(def_id).is_some() { | |
608 | self.add_type(ty); | |
a7813a04 | 609 | } else { |
ea8adc8c | 610 | let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); |
a7813a04 | 611 | |
8bb4bdeb XL |
612 | let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif"); |
613 | self.add(qualif); | |
a7813a04 | 614 | |
ea8adc8c XL |
615 | // Just in case the type is more specific than |
616 | // the definition, e.g. impl associated const | |
617 | // with type parameters, take it into account. | |
618 | self.qualif.restrict(ty, self.tcx, self.param_env); | |
a7813a04 XL |
619 | } |
620 | } | |
621 | } | |
622 | } | |
623 | } | |
624 | ||
9e0c209e | 625 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { |
ff7c6d11 | 626 | // Recurse through operands and places. |
0531ce1d XL |
627 | if let Rvalue::Ref(region, kind, ref place) = *rvalue { |
628 | let mut is_reborrow = false; | |
629 | if let Place::Projection(ref proj) = *place { | |
630 | if let ProjectionElem::Deref = proj.elem { | |
631 | let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); | |
632 | if let ty::TyRef(..) = base_ty.sty { | |
633 | is_reborrow = true; | |
634 | } | |
635 | } | |
636 | } | |
637 | ||
638 | if is_reborrow { | |
639 | self.nest(|this| { | |
640 | this.super_place(place, PlaceContext::Borrow { | |
641 | region, | |
642 | kind | |
643 | }, location); | |
644 | if !this.try_consume() { | |
645 | return; | |
646 | } | |
647 | ||
648 | if this.qualif.intersects(Qualif::STATIC_REF) { | |
649 | this.qualif = this.qualif - Qualif::STATIC_REF; | |
650 | this.add(Qualif::STATIC); | |
651 | } | |
652 | }); | |
653 | } else { | |
654 | self.super_rvalue(rvalue, location); | |
655 | } | |
656 | } else { | |
657 | self.super_rvalue(rvalue, location); | |
658 | } | |
a7813a04 XL |
659 | |
660 | match *rvalue { | |
661 | Rvalue::Use(_) | | |
662 | Rvalue::Repeat(..) | | |
7cac9316 XL |
663 | Rvalue::UnaryOp(UnOp::Neg, _) | |
664 | Rvalue::UnaryOp(UnOp::Not, _) | | |
665 | Rvalue::NullaryOp(NullOp::SizeOf, _) | | |
3157f602 | 666 | Rvalue::CheckedBinaryOp(..) | |
9e0c209e SL |
667 | Rvalue::Cast(CastKind::ReifyFnPointer, ..) | |
668 | Rvalue::Cast(CastKind::UnsafeFnPointer, ..) | | |
8bb4bdeb | 669 | Rvalue::Cast(CastKind::ClosureFnPointer, ..) | |
cc61c64b XL |
670 | Rvalue::Cast(CastKind::Unsize, ..) | |
671 | Rvalue::Discriminant(..) => {} | |
a7813a04 XL |
672 | |
673 | Rvalue::Len(_) => { | |
ff7c6d11 | 674 | // Static places in consts would have errored already, |
a7813a04 XL |
675 | // don't treat length checks as reads from statics. |
676 | self.qualif = self.qualif - Qualif::STATIC; | |
677 | } | |
678 | ||
ff7c6d11 XL |
679 | Rvalue::Ref(_, kind, ref place) => { |
680 | // Static places in consts would have errored already, | |
a7813a04 XL |
681 | // only keep track of references to them here. |
682 | if self.qualif.intersects(Qualif::STATIC) { | |
683 | self.qualif = self.qualif - Qualif::STATIC; | |
684 | self.add(Qualif::STATIC_REF); | |
685 | } | |
686 | ||
ff7c6d11 | 687 | let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); |
2c00a5a8 | 688 | if let BorrowKind::Mut { .. } = kind { |
a7813a04 XL |
689 | // In theory, any zero-sized value could be borrowed |
690 | // mutably without consequences. However, only &mut [] | |
691 | // is allowed right now, and only in functions. | |
692 | let allow = if self.mode == Mode::StaticMut { | |
693 | // Inside a `static mut`, &mut [...] is also allowed. | |
694 | match ty.sty { | |
ea8adc8c | 695 | ty::TyArray(..) | ty::TySlice(_) => true, |
a7813a04 XL |
696 | _ => false |
697 | } | |
ea8adc8c | 698 | } else if let ty::TyArray(_, len) = ty.sty { |
0531ce1d | 699 | len.val.unwrap_u64() == 0 && |
ea8adc8c | 700 | self.mode == Mode::Fn |
a7813a04 XL |
701 | } else { |
702 | false | |
703 | }; | |
704 | ||
705 | if !allow { | |
706 | self.add(Qualif::NOT_CONST); | |
707 | if self.mode != Mode::Fn { | |
2c00a5a8 XL |
708 | let mut err = struct_span_err!(self.tcx.sess, self.span, E0017, |
709 | "references in {}s may only refer \ | |
710 | to immutable values", self.mode); | |
711 | err.span_label(self.span, format!("{}s require immutable values", | |
712 | self.mode)); | |
713 | if self.tcx.sess.teach(&err.get_code().unwrap()) { | |
714 | err.note("References in statics and constants may only refer to \ | |
715 | immutable values.\n\n\ | |
716 | Statics are shared everywhere, and if they refer to \ | |
717 | mutable data one might violate memory safety since \ | |
718 | holding multiple mutable references to shared data is \ | |
719 | not allowed.\n\n\ | |
720 | If you really want global mutable state, try using \ | |
721 | static mut or a global UnsafeCell."); | |
722 | } | |
723 | err.emit(); | |
a7813a04 XL |
724 | } |
725 | } | |
726 | } else { | |
727 | // Constants cannot be borrowed if they contain interior mutability as | |
728 | // it means that our "silent insertion of statics" could change | |
729 | // initializer values (very bad). | |
730 | if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) { | |
731 | // Replace MUTABLE_INTERIOR with NOT_CONST to avoid | |
732 | // duplicate errors (from reborrowing, for example). | |
733 | self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR; | |
734 | self.add(Qualif::NOT_CONST); | |
735 | if self.mode != Mode::Fn { | |
736 | span_err!(self.tcx.sess, self.span, E0492, | |
041b39d2 | 737 | "cannot borrow a constant which may contain \ |
a7813a04 XL |
738 | interior mutability, create a static instead"); |
739 | } | |
740 | } | |
741 | } | |
742 | ||
743 | // We might have a candidate for promotion. | |
9e0c209e | 744 | let candidate = Candidate::Ref(location); |
ff7c6d11 | 745 | if self.can_promote() { |
ea8adc8c | 746 | // We can only promote direct borrows of temps. |
ff7c6d11 | 747 | if let Place::Local(local) = *place { |
ea8adc8c XL |
748 | if self.mir.local_kind(local) == LocalKind::Temp { |
749 | self.promotion_candidates.push(candidate); | |
a7813a04 XL |
750 | } |
751 | } | |
752 | } | |
753 | } | |
754 | ||
755 | Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { | |
5bcae85e | 756 | let operand_ty = operand.ty(self.mir, self.tcx); |
a7813a04 XL |
757 | let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); |
758 | let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); | |
759 | match (cast_in, cast_out) { | |
760 | (CastTy::Ptr(_), CastTy::Int(_)) | | |
761 | (CastTy::FnPtr, CastTy::Int(_)) => { | |
762 | self.add(Qualif::NOT_CONST); | |
763 | if self.mode != Mode::Fn { | |
2c00a5a8 XL |
764 | let mut err = struct_span_err!( |
765 | self.tcx.sess, | |
766 | self.span, | |
767 | E0018, | |
768 | "raw pointers cannot be cast to integers in {}s", | |
769 | self.mode | |
770 | ); | |
771 | if self.tcx.sess.teach(&err.get_code().unwrap()) { | |
772 | err.note("\ | |
773 | The value of static and constant integers must be known at compile time. You can't cast a pointer \ | |
774 | to an integer because the address of a pointer can vary. | |
775 | ||
776 | For example, if you write: | |
777 | ||
778 | ``` | |
779 | static MY_STATIC: u32 = 42; | |
780 | static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize; | |
781 | static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR; | |
782 | ``` | |
783 | ||
784 | Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However, the address can change \ | |
785 | when the program is linked, as well as change between different executions due to ASLR, and many \ | |
786 | linkers would not be able to calculate the value of `WHAT`. | |
787 | ||
788 | On the other hand, static and constant pointers can point either to a known numeric address or to \ | |
789 | the address of a symbol. | |
790 | ||
791 | ``` | |
792 | static MY_STATIC: u32 = 42; | |
793 | static MY_STATIC_ADDR: &'static u32 = &MY_STATIC; | |
794 | const CONST_ADDR: *const u8 = 0x5f3759df as *const u8; | |
795 | ``` | |
796 | ||
797 | This does not pose a problem by itself because they can't be accessed directly."); | |
798 | } | |
799 | err.emit(); | |
a7813a04 XL |
800 | } |
801 | } | |
802 | _ => {} | |
803 | } | |
804 | } | |
805 | ||
806 | Rvalue::BinaryOp(op, ref lhs, _) => { | |
5bcae85e | 807 | if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty { |
a7813a04 XL |
808 | assert!(op == BinOp::Eq || op == BinOp::Ne || |
809 | op == BinOp::Le || op == BinOp::Lt || | |
7cac9316 XL |
810 | op == BinOp::Ge || op == BinOp::Gt || |
811 | op == BinOp::Offset); | |
a7813a04 XL |
812 | |
813 | self.add(Qualif::NOT_CONST); | |
814 | if self.mode != Mode::Fn { | |
9e0c209e SL |
815 | struct_span_err!( |
816 | self.tcx.sess, self.span, E0395, | |
817 | "raw pointers cannot be compared in {}s", | |
818 | self.mode) | |
819 | .span_label( | |
820 | self.span, | |
7cac9316 | 821 | "comparing raw pointers in static") |
9e0c209e | 822 | .emit(); |
a7813a04 XL |
823 | } |
824 | } | |
825 | } | |
826 | ||
7cac9316 | 827 | Rvalue::NullaryOp(NullOp::Box, _) => { |
a7813a04 XL |
828 | self.add(Qualif::NOT_CONST); |
829 | if self.mode != Mode::Fn { | |
2c00a5a8 XL |
830 | let mut err = struct_span_err!(self.tcx.sess, self.span, E0010, |
831 | "allocations are not allowed in {}s", self.mode); | |
832 | err.span_label(self.span, format!("allocation not allowed in {}s", self.mode)); | |
833 | if self.tcx.sess.teach(&err.get_code().unwrap()) { | |
834 | err.note( | |
835 | "The value of statics and constants must be known at compile time, \ | |
836 | and they live for the entire lifetime of a program. Creating a boxed \ | |
837 | value allocates memory on the heap at runtime, and therefore cannot \ | |
838 | be done at compile time." | |
839 | ); | |
840 | } | |
841 | err.emit(); | |
a7813a04 XL |
842 | } |
843 | } | |
844 | ||
845 | Rvalue::Aggregate(ref kind, _) => { | |
cc61c64b | 846 | if let AggregateKind::Adt(def, ..) = **kind { |
8bb4bdeb | 847 | if def.has_dtor(self.tcx) { |
a7813a04 | 848 | self.add(Qualif::NEEDS_DROP); |
a7813a04 XL |
849 | } |
850 | ||
ea8adc8c | 851 | if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() { |
8bb4bdeb | 852 | let ty = rvalue.ty(self.mir, self.tcx); |
a7813a04 XL |
853 | self.add_type(ty); |
854 | assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR)); | |
a7813a04 XL |
855 | } |
856 | } | |
857 | } | |
a7813a04 XL |
858 | } |
859 | } | |
860 | ||
9e0c209e SL |
861 | fn visit_terminator_kind(&mut self, |
862 | bb: BasicBlock, | |
863 | kind: &TerminatorKind<'tcx>, | |
864 | location: Location) { | |
a7813a04 | 865 | if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind { |
9e0c209e | 866 | self.visit_operand(func, location); |
a7813a04 | 867 | |
5bcae85e | 868 | let fn_ty = func.ty(self.mir, self.tcx); |
2c00a5a8 | 869 | let mut callee_def_id = None; |
ea8adc8c | 870 | let (mut is_shuffle, mut is_const_fn) = (false, None); |
3b2f2976 | 871 | if let ty::TyFnDef(def_id, _) = fn_ty.sty { |
2c00a5a8 | 872 | callee_def_id = Some(def_id); |
3b2f2976 XL |
873 | match self.tcx.fn_sig(def_id).abi() { |
874 | Abi::RustIntrinsic | | |
875 | Abi::PlatformIntrinsic => { | |
876 | assert!(!self.tcx.is_const_fn(def_id)); | |
83c7162d | 877 | match &self.tcx.item_name(def_id).as_str()[..] { |
2c00a5a8 | 878 | "size_of" | "min_align_of" | "type_id" => is_const_fn = Some(def_id), |
3b2f2976 XL |
879 | |
880 | name if name.starts_with("simd_shuffle") => { | |
881 | is_shuffle = true; | |
882 | } | |
883 | ||
884 | _ => {} | |
885 | } | |
886 | } | |
887 | _ => { | |
ea8adc8c XL |
888 | if self.tcx.is_const_fn(def_id) { |
889 | is_const_fn = Some(def_id); | |
890 | } | |
3b2f2976 | 891 | } |
a7813a04 | 892 | } |
3b2f2976 | 893 | } |
a7813a04 | 894 | |
2c00a5a8 XL |
895 | let constant_arguments = callee_def_id.and_then(|id| { |
896 | args_required_const(self.tcx, id) | |
897 | }); | |
a7813a04 XL |
898 | for (i, arg) in args.iter().enumerate() { |
899 | self.nest(|this| { | |
9e0c209e | 900 | this.visit_operand(arg, location); |
2c00a5a8 XL |
901 | if this.mode != Mode::Fn { |
902 | return | |
903 | } | |
904 | let candidate = Candidate::Argument { bb, index: i }; | |
905 | if is_shuffle && i == 2 { | |
ff7c6d11 | 906 | if this.can_promote() { |
a7813a04 XL |
907 | this.promotion_candidates.push(candidate); |
908 | } else { | |
909 | span_err!(this.tcx.sess, this.span, E0526, | |
910 | "shuffle indices are not constant"); | |
911 | } | |
2c00a5a8 XL |
912 | return |
913 | } | |
914 | ||
915 | let constant_arguments = match constant_arguments.as_ref() { | |
916 | Some(s) => s, | |
917 | None => return, | |
918 | }; | |
919 | if !constant_arguments.contains(&i) { | |
920 | return | |
921 | } | |
922 | if this.can_promote() { | |
923 | this.promotion_candidates.push(candidate); | |
924 | } else { | |
925 | this.tcx.sess.span_err(this.span, | |
926 | &format!("argument {} is required to be a constant", | |
927 | i + 1)); | |
a7813a04 XL |
928 | } |
929 | }); | |
930 | } | |
931 | ||
932 | // Const fn calls. | |
ea8adc8c XL |
933 | if let Some(def_id) = is_const_fn { |
934 | // find corresponding rustc_const_unstable feature | |
935 | if let Some(&attr::Stability { | |
936 | rustc_const_unstable: Some(attr::RustcConstUnstable { | |
937 | feature: ref feature_name | |
938 | }), | |
939 | .. }) = self.tcx.lookup_stability(def_id) { | |
83c7162d | 940 | if |
ea8adc8c | 941 | // feature-gate is not enabled, |
0531ce1d | 942 | !self.tcx.features() |
ea8adc8c XL |
943 | .declared_lib_features |
944 | .iter() | |
945 | .any(|&(ref sym, _)| sym == feature_name) && | |
946 | ||
947 | // this doesn't come from a crate with the feature-gate enabled, | |
948 | self.def_id.is_local() && | |
949 | ||
950 | // this doesn't come from a macro that has #[allow_internal_unstable] | |
951 | !self.span.allows_unstable() | |
952 | { | |
83c7162d XL |
953 | self.qualif = Qualif::NOT_CONST; |
954 | if self.mode != Mode::Fn { | |
955 | // inside a constant environment, not having the feature gate is | |
956 | // an error | |
957 | let mut err = self.tcx.sess.struct_span_err(self.span, | |
958 | &format!("`{}` is not yet stable as a const fn", | |
959 | self.tcx.item_path_str(def_id))); | |
960 | help!(&mut err, | |
961 | "in Nightly builds, add `#![feature({})]` \ | |
962 | to the crate attributes to enable", | |
963 | feature_name); | |
964 | err.emit(); | |
965 | } | |
ea8adc8c | 966 | } |
a7813a04 XL |
967 | } |
968 | } else { | |
969 | self.qualif = Qualif::NOT_CONST; | |
970 | if self.mode != Mode::Fn { | |
971 | // FIXME(#24111) Remove this check when const fn stabilizes | |
972 | let (msg, note) = if let UnstableFeatures::Disallow = | |
973 | self.tcx.sess.opts.unstable_features { | |
974 | (format!("calls in {}s are limited to \ | |
83c7162d | 975 | tuple structs and tuple variants", |
a7813a04 XL |
976 | self.mode), |
977 | Some("a limited form of compile-time function \ | |
978 | evaluation is available on a nightly \ | |
979 | compiler via `const fn`")) | |
980 | } else { | |
981 | (format!("calls in {}s are limited \ | |
982 | to constant functions, \ | |
83c7162d | 983 | tuple structs and tuple variants", |
a7813a04 XL |
984 | self.mode), |
985 | None) | |
986 | }; | |
987 | let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg); | |
988 | if let Some(note) = note { | |
989 | err.span_note(self.span, note); | |
990 | } | |
991 | err.emit(); | |
992 | } | |
993 | } | |
994 | ||
995 | if let Some((ref dest, _)) = *destination { | |
996 | // Avoid propagating irrelevant callee/argument qualifications. | |
997 | if self.qualif.intersects(Qualif::CONST_ERROR) { | |
998 | self.qualif = Qualif::NOT_CONST; | |
999 | } else { | |
1000 | // Be conservative about the returned value of a const fn. | |
1001 | let tcx = self.tcx; | |
5bcae85e | 1002 | let ty = dest.ty(self.mir, tcx).to_ty(tcx); |
a7813a04 XL |
1003 | self.qualif = Qualif::empty(); |
1004 | self.add_type(ty); | |
ea8adc8c XL |
1005 | } |
1006 | self.assign(dest, location); | |
1007 | } | |
ff7c6d11 | 1008 | } else if let TerminatorKind::Drop { location: ref place, .. } = *kind { |
ea8adc8c | 1009 | self.super_terminator_kind(bb, kind, location); |
a7813a04 | 1010 | |
ea8adc8c XL |
1011 | // Deny *any* live drops anywhere other than functions. |
1012 | if self.mode != Mode::Fn { | |
1013 | // HACK(eddyb) Emulate a bit of dataflow analysis, | |
1014 | // conservatively, that drop elaboration will do. | |
ff7c6d11 | 1015 | let needs_drop = if let Place::Local(local) = *place { |
2c00a5a8 XL |
1016 | if self.temp_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) { |
1017 | Some(self.mir.local_decls[local].source_info.span) | |
1018 | } else { | |
1019 | None | |
1020 | } | |
ea8adc8c | 1021 | } else { |
2c00a5a8 | 1022 | Some(self.span) |
ea8adc8c XL |
1023 | }; |
1024 | ||
1025 | if let Some(span) = needs_drop { | |
1026 | // Double-check the type being dropped, to minimize false positives. | |
ff7c6d11 | 1027 | let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); |
ea8adc8c XL |
1028 | if ty.needs_drop(self.tcx, self.param_env) { |
1029 | struct_span_err!(self.tcx.sess, span, E0493, | |
1030 | "destructors cannot be evaluated at compile-time") | |
1031 | .span_label(span, format!("{}s cannot evaluate destructors", | |
1032 | self.mode)) | |
1033 | .emit(); | |
a7813a04 XL |
1034 | } |
1035 | } | |
a7813a04 XL |
1036 | } |
1037 | } else { | |
1038 | // Qualify any operands inside other terminators. | |
9e0c209e | 1039 | self.super_terminator_kind(bb, kind, location); |
a7813a04 XL |
1040 | } |
1041 | } | |
1042 | ||
9e0c209e SL |
1043 | fn visit_assign(&mut self, |
1044 | _: BasicBlock, | |
ff7c6d11 | 1045 | dest: &Place<'tcx>, |
9e0c209e SL |
1046 | rvalue: &Rvalue<'tcx>, |
1047 | location: Location) { | |
1048 | self.visit_rvalue(rvalue, location); | |
a7813a04 XL |
1049 | |
1050 | // Check the allowed const fn argument forms. | |
ff7c6d11 | 1051 | if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) { |
c30ab7b3 SL |
1052 | if self.mir.local_kind(index) == LocalKind::Var && |
1053 | self.const_fn_arg_vars.insert(index.index()) { | |
1054 | ||
a7813a04 | 1055 | // Direct use of an argument is permitted. |
ff7c6d11 XL |
1056 | match *rvalue { |
1057 | Rvalue::Use(Operand::Copy(Place::Local(local))) | | |
1058 | Rvalue::Use(Operand::Move(Place::Local(local))) => { | |
1059 | if self.mir.local_kind(local) == LocalKind::Arg { | |
1060 | return; | |
1061 | } | |
c30ab7b3 | 1062 | } |
ff7c6d11 | 1063 | _ => {} |
a7813a04 XL |
1064 | } |
1065 | ||
1066 | // Avoid a generic error for other uses of arguments. | |
1067 | if self.qualif.intersects(Qualif::FN_ARGUMENT) { | |
c30ab7b3 | 1068 | let decl = &self.mir.local_decls[index]; |
2c00a5a8 XL |
1069 | let mut err = struct_span_err!( |
1070 | self.tcx.sess, | |
1071 | decl.source_info.span, | |
1072 | E0022, | |
1073 | "arguments of constant functions can only be immutable by-value bindings" | |
1074 | ); | |
1075 | if self.tcx.sess.teach(&err.get_code().unwrap()) { | |
1076 | err.note("Constant functions are not allowed to mutate anything. Thus, \ | |
1077 | binding to an argument with a mutable pattern is not allowed."); | |
1078 | err.note("Remove any mutable bindings from the argument list to fix this \ | |
1079 | error. In case you need to mutate the argument, try lazily \ | |
1080 | initializing a global variable instead of using a const fn, or \ | |
1081 | refactoring the code to a functional style to avoid mutation if \ | |
1082 | possible."); | |
1083 | } | |
1084 | err.emit(); | |
a7813a04 XL |
1085 | return; |
1086 | } | |
1087 | } | |
1088 | } | |
1089 | ||
9e0c209e | 1090 | self.assign(dest, location); |
a7813a04 XL |
1091 | } |
1092 | ||
3157f602 XL |
1093 | fn visit_source_info(&mut self, source_info: &SourceInfo) { |
1094 | self.span = source_info.span; | |
1095 | } | |
1096 | ||
9e0c209e | 1097 | fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) { |
5bcae85e SL |
1098 | self.nest(|this| { |
1099 | this.visit_source_info(&statement.source_info); | |
1100 | match statement.kind { | |
ff7c6d11 XL |
1101 | StatementKind::Assign(ref place, ref rvalue) => { |
1102 | this.visit_assign(bb, place, rvalue, location); | |
5bcae85e SL |
1103 | } |
1104 | StatementKind::SetDiscriminant { .. } | | |
1105 | StatementKind::StorageLive(_) | | |
9e0c209e | 1106 | StatementKind::StorageDead(_) | |
8bb4bdeb | 1107 | StatementKind::InlineAsm {..} | |
041b39d2 | 1108 | StatementKind::EndRegion(_) | |
3b2f2976 | 1109 | StatementKind::Validate(..) | |
0531ce1d | 1110 | StatementKind::UserAssertTy(..) | |
9e0c209e | 1111 | StatementKind::Nop => {} |
5bcae85e SL |
1112 | } |
1113 | }); | |
a7813a04 XL |
1114 | } |
1115 | ||
9e0c209e SL |
1116 | fn visit_terminator(&mut self, |
1117 | bb: BasicBlock, | |
1118 | terminator: &Terminator<'tcx>, | |
1119 | location: Location) { | |
1120 | self.nest(|this| this.super_terminator(bb, terminator, location)); | |
a7813a04 XL |
1121 | } |
1122 | } | |
1123 | ||
8bb4bdeb | 1124 | pub fn provide(providers: &mut Providers) { |
7cac9316 XL |
1125 | *providers = Providers { |
1126 | mir_const_qualif, | |
1127 | ..*providers | |
1128 | }; | |
8bb4bdeb XL |
1129 | } |
1130 | ||
7cac9316 XL |
1131 | fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
1132 | def_id: DefId) | |
0531ce1d | 1133 | -> (u8, Lrc<IdxSetBuf<Local>>) { |
7cac9316 XL |
1134 | // NB: This `borrow()` is guaranteed to be valid (i.e., the value |
1135 | // cannot yet be stolen), because `mir_validated()`, which steals | |
1136 | // from `mir_const(), forces this query to execute before | |
1137 | // performing the steal. | |
1138 | let mir = &tcx.mir_const(def_id).borrow(); | |
1139 | ||
abe05a73 | 1140 | if mir.return_ty().references_error() { |
ea8adc8c | 1141 | tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors"); |
0531ce1d | 1142 | return (Qualif::NOT_CONST.bits(), Lrc::new(IdxSetBuf::new_empty(0))); |
a7813a04 XL |
1143 | } |
1144 | ||
ea8adc8c XL |
1145 | let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const); |
1146 | let (qualif, promoted_temps) = qualifier.qualify_const(); | |
1147 | (qualif.bits(), promoted_temps) | |
a7813a04 XL |
1148 | } |
1149 | ||
8bb4bdeb | 1150 | pub struct QualifyAndPromoteConstants; |
a7813a04 | 1151 | |
7cac9316 XL |
1152 | impl MirPass for QualifyAndPromoteConstants { |
1153 | fn run_pass<'a, 'tcx>(&self, | |
1154 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1155 | src: MirSource, | |
1156 | mir: &mut Mir<'tcx>) { | |
ea8adc8c | 1157 | // There's not really any point in promoting errorful MIR. |
abe05a73 | 1158 | if mir.return_ty().references_error() { |
ea8adc8c XL |
1159 | tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors"); |
1160 | return; | |
1161 | } | |
1162 | ||
abe05a73 XL |
1163 | if src.promoted.is_some() { |
1164 | return; | |
1165 | } | |
1166 | ||
1167 | let def_id = src.def_id; | |
1168 | let id = tcx.hir.as_local_node_id(def_id).unwrap(); | |
ea8adc8c | 1169 | let mut const_promoted_temps = None; |
abe05a73 XL |
1170 | let mode = match tcx.hir.body_owner_kind(id) { |
1171 | hir::BodyOwnerKind::Fn => { | |
041b39d2 | 1172 | if tcx.is_const_fn(def_id) { |
c30ab7b3 SL |
1173 | Mode::ConstFn |
1174 | } else { | |
1175 | Mode::Fn | |
1176 | } | |
a7813a04 | 1177 | } |
abe05a73 | 1178 | hir::BodyOwnerKind::Const => { |
ea8adc8c XL |
1179 | const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1); |
1180 | Mode::Const | |
1181 | } | |
abe05a73 XL |
1182 | hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static, |
1183 | hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut, | |
c30ab7b3 | 1184 | }; |
c30ab7b3 SL |
1185 | |
1186 | if mode == Mode::Fn || mode == Mode::ConstFn { | |
1187 | // This is ugly because Qualifier holds onto mir, | |
1188 | // which can't be mutated until its scope ends. | |
1189 | let (temps, candidates) = { | |
ea8adc8c | 1190 | let mut qualifier = Qualifier::new(tcx, def_id, mir, mode); |
c30ab7b3 SL |
1191 | if mode == Mode::ConstFn { |
1192 | // Enforce a constant-like CFG for `const fn`. | |
1193 | qualifier.qualify_const(); | |
1194 | } else { | |
1195 | while let Some((bb, data)) = qualifier.rpo.next() { | |
1196 | qualifier.visit_basic_block_data(bb, data); | |
a7813a04 | 1197 | } |
c30ab7b3 | 1198 | } |
a7813a04 | 1199 | |
c30ab7b3 SL |
1200 | (qualifier.temp_promotion_state, qualifier.promotion_candidates) |
1201 | }; | |
a7813a04 | 1202 | |
c30ab7b3 SL |
1203 | // Do the actual promotion, now that we know what's viable. |
1204 | promote_consts::promote_candidates(mir, tcx, temps, candidates); | |
1205 | } else { | |
ea8adc8c XL |
1206 | let promoted_temps = if mode == Mode::Const { |
1207 | // Already computed by `mir_const_qualif`. | |
1208 | const_promoted_temps.unwrap() | |
1209 | } else { | |
1210 | Qualifier::new(tcx, def_id, mir, mode).qualify_const().1 | |
1211 | }; | |
1212 | ||
1213 | // In `const` and `static` everything without `StorageDead` | |
1214 | // is `'static`, we don't have to create promoted MIR fragments, | |
1215 | // just remove `Drop` and `StorageDead` on "promoted" locals. | |
1216 | for block in mir.basic_blocks_mut() { | |
1217 | block.statements.retain(|statement| { | |
1218 | match statement.kind { | |
1219 | StatementKind::StorageDead(index) => { | |
1220 | !promoted_temps.contains(&index) | |
1221 | } | |
1222 | _ => true | |
1223 | } | |
1224 | }); | |
1225 | let terminator = block.terminator_mut(); | |
1226 | match terminator.kind { | |
ff7c6d11 | 1227 | TerminatorKind::Drop { location: Place::Local(index), target, .. } => { |
ea8adc8c XL |
1228 | if promoted_temps.contains(&index) { |
1229 | terminator.kind = TerminatorKind::Goto { | |
1230 | target, | |
1231 | }; | |
1232 | } | |
1233 | } | |
1234 | _ => {} | |
1235 | } | |
1236 | } | |
c30ab7b3 | 1237 | } |
3157f602 | 1238 | |
c30ab7b3 SL |
1239 | // Statics must be Sync. |
1240 | if mode == Mode::Static { | |
3b2f2976 XL |
1241 | // `#[thread_local]` statics don't have to be `Sync`. |
1242 | for attr in &tcx.get_attrs(def_id)[..] { | |
1243 | if attr.check_name("thread_local") { | |
1244 | return; | |
1245 | } | |
1246 | } | |
abe05a73 | 1247 | let ty = mir.return_ty(); |
041b39d2 | 1248 | tcx.infer_ctxt().enter(|infcx| { |
0531ce1d | 1249 | let param_env = ty::ParamEnv::empty(); |
c30ab7b3 SL |
1250 | let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic); |
1251 | let mut fulfillment_cx = traits::FulfillmentContext::new(); | |
7cac9316 XL |
1252 | fulfillment_cx.register_bound(&infcx, |
1253 | param_env, | |
1254 | ty, | |
476ff2be SL |
1255 | tcx.require_lang_item(lang_items::SyncTraitLangItem), |
1256 | cause); | |
c30ab7b3 | 1257 | if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { |
0531ce1d | 1258 | infcx.report_fulfillment_errors(&err, None, false); |
c30ab7b3 SL |
1259 | } |
1260 | }); | |
a7813a04 XL |
1261 | } |
1262 | } | |
1263 | } | |
2c00a5a8 XL |
1264 | |
1265 | fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> { | |
1266 | let attrs = tcx.get_attrs(def_id); | |
1267 | let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?; | |
1268 | let mut ret = FxHashSet(); | |
1269 | for meta in attr.meta_item_list()? { | |
1270 | match meta.literal()?.node { | |
1271 | LitKind::Int(a, _) => { ret.insert(a as usize); } | |
1272 | _ => return None, | |
1273 | } | |
1274 | } | |
1275 | Some(ret) | |
1276 | } |